Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/sound/xen/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 12 kB image not shown  

Quelle  xen_snd_front_cfg.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR MIT

/*
 * Xen para-virtual sound device
 *
 * Copyright (C) 2016-2018 EPAM Systems Inc.
 *
 * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
 */


#include <xen/xenbus.h>

#include <xen/interface/io/sndif.h>

#include "xen_snd_front.h"
#include "xen_snd_front_cfg.h"

/* Maximum number of supported streams. */
#define VSND_MAX_STREAM  8

struct cfg_hw_sample_rate {
 const char *name;
 unsigned int mask;
 unsigned int value;
};

static const struct cfg_hw_sample_rate CFG_HW_SUPPORTED_RATES[] = {
 { .name = "5512",   .mask = SNDRV_PCM_RATE_5512,   .value = 5512 },
 { .name = "8000",   .mask = SNDRV_PCM_RATE_8000,   .value = 8000 },
 { .name = "11025",  .mask = SNDRV_PCM_RATE_11025,  .value = 11025 },
 { .name = "16000",  .mask = SNDRV_PCM_RATE_16000,  .value = 16000 },
 { .name = "22050",  .mask = SNDRV_PCM_RATE_22050,  .value = 22050 },
 { .name = "32000",  .mask = SNDRV_PCM_RATE_32000,  .value = 32000 },
 { .name = "44100",  .mask = SNDRV_PCM_RATE_44100,  .value = 44100 },
 { .name = "48000",  .mask = SNDRV_PCM_RATE_48000,  .value = 48000 },
 { .name = "64000",  .mask = SNDRV_PCM_RATE_64000,  .value = 64000 },
 { .name = "96000",  .mask = SNDRV_PCM_RATE_96000,  .value = 96000 },
 { .name = "176400", .mask = SNDRV_PCM_RATE_176400, .value = 176400 },
 { .name = "192000", .mask = SNDRV_PCM_RATE_192000, .value = 192000 },
};

struct cfg_hw_sample_format {
 const char *name;
 u64 mask;
};

static const struct cfg_hw_sample_format CFG_HW_SUPPORTED_FORMATS[] = {
 {
  .name = XENSND_PCM_FORMAT_U8_STR,
  .mask = SNDRV_PCM_FMTBIT_U8
 },
 {
  .name = XENSND_PCM_FORMAT_S8_STR,
  .mask = SNDRV_PCM_FMTBIT_S8
 },
 {
  .name = XENSND_PCM_FORMAT_U16_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_U16_LE
 },
 {
  .name = XENSND_PCM_FORMAT_U16_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_U16_BE
 },
 {
  .name = XENSND_PCM_FORMAT_S16_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_S16_LE
 },
 {
  .name = XENSND_PCM_FORMAT_S16_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_S16_BE
 },
 {
  .name = XENSND_PCM_FORMAT_U24_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_U24_LE
 },
 {
  .name = XENSND_PCM_FORMAT_U24_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_U24_BE
 },
 {
  .name = XENSND_PCM_FORMAT_S24_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_S24_LE
 },
 {
  .name = XENSND_PCM_FORMAT_S24_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_S24_BE
 },
 {
  .name = XENSND_PCM_FORMAT_U32_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_U32_LE
 },
 {
  .name = XENSND_PCM_FORMAT_U32_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_U32_BE
 },
 {
  .name = XENSND_PCM_FORMAT_S32_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_S32_LE
 },
 {
  .name = XENSND_PCM_FORMAT_S32_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_S32_BE
 },
 {
  .name = XENSND_PCM_FORMAT_A_LAW_STR,
  .mask = SNDRV_PCM_FMTBIT_A_LAW
 },
 {
  .name = XENSND_PCM_FORMAT_MU_LAW_STR,
  .mask = SNDRV_PCM_FMTBIT_MU_LAW
 },
 {
  .name = XENSND_PCM_FORMAT_F32_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_FLOAT_LE
 },
 {
  .name = XENSND_PCM_FORMAT_F32_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_FLOAT_BE
 },
 {
  .name = XENSND_PCM_FORMAT_F64_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_FLOAT64_LE
 },
 {
  .name = XENSND_PCM_FORMAT_F64_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_FLOAT64_BE
 },
 {
  .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE_STR,
  .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
 },
 {
  .name = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE_STR,
  .mask = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE
 },
 {
  .name = XENSND_PCM_FORMAT_IMA_ADPCM_STR,
  .mask = SNDRV_PCM_FMTBIT_IMA_ADPCM
 },
 {
  .name = XENSND_PCM_FORMAT_MPEG_STR,
  .mask = SNDRV_PCM_FMTBIT_MPEG
 },
 {
  .name = XENSND_PCM_FORMAT_GSM_STR,
  .mask = SNDRV_PCM_FMTBIT_GSM
 },
};

