| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  *  Loopback soundcard | 
 | 3 |  * | 
 | 4 |  *  Original code: | 
 | 5 |  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> | 
 | 6 |  * | 
 | 7 |  *  More accurate positioning and full-duplex support: | 
 | 8 |  *  Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de> | 
 | 9 |  * | 
 | 10 |  *  Major (almost complete) rewrite: | 
 | 11 |  *  Copyright (c) by Takashi Iwai <tiwai@suse.de> | 
 | 12 |  * | 
 | 13 |  *  A next major update in 2010 (separate timers for playback and capture): | 
 | 14 |  *  Copyright (c) Jaroslav Kysela <perex@perex.cz> | 
 | 15 |  * | 
 | 16 |  *   This program is free software; you can redistribute it and/or modify | 
 | 17 |  *   it under the terms of the GNU General Public License as published by | 
 | 18 |  *   the Free Software Foundation; either version 2 of the License, or | 
 | 19 |  *   (at your option) any later version. | 
 | 20 |  * | 
 | 21 |  *   This program is distributed in the hope that it will be useful, | 
 | 22 |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 23 |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 24 |  *   GNU General Public License for more details. | 
 | 25 |  * | 
 | 26 |  *   You should have received a copy of the GNU General Public License | 
 | 27 |  *   along with this program; if not, write to the Free Software | 
 | 28 |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
 | 29 |  * | 
 | 30 |  */ | 
 | 31 |  | 
 | 32 | #include <linux/init.h> | 
 | 33 | #include <linux/jiffies.h> | 
 | 34 | #include <linux/slab.h> | 
 | 35 | #include <linux/time.h> | 
 | 36 | #include <linux/wait.h> | 
 | 37 | #include <linux/moduleparam.h> | 
 | 38 | #include <linux/platform_device.h> | 
 | 39 | #include <sound/core.h> | 
 | 40 | #include <sound/control.h> | 
 | 41 | #include <sound/pcm.h> | 
| Jaroslav Kysela | e74670b | 2010-10-18 09:43:10 +0200 | [diff] [blame] | 42 | #include <sound/info.h> | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 43 | #include <sound/initval.h> | 
 | 44 |  | 
 | 45 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 
 | 46 | MODULE_DESCRIPTION("A loopback soundcard"); | 
 | 47 | MODULE_LICENSE("GPL"); | 
 | 48 | MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}"); | 
 | 49 |  | 
 | 50 | #define MAX_PCM_SUBSTREAMS	8 | 
 | 51 |  | 
 | 52 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */ | 
 | 53 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */ | 
 | 54 | static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | 
 | 55 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | 
 | 56 | static int pcm_notify[SNDRV_CARDS]; | 
 | 57 |  | 
 | 58 | module_param_array(index, int, NULL, 0444); | 
 | 59 | MODULE_PARM_DESC(index, "Index value for loopback soundcard."); | 
 | 60 | module_param_array(id, charp, NULL, 0444); | 
 | 61 | MODULE_PARM_DESC(id, "ID string for loopback soundcard."); | 
 | 62 | module_param_array(enable, bool, NULL, 0444); | 
 | 63 | MODULE_PARM_DESC(enable, "Enable this loopback soundcard."); | 
 | 64 | module_param_array(pcm_substreams, int, NULL, 0444); | 
 | 65 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver."); | 
 | 66 | module_param_array(pcm_notify, int, NULL, 0444); | 
 | 67 | MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes."); | 
 | 68 |  | 
 | 69 | #define NO_PITCH 100000 | 
 | 70 |  | 
 | 71 | struct loopback_pcm; | 
 | 72 |  | 
 | 73 | struct loopback_cable { | 
 | 74 | 	spinlock_t lock; | 
 | 75 | 	struct loopback_pcm *streams[2]; | 
 | 76 | 	struct snd_pcm_hardware hw; | 
 | 77 | 	/* flags */ | 
 | 78 | 	unsigned int valid; | 
 | 79 | 	unsigned int running; | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 80 | 	unsigned int pause; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 81 | }; | 
 | 82 |  | 
 | 83 | struct loopback_setup { | 
 | 84 | 	unsigned int notify: 1; | 
 | 85 | 	unsigned int rate_shift; | 
 | 86 | 	unsigned int format; | 
 | 87 | 	unsigned int rate; | 
 | 88 | 	unsigned int channels; | 
 | 89 | 	struct snd_ctl_elem_id active_id; | 
 | 90 | 	struct snd_ctl_elem_id format_id; | 
 | 91 | 	struct snd_ctl_elem_id rate_id; | 
 | 92 | 	struct snd_ctl_elem_id channels_id; | 
 | 93 | }; | 
 | 94 |  | 
 | 95 | struct loopback { | 
 | 96 | 	struct snd_card *card; | 
 | 97 | 	struct mutex cable_lock; | 
 | 98 | 	struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2]; | 
 | 99 | 	struct snd_pcm *pcm[2]; | 
 | 100 | 	struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2]; | 
 | 101 | }; | 
 | 102 |  | 
 | 103 | struct loopback_pcm { | 
 | 104 | 	struct loopback *loopback; | 
 | 105 | 	struct snd_pcm_substream *substream; | 
 | 106 | 	struct loopback_cable *cable; | 
 | 107 | 	unsigned int pcm_buffer_size; | 
 | 108 | 	unsigned int buf_pos;	/* position in buffer */ | 
 | 109 | 	unsigned int silent_size; | 
 | 110 | 	/* PCM parameters */ | 
 | 111 | 	unsigned int pcm_period_size; | 
 | 112 | 	unsigned int pcm_bps;		/* bytes per second */ | 
 | 113 | 	unsigned int pcm_salign;	/* bytes per sample * channels */ | 
 | 114 | 	unsigned int pcm_rate_shift;	/* rate shift value */ | 
 | 115 | 	/* flags */ | 
 | 116 | 	unsigned int period_update_pending :1; | 
 | 117 | 	/* timer stuff */ | 
 | 118 | 	unsigned int irq_pos;		/* fractional IRQ position */ | 
 | 119 | 	unsigned int period_size_frac; | 
 | 120 | 	unsigned long last_jiffies; | 
 | 121 | 	struct timer_list timer; | 
 | 122 | }; | 
 | 123 |  | 
 | 124 | static struct platform_device *devices[SNDRV_CARDS]; | 
 | 125 |  | 
 | 126 | static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x) | 
 | 127 | { | 
 | 128 | 	if (dpcm->pcm_rate_shift == NO_PITCH) { | 
 | 129 | 		x /= HZ; | 
 | 130 | 	} else { | 
 | 131 | 		x = div_u64(NO_PITCH * (unsigned long long)x, | 
 | 132 | 			    HZ * (unsigned long long)dpcm->pcm_rate_shift); | 
 | 133 | 	} | 
 | 134 | 	return x - (x % dpcm->pcm_salign); | 
 | 135 | } | 
 | 136 |  | 
 | 137 | static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x) | 
 | 138 | { | 
 | 139 | 	if (dpcm->pcm_rate_shift == NO_PITCH) {	/* no pitch */ | 
 | 140 | 		return x * HZ; | 
 | 141 | 	} else { | 
 | 142 | 		x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ, | 
 | 143 | 			    NO_PITCH); | 
 | 144 | 	} | 
 | 145 | 	return x; | 
 | 146 | } | 
 | 147 |  | 
 | 148 | static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm) | 
 | 149 | { | 
 | 150 | 	int device = dpcm->substream->pstr->pcm->device; | 
 | 151 | 	 | 
 | 152 | 	if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 153 | 		device ^= 1; | 
 | 154 | 	return &dpcm->loopback->setup[dpcm->substream->number][device]; | 
 | 155 | } | 
 | 156 |  | 
 | 157 | static inline unsigned int get_notify(struct loopback_pcm *dpcm) | 
 | 158 | { | 
 | 159 | 	return get_setup(dpcm)->notify; | 
 | 160 | } | 
 | 161 |  | 
 | 162 | static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm) | 
 | 163 | { | 
 | 164 | 	return get_setup(dpcm)->rate_shift; | 
 | 165 | } | 
 | 166 |  | 
 | 167 | static void loopback_timer_start(struct loopback_pcm *dpcm) | 
 | 168 | { | 
 | 169 | 	unsigned long tick; | 
 | 170 | 	unsigned int rate_shift = get_rate_shift(dpcm); | 
 | 171 |  | 
 | 172 | 	if (rate_shift != dpcm->pcm_rate_shift) { | 
 | 173 | 		dpcm->pcm_rate_shift = rate_shift; | 
 | 174 | 		dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size); | 
 | 175 | 	} | 
