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

virtio_chmap.c (5959B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * virtio-snd: Virtio sound device
      4 * Copyright (C) 2021 OpenSynergy GmbH
      5 */
      6#include <linux/virtio_config.h>
      7
      8#include "virtio_card.h"
      9
     10/* VirtIO->ALSA channel position map */
     11static const u8 g_v2a_position_map[] = {
     12	[VIRTIO_SND_CHMAP_NONE] = SNDRV_CHMAP_UNKNOWN,
     13	[VIRTIO_SND_CHMAP_NA] = SNDRV_CHMAP_NA,
     14	[VIRTIO_SND_CHMAP_MONO] = SNDRV_CHMAP_MONO,
     15	[VIRTIO_SND_CHMAP_FL] = SNDRV_CHMAP_FL,
     16	[VIRTIO_SND_CHMAP_FR] = SNDRV_CHMAP_FR,
     17	[VIRTIO_SND_CHMAP_RL] = SNDRV_CHMAP_RL,
     18	[VIRTIO_SND_CHMAP_RR] = SNDRV_CHMAP_RR,
     19	[VIRTIO_SND_CHMAP_FC] = SNDRV_CHMAP_FC,
     20	[VIRTIO_SND_CHMAP_LFE] = SNDRV_CHMAP_LFE,
     21	[VIRTIO_SND_CHMAP_SL] = SNDRV_CHMAP_SL,
     22	[VIRTIO_SND_CHMAP_SR] = SNDRV_CHMAP_SR,
     23	[VIRTIO_SND_CHMAP_RC] = SNDRV_CHMAP_RC,
     24	[VIRTIO_SND_CHMAP_FLC] = SNDRV_CHMAP_FLC,
     25	[VIRTIO_SND_CHMAP_FRC] = SNDRV_CHMAP_FRC,
     26	[VIRTIO_SND_CHMAP_RLC] = SNDRV_CHMAP_RLC,
     27	[VIRTIO_SND_CHMAP_RRC] = SNDRV_CHMAP_RRC,
     28	[VIRTIO_SND_CHMAP_FLW] = SNDRV_CHMAP_FLW,
     29	[VIRTIO_SND_CHMAP_FRW] = SNDRV_CHMAP_FRW,
     30	[VIRTIO_SND_CHMAP_FLH] = SNDRV_CHMAP_FLH,
     31	[VIRTIO_SND_CHMAP_FCH] = SNDRV_CHMAP_FCH,
     32	[VIRTIO_SND_CHMAP_FRH] = SNDRV_CHMAP_FRH,
     33	[VIRTIO_SND_CHMAP_TC] = SNDRV_CHMAP_TC,
     34	[VIRTIO_SND_CHMAP_TFL] = SNDRV_CHMAP_TFL,
     35	[VIRTIO_SND_CHMAP_TFR] = SNDRV_CHMAP_TFR,
     36	[VIRTIO_SND_CHMAP_TFC] = SNDRV_CHMAP_TFC,
     37	[VIRTIO_SND_CHMAP_TRL] = SNDRV_CHMAP_TRL,
     38	[VIRTIO_SND_CHMAP_TRR] = SNDRV_CHMAP_TRR,
     39	[VIRTIO_SND_CHMAP_TRC] = SNDRV_CHMAP_TRC,
     40	[VIRTIO_SND_CHMAP_TFLC] = SNDRV_CHMAP_TFLC,
     41	[VIRTIO_SND_CHMAP_TFRC] = SNDRV_CHMAP_TFRC,
     42	[VIRTIO_SND_CHMAP_TSL] = SNDRV_CHMAP_TSL,
     43	[VIRTIO_SND_CHMAP_TSR] = SNDRV_CHMAP_TSR,
     44	[VIRTIO_SND_CHMAP_LLFE] = SNDRV_CHMAP_LLFE,
     45	[VIRTIO_SND_CHMAP_RLFE] = SNDRV_CHMAP_RLFE,
     46	[VIRTIO_SND_CHMAP_BC] = SNDRV_CHMAP_BC,
     47	[VIRTIO_SND_CHMAP_BLC] = SNDRV_CHMAP_BLC,
     48	[VIRTIO_SND_CHMAP_BRC] = SNDRV_CHMAP_BRC
     49};
     50
     51/**
     52 * virtsnd_chmap_parse_cfg() - Parse the channel map configuration.
     53 * @snd: VirtIO sound device.
     54 *
     55 * This function is called during initial device initialization.
     56 *
     57 * Context: Any context that permits to sleep.
     58 * Return: 0 on success, -errno on failure.
     59 */
     60int virtsnd_chmap_parse_cfg(struct virtio_snd *snd)
     61{
     62	struct virtio_device *vdev = snd->vdev;
     63	u32 i;
     64	int rc;
     65
     66	virtio_cread_le(vdev, struct virtio_snd_config, chmaps, &snd->nchmaps);
     67	if (!snd->nchmaps)
     68		return 0;
     69
     70	snd->chmaps = devm_kcalloc(&vdev->dev, snd->nchmaps,
     71				   sizeof(*snd->chmaps), GFP_KERNEL);
     72	if (!snd->chmaps)
     73		return -ENOMEM;
     74
     75	rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_CHMAP_INFO, 0,
     76				    snd->nchmaps, sizeof(*snd->chmaps),
     77				    snd->chmaps);
     78	if (rc)
     79		return rc;
     80
     81	/* Count the number of channel maps per each PCM device/stream. */
     82	for (i = 0; i < snd->nchmaps; ++i) {
     83		struct virtio_snd_chmap_info *info = &snd->chmaps[i];
     84		u32 nid = le32_to_cpu(info->hdr.hda_fn_nid);
     85		struct virtio_pcm *vpcm;
     86		struct virtio_pcm_stream *vs;
     87
     88		vpcm = virtsnd_pcm_find_or_create(snd, nid);
     89		if (IS_ERR(vpcm))
     90			return PTR_ERR(vpcm);
     91
     92		switch (info->direction) {
     93		case VIRTIO_SND_D_OUTPUT:
     94			vs = &vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
     95			break;
     96		case VIRTIO_SND_D_INPUT:
     97			vs = &vpcm->streams[SNDRV_PCM_STREAM_CAPTURE];
     98			break;
     99		default:
    100			dev_err(&vdev->dev,
    101				"chmap #%u: unknown direction (%u)\n", i,
    102				info->direction);
    103			return -EINVAL;
    104		}
    105
    106		vs->nchmaps++;
    107	}
    108
    109	return 0;
    110}
    111
    112/**
    113 * virtsnd_chmap_add_ctls() - Create an ALSA control for channel maps.
    114 * @pcm: ALSA PCM device.
    115 * @direction: PCM stream direction (SNDRV_PCM_STREAM_XXX).
    116 * @vs: VirtIO PCM stream.
    117 *
    118 * Context: Any context.
    119 * Return: 0 on success, -errno on failure.
    120 */
    121static int virtsnd_chmap_add_ctls(struct snd_pcm *pcm, int direction,
    122				  struct virtio_pcm_stream *vs)
    123{
    124	u32 i;
    125	int max_channels = 0;
    126
    127	for (i = 0; i < vs->nchmaps; i++)
    128		if (max_channels < vs->chmaps[i].channels)
    129			max_channels = vs->chmaps[i].channels;
    130
    131	return snd_pcm_add_chmap_ctls(pcm, direction, vs->chmaps, max_channels,
    132				      0, NULL);
    133}
    134
    135/**
    136 * virtsnd_chmap_build_devs() - Build ALSA controls for channel maps.
    137 * @snd: VirtIO sound device.
    138 *
    139 * Context: Any context.
    140 * Return: 0 on success, -errno on failure.
    141 */
    142int virtsnd_chmap_build_devs(struct virtio_snd *snd)
    143{
    144	struct virtio_device *vdev = snd->vdev;
    145	struct virtio_pcm *vpcm;
    146	struct virtio_pcm_stream *vs;
    147	u32 i;
    148	int rc;
    149
    150	/* Allocate channel map elements per each PCM device/stream. */
    151	list_for_each_entry(vpcm, &snd->pcm_list, list) {
    152		for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
    153			vs = &vpcm->streams[i];
    154
    155			if (!vs->nchmaps)
    156				continue;
    157
    158			vs->chmaps = devm_kcalloc(&vdev->dev, vs->nchmaps + 1,
    159						  sizeof(*vs->chmaps),
    160						  GFP_KERNEL);
    161			if (!vs->chmaps)
    162				return -ENOMEM;
    163
    164			vs->nchmaps = 0;
    165		}
    166	}
    167
    168	/* Initialize channel maps per each PCM device/stream. */
    169	for (i = 0; i < snd->nchmaps; ++i) {
    170		struct virtio_snd_chmap_info *info = &snd->chmaps[i];
    171		unsigned int channels = info->channels;
    172		unsigned int ch;
    173		struct snd_pcm_chmap_elem *chmap;
    174
    175		vpcm = virtsnd_pcm_find(snd, le32_to_cpu(info->hdr.hda_fn_nid));
    176		if (IS_ERR(vpcm))
    177			return PTR_ERR(vpcm);
    178
    179		if (info->direction == VIRTIO_SND_D_OUTPUT)
    180			vs = &vpcm->streams[SNDRV_PCM_STREAM_PLAYBACK];
    181		else
    182			vs = &vpcm->streams[SNDRV_PCM_STREAM_CAPTURE];
    183
    184		chmap = &vs->chmaps[vs->nchmaps++];
    185
    186		if (channels > ARRAY_SIZE(chmap->map))
    187			channels = ARRAY_SIZE(chmap->map);
    188
    189		chmap->channels = channels;
    190
    191		for (ch = 0; ch < channels; ++ch) {
    192			u8 position = info->positions[ch];
    193
    194			if (position >= ARRAY_SIZE(g_v2a_position_map))
    195				return -EINVAL;
    196
    197			chmap->map[ch] = g_v2a_position_map[position];
    198		}
    199	}
    200
    201	/* Create an ALSA control per each PCM device/stream. */
    202	list_for_each_entry(vpcm, &snd->pcm_list, list) {
    203		if (!vpcm->pcm)
    204			continue;
    205
    206		for (i = 0; i < ARRAY_SIZE(vpcm->streams); ++i) {
    207			vs = &vpcm->streams[i];
    208
    209			if (!vs->nchmaps)
    210				continue;
    211
    212			rc = virtsnd_chmap_add_ctls(vpcm->pcm, i, vs);
    213			if (rc)
    214				return rc;
    215		}
    216	}
    217
    218	return 0;
    219}