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