Root/sound/core/oss/mixer_oss.c

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

Archive Download this file



interactive