ALSA: xen: Use standard pcm_format_to_bits() for ALSA format bits
[linux-block.git] / sound / xen / xen_snd_front_alsa.c
CommitLineData
1cee5593
OA
1// SPDX-License-Identifier: GPL-2.0 OR MIT
2
3/*
4 * Xen para-virtual sound device
5 *
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
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"
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 struct xen_snd_front_shbuf sh_buf;
29 int index;
30
31 bool is_open;
32 struct snd_pcm_hardware pcm_hw;
33
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. */
37 atomic_t hw_ptr;
38 /* Modulo of the number of processed frames - for period detection. */
39 u32 out_frames;
40};
41
42struct xen_snd_front_pcm_instance_info {
43 struct xen_snd_front_card_info *card_info;
44 struct snd_pcm *pcm;
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;
50};
51
52struct 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;
58};
59
60struct alsa_sndif_sample_format {
61 u8 sndif;
62 snd_pcm_format_t alsa;
63};
64
65struct alsa_sndif_hw_param {
66 u8 sndif;
67 snd_pcm_hw_param_t alsa;
68};
69
70static const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
71 {
72 .sndif = XENSND_PCM_FORMAT_U8,
73 .alsa = SNDRV_PCM_FORMAT_U8
74 },
75 {
76 .sndif = XENSND_PCM_FORMAT_S8,
77 .alsa = SNDRV_PCM_FORMAT_S8
78 },
79 {
80 .sndif = XENSND_PCM_FORMAT_U16_LE,
81 .alsa = SNDRV_PCM_FORMAT_U16_LE
82 },
83 {
84 .sndif = XENSND_PCM_FORMAT_U16_BE,
85 .alsa = SNDRV_PCM_FORMAT_U16_BE
86 },
87 {
88 .sndif = XENSND_PCM_FORMAT_S16_LE,
89 .alsa = SNDRV_PCM_FORMAT_S16_LE
90 },
91 {
92 .sndif = XENSND_PCM_FORMAT_S16_BE,
93 .alsa = SNDRV_PCM_FORMAT_S16_BE
94 },
95 {
96 .sndif = XENSND_PCM_FORMAT_U24_LE,
97 .alsa = SNDRV_PCM_FORMAT_U24_LE
98 },
99 {
100 .sndif = XENSND_PCM_FORMAT_U24_BE,
101 .alsa = SNDRV_PCM_FORMAT_U24_BE
102 },
103 {
104 .sndif = XENSND_PCM_FORMAT_S24_LE,
105 .alsa = SNDRV_PCM_FORMAT_S24_LE
106 },
107 {
108 .sndif = XENSND_PCM_FORMAT_S24_BE,
109 .alsa = SNDRV_PCM_FORMAT_S24_BE
110 },
111 {
112 .sndif = XENSND_PCM_FORMAT_U32_LE,
113 .alsa = SNDRV_PCM_FORMAT_U32_LE
114 },
115 {
116 .sndif = XENSND_PCM_FORMAT_U32_BE,
117 .alsa = SNDRV_PCM_FORMAT_U32_BE
118 },
119 {
120 .sndif = XENSND_PCM_FORMAT_S32_LE,
121 .alsa = SNDRV_PCM_FORMAT_S32_LE
122 },
123 {
124 .sndif = XENSND_PCM_FORMAT_S32_BE,
125 .alsa = SNDRV_PCM_FORMAT_S32_BE
126 },
127 {
128 .sndif = XENSND_PCM_FORMAT_A_LAW,
129 .alsa = SNDRV_PCM_FORMAT_A_LAW
130 },
131 {
132 .sndif = XENSND_PCM_FORMAT_MU_LAW,
133 .alsa = SNDRV_PCM_FORMAT_MU_LAW
134 },
135 {
136 .sndif = XENSND_PCM_FORMAT_F32_LE,
137 .alsa = SNDRV_PCM_FORMAT_FLOAT_LE
138 },
139 {
140 .sndif = XENSND_PCM_FORMAT_F32_BE,
141 .alsa = SNDRV_PCM_FORMAT_FLOAT_BE
142 },
143 {
144 .sndif = XENSND_PCM_FORMAT_F64_LE,
145 .alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
146 },
147 {
148 .sndif = XENSND_PCM_FORMAT_F64_BE,
149 .alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
150 },
151 {
152 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
153 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
154 },
155 {
156 .sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
157 .alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
158 },
159 {
160 .sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
161 .alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
162 },
163 {
164 .sndif = XENSND_PCM_FORMAT_MPEG,
165 .alsa = SNDRV_PCM_FORMAT_MPEG
166 },
167 {
168 .sndif = XENSND_PCM_FORMAT_GSM,
169 .alsa = SNDRV_PCM_FORMAT_GSM
170 },
171};
172
173static int to_sndif_format(snd_pcm_format_t format)
174{
175 int i;
176
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;
180
181 return -EINVAL;
182}
183
184static u64 to_sndif_formats_mask(u64 alsa_formats)
185{
186 u64 mask;
187 int i;
188
189 mask = 0;
190 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
3ac14b39 191 if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
1cee5593
OA
192 mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
193
194 return mask;
195}
196
197static u64 to_alsa_formats_mask(u64 sndif_formats)
198{
199 u64 mask;
200 int i;
201
202 mask = 0;
203 for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
204 if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
3ac14b39 205 mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
1cee5593
OA
206
207 return mask;
208}
209
210static void stream_clear(struct xen_snd_front_pcm_stream_info *stream)
211{
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);
218}
219
220static void stream_free(struct xen_snd_front_pcm_stream_info *stream)
221{
222 xen_snd_front_shbuf_free(&stream->sh_buf);
223 stream_clear(stream);
224}
225
226static struct xen_snd_front_pcm_stream_info *
227stream_get(struct snd_pcm_substream *substream)
228{
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;
232
233 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
234 stream = &pcm_instance->streams_pb[substream->number];
235 else
236 stream = &pcm_instance->streams_cap[substream->number];
237
238 return stream;
239}
240
241static int alsa_hw_rule(struct snd_pcm_hw_params *params,
242 struct snd_pcm_hw_rule *rule)
243{
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;
262 u64 sndif_formats;
263 int changed, ret;
264
265 /* Collect all the values we need for the query. */
266
267 req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
268 (u64)(formats->bits[1]) << 32);
269
270 req.rates.min = rates->min;
271 req.rates.max = rates->max;
272
273 req.channels.min = channels->min;
274 req.channels.max = channels->max;
275
276 req.buffer.min = buffer->min;
277 req.buffer.max = buffer->max;
278
279 req.period.min = period->min;
280 req.period.max = period->max;
281
282 ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
283 &req, &resp);
284 if (ret < 0) {
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");
288 return ret;
289 }
290
291 /* Refine HW parameters after the query. */
292 changed = 0;
293
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);
299 if (ret < 0)
300 return ret;
301 changed |= ret;
302
303 interval.openmin = 0;
304 interval.openmax = 0;
305 interval.integer = 1;
306
307 interval.min = resp.rates.min;
308 interval.max = resp.rates.max;
309 ret = snd_interval_refine(rates, &interval);
310 if (ret < 0)
311 return ret;
312 changed |= ret;
313
314 interval.min = resp.channels.min;
315 interval.max = resp.channels.max;
316 ret = snd_interval_refine(channels, &interval);
317 if (ret < 0)
318 return ret;
319 changed |= ret;
320
321 interval.min = resp.buffer.min;
322 interval.max = resp.buffer.max;
323 ret = snd_interval_refine(buffer, &interval);
324 if (ret < 0)
325 return ret;
326 changed |= ret;
327
328 interval.min = resp.period.min;
329 interval.max = resp.period.max;
330 ret = snd_interval_refine(period, &interval);
331 if (ret < 0)
332 return ret;
333 changed |= ret;
334
335 return changed;
336}
337
338static int alsa_open(struct snd_pcm_substream *substream)
339{
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;
347 int ret;
348
349 /*
350 * Return our HW properties: override defaults with those configured
351 * via XenStore.
352 */
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;
362
363 stream->evt_pair = &front_info->evt_pairs[stream->index];
364
365 stream->front_info = front_info;
366
367 stream->evt_pair->evt.u.evt.substream = substream;
368
369 stream_clear(stream);
370
371 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
372
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);
376 if (ret) {
377 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
378 return ret;
379 }
380
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);
384 if (ret) {
385 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
386 return ret;
387 }
388
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);
392 if (ret) {
393 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
394 return ret;
395 }
396
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);
400 if (ret) {
401 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
402 return ret;
403 }
404
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);
408 if (ret) {
409 dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
410 return ret;
411 }
412
413 return 0;
414}
415
416static int alsa_close(struct snd_pcm_substream *substream)
417{
418 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
419
420 xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
421 return 0;
422}
423
424static int alsa_hw_params(struct snd_pcm_substream *substream,
425 struct snd_pcm_hw_params *params)
426{
427 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
428 int ret;
429
430 /*
431 * This callback may be called multiple times,
432 * so free the previously allocated shared buffer if any.
433 */
434 stream_free(stream);
435
436 ret = xen_snd_front_shbuf_alloc(stream->front_info->xb_dev,
437 &stream->sh_buf,
438 params_buffer_bytes(params));
439 if (ret < 0) {
440 stream_free(stream);
441 dev_err(&stream->front_info->xb_dev->dev,
442 "Failed to allocate buffers for stream with index %d\n",
443 stream->index);
444 return ret;
445 }
446
447 return 0;
448}
449
450static int alsa_hw_free(struct snd_pcm_substream *substream)
451{
452 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
453 int ret;
454
455 ret = xen_snd_front_stream_close(&stream->evt_pair->req);
456 stream_free(stream);
457 return ret;
458}
459
460static int alsa_prepare(struct snd_pcm_substream *substream)
461{
462 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
463
464 if (!stream->is_open) {
465 struct snd_pcm_runtime *runtime = substream->runtime;
466 u8 sndif_format;
467 int ret;
468
014cea59
CIK
469 ret = to_sndif_format(runtime->format);
470 if (ret < 0) {
1cee5593
OA
471 dev_err(&stream->front_info->xb_dev->dev,
472 "Unsupported sample format: %d\n",
473 runtime->format);
014cea59 474 return ret;
1cee5593 475 }
014cea59 476 sndif_format = ret;
1cee5593
OA
477
478 ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
479 &stream->sh_buf,
480 sndif_format,
481 runtime->channels,
482 runtime->rate,
483 snd_pcm_lib_buffer_bytes(substream),
484 snd_pcm_lib_period_bytes(substream));
485 if (ret < 0)
486 return ret;
487
488 stream->is_open = true;
489 }
490
491 return 0;
492}
493
494static int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
495{
496 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
497 int type;
498
499 switch (cmd) {
500 case SNDRV_PCM_TRIGGER_START:
501 type = XENSND_OP_TRIGGER_START;
502 break;
503
504 case SNDRV_PCM_TRIGGER_RESUME:
505 type = XENSND_OP_TRIGGER_RESUME;
506 break;
507
508 case SNDRV_PCM_TRIGGER_STOP:
509 type = XENSND_OP_TRIGGER_STOP;
510 break;
511
512 case SNDRV_PCM_TRIGGER_SUSPEND:
513 type = XENSND_OP_TRIGGER_PAUSE;
514 break;
515
516 default:
517 return -EINVAL;
518 }
519
520 return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
521}
522
523void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
524 u64 pos_bytes)
525{
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;
529
530 cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
531
532 delta = cur_frame - stream->be_cur_frame;
533 stream->be_cur_frame = cur_frame;
534
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);
538
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);
543 }
544}
545
546static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
547{
548 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
549
550 return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
551}
552
553static int alsa_pb_copy_user(struct snd_pcm_substream *substream,
554 int channel, unsigned long pos, void __user *src,
555 unsigned long count)
556{
557 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
558
559 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
560 return -EINVAL;
561
562 if (copy_from_user(stream->sh_buf.buffer + pos, src, count))
563 return -EFAULT;
564
565 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
566}
567
568static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
569 int channel, unsigned long pos, void *src,
570 unsigned long count)
571{
572 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
573
574 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
575 return -EINVAL;
576
577 memcpy(stream->sh_buf.buffer + pos, src, count);
578
579 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
580}
581
582static int alsa_cap_copy_user(struct snd_pcm_substream *substream,
583 int channel, unsigned long pos, void __user *dst,
584 unsigned long count)
585{
586 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
587 int ret;
588
589 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
590 return -EINVAL;
591
592 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
593 if (ret < 0)
594 return ret;
595
596 return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ?
597 -EFAULT : 0;
598}
599
600static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
601 int channel, unsigned long pos, void *dst,
602 unsigned long count)
603{
604 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
605 int ret;
606
607 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
608 return -EINVAL;
609
610 ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
611 if (ret < 0)
612 return ret;
613
614 memcpy(dst, stream->sh_buf.buffer + pos, count);
615
616 return 0;
617}
618
619static int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
620 int channel, unsigned long pos,
621 unsigned long count)
622{
623 struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
624
625 if (unlikely(pos + count > stream->sh_buf.buffer_sz))
626 return -EINVAL;
627
628 memset(stream->sh_buf.buffer + pos, 0, count);
629
630 return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
631}
632
633/*
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.
638 */
639
640static struct snd_pcm_ops snd_drv_alsa_playback_ops = {
641 .open = alsa_open,
642 .close = alsa_close,
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,
652};
653
654static struct snd_pcm_ops snd_drv_alsa_capture_ops = {
655 .open = alsa_open,
656 .close = alsa_close,
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,
665};
666
667static 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)
670{
671 struct snd_pcm *pcm;
672 int ret, i;
673
674 dev_dbg(&card_info->front_info->xb_dev->dev,
675 "New PCM device \"%s\" with id %d playback %d capture %d",
676 instance_cfg->name,
677 instance_cfg->device_id,
678 instance_cfg->num_streams_pb,
679 instance_cfg->num_streams_cap);
680
681 pcm_instance_info->card_info = card_info;
682
683 pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
684
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),
690 GFP_KERNEL);
691 if (!pcm_instance_info->streams_pb)
692 return -ENOMEM;
693 }
694
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),
700 GFP_KERNEL);
701 if (!pcm_instance_info->streams_cap)
702 return -ENOMEM;
703 }
704
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;
709
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;
715 }
716
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;
722 }
723
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,
728 &pcm);
729 if (ret < 0)
730 return ret;
731
732 pcm->private_data = pcm_instance_info;
733 pcm->info_flags = 0;
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));
737
738 if (instance_cfg->num_streams_pb)
739 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
740 &snd_drv_alsa_playback_ops);
741
742 if (instance_cfg->num_streams_cap)
743 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
744 &snd_drv_alsa_capture_ops);
745
746 pcm_instance_info->pcm = pcm;
747 return 0;
748}
749
750int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
751{
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;
756 int ret, i;
757
758 dev_dbg(dev, "Creating virtual sound card\n");
759
760 ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
761 sizeof(struct xen_snd_front_card_info), &card);
762 if (ret < 0)
763 return ret;
764
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),
772 GFP_KERNEL);
773 if (!card_info->pcm_instances) {
774 ret = -ENOMEM;
775 goto fail;
776 }
777
778 card_info->num_pcm_instances = cfg->num_pcm_instances;
779 card_info->pcm_hw = cfg->pcm_hw;
780
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]);
784 if (ret < 0)
785 goto fail;
786 }
787
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));
791
792 ret = snd_card_register(card);
793 if (ret < 0)
794 goto fail;
795
796 return 0;
797
798fail:
799 snd_card_free(card);
800 return ret;
801}
802
803void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
804{
805 struct xen_snd_front_card_info *card_info;
806 struct snd_card *card;
807
808 card_info = front_info->card_info;
809 if (!card_info)
810 return;
811
812 card = card_info->card;
813 if (!card)
814 return;
815
816 dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
817 card->number);
818 snd_card_free(card);
819
820 /* Card_info will be freed when destroying front_info->xb_dev->dev. */
821 card_info->card = NULL;
822}