static void cfg_hw_rates(char *list, unsigned int len,
    const char *path, struct snd_pcm_hardware *pcm_hw)
{
 char *cur_rate;
 unsigned int cur_mask;
 unsigned int cur_value;
 unsigned int rates;
 unsigned int rate_min;
 unsigned int rate_max;
 int i;

 rates = 0;
 rate_min = -1;
 rate_max = 0;
 while ((cur_rate = strsep(&list, XENSND_LIST_SEPARATOR))) {
  for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_RATES); i++)
   if (!strncasecmp(cur_rate,
      CFG_HW_SUPPORTED_RATES[i].name,
      XENSND_SAMPLE_RATE_MAX_LEN)) {
    cur_mask = CFG_HW_SUPPORTED_RATES[i].mask;
    cur_value = CFG_HW_SUPPORTED_RATES[i].value;
    rates |= cur_mask;
    if (rate_min > cur_value)
     rate_min = cur_value;
    if (rate_max < cur_value)
     rate_max = cur_value;
   }
 }

 if (rates) {
  pcm_hw->rates = rates;
  pcm_hw->rate_min = rate_min;
  pcm_hw->rate_max = rate_max;
 }
}

static void cfg_formats(char *list, unsigned int len,
   const char *path, struct snd_pcm_hardware *pcm_hw)
{
 u64 formats;
 char *cur_format;
 int i;

 formats = 0;
 while ((cur_format = strsep(&list, XENSND_LIST_SEPARATOR))) {
  for (i = 0; i < ARRAY_SIZE(CFG_HW_SUPPORTED_FORMATS); i++)
   if (!strncasecmp(cur_format,
      CFG_HW_SUPPORTED_FORMATS[i].name,
      XENSND_SAMPLE_FORMAT_MAX_LEN))
    formats |= CFG_HW_SUPPORTED_FORMATS[i].mask;
 }

 if (formats)
  pcm_hw->formats = formats;
}

#define MAX_BUFFER_SIZE  (64 * 1024)
#define MIN_PERIOD_SIZE  64
#define MAX_PERIOD_SIZE  MAX_BUFFER_SIZE
#define USE_FORMATS  (SNDRV_PCM_FMTBIT_U8 | \
     SNDRV_PCM_FMTBIT_S16_LE)
#define USE_RATE  (SNDRV_PCM_RATE_CONTINUOUS | \
     SNDRV_PCM_RATE_8000_48000)
#define USE_RATE_MIN  5512
#define USE_RATE_MAX  48000
#define USE_CHANNELS_MIN 1
#define USE_CHANNELS_MAX 2
#define USE_PERIODS_MIN  2
#define USE_PERIODS_MAX  (MAX_BUFFER_SIZE / MIN_PERIOD_SIZE)

static const struct snd_pcm_hardware SND_DRV_PCM_HW_DEFAULT = {
 .info = (SNDRV_PCM_INFO_MMAP |
   SNDRV_PCM_INFO_INTERLEAVED |
   SNDRV_PCM_INFO_RESUME |
   SNDRV_PCM_INFO_MMAP_VALID),
 .formats = USE_FORMATS,
 .rates = USE_RATE,
 .rate_min = USE_RATE_MIN,
 .rate_max = USE_RATE_MAX,
 .channels_min = USE_CHANNELS_MIN,
 .channels_max = USE_CHANNELS_MAX,
 .buffer_bytes_max = MAX_BUFFER_SIZE,
 .period_bytes_min = MIN_PERIOD_SIZE,
 .period_bytes_max = MAX_PERIOD_SIZE,
 .periods_min = USE_PERIODS_MIN,
 .periods_max = USE_PERIODS_MAX,
 .fifo_size = 0,
};

static void cfg_read_pcm_hw(const char *path,
       struct snd_pcm_hardware *parent_pcm_hw,
       struct snd_pcm_hardware *pcm_hw)
{
 char *list;
 int val;
 size_t buf_sz;
 unsigned int len;

