blob: c5379280fab727b3d636f321ec93f546cfd0a0e6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17/* USX2Y "rawusb" aka hwdep_pcm implementation
18
19 Its usb's unableness to atomically handle power of 2 period sized data chuncs
20 at standard samplerates,
21 what led to this part of the usx2y module:
22 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
23 The pair uses a hardware dependant alsa-device for mmaped pcm transport.
24 Advantage achieved:
25 The usb_hc moves pcm data from/into memory via DMA.
26 That memory is mmaped by jack's usx2y driver.
27 Jack's usx2y driver is the first/last to read/write pcm data.
28 Read/write is a combination of power of 2 period shaping and
29 float/int conversation.
30 Compared to mainline alsa/jack we leave out power of 2 period shaping inside
31 snd-usb-usx2y which needs memcpy() and additional buffers.
32 As a side effect possible unwanted pcm-data coruption resulting of
33 standard alsa's snd-usb-usx2y period shaping scheme falls away.
34 Result is sane jack operation at buffering schemes down to 128frames,
35 2 periods.
36 plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
37 cost of easier triggered i.e. aeolus xruns (128 or 256frames,
38 2periods works but is useless cause of crackling).
39
40 This is a first "proof of concept" implementation.
41 Later, funcionalities should migrate to more apropriate places:
42 Userland:
43 - The jackd could mmap its float-pcm buffers directly from alsa-lib.
44 - alsa-lib could provide power of 2 period sized shaping combined with int/float
45 conversation.
46 Currently the usx2y jack driver provides above 2 services.
47 Kernel:
48 - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
49 devices can use it.
50 Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
51*/
52
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +020053#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#include "usbusx2yaudio.c"
55
56#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
57
58#include <sound/hwdep.h>
59
60
61static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
62{
63 struct urb *urb = subs->completed_urb;
64 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
65 int i, lens = 0, hwptr_done = subs->hwptr_done;
66 usX2Ydev_t *usX2Y = subs->usX2Y;
67 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
68 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
69 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
70 head = 0;
71 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
72 snd_printdd("cap start %i\n", head);
73 }
74 for (i = 0; i < nr_of_packs(); i++) {
75 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
Takashi Iwaid3d579f2005-10-21 16:20:11 +020076 snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 return urb->iso_frame_desc[i].status;
78 }
79 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
80 }
81 if ((hwptr_done += lens) >= runtime->buffer_size)
82 hwptr_done -= runtime->buffer_size;
83 subs->hwptr_done = hwptr_done;
84 subs->transfer_done += lens;
85 /* update the pointer, call callback if necessary */
86 if (subs->transfer_done >= runtime->period_size) {
87 subs->transfer_done -= runtime->period_size;
88 snd_pcm_period_elapsed(subs->pcm_substream);
89 }
90 return 0;
91}
92
93static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
94{
95 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
96}
97
98/*
99 * prepare urb for playback data pipe
100 *
101 * we copy the data directly from the pcm buffer.
102 * the current position to be copied is held in hwptr field.
103 * since a urb can handle only a single linear buffer, if the total
104 * transferred area overflows the buffer boundary, we cannot send
105 * it directly from the buffer. thus the data is once copied to
106 * a temporary buffer and urb points to that.
107 */
108static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
109 struct urb *urb)
110{
111 int count, counts, pack;
112 usX2Ydev_t *usX2Y = subs->usX2Y;
113 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
114 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
115
116 if (0 > shm->playback_iso_start) {
117 shm->playback_iso_start = shm->captured_iso_head -
118 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
119 if (0 > shm->playback_iso_start)
120 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
121 shm->playback_iso_head = shm->playback_iso_start;
122 }
123
124 count = 0;
125 for (pack = 0; pack < nr_of_packs(); pack++) {
126 /* calculate the size of a packet */
127 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
128 if (counts < 43 || counts > 50) {
Takashi Iwaid3d579f2005-10-21 16:20:11 +0200129 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 return -EPIPE;
131 }
132 /* set up descriptor */
133 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
134 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
135 if (atomic_read(&subs->state) != state_RUNNING)
136 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
137 urb->iso_frame_desc[pack].length);
138 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
139 shm->playback_iso_head = 0;
140 count += counts;
141 }
142 urb->transfer_buffer_length = count * usX2Y->stride;
143 return 0;
144}
145
146
147static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
148{
149 int pack;
150 for (pack = 0; pack < nr_of_packs(); ++pack) {
151 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
152 if (NULL != subs) {
153 snd_usX2Y_hwdep_pcm_shm_t *shm = subs->usX2Y->hwdep_pcm_shm;
154 int head = shm->captured_iso_head + 1;
155 if (head >= ARRAY_SIZE(shm->captured_iso))
156 head = 0;
157 shm->captured_iso[head].frame = urb->start_frame + pack;
158 shm->captured_iso[head].offset = desc->offset;
159 shm->captured_iso[head].length = desc->actual_length;
160 shm->captured_iso_head = head;
161 shm->captured_iso_frames++;
162 }
163 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
164 desc->length >= SSS)
165 desc->offset -= (SSS - desc->length);
166 }
167}
168
169static inline int usX2Y_usbpcm_usbframe_complete(snd_usX2Y_substream_t *capsubs,
170 snd_usX2Y_substream_t *capsubs2,
171 snd_usX2Y_substream_t *playbacksubs, int frame)
172{
173 int err, state;
174 struct urb *urb = playbacksubs->completed_urb;
175
176 state = atomic_read(&playbacksubs->state);
177 if (NULL != urb) {
178 if (state == state_RUNNING)
179 usX2Y_urb_play_retire(playbacksubs, urb);
Takashi Iwaicb432372005-11-17 10:48:52 +0100180 else if (state >= state_PRERUNNING)
181 atomic_inc(&playbacksubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 } else {
183 switch (state) {
184 case state_STARTING1:
185 urb = playbacksubs->urb[0];
186 atomic_inc(&playbacksubs->state);
187 break;
188 case state_STARTING2:
189 urb = playbacksubs->urb[1];
190 atomic_inc(&playbacksubs->state);
191 break;
192 }
193 }
194 if (urb) {
195 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
196 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
197 return err;
198 }
199 }
200
201 playbacksubs->completed_urb = NULL;
202
203 state = atomic_read(&capsubs->state);
204 if (state >= state_PREPARED) {
205 if (state == state_RUNNING) {
206 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
207 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100208 } else if (state >= state_PRERUNNING)
209 atomic_inc(&capsubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
211 if (NULL != capsubs2)
212 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
213 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
214 return err;
215 if (NULL != capsubs2)
216 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
217 return err;
218 }
219 capsubs->completed_urb = NULL;
220 if (NULL != capsubs2)
221 capsubs2->completed_urb = NULL;
222 return 0;
223}
224
225
226static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
227{
228 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
229 usX2Ydev_t *usX2Y = subs->usX2Y;
230 snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
231
232 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
233 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", usb_get_current_frame_number(usX2Y->chip.dev), subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", urb->status, urb->start_frame);
234 return;
235 }
236 if (unlikely(urb->status)) {
237 usX2Y_error_urb_status(usX2Y, subs, urb);
238 return;
239 }
240 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
241 subs->completed_urb = urb;
242 else {
243 usX2Y_error_sequence(usX2Y, subs, urb);
244 return;
245 }
246
247 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
248 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
249 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
250 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
251 (NULL == capsubs2 || capsubs2->completed_urb) &&
252 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
253 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
254 if (nr_of_packs() <= urb->start_frame &&
255 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
256 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
257 else
258 usX2Y->wait_iso_frame += nr_of_packs();
259 } else {
260 snd_printdd("\n");
261 usX2Y_clients_stop(usX2Y);
262 }
263 }
264}
265
266
267static void usX2Y_hwdep_urb_release(struct urb** urb)
268{
269 usb_kill_urb(*urb);
270 usb_free_urb(*urb);
271 *urb = NULL;
272}
273
274/*
275 * release a substream
276 */
277static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
278{
279 int i;
280 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
281 for (i = 0; i < NRURBS; i++)
282 usX2Y_hwdep_urb_release(subs->urb + i);
283}
284
285static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
286{
287 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
288 usX2Y->prepare_subs = NULL;
289}
290
291static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
292{
293 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
294 usX2Ydev_t *usX2Y = subs->usX2Y;
295 snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
296 if (NULL != prepare_subs &&
297 urb->start_frame == prepare_subs->urb[0]->start_frame) {
298 atomic_inc(&prepare_subs->state);
299 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
300 snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
301 if (cap_subs2 != NULL)
302 atomic_inc(&cap_subs2->state);
303 }
304 usX2Y_usbpcm_subs_startup_finish(usX2Y);
305 wake_up(&usX2Y->prepare_wait_queue);
306 }
307
308 i_usX2Y_usbpcm_urb_complete(urb, regs);
309}
310
311/*
312 * initialize a substream's urbs
313 */
314static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
315{
316 int i;
317 unsigned int pipe;
318 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
319 struct usb_device *dev = subs->usX2Y->chip.dev;
320
321 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
322 usb_rcvisocpipe(dev, subs->endpoint);
323 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
324 if (!subs->maxpacksize)
325 return -EINVAL;
326
327 /* allocate and initialize data urbs */
328 for (i = 0; i < NRURBS; i++) {
Takashi Iwaicb432372005-11-17 10:48:52 +0100329 struct urb **purb = subs->urb + i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 if (*purb) {
331 usb_kill_urb(*purb);
332 continue;
333 }
334 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
335 if (NULL == *purb) {
336 usX2Y_usbpcm_urbs_release(subs);
337 return -ENOMEM;
338 }
339 (*purb)->transfer_buffer = is_playback ?
340 subs->usX2Y->hwdep_pcm_shm->playback : (
341 subs->endpoint == 0x8 ?
342 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
343 subs->usX2Y->hwdep_pcm_shm->capture0xA);
344
345 (*purb)->dev = dev;
346 (*purb)->pipe = pipe;
347 (*purb)->number_of_packets = nr_of_packs();
348 (*purb)->context = subs;
349 (*purb)->interval = 1;
350 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
351 }
352 return 0;
353}
354
355/*
356 * free the buffer
357 */
358static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
359{
360 snd_pcm_runtime_t *runtime = substream->runtime;
361 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
362 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
363 down(&subs->usX2Y->prepare_mutex);
364 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
365
366 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
367 snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
368 atomic_set(&subs->state, state_STOPPED);
369 usX2Y_usbpcm_urbs_release(subs);
370 if (!cap_subs->pcm_substream ||
371 !cap_subs->pcm_substream->runtime ||
372 !cap_subs->pcm_substream->runtime->status ||
373 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
374 atomic_set(&cap_subs->state, state_STOPPED);
375 if (NULL != cap_subs2)
376 atomic_set(&cap_subs2->state, state_STOPPED);
377 usX2Y_usbpcm_urbs_release(cap_subs);
378 if (NULL != cap_subs2)
379 usX2Y_usbpcm_urbs_release(cap_subs2);
380 }
381 } else {
382 snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
383 if (atomic_read(&playback_subs->state) < state_PREPARED) {
384 atomic_set(&subs->state, state_STOPPED);
385 if (NULL != cap_subs2)
386 atomic_set(&cap_subs2->state, state_STOPPED);
387 usX2Y_usbpcm_urbs_release(subs);
388 if (NULL != cap_subs2)
389 usX2Y_usbpcm_urbs_release(cap_subs2);
390 }
391 }
392 up(&subs->usX2Y->prepare_mutex);
393 return snd_pcm_lib_free_pages(substream);
394}
395
396static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
397{
398 usX2Ydev_t * usX2Y = subs->usX2Y;
399 usX2Y->prepare_subs = subs;
400 subs->urb[0]->start_frame = -1;
401 smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
402 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
403}
404
405static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
406{
407 int p, u, err,
408 stream = subs->pcm_substream->stream;
409 usX2Ydev_t *usX2Y = subs->usX2Y;
410
411 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
412 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
413 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
414 }
415
416 for (p = 0; 3 >= (stream + p); p += 2) {
417 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
418 if (subs != NULL) {
419 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
420 return err;
421 subs->completed_urb = NULL;
422 }
423 }
424
425 for (p = 0; p < 4; p++) {
426 snd_usX2Y_substream_t *subs = usX2Y->subs[p];
427 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
428 goto start;
429 }
430 usX2Y->wait_iso_frame = -1;
431
432 start:
433 usX2Y_usbpcm_subs_startup(subs);
434 for (u = 0; u < NRURBS; u++) {
435 for (p = 0; 3 >= (stream + p); p += 2) {
436 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
437 if (subs != NULL) {
438 struct urb *urb = subs->urb[u];
439 if (usb_pipein(urb->pipe)) {
440 unsigned long pack;
441 if (0 == u)
442 atomic_set(&subs->state, state_STARTING3);
443 urb->dev = usX2Y->chip.dev;
444 urb->transfer_flags = URB_ISO_ASAP;
445 for (pack = 0; pack < nr_of_packs(); pack++) {
446 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
447 urb->iso_frame_desc[pack].length = subs->maxpacksize;
448 }
449 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
450 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
451 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
452 err = -EPIPE;
453 goto cleanup;
454 } else {
455 snd_printdd("%i\n", urb->start_frame);
456 if (0 > usX2Y->wait_iso_frame)
457 usX2Y->wait_iso_frame = urb->start_frame;
458 }
459 urb->transfer_flags = 0;
460 } else {
461 atomic_set(&subs->state, state_STARTING1);
462 break;
463 }
464 }
465 }
466 }
467 err = 0;
468 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
469 if (atomic_read(&subs->state) != state_PREPARED)
470 err = -EPIPE;
471
472 cleanup:
473 if (err) {
474 usX2Y_subs_startup_finish(usX2Y); // Call it now
475 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
476 }
477 return err;
478}
479
480/*
481 * prepare callback
482 *
483 * set format and initialize urbs
484 */
485static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
486{
487 snd_pcm_runtime_t *runtime = substream->runtime;
488 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
489 usX2Ydev_t *usX2Y = subs->usX2Y;
490 snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
491 int err = 0;
492 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
493
494 if (NULL == usX2Y->hwdep_pcm_shm) {
495 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
496 return -ENOMEM;
497 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
498 }
499
500 down(&usX2Y->prepare_mutex);
501 usX2Y_subs_prepare(subs);
502// Start hardware streams
503// SyncStream first....
504 if (atomic_read(&capsubs->state) < state_PREPARED) {
505 if (usX2Y->format != runtime->format)
506 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
507 goto up_prepare_mutex;
508 if (usX2Y->rate != runtime->rate)
509 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
510 goto up_prepare_mutex;
511 snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
512 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
513 goto up_prepare_mutex;
514 }
515
516 if (subs != capsubs) {
517 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
518 if (atomic_read(&subs->state) < state_PREPARED) {
519 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +0200521 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 err = -ERESTARTSYS;
523 goto up_prepare_mutex;
524 }
525 }
526 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
527 goto up_prepare_mutex;
528 }
529 snd_printd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames);
530 } else
531 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
532
533 up_prepare_mutex:
534 up(&usX2Y->prepare_mutex);
535 return err;
536}
537
538static snd_pcm_hardware_t snd_usX2Y_4c =
539{
540 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
541 SNDRV_PCM_INFO_BLOCK_TRANSFER |
542 SNDRV_PCM_INFO_MMAP_VALID),
543 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
544 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
545 .rate_min = 44100,
546 .rate_max = 48000,
547 .channels_min = 2,
548 .channels_max = 4,
549 .buffer_bytes_max = (2*128*1024),
550 .period_bytes_min = 64,
551 .period_bytes_max = (128*1024),
552 .periods_min = 2,
553 .periods_max = 1024,
554 .fifo_size = 0
555};
556
557
558
559static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
560{
561 snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
562 snd_pcm_substream_chip(substream))[substream->stream];
563 snd_pcm_runtime_t *runtime = substream->runtime;
564
565 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
566 return -EBUSY;
567
568 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
569 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
570 runtime->private_data = subs;
571 subs->pcm_substream = substream;
572 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
573 return 0;
574}
575
576
577static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
578{
579 snd_pcm_runtime_t *runtime = substream->runtime;
580 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
Takashi Iwaicb432372005-11-17 10:48:52 +0100581
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 subs->pcm_substream = NULL;
Takashi Iwaicb432372005-11-17 10:48:52 +0100583 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}
585
586
587static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
588{
589 .open = snd_usX2Y_usbpcm_open,
590 .close = snd_usX2Y_usbpcm_close,
591 .ioctl = snd_pcm_lib_ioctl,
592 .hw_params = snd_usX2Y_pcm_hw_params,
593 .hw_free = snd_usX2Y_usbpcm_hw_free,
594 .prepare = snd_usX2Y_usbpcm_prepare,
595 .trigger = snd_usX2Y_pcm_trigger,
596 .pointer = snd_usX2Y_pcm_pointer,
597};
598
599
600static int usX2Y_pcms_lock_check(snd_card_t *card)
601{
602 struct list_head *list;
603 snd_device_t *dev;
604 snd_pcm_t *pcm;
605 int err = 0;
606 list_for_each(list, &card->devices) {
607 dev = snd_device(list);
608 if (dev->type != SNDRV_DEV_PCM)
609 continue;
610 pcm = dev->device_data;
611 down(&pcm->open_mutex);
612 }
613 list_for_each(list, &card->devices) {
614 int s;
615 dev = snd_device(list);
616 if (dev->type != SNDRV_DEV_PCM)
617 continue;
618 pcm = dev->device_data;
619 for (s = 0; s < 2; ++s) {
620 snd_pcm_substream_t *substream;
621 substream = pcm->streams[s].substream;
Karsten Wiese443feb82005-08-10 11:18:19 +0200622 if (substream && substream->ffile != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 err = -EBUSY;
624 }
625 }
626 return err;
627}
628
629
630static void usX2Y_pcms_unlock(snd_card_t *card)
631{
632 struct list_head *list;
633 snd_device_t *dev;
634 snd_pcm_t *pcm;
635 list_for_each(list, &card->devices) {
636 dev = snd_device(list);
637 if (dev->type != SNDRV_DEV_PCM)
638 continue;
639 pcm = dev->device_data;
640 up(&pcm->open_mutex);
641 }
642}
643
644
645static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
646{
647 // we need to be the first
648 snd_card_t *card = hw->card;
649 int err = usX2Y_pcms_lock_check(card);
650 if (0 == err)
651 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
652 usX2Y_pcms_unlock(card);
653 return err;
654}
655
656
657static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
658{
659 snd_card_t *card = hw->card;
660 int err = usX2Y_pcms_lock_check(card);
661 if (0 == err)
662 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
663 usX2Y_pcms_unlock(card);
664 return err;
665}
666
667
668static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
669{
670}
671
672
673static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
674{
675}
676
677
678static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
679{
680 unsigned long offset;
681 struct page *page;
682 void *vaddr;
683
684 offset = area->vm_pgoff << PAGE_SHIFT;
685 offset += address - area->vm_start;
686 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
687 vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
688 page = virt_to_page(vaddr);
Hugh Dickins1cdca612005-11-21 21:32:13 -0800689 get_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691 if (type)
692 *type = VM_FAULT_MINOR;
693
694 return page;
695}
696
697
698static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
699 .open = snd_usX2Y_hwdep_pcm_vm_open,
700 .close = snd_usX2Y_hwdep_pcm_vm_close,
701 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
702};
703
704
705static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
706{
707 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
Takashi Iwaicb432372005-11-17 10:48:52 +0100708 usX2Ydev_t *usX2Y = hw->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Takashi Iwaicb432372005-11-17 10:48:52 +0100710 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return -EBUSY;
712
713 /* if userspace tries to mmap beyond end of our buffer, fail */
714 if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
715 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
716 return -EINVAL;
717 }
718
719 if (!usX2Y->hwdep_pcm_shm) {
720 return -ENODEV;
721 }
722 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
723 area->vm_flags |= VM_RESERVED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 area->vm_private_data = hw->private_data;
725 return 0;
726}
727
728
729static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
730{
Takashi Iwaicb432372005-11-17 10:48:52 +0100731 usX2Ydev_t *usX2Y = hwdep->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 if (NULL != usX2Y->hwdep_pcm_shm)
733 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
734}
735
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737int usX2Y_hwdep_pcm_new(snd_card_t* card)
738{
739 int err;
740 snd_hwdep_t *hw;
741 snd_pcm_t *pcm;
742 struct usb_device *dev = usX2Y(card)->chip.dev;
743 if (1 != nr_of_packs())
744 return 0;
745
Takashi Iwaicb432372005-11-17 10:48:52 +0100746 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
750 hw->private_data = usX2Y(card);
751 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
752 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
753 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
754 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
755 hw->exclusive = 1;
756 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
757
758 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
759 if (err < 0) {
760 return err;
761 }
762 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
763 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
764
765 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 pcm->info_flags = 0;
767
768 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
769 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
770 SNDRV_DMA_TYPE_CONTINUOUS,
771 snd_dma_continuous_data(GFP_KERNEL),
772 64*1024, 128*1024)) ||
773 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
774 SNDRV_DMA_TYPE_CONTINUOUS,
775 snd_dma_continuous_data(GFP_KERNEL),
776 64*1024, 128*1024))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 return err;
778 }
779
780
781 return 0;
782}
783
784#else
785
786int usX2Y_hwdep_pcm_new(snd_card_t* card)
787{
788 return 0;
789}
790
791#endif