Blame view

sound/drivers/aloop.c 35.4 KB
597603d61   Jaroslav Kysela   ALSA: introduce t...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  /*
   *  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>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
  
  #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...
37
  #include <linux/module.h>
597603d61   Jaroslav Kysela   ALSA: introduce t...
38
39
40
41
  #include <linux/platform_device.h>
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
42
  #include <sound/info.h>
597603d61   Jaroslav Kysela   ALSA: introduce t...
43
44
45
46
47
48
49
50
51
52
53
  #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...
54
  static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
597603d61   Jaroslav Kysela   ALSA: introduce t...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  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 -...
80
  	unsigned int pause;
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
107
108
109
110
111
112
113
114
115
116
117
118
119
  };
  
  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 -...
120
  	unsigned int last_drift;
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
154
155
156
157
158
159
160
161
162
163
164
165
166
  	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...
167
  /* call in cable->lock */
597603d61   Jaroslav Kysela   ALSA: introduce t...
168
169
170
171
172
173
174
175
176
  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 -...
177
178
179
180
  	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...
181
182
  	tick = dpcm->period_size_frac - dpcm->irq_pos;
  	tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
db974553e   Takashi Iwai   ALSA: aloop: Use ...
183
  	mod_timer(&dpcm->timer, jiffies + tick);
597603d61   Jaroslav Kysela   ALSA: introduce t...
184
  }
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
185
  /* call in cable->lock */
597603d61   Jaroslav Kysela   ALSA: introduce t...
186
187
188
  static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
  {
  	del_timer(&dpcm->timer);
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
189
  	dpcm->timer.expires = 0;
597603d61   Jaroslav Kysela   ALSA: introduce t...
190
191
192
193
194
195
196
197
  }
  
  #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: ...
198
  	struct snd_pcm_runtime *runtime, *cruntime;
597603d61   Jaroslav Kysela   ALSA: introduce t...
199
200
201
202
203
204
205
206
207
208
209
  	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: ...
210
211
212
213
214
  	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...
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
248
249
250
251
252
253
254
255
256
257
  	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 -...
258
  	int err, stream = 1 << substream->stream;
597603d61   Jaroslav Kysela   ALSA: introduce t...
259
260
261
262
263
264
265
266
  
  	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 -...
267
  		dpcm->last_drift = 0;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
268
  		spin_lock(&cable->lock);	
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
269
270
  		cable->running |= stream;
  		cable->pause &= ~stream;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
271
  		loopback_timer_start(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
272
  		spin_unlock(&cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
273
274
275
276
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
277
  		spin_lock(&cable->lock);	
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
278
279
  		cable->running &= ~stream;
  		cable->pause &= ~stream;
597603d61   Jaroslav Kysela   ALSA: introduce t...
280
  		loopback_timer_stop(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
281
  		spin_unlock(&cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
282
283
284
  		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  			loopback_active_notify(dpcm);
  		break;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
285
  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
286
  	case SNDRV_PCM_TRIGGER_SUSPEND:
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
287
288
  		spin_lock(&cable->lock);	
  		cable->pause |= stream;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
289
  		loopback_timer_stop(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
290
  		spin_unlock(&cable->lock);
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
291
292
  		break;
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
293
  	case SNDRV_PCM_TRIGGER_RESUME:
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
294
295
296
  		spin_lock(&cable->lock);
  		dpcm->last_jiffies = jiffies;
  		cable->pause &= ~stream;
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
297
  		loopback_timer_start(dpcm);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
298
  		spin_unlock(&cable->lock);
5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
299
  		break;
597603d61   Jaroslav Kysela   ALSA: introduce t...
300
301
302
303
304
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  static void params_change_substream(struct loopback_pcm *dpcm,
  				    struct snd_pcm_runtime *runtime)
  {
  	struct snd_pcm_runtime *dst_runtime;
  
  	if (dpcm == NULL || dpcm->substream == NULL)
  		return;
  	dst_runtime = dpcm->substream->runtime;
  	if (dst_runtime == NULL)
  		return;
  	dst_runtime->hw = dpcm->cable->hw;
  }
  
  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_...
323
  	cable->hw.formats = pcm_format_to_bits(runtime->format);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
324
325
326
327
328
329
330
331
332
  	cable->hw.rate_min = runtime->rate;
  	cable->hw.rate_max = runtime->rate;
  	cable->hw.channels_min = runtime->channels;
  	cable->hw.channels_max = runtime->channels;
  	params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
  				runtime);
  	params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
  				runtime);
  }
597603d61   Jaroslav Kysela   ALSA: introduce t...
333
334
335
336
337
  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: ...
338
  	int bps, salign;
597603d61   Jaroslav Kysela   ALSA: introduce t...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  
  	salign = (snd_pcm_format_width(runtime->format) *
  						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: ...
362
363
364
365
  	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...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  	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 -...
403
  	char *src = runtime->dma_area;
597603d61   Jaroslav Kysela   ALSA: introduce t...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
  	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 -...
440
  	if (clear_bytes > 0) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
441
  		clear_capture_buf(capt, clear_bytes);
20d9a26db   Jaroslav Kysela   ALSA: snd-aloop -...
442
443
  		capt->silent_size = 0;
  	}
597603d61   Jaroslav Kysela   ALSA: introduce t...
444
  }
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
445
446
  static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
  					 unsigned int jiffies_delta)