 /* Inherit parent's PCM HW and read overrides from XenStore. */
 if (parent_pcm_hw)
  *pcm_hw = *parent_pcm_hw;
 else
  *pcm_hw = SND_DRV_PCM_HW_DEFAULT;

 val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MIN, 0);
 if (val)
  pcm_hw->channels_min = val;

 val = xenbus_read_unsigned(path, XENSND_FIELD_CHANNELS_MAX, 0);
 if (val)
  pcm_hw->channels_max = val;

 list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_RATES, &len);
 if (!IS_ERR(list)) {
  cfg_hw_rates(list, len, path, pcm_hw);
  kfree(list);
 }

 list = xenbus_read(XBT_NIL, path, XENSND_FIELD_SAMPLE_FORMATS, &len);
 if (!IS_ERR(list)) {
  cfg_formats(list, len, path, pcm_hw);
  kfree(list);
 }

 buf_sz = xenbus_read_unsigned(path, XENSND_FIELD_BUFFER_SIZE, 0);
 if (buf_sz)
  pcm_hw->buffer_bytes_max = buf_sz;

 /* Update configuration to match new values. */
 if (pcm_hw->channels_min > pcm_hw->channels_max)
  pcm_hw->channels_min = pcm_hw->channels_max;

 if (pcm_hw->rate_min > pcm_hw->rate_max)
  pcm_hw->rate_min = pcm_hw->rate_max;

 pcm_hw->period_bytes_max = pcm_hw->buffer_bytes_max;

 pcm_hw->periods_max = pcm_hw->period_bytes_max /
  pcm_hw->period_bytes_min;
}

static int cfg_get_stream_type(const char *path, int index,
          int *num_pb, int *num_cap)
{
 char *str = NULL;
 char *stream_path;
 int ret;

 *num_pb = 0;
 *num_cap = 0;
 stream_path = kasprintf(GFP_KERNEL, "%s/%d", path, index);
 if (!stream_path) {
  ret = -ENOMEM;
  goto fail;
 }

 str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
 if (IS_ERR(str)) {
  ret = PTR_ERR(str);
  str = NULL;
  goto fail;
 }

 if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
    sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
  (*num_pb)++;
 } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
         sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
  (*num_cap)++;
 } else {
  ret = -EINVAL;
  goto fail;
 }
 ret = 0;

fail:
 kfree(stream_path);
 kfree(str);
 return ret;
}

static int cfg_stream(struct xen_snd_front_info *front_info,
        struct xen_front_cfg_pcm_instance *pcm_instance,
        const char *path, int index, int *cur_pb, int *cur_cap,
        int *stream_cnt)
{
 char *str = NULL;
 char *stream_path;
 struct xen_front_cfg_stream *stream;
 int ret;

 stream_path = devm_kasprintf(&front_info->xb_dev->dev,
         GFP_KERNEL, "%s/%d", path, index);
 if (!stream_path) {
  ret = -ENOMEM;
  goto fail;
 }

 str = xenbus_read(XBT_NIL, stream_path, XENSND_FIELD_TYPE, NULL);
 if (IS_ERR(str)) {
  ret = PTR_ERR(str);
  str = NULL;
  goto fail;
 }

 if (!strncasecmp(str, XENSND_STREAM_TYPE_PLAYBACK,
    sizeof(XENSND_STREAM_TYPE_PLAYBACK))) {
  stream = &pcm_instance->streams_pb[(*cur_pb)++];
 } else if (!strncasecmp(str, XENSND_STREAM_TYPE_CAPTURE,
         sizeof(XENSND_STREAM_TYPE_CAPTURE))) {
  stream = &pcm_instance->streams_cap[(*cur_cap)++];
 } else {
  ret = -EINVAL;
  goto fail;
 }

 /* Get next stream index. */
 stream->index = (*stream_cnt)++;
 stream->xenstore_path = stream_path;
 /*
 * Check XenStore if PCM HW configuration exists for this stream
 * and update if so, e.g. we inherit all values from device's PCM HW,
 * but can still override some of the values for the stream.
 */

 cfg_read_pcm_hw(stream->xenstore_path,
   &pcm_instance->pcm_hw, &stream->pcm_hw);
 ret = 0;

fail:
 kfree(str);
 return ret;
}

