1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 */
4
5/* USX2Y "rawusb" aka hwdep_pcm implementation
6
7 Its usb's unableness to atomically handle power of 2 period sized data chuncs
8 at standard samplerates,
9 what led to this part of the usx2y module:
10 It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
11 The pair uses a hardware dependent alsa-device for mmaped pcm transport.
12 Advantage achieved:
13         The usb_hc moves pcm data from/into memory via DMA.
14         That memory is mmaped by jack's usx2y driver.
15         Jack's usx2y driver is the first/last to read/write pcm data.
16         Read/write is a combination of power of 2 period shaping and
17         float/int conversation.
18         Compared to mainline alsa/jack we leave out power of 2 period shaping inside
19         snd-usb-usx2y which needs memcpy() and additional buffers.
20         As a side effect possible unwanted pcm-data coruption resulting of
21         standard alsa's snd-usb-usx2y period shaping scheme falls away.
22         Result is sane jack operation at buffering schemes down to 128frames,
23         2 periods.
24         plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
25         cost of easier triggered i.e. aeolus xruns (128 or 256frames,
26         2periods works but is useless cause of crackling).
27
28 This is a first "proof of concept" implementation.
29 Later, functionalities should migrate to more appropriate places:
30 Userland:
31 - The jackd could mmap its float-pcm buffers directly from alsa-lib.
32 - alsa-lib could provide power of 2 period sized shaping combined with int/float
33   conversation.
34   Currently the usx2y jack driver provides above 2 services.
35 Kernel:
36 - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
37   devices can use it.
38   Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
39*/
40
41#include <linux/delay.h>
42#include <linux/gfp.h>
43#include "usbusx2yaudio.c"
44
45#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
46
47#include <sound/hwdep.h>
48
49static int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs)
50{
51	struct urb	*urb = subs->completed_urb;
52	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
53	int		i, lens = 0, hwptr_done = subs->hwptr_done;
54	struct usx2ydev	*usx2y = subs->usx2y;
55	int head;
56
57	if (usx2y->hwdep_pcm_shm->capture_iso_start < 0) { //FIXME
58		head = usx2y->hwdep_pcm_shm->captured_iso_head + 1;
59		if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso))
60			head = 0;
61		usx2y->hwdep_pcm_shm->capture_iso_start = head;
62		snd_printdd("cap start %i\n", head);
63	}
64	for (i = 0; i < nr_of_packs(); i++) {
65		if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
66			snd_printk(KERN_ERR
67				   "active frame status %i. Most probably some hardware problem.\n",
68				   urb->iso_frame_desc[i].status);
69			return urb->iso_frame_desc[i].status;
70		}
71		lens += urb->iso_frame_desc[i].actual_length / usx2y->stride;
72	}
73	hwptr_done += lens;
74	if (hwptr_done >= runtime->buffer_size)
75		hwptr_done -= runtime->buffer_size;
76	subs->hwptr_done = hwptr_done;
77	subs->transfer_done += lens;
78	/* update the pointer, call callback if necessary */
79	if (subs->transfer_done >= runtime->period_size) {
80		subs->transfer_done -= runtime->period_size;
81		snd_pcm_period_elapsed(subs->pcm_substream);
82	}
83	return 0;
84}
85
86static int usx2y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
87					      struct usx2ydev *usx2y)
88{
89	return (runtime->buffer_size * 1000) / usx2y->rate + 1;	//FIXME: so far only correct period_size == 2^x ?
90}
91
92/*
93 * prepare urb for playback data pipe
94 *
95 * we copy the data directly from the pcm buffer.
96 * the current position to be copied is held in hwptr field.
97 * since a urb can handle only a single linear buffer, if the total
98 * transferred area overflows the buffer boundary, we cannot send
99 * it directly from the buffer.  thus the data is once copied to
100 * a temporary buffer and urb points to that.
101 */
102static int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs,
103					struct urb *urb)
104{
105	int count, counts, pack;
106	struct usx2ydev *usx2y = subs->usx2y;
107	struct snd_usx2y_hwdep_pcm_shm *shm = usx2y->hwdep_pcm_shm;
108	struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
109
110	if (shm->playback_iso_start < 0) {
111		shm->playback_iso_start = shm->captured_iso_head -
112			usx2y_iso_frames_per_buffer(runtime, usx2y);
113		if (shm->playback_iso_start < 0)
114			shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
115		shm->playback_iso_head = shm->playback_iso_start;
116	}
117
118	count = 0;
119	for (pack = 0; pack < nr_of_packs(); pack++) {
120		/* calculate the size of a packet */
121		counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride;
122		if (counts < 43 || counts > 50) {
123			snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
124			return -EPIPE;
125		}
126		/* set up descriptor */
127		urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
128		urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
129		if (atomic_read(&subs->state) != STATE_RUNNING)
130			memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
131			       urb->iso_frame_desc[pack].length);
132		if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
133			shm->playback_iso_head = 0;
134		count += counts;
135	}
136	urb->transfer_buffer_length = count * usx2y->stride;
137	return 0;
138}
139
140static void usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream *subs,
141					      struct urb *urb)
142{
143	struct usb_iso_packet_descriptor *desc;
144	struct snd_usx2y_hwdep_pcm_shm *shm;
145	int pack, head;
146
147	for (pack = 0; pack < nr_of_packs(); ++pack) {
148		desc = urb->iso_frame_desc + pack;
149		if (subs) {
150			shm = subs->usx2y->hwdep_pcm_shm;
151			head = shm->captured_iso_head + 1;
152			if (head >= ARRAY_SIZE(shm->captured_iso))
153				head = 0;
154			shm->captured_iso[head].frame = urb->start_frame + pack;
155			shm->captured_iso[head].offset = desc->offset;
156			shm->captured_iso[head].length = desc->actual_length;
157			shm->captured_iso_head = head;
158			shm->captured_iso_frames++;
159		}
160		desc->offset += desc->length * NRURBS * nr_of_packs();
161		if (desc->offset + desc->length >= SSS)
162			desc->offset -= (SSS - desc->length);
163	}
164}
165
166static int usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream *capsubs,
167					  struct snd_usx2y_substream *capsubs2,
168					  struct snd_usx2y_substream *playbacksubs,
169					  int frame)
170{
171	int err, state;
172	struct urb *urb = playbacksubs->completed_urb;
173
174	state = atomic_read(&playbacksubs->state);
175	if (urb) {
176		if (state == STATE_RUNNING)
177			usx2y_urb_play_retire(playbacksubs, urb);
178		else if (state >= STATE_PRERUNNING)
179			atomic_inc(&playbacksubs->state);
180	} else {
181		switch (state) {
182		case STATE_STARTING1:
183			urb = playbacksubs->urb[0];
184			atomic_inc(&playbacksubs->state);
185			break;
186		case STATE_STARTING2:
187			urb = playbacksubs->urb[1];
188			atomic_inc(&playbacksubs->state);
189			break;
190		}
191	}
192	if (urb) {
193		err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
194		if (err)
195			return err;
196		err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
197		if (err)
198			return err;
199	}
200
201	playbacksubs->completed_urb = NULL;
202
203	state = atomic_read(&capsubs->state);
204	if (state >= STATE_PREPARED) {
205		if (state == STATE_RUNNING) {
206			err = usx2y_usbpcm_urb_capt_retire(capsubs);
207			if (err)
208				return err;
209		} else if (state >= STATE_PRERUNNING) {
210			atomic_inc(&capsubs->state);
211		}
212		usx2y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
213		if (capsubs2)
214			usx2y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
215		err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame);
216		if (err)
217			return err;
218		if (capsubs2) {
219			err = usx2y_urb_submit(capsubs2, capsubs2->completed_urb, frame);
220			if (err)
221				return err;
222		}
223	}
224	capsubs->completed_urb = NULL;
225	if (capsubs2)
226		capsubs2->completed_urb = NULL;
227	return 0;
228}
229
230static void i_usx2y_usbpcm_urb_complete(struct urb *urb)
231{
232	struct snd_usx2y_substream *subs = urb->context;
233	struct usx2ydev *usx2y = subs->usx2y;
234	struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs;
235
236	if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
237		snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
238			    usb_get_current_frame_number(usx2y->dev),
239			    subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240			    urb->status, urb->start_frame);
241		return;
242	}
243	if (unlikely(urb->status)) {
244		usx2y_error_urb_status(usx2y, subs, urb);
245		return;
246	}
247
248	subs->completed_urb = urb;
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	    (!capsubs2 || capsubs2->completed_urb) &&
254	    (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) {
255		if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
256			usx2y->wait_iso_frame += nr_of_packs();
257		} else {
258			snd_printdd("\n");
259			usx2y_clients_stop(usx2y);
260		}
261	}
262}
263
264static void usx2y_hwdep_urb_release(struct urb **urb)
265{
266	usb_kill_urb(*urb);
267	usb_free_urb(*urb);
268	*urb = NULL;
269}
270
271/*
272 * release a substream
273 */
274static void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs)
275{
276	int i;
277
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
283static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y)
284{
285	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete);
286	usx2y->prepare_subs = NULL;
287}
288
289static void i_usx2y_usbpcm_subs_startup(struct urb *urb)
290{
291	struct snd_usx2y_substream *subs = urb->context;
292	struct usx2ydev *usx2y = subs->usx2y;
293	struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs;
294	struct snd_usx2y_substream *cap_subs2;
295
296	if (prepare_subs &&
297	    urb->start_frame == prepare_subs->urb[0]->start_frame) {
298		atomic_inc(&prepare_subs->state);
299		if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
300			cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
301			if (cap_subs2)
302				atomic_inc(&cap_subs2->state);
303		}
304		usx2y_usbpcm_subs_startup_finish(usx2y);
305		wake_up(&usx2y->prepare_wait_queue);
306	}
307
308	i_usx2y_usbpcm_urb_complete(urb);
309}
310
311/*
312 * initialize a substream's urbs
313 */
314static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs)
315{
316	int i;
317	unsigned int pipe;
318	int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
319	struct usb_device *dev = subs->usx2y->dev;
320	struct urb **purb;
321
322	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
323			usb_rcvisocpipe(dev, subs->endpoint);
324	subs->maxpacksize = usb_maxpacket(dev, pipe);
325	if (!subs->maxpacksize)
326		return -EINVAL;
327
328	/* allocate and initialize data urbs */
329	for (i = 0; i < NRURBS; i++) {
330		purb = subs->urb + i;
331		if (*purb) {
332			usb_kill_urb(*purb);
333			continue;
334		}
335		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
336		if (!*purb) {
337			usx2y_usbpcm_urbs_release(subs);
338			return -ENOMEM;
339		}
340		(*purb)->transfer_buffer = is_playback ?
341			subs->usx2y->hwdep_pcm_shm->playback : (
342				subs->endpoint == 0x8 ?
343				subs->usx2y->hwdep_pcm_shm->capture0x8 :
344				subs->usx2y->hwdep_pcm_shm->capture0xA);
345
346		(*purb)->dev = dev;
347		(*purb)->pipe = pipe;
348		(*purb)->number_of_packets = nr_of_packs();
349		(*purb)->context = subs;
350		(*purb)->interval = 1;
351		(*purb)->complete = i_usx2y_usbpcm_subs_startup;
352	}
353	return 0;
354}
355
356/*
357 * free the buffer
358 */
359static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
360{
361	struct snd_pcm_runtime *runtime = substream->runtime;
362	struct snd_usx2y_substream *subs = runtime->private_data;
363	struct snd_usx2y_substream *cap_subs;
364	struct snd_usx2y_substream *playback_subs;
365	struct snd_usx2y_substream *cap_subs2;
366
367	mutex_lock(&subs->usx2y->pcm_mutex);
368	snd_printdd("%s(%p)\n", __func__, substream);
369
370	cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
371	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
372		cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
373		atomic_set(&subs->state, STATE_STOPPED);
374		usx2y_usbpcm_urbs_release(subs);
375		if (!cap_subs->pcm_substream ||
376		    !cap_subs->pcm_substream->runtime ||
377		    cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) {
378			atomic_set(&cap_subs->state, STATE_STOPPED);
379			if (cap_subs2)
380				atomic_set(&cap_subs2->state, STATE_STOPPED);
381			usx2y_usbpcm_urbs_release(cap_subs);
382			if (cap_subs2)
383				usx2y_usbpcm_urbs_release(cap_subs2);
384		}
385	} else {
386		playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
387		if (atomic_read(&playback_subs->state) < STATE_PREPARED) {
388			atomic_set(&subs->state, STATE_STOPPED);
389			if (cap_subs2)
390				atomic_set(&cap_subs2->state, STATE_STOPPED);
391			usx2y_usbpcm_urbs_release(subs);
392			if (cap_subs2)
393				usx2y_usbpcm_urbs_release(cap_subs2);
394		}
395	}
396	mutex_unlock(&subs->usx2y->pcm_mutex);
397	return 0;
398}
399
400static void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs)
401{
402	struct usx2ydev *usx2y = subs->usx2y;
403
404	usx2y->prepare_subs = subs;
405	subs->urb[0]->start_frame = -1;
406	smp_wmb();	// Make sure above modifications are seen by i_usx2y_subs_startup()
407	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup);
408}
409
410static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs)
411{
412	int	p, u, err, stream = subs->pcm_substream->stream;
413	struct usx2ydev *usx2y = subs->usx2y;
414	struct urb *urb;
415	unsigned long pack;
416
417	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
418		usx2y->hwdep_pcm_shm->captured_iso_head = -1;
419		usx2y->hwdep_pcm_shm->captured_iso_frames = 0;
420	}
421
422	for (p = 0; 3 >= (stream + p); p += 2) {
423		struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
424		if (subs) {
425			err = usx2y_usbpcm_urbs_allocate(subs);
426			if (err < 0)
427				return err;
428			subs->completed_urb = NULL;
429		}
430	}
431
432	for (p = 0; p < 4; p++) {
433		struct snd_usx2y_substream *subs = usx2y->subs[p];
434
435		if (subs && atomic_read(&subs->state) >= STATE_PREPARED)
436			goto start;
437	}
438
439 start:
440	usx2y_usbpcm_subs_startup(subs);
441	for (u = 0; u < NRURBS; u++) {
442		for (p = 0; 3 >= (stream + p); p += 2) {
443			struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
444
445			if (!subs)
446				continue;
447			urb = subs->urb[u];
448			if (usb_pipein(urb->pipe)) {
449				if (!u)
450					atomic_set(&subs->state, STATE_STARTING3);
451				urb->dev = usx2y->dev;
452				for (pack = 0; pack < nr_of_packs(); pack++) {
453					urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
454					urb->iso_frame_desc[pack].length = subs->maxpacksize;
455				}
456				urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
457				err = usb_submit_urb(urb, GFP_KERNEL);
458				if (err < 0) {
459					snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
460					err = -EPIPE;
461					goto cleanup;
462				}  else {
463					snd_printdd("%i\n", urb->start_frame);
464					if (!u)
465						usx2y->wait_iso_frame = urb->start_frame;
466				}
467				urb->transfer_flags = 0;
468			} else {
469				atomic_set(&subs->state, STATE_STARTING1);
470				break;
471			}
472		}
473	}
474	err = 0;
475	wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs);
476	if (atomic_read(&subs->state) != STATE_PREPARED)
477		err = -EPIPE;
478
479 cleanup:
480	if (err) {
481		usx2y_subs_startup_finish(usx2y);	// Call it now
482		usx2y_clients_stop(usx2y);	// something is completely wrong > stop everything
483	}
484	return err;
485}
486
487#define USX2Y_HWDEP_PCM_PAGES	\
488	PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm))
489
490/*
491 * prepare callback
492 *
493 * set format and initialize urbs
494 */
495static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
496{
497	struct snd_pcm_runtime *runtime = substream->runtime;
498	struct snd_usx2y_substream *subs = runtime->private_data;
499	struct usx2ydev *usx2y = subs->usx2y;
500	struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
501	int err = 0;
502
503	snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream);
504
505	mutex_lock(&usx2y->pcm_mutex);
506
507	if (!usx2y->hwdep_pcm_shm) {
508		usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES,
509							 GFP_KERNEL);
510		if (!usx2y->hwdep_pcm_shm) {
511			err = -ENOMEM;
512			goto up_prepare_mutex;
513		}
514		memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
515	}
516
517	usx2y_subs_prepare(subs);
518	// Start hardware streams
519	// SyncStream first....
520	if (atomic_read(&capsubs->state) < STATE_PREPARED) {
521		if (usx2y->format != runtime->format) {
522			err = usx2y_format_set(usx2y, runtime->format);
523			if (err < 0)
524				goto up_prepare_mutex;
525		}
526		if (usx2y->rate != runtime->rate) {
527			err = usx2y_rate_set(usx2y, runtime->rate);
528			if (err < 0)
529				goto up_prepare_mutex;
530		}
531		snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
532			    "self" : "playpipe");
533		err = usx2y_usbpcm_urbs_start(capsubs);
534		if (err < 0)
535			goto up_prepare_mutex;
536	}
537
538	if (subs != capsubs) {
539		usx2y->hwdep_pcm_shm->playback_iso_start = -1;
540		if (atomic_read(&subs->state) < STATE_PREPARED) {
541			while (usx2y_iso_frames_per_buffer(runtime, usx2y) >
542			       usx2y->hwdep_pcm_shm->captured_iso_frames) {
543				snd_printdd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
544					    usx2y_iso_frames_per_buffer(runtime, usx2y),
545					    usx2y->hwdep_pcm_shm->captured_iso_frames);
546				if (msleep_interruptible(10)) {
547					err = -ERESTARTSYS;
548					goto up_prepare_mutex;
549				}
550			}
551			err = usx2y_usbpcm_urbs_start(subs);
552			if (err < 0)
553				goto up_prepare_mutex;
554		}
555		snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
556			    usx2y_iso_frames_per_buffer(runtime, usx2y),
557			    usx2y->hwdep_pcm_shm->captured_iso_frames);
558	} else {
559		usx2y->hwdep_pcm_shm->capture_iso_start = -1;
560	}
561
562 up_prepare_mutex:
563	mutex_unlock(&usx2y->pcm_mutex);
564	return err;
565}
566
567static const struct snd_pcm_hardware snd_usx2y_4c = {
568	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
569				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
570				 SNDRV_PCM_INFO_MMAP_VALID),
571	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
572	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
573	.rate_min =                44100,
574	.rate_max =                48000,
575	.channels_min =            2,
576	.channels_max =            4,
577	.buffer_bytes_max =	(2*128*1024),
578	.period_bytes_min =	64,
579	.period_bytes_max =	(128*1024),
580	.periods_min =		2,
581	.periods_max =		1024,
582	.fifo_size =              0
583};
584
585static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream)
586{
587	struct snd_usx2y_substream	*subs =
588		((struct snd_usx2y_substream **)
589		 snd_pcm_substream_chip(substream))[substream->stream];
590	struct snd_pcm_runtime	*runtime = substream->runtime;
591
592	if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
593		return -EBUSY;
594
595	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
596		runtime->hw = snd_usx2y_2c;
597	else
598		runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c);
599	runtime->private_data = subs;
600	subs->pcm_substream = substream;
601	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
602	return 0;
603}
604
605static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream)
606{
607	struct snd_pcm_runtime *runtime = substream->runtime;
608	struct snd_usx2y_substream *subs = runtime->private_data;
609
610	subs->pcm_substream = NULL;
611	return 0;
612}
613
614static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = {
615	.open =		snd_usx2y_usbpcm_open,
616	.close =	snd_usx2y_usbpcm_close,
617	.hw_params =	snd_usx2y_pcm_hw_params,
618	.hw_free =	snd_usx2y_usbpcm_hw_free,
619	.prepare =	snd_usx2y_usbpcm_prepare,
620	.trigger =	snd_usx2y_pcm_trigger,
621	.pointer =	snd_usx2y_pcm_pointer,
622};
623
624static int usx2y_pcms_busy_check(struct snd_card *card)
625{
626	struct usx2ydev	*dev = usx2y(card);
627	struct snd_usx2y_substream *subs;
628	int i;
629
630	for (i = 0; i < dev->pcm_devs * 2; i++) {
631		subs = dev->subs[i];
632		if (subs && subs->pcm_substream &&
633		    SUBSTREAM_BUSY(subs->pcm_substream))
634			return -EBUSY;
635	}
636	return 0;
637}
638
639static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
640{
641	struct snd_card *card = hw->card;
642	int err;
643
644	mutex_lock(&usx2y(card)->pcm_mutex);
645	err = usx2y_pcms_busy_check(card);
646	if (!err)
647		usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
648	mutex_unlock(&usx2y(card)->pcm_mutex);
649	return err;
650}
651
652static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
653{
654	struct snd_card *card = hw->card;
655	int err;
656
657	mutex_lock(&usx2y(card)->pcm_mutex);
658	err = usx2y_pcms_busy_check(card);
659	if (!err)
660		usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
661	mutex_unlock(&usx2y(card)->pcm_mutex);
662	return err;
663}
664
665static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
666{
667}
668
669static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
670{
671}
672
673static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
674{
675	unsigned long offset;
676	void *vaddr;
677
678	offset = vmf->pgoff << PAGE_SHIFT;
679	vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
680	vmf->page = virt_to_page(vaddr);
681	get_page(vmf->page);
682	return 0;
683}
684
685static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = {
686	.open = snd_usx2y_hwdep_pcm_vm_open,
687	.close = snd_usx2y_hwdep_pcm_vm_close,
688	.fault = snd_usx2y_hwdep_pcm_vm_fault,
689};
690
691static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
692{
693	unsigned long	size = (unsigned long)(area->vm_end - area->vm_start);
694	struct usx2ydev	*usx2y = hw->private_data;
695
696	if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT))
697		return -EBUSY;
698
699	/* if userspace tries to mmap beyond end of our buffer, fail */
700	if (size > USX2Y_HWDEP_PCM_PAGES) {
701		snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
702		return -EINVAL;
703	}
704
705	if (!usx2y->hwdep_pcm_shm)
706		return -ENODEV;
707
708	area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops;
709	vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
710	area->vm_private_data = hw->private_data;
711	return 0;
712}
713
714static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
715{
716	struct usx2ydev *usx2y = hwdep->private_data;
717
718	if (usx2y->hwdep_pcm_shm)
719		free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
720}
721
722int usx2y_hwdep_pcm_new(struct snd_card *card)
723{
724	int err;
725	struct snd_hwdep *hw;
726	struct snd_pcm *pcm;
727	struct usb_device *dev = usx2y(card)->dev;
728
729	if (nr_of_packs() != 1)
730		return 0;
731
732	err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw);
733	if (err < 0)
734		return err;
735
736	hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
737	hw->private_data = usx2y(card);
738	hw->private_free = snd_usx2y_hwdep_pcm_private_free;
739	hw->ops.open = snd_usx2y_hwdep_pcm_open;
740	hw->ops.release = snd_usx2y_hwdep_pcm_release;
741	hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap;
742	hw->exclusive = 1;
743	sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
744
745	err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
746	if (err < 0)
747		return err;
748
749	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops);
750	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops);
751
752	pcm->private_data = usx2y(card)->subs;
753	pcm->info_flags = 0;
754
755	sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
756	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
757				   SNDRV_DMA_TYPE_CONTINUOUS,
758				   NULL,
759				   64*1024, 128*1024);
760	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
761				   SNDRV_DMA_TYPE_CONTINUOUS,
762				   NULL,
763				   64*1024, 128*1024);
764
765	return 0;
766}
767
768#else
769
770int usx2y_hwdep_pcm_new(struct snd_card *card)
771{
772	return 0;
773}
774
775#endif
776