Blame view

sound/drivers/aloop.c 35.1 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
597603d61   Jaroslav Kysela   ALSA: introduce t...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   *  Loopback soundcard
   *
   *  Original code:
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   *
   *  More accurate positioning and full-duplex support:
   *  Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de>
   *
   *  Major (almost complete) rewrite:
   *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
   *
   *  A next major update in 2010 (separate timers for playback and capture):
   *  Copyright (c) Jaroslav Kysela <perex@perex.cz>
597603d61   Jaroslav Kysela   ALSA: introduce t...
16
17
18
19
20
21
22
   */
  
  #include <linux/init.h>
  #include <linux/jiffies.h>
  #include <linux/slab.h>
  #include <linux/time.h>
  #include <linux/wait.h>
65a772172   Paul Gortmaker   sound: fix driver...
23
  #include <linux/module.h>
597603d61   Jaroslav Kysela   ALSA: introduce t...
24
25
26
27
  #include <linux/platform_device.h>
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
b088b53e2   Takashi Iwai   ALSA: aloop: Fix ...
28
  #include <sound/pcm_params.h>
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
29
  #include <sound/info.h>
597603d61   Jaroslav Kysela   ALSA: introduce t...
30
31
32
33
34
35
36
37
38
39
40
  #include <sound/initval.h>
  
  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  MODULE_DESCRIPTION("A loopback soundcard");
  MODULE_LICENSE("GPL");
  MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
  
  #define MAX_PCM_SUBSTREAMS	8
  
  static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
  static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
a67ff6a54   Rusty Russell   ALSA: module_para...
41
  static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
597603d61   Jaroslav Kysela   ALSA: introduce t...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
  static int pcm_notify[SNDRV_CARDS];
  
  module_param_array(index, int, NULL, 0444);
  MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
  module_param_array(id, charp, NULL, 0444);
  MODULE_PARM_DESC(id, "ID string for loopback soundcard.");
  module_param_array(enable, bool, NULL, 0444);
  MODULE_PARM_DESC(enable, "Enable this loopback soundcard.");
  module_param_array(pcm_substreams, int, NULL, 0444);
  MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
  module_param_array(pcm_notify, int, NULL, 0444);
  MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
  
  #define NO_PITCH 100000
  
  struct loopback_pcm;
  
  struct loopback_cable {
  	spinlock_t lock;
  	struct loopback_pcm *streams[2];
  	struct snd_pcm_hardware hw;
  	/* flags */
  	unsigned int valid;
  	unsigned int running;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
67
  	unsigned int pause;
597603d61   Jaroslav Kysela   ALSA: introduce t...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  };
  
  struct loopback_setup {
  	unsigned int notify: 1;
  	unsigned int rate_shift;
  	unsigned int format;
  	unsigned int rate;
  	unsigned int channels;
  	struct snd_ctl_elem_id active_id;
  	struct snd_ctl_elem_id format_id;
  	struct snd_ctl_elem_id rate_id;
  	struct snd_ctl_elem_id channels_id;
  };
  
  struct loopback {
  	struct snd_card *card;
  	struct mutex cable_lock;
  	struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
  	struct snd_pcm *pcm[2];
  	struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
  };
  
  struct loopback_pcm {
  	struct loopback *loopback;
  	struct snd_pcm_substream *substream;
  	struct loopback_cable *cable;
  	unsigned int pcm_buffer_size;
  	unsigned int buf_pos;	/* position in buffer */
  	unsigned int silent_size;
  	/* PCM parameters */
  	unsigned int pcm_period_size;
  	unsigned int pcm_bps;		/* bytes per second */
  	unsigned int pcm_salign;	/* bytes per sample * channels */
  	unsigned int pcm_rate_shift;	/* rate shift value */
  	/* flags */
  	unsigned int period_update_pending :1;
  	/* timer stuff */
  	unsigned int irq_pos;		/* fractional IRQ position */
  	unsigned int period_size_frac;
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
107
  	unsigned int last_drift;
597603d61   Jaroslav Kysela   ALSA: introduce t...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  	unsigned long last_jiffies;
  	struct timer_list timer;
  };
  
  static struct platform_device *devices[SNDRV_CARDS];
  
  static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x)
  {
  	if (dpcm->pcm_rate_shift == NO_PITCH) {
  		x /= HZ;
  	} else {
  		x = div_u64(NO_PITCH * (unsigned long long)x,
  			    HZ * (unsigned long long)dpcm->pcm_rate_shift);
  	}
  	return x - (x % dpcm->pcm_salign);
  }
  
  static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x)
  {
  	if (dpcm->pcm_rate_shift == NO_PITCH) {	/* no pitch */
  		return x * HZ;
  	} else {
  		x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ,
  			    NO_PITCH);
  	}
  	return x;
  }
  
  static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm)
  {
  	int device = dpcm->substream->pstr->pcm->device;
  	
  	if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		device ^= 1;
  	return &dpcm->loopback->setup[dpcm->substream->number][device];
  }
  
  static inline unsigned int get_notify(struct loopback_pcm *dpcm)
  {
  	return get_setup(dpcm)->notify;
  }
  
  static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
  {
  	return get_setup(dpcm)->rate_shift;
  }
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
154
  /* call in cable->lock */
597603d61   Jaroslav Kysela   ALSA: introduce t...
155
156
157
158
159
160
161
162
163
  static void loopback_timer_start(struct loopback_pcm *dpcm)
  {
  	unsigned long tick;
  	unsigned int rate_shift = get_rate_shift(dpcm);
  
  	if (rate_shift != dpcm->pcm_rate_shift) {
  		dpcm->pcm_rate_shift = rate_shift;
  		dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
  	}
0db710230   Jaroslav Kysela   ALSA: snd-aloop -...
164
165
166
167
  	if (dpcm->period_size_frac <= dpcm->irq_pos) {
  		dpcm->irq_pos %= dpcm->period_size_frac;
  		dpcm->period_update_pending = 1;
  	}
597603d61   Jaroslav Kysela   ALSA: introduce t...
168
169
  	tick = dpcm->period_size_frac - dpcm->irq_pos;
  	tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
db974553e   Takashi Iwai   ALSA: aloop: Use ...
170
  	mod_timer(&dpcm->timer, jiffies + tick);
597603d61   Jaroslav Kysela   ALSA: introduce t...
171
  }
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
172
  /* call in cable->lock */
