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