File Index
Files and directories
Sign in
FILE

usx2yhwdeppcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/usb/usx2y/usx2yhwdeppcm.c C
  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		dev_dbg(&usx2y->dev->dev, "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			dev_err(&usx2y->dev->dev,
 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			dev_err(&usx2y->dev->dev, "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		dev_dbg(&usx2y->dev->dev,
238			"hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
239			usb_get_current_frame_number(usx2y->dev),
240			subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
241			urb->status, urb->start_frame);
242		return;
243	}
244	if (unlikely(urb->status)) {
245		usx2y_error_urb_status(usx2y, subs, urb);
246		return;
247	}
248
249	subs->completed_urb = urb;
250	capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
251	capsubs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
252	playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
253	if (capsubs->completed_urb && atomic_read(&capsubs->state) >= STATE_PREPARED &&
254	    (!capsubs2 || capsubs2->completed_urb) &&
255	    (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) {
256		if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
257			usx2y->wait_iso_frame += nr_of_packs();
258		} else {
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	dev_dbg(&subs->usx2y->dev->dev,
279		"snd_usx2y_urbs_release() %i\n", subs->endpoint);
280	for (i = 0; i < NRURBS; i++)
281		usx2y_hwdep_urb_release(subs->urb + i);
282}
283
284static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y)
285{
286	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete);
287	usx2y->prepare_subs = NULL;
288}
289
290static void i_usx2y_usbpcm_subs_startup(struct urb *urb)
291{
292	struct snd_usx2y_substream *subs = urb->context;
293	struct usx2ydev *usx2y = subs->usx2y;
294	struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs;
295	struct snd_usx2y_substream *cap_subs2;
296
297	if (prepare_subs &&
298	    urb->start_frame == prepare_subs->urb[0]->start_frame) {
299		atomic_inc(&prepare_subs->state);
300		if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
301			cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
302			if (cap_subs2)
303				atomic_inc(&cap_subs2->state);
304		}
305		usx2y_usbpcm_subs_startup_finish(usx2y);
306		wake_up(&usx2y->prepare_wait_queue);
307	}
308
309	i_usx2y_usbpcm_urb_complete(urb);
310}
311
312/*
313 * initialize a substream's urbs
314 */
315static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs)
316{
317	int i;
318	unsigned int pipe;
319	int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
320	struct usb_device *dev = subs->usx2y->dev;
321	struct urb **purb;
322
323	pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
324			usb_rcvisocpipe(dev, subs->endpoint);
325	subs->maxpacksize = usb_maxpacket(dev, pipe);
326	if (!subs->maxpacksize)
327		return -EINVAL;
328
329	/* allocate and initialize data urbs */
330	for (i = 0; i < NRURBS; i++) {
331		purb = subs->urb + i;
332		if (*purb) {
333			usb_kill_urb(*purb);
334			continue;
335		}
336		*purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
337		if (!*purb) {
338			usx2y_usbpcm_urbs_release(subs);
339			return -ENOMEM;
340		}
341		(*purb)->transfer_buffer = is_playback ?
342			subs->usx2y->hwdep_pcm_shm->playback : (
343				subs->endpoint == 0x8 ?
344				subs->usx2y->hwdep_pcm_shm->capture0x8 :
345				subs->usx2y->hwdep_pcm_shm->capture0xA);
346
347		(*purb)->dev = dev;
348		(*purb)->pipe = pipe;
349		(*purb)->number_of_packets = nr_of_packs();
350		(*purb)->context = subs;
351		(*purb)->interval = 1;
352		(*purb)->complete = i_usx2y_usbpcm_subs_startup;
353	}
354	return 0;
355}
356
357/*
358 * free the buffer
359 */
360static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
361{
362	struct snd_pcm_runtime *runtime = substream->runtime;
363	struct snd_usx2y_substream *subs = runtime->private_data;
364	struct snd_usx2y_substream *cap_subs;
365	struct snd_usx2y_substream *playback_subs;
366	struct snd_usx2y_substream *cap_subs2;
367
368	guard(mutex)(&subs->usx2y->pcm_mutex);
369	dev_dbg(&subs->usx2y->dev->dev, "%s(%p)\n", __func__, substream);
370
371	cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
372	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
373		cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
374		atomic_set(&subs->state, STATE_STOPPED);
375		usx2y_usbpcm_urbs_release(subs);
376		if (!cap_subs->pcm_substream ||
377		    !cap_subs->pcm_substream->runtime ||
378		    cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) {
379			atomic_set(&cap_subs->state, STATE_STOPPED);
380			if (cap_subs2)
381				atomic_set(&cap_subs2->state, STATE_STOPPED);
382			usx2y_usbpcm_urbs_release(cap_subs);
383			if (cap_subs2)
384				usx2y_usbpcm_urbs_release(cap_subs2);
385		}
386	} else {
387		playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
388		if (atomic_read(&playback_subs->state) < STATE_PREPARED) {
389			atomic_set(&subs->state, STATE_STOPPED);
390			if (cap_subs2)
391				atomic_set(&cap_subs2->state, STATE_STOPPED);
392			usx2y_usbpcm_urbs_release(subs);
393			if (cap_subs2)
394				usx2y_usbpcm_urbs_release(cap_subs2);
395		}
396	}
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					dev_err(&urb->dev->dev,
460						"cannot usb_submit_urb() for urb %d, err = %d\n",
461						u, err);
462					err = -EPIPE;
463					goto cleanup;
464				}  else {
465					if (!u)
466						usx2y->wait_iso_frame = urb->start_frame;
467				}
468				urb->transfer_flags = 0;
469			} else {
470				atomic_set(&subs->state, STATE_STARTING1);
471				break;
472			}
473		}
474	}
475	err = 0;
476	wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs);
477	if (atomic_read(&subs->state) != STATE_PREPARED)
478		err = -EPIPE;
479
480 cleanup:
481	if (err) {
482		usx2y_subs_startup_finish(usx2y);	// Call it now
483		usx2y_clients_stop(usx2y);	// something is completely wrong > stop everything
484	}
485	return err;
486}
487
488#define USX2Y_HWDEP_PCM_PAGES	\
489	PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm))
490
491/*
492 * prepare callback
493 *
494 * set format and initialize urbs
495 */
496static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
497{
498	struct snd_pcm_runtime *runtime = substream->runtime;
499	struct snd_usx2y_substream *subs = runtime->private_data;
500	struct usx2ydev *usx2y = subs->usx2y;
501	struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
502	int err = 0;
503
504	dev_dbg(&usx2y->dev->dev, "snd_usx2y_pcm_prepare(%p)\n", substream);
505
506	guard(mutex)(&usx2y->pcm_mutex);
507
508	if (!usx2y->hwdep_pcm_shm) {
509		usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES,
510							 GFP_KERNEL);
511		if (!usx2y->hwdep_pcm_shm)
512			return -ENOMEM;
513		memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
514	}
515
516	usx2y_subs_prepare(subs);
517	// Start hardware streams
518	// SyncStream first....
519	if (atomic_read(&capsubs->state) < STATE_PREPARED) {
520		if (usx2y->format != runtime->format) {
521			err = usx2y_format_set(usx2y, runtime->format);
522			if (err < 0)
523				return err;
524		}
525		if (usx2y->rate != runtime->rate) {
526			err = usx2y_rate_set(usx2y, runtime->rate);
527			if (err < 0)
528				return err;
529		}
530		dev_dbg(&usx2y->dev->dev,
531			"starting capture pipe for %s\n", subs == capsubs ?
532			"self" : "playpipe");
533		err = usx2y_usbpcm_urbs_start(capsubs);
534		if (err < 0)
535			return err;
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				dev_dbg(&usx2y->dev->dev,
544					"Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
545					usx2y_iso_frames_per_buffer(runtime, usx2y),
546					usx2y->hwdep_pcm_shm->captured_iso_frames);
547				if (msleep_interruptible(10))
548					return -ERESTARTSYS;
549			}
550			err = usx2y_usbpcm_urbs_start(subs);
551			if (err < 0)
552				return err;
553		}
554		dev_dbg(&usx2y->dev->dev,
555			"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	return err;
563}
564
565static const struct snd_pcm_hardware snd_usx2y_4c = {
566	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
567				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
568				 SNDRV_PCM_INFO_MMAP_VALID),
569	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
570	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
571	.rate_min =                44100,
572	.rate_max =                48000,
573	.channels_min =            2,
574	.channels_max =            4,
575	.buffer_bytes_max =	(2*128*1024),
576	.period_bytes_min =	64,
577	.period_bytes_max =	(128*1024),
578	.periods_min =		2,
579	.periods_max =		1024,
580	.fifo_size =              0
581};
582
583static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream)
584{
585	struct snd_usx2y_substream	*subs =
586		((struct snd_usx2y_substream **)
587		 snd_pcm_substream_chip(substream))[substream->stream];
588	struct snd_pcm_runtime	*runtime = substream->runtime;
589
590	if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
591		return -EBUSY;
592
593	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
594		runtime->hw = snd_usx2y_2c;
595	else
596		runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c);
597	runtime->private_data = subs;
598	subs->pcm_substream = substream;
599	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
600	return 0;
601}
602
603static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream)
604{
605	struct snd_pcm_runtime *runtime = substream->runtime;
606	struct snd_usx2y_substream *subs = runtime->private_data;
607
608	subs->pcm_substream = NULL;
609	return 0;
610}
611
612static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = {
613	.open =		snd_usx2y_usbpcm_open,
614	.close =	snd_usx2y_usbpcm_close,
615	.hw_params =	snd_usx2y_pcm_hw_params,
616	.hw_free =	snd_usx2y_usbpcm_hw_free,
617	.prepare =	snd_usx2y_usbpcm_prepare,
618	.trigger =	snd_usx2y_pcm_trigger,
619	.pointer =	snd_usx2y_pcm_pointer,
620};
621
622static int usx2y_pcms_busy_check(struct snd_card *card)
623{
624	struct usx2ydev	*dev = usx2y(card);
625	struct snd_usx2y_substream *subs;
626	int i;
627
628	for (i = 0; i < dev->pcm_devs * 2; i++) {
629		subs = dev->subs[i];
630		if (subs && subs->pcm_substream &&
631		    SUBSTREAM_BUSY(subs->pcm_substream))
632			return -EBUSY;
633	}
634	return 0;
635}
636
637static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
638{
639	struct snd_card *card = hw->card;
640	int err;
641
642	guard(mutex)(&usx2y(card)->pcm_mutex);
643	err = usx2y_pcms_busy_check(card);
644	if (!err)
645		usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
646	return err;
647}
648
649static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
650{
651	struct snd_card *card = hw->card;
652	int err;
653
654	guard(mutex)(&usx2y(card)->pcm_mutex);
655	err = usx2y_pcms_busy_check(card);
656	if (!err)
657		usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
658	return err;
659}
660
661static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
662{
663}
664
665static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
666{
667}
668
669static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
670{
671	unsigned long offset;
672	void *vaddr;
673
674	offset = vmf->pgoff << PAGE_SHIFT;
675	vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
676	vmf->page = virt_to_page(vaddr);
677	get_page(vmf->page);
678	return 0;
679}
680
681static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = {
682	.open = snd_usx2y_hwdep_pcm_vm_open,
683	.close = snd_usx2y_hwdep_pcm_vm_close,
684	.fault = snd_usx2y_hwdep_pcm_vm_fault,
685};
686
687static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
688{
689	unsigned long	size = (unsigned long)(area->vm_end - area->vm_start);
690	struct usx2ydev	*usx2y = hw->private_data;
691
692	if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT))
693		return -EBUSY;
694
695	/* if userspace tries to mmap beyond end of our buffer, fail */
696	if (size > USX2Y_HWDEP_PCM_PAGES) {
697		dev_dbg(hw->card->dev, "%s: %lu > %lu\n", __func__,
698			size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
699		return -EINVAL;
700	}
701
702	if (!usx2y->hwdep_pcm_shm)
703		return -ENODEV;
704
705	area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops;
706	vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
707	area->vm_private_data = hw->private_data;
708	return 0;
709}
710
711static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
712{
713	struct usx2ydev *usx2y = hwdep->private_data;
714
715	if (usx2y->hwdep_pcm_shm)
716		free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
717}
718
719int usx2y_hwdep_pcm_new(struct snd_card *card)
720{
721	int err;
722	struct snd_hwdep *hw;
723	struct snd_pcm *pcm;
724	struct usb_device *dev = usx2y(card)->dev;
725
726	if (nr_of_packs() != 1)
727		return 0;
728
729	err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw);
730	if (err < 0)
731		return err;
732
733	hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
734	hw->private_data = usx2y(card);
735	hw->private_free = snd_usx2y_hwdep_pcm_private_free;
736	hw->ops.open = snd_usx2y_hwdep_pcm_open;
737	hw->ops.release = snd_usx2y_hwdep_pcm_release;
738	hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap;
739	hw->exclusive = 1;
740	sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
741
742	err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
743	if (err < 0)
744		return err;
745
746	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops);
747	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops);
748
749	pcm->private_data = usx2y(card)->subs;
750	pcm->info_flags = 0;
751
752	sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
753	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
754				   SNDRV_DMA_TYPE_CONTINUOUS,
755				   NULL,
756				   64*1024, 128*1024);
757	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
758				   SNDRV_DMA_TYPE_CONTINUOUS,
759				   NULL,
760				   64*1024, 128*1024);
761
762	return 0;
763}
764
765#else
766
767int usx2y_hwdep_pcm_new(struct snd_card *card)
768{
769	return 0;
770}
771
772#endif
~/ Read-only file preview.