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