597603d61   Jaroslav Kysela   ALSA: introduce t...
173
174
175
  static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
  {
  	del_timer(&dpcm->timer);
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
176
  	dpcm->timer.expires = 0;
597603d61   Jaroslav Kysela   ALSA: introduce t...
177
  }
67a01afaf   Takashi Iwai   ALSA: aloop: Sync...
178
179
180
181
  static inline void loopback_timer_stop_sync(struct loopback_pcm *dpcm)
  {
  	del_timer_sync(&dpcm->timer);
  }
597603d61   Jaroslav Kysela   ALSA: introduce t...
182
183
184
185
186
187
  #define CABLE_VALID_PLAYBACK	(1 << SNDRV_PCM_STREAM_PLAYBACK)
  #define CABLE_VALID_CAPTURE	(1 << SNDRV_PCM_STREAM_CAPTURE)
  #define CABLE_VALID_BOTH	(CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
  
  static int loopback_check_format(struct loopback_cable *cable, int stream)
  {
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
188
  	struct snd_pcm_runtime *runtime, *cruntime;
597603d61   Jaroslav Kysela   ALSA: introduce t...
189
190
191
192
193
194
195
196
197
198
199
  	struct loopback_setup *setup;
  	struct snd_card *card;
  	int check;
  
  	if (cable->valid != CABLE_VALID_BOTH) {
  		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
  			goto __notify;
  		return 0;
  	}
  	runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
  							substream->runtime;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
200
201
202
203
204
  	cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
  							substream->runtime;
  	check = runtime->format != cruntime->format ||
  		runtime->rate != cruntime->rate ||
  		runtime->channels != cruntime->channels;
597603d61   Jaroslav Kysela   ALSA: introduce t...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	if (!check)
  		return 0;
  	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
  		return -EIO;
  	} else {
  		snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
  					substream, SNDRV_PCM_STATE_DRAINING);
  	      __notify:
  		runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
  							substream->runtime;
  		setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
  		card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
  		if (setup->format != runtime->format) {
  			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
  							&setup->format_id);
  			setup->format = runtime->format;
  		}
  		if (setup->rate != runtime->rate) {
  			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
  							&setup->rate_id);
  			setup->rate = runtime->rate;
  		}
  		if (setup->channels != runtime->channels) {
  			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
  							&setup->channels_id);
  			setup->channels = runtime->channels;
  		}
  	}
  	return 0;
  }
  
  static void loopback_active_notify(struct loopback_pcm *dpcm)
  {
  	snd_ctl_notify(dpcm->loopback->card,
  		       SNDRV_CTL_EVENT_MASK_VALUE,
  		       &get_setup(dpcm)->active_id);
  }
  
  static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback_pcm *dpcm = runtime->private_data;
  	struct loopback_cable *cable = dpcm->cable;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
248
  	int err, stream = 1 << substream->stream;
597603d61   Jaroslav Kysela   ALSA: introduce t...
249
250
251
252
253
254
255
256
  
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  		err = loopback_check_format(cable, substream->stream);
  		if (err < 0)
  			return err;
  		dpcm->last_jiffies = jiffies;
  		dpcm->pcm_rate_shift = 0;
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
257
  		dpcm->last_drift = 0;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
258
  		spin_lock(&cable->lock);	
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
259
260
  		cable->running |= stream;
  		cable->pause &= ~stream;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
