Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: xen-front: Implement ALSA virtual sound driver

Implement essential initialization of the sound driver:
- introduce required data structures
- handle driver registration
- handle sound card registration
- register sound driver on backend connection
- remove sound driver on backend disconnect

Initialize virtual sound card with streams according to the
Xen store configuration.

Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states

Implement requests from front to back for ALSA
PCM operations.
- report ALSA period elapsed event: handle XENSND_EVT_CUR_POS
notifications from the backend when stream position advances
during playback/capture. The event carries a value of how
many octets were played/captured at the time of the event.
- implement explicit stream parameter negotiation between
backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request
to read/update configuration space for the parameter given:
request passes desired parameter interval and the response to
this request returns min/max interval for the parameter to be used.

Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Oleksandr Andrushchenko and committed by
Takashi Iwai
1cee5593 d6e0fbb8

+1056 -3
+2 -1
sound/xen/Makefile
··· 3 3 snd_xen_front-objs := xen_snd_front.o \ 4 4 xen_snd_front_cfg.o \ 5 5 xen_snd_front_evtchnl.o \ 6 - xen_snd_front_shbuf.o 6 + xen_snd_front_shbuf.o \ 7 + xen_snd_front_alsa.o 7 8 8 9 obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o
+180 -1
sound/xen/xen_snd_front.c
··· 19 19 #include <xen/interface/io/sndif.h> 20 20 21 21 #include "xen_snd_front.h" 22 + #include "xen_snd_front_alsa.h" 22 23 #include "xen_snd_front_evtchnl.h" 24 + #include "xen_snd_front_shbuf.h" 25 + 26 + static struct xensnd_req * 27 + be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) 28 + { 29 + struct xensnd_req *req; 30 + 31 + req = RING_GET_REQUEST(&evtchnl->u.req.ring, 32 + evtchnl->u.req.ring.req_prod_pvt); 33 + req->operation = operation; 34 + req->id = evtchnl->evt_next_id++; 35 + evtchnl->evt_id = req->id; 36 + return req; 37 + } 38 + 39 + static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl) 40 + { 41 + if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) 42 + return -EIO; 43 + 44 + reinit_completion(&evtchnl->u.req.completion); 45 + xen_snd_front_evtchnl_flush(evtchnl); 46 + return 0; 47 + } 48 + 49 + static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl) 50 + { 51 + if (wait_for_completion_timeout(&evtchnl->u.req.completion, 52 + msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0) 53 + return -ETIMEDOUT; 54 + 55 + return evtchnl->u.req.resp_status; 56 + } 57 + 58 + int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, 59 + struct xensnd_query_hw_param *hw_param_req, 60 + struct xensnd_query_hw_param *hw_param_resp) 61 + { 62 + struct xensnd_req *req; 63 + int ret; 64 + 65 + mutex_lock(&evtchnl->u.req.req_io_lock); 66 + 67 + mutex_lock(&evtchnl->ring_io_lock); 68 + req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY); 69 + req->op.hw_param = *hw_param_req; 70 + mutex_unlock(&evtchnl->ring_io_lock); 71 + 72 + ret = be_stream_do_io(evtchnl); 73 + 74 + if (ret == 0) 75 + ret = be_stream_wait_io(evtchnl); 76 + 77 + if (ret == 0) 78 + *hw_param_resp = evtchnl->u.req.resp.hw_param; 79 + 80 + mutex_unlock(&evtchnl->u.req.req_io_lock); 81 + return ret; 82 + } 83 + 84 + int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, 85 + struct xen_snd_front_shbuf *sh_buf, 86 + u8 format, unsigned int channels, 87 + unsigned int rate, u32 buffer_sz, 88 + u32 period_sz) 89 + { 90 + struct xensnd_req *req; 91 + int ret; 92 + 93 + mutex_lock(&evtchnl->u.req.req_io_lock); 94 + 95 + mutex_lock(&evtchnl->ring_io_lock); 96 + req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN); 97 + req->op.open.pcm_format = format; 98 + req->op.open.pcm_channels = channels; 99 + req->op.open.pcm_rate = rate; 100 + req->op.open.buffer_sz = buffer_sz; 101 + req->op.open.period_sz = period_sz; 102 + req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); 103 + mutex_unlock(&evtchnl->ring_io_lock); 104 + 105 + ret = be_stream_do_io(evtchnl); 106 + 107 + if (ret == 0) 108 + ret = be_stream_wait_io(evtchnl); 109 + 110 + mutex_unlock(&evtchnl->u.req.req_io_lock); 111 + return ret; 112 + } 113 + 114 + int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl) 115 + { 116 + struct xensnd_req *req; 117 + int ret; 118 + 119 + mutex_lock(&evtchnl->u.req.req_io_lock); 120 + 121 + mutex_lock(&evtchnl->ring_io_lock); 122 + req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE); 123 + mutex_unlock(&evtchnl->ring_io_lock); 124 + 125 + ret = be_stream_do_io(evtchnl); 126 + 127 + if (ret == 0) 128 + ret = be_stream_wait_io(evtchnl); 129 + 130 + mutex_unlock(&evtchnl->u.req.req_io_lock); 131 + return ret; 132 + } 133 + 134 + int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl, 135 + unsigned long pos, unsigned long count) 136 + { 137 + struct xensnd_req *req; 138 + int ret; 139 + 140 + mutex_lock(&evtchnl->u.req.req_io_lock); 141 + 142 + mutex_lock(&evtchnl->ring_io_lock); 143 + req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE); 144 + req->op.rw.length = count; 145 + req->op.rw.offset = pos; 146 + mutex_unlock(&evtchnl->ring_io_lock); 147 + 148 + ret = be_stream_do_io(evtchnl); 149 + 150 + if (ret == 0) 151 + ret = be_stream_wait_io(evtchnl); 152 + 153 + mutex_unlock(&evtchnl->u.req.req_io_lock); 154 + return ret; 155 + } 156 + 157 + int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl, 158 + unsigned long pos, unsigned long count) 159 + { 160 + struct xensnd_req *req; 161 + int ret; 162 + 163 + mutex_lock(&evtchnl->u.req.req_io_lock); 164 + 165 + mutex_lock(&evtchnl->ring_io_lock); 166 + req = be_stream_prepare_req(evtchnl, XENSND_OP_READ); 167 + req->op.rw.length = count; 168 + req->op.rw.offset = pos; 169 + mutex_unlock(&evtchnl->ring_io_lock); 170 + 171 + ret = be_stream_do_io(evtchnl); 172 + 173 + if (ret == 0) 174 + ret = be_stream_wait_io(evtchnl); 175 + 176 + mutex_unlock(&evtchnl->u.req.req_io_lock); 177 + return ret; 178 + } 179 + 180 + int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl, 181 + int type) 182 + { 183 + struct xensnd_req *req; 184 + int ret; 185 + 186 + mutex_lock(&evtchnl->u.req.req_io_lock); 187 + 188 + mutex_lock(&evtchnl->ring_io_lock); 189 + req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER); 190 + req->op.trigger.type = type; 191 + mutex_unlock(&evtchnl->ring_io_lock); 192 + 193 + ret = be_stream_do_io(evtchnl); 194 + 195 + if (ret == 0) 196 + ret = be_stream_wait_io(evtchnl); 197 + 198 + mutex_unlock(&evtchnl->u.req.req_io_lock); 199 + return ret; 200 + } 23 201 24 202 static void xen_snd_drv_fini(struct xen_snd_front_info *front_info) 25 203 { 204 + xen_snd_front_alsa_fini(front_info); 26 205 xen_snd_front_evtchnl_free_all(front_info); 27 206 } 28 207 ··· 224 45 225 46 static int sndback_connect(struct xen_snd_front_info *front_info) 226 47 { 227 - return 0; 48 + return xen_snd_front_alsa_init(front_info); 228 49 } 229 50 230 51 static void sndback_disconnect(struct xen_snd_front_info *front_info)
+27
sound/xen/xen_snd_front.h
··· 13 13 14 14 #include "xen_snd_front_cfg.h" 15 15 16 + struct xen_snd_front_card_info; 17 + struct xen_snd_front_evtchnl; 16 18 struct xen_snd_front_evtchnl_pair; 19 + struct xen_snd_front_shbuf; 20 + struct xensnd_query_hw_param; 17 21 18 22 struct xen_snd_front_info { 19 23 struct xenbus_device *xb_dev; 24 + 25 + struct xen_snd_front_card_info *card_info; 20 26 21 27 int num_evt_pairs; 22 28 struct xen_snd_front_evtchnl_pair *evt_pairs; 23 29 24 30 struct xen_front_cfg_card cfg; 25 31 }; 32 + 33 + int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, 34 + struct xensnd_query_hw_param *hw_param_req, 35 + struct xensnd_query_hw_param *hw_param_resp); 36 + 37 + int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, 38 + struct xen_snd_front_shbuf *sh_buf, 39 + u8 format, unsigned int channels, 40 + unsigned int rate, u32 buffer_sz, 41 + u32 period_sz); 42 + 43 + int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl); 44 + 45 + int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl, 46 + unsigned long pos, unsigned long count); 47 + 48 + int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl, 49 + unsigned long pos, unsigned long count); 50 + 51 + int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl, 52 + int type); 26 53 27 54 #endif /* __XEN_SND_FRONT_H */
+821
sound/xen/xen_snd_front_alsa.c
··· 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 + 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; 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 + 42 + struct 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 + 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; 58 + }; 59 + 60 + struct alsa_sndif_sample_format { 61 + u8 sndif; 62 + snd_pcm_format_t alsa; 63 + }; 64 + 65 + struct alsa_sndif_hw_param { 66 + u8 sndif; 67 + snd_pcm_hw_param_t alsa; 68 + }; 69 + 70 + static 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 + 173 + static 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 + 184 + static 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++) 191 + if (1 << ALSA_SNDIF_FORMATS[i].alsa & alsa_formats) 192 + mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif; 193 + 194 + return mask; 195 + } 196 + 197 + static 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) 205 + mask |= 1 << ALSA_SNDIF_FORMATS[i].alsa; 206 + 207 + return mask; 208 + } 209 + 210 + static 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 + 220 + static 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 + 226 + static struct xen_snd_front_pcm_stream_info * 227 + stream_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 + 241 + static 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 + 338 + static 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 + 416 + static 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 + 424 + static 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 + 450 + static 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 + 460 + static 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 + 469 + sndif_format = to_sndif_format(runtime->format); 470 + if (sndif_format < 0) { 471 + dev_err(&stream->front_info->xb_dev->dev, 472 + "Unsupported sample format: %d\n", 473 + runtime->format); 474 + return sndif_format; 475 + } 476 + 477 + ret = xen_snd_front_stream_prepare(&stream->evt_pair->req, 478 + &stream->sh_buf, 479 + sndif_format, 480 + runtime->channels, 481 + runtime->rate, 482 + snd_pcm_lib_buffer_bytes(substream), 483 + snd_pcm_lib_period_bytes(substream)); 484 + if (ret < 0) 485 + return ret; 486 + 487 + stream->is_open = true; 488 + } 489 + 490 + return 0; 491 + } 492 + 493 + static int alsa_trigger(struct snd_pcm_substream *substream, int cmd) 494 + { 495 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 496 + int type; 497 + 498 + switch (cmd) { 499 + case SNDRV_PCM_TRIGGER_START: 500 + type = XENSND_OP_TRIGGER_START; 501 + break; 502 + 503 + case SNDRV_PCM_TRIGGER_RESUME: 504 + type = XENSND_OP_TRIGGER_RESUME; 505 + break; 506 + 507 + case SNDRV_PCM_TRIGGER_STOP: 508 + type = XENSND_OP_TRIGGER_STOP; 509 + break; 510 + 511 + case SNDRV_PCM_TRIGGER_SUSPEND: 512 + type = XENSND_OP_TRIGGER_PAUSE; 513 + break; 514 + 515 + default: 516 + return -EINVAL; 517 + } 518 + 519 + return xen_snd_front_stream_trigger(&stream->evt_pair->req, type); 520 + } 521 + 522 + void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, 523 + u64 pos_bytes) 524 + { 525 + struct snd_pcm_substream *substream = evtchnl->u.evt.substream; 526 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 527 + snd_pcm_uframes_t delta, new_hw_ptr, cur_frame; 528 + 529 + cur_frame = bytes_to_frames(substream->runtime, pos_bytes); 530 + 531 + delta = cur_frame - stream->be_cur_frame; 532 + stream->be_cur_frame = cur_frame; 533 + 534 + new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); 535 + new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size; 536 + atomic_set(&stream->hw_ptr, (int)new_hw_ptr); 537 + 538 + stream->out_frames += delta; 539 + if (stream->out_frames > substream->runtime->period_size) { 540 + stream->out_frames %= substream->runtime->period_size; 541 + snd_pcm_period_elapsed(substream); 542 + } 543 + } 544 + 545 + static snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream) 546 + { 547 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 548 + 549 + return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr); 550 + } 551 + 552 + static int alsa_pb_copy_user(struct snd_pcm_substream *substream, 553 + int channel, unsigned long pos, void __user *src, 554 + unsigned long count) 555 + { 556 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 557 + 558 + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) 559 + return -EINVAL; 560 + 561 + if (copy_from_user(stream->sh_buf.buffer + pos, src, count)) 562 + return -EFAULT; 563 + 564 + return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); 565 + } 566 + 567 + static int alsa_pb_copy_kernel(struct snd_pcm_substream *substream, 568 + int channel, unsigned long pos, void *src, 569 + unsigned long count) 570 + { 571 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 572 + 573 + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) 574 + return -EINVAL; 575 + 576 + memcpy(stream->sh_buf.buffer + pos, src, count); 577 + 578 + return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); 579 + } 580 + 581 + static int alsa_cap_copy_user(struct snd_pcm_substream *substream, 582 + int channel, unsigned long pos, void __user *dst, 583 + unsigned long count) 584 + { 585 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 586 + int ret; 587 + 588 + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) 589 + return -EINVAL; 590 + 591 + ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); 592 + if (ret < 0) 593 + return ret; 594 + 595 + return copy_to_user(dst, stream->sh_buf.buffer + pos, count) ? 596 + -EFAULT : 0; 597 + } 598 + 599 + static int alsa_cap_copy_kernel(struct snd_pcm_substream *substream, 600 + int channel, unsigned long pos, void *dst, 601 + unsigned long count) 602 + { 603 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 604 + int ret; 605 + 606 + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) 607 + return -EINVAL; 608 + 609 + ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count); 610 + if (ret < 0) 611 + return ret; 612 + 613 + memcpy(dst, stream->sh_buf.buffer + pos, count); 614 + 615 + return 0; 616 + } 617 + 618 + static int alsa_pb_fill_silence(struct snd_pcm_substream *substream, 619 + int channel, unsigned long pos, 620 + unsigned long count) 621 + { 622 + struct xen_snd_front_pcm_stream_info *stream = stream_get(substream); 623 + 624 + if (unlikely(pos + count > stream->sh_buf.buffer_sz)) 625 + return -EINVAL; 626 + 627 + memset(stream->sh_buf.buffer + pos, 0, count); 628 + 629 + return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count); 630 + } 631 + 632 + /* 633 + * FIXME: The mmaped data transfer is asynchronous and there is no 634 + * ack signal from user-space when it is done. This is the 635 + * reason it is not implemented in the PV driver as we do need 636 + * to know when the buffer can be transferred to the backend. 637 + */ 638 + 639 + static struct snd_pcm_ops snd_drv_alsa_playback_ops = { 640 + .open = alsa_open, 641 + .close = alsa_close, 642 + .ioctl = snd_pcm_lib_ioctl, 643 + .hw_params = alsa_hw_params, 644 + .hw_free = alsa_hw_free, 645 + .prepare = alsa_prepare, 646 + .trigger = alsa_trigger, 647 + .pointer = alsa_pointer, 648 + .copy_user = alsa_pb_copy_user, 649 + .copy_kernel = alsa_pb_copy_kernel, 650 + .fill_silence = alsa_pb_fill_silence, 651 + }; 652 + 653 + static struct snd_pcm_ops snd_drv_alsa_capture_ops = { 654 + .open = alsa_open, 655 + .close = alsa_close, 656 + .ioctl = snd_pcm_lib_ioctl, 657 + .hw_params = alsa_hw_params, 658 + .hw_free = alsa_hw_free, 659 + .prepare = alsa_prepare, 660 + .trigger = alsa_trigger, 661 + .pointer = alsa_pointer, 662 + .copy_user = alsa_cap_copy_user, 663 + .copy_kernel = alsa_cap_copy_kernel, 664 + }; 665 + 666 + static int new_pcm_instance(struct xen_snd_front_card_info *card_info, 667 + struct xen_front_cfg_pcm_instance *instance_cfg, 668 + struct xen_snd_front_pcm_instance_info *pcm_instance_info) 669 + { 670 + struct snd_pcm *pcm; 671 + int ret, i; 672 + 673 + dev_dbg(&card_info->front_info->xb_dev->dev, 674 + "New PCM device \"%s\" with id %d playback %d capture %d", 675 + instance_cfg->name, 676 + instance_cfg->device_id, 677 + instance_cfg->num_streams_pb, 678 + instance_cfg->num_streams_cap); 679 + 680 + pcm_instance_info->card_info = card_info; 681 + 682 + pcm_instance_info->pcm_hw = instance_cfg->pcm_hw; 683 + 684 + if (instance_cfg->num_streams_pb) { 685 + pcm_instance_info->streams_pb = 686 + devm_kcalloc(&card_info->card->card_dev, 687 + instance_cfg->num_streams_pb, 688 + sizeof(struct xen_snd_front_pcm_stream_info), 689 + GFP_KERNEL); 690 + if (!pcm_instance_info->streams_pb) 691 + return -ENOMEM; 692 + } 693 + 694 + if (instance_cfg->num_streams_cap) { 695 + pcm_instance_info->streams_cap = 696 + devm_kcalloc(&card_info->card->card_dev, 697 + instance_cfg->num_streams_cap, 698 + sizeof(struct xen_snd_front_pcm_stream_info), 699 + GFP_KERNEL); 700 + if (!pcm_instance_info->streams_cap) 701 + return -ENOMEM; 702 + } 703 + 704 + pcm_instance_info->num_pcm_streams_pb = 705 + instance_cfg->num_streams_pb; 706 + pcm_instance_info->num_pcm_streams_cap = 707 + instance_cfg->num_streams_cap; 708 + 709 + for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) { 710 + pcm_instance_info->streams_pb[i].pcm_hw = 711 + instance_cfg->streams_pb[i].pcm_hw; 712 + pcm_instance_info->streams_pb[i].index = 713 + instance_cfg->streams_pb[i].index; 714 + } 715 + 716 + for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) { 717 + pcm_instance_info->streams_cap[i].pcm_hw = 718 + instance_cfg->streams_cap[i].pcm_hw; 719 + pcm_instance_info->streams_cap[i].index = 720 + instance_cfg->streams_cap[i].index; 721 + } 722 + 723 + ret = snd_pcm_new(card_info->card, instance_cfg->name, 724 + instance_cfg->device_id, 725 + instance_cfg->num_streams_pb, 726 + instance_cfg->num_streams_cap, 727 + &pcm); 728 + if (ret < 0) 729 + return ret; 730 + 731 + pcm->private_data = pcm_instance_info; 732 + pcm->info_flags = 0; 733 + /* we want to handle all PCM operations in non-atomic context */ 734 + pcm->nonatomic = true; 735 + strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name)); 736 + 737 + if (instance_cfg->num_streams_pb) 738 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 739 + &snd_drv_alsa_playback_ops); 740 + 741 + if (instance_cfg->num_streams_cap) 742 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 743 + &snd_drv_alsa_capture_ops); 744 + 745 + pcm_instance_info->pcm = pcm; 746 + return 0; 747 + } 748 + 749 + int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info) 750 + { 751 + struct device *dev = &front_info->xb_dev->dev; 752 + struct xen_front_cfg_card *cfg = &front_info->cfg; 753 + struct xen_snd_front_card_info *card_info; 754 + struct snd_card *card; 755 + int ret, i; 756 + 757 + dev_dbg(dev, "Creating virtual sound card\n"); 758 + 759 + ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE, 760 + sizeof(struct xen_snd_front_card_info), &card); 761 + if (ret < 0) 762 + return ret; 763 + 764 + card_info = card->private_data; 765 + card_info->front_info = front_info; 766 + front_info->card_info = card_info; 767 + card_info->card = card; 768 + card_info->pcm_instances = 769 + devm_kcalloc(dev, cfg->num_pcm_instances, 770 + sizeof(struct xen_snd_front_pcm_instance_info), 771 + GFP_KERNEL); 772 + if (!card_info->pcm_instances) { 773 + ret = -ENOMEM; 774 + goto fail; 775 + } 776 + 777 + card_info->num_pcm_instances = cfg->num_pcm_instances; 778 + card_info->pcm_hw = cfg->pcm_hw; 779 + 780 + for (i = 0; i < cfg->num_pcm_instances; i++) { 781 + ret = new_pcm_instance(card_info, &cfg->pcm_instances[i], 782 + &card_info->pcm_instances[i]); 783 + if (ret < 0) 784 + goto fail; 785 + } 786 + 787 + strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver)); 788 + strncpy(card->shortname, cfg->name_short, sizeof(card->shortname)); 789 + strncpy(card->longname, cfg->name_long, sizeof(card->longname)); 790 + 791 + ret = snd_card_register(card); 792 + if (ret < 0) 793 + goto fail; 794 + 795 + return 0; 796 + 797 + fail: 798 + snd_card_free(card); 799 + return ret; 800 + } 801 + 802 + void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info) 803 + { 804 + struct xen_snd_front_card_info *card_info; 805 + struct snd_card *card; 806 + 807 + card_info = front_info->card_info; 808 + if (!card_info) 809 + return; 810 + 811 + card = card_info->card; 812 + if (!card) 813 + return; 814 + 815 + dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n", 816 + card->number); 817 + snd_card_free(card); 818 + 819 + /* Card_info will be freed when destroying front_info->xb_dev->dev. */ 820 + card_info->card = NULL; 821 + }
+23
sound/xen/xen_snd_front_alsa.h
··· 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 + #ifndef __XEN_SND_FRONT_ALSA_H 12 + #define __XEN_SND_FRONT_ALSA_H 13 + 14 + struct xen_snd_front_info; 15 + 16 + int xen_snd_front_alsa_init(struct xen_snd_front_info *front_info); 17 + 18 + void xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info); 19 + 20 + void xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl, 21 + u64 pos_bytes); 22 + 23 + #endif /* __XEN_SND_FRONT_ALSA_H */
+3 -1
sound/xen/xen_snd_front_evtchnl.c
··· 14 14 #include <xen/xenbus.h> 15 15 16 16 #include "xen_snd_front.h" 17 + #include "xen_snd_front_alsa.h" 17 18 #include "xen_snd_front_cfg.h" 18 19 #include "xen_snd_front_evtchnl.h" 19 20 ··· 119 118 120 119 switch (event->type) { 121 120 case XENSND_EVT_CUR_POS: 122 - /* Do nothing at the moment. */ 121 + xen_snd_front_alsa_handle_cur_pos(channel, 122 + event->op.cur_pos.position); 123 123 break; 124 124 } 125 125 }