cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

usx2yhwdeppcm.c (23500B)


      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->status ||
    378		    cap_subs->pcm_substream->runtime->status->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	mutex_unlock(&subs->usx2y->pcm_mutex);
    398	return 0;
    399}
    400
    401static void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs)
    402{
    403	struct usx2ydev *usx2y = subs->usx2y;
    404
    405	usx2y->prepare_subs = subs;
    406	subs->urb[0]->start_frame = -1;
    407	smp_wmb();	// Make sure above modifications are seen by i_usx2y_subs_startup()
    408	usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup);
    409}
    410
    411static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs)
    412{
    413	int	p, u, err, stream = subs->pcm_substream->stream;
    414	struct usx2ydev *usx2y = subs->usx2y;
    415	struct urb *urb;
    416	unsigned long pack;
    417
    418	if (stream == SNDRV_PCM_STREAM_CAPTURE) {
    419		usx2y->hwdep_pcm_shm->captured_iso_head = -1;
    420		usx2y->hwdep_pcm_shm->captured_iso_frames = 0;
    421	}
    422
    423	for (p = 0; 3 >= (stream + p); p += 2) {
    424		struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
    425		if (subs) {
    426			err = usx2y_usbpcm_urbs_allocate(subs);
    427			if (err < 0)
    428				return err;
    429			subs->completed_urb = NULL;
    430		}
    431	}
    432
    433	for (p = 0; p < 4; p++) {
    434		struct snd_usx2y_substream *subs = usx2y->subs[p];
    435
    436		if (subs && atomic_read(&subs->state) >= STATE_PREPARED)
    437			goto start;
    438	}
    439
    440 start:
    441	usx2y_usbpcm_subs_startup(subs);
    442	for (u = 0; u < NRURBS; u++) {
    443		for (p = 0; 3 >= (stream + p); p += 2) {
    444			struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
    445
    446			if (!subs)
    447				continue;
    448			urb = subs->urb[u];
    449			if (usb_pipein(urb->pipe)) {
    450				if (!u)
    451					atomic_set(&subs->state, STATE_STARTING3);
    452				urb->dev = usx2y->dev;
    453				for (pack = 0; pack < nr_of_packs(); pack++) {
    454					urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
    455					urb->iso_frame_desc[pack].length = subs->maxpacksize;
    456				}
    457				urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
    458				err = usb_submit_urb(urb, GFP_KERNEL);
    459				if (err < 0) {
    460					snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
    461					err = -EPIPE;
    462					goto cleanup;
    463				}  else {
    464					snd_printdd("%i\n", urb->start_frame);
    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	snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream);
    505
    506	mutex_lock(&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			err = -ENOMEM;
    513			goto up_prepare_mutex;
    514		}
    515		memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
    516	}
    517
    518	usx2y_subs_prepare(subs);
    519	// Start hardware streams
    520	// SyncStream first....
    521	if (atomic_read(&capsubs->state) < STATE_PREPARED) {
    522		if (usx2y->format != runtime->format) {
    523			err = usx2y_format_set(usx2y, runtime->format);
    524			if (err < 0)
    525				goto up_prepare_mutex;
    526		}
    527		if (usx2y->rate != runtime->rate) {
    528			err = usx2y_rate_set(usx2y, runtime->rate);
    529			if (err < 0)
    530				goto up_prepare_mutex;
    531		}
    532		snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
    533			    "self" : "playpipe");
    534		err = usx2y_usbpcm_urbs_start(capsubs);
    535		if (err < 0)
    536			goto up_prepare_mutex;
    537	}
    538
    539	if (subs != capsubs) {
    540		usx2y->hwdep_pcm_shm->playback_iso_start = -1;
    541		if (atomic_read(&subs->state) < STATE_PREPARED) {
    542			while (usx2y_iso_frames_per_buffer(runtime, usx2y) >
    543			       usx2y->hwdep_pcm_shm->captured_iso_frames) {
    544				snd_printdd("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					err = -ERESTARTSYS;
    549					goto up_prepare_mutex;
    550				}
    551			}
    552			err = usx2y_usbpcm_urbs_start(subs);
    553			if (err < 0)
    554				goto up_prepare_mutex;
    555		}
    556		snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
    557			    usx2y_iso_frames_per_buffer(runtime, usx2y),
    558			    usx2y->hwdep_pcm_shm->captured_iso_frames);
    559	} else {
    560		usx2y->hwdep_pcm_shm->capture_iso_start = -1;
    561	}
    562
    563 up_prepare_mutex:
    564	mutex_unlock(&usx2y->pcm_mutex);
    565	return err;
    566}
    567
    568static const struct snd_pcm_hardware snd_usx2y_4c = {
    569	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
    570				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
    571				 SNDRV_PCM_INFO_MMAP_VALID),
    572	.formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
    573	.rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
    574	.rate_min =                44100,
    575	.rate_max =                48000,
    576	.channels_min =            2,
    577	.channels_max =            4,
    578	.buffer_bytes_max =	(2*128*1024),
    579	.period_bytes_min =	64,
    580	.period_bytes_max =	(128*1024),
    581	.periods_min =		2,
    582	.periods_max =		1024,
    583	.fifo_size =              0
    584};
    585
    586static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream)
    587{
    588	struct snd_usx2y_substream	*subs =
    589		((struct snd_usx2y_substream **)
    590		 snd_pcm_substream_chip(substream))[substream->stream];
    591	struct snd_pcm_runtime	*runtime = substream->runtime;
    592
    593	if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
    594		return -EBUSY;
    595
    596	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    597		runtime->hw = snd_usx2y_2c;
    598	else
    599		runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c);
    600	runtime->private_data = subs;
    601	subs->pcm_substream = substream;
    602	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
    603	return 0;
    604}
    605
    606static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream)
    607{
    608	struct snd_pcm_runtime *runtime = substream->runtime;
    609	struct snd_usx2y_substream *subs = runtime->private_data;
    610
    611	subs->pcm_substream = NULL;
    612	return 0;
    613}
    614
    615static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = {
    616	.open =		snd_usx2y_usbpcm_open,
    617	.close =	snd_usx2y_usbpcm_close,
    618	.hw_params =	snd_usx2y_pcm_hw_params,
    619	.hw_free =	snd_usx2y_usbpcm_hw_free,
    620	.prepare =	snd_usx2y_usbpcm_prepare,
    621	.trigger =	snd_usx2y_pcm_trigger,
    622	.pointer =	snd_usx2y_pcm_pointer,
    623};
    624
    625static int usx2y_pcms_busy_check(struct snd_card *card)
    626{
    627	struct usx2ydev	*dev = usx2y(card);
    628	struct snd_usx2y_substream *subs;
    629	int i;
    630
    631	for (i = 0; i < dev->pcm_devs * 2; i++) {
    632		subs = dev->subs[i];
    633		if (subs && subs->pcm_substream &&
    634		    SUBSTREAM_BUSY(subs->pcm_substream))
    635			return -EBUSY;
    636	}
    637	return 0;
    638}
    639
    640static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
    641{
    642	struct snd_card *card = hw->card;
    643	int err;
    644
    645	mutex_lock(&usx2y(card)->pcm_mutex);
    646	err = usx2y_pcms_busy_check(card);
    647	if (!err)
    648		usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
    649	mutex_unlock(&usx2y(card)->pcm_mutex);
    650	return err;
    651}
    652
    653static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
    654{
    655	struct snd_card *card = hw->card;
    656	int err;
    657
    658	mutex_lock(&usx2y(card)->pcm_mutex);
    659	err = usx2y_pcms_busy_check(card);
    660	if (!err)
    661		usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
    662	mutex_unlock(&usx2y(card)->pcm_mutex);
    663	return err;
    664}
    665
    666static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
    667{
    668}
    669
    670static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
    671{
    672}
    673
    674static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
    675{
    676	unsigned long offset;
    677	void *vaddr;
    678
    679	offset = vmf->pgoff << PAGE_SHIFT;
    680	vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
    681	vmf->page = virt_to_page(vaddr);
    682	get_page(vmf->page);
    683	return 0;
    684}
    685
    686static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = {
    687	.open = snd_usx2y_hwdep_pcm_vm_open,
    688	.close = snd_usx2y_hwdep_pcm_vm_close,
    689	.fault = snd_usx2y_hwdep_pcm_vm_fault,
    690};
    691
    692static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
    693{
    694	unsigned long	size = (unsigned long)(area->vm_end - area->vm_start);
    695	struct usx2ydev	*usx2y = hw->private_data;
    696
    697	if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT))
    698		return -EBUSY;
    699
    700	/* if userspace tries to mmap beyond end of our buffer, fail */
    701	if (size > USX2Y_HWDEP_PCM_PAGES) {
    702		snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
    703		return -EINVAL;
    704	}
    705
    706	if (!usx2y->hwdep_pcm_shm)
    707		return -ENODEV;
    708
    709	area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops;
    710	area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
    711	area->vm_private_data = hw->private_data;
    712	return 0;
    713}
    714
    715static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
    716{
    717	struct usx2ydev *usx2y = hwdep->private_data;
    718
    719	if (usx2y->hwdep_pcm_shm)
    720		free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
    721}
    722
    723int usx2y_hwdep_pcm_new(struct snd_card *card)
    724{
    725	int err;
    726	struct snd_hwdep *hw;
    727	struct snd_pcm *pcm;
    728	struct usb_device *dev = usx2y(card)->dev;
    729
    730	if (nr_of_packs() != 1)
    731		return 0;
    732
    733	err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw);
    734	if (err < 0)
    735		return err;
    736
    737	hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
    738	hw->private_data = usx2y(card);
    739	hw->private_free = snd_usx2y_hwdep_pcm_private_free;
    740	hw->ops.open = snd_usx2y_hwdep_pcm_open;
    741	hw->ops.release = snd_usx2y_hwdep_pcm_release;
    742	hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap;
    743	hw->exclusive = 1;
    744	sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
    745
    746	err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
    747	if (err < 0)
    748		return err;
    749
    750	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops);
    751	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops);
    752
    753	pcm->private_data = usx2y(card)->subs;
    754	pcm->info_flags = 0;
    755
    756	sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
    757	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
    758				   SNDRV_DMA_TYPE_CONTINUOUS,
    759				   NULL,
    760				   64*1024, 128*1024);
    761	snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
    762				   SNDRV_DMA_TYPE_CONTINUOUS,
    763				   NULL,
    764				   64*1024, 128*1024);
    765
    766	return 0;
    767}
    768
    769#else
    770
    771int usx2y_hwdep_pcm_new(struct snd_card *card)
    772{
    773	return 0;
    774}
    775
    776#endif