261
  		loopback_timer_start(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
262
  		spin_unlock(&cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
263
264
265
266
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
267
  		spin_lock(&cable->lock);	
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
268
269
  		cable->running &= ~stream;
  		cable->pause &= ~stream;
597603d61   Jaroslav Kysela   ALSA: introduce t...
270
  		loopback_timer_stop(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
271
  		spin_unlock(&cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
272
273
274
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
  		break;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
275
  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
276
  	case SNDRV_PCM_TRIGGER_SUSPEND:
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
277
278
  		spin_lock(&cable->lock);	
  		cable->pause |= stream;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
279
  		loopback_timer_stop(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
280
  		spin_unlock(&cable->lock);
306a4f3ca   Robert Rosengren   ALSA: aloop: Mark...
281
282
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
283
284
  		break;
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
285
  	case SNDRV_PCM_TRIGGER_RESUME:
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
286
287
288
  		spin_lock(&cable->lock);
  		dpcm->last_jiffies = jiffies;
  		cable->pause &= ~stream;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
289
  		loopback_timer_start(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
290
  		spin_unlock(&cable->lock);
306a4f3ca   Robert Rosengren   ALSA: aloop: Mark...
291
292
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
293
  		break;
597603d61   Jaroslav Kysela   ALSA: introduce t...
294
295
296
297
298
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
299
300
301
302
303
  static void params_change(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback_pcm *dpcm = runtime->private_data;
  	struct loopback_cable *cable = dpcm->cable;
74c34ca1c   Eldad Zack   ALSA: pcm_format_...
304
  	cable->hw.formats = pcm_format_to_bits(runtime->format);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
305
306
307
308
  	cable->hw.rate_min = runtime->rate;
  	cable->hw.rate_max = runtime->rate;
  	cable->hw.channels_min = runtime->channels;
  	cable->hw.channels_max = runtime->channels;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
309
  }
597603d61   Jaroslav Kysela   ALSA: introduce t...
310
311
312
313
314
  static int loopback_prepare(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback_pcm *dpcm = runtime->private_data;
  	struct loopback_cable *cable = dpcm->cable;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
315
  	int bps, salign;
597603d61   Jaroslav Kysela   ALSA: introduce t...
316

67a01afaf   Takashi Iwai   ALSA: aloop: Sync...
317
  	loopback_timer_stop_sync(dpcm);
50e090841   Timo Wischer   ALSA: aloop: Supp...
318
  	salign = (snd_pcm_format_physical_width(runtime->format) *
597603d61   Jaroslav Kysela   ALSA: introduce t...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  						runtime->channels) / 8;
  	bps = salign * runtime->rate;
  	if (bps <= 0 || salign <= 0)
  		return -EINVAL;
  
  	dpcm->buf_pos = 0;
  	dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
  	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  		/* clear capture buffer */
  		dpcm->silent_size = dpcm->pcm_buffer_size;
  		snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
  					   runtime->buffer_size * runtime->channels);
  	}
  
  	dpcm->irq_pos = 0;
  	dpcm->period_update_pending = 0;
  	dpcm->pcm_bps = bps;
  	dpcm->pcm_salign = salign;
  	dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
  
  	mutex_lock(&dpcm->loopback->cable_lock);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
340
341
342
343
  	if (!(cable->valid & ~(1 << substream->stream)) ||
              (get_setup(dpcm)->notify &&
  	     substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
  		params_change(substream);
597603d61   Jaroslav Kysela   ALSA: introduce t...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  	cable->valid |= 1 << substream->stream;
  	mutex_unlock(&dpcm->loopback->cable_lock);
  
  	return 0;
  }
  
  static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
  {
  	struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
  	char *dst = runtime->dma_area;
  	unsigned int dst_off = dpcm->buf_pos;
  
  	if (dpcm->silent_size >= dpcm->pcm_buffer_size)
  		return;
  	if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size)
  		bytes = dpcm->pcm_buffer_size - dpcm->silent_size;
  
  	for (;;) {
  		unsigned int size = bytes;
  		if (dst_off + size > dpcm->pcm_buffer_size)
  			size = dpcm->pcm_buffer_size - dst_off;
  		snd_pcm_format_set_silence(runtime->format, dst + dst_off,
  					   bytes_to_frames(runtime, size) *
  					   	runtime->channels);
  		dpcm->silent_size += size;
  		bytes -= size;
  		if (!bytes)
  			break;
  		dst_off = 0;
  	}
  }
  
  static void copy_play_buf(struct loopback_pcm *play,
  			  struct loopback_pcm *capt,
  			  unsigned int bytes)
  {
  	struct snd_pcm_runtime *runtime = play->substream->runtime;
20d9a26db   Jaroslav Kysela   ALSA: snd-aloop -...
381
  	char *src = runtime->dma_area;
597603d61   Jaroslav Kysela   ALSA: introduce t...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  	char *dst = capt->substream->runtime->dma_area;
  	unsigned int src_off = play->buf_pos;
  	unsigned int dst_off = capt->buf_pos;
  	unsigned int clear_bytes = 0;
  
  	/* check if playback is draining, trim the capture copy size
  	 * when our pointer is at the end of playback ring buffer */
  	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
  	    snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { 
  	    	snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
  		appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
  		appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
  		appl_ptr1 += play->buf_pos / play->pcm_salign;
  		if (appl_ptr < appl_ptr1)
  			appl_ptr1 -= runtime->buffer_size;
  		diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
  		if (diff < bytes) {
  			clear_bytes = bytes - diff;
  			bytes = diff;
  		}
  	}
  
  	for (;;) {
  		unsigned int size = bytes;
  		if (src_off + size > play->pcm_buffer_size)
  			size = play->pcm_buffer_size - src_off;
  		if (dst_off + size > capt->pcm_buffer_size)
  			size = capt->pcm_buffer_size - dst_off;
  		memcpy(dst + dst_off, src + src_off, size);
  		capt->silent_size = 0;
  		bytes -= size;
  		if (!bytes)
  			break;
  		src_off = (src_off + size) % play->pcm_buffer_size;
  		dst_off = (dst_off + size) % capt->pcm_buffer_size;
  	}
20d9a26db   Jaroslav Kysela   ALSA: snd-aloop -...
418
  	if (clear_bytes > 0) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
419
  		clear_capture_buf(capt, clear_bytes);
20d9a26db   Jaroslav Kysela   ALSA: snd-aloop -...
420
421
  		capt->silent_size = 0;
  	}
597603d61   Jaroslav Kysela   ALSA: introduce t...
422
  }
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
423
424
  static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
  					 unsigned int jiffies_delta)
597603d61   Jaroslav Kysela   ALSA: introduce t...
425
  {
597603d61   Jaroslav Kysela   ALSA: introduce t...
426
  	unsigned long last_pos;
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
427
  	unsigned int delta;
597603d61   Jaroslav Kysela   ALSA: introduce t...
428
429
  
  	last_pos = byte_pos(dpcm, dpcm->irq_pos);
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
430
431
432
433
434
  	dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
  	delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
  	if (delta >= dpcm->last_drift)
  		delta -= dpcm->last_drift;
  	dpcm->last_drift = 0;
597603d61   Jaroslav Kysela   ALSA: introduce t...
435
436
437
438
  	if (dpcm->irq_pos >= dpcm->period_size_frac) {
  		dpcm->irq_pos %= dpcm->period_size_frac;
  		dpcm->period_update_pending = 1;
  	}
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
439
440
441
442
443
444
445
446
  	return delta;
  }
  
  static inline void bytepos_finish(struct loopback_pcm *dpcm,
  				  unsigned int delta)
  {
  	dpcm->buf_pos += delta;
  	dpcm->buf_pos %= dpcm->pcm_buffer_size;
597603d61   Jaroslav Kysela   ALSA: introduce t...
447
  }
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
448
  /* call in cable->lock */
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
449
  static unsigned int loopback_pos_update(struct loopback_cable *cable)
597603d61   Jaroslav Kysela   ALSA: introduce t...
450
451
452
453
454
455
  {
  	struct loopback_pcm *dpcm_play =
  			cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
  	struct loopback_pcm *dpcm_capt =
  			cable->streams[SNDRV_PCM_STREAM_CAPTURE];
  	unsigned long delta_play = 0, delta_capt = 0;
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
456
  	unsigned int running, count1, count2;
597603d61   Jaroslav Kysela   ALSA: introduce t...
457

5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
458
  	running = cable->running ^ cable->pause;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
459
  	if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
460
461
462
  		delta_play = jiffies - dpcm_play->last_jiffies;
  		dpcm_play->last_jiffies += delta_play;
  	}
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
463
  	if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
464
465
466
  		delta_capt = jiffies - dpcm_capt->last_jiffies;
  		dpcm_capt->last_jiffies += delta_capt;
  	}
98d21df43   Takashi Iwai   ALSA: aloop - Fix...
467
468
  	if (delta_play == 0 && delta_capt == 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
469
470
  		
  	if (delta_play > delta_capt) {
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
471
472
  		count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
  		bytepos_finish(dpcm_play, count1);
597603d61   Jaroslav Kysela   ALSA: introduce t...
473
474
  		delta_play = delta_capt;
  	} else if (delta_play < delta_capt) {
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
475
476
477
  		count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
  		clear_capture_buf(dpcm_capt, count1);
  		bytepos_finish(dpcm_capt, count1);
597603d61   Jaroslav Kysela   ALSA: introduce t...
478
479
  		delta_capt = delta_play;
  	}
98d21df43   Takashi Iwai   ALSA: aloop - Fix...
480
481
  	if (delta_play == 0 && delta_capt == 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
482
  	/* note delta_capt == delta_play at this moment */
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
483
484
485
486
487
488
489
490
491
492
493
  	count1 = bytepos_delta(dpcm_play, delta_play);
  	count2 = bytepos_delta(dpcm_capt, delta_capt);
  	if (count1 < count2) {
  		dpcm_capt->last_drift = count2 - count1;
  		count1 = count2;
  	} else if (count1 > count2) {
  		dpcm_play->last_drift = count1 - count2;
  	}
  	copy_play_buf(dpcm_play, dpcm_capt, count1);
  	bytepos_finish(dpcm_play, count1);
  	bytepos_finish(dpcm_capt, count1);
98d21df43   Takashi Iwai   ALSA: aloop - Fix...
494
   unlock:
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
495
  	return running;
597603d61   Jaroslav Kysela   ALSA: introduce t...
496
  }
bc47ba90b   Kees Cook   ALSA: drivers: Co...
497
  static void loopback_timer_function(struct timer_list *t)
597603d61   Jaroslav Kysela   ALSA: introduce t...
498
  {
bc47ba90b   Kees Cook   ALSA: drivers: Co...
499
  	struct loopback_pcm *dpcm = from_timer(dpcm, t, timer);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
500
  	unsigned long flags;
597603d61   Jaroslav Kysela   ALSA: introduce t...
501

999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
502
503
  	spin_lock_irqsave(&dpcm->cable->lock, flags);
  	if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
504
  		loopback_timer_start(dpcm);
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
505
506
  		if (dpcm->period_update_pending) {
  			dpcm->period_update_pending = 0;
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
507
508
  			spin_unlock_irqrestore(&dpcm->cable->lock, flags);
  			/* need to unlock before calling below */
597603d61   Jaroslav Kysela   ALSA: introduce t...
509
  			snd_pcm_period_elapsed(dpcm->substream);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
510
  			return;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
511
  		}
597603d61   Jaroslav Kysela   ALSA: introduce t...
512
  	}
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
513
  	spin_unlock_irqrestore(&dpcm->cable->lock, flags);
597603d61   Jaroslav Kysela   ALSA: introduce t...
514
515
516
517
518
519
  }
  
  static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback_pcm *dpcm = runtime->private_data;
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
520
  	snd_pcm_uframes_t pos;
597603d61   Jaroslav Kysela   ALSA: introduce t...
521

999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
522
  	spin_lock(&dpcm->cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
523
  	loopback_pos_update(dpcm->cable);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
524
525
526
  	pos = dpcm->buf_pos;
  	spin_unlock(&dpcm->cable->lock);
  	return bytes_to_frames(runtime, pos);
597603d61   Jaroslav Kysela   ALSA: introduce t...
527
  }
b6c0b7156   Bhumika Goyal   ALSA: drivers: ma...
528
  static const struct snd_pcm_hardware loopback_pcm_hardware =
597603d61   Jaroslav Kysela   ALSA: introduce t...
529
530
  {
  	.info =		(SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
531
532
  			 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
  			 SNDRV_PCM_INFO_RESUME),
597603d61   Jaroslav Kysela   ALSA: introduce t...
533
  	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
50e090841   Timo Wischer   ALSA: aloop: Supp...
534
535
  			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
  			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
597603d61   Jaroslav Kysela   ALSA: introduce t...
536
537
538
539
540
541
542
543
544
  			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
  			 SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
  	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
  	.rate_min =		8000,
  	.rate_max =		192000,
  	.channels_min =		1,
  	.channels_max =		32,
  	.buffer_bytes_max =	2 * 1024 * 1024,
  	.period_bytes_min =	64,
0db710230   Jaroslav Kysela   ALSA: snd-aloop -...
545
546
547
  	/* note check overflow in frac_pos() using pcm_rate_shift before
  	   changing period_bytes_max value */
  	.period_bytes_max =	1024 * 1024,
597603d61   Jaroslav Kysela   ALSA: introduce t...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
  	.periods_min =		1,
  	.periods_max =		1024,
  	.fifo_size =		0,
  };
  
  static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
  {
  	struct loopback_pcm *dpcm = runtime->private_data;
  	kfree(dpcm);
  }
  
  static int loopback_hw_params(struct snd_pcm_substream *substream,
  			      struct snd_pcm_hw_params *params)
  {
6b69a0e52   Takashi Iwai   ALSA: aloop - Use...
562
563
  	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
  						params_buffer_bytes(params));
597603d61   Jaroslav Kysela   ALSA: introduce t...
564
565
566
567
568
569
570
571
572
573
574
  }
  
  static int loopback_hw_free(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback_pcm *dpcm = runtime->private_data;
  	struct loopback_cable *cable = dpcm->cable;
  
  	mutex_lock(&dpcm->loopback->cable_lock);
  	cable->valid &= ~(1 << substream->stream);
  	mutex_unlock(&dpcm->loopback->cable_lock);
6b69a0e52   Takashi Iwai   ALSA: aloop - Use...
575
  	return snd_pcm_lib_free_vmalloc_buffer(substream);
597603d61   Jaroslav Kysela   ALSA: introduce t...
576
577
578
579
580
581
582
583
584
  }
  
  static unsigned int get_cable_index(struct snd_pcm_substream *substream)
  {
  	if (!substream->pcm->device)
  		return substream->stream;
  	else
  		return !substream->stream;
  }
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
585
586
587
  static int rule_format(struct snd_pcm_hw_params *params,
  		       struct snd_pcm_hw_rule *rule)
  {
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
588
589
  	struct loopback_pcm *dpcm = rule->private;
  	struct loopback_cable *cable = dpcm->cable;
b088b53e2   Takashi Iwai   ALSA: aloop: Fix ...
590
  	struct snd_mask m;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
591

b088b53e2   Takashi Iwai   ALSA: aloop: Fix ...
592
  	snd_mask_none(&m);
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
593
594
595
596
  	mutex_lock(&dpcm->loopback->cable_lock);
  	m.bits[0] = (u_int32_t)cable->hw.formats;
  	m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
  	mutex_unlock(&dpcm->loopback->cable_lock);
b088b53e2   Takashi Iwai   ALSA: aloop: Fix ...
597
  	return snd_mask_refine(hw_param_mask(params, rule->var), &m);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
598
599
600
601
602
  }
  
  static int rule_rate(struct snd_pcm_hw_params *params,
  		     struct snd_pcm_hw_rule *rule)
  {
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
603
604
  	struct loopback_pcm *dpcm = rule->private;
  	struct loopback_cable *cable = dpcm->cable;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
605
  	struct snd_interval t;
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
606
607
608
609
  	mutex_lock(&dpcm->loopback->cable_lock);
  	t.min = cable->hw.rate_min;
  	t.max = cable->hw.rate_max;
  	mutex_unlock(&dpcm->loopback->cable_lock);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
610
611
612
613
614
615
616
617
          t.openmin = t.openmax = 0;
          t.integer = 0;
  	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
  }
  
  static int rule_channels(struct snd_pcm_hw_params *params,
  			 struct snd_pcm_hw_rule *rule)
  {
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
618
619
  	struct loopback_pcm *dpcm = rule->private;
  	struct loopback_cable *cable = dpcm->cable;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
620
  	struct snd_interval t;
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
621
622
623
624
  	mutex_lock(&dpcm->loopback->cable_lock);
  	t.min = cable->hw.channels_min;
  	t.max = cable->hw.channels_max;
  	mutex_unlock(&dpcm->loopback->cable_lock);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
625
626
627
628
          t.openmin = t.openmax = 0;
          t.integer = 0;
  	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
  }
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
629
630
631
632
633
634
635
636
637
638
639
  static void free_cable(struct snd_pcm_substream *substream)
  {
  	struct loopback *loopback = substream->private_data;
  	int dev = get_cable_index(substream);
  	struct loopback_cable *cable;
  
  	cable = loopback->cables[substream->number][dev];
  	if (!cable)
  		return;
  	if (cable->streams[!substream->stream]) {
  		/* other stream is still alive */
8e6b1a72a   Takashi Iwai   ALSA: aloop: Fix ...
640
  		spin_lock_irq(&cable->lock);
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
641
  		cable->streams[substream->stream] = NULL;
8e6b1a72a   Takashi Iwai   ALSA: aloop: Fix ...
642
  		spin_unlock_irq(&cable->lock);
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
643
644
645
646
647
648
  	} else {
  		/* free the cable */
  		loopback->cables[substream->number][dev] = NULL;
  		kfree(cable);
  	}
  }
597603d61   Jaroslav Kysela   ALSA: introduce t...
649
650
651
652
653
  static int loopback_open(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct loopback *loopback = substream->private_data;
  	struct loopback_pcm *dpcm;
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
654
  	struct loopback_cable *cable = NULL;
597603d61   Jaroslav Kysela   ALSA: introduce t...
655
656
657
658
659
660
661
662
663
664
665
  	int err = 0;
  	int dev = get_cable_index(substream);
  
  	mutex_lock(&loopback->cable_lock);
  	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
  	if (!dpcm) {
  		err = -ENOMEM;
  		goto unlock;
  	}
  	dpcm->loopback = loopback;
  	dpcm->substream = substream;
bc47ba90b   Kees Cook   ALSA: drivers: Co...
666
  	timer_setup(&dpcm->timer, loopback_timer_function, 0);
597603d61   Jaroslav Kysela   ALSA: introduce t...
667
668
669
670
671
  
  	cable = loopback->cables[substream->number][dev];
  	if (!cable) {
  		cable = kzalloc(sizeof(*cable), GFP_KERNEL);
  		if (!cable) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
672
673
674
675
676
677
678
679
  			err = -ENOMEM;
  			goto unlock;
  		}
  		spin_lock_init(&cable->lock);
  		cable->hw = loopback_pcm_hardware;
  		loopback->cables[substream->number][dev] = cable;
  	}
  	dpcm->cable = cable;
597603d61   Jaroslav Kysela   ALSA: introduce t...
680
681
  
  	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
682
683
684
685
686
  	/* use dynamic rules based on actual runtime->hw values */
  	/* note that the default rules created in the PCM midlevel code */
  	/* are cached -> they do not reflect the actual state */
  	err = snd_pcm_hw_rule_add(runtime, 0,
  				  SNDRV_PCM_HW_PARAM_FORMAT,
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
687
  				  rule_format, dpcm,
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
688
689
690
691
692
  				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
  	if (err < 0)
  		goto unlock;
  	err = snd_pcm_hw_rule_add(runtime, 0,
  				  SNDRV_PCM_HW_PARAM_RATE,
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
693
  				  rule_rate, dpcm,
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
694
695
696
697
698
  				  SNDRV_PCM_HW_PARAM_RATE, -1);
  	if (err < 0)
  		goto unlock;
  	err = snd_pcm_hw_rule_add(runtime, 0,
  				  SNDRV_PCM_HW_PARAM_CHANNELS,
898dfe468   Takashi Iwai   ALSA: aloop: Fix ...
699
  				  rule_channels, dpcm,
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
700
701
702
  				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
  	if (err < 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
703
704
  	runtime->private_data = dpcm;
  	runtime->private_free = loopback_runtime_free;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
705
  	if (get_notify(dpcm))
597603d61   Jaroslav Kysela   ALSA: introduce t...
706
  		runtime->hw = loopback_pcm_hardware;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
707
  	else
597603d61   Jaroslav Kysela   ALSA: introduce t...
708
  		runtime->hw = cable->hw;
8e6b1a72a   Takashi Iwai   ALSA: aloop: Fix ...
709
710
711
712
  
  	spin_lock_irq(&cable->lock);
  	cable->streams[substream->stream] = dpcm;
  	spin_unlock_irq(&cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
713
   unlock:
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
714
715
716
717
  	if (err < 0) {
  		free_cable(substream);
  		kfree(dpcm);
  	}
597603d61   Jaroslav Kysela   ALSA: introduce t...
718
719
720
721
722
723
724
725
  	mutex_unlock(&loopback->cable_lock);
  	return err;
  }
  
  static int loopback_close(struct snd_pcm_substream *substream)
  {
  	struct loopback *loopback = substream->private_data;
  	struct loopback_pcm *dpcm = substream->runtime->private_data;
597603d61   Jaroslav Kysela   ALSA: introduce t...
726

67a01afaf   Takashi Iwai   ALSA: aloop: Sync...
727
  	loopback_timer_stop_sync(dpcm);
597603d61   Jaroslav Kysela   ALSA: introduce t...
728
  	mutex_lock(&loopback->cable_lock);
9685347aa   Takashi Iwai   ALSA: aloop: Rele...
729
  	free_cable(substream);
597603d61   Jaroslav Kysela   ALSA: introduce t...
730
731
732
  	mutex_unlock(&loopback->cable_lock);
  	return 0;
  }
9f88058e7   Takashi Iwai   ALSA: aloop: Redu...
733
  static const struct snd_pcm_ops loopback_pcm_ops = {
597603d61   Jaroslav Kysela   ALSA: introduce t...
734
735
736
737
738
739
740
741
  	.open =		loopback_open,
  	.close =	loopback_close,
  	.ioctl =	snd_pcm_lib_ioctl,
  	.hw_params =	loopback_hw_params,
  	.hw_free =	loopback_hw_free,
  	.prepare =	loopback_prepare,
  	.trigger =	loopback_trigger,
  	.pointer =	loopback_pointer,
6b69a0e52   Takashi Iwai   ALSA: aloop - Use...
742
  	.page =		snd_pcm_lib_get_vmalloc_page,
597603d61   Jaroslav Kysela   ALSA: introduce t...
743
  };
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
744
745
  static int loopback_pcm_new(struct loopback *loopback,
  			    int device, int substreams)
597603d61   Jaroslav Kysela   ALSA: introduce t...
746
747
748
749
750
751
752
753
  {
  	struct snd_pcm *pcm;
  	int err;
  
  	err = snd_pcm_new(loopback->card, "Loopback PCM", device,
  			  substreams, substreams, &pcm);
  	if (err < 0)
  		return err;
9f88058e7   Takashi Iwai   ALSA: aloop: Redu...
754
755
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops);
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops);
597603d61   Jaroslav Kysela   ALSA: introduce t...
756
757
758
759
760
761
  
  	pcm->private_data = loopback;
  	pcm->info_flags = 0;
  	strcpy(pcm->name, "Loopback PCM");
  
  	loopback->pcm[device] = pcm;
597603d61   Jaroslav Kysela   ALSA: introduce t...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  	return 0;
  }
  
  static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol,   
  				    struct snd_ctl_elem_info *uinfo) 
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 80000;
  	uinfo->value.integer.max = 120000;
  	uinfo->value.integer.step = 1;
  	return 0;
  }                                  
  
  static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