597603d61   Jaroslav Kysela   ALSA: introduce t...
447
  {
597603d61   Jaroslav Kysela   ALSA: introduce t...
448
  	unsigned long last_pos;
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
449
  	unsigned int delta;
597603d61   Jaroslav Kysela   ALSA: introduce t...
450
451
  
  	last_pos = byte_pos(dpcm, dpcm->irq_pos);
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
452
453
454
455
456
  	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...
457
458
459
460
  	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 -...
461
462
463
464
465
466
467
468
  	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...
469
  }
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
470
  /* call in cable->lock */
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
471
  static unsigned int loopback_pos_update(struct loopback_cable *cable)
597603d61   Jaroslav Kysela   ALSA: introduce t...
472
473
474
475
476
477
  {
  	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 -...
478
  	unsigned int running, count1, count2;
597603d61   Jaroslav Kysela   ALSA: introduce t...
479

5de9e45fc   Jaroslav Kysela   ALSA: snd-aloop -...
480
  	running = cable->running ^ cable->pause;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
481
  	if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
482
483
484
  		delta_play = jiffies - dpcm_play->last_jiffies;
  		dpcm_play->last_jiffies += delta_play;
  	}
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
485
  	if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
486
487
488
  		delta_capt = jiffies - dpcm_capt->last_jiffies;
  		dpcm_capt->last_jiffies += delta_capt;
  	}
