Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  xen_snd_front_alsa.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 <linux/platform_device.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>

#include <xen/xenbus.h>
#include <xen/xen-front-pgdir-shbuf.h>

#include "xen_snd_front.h"
#include "xen_snd_front_alsa.h"
#include "xen_snd_front_cfg.h"
#include "xen_snd_front_evtchnl.h"

struct xen_snd_front_pcm_stream_info {
 struct xen_snd_front_info *front_info;
 struct xen_snd_front_evtchnl_pair *evt_pair;

 /* This is the shared buffer with its backing storage. */
 struct xen_front_pgdir_shbuf shbuf;
 u8 *buffer;
 size_t buffer_sz;
 int num_pages;
 struct page **pages;

 int index;

 bool is_open;
 struct snd_pcm_hardware pcm_hw;

 /* Number of processed frames as reported by the backend. */
 snd_pcm_uframes_t be_cur_frame;
 /* Current HW pointer to be reported via .period callback. */
 atomic_t hw_ptr;
 /* Modulo of the number of processed frames - for period detection. */
 u32 out_frames;
};

struct xen_snd_front_pcm_instance_info {
 struct xen_snd_front_card_info *card_info;
 struct snd_pcm *pcm;
 struct snd_pcm_hardware pcm_hw;
 int num_pcm_streams_pb;
 struct xen_snd_front_pcm_stream_info *streams_pb;
 int num_pcm_streams_cap;
 struct xen_snd_front_pcm_stream_info *streams_cap;
};

struct xen_snd_front_card_info {
 struct xen_snd_front_info *front_info;
 struct snd_card *card;
 struct snd_pcm_hardware pcm_hw;
 int num_pcm_instances;
 struct xen_snd_front_pcm_instance_info *pcm_instances;
};

struct alsa_sndif_sample_format {
 u8 sndif;
 snd_pcm_format_t alsa;
};

static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
 {
  .sndif = XENSND_PCM_FORMAT_U8,
  .alsa = SNDRV_PCM_FORMAT_U8
 },
 {
  .sndif = XENSND_PCM_FORMAT_S8,
  .alsa = SNDRV_PCM_FORMAT_S8
 },
 {
  .sndif = XENSND_PCM_FORMAT_U16_LE,
  .alsa = SNDRV_PCM_FORMAT_U16_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_U16_BE,
  .alsa = SNDRV_PCM_FORMAT_U16_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S16_LE,
  .alsa = SNDRV_PCM_FORMAT_S16_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S16_BE,
  .alsa = SNDRV_PCM_FORMAT_S16_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_U24_LE,
  .alsa = SNDRV_PCM_FORMAT_U24_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_U24_BE,
  .alsa = SNDRV_PCM_FORMAT_U24_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S24_LE,
  .alsa = SNDRV_PCM_FORMAT_S24_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S24_BE,
  .alsa = SNDRV_PCM_FORMAT_S24_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_U32_LE,
  .alsa = SNDRV_PCM_FORMAT_U32_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_U32_BE,
  .alsa = SNDRV_PCM_FORMAT_U32_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S32_LE,
  .alsa = SNDRV_PCM_FORMAT_S32_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_S32_BE,
  .alsa = SNDRV_PCM_FORMAT_S32_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_A_LAW,
  .alsa = SNDRV_PCM_FORMAT_A_LAW
 },
 {
  .sndif = XENSND_PCM_FORMAT_MU_LAW,
  .alsa = SNDRV_PCM_FORMAT_MU_LAW
 },
 {
  .sndif = XENSND_PCM_FORMAT_F32_LE,
  .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_F32_BE,
  .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_F64_LE,
  .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_F64_BE,
  .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
  .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
 },
 {
  .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
  .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
 },
 {
  .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
  .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
 },
 {
  .sndif = XENSND_PCM_FORMAT_MPEG,
  .alsa = SNDRV_PCM_FORMAT_MPEG
 },
 {
  .sndif = XENSND_PCM_FORMAT_GSM,
  .alsa = SNDRV_PCM_FORMAT_GSM
 },
};

static int to_sndif_format(snd_pcm_format_t format)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
  if (ALSA_SNDIF_FORMATS[i].alsa == format)
   return ALSA_SNDIF_FORMATS[i].sndif;

 return -EINVAL;
}

