834 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 <linux/platform_device.h>12 13#include <sound/core.h>14#include <sound/pcm.h>15#include <sound/pcm_params.h>16 17#include <xen/xenbus.h>18#include <xen/xen-front-pgdir-shbuf.h>19 20#include "xen_snd_front.h"21#include "xen_snd_front_alsa.h"22#include "xen_snd_front_cfg.h"23#include "xen_snd_front_evtchnl.h"24 25struct xen_snd_front_pcm_stream_info {26 struct xen_snd_front_info *front_info;27 struct xen_snd_front_evtchnl_pair *evt_pair;28 29 /* This is the shared buffer with its backing storage. */30 struct xen_front_pgdir_shbuf shbuf;31 u8 *buffer;32 size_t buffer_sz;33 int num_pages;34 struct page **pages;35 36 int index;37 38 bool is_open;39 struct snd_pcm_hardware pcm_hw;40 41 /* Number of processed frames as reported by the backend. */42 snd_pcm_uframes_t be_cur_frame;43 /* Current HW pointer to be reported via .period callback. */44 atomic_t hw_ptr;45 /* Modulo of the number of processed frames - for period detection. */46 u32 out_frames;47};48 49struct xen_snd_front_pcm_instance_info {50 struct xen_snd_front_card_info *card_info;51 struct snd_pcm *pcm;52 struct snd_pcm_hardware pcm_hw;53 int num_pcm_streams_pb;54 struct xen_snd_front_pcm_stream_info *streams_pb;55 int num_pcm_streams_cap;56 struct xen_snd_front_pcm_stream_info *streams_cap;57};58 59struct xen_snd_front_card_info {60 struct xen_snd_front_info *front_info;61 struct snd_card *card;62 struct snd_pcm_hardware pcm_hw;63 int num_pcm_instances;64 struct xen_snd_front_pcm_instance_info *pcm_instances;65};66 67struct alsa_sndif_sample_format {68 u8 sndif;69 snd_pcm_format_t alsa;70};71 72static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {73 {74 .sndif = XENSND_PCM_FORMAT_U8,75 .alsa = SNDRV_PCM_FORMAT_U876 },77 {78 .sndif = XENSND_PCM_FORMAT_S8,79 .alsa = SNDRV_PCM_FORMAT_S880 },81 {82 .sndif = XENSND_PCM_FORMAT_U16_LE,83 .alsa = SNDRV_PCM_FORMAT_U16_LE84 },85 {86 .sndif = XENSND_PCM_FORMAT_U16_BE,87 .alsa = SNDRV_PCM_FORMAT_U16_BE88 },89 {90 .sndif = XENSND_PCM_FORMAT_S16_LE,91 .alsa = SNDRV_PCM_FORMAT_S16_LE92 },93 {94 .sndif = XENSND_PCM_FORMAT_S16_BE,95 .alsa = SNDRV_PCM_FORMAT_S16_BE96 },97 {98 .sndif = XENSND_PCM_FORMAT_U24_LE,99 .alsa = SNDRV_PCM_FORMAT_U24_LE100 },101 {102 .sndif = XENSND_PCM_FORMAT_U24_BE,103 .alsa = SNDRV_PCM_FORMAT_U24_BE104 },105 {106 .sndif = XENSND_PCM_FORMAT_S24_LE,107 .alsa = SNDRV_PCM_FORMAT_S24_LE108 },109 {110 .sndif = XENSND_PCM_FORMAT_S24_BE,111 .alsa = SNDRV_PCM_FORMAT_S24_BE112 },113 {114 .sndif = XENSND_PCM_FORMAT_U32_LE,115 .alsa = SNDRV_PCM_FORMAT_U32_LE116 },117 {118 .sndif = XENSND_PCM_FORMAT_U32_BE,119 .alsa = SNDRV_PCM_FORMAT_U32_BE120 },121 {122 .sndif = XENSND_PCM_FORMAT_S32_LE,123 .alsa = SNDRV_PCM_FORMAT_S32_LE124 },125 {126 .sndif = XENSND_PCM_FORMAT_S32_BE,127 .alsa = SNDRV_PCM_FORMAT_S32_BE128 },129 {130 .sndif = XENSND_PCM_FORMAT_A_LAW,131 .alsa = SNDRV_PCM_FORMAT_A_LAW132 },133 {134 .sndif = XENSND_PCM_FORMAT_MU_LAW,135 .alsa = SNDRV_PCM_FORMAT_MU_LAW136 },137 {138 .sndif = XENSND_PCM_FORMAT_F32_LE,139 .alsa = SNDRV_PCM_FORMAT_FLOAT_LE140 },141 {142 .sndif = XENSND_PCM_FORMAT_F32_BE,143 .alsa = SNDRV_PCM_FORMAT_FLOAT_BE144 },145 {146 .sndif = XENSND_PCM_FORMAT_F64_LE,147 .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE148 },149 {150 .sndif = XENSND_PCM_FORMAT_F64_BE,151 .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE152 },153 {154 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,155 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE156 },157 {158 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,159 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE160 },161 {162 .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,163 .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM164 },165 {166 .sndif = XENSND_PCM_FORMAT_MPEG,167 .alsa = SNDRV_PCM_FORMAT_MPEG168 },169 {170 .sndif = XENSND_PCM_FORMAT_GSM,171 .alsa = SNDRV_PCM_FORMAT_GSM172 },173};174 175static int to_sndif_format(snd_pcm_format_t format)176{177 int i;178 179 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)180 if (ALSA_SNDIF_FORMATS[i].alsa == format)181 return ALSA_SNDIF_FORMATS[i].sndif;182 183 return -EINVAL;184}185 186static u64 to_sndif_formats_mask(u64 alsa_formats)187{188 u64 mask;189 int i;190 191 mask = 0;192 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)193 if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)194 mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif);195 196 return mask;197}198 199static u64 to_alsa_formats_mask(u64 sndif_formats)200{201 u64 mask;202 int i;203 204 mask = 0;205 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)206 if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats)207 mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);208 209 return mask;210}211 212static void stream_clear(struct xen_snd_front_pcm_stream_info *stream)213{214 stream->is_open = false;215 stream->be_cur_frame = 0;216 stream->out_frames = 0;217 atomic_set(&stream->hw_ptr, 0);218 xen_snd_front_evtchnl_pair_clear(stream->evt_pair);219 memset(&stream->shbuf, 0, sizeof(stream->shbuf));220 stream->buffer = NULL;221 stream->buffer_sz = 0;222 stream->pages = NULL;223 stream->num_pages = 0;224}225 226static void stream_free(struct xen_snd_front_pcm_stream_info *stream)227{228 xen_front_pgdir_shbuf_unmap(&stream->shbuf);229 xen_front_pgdir_shbuf_free(&stream->shbuf);230 if (stream->buffer)231 free_pages_exact(stream->buffer, stream->buffer_sz);232 kfree(stream->pages);233 stream_clear(stream);234}235 236static struct xen_snd_front_pcm_stream_info *237stream_get(struct snd_pcm_substream *substream)238{239 struct xen_snd_front_pcm_instance_info *pcm_instance =240 snd_pcm_substream_chip(substream);241 struct xen_snd_front_pcm_stream_info *stream;242 243 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)244 stream = &pcm_instance->streams_pb[substream->number];245 else246 stream = &pcm_instance->streams_cap[substream->number];247 248 return stream;249}250 251static int alsa_hw_rule(struct snd_pcm_hw_params *params,252 struct snd_pcm_hw_rule *rule)253{254 struct xen_snd_front_pcm_stream_info *stream = rule->private;255 struct device *dev = &stream->front_info->xb_dev->dev;256 struct snd_mask *formats =257 hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);258 struct snd_interval *rates =259 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);260 struct snd_interval *channels =261 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);262 struct snd_interval *period =263 hw_param_interval(params,264 SNDRV_PCM_HW_PARAM_PERIOD_SIZE);265 struct snd_interval *buffer =266 hw_param_interval(params,267 SNDRV_PCM_HW_PARAM_BUFFER_SIZE);268 struct xensnd_query_hw_param req;269 struct xensnd_query_hw_param resp;270 struct snd_interval interval;271 struct snd_mask mask;272 u64 sndif_formats;273 int changed, ret;274 275 /* Collect all the values we need for the query. */276 277 req.formats = to_sndif_formats_mask((u64)formats->bits[0] |278 (u64)(formats->bits[1]) << 32);279 280 req.rates.min = rates->min;281 req.rates.max = rates->max;282 283 req.channels.min = channels->min;284 req.channels.max = channels->max;285 286 req.buffer.min = buffer->min;287 req.buffer.max = buffer->max;288 289 req.period.min = period->min;290 req.period.max = period->max;291 292 ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,293 &req, &resp);294 if (ret < 0) {295 /* Check if this is due to backend communication error. */296 if (ret == -EIO || ret == -ETIMEDOUT)297 dev_err(dev, "Failed to query ALSA HW parameters\n");298 return ret;299 }300 301 /* Refine HW parameters after the query. */302 changed = 0;303 304 sndif_formats = to_alsa_formats_mask(resp.formats);305 snd_mask_none(&mask);306 mask.bits[0] = (u32)sndif_formats;307 mask.bits[1] = (u32)(sndif_formats >> 32);308 ret = snd_mask_refine(formats, &mask);309 if (ret < 0)310 return ret;311 changed |= ret;312 313 interval.openmin = 0;314 interval.openmax = 0;315 interval.integer = 1;316 317 interval.min = resp.rates.min;318 interval.max = resp.rates.max;319 ret = snd_interval_refine(rates, &interval);320 if (ret < 0)321 return ret;322 changed |= ret;323 324 interval.min = resp.channels.min;325 interval.max = resp.channels.max;326 ret = snd_interval_refine(channels, &interval);327 if (ret < 0)328 return ret;329 changed |= ret;330 331 interval.min = resp.buffer.min;332 interval.max = resp.buffer.max;333 ret = snd_interval_refine(buffer, &interval);334 if (ret < 0)335 return ret;336 changed |= ret;337 338 interval.min = resp.period.min;339 interval.max = resp.period.max;340 ret = snd_interval_refine(period, &interval);341 if (ret < 0)342 return ret;343 changed |= ret;344 345 return changed;346}347 348static int alsa_open(struct snd_pcm_substream *substream)349{350 struct xen_snd_front_pcm_instance_info *pcm_instance =351 snd_pcm_substream_chip(substream);352 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);353 struct snd_pcm_runtime *runtime = substream->runtime;354 struct xen_snd_front_info *front_info =355 pcm_instance->card_info->front_info;356 struct device *dev = &front_info->xb_dev->dev;357 int ret;358 359 /*360 * Return our HW properties: override defaults with those configured361 * via XenStore.362 */363 runtime->hw = stream->pcm_hw;364 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |365 SNDRV_PCM_INFO_MMAP_VALID |366 SNDRV_PCM_INFO_DOUBLE |367 SNDRV_PCM_INFO_BATCH |368 SNDRV_PCM_INFO_NONINTERLEAVED |369 SNDRV_PCM_INFO_RESUME |370 SNDRV_PCM_INFO_PAUSE);371 runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;372 373 stream->evt_pair = &front_info->evt_pairs[stream->index];374 375 stream->front_info = front_info;376 377 stream->evt_pair->evt.u.evt.substream = substream;378 379 stream_clear(stream);380 381 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);382 383 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,384 alsa_hw_rule, stream,385 SNDRV_PCM_HW_PARAM_FORMAT, -1);386 if (ret) {387 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");388 return ret;389 }390 391 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,392 alsa_hw_rule, stream,393 SNDRV_PCM_HW_PARAM_RATE, -1);394 if (ret) {395 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");396 return ret;397 }398 399 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,400 alsa_hw_rule, stream,401 SNDRV_PCM_HW_PARAM_CHANNELS, -1);402 if (ret) {403 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");404 return ret;405 }406 407 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,408 alsa_hw_rule, stream,409 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);410 if (ret) {411 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");412 return ret;413 }414 415 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,416 alsa_hw_rule, stream,417 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);418 if (ret) {419 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");420 return ret;421 }422 423 return 0;424}425 426static int alsa_close(struct snd_pcm_substream *substream)427{428 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);429 430 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);431 return 0;432}433 434static int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream,435 size_t buffer_sz)436{437 int i;438 439 stream->buffer = alloc_pages_exact(buffer_sz, GFP_KERNEL);440 if (!stream->buffer)441 return -ENOMEM;442 443 stream->buffer_sz = buffer_sz;444 stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE);445 stream->pages = kcalloc(stream->num_pages, sizeof(struct page *),446 GFP_KERNEL);447 if (!stream->pages)448 return -ENOMEM;449 450 for (i = 0; i < stream->num_pages; i++)451 stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE);452 453 return 0;454}455 456static int alsa_hw_params(struct snd_pcm_substream *substream,457 struct snd_pcm_hw_params *params)458{459 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);460 struct xen_snd_front_info *front_info = stream->front_info;461 struct xen_front_pgdir_shbuf_cfg buf_cfg;462 int ret;463 464 /*465 * This callback may be called multiple times,466 * so free the previously allocated shared buffer if any.467 */468 stream_free(stream);469 ret = shbuf_setup_backstore(stream, params_buffer_bytes(params));470 if (ret < 0)471 goto fail;472 473 memset(&buf_cfg, 0, sizeof(buf_cfg));474 buf_cfg.xb_dev = front_info->xb_dev;475 buf_cfg.pgdir = &stream->shbuf;476 buf_cfg.num_pages = stream->num_pages;477 buf_cfg.pages = stream->pages;478 479 ret = xen_front_pgdir_shbuf_alloc(&buf_cfg);480 if (ret < 0)481 goto fail;482 483 ret = xen_front_pgdir_shbuf_map(&stream->shbuf);484 if (ret < 0)485 goto fail;486 487 return 0;488 489fail:490 stream_free(stream);491 dev_err(&front_info->xb_dev->dev,492 "Failed to allocate buffers for stream with index %d\n",493 stream->index);494 return ret;495}496 497static int alsa_hw_free(struct snd_pcm_substream *substream)498{499 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);500 int ret;501 502 ret = xen_snd_front_stream_close(&stream->evt_pair->req);503 stream_free(stream);504 return ret;505}506 507static int alsa_prepare(struct snd_pcm_substream *substream)508{509 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);510 511 if (!stream->is_open) {512 struct snd_pcm_runtime *runtime = substream->runtime;513 u8 sndif_format;514 int ret;515 516 ret = to_sndif_format(runtime->format);517 if (ret < 0) {518 dev_err(&stream->front_info->xb_dev->dev,519 "Unsupported sample format: %d\n",520 runtime->format);521 return ret;522 }523 sndif_format = ret;524 525 ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,526 &stream->shbuf,527 sndif_format,528 runtime->channels,529 runtime->rate,530 snd_pcm_lib_buffer_bytes(substream),531 snd_pcm_lib_period_bytes(substream));532 if (ret < 0)533 return ret;534 535 stream->is_open = true;536 }537 538 return 0;539}540 541static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)542{543 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);544 int type;545 546 switch (cmd) {547 case SNDRV_PCM_TRIGGER_START:548 type = XENSND_OP_TRIGGER_START;549 break;550 551 case SNDRV_PCM_TRIGGER_RESUME:552 type = XENSND_OP_TRIGGER_RESUME;553 break;554 555 case SNDRV_PCM_TRIGGER_STOP:556 type = XENSND_OP_TRIGGER_STOP;557 break;558 559 case SNDRV_PCM_TRIGGER_SUSPEND:560 type = XENSND_OP_TRIGGER_PAUSE;561 break;562 563 default:564 return -EINVAL;565 }566 567 return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);568}569 570void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,571 u64 pos_bytes)572{573 struct snd_pcm_substream *substream = evtchnl->u.evt.substream;574 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);575 snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;576 577 cur_frame = bytes_to_frames(substream->runtime, pos_bytes);578 579 delta = cur_frame - stream->be_cur_frame;580 stream->be_cur_frame = cur_frame;581 582 new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);583 new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;584 atomic_set(&stream->hw_ptr, (int)new_hw_ptr);585 586 stream->out_frames += delta;587 if (stream->out_frames > substream->runtime->period_size) {588 stream->out_frames %= substream->runtime->period_size;589 snd_pcm_period_elapsed(substream);590 }591}592 593static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)594{595 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);596 597 return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);598}599 600static int alsa_pb_copy(struct snd_pcm_substream *substream,601 int channel, unsigned long pos, struct iov_iter *src,602 unsigned long count)603{604 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);605 606 if (unlikely(pos + count > stream->buffer_sz))607 return -EINVAL;608 609 if (copy_from_iter(stream->buffer + pos, count, src) != count)610 return -EFAULT;611 612 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);613}614 615static int alsa_cap_copy(struct snd_pcm_substream *substream,616 int channel, unsigned long pos, struct iov_iter *dst,617 unsigned long count)618{619 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);620 int ret;621 622 if (unlikely(pos + count > stream->buffer_sz))623 return -EINVAL;624 625 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);626 if (ret < 0)627 return ret;628 629 if (copy_to_iter(stream->buffer + pos, count, dst) != count)630 return -EFAULT;631 return 0;632}633 634static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,635 int channel, unsigned long pos,636 unsigned long count)637{638 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);639 640 if (unlikely(pos + count > stream->buffer_sz))641 return -EINVAL;642 643 memset(stream->buffer + pos, 0, count);644 645 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);646}647 648/*649 * FIXME: The mmaped data transfer is asynchronous and there is no650 * ack signal from user-space when it is done. This is the651 * reason it is not implemented in the PV driver as we do need652 * to know when the buffer can be transferred to the backend.653 */654 655static const struct snd_pcm_ops snd_drv_alsa_playback_ops = {656 .open = alsa_open,657 .close = alsa_close,658 .hw_params = alsa_hw_params,659 .hw_free = alsa_hw_free,660 .prepare = alsa_prepare,661 .trigger = alsa_trigger,662 .pointer = alsa_pointer,663 .copy = alsa_pb_copy,664 .fill_silence = alsa_pb_fill_silence,665};666 667static const struct snd_pcm_ops snd_drv_alsa_capture_ops = {668 .open = alsa_open,669 .close = alsa_close,670 .hw_params = alsa_hw_params,671 .hw_free = alsa_hw_free,672 .prepare = alsa_prepare,673 .trigger = alsa_trigger,674 .pointer = alsa_pointer,675 .copy = alsa_cap_copy,676};677 678static int new_pcm_instance(struct xen_snd_front_card_info *card_info,679 struct xen_front_cfg_pcm_instance *instance_cfg,680 struct xen_snd_front_pcm_instance_info *pcm_instance_info)681{682 struct snd_pcm *pcm;683 int ret, i;684 685 dev_dbg(&card_info->front_info->xb_dev->dev,686 "New PCM device \"%s\" with id %d playback %d capture %d",687 instance_cfg->name,688 instance_cfg->device_id,689 instance_cfg->num_streams_pb,690 instance_cfg->num_streams_cap);691 692 pcm_instance_info->card_info = card_info;693 694 pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;695 696 if (instance_cfg->num_streams_pb) {697 pcm_instance_info->streams_pb =698 devm_kcalloc(&card_info->card->card_dev,699 instance_cfg->num_streams_pb,700 sizeof(struct xen_snd_front_pcm_stream_info),701 GFP_KERNEL);702 if (!pcm_instance_info->streams_pb)703 return -ENOMEM;704 }705 706 if (instance_cfg->num_streams_cap) {707 pcm_instance_info->streams_cap =708 devm_kcalloc(&card_info->card->card_dev,709 instance_cfg->num_streams_cap,710 sizeof(struct xen_snd_front_pcm_stream_info),711 GFP_KERNEL);712 if (!pcm_instance_info->streams_cap)713 return -ENOMEM;714 }715 716 pcm_instance_info->num_pcm_streams_pb =717 instance_cfg->num_streams_pb;718 pcm_instance_info->num_pcm_streams_cap =719 instance_cfg->num_streams_cap;720 721 for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {722 pcm_instance_info->streams_pb[i].pcm_hw =723 instance_cfg->streams_pb[i].pcm_hw;724 pcm_instance_info->streams_pb[i].index =725 instance_cfg->streams_pb[i].index;726 }727 728 for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {729 pcm_instance_info->streams_cap[i].pcm_hw =730 instance_cfg->streams_cap[i].pcm_hw;731 pcm_instance_info->streams_cap[i].index =732 instance_cfg->streams_cap[i].index;733 }734 735 ret = snd_pcm_new(card_info->card, instance_cfg->name,736 instance_cfg->device_id,737 instance_cfg->num_streams_pb,738 instance_cfg->num_streams_cap,739 &pcm);740 if (ret < 0)741 return ret;742 743 pcm->private_data = pcm_instance_info;744 pcm->info_flags = 0;745 /* we want to handle all PCM operations in non-atomic context */746 pcm->nonatomic = true;747 strscpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));748 749 if (instance_cfg->num_streams_pb)750 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,751 &snd_drv_alsa_playback_ops);752 753 if (instance_cfg->num_streams_cap)754 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,755 &snd_drv_alsa_capture_ops);756 757 pcm_instance_info->pcm = pcm;758 return 0;759}760 761int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)762{763 struct device *dev = &front_info->xb_dev->dev;764 struct xen_front_cfg_card *cfg = &front_info->cfg;765 struct xen_snd_front_card_info *card_info;766 struct snd_card *card;767 int ret, i;768 769 dev_dbg(dev, "Creating virtual sound card\n");770 771 ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,772 sizeof(struct xen_snd_front_card_info), &card);773 if (ret < 0)774 return ret;775 776 card_info = card->private_data;777 card_info->front_info = front_info;778 front_info->card_info = card_info;779 card_info->card = card;780 card_info->pcm_instances =781 devm_kcalloc(dev, cfg->num_pcm_instances,782 sizeof(struct xen_snd_front_pcm_instance_info),783 GFP_KERNEL);784 if (!card_info->pcm_instances) {785 ret = -ENOMEM;786 goto fail;787 }788 789 card_info->num_pcm_instances = cfg->num_pcm_instances;790 card_info->pcm_hw = cfg->pcm_hw;791 792 for (i = 0; i < cfg->num_pcm_instances; i++) {793 ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],794 &card_info->pcm_instances[i]);795 if (ret < 0)796 goto fail;797 }798 799 strscpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));800 strscpy(card->shortname, cfg->name_short, sizeof(card->shortname));801 strscpy(card->longname, cfg->name_long, sizeof(card->longname));802 803 ret = snd_card_register(card);804 if (ret < 0)805 goto fail;806 807 return 0;808 809fail:810 snd_card_free(card);811 return ret;812}813 814void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)815{816 struct xen_snd_front_card_info *card_info;817 struct snd_card *card;818 819 card_info = front_info->card_info;820 if (!card_info)821 return;822 823 card = card_info->card;824 if (!card)825 return;826 827 dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",828 card->number);829 snd_card_free(card);830 831 /* Card_info will be freed when destroying front_info->xb_dev->dev. */832 card_info->card = NULL;833}834