1 // SPDX-License-Identifier: GPL-2.0 OR MIT
4 * Xen para-virtual sound device
6 * Copyright (C) 2016-2018 EPAM Systems Inc.
8 * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
11 #include <linux/platform_device.h>
13 #include <sound/core.h>
14 #include <sound/pcm.h>
15 #include <sound/pcm_params.h>
17 #include <xen/xenbus.h>
19 #include "xen_snd_front.h"
20 #include "xen_snd_front_alsa.h"
21 #include "xen_snd_front_cfg.h"
22 #include "xen_snd_front_evtchnl.h"
23 #include "xen_snd_front_shbuf.h"
25 struct xen_snd_front_pcm_stream_info {
26 struct xen_snd_front_info *front_info;
27 struct xen_snd_front_evtchnl_pair *evt_pair;
28 struct xen_snd_front_shbuf sh_buf;
32 struct snd_pcm_hardware pcm_hw;
34 /* Number of processed frames as reported by the backend. */
35 snd_pcm_uframes_t be_cur_frame;
36 /* Current HW pointer to be reported via .period callback. */
38 /* Modulo of the number of processed frames - for period detection. */
42 struct xen_snd_front_pcm_instance_info {
43 struct xen_snd_front_card_info *card_info;
45 struct snd_pcm_hardware pcm_hw;
46 int num_pcm_streams_pb;
47 struct xen_snd_front_pcm_stream_info *streams_pb;
48 int num_pcm_streams_cap;
49 struct xen_snd_front_pcm_stream_info *streams_cap;
52 struct xen_snd_front_card_info {
53 struct xen_snd_front_info *front_info;
54 struct snd_card *card;
55 struct snd_pcm_hardware pcm_hw;
56 int num_pcm_instances;
57 struct xen_snd_front_pcm_instance_info *pcm_instances;
60 struct alsa_sndif_sample_format {
62 snd_pcm_format_t alsa;
65 struct alsa_sndif_hw_param {
67 snd_pcm_hw_param_t alsa;
70 static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
72 .sndif = XENSND_PCM_FORMAT_U8,
73 .alsa = SNDRV_PCM_FORMAT_U8
76 .sndif = XENSND_PCM_FORMAT_S8,
77 .alsa = SNDRV_PCM_FORMAT_S8
80 .sndif = XENSND_PCM_FORMAT_U16_LE,
81 .alsa = SNDRV_PCM_FORMAT_U16_LE
84 .sndif = XENSND_PCM_FORMAT_U16_BE,
85 .alsa = SNDRV_PCM_FORMAT_U16_BE
88 .sndif = XENSND_PCM_FORMAT_S16_LE,
89 .alsa = SNDRV_PCM_FORMAT_S16_LE
92 .sndif = XENSND_PCM_FORMAT_S16_BE,
93 .alsa = SNDRV_PCM_FORMAT_S16_BE
96 .sndif = XENSND_PCM_FORMAT_U24_LE,
97 .alsa = SNDRV_PCM_FORMAT_U24_LE
100 .sndif = XENSND_PCM_FORMAT_U24_BE,
101 .alsa = SNDRV_PCM_FORMAT_U24_BE
104 .sndif = XENSND_PCM_FORMAT_S24_LE,
105 .alsa = SNDRV_PCM_FORMAT_S24_LE
108 .sndif = XENSND_PCM_FORMAT_S24_BE,
109 .alsa = SNDRV_PCM_FORMAT_S24_BE
112 .sndif = XENSND_PCM_FORMAT_U32_LE,
113 .alsa = SNDRV_PCM_FORMAT_U32_LE
116 .sndif = XENSND_PCM_FORMAT_U32_BE,
117 .alsa = SNDRV_PCM_FORMAT_U32_BE
120 .sndif = XENSND_PCM_FORMAT_S32_LE,
121 .alsa = SNDRV_PCM_FORMAT_S32_LE
124 .sndif = XENSND_PCM_FORMAT_S32_BE,
125 .alsa = SNDRV_PCM_FORMAT_S32_BE
128 .sndif = XENSND_PCM_FORMAT_A_LAW,
129 .alsa = SNDRV_PCM_FORMAT_A_LAW
132 .sndif = XENSND_PCM_FORMAT_MU_LAW,
133 .alsa = SNDRV_PCM_FORMAT_MU_LAW
136 .sndif = XENSND_PCM_FORMAT_F32_LE,
137 .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
140 .sndif = XENSND_PCM_FORMAT_F32_BE,
141 .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
144 .sndif = XENSND_PCM_FORMAT_F64_LE,
145 .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
148 .sndif = XENSND_PCM_FORMAT_F64_BE,
149 .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
152 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
153 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
156 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
157 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
160 .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
161 .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
164 .sndif = XENSND_PCM_FORMAT_MPEG,
165 .alsa = SNDRV_PCM_FORMAT_MPEG
168 .sndif = XENSND_PCM_FORMAT_GSM,
169 .alsa = SNDRV_PCM_FORMAT_GSM
173 static int to_sndif_format(snd_pcm_format_t format)
177 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
178 if (ALSA_SNDIF_FORMATS[i].alsa == format)
179 return ALSA_SNDIF_FORMATS[i].sndif;
184 static u64 to_sndif_formats_mask(u64 alsa_formats)
190 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
191 if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
192 mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
197 static u64 to_alsa_formats_mask(u64 sndif_formats)
203 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
204 if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
205 mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
210 static void stream_clear(struct xen_snd_front_pcm_stream_info *stream)
212 stream->is_open = false;
213 stream->be_cur_frame = 0;
214 stream->out_frames = 0;
215 atomic_set(&stream->hw_ptr, 0);
216 xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
217 xen_snd_front_shbuf_clear(&stream->sh_buf);
220 static void stream_free(struct xen_snd_front_pcm_stream_info *stream)
222 xen_snd_front_shbuf_free(&stream->sh_buf);
223 stream_clear(stream);
226 static struct xen_snd_front_pcm_stream_info *
227 stream_get(struct snd_pcm_substream *substream)
229 struct xen_snd_front_pcm_instance_info *pcm_instance =
230 snd_pcm_substream_chip(substream);
231 struct xen_snd_front_pcm_stream_info *stream;
233 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
234 stream = &pcm_instance->streams_pb[substream->number];
236 stream = &pcm_instance->streams_cap[substream->number];
241 static int alsa_hw_rule(struct snd_pcm_hw_params *params,
242 struct snd_pcm_hw_rule *rule)
244 struct xen_snd_front_pcm_stream_info *stream = rule->private;
245 struct device *dev = &stream->front_info->xb_dev->dev;
246 struct snd_mask *formats =
247 hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
248 struct snd_interval *rates =
249 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
250 struct snd_interval *channels =
251 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
252 struct snd_interval *period =
253 hw_param_interval(params,
254 SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
255 struct snd_interval *buffer =
256 hw_param_interval(params,
257 SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
258 struct xensnd_query_hw_param req;
259 struct xensnd_query_hw_param resp;
260 struct snd_interval interval;
261 struct snd_mask mask;
265 /* Collect all the values we need for the query. */
267 req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
268 (u64)(formats->bits[1]) << 32);
270 req.rates.min = rates->min;
271 req.rates.max = rates->max;
273 req.channels.min = channels->min;
274 req.channels.max = channels->max;
276 req.buffer.min = buffer->min;
277 req.buffer.max = buffer->max;
279 req.period.min = period->min;
280 req.period.max = period->max;
282 ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
285 /* Check if this is due to backend communication error. */
286 if (ret == -EIO || ret == -ETIMEDOUT)
287 dev_err(dev, "Failed to query ALSA HW parameters\n");
291 /* Refine HW parameters after the query. */
294 sndif_formats = to_alsa_formats_mask(resp.formats);
295 snd_mask_none(&mask);
296 mask.bits[0] = (u32)sndif_formats;
297 mask.bits[1] = (u32)(sndif_formats >> 32);
298 ret = snd_mask_refine(formats, &mask);
303 interval.openmin = 0;
304 interval.openmax = 0;
305 interval.integer = 1;
307 interval.min = resp.rates.min;
308 interval.max = resp.rates.max;
309 ret = snd_interval_refine(rates, &interval);
314 interval.min = resp.channels.min;
315 interval.max = resp.channels.max;
316 ret = snd_interval_refine(channels, &interval);
321 interval.min = resp.buffer.min;
322 interval.max = resp.buffer.max;
323 ret = snd_interval_refine(buffer, &interval);
328 interval.min = resp.period.min;
329 interval.max = resp.period.max;
330 ret = snd_interval_refine(period, &interval);
338 static int alsa_open(struct snd_pcm_substream *substream)
340 struct xen_snd_front_pcm_instance_info *pcm_instance =
341 snd_pcm_substream_chip(substream);
342 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
343 struct snd_pcm_runtime *runtime = substream->runtime;
344 struct xen_snd_front_info *front_info =
345 pcm_instance->card_info->front_info;
346 struct device *dev = &front_info->xb_dev->dev;
350 * Return our HW properties: override defaults with those configured
353 runtime->hw = stream->pcm_hw;
354 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
355 SNDRV_PCM_INFO_MMAP_VALID |
356 SNDRV_PCM_INFO_DOUBLE |
357 SNDRV_PCM_INFO_BATCH |
358 SNDRV_PCM_INFO_NONINTERLEAVED |
359 SNDRV_PCM_INFO_RESUME |
360 SNDRV_PCM_INFO_PAUSE);
361 runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
363 stream->evt_pair = &front_info->evt_pairs[stream->index];
365 stream->front_info = front_info;
367 stream->evt_pair->evt.u.evt.substream = substream;
369 stream_clear(stream);
371 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
373 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
374 alsa_hw_rule, stream,
375 SNDRV_PCM_HW_PARAM_FORMAT, -1);
377 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
381 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
382 alsa_hw_rule, stream,
383 SNDRV_PCM_HW_PARAM_RATE, -1);
385 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
389 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
390 alsa_hw_rule, stream,
391 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
393 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
397 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
398 alsa_hw_rule, stream,
399 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
401 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
405 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
406 alsa_hw_rule, stream,
407 SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
409 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
416 static int alsa_close(struct snd_pcm_substream *substream)
418 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
420 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
424 static int alsa_hw_params(struct snd_pcm_substream *substream,
425 struct snd_pcm_hw_params *params)
427 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
431 * This callback may be called multiple times,
432 * so free the previously allocated shared buffer if any.
436 ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
438 params_buffer_bytes(params));
441 dev_err(&stream->front_info->xb_dev->dev,
442 "Failed to allocate buffers for stream with index %d\n",
450 static int alsa_hw_free(struct snd_pcm_substream *substream)
452 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
455 ret = xen_snd_front_stream_close(&stream->evt_pair->req);
460 static int alsa_prepare(struct snd_pcm_substream *substream)
462 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
464 if (!stream->is_open) {
465 struct snd_pcm_runtime *runtime = substream->runtime;
469 ret = to_sndif_format(runtime->format);
471 dev_err(&stream->front_info->xb_dev->dev,
472 "Unsupported sample format: %d\n",
478 ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
483 snd_pcm_lib_buffer_bytes(substream),
484 snd_pcm_lib_period_bytes(substream));
488 stream->is_open = true;
494 static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
496 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
500 case SNDRV_PCM_TRIGGER_START:
501 type = XENSND_OP_TRIGGER_START;
504 case SNDRV_PCM_TRIGGER_RESUME:
505 type = XENSND_OP_TRIGGER_RESUME;
508 case SNDRV_PCM_TRIGGER_STOP:
509 type = XENSND_OP_TRIGGER_STOP;
512 case SNDRV_PCM_TRIGGER_SUSPEND:
513 type = XENSND_OP_TRIGGER_PAUSE;
520 return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
523 void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
526 struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
527 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
528 snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
530 cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
532 delta = cur_frame - stream->be_cur_frame;
533 stream->be_cur_frame = cur_frame;
535 new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
536 new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
537 atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
539 stream->out_frames += delta;
540 if (stream->out_frames > substream->runtime->period_size) {
541 stream->out_frames %= substream->runtime->period_size;
542 snd_pcm_period_elapsed(substream);
546 static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
548 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
550 return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
553 static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
554 int channel, unsigned long pos, void __user *src,
557 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
559 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
562 if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
565 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
568 static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
569 int channel, unsigned long pos, void *src,
572 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
574 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
577 memcpy(stream->sh_buf.buffer + pos, src, count);
579 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
582 static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
583 int channel, unsigned long pos, void __user *dst,
586 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
589 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
592 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
596 return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
600 static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
601 int channel, unsigned long pos, void *dst,
604 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
607 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
610 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
614 memcpy(dst, stream->sh_buf.buffer + pos, count);
619 static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
620 int channel, unsigned long pos,
623 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
625 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
628 memset(stream->sh_buf.buffer + pos, 0, count);
630 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
634 * FIXME: The mmaped data transfer is asynchronous and there is no
635 * ack signal from user-space when it is done. This is the
636 * reason it is not implemented in the PV driver as we do need
637 * to know when the buffer can be transferred to the backend.
640 static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
643 .ioctl = snd_pcm_lib_ioctl,
644 .hw_params = alsa_hw_params,
645 .hw_free = alsa_hw_free,
646 .prepare = alsa_prepare,
647 .trigger = alsa_trigger,
648 .pointer = alsa_pointer,
649 .copy_user = alsa_pb_copy_user,
650 .copy_kernel = alsa_pb_copy_kernel,
651 .fill_silence = alsa_pb_fill_silence,
654 static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
657 .ioctl = snd_pcm_lib_ioctl,
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_user = alsa_cap_copy_user,
664 .copy_kernel = alsa_cap_copy_kernel,
667 static int new_pcm_instance(struct xen_snd_front_card_info *card_info,
668 struct xen_front_cfg_pcm_instance *instance_cfg,
669 struct xen_snd_front_pcm_instance_info *pcm_instance_info)
674 dev_dbg(&card_info->front_info->xb_dev->dev,
675 "New PCM device \"%s\" with id %d playback %d capture %d",
677 instance_cfg->device_id,
678 instance_cfg->num_streams_pb,
679 instance_cfg->num_streams_cap);
681 pcm_instance_info->card_info = card_info;
683 pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
685 if (instance_cfg->num_streams_pb) {
686 pcm_instance_info->streams_pb =
687 devm_kcalloc(&card_info->card->card_dev,
688 instance_cfg->num_streams_pb,
689 sizeof(struct xen_snd_front_pcm_stream_info),
691 if (!pcm_instance_info->streams_pb)
695 if (instance_cfg->num_streams_cap) {
696 pcm_instance_info->streams_cap =
697 devm_kcalloc(&card_info->card->card_dev,
698 instance_cfg->num_streams_cap,
699 sizeof(struct xen_snd_front_pcm_stream_info),
701 if (!pcm_instance_info->streams_cap)
705 pcm_instance_info->num_pcm_streams_pb =
706 instance_cfg->num_streams_pb;
707 pcm_instance_info->num_pcm_streams_cap =
708 instance_cfg->num_streams_cap;
710 for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
711 pcm_instance_info->streams_pb[i].pcm_hw =
712 instance_cfg->streams_pb[i].pcm_hw;
713 pcm_instance_info->streams_pb[i].index =
714 instance_cfg->streams_pb[i].index;
717 for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
718 pcm_instance_info->streams_cap[i].pcm_hw =
719 instance_cfg->streams_cap[i].pcm_hw;
720 pcm_instance_info->streams_cap[i].index =
721 instance_cfg->streams_cap[i].index;
724 ret = snd_pcm_new(card_info->card, instance_cfg->name,
725 instance_cfg->device_id,
726 instance_cfg->num_streams_pb,
727 instance_cfg->num_streams_cap,
732 pcm->private_data = pcm_instance_info;
734 /* we want to handle all PCM operations in non-atomic context */
735 pcm->nonatomic = true;
736 strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
738 if (instance_cfg->num_streams_pb)
739 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
740 &snd_drv_alsa_playback_ops);
742 if (instance_cfg->num_streams_cap)
743 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
744 &snd_drv_alsa_capture_ops);
746 pcm_instance_info->pcm = pcm;
750 int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
752 struct device *dev = &front_info->xb_dev->dev;
753 struct xen_front_cfg_card *cfg = &front_info->cfg;
754 struct xen_snd_front_card_info *card_info;
755 struct snd_card *card;
758 dev_dbg(dev, "Creating virtual sound card\n");
760 ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
761 sizeof(struct xen_snd_front_card_info), &card);
765 card_info = card->private_data;
766 card_info->front_info = front_info;
767 front_info->card_info = card_info;
768 card_info->card = card;
769 card_info->pcm_instances =
770 devm_kcalloc(dev, cfg->num_pcm_instances,
771 sizeof(struct xen_snd_front_pcm_instance_info),
773 if (!card_info->pcm_instances) {
778 card_info->num_pcm_instances = cfg->num_pcm_instances;
779 card_info->pcm_hw = cfg->pcm_hw;
781 for (i = 0; i < cfg->num_pcm_instances; i++) {
782 ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
783 &card_info->pcm_instances[i]);
788 strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
789 strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
790 strncpy(card->longname, cfg->name_long, sizeof(card->longname));
792 ret = snd_card_register(card);
803 void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
805 struct xen_snd_front_card_info *card_info;
806 struct snd_card *card;
808 card_info = front_info->card_info;
812 card = card_info->card;
816 dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
820 /* Card_info will be freed when destroying front_info->xb_dev->dev. */
821 card_info->card = NULL;