aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/usx2y/usx2yhwdeppcm.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/usb/usx2y/usx2yhwdeppcm.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/usb/usx2y/usx2yhwdeppcm.c')
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c807
1 files changed, 807 insertions, 0 deletions
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
new file mode 100644
index 000000000000..bb2c8e9000c6
--- /dev/null
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -0,0 +1,807 @@
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.
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
53#include "usbusx2yaudio.c"
54
55#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
56
57#include <sound/hwdep.h>
58
59
60static int usX2Y_usbpcm_urb_capt_retire(snd_usX2Y_substream_t *subs)
61{
62 struct urb *urb = subs->completed_urb;
63 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
64 int i, lens = 0, hwptr_done = subs->hwptr_done;
65 usX2Ydev_t *usX2Y = subs->usX2Y;
66 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
67 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
68 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
69 head = 0;
70 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
71 snd_printdd("cap start %i\n", head);
72 }
73 for (i = 0; i < nr_of_packs(); i++) {
74 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
75 snd_printk("activ frame status %i. Most propably some hardware problem.\n", urb->iso_frame_desc[i].status);
76 return urb->iso_frame_desc[i].status;
77 }
78 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
79 }
80 if ((hwptr_done += lens) >= runtime->buffer_size)
81 hwptr_done -= runtime->buffer_size;
82 subs->hwptr_done = hwptr_done;
83 subs->transfer_done += lens;
84 /* update the pointer, call callback if necessary */
85 if (subs->transfer_done >= runtime->period_size) {
86 subs->transfer_done -= runtime->period_size;
87 snd_pcm_period_elapsed(subs->pcm_substream);
88 }
89 return 0;
90}
91
92static inline int usX2Y_iso_frames_per_buffer(snd_pcm_runtime_t *runtime, usX2Ydev_t * usX2Y)
93{
94 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
95}
96
97/*
98 * prepare urb for playback data pipe
99 *
100 * we copy the data directly from the pcm buffer.
101 * the current position to be copied is held in hwptr field.
102 * since a urb can handle only a single linear buffer, if the total
103 * transferred area overflows the buffer boundary, we cannot send
104 * it directly from the buffer. thus the data is once copied to
105 * a temporary buffer and urb points to that.
106 */
107static int usX2Y_hwdep_urb_play_prepare(snd_usX2Y_substream_t *subs,
108 struct urb *urb)
109{
110 int count, counts, pack;
111 usX2Ydev_t *usX2Y = subs->usX2Y;
112 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
113 snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime;
114
115 if (0 > shm->playback_iso_start) {
116 shm->playback_iso_start = shm->captured_iso_head -
117 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
118 if (0 > shm->playback_iso_start)
119 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
120 shm->playback_iso_head = shm->playback_iso_start;
121 }
122
123 count = 0;
124 for (pack = 0; pack < nr_of_packs(); pack++) {
125 /* calculate the size of a packet */
126 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
127 if (counts < 43 || counts > 50) {
128 snd_printk("should not be here with counts=%i\n", counts);
129 return -EPIPE;
130 }
131 /* set up descriptor */
132 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
133 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
134 if (atomic_read(&subs->state) != state_RUNNING)
135 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
136 urb->iso_frame_desc[pack].length);
137 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
138 shm->playback_iso_head = 0;
139 count += counts;
140 }
141 urb->transfer_buffer_length = count * usX2Y->stride;
142 return 0;
143}
144
145
146static inline void usX2Y_usbpcm_urb_capt_iso_advance(snd_usX2Y_substream_t *subs, struct urb *urb)
147{
148 int pack;
149 for (pack = 0; pack < nr_of_packs(); ++pack) {
150 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
151 if (NULL != subs) {
152 snd_usX2Y_hwdep_pcm_shm_t *shm = subs->usX2Y->hwdep_pcm_shm;
153 int head = shm->captured_iso_head + 1;
154 if (head >= ARRAY_SIZE(shm->captured_iso))
155 head = 0;
156 shm->captured_iso[head].frame = urb->start_frame + pack;
157 shm->captured_iso[head].offset = desc->offset;
158 shm->captured_iso[head].length = desc->actual_length;
159 shm->captured_iso_head = head;
160 shm->captured_iso_frames++;
161 }
162 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
163 desc->length >= SSS)
164 desc->offset -= (SSS - desc->length);
165 }
166}
167
168static inline int usX2Y_usbpcm_usbframe_complete(snd_usX2Y_substream_t *capsubs,
169 snd_usX2Y_substream_t *capsubs2,
170 snd_usX2Y_substream_t *playbacksubs, int frame)
171{
172 int err, state;
173 struct urb *urb = playbacksubs->completed_urb;
174
175 state = atomic_read(&playbacksubs->state);
176 if (NULL != urb) {
177 if (state == state_RUNNING)
178 usX2Y_urb_play_retire(playbacksubs, urb);
179 else
180 if (state >= state_PRERUNNING) {
181 atomic_inc(&playbacksubs->state);
182 }
183 } else {
184 switch (state) {
185 case state_STARTING1:
186 urb = playbacksubs->urb[0];
187 atomic_inc(&playbacksubs->state);
188 break;
189 case state_STARTING2:
190 urb = playbacksubs->urb[1];
191 atomic_inc(&playbacksubs->state);
192 break;
193 }
194 }
195 if (urb) {
196 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
197 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
198 return err;
199 }
200 }
201
202 playbacksubs->completed_urb = NULL;
203
204 state = atomic_read(&capsubs->state);
205 if (state >= state_PREPARED) {
206 if (state == state_RUNNING) {
207 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
208 return err;
209 } else {
210 if (state >= state_PRERUNNING)
211 atomic_inc(&capsubs->state);
212 }
213 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
214 if (NULL != capsubs2)
215 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
216 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
217 return err;
218 if (NULL != capsubs2)
219 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
220 return err;
221 }
222 capsubs->completed_urb = NULL;
223 if (NULL != capsubs2)
224 capsubs2->completed_urb = NULL;
225 return 0;
226}
227
228
229static void i_usX2Y_usbpcm_urb_complete(struct urb *urb, struct pt_regs *regs)
230{
231 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
232 usX2Ydev_t *usX2Y = subs->usX2Y;
233 snd_usX2Y_substream_t *capsubs, *capsubs2, *playbacksubs;
234
235 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
236 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);
237 return;
238 }
239 if (unlikely(urb->status)) {
240 usX2Y_error_urb_status(usX2Y, subs, urb);
241 return;
242 }
243 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
244 subs->completed_urb = urb;
245 else {
246 usX2Y_error_sequence(usX2Y, subs, urb);
247 return;
248 }
249
250 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
251 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
252 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
253 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
254 (NULL == capsubs2 || capsubs2->completed_urb) &&
255 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
256 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
257 if (nr_of_packs() <= urb->start_frame &&
258 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
259 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
260 else
261 usX2Y->wait_iso_frame += nr_of_packs();
262 } else {
263 snd_printdd("\n");
264 usX2Y_clients_stop(usX2Y);
265 }
266 }
267}
268
269
270static void usX2Y_hwdep_urb_release(struct urb** urb)
271{
272 usb_kill_urb(*urb);
273 usb_free_urb(*urb);
274 *urb = NULL;
275}
276
277/*
278 * release a substream
279 */
280static void usX2Y_usbpcm_urbs_release(snd_usX2Y_substream_t *subs)
281{
282 int i;
283 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
284 for (i = 0; i < NRURBS; i++)
285 usX2Y_hwdep_urb_release(subs->urb + i);
286}
287
288static void usX2Y_usbpcm_subs_startup_finish(usX2Ydev_t * usX2Y)
289{
290 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
291 usX2Y->prepare_subs = NULL;
292}
293
294static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
295{
296 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context;
297 usX2Ydev_t *usX2Y = subs->usX2Y;
298 snd_usX2Y_substream_t *prepare_subs = usX2Y->prepare_subs;
299 if (NULL != prepare_subs &&
300 urb->start_frame == prepare_subs->urb[0]->start_frame) {
301 atomic_inc(&prepare_subs->state);
302 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
303 snd_usX2Y_substream_t *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
304 if (cap_subs2 != NULL)
305 atomic_inc(&cap_subs2->state);
306 }
307 usX2Y_usbpcm_subs_startup_finish(usX2Y);
308 wake_up(&usX2Y->prepare_wait_queue);
309 }
310
311 i_usX2Y_usbpcm_urb_complete(urb, regs);
312}
313
314/*
315 * initialize a substream's urbs
316 */
317static int usX2Y_usbpcm_urbs_allocate(snd_usX2Y_substream_t *subs)
318{
319 int i;
320 unsigned int pipe;
321 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
322 struct usb_device *dev = subs->usX2Y->chip.dev;
323
324 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
325 usb_rcvisocpipe(dev, subs->endpoint);
326 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
327 if (!subs->maxpacksize)
328 return -EINVAL;
329
330 /* allocate and initialize data urbs */
331 for (i = 0; i < NRURBS; i++) {
332 struct urb** purb = subs->urb + i;
333 if (*purb) {
334 usb_kill_urb(*purb);
335 continue;
336 }
337 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
338 if (NULL == *purb) {
339 usX2Y_usbpcm_urbs_release(subs);
340 return -ENOMEM;
341 }
342 (*purb)->transfer_buffer = is_playback ?
343 subs->usX2Y->hwdep_pcm_shm->playback : (
344 subs->endpoint == 0x8 ?
345 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
346 subs->usX2Y->hwdep_pcm_shm->capture0xA);
347
348 (*purb)->dev = dev;
349 (*purb)->pipe = pipe;
350 (*purb)->number_of_packets = nr_of_packs();
351 (*purb)->context = subs;
352 (*purb)->interval = 1;
353 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
354 }
355 return 0;
356}
357
358/*
359 * free the buffer
360 */
361static int snd_usX2Y_usbpcm_hw_free(snd_pcm_substream_t *substream)
362{
363 snd_pcm_runtime_t *runtime = substream->runtime;
364 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data,
365 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
366 down(&subs->usX2Y->prepare_mutex);
367 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
368
369 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
370 snd_usX2Y_substream_t *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
371 atomic_set(&subs->state, state_STOPPED);
372 usX2Y_usbpcm_urbs_release(subs);
373 if (!cap_subs->pcm_substream ||
374 !cap_subs->pcm_substream->runtime ||
375 !cap_subs->pcm_substream->runtime->status ||
376 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
377 atomic_set(&cap_subs->state, state_STOPPED);
378 if (NULL != cap_subs2)
379 atomic_set(&cap_subs2->state, state_STOPPED);
380 usX2Y_usbpcm_urbs_release(cap_subs);
381 if (NULL != cap_subs2)
382 usX2Y_usbpcm_urbs_release(cap_subs2);
383 }
384 } else {
385 snd_usX2Y_substream_t *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
386 if (atomic_read(&playback_subs->state) < state_PREPARED) {
387 atomic_set(&subs->state, state_STOPPED);
388 if (NULL != cap_subs2)
389 atomic_set(&cap_subs2->state, state_STOPPED);
390 usX2Y_usbpcm_urbs_release(subs);
391 if (NULL != cap_subs2)
392 usX2Y_usbpcm_urbs_release(cap_subs2);
393 }
394 }
395 up(&subs->usX2Y->prepare_mutex);
396 return snd_pcm_lib_free_pages(substream);
397}
398
399static void usX2Y_usbpcm_subs_startup(snd_usX2Y_substream_t *subs)
400{
401 usX2Ydev_t * usX2Y = subs->usX2Y;
402 usX2Y->prepare_subs = subs;
403 subs->urb[0]->start_frame = -1;
404 smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
405 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
406}
407
408static int usX2Y_usbpcm_urbs_start(snd_usX2Y_substream_t *subs)
409{
410 int p, u, err,
411 stream = subs->pcm_substream->stream;
412 usX2Ydev_t *usX2Y = subs->usX2Y;
413
414 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
415 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
416 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
417 }
418
419 for (p = 0; 3 >= (stream + p); p += 2) {
420 snd_usX2Y_substream_t *subs = usX2Y->subs[stream + p];
421 if (subs != NULL) {
422 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
423 return err;
424 subs->completed_urb = NULL;
425 }
426 }
427
428 for (p = 0; p < 4; p++) {
429 snd_usX2Y_substream_t *subs = usX2Y->subs[p];
430 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
431 goto start;
432 }
433 usX2Y->wait_iso_frame = -1;
434
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 snd_usX2Y_substream_t *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->chip.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;
451 }
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 (0 > usX2Y->wait_iso_frame)
460 usX2Y->wait_iso_frame = urb->start_frame;
461 }
462 urb->transfer_flags = 0;
463 } else {
464 atomic_set(&subs->state, state_STARTING1);
465 break;
466 }
467 }
468 }
469 }
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;
474
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
479 }
480 return err;
481}
482
483/*
484 * prepare callback
485 *
486 * set format and initialize urbs
487 */
488static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream)
489{
490 snd_pcm_runtime_t *runtime = substream->runtime;
491 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
492 usX2Ydev_t *usX2Y = subs->usX2Y;
493 snd_usX2Y_substream_t *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
494 int err = 0;
495 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
496
497 if (NULL == usX2Y->hwdep_pcm_shm) {
498 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(snd_usX2Y_hwdep_pcm_shm_t), GFP_KERNEL)))
499 return -ENOMEM;
500 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
501 }
502
503 down(&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 ? "self" : "playpipe");
515 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
516 goto up_prepare_mutex;
517 }
518
519 if (subs != capsubs) {
520 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
521 if (atomic_read(&subs->state) < state_PREPARED) {
522 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) {
523 signed long timeout;
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 set_current_state(TASK_INTERRUPTIBLE);
526 timeout = schedule_timeout(HZ/100 + 1);
527 if (signal_pending(current)) {
528 err = -ERESTARTSYS;
529 goto up_prepare_mutex;
530 }
531 }
532 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
533 goto up_prepare_mutex;
534 }
535 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);
536 } else
537 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
538
539 up_prepare_mutex:
540 up(&usX2Y->prepare_mutex);
541 return err;
542}
543
544static snd_pcm_hardware_t snd_usX2Y_4c =
545{
546 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
547 SNDRV_PCM_INFO_BLOCK_TRANSFER |
548 SNDRV_PCM_INFO_MMAP_VALID),
549 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
550 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
551 .rate_min = 44100,
552 .rate_max = 48000,
553 .channels_min = 2,
554 .channels_max = 4,
555 .buffer_bytes_max = (2*128*1024),
556 .period_bytes_min = 64,
557 .period_bytes_max = (128*1024),
558 .periods_min = 2,
559 .periods_max = 1024,
560 .fifo_size = 0
561};
562
563
564
565static int snd_usX2Y_usbpcm_open(snd_pcm_substream_t *substream)
566{
567 snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **)
568 snd_pcm_substream_chip(substream))[substream->stream];
569 snd_pcm_runtime_t *runtime = substream->runtime;
570
571 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
572 return -EBUSY;
573
574 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
575 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
576 runtime->private_data = subs;
577 subs->pcm_substream = substream;
578 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
579 return 0;
580}
581
582
583static int snd_usX2Y_usbpcm_close(snd_pcm_substream_t *substream)
584{
585 snd_pcm_runtime_t *runtime = substream->runtime;
586 snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data;
587 int err = 0;
588 snd_printd("\n");
589 subs->pcm_substream = NULL;
590 return err;
591}
592
593
594static snd_pcm_ops_t snd_usX2Y_usbpcm_ops =
595{
596 .open = snd_usX2Y_usbpcm_open,
597 .close = snd_usX2Y_usbpcm_close,
598 .ioctl = snd_pcm_lib_ioctl,
599 .hw_params = snd_usX2Y_pcm_hw_params,
600 .hw_free = snd_usX2Y_usbpcm_hw_free,
601 .prepare = snd_usX2Y_usbpcm_prepare,
602 .trigger = snd_usX2Y_pcm_trigger,
603 .pointer = snd_usX2Y_pcm_pointer,
604};
605
606
607static int usX2Y_pcms_lock_check(snd_card_t *card)
608{
609 struct list_head *list;
610 snd_device_t *dev;
611 snd_pcm_t *pcm;
612 int err = 0;
613 list_for_each(list, &card->devices) {
614 dev = snd_device(list);
615 if (dev->type != SNDRV_DEV_PCM)
616 continue;
617 pcm = dev->device_data;
618 down(&pcm->open_mutex);
619 }
620 list_for_each(list, &card->devices) {
621 int s;
622 dev = snd_device(list);
623 if (dev->type != SNDRV_DEV_PCM)
624 continue;
625 pcm = dev->device_data;
626 for (s = 0; s < 2; ++s) {
627 snd_pcm_substream_t *substream;
628 substream = pcm->streams[s].substream;
629 if (substream && substream->open_flag)
630 err = -EBUSY;
631 }
632 }
633 return err;
634}
635
636
637static void usX2Y_pcms_unlock(snd_card_t *card)
638{
639 struct list_head *list;
640 snd_device_t *dev;
641 snd_pcm_t *pcm;
642 list_for_each(list, &card->devices) {
643 dev = snd_device(list);
644 if (dev->type != SNDRV_DEV_PCM)
645 continue;
646 pcm = dev->device_data;
647 up(&pcm->open_mutex);
648 }
649}
650
651
652static int snd_usX2Y_hwdep_pcm_open(snd_hwdep_t *hw, struct file *file)
653{
654 // we need to be the first
655 snd_card_t *card = hw->card;
656 int err = usX2Y_pcms_lock_check(card);
657 if (0 == err)
658 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
659 usX2Y_pcms_unlock(card);
660 return err;
661}
662
663
664static int snd_usX2Y_hwdep_pcm_release(snd_hwdep_t *hw, struct file *file)
665{
666 snd_card_t *card = hw->card;
667 int err = usX2Y_pcms_lock_check(card);
668 if (0 == err)
669 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
670 usX2Y_pcms_unlock(card);
671 return err;
672}
673
674
675static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
676{
677}
678
679
680static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
681{
682}
683
684
685static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
686{
687 unsigned long offset;
688 struct page *page;
689 void *vaddr;
690
691 offset = area->vm_pgoff << PAGE_SHIFT;
692 offset += address - area->vm_start;
693 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
694 vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->hwdep_pcm_shm + offset;
695 page = virt_to_page(vaddr);
696
697 if (type)
698 *type = VM_FAULT_MINOR;
699
700 return page;
701}
702
703
704static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
705 .open = snd_usX2Y_hwdep_pcm_vm_open,
706 .close = snd_usX2Y_hwdep_pcm_vm_close,
707 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
708};
709
710
711static int snd_usX2Y_hwdep_pcm_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area)
712{
713 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
714 usX2Ydev_t *usX2Y = (usX2Ydev_t*)hw->private_data;
715
716 if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT))
717 return -EBUSY;
718
719 /* if userspace tries to mmap beyond end of our buffer, fail */
720 if (size > PAGE_ALIGN(sizeof(snd_usX2Y_hwdep_pcm_shm_t))) {
721 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(snd_usX2Y_hwdep_pcm_shm_t));
722 return -EINVAL;
723 }
724
725 if (!usX2Y->hwdep_pcm_shm) {
726 return -ENODEV;
727 }
728 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
729 area->vm_flags |= VM_RESERVED;
730 snd_printd("vm_flags=0x%lX\n", area->vm_flags);
731 area->vm_private_data = hw->private_data;
732 return 0;
733}
734
735
736static void snd_usX2Y_hwdep_pcm_private_free(snd_hwdep_t *hwdep)
737{
738 usX2Ydev_t *usX2Y = (usX2Ydev_t *)hwdep->private_data;
739 if (NULL != usX2Y->hwdep_pcm_shm)
740 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(snd_usX2Y_hwdep_pcm_shm_t));
741}
742
743
744static void snd_usX2Y_usbpcm_private_free(snd_pcm_t *pcm)
745{
746 snd_pcm_lib_preallocate_free_for_all(pcm);
747}
748
749
750int usX2Y_hwdep_pcm_new(snd_card_t* card)
751{
752 int err;
753 snd_hwdep_t *hw;
754 snd_pcm_t *pcm;
755 struct usb_device *dev = usX2Y(card)->chip.dev;
756 if (1 != nr_of_packs())
757 return 0;
758
759 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0) {
760 snd_printd("\n");
761 return err;
762 }
763 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
764 hw->private_data = usX2Y(card);
765 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
766 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
767 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
768 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
769 hw->exclusive = 1;
770 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
771
772 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
773 if (err < 0) {
774 return err;
775 }
776 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
777 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
778
779 pcm->private_data = usX2Y(card)->subs;
780 pcm->private_free = snd_usX2Y_usbpcm_private_free;
781 pcm->info_flags = 0;
782
783 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
784 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
785 SNDRV_DMA_TYPE_CONTINUOUS,
786 snd_dma_continuous_data(GFP_KERNEL),
787 64*1024, 128*1024)) ||
788 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
789 SNDRV_DMA_TYPE_CONTINUOUS,
790 snd_dma_continuous_data(GFP_KERNEL),
791 64*1024, 128*1024))) {
792 snd_usX2Y_usbpcm_private_free(pcm);
793 return err;
794 }
795
796
797 return 0;
798}
799
800#else
801
802int usX2Y_hwdep_pcm_new(snd_card_t* card)
803{
804 return 0;
805}
806
807#endif