blob: 2a528e56afd50db1c7f7cb309e1734c826cef3c2 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090054#include <linux/gfp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include "usbusx2yaudio.c"
56
57#if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1)
58
59#include <sound/hwdep.h>
60
61
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010062static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063{
64 struct urb *urb = subs->completed_urb;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010065 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 int i, lens = 0, hwptr_done = subs->hwptr_done;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010067 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
69 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
70 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
71 head = 0;
72 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
73 snd_printdd("cap start %i\n", head);
74 }
75 for (i = 0; i < nr_of_packs(); i++) {
76 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
Takashi Iwaid3d579f2005-10-21 16:20:11 +020077 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 -070078 return urb->iso_frame_desc[i].status;
79 }
80 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
81 }
82 if ((hwptr_done += lens) >= runtime->buffer_size)
83 hwptr_done -= runtime->buffer_size;
84 subs->hwptr_done = hwptr_done;
85 subs->transfer_done += lens;
86 /* update the pointer, call callback if necessary */
87 if (subs->transfer_done >= runtime->period_size) {
88 subs->transfer_done -= runtime->period_size;
89 snd_pcm_period_elapsed(subs->pcm_substream);
90 }
91 return 0;
92}
93
Takashi Iwaibbe85bb2005-11-17 15:08:26 +010094static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
95 struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
98}
99
100/*
101 * prepare urb for playback data pipe
102 *
103 * we copy the data directly from the pcm buffer.
104 * the current position to be copied is held in hwptr field.
105 * since a urb can handle only a single linear buffer, if the total
106 * transferred area overflows the buffer boundary, we cannot send
107 * it directly from the buffer. thus the data is once copied to
108 * a temporary buffer and urb points to that.
109 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100110static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
111 struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112{
113 int count, counts, pack;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100114 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100116 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118 if (0 > shm->playback_iso_start) {
119 shm->playback_iso_start = shm->captured_iso_head -
120 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
121 if (0 > shm->playback_iso_start)
122 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
123 shm->playback_iso_head = shm->playback_iso_start;
124 }
125
126 count = 0;
127 for (pack = 0; pack < nr_of_packs(); pack++) {
128 /* calculate the size of a packet */
129 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
130 if (counts < 43 || counts > 50) {
Takashi Iwaid3d579f2005-10-21 16:20:11 +0200131 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 return -EPIPE;
133 }
134 /* set up descriptor */
135 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
136 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
137 if (atomic_read(&subs->state) != state_RUNNING)
138 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
139 urb->iso_frame_desc[pack].length);
140 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
141 shm->playback_iso_head = 0;
142 count += counts;
143 }
144 urb->transfer_buffer_length = count * usX2Y->stride;
145 return 0;
146}
147
148
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100149static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
150 struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
152 int pack;
153 for (pack = 0; pack < nr_of_packs(); ++pack) {
154 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
155 if (NULL != subs) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100156 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 int head = shm->captured_iso_head + 1;
158 if (head >= ARRAY_SIZE(shm->captured_iso))
159 head = 0;
160 shm->captured_iso[head].frame = urb->start_frame + pack;
161 shm->captured_iso[head].offset = desc->offset;
162 shm->captured_iso[head].length = desc->actual_length;
163 shm->captured_iso_head = head;
164 shm->captured_iso_frames++;
165 }
166 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
167 desc->length >= SSS)
168 desc->offset -= (SSS - desc->length);
169 }
170}
171
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100172static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
173 struct snd_usX2Y_substream *capsubs2,
174 struct snd_usX2Y_substream *playbacksubs,
175 int frame)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176{
177 int err, state;
178 struct urb *urb = playbacksubs->completed_urb;
179
180 state = atomic_read(&playbacksubs->state);
181 if (NULL != urb) {
182 if (state == state_RUNNING)
183 usX2Y_urb_play_retire(playbacksubs, urb);
Takashi Iwaicb432372005-11-17 10:48:52 +0100184 else if (state >= state_PRERUNNING)
185 atomic_inc(&playbacksubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 } else {
187 switch (state) {
188 case state_STARTING1:
189 urb = playbacksubs->urb[0];
190 atomic_inc(&playbacksubs->state);
191 break;
192 case state_STARTING2:
193 urb = playbacksubs->urb[1];
194 atomic_inc(&playbacksubs->state);
195 break;
196 }
197 }
198 if (urb) {
199 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
200 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
201 return err;
202 }
203 }
204
205 playbacksubs->completed_urb = NULL;
206
207 state = atomic_read(&capsubs->state);
208 if (state >= state_PREPARED) {
209 if (state == state_RUNNING) {
210 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
211 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100212 } else if (state >= state_PRERUNNING)
213 atomic_inc(&capsubs->state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
215 if (NULL != capsubs2)
216 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
217 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
218 return err;
219 if (NULL != capsubs2)
220 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
221 return err;
222 }
223 capsubs->completed_urb = NULL;
224 if (NULL != capsubs2)
225 capsubs2->completed_urb = NULL;
226 return 0;
227}
228
229
David Howells7d12e782006-10-05 14:55:46 +0100230static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100232 struct snd_usX2Y_substream *subs = urb->context;
233 struct usX2Ydev *usX2Y = subs->usX2Y;
234 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100237 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
Clemens Ladischa014bba2009-11-16 12:26:30 +0100238 usb_get_current_frame_number(usX2Y->dev),
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100239 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240 urb->status, urb->start_frame);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 return;
242 }
243 if (unlikely(urb->status)) {
244 usX2Y_error_urb_status(usX2Y, subs, urb);
245 return;
246 }
Karsten Wiesebc6191b2007-01-10 19:02:26 +0100247 if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 subs->completed_urb = urb;
249 else {
250 usX2Y_error_sequence(usX2Y, subs, urb);
251 return;
252 }
253
254 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
255 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
256 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
257 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
258 (NULL == capsubs2 || capsubs2->completed_urb) &&
259 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
Karsten Wiese635bbb32006-10-04 17:17:32 +0200260 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
261 usX2Y->wait_iso_frame += nr_of_packs();
262 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 snd_printdd("\n");
264 usX2Y_clients_stop(usX2Y);
265 }
266 }
267}
268
269
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100270static void usX2Y_hwdep_urb_release(struct urb **urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
272 usb_kill_urb(*urb);
273 usb_free_urb(*urb);
274 *urb = NULL;
275}
276
277/*
278 * release a substream
279 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100280static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
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
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100288static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
291 usX2Y->prepare_subs = NULL;
292}
293
David Howells7d12e782006-10-05 14:55:46 +0100294static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100296 struct snd_usX2Y_substream *subs = urb->context;
297 struct usX2Ydev *usX2Y = subs->usX2Y;
298 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 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]) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100303 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 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
David Howells7d12e782006-10-05 14:55:46 +0100311 i_usX2Y_usbpcm_urb_complete(urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312}
313
314/*
315 * initialize a substream's urbs
316 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100317static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318{
319 int i;
320 unsigned int pipe;
321 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Clemens Ladischa014bba2009-11-16 12:26:30 +0100322 struct usb_device *dev = subs->usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
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++) {
Takashi Iwaicb432372005-11-17 10:48:52 +0100332 struct urb **purb = subs->urb + i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 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 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100361static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100363 struct snd_pcm_runtime *runtime = substream->runtime;
364 struct snd_usX2Y_substream *subs = runtime->private_data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Ingo Molnar12aa7572006-01-16 16:36:05 +0100366 mutex_lock(&subs->usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
368
369 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100370 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 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 {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100385 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 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 }
Ingo Molnar12aa7572006-01-16 16:36:05 +0100395 mutex_unlock(&subs->usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 return snd_pcm_lib_free_pages(substream);
397}
398
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100399static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100401 struct usX2Ydev * usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 usX2Y->prepare_subs = subs;
403 subs->urb[0]->start_frame = -1;
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -0800404 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
406}
407
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100408static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409{
410 int p, u, err,
411 stream = subs->pcm_substream->stream;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100412 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
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) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100420 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 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++) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100429 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
431 goto start;
432 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434 start:
435 usX2Y_usbpcm_subs_startup(subs);
436 for (u = 0; u < NRURBS; u++) {
437 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100438 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 if (subs != NULL) {
440 struct urb *urb = subs->urb[u];
441 if (usb_pipein(urb->pipe)) {
442 unsigned long pack;
443 if (0 == u)
444 atomic_set(&subs->state, state_STARTING3);
Clemens Ladischa014bba2009-11-16 12:26:30 +0100445 urb->dev = usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 urb->transfer_flags = URB_ISO_ASAP;
447 for (pack = 0; pack < nr_of_packs(); pack++) {
448 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
449 urb->iso_frame_desc[pack].length = subs->maxpacksize;
450 }
451 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
452 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
453 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
454 err = -EPIPE;
455 goto cleanup;
456 } else {
457 snd_printdd("%i\n", urb->start_frame);
Karsten Wiese635bbb32006-10-04 17:17:32 +0200458 if (u == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 usX2Y->wait_iso_frame = urb->start_frame;
460 }
461 urb->transfer_flags = 0;
462 } else {
463 atomic_set(&subs->state, state_STARTING1);
464 break;
465 }
466 }
467 }
468 }
469 err = 0;
470 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
471 if (atomic_read(&subs->state) != state_PREPARED)
472 err = -EPIPE;
473
474 cleanup:
475 if (err) {
476 usX2Y_subs_startup_finish(usX2Y); // Call it now
477 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
478 }
479 return err;
480}
481
482/*
483 * prepare callback
484 *
485 * set format and initialize urbs
486 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100487static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100489 struct snd_pcm_runtime *runtime = substream->runtime;
490 struct snd_usX2Y_substream *subs = runtime->private_data;
491 struct usX2Ydev *usX2Y = subs->usX2Y;
492 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 int err = 0;
494 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
495
496 if (NULL == usX2Y->hwdep_pcm_shm) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100497 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 -0700498 return -ENOMEM;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100499 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 }
501
Ingo Molnar12aa7572006-01-16 16:36:05 +0100502 mutex_lock(&usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 usX2Y_subs_prepare(subs);
504// Start hardware streams
505// SyncStream first....
506 if (atomic_read(&capsubs->state) < state_PREPARED) {
507 if (usX2Y->format != runtime->format)
508 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
509 goto up_prepare_mutex;
510 if (usX2Y->rate != runtime->rate)
511 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
512 goto up_prepare_mutex;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100513 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
514 "self" : "playpipe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 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) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100522 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
523 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
524 snd_printdd("Wait: iso_frames_per_buffer=%i,"
525 "captured_iso_frames=%i\n",
526 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
527 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +0200528 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 err = -ERESTARTSYS;
530 goto up_prepare_mutex;
531 }
532 }
533 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
534 goto up_prepare_mutex;
535 }
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100536 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
537 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
538 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 } else
540 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
541
542 up_prepare_mutex:
Ingo Molnar12aa7572006-01-16 16:36:05 +0100543 mutex_unlock(&usX2Y->prepare_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 return err;
545}
546
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100547static struct snd_pcm_hardware snd_usX2Y_4c =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548{
549 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
550 SNDRV_PCM_INFO_BLOCK_TRANSFER |
551 SNDRV_PCM_INFO_MMAP_VALID),
552 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
553 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
554 .rate_min = 44100,
555 .rate_max = 48000,
556 .channels_min = 2,
557 .channels_max = 4,
558 .buffer_bytes_max = (2*128*1024),
559 .period_bytes_min = 64,
560 .period_bytes_max = (128*1024),
561 .periods_min = 2,
562 .periods_max = 1024,
563 .fifo_size = 0
564};
565
566
567
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100568static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100570 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 snd_pcm_substream_chip(substream))[substream->stream];
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100572 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
574 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
575 return -EBUSY;
576
577 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
578 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
579 runtime->private_data = subs;
580 subs->pcm_substream = substream;
581 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
582 return 0;
583}
584
585
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100586static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100588 struct snd_pcm_runtime *runtime = substream->runtime;
589 struct snd_usX2Y_substream *subs = runtime->private_data;
Takashi Iwaicb432372005-11-17 10:48:52 +0100590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 subs->pcm_substream = NULL;
Takashi Iwaicb432372005-11-17 10:48:52 +0100592 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593}
594
595
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100596static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
598 .open = snd_usX2Y_usbpcm_open,
599 .close = snd_usX2Y_usbpcm_close,
600 .ioctl = snd_pcm_lib_ioctl,
601 .hw_params = snd_usX2Y_pcm_hw_params,
602 .hw_free = snd_usX2Y_usbpcm_hw_free,
603 .prepare = snd_usX2Y_usbpcm_prepare,
604 .trigger = snd_usX2Y_pcm_trigger,
605 .pointer = snd_usX2Y_pcm_pointer,
606};
607
608
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100609static int usX2Y_pcms_lock_check(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610{
611 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100612 struct snd_device *dev;
613 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 int err = 0;
615 list_for_each(list, &card->devices) {
616 dev = snd_device(list);
617 if (dev->type != SNDRV_DEV_PCM)
618 continue;
619 pcm = dev->device_data;
Ingo Molnar12aa7572006-01-16 16:36:05 +0100620 mutex_lock(&pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 }
622 list_for_each(list, &card->devices) {
623 int s;
624 dev = snd_device(list);
625 if (dev->type != SNDRV_DEV_PCM)
626 continue;
627 pcm = dev->device_data;
628 for (s = 0; s < 2; ++s) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100629 struct snd_pcm_substream *substream;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 substream = pcm->streams[s].substream;
Karsten Wiese9b08c2a2006-10-04 17:16:46 +0200631 if (substream && SUBSTREAM_BUSY(substream))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 err = -EBUSY;
633 }
634 }
635 return err;
636}
637
638
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100639static void usX2Y_pcms_unlock(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640{
641 struct list_head *list;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100642 struct snd_device *dev;
643 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 list_for_each(list, &card->devices) {
645 dev = snd_device(list);
646 if (dev->type != SNDRV_DEV_PCM)
647 continue;
648 pcm = dev->device_data;
Ingo Molnar12aa7572006-01-16 16:36:05 +0100649 mutex_unlock(&pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
651}
652
653
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100654static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655{
656 // we need to be the first
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100657 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 int err = usX2Y_pcms_lock_check(card);
659 if (0 == err)
660 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
661 usX2Y_pcms_unlock(card);
662 return err;
663}
664
665
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100666static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100668 struct snd_card *card = hw->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 int err = usX2Y_pcms_lock_check(card);
670 if (0 == err)
671 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
672 usX2Y_pcms_unlock(card);
673 return err;
674}
675
676
677static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
678{
679}
680
681
682static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
683{
684}
685
686
Nick Piggineb415b82007-12-13 16:16:40 +0100687static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area,
688 struct vm_fault *vmf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
690 unsigned long offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 void *vaddr;
692
Nick Piggineb415b82007-12-13 16:16:40 +0100693 offset = vmf->pgoff << PAGE_SHIFT;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100694 vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset;
Nick Piggineb415b82007-12-13 16:16:40 +0100695 vmf->page = virt_to_page(vaddr);
696 get_page(vmf->page);
697 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698}
699
700
Alexey Dobriyanf0f37e22009-09-27 22:29:37 +0400701static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 .open = snd_usX2Y_hwdep_pcm_vm_open,
703 .close = snd_usX2Y_hwdep_pcm_vm_close,
Nick Piggineb415b82007-12-13 16:16:40 +0100704 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705};
706
707
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100708static 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 -0700709{
710 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100711 struct usX2Ydev *usX2Y = hw->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
Takashi Iwaicb432372005-11-17 10:48:52 +0100713 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return -EBUSY;
715
716 /* if userspace tries to mmap beyond end of our buffer, fail */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100717 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
718 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 return -EINVAL;
720 }
721
722 if (!usX2Y->hwdep_pcm_shm) {
723 return -ENODEV;
724 }
725 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
Nick Piggin2f987352008-02-02 03:08:53 +0100726 area->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 area->vm_private_data = hw->private_data;
728 return 0;
729}
730
731
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100732static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100734 struct usX2Ydev *usX2Y = hwdep->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 if (NULL != usX2Y->hwdep_pcm_shm)
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100736 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
739
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100740int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741{
742 int err;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100743 struct snd_hwdep *hw;
744 struct snd_pcm *pcm;
Clemens Ladischa014bba2009-11-16 12:26:30 +0100745 struct usb_device *dev = usX2Y(card)->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 if (1 != nr_of_packs())
747 return 0;
748
Takashi Iwaicb432372005-11-17 10:48:52 +0100749 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
753 hw->private_data = usX2Y(card);
754 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
755 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
756 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
757 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
758 hw->exclusive = 1;
759 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
760
761 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
762 if (err < 0) {
763 return err;
764 }
765 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
766 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
767
768 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 pcm->info_flags = 0;
770
771 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
772 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
773 SNDRV_DMA_TYPE_CONTINUOUS,
774 snd_dma_continuous_data(GFP_KERNEL),
775 64*1024, 128*1024)) ||
776 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
777 SNDRV_DMA_TYPE_CONTINUOUS,
778 snd_dma_continuous_data(GFP_KERNEL),
779 64*1024, 128*1024))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 return err;
781 }
782
783
784 return 0;
785}
786
787#else
788
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100789int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790{
791 return 0;
792}
793
794#endif