mixer_oss.c (41938B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * OSS emulation layer for the mixer interface 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7#include <linux/init.h> 8#include <linux/slab.h> 9#include <linux/time.h> 10#include <linux/string.h> 11#include <linux/module.h> 12#include <linux/compat.h> 13#include <sound/core.h> 14#include <sound/minors.h> 15#include <sound/control.h> 16#include <sound/info.h> 17#include <sound/mixer_oss.h> 18#include <linux/soundcard.h> 19 20#define OSS_ALSAEMULVER _SIOR ('M', 249, int) 21 22MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 23MODULE_DESCRIPTION("Mixer OSS emulation for ALSA."); 24MODULE_LICENSE("GPL"); 25MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER); 26 27static int snd_mixer_oss_open(struct inode *inode, struct file *file) 28{ 29 struct snd_card *card; 30 struct snd_mixer_oss_file *fmixer; 31 int err; 32 33 err = nonseekable_open(inode, file); 34 if (err < 0) 35 return err; 36 37 card = snd_lookup_oss_minor_data(iminor(inode), 38 SNDRV_OSS_DEVICE_TYPE_MIXER); 39 if (card == NULL) 40 return -ENODEV; 41 if (card->mixer_oss == NULL) { 42 snd_card_unref(card); 43 return -ENODEV; 44 } 45 err = snd_card_file_add(card, file); 46 if (err < 0) { 47 snd_card_unref(card); 48 return err; 49 } 50 fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL); 51 if (fmixer == NULL) { 52 snd_card_file_remove(card, file); 53 snd_card_unref(card); 54 return -ENOMEM; 55 } 56 fmixer->card = card; 57 fmixer->mixer = card->mixer_oss; 58 file->private_data = fmixer; 59 if (!try_module_get(card->module)) { 60 kfree(fmixer); 61 snd_card_file_remove(card, file); 62 snd_card_unref(card); 63 return -EFAULT; 64 } 65 snd_card_unref(card); 66 return 0; 67} 68 69static int snd_mixer_oss_release(struct inode *inode, struct file *file) 70{ 71 struct snd_mixer_oss_file *fmixer; 72 73 if (file->private_data) { 74 fmixer = file->private_data; 75 module_put(fmixer->card->module); 76 snd_card_file_remove(fmixer->card, file); 77 kfree(fmixer); 78 } 79 return 0; 80} 81 82static int snd_mixer_oss_info(struct snd_mixer_oss_file *fmixer, 83 mixer_info __user *_info) 84{ 85 struct snd_card *card = fmixer->card; 86 struct snd_mixer_oss *mixer = fmixer->mixer; 87 struct mixer_info info; 88 89 memset(&info, 0, sizeof(info)); 90 strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); 91 strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); 92 info.modify_counter = card->mixer_oss_change_count; 93 if (copy_to_user(_info, &info, sizeof(info))) 94 return -EFAULT; 95 return 0; 96} 97 98static int snd_mixer_oss_info_obsolete(struct snd_mixer_oss_file *fmixer, 99 _old_mixer_info __user *_info) 100{ 101 struct snd_card *card = fmixer->card; 102 struct snd_mixer_oss *mixer = fmixer->mixer; 103 _old_mixer_info info; 104 105 memset(&info, 0, sizeof(info)); 106 strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id)); 107 strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name)); 108 if (copy_to_user(_info, &info, sizeof(info))) 109 return -EFAULT; 110 return 0; 111} 112 113static int snd_mixer_oss_caps(struct snd_mixer_oss_file *fmixer) 114{ 115 struct snd_mixer_oss *mixer = fmixer->mixer; 116 int result = 0; 117 118 if (mixer == NULL) 119 return -EIO; 120 if (mixer->get_recsrc && mixer->put_recsrc) 121 result |= SOUND_CAP_EXCL_INPUT; 122 return result; 123} 124 125static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer) 126{ 127 struct snd_mixer_oss *mixer = fmixer->mixer; 128 struct snd_mixer_oss_slot *pslot; 129 int result = 0, chn; 130 131 if (mixer == NULL) 132 return -EIO; 133 mutex_lock(&mixer->reg_mutex); 134 for (chn = 0; chn < 31; chn++) { 135 pslot = &mixer->slots[chn]; 136 if (pslot->put_volume || pslot->put_recsrc) 137 result |= 1 << chn; 138 } 139 mutex_unlock(&mixer->reg_mutex); 140 return result; 141} 142 143static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer) 144{ 145 struct snd_mixer_oss *mixer = fmixer->mixer; 146 struct snd_mixer_oss_slot *pslot; 147 int result = 0, chn; 148 149 if (mixer == NULL) 150 return -EIO; 151 mutex_lock(&mixer->reg_mutex); 152 for (chn = 0; chn < 31; chn++) { 153 pslot = &mixer->slots[chn]; 154 if (pslot->put_volume && pslot->stereo) 155 result |= 1 << chn; 156 } 157 mutex_unlock(&mixer->reg_mutex); 158 return result; 159} 160 161static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer) 162{ 163 struct snd_mixer_oss *mixer = fmixer->mixer; 164 int result = 0; 165 166 if (mixer == NULL) 167 return -EIO; 168 mutex_lock(&mixer->reg_mutex); 169 if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ 170 result = mixer->mask_recsrc; 171 } else { 172 struct snd_mixer_oss_slot *pslot; 173 int chn; 174 for (chn = 0; chn < 31; chn++) { 175 pslot = &mixer->slots[chn]; 176 if (pslot->put_recsrc) 177 result |= 1 << chn; 178 } 179 } 180 mutex_unlock(&mixer->reg_mutex); 181 return result; 182} 183 184static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) 185{ 186 struct snd_mixer_oss *mixer = fmixer->mixer; 187 int result = 0; 188 189 if (mixer == NULL) 190 return -EIO; 191 mutex_lock(&mixer->reg_mutex); 192 if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ 193 unsigned int index; 194 result = mixer->get_recsrc(fmixer, &index); 195 if (result < 0) 196 goto unlock; 197 result = 1 << index; 198 } else { 199 struct snd_mixer_oss_slot *pslot; 200 int chn; 201 for (chn = 0; chn < 31; chn++) { 202 pslot = &mixer->slots[chn]; 203 if (pslot->get_recsrc) { 204 int active = 0; 205 pslot->get_recsrc(fmixer, pslot, &active); 206 if (active) 207 result |= 1 << chn; 208 } 209 } 210 } 211 mixer->oss_recsrc = result; 212 unlock: 213 mutex_unlock(&mixer->reg_mutex); 214 return result; 215} 216 217static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc) 218{ 219 struct snd_mixer_oss *mixer = fmixer->mixer; 220 struct snd_mixer_oss_slot *pslot; 221 int chn, active; 222 unsigned int index; 223 int result = 0; 224 225 if (mixer == NULL) 226 return -EIO; 227 mutex_lock(&mixer->reg_mutex); 228 if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */ 229 if (recsrc & ~mixer->oss_recsrc) 230 recsrc &= ~mixer->oss_recsrc; 231 mixer->put_recsrc(fmixer, ffz(~recsrc)); 232 mixer->get_recsrc(fmixer, &index); 233 result = 1 << index; 234 } 235 for (chn = 0; chn < 31; chn++) { 236 pslot = &mixer->slots[chn]; 237 if (pslot->put_recsrc) { 238 active = (recsrc & (1 << chn)) ? 1 : 0; 239 pslot->put_recsrc(fmixer, pslot, active); 240 } 241 } 242 if (! result) { 243 for (chn = 0; chn < 31; chn++) { 244 pslot = &mixer->slots[chn]; 245 if (pslot->get_recsrc) { 246 active = 0; 247 pslot->get_recsrc(fmixer, pslot, &active); 248 if (active) 249 result |= 1 << chn; 250 } 251 } 252 } 253 mutex_unlock(&mixer->reg_mutex); 254 return result; 255} 256 257static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot) 258{ 259 struct snd_mixer_oss *mixer = fmixer->mixer; 260 struct snd_mixer_oss_slot *pslot; 261 int result = 0, left, right; 262 263 if (mixer == NULL || slot > 30) 264 return -EIO; 265 mutex_lock(&mixer->reg_mutex); 266 pslot = &mixer->slots[slot]; 267 left = pslot->volume[0]; 268 right = pslot->volume[1]; 269 if (pslot->get_volume) 270 result = pslot->get_volume(fmixer, pslot, &left, &right); 271 if (!pslot->stereo) 272 right = left; 273 if (snd_BUG_ON(left < 0 || left > 100)) { 274 result = -EIO; 275 goto unlock; 276 } 277 if (snd_BUG_ON(right < 0 || right > 100)) { 278 result = -EIO; 279 goto unlock; 280 } 281 if (result >= 0) { 282 pslot->volume[0] = left; 283 pslot->volume[1] = right; 284 result = (left & 0xff) | ((right & 0xff) << 8); 285 } 286 unlock: 287 mutex_unlock(&mixer->reg_mutex); 288 return result; 289} 290 291static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer, 292 int slot, int volume) 293{ 294 struct snd_mixer_oss *mixer = fmixer->mixer; 295 struct snd_mixer_oss_slot *pslot; 296 int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff; 297 298 if (mixer == NULL || slot > 30) 299 return -EIO; 300 mutex_lock(&mixer->reg_mutex); 301 pslot = &mixer->slots[slot]; 302 if (left > 100) 303 left = 100; 304 if (right > 100) 305 right = 100; 306 if (!pslot->stereo) 307 right = left; 308 if (pslot->put_volume) 309 result = pslot->put_volume(fmixer, pslot, left, right); 310 if (result < 0) 311 goto unlock; 312 pslot->volume[0] = left; 313 pslot->volume[1] = right; 314 result = (left & 0xff) | ((right & 0xff) << 8); 315 unlock: 316 mutex_unlock(&mixer->reg_mutex); 317 return result; 318} 319 320static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg) 321{ 322 void __user *argp = (void __user *)arg; 323 int __user *p = argp; 324 int tmp; 325 326 if (snd_BUG_ON(!fmixer)) 327 return -ENXIO; 328 if (((cmd >> 8) & 0xff) == 'M') { 329 switch (cmd) { 330 case SOUND_MIXER_INFO: 331 return snd_mixer_oss_info(fmixer, argp); 332 case SOUND_OLD_MIXER_INFO: 333 return snd_mixer_oss_info_obsolete(fmixer, argp); 334 case SOUND_MIXER_WRITE_RECSRC: 335 if (get_user(tmp, p)) 336 return -EFAULT; 337 tmp = snd_mixer_oss_set_recsrc(fmixer, tmp); 338 if (tmp < 0) 339 return tmp; 340 return put_user(tmp, p); 341 case OSS_GETVERSION: 342 return put_user(SNDRV_OSS_VERSION, p); 343 case OSS_ALSAEMULVER: 344 return put_user(1, p); 345 case SOUND_MIXER_READ_DEVMASK: 346 tmp = snd_mixer_oss_devmask(fmixer); 347 if (tmp < 0) 348 return tmp; 349 return put_user(tmp, p); 350 case SOUND_MIXER_READ_STEREODEVS: 351 tmp = snd_mixer_oss_stereodevs(fmixer); 352 if (tmp < 0) 353 return tmp; 354 return put_user(tmp, p); 355 case SOUND_MIXER_READ_RECMASK: 356 tmp = snd_mixer_oss_recmask(fmixer); 357 if (tmp < 0) 358 return tmp; 359 return put_user(tmp, p); 360 case SOUND_MIXER_READ_CAPS: 361 tmp = snd_mixer_oss_caps(fmixer); 362 if (tmp < 0) 363 return tmp; 364 return put_user(tmp, p); 365 case SOUND_MIXER_READ_RECSRC: 366 tmp = snd_mixer_oss_get_recsrc(fmixer); 367 if (tmp < 0) 368 return tmp; 369 return put_user(tmp, p); 370 } 371 } 372 if (cmd & SIOC_IN) { 373 if (get_user(tmp, p)) 374 return -EFAULT; 375 tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp); 376 if (tmp < 0) 377 return tmp; 378 return put_user(tmp, p); 379 } else if (cmd & SIOC_OUT) { 380 tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff); 381 if (tmp < 0) 382 return tmp; 383 return put_user(tmp, p); 384 } 385 return -ENXIO; 386} 387 388static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 389{ 390 return snd_mixer_oss_ioctl1(file->private_data, cmd, arg); 391} 392 393int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg) 394{ 395 struct snd_mixer_oss_file fmixer; 396 397 if (snd_BUG_ON(!card)) 398 return -ENXIO; 399 if (card->mixer_oss == NULL) 400 return -ENXIO; 401 memset(&fmixer, 0, sizeof(fmixer)); 402 fmixer.card = card; 403 fmixer.mixer = card->mixer_oss; 404 return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); 405} 406EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); 407 408#ifdef CONFIG_COMPAT 409/* all compatible */ 410static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd, 411 unsigned long arg) 412{ 413 return snd_mixer_oss_ioctl1(file->private_data, cmd, 414 (unsigned long)compat_ptr(arg)); 415} 416#else 417#define snd_mixer_oss_ioctl_compat NULL 418#endif 419 420/* 421 * REGISTRATION PART 422 */ 423 424static const struct file_operations snd_mixer_oss_f_ops = 425{ 426 .owner = THIS_MODULE, 427 .open = snd_mixer_oss_open, 428 .release = snd_mixer_oss_release, 429 .llseek = no_llseek, 430 .unlocked_ioctl = snd_mixer_oss_ioctl, 431 .compat_ioctl = snd_mixer_oss_ioctl_compat, 432}; 433 434/* 435 * utilities 436 */ 437 438static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax) 439{ 440 long orange = omax - omin, nrange = nmax - nmin; 441 442 if (orange == 0) 443 return 0; 444 return DIV_ROUND_CLOSEST(nrange * (val - omin), orange) + nmin; 445} 446 447/* convert from alsa native to oss values (0-100) */ 448static long snd_mixer_oss_conv1(long val, long min, long max, int *old) 449{ 450 if (val == snd_mixer_oss_conv(*old, 0, 100, min, max)) 451 return *old; 452 return snd_mixer_oss_conv(val, min, max, 0, 100); 453} 454 455/* convert from oss to alsa native values */ 456static long snd_mixer_oss_conv2(long val, long min, long max) 457{ 458 return snd_mixer_oss_conv(val, 0, 100, min, max); 459} 460 461#if 0 462static void snd_mixer_oss_recsrce_set(struct snd_card *card, int slot) 463{ 464 struct snd_mixer_oss *mixer = card->mixer_oss; 465 if (mixer) 466 mixer->mask_recsrc |= 1 << slot; 467} 468 469static int snd_mixer_oss_recsrce_get(struct snd_card *card, int slot) 470{ 471 struct snd_mixer_oss *mixer = card->mixer_oss; 472 if (mixer && (mixer->mask_recsrc & (1 << slot))) 473 return 1; 474 return 0; 475} 476#endif 477 478#define SNDRV_MIXER_OSS_SIGNATURE 0x65999250 479 480#define SNDRV_MIXER_OSS_ITEM_GLOBAL 0 481#define SNDRV_MIXER_OSS_ITEM_GSWITCH 1 482#define SNDRV_MIXER_OSS_ITEM_GROUTE 2 483#define SNDRV_MIXER_OSS_ITEM_GVOLUME 3 484#define SNDRV_MIXER_OSS_ITEM_PSWITCH 4 485#define SNDRV_MIXER_OSS_ITEM_PROUTE 5 486#define SNDRV_MIXER_OSS_ITEM_PVOLUME 6 487#define SNDRV_MIXER_OSS_ITEM_CSWITCH 7 488#define SNDRV_MIXER_OSS_ITEM_CROUTE 8 489#define SNDRV_MIXER_OSS_ITEM_CVOLUME 9 490#define SNDRV_MIXER_OSS_ITEM_CAPTURE 10 491 492#define SNDRV_MIXER_OSS_ITEM_COUNT 11 493 494#define SNDRV_MIXER_OSS_PRESENT_GLOBAL (1<<0) 495#define SNDRV_MIXER_OSS_PRESENT_GSWITCH (1<<1) 496#define SNDRV_MIXER_OSS_PRESENT_GROUTE (1<<2) 497#define SNDRV_MIXER_OSS_PRESENT_GVOLUME (1<<3) 498#define SNDRV_MIXER_OSS_PRESENT_PSWITCH (1<<4) 499#define SNDRV_MIXER_OSS_PRESENT_PROUTE (1<<5) 500#define SNDRV_MIXER_OSS_PRESENT_PVOLUME (1<<6) 501#define SNDRV_MIXER_OSS_PRESENT_CSWITCH (1<<7) 502#define SNDRV_MIXER_OSS_PRESENT_CROUTE (1<<8) 503#define SNDRV_MIXER_OSS_PRESENT_CVOLUME (1<<9) 504#define SNDRV_MIXER_OSS_PRESENT_CAPTURE (1<<10) 505 506struct slot { 507 unsigned int signature; 508 unsigned int present; 509 unsigned int channels; 510 unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT]; 511 unsigned int capture_item; 512 const struct snd_mixer_oss_assign_table *assigned; 513 unsigned int allocated: 1; 514}; 515 516#define ID_UNKNOWN ((unsigned int)-1) 517 518static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, const char *name, int index) 519{ 520 struct snd_card *card = mixer->card; 521 struct snd_ctl_elem_id id; 522 523 memset(&id, 0, sizeof(id)); 524 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 525 strscpy(id.name, name, sizeof(id.name)); 526 id.index = index; 527 return snd_ctl_find_id(card, &id); 528} 529 530static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer, 531 struct snd_mixer_oss_slot *pslot, 532 unsigned int numid, 533 int *left, int *right) 534{ 535 struct snd_ctl_elem_info *uinfo; 536 struct snd_ctl_elem_value *uctl; 537 struct snd_kcontrol *kctl; 538 struct snd_card *card = fmixer->card; 539 540 if (numid == ID_UNKNOWN) 541 return; 542 down_read(&card->controls_rwsem); 543 kctl = snd_ctl_find_numid(card, numid); 544 if (!kctl) { 545 up_read(&card->controls_rwsem); 546 return; 547 } 548 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 549 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 550 if (uinfo == NULL || uctl == NULL) 551 goto __unalloc; 552 if (kctl->info(kctl, uinfo)) 553 goto __unalloc; 554 if (kctl->get(kctl, uctl)) 555 goto __unalloc; 556 if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN && 557 uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1) 558 goto __unalloc; 559 *left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]); 560 if (uinfo->count > 1) 561 *right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]); 562 __unalloc: 563 up_read(&card->controls_rwsem); 564 kfree(uctl); 565 kfree(uinfo); 566} 567 568static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer, 569 struct snd_mixer_oss_slot *pslot, 570 unsigned int numid, 571 int *left, int *right, 572 int route) 573{ 574 struct snd_ctl_elem_info *uinfo; 575 struct snd_ctl_elem_value *uctl; 576 struct snd_kcontrol *kctl; 577 struct snd_card *card = fmixer->card; 578 579 if (numid == ID_UNKNOWN) 580 return; 581 down_read(&card->controls_rwsem); 582 kctl = snd_ctl_find_numid(card, numid); 583 if (!kctl) { 584 up_read(&card->controls_rwsem); 585 return; 586 } 587 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 588 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 589 if (uinfo == NULL || uctl == NULL) 590 goto __unalloc; 591 if (kctl->info(kctl, uinfo)) 592 goto __unalloc; 593 if (kctl->get(kctl, uctl)) 594 goto __unalloc; 595 if (!uctl->value.integer.value[0]) { 596 *left = 0; 597 if (uinfo->count == 1) 598 *right = 0; 599 } 600 if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1]) 601 *right = 0; 602 __unalloc: 603 up_read(&card->controls_rwsem); 604 kfree(uctl); 605 kfree(uinfo); 606} 607 608static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer, 609 struct snd_mixer_oss_slot *pslot, 610 int *left, int *right) 611{ 612 struct slot *slot = pslot->private_data; 613 614 *left = *right = 100; 615 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { 616 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); 617 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { 618 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); 619 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { 620 snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); 621 } 622 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { 623 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); 624 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { 625 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); 626 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { 627 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); 628 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { 629 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); 630 } 631 return 0; 632} 633 634static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer, 635 struct snd_mixer_oss_slot *pslot, 636 unsigned int numid, 637 int left, int right) 638{ 639 struct snd_ctl_elem_info *uinfo; 640 struct snd_ctl_elem_value *uctl; 641 struct snd_kcontrol *kctl; 642 struct snd_card *card = fmixer->card; 643 int res; 644 645 if (numid == ID_UNKNOWN) 646 return; 647 down_read(&card->controls_rwsem); 648 kctl = snd_ctl_find_numid(card, numid); 649 if (!kctl) { 650 up_read(&card->controls_rwsem); 651 return; 652 } 653 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 654 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 655 if (uinfo == NULL || uctl == NULL) 656 goto __unalloc; 657 if (kctl->info(kctl, uinfo)) 658 goto __unalloc; 659 if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN && 660 uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1) 661 goto __unalloc; 662 uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max); 663 if (uinfo->count > 1) 664 uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max); 665 res = kctl->put(kctl, uctl); 666 if (res < 0) 667 goto __unalloc; 668 if (res > 0) 669 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 670 __unalloc: 671 up_read(&card->controls_rwsem); 672 kfree(uctl); 673 kfree(uinfo); 674} 675 676static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer, 677 struct snd_mixer_oss_slot *pslot, 678 unsigned int numid, 679 int left, int right, 680 int route) 681{ 682 struct snd_ctl_elem_info *uinfo; 683 struct snd_ctl_elem_value *uctl; 684 struct snd_kcontrol *kctl; 685 struct snd_card *card = fmixer->card; 686 int res; 687 688 if (numid == ID_UNKNOWN) 689 return; 690 down_read(&card->controls_rwsem); 691 kctl = snd_ctl_find_numid(card, numid); 692 if (!kctl) { 693 up_read(&card->controls_rwsem); 694 return; 695 } 696 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 697 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 698 if (uinfo == NULL || uctl == NULL) 699 goto __unalloc; 700 if (kctl->info(kctl, uinfo)) 701 goto __unalloc; 702 if (uinfo->count > 1) { 703 uctl->value.integer.value[0] = left > 0 ? 1 : 0; 704 uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0; 705 if (route) { 706 uctl->value.integer.value[1] = 707 uctl->value.integer.value[2] = 0; 708 } 709 } else { 710 uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0; 711 } 712 res = kctl->put(kctl, uctl); 713 if (res < 0) 714 goto __unalloc; 715 if (res > 0) 716 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 717 __unalloc: 718 up_read(&card->controls_rwsem); 719 kfree(uctl); 720 kfree(uinfo); 721} 722 723static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer, 724 struct snd_mixer_oss_slot *pslot, 725 int left, int right) 726{ 727 struct slot *slot = pslot->private_data; 728 729 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) { 730 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right); 731 if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) 732 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); 733 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) { 734 snd_mixer_oss_put_volume1_vol(fmixer, pslot, 735 slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right); 736 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) { 737 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right); 738 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) { 739 snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right); 740 } 741 if (left || right) { 742 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) 743 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); 744 if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) 745 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0); 746 if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) 747 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); 748 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) 749 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); 750 if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) 751 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1); 752 if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) 753 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); 754 } else { 755 if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) { 756 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0); 757 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { 758 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0); 759 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) { 760 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0); 761 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) { 762 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1); 763 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { 764 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1); 765 } else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) { 766 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1); 767 } 768 } 769 return 0; 770} 771 772static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer, 773 struct snd_mixer_oss_slot *pslot, 774 int *active) 775{ 776 struct slot *slot = pslot->private_data; 777 int left, right; 778 779 left = right = 1; 780 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0); 781 *active = (left || right) ? 1 : 0; 782 return 0; 783} 784 785static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer, 786 struct snd_mixer_oss_slot *pslot, 787 int *active) 788{ 789 struct slot *slot = pslot->private_data; 790 int left, right; 791 792 left = right = 1; 793 snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1); 794 *active = (left || right) ? 1 : 0; 795 return 0; 796} 797 798static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer, 799 struct snd_mixer_oss_slot *pslot, 800 int active) 801{ 802 struct slot *slot = pslot->private_data; 803 804 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0); 805 return 0; 806} 807 808static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer, 809 struct snd_mixer_oss_slot *pslot, 810 int active) 811{ 812 struct slot *slot = pslot->private_data; 813 814 snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1); 815 return 0; 816} 817 818static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int *active_index) 819{ 820 struct snd_card *card = fmixer->card; 821 struct snd_mixer_oss *mixer = fmixer->mixer; 822 struct snd_kcontrol *kctl; 823 struct snd_mixer_oss_slot *pslot; 824 struct slot *slot; 825 struct snd_ctl_elem_info *uinfo; 826 struct snd_ctl_elem_value *uctl; 827 int err, idx; 828 829 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 830 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 831 if (uinfo == NULL || uctl == NULL) { 832 err = -ENOMEM; 833 goto __free_only; 834 } 835 down_read(&card->controls_rwsem); 836 kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); 837 if (! kctl) { 838 err = -ENOENT; 839 goto __unlock; 840 } 841 err = kctl->info(kctl, uinfo); 842 if (err < 0) 843 goto __unlock; 844 err = kctl->get(kctl, uctl); 845 if (err < 0) 846 goto __unlock; 847 for (idx = 0; idx < 32; idx++) { 848 if (!(mixer->mask_recsrc & (1 << idx))) 849 continue; 850 pslot = &mixer->slots[idx]; 851 slot = pslot->private_data; 852 if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) 853 continue; 854 if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) 855 continue; 856 if (slot->capture_item == uctl->value.enumerated.item[0]) { 857 *active_index = idx; 858 break; 859 } 860 } 861 err = 0; 862 __unlock: 863 up_read(&card->controls_rwsem); 864 __free_only: 865 kfree(uctl); 866 kfree(uinfo); 867 return err; 868} 869 870static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index) 871{ 872 struct snd_card *card = fmixer->card; 873 struct snd_mixer_oss *mixer = fmixer->mixer; 874 struct snd_kcontrol *kctl; 875 struct snd_mixer_oss_slot *pslot; 876 struct slot *slot = NULL; 877 struct snd_ctl_elem_info *uinfo; 878 struct snd_ctl_elem_value *uctl; 879 int err; 880 unsigned int idx; 881 882 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 883 uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); 884 if (uinfo == NULL || uctl == NULL) { 885 err = -ENOMEM; 886 goto __free_only; 887 } 888 down_read(&card->controls_rwsem); 889 kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); 890 if (! kctl) { 891 err = -ENOENT; 892 goto __unlock; 893 } 894 err = kctl->info(kctl, uinfo); 895 if (err < 0) 896 goto __unlock; 897 for (idx = 0; idx < 32; idx++) { 898 if (!(mixer->mask_recsrc & (1 << idx))) 899 continue; 900 pslot = &mixer->slots[idx]; 901 slot = pslot->private_data; 902 if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE) 903 continue; 904 if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE)) 905 continue; 906 if (idx == active_index) 907 break; 908 slot = NULL; 909 } 910 if (! slot) 911 goto __unlock; 912 for (idx = 0; idx < uinfo->count; idx++) 913 uctl->value.enumerated.item[idx] = slot->capture_item; 914 err = kctl->put(kctl, uctl); 915 if (err > 0) 916 snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); 917 err = 0; 918 __unlock: 919 up_read(&card->controls_rwsem); 920 __free_only: 921 kfree(uctl); 922 kfree(uinfo); 923 return err; 924} 925 926struct snd_mixer_oss_assign_table { 927 int oss_id; 928 const char *name; 929 int index; 930}; 931 932static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item) 933{ 934 struct snd_ctl_elem_info *info; 935 struct snd_kcontrol *kcontrol; 936 struct snd_card *card = mixer->card; 937 int err; 938 939 down_read(&card->controls_rwsem); 940 kcontrol = snd_mixer_oss_test_id(mixer, name, index); 941 if (kcontrol == NULL) { 942 up_read(&card->controls_rwsem); 943 return 0; 944 } 945 info = kmalloc(sizeof(*info), GFP_KERNEL); 946 if (! info) { 947 up_read(&card->controls_rwsem); 948 return -ENOMEM; 949 } 950 err = kcontrol->info(kcontrol, info); 951 if (err < 0) { 952 up_read(&card->controls_rwsem); 953 kfree(info); 954 return err; 955 } 956 slot->numid[item] = kcontrol->id.numid; 957 up_read(&card->controls_rwsem); 958 if (info->count > slot->channels) 959 slot->channels = info->count; 960 slot->present |= 1 << item; 961 kfree(info); 962 return 0; 963} 964 965static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn) 966{ 967 struct slot *p = chn->private_data; 968 if (p) { 969 if (p->allocated && p->assigned) { 970 kfree_const(p->assigned->name); 971 kfree_const(p->assigned); 972 } 973 kfree(p); 974 } 975} 976 977static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot) 978{ 979 int idx = rslot->number; /* remember this */ 980 if (rslot->private_free) 981 rslot->private_free(rslot); 982 memset(rslot, 0, sizeof(*rslot)); 983 rslot->number = idx; 984} 985 986/* In a separate function to keep gcc 3.2 happy - do NOT merge this in 987 snd_mixer_oss_build_input! */ 988static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer, 989 const struct snd_mixer_oss_assign_table *ptr, 990 struct slot *slot) 991{ 992 char str[64]; 993 int err; 994 995 err = snd_mixer_oss_build_test(mixer, slot, ptr->name, ptr->index, 996 SNDRV_MIXER_OSS_ITEM_GLOBAL); 997 if (err) 998 return err; 999 sprintf(str, "%s Switch", ptr->name); 1000 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1001 SNDRV_MIXER_OSS_ITEM_GSWITCH); 1002 if (err) 1003 return err; 1004 sprintf(str, "%s Route", ptr->name); 1005 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1006 SNDRV_MIXER_OSS_ITEM_GROUTE); 1007 if (err) 1008 return err; 1009 sprintf(str, "%s Volume", ptr->name); 1010 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1011 SNDRV_MIXER_OSS_ITEM_GVOLUME); 1012 if (err) 1013 return err; 1014 sprintf(str, "%s Playback Switch", ptr->name); 1015 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1016 SNDRV_MIXER_OSS_ITEM_PSWITCH); 1017 if (err) 1018 return err; 1019 sprintf(str, "%s Playback Route", ptr->name); 1020 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1021 SNDRV_MIXER_OSS_ITEM_PROUTE); 1022 if (err) 1023 return err; 1024 sprintf(str, "%s Playback Volume", ptr->name); 1025 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1026 SNDRV_MIXER_OSS_ITEM_PVOLUME); 1027 if (err) 1028 return err; 1029 sprintf(str, "%s Capture Switch", ptr->name); 1030 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1031 SNDRV_MIXER_OSS_ITEM_CSWITCH); 1032 if (err) 1033 return err; 1034 sprintf(str, "%s Capture Route", ptr->name); 1035 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1036 SNDRV_MIXER_OSS_ITEM_CROUTE); 1037 if (err) 1038 return err; 1039 sprintf(str, "%s Capture Volume", ptr->name); 1040 err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index, 1041 SNDRV_MIXER_OSS_ITEM_CVOLUME); 1042 if (err) 1043 return err; 1044 1045 return 0; 1046} 1047 1048/* 1049 * build an OSS mixer element. 1050 * ptr_allocated means the entry is dynamically allocated (change via proc file). 1051 * when replace_old = 1, the old entry is replaced with the new one. 1052 */ 1053static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, 1054 const struct snd_mixer_oss_assign_table *ptr, 1055 int ptr_allocated, int replace_old) 1056{ 1057 struct slot slot; 1058 struct slot *pslot; 1059 struct snd_kcontrol *kctl; 1060 struct snd_mixer_oss_slot *rslot; 1061 char str[64]; 1062 1063 /* check if already assigned */ 1064 if (mixer->slots[ptr->oss_id].get_volume && ! replace_old) 1065 return 0; 1066 1067 memset(&slot, 0, sizeof(slot)); 1068 memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */ 1069 if (snd_mixer_oss_build_test_all(mixer, ptr, &slot)) 1070 return 0; 1071 down_read(&mixer->card->controls_rwsem); 1072 kctl = NULL; 1073 if (!ptr->index) 1074 kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0); 1075 if (kctl) { 1076 struct snd_ctl_elem_info *uinfo; 1077 1078 uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL); 1079 if (! uinfo) { 1080 up_read(&mixer->card->controls_rwsem); 1081 return -ENOMEM; 1082 } 1083 1084 if (kctl->info(kctl, uinfo)) { 1085 up_read(&mixer->card->controls_rwsem); 1086 kfree(uinfo); 1087 return 0; 1088 } 1089 strcpy(str, ptr->name); 1090 if (!strcmp(str, "Master")) 1091 strcpy(str, "Mix"); 1092 if (!strcmp(str, "Master Mono")) 1093 strcpy(str, "Mix Mono"); 1094 slot.capture_item = 0; 1095 if (!strcmp(uinfo->value.enumerated.name, str)) { 1096 slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; 1097 } else { 1098 for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) { 1099 uinfo->value.enumerated.item = slot.capture_item; 1100 if (kctl->info(kctl, uinfo)) { 1101 up_read(&mixer->card->controls_rwsem); 1102 kfree(uinfo); 1103 return 0; 1104 } 1105 if (!strcmp(uinfo->value.enumerated.name, str)) { 1106 slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE; 1107 break; 1108 } 1109 } 1110 } 1111 kfree(uinfo); 1112 } 1113 up_read(&mixer->card->controls_rwsem); 1114 if (slot.present != 0) { 1115 pslot = kmalloc(sizeof(slot), GFP_KERNEL); 1116 if (! pslot) 1117 return -ENOMEM; 1118 *pslot = slot; 1119 pslot->signature = SNDRV_MIXER_OSS_SIGNATURE; 1120 pslot->assigned = ptr; 1121 pslot->allocated = ptr_allocated; 1122 rslot = &mixer->slots[ptr->oss_id]; 1123 mixer_slot_clear(rslot); 1124 rslot->stereo = slot.channels > 1 ? 1 : 0; 1125 rslot->get_volume = snd_mixer_oss_get_volume1; 1126 rslot->put_volume = snd_mixer_oss_put_volume1; 1127 /* note: ES18xx have both Capture Source and XX Capture Volume !!! */ 1128 if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) { 1129 rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw; 1130 rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw; 1131 } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) { 1132 rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route; 1133 rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route; 1134 } else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) { 1135 mixer->mask_recsrc |= 1 << ptr->oss_id; 1136 } 1137 rslot->private_data = pslot; 1138 rslot->private_free = snd_mixer_oss_slot_free; 1139 return 1; 1140 } 1141 return 0; 1142} 1143 1144#ifdef CONFIG_SND_PROC_FS 1145/* 1146 */ 1147#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name 1148static const char * const oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = { 1149 MIXER_VOL(VOLUME), 1150 MIXER_VOL(BASS), 1151 MIXER_VOL(TREBLE), 1152 MIXER_VOL(SYNTH), 1153 MIXER_VOL(PCM), 1154 MIXER_VOL(SPEAKER), 1155 MIXER_VOL(LINE), 1156 MIXER_VOL(MIC), 1157 MIXER_VOL(CD), 1158 MIXER_VOL(IMIX), 1159 MIXER_VOL(ALTPCM), 1160 MIXER_VOL(RECLEV), 1161 MIXER_VOL(IGAIN), 1162 MIXER_VOL(OGAIN), 1163 MIXER_VOL(LINE1), 1164 MIXER_VOL(LINE2), 1165 MIXER_VOL(LINE3), 1166 MIXER_VOL(DIGITAL1), 1167 MIXER_VOL(DIGITAL2), 1168 MIXER_VOL(DIGITAL3), 1169 MIXER_VOL(PHONEIN), 1170 MIXER_VOL(PHONEOUT), 1171 MIXER_VOL(VIDEO), 1172 MIXER_VOL(RADIO), 1173 MIXER_VOL(MONITOR), 1174}; 1175 1176/* 1177 * /proc interface 1178 */ 1179 1180static void snd_mixer_oss_proc_read(struct snd_info_entry *entry, 1181 struct snd_info_buffer *buffer) 1182{ 1183 struct snd_mixer_oss *mixer = entry->private_data; 1184 int i; 1185 1186 mutex_lock(&mixer->reg_mutex); 1187 for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) { 1188 struct slot *p; 1189 1190 if (! oss_mixer_names[i]) 1191 continue; 1192 p = (struct slot *)mixer->slots[i].private_data; 1193 snd_iprintf(buffer, "%s ", oss_mixer_names[i]); 1194 if (p && p->assigned) 1195 snd_iprintf(buffer, "\"%s\" %d\n", 1196 p->assigned->name, 1197 p->assigned->index); 1198 else 1199 snd_iprintf(buffer, "\"\" 0\n"); 1200 } 1201 mutex_unlock(&mixer->reg_mutex); 1202} 1203 1204static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, 1205 struct snd_info_buffer *buffer) 1206{ 1207 struct snd_mixer_oss *mixer = entry->private_data; 1208 char line[128], str[32], idxstr[16]; 1209 const char *cptr; 1210 unsigned int idx; 1211 int ch; 1212 struct snd_mixer_oss_assign_table *tbl; 1213 struct slot *slot; 1214 1215 while (!snd_info_get_line(buffer, line, sizeof(line))) { 1216 cptr = snd_info_get_str(str, line, sizeof(str)); 1217 for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++) 1218 if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0) 1219 break; 1220 if (ch >= SNDRV_OSS_MAX_MIXERS) { 1221 pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n", 1222 str); 1223 continue; 1224 } 1225 cptr = snd_info_get_str(str, cptr, sizeof(str)); 1226 if (! *str) { 1227 /* remove the entry */ 1228 mutex_lock(&mixer->reg_mutex); 1229 mixer_slot_clear(&mixer->slots[ch]); 1230 mutex_unlock(&mixer->reg_mutex); 1231 continue; 1232 } 1233 snd_info_get_str(idxstr, cptr, sizeof(idxstr)); 1234 idx = simple_strtoul(idxstr, NULL, 10); 1235 if (idx >= 0x4000) { /* too big */ 1236 pr_err("ALSA: mixer_oss: invalid index %d\n", idx); 1237 continue; 1238 } 1239 mutex_lock(&mixer->reg_mutex); 1240 slot = (struct slot *)mixer->slots[ch].private_data; 1241 if (slot && slot->assigned && 1242 slot->assigned->index == idx && ! strcmp(slot->assigned->name, str)) 1243 /* not changed */ 1244 goto __unlock; 1245 tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); 1246 if (!tbl) 1247 goto __unlock; 1248 tbl->oss_id = ch; 1249 tbl->name = kstrdup(str, GFP_KERNEL); 1250 if (! tbl->name) { 1251 kfree(tbl); 1252 goto __unlock; 1253 } 1254 tbl->index = idx; 1255 if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) { 1256 kfree(tbl->name); 1257 kfree(tbl); 1258 } 1259 __unlock: 1260 mutex_unlock(&mixer->reg_mutex); 1261 } 1262} 1263 1264static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer) 1265{ 1266 struct snd_info_entry *entry; 1267 1268 entry = snd_info_create_card_entry(mixer->card, "oss_mixer", 1269 mixer->card->proc_root); 1270 if (! entry) 1271 return; 1272 entry->content = SNDRV_INFO_CONTENT_TEXT; 1273 entry->mode = S_IFREG | 0644; 1274 entry->c.text.read = snd_mixer_oss_proc_read; 1275 entry->c.text.write = snd_mixer_oss_proc_write; 1276 entry->private_data = mixer; 1277 if (snd_info_register(entry) < 0) { 1278 snd_info_free_entry(entry); 1279 entry = NULL; 1280 } 1281 mixer->proc_entry = entry; 1282} 1283 1284static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer) 1285{ 1286 snd_info_free_entry(mixer->proc_entry); 1287 mixer->proc_entry = NULL; 1288} 1289#else /* !CONFIG_SND_PROC_FS */ 1290#define snd_mixer_oss_proc_init(mix) 1291#define snd_mixer_oss_proc_done(mix) 1292#endif /* CONFIG_SND_PROC_FS */ 1293 1294static void snd_mixer_oss_build(struct snd_mixer_oss *mixer) 1295{ 1296 static const struct snd_mixer_oss_assign_table table[] = { 1297 { SOUND_MIXER_VOLUME, "Master", 0 }, 1298 { SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */ 1299 { SOUND_MIXER_BASS, "Tone Control - Bass", 0 }, 1300 { SOUND_MIXER_TREBLE, "Tone Control - Treble", 0 }, 1301 { SOUND_MIXER_SYNTH, "Synth", 0 }, 1302 { SOUND_MIXER_SYNTH, "FM", 0 }, /* fallback */ 1303 { SOUND_MIXER_SYNTH, "Music", 0 }, /* fallback */ 1304 { SOUND_MIXER_PCM, "PCM", 0 }, 1305 { SOUND_MIXER_SPEAKER, "Beep", 0 }, 1306 { SOUND_MIXER_SPEAKER, "PC Speaker", 0 }, /* fallback */ 1307 { SOUND_MIXER_SPEAKER, "Speaker", 0 }, /* fallback */ 1308 { SOUND_MIXER_LINE, "Line", 0 }, 1309 { SOUND_MIXER_MIC, "Mic", 0 }, 1310 { SOUND_MIXER_CD, "CD", 0 }, 1311 { SOUND_MIXER_IMIX, "Monitor Mix", 0 }, 1312 { SOUND_MIXER_ALTPCM, "PCM", 1 }, 1313 { SOUND_MIXER_ALTPCM, "Headphone", 0 }, /* fallback */ 1314 { SOUND_MIXER_ALTPCM, "Wave", 0 }, /* fallback */ 1315 { SOUND_MIXER_RECLEV, "-- nothing --", 0 }, 1316 { SOUND_MIXER_IGAIN, "Capture", 0 }, 1317 { SOUND_MIXER_OGAIN, "Playback", 0 }, 1318 { SOUND_MIXER_LINE1, "Aux", 0 }, 1319 { SOUND_MIXER_LINE2, "Aux", 1 }, 1320 { SOUND_MIXER_LINE3, "Aux", 2 }, 1321 { SOUND_MIXER_DIGITAL1, "Digital", 0 }, 1322 { SOUND_MIXER_DIGITAL1, "IEC958", 0 }, /* fallback */ 1323 { SOUND_MIXER_DIGITAL1, "IEC958 Optical", 0 }, /* fallback */ 1324 { SOUND_MIXER_DIGITAL1, "IEC958 Coaxial", 0 }, /* fallback */ 1325 { SOUND_MIXER_DIGITAL2, "Digital", 1 }, 1326 { SOUND_MIXER_DIGITAL3, "Digital", 2 }, 1327 { SOUND_MIXER_PHONEIN, "Phone", 0 }, 1328 { SOUND_MIXER_PHONEOUT, "Master Mono", 0 }, 1329 { SOUND_MIXER_PHONEOUT, "Speaker", 0 }, /*fallback*/ 1330 { SOUND_MIXER_PHONEOUT, "Mono", 0 }, /*fallback*/ 1331 { SOUND_MIXER_PHONEOUT, "Phone", 0 }, /* fallback */ 1332 { SOUND_MIXER_VIDEO, "Video", 0 }, 1333 { SOUND_MIXER_RADIO, "Radio", 0 }, 1334 { SOUND_MIXER_MONITOR, "Monitor", 0 } 1335 }; 1336 unsigned int idx; 1337 1338 for (idx = 0; idx < ARRAY_SIZE(table); idx++) 1339 snd_mixer_oss_build_input(mixer, &table[idx], 0, 0); 1340 if (mixer->mask_recsrc) { 1341 mixer->get_recsrc = snd_mixer_oss_get_recsrc2; 1342 mixer->put_recsrc = snd_mixer_oss_put_recsrc2; 1343 } 1344} 1345 1346/* 1347 * 1348 */ 1349 1350static int snd_mixer_oss_free1(void *private) 1351{ 1352 struct snd_mixer_oss *mixer = private; 1353 struct snd_card *card; 1354 int idx; 1355 1356 if (!mixer) 1357 return 0; 1358 card = mixer->card; 1359 if (snd_BUG_ON(mixer != card->mixer_oss)) 1360 return -ENXIO; 1361 card->mixer_oss = NULL; 1362 for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) { 1363 struct snd_mixer_oss_slot *chn = &mixer->slots[idx]; 1364 if (chn->private_free) 1365 chn->private_free(chn); 1366 } 1367 kfree(mixer); 1368 return 0; 1369} 1370 1371static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd) 1372{ 1373 struct snd_mixer_oss *mixer; 1374 1375 if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) { 1376 int idx, err; 1377 1378 mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL); 1379 if (mixer == NULL) 1380 return -ENOMEM; 1381 mutex_init(&mixer->reg_mutex); 1382 err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, 1383 card, 0, 1384 &snd_mixer_oss_f_ops, card); 1385 if (err < 0) { 1386 dev_err(card->dev, 1387 "unable to register OSS mixer device %i:%i\n", 1388 card->number, 0); 1389 kfree(mixer); 1390 return err; 1391 } 1392 mixer->oss_dev_alloc = 1; 1393 mixer->card = card; 1394 if (*card->mixername) 1395 strscpy(mixer->name, card->mixername, sizeof(mixer->name)); 1396 else 1397 snprintf(mixer->name, sizeof(mixer->name), 1398 "mixer%i", card->number); 1399#ifdef SNDRV_OSS_INFO_DEV_MIXERS 1400 snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, 1401 card->number, 1402 mixer->name); 1403#endif 1404 for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) 1405 mixer->slots[idx].number = idx; 1406 card->mixer_oss = mixer; 1407 snd_mixer_oss_build(mixer); 1408 snd_mixer_oss_proc_init(mixer); 1409 } else { 1410 mixer = card->mixer_oss; 1411 if (mixer == NULL) 1412 return 0; 1413 if (mixer->oss_dev_alloc) { 1414#ifdef SNDRV_OSS_INFO_DEV_MIXERS 1415 snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); 1416#endif 1417 snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); 1418 mixer->oss_dev_alloc = 0; 1419 } 1420 if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) 1421 return 0; 1422 snd_mixer_oss_proc_done(mixer); 1423 return snd_mixer_oss_free1(mixer); 1424 } 1425 return 0; 1426} 1427 1428static int __init alsa_mixer_oss_init(void) 1429{ 1430 struct snd_card *card; 1431 int idx; 1432 1433 snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler; 1434 for (idx = 0; idx < SNDRV_CARDS; idx++) { 1435 card = snd_card_ref(idx); 1436 if (card) { 1437 snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER); 1438 snd_card_unref(card); 1439 } 1440 } 1441 return 0; 1442} 1443 1444static void __exit alsa_mixer_oss_exit(void) 1445{ 1446 struct snd_card *card; 1447 int idx; 1448 1449 snd_mixer_oss_notify_callback = NULL; 1450 for (idx = 0; idx < SNDRV_CARDS; idx++) { 1451 card = snd_card_ref(idx); 1452 if (card) { 1453 snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE); 1454 snd_card_unref(card); 1455 } 1456 } 1457} 1458 1459module_init(alsa_mixer_oss_init) 1460module_exit(alsa_mixer_oss_exit)