source: GPL/alsa-kernel/drivers/dummy.c@ 18

Last change on this file since 18 was 18, checked in by vladest, 20 years ago

initial import

File size: 22.2 KB
Line 
1/*
2 * Dummy soundcard
3 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */
20
21#define SNDRV_MAIN_OBJECT_FILE
22#include <sound/driver.h>
23#include <sound/control.h>
24#include <sound/pcm.h>
25#include <sound/rawmidi.h>
26#define SNDRV_GET_ID
27#include <sound/initval.h>
28
29MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
30MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
31MODULE_LICENSE("GPL");
32MODULE_CLASSES("{sound}");
33MODULE_DEVICES("{{ALSA,Dummy soundcard}}");
34
35#define MAX_PCM_DEVICES 4
36#define MAX_PCM_SUBSTREAMS 16
37#define MAX_MIDI_DEVICES 2
38
39
40#if 0 /* RME9652 emulation */
41#define MAX_BUFFER_SIZE (26 * 64 * 1024)
42#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
43#define USE_CHANNELS_MIN 26
44#define USE_CHANNELS_MAX 26
45#define USE_PERIODS_MIN 2
46#define USE_PERIODS_MAX 2
47#endif
48
49#if 0 /* ICE1712 emulation */
50#define MAX_BUFFER_SIZE (256 * 1024)
51#define USE_FORMATS SNDRV_PCM_FMTBIT_S32_LE
52#define USE_CHANNELS_MIN 10
53#define USE_CHANNELS_MAX 10
54#define USE_PERIODS_MIN 1
55#define USE_PERIODS_MAX 1024
56#endif
57
58#if 0 /* UDA1341 emulation */
59#define MAX_BUFFER_SIZE (16380)
60#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
61#define USE_CHANNELS_MIN 2
62#define USE_CHANNELS_MAX 2
63#define USE_PERIODS_MIN 2
64#define USE_PERIODS_MAX 255
65#endif
66
67#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
68#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
69#define USE_CHANNELS_MIN 2
70#define USE_CHANNELS_MAX 2
71#define USE_RATE SNDRV_PCM_RATE_48000
72#define USE_RATE_MIN 48000
73#define USE_RATE_MAX 48000
74#endif
75
76
77/* defaults */
78#ifndef MAX_BUFFER_SIZE
79#define MAX_BUFFER_SIZE (64*1024)
80#endif
81#ifndef USE_FORMATS
82#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
83#endif
84#ifndef USE_RATE
85#define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
86#define USE_RATE_MIN 5500
87#define USE_RATE_MAX 48000
88#endif
89#ifndef USE_CHANNELS_MIN
90#define USE_CHANNELS_MIN 1
91#endif
92#ifndef USE_CHANNELS_MAX
93#define USE_CHANNELS_MAX 2
94#endif
95#ifndef USE_PERIODS_MIN
96#define USE_PERIODS_MIN 1
97#endif
98#ifndef USE_PERIODS_MAX
99#define USE_PERIODS_MAX 1024
100#endif
101
102static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
103static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
104#if 0
105static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
106static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
107static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
108#else
109static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
110static int pcm_devs[SNDRV_CARDS] = SNDDRV_DEFAULT_PCM_DEVS;
111static int pcm_substreams[SNDRV_CARDS] = SNDDRV_DEFAULT_PCM_SUBSTREAMS;
112#endif
113//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
114
115MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
116MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
117MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
118MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
119MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
120MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
121MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
122MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
123MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
124MODULE_PARM(pcm_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
125MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
126MODULE_PARM_SYNTAX(pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list");
127MODULE_PARM(pcm_substreams, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
128MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
129MODULE_PARM_SYNTAX(pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list");
130//MODULE_PARM(midi_devs, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
131//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
132//MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list");
133#define MIXER_ADDR_MASTER 0
134#define MIXER_ADDR_LINE 1
135#define MIXER_ADDR_MIC 2
136#define MIXER_ADDR_SYNTH 3
137#define MIXER_ADDR_CD 4
138#define MIXER_ADDR_LAST 4
139
140typedef struct snd_card_dummy {
141 snd_card_t *card;
142 spinlock_t mixer_lock;
143 int mixer_volume[MIXER_ADDR_LAST+1][2];
144 int capture_source[MIXER_ADDR_LAST+1][2];
145} snd_card_dummy_t;
146
147typedef struct snd_card_dummy_pcm {
148 snd_card_dummy_t *dummy;
149 spinlock_t lock;
150 struct timer_list timer;
151 unsigned int pcm_size;
152 unsigned int pcm_count;
153 unsigned int pcm_bps; /* bytes per second */
154 unsigned int pcm_jiffie; /* bytes per one jiffie */
155 unsigned int pcm_irq_pos; /* IRQ position */
156 unsigned int pcm_buf_pos; /* position in buffer */
157 snd_pcm_substream_t *substream;
158} snd_card_dummy_pcm_t;
159
160static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
161
162
163static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream,
164 unsigned int cmd,
165 void *arg)
166{
167 return snd_pcm_lib_ioctl(substream, cmd, arg);
168}
169
170static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream,
171 unsigned int cmd,
172 void *arg)
173{
174 return snd_pcm_lib_ioctl(substream, cmd, arg);
175}
176
177static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream)
178{
179 snd_pcm_runtime_t *runtime = substream->runtime;
180 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
181
182 dpcm->timer.expires = 1 + jiffies;
183 add_timer(&dpcm->timer);
184}
185
186static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream)
187{
188 snd_pcm_runtime_t *runtime = substream->runtime;
189 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
190
191 del_timer(&dpcm->timer);
192}
193
194static int snd_card_dummy_playback_trigger(snd_pcm_substream_t * substream,
195 int cmd)
196{
197 if (cmd == SNDRV_PCM_TRIGGER_START) {
198 snd_card_dummy_pcm_timer_start(substream);
199 } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
200 snd_card_dummy_pcm_timer_stop(substream);
201 } else {
202 return -EINVAL;
203 }
204 return 0;
205}
206
207static int snd_card_dummy_capture_trigger(snd_pcm_substream_t * substream,
208 int cmd)
209{
210 if (cmd == SNDRV_PCM_TRIGGER_START) {
211 snd_card_dummy_pcm_timer_start(substream);
212 } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
213 snd_card_dummy_pcm_timer_stop(substream);
214 } else {
215 return -EINVAL;
216 }
217 return 0;
218}
219
220static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream)
221{
222 snd_pcm_runtime_t *runtime = substream->runtime;
223 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
224 unsigned int bps;
225
226 bps = runtime->rate * runtime->channels;
227 bps *= snd_pcm_format_width(runtime->format);
228 bps /= 8;
229 if (bps <= 0)
230 return -EINVAL;
231 dpcm->pcm_bps = bps;
232 dpcm->pcm_jiffie = bps / HZ;
233 dpcm->pcm_size = snd_pcm_lib_buffer_bytes(substream);
234 dpcm->pcm_count = snd_pcm_lib_period_bytes(substream);
235 dpcm->pcm_irq_pos = 0;
236 dpcm->pcm_buf_pos = 0;
237 return 0;
238}
239
240static int snd_card_dummy_playback_prepare(snd_pcm_substream_t * substream)
241{
242 return snd_card_dummy_pcm_prepare(substream);
243}
244
245static int snd_card_dummy_capture_prepare(snd_pcm_substream_t * substream)
246{
247 return snd_card_dummy_pcm_prepare(substream);
248}
249
250static void snd_card_dummy_pcm_timer_function(unsigned long data)
251{
252 snd_card_dummy_pcm_t *dpcm = (snd_card_dummy_pcm_t *)data;
253
254 dpcm->timer.expires = 1 + jiffies;
255 add_timer(&dpcm->timer);
256 spin_lock_irq(&dpcm->lock);
257 dpcm->pcm_irq_pos += dpcm->pcm_jiffie;
258 dpcm->pcm_buf_pos += dpcm->pcm_jiffie;
259 dpcm->pcm_buf_pos %= dpcm->pcm_size;
260 if (dpcm->pcm_irq_pos >= dpcm->pcm_count) {
261 dpcm->pcm_irq_pos %= dpcm->pcm_count;
262 snd_pcm_period_elapsed(dpcm->substream);
263 }
264 spin_unlock_irq(&dpcm->lock);
265}
266
267static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream)
268{
269 snd_pcm_runtime_t *runtime = substream->runtime;
270 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
271
272 return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
273}
274
275static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream)
276{
277 snd_pcm_runtime_t *runtime = substream->runtime;
278 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
279
280 return bytes_to_frames(runtime, dpcm->pcm_buf_pos);
281}
282
283static snd_pcm_hardware_t snd_card_dummy_playback =
284{
285 /* info: */ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
286 SNDRV_PCM_INFO_MMAP_VALID),
287 /* formats: */ USE_FORMATS,
288 /* rates: */ USE_RATE,
289 /* rate_min: */ USE_RATE_MIN,
290 /* rate_max: */ USE_RATE_MAX,
291 /* channels_min: */ USE_CHANNELS_MIN,
292 /* channels_max: */ USE_CHANNELS_MAX,
293 /* buffer_bytes_max: */ MAX_BUFFER_SIZE,
294 /* period_bytes_min: */ 64,
295 /* period_bytes_max: */ MAX_BUFFER_SIZE,
296 /* periods_min: */ USE_PERIODS_MIN,
297 /* periods_max: */ USE_PERIODS_MAX,
298 /* fifo_size: */ 0,
299};
300
301static snd_pcm_hardware_t snd_card_dummy_capture =
302{
303 /* info: */ (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
304 SNDRV_PCM_INFO_MMAP_VALID),
305 /* formats: */ USE_FORMATS,
306 /* rates: */ USE_RATE,
307 /* rate_min: */ USE_RATE_MIN,
308 /* rate_max: */ USE_RATE_MAX,
309 /* channels_min: */ USE_CHANNELS_MIN,
310 /* channels_max: */ USE_CHANNELS_MAX,
311 /* buffer_bytes_max: */ MAX_BUFFER_SIZE,
312 /* period_bytes_min: */ 64,
313 /* period_bytes_max: */ MAX_BUFFER_SIZE,
314 /* periods_min: */ USE_PERIODS_MIN,
315 /* periods_max: */ USE_PERIODS_MAX,
316 /* fifo_size: */ 0,
317};
318
319static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime)
320{
321 snd_card_dummy_pcm_t *dpcm = runtime->private_data;
322 kfree(dpcm);
323}
324
325static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream)
326{
327 snd_pcm_runtime_t *runtime = substream->runtime;
328 snd_card_dummy_pcm_t *dpcm;
329
330 dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL);
331 if (dpcm == NULL)
332 return -ENOMEM;
333 if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
334 kfree(dpcm);
335 return -ENOMEM;
336 }
337 init_timer(&dpcm->timer);
338 dpcm->timer.data = (unsigned long) dpcm;
339 dpcm->timer.function = snd_card_dummy_pcm_timer_function;
340 spin_lock_init(&dpcm->lock);
341 dpcm->substream = substream;
342 runtime->private_data = dpcm;
343 runtime->private_free = snd_card_dummy_runtime_free;
344 runtime->hw = snd_card_dummy_playback;
345 if (substream->pcm->device & 1) {
346 runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
347 runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
348 }
349 if (substream->pcm->device & 2)
350 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
351 return 0;
352}
353
354static int snd_card_dummy_capture_open(snd_pcm_substream_t * substream)
355{
356 snd_pcm_runtime_t *runtime = substream->runtime;
357 snd_card_dummy_pcm_t *dpcm;
358
359 dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL);
360 if (dpcm == NULL)
361 return -ENOMEM;
362 if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) {
363 kfree(dpcm);
364 return -ENOMEM;
365 }
366 memset(runtime->dma_area, 0, runtime->dma_bytes);
367 init_timer(&dpcm->timer);
368 dpcm->timer.data = (unsigned long) dpcm;
369 dpcm->timer.function = snd_card_dummy_pcm_timer_function;
370 spin_lock_init(&dpcm->lock);
371 dpcm->substream = substream;
372 runtime->private_data = dpcm;
373 runtime->private_free = snd_card_dummy_runtime_free;
374 runtime->hw = snd_card_dummy_capture;
375 if (substream->pcm->device == 1) {
376 runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
377 runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
378 }
379 if (substream->pcm->device & 2)
380 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
381 return 0;
382}
383
384static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream)
385{
386 snd_pcm_runtime_t *runtime = substream->runtime;
387
388 snd_free_pages(runtime->dma_area, runtime->dma_bytes);
389 return 0;
390}
391
392static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream)
393{
394 snd_pcm_runtime_t *runtime = substream->runtime;
395
396 snd_free_pages(runtime->dma_area, runtime->dma_bytes);
397 return 0;
398}
399
400static snd_pcm_ops_t snd_card_dummy_playback_ops = {
401 /* open: */ snd_card_dummy_playback_open,
402 /* close: */ snd_card_dummy_playback_close,
403 /* ioctl: */ snd_card_dummy_playback_ioctl,
404 NULL, NULL,
405 /* prepare: */ snd_card_dummy_playback_prepare,
406 /* trigger: */ snd_card_dummy_playback_trigger,
407 /* pointer: */ snd_card_dummy_playback_pointer,
408 NULL, NULL
409};
410
411static snd_pcm_ops_t snd_card_dummy_capture_ops = {
412 /* open: */ snd_card_dummy_capture_open,
413 /* close: */ snd_card_dummy_capture_close,
414 /* ioctl: */ snd_card_dummy_capture_ioctl,
415 NULL, NULL,
416 /* prepare: */ snd_card_dummy_capture_prepare,
417 /* trigger: */ snd_card_dummy_capture_trigger,
418 /* pointer: */ snd_card_dummy_capture_pointer,
419 NULL, NULL
420};
421
422static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams)
423{
424 snd_pcm_t *pcm;
425 int err;
426
427 if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, substreams, substreams, &pcm)) < 0)
428 return err;
429 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
430 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
431 pcm->private_data = dummy;
432 pcm->info_flags = 0;
433 strcpy(pcm->name, "Dummy PCM");
434 return 0;
435}
436#define DUMMY_VOLUME(xname, xindex, addr) \
437 { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
438 0, 0, snd_dummy_volume_info, \
439 snd_dummy_volume_get, snd_dummy_volume_put, \
440 addr }
441
442static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
443{
444 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
445 uinfo->count = 2;
446 uinfo->value.integer.min = -50;
447 uinfo->value.integer.max = 100;
448 return 0;
449}
450
451static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
452{
453 snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
454 unsigned long flags;
455 int addr = kcontrol->private_value;
456
457 spin_lock_irqsave(&dummy->mixer_lock, flags);
458 ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
459 ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
460 spin_unlock_irqrestore(&dummy->mixer_lock, flags);
461 return 0;
462}
463
464static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
465{
466 snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
467 unsigned long flags;
468 int change, addr = kcontrol->private_value;
469 int left, right;
470
471 left = ucontrol->value.integer.value[0];
472 if (left < -50)
473 left = -50;
474 if (left > 100)
475 left = 100;
476 right = ucontrol->value.integer.value[1];
477 if (right < -50)
478 right = -50;
479 if (right > 100)
480 right = 100;
481 spin_lock_irqsave(&dummy->mixer_lock, flags);
482 change = dummy->mixer_volume[addr][0] != left ||
483 dummy->mixer_volume[addr][1] != right;
484 dummy->mixer_volume[addr][0] = left;
485 dummy->mixer_volume[addr][1] = right;
486 spin_unlock_irqrestore(&dummy->mixer_lock, flags);
487 return change;
488}
489
490#define DUMMY_CAPSRC(xname, xindex, addr) \
491 { SNDRV_CTL_ELEM_IFACE_MIXER, 0, 0, xname, xindex, \
492 0, 0, snd_dummy_capsrc_info, \
493 snd_dummy_capsrc_get, snd_dummy_capsrc_put, \
494 addr }
495
496static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
497{
498 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
499 uinfo->count = 2;
500 uinfo->value.integer.min = 0;
501 uinfo->value.integer.max = 1;
502 return 0;
503}
504
505static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
506{
507 snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
508 unsigned long flags;
509 int addr = kcontrol->private_value;
510
511 spin_lock_irqsave(&dummy->mixer_lock, flags);
512 ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
513 ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
514 spin_unlock_irqrestore(&dummy->mixer_lock, flags);
515 return 0;
516}
517
518static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
519{
520 snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol);
521 unsigned long flags;
522 int change, addr = kcontrol->private_value;
523 int left, right;
524
525 left = ucontrol->value.integer.value[0] & 1;
526 right = ucontrol->value.integer.value[1] & 1;
527 spin_lock_irqsave(&dummy->mixer_lock, flags);
528 change = dummy->capture_source[addr][0] != left &&
529 dummy->capture_source[addr][1] != right;
530 dummy->capture_source[addr][0] = left;
531 dummy->capture_source[addr][1] = right;
532 spin_unlock_irqrestore(&dummy->mixer_lock, flags);
533 return change;
534}
535
536#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t))
537
538static snd_kcontrol_new_t snd_dummy_controls[] = {
539 DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
540 DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
541 DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
542 DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER),
543 DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
544 DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER),
545 DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
546 DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER),
547 DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
548 DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER)
549};
550
551int __init snd_card_dummy_new_mixer(snd_card_dummy_t * dummy)
552{
553 snd_card_t *card = dummy->card;
554 unsigned int idx;
555 int err;
556
557 snd_assert(dummy != NULL, return -EINVAL);
558 spin_lock_init(&dummy->mixer_lock);
559 strcpy(card->mixername, "Dummy Mixer");
560
561 for (idx = 0; idx < DUMMY_CONTROLS; idx++) {
562 if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
563 return err;
564 }
565 return 0;
566}
567
568static int __init snd_card_dummy_probe(int dev)
569{
570 snd_card_t *card;
571 struct snd_card_dummy *dummy;
572 int idx, err;
573
574 if (!enable[dev])
575 return -ENODEV;
576 card = snd_card_new(index[dev], id[dev], THIS_MODULE,
577 sizeof(struct snd_card_dummy));
578 if (card == NULL)
579 return -ENOMEM;
580 dummy = (struct snd_card_dummy *)card->private_data;
581 dummy->card = card;
582 for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
583 if (pcm_substreams[dev] < 1)
584 pcm_substreams[dev] = 1;
585 if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
586 pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
587 if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0)
588 goto __nodev;
589 }
590 if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
591 goto __nodev;
592 strcpy(card->driver, "Dummy");
593 strcpy(card->shortname, "Dummy");
594 sprintf(card->longname, "Dummy %i", dev + 1);
595 if ((err = snd_card_register(card)) == 0) {
596 snd_dummy_cards[dev] = card;
597 return 0;
598 }
599__nodev:
600 snd_card_free(card);
601 return err;
602}
603
604static int __init alsa_card_dummy_init(void)
605{
606 int dev, cards;
607
608 for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) {
609 if (snd_card_dummy_probe(dev) < 0) {
610#ifdef MODULE
611 snd_printk("Dummy soundcard #%i not found or device busy\n", dev + 1);
612#endif
613 break;
614 }
615 cards++;
616 }
617 if (!cards) {
618#ifdef MODULE
619 snd_printk("Dummy soundcard not found or device busy\n");
620#endif
621 return -ENODEV;
622 }
623 return 0;
624}
625
626static void __exit alsa_card_dummy_exit(void)
627{
628 int idx;
629
630 for (idx = 0; idx < SNDRV_CARDS; idx++)
631 snd_card_free(snd_dummy_cards[idx]);
632}
633
634module_init(alsa_card_dummy_init)
635module_exit(alsa_card_dummy_exit)
636
637#ifndef MODULE
638
639/* format is: snd-card-dummy=snd_enable,snd_index,snd_id,
640 snd_pcm_devs,snd_pcm_substreams */
641
642static int __init alsa_card_dummy_setup(char *str)
643{
644 static unsigned __initdata nr_dev = 0;
645
646 if (nr_dev >= SNDRV_CARDS)
647 return 0;
648 (void)(get_option(&str,&enable[nr_dev]) == 2 &&
649 get_option(&str,&index[nr_dev]) == 2 &&
650 get_id(&str,&id[nr_dev]) == 2 &&
651 get_option(&str,&pcm_devs[nr_dev]) == 2 &&
652 get_option(&str,&pcm_substreams[nr_dev]) == 2);
653 nr_dev++;
654 return 1;
655}
656
657__setup("snd-dummy=", alsa_card_dummy_setup);
658
659#endif /* ifndef MODULE */
Note: See TracBrowser for help on using the repository browser.