1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Line 6 Linux USB driver |
4 | * |
5 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
6 | */ |
7 | |
8 | #include <linux/slab.h> |
9 | #include <linux/export.h> |
10 | #include <sound/core.h> |
11 | #include <sound/control.h> |
12 | #include <sound/pcm.h> |
13 | #include <sound/pcm_params.h> |
14 | |
15 | #include "capture.h" |
16 | #include "driver.h" |
17 | #include "playback.h" |
18 | |
19 | /* impulse response volume controls */ |
20 | static int snd_line6_impulse_volume_info(struct snd_kcontrol *kcontrol, |
21 | struct snd_ctl_elem_info *uinfo) |
22 | { |
23 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
24 | uinfo->count = 1; |
25 | uinfo->value.integer.min = 0; |
26 | uinfo->value.integer.max = 255; |
27 | return 0; |
28 | } |
29 | |
30 | static int snd_line6_impulse_volume_get(struct snd_kcontrol *kcontrol, |
31 | struct snd_ctl_elem_value *ucontrol) |
32 | { |
33 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
34 | |
35 | ucontrol->value.integer.value[0] = line6pcm->impulse_volume; |
36 | return 0; |
37 | } |
38 | |
39 | static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, |
40 | struct snd_ctl_elem_value *ucontrol) |
41 | { |
42 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
43 | int value = ucontrol->value.integer.value[0]; |
44 | int err; |
45 | |
46 | if (line6pcm->impulse_volume == value) |
47 | return 0; |
48 | |
49 | line6pcm->impulse_volume = value; |
50 | if (value > 0) { |
51 | err = line6_pcm_acquire(line6pcm, type: LINE6_STREAM_IMPULSE, start: true); |
52 | if (err < 0) { |
53 | line6pcm->impulse_volume = 0; |
54 | return err; |
55 | } |
56 | } else { |
57 | line6_pcm_release(line6pcm, type: LINE6_STREAM_IMPULSE); |
58 | } |
59 | return 1; |
60 | } |
61 | |
62 | /* impulse response period controls */ |
63 | static int snd_line6_impulse_period_info(struct snd_kcontrol *kcontrol, |
64 | struct snd_ctl_elem_info *uinfo) |
65 | { |
66 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
67 | uinfo->count = 1; |
68 | uinfo->value.integer.min = 0; |
69 | uinfo->value.integer.max = 2000; |
70 | return 0; |
71 | } |
72 | |
73 | static int snd_line6_impulse_period_get(struct snd_kcontrol *kcontrol, |
74 | struct snd_ctl_elem_value *ucontrol) |
75 | { |
76 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
77 | |
78 | ucontrol->value.integer.value[0] = line6pcm->impulse_period; |
79 | return 0; |
80 | } |
81 | |
82 | static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, |
83 | struct snd_ctl_elem_value *ucontrol) |
84 | { |
85 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
86 | int value = ucontrol->value.integer.value[0]; |
87 | |
88 | if (line6pcm->impulse_period == value) |
89 | return 0; |
90 | |
91 | line6pcm->impulse_period = value; |
92 | return 1; |
93 | } |
94 | |
95 | /* |
96 | Unlink all currently active URBs. |
97 | */ |
98 | static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, |
99 | struct line6_pcm_stream *pcms) |
100 | { |
101 | int i; |
102 | |
103 | for (i = 0; i < line6pcm->line6->iso_buffers; i++) { |
104 | if (test_bit(i, &pcms->active_urbs)) { |
105 | if (!test_and_set_bit(nr: i, addr: &pcms->unlink_urbs)) |
106 | usb_unlink_urb(urb: pcms->urbs[i]); |
107 | } |
108 | } |
109 | } |
110 | |
111 | /* |
112 | Wait until unlinking of all currently active URBs has been finished. |
113 | */ |
114 | static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, |
115 | struct line6_pcm_stream *pcms) |
116 | { |
117 | int timeout = HZ; |
118 | int i; |
119 | int alive; |
120 | |
121 | do { |
122 | alive = 0; |
123 | for (i = 0; i < line6pcm->line6->iso_buffers; i++) { |
124 | if (test_bit(i, &pcms->active_urbs)) |
125 | alive++; |
126 | } |
127 | if (!alive) |
128 | break; |
129 | set_current_state(TASK_UNINTERRUPTIBLE); |
130 | schedule_timeout(timeout: 1); |
131 | } while (--timeout > 0); |
132 | if (alive) |
133 | dev_err(line6pcm->line6->ifcdev, |
134 | "timeout: still %d active urbs..\n" , alive); |
135 | } |
136 | |
137 | static inline struct line6_pcm_stream * |
138 | get_stream(struct snd_line6_pcm *line6pcm, int direction) |
139 | { |
140 | return (direction == SNDRV_PCM_STREAM_PLAYBACK) ? |
141 | &line6pcm->out : &line6pcm->in; |
142 | } |
143 | |
144 | /* allocate a buffer if not opened yet; |
145 | * call this in line6pcm.state_mutex |
146 | */ |
147 | static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, |
148 | struct line6_pcm_stream *pstr, int direction, int type) |
149 | { |
150 | const int pkt_size = |
151 | (direction == SNDRV_PCM_STREAM_PLAYBACK) ? |
152 | line6pcm->max_packet_size_out : |
153 | line6pcm->max_packet_size_in; |
154 | |
155 | /* Invoked multiple times in a row so allocate once only */ |
156 | if (!test_and_set_bit(nr: type, addr: &pstr->opened) && !pstr->buffer) { |
157 | pstr->buffer = |
158 | kmalloc(array3_size(line6pcm->line6->iso_buffers, |
159 | LINE6_ISO_PACKETS, pkt_size), |
160 | GFP_KERNEL); |
161 | if (!pstr->buffer) |
162 | return -ENOMEM; |
163 | } |
164 | return 0; |
165 | } |
166 | |
167 | /* free a buffer if all streams are closed; |
168 | * call this in line6pcm.state_mutex |
169 | */ |
170 | static void line6_buffer_release(struct snd_line6_pcm *line6pcm, |
171 | struct line6_pcm_stream *pstr, int type) |
172 | { |
173 | clear_bit(nr: type, addr: &pstr->opened); |
174 | if (!pstr->opened) { |
175 | line6_wait_clear_audio_urbs(line6pcm, pcms: pstr); |
176 | kfree(objp: pstr->buffer); |
177 | pstr->buffer = NULL; |
178 | } |
179 | } |
180 | |
181 | /* start a PCM stream */ |
182 | static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, |
183 | int type) |
184 | { |
185 | unsigned long flags; |
186 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); |
187 | int ret = 0; |
188 | |
189 | spin_lock_irqsave(&pstr->lock, flags); |
190 | if (!test_and_set_bit(nr: type, addr: &pstr->running) && |
191 | !(pstr->active_urbs || pstr->unlink_urbs)) { |
192 | pstr->count = 0; |
193 | /* Submit all currently available URBs */ |
194 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) |
195 | ret = line6_submit_audio_out_all_urbs(line6pcm); |
196 | else |
197 | ret = line6_submit_audio_in_all_urbs(line6pcm); |
198 | } |
199 | |
200 | if (ret < 0) |
201 | clear_bit(nr: type, addr: &pstr->running); |
202 | spin_unlock_irqrestore(lock: &pstr->lock, flags); |
203 | return ret; |
204 | } |
205 | |
206 | /* stop a PCM stream; this doesn't sync with the unlinked URBs */ |
207 | static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, |
208 | int type) |
209 | { |
210 | unsigned long flags; |
211 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); |
212 | |
213 | spin_lock_irqsave(&pstr->lock, flags); |
214 | clear_bit(nr: type, addr: &pstr->running); |
215 | if (!pstr->running) { |
216 | spin_unlock_irqrestore(lock: &pstr->lock, flags); |
217 | line6_unlink_audio_urbs(line6pcm, pcms: pstr); |
218 | spin_lock_irqsave(&pstr->lock, flags); |
219 | if (direction == SNDRV_PCM_STREAM_CAPTURE) { |
220 | line6pcm->prev_fbuf = NULL; |
221 | line6pcm->prev_fsize = 0; |
222 | } |
223 | } |
224 | spin_unlock_irqrestore(lock: &pstr->lock, flags); |
225 | } |
226 | |
227 | /* common PCM 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); |
231 | struct snd_pcm_substream *s; |
232 | int err; |
233 | |
234 | clear_bit(nr: LINE6_FLAG_PREPARED, addr: &line6pcm->flags); |
235 | |
236 | snd_pcm_group_for_each_entry(s, substream) { |
237 | if (s->pcm->card != substream->pcm->card) |
238 | continue; |
239 | |
240 | switch (cmd) { |
241 | case SNDRV_PCM_TRIGGER_START: |
242 | case SNDRV_PCM_TRIGGER_RESUME: |
243 | if (s->stream == SNDRV_PCM_STREAM_CAPTURE && |
244 | (line6pcm->line6->properties->capabilities & |
245 | LINE6_CAP_IN_NEEDS_OUT)) { |
246 | err = line6_stream_start(line6pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, |
247 | type: LINE6_STREAM_CAPTURE_HELPER); |
248 | if (err < 0) |
249 | return err; |
250 | } |
251 | err = line6_stream_start(line6pcm, direction: s->stream, |
252 | type: LINE6_STREAM_PCM); |
253 | if (err < 0) |
254 | return err; |
255 | break; |
256 | |
257 | case SNDRV_PCM_TRIGGER_STOP: |
258 | case SNDRV_PCM_TRIGGER_SUSPEND: |
259 | if (s->stream == SNDRV_PCM_STREAM_CAPTURE && |
260 | (line6pcm->line6->properties->capabilities & |
261 | LINE6_CAP_IN_NEEDS_OUT)) { |
262 | line6_stream_stop(line6pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, |
263 | type: LINE6_STREAM_CAPTURE_HELPER); |
264 | } |
265 | line6_stream_stop(line6pcm, direction: s->stream, |
266 | type: LINE6_STREAM_PCM); |
267 | break; |
268 | |
269 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
270 | if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) |
271 | return -EINVAL; |
272 | set_bit(nr: LINE6_FLAG_PAUSE_PLAYBACK, addr: &line6pcm->flags); |
273 | break; |
274 | |
275 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
276 | if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) |
277 | return -EINVAL; |
278 | clear_bit(nr: LINE6_FLAG_PAUSE_PLAYBACK, addr: &line6pcm->flags); |
279 | break; |
280 | |
281 | default: |
282 | return -EINVAL; |
283 | } |
284 | } |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | /* common PCM pointer callback */ |
290 | snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) |
291 | { |
292 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
293 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction: substream->stream); |
294 | |
295 | return pstr->pos_done; |
296 | } |
297 | |
298 | /* Acquire and optionally start duplex streams: |
299 | * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR |
300 | */ |
301 | int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start) |
302 | { |
303 | struct line6_pcm_stream *pstr; |
304 | int ret = 0, dir; |
305 | |
306 | /* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */ |
307 | mutex_lock(&line6pcm->state_mutex); |
308 | for (dir = 0; dir < 2; dir++) { |
309 | pstr = get_stream(line6pcm, direction: dir); |
310 | ret = line6_buffer_acquire(line6pcm, pstr, direction: dir, type); |
311 | if (ret < 0) |
312 | goto error; |
313 | if (!pstr->running) |
314 | line6_wait_clear_audio_urbs(line6pcm, pcms: pstr); |
315 | } |
316 | if (start) { |
317 | for (dir = 0; dir < 2; dir++) { |
318 | ret = line6_stream_start(line6pcm, direction: dir, type); |
319 | if (ret < 0) |
320 | goto error; |
321 | } |
322 | } |
323 | error: |
324 | mutex_unlock(lock: &line6pcm->state_mutex); |
325 | if (ret < 0) |
326 | line6_pcm_release(line6pcm, type); |
327 | return ret; |
328 | } |
329 | EXPORT_SYMBOL_GPL(line6_pcm_acquire); |
330 | |
331 | /* Stop and release duplex streams */ |
332 | void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type) |
333 | { |
334 | struct line6_pcm_stream *pstr; |
335 | int dir; |
336 | |
337 | mutex_lock(&line6pcm->state_mutex); |
338 | for (dir = 0; dir < 2; dir++) |
339 | line6_stream_stop(line6pcm, direction: dir, type); |
340 | for (dir = 0; dir < 2; dir++) { |
341 | pstr = get_stream(line6pcm, direction: dir); |
342 | line6_buffer_release(line6pcm, pstr, type); |
343 | } |
344 | mutex_unlock(lock: &line6pcm->state_mutex); |
345 | } |
346 | EXPORT_SYMBOL_GPL(line6_pcm_release); |
347 | |
348 | /* common PCM hw_params callback */ |
349 | int snd_line6_hw_params(struct snd_pcm_substream *substream, |
350 | struct snd_pcm_hw_params *hw_params) |
351 | { |
352 | int ret; |
353 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
354 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction: substream->stream); |
355 | |
356 | mutex_lock(&line6pcm->state_mutex); |
357 | ret = line6_buffer_acquire(line6pcm, pstr, direction: substream->stream, |
358 | type: LINE6_STREAM_PCM); |
359 | if (ret < 0) |
360 | goto error; |
361 | |
362 | pstr->period = params_period_bytes(p: hw_params); |
363 | error: |
364 | mutex_unlock(lock: &line6pcm->state_mutex); |
365 | return ret; |
366 | } |
367 | |
368 | /* common PCM hw_free callback */ |
369 | int snd_line6_hw_free(struct snd_pcm_substream *substream) |
370 | { |
371 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
372 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction: substream->stream); |
373 | |
374 | mutex_lock(&line6pcm->state_mutex); |
375 | line6_buffer_release(line6pcm, pstr, type: LINE6_STREAM_PCM); |
376 | mutex_unlock(lock: &line6pcm->state_mutex); |
377 | return 0; |
378 | } |
379 | |
380 | |
381 | /* control info callback */ |
382 | static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, |
383 | struct snd_ctl_elem_info *uinfo) |
384 | { |
385 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
386 | uinfo->count = 2; |
387 | uinfo->value.integer.min = 0; |
388 | uinfo->value.integer.max = 256; |
389 | return 0; |
390 | } |
391 | |
392 | /* control get callback */ |
393 | static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, |
394 | struct snd_ctl_elem_value *ucontrol) |
395 | { |
396 | int i; |
397 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
398 | |
399 | for (i = 0; i < 2; i++) |
400 | ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; |
401 | |
402 | return 0; |
403 | } |
404 | |
405 | /* control put callback */ |
406 | static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, |
407 | struct snd_ctl_elem_value *ucontrol) |
408 | { |
409 | int i, changed = 0; |
410 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); |
411 | |
412 | for (i = 0; i < 2; i++) |
413 | if (line6pcm->volume_playback[i] != |
414 | ucontrol->value.integer.value[i]) { |
415 | line6pcm->volume_playback[i] = |
416 | ucontrol->value.integer.value[i]; |
417 | changed = 1; |
418 | } |
419 | |
420 | return changed; |
421 | } |
422 | |
423 | /* control definition */ |
424 | static const struct snd_kcontrol_new line6_controls[] = { |
425 | { |
426 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
427 | .name = "PCM Playback Volume" , |
428 | .info = snd_line6_control_playback_info, |
429 | .get = snd_line6_control_playback_get, |
430 | .put = snd_line6_control_playback_put |
431 | }, |
432 | { |
433 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
434 | .name = "Impulse Response Volume" , |
435 | .info = snd_line6_impulse_volume_info, |
436 | .get = snd_line6_impulse_volume_get, |
437 | .put = snd_line6_impulse_volume_put |
438 | }, |
439 | { |
440 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
441 | .name = "Impulse Response Period" , |
442 | .info = snd_line6_impulse_period_info, |
443 | .get = snd_line6_impulse_period_get, |
444 | .put = snd_line6_impulse_period_put |
445 | }, |
446 | }; |
447 | |
448 | /* |
449 | Cleanup the PCM device. |
450 | */ |
451 | static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers) |
452 | { |
453 | int i; |
454 | |
455 | /* Most likely impossible in current code... */ |
456 | if (pcms->urbs == NULL) |
457 | return; |
458 | |
459 | for (i = 0; i < iso_buffers; i++) { |
460 | if (pcms->urbs[i]) { |
461 | usb_kill_urb(urb: pcms->urbs[i]); |
462 | usb_free_urb(urb: pcms->urbs[i]); |
463 | } |
464 | } |
465 | kfree(objp: pcms->urbs); |
466 | pcms->urbs = NULL; |
467 | } |
468 | |
469 | static void line6_cleanup_pcm(struct snd_pcm *pcm) |
470 | { |
471 | struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); |
472 | |
473 | cleanup_urbs(pcms: &line6pcm->out, iso_buffers: line6pcm->line6->iso_buffers); |
474 | cleanup_urbs(pcms: &line6pcm->in, iso_buffers: line6pcm->line6->iso_buffers); |
475 | kfree(objp: line6pcm); |
476 | } |
477 | |
478 | /* create a PCM device */ |
479 | static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) |
480 | { |
481 | struct snd_pcm *pcm; |
482 | int err; |
483 | |
484 | err = snd_pcm_new(card: line6->card, id: (char *)line6->properties->name, |
485 | device: 0, playback_count: 1, capture_count: 1, rpcm: pcm_ret); |
486 | if (err < 0) |
487 | return err; |
488 | pcm = *pcm_ret; |
489 | strcpy(p: pcm->name, q: line6->properties->name); |
490 | |
491 | /* set operators */ |
492 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, |
493 | ops: &snd_line6_playback_ops); |
494 | snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &snd_line6_capture_ops); |
495 | |
496 | /* pre-allocation of buffers */ |
497 | snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, |
498 | NULL, size: 64 * 1024, max: 128 * 1024); |
499 | return 0; |
500 | } |
501 | |
502 | /* |
503 | Sync with PCM stream stops. |
504 | */ |
505 | void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) |
506 | { |
507 | line6_unlink_audio_urbs(line6pcm, pcms: &line6pcm->out); |
508 | line6_unlink_audio_urbs(line6pcm, pcms: &line6pcm->in); |
509 | line6_wait_clear_audio_urbs(line6pcm, pcms: &line6pcm->out); |
510 | line6_wait_clear_audio_urbs(line6pcm, pcms: &line6pcm->in); |
511 | } |
512 | |
513 | /* |
514 | Create and register the PCM device and mixer entries. |
515 | Create URBs for playback and capture. |
516 | */ |
517 | int line6_init_pcm(struct usb_line6 *line6, |
518 | struct line6_pcm_properties *properties) |
519 | { |
520 | int i, err; |
521 | unsigned ep_read = line6->properties->ep_audio_r; |
522 | unsigned ep_write = line6->properties->ep_audio_w; |
523 | struct snd_pcm *pcm; |
524 | struct snd_line6_pcm *line6pcm; |
525 | |
526 | if (!(line6->properties->capabilities & LINE6_CAP_PCM)) |
527 | return 0; /* skip PCM initialization and report success */ |
528 | |
529 | err = snd_line6_new_pcm(line6, pcm_ret: &pcm); |
530 | if (err < 0) |
531 | return err; |
532 | |
533 | line6pcm = kzalloc(size: sizeof(*line6pcm), GFP_KERNEL); |
534 | if (!line6pcm) |
535 | return -ENOMEM; |
536 | |
537 | mutex_init(&line6pcm->state_mutex); |
538 | line6pcm->pcm = pcm; |
539 | line6pcm->properties = properties; |
540 | line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; |
541 | line6pcm->volume_monitor = 255; |
542 | line6pcm->line6 = line6; |
543 | |
544 | spin_lock_init(&line6pcm->out.lock); |
545 | spin_lock_init(&line6pcm->in.lock); |
546 | line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; |
547 | |
548 | line6->line6pcm = line6pcm; |
549 | |
550 | pcm->private_data = line6pcm; |
551 | pcm->private_free = line6_cleanup_pcm; |
552 | |
553 | line6pcm->max_packet_size_in = |
554 | usb_maxpacket(udev: line6->usbdev, |
555 | usb_rcvisocpipe(line6->usbdev, ep_read)); |
556 | line6pcm->max_packet_size_out = |
557 | usb_maxpacket(udev: line6->usbdev, |
558 | usb_sndisocpipe(line6->usbdev, ep_write)); |
559 | if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) { |
560 | dev_err(line6pcm->line6->ifcdev, |
561 | "cannot get proper max packet size\n" ); |
562 | return -EINVAL; |
563 | } |
564 | |
565 | err = line6_create_audio_out_urbs(line6pcm); |
566 | if (err < 0) |
567 | return err; |
568 | |
569 | err = line6_create_audio_in_urbs(line6pcm); |
570 | if (err < 0) |
571 | return err; |
572 | |
573 | /* mixer: */ |
574 | for (i = 0; i < ARRAY_SIZE(line6_controls); i++) { |
575 | err = snd_ctl_add(card: line6->card, |
576 | kcontrol: snd_ctl_new1(kcontrolnew: &line6_controls[i], private_data: line6pcm)); |
577 | if (err < 0) |
578 | return err; |
579 | } |
580 | |
581 | return 0; |
582 | } |
583 | EXPORT_SYMBOL_GPL(line6_init_pcm); |
584 | |
585 | /* prepare pcm callback */ |
586 | int snd_line6_prepare(struct snd_pcm_substream *substream) |
587 | { |
588 | struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); |
589 | struct line6_pcm_stream *pstr = get_stream(line6pcm, direction: substream->stream); |
590 | |
591 | mutex_lock(&line6pcm->state_mutex); |
592 | if (!pstr->running) |
593 | line6_wait_clear_audio_urbs(line6pcm, pcms: pstr); |
594 | |
595 | if (!test_and_set_bit(nr: LINE6_FLAG_PREPARED, addr: &line6pcm->flags)) { |
596 | line6pcm->out.count = 0; |
597 | line6pcm->out.pos = 0; |
598 | line6pcm->out.pos_done = 0; |
599 | line6pcm->out.bytes = 0; |
600 | line6pcm->in.count = 0; |
601 | line6pcm->in.pos_done = 0; |
602 | line6pcm->in.bytes = 0; |
603 | } |
604 | |
605 | mutex_unlock(lock: &line6pcm->state_mutex); |
606 | return 0; |
607 | } |
608 | |