| Jaroslav Kysela | 0db7102 | 2010-10-14 21:46:12 +0200 | [diff] [blame] | 176 | 	if (dpcm->period_size_frac <= dpcm->irq_pos) { | 
 | 177 | 		dpcm->irq_pos %= dpcm->period_size_frac; | 
 | 178 | 		dpcm->period_update_pending = 1; | 
 | 179 | 	} | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 180 | 	tick = dpcm->period_size_frac - dpcm->irq_pos; | 
 | 181 | 	tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps; | 
 | 182 | 	dpcm->timer.expires = jiffies + tick; | 
 | 183 | 	add_timer(&dpcm->timer); | 
 | 184 | } | 
 | 185 |  | 
 | 186 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | 
 | 187 | { | 
 | 188 | 	del_timer(&dpcm->timer); | 
| Jaroslav Kysela | e74670b | 2010-10-18 09:43:10 +0200 | [diff] [blame] | 189 | 	dpcm->timer.expires = 0; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 190 | } | 
 | 191 |  | 
 | 192 | #define CABLE_VALID_PLAYBACK	(1 << SNDRV_PCM_STREAM_PLAYBACK) | 
 | 193 | #define CABLE_VALID_CAPTURE	(1 << SNDRV_PCM_STREAM_CAPTURE) | 
 | 194 | #define CABLE_VALID_BOTH	(CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE) | 
 | 195 |  | 
 | 196 | static int loopback_check_format(struct loopback_cable *cable, int stream) | 
 | 197 | { | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 198 | 	struct snd_pcm_runtime *runtime, *cruntime; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 199 | 	struct loopback_setup *setup; | 
 | 200 | 	struct snd_card *card; | 
 | 201 | 	int check; | 
 | 202 |  | 
 | 203 | 	if (cable->valid != CABLE_VALID_BOTH) { | 
 | 204 | 		if (stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 205 | 			goto __notify; | 
 | 206 | 		return 0; | 
 | 207 | 	} | 
 | 208 | 	runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> | 
 | 209 | 							substream->runtime; | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 210 | 	cruntime = cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> | 
 | 211 | 							substream->runtime; | 
 | 212 | 	check = runtime->format != cruntime->format || | 
 | 213 | 		runtime->rate != cruntime->rate || | 
 | 214 | 		runtime->channels != cruntime->channels; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 215 | 	if (!check) | 
 | 216 | 		return 0; | 
 | 217 | 	if (stream == SNDRV_PCM_STREAM_CAPTURE) { | 
 | 218 | 		return -EIO; | 
 | 219 | 	} else { | 
 | 220 | 		snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> | 
 | 221 | 					substream, SNDRV_PCM_STATE_DRAINING); | 
 | 222 | 	      __notify: | 
 | 223 | 		runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> | 
 | 224 | 							substream->runtime; | 
 | 225 | 		setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]); | 
 | 226 | 		card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card; | 
 | 227 | 		if (setup->format != runtime->format) { | 
 | 228 | 			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | 
 | 229 | 							&setup->format_id); | 
 | 230 | 			setup->format = runtime->format; | 
 | 231 | 		} | 
 | 232 | 		if (setup->rate != runtime->rate) { | 
 | 233 | 			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | 
 | 234 | 							&setup->rate_id); | 
 | 235 | 			setup->rate = runtime->rate; | 
 | 236 | 		} | 
 | 237 | 		if (setup->channels != runtime->channels) { | 
 | 238 | 			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, | 
 | 239 | 							&setup->channels_id); | 
 | 240 | 			setup->channels = runtime->channels; | 
 | 241 | 		} | 
 | 242 | 	} | 
 | 243 | 	return 0; | 
 | 244 | } | 
 | 245 |  | 
 | 246 | static void loopback_active_notify(struct loopback_pcm *dpcm) | 
 | 247 | { | 
 | 248 | 	snd_ctl_notify(dpcm->loopback->card, | 
 | 249 | 		       SNDRV_CTL_EVENT_MASK_VALUE, | 
 | 250 | 		       &get_setup(dpcm)->active_id); | 
 | 251 | } | 
 | 252 |  | 
 | 253 | static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | 
 | 254 | { | 
 | 255 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 256 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 257 | 	struct loopback_cable *cable = dpcm->cable; | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 258 | 	int err, stream = 1 << substream->stream; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 259 |  | 
 | 260 | 	switch (cmd) { | 
 | 261 | 	case SNDRV_PCM_TRIGGER_START: | 
 | 262 | 		err = loopback_check_format(cable, substream->stream); | 
 | 263 | 		if (err < 0) | 
 | 264 | 			return err; | 
 | 265 | 		dpcm->last_jiffies = jiffies; | 
 | 266 | 		dpcm->pcm_rate_shift = 0; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 267 | 		spin_lock(&cable->lock);	 | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 268 | 		cable->running |= stream; | 
 | 269 | 		cable->pause &= ~stream; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 270 | 		spin_unlock(&cable->lock); | 
 | 271 | 		loopback_timer_start(dpcm); | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 272 | 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 273 | 			loopback_active_notify(dpcm); | 
 | 274 | 		break; | 
 | 275 | 	case SNDRV_PCM_TRIGGER_STOP: | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 276 | 		spin_lock(&cable->lock);	 | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 277 | 		cable->running &= ~stream; | 
 | 278 | 		cable->pause &= ~stream; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 279 | 		spin_unlock(&cable->lock); | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 280 | 		loopback_timer_stop(dpcm); | 
 | 281 | 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
 | 282 | 			loopback_active_notify(dpcm); | 
 | 283 | 		break; | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 284 | 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
 | 285 | 		spin_lock(&cable->lock);	 | 
 | 286 | 		cable->pause |= stream; | 
 | 287 | 		spin_unlock(&cable->lock); | 
 | 288 | 		loopback_timer_stop(dpcm); | 
 | 289 | 		break; | 
 | 290 | 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
 | 291 | 		spin_lock(&cable->lock); | 
 | 292 | 		dpcm->last_jiffies = jiffies; | 
 | 293 | 		cable->pause &= ~stream; | 
 | 294 | 		spin_unlock(&cable->lock); | 
 | 295 | 		loopback_timer_start(dpcm); | 
 | 296 | 		break; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 297 | 	default: | 
 | 298 | 		return -EINVAL; | 
 | 299 | 	} | 
 | 300 | 	return 0; | 
 | 301 | } | 
 | 302 |  | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 303 | static void params_change_substream(struct loopback_pcm *dpcm, | 
 | 304 | 				    struct snd_pcm_runtime *runtime) | 
 | 305 | { | 
 | 306 | 	struct snd_pcm_runtime *dst_runtime; | 
 | 307 |  | 
 | 308 | 	if (dpcm == NULL || dpcm->substream == NULL) | 
 | 309 | 		return; | 
 | 310 | 	dst_runtime = dpcm->substream->runtime; | 
 | 311 | 	if (dst_runtime == NULL) | 
 | 312 | 		return; | 
 | 313 | 	dst_runtime->hw = dpcm->cable->hw; | 
 | 314 | } | 
 | 315 |  | 
 | 316 | static void params_change(struct snd_pcm_substream *substream) | 
 | 317 | { | 
 | 318 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 319 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 320 | 	struct loopback_cable *cable = dpcm->cable; | 
 | 321 |  | 
 | 322 | 	cable->hw.formats = (1ULL << runtime->format); | 
 | 323 | 	cable->hw.rate_min = runtime->rate; | 
 | 324 | 	cable->hw.rate_max = runtime->rate; | 
 | 325 | 	cable->hw.channels_min = runtime->channels; | 
 | 326 | 	cable->hw.channels_max = runtime->channels; | 
 | 327 | 	params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK], | 
 | 328 | 				runtime); | 
 | 329 | 	params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE], | 
 | 330 | 				runtime); | 
 | 331 | } | 
 | 332 |  | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 333 | static int loopback_prepare(struct snd_pcm_substream *substream) | 
 | 334 | { | 
 | 335 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 336 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 337 | 	struct loopback_cable *cable = dpcm->cable; | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 338 | 	int bps, salign; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 339 |  | 
 | 340 | 	salign = (snd_pcm_format_width(runtime->format) * | 
 | 341 | 						runtime->channels) / 8; | 
 | 342 | 	bps = salign * runtime->rate; | 
 | 343 | 	if (bps <= 0 || salign <= 0) | 
 | 344 | 		return -EINVAL; | 
 | 345 |  | 
 | 346 | 	dpcm->buf_pos = 0; | 
 | 347 | 	dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size); | 
 | 348 | 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | 
 | 349 | 		/* clear capture buffer */ | 
 | 350 | 		dpcm->silent_size = dpcm->pcm_buffer_size; | 
 | 351 | 		snd_pcm_format_set_silence(runtime->format, runtime->dma_area, | 
 | 352 | 					   runtime->buffer_size * runtime->channels); | 
 | 353 | 	} | 
 | 354 |  | 
 | 355 | 	dpcm->irq_pos = 0; | 
 | 356 | 	dpcm->period_update_pending = 0; | 
 | 357 | 	dpcm->pcm_bps = bps; | 
 | 358 | 	dpcm->pcm_salign = salign; | 
 | 359 | 	dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size); | 
 | 360 |  | 
 | 361 | 	mutex_lock(&dpcm->loopback->cable_lock); | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 362 | 	if (!(cable->valid & ~(1 << substream->stream)) || | 
 | 363 |             (get_setup(dpcm)->notify && | 
 | 364 | 	     substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) | 
 | 365 | 		params_change(substream); | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 366 | 	cable->valid |= 1 << substream->stream; | 
 | 367 | 	mutex_unlock(&dpcm->loopback->cable_lock); | 
 | 368 |  | 
 | 369 | 	return 0; | 
 | 370 | } | 
 | 371 |  | 
 | 372 | static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes) | 
 | 373 | { | 
 | 374 | 	struct snd_pcm_runtime *runtime = dpcm->substream->runtime; | 
 | 375 | 	char *dst = runtime->dma_area; | 
 | 376 | 	unsigned int dst_off = dpcm->buf_pos; | 
 | 377 |  | 
 | 378 | 	if (dpcm->silent_size >= dpcm->pcm_buffer_size) | 
 | 379 | 		return; | 
 | 380 | 	if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size) | 
 | 381 | 		bytes = dpcm->pcm_buffer_size - dpcm->silent_size; | 
 | 382 |  | 
 | 383 | 	for (;;) { | 
 | 384 | 		unsigned int size = bytes; | 
 | 385 | 		if (dst_off + size > dpcm->pcm_buffer_size) | 
 | 386 | 			size = dpcm->pcm_buffer_size - dst_off; | 
 | 387 | 		snd_pcm_format_set_silence(runtime->format, dst + dst_off, | 
 | 388 | 					   bytes_to_frames(runtime, size) * | 
 | 389 | 					   	runtime->channels); | 
 | 390 | 		dpcm->silent_size += size; | 
 | 391 | 		bytes -= size; | 
 | 392 | 		if (!bytes) | 
 | 393 | 			break; | 
 | 394 | 		dst_off = 0; | 
 | 395 | 	} | 
 | 396 | } | 
 | 397 |  | 
 | 398 | static void copy_play_buf(struct loopback_pcm *play, | 
 | 399 | 			  struct loopback_pcm *capt, | 
 | 400 | 			  unsigned int bytes) | 
 | 401 | { | 
 | 402 | 	struct snd_pcm_runtime *runtime = play->substream->runtime; | 
| Jaroslav Kysela | 20d9a26 | 2010-09-30 00:16:50 +0200 | [diff] [blame] | 403 | 	char *src = runtime->dma_area; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 404 | 	char *dst = capt->substream->runtime->dma_area; | 
 | 405 | 	unsigned int src_off = play->buf_pos; | 
 | 406 | 	unsigned int dst_off = capt->buf_pos; | 
 | 407 | 	unsigned int clear_bytes = 0; | 
 | 408 |  | 
 | 409 | 	/* check if playback is draining, trim the capture copy size | 
 | 410 | 	 * when our pointer is at the end of playback ring buffer */ | 
 | 411 | 	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && | 
 | 412 | 	    snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {  | 
 | 413 | 	    	snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; | 
 | 414 | 		appl_ptr = appl_ptr1 = runtime->control->appl_ptr; | 
 | 415 | 		appl_ptr1 -= appl_ptr1 % runtime->buffer_size; | 
 | 416 | 		appl_ptr1 += play->buf_pos / play->pcm_salign; | 
 | 417 | 		if (appl_ptr < appl_ptr1) | 
 | 418 | 			appl_ptr1 -= runtime->buffer_size; | 
 | 419 | 		diff = (appl_ptr - appl_ptr1) * play->pcm_salign; | 
 | 420 | 		if (diff < bytes) { | 
 | 421 | 			clear_bytes = bytes - diff; | 
 | 422 | 			bytes = diff; | 
 | 423 | 		} | 
 | 424 | 	} | 
 | 425 |  | 
 | 426 | 	for (;;) { | 
 | 427 | 		unsigned int size = bytes; | 
 | 428 | 		if (src_off + size > play->pcm_buffer_size) | 
 | 429 | 			size = play->pcm_buffer_size - src_off; | 
 | 430 | 		if (dst_off + size > capt->pcm_buffer_size) | 
 | 431 | 			size = capt->pcm_buffer_size - dst_off; | 
 | 432 | 		memcpy(dst + dst_off, src + src_off, size); | 
 | 433 | 		capt->silent_size = 0; | 
 | 434 | 		bytes -= size; | 
 | 435 | 		if (!bytes) | 
 | 436 | 			break; | 
 | 437 | 		src_off = (src_off + size) % play->pcm_buffer_size; | 
 | 438 | 		dst_off = (dst_off + size) % capt->pcm_buffer_size; | 
 | 439 | 	} | 
 | 440 |  | 
| Jaroslav Kysela | 20d9a26 | 2010-09-30 00:16:50 +0200 | [diff] [blame] | 441 | 	if (clear_bytes > 0) { | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 442 | 		clear_capture_buf(capt, clear_bytes); | 
| Jaroslav Kysela | 20d9a26 | 2010-09-30 00:16:50 +0200 | [diff] [blame] | 443 | 		capt->silent_size = 0; | 
 | 444 | 	} | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 445 | } | 
 | 446 |  | 
 | 447 | #define BYTEPOS_UPDATE_POSONLY	0 | 
 | 448 | #define BYTEPOS_UPDATE_CLEAR	1 | 
 | 449 | #define BYTEPOS_UPDATE_COPY	2 | 
 | 450 |  | 
 | 451 | static void loopback_bytepos_update(struct loopback_pcm *dpcm, | 
 | 452 | 				    unsigned int delta, | 
 | 453 | 				    unsigned int cmd) | 
 | 454 | { | 
 | 455 | 	unsigned int count; | 
 | 456 | 	unsigned long last_pos; | 
 | 457 |  | 
 | 458 | 	last_pos = byte_pos(dpcm, dpcm->irq_pos); | 
 | 459 | 	dpcm->irq_pos += delta * dpcm->pcm_bps; | 
 | 460 | 	count = byte_pos(dpcm, dpcm->irq_pos) - last_pos; | 
 | 461 | 	if (!count) | 
 | 462 | 		return; | 
 | 463 | 	if (cmd == BYTEPOS_UPDATE_CLEAR) | 
 | 464 | 		clear_capture_buf(dpcm, count); | 
 | 465 | 	else if (cmd == BYTEPOS_UPDATE_COPY) | 
 | 466 | 		copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK], | 
 | 467 | 			      dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE], | 
 | 468 | 			      count); | 
 | 469 | 	dpcm->buf_pos += count; | 
 | 470 | 	dpcm->buf_pos %= dpcm->pcm_buffer_size; | 
 | 471 | 	if (dpcm->irq_pos >= dpcm->period_size_frac) { | 
 | 472 | 		dpcm->irq_pos %= dpcm->period_size_frac; | 
 | 473 | 		dpcm->period_update_pending = 1; | 
 | 474 | 	} | 
 | 475 | } | 
 | 476 |  | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 477 | static unsigned int loopback_pos_update(struct loopback_cable *cable) | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 478 | { | 
 | 479 | 	struct loopback_pcm *dpcm_play = | 
 | 480 | 			cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; | 
 | 481 | 	struct loopback_pcm *dpcm_capt = | 
 | 482 | 			cable->streams[SNDRV_PCM_STREAM_CAPTURE]; | 
 | 483 | 	unsigned long delta_play = 0, delta_capt = 0; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 484 | 	unsigned int running; | 
| Takashi Iwai | 98d21df | 2011-03-18 07:31:53 +0100 | [diff] [blame] | 485 | 	unsigned long flags; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 486 |  | 
| Takashi Iwai | 98d21df | 2011-03-18 07:31:53 +0100 | [diff] [blame] | 487 | 	spin_lock_irqsave(&cable->lock, flags); | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 488 | 	running = cable->running ^ cable->pause; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 489 | 	if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 490 | 		delta_play = jiffies - dpcm_play->last_jiffies; | 
 | 491 | 		dpcm_play->last_jiffies += delta_play; | 
 | 492 | 	} | 
 | 493 |  | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 494 | 	if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 495 | 		delta_capt = jiffies - dpcm_capt->last_jiffies; | 
 | 496 | 		dpcm_capt->last_jiffies += delta_capt; | 
 | 497 | 	} | 
 | 498 |  | 
