18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Xen para-virtual sound device 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 EPAM Systems Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <xen/xenbus.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <xen/interface/io/sndif.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "xen_snd_front.h" 168c2ecf20Sopenharmony_ci#include "xen_snd_front_cfg.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Maximum number of supported streams. */ 198c2ecf20Sopenharmony_ci#define VSND_MAX_STREAM 8 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct cfg_hw_sample_rate { 228c2ecf20Sopenharmony_ci const char *name; 238c2ecf20Sopenharmony_ci unsigned int mask; 248c2ecf20Sopenharmony_ci unsigned int value; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = { 288c2ecf20Sopenharmony_ci { .name = "5512", .mask = SNDRV_PCM_RATE_5512, .value = 5512 }, 298c2ecf20Sopenharmony_ci { .name = "8000", .mask = SNDRV_PCM_RATE_8000, .value = 8000 }, 308c2ecf20Sopenharmony_ci { .name = "11025", .mask = SNDRV_PCM_RATE_11025, .value = 11025 }, 318c2ecf20Sopenharmony_ci { .name = "16000", .mask = SNDRV_PCM_RATE_16000, .value = 16000 }, 328c2ecf20Sopenharmony_ci { .name = "22050", .mask = SNDRV_PCM_RATE_22050, .value = 22050 }, 338c2ecf20Sopenharmony_ci { .name = "32000", .mask = SNDRV_PCM_RATE_32000, .value = 32000 }, 348c2ecf20Sopenharmony_ci { .name = "44100", .mask = SNDRV_PCM_RATE_44100, .value = 44100 }, 358c2ecf20Sopenharmony_ci { .name = "48000", .mask = SNDRV_PCM_RATE_48000, .value = 48000 }, 368c2ecf20Sopenharmony_ci { .name = "64000", .mask = SNDRV_PCM_RATE_64000, .value = 64000 }, 378c2ecf20Sopenharmony_ci { .name = "96000", .mask = SNDRV_PCM_RATE_96000, .value = 96000 }, 388c2ecf20Sopenharmony_ci { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 }, 398c2ecf20Sopenharmony_ci { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 }, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct cfg_hw_sample_format { 438c2ecf20Sopenharmony_ci const char *name; 448c2ecf20Sopenharmony_ci u64 mask; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = { 488c2ecf20Sopenharmony_ci { 498c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U8_STR, 508c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U8 518c2ecf20Sopenharmony_ci }, 528c2ecf20Sopenharmony_ci { 538c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S8_STR, 548c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S8 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci { 578c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U16_LE_STR, 588c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U16_LE 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U16_BE_STR, 628c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U16_BE 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S16_LE_STR, 668c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S16_LE 678c2ecf20Sopenharmony_ci }, 688c2ecf20Sopenharmony_ci { 698c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S16_BE_STR, 708c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S16_BE 718c2ecf20Sopenharmony_ci }, 728c2ecf20Sopenharmony_ci { 738c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U24_LE_STR, 748c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U24_LE 758c2ecf20Sopenharmony_ci }, 768c2ecf20Sopenharmony_ci { 778c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U24_BE_STR, 788c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U24_BE 798c2ecf20Sopenharmony_ci }, 808c2ecf20Sopenharmony_ci { 818c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S24_LE_STR, 828c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S24_LE 838c2ecf20Sopenharmony_ci }, 848c2ecf20Sopenharmony_ci { 858c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S24_BE_STR, 868c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S24_BE 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U32_LE_STR, 908c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U32_LE 918c2ecf20Sopenharmony_ci }, 928c2ecf20Sopenharmony_ci { 938c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_U32_BE_STR, 948c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_U32_BE 958c2ecf20Sopenharmony_ci }, 968c2ecf20Sopenharmony_ci { 978c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S32_LE_STR, 988c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S32_LE 998c2ecf20Sopenharmony_ci }, 1008c2ecf20Sopenharmony_ci { 1018c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_S32_BE_STR, 1028c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_S32_BE 1038c2ecf20Sopenharmony_ci }, 1048c2ecf20Sopenharmony_ci { 1058c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_A_LAW_STR, 1068c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_A_LAW 1078c2ecf20Sopenharmony_ci }, 1088c2ecf20Sopenharmony_ci { 1098c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_MU_LAW_STR, 1108c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_MU_LAW 1118c2ecf20Sopenharmony_ci }, 1128c2ecf20Sopenharmony_ci { 1138c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_F32_LE_STR, 1148c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_FLOAT_LE 1158c2ecf20Sopenharmony_ci }, 1168c2ecf20Sopenharmony_ci { 1178c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_F32_BE_STR, 1188c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_FLOAT_BE 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci { 1218c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_F64_LE_STR, 1228c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE 1238c2ecf20Sopenharmony_ci }, 1248c2ecf20Sopenharmony_ci { 1258c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_F64_BE_STR, 1268c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE 1278c2ecf20Sopenharmony_ci }, 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR, 1308c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE 1318c2ecf20Sopenharmony_ci }, 1328c2ecf20Sopenharmony_ci { 1338c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR, 1348c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE 1358c2ecf20Sopenharmony_ci }, 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR, 1388c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM 1398c2ecf20Sopenharmony_ci }, 1408c2ecf20Sopenharmony_ci { 1418c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_MPEG_STR, 1428c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_MPEG 1438c2ecf20Sopenharmony_ci }, 1448c2ecf20Sopenharmony_ci { 1458c2ecf20Sopenharmony_ci .name = XENSND_PCM_FORMAT_GSM_STR, 1468c2ecf20Sopenharmony_ci .mask = SNDRV_PCM_FMTBIT_GSM 1478c2ecf20Sopenharmony_ci }, 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void cfg_hw_rates(char *list, unsigned int len, 1518c2ecf20Sopenharmony_ci const char *path, struct snd_pcm_hardware *pcm_hw) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci char *cur_rate; 1548c2ecf20Sopenharmony_ci unsigned int cur_mask; 1558c2ecf20Sopenharmony_ci unsigned int cur_value; 1568c2ecf20Sopenharmony_ci unsigned int rates; 1578c2ecf20Sopenharmony_ci unsigned int rate_min; 1588c2ecf20Sopenharmony_ci unsigned int rate_max; 1598c2ecf20Sopenharmony_ci int i; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci rates = 0; 1628c2ecf20Sopenharmony_ci rate_min = -1; 1638c2ecf20Sopenharmony_ci rate_max = 0; 1648c2ecf20Sopenharmony_ci while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) { 1658c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++) 1668c2ecf20Sopenharmony_ci if (!strncasecmp(cur_rate, 1678c2ecf20Sopenharmony_ci CFG_HW_SUPPORTED_RATES[i].name, 1688c2ecf20Sopenharmony_ci XENSND_SAMPLE_RATE_MAX_LEN)) { 1698c2ecf20Sopenharmony_ci cur_mask = CFG_HW_SUPPORTED_RATES[i].mask; 1708c2ecf20Sopenharmony_ci cur_value = CFG_HW_SUPPORTED_RATES[i].value; 1718c2ecf20Sopenharmony_ci rates |= cur_mask; 1728c2ecf20Sopenharmony_ci if (rate_min > cur_value) 1738c2ecf20Sopenharmony_ci rate_min = cur_value; 1748c2ecf20Sopenharmony_ci if (rate_max < cur_value) 1758c2ecf20Sopenharmony_ci rate_max = cur_value; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (rates) { 1808c2ecf20Sopenharmony_ci pcm_hw->rates = rates; 1818c2ecf20Sopenharmony_ci pcm_hw->rate_min = rate_min; 1828c2ecf20Sopenharmony_ci pcm_hw->rate_max = rate_max; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void cfg_formats(char *list, unsigned int len, 1878c2ecf20Sopenharmony_ci const char *path, struct snd_pcm_hardware *pcm_hw) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci u64 formats; 1908c2ecf20Sopenharmony_ci char *cur_format; 1918c2ecf20Sopenharmony_ci int i; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci formats = 0; 1948c2ecf20Sopenharmony_ci while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) { 1958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++) 1968c2ecf20Sopenharmony_ci if (!strncasecmp(cur_format, 1978c2ecf20Sopenharmony_ci CFG_HW_SUPPORTED_FORMATS[i].name, 1988c2ecf20Sopenharmony_ci XENSND_SAMPLE_FORMAT_MAX_LEN)) 1998c2ecf20Sopenharmony_ci formats |= CFG_HW_SUPPORTED_FORMATS[i].mask; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (formats) 2038c2ecf20Sopenharmony_ci pcm_hw->formats = formats; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define MAX_BUFFER_SIZE (64 * 1024) 2078c2ecf20Sopenharmony_ci#define MIN_PERIOD_SIZE 64 2088c2ecf20Sopenharmony_ci#define MAX_PERIOD_SIZE MAX_BUFFER_SIZE 2098c2ecf20Sopenharmony_ci#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | \ 2108c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE) 2118c2ecf20Sopenharmony_ci#define USE_RATE (SNDRV_PCM_RATE_CONTINUOUS | \ 2128c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_8000_48000) 2138c2ecf20Sopenharmony_ci#define USE_RATE_MIN 5512 2148c2ecf20Sopenharmony_ci#define USE_RATE_MAX 48000 2158c2ecf20Sopenharmony_ci#define USE_CHANNELS_MIN 1 2168c2ecf20Sopenharmony_ci#define USE_CHANNELS_MAX 2 2178c2ecf20Sopenharmony_ci#define USE_PERIODS_MIN 2 2188c2ecf20Sopenharmony_ci#define USE_PERIODS_MAX (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE) 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = { 2218c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 2228c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 2238c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | 2248c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 2258c2ecf20Sopenharmony_ci .formats = USE_FORMATS, 2268c2ecf20Sopenharmony_ci .rates = USE_RATE, 2278c2ecf20Sopenharmony_ci .rate_min = USE_RATE_MIN, 2288c2ecf20Sopenharmony_ci .rate_max = USE_RATE_MAX, 2298c2ecf20Sopenharmony_ci .channels_min = USE_CHANNELS_MIN, 2308c2ecf20Sopenharmony_ci .channels_max = USE_CHANNELS_MAX, 2318c2ecf20Sopenharmony_ci .buffer_bytes_max = MAX_BUFFER_SIZE, 2328c2ecf20Sopenharmony_ci .period_bytes_min = MIN_PERIOD_SIZE, 2338c2ecf20Sopenharmony_ci .period_bytes_max = MAX_PERIOD_SIZE, 2348c2ecf20Sopenharmony_ci .periods_min = USE_PERIODS_MIN, 2358c2ecf20Sopenharmony_ci .periods_max = USE_PERIODS_MAX, 2368c2ecf20Sopenharmony_ci .fifo_size = 0, 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void cfg_read_pcm_hw(const char *path, 2408c2ecf20Sopenharmony_ci struct snd_pcm_hardware *parent_pcm_hw, 2418c2ecf20Sopenharmony_ci struct snd_pcm_hardware *pcm_hw) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci char *list; 2448c2ecf20Sopenharmony_ci int val; 2458c2ecf20Sopenharmony_ci size_t buf_sz; 2468c2ecf20Sopenharmony_ci unsigned int len; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* Inherit parent's PCM HW and read overrides from XenStore. */ 2498c2ecf20Sopenharmony_ci if (parent_pcm_hw) 2508c2ecf20Sopenharmony_ci *pcm_hw = *parent_pcm_hw; 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci *pcm_hw = SND_DRV_PCM_HW_DEFAULT; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0); 2558c2ecf20Sopenharmony_ci if (val) 2568c2ecf20Sopenharmony_ci pcm_hw->channels_min = val; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0); 2598c2ecf20Sopenharmony_ci if (val) 2608c2ecf20Sopenharmony_ci pcm_hw->channels_max = val; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len); 2638c2ecf20Sopenharmony_ci if (!IS_ERR(list)) { 2648c2ecf20Sopenharmony_ci cfg_hw_rates(list, len, path, pcm_hw); 2658c2ecf20Sopenharmony_ci kfree(list); 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len); 2698c2ecf20Sopenharmony_ci if (!IS_ERR(list)) { 2708c2ecf20Sopenharmony_ci cfg_formats(list, len, path, pcm_hw); 2718c2ecf20Sopenharmony_ci kfree(list); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0); 2758c2ecf20Sopenharmony_ci if (buf_sz) 2768c2ecf20Sopenharmony_ci pcm_hw->buffer_bytes_max = buf_sz; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Update configuration to match new values. */ 2798c2ecf20Sopenharmony_ci if (pcm_hw->channels_min > pcm_hw->channels_max) 2808c2ecf20Sopenharmony_ci pcm_hw->channels_min = pcm_hw->channels_max; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (pcm_hw->rate_min > pcm_hw->rate_max) 2838c2ecf20Sopenharmony_ci pcm_hw->rate_min = pcm_hw->rate_max; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci pcm_hw->periods_max = pcm_hw->period_bytes_max / 2888c2ecf20Sopenharmony_ci pcm_hw->period_bytes_min; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int cfg_get_stream_type(const char *path, int index, 2928c2ecf20Sopenharmony_ci int *num_pb, int *num_cap) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci char *str = NULL; 2958c2ecf20Sopenharmony_ci char *stream_path; 2968c2ecf20Sopenharmony_ci int ret; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci *num_pb = 0; 2998c2ecf20Sopenharmony_ci *num_cap = 0; 3008c2ecf20Sopenharmony_ci stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index); 3018c2ecf20Sopenharmony_ci if (!stream_path) { 3028c2ecf20Sopenharmony_ci ret = -ENOMEM; 3038c2ecf20Sopenharmony_ci goto fail; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); 3078c2ecf20Sopenharmony_ci if (IS_ERR(str)) { 3088c2ecf20Sopenharmony_ci ret = PTR_ERR(str); 3098c2ecf20Sopenharmony_ci str = NULL; 3108c2ecf20Sopenharmony_ci goto fail; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 3148c2ecf20Sopenharmony_ci sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 3158c2ecf20Sopenharmony_ci (*num_pb)++; 3168c2ecf20Sopenharmony_ci } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 3178c2ecf20Sopenharmony_ci sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 3188c2ecf20Sopenharmony_ci (*num_cap)++; 3198c2ecf20Sopenharmony_ci } else { 3208c2ecf20Sopenharmony_ci ret = -EINVAL; 3218c2ecf20Sopenharmony_ci goto fail; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci ret = 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cifail: 3268c2ecf20Sopenharmony_ci kfree(stream_path); 3278c2ecf20Sopenharmony_ci kfree(str); 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int cfg_stream(struct xen_snd_front_info *front_info, 3328c2ecf20Sopenharmony_ci struct xen_front_cfg_pcm_instance *pcm_instance, 3338c2ecf20Sopenharmony_ci const char *path, int index, int *cur_pb, int *cur_cap, 3348c2ecf20Sopenharmony_ci int *stream_cnt) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci char *str = NULL; 3378c2ecf20Sopenharmony_ci char *stream_path; 3388c2ecf20Sopenharmony_ci struct xen_front_cfg_stream *stream; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci stream_path = devm_kasprintf(&front_info->xb_dev->dev, 3428c2ecf20Sopenharmony_ci GFP_KERNEL, "%s/%d", path, index); 3438c2ecf20Sopenharmony_ci if (!stream_path) { 3448c2ecf20Sopenharmony_ci ret = -ENOMEM; 3458c2ecf20Sopenharmony_ci goto fail; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL); 3498c2ecf20Sopenharmony_ci if (IS_ERR(str)) { 3508c2ecf20Sopenharmony_ci ret = PTR_ERR(str); 3518c2ecf20Sopenharmony_ci str = NULL; 3528c2ecf20Sopenharmony_ci goto fail; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK, 3568c2ecf20Sopenharmony_ci sizeof(XENSND_STREAM_TYPE_PLAYBACK))) { 3578c2ecf20Sopenharmony_ci stream = &pcm_instance->streams_pb[(*cur_pb)++]; 3588c2ecf20Sopenharmony_ci } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE, 3598c2ecf20Sopenharmony_ci sizeof(XENSND_STREAM_TYPE_CAPTURE))) { 3608c2ecf20Sopenharmony_ci stream = &pcm_instance->streams_cap[(*cur_cap)++]; 3618c2ecf20Sopenharmony_ci } else { 3628c2ecf20Sopenharmony_ci ret = -EINVAL; 3638c2ecf20Sopenharmony_ci goto fail; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Get next stream index. */ 3678c2ecf20Sopenharmony_ci stream->index = (*stream_cnt)++; 3688c2ecf20Sopenharmony_ci stream->xenstore_path = stream_path; 3698c2ecf20Sopenharmony_ci /* 3708c2ecf20Sopenharmony_ci * Check XenStore if PCM HW configuration exists for this stream 3718c2ecf20Sopenharmony_ci * and update if so, e.g. we inherit all values from device's PCM HW, 3728c2ecf20Sopenharmony_ci * but can still override some of the values for the stream. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci cfg_read_pcm_hw(stream->xenstore_path, 3758c2ecf20Sopenharmony_ci &pcm_instance->pcm_hw, &stream->pcm_hw); 3768c2ecf20Sopenharmony_ci ret = 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cifail: 3798c2ecf20Sopenharmony_ci kfree(str); 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int cfg_device(struct xen_snd_front_info *front_info, 3848c2ecf20Sopenharmony_ci struct xen_front_cfg_pcm_instance *pcm_instance, 3858c2ecf20Sopenharmony_ci struct snd_pcm_hardware *parent_pcm_hw, 3868c2ecf20Sopenharmony_ci const char *path, int node_index, int *stream_cnt) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci char *str; 3898c2ecf20Sopenharmony_ci char *device_path; 3908c2ecf20Sopenharmony_ci int ret, i, num_streams; 3918c2ecf20Sopenharmony_ci int num_pb, num_cap; 3928c2ecf20Sopenharmony_ci int cur_pb, cur_cap; 3938c2ecf20Sopenharmony_ci char node[3]; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index); 3968c2ecf20Sopenharmony_ci if (!device_path) 3978c2ecf20Sopenharmony_ci return -ENOMEM; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL); 4008c2ecf20Sopenharmony_ci if (!IS_ERR(str)) { 4018c2ecf20Sopenharmony_ci strlcpy(pcm_instance->name, str, sizeof(pcm_instance->name)); 4028c2ecf20Sopenharmony_ci kfree(str); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci pcm_instance->device_id = node_index; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * Check XenStore if PCM HW configuration exists for this device 4098c2ecf20Sopenharmony_ci * and update if so, e.g. we inherit all values from card's PCM HW, 4108c2ecf20Sopenharmony_ci * but can still override some of the values for the device. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Find out how many streams were configured in Xen store. */ 4158c2ecf20Sopenharmony_ci num_streams = 0; 4168c2ecf20Sopenharmony_ci do { 4178c2ecf20Sopenharmony_ci snprintf(node, sizeof(node), "%d", num_streams); 4188c2ecf20Sopenharmony_ci if (!xenbus_exists(XBT_NIL, device_path, node)) 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci num_streams++; 4228c2ecf20Sopenharmony_ci } while (num_streams < VSND_MAX_STREAM); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pcm_instance->num_streams_pb = 0; 4258c2ecf20Sopenharmony_ci pcm_instance->num_streams_cap = 0; 4268c2ecf20Sopenharmony_ci /* Get number of playback and capture streams. */ 4278c2ecf20Sopenharmony_ci for (i = 0; i < num_streams; i++) { 4288c2ecf20Sopenharmony_ci ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap); 4298c2ecf20Sopenharmony_ci if (ret < 0) 4308c2ecf20Sopenharmony_ci goto fail; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci pcm_instance->num_streams_pb += num_pb; 4338c2ecf20Sopenharmony_ci pcm_instance->num_streams_cap += num_cap; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (pcm_instance->num_streams_pb) { 4378c2ecf20Sopenharmony_ci pcm_instance->streams_pb = 4388c2ecf20Sopenharmony_ci devm_kcalloc(&front_info->xb_dev->dev, 4398c2ecf20Sopenharmony_ci pcm_instance->num_streams_pb, 4408c2ecf20Sopenharmony_ci sizeof(struct xen_front_cfg_stream), 4418c2ecf20Sopenharmony_ci GFP_KERNEL); 4428c2ecf20Sopenharmony_ci if (!pcm_instance->streams_pb) { 4438c2ecf20Sopenharmony_ci ret = -ENOMEM; 4448c2ecf20Sopenharmony_ci goto fail; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (pcm_instance->num_streams_cap) { 4498c2ecf20Sopenharmony_ci pcm_instance->streams_cap = 4508c2ecf20Sopenharmony_ci devm_kcalloc(&front_info->xb_dev->dev, 4518c2ecf20Sopenharmony_ci pcm_instance->num_streams_cap, 4528c2ecf20Sopenharmony_ci sizeof(struct xen_front_cfg_stream), 4538c2ecf20Sopenharmony_ci GFP_KERNEL); 4548c2ecf20Sopenharmony_ci if (!pcm_instance->streams_cap) { 4558c2ecf20Sopenharmony_ci ret = -ENOMEM; 4568c2ecf20Sopenharmony_ci goto fail; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci cur_pb = 0; 4618c2ecf20Sopenharmony_ci cur_cap = 0; 4628c2ecf20Sopenharmony_ci for (i = 0; i < num_streams; i++) { 4638c2ecf20Sopenharmony_ci ret = cfg_stream(front_info, pcm_instance, device_path, i, 4648c2ecf20Sopenharmony_ci &cur_pb, &cur_cap, stream_cnt); 4658c2ecf20Sopenharmony_ci if (ret < 0) 4668c2ecf20Sopenharmony_ci goto fail; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci ret = 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cifail: 4718c2ecf20Sopenharmony_ci kfree(device_path); 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ciint xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, 4768c2ecf20Sopenharmony_ci int *stream_cnt) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct xenbus_device *xb_dev = front_info->xb_dev; 4798c2ecf20Sopenharmony_ci struct xen_front_cfg_card *cfg = &front_info->cfg; 4808c2ecf20Sopenharmony_ci int ret, num_devices, i; 4818c2ecf20Sopenharmony_ci char node[3]; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci *stream_cnt = 0; 4848c2ecf20Sopenharmony_ci num_devices = 0; 4858c2ecf20Sopenharmony_ci do { 4868c2ecf20Sopenharmony_ci snprintf(node, sizeof(node), "%d", num_devices); 4878c2ecf20Sopenharmony_ci if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci num_devices++; 4918c2ecf20Sopenharmony_ci } while (num_devices < SNDRV_PCM_DEVICES); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!num_devices) { 4948c2ecf20Sopenharmony_ci dev_warn(&xb_dev->dev, 4958c2ecf20Sopenharmony_ci "No devices configured for sound card at %s\n", 4968c2ecf20Sopenharmony_ci xb_dev->nodename); 4978c2ecf20Sopenharmony_ci return -ENODEV; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Start from default PCM HW configuration for the card. */ 5018c2ecf20Sopenharmony_ci cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci cfg->pcm_instances = 5048c2ecf20Sopenharmony_ci devm_kcalloc(&front_info->xb_dev->dev, num_devices, 5058c2ecf20Sopenharmony_ci sizeof(struct xen_front_cfg_pcm_instance), 5068c2ecf20Sopenharmony_ci GFP_KERNEL); 5078c2ecf20Sopenharmony_ci if (!cfg->pcm_instances) 5088c2ecf20Sopenharmony_ci return -ENOMEM; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci for (i = 0; i < num_devices; i++) { 5118c2ecf20Sopenharmony_ci ret = cfg_device(front_info, &cfg->pcm_instances[i], 5128c2ecf20Sopenharmony_ci &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt); 5138c2ecf20Sopenharmony_ci if (ret < 0) 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci cfg->num_pcm_instances = num_devices; 5178c2ecf20Sopenharmony_ci return 0; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 520