98d21df43   Takashi Iwai   ALSA: aloop - Fix...
489
490
  	if (delta_play == 0 && delta_capt == 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
491
492
  		
  	if (delta_play > delta_capt) {
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
493
494
  		count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
  		bytepos_finish(dpcm_play, count1);
597603d61   Jaroslav Kysela   ALSA: introduce t...
495
496
  		delta_play = delta_capt;
  	} else if (delta_play < delta_capt) {
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
497
498
499
  		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...
500
501
  		delta_capt = delta_play;
  	}
98d21df43   Takashi Iwai   ALSA: aloop - Fix...
502
503
  	if (delta_play == 0 && delta_capt == 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
504
  	/* note delta_capt == delta_play at this moment */
b012513c6   Jaroslav Kysela   ALSA: snd-aloop -...
505
506
507
508
509
510
511
512
513
514
515
  	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...
516
   unlock:
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
517
  	return running;
597603d61   Jaroslav Kysela   ALSA: introduce t...
518
519
520
521
522
  }
  
  static void loopback_timer_function(unsigned long data)
  {
  	struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
523
  	unsigned long flags;
597603d61   Jaroslav Kysela   ALSA: introduce t...
524

999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
525
526
  	spin_lock_irqsave(&dpcm->cable->lock, flags);
  	if (loopback_pos_update(dpcm->cable) & (1 << dpcm->substream->stream)) {
597603d61   Jaroslav Kysela   ALSA: introduce t...
527
  		loopback_timer_start(dpcm);
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
528
529
  		if (dpcm->period_update_pending) {
  			dpcm->period_update_pending = 0;
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
530
531
  			spin_unlock_irqrestore(&dpcm->cable->lock, flags);
  			/* need to unlock before calling below */
597603d61   Jaroslav Kysela   ALSA: introduce t...
532
  			snd_pcm_period_elapsed(dpcm->substream);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
533
  			return;
dd04bb12d   Jaroslav Kysela   ALSA: snd-aloop -...
534
  		}
597603d61   Jaroslav Kysela   ALSA: introduce t...
535
  	}
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
536
  	spin_unlock_irqrestore(&dpcm->cable->lock, flags);
597603d61   Jaroslav Kysela   ALSA: introduce t...
537
538
539
540
541
542
  }
  
  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...
543
  	snd_pcm_uframes_t pos;
597603d61   Jaroslav Kysela   ALSA: introduce t...
544

999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
545
  	spin_lock(&dpcm->cable->lock);
597603d61   Jaroslav Kysela   ALSA: introduce t...
546
  	loopback_pos_update(dpcm->cable);
999fc9f6a   Takashi Iwai   ALSA: aloop - Clo...
547
548
549
  	pos = dpcm->buf_pos;
  	spin_unlock(&dpcm->cable->lock);
  	return bytes_to_frames(runtime, pos);
597603d61   Jaroslav Kysela   ALSA: introduce t...
550
551
552
553
554
  }
  
  static struct snd_pcm_hardware loopback_pcm_hardware =
  {
  	.info =		(SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
edac89438   Takashi Iwai   ALSA: aloop: Fix ...
555
556
  			 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
  			 SNDRV_PCM_INFO_RESUME),
597603d61   Jaroslav Kysela   ALSA: introduce t...
557
558
559
560
561
562
563
564
565
566
  	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
  			 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 -...
567
568
569
  	/* 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...
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  	.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...
584
585
  	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
  						params_buffer_bytes(params));
597603d61   Jaroslav Kysela   ALSA: introduce t...
586
587
588
589
590
591
592
593
594
595
596
  }
  
  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...
597
  	return snd_pcm_lib_free_vmalloc_buffer(substream);
597603d61   Jaroslav Kysela   ALSA: introduce t...
598
599
600
601
602
603
604
605
606
  }
  
  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: ...
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
  static int rule_format(struct snd_pcm_hw_params *params,
  		       struct snd_pcm_hw_rule *rule)
  {
  
  	struct snd_pcm_hardware *hw = rule->private;
  	struct snd_mask *maskp = hw_param_mask(params, rule->var);
  
  	maskp->bits[0] &= (u_int32_t)hw->formats;
  	maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
  	memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
  	if (! maskp->bits[0] && ! maskp->bits[1])
  		return -EINVAL;
  	return 0;
  }
  
  static int rule_rate(struct snd_pcm_hw_params *params,
  		     struct snd_pcm_hw_rule *rule)
  {
  	struct snd_pcm_hardware *hw = rule->private;
  	struct snd_interval t;
  
          t.min = hw->rate_min;
          t.max = hw->rate_max;
          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)
  {
  	struct snd_pcm_hardware *hw = rule->private;
  	struct snd_interval t;
  
          t.min = hw->channels_min;
          t.max = hw->channels_max;
          t.openmin = t.openmax = 0;
          t.integer = 0;
  	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
  }
597603d61   Jaroslav Kysela   ALSA: introduce t...
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
  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;
  	struct loopback_cable *cable;
  	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;
  	setup_timer(&dpcm->timer, loopback_timer_function,
  		    (unsigned long)dpcm);
  
  	cable = loopback->cables[substream->number][dev];
  	if (!cable) {
  		cable = kzalloc(sizeof(*cable), GFP_KERNEL);
  		if (!cable) {
  			kfree(dpcm);
  			err = -ENOMEM;
  			goto unlock;
  		}
  		spin_lock_init(&cable->lock);
  		cable->hw = loopback_pcm_hardware;
  		loopback->cables[substream->number][dev] = cable;
  	}
  	dpcm->cable = cable;
  	cable->streams[substream->stream] = dpcm;
  
  	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  	/* 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,
  				  rule_format, &runtime->hw,
  				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
  	if (err < 0)
  		goto unlock;
  	err = snd_pcm_hw_rule_add(runtime, 0,
  				  SNDRV_PCM_HW_PARAM_RATE,
  				  rule_rate, &runtime->hw,
  				  SNDRV_PCM_HW_PARAM_RATE, -1);
  	if (err < 0)
  		goto unlock;
  	err = snd_pcm_hw_rule_add(runtime, 0,
  				  SNDRV_PCM_HW_PARAM_CHANNELS,
  				  rule_channels, &runtime->hw,
  				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
  	if (err < 0)
  		goto unlock;
597603d61   Jaroslav Kysela   ALSA: introduce t...
704
705
  	runtime->private_data = dpcm;
  	runtime->private_free = loopback_runtime_free;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
706
  	if (get_notify(dpcm))
597603d61   Jaroslav Kysela   ALSA: introduce t...
707
  		runtime->hw = loopback_pcm_hardware;
b1c73fc8e   Jaroslav Kysela   ALSA: snd-aloop: ...
708
  	else
597603d61   Jaroslav Kysela   ALSA: introduce t...
709
  		runtime->hw = cable->hw;
597603d61   Jaroslav Kysela   ALSA: introduce t...
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
   unlock:
  	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;
  	struct loopback_cable *cable;
  	int dev = get_cable_index(substream);
  
  	loopback_timer_stop(dpcm);
  	mutex_lock(&loopback->cable_lock);
  	cable = loopback->cables[substream->number][dev];
  	if (cable->streams[!substream->stream]) {
  		/* other stream is still alive */
  		cable->streams[substream->stream] = NULL;
  	} else {
  		/* free the cable */
  		loopback->cables[substream->number][dev] = NULL;
  		kfree(cable);
  	}
  	mutex_unlock(&loopback->cable_lock);
  	return 0;
  }
  
  static struct snd_pcm_ops loopback_playback_ops = {
  	.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...
746
747
  	.page =		snd_pcm_lib_get_vmalloc_page,
  	.mmap =		snd_pcm_lib_mmap_vmalloc,
597603d61   Jaroslav Kysela   ALSA: introduce t...
748
749
750
751
752
753
754
755
756
757
758
  };
  
  static struct snd_pcm_ops loopback_capture_ops = {
  	.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...
759
760
  	.page =		snd_pcm_lib_get_vmalloc_page,
  	.mmap =		snd_pcm_lib_mmap_vmalloc,
597603d61   Jaroslav Kysela   ALSA: introduce t...
761
  };
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
762
763
  static int loopback_pcm_new(struct loopback *loopback,
  			    int device, int substreams)
