Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/xen/xen_snd_front.c
26377 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
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
395