static u64 to_sndif_formats_mask(u64 alsa_formats)
{
 u64 mask;
 int i;

 mask = 0;
 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
  if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
   mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif);

 return mask;
}

static u64 to_alsa_formats_mask(u64 sndif_formats)
{
 u64 mask;
 int i;

 mask = 0;
 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
  if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats)
   mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);

 return mask;
}

static void stream_clear(struct xen_snd_front_pcm_stream_info *stream)
{
 stream->is_open = false;
 stream->be_cur_frame = 0;
 stream->out_frames = 0;
 atomic_set(&stream->hw_ptr, 0);
 xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
 memset(&stream->shbuf, 0, sizeof(stream->shbuf));
 stream->buffer = NULL;
 stream->buffer_sz = 0;
 stream->pages = NULL;
 stream->num_pages = 0;
}

static void stream_free(struct xen_snd_front_pcm_stream_info *stream)
{
 xen_front_pgdir_shbuf_unmap(&stream->shbuf);
 xen_front_pgdir_shbuf_free(&stream->shbuf);
 if (stream->buffer)
  free_pages_exact(stream->buffer, stream->buffer_sz);
 kfree(stream->pages);
 stream_clear(stream);
}

static struct xen_snd_front_pcm_stream_info *
stream_get(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_instance_info *pcm_instance =
   snd_pcm_substream_chip(substream);
 struct xen_snd_front_pcm_stream_info *stream;

 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  stream = &pcm_instance->streams_pb[substream->number];
 else
  stream = &pcm_instance->streams_cap[substream->number];

 return stream;
}

static int alsa_hw_rule(struct snd_pcm_hw_params *params,
   struct snd_pcm_hw_rule *rule)
{
 struct xen_snd_front_pcm_stream_info *stream = rule->private;
 struct device *dev = &stream->front_info->xb_dev->dev;
 struct snd_mask *formats =
   hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
 struct snd_interval *rates =
   hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
 struct snd_interval *channels =
   hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
 struct snd_interval *period =
   hw_param_interval(params,
       SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
 struct snd_interval *buffer =
   hw_param_interval(params,
       SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
 struct xensnd_query_hw_param req;
 struct xensnd_query_hw_param resp;
 struct snd_interval interval;
 struct snd_mask mask;
 u64 sndif_formats;
 int changed, ret;

 /* Collect all the values we need for the query. */

 req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
         (u64)(formats->bits[1]) << 32);

 req.rates.min = rates->min;
 req.rates.max = rates->max;

 req.channels.min = channels->min;
 req.channels.max = channels->max;

 req.buffer.min = buffer->min;
 req.buffer.max = buffer->max;

 req.period.min = period->min;
 req.period.max = period->max;

 ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
        &req, &resp);
 if (ret < 0) {
  /* Check if this is due to backend communication error. */
  if (ret == -EIO || ret == -ETIMEDOUT)
   dev_err(dev, "Failed to query ALSA HW parameters\n");
  return ret;
 }

 /* Refine HW parameters after the query. */
 changed  = 0;

 sndif_formats = to_alsa_formats_mask(resp.formats);
 snd_mask_none(&mask);
 mask.bits[0] = (u32)sndif_formats;
 mask.bits[1] = (u32)(sndif_formats >> 32);
 ret = snd_mask_refine(formats, &mask);
 if (ret < 0)
  return ret;
 changed |= ret;

 interval.openmin = 0;
 interval.openmax = 0;
 interval.integer = 1;

 interval.min = resp.rates.min;
 interval.max = resp.rates.max;
 ret = snd_interval_refine(rates, &interval);
 if (ret < 0)
  return ret;
 changed |= ret;

 interval.min = resp.channels.min;
 interval.max = resp.channels.max;
 ret = snd_interval_refine(channels, &interval);
 if (ret < 0)
  return ret;
 changed |= ret;

 interval.min = resp.buffer.min;
 interval.max = resp.buffer.max;
 ret = snd_interval_refine(buffer, &interval);
 if (ret < 0)
  return ret;
 changed |= ret;

 interval.min = resp.period.min;
 interval.max = resp.period.max;
 ret = snd_interval_refine(period, &interval);
 if (ret < 0)
  return ret;
 changed |= ret;

