Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR MIT
0002 
0003 /*
0004  * Xen para-virtual sound device
0005  *
0006  * Copyright (C) 2016-2018 EPAM Systems Inc.
0007  *
0008  * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
0009  */
0010 
0011 #include <xen/xenbus.h>
0012 
0013 #include <xen/interface/io/sndif.h>
0014 
0015 #include "xen_snd_front.h"
0016 #include "xen_snd_front_cfg.h"
0017 
0018 /* Maximum number of supported streams. */
0019 #define VSND_MAX_STREAM     8
0020 
0021 struct cfg_hw_sample_rate {
0022     const char *name;
0023     unsigned int mask;
0024     unsigned int value;
0025 };
0026 
0027 static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
0028     { .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
0029     { .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
0030     { .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
0031     { .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
0032     { .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
0033     { .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
0034     { .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
0035     { .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
0036     { .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
0037     { .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
0038     { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
0039     { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
0040 };
0041 
0042 struct cfg_hw_sample_format {
0043     const char *name;
0044     u64 mask;
0045 };
0046 
0047 static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
0048     {
0049         .name = XENSND_PCM_FORMAT_U8_STR,
0050         .mask = SNDRV_PCM_FMTBIT_U8
0051     },
0052     {
0053         .name = XENSND_PCM_FORMAT_S8_STR,
0054         .mask = SNDRV_PCM_FMTBIT_S8
0055     },
0056     {
0057         .name = XENSND_PCM_FORMAT_U16_LE_STR,
0058         .mask = SNDRV_PCM_FMTBIT_U16_LE
0059     },
0060     {
0061         .name = XENSND_PCM_FORMAT_U16_BE_STR,
0062         .mask = SNDRV_PCM_FMTBIT_U16_BE
0063     },
0064     {
0065         .name = XENSND_PCM_FORMAT_S16_LE_STR,
0066         .mask = SNDRV_PCM_FMTBIT_S16_LE
0067     },
0068     {
0069         .name = XENSND_PCM_FORMAT_S16_BE_STR,
0070         .mask = SNDRV_PCM_FMTBIT_S16_BE
0071     },
0072     {
0073         .name = XENSND_PCM_FORMAT_U24_LE_STR,
0074         .mask = SNDRV_PCM_FMTBIT_U24_LE
0075     },
0076     {
0077         .name = XENSND_PCM_FORMAT_U24_BE_STR,
0078         .mask = SNDRV_PCM_FMTBIT_U24_BE
0079     },
0080     {
0081         .name = XENSND_PCM_FORMAT_S24_LE_STR,
0082         .mask = SNDRV_PCM_FMTBIT_S24_LE
0083     },
0084     {
0085         .name = XENSND_PCM_FORMAT_S24_BE_STR,
0086         .mask = SNDRV_PCM_FMTBIT_S24_BE
0087     },
0088     {
0089         .name = XENSND_PCM_FORMAT_U32_LE_STR,
0090         .mask = SNDRV_PCM_FMTBIT_U32_LE
0091     },
0092     {
0093         .name = XENSND_PCM_FORMAT_U32_BE_STR,
0094         .mask = SNDRV_PCM_FMTBIT_U32_BE
0095     },
0096     {
0097         .name = XENSND_PCM_FORMAT_S32_LE_STR,
0098         .mask = SNDRV_PCM_FMTBIT_S32_LE
0099     },
0100     {
0101         .name = XENSND_PCM_FORMAT_S32_BE_STR,
0102         .mask = SNDRV_PCM_FMTBIT_S32_BE
0103     },
0104     {
0105         .name = XENSND_PCM_FORMAT_A_LAW_STR,
0106         .mask = SNDRV_PCM_FMTBIT_A_LAW
0107     },
0108     {
0109         .name = XENSND_PCM_FORMAT_MU_LAW_STR,
0110         .mask = SNDRV_PCM_FMTBIT_MU_LAW
0111     },
0112     {
0113         .name = XENSND_PCM_FORMAT_F32_LE_STR,
0114         .mask = SNDRV_PCM_FMTBIT_FLOAT_LE
0115     },
0116     {
0117         .name = XENSND_PCM_FORMAT_F32_BE_STR,
0118         .mask = SNDRV_PCM_FMTBIT_FLOAT_BE
0119     },
0120     {
0121         .name = XENSND_PCM_FORMAT_F64_LE_STR,
0122         .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
0123     },
0124     {
0125         .name = XENSND_PCM_FORMAT_F64_BE_STR,
0126         .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
0127     },
0128     {
0129         .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
0130         .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
0131     },
0132     {
0133         .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
0134         .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
0135     },
0136     {
0137         .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
0138         .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
0139     },
0140     {
0141         .name = XENSND_PCM_FORMAT_MPEG_STR,
0142         .mask = SNDRV_PCM_FMTBIT_MPEG
0143     },
0144     {
0145         .name = XENSND_PCM_FORMAT_GSM_STR,
0146         .mask = SNDRV_PCM_FMTBIT_GSM
0147     },
0148 };
0149 
0150 static void cfg_hw_rates(char *list, unsigned int len,
0151              const char *path, struct snd_pcm_hardware *pcm_hw)
0152 {
0153     char *cur_rate;
0154     unsigned int cur_mask;
0155     unsigned int cur_value;
0156     unsigned int rates;
0157     unsigned int rate_min;
0158     unsigned int rate_max;
0159     int i;
0160 
0161     rates = 0;
0162     rate_min = -1;
0163     rate_max = 0;
0164     while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
0165         for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
0166             if (!strncasecmp(cur_rate,
0167                      CFG_HW_SUPPORTED_RATES[i].name,
0168                      XENSND_SAMPLE_RATE_MAX_LEN)) {
0169                 cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
0170                 cur_value = CFG_HW_SUPPORTED_RATES[i].value;
0171                 rates |= cur_mask;
0172                 if (rate_min > cur_value)
0173                     rate_min = cur_value;
0174                 if (rate_max < cur_value)
0175                     rate_max = cur_value;
0176             }
0177     }
0178 
0179     if (rates) {
0180         pcm_hw->rates = rates;
0181         pcm_hw->rate_min = rate_min;
0182         pcm_hw->rate_max = rate_max;
0183     }
0184 }
0185 
0186 static void cfg_formats(char *list, unsigned int len,
0187             const char *path, struct snd_pcm_hardware *pcm_hw)
0188 {
0189     u64 formats;
0190     char *cur_format;
0191     int i;
0192 
0193     formats = 0;
0194     while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
0195         for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
0196             if (!strncasecmp(cur_format,
0197                      CFG_HW_SUPPORTED_FORMATS[i].name,
0198                      XENSND_SAMPLE_FORMAT_MAX_LEN))
0199                 formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
0200     }
0201 
0202     if (formats)
0203         pcm_hw->formats = formats;
0204 }
0205 
0206 #define MAX_BUFFER_SIZE     (64 * 1024)
0207 #define MIN_PERIOD_SIZE     64
0208 #define MAX_PERIOD_SIZE     MAX_BUFFER_SIZE
0209 #define USE_FORMATS     (SNDRV_PCM_FMTBIT_U8 | \
0210                  SNDRV_PCM_FMTBIT_S16_LE)
0211 #define USE_RATE        (SNDRV_PCM_RATE_CONTINUOUS | \
0212                  SNDRV_PCM_RATE_8000_48000)
0213 #define USE_RATE_MIN        5512
0214 #define USE_RATE_MAX        48000
0215 #define USE_CHANNELS_MIN    1
0216 #define USE_CHANNELS_MAX    2
0217 #define USE_PERIODS_MIN     2
0218 #define USE_PERIODS_MAX     (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)
0219 
0220 static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
0221     .info = (SNDRV_PCM_INFO_MMAP |
0222          SNDRV_PCM_INFO_INTERLEAVED |
0223          SNDRV_PCM_INFO_RESUME |
0224          SNDRV_PCM_INFO_MMAP_VALID),
0225     .formats = USE_FORMATS,
0226     .rates = USE_RATE,
0227     .rate_min = USE_RATE_MIN,
0228     .rate_max = USE_RATE_MAX,
0229     .channels_min = USE_CHANNELS_MIN,
0230     .channels_max = USE_CHANNELS_MAX,
0231     .buffer_bytes_max = MAX_BUFFER_SIZE,
0232     .period_bytes_min = MIN_PERIOD_SIZE,
0233     .period_bytes_max = MAX_PERIOD_SIZE,
0234     .periods_min = USE_PERIODS_MIN,
0235     .periods_max = USE_PERIODS_MAX,
0236     .fifo_size = 0,
0237 };
0238 
0239 static void cfg_read_pcm_hw(const char *path,
0240                 struct snd_pcm_hardware *parent_pcm_hw,
0241                 struct snd_pcm_hardware *pcm_hw)
0242 {
0243     char *list;
0244     int val;
0245     size_t buf_sz;
0246     unsigned int len;
0247 
0248     /* Inherit parent's PCM HW and read overrides from XenStore. */
0249     if (parent_pcm_hw)
0250         *pcm_hw = *parent_pcm_hw;
0251     else
0252         *pcm_hw = SND_DRV_PCM_HW_DEFAULT;
0253 
0254     val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
0255     if (val)
0256         pcm_hw->channels_min = val;
0257 
0258     val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
0259     if (val)
0260         pcm_hw->channels_max = val;
0261 
0262     list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
0263     if (!IS_ERR(list)) {
0264         cfg_hw_rates(list, len, path, pcm_hw);
0265         kfree(list);
0266     }
0267 
0268     list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
0269     if (!IS_ERR(list)) {
0270         cfg_formats(list, len, path, pcm_hw);
0271         kfree(list);
0272     }
0273 
0274     buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
0275     if (buf_sz)
0276         pcm_hw->buffer_bytes_max = buf_sz;
0277 
0278     /* Update configuration to match new values. */
0279     if (pcm_hw->channels_min > pcm_hw->channels_max)
0280         pcm_hw->channels_min = pcm_hw->channels_max;
0281 
0282     if (pcm_hw->rate_min > pcm_hw->rate_max)
0283         pcm_hw->rate_min = pcm_hw->rate_max;
0284 
0285     pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;
0286 
0287     pcm_hw->periods_max = pcm_hw->period_bytes_max /
0288         pcm_hw->period_bytes_min;
0289 }
0290 
0291 static int cfg_get_stream_type(const char *path, int index,
0292                    int *num_pb, int *num_cap)
0293 {
0294     char *str = NULL;
0295     char *stream_path;
0296     int ret;
0297 
0298     *num_pb = 0;
0299     *num_cap = 0;
0300     stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
0301     if (!stream_path) {
0302         ret = -ENOMEM;
0303         goto fail;
0304     }
0305 
0306     str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
0307     if (IS_ERR(str)) {
0308         ret = PTR_ERR(str);
0309         str = NULL;
0310         goto fail;
0311     }
0312 
0313     if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
0314              sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
0315         (*num_pb)++;
0316     } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
0317                   sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
0318         (*num_cap)++;
0319     } else {
0320         ret = -EINVAL;
0321         goto fail;
0322     }
0323     ret = 0;
0324 
0325 fail:
0326     kfree(stream_path);
0327     kfree(str);
0328     return ret;
0329 }
0330 
0331 static int cfg_stream(struct xen_snd_front_info *front_info,
0332               struct xen_front_cfg_pcm_instance *pcm_instance,
0333               const char *path, int index, int *cur_pb, int *cur_cap,
0334               int *stream_cnt)
0335 {
0336     char *str = NULL;
0337     char *stream_path;
0338     struct xen_front_cfg_stream *stream;
0339     int ret;
0340 
0341     stream_path = devm_kasprintf(&front_info->xb_dev->dev,
0342                      GFP_KERNEL, "%s/%d", path, index);
0343     if (!stream_path) {
0344         ret = -ENOMEM;
0345         goto fail;
0346     }
0347 
0348     str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
0349     if (IS_ERR(str)) {
0350         ret = PTR_ERR(str);
0351         str = NULL;
0352         goto fail;
0353     }
0354 
0355     if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
0356              sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
0357         stream = &pcm_instance->streams_pb[(*cur_pb)++];
0358     } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
0359                   sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
0360         stream = &pcm_instance->streams_cap[(*cur_cap)++];
0361     } else {
0362         ret = -EINVAL;
0363         goto fail;
0364     }
0365 
0366     /* Get next stream index. */
0367     stream->index = (*stream_cnt)++;
0368     stream->xenstore_path = stream_path;
0369     /*
0370      * Check XenStore if PCM HW configuration exists for this stream
0371      * and update if so, e.g. we inherit all values from device's PCM HW,
0372      * but can still override some of the values for the stream.
0373      */
0374     cfg_read_pcm_hw(stream->xenstore_path,
0375             &pcm_instance->pcm_hw, &stream->pcm_hw);
0376     ret = 0;
0377 
0378 fail:
0379     kfree(str);
0380     return ret;
0381 }
0382 
0383 static int cfg_device(struct xen_snd_front_info *front_info,
0384               struct xen_front_cfg_pcm_instance *pcm_instance,
0385               struct snd_pcm_hardware *parent_pcm_hw,
0386               const char *path, int node_index, int *stream_cnt)
0387 {
0388     char *str;
0389     char *device_path;
0390     int ret, i, num_streams;
0391     int num_pb, num_cap;
0392     int cur_pb, cur_cap;
0393     char node[3];
0394 
0395     device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
0396     if (!device_path)
0397         return -ENOMEM;
0398 
0399     str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
0400     if (!IS_ERR(str)) {
0401         strscpy(pcm_instance->name, str, sizeof(pcm_instance->name));
0402         kfree(str);
0403     }
0404 
0405     pcm_instance->device_id = node_index;
0406 
0407     /*
0408      * Check XenStore if PCM HW configuration exists for this device
0409      * and update if so, e.g. we inherit all values from card's PCM HW,
0410      * but can still override some of the values for the device.
0411      */
0412     cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);
0413 
0414     /* Find out how many streams were configured in Xen store. */
0415     num_streams = 0;
0416     do {
0417         snprintf(node, sizeof(node), "%d", num_streams);
0418         if (!xenbus_exists(XBT_NIL, device_path, node))
0419             break;
0420 
0421         num_streams++;
0422     } while (num_streams < VSND_MAX_STREAM);
0423 
0424     pcm_instance->num_streams_pb = 0;
0425     pcm_instance->num_streams_cap = 0;
0426     /* Get number of playback and capture streams. */
0427     for (i = 0; i < num_streams; i++) {
0428         ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
0429         if (ret < 0)
0430             goto fail;
0431 
0432         pcm_instance->num_streams_pb += num_pb;
0433         pcm_instance->num_streams_cap += num_cap;
0434     }
0435 
0436     if (pcm_instance->num_streams_pb) {
0437         pcm_instance->streams_pb =
0438                 devm_kcalloc(&front_info->xb_dev->dev,
0439                          pcm_instance->num_streams_pb,
0440                          sizeof(struct xen_front_cfg_stream),
0441                          GFP_KERNEL);
0442         if (!pcm_instance->streams_pb) {
0443             ret = -ENOMEM;
0444             goto fail;
0445         }
0446     }
0447 
0448     if (pcm_instance->num_streams_cap) {
0449         pcm_instance->streams_cap =
0450                 devm_kcalloc(&front_info->xb_dev->dev,
0451                          pcm_instance->num_streams_cap,
0452                          sizeof(struct xen_front_cfg_stream),
0453                          GFP_KERNEL);
0454         if (!pcm_instance->streams_cap) {
0455             ret = -ENOMEM;
0456             goto fail;
0457         }
0458     }
0459 
0460     cur_pb = 0;
0461     cur_cap = 0;
0462     for (i = 0; i < num_streams; i++) {
0463         ret = cfg_stream(front_info, pcm_instance, device_path, i,
0464                  &cur_pb, &cur_cap, stream_cnt);
0465         if (ret < 0)
0466             goto fail;
0467     }
0468     ret = 0;
0469 
0470 fail:
0471     kfree(device_path);
0472     return ret;
0473 }
0474 
0475 int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
0476                int *stream_cnt)
0477 {
0478     struct xenbus_device *xb_dev = front_info->xb_dev;
0479     struct xen_front_cfg_card *cfg = &front_info->cfg;
0480     int ret, num_devices, i;
0481     char node[3];
0482 
0483     *stream_cnt = 0;
0484     num_devices = 0;
0485     do {
0486         snprintf(node, sizeof(node), "%d", num_devices);
0487         if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
0488             break;
0489 
0490         num_devices++;
0491     } while (num_devices < SNDRV_PCM_DEVICES);
0492 
0493     if (!num_devices) {
0494         dev_warn(&xb_dev->dev,
0495              "No devices configured for sound card at %s\n",
0496              xb_dev->nodename);
0497         return -ENODEV;
0498     }
0499 
0500     /* Start from default PCM HW configuration for the card. */
0501     cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);
0502 
0503     cfg->pcm_instances =
0504             devm_kcalloc(&front_info->xb_dev->dev, num_devices,
0505                      sizeof(struct xen_front_cfg_pcm_instance),
0506                      GFP_KERNEL);
0507     if (!cfg->pcm_instances)
0508         return -ENOMEM;
0509 
0510     for (i = 0; i < num_devices; i++) {
0511         ret = cfg_device(front_info, &cfg->pcm_instances[i],
0512                  &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
0513         if (ret < 0)
0514             return ret;
0515     }
0516     cfg->num_pcm_instances = num_devices;
0517     return 0;
0518 }
0519