brintos

brintos / linux-shallow public Read only

0
0
Text · 12.7 KiB · 55ecf76 Raw
520 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2 3/*4 * Xen para-virtual sound device5 *6 * Copyright (C) 2016-2018 EPAM Systems Inc.7 *8 * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>9 */10 11#include <xen/xenbus.h>12 13#include <xen/interface/io/sndif.h>14 15#include "xen_snd_front.h"16#include "xen_snd_front_cfg.h"17 18/* Maximum number of supported streams. */19#define VSND_MAX_STREAM		820 21struct cfg_hw_sample_rate {22	const char *name;23	unsigned int mask;24	unsigned int value;25};26 27static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {28	{ .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },29	{ .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },30	{ .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },31	{ .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },32	{ .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },33	{ .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },34	{ .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },35	{ .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },36	{ .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },37	{ .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },38	{ .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },39	{ .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },40};41 42struct cfg_hw_sample_format {43	const char *name;44	u64 mask;45};46 47static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {48	{49		.name = XENSND_PCM_FORMAT_U8_STR,50		.mask = SNDRV_PCM_FMTBIT_U851	},52	{53		.name = XENSND_PCM_FORMAT_S8_STR,54		.mask = SNDRV_PCM_FMTBIT_S855	},56	{57		.name = XENSND_PCM_FORMAT_U16_LE_STR,58		.mask = SNDRV_PCM_FMTBIT_U16_LE59	},60	{61		.name = XENSND_PCM_FORMAT_U16_BE_STR,62		.mask = SNDRV_PCM_FMTBIT_U16_BE63	},64	{65		.name = XENSND_PCM_FORMAT_S16_LE_STR,66		.mask = SNDRV_PCM_FMTBIT_S16_LE67	},68	{69		.name = XENSND_PCM_FORMAT_S16_BE_STR,70		.mask = SNDRV_PCM_FMTBIT_S16_BE71	},72	{73		.name = XENSND_PCM_FORMAT_U24_LE_STR,74		.mask = SNDRV_PCM_FMTBIT_U24_LE75	},76	{77		.name = XENSND_PCM_FORMAT_U24_BE_STR,78		.mask = SNDRV_PCM_FMTBIT_U24_BE79	},80	{81		.name = XENSND_PCM_FORMAT_S24_LE_STR,82		.mask = SNDRV_PCM_FMTBIT_S24_LE83	},84	{85		.name = XENSND_PCM_FORMAT_S24_BE_STR,86		.mask = SNDRV_PCM_FMTBIT_S24_BE87	},88	{89		.name = XENSND_PCM_FORMAT_U32_LE_STR,90		.mask = SNDRV_PCM_FMTBIT_U32_LE91	},92	{93		.name = XENSND_PCM_FORMAT_U32_BE_STR,94		.mask = SNDRV_PCM_FMTBIT_U32_BE95	},96	{97		.name = XENSND_PCM_FORMAT_S32_LE_STR,98		.mask = SNDRV_PCM_FMTBIT_S32_LE99	},100	{101		.name = XENSND_PCM_FORMAT_S32_BE_STR,102		.mask = SNDRV_PCM_FMTBIT_S32_BE103	},104	{105		.name = XENSND_PCM_FORMAT_A_LAW_STR,106		.mask = SNDRV_PCM_FMTBIT_A_LAW107	},108	{109		.name = XENSND_PCM_FORMAT_MU_LAW_STR,110		.mask = SNDRV_PCM_FMTBIT_MU_LAW111	},112	{113		.name = XENSND_PCM_FORMAT_F32_LE_STR,114		.mask = SNDRV_PCM_FMTBIT_FLOAT_LE115	},116	{117		.name = XENSND_PCM_FORMAT_F32_BE_STR,118		.mask = SNDRV_PCM_FMTBIT_FLOAT_BE119	},120	{121		.name = XENSND_PCM_FORMAT_F64_LE_STR,122		.mask = SNDRV_PCM_FMTBIT_FLOAT64_LE123	},124	{125		.name = XENSND_PCM_FORMAT_F64_BE_STR,126		.mask = SNDRV_PCM_FMTBIT_FLOAT64_BE127	},128	{129		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,130		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE131	},132	{133		.name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,134		.mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE135	},136	{137		.name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,138		.mask = SNDRV_PCM_FMTBIT_IMA_ADPCM139	},140	{141		.name = XENSND_PCM_FORMAT_MPEG_STR,142		.mask = SNDRV_PCM_FMTBIT_MPEG143	},144	{145		.name = XENSND_PCM_FORMAT_GSM_STR,146		.mask = SNDRV_PCM_FMTBIT_GSM147	},148};149 150static void cfg_hw_rates(char *list, unsigned int len,151			 const char *path, struct snd_pcm_hardware *pcm_hw)152{153	char *cur_rate;154	unsigned int cur_mask;155	unsigned int cur_value;156	unsigned int rates;157	unsigned int rate_min;158	unsigned int rate_max;159	int i;160 161	rates = 0;162	rate_min = -1;163	rate_max = 0;164	while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {165		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)166			if (!strncasecmp(cur_rate,167					 CFG_HW_SUPPORTED_RATES[i].name,168					 XENSND_SAMPLE_RATE_MAX_LEN)) {169				cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;170				cur_value = CFG_HW_SUPPORTED_RATES[i].value;171				rates |= cur_mask;172				if (rate_min > cur_value)173					rate_min = cur_value;174				if (rate_max < cur_value)175					rate_max = cur_value;176			}177	}178 179	if (rates) {180		pcm_hw->rates = rates;181		pcm_hw->rate_min = rate_min;182		pcm_hw->rate_max = rate_max;183	}184}185 186static void cfg_formats(char *list, unsigned int len,187			const char *path, struct snd_pcm_hardware *pcm_hw)188{189	u64 formats;190	char *cur_format;191	int i;192 193	formats = 0;194	while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {195		for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)196			if (!strncasecmp(cur_format,197					 CFG_HW_SUPPORTED_FORMATS[i].name,198					 XENSND_SAMPLE_FORMAT_MAX_LEN))199				formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;200	}201 202	if (formats)203		pcm_hw->formats = formats;204}205 206#define MAX_BUFFER_SIZE		(64 * 1024)207#define MIN_PERIOD_SIZE		64208#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE209#define USE_FORMATS		(SNDRV_PCM_FMTBIT_U8 | \210				 SNDRV_PCM_FMTBIT_S16_LE)211#define USE_RATE		(SNDRV_PCM_RATE_CONTINUOUS | \212				 SNDRV_PCM_RATE_8000_48000)213#define USE_RATE_MIN		5512214#define USE_RATE_MAX		48000215#define USE_CHANNELS_MIN	1216#define USE_CHANNELS_MAX	2217#define USE_PERIODS_MIN		2218#define USE_PERIODS_MAX		(MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)219 220static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {221	.info = (SNDRV_PCM_INFO_MMAP |222		 SNDRV_PCM_INFO_INTERLEAVED |223		 SNDRV_PCM_INFO_RESUME |224		 SNDRV_PCM_INFO_MMAP_VALID),225	.formats = USE_FORMATS,226	.rates = USE_RATE,227	.rate_min = USE_RATE_MIN,228	.rate_max = USE_RATE_MAX,229	.channels_min = USE_CHANNELS_MIN,230	.channels_max = USE_CHANNELS_MAX,231	.buffer_bytes_max = MAX_BUFFER_SIZE,232	.period_bytes_min = MIN_PERIOD_SIZE,233	.period_bytes_max = MAX_PERIOD_SIZE,234	.periods_min = USE_PERIODS_MIN,235	.periods_max = USE_PERIODS_MAX,236	.fifo_size = 0,237};238 239static void cfg_read_pcm_hw(const char *path,240			    struct snd_pcm_hardware *parent_pcm_hw,241			    struct snd_pcm_hardware *pcm_hw)242{243	char *list;244	int val;245	size_t buf_sz;246	unsigned int len;247 248	/* Inherit parent's PCM HW and read overrides from XenStore. */249	if (parent_pcm_hw)250		*pcm_hw = *parent_pcm_hw;251	else252		*pcm_hw = SND_DRV_PCM_HW_DEFAULT;253 254	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);255	if (val)256		pcm_hw->channels_min = val;257 258	val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);259	if (val)260		pcm_hw->channels_max = val;261 262	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);263	if (!IS_ERR(list)) {264		cfg_hw_rates(list, len, path, pcm_hw);265		kfree(list);266	}267 268	list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);269	if (!IS_ERR(list)) {270		cfg_formats(list, len, path, pcm_hw);271		kfree(list);272	}273 274	buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);275	if (buf_sz)276		pcm_hw->buffer_bytes_max = buf_sz;277 278	/* Update configuration to match new values. */279	if (pcm_hw->channels_min > pcm_hw->channels_max)280		pcm_hw->channels_min = pcm_hw->channels_max;281 282	if (pcm_hw->rate_min > pcm_hw->rate_max)283		pcm_hw->rate_min = pcm_hw->rate_max;284 285	pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;286 287	pcm_hw->periods_max = pcm_hw->period_bytes_max /288		pcm_hw->period_bytes_min;289}290 291static int cfg_get_stream_type(const char *path, int index,292			       int *num_pb, int *num_cap)293{294	char *str = NULL;295	char *stream_path;296	int ret;297 298	*num_pb = 0;299	*num_cap = 0;300	stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);301	if (!stream_path) {302		ret = -ENOMEM;303		goto fail;304	}305 306	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);307	if (IS_ERR(str)) {308		ret = PTR_ERR(str);309		str = NULL;310		goto fail;311	}312 313	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,314			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {315		(*num_pb)++;316	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,317			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {318		(*num_cap)++;319	} else {320		ret = -EINVAL;321		goto fail;322	}323	ret = 0;324 325fail:326	kfree(stream_path);327	kfree(str);328	return ret;329}330 331static int cfg_stream(struct xen_snd_front_info *front_info,332		      struct xen_front_cfg_pcm_instance *pcm_instance,333		      const char *path, int index, int *cur_pb, int *cur_cap,334		      int *stream_cnt)335{336	char *str = NULL;337	char *stream_path;338	struct xen_front_cfg_stream *stream;339	int ret;340 341	stream_path = devm_kasprintf(&front_info->xb_dev->dev,342				     GFP_KERNEL, "%s/%d", path, index);343	if (!stream_path) {344		ret = -ENOMEM;345		goto fail;346	}347 348	str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);349	if (IS_ERR(str)) {350		ret = PTR_ERR(str);351		str = NULL;352		goto fail;353	}354 355	if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,356			 sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {357		stream = &pcm_instance->streams_pb[(*cur_pb)++];358	} else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,359			      sizeof(XENSND_STREAM_TYPE_CAPTURE))) {360		stream = &pcm_instance->streams_cap[(*cur_cap)++];361	} else {362		ret = -EINVAL;363		goto fail;364	}365 366	/* Get next stream index. */367	stream->index = (*stream_cnt)++;368	stream->xenstore_path = stream_path;369	/*370	 * Check XenStore if PCM HW configuration exists for this stream371	 * and update if so, e.g. we inherit all values from device's PCM HW,372	 * but can still override some of the values for the stream.373	 */374	cfg_read_pcm_hw(stream->xenstore_path,375			&pcm_instance->pcm_hw, &stream->pcm_hw);376	ret = 0;377 378fail:379	kfree(str);380	return ret;381}382 383static int cfg_device(struct xen_snd_front_info *front_info,384		      struct xen_front_cfg_pcm_instance *pcm_instance,385		      struct snd_pcm_hardware *parent_pcm_hw,386		      const char *path, int node_index, int *stream_cnt)387{388	char *str;389	char *device_path;390	int ret, i, num_streams;391	int num_pb, num_cap;392	int cur_pb, cur_cap;393	char node[3];394 395	device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);396	if (!device_path)397		return -ENOMEM;398 399	str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);400	if (!IS_ERR(str)) {401		strscpy(pcm_instance->name, str, sizeof(pcm_instance->name));402		kfree(str);403	}404 405	pcm_instance->device_id = node_index;406 407	/*408	 * Check XenStore if PCM HW configuration exists for this device409	 * and update if so, e.g. we inherit all values from card's PCM HW,410	 * but can still override some of the values for the device.411	 */412	cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);413 414	/* Find out how many streams were configured in Xen store. */415	num_streams = 0;416	do {417		snprintf(node, sizeof(node), "%d", num_streams);418		if (!xenbus_exists(XBT_NIL, device_path, node))419			break;420 421		num_streams++;422	} while (num_streams < VSND_MAX_STREAM);423 424	pcm_instance->num_streams_pb = 0;425	pcm_instance->num_streams_cap = 0;426	/* Get number of playback and capture streams. */427	for (i = 0; i < num_streams; i++) {428		ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);429		if (ret < 0)430			goto fail;431 432		pcm_instance->num_streams_pb += num_pb;433		pcm_instance->num_streams_cap += num_cap;434	}435 436	if (pcm_instance->num_streams_pb) {437		pcm_instance->streams_pb =438				devm_kcalloc(&front_info->xb_dev->dev,439					     pcm_instance->num_streams_pb,440					     sizeof(struct xen_front_cfg_stream),441					     GFP_KERNEL);442		if (!pcm_instance->streams_pb) {443			ret = -ENOMEM;444			goto fail;445		}446	}447 448	if (pcm_instance->num_streams_cap) {449		pcm_instance->streams_cap =450				devm_kcalloc(&front_info->xb_dev->dev,451					     pcm_instance->num_streams_cap,452					     sizeof(struct xen_front_cfg_stream),453					     GFP_KERNEL);454		if (!pcm_instance->streams_cap) {455			ret = -ENOMEM;456			goto fail;457		}458	}459 460	cur_pb = 0;461	cur_cap = 0;462	for (i = 0; i < num_streams; i++) {463		ret = cfg_stream(front_info, pcm_instance, device_path, i,464				 &cur_pb, &cur_cap, stream_cnt);465		if (ret < 0)466			goto fail;467	}468	ret = 0;469 470fail:471	kfree(device_path);472	return ret;473}474 475int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,476			   int *stream_cnt)477{478	struct xenbus_device *xb_dev = front_info->xb_dev;479	struct xen_front_cfg_card *cfg = &front_info->cfg;480	int ret, num_devices, i;481	char node[3];482 483	*stream_cnt = 0;484	num_devices = 0;485	do {486		scnprintf(node, sizeof(node), "%d", num_devices);487		if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))488			break;489 490		num_devices++;491	} while (num_devices < SNDRV_PCM_DEVICES);492 493	if (!num_devices) {494		dev_warn(&xb_dev->dev,495			 "No devices configured for sound card at %s\n",496			 xb_dev->nodename);497		return -ENODEV;498	}499 500	/* Start from default PCM HW configuration for the card. */501	cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);502 503	cfg->pcm_instances =504			devm_kcalloc(&front_info->xb_dev->dev, num_devices,505				     sizeof(struct xen_front_cfg_pcm_instance),506				     GFP_KERNEL);507	if (!cfg->pcm_instances)508		return -ENOMEM;509 510	for (i = 0; i < num_devices; i++) {511		ret = cfg_device(front_info, &cfg->pcm_instances[i],512				 &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);513		if (ret < 0)514			return ret;515	}516	cfg->num_pcm_instances = num_devices;517	return 0;518}519 520