 return changed;
}

static int alsa_open(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_instance_info *pcm_instance =
   snd_pcm_substream_chip(substream);
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 struct snd_pcm_runtime *runtime = substream->runtime;
 struct xen_snd_front_info *front_info =
   pcm_instance->card_info->front_info;
 struct device *dev = &front_info->xb_dev->dev;
 int ret;

 /*
 * Return our HW properties: override defaults with those configured
 * via XenStore.
 */

 runtime->hw = stream->pcm_hw;
 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
         SNDRV_PCM_INFO_MMAP_VALID |
         SNDRV_PCM_INFO_DOUBLE |
         SNDRV_PCM_INFO_BATCH |
         SNDRV_PCM_INFO_NONINTERLEAVED |
         SNDRV_PCM_INFO_RESUME |
         SNDRV_PCM_INFO_PAUSE);
 runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;

 stream->evt_pair = &front_info->evt_pairs[stream->index];

 stream->front_info = front_info;

 stream->evt_pair->evt.u.evt.substream = substream;

 stream_clear(stream);

 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);

 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
      alsa_hw_rule, stream,
      SNDRV_PCM_HW_PARAM_FORMAT, -1);
 if (ret) {
  dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
  return ret;
 }

 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
      alsa_hw_rule, stream,
      SNDRV_PCM_HW_PARAM_RATE, -1);
 if (ret) {
  dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
  return ret;
 }

 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
      alsa_hw_rule, stream,
      SNDRV_PCM_HW_PARAM_CHANNELS, -1);
 if (ret) {
  dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
  return ret;
 }

 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
      alsa_hw_rule, stream,
      SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
 if (ret) {
  dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
  return ret;
 }

 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
      alsa_hw_rule, stream,
      SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
 if (ret) {
  dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
  return ret;
 }

 return 0;
}

static int alsa_close(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);

 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
 return 0;
}

static int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream,
     size_t buffer_sz)
{
 int i;

 stream->buffer = alloc_pages_exact(buffer_sz, GFP_KERNEL);
 if (!stream->buffer)
  return -ENOMEM;

 stream->buffer_sz = buffer_sz;
 stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE);
 stream->pages = kcalloc(stream->num_pages, sizeof(struct page *),
    GFP_KERNEL);
 if (!stream->pages)
  return -ENOMEM;

 for (i = 0; i < stream->num_pages; i++)
  stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE);

 return 0;
}

static int alsa_hw_params(struct snd_pcm_substream *substream,
     struct snd_pcm_hw_params *params)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 struct xen_snd_front_info *front_info = stream->front_info;
 struct xen_front_pgdir_shbuf_cfg buf_cfg;
 int ret;

 /*
 * This callback may be called multiple times,
 * so free the previously allocated shared buffer if any.
 */

 stream_free(stream);
 ret = shbuf_setup_backstore(stream, params_buffer_bytes(params));
 if (ret < 0)
  goto fail;

 memset(&buf_cfg, 0, sizeof(buf_cfg));
 buf_cfg.xb_dev = front_info->xb_dev;
 buf_cfg.pgdir = &stream->shbuf;
 buf_cfg.num_pages = stream->num_pages;
 buf_cfg.pages = stream->pages;

 ret = xen_front_pgdir_shbuf_alloc(&buf_cfg);
 if (ret < 0)
  goto fail;

 ret = xen_front_pgdir_shbuf_map(&stream->shbuf);
 if (ret < 0)
  goto fail;

 return 0;

fail:
 stream_free(stream);
 dev_err(&front_info->xb_dev->dev,
  "Failed to allocate buffers for stream with index %d\n",
  stream->index);
 return ret;
}

static int alsa_hw_free(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 int ret;

 ret = xen_snd_front_stream_close(&stream->evt_pair->req);
 stream_free(stream);
 return ret;
}

