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