diff options
author | Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> | 2018-05-14 09:27:41 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-05-16 12:59:57 +0200 |
commit | 1cee559351a7cb57b405554bac10a6f33c28ed09 (patch) | |
tree | 13c0dc77c467e9e69681ac2138a5396a29f40924 /sound/xen/xen_snd_front.c | |
parent | d6e0fbb82e73a01e4cb3631b8b3dd7aae09ab14c (diff) |
ALSA: xen-front: Implement ALSA virtual sound driver
Implement essential initialization of the sound driver:
- introduce required data structures
- handle driver registration
- handle sound card registration
- register sound driver on backend connection
- remove sound driver on backend disconnect
Initialize virtual sound card with streams according to the
Xen store configuration.
Implement ALSA driver operations including:
- manage frontend/backend shared buffers
- manage Xen bus event channel states
Implement requests from front to back for ALSA
PCM operations.
- report ALSA period elapsed event: handle XENSND_EVT_CUR_POS
notifications from the backend when stream position advances
during playback/capture. The event carries a value of how
many octets were played/captured at the time of the event.
- implement explicit stream parameter negotiation between
backend and frontend: handle XENSND_OP_HW_PARAM_QUERY request
to read/update configuration space for the parameter given:
request passes desired parameter interval and the response to
this request returns min/max interval for the parameter to be used.
Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/xen/xen_snd_front.c')
-rw-r--r-- | sound/xen/xen_snd_front.c | 181 |
1 files changed, 180 insertions, 1 deletions
diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c index cdf66ea516c4..c18973a9bc9b 100644 --- a/sound/xen/xen_snd_front.c +++ b/sound/xen/xen_snd_front.c @@ -19,10 +19,189 @@ #include <xen/interface/io/sndif.h> #include "xen_snd_front.h" +#include "xen_snd_front_alsa.h" #include "xen_snd_front_evtchnl.h" +#include "xen_snd_front_shbuf.h" + +static struct xensnd_req * +be_stream_prepare_req(struct xen_snd_front_evtchnl *evtchnl, u8 operation) +{ + struct xensnd_req *req; + + req = RING_GET_REQUEST(&evtchnl->u.req.ring, + evtchnl->u.req.ring.req_prod_pvt); + req->operation = operation; + req->id = evtchnl->evt_next_id++; + evtchnl->evt_id = req->id; + return req; +} + +static int be_stream_do_io(struct xen_snd_front_evtchnl *evtchnl) +{ + if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) + return -EIO; + + reinit_completion(&evtchnl->u.req.completion); + xen_snd_front_evtchnl_flush(evtchnl); + return 0; +} + +static int be_stream_wait_io(struct xen_snd_front_evtchnl *evtchnl) +{ + if (wait_for_completion_timeout(&evtchnl->u.req.completion, + msecs_to_jiffies(VSND_WAIT_BACK_MS)) <= 0) + return -ETIMEDOUT; + + return evtchnl->u.req.resp_status; +} + +int xen_snd_front_stream_query_hw_param(struct xen_snd_front_evtchnl *evtchnl, + struct xensnd_query_hw_param *hw_param_req, + struct xensnd_query_hw_param *hw_param_resp) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_HW_PARAM_QUERY); + req->op.hw_param = *hw_param_req; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + if (ret == 0) + *hw_param_resp = evtchnl->u.req.resp.hw_param; + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_prepare(struct xen_snd_front_evtchnl *evtchnl, + struct xen_snd_front_shbuf *sh_buf, + u8 format, unsigned int channels, + unsigned int rate, u32 buffer_sz, + u32 period_sz) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_OPEN); + req->op.open.pcm_format = format; + req->op.open.pcm_channels = channels; + req->op.open.pcm_rate = rate; + req->op.open.buffer_sz = buffer_sz; + req->op.open.period_sz = period_sz; + req->op.open.gref_directory = xen_snd_front_shbuf_get_dir_start(sh_buf); + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_close(struct xen_snd_front_evtchnl *evtchnl) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_CLOSE); + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_write(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_WRITE); + req->op.rw.length = count; + req->op.rw.offset = pos; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_read(struct xen_snd_front_evtchnl *evtchnl, + unsigned long pos, unsigned long count) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_READ); + req->op.rw.length = count; + req->op.rw.offset = pos; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} + +int xen_snd_front_stream_trigger(struct xen_snd_front_evtchnl *evtchnl, + int type) +{ + struct xensnd_req *req; + int ret; + + mutex_lock(&evtchnl->u.req.req_io_lock); + + mutex_lock(&evtchnl->ring_io_lock); + req = be_stream_prepare_req(evtchnl, XENSND_OP_TRIGGER); + req->op.trigger.type = type; + mutex_unlock(&evtchnl->ring_io_lock); + + ret = be_stream_do_io(evtchnl); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + mutex_unlock(&evtchnl->u.req.req_io_lock); + return ret; +} static void xen_snd_drv_fini(struct xen_snd_front_info *front_info) { + xen_snd_front_alsa_fini(front_info); xen_snd_front_evtchnl_free_all(front_info); } @@ -45,7 +224,7 @@ static int sndback_initwait(struct xen_snd_front_info *front_info) static int sndback_connect(struct xen_snd_front_info *front_info) { - return 0; + return xen_snd_front_alsa_init(front_info); } static void sndback_disconnect(struct xen_snd_front_info *front_info) |