static int alsa_prepare(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);

 if (!stream->is_open) {
  struct snd_pcm_runtime *runtime = substream->runtime;
  u8 sndif_format;
  int ret;

  ret = to_sndif_format(runtime->format);
  if (ret < 0) {
   dev_err(&stream->front_info->xb_dev->dev,
    "Unsupported sample format: %d\n",
    runtime->format);
   return ret;
  }
  sndif_format = ret;

  ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
         &stream->shbuf,
         sndif_format,
         runtime->channels,
         runtime->rate,
         snd_pcm_lib_buffer_bytes(substream),
         snd_pcm_lib_period_bytes(substream));
  if (ret < 0)
   return ret;

  stream->is_open = true;
 }

 return 0;
}

static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 int type;

 switch (cmd) {
 case SNDRV_PCM_TRIGGER_START:
  type = XENSND_OP_TRIGGER_START;
  break;

 case SNDRV_PCM_TRIGGER_RESUME:
  type = XENSND_OP_TRIGGER_RESUME;
  break;

 case SNDRV_PCM_TRIGGER_STOP:
  type = XENSND_OP_TRIGGER_STOP;
  break;

 case SNDRV_PCM_TRIGGER_SUSPEND:
  type = XENSND_OP_TRIGGER_PAUSE;
  break;

 default:
  return -EINVAL;
 }

 return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
}

void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
           u64 pos_bytes)
{
 struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;

 cur_frame = bytes_to_frames(substream->runtime, pos_bytes);

 delta = cur_frame - stream->be_cur_frame;
 stream->be_cur_frame = cur_frame;

 new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
 new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
 atomic_set(&stream->hw_ptr, (int)new_hw_ptr);

 stream->out_frames += delta;
 if (stream->out_frames > substream->runtime->period_size) {
  stream->out_frames %= substream->runtime->period_size;
  snd_pcm_period_elapsed(substream);
 }
}

static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);

 return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
}

static int alsa_pb_copy(struct snd_pcm_substream *substream,
   int channel, unsigned long pos, struct iov_iter *src,
   unsigned long count)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);

 if (unlikely(pos + count > stream->buffer_sz))
  return -EINVAL;

 if (copy_from_iter(stream->buffer + pos, count, src) != count)
  return -EFAULT;

 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
}

static int alsa_cap_copy(struct snd_pcm_substream *substream,
    int channel, unsigned long pos, struct iov_iter *dst,
    unsigned long count)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
 int ret;

 if (unlikely(pos + count > stream->buffer_sz))
  return -EINVAL;

 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
 if (ret < 0)
  return ret;

 if (copy_to_iter(stream->buffer + pos, count, dst) != count)
  return -EFAULT;
 return 0;
}

static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
    int channel, unsigned long pos,
    unsigned long count)
{
 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);

 if (unlikely(pos + count > stream->buffer_sz))
  return -EINVAL;

 memset(stream->buffer + pos, 0, count);

 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
}

/*
 * FIXME: The mmaped data transfer is asynchronous and there is no
 * ack signal from user-space when it is done. This is the
 * reason it is not implemented in the PV driver as we do need
 * to know when the buffer can be transferred to the backend.
 */


static const struct snd_pcm_ops snd_drv_alsa_playback_ops = {
 .open  = alsa_open,
 .close  = alsa_close,
 .hw_params = alsa_hw_params,
 .hw_free = alsa_hw_free,
 .prepare = alsa_prepare,
 .trigger = alsa_trigger,
 .pointer = alsa_pointer,
 .copy  = alsa_pb_copy,
 .fill_silence = alsa_pb_fill_silence,
};

static const struct snd_pcm_ops snd_drv_alsa_capture_ops = {
 .open  = alsa_open,
 .close  = alsa_close,
 .hw_params = alsa_hw_params,
 .hw_free = alsa_hw_free,
 .prepare = alsa_prepare,
 .trigger = alsa_trigger,
 .pointer = alsa_pointer,
 .copy  = alsa_cap_copy,
};

static int new_pcm_instance(struct xen_snd_front_card_info *card_info,
       struct xen_front_cfg_pcm_instance *instance_cfg,
       struct xen_snd_front_pcm_instance_info *pcm_instance_info)
{
 struct snd_pcm *pcm;
 int ret, i;

 dev_dbg(&card_info->front_info->xb_dev->dev,
  "New PCM device \"%s\" with id %d playback %d capture %d",
  instance_cfg->name,
  instance_cfg->device_id,
  instance_cfg->num_streams_pb,
  instance_cfg->num_streams_cap);

