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/delay.h>
12#include <linux/module.h>
13
14#include <xen/page.h>
15#include <xen/platform_pci.h>
16#include <xen/xen.h>
17#include <xen/xenbus.h>
18
19#include <xen/xen-front-pgdir-shbuf.h>
20#include <xen/interface/io/sndif.h>
21
22#include "xen_snd_front.h"
23#include "xen_snd_front_alsa.h"
24#include "xen_snd_front_evtchnl.h"
25
26static struct xensnd_req *
27be_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
39static 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(x: &evtchnl->u.req.completion);
45 xen_snd_front_evtchnl_flush(evtchnl);
46 return 0;
47}
48
49static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl)
50{
51 if (wait_for_completion_timeout(x: &evtchnl->u.req.completion,
52 timeout: msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0)
53 return -ETIMEDOUT;
54
55 return evtchnl->u.req.resp_status;
56}
57
58int 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 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
66
67 scoped_guard(mutex, &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 }
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 return ret;
81}
82
83int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl,
84 struct xen_front_pgdir_shbuf *shbuf,
85 u8 format, unsigned int channels,
86 unsigned int rate, u32 buffer_sz,
87 u32 period_sz)
88{
89 struct xensnd_req *req;
90 int ret;
91
92 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
93
94 scoped_guard(mutex, &evtchnl->ring_io_lock) {
95 req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN);
96 req->op.open.pcm_format = format;
97 req->op.open.pcm_channels = channels;
98 req->op.open.pcm_rate = rate;
99 req->op.open.buffer_sz = buffer_sz;
100 req->op.open.period_sz = period_sz;
101 req->op.open.gref_directory =
102 xen_front_pgdir_shbuf_get_dir_start(buf: shbuf);
103 }
104
105 ret = be_stream_do_io(evtchnl);
106
107 if (ret == 0)
108 ret = be_stream_wait_io(evtchnl);
109
110 return ret;
111}
112
113int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl)
114{
115 __always_unused struct xensnd_req *req;
116 int ret;
117
118 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
119
120 scoped_guard(mutex, &evtchnl->ring_io_lock) {
121 req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE);
122 }
123
124 ret = be_stream_do_io(evtchnl);
125
126 if (ret == 0)
127 ret = be_stream_wait_io(evtchnl);
128
129 return ret;
130}
131
132int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl,
133 unsigned long pos, unsigned long count)
134{
135 struct xensnd_req *req;
136 int ret;
137
138 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
139
140 scoped_guard(mutex, &evtchnl->ring_io_lock) {
141 req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE);
142 req->op.rw.length = count;
143 req->op.rw.offset = pos;
144 }
145
146 ret = be_stream_do_io(evtchnl);
147
148 if (ret == 0)
149 ret = be_stream_wait_io(evtchnl);
150
151 return ret;
152}
153
154int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl,
155 unsigned long pos, unsigned long count)
156{
157 struct xensnd_req *req;
158 int ret;
159
160 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
161
162 scoped_guard(mutex, &evtchnl->ring_io_lock) {
163 req = be_stream_prepare_req(evtchnl, XENSND_OP_READ);
164 req->op.rw.length = count;
165 req->op.rw.offset = pos;
166 }
167
168 ret = be_stream_do_io(evtchnl);
169
170 if (ret == 0)
171 ret = be_stream_wait_io(evtchnl);
172
173 return ret;
174}
175
176int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl,
177 int type)
178{
179 struct xensnd_req *req;
180 int ret;
181
182 guard(mutex)(T: &evtchnl->u.req.req_io_lock);
183
184 scoped_guard(mutex, &evtchnl->ring_io_lock) {
185 req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER);
186 req->op.trigger.type = type;
187 }
188
189 ret = be_stream_do_io(evtchnl);
190
191 if (ret == 0)
192 ret = be_stream_wait_io(evtchnl);
193
194 return ret;
195}
196
197static void xen_snd_drv_fini(struct xen_snd_front_info *front_info)
198{
199 xen_snd_front_alsa_fini(front_info);
200 xen_snd_front_evtchnl_free_all(front_info);
201}
202
203static int sndback_initwait(struct xen_snd_front_info *front_info)
204{
205 int num_streams;
206 int ret;
207
208 ret = xen_snd_front_cfg_card(front_info, stream_cnt: &num_streams);
209 if (ret < 0)
210 return ret;
211
212 /* create event channels for all streams and publish */
213 ret = xen_snd_front_evtchnl_create_all(front_info, num_streams);
214 if (ret < 0)
215 return ret;
216
217 return xen_snd_front_evtchnl_publish_all(front_info);
218}
219
220static int sndback_connect(struct xen_snd_front_info *front_info)
221{
222 return xen_snd_front_alsa_init(front_info);
223}
224
225static void sndback_disconnect(struct xen_snd_front_info *front_info)
226{
227 xen_snd_drv_fini(front_info);
228 xenbus_switch_state(dev: front_info->xb_dev, new_state: XenbusStateInitialising);
229}
230
231static void sndback_changed(struct xenbus_device *xb_dev,
232 enum xenbus_state backend_state)
233{
234 struct xen_snd_front_info *front_info = dev_get_drvdata(dev: &xb_dev->dev);
235 int ret;
236
237 dev_dbg(&xb_dev->dev, "Backend state is %s, front is %s\n",
238 xenbus_strstate(backend_state),
239 xenbus_strstate(xb_dev->state));
240
241 switch (backend_state) {
242 case XenbusStateReconfiguring:
243 case XenbusStateReconfigured:
244 case XenbusStateInitialised:
245 break;
246
247 case XenbusStateInitialising:
248 /* Recovering after backend unexpected closure. */
249 sndback_disconnect(front_info);
250 break;
251
252 case XenbusStateInitWait:
253 /* Recovering after backend unexpected closure. */
254 sndback_disconnect(front_info);
255
256 ret = sndback_initwait(front_info);
257 if (ret < 0)
258 xenbus_dev_fatal(dev: xb_dev, err: ret, fmt: "initializing frontend");
259 else
260 xenbus_switch_state(dev: xb_dev, new_state: XenbusStateInitialised);
261 break;
262
263 case XenbusStateConnected:
264 if (xb_dev->state != XenbusStateInitialised)
265 break;
266
267 ret = sndback_connect(front_info);
268 if (ret < 0)
269 xenbus_dev_fatal(dev: xb_dev, err: ret, fmt: "initializing frontend");
270 else
271 xenbus_switch_state(dev: xb_dev, new_state: XenbusStateConnected);
272 break;
273
274 case XenbusStateClosing:
275 /*
276 * In this state backend starts freeing resources,
277 * so let it go into closed state first, so we can also
278 * remove ours.
279 */
280 break;
281
282 case XenbusStateUnknown:
283 case XenbusStateClosed:
284 if (xb_dev->state == XenbusStateClosed)
285 break;
286
287 sndback_disconnect(front_info);
288 break;
289 }
290}
291
292static int xen_drv_probe(struct xenbus_device *xb_dev,
293 const struct xenbus_device_id *id)
294{
295 struct xen_snd_front_info *front_info;
296
297 front_info = devm_kzalloc(dev: &xb_dev->dev,
298 size: sizeof(*front_info), GFP_KERNEL);
299 if (!front_info)
300 return -ENOMEM;
301
302 front_info->xb_dev = xb_dev;
303 dev_set_drvdata(dev: &xb_dev->dev, data: front_info);
304
305 return xenbus_switch_state(dev: xb_dev, new_state: XenbusStateInitialising);
306}
307
308static void xen_drv_remove(struct xenbus_device *dev)
309{
310 struct xen_snd_front_info *front_info = dev_get_drvdata(dev: &dev->dev);
311 int to = 100;
312
313 xenbus_switch_state(dev, new_state: XenbusStateClosing);
314
315 /*
316 * On driver removal it is disconnected from XenBus,
317 * so no backend state change events come via .otherend_changed
318 * callback. This prevents us from exiting gracefully, e.g.
319 * signaling the backend to free event channels, waiting for its
320 * state to change to XenbusStateClosed and cleaning at our end.
321 * Normally when front driver removed backend will finally go into
322 * XenbusStateInitWait state.
323 *
324 * Workaround: read backend's state manually and wait with time-out.
325 */
326 while ((xenbus_read_unsigned(dir: front_info->xb_dev->otherend, node: "state",
327 default_val: XenbusStateUnknown) != XenbusStateInitWait) &&
328 --to)
329 msleep(msecs: 10);
330
331 if (!to) {
332 unsigned int state;
333
334 state = xenbus_read_unsigned(dir: front_info->xb_dev->otherend,
335 node: "state", default_val: XenbusStateUnknown);
336 pr_err("Backend state is %s while removing driver\n",
337 xenbus_strstate(state));
338 }
339
340 xen_snd_drv_fini(front_info);
341 xenbus_frontend_closed(dev);
342}
343
344static const struct xenbus_device_id xen_drv_ids[] = {
345 { XENSND_DRIVER_NAME },
346 { "" }
347};
348
349static struct xenbus_driver xen_driver = {
350 .ids = xen_drv_ids,
351 .probe = xen_drv_probe,
352 .remove = xen_drv_remove,
353 .otherend_changed = sndback_changed,
354 .not_essential = true,
355};
356
357static int __init xen_drv_init(void)
358{
359 if (!xen_domain())
360 return -ENODEV;
361
362 if (!xen_has_pv_devices())
363 return -ENODEV;
364
365 /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */
366 if (XEN_PAGE_SIZE != PAGE_SIZE) {
367 pr_err(XENSND_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n",
368 XEN_PAGE_SIZE, PAGE_SIZE);
369 return -ENODEV;
370 }
371
372 pr_info("Initialising Xen " XENSND_DRIVER_NAME " frontend driver\n");
373 return xenbus_register_frontend(&xen_driver);
374}
375
376static void __exit xen_drv_fini(void)
377{
378 pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n");
379 xenbus_unregister_driver(drv: &xen_driver);
380}
381
382module_init(xen_drv_init);
383module_exit(xen_drv_fini);
384
385MODULE_DESCRIPTION("Xen virtual sound device frontend");
386MODULE_LICENSE("GPL");
387MODULE_ALIAS("xen:" XENSND_DRIVER_NAME);
388

source code of linux/sound/xen/xen_snd_front.c