Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/xen/xen_snd_front.c
29265 views
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 <[email protected]>
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
guard(mutex)(&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
83
int 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)(&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(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
113
int 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)(&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
132
int 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)(&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
154
int 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)(&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
176
int 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)(&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
197
static 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
203
static 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, &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
220
static int sndback_connect(struct xen_snd_front_info *front_info)
221
{
222
return xen_snd_front_alsa_init(front_info);
223
}
224
225
static void sndback_disconnect(struct xen_snd_front_info *front_info)
226
{
227
xen_snd_drv_fini(front_info);
228
xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising);
229
}
230
231
static 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(&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(xb_dev, ret, "initializing frontend");
259
else
260
xenbus_switch_state(xb_dev, 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(xb_dev, ret, "initializing frontend");
270
else
271
xenbus_switch_state(xb_dev, 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
292
static 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(&xb_dev->dev,
298
sizeof(*front_info), GFP_KERNEL);
299
if (!front_info)
300
return -ENOMEM;
301
302
front_info->xb_dev = xb_dev;
303
dev_set_drvdata(&xb_dev->dev, front_info);
304
305
return xenbus_switch_state(xb_dev, XenbusStateInitialising);
306
}
307
308
static void xen_drv_remove(struct xenbus_device *dev)
309
{
310
struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
311
int to = 100;
312
313
xenbus_switch_state(dev, 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(front_info->xb_dev->otherend, "state",
327
XenbusStateUnknown) != XenbusStateInitWait) &&
328
--to)
329
msleep(10);
330
331
if (!to) {
332
unsigned int state;
333
334
state = xenbus_read_unsigned(front_info->xb_dev->otherend,
335
"state", 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
344
static const struct xenbus_device_id xen_drv_ids[] = {
345
{ XENSND_DRIVER_NAME },
346
{ "" }
347
};
348
349
static 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
357
static 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
376
static void __exit xen_drv_fini(void)
377
{
378
pr_info("Unregistering Xen " XENSND_DRIVER_NAME " frontend driver\n");
379
xenbus_unregister_driver(&xen_driver);
380
}
381
382
module_init(xen_drv_init);
383
module_exit(xen_drv_fini);
384
385
MODULE_DESCRIPTION("Xen virtual sound device frontend");
386
MODULE_LICENSE("GPL");
387
MODULE_ALIAS("xen:" XENSND_DRIVER_NAME);
388
389