// SPDX-License-Identifier: GPL-2.0-or-later /* * US-X2Y AUDIO * Copyright (c) 2002-2004 by Karsten Wiese * * based on * * (Tentative) USB Audio Driver for ALSA * * Main and PCM part * * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> * * Many codes borrowed from audio.c by * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch)
*/
for (i = 0; i < nr_of_packs(); i++) {
cp = (unsignedchar *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
dev_err(&usx2y->dev->dev, "%s: active frame status %i. Most probably some hardware problem.\n",
__func__,
urb->iso_frame_desc[i].status); return urb->iso_frame_desc[i].status;
}
len = urb->iso_frame_desc[i].actual_length / usx2y->stride; if (!len) {
dev_dbg(&usx2y->dev->dev, "%s: 0 == len ERROR!\n", __func__); continue;
}
subs->hwptr_done = hwptr_done;
subs->transfer_done += lens; /* update the pointer, call callback if necessary */ if (subs->transfer_done >= runtime->period_size) {
subs->transfer_done -= runtime->period_size;
snd_pcm_period_elapsed(subs->pcm_substream);
} return 0;
}
/* * prepare urb for playback data pipe * * we copy the data directly from the pcm buffer. * the current position to be copied is held in hwptr field. * since a urb can handle only a single linear buffer, if the total * transferred area overflows the buffer boundary, we cannot send * it directly from the buffer. thus the data is once copied to * a temporary buffer and urb points to that.
*/ staticint usx2y_urb_play_prepare(struct snd_usx2y_substream *subs, struct urb *cap_urb, struct urb *urb)
{ struct usx2ydev *usx2y = subs->usx2y; struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; int count, counts, pack, len;
count = 0; for (pack = 0; pack < nr_of_packs(); pack++) { /* calculate the size of a packet */
counts = cap_urb->iso_frame_desc[pack].actual_length / usx2y->stride;
count += counts; if (counts < 43 || counts > 50) {
dev_err(&usx2y->dev->dev, "%s: should not be here with counts=%i\n",
__func__, counts); return -EPIPE;
} /* set up descriptor */
urb->iso_frame_desc[pack].offset = pack ?
urb->iso_frame_desc[pack - 1].offset +
urb->iso_frame_desc[pack - 1].length :
0;
urb->iso_frame_desc[pack].length = cap_urb->iso_frame_desc[pack].actual_length;
} if (atomic_read(&subs->state) >= STATE_PRERUNNING) { if (subs->hwptr + count > runtime->buffer_size) { /* err, the transferred area goes over buffer boundary. * copy the data to the temp buffer.
*/
len = runtime->buffer_size - subs->hwptr;
urb->transfer_buffer = subs->tmpbuf;
memcpy(subs->tmpbuf, runtime->dma_area +
subs->hwptr * usx2y->stride, len * usx2y->stride);
memcpy(subs->tmpbuf + len * usx2y->stride,
runtime->dma_area, (count - len) * usx2y->stride);
subs->hwptr += count;
subs->hwptr -= runtime->buffer_size;
} else { /* set the buffer pointer */
urb->transfer_buffer = runtime->dma_area + subs->hwptr * usx2y->stride;
subs->hwptr += count; if (subs->hwptr >= runtime->buffer_size)
subs->hwptr -= runtime->buffer_size;
}
} else {
urb->transfer_buffer = subs->tmpbuf;
}
urb->transfer_buffer_length = count * usx2y->stride; return 0;
}
/* * process after playback data complete * * update the current position and call callback if a period is processed.
*/ staticvoid usx2y_urb_play_retire(struct snd_usx2y_substream *subs, struct urb *urb)
{ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; int len = urb->actual_length / subs->usx2y->stride;
staticint usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, int frame)
{ int err;
if (!urb) return -ENODEV;
urb->start_frame = frame + NRURBS * nr_of_packs(); // let hcd do rollover sanity checks
urb->hcpriv = NULL;
urb->dev = subs->usx2y->dev; /* we need to set this at each time */
err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) {
dev_err(&urb->dev->dev, "%s: usb_submit_urb() returned %i\n",
__func__, err); return err;
} return 0;
}
staticint usx2y_usbframe_complete(struct snd_usx2y_substream *capsubs, struct snd_usx2y_substream *playbacksubs, int frame)
{ int err, state; struct urb *urb = playbacksubs->completed_urb;
state = atomic_read(&playbacksubs->state); if (urb) { if (state == STATE_RUNNING)
usx2y_urb_play_retire(playbacksubs, urb); elseif (state >= STATE_PRERUNNING)
atomic_inc(&playbacksubs->state);
} else { switch (state) { case STATE_STARTING1:
urb = playbacksubs->urb[0];
atomic_inc(&playbacksubs->state); break; case STATE_STARTING2:
urb = playbacksubs->urb[1];
atomic_inc(&playbacksubs->state); break;
}
} if (urb) {
err = usx2y_urb_play_prepare(playbacksubs, capsubs->completed_urb, urb); if (err) return err;
err = usx2y_urb_submit(playbacksubs, urb, frame); if (err) return err;
}
playbacksubs->completed_urb = NULL;
state = atomic_read(&capsubs->state); if (state >= STATE_PREPARED) { if (state == STATE_RUNNING) {
err = usx2y_urb_capt_retire(capsubs); if (err) return err;
} elseif (state >= STATE_PRERUNNING) {
atomic_inc(&capsubs->state);
}
err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame); if (err) return err;
}
capsubs->completed_urb = NULL; return 0;
}
for (s = 0; s < 4; s++) {
subs = usx2y->subs[s]; if (subs) { for (u = 0; u < NRURBS; u++) {
urb = subs->urb[u]; if (urb)
urb->complete = complete;
}
}
}
}
staticvoid usx2y_urb_release(struct urb **urb, int free_tb)
{ if (*urb) {
usb_kill_urb(*urb); if (free_tb)
kfree((*urb)->transfer_buffer);
usb_free_urb(*urb);
*urb = NULL;
}
}
/* * release a substreams urbs
*/ staticvoid usx2y_urbs_release(struct snd_usx2y_substream *subs)
{ int i;
dev_dbg(&subs->usx2y->dev->dev, "%s %i\n", __func__, subs->endpoint); for (i = 0; i < NRURBS; i++)
usx2y_urb_release(subs->urb + i,
subs != subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]);
kfree(subs->tmpbuf);
subs->tmpbuf = NULL;
}
/* * initialize a substream's urbs
*/ staticint usx2y_urbs_allocate(struct snd_usx2y_substream *subs)
{ int i; unsignedint pipe; int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; struct usb_device *dev = subs->usx2y->dev; struct urb **purb;
/* * allocate a buffer, setup samplerate * * so far we use a physically linear buffer although packetize transfer * doesn't need a continuous area. * if sg buffer is supported on the later version of alsa, we'll follow * that.
*/ struct s_c2 { char c1, c2;
};
mutex_lock(&usx2y(card)->pcm_mutex);
dev_dbg(&dev->dev->dev, "%s(%p, %p)\n", __func__, substream, hw_params); /* all pcm substreams off one usx2y have to operate at the same * rate & format
*/ for (i = 0; i < dev->pcm_devs * 2; i++) {
subs = dev->subs[i]; if (!subs) continue;
test_substream = subs->pcm_substream; if (!test_substream || test_substream == substream ||
!test_substream->runtime) continue; if ((test_substream->runtime->format &&
test_substream->runtime->format != format) ||
(test_substream->runtime->rate &&
test_substream->runtime->rate != rate)) {
err = -EINVAL; goto error;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.