 pcm_instance_info->card_info = card_info;

 pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;

 if (instance_cfg->num_streams_pb) {
  pcm_instance_info->streams_pb =
    devm_kcalloc(&card_info->card->card_dev,
          instance_cfg->num_streams_pb,
          sizeof(struct xen_snd_front_pcm_stream_info),
          GFP_KERNEL);
  if (!pcm_instance_info->streams_pb)
   return -ENOMEM;
 }

 if (instance_cfg->num_streams_cap) {
  pcm_instance_info->streams_cap =
    devm_kcalloc(&card_info->card->card_dev,
          instance_cfg->num_streams_cap,
          sizeof(struct xen_snd_front_pcm_stream_info),
          GFP_KERNEL);
  if (!pcm_instance_info->streams_cap)
   return -ENOMEM;
 }

 pcm_instance_info->num_pcm_streams_pb =
   instance_cfg->num_streams_pb;
 pcm_instance_info->num_pcm_streams_cap =
   instance_cfg->num_streams_cap;

 for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
  pcm_instance_info->streams_pb[i].pcm_hw =
   instance_cfg->streams_pb[i].pcm_hw;
  pcm_instance_info->streams_pb[i].index =
   instance_cfg->streams_pb[i].index;
 }

 for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
  pcm_instance_info->streams_cap[i].pcm_hw =
   instance_cfg->streams_cap[i].pcm_hw;
  pcm_instance_info->streams_cap[i].index =
   instance_cfg->streams_cap[i].index;
 }

 ret = snd_pcm_new(card_info->card, instance_cfg->name,
     instance_cfg->device_id,
     instance_cfg->num_streams_pb,
     instance_cfg->num_streams_cap,
     &pcm);
 if (ret < 0)
  return ret;

 pcm->private_data = pcm_instance_info;
 pcm->info_flags = 0;
 /* we want to handle all PCM operations in non-atomic context */
 pcm->nonatomic = true;
 strscpy(pcm->name, "Virtual card PCM"sizeof(pcm->name));

 if (instance_cfg->num_streams_pb)
  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
    &snd_drv_alsa_playback_ops);

 if (instance_cfg->num_streams_cap)
  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
    &snd_drv_alsa_capture_ops);

 pcm_instance_info->pcm = pcm;
 return 0;
}

int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
{
 struct device *dev = &front_info->xb_dev->dev;
 struct xen_front_cfg_card *cfg = &front_info->cfg;
 struct xen_snd_front_card_info *card_info;
 struct snd_card *card;
 int ret, i;

 dev_dbg(dev, "Creating virtual sound card\n");

 ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
      sizeof(struct xen_snd_front_card_info), &card);
 if (ret < 0)
  return ret;

 card_info = card->private_data;
 card_info->front_info = front_info;
 front_info->card_info = card_info;
 card_info->card = card;
 card_info->pcm_instances =
   devm_kcalloc(dev, cfg->num_pcm_instances,
         sizeof(struct xen_snd_front_pcm_instance_info),
         GFP_KERNEL);
 if (!card_info->pcm_instances) {
  ret = -ENOMEM;
  goto fail;
 }

 card_info->num_pcm_instances = cfg->num_pcm_instances;
 card_info->pcm_hw = cfg->pcm_hw;

 for (i = 0; i < cfg->num_pcm_instances; i++) {
  ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
           &card_info->pcm_instances[i]);
  if (ret < 0)
   goto fail;
 }

 strscpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
 strscpy(card->shortname, cfg->name_short, sizeof(card->shortname));
 strscpy(card->longname, cfg->name_long, sizeof(card->longname));

 ret = snd_card_register(card);
 if (ret < 0)
  goto fail;

 return 0;

fail:
 snd_card_free(card);
 return ret;
}

void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
{
 struct xen_snd_front_card_info *card_info;
 struct snd_card *card;

 card_info = front_info->card_info;
 if (!card_info)
  return;

 card = card_info->card;
 if (!card)
  return;

 dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
  card->number);
 snd_card_free(card);

 /* Card_info will be freed when destroying front_info->xb_dev->dev. */
 card_info->card = NULL;
}

Messung V0.5
C=94 H=92 G=92

¤ Dauer der Verarbeitung: 0.2 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge