GUI: Fix Tomato RAF theme for all builds. Compilation typo.
[tomato.git] / release / src-rt-6.x.4708 / linux / linux-2.6.36 / sound / usb / usx2y / usx2yhwdeppcm.c
blob431cc12983489a481f71faa05d3c031e860efe79
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 <linux/gfp.h>
55 #include "usbusx2yaudio.c"
57 #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && \
58 USX2Y_NRPACKS == 1)
60 #include <sound/hwdep.h>
63 static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
65 struct urb *urb = subs->completed_urb;
66 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
67 int i, lens = 0, hwptr_done = subs->hwptr_done;
68 struct usX2Ydev *usX2Y = subs->usX2Y;
69 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) {
70 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
71 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
72 head = 0;
73 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
74 snd_printdd("cap start %i\n", head);
76 for (i = 0; i < nr_of_packs(); i++) {
77 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
78 snd_printk(KERN_ERR "activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
79 return urb->iso_frame_desc[i].status;
81 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
83 if ((hwptr_done += lens) >= runtime->buffer_size)
84 hwptr_done -= runtime->buffer_size;
85 subs->hwptr_done = hwptr_done;
86 subs->transfer_done += lens;
87 /* update the pointer, call callback if necessary */
88 if (subs->transfer_done >= runtime->period_size) {
89 subs->transfer_done -= runtime->period_size;
90 snd_pcm_period_elapsed(subs->pcm_substream);
92 return 0;
95 static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
96 struct usX2Ydev * usX2Y)
98 return (runtime->buffer_size * 1000) / usX2Y->rate + 1;
102 * prepare urb for playback data pipe
104 * we copy the data directly from the pcm buffer.
105 * the current position to be copied is held in hwptr field.
106 * since a urb can handle only a single linear buffer, if the total
107 * transferred area overflows the buffer boundary, we cannot send
108 * it directly from the buffer. thus the data is once copied to
109 * a temporary buffer and urb points to that.
111 static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
112 struct urb *urb)
114 int count, counts, pack;
115 struct usX2Ydev *usX2Y = subs->usX2Y;
116 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
117 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
119 if (0 > shm->playback_iso_start) {
120 shm->playback_iso_start = shm->captured_iso_head -
121 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
122 if (0 > shm->playback_iso_start)
123 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
124 shm->playback_iso_head = shm->playback_iso_start;
127 count = 0;
128 for (pack = 0; pack < nr_of_packs(); pack++) {
129 /* calculate the size of a packet */
130 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
131 if (counts < 43 || counts > 50) {
132 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
133 return -EPIPE;
135 /* set up descriptor */
136 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
137 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
138 if (atomic_read(&subs->state) != state_RUNNING)
139 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
140 urb->iso_frame_desc[pack].length);
141 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
142 shm->playback_iso_head = 0;
143 count += counts;
145 urb->transfer_buffer_length = count * usX2Y->stride;
146 return 0;
150 static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
151 struct urb *urb)
153 int pack;
154 for (pack = 0; pack < nr_of_packs(); ++pack) {
155 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
156 if (NULL != subs) {
157 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
158 int head = shm->captured_iso_head + 1;
159 if (head >= ARRAY_SIZE(shm->captured_iso))
160 head = 0;
161 shm->captured_iso[head].frame = urb->start_frame + pack;
162 shm->captured_iso[head].offset = desc->offset;
163 shm->captured_iso[head].length = desc->actual_length;
164 shm->captured_iso_head = head;
165 shm->captured_iso_frames++;
167 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
168 desc->length >= SSS)
169 desc->offset -= (SSS - desc->length);
173 static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
174 struct snd_usX2Y_substream *capsubs2,
175 struct snd_usX2Y_substream *playbacksubs,
176 int frame)
178 int err, state;
179 struct urb *urb = playbacksubs->completed_urb;
181 state = atomic_read(&playbacksubs->state);
182 if (NULL != urb) {
183 if (state == state_RUNNING)
184 usX2Y_urb_play_retire(playbacksubs, urb);
185 else if (state >= state_PRERUNNING)
186 atomic_inc(&playbacksubs->state);
187 } else {
188 switch (state) {
189 case state_STARTING1:
190 urb = playbacksubs->urb[0];
191 atomic_inc(&playbacksubs->state);
192 break;
193 case state_STARTING2:
194 urb = playbacksubs->urb[1];
195 atomic_inc(&playbacksubs->state);
196 break;
199 if (urb) {
200 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
201 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
202 return err;
206 playbacksubs->completed_urb = NULL;
208 state = atomic_read(&capsubs->state);
209 if (state >= state_PREPARED) {
210 if (state == state_RUNNING) {
211 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
212 return err;
213 } else if (state >= state_PRERUNNING)
214 atomic_inc(&capsubs->state);
215 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
216 if (NULL != capsubs2)
217 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
218 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
219 return err;
220 if (NULL != capsubs2)
221 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
222 return err;
224 capsubs->completed_urb = NULL;
225 if (NULL != capsubs2)
226 capsubs2->completed_urb = NULL;
227 return 0;
231 static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
233 struct snd_usX2Y_substream *subs = urb->context;
234 struct usX2Ydev *usX2Y = subs->usX2Y;
235 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
237 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
238 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
239 usb_get_current_frame_number(usX2Y->dev),
240 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
241 urb->status, urb->start_frame);
242 return;
244 if (unlikely(urb->status)) {
245 usX2Y_error_urb_status(usX2Y, subs, urb);
246 return;
248 if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
249 subs->completed_urb = urb;
250 else {
251 usX2Y_error_sequence(usX2Y, subs, urb);
252 return;
255 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
256 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
257 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
258 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
259 (NULL == capsubs2 || capsubs2->completed_urb) &&
260 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
261 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
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(struct snd_usX2Y_substream *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(struct usX2Ydev * 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)
297 struct snd_usX2Y_substream *subs = urb->context;
298 struct usX2Ydev *usX2Y = subs->usX2Y;
299 struct snd_usX2Y_substream *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 struct snd_usX2Y_substream *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);
316 * initialize a substream's urbs
318 static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *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->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(struct snd_pcm_substream *substream)
364 struct snd_pcm_runtime *runtime = substream->runtime;
365 struct snd_usX2Y_substream *subs = runtime->private_data,
366 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
367 mutex_lock(&subs->usX2Y->prepare_mutex);
368 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
370 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
371 struct snd_usX2Y_substream *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 struct snd_usX2Y_substream *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 mutex_unlock(&subs->usX2Y->prepare_mutex);
397 return snd_pcm_lib_free_pages(substream);
400 static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
402 struct usX2Ydev * usX2Y = subs->usX2Y;
403 usX2Y->prepare_subs = subs;
404 subs->urb[0]->start_frame = -1;
405 smp_wmb(); // Make sure 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(struct snd_usX2Y_substream *subs)
411 int p, u, err,
412 stream = subs->pcm_substream->stream;
413 struct usX2Ydev *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 struct snd_usX2Y_substream *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 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
431 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
432 goto start;
435 start:
436 usX2Y_usbpcm_subs_startup(subs);
437 for (u = 0; u < NRURBS; u++) {
438 for (p = 0; 3 >= (stream + p); p += 2) {
439 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
440 if (subs != NULL) {
441 struct urb *urb = subs->urb[u];
442 if (usb_pipein(urb->pipe)) {
443 unsigned long pack;
444 if (0 == u)
445 atomic_set(&subs->state, state_STARTING3);
446 urb->dev = usX2Y->dev;
447 urb->transfer_flags = URB_ISO_ASAP;
448 for (pack = 0; pack < nr_of_packs(); pack++) {
449 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
450 urb->iso_frame_desc[pack].length = subs->maxpacksize;
452 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
453 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
454 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
455 err = -EPIPE;
456 goto cleanup;
457 } else {
458 snd_printdd("%i\n", urb->start_frame);
459 if (u == 0)
460 usX2Y->wait_iso_frame = urb->start_frame;
462 urb->transfer_flags = 0;
463 } else {
464 atomic_set(&subs->state, state_STARTING1);
465 break;
470 err = 0;
471 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
472 if (atomic_read(&subs->state) != state_PREPARED)
473 err = -EPIPE;
475 cleanup:
476 if (err) {
477 usX2Y_subs_startup_finish(usX2Y); // Call it now
478 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
480 return err;
484 * prepare callback
486 * set format and initialize urbs
488 static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
490 struct snd_pcm_runtime *runtime = substream->runtime;
491 struct snd_usX2Y_substream *subs = runtime->private_data;
492 struct usX2Ydev *usX2Y = subs->usX2Y;
493 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
494 int err = 0;
495 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
497 if (NULL == usX2Y->hwdep_pcm_shm) {
498 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
499 return -ENOMEM;
500 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
503 mutex_lock(&usX2Y->prepare_mutex);
504 usX2Y_subs_prepare(subs);
505 // Start hardware streams
506 // SyncStream first....
507 if (atomic_read(&capsubs->state) < state_PREPARED) {
508 if (usX2Y->format != runtime->format)
509 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
510 goto up_prepare_mutex;
511 if (usX2Y->rate != runtime->rate)
512 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
513 goto up_prepare_mutex;
514 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
515 "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) >
524 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
525 snd_printdd("Wait: iso_frames_per_buffer=%i,"
526 "captured_iso_frames=%i\n",
527 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
528 usX2Y->hwdep_pcm_shm->captured_iso_frames);
529 if (msleep_interruptible(10)) {
530 err = -ERESTARTSYS;
531 goto up_prepare_mutex;
534 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
535 goto up_prepare_mutex;
537 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
538 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
539 usX2Y->hwdep_pcm_shm->captured_iso_frames);
540 } else
541 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
543 up_prepare_mutex:
544 mutex_unlock(&usX2Y->prepare_mutex);
545 return err;
548 static struct snd_pcm_hardware snd_usX2Y_4c =
550 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
551 SNDRV_PCM_INFO_BLOCK_TRANSFER |
552 SNDRV_PCM_INFO_MMAP_VALID),
553 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
554 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
555 .rate_min = 44100,
556 .rate_max = 48000,
557 .channels_min = 2,
558 .channels_max = 4,
559 .buffer_bytes_max = (2*128*1024),
560 .period_bytes_min = 64,
561 .period_bytes_max = (128*1024),
562 .periods_min = 2,
563 .periods_max = 1024,
564 .fifo_size = 0
569 static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
571 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
572 snd_pcm_substream_chip(substream))[substream->stream];
573 struct snd_pcm_runtime *runtime = substream->runtime;
575 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
576 return -EBUSY;
578 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
579 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
580 runtime->private_data = subs;
581 subs->pcm_substream = substream;
582 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
583 return 0;
587 static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
589 struct snd_pcm_runtime *runtime = substream->runtime;
590 struct snd_usX2Y_substream *subs = runtime->private_data;
592 subs->pcm_substream = NULL;
593 return 0;
597 static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
599 .open = snd_usX2Y_usbpcm_open,
600 .close = snd_usX2Y_usbpcm_close,
601 .ioctl = snd_pcm_lib_ioctl,
602 .hw_params = snd_usX2Y_pcm_hw_params,
603 .hw_free = snd_usX2Y_usbpcm_hw_free,
604 .prepare = snd_usX2Y_usbpcm_prepare,
605 .trigger = snd_usX2Y_pcm_trigger,
606 .pointer = snd_usX2Y_pcm_pointer,
610 static int usX2Y_pcms_lock_check(struct snd_card *card)
612 struct list_head *list;
613 struct snd_device *dev;
614 struct snd_pcm *pcm;
615 int err = 0;
616 list_for_each(list, &card->devices) {
617 dev = snd_device(list);
618 if (dev->type != SNDRV_DEV_PCM)
619 continue;
620 pcm = dev->device_data;
621 mutex_lock(&pcm->open_mutex);
623 list_for_each(list, &card->devices) {
624 int s;
625 dev = snd_device(list);
626 if (dev->type != SNDRV_DEV_PCM)
627 continue;
628 pcm = dev->device_data;
629 for (s = 0; s < 2; ++s) {
630 struct snd_pcm_substream *substream;
631 substream = pcm->streams[s].substream;
632 if (substream && SUBSTREAM_BUSY(substream))
633 err = -EBUSY;
636 return err;
640 static void usX2Y_pcms_unlock(struct snd_card *card)
642 struct list_head *list;
643 struct snd_device *dev;
644 struct snd_pcm *pcm;
645 list_for_each(list, &card->devices) {
646 dev = snd_device(list);
647 if (dev->type != SNDRV_DEV_PCM)
648 continue;
649 pcm = dev->device_data;
650 mutex_unlock(&pcm->open_mutex);
655 static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
657 // we need to be the first
658 struct snd_card *card = hw->card;
659 int err = usX2Y_pcms_lock_check(card);
660 if (0 == err)
661 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
662 usX2Y_pcms_unlock(card);
663 return err;
667 static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
669 struct snd_card *card = hw->card;
670 int err = usX2Y_pcms_lock_check(card);
671 if (0 == err)
672 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
673 usX2Y_pcms_unlock(card);
674 return err;
678 static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
683 static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
688 static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
689 struct vm_fault *vmf)
691 unsigned long offset;
692 void *vaddr;
694 offset = vmf->pgoff << PAGE_SHIFT;
695 vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
696 vmf->page = virt_to_page(vaddr);
697 get_page(vmf->page);
698 return 0;
702 static const 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 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
709 static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
711 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
712 struct usX2Ydev *usX2Y = hw->private_data;
714 if (!(usX2Y->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(struct snd_usX2Y_hwdep_pcm_shm))) {
719 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
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 | VM_DONTEXPAND;
728 area->vm_private_data = hw->private_data;
729 return 0;
733 static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
735 struct usX2Ydev *usX2Y = hwdep->private_data;
736 if (NULL != usX2Y->hwdep_pcm_shm)
737 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
741 int usX2Y_hwdep_pcm_new(struct snd_card *card)
743 int err;
744 struct snd_hwdep *hw;
745 struct snd_pcm *pcm;
746 struct usb_device *dev = usX2Y(card)->dev;
747 if (1 != nr_of_packs())
748 return 0;
750 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
751 return err;
753 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
754 hw->private_data = usX2Y(card);
755 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
756 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
757 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
758 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
759 hw->exclusive = 1;
760 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
762 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
763 if (err < 0) {
764 return err;
766 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
767 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
769 pcm->private_data = usX2Y(card)->subs;
770 pcm->info_flags = 0;
772 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
773 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
774 SNDRV_DMA_TYPE_CONTINUOUS,
775 snd_dma_continuous_data(GFP_KERNEL),
776 64*1024, 128*1024)) ||
777 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
778 SNDRV_DMA_TYPE_CONTINUOUS,
779 snd_dma_continuous_data(GFP_KERNEL),
780 64*1024, 128*1024))) {
781 return err;
785 return 0;
788 #else
790 int usX2Y_hwdep_pcm_new(struct snd_card *card)
792 return 0;
795 #endif