597603d61   Jaroslav Kysela   ALSA: introduce t...
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
  {
  	struct snd_pcm *pcm;
  	int err;
  
  	err = snd_pcm_new(loopback->card, "Loopback PCM", device,
  			  substreams, substreams, &pcm);
  	if (err < 0)
  		return err;
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
  
  	pcm->private_data = loopback;
  	pcm->info_flags = 0;
  	strcpy(pcm->name, "Loopback PCM");
  
  	loopback->pcm[device] = pcm;
597603d61   Jaroslav Kysela   ALSA: introduce t...
780
781
782
783
784
785
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
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
  	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);
  	
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].rate_shift;
  	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);
  	
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].notify;
  	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;
  	if (val != loopback->setup[kcontrol->id.subdevice]
  				[kcontrol->id.device].notify) {
  		loopback->setup[kcontrol->id.subdevice]
  			[kcontrol->id.device].notify = val;
  		change = 1;
  	}
  	return change;
  }
  
  static int loopback_active_get(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_value *ucontrol)
  {
  	struct loopback *loopback = snd_kcontrol_chip(kcontrol);
  	struct loopback_cable *cable = loopback->cables
ac446fb7e   Jaroslav Kysela   ALSA: snd-aloop -...
861
  			[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
  	unsigned int val = 0;
  
  	if (cable != NULL)
  		val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
  									1 : 0;
  	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);
  	
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
  			       [kcontrol->id.device].rate;
  	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);
  	
  	ucontrol->value.integer.value[0] =
  		loopback->setup[kcontrol->id.subdevice]
