Linux v2.6.13
[linux-2.6/next.git] / sound / usb / usx2y / usx2yhwdeppcm.c
blobef28061287f200c7a6c07d0f841443c9b3388eec
1 /*
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.
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.
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
17 /* USX2Y "rawusb" aka hwdep_pcm implementation
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).
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.
53 #include <linux/delay.h>
54 #include "usbusx2yaudio.c"
56 #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
58 #include <sound/hwdep.h>
61 static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
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);
74 for (i = 0; i < nr_of_packs(); i++) {
75 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
76 snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
77 return urb->iso_frame_desc[i].status;
79 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
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);
90 return 0;
93 static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
95 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
99 * prepare urb for playback data pipe
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.
108 static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
109 struct urb *urb)
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;
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;
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) {
129 snd_printk("should not be here with counts=%i\n", counts);
130 return -EPIPE;
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;
142 urb->transfer_buffer_length = count * usX2Y->stride;
143 return 0;
147 static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
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++;
163 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
164 desc->length >= SSS)
165 desc->offset -= (SSS - desc->length);
169 static 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)
173 int err, state;
174 struct urb *urb = playbacksubs->completed_urb;
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);
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;
196 if (urb) {
197 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
198 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
199 return err;
203 playbacksubs->completed_urb = NULL;
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);
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;
223 capsubs->completed_urb = NULL;
224 if (NULL != capsubs2)
225 capsubs2->completed_urb = NULL;
226 return 0;
230 static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
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;
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;
240 if (unlikely(urb->status)) {
241 usX2Y_error_urb_status(usX2Y, subs, urb);
242 return;
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;
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);
271 static void usX2Y_hwdep_urb_release(struct urb** urb)
273 usb_kill_urb(*urb);
274 usb_free_urb(*urb);
275 *urb = NULL;
279 * release a substream
281 static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
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);
289 static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
291 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
292 usX2Y->prepare_subs = NULL;
295 static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
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);
308 usX2Y_usbpcm_subs_startup_finish(usX2Y);
309 wake_up(&usX2Y->prepare_wait_queue);
312 i_usX2Y_usbpcm_urb_complete(urb, regs);
316 * initialize a substream's urbs
318 static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
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;
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;
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;
338 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
339 if (NULL == *purb) {
340 usX2Y_usbpcm_urbs_release(subs);
341 return -ENOMEM;
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);
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;
356 return 0;
360 * free the buffer
362 static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
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);
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);
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);
396 up(&subs->usX2Y->prepare_mutex);
397 return snd_pcm_lib_free_pages(substream);
400 static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
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);
409 static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
411 int p, u, err,
412 stream = subs->pcm_substream->stream;
413 usX2Ydev_t *usX2Y = subs->usX2Y;
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;
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;
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;
434 usX2Y->wait_iso_frame = -1;
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;
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;
463 urb->transfer_flags = 0;
464 } else {
465 atomic_set(&subs->state, state_STARTING1);
466 break;
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;
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
481 return err;
485 * prepare callback
487 * set format and initialize urbs
489 static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
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);
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));
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;
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) {
524 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);
525 if (msleep_interruptible(10)) {
526 err = -ERESTARTSYS;
527 goto up_prepare_mutex;
530 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
531 goto up_prepare_mutex;
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;
537 up_prepare_mutex:
538 up(&usX2Y->prepare_mutex);
539 return err;
542 static snd_pcm_hardware_t snd_usX2Y_4c =
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
563 static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
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;
569 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
570 return -EBUSY;
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;
581 static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
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;
592 static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
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,
605 static int usX2Y_pcms_lock_check(snd_card_t *card)
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);
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;
627 if (substream && substream->open_flag)
628 err = -EBUSY;
631 return err;
635 static void usX2Y_pcms_unlock(snd_card_t *card)
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);
650 static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
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;
662 static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
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;
673 static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
678 static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
683 static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
685 unsigned long offset;
686 struct page *page;
687 void *vaddr;
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);
695 if (type)
696 *type = VM_FAULT_MINOR;
698 return page;
702 static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
703 .open = snd_usX2Y_hwdep_pcm_vm_open,
704 .close = snd_usX2Y_hwdep_pcm_vm_close,
705 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
709 static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
711 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
712 usX2Ydev_t *usX2Y = (usX2Ydev_t*)hw->private_data;
714 if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
715 return -EBUSY;
717 /* if userspace tries to mmap beyond end of our buffer, fail */
718 if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
719 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
720 return -EINVAL;
723 if (!usX2Y->hwdep_pcm_shm) {
724 return -ENODEV;
726 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
727 area->vm_flags |= VM_RESERVED;
728 snd_printd("vm_flags=0x%lX\n", area->vm_flags);
729 area->vm_private_data = hw->private_data;
730 return 0;
734 static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
736 usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
737 if (NULL != usX2Y->hwdep_pcm_shm)
738 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
742 static void snd_usX2Y_usbpcm_private_free(snd_pcm_t *pcm)
744 snd_pcm_lib_preallocate_free_for_all(pcm);
748 int usX2Y_hwdep_pcm_new(snd_card_t* card)
750 int err;
751 snd_hwdep_t *hw;
752 snd_pcm_t *pcm;
753 struct usb_device *dev = usX2Y(card)->chip.dev;
754 if (1 != nr_of_packs())
755 return 0;
757 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
758 snd_printd("\n");
759 return err;
761 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
762 hw->private_data = usX2Y(card);
763 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
764 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
765 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
766 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
767 hw->exclusive = 1;
768 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
770 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
771 if (err < 0) {
772 return err;
774 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
775 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
777 pcm->private_data = usX2Y(card)->subs;
778 pcm->private_free = snd_usX2Y_usbpcm_private_free;
779 pcm->info_flags = 0;
781 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
782 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
783 SNDRV_DMA_TYPE_CONTINUOUS,
784 snd_dma_continuous_data(GFP_KERNEL),
785 64*1024, 128*1024)) ||
786 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
787 SNDRV_DMA_TYPE_CONTINUOUS,
788 snd_dma_continuous_data(GFP_KERNEL),
789 64*1024, 128*1024))) {
790 snd_usX2Y_usbpcm_private_free(pcm);
791 return err;
795 return 0;
798 #else
800 int usX2Y_hwdep_pcm_new(snd_card_t* card)
802 return 0;
805 #endif