blob: 4bbf52bd6025bd07fe266e496242f78d64022b29 [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);
180 else
181 if (state >= state_PRERUNNING) {
182 atomic_inc(&playbacksubs->state);
183 }
184 } else {
185 switch (state) {
186 case state_STARTING1:
187 urb = playbacksubs->urb[0];
188 atomic_inc(&playbacksubs->state);
189 break;
190 case state_STARTING2:
191 urb = playbacksubs->urb[1];
192 atomic_inc(&playbacksubs->state);
193 break;
194 }
195 }
196 if (urb) {
197 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
198 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
199 return err;
200 }
201 }
202
203 playbacksubs->completed_urb = NULL;
204
205 state = atomic_read(&capsubs->state);
206 if (state >= state_PREPARED) {
207 if (state == state_RUNNING) {
208 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
209 return err;
210 } else {
211 if (state >= state_PRERUNNING)
212 atomic_inc(&capsubs->state);
213 }
214 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
215 if (NULL != capsubs2)
216 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
217 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
218 return err;
219 if (NULL != capsubs2)
220 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
221 return err;
222 }
223 capsubs->completed_urb = NULL;
224 if (NULL != capsubs2)
225 capsubs2->completed_urb = NULL;
226 return 0;
227}
228
229
230static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
231{
232 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
233 usX2Ydev_t *usX2Y = subs->usX2Y;
234 snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
235
236 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
237 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);
238 return;
239 }
240 if (unlikely(urb->status)) {
241 usX2Y_error_urb_status(usX2Y, subs, urb);
242 return;
243 }
244 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
245 subs->completed_urb = urb;
246 else {
247 usX2Y_error_sequence(usX2Y, subs, urb);
248 return;
249 }
250
251 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
252 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
253 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
254 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
255 (NULL == capsubs2 || capsubs2->completed_urb) &&
256 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
257 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
258 if (nr_of_packs() <= urb->start_frame &&
259 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
260 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
261 else
262 usX2Y->wait_iso_frame += nr_of_packs();
263 } else {
264 snd_printdd("\n");
265 usX2Y_clients_stop(usX2Y);
266 }
267 }
268}
269
270
271static void usX2Y_hwdep_urb_release(struct urb** urb)
272{
273 usb_kill_urb(*urb);
274 usb_free_urb(*urb);
275 *urb = NULL;
276}
277
278/*
279 * release a substream
280 */
281static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
282{
283 int i;
284 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
285 for (i = 0; i < NRURBS; i++)
286 usX2Y_hwdep_urb_release(subs->urb + i);
287}
288
289static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
290{
291 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
292 usX2Y->prepare_subs = NULL;
293}
294
295static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
296{
297 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
298 usX2Ydev_t *usX2Y = subs->usX2Y;
299 snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
300 if (NULL != prepare_subs &&
301 urb->start_frame == prepare_subs->urb[0]->start_frame) {
302 atomic_inc(&prepare_subs->state);
303 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
304 snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
305 if (cap_subs2 != NULL)
306 atomic_inc(&cap_subs2->state);
307 }
308 usX2Y_usbpcm_subs_startup_finish(usX2Y);
309 wake_up(&usX2Y->prepare_wait_queue);
310 }
311
312 i_usX2Y_usbpcm_urb_complete(urb, regs);
313}
314
315/*
316 * initialize a substream's urbs
317 */
318static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
319{
320 int i;
321 unsigned int pipe;
322 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
323 struct usb_device *dev = subs->usX2Y->chip.dev;
324
325 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
326 usb_rcvisocpipe(dev, subs->endpoint);
327 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
328 if (!subs->maxpacksize)
329 return -EINVAL;
330
331 /* allocate and initialize data urbs */
332 for (i = 0; i < NRURBS; i++) {
333 struct urb** purb = subs->urb + i;
334 if (*purb) {
335 usb_kill_urb(*purb);
336 continue;
337 }
338 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
339 if (NULL == *purb) {
340 usX2Y_usbpcm_urbs_release(subs);
341 return -ENOMEM;
342 }
343 (*purb)->transfer_buffer = is_playback ?
344 subs->usX2Y->hwdep_pcm_shm->playback : (
345 subs->endpoint == 0x8 ?
346 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
347 subs->usX2Y->hwdep_pcm_shm->capture0xA);
348
349 (*purb)->dev = dev;
350 (*purb)->pipe = pipe;
351 (*purb)->number_of_packets = nr_of_packs();
352 (*purb)->context = subs;
353 (*purb)->interval = 1;
354 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
355 }
356 return 0;
357}
358
359/*
360 * free the buffer
361 */
362static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
363{
364 snd_pcm_runtime_t *runtime = substream->runtime;
365 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
366 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
367 down(&subs->usX2Y->prepare_mutex);
368 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
369
370 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
371 snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
372 atomic_set(&subs->state, state_STOPPED);
373 usX2Y_usbpcm_urbs_release(subs);
374 if (!cap_subs->pcm_substream ||
375 !cap_subs->pcm_substream->runtime ||
376 !cap_subs->pcm_substream->runtime->status ||
377 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
378 atomic_set(&cap_subs->state, state_STOPPED);
379 if (NULL != cap_subs2)
380 atomic_set(&cap_subs2->state, state_STOPPED);
381 usX2Y_usbpcm_urbs_release(cap_subs);
382 if (NULL != cap_subs2)
383 usX2Y_usbpcm_urbs_release(cap_subs2);
384 }
385 } else {
386 snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
387 if (atomic_read(&playback_subs->state) < state_PREPARED) {
388 atomic_set(&subs->state, state_STOPPED);
389 if (NULL != cap_subs2)
390 atomic_set(&cap_subs2->state, state_STOPPED);
391 usX2Y_usbpcm_urbs_release(subs);
392 if (NULL != cap_subs2)
393 usX2Y_usbpcm_urbs_release(cap_subs2);
394 }
395 }
396 up(&subs->usX2Y->prepare_mutex);
397 return snd_pcm_lib_free_pages(substream);
398}
399
400static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
401{
402 usX2Ydev_t * usX2Y = subs->usX2Y;
403 usX2Y->prepare_subs = subs;
404 subs->urb[0]->start_frame = -1;
405 smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
406 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
407}
408
409static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
410{
411 int p, u, err,
412 stream = subs->pcm_substream->stream;
413 usX2Ydev_t *usX2Y = subs->usX2Y;
414
415 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
416 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
417 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
418 }
419
420 for (p = 0; 3 >= (stream + p); p += 2) {
421 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
422 if (subs != NULL) {
423 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
424 return err;
425 subs->completed_urb = NULL;
426 }
427 }
428
429 for (p = 0; p < 4; p++) {
430 snd_usX2Y_substream_t *subs = usX2Y->subs[p];
431 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
432 goto start;
433 }
434 usX2Y->wait_iso_frame = -1;
435
436 start:
437 usX2Y_usbpcm_subs_startup(subs);
438 for (u = 0; u < NRURBS; u++) {
439 for (p = 0; 3 >= (stream + p); p += 2) {
440 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
441 if (subs != NULL) {
442 struct urb *urb = subs->urb[u];
443 if (usb_pipein(urb->pipe)) {
444 unsigned long pack;
445 if (0 == u)
446 atomic_set(&subs->state, state_STARTING3);
447 urb->dev = usX2Y->chip.dev;
448 urb->transfer_flags = URB_ISO_ASAP;
449 for (pack = 0; pack < nr_of_packs(); pack++) {
450 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
451 urb->iso_frame_desc[pack].length = subs->maxpacksize;
452 }
453 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
454 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
455 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
456 err = -EPIPE;
457 goto cleanup;
458 } else {
459 snd_printdd("%i\n", urb->start_frame);
460 if (0 > usX2Y->wait_iso_frame)
461 usX2Y->wait_iso_frame = urb->start_frame;
462 }
463 urb->transfer_flags = 0;
464 } else {
465 atomic_set(&subs->state, state_STARTING1);
466 break;
467 }
468 }
469 }
470 }
471 err = 0;
472 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
473 if (atomic_read(&subs->state) != state_PREPARED)
474 err = -EPIPE;
475
476 cleanup:
477 if (err) {
478 usX2Y_subs_startup_finish(usX2Y); // Call it now
479 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
480 }
481 return err;
482}
483
484/*
485 * prepare callback
486 *
487 * set format and initialize urbs
488 */
489static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
490{
491 snd_pcm_runtime_t *runtime = substream->runtime;
492 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
493 usX2Ydev_t *usX2Y = subs->usX2Y;
494 snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
495 int err = 0;
496 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
497
498 if (NULL == usX2Y->hwdep_pcm_shm) {
499 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
500 return -ENOMEM;
501 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
502 }
503
504 down(&usX2Y->prepare_mutex);
505 usX2Y_subs_prepare(subs);
506// Start hardware streams
507// SyncStream first....
508 if (atomic_read(&capsubs->state) < state_PREPARED) {
509 if (usX2Y->format != runtime->format)
510 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
511 goto up_prepare_mutex;
512 if (usX2Y->rate != runtime->rate)
513 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
514 goto up_prepare_mutex;
515 snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe");
516 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
517 goto up_prepare_mutex;
518 }
519
520 if (subs != capsubs) {
521 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
522 if (atomic_read(&subs->state) < state_PREPARED) {
523 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 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 +0200525 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 err = -ERESTARTSYS;
527 goto up_prepare_mutex;
528 }
529 }
530 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
531 goto up_prepare_mutex;
532 }
533 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);
534 } else
535 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
536
537 up_prepare_mutex:
538 up(&usX2Y->prepare_mutex);
539 return err;
540}
541
542static snd_pcm_hardware_t snd_usX2Y_4c =
543{
544 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
545 SNDRV_PCM_INFO_BLOCK_TRANSFER |
546 SNDRV_PCM_INFO_MMAP_VALID),
547 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
548 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
549 .rate_min = 44100,
550 .rate_max = 48000,
551 .channels_min = 2,
552 .channels_max = 4,
553 .buffer_bytes_max = (2*128*1024),
554 .period_bytes_min = 64,
555 .period_bytes_max = (128*1024),
556 .periods_min = 2,
557 .periods_max = 1024,
558 .fifo_size = 0
559};
560
561
562
563static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
564{
565 snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
566 snd_pcm_substream_chip(substream))[substream->stream];
567 snd_pcm_runtime_t *runtime = substream->runtime;
568
569 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
570 return -EBUSY;
571
572 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
573 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
574 runtime->private_data = subs;
575 subs->pcm_substream = substream;
576 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
577 return 0;
578}
579
580
581static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
582{
583 snd_pcm_runtime_t *runtime = substream->runtime;
584 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
585 int err = 0;
586 snd_printd("\n");
587 subs->pcm_substream = NULL;
588 return err;
589}
590
591
592static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
593{
594 .open = snd_usX2Y_usbpcm_open,
595 .close = snd_usX2Y_usbpcm_close,
596 .ioctl = snd_pcm_lib_ioctl,
597 .hw_params = snd_usX2Y_pcm_hw_params,
598 .hw_free = snd_usX2Y_usbpcm_hw_free,
599 .prepare = snd_usX2Y_usbpcm_prepare,
600 .trigger = snd_usX2Y_pcm_trigger,
601 .pointer = snd_usX2Y_pcm_pointer,
602};
603
604
605static int usX2Y_pcms_lock_check(snd_card_t *card)
606{
607 struct list_head *list;
608 snd_device_t *dev;
609 snd_pcm_t *pcm;
610 int err = 0;
611 list_for_each(list, &card->devices) {
612 dev = snd_device(list);
613 if (dev->type != SNDRV_DEV_PCM)
614 continue;
615 pcm = dev->device_data;
616 down(&pcm->open_mutex);
617 }
618 list_for_each(list, &card->devices) {
619 int s;
620 dev = snd_device(list);
621 if (dev->type != SNDRV_DEV_PCM)
622 continue;
623 pcm = dev->device_data;
624 for (s = 0; s < 2; ++s) {
625 snd_pcm_substream_t *substream;
626 substream = pcm->streams[s].substream;
Karsten Wiese443feb82005-08-10 11:18:19 +0200627 if (substream && substream->ffile != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 err = -EBUSY;
629 }
630 }
631 return err;
632}
633
634
635static void usX2Y_pcms_unlock(snd_card_t *card)
636{
637 struct list_head *list;
638 snd_device_t *dev;
639 snd_pcm_t *pcm;
640 list_for_each(list, &card->devices) {
641 dev = snd_device(list);
642 if (dev->type != SNDRV_DEV_PCM)
643 continue;
644 pcm = dev->device_data;
645 up(&pcm->open_mutex);
646 }
647}
648
649
650static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
651{
652 // we need to be the first
653 snd_card_t *card = hw->card;
654 int err = usX2Y_pcms_lock_check(card);
655 if (0 == err)
656 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
657 usX2Y_pcms_unlock(card);
658 return err;
659}
660
661
662static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
663{
664 snd_card_t *card = hw->card;
665 int err = usX2Y_pcms_lock_check(card);
666 if (0 == err)
667 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
668 usX2Y_pcms_unlock(card);
669 return err;
670}
671
672
673static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
674{
675}
676
677
678static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
679{
680}
681
682
683static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
684{
685 unsigned long offset;
686 struct page *page;
687 void *vaddr;
688
689 offset = area->vm_pgoff << PAGE_SHIFT;
690 offset += address - area->vm_start;
691 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
692 vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
693 page = virt_to_page(vaddr);
Hugh Dickins1cdca612005-11-21 21:32:13 -0800694 get_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 if (type)
697 *type = VM_FAULT_MINOR;
698
699 return page;
700}
701
702
703static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
704 .open = snd_usX2Y_hwdep_pcm_vm_open,
705 .close = snd_usX2Y_hwdep_pcm_vm_close,
706 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
707};
708
709
710static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
711{
712 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
713 usX2Ydev_t *usX2Y = (usX2Ydev_t*)hw->private_data;
714
715 if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
716 return -EBUSY;
717
718 /* if userspace tries to mmap beyond end of our buffer, fail */
719 if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
720 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
721 return -EINVAL;
722 }
723
724 if (!usX2Y->hwdep_pcm_shm) {
725 return -ENODEV;
726 }
727 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
728 area->vm_flags |= VM_RESERVED;
729 snd_printd("vm_flags=0x%lX\n", area->vm_flags);
730 area->vm_private_data = hw->private_data;
731 return 0;
732}
733
734
735static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
736{
737 usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
738 if (NULL != usX2Y->hwdep_pcm_shm)
739 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
740}
741
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743int usX2Y_hwdep_pcm_new(snd_card_t* card)
744{
745 int err;
746 snd_hwdep_t *hw;
747 snd_pcm_t *pcm;
748 struct usb_device *dev = usX2Y(card)->chip.dev;
749 if (1 != nr_of_packs())
750 return 0;
751
752 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
753 snd_printd("\n");
754 return err;
755 }
756 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
757 hw->private_data = usX2Y(card);
758 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
759 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
760 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
761 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
762 hw->exclusive = 1;
763 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
764
765 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
766 if (err < 0) {
767 return err;
768 }
769 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
770 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
771
772 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 pcm->info_flags = 0;
774
775 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
776 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
777 SNDRV_DMA_TYPE_CONTINUOUS,
778 snd_dma_continuous_data(GFP_KERNEL),
779 64*1024, 128*1024)) ||
780 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
781 SNDRV_DMA_TYPE_CONTINUOUS,
782 snd_dma_continuous_data(GFP_KERNEL),
783 64*1024, 128*1024))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 return err;
785 }
786
787
788 return 0;
789}
790
791#else
792
793int usX2Y_hwdep_pcm_new(snd_card_t* card)
794{
795 return 0;
796}
797
798#endif