blob: ace8185c3f6d1477c650cc9ace5d90b85efd66f8 [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.
Lucas De Marchi25985ed2011-03-30 22:57:33 -030023 The pair uses a hardware dependent alsa-device for mmaped pcm transport.
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 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).
Andrea Gelminifa2eb002010-10-16 15:19:20 +020039
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 This is a first "proof of concept" implementation.
Lucas De Marchi25985ed2011-03-30 22:57:33 -030041 Later, functionalities should migrate to more appropriate places:
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 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
Nicolas Kaiser1d2019f2010-10-05 17:38:12 +020057#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
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 */
Masanari Iida6e8d5d22012-02-15 00:38:55 +090077 snd_printk(KERN_ERR "active frame status %i. Most probably 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
Daniel Macka9d14bc2013-10-02 17:49:50 +0200248 subs->completed_urb = urb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
250 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
251 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
252 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
253 (NULL == capsubs2 || capsubs2->completed_urb) &&
254 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
Karsten Wiese635bbb32006-10-04 17:17:32 +0200255 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
256 usX2Y->wait_iso_frame += nr_of_packs();
257 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 snd_printdd("\n");
259 usX2Y_clients_stop(usX2Y);
260 }
261 }
262}
263
264
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100265static void usX2Y_hwdep_urb_release(struct urb **urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{
267 usb_kill_urb(*urb);
268 usb_free_urb(*urb);
269 *urb = NULL;
270}
271
272/*
273 * release a substream
274 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100275static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
277 int i;
278 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
279 for (i = 0; i < NRURBS; i++)
280 usX2Y_hwdep_urb_release(subs->urb + i);
281}
282
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100283static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284{
285 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
286 usX2Y->prepare_subs = NULL;
287}
288
David Howells7d12e782006-10-05 14:55:46 +0100289static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100291 struct snd_usX2Y_substream *subs = urb->context;
292 struct usX2Ydev *usX2Y = subs->usX2Y;
293 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 if (NULL != prepare_subs &&
295 urb->start_frame == prepare_subs->urb[0]->start_frame) {
296 atomic_inc(&prepare_subs->state);
297 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100298 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 if (cap_subs2 != NULL)
300 atomic_inc(&cap_subs2->state);
301 }
302 usX2Y_usbpcm_subs_startup_finish(usX2Y);
303 wake_up(&usX2Y->prepare_wait_queue);
304 }
305
David Howells7d12e782006-10-05 14:55:46 +0100306 i_usX2Y_usbpcm_urb_complete(urb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307}
308
309/*
310 * initialize a substream's urbs
311 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100312static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
314 int i;
315 unsigned int pipe;
316 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Clemens Ladischa014bba2009-11-16 12:26:30 +0100317 struct usb_device *dev = subs->usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
320 usb_rcvisocpipe(dev, subs->endpoint);
321 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
322 if (!subs->maxpacksize)
323 return -EINVAL;
324
325 /* allocate and initialize data urbs */
326 for (i = 0; i < NRURBS; i++) {
Takashi Iwaicb432372005-11-17 10:48:52 +0100327 struct urb **purb = subs->urb + i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if (*purb) {
329 usb_kill_urb(*purb);
330 continue;
331 }
332 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
333 if (NULL == *purb) {
334 usX2Y_usbpcm_urbs_release(subs);
335 return -ENOMEM;
336 }
337 (*purb)->transfer_buffer = is_playback ?
338 subs->usX2Y->hwdep_pcm_shm->playback : (
339 subs->endpoint == 0x8 ?
340 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
341 subs->usX2Y->hwdep_pcm_shm->capture0xA);
342
343 (*purb)->dev = dev;
344 (*purb)->pipe = pipe;
345 (*purb)->number_of_packets = nr_of_packs();
346 (*purb)->context = subs;
347 (*purb)->interval = 1;
348 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
349 }
350 return 0;
351}
352
353/*
354 * free the buffer
355 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100356static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100358 struct snd_pcm_runtime *runtime = substream->runtime;
359 struct snd_usX2Y_substream *subs = runtime->private_data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
Takashi Iwaie2439a52014-02-14 09:05:47 +0100361 mutex_lock(&subs->usX2Y->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
363
364 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100365 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 atomic_set(&subs->state, state_STOPPED);
367 usX2Y_usbpcm_urbs_release(subs);
368 if (!cap_subs->pcm_substream ||
369 !cap_subs->pcm_substream->runtime ||
370 !cap_subs->pcm_substream->runtime->status ||
371 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
372 atomic_set(&cap_subs->state, state_STOPPED);
373 if (NULL != cap_subs2)
374 atomic_set(&cap_subs2->state, state_STOPPED);
375 usX2Y_usbpcm_urbs_release(cap_subs);
376 if (NULL != cap_subs2)
377 usX2Y_usbpcm_urbs_release(cap_subs2);
378 }
379 } else {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100380 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 if (atomic_read(&playback_subs->state) < state_PREPARED) {
382 atomic_set(&subs->state, state_STOPPED);
383 if (NULL != cap_subs2)
384 atomic_set(&cap_subs2->state, state_STOPPED);
385 usX2Y_usbpcm_urbs_release(subs);
386 if (NULL != cap_subs2)
387 usX2Y_usbpcm_urbs_release(cap_subs2);
388 }
389 }
Takashi Iwaie2439a52014-02-14 09:05:47 +0100390 mutex_unlock(&subs->usX2Y->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 return snd_pcm_lib_free_pages(substream);
392}
393
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100394static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100396 struct usX2Ydev * usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 usX2Y->prepare_subs = subs;
398 subs->urb[0]->start_frame = -1;
Alexey Dobriyan7f927fc2006-03-28 01:56:53 -0800399 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
401}
402
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100403static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
405 int p, u, err,
406 stream = subs->pcm_substream->stream;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100407 struct usX2Ydev *usX2Y = subs->usX2Y;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
410 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
411 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
412 }
413
414 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100415 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 if (subs != NULL) {
417 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
418 return err;
419 subs->completed_urb = NULL;
420 }
421 }
422
423 for (p = 0; p < 4; p++) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100424 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
426 goto start;
427 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
429 start:
430 usX2Y_usbpcm_subs_startup(subs);
431 for (u = 0; u < NRURBS; u++) {
432 for (p = 0; 3 >= (stream + p); p += 2) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100433 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 if (subs != NULL) {
435 struct urb *urb = subs->urb[u];
436 if (usb_pipein(urb->pipe)) {
437 unsigned long pack;
438 if (0 == u)
439 atomic_set(&subs->state, state_STARTING3);
Clemens Ladischa014bba2009-11-16 12:26:30 +0100440 urb->dev = usX2Y->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 for (pack = 0; pack < nr_of_packs(); pack++) {
442 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
443 urb->iso_frame_desc[pack].length = subs->maxpacksize;
444 }
445 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
446 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
447 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
448 err = -EPIPE;
449 goto cleanup;
450 } else {
451 snd_printdd("%i\n", urb->start_frame);
Karsten Wiese635bbb32006-10-04 17:17:32 +0200452 if (u == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 usX2Y->wait_iso_frame = urb->start_frame;
454 }
455 urb->transfer_flags = 0;
456 } else {
457 atomic_set(&subs->state, state_STARTING1);
458 break;
459 }
460 }
461 }
462 }
463 err = 0;
464 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
465 if (atomic_read(&subs->state) != state_PREPARED)
466 err = -EPIPE;
467
468 cleanup:
469 if (err) {
470 usX2Y_subs_startup_finish(usX2Y); // Call it now
471 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
472 }
473 return err;
474}
475
476/*
477 * prepare callback
478 *
479 * set format and initialize urbs
480 */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100481static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100483 struct snd_pcm_runtime *runtime = substream->runtime;
484 struct snd_usX2Y_substream *subs = runtime->private_data;
485 struct usX2Ydev *usX2Y = subs->usX2Y;
486 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 int err = 0;
488 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
489
490 if (NULL == usX2Y->hwdep_pcm_shm) {
Takashi Iwai734b5a02018-11-23 19:38:13 +0100491 usX2Y->hwdep_pcm_shm = alloc_pages_exact(sizeof(struct snd_usX2Y_hwdep_pcm_shm),
492 GFP_KERNEL);
493 if (!usX2Y->hwdep_pcm_shm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 return -ENOMEM;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100495 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 }
497
Takashi Iwaie2439a52014-02-14 09:05:47 +0100498 mutex_lock(&usX2Y->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 usX2Y_subs_prepare(subs);
500// Start hardware streams
501// SyncStream first....
502 if (atomic_read(&capsubs->state) < state_PREPARED) {
503 if (usX2Y->format != runtime->format)
504 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
505 goto up_prepare_mutex;
506 if (usX2Y->rate != runtime->rate)
507 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
508 goto up_prepare_mutex;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100509 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
510 "self" : "playpipe");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
512 goto up_prepare_mutex;
513 }
514
515 if (subs != capsubs) {
516 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
517 if (atomic_read(&subs->state) < state_PREPARED) {
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100518 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
519 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
520 snd_printdd("Wait: iso_frames_per_buffer=%i,"
521 "captured_iso_frames=%i\n",
522 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
523 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Nishanth Aravamudanb27c1872005-07-09 10:54:37 +0200524 if (msleep_interruptible(10)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 err = -ERESTARTSYS;
526 goto up_prepare_mutex;
527 }
528 }
529 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
530 goto up_prepare_mutex;
531 }
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100532 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
533 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
534 usX2Y->hwdep_pcm_shm->captured_iso_frames);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 } else
536 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
537
538 up_prepare_mutex:
Takashi Iwaie2439a52014-02-14 09:05:47 +0100539 mutex_unlock(&usX2Y->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 return err;
541}
542
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100543static struct snd_pcm_hardware snd_usX2Y_4c =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
545 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
546 SNDRV_PCM_INFO_BLOCK_TRANSFER |
547 SNDRV_PCM_INFO_MMAP_VALID),
548 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
549 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
550 .rate_min = 44100,
551 .rate_max = 48000,
552 .channels_min = 2,
553 .channels_max = 4,
554 .buffer_bytes_max = (2*128*1024),
555 .period_bytes_min = 64,
556 .period_bytes_max = (128*1024),
557 .periods_min = 2,
558 .periods_max = 1024,
559 .fifo_size = 0
560};
561
562
563
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100564static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100566 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 snd_pcm_substream_chip(substream))[substream->stream];
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100568 struct snd_pcm_runtime *runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
570 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
571 return -EBUSY;
572
573 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
574 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
575 runtime->private_data = subs;
576 subs->pcm_substream = substream;
577 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
578 return 0;
579}
580
581
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100582static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100584 struct snd_pcm_runtime *runtime = substream->runtime;
585 struct snd_usX2Y_substream *subs = runtime->private_data;
Takashi Iwaicb432372005-11-17 10:48:52 +0100586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 subs->pcm_substream = NULL;
Takashi Iwaicb432372005-11-17 10:48:52 +0100588 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589}
590
591
Arvind Yadav31cb1fb2017-08-18 13:15:21 +0530592static const struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593{
594 .open = snd_usX2Y_usbpcm_open,
595 .close = snd_usX2Y_usbpcm_close,
596 .ioctl = snd_pcm_lib_ioctl,
597 .hw_params = snd_usX2Y_pcm_hw_params,
598 .hw_free = snd_usX2Y_usbpcm_hw_free,
599 .prepare = snd_usX2Y_usbpcm_prepare,
600 .trigger = snd_usX2Y_pcm_trigger,
601 .pointer = snd_usX2Y_pcm_pointer,
602};
603
604
Takashi Iwaie2439a52014-02-14 09:05:47 +0100605static int usX2Y_pcms_busy_check(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606{
Takashi Iwaie2439a52014-02-14 09:05:47 +0100607 struct usX2Ydev *dev = usX2Y(card);
608 int i;
609
610 for (i = 0; i < dev->pcm_devs * 2; i++) {
611 struct snd_usX2Y_substream *subs = dev->subs[i];
612 if (subs && subs->pcm_substream &&
613 SUBSTREAM_BUSY(subs->pcm_substream))
614 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 }
Takashi Iwaie2439a52014-02-14 09:05:47 +0100616 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617}
618
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100619static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100621 struct snd_card *card = hw->card;
Takashi Iwaie2439a52014-02-14 09:05:47 +0100622 int err;
623
624 mutex_lock(&usX2Y(card)->pcm_mutex);
625 err = usX2Y_pcms_busy_check(card);
626 if (!err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
Takashi Iwaie2439a52014-02-14 09:05:47 +0100628 mutex_unlock(&usX2Y(card)->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 return err;
630}
631
632
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100633static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100635 struct snd_card *card = hw->card;
Takashi Iwaie2439a52014-02-14 09:05:47 +0100636 int err;
637
638 mutex_lock(&usX2Y(card)->pcm_mutex);
639 err = usX2Y_pcms_busy_check(card);
640 if (!err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
Takashi Iwaie2439a52014-02-14 09:05:47 +0100642 mutex_unlock(&usX2Y(card)->pcm_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return err;
644}
645
646
647static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
648{
649}
650
651
652static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
653{
654}
655
656
Souptick Joarder29581052018-04-25 09:44:45 +0530657static vm_fault_t snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
659 unsigned long offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 void *vaddr;
661
Nick Piggineb415b82007-12-13 16:16:40 +0100662 offset = vmf->pgoff << PAGE_SHIFT;
Dave Jiang11bac802017-02-24 14:56:41 -0800663 vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
Nick Piggineb415b82007-12-13 16:16:40 +0100664 vmf->page = virt_to_page(vaddr);
665 get_page(vmf->page);
666 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667}
668
669
Alexey Dobriyanf0f37e2f2009-09-27 22:29:37 +0400670static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 .open = snd_usX2Y_hwdep_pcm_vm_open,
672 .close = snd_usX2Y_hwdep_pcm_vm_close,
Nick Piggineb415b82007-12-13 16:16:40 +0100673 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674};
675
676
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100677static 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 -0700678{
679 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100680 struct usX2Ydev *usX2Y = hw->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
Takashi Iwaicb432372005-11-17 10:48:52 +0100682 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return -EBUSY;
684
685 /* if userspace tries to mmap beyond end of our buffer, fail */
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100686 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
687 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 return -EINVAL;
689 }
690
691 if (!usX2Y->hwdep_pcm_shm) {
692 return -ENODEV;
693 }
694 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
Konstantin Khlebnikov314e51b2012-10-08 16:29:02 -0700695 area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 area->vm_private_data = hw->private_data;
697 return 0;
698}
699
700
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100701static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702{
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100703 struct usX2Ydev *usX2Y = hwdep->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 if (NULL != usX2Y->hwdep_pcm_shm)
Takashi Iwai734b5a02018-11-23 19:38:13 +0100705 free_pages_exact(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706}
707
708
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100709int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710{
711 int err;
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100712 struct snd_hwdep *hw;
713 struct snd_pcm *pcm;
Clemens Ladischa014bba2009-11-16 12:26:30 +0100714 struct usb_device *dev = usX2Y(card)->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 if (1 != nr_of_packs())
716 return 0;
717
Takashi Iwaicb432372005-11-17 10:48:52 +0100718 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 return err;
Takashi Iwaicb432372005-11-17 10:48:52 +0100720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
722 hw->private_data = usX2Y(card);
723 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
724 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
725 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
726 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
727 hw->exclusive = 1;
Mauro Carvalho Chehaba5f86612017-04-16 21:51:11 -0300728 sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
730 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
731 if (err < 0) {
732 return err;
733 }
734 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
735 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
736
737 pcm->private_data = usX2Y(card)->subs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 pcm->info_flags = 0;
739
740 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
Takashi Iwai4d1b5302019-02-04 16:36:10 +0100741 snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
742 SNDRV_DMA_TYPE_CONTINUOUS,
743 snd_dma_continuous_data(GFP_KERNEL),
744 64*1024, 128*1024);
745 snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
746 SNDRV_DMA_TYPE_CONTINUOUS,
747 snd_dma_continuous_data(GFP_KERNEL),
748 64*1024, 128*1024);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
750 return 0;
751}
752
753#else
754
Takashi Iwaibbe85bb2005-11-17 15:08:26 +0100755int usX2Y_hwdep_pcm_new(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756{
757 return 0;
758}
759
760#endif