| Takashi Iwai | 98d21df | 2011-03-18 07:31:53 +0100 | [diff] [blame] | 499 | 	if (delta_play == 0 && delta_capt == 0) | 
 | 500 | 		goto unlock; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 501 | 		 | 
 | 502 | 	if (delta_play > delta_capt) { | 
 | 503 | 		loopback_bytepos_update(dpcm_play, delta_play - delta_capt, | 
 | 504 | 					BYTEPOS_UPDATE_POSONLY); | 
 | 505 | 		delta_play = delta_capt; | 
 | 506 | 	} else if (delta_play < delta_capt) { | 
 | 507 | 		loopback_bytepos_update(dpcm_capt, delta_capt - delta_play, | 
 | 508 | 					BYTEPOS_UPDATE_CLEAR); | 
 | 509 | 		delta_capt = delta_play; | 
 | 510 | 	} | 
 | 511 |  | 
| Takashi Iwai | 98d21df | 2011-03-18 07:31:53 +0100 | [diff] [blame] | 512 | 	if (delta_play == 0 && delta_capt == 0) | 
 | 513 | 		goto unlock; | 
 | 514 |  | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 515 | 	/* note delta_capt == delta_play at this moment */ | 
 | 516 | 	loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); | 
 | 517 | 	loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); | 
