static u32 pcm_buffer_ms = 160;
module_param(pcm_buffer_ms, uint, 0644);
MODULE_PARM_DESC(pcm_buffer_ms, "PCM substream buffer time in milliseconds");
static u32 pcm_periods_min = 2;
module_param(pcm_periods_min, uint, 0644);
MODULE_PARM_DESC(pcm_periods_min, "Minimum number of PCM periods");
static u32 pcm_periods_max = 16;
module_param(pcm_periods_max, uint, 0644);
MODULE_PARM_DESC(pcm_periods_max, "Maximum number of PCM periods");
static u32 pcm_period_ms_min = 10;
module_param(pcm_period_ms_min, uint, 0644);
MODULE_PARM_DESC(pcm_period_ms_min, "Minimum PCM period time in milliseconds");
static u32 pcm_period_ms_max = 80;
module_param(pcm_period_ms_max, uint, 0644);
MODULE_PARM_DESC(pcm_period_ms_max, "Maximum PCM period time in milliseconds");
for (i = 0; i < ARRAY_SIZE(g_v2a_format_map); ++i) if (values & (1ULL << i)) {
snd_pcm_format_t alsa_fmt = g_v2a_format_map[i]; int bytes = snd_pcm_format_physical_width(alsa_fmt) / 8;
if (!sample_min || sample_min > bytes)
sample_min = bytes;
if (!vss->hw.formats) {
dev_err(&vdev->dev, "SID %u: no supported PCM sample formats found\n",
vss->sid); return -EINVAL;
}
values = le64_to_cpu(info->rates);
vss->hw.rates = 0;
for (i = 0; i < ARRAY_SIZE(g_v2a_rate_map); ++i) if (values & (1ULL << i)) { if (!vss->hw.rate_min ||
vss->hw.rate_min > g_v2a_rate_map[i].rate)
vss->hw.rate_min = g_v2a_rate_map[i].rate;
if (vss->hw.rate_max < g_v2a_rate_map[i].rate)
vss->hw.rate_max = g_v2a_rate_map[i].rate;
vss->hw.rates |= g_v2a_rate_map[i].alsa_bit;
}
if (!vss->hw.rates) {
dev_err(&vdev->dev, "SID %u: no supported PCM frame rates found\n",
vss->sid); return -EINVAL;
}
/* * We must ensure that there is enough space in the buffer to store * pcm_buffer_ms ms for the combination (Cmax, Smax, Rmax), where: * Cmax = maximum supported number of channels, * Smax = maximum supported sample size in bytes, * Rmax = maximum supported frame rate.
*/
vss->hw.buffer_bytes_max =
PAGE_ALIGN(sample_max * vss->hw.channels_max * pcm_buffer_ms *
(vss->hw.rate_max / MSEC_PER_SEC));
/* * We must ensure that the minimum period size is enough to store * pcm_period_ms_min ms for the combination (Cmin, Smin, Rmin), where: * Cmin = minimum supported number of channels, * Smin = minimum supported sample size in bytes, * Rmin = minimum supported frame rate.
*/
vss->hw.period_bytes_min =
sample_min * vss->hw.channels_min * pcm_period_ms_min *
(vss->hw.rate_min / MSEC_PER_SEC);
/* * We must ensure that the maximum period size is enough to store * pcm_period_ms_max ms for the combination (Cmax, Smax, Rmax).
*/
vss->hw.period_bytes_max =
sample_max * vss->hw.channels_max * pcm_period_ms_max *
(vss->hw.rate_max / MSEC_PER_SEC);
return 0;
}
/** * virtsnd_pcm_find() - Find the PCM device for the specified node ID. * @snd: VirtIO sound device. * @nid: Function node ID. * * Context: Any context. * Return: a pointer to the PCM device or ERR_PTR(-ENOENT).
*/ struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid)
{ struct virtio_pcm *vpcm;
list_for_each_entry(vpcm, &snd->pcm_list, list) if (vpcm->nid == nid) return vpcm;
return ERR_PTR(-ENOENT);
}
/** * virtsnd_pcm_find_or_create() - Find or create the PCM device for the * specified node ID. * @snd: VirtIO sound device. * @nid: Function node ID. * * Context: Any context that permits to sleep. * Return: a pointer to the PCM device or ERR_PTR(-errno).
*/ struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid)
{ struct virtio_device *vdev = snd->vdev; struct virtio_pcm *vpcm;
vpcm = virtsnd_pcm_find(snd, nid); if (!IS_ERR(vpcm)) return vpcm;
vpcm = devm_kzalloc(&vdev->dev, sizeof(*vpcm), GFP_KERNEL); if (!vpcm) return ERR_PTR(-ENOMEM);
/** * virtsnd_pcm_validate() - Validate if the device can be started. * @vdev: VirtIO parent device. * * Context: Any context. * Return: 0 on success, -EINVAL on failure.
*/ int virtsnd_pcm_validate(struct virtio_device *vdev)
{ if (pcm_periods_min < 2 || pcm_periods_min > pcm_periods_max) {
dev_err(&vdev->dev, "invalid range [%u %u] of the number of PCM periods\n",
pcm_periods_min, pcm_periods_max); return -EINVAL;
}
if (!pcm_period_ms_min || pcm_period_ms_min > pcm_period_ms_max) {
dev_err(&vdev->dev, "invalid range [%u %u] of the size of the PCM period\n",
pcm_period_ms_min, pcm_period_ms_max); return -EINVAL;
}
if (pcm_buffer_ms < pcm_periods_min * pcm_period_ms_min) {
dev_err(&vdev->dev, "pcm_buffer_ms(=%u) value cannot be < %u ms\n",
pcm_buffer_ms, pcm_periods_min * pcm_period_ms_min); return -EINVAL;
}
if (pcm_period_ms_max > pcm_buffer_ms / 2) {
dev_err(&vdev->dev, "pcm_period_ms_max(=%u) value cannot be > %u ms\n",
pcm_period_ms_max, pcm_buffer_ms / 2); return -EINVAL;
}
return 0;
}
/** * virtsnd_pcm_period_elapsed() - Kernel work function to handle the elapsed * period state. * @work: Elapsed period work. * * The main purpose of this function is to call snd_pcm_period_elapsed() in * a process context, not in an interrupt context. This is necessary because PCM * devices operate in non-atomic mode. * * Context: Process context.
*/ staticvoid virtsnd_pcm_period_elapsed(struct work_struct *work)
{ struct virtio_pcm_substream *vss =
container_of(work, struct virtio_pcm_substream, elapsed_period);
snd_pcm_period_elapsed(vss->substream);
}
/** * virtsnd_pcm_parse_cfg() - Parse the stream configuration. * @snd: VirtIO sound device. * * This function is called during initial device initialization. * * Context: Any context that permits to sleep. * Return: 0 on success, -errno on failure.
*/ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd)
{ struct virtio_device *vdev = snd->vdev; struct virtio_snd_pcm_info *info;
u32 i; int rc;
virtio_cread_le(vdev, struct virtio_snd_config, streams,
&snd->nsubstreams); if (!snd->nsubstreams) return 0;
snd->substreams = devm_kcalloc(&vdev->dev, snd->nsubstreams, sizeof(*snd->substreams), GFP_KERNEL); if (!snd->substreams) return -ENOMEM;
/* * Initialize critical substream fields early in case we hit an * error path and end up trying to clean up uninitialized structures * elsewhere.
*/ for (i = 0; i < snd->nsubstreams; ++i) { struct virtio_pcm_substream *vss = &snd->substreams[i];
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.