Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
e1a164d7 | 2 | * Line6 Linux USB driver - 0.9.1beta |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
5 | * |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation, version 2. | |
9 | * | |
10 | */ | |
11 | ||
5a0e3ad6 | 12 | #include <linux/slab.h> |
705ececd MG |
13 | #include <sound/core.h> |
14 | #include <sound/control.h> | |
15 | #include <sound/pcm.h> | |
16 | #include <sound/pcm_params.h> | |
17 | ||
18 | #include "audio.h" | |
19 | #include "capture.h" | |
1027f476 | 20 | #include "driver.h" |
705ececd MG |
21 | #include "playback.h" |
22 | #include "pod.h" | |
23 | ||
1027f476 MG |
24 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE |
25 | ||
e1a164d7 | 26 | static struct snd_line6_pcm *dev2pcm(struct device *dev) |
1027f476 MG |
27 | { |
28 | struct usb_interface *interface = to_usb_interface(dev); | |
29 | struct usb_line6 *line6 = usb_get_intfdata(interface); | |
30 | struct snd_line6_pcm *line6pcm = line6->line6pcm; | |
31 | return line6pcm; | |
32 | } | |
33 | ||
34 | /* | |
35 | "read" request on "impulse_volume" special file. | |
36 | */ | |
e7c8a7e3 GKH |
37 | static ssize_t impulse_volume_show(struct device *dev, |
38 | struct device_attribute *attr, char *buf) | |
1027f476 MG |
39 | { |
40 | return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); | |
41 | } | |
42 | ||
43 | /* | |
44 | "write" request on "impulse_volume" special file. | |
45 | */ | |
e7c8a7e3 GKH |
46 | static ssize_t impulse_volume_store(struct device *dev, |
47 | struct device_attribute *attr, | |
48 | const char *buf, size_t count) | |
1027f476 MG |
49 | { |
50 | struct snd_line6_pcm *line6pcm = dev2pcm(dev); | |
cdf5e551 | 51 | int value; |
a3762902 | 52 | int ret; |
cdf5e551 | 53 | |
a3762902 LN |
54 | ret = kstrtoint(buf, 10, &value); |
55 | if (ret < 0) | |
56 | return ret; | |
cdf5e551 | 57 | |
1027f476 MG |
58 | line6pcm->impulse_volume = value; |
59 | ||
e1a164d7 | 60 | if (value > 0) |
0ca54888 | 61 | line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); |
1027f476 | 62 | else |
0ca54888 | 63 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); |
1027f476 MG |
64 | |
65 | return count; | |
66 | } | |
e7c8a7e3 | 67 | static DEVICE_ATTR_RW(impulse_volume); |
1027f476 MG |
68 | |
69 | /* | |
70 | "read" request on "impulse_period" special file. | |
71 | */ | |
e7c8a7e3 GKH |
72 | static ssize_t impulse_period_show(struct device *dev, |
73 | struct device_attribute *attr, char *buf) | |
1027f476 MG |
74 | { |
75 | return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); | |
76 | } | |
77 | ||
78 | /* | |
79 | "write" request on "impulse_period" special file. | |
80 | */ | |
e7c8a7e3 GKH |
81 | static ssize_t impulse_period_store(struct device *dev, |
82 | struct device_attribute *attr, | |
83 | const char *buf, size_t count) | |
1027f476 | 84 | { |
a3762902 LN |
85 | int value; |
86 | int ret; | |
87 | ||
88 | ret = kstrtoint(buf, 10, &value); | |
89 | if (ret < 0) | |
90 | return ret; | |
91 | ||
92 | dev2pcm(dev)->impulse_period = value; | |
1027f476 MG |
93 | return count; |
94 | } | |
e7c8a7e3 | 95 | static DEVICE_ATTR_RW(impulse_period); |
1027f476 MG |
96 | |
97 | #endif | |
98 | ||
6b02a17e MG |
99 | static bool test_flags(unsigned long flags0, unsigned long flags1, |
100 | unsigned long mask) | |
101 | { | |
102 | return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); | |
103 | } | |
104 | ||
0ca54888 | 105 | int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) |
1027f476 | 106 | { |
9f613601 AB |
107 | unsigned long flags_old, flags_new, flags_final; |
108 | int err; | |
109 | ||
110 | do { | |
111 | flags_old = ACCESS_ONCE(line6pcm->flags); | |
112 | flags_new = flags_old | channels; | |
113 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | |
114 | ||
115 | flags_final = flags_old; | |
82a74d4a | 116 | |
1027f476 | 117 | line6pcm->prev_fbuf = NULL; |
e1a164d7 | 118 | |
0ca54888 | 119 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { |
928f25ee | 120 | /* Invoked multiple times in a row so allocate once only */ |
0ca54888 MG |
121 | if (!line6pcm->buffer_in) { |
122 | line6pcm->buffer_in = | |
123 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | |
124 | line6pcm->max_packet_size, GFP_KERNEL); | |
0ca54888 | 125 | if (!line6pcm->buffer_in) { |
0ca54888 MG |
126 | err = -ENOMEM; |
127 | goto pcm_acquire_error; | |
128 | } | |
129 | ||
130 | flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; | |
131 | } | |
132 | } | |
133 | ||
134 | if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { | |
1027f476 | 135 | /* |
e1a164d7 MG |
136 | Waiting for completion of active URBs in the stop handler is |
137 | a bug, we therefore report an error if capturing is restarted | |
138 | too soon. | |
139 | */ | |
0ca54888 MG |
140 | if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { |
141 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | |
1027f476 | 142 | return -EBUSY; |
6b02a17e MG |
143 | } |
144 | ||
1027f476 MG |
145 | line6pcm->count_in = 0; |
146 | line6pcm->prev_fsize = 0; | |
147 | err = line6_submit_audio_in_all_urbs(line6pcm); | |
e1a164d7 | 148 | |
6b02a17e | 149 | if (err < 0) |
0ca54888 MG |
150 | goto pcm_acquire_error; |
151 | ||
152 | flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; | |
1027f476 | 153 | } |
e1a164d7 | 154 | |
0ca54888 | 155 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { |
928f25ee | 156 | /* Invoked multiple times in a row so allocate once only */ |
0ca54888 MG |
157 | if (!line6pcm->buffer_out) { |
158 | line6pcm->buffer_out = | |
159 | kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * | |
160 | line6pcm->max_packet_size, GFP_KERNEL); | |
0ca54888 | 161 | if (!line6pcm->buffer_out) { |
0ca54888 MG |
162 | err = -ENOMEM; |
163 | goto pcm_acquire_error; | |
164 | } | |
1027f476 | 165 | |
0ca54888 MG |
166 | flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; |
167 | } | |
168 | } | |
6b02a17e | 169 | |
0ca54888 MG |
170 | if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { |
171 | /* | |
172 | See comment above regarding PCM restart. | |
173 | */ | |
174 | if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { | |
175 | dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); | |
176 | return -EBUSY; | |
6b02a17e MG |
177 | } |
178 | ||
1027f476 MG |
179 | line6pcm->count_out = 0; |
180 | err = line6_submit_audio_out_all_urbs(line6pcm); | |
e1a164d7 | 181 | |
6b02a17e | 182 | if (err < 0) |
0ca54888 MG |
183 | goto pcm_acquire_error; |
184 | ||
185 | flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; | |
1027f476 | 186 | } |
e1a164d7 | 187 | |
1027f476 | 188 | return 0; |
6b02a17e | 189 | |
0ca54888 MG |
190 | pcm_acquire_error: |
191 | /* | |
192 | If not all requested resources/streams could be obtained, release | |
193 | those which were successfully obtained (if any). | |
194 | */ | |
195 | line6_pcm_release(line6pcm, flags_final & channels); | |
6b02a17e | 196 | return err; |
1027f476 MG |
197 | } |
198 | ||
0ca54888 | 199 | int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) |
1027f476 | 200 | { |
9f613601 AB |
201 | unsigned long flags_old, flags_new; |
202 | ||
203 | do { | |
204 | flags_old = ACCESS_ONCE(line6pcm->flags); | |
205 | flags_new = flags_old & ~channels; | |
206 | } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); | |
1027f476 | 207 | |
0ca54888 | 208 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) |
1027f476 | 209 | line6_unlink_audio_in_urbs(line6pcm); |
6b02a17e | 210 | |
0ca54888 MG |
211 | if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { |
212 | line6_wait_clear_audio_in_urbs(line6pcm); | |
213 | line6_free_capture_buffer(line6pcm); | |
1027f476 MG |
214 | } |
215 | ||
0ca54888 | 216 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) |
1027f476 | 217 | line6_unlink_audio_out_urbs(line6pcm); |
6b02a17e | 218 | |
0ca54888 MG |
219 | if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { |
220 | line6_wait_clear_audio_out_urbs(line6pcm); | |
221 | line6_free_playback_buffer(line6pcm); | |
1027f476 | 222 | } |
1027f476 MG |
223 | |
224 | return 0; | |
225 | } | |
226 | ||
705ececd MG |
227 | /* trigger callback */ |
228 | int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) | |
229 | { | |
230 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
705ececd MG |
231 | struct snd_pcm_substream *s; |
232 | int err; | |
233 | unsigned long flags; | |
234 | ||
235 | spin_lock_irqsave(&line6pcm->lock_trigger, flags); | |
0ca54888 | 236 | clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); |
705ececd | 237 | |
705ececd | 238 | snd_pcm_group_for_each_entry(s, substream) { |
68dc3dde | 239 | switch (s->stream) { |
705ececd | 240 | case SNDRV_PCM_STREAM_PLAYBACK: |
1027f476 | 241 | err = snd_line6_playback_trigger(line6pcm, cmd); |
705ececd | 242 | |
68dc3dde GKH |
243 | if (err < 0) { |
244 | spin_unlock_irqrestore(&line6pcm->lock_trigger, | |
245 | flags); | |
705ececd MG |
246 | return err; |
247 | } | |
248 | ||
249 | break; | |
250 | ||
251 | case SNDRV_PCM_STREAM_CAPTURE: | |
1027f476 | 252 | err = snd_line6_capture_trigger(line6pcm, cmd); |
705ececd | 253 | |
68dc3dde GKH |
254 | if (err < 0) { |
255 | spin_unlock_irqrestore(&line6pcm->lock_trigger, | |
256 | flags); | |
705ececd MG |
257 | return err; |
258 | } | |
259 | ||
260 | break; | |
261 | ||
262 | default: | |
e1a164d7 MG |
263 | dev_err(line6pcm->line6->ifcdev, |
264 | "Unknown stream direction %d\n", s->stream); | |
705ececd MG |
265 | } |
266 | } | |
267 | ||
268 | spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); | |
269 | return 0; | |
270 | } | |
271 | ||
272 | /* control info callback */ | |
1027f476 MG |
273 | static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, |
274 | struct snd_ctl_elem_info *uinfo) | |
68dc3dde | 275 | { |
705ececd MG |
276 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
277 | uinfo->count = 2; | |
278 | uinfo->value.integer.min = 0; | |
279 | uinfo->value.integer.max = 256; | |
280 | return 0; | |
281 | } | |
282 | ||
283 | /* control get callback */ | |
1027f476 MG |
284 | static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, |
285 | struct snd_ctl_elem_value *ucontrol) | |
68dc3dde | 286 | { |
705ececd MG |
287 | int i; |
288 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
289 | ||
68dc3dde | 290 | for (i = 2; i--;) |
1027f476 | 291 | ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; |
705ececd MG |
292 | |
293 | return 0; | |
294 | } | |
295 | ||
296 | /* control put callback */ | |
1027f476 MG |
297 | static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, |
298 | struct snd_ctl_elem_value *ucontrol) | |
68dc3dde | 299 | { |
705ececd MG |
300 | int i, changed = 0; |
301 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
302 | ||
68dc3dde | 303 | for (i = 2; i--;) |
e1a164d7 MG |
304 | if (line6pcm->volume_playback[i] != |
305 | ucontrol->value.integer.value[i]) { | |
306 | line6pcm->volume_playback[i] = | |
307 | ucontrol->value.integer.value[i]; | |
705ececd MG |
308 | changed = 1; |
309 | } | |
310 | ||
311 | return changed; | |
312 | } | |
313 | ||
314 | /* control definition */ | |
1027f476 | 315 | static struct snd_kcontrol_new line6_control_playback = { |
705ececd MG |
316 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
317 | .name = "PCM Playback Volume", | |
318 | .index = 0, | |
319 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
1027f476 MG |
320 | .info = snd_line6_control_playback_info, |
321 | .get = snd_line6_control_playback_get, | |
322 | .put = snd_line6_control_playback_put | |
705ececd MG |
323 | }; |
324 | ||
325 | /* | |
326 | Cleanup the PCM device. | |
327 | */ | |
328 | static void line6_cleanup_pcm(struct snd_pcm *pcm) | |
329 | { | |
330 | int i; | |
331 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); | |
332 | ||
1027f476 MG |
333 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE |
334 | device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); | |
335 | device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); | |
336 | #endif | |
337 | ||
68dc3dde GKH |
338 | for (i = LINE6_ISO_BUFFERS; i--;) { |
339 | if (line6pcm->urb_audio_out[i]) { | |
705ececd MG |
340 | usb_kill_urb(line6pcm->urb_audio_out[i]); |
341 | usb_free_urb(line6pcm->urb_audio_out[i]); | |
342 | } | |
68dc3dde | 343 | if (line6pcm->urb_audio_in[i]) { |
705ececd MG |
344 | usb_kill_urb(line6pcm->urb_audio_in[i]); |
345 | usb_free_urb(line6pcm->urb_audio_in[i]); | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | /* create a PCM device */ | |
351 | static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) | |
352 | { | |
353 | struct snd_pcm *pcm; | |
354 | int err; | |
355 | ||
68dc3dde | 356 | err = snd_pcm_new(line6pcm->line6->card, |
e1a164d7 MG |
357 | (char *)line6pcm->line6->properties->name, |
358 | 0, 1, 1, &pcm); | |
68dc3dde | 359 | if (err < 0) |
705ececd MG |
360 | return err; |
361 | ||
362 | pcm->private_data = line6pcm; | |
363 | pcm->private_free = line6_cleanup_pcm; | |
364 | line6pcm->pcm = pcm; | |
365 | strcpy(pcm->name, line6pcm->line6->properties->name); | |
366 | ||
367 | /* set operators */ | |
afb9091d SB |
368 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
369 | &snd_line6_playback_ops); | |
e1a164d7 | 370 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); |
705ececd MG |
371 | |
372 | /* pre-allocation of buffers */ | |
68dc3dde | 373 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, |
e1a164d7 MG |
374 | snd_dma_continuous_data |
375 | (GFP_KERNEL), 64 * 1024, | |
376 | 128 * 1024); | |
705ececd MG |
377 | |
378 | return 0; | |
379 | } | |
380 | ||
381 | /* PCM device destructor */ | |
382 | static int snd_line6_pcm_free(struct snd_device *device) | |
383 | { | |
384 | return 0; | |
385 | } | |
386 | ||
1027f476 MG |
387 | /* |
388 | Stop substream if still running. | |
389 | */ | |
390 | static void pcm_disconnect_substream(struct snd_pcm_substream *substream) | |
391 | { | |
86f0b5b8 TI |
392 | if (substream->runtime && snd_pcm_running(substream)) { |
393 | snd_pcm_stream_lock_irq(substream); | |
1027f476 | 394 | snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); |
86f0b5b8 TI |
395 | snd_pcm_stream_unlock_irq(substream); |
396 | } | |
1027f476 MG |
397 | } |
398 | ||
399 | /* | |
400 | Stop PCM stream. | |
401 | */ | |
402 | void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) | |
403 | { | |
e1a164d7 MG |
404 | pcm_disconnect_substream(get_substream |
405 | (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); | |
406 | pcm_disconnect_substream(get_substream | |
407 | (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); | |
1027f476 MG |
408 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); |
409 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); | |
410 | } | |
411 | ||
705ececd MG |
412 | /* |
413 | Create and register the PCM device and mixer entries. | |
414 | Create URBs for playback and capture. | |
415 | */ | |
68dc3dde GKH |
416 | int line6_init_pcm(struct usb_line6 *line6, |
417 | struct line6_pcm_properties *properties) | |
705ececd MG |
418 | { |
419 | static struct snd_device_ops pcm_ops = { | |
420 | .dev_free = snd_line6_pcm_free, | |
421 | }; | |
422 | ||
423 | int err; | |
16d603d3 CR |
424 | unsigned ep_read = line6->properties->ep_audio_r; |
425 | unsigned ep_write = line6->properties->ep_audio_w; | |
705ececd MG |
426 | struct snd_line6_pcm *line6pcm; |
427 | ||
4cb1a4ae | 428 | if (!(line6->properties->capabilities & LINE6_CAP_PCM)) |
e1a164d7 | 429 | return 0; /* skip PCM initialization and report success */ |
705ececd | 430 | |
5565c59e | 431 | line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); |
705ececd | 432 | |
68dc3dde | 433 | if (line6pcm == NULL) |
705ececd MG |
434 | return -ENOMEM; |
435 | ||
1027f476 MG |
436 | line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; |
437 | line6pcm->volume_monitor = 255; | |
705ececd | 438 | line6pcm->line6 = line6; |
3b08db37 SH |
439 | |
440 | /* Read and write buffers are sized identically, so choose minimum */ | |
441 | line6pcm->max_packet_size = min( | |
442 | usb_maxpacket(line6->usbdev, | |
443 | usb_rcvisocpipe(line6->usbdev, ep_read), 0), | |
444 | usb_maxpacket(line6->usbdev, | |
445 | usb_sndisocpipe(line6->usbdev, ep_write), 1)); | |
446 | ||
705ececd MG |
447 | line6pcm->properties = properties; |
448 | line6->line6pcm = line6pcm; | |
449 | ||
450 | /* PCM device: */ | |
68dc3dde GKH |
451 | err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops); |
452 | if (err < 0) | |
705ececd MG |
453 | return err; |
454 | ||
68dc3dde GKH |
455 | err = snd_line6_new_pcm(line6pcm); |
456 | if (err < 0) | |
705ececd MG |
457 | return err; |
458 | ||
459 | spin_lock_init(&line6pcm->lock_audio_out); | |
460 | spin_lock_init(&line6pcm->lock_audio_in); | |
461 | spin_lock_init(&line6pcm->lock_trigger); | |
462 | ||
1027f476 | 463 | err = line6_create_audio_out_urbs(line6pcm); |
68dc3dde | 464 | if (err < 0) |
705ececd MG |
465 | return err; |
466 | ||
1027f476 | 467 | err = line6_create_audio_in_urbs(line6pcm); |
68dc3dde | 468 | if (err < 0) |
705ececd MG |
469 | return err; |
470 | ||
471 | /* mixer: */ | |
e1a164d7 MG |
472 | err = |
473 | snd_ctl_add(line6->card, | |
474 | snd_ctl_new1(&line6_control_playback, line6pcm)); | |
1027f476 MG |
475 | if (err < 0) |
476 | return err; | |
477 | ||
478 | #ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE | |
479 | /* impulse response test: */ | |
480 | err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); | |
68dc3dde | 481 | if (err < 0) |
705ececd MG |
482 | return err; |
483 | ||
1027f476 MG |
484 | err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); |
485 | if (err < 0) | |
486 | return err; | |
487 | ||
488 | line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; | |
489 | #endif | |
490 | ||
705ececd MG |
491 | return 0; |
492 | } | |
493 | ||
494 | /* prepare pcm callback */ | |
495 | int snd_line6_prepare(struct snd_pcm_substream *substream) | |
496 | { | |
497 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); | |
498 | ||
665f3f50 SH |
499 | switch (substream->stream) { |
500 | case SNDRV_PCM_STREAM_PLAYBACK: | |
0ca54888 | 501 | if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) |
6b02a17e MG |
502 | line6_unlink_wait_clear_audio_out_urbs(line6pcm); |
503 | ||
665f3f50 SH |
504 | break; |
505 | ||
506 | case SNDRV_PCM_STREAM_CAPTURE: | |
0ca54888 | 507 | if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) |
6b02a17e MG |
508 | line6_unlink_wait_clear_audio_in_urbs(line6pcm); |
509 | ||
665f3f50 SH |
510 | break; |
511 | ||
512 | default: | |
513 | MISSING_CASE; | |
514 | } | |
515 | ||
0ca54888 | 516 | if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { |
1027f476 | 517 | line6pcm->count_out = 0; |
705ececd MG |
518 | line6pcm->pos_out = 0; |
519 | line6pcm->pos_out_done = 0; | |
705ececd | 520 | line6pcm->bytes_out = 0; |
1027f476 | 521 | line6pcm->count_in = 0; |
705ececd MG |
522 | line6pcm->pos_in_done = 0; |
523 | line6pcm->bytes_in = 0; | |
524 | } | |
525 | ||
526 | return 0; | |
527 | } |