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