From patchwork Mon May 14 06:27:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksandr Andrushchenko X-Patchwork-Id: 10396779 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1E3C960536 for ; Mon, 14 May 2018 06:28:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 08264287DE for ; Mon, 14 May 2018 06:28:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id F070F28878; Mon, 14 May 2018 06:28:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 86BFC287E7 for ; Mon, 14 May 2018 06:28:28 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 924482675D0; Mon, 14 May 2018 08:28:02 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 27D05267282; Mon, 14 May 2018 08:27:59 +0200 (CEST) Received: from mail-lf0-f66.google.com (mail-lf0-f66.google.com [209.85.215.66]) by alsa0.perex.cz (Postfix) with ESMTP id 0617E266DF0 for ; Mon, 14 May 2018 08:27:56 +0200 (CEST) Received: by mail-lf0-f66.google.com with SMTP id m17-v6so11007552lfj.8 for ; Sun, 13 May 2018 23:27:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=uuPhljMs2rRkLTjHukUf37JjnCCtjremMBbkXHpZcTQ=; b=QHTHCVayUXv8War+WFEtqBdzOdiLGHN2SCern4Yde+wAdMXHfb6/eaZWVRMr919/q6 un61D31W8mpwU0s6U8Y4YsB3Fyrsq9FcK/SUPy9j69YJH90euCzdKn3w2UHECmTKOhwB ozKOygr8JbPypsOnJ/xcRR/J70ipjbRsGN6B755HFiZtY2GK0dtFksqMMFLH2GzimKQs NUcKkcpCtcYZ+wByI7Kec0AZqysbjHnDBLsWuGQIDHEVqysxAR3weEE9H30HORk1MtXu C7XTaH0td9Z2Akrc8wJs3UU6su+UH3tqLD/U+4EmgOlnI2bQzEGGch6MgLgvYllDhyMC q0CQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=uuPhljMs2rRkLTjHukUf37JjnCCtjremMBbkXHpZcTQ=; b=oOz2Vpjzs2bDz8ZY+ke/VUh74daU02RfgLtDoefBN6VokoprlC7RRzMWeTWpCRAwE6 tAPHy9SBtxxIphR1di5ex7vm/gS9a44uzSf+A8d0HBjriiv//x4F+hO1yCyozbP6bhDI 7QOSrZLxlpmNvRdNJckrcyWahVYwjNhRioc+if8shNJxCOM8RBogl3J6VvQ4iaxWSWDV 2m/+2OVHl/HHSxMcEo25s907lJvRP5JfYEYngjlneqNtnlvx2T8lL/q9XWU1Ur8mmbdk 00Vwb1yWpypJ3ACB+6IeaBMpG365tDOZR+asi/MI02dBK4qwxMcPCfuTYjIVlfuKLrjk Shbw== X-Gm-Message-State: ALKqPwet3a7ai72HmarrlvQXiU8UNHJ+7FA76Q585Bem7v2bVUqkR5oj fmarbvabDl3GHTczHSmu/eY= X-Google-Smtp-Source: AB8JxZrqQWK4QTwyN6vXX0HesQ1k84uBOm1lNEaaP+KgYCPf6lgE1Nx3ivdCX/tjVqbiIaPwtLsddQ== X-Received: by 2002:a19:9bd2:: with SMTP id d201-v6mr7315321lfe.52.1526279275621; Sun, 13 May 2018 23:27:55 -0700 (PDT) Received: from a2k-HP-ProDesk-600-G2-SFF.kyiv.epam.com (ll-54.209.223.85.sovam.net.ua. [85.223.209.54]) by smtp.gmail.com with ESMTPSA id k127-v6sm2187860lfe.21.2018.05.13.23.27.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 13 May 2018 23:27:54 -0700 (PDT) From: Oleksandr Andrushchenko To: xen-devel@lists.xenproject.org, linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, jgross@suse.com, boris.ostrovsky@oracle.com, konrad.wilk@oracle.com, perex@perex.cz, tiwai@suse.com Date: Mon, 14 May 2018 09:27:39 +0300 Message-Id: <20180514062742.25879-4-andr2000@gmail.com> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180514062742.25879-1-andr2000@gmail.com> References: <20180514062742.25879-1-andr2000@gmail.com> Cc: andr2000@gmail.com, Oleksandr Andrushchenko Subject: [alsa-devel] [PATCH v3 3/6] ALSA: xen-front: Implement Xen event channel handling X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP From: Oleksandr Andrushchenko Handle Xen event channels: - create for all configured streams and publish corresponding ring references and event channels in Xen store, so backend can connect - implement event channels interrupt handlers - create and destroy event channels with respect to Xen bus state Signed-off-by: Oleksandr Andrushchenko --- sound/xen/Makefile | 3 +- sound/xen/xen_snd_front.c | 9 +- sound/xen/xen_snd_front.h | 5 + sound/xen/xen_snd_front_evtchnl.c | 494 ++++++++++++++++++++++++++++++ sound/xen/xen_snd_front_evtchnl.h | 95 ++++++ 5 files changed, 604 insertions(+), 2 deletions(-) create mode 100644 sound/xen/xen_snd_front_evtchnl.c create mode 100644 sound/xen/xen_snd_front_evtchnl.h diff --git a/sound/xen/Makefile b/sound/xen/Makefile index 06705bef61fa..03c669984000 100644 --- a/sound/xen/Makefile +++ b/sound/xen/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 OR MIT snd_xen_front-objs := xen_snd_front.o \ - xen_snd_front_cfg.o + xen_snd_front_cfg.o \ + xen_snd_front_evtchnl.o obj-$(CONFIG_SND_XEN_FRONTEND) += snd_xen_front.o diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index 70fa91683c71..277214d4fd0a 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -18,9 +18,11 @@ #include #include "xen_snd_front.h" +#include "xen_snd_front_evtchnl.h" static void xen_snd_drv_fini(struct xen_snd_front_info *front_info) { + xen_snd_front_evtchnl_free_all(front_info); } static int sndback_initwait(struct xen_snd_front_info *front_info) @@ -32,7 +34,12 @@ static int sndback_initwait(struct xen_snd_front_info *front_info) if (ret < 0) return ret; - return 0; + /* create event channels for all streams and publish */ + ret = xen_snd_front_evtchnl_create_all(front_info, num_streams); + if (ret < 0) + return ret; + + return xen_snd_front_evtchnl_publish_all(front_info); } static int sndback_connect(struct xen_snd_front_info *front_info) diff --git a/sound/xen/xen_snd_front.h b/sound/xen/xen_snd_front.h index b52226cb30bc..9d0c92100c7b 100644 --- a/sound/xen/xen_snd_front.h +++ b/sound/xen/xen_snd_front.h @@ -13,9 +13,14 @@ #include "xen_snd_front_cfg.h" +struct xen_snd_front_evtchnl_pair; + struct xen_snd_front_info { struct xenbus_device *xb_dev; + int num_evt_pairs; + struct xen_snd_front_evtchnl_pair *evt_pairs; + struct xen_front_cfg_card cfg; }; diff --git a/sound/xen/xen_snd_front_evtchnl.c b/sound/xen/xen_snd_front_evtchnl.c new file mode 100644 index 000000000000..1faafff08807 --- /dev/null +++ b/sound/xen/xen_snd_front_evtchnl.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#include +#include +#include +#include + +#include "xen_snd_front.h" +#include "xen_snd_front_cfg.h" +#include "xen_snd_front_evtchnl.h" + +static irqreturn_t evtchnl_interrupt_req(int irq, void *dev_id) +{ + struct xen_snd_front_evtchnl *channel = dev_id; + struct xen_snd_front_info *front_info = channel->front_info; + struct xensnd_resp *resp; + RING_IDX i, rp; + + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + + mutex_lock(&channel->ring_io_lock); + +again: + rp = channel->u.req.ring.sring->rsp_prod; + /* Ensure we see queued responses up to rp. */ + rmb(); + + /* + * Assume that the backend is trusted to always write sane values + * to the ring counters, so no overflow checks on frontend side + * are required. + */ + for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { + resp = RING_GET_RESPONSE(&channel->u.req.ring, i); + if (resp->id != channel->evt_id) + continue; + switch (resp->operation) { + case XENSND_OP_OPEN: + /* fall through */ + case XENSND_OP_CLOSE: + /* fall through */ + case XENSND_OP_READ: + /* fall through */ + case XENSND_OP_WRITE: + /* fall through */ + case XENSND_OP_TRIGGER: + channel->u.req.resp_status = resp->status; + complete(&channel->u.req.completion); + break; + case XENSND_OP_HW_PARAM_QUERY: + channel->u.req.resp_status = resp->status; + channel->u.req.resp.hw_param = + resp->resp.hw_param; + complete(&channel->u.req.completion); + break; + + default: + dev_err(&front_info->xb_dev->dev, + "Operation %d is not supported\n", + resp->operation); + break; + } + } + + channel->u.req.ring.rsp_cons = i; + if (i != channel->u.req.ring.req_prod_pvt) { + int more_to_do; + + RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring, + more_to_do); + if (more_to_do) + goto again; + } else { + channel->u.req.ring.sring->rsp_event = i + 1; + } + + mutex_unlock(&channel->ring_io_lock); + return IRQ_HANDLED; +} + +static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id) +{ + struct xen_snd_front_evtchnl *channel = dev_id; + struct xensnd_event_page *page = channel->u.evt.page; + u32 cons, prod; + + if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) + return IRQ_HANDLED; + + mutex_lock(&channel->ring_io_lock); + + prod = page->in_prod; + /* Ensure we see ring contents up to prod. */ + virt_rmb(); + if (prod == page->in_cons) + goto out; + + /* + * Assume that the backend is trusted to always write sane values + * to the ring counters, so no overflow checks on frontend side + * are required. + */ + for (cons = page->in_cons; cons != prod; cons++) { + struct xensnd_evt *event; + + event = &XENSND_IN_RING_REF(page, cons); + if (unlikely(event->id != channel->evt_id++)) + continue; + + switch (event->type) { + case XENSND_EVT_CUR_POS: + /* Do nothing at the moment. */ + break; + } + } + + page->in_cons = cons; + /* Ensure ring contents. */ + virt_wmb(); + +out: + mutex_unlock(&channel->ring_io_lock); + return IRQ_HANDLED; +} + +void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *channel) +{ + int notify; + + channel->u.req.ring.req_prod_pvt++; + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify); + if (notify) + notify_remote_via_irq(channel->irq); +} + +static void evtchnl_free(struct xen_snd_front_info *front_info, + struct xen_snd_front_evtchnl *channel) +{ + unsigned long page = 0; + + if (channel->type == EVTCHNL_TYPE_REQ) + page = (unsigned long)channel->u.req.ring.sring; + else if (channel->type == EVTCHNL_TYPE_EVT) + page = (unsigned long)channel->u.evt.page; + + if (!page) + return; + + channel->state = EVTCHNL_STATE_DISCONNECTED; + if (channel->type == EVTCHNL_TYPE_REQ) { + /* Release all who still waits for response if any. */ + channel->u.req.resp_status = -EIO; + complete_all(&channel->u.req.completion); + } + + if (channel->irq) + unbind_from_irqhandler(channel->irq, channel); + + if (channel->port) + xenbus_free_evtchn(front_info->xb_dev, channel->port); + + /* End access and free the page. */ + if (channel->gref != GRANT_INVALID_REF) + gnttab_end_foreign_access(channel->gref, 0, page); + else + free_page(page); + + memset(channel, 0, sizeof(*channel)); +} + +void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info) +{ + int i; + + if (!front_info->evt_pairs) + return; + + for (i = 0; i < front_info->num_evt_pairs; i++) { + evtchnl_free(front_info, &front_info->evt_pairs[i].req); + evtchnl_free(front_info, &front_info->evt_pairs[i].evt); + } + + kfree(front_info->evt_pairs); + front_info->evt_pairs = NULL; +} + +static int evtchnl_alloc(struct xen_snd_front_info *front_info, int index, + struct xen_snd_front_evtchnl *channel, + enum xen_snd_front_evtchnl_type type) +{ + struct xenbus_device *xb_dev = front_info->xb_dev; + unsigned long page; + grant_ref_t gref; + irq_handler_t handler; + char *handler_name = NULL; + int ret; + + memset(channel, 0, sizeof(*channel)); + channel->type = type; + channel->index = index; + channel->front_info = front_info; + channel->state = EVTCHNL_STATE_DISCONNECTED; + channel->gref = GRANT_INVALID_REF; + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto fail; + } + + handler_name = kasprintf(GFP_KERNEL, "%s-%s", XENSND_DRIVER_NAME, + type == EVTCHNL_TYPE_REQ ? + XENSND_FIELD_RING_REF : + XENSND_FIELD_EVT_RING_REF); + if (!handler_name) { + ret = -ENOMEM; + goto fail; + } + + mutex_init(&channel->ring_io_lock); + + if (type == EVTCHNL_TYPE_REQ) { + struct xen_sndif_sring *sring = (struct xen_sndif_sring *)page; + + init_completion(&channel->u.req.completion); + mutex_init(&channel->u.req.req_io_lock); + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&channel->u.req.ring, sring, XEN_PAGE_SIZE); + + ret = xenbus_grant_ring(xb_dev, sring, 1, &gref); + if (ret < 0) { + channel->u.req.ring.sring = NULL; + goto fail; + } + + handler = evtchnl_interrupt_req; + } else { + ret = gnttab_grant_foreign_access(xb_dev->otherend_id, + virt_to_gfn((void *)page), 0); + if (ret < 0) + goto fail; + + channel->u.evt.page = (struct xensnd_event_page *)page; + gref = ret; + handler = evtchnl_interrupt_evt; + } + + channel->gref = gref; + + ret = xenbus_alloc_evtchn(xb_dev, &channel->port); + if (ret < 0) + goto fail; + + ret = bind_evtchn_to_irq(channel->port); + if (ret < 0) { + dev_err(&xb_dev->dev, + "Failed to bind IRQ for domid %d port %d: %d\n", + front_info->xb_dev->otherend_id, channel->port, ret); + goto fail; + } + + channel->irq = ret; + + ret = request_threaded_irq(channel->irq, NULL, handler, + IRQF_ONESHOT, handler_name, channel); + if (ret < 0) { + dev_err(&xb_dev->dev, "Failed to request IRQ %d: %d\n", + channel->irq, ret); + goto fail; + } + + kfree(handler_name); + return 0; + +fail: + if (page) + free_page(page); + kfree(handler_name); + dev_err(&xb_dev->dev, "Failed to allocate ring: %d\n", ret); + return ret; +} + +int xen_snd_front_evtchnl_create_all(struct xen_snd_front_info *front_info, + int num_streams) +{ + struct xen_front_cfg_card *cfg = &front_info->cfg; + struct device *dev = &front_info->xb_dev->dev; + int d, ret = 0; + + front_info->evt_pairs = + kcalloc(num_streams, + sizeof(struct xen_snd_front_evtchnl_pair), + GFP_KERNEL); + if (!front_info->evt_pairs) + return -ENOMEM; + + /* Iterate over devices and their streams and create event channels. */ + for (d = 0; d < cfg->num_pcm_instances; d++) { + struct xen_front_cfg_pcm_instance *pcm_instance; + int s, index; + + pcm_instance = &cfg->pcm_instances[d]; + + for (s = 0; s < pcm_instance->num_streams_pb; s++) { + index = pcm_instance->streams_pb[s].index; + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].req, + EVTCHNL_TYPE_REQ); + if (ret < 0) { + dev_err(dev, "Error allocating control channel\n"); + goto fail; + } + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].evt, + EVTCHNL_TYPE_EVT); + if (ret < 0) { + dev_err(dev, "Error allocating in-event channel\n"); + goto fail; + } + } + + for (s = 0; s < pcm_instance->num_streams_cap; s++) { + index = pcm_instance->streams_cap[s].index; + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].req, + EVTCHNL_TYPE_REQ); + if (ret < 0) { + dev_err(dev, "Error allocating control channel\n"); + goto fail; + } + + ret = evtchnl_alloc(front_info, index, + &front_info->evt_pairs[index].evt, + EVTCHNL_TYPE_EVT); + if (ret < 0) { + dev_err(dev, "Error allocating in-event channel\n"); + goto fail; + } + } + } + if (ret < 0) + goto fail; + + front_info->num_evt_pairs = num_streams; + return 0; + +fail: + xen_snd_front_evtchnl_free_all(front_info); + return ret; +} + +static int evtchnl_publish(struct xenbus_transaction xbt, + struct xen_snd_front_evtchnl *channel, + const char *path, const char *node_ring, + const char *node_chnl) +{ + struct xenbus_device *xb_dev = channel->front_info->xb_dev; + int ret; + + /* Write control channel ring reference. */ + ret = xenbus_printf(xbt, path, node_ring, "%u", channel->gref); + if (ret < 0) { + dev_err(&xb_dev->dev, "Error writing ring-ref: %d\n", ret); + return ret; + } + + /* Write event channel ring reference. */ + ret = xenbus_printf(xbt, path, node_chnl, "%u", channel->port); + if (ret < 0) { + dev_err(&xb_dev->dev, "Error writing event channel: %d\n", ret); + return ret; + } + + return 0; +} + +int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info) +{ + struct xen_front_cfg_card *cfg = &front_info->cfg; + struct xenbus_transaction xbt; + int ret, d; + +again: + ret = xenbus_transaction_start(&xbt); + if (ret < 0) { + xenbus_dev_fatal(front_info->xb_dev, ret, + "starting transaction"); + return ret; + } + + for (d = 0; d < cfg->num_pcm_instances; d++) { + struct xen_front_cfg_pcm_instance *pcm_instance; + int s, index; + + pcm_instance = &cfg->pcm_instances[d]; + + for (s = 0; s < pcm_instance->num_streams_pb; s++) { + index = pcm_instance->streams_pb[s].index; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].req, + pcm_instance->streams_pb[s].xenstore_path, + XENSND_FIELD_RING_REF, + XENSND_FIELD_EVT_CHNL); + if (ret < 0) + goto fail; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].evt, + pcm_instance->streams_pb[s].xenstore_path, + XENSND_FIELD_EVT_RING_REF, + XENSND_FIELD_EVT_EVT_CHNL); + if (ret < 0) + goto fail; + } + + for (s = 0; s < pcm_instance->num_streams_cap; s++) { + index = pcm_instance->streams_cap[s].index; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].req, + pcm_instance->streams_cap[s].xenstore_path, + XENSND_FIELD_RING_REF, + XENSND_FIELD_EVT_CHNL); + if (ret < 0) + goto fail; + + ret = evtchnl_publish(xbt, + &front_info->evt_pairs[index].evt, + pcm_instance->streams_cap[s].xenstore_path, + XENSND_FIELD_EVT_RING_REF, + XENSND_FIELD_EVT_EVT_CHNL); + if (ret < 0) + goto fail; + } + } + ret = xenbus_transaction_end(xbt, 0); + if (ret < 0) { + if (ret == -EAGAIN) + goto again; + + xenbus_dev_fatal(front_info->xb_dev, ret, + "completing transaction"); + goto fail_to_end; + } + return 0; +fail: + xenbus_transaction_end(xbt, 1); +fail_to_end: + xenbus_dev_fatal(front_info->xb_dev, ret, "writing XenStore"); + return ret; +} + +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected) +{ + enum xen_snd_front_evtchnl_state state; + + if (is_connected) + state = EVTCHNL_STATE_CONNECTED; + else + state = EVTCHNL_STATE_DISCONNECTED; + + mutex_lock(&evt_pair->req.ring_io_lock); + evt_pair->req.state = state; + mutex_unlock(&evt_pair->req.ring_io_lock); + + mutex_lock(&evt_pair->evt.ring_io_lock); + evt_pair->evt.state = state; + mutex_unlock(&evt_pair->evt.ring_io_lock); +} + +void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair) +{ + mutex_lock(&evt_pair->req.ring_io_lock); + evt_pair->req.evt_next_id = 0; + mutex_unlock(&evt_pair->req.ring_io_lock); + + mutex_lock(&evt_pair->evt.ring_io_lock); + evt_pair->evt.evt_next_id = 0; + mutex_unlock(&evt_pair->evt.ring_io_lock); +} + diff --git a/sound/xen/xen_snd_front_evtchnl.h b/sound/xen/xen_snd_front_evtchnl.h new file mode 100644 index 000000000000..cbe51fd1ec15 --- /dev/null +++ b/sound/xen/xen_snd_front_evtchnl.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ + +/* + * Xen para-virtual sound device + * + * Copyright (C) 2016-2018 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#ifndef __XEN_SND_FRONT_EVTCHNL_H +#define __XEN_SND_FRONT_EVTCHNL_H + +#include + +struct xen_snd_front_info; + +#ifndef GRANT_INVALID_REF +/* + * FIXME: usage of grant reference 0 as invalid grant reference: + * grant reference 0 is valid, but never exposed to a PV driver, + * because of the fact it is already in use/reserved by the PV console. + */ +#define GRANT_INVALID_REF 0 +#endif + +/* Timeout in ms to wait for backend to respond. */ +#define VSND_WAIT_BACK_MS 3000 + +enum xen_snd_front_evtchnl_state { + EVTCHNL_STATE_DISCONNECTED, + EVTCHNL_STATE_CONNECTED, +}; + +enum xen_snd_front_evtchnl_type { + EVTCHNL_TYPE_REQ, + EVTCHNL_TYPE_EVT, +}; + +struct xen_snd_front_evtchnl { + struct xen_snd_front_info *front_info; + int gref; + int port; + int irq; + int index; + /* State of the event channel. */ + enum xen_snd_front_evtchnl_state state; + enum xen_snd_front_evtchnl_type type; + /* Either response id or incoming event id. */ + u16 evt_id; + /* Next request id or next expected event id. */ + u16 evt_next_id; + /* Shared ring access lock. */ + struct mutex ring_io_lock; + union { + struct { + struct xen_sndif_front_ring ring; + struct completion completion; + /* Serializer for backend IO: request/response. */ + struct mutex req_io_lock; + + /* Latest response status. */ + int resp_status; + union { + struct xensnd_query_hw_param hw_param; + } resp; + } req; + struct { + struct xensnd_event_page *page; + /* This is needed to handle XENSND_EVT_CUR_POS event. */ + struct snd_pcm_substream *substream; + } evt; + } u; +}; + +struct xen_snd_front_evtchnl_pair { + struct xen_snd_front_evtchnl req; + struct xen_snd_front_evtchnl evt; +}; + +int xen_snd_front_evtchnl_create_all(struct xen_snd_front_info *front_info, + int num_streams); + +void xen_snd_front_evtchnl_free_all(struct xen_snd_front_info *front_info); + +int xen_snd_front_evtchnl_publish_all(struct xen_snd_front_info *front_info); + +void xen_snd_front_evtchnl_flush(struct xen_snd_front_evtchnl *evtchnl); + +void xen_snd_front_evtchnl_pair_set_connected(struct xen_snd_front_evtchnl_pair *evt_pair, + bool is_connected); + +void xen_snd_front_evtchnl_pair_clear(struct xen_snd_front_evtchnl_pair *evt_pair); + +#endif /* __XEN_SND_FRONT_EVTCHNL_H */