| Takashi Iwai | 98d21df | 2011-03-18 07:31:53 +0100 | [diff] [blame] | 518 |  unlock: | 
 | 519 | 	spin_unlock_irqrestore(&cable->lock, flags); | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 520 | 	return running; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 521 | } | 
 | 522 |  | 
 | 523 | static void loopback_timer_function(unsigned long data) | 
 | 524 | { | 
 | 525 | 	struct loopback_pcm *dpcm = (struct loopback_pcm *)data; | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 526 | 	unsigned int running; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 527 |  | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 528 | 	running = loopback_pos_update(dpcm->cable); | 
 | 529 | 	if (running & (1 << dpcm->substream->stream)) { | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 530 | 		loopback_timer_start(dpcm); | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 531 | 		if (dpcm->period_update_pending) { | 
 | 532 | 			dpcm->period_update_pending = 0; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 533 | 			snd_pcm_period_elapsed(dpcm->substream); | 
| Jaroslav Kysela | dd04bb1 | 2010-10-20 08:27:02 +0200 | [diff] [blame] | 534 | 		} | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 535 | 	} | 
 | 536 | } | 
 | 537 |  | 
 | 538 | static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) | 
 | 539 | { | 
 | 540 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 541 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 542 |  | 
 | 543 | 	loopback_pos_update(dpcm->cable); | 
 | 544 | 	return bytes_to_frames(runtime, dpcm->buf_pos); | 
 | 545 | } | 
 | 546 |  | 
 | 547 | static struct snd_pcm_hardware loopback_pcm_hardware = | 
 | 548 | { | 
 | 549 | 	.info =		(SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 550 | 			 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 551 | 	.formats =	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | 
 | 552 | 			 SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | | 
 | 553 | 			 SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), | 
 | 554 | 	.rates =	SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000, | 
 | 555 | 	.rate_min =		8000, | 
 | 556 | 	.rate_max =		192000, | 
 | 557 | 	.channels_min =		1, | 
 | 558 | 	.channels_max =		32, | 
 | 559 | 	.buffer_bytes_max =	2 * 1024 * 1024, | 
 | 560 | 	.period_bytes_min =	64, | 
| Jaroslav Kysela | 0db7102 | 2010-10-14 21:46:12 +0200 | [diff] [blame] | 561 | 	/* note check overflow in frac_pos() using pcm_rate_shift before | 
 | 562 | 	   changing period_bytes_max value */ | 
 | 563 | 	.period_bytes_max =	1024 * 1024, | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 564 | 	.periods_min =		1, | 
 | 565 | 	.periods_max =		1024, | 
 | 566 | 	.fifo_size =		0, | 
 | 567 | }; | 
 | 568 |  | 
 | 569 | static void loopback_runtime_free(struct snd_pcm_runtime *runtime) | 
 | 570 | { | 
 | 571 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 572 | 	kfree(dpcm); | 
 | 573 | } | 
 | 574 |  | 
 | 575 | static int loopback_hw_params(struct snd_pcm_substream *substream, | 
 | 576 | 			      struct snd_pcm_hw_params *params) | 
 | 577 | { | 
 | 578 | 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); | 
 | 579 | } | 
 | 580 |  | 
 | 581 | static int loopback_hw_free(struct snd_pcm_substream *substream) | 
 | 582 | { | 
 | 583 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 584 | 	struct loopback_pcm *dpcm = runtime->private_data; | 
 | 585 | 	struct loopback_cable *cable = dpcm->cable; | 
 | 586 |  | 
 | 587 | 	mutex_lock(&dpcm->loopback->cable_lock); | 
 | 588 | 	cable->valid &= ~(1 << substream->stream); | 
 | 589 | 	mutex_unlock(&dpcm->loopback->cable_lock); | 
 | 590 | 	return snd_pcm_lib_free_pages(substream); | 
 | 591 | } | 
 | 592 |  | 
 | 593 | static unsigned int get_cable_index(struct snd_pcm_substream *substream) | 
 | 594 | { | 
 | 595 | 	if (!substream->pcm->device) | 
 | 596 | 		return substream->stream; | 
 | 597 | 	else | 
 | 598 | 		return !substream->stream; | 
 | 599 | } | 
 | 600 |  | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 601 | static int rule_format(struct snd_pcm_hw_params *params, | 
 | 602 | 		       struct snd_pcm_hw_rule *rule) | 
 | 603 | { | 
 | 604 |  | 
 | 605 | 	struct snd_pcm_hardware *hw = rule->private; | 
 | 606 | 	struct snd_mask *maskp = hw_param_mask(params, rule->var); | 
 | 607 |  | 
 | 608 | 	maskp->bits[0] &= (u_int32_t)hw->formats; | 
 | 609 | 	maskp->bits[1] &= (u_int32_t)(hw->formats >> 32); | 
 | 610 | 	memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ | 
 | 611 | 	if (! maskp->bits[0] && ! maskp->bits[1]) | 
 | 612 | 		return -EINVAL; | 
 | 613 | 	return 0; | 
 | 614 | } | 
 | 615 |  | 
 | 616 | static int rule_rate(struct snd_pcm_hw_params *params, | 
 | 617 | 		     struct snd_pcm_hw_rule *rule) | 
 | 618 | { | 
 | 619 | 	struct snd_pcm_hardware *hw = rule->private; | 
 | 620 | 	struct snd_interval t; | 
 | 621 |  | 
 | 622 |         t.min = hw->rate_min; | 
 | 623 |         t.max = hw->rate_max; | 
 | 624 |         t.openmin = t.openmax = 0; | 
 | 625 |         t.integer = 0; | 
 | 626 | 	return snd_interval_refine(hw_param_interval(params, rule->var), &t); | 
 | 627 | } | 
 | 628 |  | 
 | 629 | static int rule_channels(struct snd_pcm_hw_params *params, | 
 | 630 | 			 struct snd_pcm_hw_rule *rule) | 
 | 631 | { | 
 | 632 | 	struct snd_pcm_hardware *hw = rule->private; | 
 | 633 | 	struct snd_interval t; | 
 | 634 |  | 
 | 635 |         t.min = hw->channels_min; | 
 | 636 |         t.max = hw->channels_max; | 
 | 637 |         t.openmin = t.openmax = 0; | 
 | 638 |         t.integer = 0; | 
 | 639 | 	return snd_interval_refine(hw_param_interval(params, rule->var), &t); | 
 | 640 | } | 
 | 641 |  | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 642 | static int loopback_open(struct snd_pcm_substream *substream) | 
 | 643 | { | 
 | 644 | 	struct snd_pcm_runtime *runtime = substream->runtime; | 
 | 645 | 	struct loopback *loopback = substream->private_data; | 
 | 646 | 	struct loopback_pcm *dpcm; | 
 | 647 | 	struct loopback_cable *cable; | 
 | 648 | 	int err = 0; | 
 | 649 | 	int dev = get_cable_index(substream); | 
 | 650 |  | 
 | 651 | 	mutex_lock(&loopback->cable_lock); | 
 | 652 | 	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | 
 | 653 | 	if (!dpcm) { | 
 | 654 | 		err = -ENOMEM; | 
 | 655 | 		goto unlock; | 
 | 656 | 	} | 
 | 657 | 	dpcm->loopback = loopback; | 
 | 658 | 	dpcm->substream = substream; | 
 | 659 | 	setup_timer(&dpcm->timer, loopback_timer_function, | 
 | 660 | 		    (unsigned long)dpcm); | 
 | 661 |  | 
 | 662 | 	cable = loopback->cables[substream->number][dev]; | 
 | 663 | 	if (!cable) { | 
 | 664 | 		cable = kzalloc(sizeof(*cable), GFP_KERNEL); | 
 | 665 | 		if (!cable) { | 
 | 666 | 			kfree(dpcm); | 
 | 667 | 			err = -ENOMEM; | 
 | 668 | 			goto unlock; | 
 | 669 | 		} | 
 | 670 | 		spin_lock_init(&cable->lock); | 
 | 671 | 		cable->hw = loopback_pcm_hardware; | 
 | 672 | 		loopback->cables[substream->number][dev] = cable; | 
 | 673 | 	} | 
 | 674 | 	dpcm->cable = cable; | 
 | 675 | 	cable->streams[substream->stream] = dpcm; | 
 | 676 |  | 
 | 677 | 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | 
 | 678 |  | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 679 | 	/* use dynamic rules based on actual runtime->hw values */ | 
 | 680 | 	/* note that the default rules created in the PCM midlevel code */ | 
 | 681 | 	/* are cached -> they do not reflect the actual state */ | 
 | 682 | 	err = snd_pcm_hw_rule_add(runtime, 0, | 
 | 683 | 				  SNDRV_PCM_HW_PARAM_FORMAT, | 
 | 684 | 				  rule_format, &runtime->hw, | 
 | 685 | 				  SNDRV_PCM_HW_PARAM_FORMAT, -1); | 
 | 686 | 	if (err < 0) | 
 | 687 | 		goto unlock; | 
 | 688 | 	err = snd_pcm_hw_rule_add(runtime, 0, | 
 | 689 | 				  SNDRV_PCM_HW_PARAM_RATE, | 
 | 690 | 				  rule_rate, &runtime->hw, | 
 | 691 | 				  SNDRV_PCM_HW_PARAM_RATE, -1); | 
 | 692 | 	if (err < 0) | 
 | 693 | 		goto unlock; | 
 | 694 | 	err = snd_pcm_hw_rule_add(runtime, 0, | 
 | 695 | 				  SNDRV_PCM_HW_PARAM_CHANNELS, | 
 | 696 | 				  rule_channels, &runtime->hw, | 
 | 697 | 				  SNDRV_PCM_HW_PARAM_CHANNELS, -1); | 
 | 698 | 	if (err < 0) | 
 | 699 | 		goto unlock; | 
 | 700 |  | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 701 | 	runtime->private_data = dpcm; | 
 | 702 | 	runtime->private_free = loopback_runtime_free; | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 703 | 	if (get_notify(dpcm)) | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 704 | 		runtime->hw = loopback_pcm_hardware; | 
| Jaroslav Kysela | b1c73fc | 2010-10-11 10:45:00 +0200 | [diff] [blame] | 705 | 	else | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 706 | 		runtime->hw = cable->hw; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 707 |  unlock: | 
 | 708 | 	mutex_unlock(&loopback->cable_lock); | 
 | 709 | 	return err; | 
 | 710 | } | 
 | 711 |  | 
 | 712 | static int loopback_close(struct snd_pcm_substream *substream) | 
 | 713 | { | 
 | 714 | 	struct loopback *loopback = substream->private_data; | 
 | 715 | 	struct loopback_pcm *dpcm = substream->runtime->private_data; | 
 | 716 | 	struct loopback_cable *cable; | 
 | 717 | 	int dev = get_cable_index(substream); | 
 | 718 |  | 
 | 719 | 	loopback_timer_stop(dpcm); | 
 | 720 | 	mutex_lock(&loopback->cable_lock); | 
 | 721 | 	cable = loopback->cables[substream->number][dev]; | 
 | 722 | 	if (cable->streams[!substream->stream]) { | 
 | 723 | 		/* other stream is still alive */ | 
 | 724 | 		cable->streams[substream->stream] = NULL; | 
 | 725 | 	} else { | 
 | 726 | 		/* free the cable */ | 
 | 727 | 		loopback->cables[substream->number][dev] = NULL; | 
 | 728 | 		kfree(cable); | 
 | 729 | 	} | 
 | 730 | 	mutex_unlock(&loopback->cable_lock); | 
 | 731 | 	return 0; | 
 | 732 | } | 
 | 733 |  | 
 | 734 | static struct snd_pcm_ops loopback_playback_ops = { | 
 | 735 | 	.open =		loopback_open, | 
 | 736 | 	.close =	loopback_close, | 
 | 737 | 	.ioctl =	snd_pcm_lib_ioctl, | 
 | 738 | 	.hw_params =	loopback_hw_params, | 
 | 739 | 	.hw_free =	loopback_hw_free, | 
 | 740 | 	.prepare =	loopback_prepare, | 
 | 741 | 	.trigger =	loopback_trigger, | 
 | 742 | 	.pointer =	loopback_pointer, | 
 | 743 | }; | 
 | 744 |  | 
 | 745 | static struct snd_pcm_ops loopback_capture_ops = { | 
 | 746 | 	.open =		loopback_open, | 
 | 747 | 	.close =	loopback_close, | 
 | 748 | 	.ioctl =	snd_pcm_lib_ioctl, | 
 | 749 | 	.hw_params =	loopback_hw_params, | 
 | 750 | 	.hw_free =	loopback_hw_free, | 
 | 751 | 	.prepare =	loopback_prepare, | 
 | 752 | 	.trigger =	loopback_trigger, | 
 | 753 | 	.pointer =	loopback_pointer, | 
 | 754 | }; | 
 | 755 |  | 
 | 756 | static int __devinit loopback_pcm_new(struct loopback *loopback, | 
 | 757 | 				      int device, int substreams) | 
 | 758 | { | 
 | 759 | 	struct snd_pcm *pcm; | 
 | 760 | 	int err; | 
 | 761 |  | 
 | 762 | 	err = snd_pcm_new(loopback->card, "Loopback PCM", device, | 
 | 763 | 			  substreams, substreams, &pcm); | 
 | 764 | 	if (err < 0) | 
 | 765 | 		return err; | 
 | 766 | 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops); | 
 | 767 | 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops); | 
 | 768 |  | 
 | 769 | 	pcm->private_data = loopback; | 
 | 770 | 	pcm->info_flags = 0; | 
 | 771 | 	strcpy(pcm->name, "Loopback PCM"); | 
 | 772 |  | 
 | 773 | 	loopback->pcm[device] = pcm; | 
 | 774 |  | 
 | 775 | 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | 
 | 776 | 			snd_dma_continuous_data(GFP_KERNEL), | 
 | 777 | 			0, 2 * 1024 * 1024); | 
 | 778 | 	return 0; | 
 | 779 | } | 
 | 780 |  | 
 | 781 | static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol,    | 
 | 782 | 				    struct snd_ctl_elem_info *uinfo)  | 
 | 783 | { | 
 | 784 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 785 | 	uinfo->count = 1; | 
 | 786 | 	uinfo->value.integer.min = 80000; | 
 | 787 | 	uinfo->value.integer.max = 120000; | 
 | 788 | 	uinfo->value.integer.step = 1; | 
 | 789 | 	return 0; | 
 | 790 | }                                   | 
 | 791 |  | 
 | 792 | static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol, | 
 | 793 | 				   struct snd_ctl_elem_value *ucontrol) | 
 | 794 | { | 
 | 795 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 796 | 	 | 
 | 797 | 	ucontrol->value.integer.value[0] = | 
 | 798 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 799 | 			       [kcontrol->id.device].rate_shift; | 
 | 800 | 	return 0; | 
 | 801 | } | 
 | 802 |  | 
 | 803 | static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol, | 
 | 804 | 				   struct snd_ctl_elem_value *ucontrol) | 
 | 805 | { | 
 | 806 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 807 | 	unsigned int val; | 
 | 808 | 	int change = 0; | 
 | 809 |  | 
 | 810 | 	val = ucontrol->value.integer.value[0]; | 
 | 811 | 	if (val < 80000) | 
 | 812 | 		val = 80000; | 
 | 813 | 	if (val > 120000) | 
 | 814 | 		val = 120000;	 | 
 | 815 | 	mutex_lock(&loopback->cable_lock); | 
 | 816 | 	if (val != loopback->setup[kcontrol->id.subdevice] | 
 | 817 | 				  [kcontrol->id.device].rate_shift) { | 
 | 818 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 819 | 			       [kcontrol->id.device].rate_shift = val; | 
 | 820 | 		change = 1; | 
 | 821 | 	} | 
 | 822 | 	mutex_unlock(&loopback->cable_lock); | 
 | 823 | 	return change; | 
 | 824 | } | 
 | 825 |  | 
 | 826 | static int loopback_notify_get(struct snd_kcontrol *kcontrol, | 
 | 827 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | 828 | { | 
 | 829 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 830 | 	 | 
 | 831 | 	ucontrol->value.integer.value[0] = | 
 | 832 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 833 | 			       [kcontrol->id.device].notify; | 
 | 834 | 	return 0; | 
 | 835 | } | 
 | 836 |  | 
 | 837 | static int loopback_notify_put(struct snd_kcontrol *kcontrol, | 
 | 838 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | 839 | { | 
 | 840 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 841 | 	unsigned int val; | 
 | 842 | 	int change = 0; | 
 | 843 |  | 
 | 844 | 	val = ucontrol->value.integer.value[0] ? 1 : 0; | 
 | 845 | 	if (val != loopback->setup[kcontrol->id.subdevice] | 
 | 846 | 				[kcontrol->id.device].notify) { | 
 | 847 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 848 | 			[kcontrol->id.device].notify = val; | 
 | 849 | 		change = 1; | 
 | 850 | 	} | 
 | 851 | 	return change; | 
 | 852 | } | 
 | 853 |  | 
 | 854 | static int loopback_active_get(struct snd_kcontrol *kcontrol, | 
 | 855 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | 856 | { | 
 | 857 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 858 | 	struct loopback_cable *cable = loopback->cables | 
| Jaroslav Kysela | ac446fb | 2010-10-02 16:00:53 +0200 | [diff] [blame] | 859 | 			[kcontrol->id.subdevice][kcontrol->id.device ^ 1]; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 860 | 	unsigned int val = 0; | 
 | 861 |  | 
 | 862 | 	if (cable != NULL) | 
 | 863 | 		val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? | 
 | 864 | 									1 : 0; | 
 | 865 | 	ucontrol->value.integer.value[0] = val; | 
 | 866 | 	return 0; | 
 | 867 | } | 
 | 868 |  | 
 | 869 | static int loopback_format_info(struct snd_kcontrol *kcontrol,    | 
 | 870 | 				struct snd_ctl_elem_info *uinfo)  | 
 | 871 | { | 
 | 872 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 873 | 	uinfo->count = 1; | 
 | 874 | 	uinfo->value.integer.min = 0; | 
 | 875 | 	uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST; | 
 | 876 | 	uinfo->value.integer.step = 1; | 
 | 877 | 	return 0; | 
 | 878 | }                                   | 
 | 879 |  | 
 | 880 | static int loopback_format_get(struct snd_kcontrol *kcontrol, | 
 | 881 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | 882 | { | 
 | 883 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 884 | 	 | 
 | 885 | 	ucontrol->value.integer.value[0] = | 
 | 886 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 887 | 			       [kcontrol->id.device].format; | 
 | 888 | 	return 0; | 
 | 889 | } | 
 | 890 |  | 
 | 891 | static int loopback_rate_info(struct snd_kcontrol *kcontrol,    | 
 | 892 | 			      struct snd_ctl_elem_info *uinfo)  | 
 | 893 | { | 
 | 894 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 895 | 	uinfo->count = 1; | 
 | 896 | 	uinfo->value.integer.min = 0; | 
 | 897 | 	uinfo->value.integer.max = 192000; | 
 | 898 | 	uinfo->value.integer.step = 1; | 
 | 899 | 	return 0; | 
 | 900 | }                                   | 
 | 901 |  | 
 | 902 | static int loopback_rate_get(struct snd_kcontrol *kcontrol, | 
 | 903 | 			     struct snd_ctl_elem_value *ucontrol) | 
 | 904 | { | 
 | 905 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 906 | 	 | 
 | 907 | 	ucontrol->value.integer.value[0] = | 
 | 908 | 		loopback->setup[kcontrol->id.subdevice] | 
 | 909 | 			       [kcontrol->id.device].rate; | 
 | 910 | 	return 0; | 
 | 911 | } | 
 | 912 |  | 
 | 913 | static int loopback_channels_info(struct snd_kcontrol *kcontrol,    | 
 | 914 | 				  struct snd_ctl_elem_info *uinfo)  | 
 | 915 | { | 
 | 916 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | 
 | 917 | 	uinfo->count = 1; | 
 | 918 | 	uinfo->value.integer.min = 1; | 
 | 919 | 	uinfo->value.integer.max = 1024; | 
 | 920 | 	uinfo->value.integer.step = 1; | 
 | 921 | 	return 0; | 
 | 922 | }                                   | 
 | 923 |  | 
 | 924 | static int loopback_channels_get(struct snd_kcontrol *kcontrol, | 
 | 925 | 				 struct snd_ctl_elem_value *ucontrol) | 
 | 926 | { | 
 | 927 | 	struct loopback *loopback = snd_kcontrol_chip(kcontrol); | 
 | 928 | 	 | 
 | 929 | 	ucontrol->value.integer.value[0] = | 
 | 930 | 		loopback->setup[kcontrol->id.subdevice] | 
| Jaroslav Kysela | 1446c5f | 2010-09-15 08:01:57 +0200 | [diff] [blame] | 931 | 			       [kcontrol->id.device].channels; | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 932 | 	return 0; | 
 | 933 | } | 
 | 934 |  | 
 | 935 | static struct snd_kcontrol_new loopback_controls[]  __devinitdata = { | 
 | 936 | { | 
 | 937 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 938 | 	.name =         "PCM Rate Shift 100000", | 
 | 939 | 	.info =         loopback_rate_shift_info, | 
 | 940 | 	.get =          loopback_rate_shift_get, | 
 | 941 | 	.put =          loopback_rate_shift_put, | 
 | 942 | }, | 
 | 943 | { | 
 | 944 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 945 | 	.name =         "PCM Notify", | 
 | 946 | 	.info =         snd_ctl_boolean_mono_info, | 
 | 947 | 	.get =          loopback_notify_get, | 
 | 948 | 	.put =          loopback_notify_put, | 
 | 949 | }, | 
 | 950 | #define ACTIVE_IDX 2 | 
 | 951 | { | 
 | 952 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READ, | 
 | 953 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 954 | 	.name =         "PCM Slave Active", | 
 | 955 | 	.info =         snd_ctl_boolean_mono_info, | 
 | 956 | 	.get =          loopback_active_get, | 
 | 957 | }, | 
 | 958 | #define FORMAT_IDX 3 | 
 | 959 | { | 
 | 960 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READ, | 
 | 961 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 962 | 	.name =         "PCM Slave Format", | 
 | 963 | 	.info =         loopback_format_info, | 
 | 964 | 	.get =          loopback_format_get | 
 | 965 | }, | 
 | 966 | #define RATE_IDX 4 | 
 | 967 | { | 
 | 968 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READ, | 
 | 969 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 970 | 	.name =         "PCM Slave Rate", | 
 | 971 | 	.info =         loopback_rate_info, | 
 | 972 | 	.get =          loopback_rate_get | 
 | 973 | }, | 
 | 974 | #define CHANNELS_IDX 5 | 
 | 975 | { | 
 | 976 | 	.access =	SNDRV_CTL_ELEM_ACCESS_READ, | 
 | 977 | 	.iface =        SNDRV_CTL_ELEM_IFACE_PCM, | 
 | 978 | 	.name =         "PCM Slave Channels", | 
 | 979 | 	.info =         loopback_channels_info, | 
 | 980 | 	.get =          loopback_channels_get | 
 | 981 | } | 
 | 982 | }; | 
 | 983 |  | 
 | 984 | static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) | 
 | 985 | { | 
 | 986 | 	struct snd_card *card = loopback->card; | 
 | 987 | 	struct snd_pcm *pcm; | 
 | 988 | 	struct snd_kcontrol *kctl; | 
 | 989 | 	struct loopback_setup *setup; | 
 | 990 | 	int err, dev, substr, substr_count, idx; | 
 | 991 |  | 
 | 992 | 	strcpy(card->mixername, "Loopback Mixer"); | 
 | 993 | 	for (dev = 0; dev < 2; dev++) { | 
 | 994 | 		pcm = loopback->pcm[dev]; | 
 | 995 | 		substr_count = | 
 | 996 | 		    pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count; | 
 | 997 | 		for (substr = 0; substr < substr_count; substr++) { | 
 | 998 | 			setup = &loopback->setup[substr][dev]; | 
 | 999 | 			setup->notify = notify; | 
 | 1000 | 			setup->rate_shift = NO_PITCH; | 
 | 1001 | 			setup->format = SNDRV_PCM_FORMAT_S16_LE; | 
 | 1002 | 			setup->rate = 48000; | 
 | 1003 | 			setup->channels = 2; | 
 | 1004 | 			for (idx = 0; idx < ARRAY_SIZE(loopback_controls); | 
 | 1005 | 									idx++) { | 
 | 1006 | 				kctl = snd_ctl_new1(&loopback_controls[idx], | 
 | 1007 | 						    loopback); | 
 | 1008 | 				if (!kctl) | 
 | 1009 | 					return -ENOMEM; | 
 | 1010 | 				kctl->id.device = dev; | 
 | 1011 | 				kctl->id.subdevice = substr; | 
 | 1012 | 				switch (idx) { | 
 | 1013 | 				case ACTIVE_IDX: | 
 | 1014 | 					setup->active_id = kctl->id; | 
 | 1015 | 					break; | 
 | 1016 | 				case FORMAT_IDX: | 
 | 1017 | 					setup->format_id = kctl->id; | 
 | 1018 | 					break; | 
 | 1019 | 				case RATE_IDX: | 
 | 1020 | 					setup->rate_id = kctl->id; | 
 | 1021 | 					break; | 
 | 1022 | 				case CHANNELS_IDX: | 
 | 1023 | 					setup->channels_id = kctl->id; | 
 | 1024 | 					break; | 
 | 1025 | 				default: | 
 | 1026 | 					break; | 
 | 1027 | 				} | 
 | 1028 | 				err = snd_ctl_add(card, kctl); | 
 | 1029 | 				if (err < 0) | 
 | 1030 | 					return err; | 
 | 1031 | 			} | 
 | 1032 | 		} | 
 | 1033 | 	} | 
 | 1034 | 	return 0; | 
 | 1035 | } | 
 | 1036 |  | 
| Jaroslav Kysela | e74670b | 2010-10-18 09:43:10 +0200 | [diff] [blame] | 1037 | #ifdef CONFIG_PROC_FS | 
 | 1038 |  | 
 | 1039 | static void print_dpcm_info(struct snd_info_buffer *buffer, | 
 | 1040 | 			    struct loopback_pcm *dpcm, | 
 | 1041 | 			    const char *id) | 
 | 1042 | { | 
 | 1043 | 	snd_iprintf(buffer, "  %s\n", id); | 
 | 1044 | 	if (dpcm == NULL) { | 
 | 1045 | 		snd_iprintf(buffer, "    inactive\n"); | 
 | 1046 | 		return; | 
 | 1047 | 	} | 
 | 1048 | 	snd_iprintf(buffer, "    buffer_size:\t%u\n", dpcm->pcm_buffer_size); | 
 | 1049 | 	snd_iprintf(buffer, "    buffer_pos:\t\t%u\n", dpcm->buf_pos); | 
 | 1050 | 	snd_iprintf(buffer, "    silent_size:\t%u\n", dpcm->silent_size); | 
 | 1051 | 	snd_iprintf(buffer, "    period_size:\t%u\n", dpcm->pcm_period_size); | 
 | 1052 | 	snd_iprintf(buffer, "    bytes_per_sec:\t%u\n", dpcm->pcm_bps); | 
 | 1053 | 	snd_iprintf(buffer, "    sample_align:\t%u\n", dpcm->pcm_salign); | 
 | 1054 | 	snd_iprintf(buffer, "    rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); | 
 | 1055 | 	snd_iprintf(buffer, "    update_pending:\t%u\n", | 
 | 1056 | 						dpcm->period_update_pending); | 
 | 1057 | 	snd_iprintf(buffer, "    irq_pos:\t\t%u\n", dpcm->irq_pos); | 
 | 1058 | 	snd_iprintf(buffer, "    period_frac:\t%u\n", dpcm->period_size_frac); | 
 | 1059 | 	snd_iprintf(buffer, "    last_jiffies:\t%lu (%lu)\n", | 
 | 1060 | 					dpcm->last_jiffies, jiffies); | 
 | 1061 | 	snd_iprintf(buffer, "    timer_expires:\t%lu\n", dpcm->timer.expires); | 
 | 1062 | } | 
 | 1063 |  | 
 | 1064 | static void print_substream_info(struct snd_info_buffer *buffer, | 
 | 1065 | 				 struct loopback *loopback, | 
 | 1066 | 				 int sub, | 
 | 1067 | 				 int num) | 
 | 1068 | { | 
 | 1069 | 	struct loopback_cable *cable = loopback->cables[sub][num]; | 
 | 1070 |  | 
 | 1071 | 	snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); | 
 | 1072 | 	if (cable == NULL) { | 
 | 1073 | 		snd_iprintf(buffer, "  inactive\n"); | 
 | 1074 | 		return; | 
 | 1075 | 	} | 
 | 1076 | 	snd_iprintf(buffer, "  valid: %u\n", cable->valid); | 
 | 1077 | 	snd_iprintf(buffer, "  running: %u\n", cable->running); | 
| Jaroslav Kysela | 5de9e45 | 2010-10-20 09:33:03 +0200 | [diff] [blame] | 1078 | 	snd_iprintf(buffer, "  pause: %u\n", cable->pause); | 
| Jaroslav Kysela | e74670b | 2010-10-18 09:43:10 +0200 | [diff] [blame] | 1079 | 	print_dpcm_info(buffer, cable->streams[0], "Playback"); | 
 | 1080 | 	print_dpcm_info(buffer, cable->streams[1], "Capture"); | 
 | 1081 | } | 
 | 1082 |  | 
 | 1083 | static void print_cable_info(struct snd_info_entry *entry, | 
 | 1084 | 			     struct snd_info_buffer *buffer) | 
 | 1085 | { | 
 | 1086 | 	struct loopback *loopback = entry->private_data; | 
 | 1087 | 	int sub, num; | 
 | 1088 |  | 
 | 1089 | 	mutex_lock(&loopback->cable_lock); | 
 | 1090 | 	num = entry->name[strlen(entry->name)-1]; | 
 | 1091 | 	num = num == '0' ? 0 : 1; | 
 | 1092 | 	for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) | 
 | 1093 | 		print_substream_info(buffer, loopback, sub, num); | 
 | 1094 | 	mutex_unlock(&loopback->cable_lock); | 
 | 1095 | } | 
 | 1096 |  | 
 | 1097 | static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) | 
 | 1098 | { | 
 | 1099 | 	char name[32]; | 
 | 1100 | 	struct snd_info_entry *entry; | 
 | 1101 | 	int err; | 
 | 1102 |  | 
 | 1103 | 	snprintf(name, sizeof(name), "cable#%d", cidx); | 
 | 1104 | 	err = snd_card_proc_new(loopback->card, name, &entry); | 
 | 1105 | 	if (err < 0) | 
 | 1106 | 		return err; | 
 | 1107 |  | 
 | 1108 | 	snd_info_set_text_ops(entry, loopback, print_cable_info); | 
 | 1109 | 	return 0; | 
 | 1110 | } | 
 | 1111 |  | 
 | 1112 | #else /* !CONFIG_PROC_FS */ | 
 | 1113 |  | 
 | 1114 | #define loopback_proc_new(loopback, cidx) do { } while (0) | 
 | 1115 |  | 
 | 1116 | #endif | 
 | 1117 |  | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 1118 | static int __devinit loopback_probe(struct platform_device *devptr) | 
 | 1119 | { | 
 | 1120 | 	struct snd_card *card; | 
 | 1121 | 	struct loopback *loopback; | 
 | 1122 | 	int dev = devptr->id; | 
 | 1123 | 	int err; | 
 | 1124 |  | 
 | 1125 | 	err = snd_card_create(index[dev], id[dev], THIS_MODULE, | 
 | 1126 | 			      sizeof(struct loopback), &card); | 
 | 1127 | 	if (err < 0) | 
 | 1128 | 		return err; | 
 | 1129 | 	loopback = card->private_data; | 
 | 1130 |  | 
 | 1131 | 	if (pcm_substreams[dev] < 1) | 
 | 1132 | 		pcm_substreams[dev] = 1; | 
 | 1133 | 	if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS) | 
 | 1134 | 		pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; | 
 | 1135 | 	 | 
 | 1136 | 	loopback->card = card; | 
 | 1137 | 	mutex_init(&loopback->cable_lock); | 
 | 1138 |  | 
 | 1139 | 	err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]); | 
 | 1140 | 	if (err < 0) | 
 | 1141 | 		goto __nodev; | 
 | 1142 | 	err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]); | 
 | 1143 | 	if (err < 0) | 
 | 1144 | 		goto __nodev; | 
 | 1145 | 	err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); | 
 | 1146 | 	if (err < 0) | 
 | 1147 | 		goto __nodev; | 
