]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - sound/usb/usx2y/usx2yhwdeppcm.c
mm, fs: reduce fault, page_mkwrite, and pfn_mkwrite to take only vmf
[mirror_ubuntu-jammy-kernel.git] / sound / usb / usx2y / usx2yhwdeppcm.c
CommitLineData
1da177e4
LT
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15 */
16
17/* USX2Y "rawusb" aka hwdep_pcm implementation
18
19 Its usb's unableness to atomically handle power of 2 period sized data chuncs
20 at standard samplerates,
21 what led to this part of the usx2y module:
22 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
25985edc 23 The pair uses a hardware dependent alsa-device for mmaped pcm transport.
1da177e4
LT
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).
fa2eb005 39
1da177e4 40 This is a first "proof of concept" implementation.
25985edc 41 Later, functionalities should migrate to more appropriate places:
1da177e4
LT
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
b27c187f 53#include <linux/delay.h>
5a0e3ad6 54#include <linux/gfp.h>
1da177e4
LT
55#include "usbusx2yaudio.c"
56
1d2019fb 57#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
1da177e4
LT
58
59#include <sound/hwdep.h>
60
61
bbe85bbd 62static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
1da177e4
LT
63{
64 struct urb *urb = subs->completed_urb;
bbe85bbd 65 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
1da177e4 66 int i, lens = 0, hwptr_done = subs->hwptr_done;
bbe85bbd 67 struct usX2Ydev *usX2Y = subs->usX2Y;
1da177e4
LT
68 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 */
6e8d5d2f 77 snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
1da177e4
LT
78 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
bbe85bbd
TI
94static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
95 struct usX2Ydev * usX2Y)
1da177e4
LT
96{
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 */
bbe85bbd
TI
110static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
111 struct urb *urb)
1da177e4
LT
112{
113 int count, counts, pack;
bbe85bbd 114 struct usX2Ydev *usX2Y = subs->usX2Y;
1da177e4 115 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
bbe85bbd 116 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
1da177e4
LT
117
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) {
d3d579f8 131 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
1da177e4
LT
132 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
bbe85bbd
TI
149static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
150 struct urb *urb)
1da177e4
LT
151{
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) {
bbe85bbd 156 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
1da177e4
LT
157 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
bbe85bbd
TI
172static 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)
1da177e4
LT
176{
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);
cb432379
TI
184 else if (state >= state_PRERUNNING)
185 atomic_inc(&playbacksubs->state);
1da177e4
LT
186 } 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;
cb432379
TI
212 } else if (state >= state_PRERUNNING)
213 atomic_inc(&capsubs->state);
1da177e4
LT
214 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
7d12e780 230static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
1da177e4 231{
bbe85bbd
TI
232 struct snd_usX2Y_substream *subs = urb->context;
233 struct usX2Ydev *usX2Y = subs->usX2Y;
234 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
1da177e4
LT
235
236 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
bbe85bbd 237 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
a014bbad 238 usb_get_current_frame_number(usX2Y->dev),
bbe85bbd
TI
239 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240 urb->status, urb->start_frame);
1da177e4
LT
241 return;
242 }
243 if (unlikely(urb->status)) {
244 usX2Y_error_urb_status(usX2Y, subs, urb);
245 return;
246 }
1da177e4 247
a9d14bc0 248 subs->completed_urb = urb;
1da177e4
LT
249 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)) {
635bbb35
KW
255 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
256 usX2Y->wait_iso_frame += nr_of_packs();
257 else {
1da177e4
LT
258 snd_printdd("\n");
259 usX2Y_clients_stop(usX2Y);
260 }
261 }
262}
263
264
bbe85bbd 265static void usX2Y_hwdep_urb_release(struct urb **urb)
1da177e4
LT
266{
267 usb_kill_urb(*urb);
268 usb_free_urb(*urb);
269 *urb = NULL;
270}
271
272/*
273 * release a substream
274 */
bbe85bbd 275static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
1da177e4
LT
276{
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
bbe85bbd 283static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
1da177e4
LT
284{
285 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
286 usX2Y->prepare_subs = NULL;
287}
288
7d12e780 289static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
1da177e4 290{
bbe85bbd
TI
291 struct snd_usX2Y_substream *subs = urb->context;
292 struct usX2Ydev *usX2Y = subs->usX2Y;
293 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
1da177e4
LT
294 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]) {
bbe85bbd 298 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
1da177e4
LT
299 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
7d12e780 306 i_usX2Y_usbpcm_urb_complete(urb);
1da177e4
LT
307}
308
309/*
310 * initialize a substream's urbs
311 */
bbe85bbd 312static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
1da177e4
LT
313{
314 int i;
315 unsigned int pipe;
316 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
a014bbad 317 struct usb_device *dev = subs->usX2Y->dev;
1da177e4
LT
318
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++) {
cb432379 327 struct urb **purb = subs->urb + i;
1da177e4
LT
328 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 */
bbe85bbd 356static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
1da177e4 357{
bbe85bbd
TI
358 struct snd_pcm_runtime *runtime = substream->runtime;
359 struct snd_usX2Y_substream *subs = runtime->private_data,
1da177e4 360 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
e2439a54 361 mutex_lock(&subs->usX2Y->pcm_mutex);
1da177e4
LT
362 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
363
364 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
bbe85bbd 365 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
1da177e4
LT
366 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 {
bbe85bbd 380 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
1da177e4
LT
381 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 }
e2439a54 390 mutex_unlock(&subs->usX2Y->pcm_mutex);
1da177e4
LT
391 return snd_pcm_lib_free_pages(substream);
392}
393
bbe85bbd 394static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
1da177e4 395{
bbe85bbd 396 struct usX2Ydev * usX2Y = subs->usX2Y;
1da177e4
LT
397 usX2Y->prepare_subs = subs;
398 subs->urb[0]->start_frame = -1;
7f927fcc 399 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
1da177e4
LT
400 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
401}
402
bbe85bbd 403static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
1da177e4
LT
404{
405 int p, u, err,
406 stream = subs->pcm_substream->stream;
bbe85bbd 407 struct usX2Ydev *usX2Y = subs->usX2Y;
1da177e4
LT
408
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) {
bbe85bbd 415 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
1da177e4
LT
416 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++) {
bbe85bbd 424 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
1da177e4
LT
425 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
426 goto start;
427 }
1da177e4
LT
428
429 start:
430 usX2Y_usbpcm_subs_startup(subs);
431 for (u = 0; u < NRURBS; u++) {
432 for (p = 0; 3 >= (stream + p); p += 2) {
bbe85bbd 433 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
1da177e4
LT
434 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);
a014bbad 440 urb->dev = usX2Y->dev;
1da177e4
LT
441 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);
635bbb35 452 if (u == 0)
1da177e4
LT
453 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 */
bbe85bbd 481static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
1da177e4 482{
bbe85bbd
TI
483 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];
1da177e4
LT
487 int err = 0;
488 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
489
490 if (NULL == usX2Y->hwdep_pcm_shm) {
bbe85bbd 491 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
1da177e4 492 return -ENOMEM;
bbe85bbd 493 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
1da177e4
LT
494 }
495
e2439a54 496 mutex_lock(&usX2Y->pcm_mutex);
1da177e4
LT
497 usX2Y_subs_prepare(subs);
498// Start hardware streams
499// SyncStream first....
500 if (atomic_read(&capsubs->state) < state_PREPARED) {
501 if (usX2Y->format != runtime->format)
502 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
503 goto up_prepare_mutex;
504 if (usX2Y->rate != runtime->rate)
505 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
506 goto up_prepare_mutex;
bbe85bbd
TI
507 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
508 "self" : "playpipe");
1da177e4
LT
509 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
510 goto up_prepare_mutex;
511 }
512
513 if (subs != capsubs) {
514 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
515 if (atomic_read(&subs->state) < state_PREPARED) {
bbe85bbd
TI
516 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
517 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
518 snd_printdd("Wait: iso_frames_per_buffer=%i,"
519 "captured_iso_frames=%i\n",
520 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
521 usX2Y->hwdep_pcm_shm->captured_iso_frames);
b27c187f 522 if (msleep_interruptible(10)) {
1da177e4
LT
523 err = -ERESTARTSYS;
524 goto up_prepare_mutex;
525 }
526 }
527 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
528 goto up_prepare_mutex;
529 }
bbe85bbd
TI
530 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
531 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
532 usX2Y->hwdep_pcm_shm->captured_iso_frames);
1da177e4
LT
533 } else
534 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
535
536 up_prepare_mutex:
e2439a54 537 mutex_unlock(&usX2Y->pcm_mutex);
1da177e4
LT
538 return err;
539}
540
bbe85bbd 541static struct snd_pcm_hardware snd_usX2Y_4c =
1da177e4
LT
542{
543 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
544 SNDRV_PCM_INFO_BLOCK_TRANSFER |
545 SNDRV_PCM_INFO_MMAP_VALID),
546 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
547 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
548 .rate_min = 44100,
549 .rate_max = 48000,
550 .channels_min = 2,
551 .channels_max = 4,
552 .buffer_bytes_max = (2*128*1024),
553 .period_bytes_min = 64,
554 .period_bytes_max = (128*1024),
555 .periods_min = 2,
556 .periods_max = 1024,
557 .fifo_size = 0
558};
559
560
561
bbe85bbd 562static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
1da177e4 563{
bbe85bbd 564 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
1da177e4 565 snd_pcm_substream_chip(substream))[substream->stream];
bbe85bbd 566 struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4
LT
567
568 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
569 return -EBUSY;
570
571 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
572 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
573 runtime->private_data = subs;
574 subs->pcm_substream = substream;
575 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
576 return 0;
577}
578
579
bbe85bbd 580static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
1da177e4 581{
bbe85bbd
TI
582 struct snd_pcm_runtime *runtime = substream->runtime;
583 struct snd_usX2Y_substream *subs = runtime->private_data;
cb432379 584
1da177e4 585 subs->pcm_substream = NULL;
cb432379 586 return 0;
1da177e4
LT
587}
588
589
bbe85bbd 590static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
1da177e4
LT
591{
592 .open = snd_usX2Y_usbpcm_open,
593 .close = snd_usX2Y_usbpcm_close,
594 .ioctl = snd_pcm_lib_ioctl,
595 .hw_params = snd_usX2Y_pcm_hw_params,
596 .hw_free = snd_usX2Y_usbpcm_hw_free,
597 .prepare = snd_usX2Y_usbpcm_prepare,
598 .trigger = snd_usX2Y_pcm_trigger,
599 .pointer = snd_usX2Y_pcm_pointer,
600};
601
602
e2439a54 603static int usX2Y_pcms_busy_check(struct snd_card *card)
1da177e4 604{
e2439a54
TI
605 struct usX2Ydev *dev = usX2Y(card);
606 int i;
1da177e4 607
e2439a54
TI
608 for (i = 0; i < dev->pcm_devs * 2; i++) {
609 struct snd_usX2Y_substream *subs = dev->subs[i];
610 if (subs && subs->pcm_substream &&
611 SUBSTREAM_BUSY(subs->pcm_substream))
612 return -EBUSY;
1da177e4 613 }
e2439a54 614 return 0;
1da177e4
LT
615}
616
bbe85bbd 617static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
1da177e4 618{
bbe85bbd 619 struct snd_card *card = hw->card;
e2439a54
TI
620 int err;
621
622 mutex_lock(&usX2Y(card)->pcm_mutex);
623 err = usX2Y_pcms_busy_check(card);
624 if (!err)
1da177e4 625 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
e2439a54 626 mutex_unlock(&usX2Y(card)->pcm_mutex);
1da177e4
LT
627 return err;
628}
629
630
bbe85bbd 631static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
1da177e4 632{
bbe85bbd 633 struct snd_card *card = hw->card;
e2439a54
TI
634 int err;
635
636 mutex_lock(&usX2Y(card)->pcm_mutex);
637 err = usX2Y_pcms_busy_check(card);
638 if (!err)
1da177e4 639 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
e2439a54 640 mutex_unlock(&usX2Y(card)->pcm_mutex);
1da177e4
LT
641 return err;
642}
643
644
645static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
646{
647}
648
649
650static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
651{
652}
653
654
11bac800 655static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
1da177e4
LT
656{
657 unsigned long offset;
1da177e4
LT
658 void *vaddr;
659
eb415b8f 660 offset = vmf->pgoff << PAGE_SHIFT;
11bac800 661 vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
eb415b8f
NP
662 vmf->page = virt_to_page(vaddr);
663 get_page(vmf->page);
664 return 0;
1da177e4
LT
665}
666
667
f0f37e2f 668static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
1da177e4
LT
669 .open = snd_usX2Y_hwdep_pcm_vm_open,
670 .close = snd_usX2Y_hwdep_pcm_vm_close,
eb415b8f 671 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
1da177e4
LT
672};
673
674
bbe85bbd 675static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
1da177e4
LT
676{
677 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
bbe85bbd 678 struct usX2Ydev *usX2Y = hw->private_data;
1da177e4 679
cb432379 680 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
1da177e4
LT
681 return -EBUSY;
682
683 /* if userspace tries to mmap beyond end of our buffer, fail */
bbe85bbd
TI
684 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
685 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
1da177e4
LT
686 return -EINVAL;
687 }
688
689 if (!usX2Y->hwdep_pcm_shm) {
690 return -ENODEV;
691 }
692 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
314e51b9 693 area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
1da177e4
LT
694 area->vm_private_data = hw->private_data;
695 return 0;
696}
697
698
bbe85bbd 699static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
1da177e4 700{
bbe85bbd 701 struct usX2Ydev *usX2Y = hwdep->private_data;
1da177e4 702 if (NULL != usX2Y->hwdep_pcm_shm)
bbe85bbd 703 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
1da177e4
LT
704}
705
706
bbe85bbd 707int usX2Y_hwdep_pcm_new(struct snd_card *card)
1da177e4
LT
708{
709 int err;
bbe85bbd
TI
710 struct snd_hwdep *hw;
711 struct snd_pcm *pcm;
a014bbad 712 struct usb_device *dev = usX2Y(card)->dev;
1da177e4
LT
713 if (1 != nr_of_packs())
714 return 0;
715
cb432379 716 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
1da177e4 717 return err;
cb432379 718
1da177e4
LT
719 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
720 hw->private_data = usX2Y(card);
721 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
722 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
723 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
724 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
725 hw->exclusive = 1;
726 sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
727
728 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
729 if (err < 0) {
730 return err;
731 }
732 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
733 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
734
735 pcm->private_data = usX2Y(card)->subs;
1da177e4
LT
736 pcm->info_flags = 0;
737
738 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
739 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
740 SNDRV_DMA_TYPE_CONTINUOUS,
741 snd_dma_continuous_data(GFP_KERNEL),
742 64*1024, 128*1024)) ||
743 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
744 SNDRV_DMA_TYPE_CONTINUOUS,
745 snd_dma_continuous_data(GFP_KERNEL),
746 64*1024, 128*1024))) {
1da177e4
LT
747 return err;
748 }
749
750
751 return 0;
752}
753
754#else
755
bbe85bbd 756int usX2Y_hwdep_pcm_new(struct snd_card *card)
1da177e4
LT
757{
758 return 0;
759}
760
761#endif