~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/xen/xen_snd_front.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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(&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_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(shbuf);
104         mutex_unlock(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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, &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(front_info->xb_dev, 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(&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(xb_dev, ret, "initializing frontend");
265                 else
266                         xenbus_switch_state(xb_dev, 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(xb_dev, ret, "initializing frontend");
276                 else
277                         xenbus_switch_state(xb_dev, 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(&xb_dev->dev,
304                                   sizeof(*front_info), GFP_KERNEL);
305         if (!front_info)
306                 return -ENOMEM;
307 
308         front_info->xb_dev = xb_dev;
309         dev_set_drvdata(&xb_dev->dev, front_info);
310 
311         return xenbus_switch_state(xb_dev, 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);
317         int to = 100;
318 
319         xenbus_switch_state(dev, 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(front_info->xb_dev->otherend, "state",
333                                      XenbusStateUnknown) != XenbusStateInitWait) &&
334                --to)
335                 msleep(10);
336 
337         if (!to) {
338                 unsigned int state;
339 
340                 state = xenbus_read_unsigned(front_info->xb_dev->otherend,
341                                              "state", 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(&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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php