781
  	mutex_lock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
782
783
784
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].rate_shift;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
785
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
  	return 0;
  }
  
  static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	unsigned int val;
  	int change = 0;
  
  	val = ucontrol->value.integer.value[0];
  	if (val < 80000)
  		val = 80000;
  	if (val > 120000)
  		val = 120000;	
  	mutex_lock(&loopback->cable_lock);
  	if (val != loopback->setup[kcontrol->id.subdevice]
  				  [kcontrol->id.device].rate_shift) {
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].rate_shift = val;
  		change = 1;
  	}
  	mutex_unlock(&loopback->cable_lock);
  	return change;
  }
  
  static int loopback_notify_get(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
817
  	mutex_lock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
818
819
820
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].notify;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
821
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
822
823
824
825
826
827
828
829
830
831
832
  	return 0;
  }
  
  static int loopback_notify_put(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	unsigned int val;
  	int change = 0;
  
  	val = ucontrol->value.integer.value[0] ? 1 : 0;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
833
  	mutex_lock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
834
835
836
837
838
839
  	if (val != loopback->setup[kcontrol->id.subdevice]
  				[kcontrol->id.device].notify) {
  		loopback->setup[kcontrol->id.subdevice]
  			[kcontrol->id.device].notify = val;
  		change = 1;
  	}
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
840
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
841
842
843
844
845
846
847
  	return change;
  }
  
  static int loopback_active_get(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
848
  	struct loopback_cable *cable;
597603d61   Jaroslav Kysela   ALSA: introduce t...
849
  	unsigned int val = 0;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
850
851
  	mutex_lock(&loopback->cable_lock);
  	cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
306a4f3ca   Robert Rosengren   ALSA: aloop: Mark...
852
853
854
855
856
  	if (cable != NULL) {
  		unsigned int running = cable->running ^ cable->pause;
  
  		val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
  	}
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
857
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
  	ucontrol->value.integer.value[0] = val;
  	return 0;
  }
  
  static int loopback_format_info(struct snd_kcontrol *kcontrol,   
  				struct snd_ctl_elem_info *uinfo) 
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
  	uinfo->value.integer.step = 1;
  	return 0;
  }                                  
  
  static int loopback_format_get(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].format;
  	return 0;
  }
  
  static int loopback_rate_info(struct snd_kcontrol *kcontrol,   
  			      struct snd_ctl_elem_info *uinfo) 
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 192000;
  	uinfo->value.integer.step = 1;
  	return 0;
  }                                  
  
  static int loopback_rate_get(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
900
  	mutex_lock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
901
902
903
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].rate;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
904
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
  	return 0;
  }
  
  static int loopback_channels_info(struct snd_kcontrol *kcontrol,   
  				  struct snd_ctl_elem_info *uinfo) 
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 1;
  	uinfo->value.integer.max = 1024;
  	uinfo->value.integer.step = 1;
  	return 0;
  }                                  
  
  static int loopback_channels_get(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
924
  	mutex_lock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
925
926
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
1446c5fba   Jaroslav Kysela   ALSA: snd-aloop -...
927
  			       [kcontrol->id.device].channels;
76b3421b3   Takashi Iwai   ALSA: aloop: Add ...
928
  	mutex_unlock(&loopback->cable_lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
929
930
  	return 0;
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
931
  static struct snd_kcontrol_new loopback_controls[]  = {
597603d61   Jaroslav Kysela   ALSA: introduce t...
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
  {
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Rate Shift 100000",
  	.info =         loopback_rate_shift_info,
  	.get =          loopback_rate_shift_get,
  	.put =          loopback_rate_shift_put,
  },
  {
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Notify",
  	.info =         snd_ctl_boolean_mono_info,
  	.get =          loopback_notify_get,
  	.put =          loopback_notify_put,
  },
  #define ACTIVE_IDX 2
  {
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Slave Active",
  	.info =         snd_ctl_boolean_mono_info,
  	.get =          loopback_active_get,
  },
  #define FORMAT_IDX 3
  {
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Slave Format",
  	.info =         loopback_format_info,
  	.get =          loopback_format_get
  },
  #define RATE_IDX 4
  {
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Slave Rate",
  	.info =         loopback_rate_info,
  	.get =          loopback_rate_get
  },
  #define CHANNELS_IDX 5
  {
  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
  	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
  	.name =         "PCM Slave Channels",
  	.info =         loopback_channels_info,
  	.get =          loopback_channels_get
  }
  };
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
979
  static int loopback_mixer_new(struct loopback *loopback, int notify)
597603d61   Jaroslav Kysela   ALSA: introduce t...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
  {
  	struct snd_card *card = loopback->card;
  	struct snd_pcm *pcm;
  	struct snd_kcontrol *kctl;
  	struct loopback_setup *setup;
  	int err, dev, substr, substr_count, idx;
  
  	strcpy(card->mixername, "Loopback Mixer");
  	for (dev = 0; dev < 2; dev++) {
  		pcm = loopback->pcm[dev];
  		substr_count =
  		    pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
  		for (substr = 0; substr < substr_count; substr++) {
  			setup = &loopback->setup[substr][dev];
  			setup->notify = notify;
  			setup->rate_shift = NO_PITCH;
  			setup->format = SNDRV_PCM_FORMAT_S16_LE;
  			setup->rate = 48000;
  			setup->channels = 2;
  			for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
  									idx++) {
  				kctl = snd_ctl_new1(&loopback_controls[idx],
  						    loopback);
  				if (!kctl)
  					return -ENOMEM;
  				kctl->id.device = dev;
  				kctl->id.subdevice = substr;
  				switch (idx) {
  				case ACTIVE_IDX:
  					setup->active_id = kctl->id;
  					break;
  				case FORMAT_IDX:
  					setup->format_id = kctl->id;
  					break;
  				case RATE_IDX:
  					setup->rate_id = kctl->id;
  					break;
  				case CHANNELS_IDX:
  					setup->channels_id = kctl->id;
  					break;
  				default:
  					break;
  				}
  				err = snd_ctl_add(card, kctl);
  				if (err < 0)
  					return err;
  			}
  		}
  	}
  	return 0;
  }
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
  static void print_dpcm_info(struct snd_info_buffer *buffer,
  			    struct loopback_pcm *dpcm,
  			    const char *id)
  {
  	snd_iprintf(buffer, "  %s
  ", id);
  	if (dpcm == NULL) {
  		snd_iprintf(buffer, "    inactive
  ");
  		return;
  	}
  	snd_iprintf(buffer, "    buffer_size:\t%u
  ", dpcm->pcm_buffer_size);
  	snd_iprintf(buffer, "    buffer_pos:\t\t%u
  ", dpcm->buf_pos);
  	snd_iprintf(buffer, "    silent_size:\t%u
  ", dpcm->silent_size);
  	snd_iprintf(buffer, "    period_size:\t%u
  ", dpcm->pcm_period_size);
  	snd_iprintf(buffer, "    bytes_per_sec:\t%u
  ", dpcm->pcm_bps);
  	snd_iprintf(buffer, "    sample_align:\t%u
  ", dpcm->pcm_salign);
  	snd_iprintf(buffer, "    rate_shift:\t\t%u
  ", dpcm->pcm_rate_shift);
  	snd_iprintf(buffer, "    update_pending:\t%u
  ",
  						dpcm->period_update_pending);
  	snd_iprintf(buffer, "    irq_pos:\t\t%u
  ", dpcm->irq_pos);
  	snd_iprintf(buffer, "    period_frac:\t%u
  ", dpcm->period_size_frac);
  	snd_iprintf(buffer, "    last_jiffies:\t%lu (%lu)
  ",
  					dpcm->last_jiffies, jiffies);
  	snd_iprintf(buffer, "    timer_expires:\t%lu
  ", dpcm->timer.expires);
  }
  
  static void print_substream_info(struct snd_info_buffer *buffer,
  				 struct loopback *loopback,
  				 int sub,
  				 int num)
  {
  	struct loopback_cable *cable = loopback->cables[sub][num];
  
  	snd_iprintf(buffer, "Cable %i substream %i:
  ", num, sub);
  	if (cable == NULL) {
  		snd_iprintf(buffer, "  inactive
  ");
  		return;
  	}
  	snd_iprintf(buffer, "  valid: %u
  ", cable->valid);
  	snd_iprintf(buffer, "  running: %u
  ", cable->running);
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
1088
1089
  	snd_iprintf(buffer, "  pause: %u
  ", cable->pause);
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
  	print_dpcm_info(buffer, cable->streams[0], "Playback");
  	print_dpcm_info(buffer, cable->streams[1], "Capture");
  }
  
  static void print_cable_info(struct snd_info_entry *entry,
  			     struct snd_info_buffer *buffer)
  {
  	struct loopback *loopback = entry->private_data;
  	int sub, num;
  
  	mutex_lock(&loopback->cable_lock);
  	num = entry->name[strlen(entry->name)-1];
  	num = num == '0' ? 0 : 1;
  	for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++)
  		print_substream_info(buffer, loopback, sub, num);
  	mutex_unlock(&loopback->cable_lock);
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1107
  static int loopback_proc_new(struct loopback *loopback, int cidx)
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1108
1109
  {
  	char name[32];
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1110
1111
  
  	snprintf(name, sizeof(name), "cable#%d", cidx);
815d808c7   Takashi Iwai   ALSA: drivers: Cl...
1112
1113
  	return snd_card_ro_proc_new(loopback->card, name, loopback,
  				    print_cable_info);
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1114
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1115
  static int loopback_probe(struct platform_device *devptr)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1116
1117
1118
1119
1120
  {
  	struct snd_card *card;
  	struct loopback *loopback;
  	int dev = devptr->id;
  	int err;
5872f3f62   Takashi Iwai   ALSA: drivers: Co...
1121
1122
  	err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
  			   sizeof(struct loopback), &card);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
  	if (err < 0)
  		return err;
  	loopback = card->private_data;
  
  	if (pcm_substreams[dev] < 1)
  		pcm_substreams[dev] = 1;
  	if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
  		pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
  	
  	loopback->card = card;
  	mutex_init(&loopback->cable_lock);
  
  	err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
  	if (err < 0)
  		goto __nodev;
  	err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
  	if (err < 0)
  		goto __nodev;
  	err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
  	if (err < 0)
  		goto __nodev;
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1144
1145
  	loopback_proc_new(loopback, 0);
  	loopback_proc_new(loopback, 1);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
  	strcpy(card->driver, "Loopback");
  	strcpy(card->shortname, "Loopback");
  	sprintf(card->longname, "Loopback %i", dev + 1);
  	err = snd_card_register(card);
  	if (!err) {
  		platform_set_drvdata(devptr, card);
  		return 0;
  	}
        __nodev:
  	snd_card_free(card);
  	return err;
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1158
  static int loopback_remove(struct platform_device *devptr)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1159
1160
  {
  	snd_card_free(platform_get_drvdata(devptr));
597603d61   Jaroslav Kysela   ALSA: introduce t...
1161
1162
  	return 0;
  }
d34e4e00a   Takashi Iwai   ALSA: platform: C...
1163
  #ifdef CONFIG_PM_SLEEP
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1164
  static int loopback_suspend(struct device *pdev)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1165
  {
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1166
  	struct snd_card *card = dev_get_drvdata(pdev);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1167
1168
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1169
1170
1171
  	return 0;
  }
  	
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1172
  static int loopback_resume(struct device *pdev)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1173
  {
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1174
  	struct snd_card *card = dev_get_drvdata(pdev);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1175
1176
1177
1178
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
  	return 0;
  }
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1179
1180
1181
1182
1183
  
  static SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
  #define LOOPBACK_PM_OPS	&loopback_pm
  #else
  #define LOOPBACK_PM_OPS	NULL
597603d61   Jaroslav Kysela   ALSA: introduce t...
1184
1185
1186
1187
1188
1189
  #endif
  
  #define SND_LOOPBACK_DRIVER	"snd_aloop"
  
  static struct platform_driver loopback_driver = {
  	.probe		= loopback_probe,
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1190
  	.remove		= loopback_remove,
597603d61   Jaroslav Kysela   ALSA: introduce t...
1191
  	.driver		= {
8bf01d8ab   Takashi Iwai   ALSA: Add missing...
1192
  		.name	= SND_LOOPBACK_DRIVER,
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1193
  		.pm	= LOOPBACK_PM_OPS,
597603d61   Jaroslav Kysela   ALSA: introduce t...
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
  	},
  };
  
  static void loopback_unregister_all(void)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(devices); ++i)
  		platform_device_unregister(devices[i]);
  	platform_driver_unregister(&loopback_driver);
  }
  
  static int __init alsa_card_loopback_init(void)
  {
  	int i, err, cards;
  
  	err = platform_driver_register(&loopback_driver);
  	if (err < 0)
  		return err;
  
  
  	cards = 0;
  	for (i = 0; i < SNDRV_CARDS; i++) {
  		struct platform_device *device;
  		if (!enable[i])
  			continue;
  		device = platform_device_register_simple(SND_LOOPBACK_DRIVER,
  							 i, NULL, 0);
  		if (IS_ERR(device))
  			continue;
  		if (!platform_get_drvdata(device)) {
  			platform_device_unregister(device);
  			continue;
  		}
  		devices[i] = device;
  		cards++;
  	}
  	if (!cards) {
  #ifdef MODULE
  		printk(KERN_ERR "aloop: No loopback enabled
  ");
  #endif
  		loopback_unregister_all();
  		return -ENODEV;
  	}
  	return 0;
  }
  
  static void __exit alsa_card_loopback_exit(void)
  {
  	loopback_unregister_all();
  }
  
  module_init(alsa_card_loopback_init)
  module_exit(alsa_card_loopback_exit)