/* * I/O messages lifetime * --------------------- * * Allocation: * Messages are initially allocated in the ops->hw_params() after the size and * number of periods have been successfully negotiated. * * Freeing: * Messages can be safely freed after the queue has been successfully flushed * (RELEASE command in the ops->sync_stop()) and the ops->hw_free() has been * called. * * When the substream stops, the ops->sync_stop() waits until the device has * completed all pending messages. This wait can be interrupted either by a * signal or due to a timeout. In this case, the device can still access * messages even after calling ops->hw_free(). It can also issue an interrupt, * and the interrupt handler will also try to access message structures. * * Therefore, freeing of already allocated messages occurs: * * - in ops->hw_params(), if this operator was called several times in a row, * or if ops->hw_free() failed to free messages previously; * * - in ops->hw_free(), if the queue has been successfully flushed; * * - in dev->release().
*/
/* Map for converting ALSA format to VirtIO format. */ struct virtsnd_a2v_format {
snd_pcm_format_t alsa_bit; unsignedint vio_bit;
};
/* * If the substream has already been used, then the I/O queue may be in * an invalid state. Just in case, we do a check and try to return the * queue to its original state, if necessary.
*/ return virtsnd_pcm_sync_stop(substream);
}
/** * virtsnd_pcm_close() - Close the PCM substream. * @substream: Kernel ALSA substream. * * Context: Process context. * Return: 0.
*/ staticint virtsnd_pcm_close(struct snd_pcm_substream *substream)
{ return 0;
}
/** * virtsnd_pcm_dev_set_params() - Set the parameters of the PCM substream on * the device side. * @vss: VirtIO PCM substream. * @buffer_bytes: Size of the hardware buffer. * @period_bytes: Size of the hardware period. * @channels: Selected number of channels. * @format: Selected sample format (SNDRV_PCM_FORMAT_XXX). * @rate: Selected frame rate. * * Context: Any context that permits to sleep. * Return: 0 on success, -errno on failure.
*/ staticint virtsnd_pcm_dev_set_params(struct virtio_pcm_substream *vss, unsignedint buffer_bytes, unsignedint period_bytes, unsignedint channels,
snd_pcm_format_t format, unsignedint rate)
{ struct virtio_snd_msg *msg; struct virtio_snd_pcm_set_params *request; unsignedint i; int vformat = -1; int vrate = -1;
for (i = 0; i < ARRAY_SIZE(g_a2v_format_map); ++i) if (g_a2v_format_map[i].alsa_bit == format) {
vformat = g_a2v_format_map[i].vio_bit;
break;
}
for (i = 0; i < ARRAY_SIZE(g_a2v_rate_map); ++i) if (g_a2v_rate_map[i].rate == rate) {
vrate = g_a2v_rate_map[i].vio_bit;
break;
}
if (vformat == -1 || vrate == -1) return -EINVAL;
msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_SET_PARAMS,
GFP_KERNEL); if (!msg) return -ENOMEM;
/* * Free previously allocated messages if ops->hw_params() is called * several times in a row, or if ops->hw_free() failed to free messages.
*/
virtsnd_pcm_msg_free(vss);
/** * virtsnd_pcm_sync_stop() - Synchronous PCM substream stop. * @substream: Kernel ALSA substream. * * The function can be called both from the upper level or from the driver * itself. * * Context: Process context. Takes and releases the VirtIO substream spinlock. * Return: 0 on success, -errno on failure.
*/ staticint virtsnd_pcm_sync_stop(struct snd_pcm_substream *substream)
{ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); struct virtio_snd *snd = vss->snd; struct virtio_snd_msg *msg; unsignedint js = msecs_to_jiffies(virtsnd_msg_timeout_ms); int rc;
cancel_work_sync(&vss->elapsed_period);
if (!vss->stopped) return 0;
msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_RELEASE,
GFP_KERNEL); if (!msg) return -ENOMEM;
rc = virtsnd_ctl_msg_send_sync(snd, msg); if (rc) return rc;
/* * The spec states that upon receipt of the RELEASE command "the device * MUST complete all pending I/O messages for the specified stream ID". * Thus, we consider the absence of I/O messages in the queue as an * indication that the substream has been released.
*/
rc = wait_event_interruptible_timeout(vss->msg_empty,
!virtsnd_pcm_msg_pending_num(vss),
js); if (rc <= 0) {
dev_warn(&snd->vdev->dev, "SID %u: failed to flush I/O queue\n",
vss->sid);
return !rc ? -ETIMEDOUT : rc;
}
vss->stopped = false;
return 0;
}
/** * virtsnd_pcm_pb_pointer() - Get the current hardware position for the PCM * substream for playback. * @substream: Kernel ALSA substream. * * Context: Any context. * Return: Hardware position in frames inside [0 ... buffer_size) range.
*/ static snd_pcm_uframes_t
virtsnd_pcm_pb_pointer(struct snd_pcm_substream *substream)
{ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
/** * virtsnd_pcm_cp_pointer() - Get the current hardware position for the PCM * substream for capture. * @substream: Kernel ALSA substream. * * Context: Any context. * Return: Hardware position in frames inside [0 ... buffer_size) range.
*/ static snd_pcm_uframes_t
virtsnd_pcm_cp_pointer(struct snd_pcm_substream *substream)
{ struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream);
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.