| Jaroslav Kysela | e74670b | 2010-10-18 09:43:10 +0200 | [diff] [blame] | 1148 | 	loopback_proc_new(loopback, 0); | 
 | 1149 | 	loopback_proc_new(loopback, 1); | 
| Jaroslav Kysela | 597603d | 2010-08-09 14:21:11 +0200 | [diff] [blame] | 1150 | 	strcpy(card->driver, "Loopback"); | 
 | 1151 | 	strcpy(card->shortname, "Loopback"); | 
 | 1152 | 	sprintf(card->longname, "Loopback %i", dev + 1); | 
 | 1153 | 	err = snd_card_register(card); | 
 | 1154 | 	if (!err) { | 
 | 1155 | 		platform_set_drvdata(devptr, card); | 
 | 1156 | 		return 0; | 
 | 1157 | 	} | 
 | 1158 |       __nodev: | 
 | 1159 | 	snd_card_free(card); | 
 | 1160 | 	return err; | 
 | 1161 | } | 
 | 1162 |  | 
 | 1163 | static int __devexit loopback_remove(struct platform_device *devptr) | 
 | 1164 | { | 
 | 1165 | 	snd_card_free(platform_get_drvdata(devptr)); | 
 | 1166 | 	platform_set_drvdata(devptr, NULL); | 
 | 1167 | 	return 0; | 
 | 1168 | } | 
 | 1169 |  | 
 | 1170 | #ifdef CONFIG_PM | 
 | 1171 | static int loopback_suspend(struct platform_device *pdev, | 
 | 1172 | 				pm_message_t state) | 
 | 1173 | { | 
 | 1174 | 	struct snd_card *card = platform_get_drvdata(pdev); | 
 | 1175 | 	struct loopback *loopback = card->private_data; | 
 | 1176 |  | 
 | 1177 | 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | 
 | 1178 |  | 
 | 1179 | 	snd_pcm_suspend_all(loopback->pcm[0]); | 
 | 1180 | 	snd_pcm_suspend_all(loopback->pcm[1]); | 
 | 1181 | 	return 0; | 
 | 1182 | } | 
 | 1183 | 	 | 
 | 1184 | static int loopback_resume(struct platform_device *pdev) | 
 | 1185 | { | 
 | 1186 | 	struct snd_card *card = platform_get_drvdata(pdev); | 
 | 1187 |  | 
 | 1188 | 	snd_power_change_state(card, SNDRV_CTL_POWER_D0); | 
 | 1189 | 	return 0; | 
 | 1190 | } | 
 | 1191 | #endif | 
 | 1192 |  | 
 | 1193 | #define SND_LOOPBACK_DRIVER	"snd_aloop" | 
 | 1194 |  | 
 | 1195 | static struct platform_driver loopback_driver = { | 
 | 1196 | 	.probe		= loopback_probe, | 
 | 1197 | 	.remove		= __devexit_p(loopback_remove), | 
 | 1198 | #ifdef CONFIG_PM | 
 | 1199 | 	.suspend	= loopback_suspend, | 
 | 1200 | 	.resume		= loopback_resume, | 
 | 1201 | #endif | 
 | 1202 | 	.driver		= { | 
 | 1203 | 		.name	= SND_LOOPBACK_DRIVER | 
 | 1204 | 	}, | 
 | 1205 | }; | 
 | 1206 |  | 
 | 1207 | static void loopback_unregister_all(void) | 
 | 1208 | { | 
 | 1209 | 	int i; | 
 | 1210 |  | 
 | 1211 | 	for (i = 0; i < ARRAY_SIZE(devices); ++i) | 
 | 1212 | 		platform_device_unregister(devices[i]); | 
 | 1213 | 	platform_driver_unregister(&loopback_driver); | 
 | 1214 | } | 
 | 1215 |  | 
 | 1216 | static int __init alsa_card_loopback_init(void) | 
 | 1217 | { | 
 | 1218 | 	int i, err, cards; | 
 | 1219 |  | 
 | 1220 | 	err = platform_driver_register(&loopback_driver); | 
 | 1221 | 	if (err < 0) | 
 | 1222 | 		return err; | 
 | 1223 |  | 
 | 1224 |  | 
 | 1225 | 	cards = 0; | 
 | 1226 | 	for (i = 0; i < SNDRV_CARDS; i++) { | 
 | 1227 | 		struct platform_device *device; | 
 | 1228 | 		if (!enable[i]) | 
 | 1229 | 			continue; | 
 | 1230 | 		device = platform_device_register_simple(SND_LOOPBACK_DRIVER, | 
 | 1231 | 							 i, NULL, 0); | 
 | 1232 | 		if (IS_ERR(device)) | 
 | 1233 | 			continue; | 
 | 1234 | 		if (!platform_get_drvdata(device)) { | 
 | 1235 | 			platform_device_unregister(device); | 
 | 1236 | 			continue; | 
 | 1237 | 		} | 
 | 1238 | 		devices[i] = device; | 
 | 1239 | 		cards++; | 
 | 1240 | 	} | 
 | 1241 | 	if (!cards) { | 
 | 1242 | #ifdef MODULE | 
 | 1243 | 		printk(KERN_ERR "aloop: No loopback enabled\n"); | 
 | 1244 | #endif | 
 | 1245 | 		loopback_unregister_all(); | 
 | 1246 | 		return -ENODEV; | 
 | 1247 | 	} | 
 | 1248 | 	return 0; | 
 | 1249 | } | 
 | 1250 |  | 
 | 1251 | static void __exit alsa_card_loopback_exit(void) | 
 | 1252 | { | 
 | 1253 | 	loopback_unregister_all(); | 
 | 1254 | } | 
 | 1255 |  | 
 | 1256 | module_init(alsa_card_loopback_init) | 
 | 1257 | module_exit(alsa_card_loopback_exit) |