1446c5fba   Jaroslav Kysela   ALSA: snd-aloop -...
933
  			       [kcontrol->id.device].channels;
597603d61   Jaroslav Kysela   ALSA: introduce t...
934
935
  	return 0;
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
936
  static struct snd_kcontrol_new loopback_controls[]  = {
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
979
980
981
982
983
  {
  	.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...
984
  static int loopback_mixer_new(struct loopback *loopback, int notify)
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
1031
1032
1033
1034
1035
  {
  	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: ...
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
1088
1089
1090
1091
1092
  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 -...
1093
1094
  	snd_iprintf(buffer, "  pause: %u
  ", cable->pause);
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
  	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...
1112
  static int loopback_proc_new(struct loopback *loopback, int cidx)
e74670b6f   Jaroslav Kysela   ALSA: snd-aloop: ...
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
  {
  	char name[32];
  	struct snd_info_entry *entry;
  	int err;
  
  	snprintf(name, sizeof(name), "cable#%d", cidx);
  	err = snd_card_proc_new(loopback->card, name, &entry);
  	if (err < 0)
  		return err;
  
  	snd_info_set_text_ops(entry, loopback, print_cable_info);
  	return 0;
  }
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1126
  static int loopback_probe(struct platform_device *devptr)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1127
1128
1129
1130
1131
  {
  	struct snd_card *card;
  	struct loopback *loopback;
  	int dev = devptr->id;
  	int err;
5872f3f62   Takashi Iwai   ALSA: drivers: Co...
1132
1133
  	err = snd_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
  			   sizeof(struct loopback), &card);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
  	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: ...
1155
1156
  	loopback_proc_new(loopback, 0);
  	loopback_proc_new(loopback, 1);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
  	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...
1169
  static int loopback_remove(struct platform_device *devptr)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1170
1171
  {
  	snd_card_free(platform_get_drvdata(devptr));
597603d61   Jaroslav Kysela   ALSA: introduce t...
1172
1173
  	return 0;
  }
d34e4e00a   Takashi Iwai   ALSA: platform: C...
1174
  #ifdef CONFIG_PM_SLEEP
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1175
  static int loopback_suspend(struct device *pdev)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1176
  {
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1177
  	struct snd_card *card = dev_get_drvdata(pdev);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1178
1179
1180
1181
1182
1183
1184
1185
1186
  	struct loopback *loopback = card->private_data;
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
  
  	snd_pcm_suspend_all(loopback->pcm[0]);
  	snd_pcm_suspend_all(loopback->pcm[1]);
  	return 0;
  }
  	
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1187
  static int loopback_resume(struct device *pdev)
597603d61   Jaroslav Kysela   ALSA: introduce t...
1188
  {
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1189
  	struct snd_card *card = dev_get_drvdata(pdev);
597603d61   Jaroslav Kysela   ALSA: introduce t...
1190
1191
1192
1193
  
  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
  	return 0;
  }
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1194
1195
1196
1197
1198
  
  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...
1199
1200
1201
1202
1203
1204
  #endif
  
  #define SND_LOOPBACK_DRIVER	"snd_aloop"
  
  static struct platform_driver loopback_driver = {
  	.probe		= loopback_probe,
fbbb01a12   Bill Pemberton   ALSA: drivers: re...
1205
  	.remove		= loopback_remove,
597603d61   Jaroslav Kysela   ALSA: introduce t...
1206
  	.driver		= {
8bf01d8ab   Takashi Iwai   ALSA: Add missing...
1207
  		.name	= SND_LOOPBACK_DRIVER,
284e7ca75   Takashi Iwai   ALSA: convert PM ...
1208
  		.pm	= LOOPBACK_PM_OPS,
597603d61   Jaroslav Kysela   ALSA: introduce t...
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
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
  	},
  };
  
  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)