Commit | Line | Data |
---|---|---|
788ef64a OA |
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 <xen/events.h> | |
12 | #include <xen/grant_table.h> | |
13 | #include <xen/xen.h> | |
14 | #include <xen/xenbus.h> | |
15 | ||
16 | #include "xen_snd_front.h" | |
1cee5593 | 17 | #include "xen_snd_front_alsa.h" |
788ef64a OA |
18 | #include "xen_snd_front_cfg.h" |
19 | #include "xen_snd_front_evtchnl.h" | |
20 | ||
21 | static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id) | |
22 | { | |
23 | struct xen_snd_front_evtchnl *channel = dev_id; | |
24 | struct xen_snd_front_info *front_info = channel->front_info; | |
25 | struct xensnd_resp *resp; | |
26 | RING_IDX i, rp; | |
27 | ||
28 | if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) | |
29 | return IRQ_HANDLED; | |
30 | ||
31 | mutex_lock(&channel->ring_io_lock); | |
32 | ||
33 | again: | |
34 | rp = channel->u.req.ring.sring->rsp_prod; | |
35 | /* Ensure we see queued responses up to rp. */ | |
36 | rmb(); | |
37 | ||
38 | /* | |
39 | * Assume that the backend is trusted to always write sane values | |
40 | * to the ring counters, so no overflow checks on frontend side | |
41 | * are required. | |
42 | */ | |
43 | for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { | |
44 | resp = RING_GET_RESPONSE(&channel->u.req.ring, i); | |
45 | if (resp->id != channel->evt_id) | |
46 | continue; | |
47 | switch (resp->operation) { | |
48 | case XENSND_OP_OPEN: | |
788ef64a | 49 | case XENSND_OP_CLOSE: |
788ef64a | 50 | case XENSND_OP_READ: |
788ef64a | 51 | case XENSND_OP_WRITE: |
788ef64a OA |
52 | case XENSND_OP_TRIGGER: |
53 | channel->u.req.resp_status = resp->status; | |
54 | complete(&channel->u.req.completion); | |
55 | break; | |
56 | case XENSND_OP_HW_PARAM_QUERY: | |
57 | channel->u.req.resp_status = resp->status; | |
58 | channel->u.req.resp.hw_param = | |
59 | resp->resp.hw_param; | |
60 | complete(&channel->u.req.completion); | |
61 | break; | |
62 | ||
63 | default: | |
64 | dev_err(&front_info->xb_dev->dev, | |
65 | "Operation %d is not supported\n", | |
66 | resp->operation); | |
67 | break; | |
68 | } | |
69 | } | |
70 | ||
71 | channel->u.req.ring.rsp_cons = i; | |
72 | if (i != channel->u.req.ring.req_prod_pvt) { | |
73 | int more_to_do; | |
74 | ||
75 | RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring, | |
76 | more_to_do); | |
77 | if (more_to_do) | |
78 | goto again; | |
79 | } else { | |
80 | channel->u.req.ring.sring->rsp_event = i + 1; | |
81 | } | |
82 | ||
83 | mutex_unlock(&channel->ring_io_lock); | |
84 | return IRQ_HANDLED; | |
85 | } | |
86 | ||
87 | static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) | |
88 | { | |
89 | struct xen_snd_front_evtchnl *channel = dev_id; | |
90 | struct xensnd_event_page *page = channel->u.evt.page; | |
91 | u32 cons, prod; | |
92 | ||
93 | if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) | |
94 | return IRQ_HANDLED; | |
95 | ||
96 | mutex_lock(&channel->ring_io_lock); | |
97 | ||
98 | prod = page->in_prod; | |
99 | /* Ensure we see ring contents up to prod. */ | |
100 | virt_rmb(); | |
101 | if (prod == page->in_cons) | |
102 | goto out; | |
103 | ||
104 | /* | |
105 | * Assume that the backend is trusted to always write sane values | |
106 | * to the ring counters, so no overflow checks on frontend side | |
107 | * are required. | |
108 | */ | |
109 | for (cons = page->in_cons; cons != prod; cons++) { | |
110 | struct xensnd_evt *event; | |
111 | ||
112 | event = &XENSND_IN_RING_REF(page, cons); | |
113 | if (unlikely(event->id != channel->evt_id++)) | |
114 | continue; | |
115 | ||
116 | switch (event->type) { | |
117 | case XENSND_EVT_CUR_POS: | |
1cee5593 OA |
118 | xen_snd_front_alsa_handle_cur_pos(channel, |
119 | event->op.cur_pos.position); | |
788ef64a OA |
120 | break; |
121 | } | |
122 | } | |
123 | ||
124 | page->in_cons = cons; | |
125 | /* Ensure ring contents. */ | |
126 | virt_wmb(); | |
127 | ||
128 | out: | |
129 | mutex_unlock(&channel->ring_io_lock); | |
130 | return IRQ_HANDLED; | |
131 | } | |
132 | ||
133 | void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel) | |
134 | { | |
135 | int notify; | |
136 | ||
137 | channel->u.req.ring.req_prod_pvt++; | |
138 | RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify); | |
139 | if (notify) | |
140 | notify_remote_via_irq(channel->irq); | |
141 | } | |
142 | ||
143 | static void evtchnl_free(struct xen_snd_front_info *front_info, | |
144 | struct xen_snd_front_evtchnl *channel) | |
145 | { | |
360dc89d | 146 | void *page = NULL; |
788ef64a OA |
147 | |
148 | if (channel->type == EVTCHNL_TYPE_REQ) | |
360dc89d | 149 | page = channel->u.req.ring.sring; |
788ef64a | 150 | else if (channel->type == EVTCHNL_TYPE_EVT) |
360dc89d | 151 | page = channel->u.evt.page; |
788ef64a OA |
152 | |
153 | if (!page) | |
154 | return; | |
155 | ||
156 | channel->state = EVTCHNL_STATE_DISCONNECTED; | |
157 | if (channel->type == EVTCHNL_TYPE_REQ) { | |
158 | /* Release all who still waits for response if any. */ | |
159 | channel->u.req.resp_status = -EIO; | |
160 | complete_all(&channel->u.req.completion); | |
161 | } | |
162 | ||
163 | if (channel->irq) | |
164 | unbind_from_irqhandler(channel->irq, channel); | |
165 | ||
166 | if (channel->port) | |
167 | xenbus_free_evtchn(front_info->xb_dev, channel->port); | |
168 | ||
169 | /* End access and free the page. */ | |
360dc89d | 170 | xenbus_teardown_ring(&page, 1, &channel->gref); |
788ef64a OA |
171 | |
172 | memset(channel, 0, sizeof(*channel)); | |
173 | } | |
174 | ||
175 | void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info) | |
176 | { | |
177 | int i; | |
178 | ||
179 | if (!front_info->evt_pairs) | |
180 | return; | |
181 | ||
182 | for (i = 0; i < front_info->num_evt_pairs; i++) { | |
183 | evtchnl_free(front_info, &front_info->evt_pairs[i].req); | |
184 | evtchnl_free(front_info, &front_info->evt_pairs[i].evt); | |
185 | } | |
186 | ||
187 | kfree(front_info->evt_pairs); | |
188 | front_info->evt_pairs = NULL; | |
189 | } | |
190 | ||
191 | static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index, | |
192 | struct xen_snd_front_evtchnl *channel, | |
193 | enum xen_snd_front_evtchnl_type type) | |
194 | { | |
195 | struct xenbus_device *xb_dev = front_info->xb_dev; | |
360dc89d | 196 | void *page; |
788ef64a OA |
197 | irq_handler_t handler; |
198 | char *handler_name = NULL; | |
199 | int ret; | |
200 | ||
201 | memset(channel, 0, sizeof(*channel)); | |
202 | channel->type = type; | |
203 | channel->index = index; | |
204 | channel->front_info = front_info; | |
205 | channel->state = EVTCHNL_STATE_DISCONNECTED; | |
360dc89d JG |
206 | ret = xenbus_setup_ring(xb_dev, GFP_KERNEL, &page, 1, &channel->gref); |
207 | if (ret) | |
788ef64a | 208 | goto fail; |
788ef64a OA |
209 | |
210 | handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME, | |
211 | type == EVTCHNL_TYPE_REQ ? | |
212 | XENSND_FIELD_RING_REF : | |
213 | XENSND_FIELD_EVT_RING_REF); | |
214 | if (!handler_name) { | |
215 | ret = -ENOMEM; | |
216 | goto fail; | |
217 | } | |
218 | ||
219 | mutex_init(&channel->ring_io_lock); | |
220 | ||
221 | if (type == EVTCHNL_TYPE_REQ) { | |
360dc89d | 222 | struct xen_sndif_sring *sring = page; |
788ef64a OA |
223 | |
224 | init_completion(&channel->u.req.completion); | |
225 | mutex_init(&channel->u.req.req_io_lock); | |
360dc89d | 226 | XEN_FRONT_RING_INIT(&channel->u.req.ring, sring, XEN_PAGE_SIZE); |
788ef64a OA |
227 | |
228 | handler = evtchnl_interrupt_req; | |
229 | } else { | |
360dc89d | 230 | channel->u.evt.page = page; |
788ef64a OA |
231 | handler = evtchnl_interrupt_evt; |
232 | } | |
233 | ||
788ef64a OA |
234 | ret = xenbus_alloc_evtchn(xb_dev, &channel->port); |
235 | if (ret < 0) | |
236 | goto fail; | |
237 | ||
238 | ret = bind_evtchn_to_irq(channel->port); | |
239 | if (ret < 0) { | |
240 | dev_err(&xb_dev->dev, | |
241 | "Failed to bind IRQ for domid %d port %d: %d\n", | |
242 | front_info->xb_dev->otherend_id, channel->port, ret); | |
243 | goto fail; | |
244 | } | |
245 | ||
246 | channel->irq = ret; | |
247 | ||
248 | ret = request_threaded_irq(channel->irq, NULL, handler, | |
249 | IRQF_ONESHOT, handler_name, channel); | |
250 | if (ret < 0) { | |
251 | dev_err(&xb_dev->dev, "Failed to request IRQ %d: %d\n", | |
252 | channel->irq, ret); | |
253 | goto fail; | |
254 | } | |
255 | ||
256 | kfree(handler_name); | |
257 | return 0; | |
258 | ||
259 | fail: | |
788ef64a OA |
260 | kfree(handler_name); |
261 | dev_err(&xb_dev->dev, "Failed to allocate ring: %d\n", ret); | |
262 | return ret; | |
263 | } | |
264 | ||
265 | int xen_snd_front_evtchnl_create_all(struct xen_snd_front_info *front_info, | |
266 | int num_streams) | |
267 | { | |
268 | struct xen_front_cfg_card *cfg = &front_info->cfg; | |
269 | struct device *dev = &front_info->xb_dev->dev; | |
270 | int d, ret = 0; | |
271 | ||
272 | front_info->evt_pairs = | |
273 | kcalloc(num_streams, | |
274 | sizeof(struct xen_snd_front_evtchnl_pair), | |
275 | GFP_KERNEL); | |
276 | if (!front_info->evt_pairs) | |
277 | return -ENOMEM; | |
278 | ||
279 | /* Iterate over devices and their streams and create event channels. */ | |
280 | for (d = 0; d < cfg->num_pcm_instances; d++) { | |
281 | struct xen_front_cfg_pcm_instance *pcm_instance; | |
282 | int s, index; | |
283 | ||
284 | pcm_instance = &cfg->pcm_instances[d]; | |
285 | ||
286 | for (s = 0; s < pcm_instance->num_streams_pb; s++) { | |
287 | index = pcm_instance->streams_pb[s].index; | |
288 | ||
289 | ret = evtchnl_alloc(front_info, index, | |
290 | &front_info->evt_pairs[index].req, | |
291 | EVTCHNL_TYPE_REQ); | |
292 | if (ret < 0) { | |
293 | dev_err(dev, "Error allocating control channel\n"); | |
294 | goto fail; | |
295 | } | |
296 | ||
297 | ret = evtchnl_alloc(front_info, index, | |
298 | &front_info->evt_pairs[index].evt, | |
299 | EVTCHNL_TYPE_EVT); | |
300 | if (ret < 0) { | |
301 | dev_err(dev, "Error allocating in-event channel\n"); | |
302 | goto fail; | |
303 | } | |
304 | } | |
305 | ||
306 | for (s = 0; s < pcm_instance->num_streams_cap; s++) { | |
307 | index = pcm_instance->streams_cap[s].index; | |
308 | ||
309 | ret = evtchnl_alloc(front_info, index, | |
310 | &front_info->evt_pairs[index].req, | |
311 | EVTCHNL_TYPE_REQ); | |
312 | if (ret < 0) { | |
313 | dev_err(dev, "Error allocating control channel\n"); | |
314 | goto fail; | |
315 | } | |
316 | ||
317 | ret = evtchnl_alloc(front_info, index, | |
318 | &front_info->evt_pairs[index].evt, | |
319 | EVTCHNL_TYPE_EVT); | |
320 | if (ret < 0) { | |
321 | dev_err(dev, "Error allocating in-event channel\n"); | |
322 | goto fail; | |
323 | } | |
324 | } | |
325 | } | |
788ef64a OA |
326 | |
327 | front_info->num_evt_pairs = num_streams; | |
328 | return 0; | |
329 | ||
330 | fail: | |
331 | xen_snd_front_evtchnl_free_all(front_info); | |
332 | return ret; | |
333 | } | |
334 | ||
335 | static int evtchnl_publish(struct xenbus_transaction xbt, | |
336 | struct xen_snd_front_evtchnl *channel, | |
337 | const char *path, const char *node_ring, | |
338 | const char *node_chnl) | |
339 | { | |
340 | struct xenbus_device *xb_dev = channel->front_info->xb_dev; | |
341 | int ret; | |
342 | ||
343 | /* Write control channel ring reference. */ | |
344 | ret = xenbus_printf(xbt, path, node_ring, "%u", channel->gref); | |
345 | if (ret < 0) { | |
346 | dev_err(&xb_dev->dev, "Error writing ring-ref: %d\n", ret); | |
347 | return ret; | |
348 | } | |
349 | ||
350 | /* Write event channel ring reference. */ | |
351 | ret = xenbus_printf(xbt, path, node_chnl, "%u", channel->port); | |
352 | if (ret < 0) { | |
353 | dev_err(&xb_dev->dev, "Error writing event channel: %d\n", ret); | |
354 | return ret; | |
355 | } | |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
360 | int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info) | |
361 | { | |
362 | struct xen_front_cfg_card *cfg = &front_info->cfg; | |
363 | struct xenbus_transaction xbt; | |
364 | int ret, d; | |
365 | ||
366 | again: | |
367 | ret = xenbus_transaction_start(&xbt); | |
368 | if (ret < 0) { | |
369 | xenbus_dev_fatal(front_info->xb_dev, ret, | |
370 | "starting transaction"); | |
371 | return ret; | |
372 | } | |
373 | ||
374 | for (d = 0; d < cfg->num_pcm_instances; d++) { | |
375 | struct xen_front_cfg_pcm_instance *pcm_instance; | |
376 | int s, index; | |
377 | ||
378 | pcm_instance = &cfg->pcm_instances[d]; | |
379 | ||
380 | for (s = 0; s < pcm_instance->num_streams_pb; s++) { | |
381 | index = pcm_instance->streams_pb[s].index; | |
382 | ||
383 | ret = evtchnl_publish(xbt, | |
384 | &front_info->evt_pairs[index].req, | |
385 | pcm_instance->streams_pb[s].xenstore_path, | |
386 | XENSND_FIELD_RING_REF, | |
387 | XENSND_FIELD_EVT_CHNL); | |
388 | if (ret < 0) | |
389 | goto fail; | |
390 | ||
391 | ret = evtchnl_publish(xbt, | |
392 | &front_info->evt_pairs[index].evt, | |
393 | pcm_instance->streams_pb[s].xenstore_path, | |
394 | XENSND_FIELD_EVT_RING_REF, | |
395 | XENSND_FIELD_EVT_EVT_CHNL); | |
396 | if (ret < 0) | |
397 | goto fail; | |
398 | } | |
399 | ||
400 | for (s = 0; s < pcm_instance->num_streams_cap; s++) { | |
401 | index = pcm_instance->streams_cap[s].index; | |
402 | ||
403 | ret = evtchnl_publish(xbt, | |
404 | &front_info->evt_pairs[index].req, | |
405 | pcm_instance->streams_cap[s].xenstore_path, | |
406 | XENSND_FIELD_RING_REF, | |
407 | XENSND_FIELD_EVT_CHNL); | |
408 | if (ret < 0) | |
409 | goto fail; | |
410 | ||
411 | ret = evtchnl_publish(xbt, | |
412 | &front_info->evt_pairs[index].evt, | |
413 | pcm_instance->streams_cap[s].xenstore_path, | |
414 | XENSND_FIELD_EVT_RING_REF, | |
415 | XENSND_FIELD_EVT_EVT_CHNL); | |
416 | if (ret < 0) | |
417 | goto fail; | |
418 | } | |
419 | } | |
420 | ret = xenbus_transaction_end(xbt, 0); | |
421 | if (ret < 0) { | |
422 | if (ret == -EAGAIN) | |
423 | goto again; | |
424 | ||
425 | xenbus_dev_fatal(front_info->xb_dev, ret, | |
426 | "completing transaction"); | |
427 | goto fail_to_end; | |
428 | } | |
429 | return 0; | |
430 | fail: | |
431 | xenbus_transaction_end(xbt, 1); | |
432 | fail_to_end: | |
433 | xenbus_dev_fatal(front_info->xb_dev, ret, "writing XenStore"); | |
434 | return ret; | |
435 | } | |
436 | ||
437 | void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, | |
438 | bool is_connected) | |
439 | { | |
440 | enum xen_snd_front_evtchnl_state state; | |
441 | ||
442 | if (is_connected) | |
443 | state = EVTCHNL_STATE_CONNECTED; | |
444 | else | |
445 | state = EVTCHNL_STATE_DISCONNECTED; | |
446 | ||
447 | mutex_lock(&evt_pair->req.ring_io_lock); | |
448 | evt_pair->req.state = state; | |
449 | mutex_unlock(&evt_pair->req.ring_io_lock); | |
450 | ||
451 | mutex_lock(&evt_pair->evt.ring_io_lock); | |
452 | evt_pair->evt.state = state; | |
453 | mutex_unlock(&evt_pair->evt.ring_io_lock); | |
454 | } | |
455 | ||
456 | void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair) | |
457 | { | |
458 | mutex_lock(&evt_pair->req.ring_io_lock); | |
459 | evt_pair->req.evt_next_id = 0; | |
460 | mutex_unlock(&evt_pair->req.ring_io_lock); | |
461 | ||
462 | mutex_lock(&evt_pair->evt.ring_io_lock); | |
463 | evt_pair->evt.evt_next_id = 0; | |
464 | mutex_unlock(&evt_pair->evt.ring_io_lock); | |
465 | } | |
466 |