/* return the estimated delay based on USB frame counters */ static snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
{ unsignedint current_frame_number; unsignedint frame_diff; int est_delay; int queued;
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
queued = bytes_to_frames(runtime, subs->inflight_bytes); if (!queued) return 0;
} elseif (!subs->running) { return 0;
}
current_frame_number = usb_get_current_frame_number(subs->dev); /* * HCD implementations use different widths, use lower 8 bits. * The delay will be managed up to 256ms, which is more than * enough
*/
frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
/* Approximation based on number of samples per USB frame (ms),
some truncation for 44.1 but the estimate is good enough */
est_delay = frame_diff * runtime->rate / 1000;
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
est_delay = queued - est_delay; if (est_delay < 0)
est_delay = 0;
}
return est_delay;
}
/* * return the current pcm pointer. just based on the hwptr_done value.
*/ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; unsignedint hwptr_done;
/* * find a matching audio format
*/ staticconststruct audioformat *
find_format(struct list_head *fmt_list_head, snd_pcm_format_t format, unsignedint rate, unsignedint channels, bool strict_match, struct snd_usb_substream *subs)
{ conststruct audioformat *fp; conststruct audioformat *found = NULL; int cur_attr = 0, attr;
list_for_each_entry(fp, fmt_list_head, list) { if (strict_match) { if (!(fp->formats & pcm_format_to_bits(format))) continue; if (fp->channels != channels) continue;
} if (rate < fp->rate_min || rate > fp->rate_max) continue; if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { unsignedint i; for (i = 0; i < fp->nr_rates; i++) if (fp->rate_table[i] == rate) break; if (i >= fp->nr_rates) continue;
}
attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; if (!found) {
found = fp;
cur_attr = attr; continue;
} /* avoid async out and adaptive in if the other method * supports the same format. * this is a workaround for the case like * M-audio audiophile USB.
*/ if (subs && attr != cur_attr) { if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
(attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
subs->direction == SNDRV_PCM_STREAM_CAPTURE)) continue; if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
(cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
found = fp;
cur_attr = attr; continue;
}
} /* find the format with the largest max. packet size */ if (fp->maxpacksize > found->maxpacksize) {
found = fp;
cur_attr = attr;
}
} return found;
}
/* * In case of illegal SYNC_NONE for OUT endpoint, we keep going to see * if we don't find a sync endpoint, as on M-Audio Transit. In case of * error fall back to SYNC mode and don't create sync endpoint
*/
/* check sync-pipe endpoint */ /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking
the audio fields in the endpoint descriptors */ if ((sync_attr & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 1)->bSynchAddress != 0)) {
dev_err(&dev->dev, "%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
fmt->iface, fmt->altsetting,
get_endpoint(alts, 1)->bmAttributes,
get_endpoint(alts, 1)->bLength,
get_endpoint(alts, 1)->bSynchAddress); if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) return 0; return -EINVAL;
}
ep = get_endpoint(alts, 1)->bEndpointAddress; if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 0)->bSynchAddress != 0 &&
((is_playback && ep != (unsignedint)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
(!is_playback && ep != (unsignedint)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
dev_err(&dev->dev, "%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
fmt->iface, fmt->altsetting,
is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) return 0; return -EINVAL;
}
/* * hw_params callback * * allocate a buffer and set the given audio format. * * 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.
*/ staticint snd_usb_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
{ struct snd_usb_substream *subs = substream->runtime->private_data;
return snd_usb_hw_params(subs, hw_params);
}
int snd_usb_hw_free(struct snd_usb_substream *subs)
{ struct snd_usb_audio *chip = subs->stream->chip;
snd_media_stop_pipeline(subs);
mutex_lock(&chip->mutex);
subs->cur_audiofmt = NULL;
mutex_unlock(&chip->mutex); if (!snd_usb_lock_shutdown(chip)) { if (stop_endpoints(subs, false))
sync_pending_stops(subs);
close_endpoints(chip, subs);
snd_usb_unlock_shutdown(chip);
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_usb_hw_free);
/* * hw_free callback * * reset the audio format and release the buffer
*/ staticint snd_usb_pcm_hw_free(struct snd_pcm_substream *substream)
{ struct snd_usb_substream *subs = substream->runtime->private_data;
/* check whether early start is needed for playback stream */ staticint lowlatency_playback_available(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{ struct snd_usb_audio *chip = subs->stream->chip;
if (subs->direction == SNDRV_PCM_STREAM_CAPTURE) returnfalse; /* disabled via module option? */ if (!chip->lowlatency) returnfalse; if (in_free_wheeling_mode(runtime)) returnfalse; /* implicit feedback mode has own operation mode */ if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) returnfalse; returntrue;
}
/* * prepare callback * * only a few subtle things...
*/ staticint snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
{ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; struct snd_usb_audio *chip = subs->stream->chip; int retry = 0; int ret;
ret = snd_usb_lock_shutdown(chip); if (ret < 0) return ret; if (snd_BUG_ON(!subs->data_endpoint)) {
ret = -EIO; goto unlock;
}
ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); if (ret < 0) goto unlock;
again: if (subs->sync_endpoint) {
ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint); if (ret < 0) goto unlock;
}
ret = snd_usb_endpoint_prepare(chip, subs->data_endpoint); if (ret < 0) goto unlock; elseif (ret > 0)
snd_usb_set_format_quirk(subs, subs->cur_audiofmt);
ret = 0;
/* get the specified endpoint object that is being used by other streams * (i.e. the parameter is locked)
*/ staticconststruct snd_usb_endpoint *
get_endpoint_in_use(struct snd_usb_audio *chip, int endpoint, conststruct snd_usb_endpoint *ref_ep)
{ conststruct snd_usb_endpoint *ep;
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
hwc_debug("hw_rule_periods: (%u,%u)\n", it->min, it->max);
rmin = UINT_MAX;
rmax = 0;
list_for_each_entry(fp, &subs->fmt_list, list) { if (!hw_check_valid_format(subs, params, fp)) continue;
ep = get_endpoint_in_use(chip, fp->endpoint,
subs->data_endpoint); if (ep) {
hwc_debug("periods limit %d for ep#%x\n",
ep->cur_buffer_periods, fp->endpoint);
rmin = min(rmin, ep->cur_buffer_periods);
rmax = max(rmax, ep->cur_buffer_periods); continue;
}
if (fp->implicit_fb) {
ep = get_endpoint_in_use(chip, fp->sync_ep,
subs->sync_endpoint); if (ep) {
hwc_debug("periods limit %d for sync_ep#%x\n",
ep->cur_buffer_periods, fp->sync_ep);
rmin = min(rmin, ep->cur_buffer_periods);
rmax = max(rmax, ep->cur_buffer_periods); continue;
}
}
}
if (!rmax) return 0; /* no limit by implicit fb */ return apply_hw_params_minmax(it, rmin, rmax);
}
/* * set up the runtime hardware information.
*/
staticint setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
{ conststruct audioformat *fp; unsignedint pt, ptmin; int param_period_time_if_needed = -1; int err;
runtime->hw.formats = subs->formats;
runtime->hw.rate_min = 0x7fffffff;
runtime->hw.rate_max = 0;
runtime->hw.channels_min = 256;
runtime->hw.channels_max = 0;
runtime->hw.rates = 0;
ptmin = UINT_MAX; /* check min/max rates and channels */
list_for_each_entry(fp, &subs->fmt_list, list) {
runtime->hw.rates |= fp->rates; if (runtime->hw.rate_min > fp->rate_min)
runtime->hw.rate_min = fp->rate_min; if (runtime->hw.rate_max < fp->rate_max)
runtime->hw.rate_max = fp->rate_max; if (runtime->hw.channels_min > fp->channels)
runtime->hw.channels_min = fp->channels; if (runtime->hw.channels_max < fp->channels)
runtime->hw.channels_max = fp->channels; if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) { /* FIXME: there might be more than one audio formats... */
runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
fp->frame_size;
}
pt = 125 * (1 << fp->datainterval);
ptmin = min(ptmin, pt);
}
param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; if (subs->speed == USB_SPEED_FULL) /* full speed devices have fixed data packet interval */
ptmin = 1000; if (ptmin == 1000) /* if period time doesn't go below 1 ms, no rules needed */
param_period_time_if_needed = -1;
/* set max period and buffer sizes for 1 and 2 seconds, respectively */
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
0, 1000000); if (err < 0) return err;
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_TIME,
0, 2000000); if (err < 0) return err;
runtime->hw = snd_usb_hardware; /* need an explicit sync to catch applptr update in low-latency mode */ if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
as->chip->lowlatency)
runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR;
runtime->private_data = subs;
subs->pcm_substream = substream; /* runtime PM is also done there */
ret = setup_hw_info(runtime, subs); if (ret < 0) goto err_open;
ret = snd_usb_autoresume(subs->stream->chip); if (ret < 0) goto err_open;
ret = snd_media_stream_init(subs, as->pcm, direction); if (ret < 0) goto err_resume;
staticint snd_usb_pcm_close(struct snd_pcm_substream *substream)
{ int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; struct snd_usb_audio *chip = subs->stream->chip; int ret;
snd_media_stop_pipeline(subs);
if (!snd_usb_lock_shutdown(subs->stream->chip)) {
ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1);
snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) return ret;
}
/* Since a URB can handle only a single linear buffer, we must use double * buffering when the data to be transferred overflows the buffer boundary. * To avoid inconsistencies when updating hwptr_done, we use double buffering * for all URBs.
*/ staticvoid retire_capture_urb(struct snd_usb_substream *subs, struct urb *urb)
{ struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; unsignedint stride, frames, bytes, oldptr; int i, period_elapsed = 0; unsignedlong flags; unsignedchar *cp; int current_frame_number;
/* read frame number here, update pointer in critical section */
current_frame_number = usb_get_current_frame_number(subs->dev);
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
cp = (unsignedchar *)urb->transfer_buffer + urb->iso_frame_desc[i].offset + subs->pkt_offset_adj; if (urb->iso_frame_desc[i].status)
dev_dbg_ratelimited(&subs->dev->dev, "frame %d active: %d\n", i,
urb->iso_frame_desc[i].status);
bytes = urb->iso_frame_desc[i].actual_length; if (subs->stream_offset_adj > 0) { unsignedint adj = min(subs->stream_offset_adj, bytes);
cp += adj;
bytes -= adj;
subs->stream_offset_adj -= adj;
}
frames = bytes / stride; if (!subs->txfr_quirk)
bytes = frames * stride; if (bytes % (runtime->sample_bits >> 3) != 0) { int oldbytes = bytes;
bytes = frames * stride;
dev_warn_ratelimited(&subs->dev->dev, "Corrected urb data len. %d->%d\n",
oldbytes, bytes);
} /* update the current pointer */
spin_lock_irqsave(&subs->lock, flags);
oldptr = subs->hwptr_done;
subs->hwptr_done += bytes; if (subs->hwptr_done >= subs->buffer_bytes)
subs->hwptr_done -= subs->buffer_bytes;
frames = (bytes + (oldptr % stride)) / stride;
subs->transfer_done += frames; if (subs->transfer_done >= runtime->period_size) {
subs->transfer_done -= runtime->period_size;
period_elapsed = 1;
}
/* * The DSP DOP format defines a way to transport DSD samples over * normal PCM data endpoints. It requires stuffing of marker bytes * (0x05 and 0xfa, alternating per sample frame), and then expects * 2 additional bytes of actual payload. The whole frame is stored * LSB. * * Hence, for a stereo transport, the buffer layout looks like this, * where L refers to left channel samples and R to right. * * L1 L2 0x05 R1 R2 0x05 L3 L4 0xfa R3 R4 0xfa * L5 L6 0x05 R5 R6 0x05 L7 L8 0xfa R7 R8 0xfa * ..... *
*/
staticunsignedint copy_to_urb_quirk(struct snd_usb_substream *subs, struct urb *urb, int stride, unsignedint bytes)
{
__le32 packet_length; int i;
/* Put __le32 length descriptor at start of each packet. */ for (i = 0; i < urb->number_of_packets; i++) { unsignedint length = urb->iso_frame_desc[i].length; unsignedint offset = urb->iso_frame_desc[i].offset;
if (subs->trigger_tstamp_pending_update) { /* this is the first actual URB submitted, * update trigger timestamp to reflect actual start time
*/
snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
subs->trigger_tstamp_pending_update = false;
}
unlock:
spin_unlock_irqrestore(&subs->lock, flags); if (err < 0) return err;
urb->transfer_buffer_length = bytes; if (period_elapsed) { if (in_stream_lock)
snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream); else
snd_pcm_period_elapsed(subs->pcm_substream);
} return 0;
}
/* * process after playback data complete * - decrease the delay count again
*/ staticvoid retire_playback_urb(struct snd_usb_substream *subs, struct urb *urb)
{ unsignedlong flags; struct snd_urb_ctx *ctx = urb->context; bool period_elapsed = false;
spin_lock_irqsave(&subs->lock, flags); if (ctx->queued) { if (subs->inflight_bytes >= ctx->queued)
subs->inflight_bytes -= ctx->queued; else
subs->inflight_bytes = 0;
}
subs->last_frame_number = usb_get_current_frame_number(subs->dev); if (subs->running) {
period_elapsed = subs->period_elapsed_pending;
subs->period_elapsed_pending = 0;
}
spin_unlock_irqrestore(&subs->lock, flags); if (period_elapsed)
snd_pcm_period_elapsed(subs->pcm_substream);
}
/* PCM ack callback for the playback stream; * this plays a role only when the stream is running in low-latency mode.
*/ staticint snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
{ struct snd_usb_substream *subs = substream->runtime->private_data; struct snd_usb_endpoint *ep;
if (!subs->lowlatency_playback || !subs->running) return 0;
ep = subs->data_endpoint; if (!ep) return 0; /* When no more in-flight URBs available, try to process the pending * outputs here
*/ if (!ep->active_mask) return snd_usb_queue_pending_output_urbs(ep, true); return 0;
}
staticint snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{ struct snd_usb_substream *subs = substream->runtime->private_data; int err;
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.