static int cfg_device(struct xen_snd_front_info *front_info,
        struct xen_front_cfg_pcm_instance *pcm_instance,
        struct snd_pcm_hardware *parent_pcm_hw,
        const char *path, int node_index, int *stream_cnt)
{
 char *str;
 char *device_path;
 int ret, i, num_streams;
 int num_pb, num_cap;
 int cur_pb, cur_cap;
 char node[3];

 device_path = kasprintf(GFP_KERNEL, "%s/%d", path, node_index);
 if (!device_path)
  return -ENOMEM;

 str = xenbus_read(XBT_NIL, device_path, XENSND_FIELD_DEVICE_NAME, NULL);
 if (!IS_ERR(str)) {
  strscpy(pcm_instance->name, str, sizeof(pcm_instance->name));
  kfree(str);
 }

 pcm_instance->device_id = node_index;

 /*
 * Check XenStore if PCM HW configuration exists for this device
 * and update if so, e.g. we inherit all values from card's PCM HW,
 * but can still override some of the values for the device.
 */

 cfg_read_pcm_hw(device_path, parent_pcm_hw, &pcm_instance->pcm_hw);

 /* Find out how many streams were configured in Xen store. */
 num_streams = 0;
 do {
  snprintf(node, sizeof(node), "%d", num_streams);
  if (!xenbus_exists(XBT_NIL, device_path, node))
   break;

  num_streams++;
 } while (num_streams < VSND_MAX_STREAM);

 pcm_instance->num_streams_pb = 0;
 pcm_instance->num_streams_cap = 0;
 /* Get number of playback and capture streams. */
 for (i = 0; i < num_streams; i++) {
  ret = cfg_get_stream_type(device_path, i, &num_pb, &num_cap);
  if (ret < 0)
   goto fail;

  pcm_instance->num_streams_pb += num_pb;
  pcm_instance->num_streams_cap += num_cap;
 }

 if (pcm_instance->num_streams_pb) {
  pcm_instance->streams_pb =
    devm_kcalloc(&front_info->xb_dev->dev,
          pcm_instance->num_streams_pb,
          sizeof(struct xen_front_cfg_stream),
          GFP_KERNEL);
  if (!pcm_instance->streams_pb) {
   ret = -ENOMEM;
   goto fail;
  }
 }

 if (pcm_instance->num_streams_cap) {
  pcm_instance->streams_cap =
    devm_kcalloc(&front_info->xb_dev->dev,
          pcm_instance->num_streams_cap,
          sizeof(struct xen_front_cfg_stream),
          GFP_KERNEL);
  if (!pcm_instance->streams_cap) {
   ret = -ENOMEM;
   goto fail;
  }
 }

 cur_pb = 0;
 cur_cap = 0;
 for (i = 0; i < num_streams; i++) {
  ret = cfg_stream(front_info, pcm_instance, device_path, i,
     &cur_pb, &cur_cap, stream_cnt);
  if (ret < 0)
   goto fail;
 }
 ret = 0;

fail:
 kfree(device_path);
 return ret;
}

int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info,
      int *stream_cnt)
{
 struct xenbus_device *xb_dev = front_info->xb_dev;
 struct xen_front_cfg_card *cfg = &front_info->cfg;
 int ret, num_devices, i;
 char node[3];

 *stream_cnt = 0;
 num_devices = 0;
 do {
  scnprintf(node, sizeof(node), "%d", num_devices);
  if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node))
   break;

  num_devices++;
 } while (num_devices < SNDRV_PCM_DEVICES);

 if (!num_devices) {
  dev_warn(&xb_dev->dev,
    "No devices configured for sound card at %s\n",
    xb_dev->nodename);
  return -ENODEV;
 }

 /* Start from default PCM HW configuration for the card. */
 cfg_read_pcm_hw(xb_dev->nodename, NULL, &cfg->pcm_hw);

 cfg->pcm_instances =
   devm_kcalloc(&front_info->xb_dev->dev, num_devices,
         sizeof(struct xen_front_cfg_pcm_instance),
         GFP_KERNEL);
 if (!cfg->pcm_instances)
  return -ENOMEM;

 for (i = 0; i < num_devices; i++) {
  ret = cfg_device(front_info, &cfg->pcm_instances[i],
     &cfg->pcm_hw, xb_dev->nodename, i, stream_cnt);
  if (ret < 0)
   return ret;
 }
 cfg->num_pcm_instances = num_devices;
 return 0;
}


Messung V0.5
C=98 H=78 G=88

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.