blob: 796a7dcef09d5918c65bc36f49b5d58270daa6ae [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
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010061static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062{
63 struct urb *urb = subs->completed_urb;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010064 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 int i, lens = 0, hwptr_done = subs->hwptr_done;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010066 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 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
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010093static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
94 struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095{
96 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
97}
98
99/*
100 * prepare urb for playback data pipe
101 *
102 * we copy the data directly from the pcm buffer.
103 * the current position to be copied is held in hwptr field.
104 * since a urb can handle only a single linear buffer, if the total
105 * transferred area overflows the buffer boundary, we cannot send
106 * it directly from the buffer. thus the data is once copied to
107 * a temporary buffer and urb points to that.
108 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100109static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
110 struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 int count, counts, pack;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100113 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100115 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
117 if (0 > shm->playback_iso_start) {
118 shm->playback_iso_start = shm->captured_iso_head -
119 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
120 if (0 > shm->playback_iso_start)
121 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
122 shm->playback_iso_head = shm->playback_iso_start;
123 }
124
125 count = 0;
126 for (pack = 0; pack < nr_of_packs(); pack++) {
127 /* calculate the size of a packet */
128 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
129 if (counts < 43 || counts > 50) {
Takashi Iwaid3d579f2005-10-21 16:20:11 +0200130 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 return -EPIPE;
132 }
133 /* set up descriptor */
134 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
135 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
136 if (atomic_read(&subs->state) != state_RUNNING)
137 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
138 urb->iso_frame_desc[pack].length);
139 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
140 shm->playback_iso_head = 0;
141 count += counts;
142 }
143 urb->transfer_buffer_length = count * usX2Y->stride;
144 return 0;
145}
146
147
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100148static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
149 struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 int pack;
152 for (pack = 0; pack < nr_of_packs(); ++pack) {
153 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
154 if (NULL != subs) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100155 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 int head = shm->captured_iso_head + 1;
157 if (head >= ARRAY_SIZE(shm->captured_iso))
158 head = 0;
159 shm->captured_iso[head].frame = urb->start_frame + pack;
160 shm->captured_iso[head].offset = desc->offset;
161 shm->captured_iso[head].length = desc->actual_length;
162 shm->captured_iso_head = head;
163 shm->captured_iso_frames++;
164 }
165 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
166 desc->length >= SSS)
167 desc->offset -= (SSS - desc->length);
168 }
169}
170
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100171static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
172 struct snd_usX2Y_substream *capsubs2,
173 struct snd_usX2Y_substream *playbacksubs,
174 int frame)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
176 int err, state;
177 struct urb *urb = playbacksubs->completed_urb;
178
179 state = atomic_read(&playbacksubs->state);
180 if (NULL != urb) {
181 if (state == state_RUNNING)
182 usX2Y_urb_play_retire(playbacksubs, urb);
Takashi Iwaicb432372005-11-17 10:48:52 +0100183 else if (state >= state_PRERUNNING)
184 atomic_inc(&playbacksubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 } else {
186 switch (state) {
187 case state_STARTING1:
188 urb = playbacksubs->urb[0];
189 atomic_inc(&playbacksubs->state);
190 break;
191 case state_STARTING2:
192 urb = playbacksubs->urb[1];
193 atomic_inc(&playbacksubs->state);
194 break;
195 }
196 }
197 if (urb) {
198 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
199 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
200 return err;
201 }
202 }
203
204 playbacksubs->completed_urb = NULL;
205
206 state = atomic_read(&capsubs->state);
207 if (state >= state_PREPARED) {
208 if (state == state_RUNNING) {
209 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
210 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100211 } else if (state >= state_PRERUNNING)
212 atomic_inc(&capsubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 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{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100231 struct snd_usX2Y_substream *subs = urb->context;
232 struct usX2Ydev *usX2Y = subs->usX2Y;
233 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100236 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
237 usb_get_current_frame_number(usX2Y->chip.dev),
238 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
239 urb->status, urb->start_frame);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 return;
241 }
242 if (unlikely(urb->status)) {
243 usX2Y_error_urb_status(usX2Y, subs, urb);
244 return;
245 }
246 if (likely((0xFFFF & urb->start_frame) == usX2Y->wait_iso_frame))
247 subs->completed_urb = urb;
248 else {
249 usX2Y_error_sequence(usX2Y, subs, urb);
250 return;
251 }
252
253 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
254 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
255 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
256 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
257 (NULL == capsubs2 || capsubs2->completed_urb) &&
258 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
259 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
260 if (nr_of_packs() <= urb->start_frame &&
261 urb->start_frame <= (2 * nr_of_packs() - 1)) // uhci and ohci
262 usX2Y->wait_iso_frame = urb->start_frame - nr_of_packs();
263 else
264 usX2Y->wait_iso_frame += nr_of_packs();
265 } else {
266 snd_printdd("\n");
267 usX2Y_clients_stop(usX2Y);
268 }
269 }
270}
271
272
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100273static void usX2Y_hwdep_urb_release(struct urb **urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
275 usb_kill_urb(*urb);
276 usb_free_urb(*urb);
277 *urb = NULL;
278}
279
280/*
281 * release a substream
282 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100283static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 int i;
286 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
287 for (i = 0; i < NRURBS; i++)
288 usX2Y_hwdep_urb_release(subs->urb + i);
289}
290
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100291static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
294 usX2Y->prepare_subs = NULL;
295}
296
297static void i_usX2Y_usbpcm_subs_startup(struct urb *urb, struct pt_regs *regs)
298{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100299 struct snd_usX2Y_substream *subs = urb->context;
300 struct usX2Ydev *usX2Y = subs->usX2Y;
301 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (NULL != prepare_subs &&
303 urb->start_frame == prepare_subs->urb[0]->start_frame) {
304 atomic_inc(&prepare_subs->state);
305 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100306 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 if (cap_subs2 != NULL)
308 atomic_inc(&cap_subs2->state);
309 }
310 usX2Y_usbpcm_subs_startup_finish(usX2Y);
311 wake_up(&usX2Y->prepare_wait_queue);
312 }
313
314 i_usX2Y_usbpcm_urb_complete(urb, regs);
315}
316
317/*
318 * initialize a substream's urbs
319 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100320static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
322 int i;
323 unsigned int pipe;
324 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
325 struct usb_device *dev = subs->usX2Y->chip.dev;
326
327 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
328 usb_rcvisocpipe(dev, subs->endpoint);
329 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
330 if (!subs->maxpacksize)
331 return -EINVAL;
332
333 /* allocate and initialize data urbs */
334 for (i = 0; i < NRURBS; i++) {
Takashi Iwaicb432372005-11-17 10:48:52 +0100335 struct urb **purb = subs->urb + i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 if (*purb) {
337 usb_kill_urb(*purb);
338 continue;
339 }
340 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
341 if (NULL == *purb) {
342 usX2Y_usbpcm_urbs_release(subs);
343 return -ENOMEM;
344 }
345 (*purb)->transfer_buffer = is_playback ?
346 subs->usX2Y->hwdep_pcm_shm->playback : (
347 subs->endpoint == 0x8 ?
348 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
349 subs->usX2Y->hwdep_pcm_shm->capture0xA);
350
351 (*purb)->dev = dev;
352 (*purb)->pipe = pipe;
353 (*purb)->number_of_packets = nr_of_packs();
354 (*purb)->context = subs;
355 (*purb)->interval = 1;
356 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
357 }
358 return 0;
359}
360
361/*
362 * free the buffer
363 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100364static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100366 struct snd_pcm_runtime *runtime = substream->runtime;
367 struct snd_usX2Y_substream *subs = runtime->private_data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
369 down(&subs->usX2Y->prepare_mutex);
370 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
371
372 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100373 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 atomic_set(&subs->state, state_STOPPED);
375 usX2Y_usbpcm_urbs_release(subs);
376 if (!cap_subs->pcm_substream ||
377 !cap_subs->pcm_substream->runtime ||
378 !cap_subs->pcm_substream->runtime->status ||
379 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
380 atomic_set(&cap_subs->state, state_STOPPED);
381 if (NULL != cap_subs2)
382 atomic_set(&cap_subs2->state, state_STOPPED);
383 usX2Y_usbpcm_urbs_release(cap_subs);
384 if (NULL != cap_subs2)
385 usX2Y_usbpcm_urbs_release(cap_subs2);
386 }
387 } else {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100388 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 if (atomic_read(&playback_subs->state) < state_PREPARED) {
390 atomic_set(&subs->state, state_STOPPED);
391 if (NULL != cap_subs2)
392 atomic_set(&cap_subs2->state, state_STOPPED);
393 usX2Y_usbpcm_urbs_release(subs);
394 if (NULL != cap_subs2)
395 usX2Y_usbpcm_urbs_release(cap_subs2);
396 }
397 }
398 up(&subs->usX2Y->prepare_mutex);
399 return snd_pcm_lib_free_pages(substream);
400}
401
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100402static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100404 struct usX2Ydev * usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 usX2Y->prepare_subs = subs;
406 subs->urb[0]->start_frame = -1;
407 smp_wmb(); // Make shure above modifications are seen by i_usX2Y_subs_startup()
408 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
409}
410
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100411static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
413 int p, u, err,
414 stream = subs->pcm_substream->stream;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100415 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416
417 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
418 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
419 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
420 }
421
422 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100423 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 if (subs != NULL) {
425 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
426 return err;
427 subs->completed_urb = NULL;
428 }
429 }
430
431 for (p = 0; p < 4; p++) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100432 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
434 goto start;
435 }
436 usX2Y->wait_iso_frame = -1;
437
438 start:
439 usX2Y_usbpcm_subs_startup(subs);
440 for (u = 0; u < NRURBS; u++) {
441 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100442 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 if (subs != NULL) {
444 struct urb *urb = subs->urb[u];
445 if (usb_pipein(urb->pipe)) {
446 unsigned long pack;
447 if (0 == u)
448 atomic_set(&subs->state, state_STARTING3);
449 urb->dev = usX2Y->chip.dev;
450 urb->transfer_flags = URB_ISO_ASAP;
451 for (pack = 0; pack < nr_of_packs(); pack++) {
452 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
453 urb->iso_frame_desc[pack].length = subs->maxpacksize;
454 }
455 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
456 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
457 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
458 err = -EPIPE;
459 goto cleanup;
460 } else {
461 snd_printdd("%i\n", urb->start_frame);
462 if (0 > usX2Y->wait_iso_frame)
463 usX2Y->wait_iso_frame = urb->start_frame;
464 }
465 urb->transfer_flags = 0;
466 } else {
467 atomic_set(&subs->state, state_STARTING1);
468 break;
469 }
470 }
471 }
472 }
473 err = 0;
474 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
475 if (atomic_read(&subs->state) != state_PREPARED)
476 err = -EPIPE;
477
478 cleanup:
479 if (err) {
480 usX2Y_subs_startup_finish(usX2Y); // Call it now
481 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
482 }
483 return err;
484}
485
486/*
487 * prepare callback
488 *
489 * set format and initialize urbs
490 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100491static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100493 struct snd_pcm_runtime *runtime = substream->runtime;
494 struct snd_usX2Y_substream *subs = runtime->private_data;
495 struct usX2Ydev *usX2Y = subs->usX2Y;
496 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 int err = 0;
498 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
499
500 if (NULL == usX2Y->hwdep_pcm_shm) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100501 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 return -ENOMEM;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100503 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 }
505
506 down(&usX2Y->prepare_mutex);
507 usX2Y_subs_prepare(subs);
508// Start hardware streams
509// SyncStream first....
510 if (atomic_read(&capsubs->state) < state_PREPARED) {
511 if (usX2Y->format != runtime->format)
512 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
513 goto up_prepare_mutex;
514 if (usX2Y->rate != runtime->rate)
515 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
516 goto up_prepare_mutex;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100517 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
518 "self" : "playpipe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
520 goto up_prepare_mutex;
521 }
522
523 if (subs != capsubs) {
524 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
525 if (atomic_read(&subs->state) < state_PREPARED) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100526 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
527 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
528 snd_printdd("Wait: iso_frames_per_buffer=%i,"
529 "captured_iso_frames=%i\n",
530 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
531 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +0200532 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 err = -ERESTARTSYS;
534 goto up_prepare_mutex;
535 }
536 }
537 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
538 goto up_prepare_mutex;
539 }
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100540 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
541 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
542 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 } else
544 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
545
546 up_prepare_mutex:
547 up(&usX2Y->prepare_mutex);
548 return err;
549}
550
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100551static struct snd_pcm_hardware snd_usX2Y_4c =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
553 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
554 SNDRV_PCM_INFO_BLOCK_TRANSFER |
555 SNDRV_PCM_INFO_MMAP_VALID),
556 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
557 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
558 .rate_min = 44100,
559 .rate_max = 48000,
560 .channels_min = 2,
561 .channels_max = 4,
562 .buffer_bytes_max = (2*128*1024),
563 .period_bytes_min = 64,
564 .period_bytes_max = (128*1024),
565 .periods_min = 2,
566 .periods_max = 1024,
567 .fifo_size = 0
568};
569
570
571
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100572static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100574 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 snd_pcm_substream_chip(substream))[substream->stream];
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100576 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
578 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
579 return -EBUSY;
580
581 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
582 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
583 runtime->private_data = subs;
584 subs->pcm_substream = substream;
585 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
586 return 0;
587}
588
589
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100590static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100592 struct snd_pcm_runtime *runtime = substream->runtime;
593 struct snd_usX2Y_substream *subs = runtime->private_data;
Takashi Iwaicb432372005-11-17 10:48:52 +0100594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 subs->pcm_substream = NULL;
Takashi Iwaicb432372005-11-17 10:48:52 +0100596 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597}
598
599
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100600static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601{
602 .open = snd_usX2Y_usbpcm_open,
603 .close = snd_usX2Y_usbpcm_close,
604 .ioctl = snd_pcm_lib_ioctl,
605 .hw_params = snd_usX2Y_pcm_hw_params,
606 .hw_free = snd_usX2Y_usbpcm_hw_free,
607 .prepare = snd_usX2Y_usbpcm_prepare,
608 .trigger = snd_usX2Y_pcm_trigger,
609 .pointer = snd_usX2Y_pcm_pointer,
610};
611
612
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100613static int usX2Y_pcms_lock_check(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614{
615 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100616 struct snd_device *dev;
617 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 int err = 0;
619 list_for_each(list, &card->devices) {
620 dev = snd_device(list);
621 if (dev->type != SNDRV_DEV_PCM)
622 continue;
623 pcm = dev->device_data;
624 down(&pcm->open_mutex);
625 }
626 list_for_each(list, &card->devices) {
627 int s;
628 dev = snd_device(list);
629 if (dev->type != SNDRV_DEV_PCM)
630 continue;
631 pcm = dev->device_data;
632 for (s = 0; s < 2; ++s) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100633 struct snd_pcm_substream *substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 substream = pcm->streams[s].substream;
Karsten Wiese443feb82005-08-10 11:18:19 +0200635 if (substream && substream->ffile != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 err = -EBUSY;
637 }
638 }
639 return err;
640}
641
642
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100643static void usX2Y_pcms_unlock(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
645 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100646 struct snd_device *dev;
647 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 list_for_each(list, &card->devices) {
649 dev = snd_device(list);
650 if (dev->type != SNDRV_DEV_PCM)
651 continue;
652 pcm = dev->device_data;
653 up(&pcm->open_mutex);
654 }
655}
656
657
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100658static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 // we need to be the first
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100661 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 int err = usX2Y_pcms_lock_check(card);
663 if (0 == err)
664 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
665 usX2Y_pcms_unlock(card);
666 return err;
667}
668
669
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100670static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100672 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 int err = usX2Y_pcms_lock_check(card);
674 if (0 == err)
675 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
676 usX2Y_pcms_unlock(card);
677 return err;
678}
679
680
681static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
682{
683}
684
685
686static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
687{
688}
689
690
691static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type)
692{
693 unsigned long offset;
694 struct page *page;
695 void *vaddr;
696
697 offset = area->vm_pgoff << PAGE_SHIFT;
698 offset += address - area->vm_start;
699 snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM);
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100700 vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 page = virt_to_page(vaddr);
Hugh Dickins1cdca612005-11-21 21:32:13 -0800702 get_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
704 if (type)
705 *type = VM_FAULT_MINOR;
706
707 return page;
708}
709
710
711static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
712 .open = snd_usX2Y_hwdep_pcm_vm_open,
713 .close = snd_usX2Y_hwdep_pcm_vm_close,
714 .nopage = snd_usX2Y_hwdep_pcm_vm_nopage,
715};
716
717
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100718static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719{
720 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100721 struct usX2Ydev *usX2Y = hw->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
Takashi Iwaicb432372005-11-17 10:48:52 +0100723 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return -EBUSY;
725
726 /* if userspace tries to mmap beyond end of our buffer, fail */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100727 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
728 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 return -EINVAL;
730 }
731
732 if (!usX2Y->hwdep_pcm_shm) {
733 return -ENODEV;
734 }
735 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
736 area->vm_flags |= VM_RESERVED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 area->vm_private_data = hw->private_data;
738 return 0;
739}
740
741
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100742static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100744 struct usX2Ydev *usX2Y = hwdep->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 if (NULL != usX2Y->hwdep_pcm_shm)
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100746 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747}
748
749
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100750int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
752 int err;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100753 struct snd_hwdep *hw;
754 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 struct usb_device *dev = usX2Y(card)->chip.dev;
756 if (1 != nr_of_packs())
757 return 0;
758
Takashi Iwaicb432372005-11-17 10:48:52 +0100759 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
763 hw->private_data = usX2Y(card);
764 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
765 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
766 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
767 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
768 hw->exclusive = 1;
769 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
770
771 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
772 if (err < 0) {
773 return err;
774 }
775 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
776 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
777
778 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 pcm->info_flags = 0;
780
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))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 return err;
791 }
792
793
794 return 0;
795}
796
797#else
798
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100799int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800{
801 return 0;
802}
803
804#endif