blob: 9ed6c3956ca790e7afe83a5a8cb4d7cff56993d1 [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
David Howells7d12e782006-10-05 14:55:46 +0100229static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
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",
Clemens Ladischa014bba2009-11-16 12:26:30 +0100237 usb_get_current_frame_number(usX2Y->dev),
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100238 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 }
Karsten Wiesebc6191b2007-01-10 19:02:26 +0100246 if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 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)) {
Karsten Wiese635bbb32006-10-04 17:17:32 +0200259 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
260 usX2Y->wait_iso_frame += nr_of_packs();
261 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 snd_printdd("\n");
263 usX2Y_clients_stop(usX2Y);
264 }
265 }
266}
267
268
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100269static void usX2Y_hwdep_urb_release(struct urb **urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
271 usb_kill_urb(*urb);
272 usb_free_urb(*urb);
273 *urb = NULL;
274}
275
276/*
277 * release a substream
278 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100279static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
281 int i;
282 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
283 for (i = 0; i < NRURBS; i++)
284 usX2Y_hwdep_urb_release(subs->urb + i);
285}
286
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100287static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
289 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
290 usX2Y->prepare_subs = NULL;
291}
292
David Howells7d12e782006-10-05 14:55:46 +0100293static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100295 struct snd_usX2Y_substream *subs = urb->context;
296 struct usX2Ydev *usX2Y = subs->usX2Y;
297 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 if (NULL != prepare_subs &&
299 urb->start_frame == prepare_subs->urb[0]->start_frame) {
300 atomic_inc(&prepare_subs->state);
301 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100302 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 if (cap_subs2 != NULL)
304 atomic_inc(&cap_subs2->state);
305 }
306 usX2Y_usbpcm_subs_startup_finish(usX2Y);
307 wake_up(&usX2Y->prepare_wait_queue);
308 }
309
David Howells7d12e782006-10-05 14:55:46 +0100310 i_usX2Y_usbpcm_urb_complete(urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
313/*
314 * initialize a substream's urbs
315 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100316static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317{
318 int i;
319 unsigned int pipe;
320 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Clemens Ladischa014bba2009-11-16 12:26:30 +0100321 struct usb_device *dev = subs->usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
324 usb_rcvisocpipe(dev, subs->endpoint);
325 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
326 if (!subs->maxpacksize)
327 return -EINVAL;
328
329 /* allocate and initialize data urbs */
330 for (i = 0; i < NRURBS; i++) {
Takashi Iwaicb432372005-11-17 10:48:52 +0100331 struct urb **purb = subs->urb + i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 if (*purb) {
333 usb_kill_urb(*purb);
334 continue;
335 }
336 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
337 if (NULL == *purb) {
338 usX2Y_usbpcm_urbs_release(subs);
339 return -ENOMEM;
340 }
341 (*purb)->transfer_buffer = is_playback ?
342 subs->usX2Y->hwdep_pcm_shm->playback : (
343 subs->endpoint == 0x8 ?
344 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
345 subs->usX2Y->hwdep_pcm_shm->capture0xA);
346
347 (*purb)->dev = dev;
348 (*purb)->pipe = pipe;
349 (*purb)->number_of_packets = nr_of_packs();
350 (*purb)->context = subs;
351 (*purb)->interval = 1;
352 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
353 }
354 return 0;
355}
356
357/*
358 * free the buffer
359 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100360static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100362 struct snd_pcm_runtime *runtime = substream->runtime;
363 struct snd_usX2Y_substream *subs = runtime->private_data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Ingo Molnar12aa7572006-01-16 16:36:05 +0100365 mutex_lock(&subs->usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
367
368 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100369 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 atomic_set(&subs->state, state_STOPPED);
371 usX2Y_usbpcm_urbs_release(subs);
372 if (!cap_subs->pcm_substream ||
373 !cap_subs->pcm_substream->runtime ||
374 !cap_subs->pcm_substream->runtime->status ||
375 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
376 atomic_set(&cap_subs->state, state_STOPPED);
377 if (NULL != cap_subs2)
378 atomic_set(&cap_subs2->state, state_STOPPED);
379 usX2Y_usbpcm_urbs_release(cap_subs);
380 if (NULL != cap_subs2)
381 usX2Y_usbpcm_urbs_release(cap_subs2);
382 }
383 } else {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100384 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 if (atomic_read(&playback_subs->state) < state_PREPARED) {
386 atomic_set(&subs->state, state_STOPPED);
387 if (NULL != cap_subs2)
388 atomic_set(&cap_subs2->state, state_STOPPED);
389 usX2Y_usbpcm_urbs_release(subs);
390 if (NULL != cap_subs2)
391 usX2Y_usbpcm_urbs_release(cap_subs2);
392 }
393 }
Ingo Molnar12aa7572006-01-16 16:36:05 +0100394 mutex_unlock(&subs->usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 return snd_pcm_lib_free_pages(substream);
396}
397
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100398static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100400 struct usX2Ydev * usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 usX2Y->prepare_subs = subs;
402 subs->urb[0]->start_frame = -1;
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -0800403 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
405}
406
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100407static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 int p, u, err,
410 stream = subs->pcm_substream->stream;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100411 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
413 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
414 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
415 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
416 }
417
418 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100419 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 if (subs != NULL) {
421 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
422 return err;
423 subs->completed_urb = NULL;
424 }
425 }
426
427 for (p = 0; p < 4; p++) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100428 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
430 goto start;
431 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
433 start:
434 usX2Y_usbpcm_subs_startup(subs);
435 for (u = 0; u < NRURBS; u++) {
436 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100437 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 if (subs != NULL) {
439 struct urb *urb = subs->urb[u];
440 if (usb_pipein(urb->pipe)) {
441 unsigned long pack;
442 if (0 == u)
443 atomic_set(&subs->state, state_STARTING3);
Clemens Ladischa014bba2009-11-16 12:26:30 +0100444 urb->dev = usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 urb->transfer_flags = URB_ISO_ASAP;
446 for (pack = 0; pack < nr_of_packs(); pack++) {
447 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
448 urb->iso_frame_desc[pack].length = subs->maxpacksize;
449 }
450 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
451 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
452 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
453 err = -EPIPE;
454 goto cleanup;
455 } else {
456 snd_printdd("%i\n", urb->start_frame);
Karsten Wiese635bbb32006-10-04 17:17:32 +0200457 if (u == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 usX2Y->wait_iso_frame = urb->start_frame;
459 }
460 urb->transfer_flags = 0;
461 } else {
462 atomic_set(&subs->state, state_STARTING1);
463 break;
464 }
465 }
466 }
467 }
468 err = 0;
469 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
470 if (atomic_read(&subs->state) != state_PREPARED)
471 err = -EPIPE;
472
473 cleanup:
474 if (err) {
475 usX2Y_subs_startup_finish(usX2Y); // Call it now
476 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
477 }
478 return err;
479}
480
481/*
482 * prepare callback
483 *
484 * set format and initialize urbs
485 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100486static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100488 struct snd_pcm_runtime *runtime = substream->runtime;
489 struct snd_usX2Y_substream *subs = runtime->private_data;
490 struct usX2Ydev *usX2Y = subs->usX2Y;
491 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 int err = 0;
493 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
494
495 if (NULL == usX2Y->hwdep_pcm_shm) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100496 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 -0700497 return -ENOMEM;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100498 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 }
500
Ingo Molnar12aa7572006-01-16 16:36:05 +0100501 mutex_lock(&usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 usX2Y_subs_prepare(subs);
503// Start hardware streams
504// SyncStream first....
505 if (atomic_read(&capsubs->state) < state_PREPARED) {
506 if (usX2Y->format != runtime->format)
507 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
508 goto up_prepare_mutex;
509 if (usX2Y->rate != runtime->rate)
510 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
511 goto up_prepare_mutex;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100512 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
513 "self" : "playpipe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
515 goto up_prepare_mutex;
516 }
517
518 if (subs != capsubs) {
519 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
520 if (atomic_read(&subs->state) < state_PREPARED) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100521 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
522 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
523 snd_printdd("Wait: iso_frames_per_buffer=%i,"
524 "captured_iso_frames=%i\n",
525 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
526 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +0200527 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 err = -ERESTARTSYS;
529 goto up_prepare_mutex;
530 }
531 }
532 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
533 goto up_prepare_mutex;
534 }
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100535 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
536 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
537 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 } else
539 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
540
541 up_prepare_mutex:
Ingo Molnar12aa7572006-01-16 16:36:05 +0100542 mutex_unlock(&usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 return err;
544}
545
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100546static struct snd_pcm_hardware snd_usX2Y_4c =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547{
548 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
549 SNDRV_PCM_INFO_BLOCK_TRANSFER |
550 SNDRV_PCM_INFO_MMAP_VALID),
551 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
552 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
553 .rate_min = 44100,
554 .rate_max = 48000,
555 .channels_min = 2,
556 .channels_max = 4,
557 .buffer_bytes_max = (2*128*1024),
558 .period_bytes_min = 64,
559 .period_bytes_max = (128*1024),
560 .periods_min = 2,
561 .periods_max = 1024,
562 .fifo_size = 0
563};
564
565
566
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100567static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100569 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 snd_pcm_substream_chip(substream))[substream->stream];
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100571 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
573 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
574 return -EBUSY;
575
576 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
577 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
578 runtime->private_data = subs;
579 subs->pcm_substream = substream;
580 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
581 return 0;
582}
583
584
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100585static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100587 struct snd_pcm_runtime *runtime = substream->runtime;
588 struct snd_usX2Y_substream *subs = runtime->private_data;
Takashi Iwaicb432372005-11-17 10:48:52 +0100589
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 subs->pcm_substream = NULL;
Takashi Iwaicb432372005-11-17 10:48:52 +0100591 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592}
593
594
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100595static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596{
597 .open = snd_usX2Y_usbpcm_open,
598 .close = snd_usX2Y_usbpcm_close,
599 .ioctl = snd_pcm_lib_ioctl,
600 .hw_params = snd_usX2Y_pcm_hw_params,
601 .hw_free = snd_usX2Y_usbpcm_hw_free,
602 .prepare = snd_usX2Y_usbpcm_prepare,
603 .trigger = snd_usX2Y_pcm_trigger,
604 .pointer = snd_usX2Y_pcm_pointer,
605};
606
607
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100608static int usX2Y_pcms_lock_check(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609{
610 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100611 struct snd_device *dev;
612 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 int err = 0;
614 list_for_each(list, &card->devices) {
615 dev = snd_device(list);
616 if (dev->type != SNDRV_DEV_PCM)
617 continue;
618 pcm = dev->device_data;
Ingo Molnar12aa7572006-01-16 16:36:05 +0100619 mutex_lock(&pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 }
621 list_for_each(list, &card->devices) {
622 int s;
623 dev = snd_device(list);
624 if (dev->type != SNDRV_DEV_PCM)
625 continue;
626 pcm = dev->device_data;
627 for (s = 0; s < 2; ++s) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100628 struct snd_pcm_substream *substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 substream = pcm->streams[s].substream;
Karsten Wiese9b08c2a2006-10-04 17:16:46 +0200630 if (substream && SUBSTREAM_BUSY(substream))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 err = -EBUSY;
632 }
633 }
634 return err;
635}
636
637
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100638static void usX2Y_pcms_unlock(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639{
640 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100641 struct snd_device *dev;
642 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 list_for_each(list, &card->devices) {
644 dev = snd_device(list);
645 if (dev->type != SNDRV_DEV_PCM)
646 continue;
647 pcm = dev->device_data;
Ingo Molnar12aa7572006-01-16 16:36:05 +0100648 mutex_unlock(&pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 }
650}
651
652
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100653static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654{
655 // we need to be the first
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100656 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 int err = usX2Y_pcms_lock_check(card);
658 if (0 == err)
659 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
660 usX2Y_pcms_unlock(card);
661 return err;
662}
663
664
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100665static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100667 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 int err = usX2Y_pcms_lock_check(card);
669 if (0 == err)
670 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
671 usX2Y_pcms_unlock(card);
672 return err;
673}
674
675
676static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
677{
678}
679
680
681static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
682{
683}
684
685
Nick Piggineb415b82007-12-13 16:16:40 +0100686static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
687 struct vm_fault *vmf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
689 unsigned long offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 void *vaddr;
691
Nick Piggineb415b82007-12-13 16:16:40 +0100692 offset = vmf->pgoff << PAGE_SHIFT;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100693 vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
Nick Piggineb415b82007-12-13 16:16:40 +0100694 vmf->page = virt_to_page(vaddr);
695 get_page(vmf->page);
696 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697}
698
699
Alexey Dobriyanf0f37e22009-09-27 22:29:37 +0400700static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 .open = snd_usX2Y_hwdep_pcm_vm_open,
702 .close = snd_usX2Y_hwdep_pcm_vm_close,
Nick Piggineb415b82007-12-13 16:16:40 +0100703 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704};
705
706
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100707static 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 -0700708{
709 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100710 struct usX2Ydev *usX2Y = hw->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Takashi Iwaicb432372005-11-17 10:48:52 +0100712 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 return -EBUSY;
714
715 /* if userspace tries to mmap beyond end of our buffer, fail */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100716 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
717 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 return -EINVAL;
719 }
720
721 if (!usX2Y->hwdep_pcm_shm) {
722 return -ENODEV;
723 }
724 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
Nick Piggin2f987352008-02-02 03:08:53 +0100725 area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 area->vm_private_data = hw->private_data;
727 return 0;
728}
729
730
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100731static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100733 struct usX2Ydev *usX2Y = hwdep->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 if (NULL != usX2Y->hwdep_pcm_shm)
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100735 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736}
737
738
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100739int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 int err;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100742 struct snd_hwdep *hw;
743 struct snd_pcm *pcm;
Clemens Ladischa014bba2009-11-16 12:26:30 +0100744 struct usb_device *dev = usX2Y(card)->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 if (1 != nr_of_packs())
746 return 0;
747
Takashi Iwaicb432372005-11-17 10:48:52 +0100748 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
752 hw->private_data = usX2Y(card);
753 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
754 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
755 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
756 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
757 hw->exclusive = 1;
758 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
759
760 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
761 if (err < 0) {
762 return err;
763 }
764 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
765 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
766
767 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 pcm->info_flags = 0;
769
770 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
771 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
772 SNDRV_DMA_TYPE_CONTINUOUS,
773 snd_dma_continuous_data(GFP_KERNEL),
774 64*1024, 128*1024)) ||
775 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
776 SNDRV_DMA_TYPE_CONTINUOUS,
777 snd_dma_continuous_data(GFP_KERNEL),
778 64*1024, 128*1024))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 return err;
780 }
781
782
783 return 0;
784}
785
786#else
787
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100788int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789{
790 return 0;
791}
792
793#endif