From mboxrd@z Thu Jan 1 00:00:00 1970 From: Oleksandr Dmytryshyn Subject: [PATCH 3/3] xen-sndfront: add capture support Date: Mon, 19 Jan 2015 10:19:34 +0200 Message-ID: <1421655574-10038-4-git-send-email-oleksandr.dmytryshyn@globallogic.com> References: <1421655574-10038-1-git-send-email-oleksandr.dmytryshyn@globallogic.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1421655574-10038-1-git-send-email-oleksandr.dmytryshyn@globallogic.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org To: xen-devel@lists.xen.org Cc: Takashi Iwai , Iurii Konovalenko , Jaroslav Kysela List-Id: xen-devel@lists.xenproject.org From: Iurii Konovalenko Now both play and capture is supported. Signed-off-by: Iurii Konovalenko Signed-off-by: Oleksandr Dmytryshyn --- include/xen/interface/io/sndif.h | 93 +++++++++++++--- sound/drivers/xen-sndfront.c | 222 ++++++++++++++++++++++++++++++--------- 2 files changed, 252 insertions(+), 63 deletions(-) diff --git a/include/xen/interface/io/sndif.h b/include/xen/interface/io/sndif.h index 2fae4df..dafd90b 100644 --- a/include/xen/interface/io/sndif.h +++ b/include/xen/interface/io/sndif.h @@ -11,17 +11,70 @@ #include /* + * PCM FORMATS. + */ +#define SNDIF_PCM_FORMAT_S8 (0) +#define SNDIF_PCM_FORMAT_U8 (1) +#define SNDIF_PCM_FORMAT_S16_LE (2) +#define SNDIF_PCM_FORMAT_S16_BE (3) +#define SNDIF_PCM_FORMAT_U16_LE (4) +#define SNDIF_PCM_FORMAT_U16_BE (5) + +/* low three bytes */ +#define SNDIF_PCM_FORMAT_S24_LE (6) + +/* low three bytes */ +#define SNDIF_PCM_FORMAT_S24_BE (7) + +/* low three bytes */ +#define SNDIF_PCM_FORMAT_U24_LE (8) + +/* low three bytes */ +#define SNDIF_PCM_FORMAT_U24_BE (9) + +#define SNDIF_PCM_FORMAT_S32_LE (10) +#define SNDIF_PCM_FORMAT_S32_BE (11) +#define SNDIF_PCM_FORMAT_U32_LE (12) +#define SNDIF_PCM_FORMAT_U32_BE (13) + +/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDIF_PCM_FORMAT_FLOAT_LE (14) + +/* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ +#define SNDIF_PCM_FORMAT_FLOAT_BE (15) + +/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDIF_PCM_FORMAT_FLOAT64_LE (16) + +/* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ +#define SNDIF_PCM_FORMAT_FLOAT64_BE (17) + +/* IEC-958 subframe, Little Endian */ +#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_LE (18) + +/* IEC-958 subframe, Big Endian */ +#define SNDIF_PCM_FORMAT_IEC958_SUBFRAME_BE (19) + +#define SNDIF_PCM_FORMAT_MU_LAW (20) +#define SNDIF_PCM_FORMAT_A_LAW (21) +#define SNDIF_PCM_FORMAT_IMA_ADPCM (22) +#define SNDIF_PCM_FORMAT_MPEG (23) +#define SNDIF_PCM_FORMAT_GSM (24) +#define SNDIF_PCM_FORMAT_SPECIAL (31) + +/* * REQUEST CODES. */ #define SNDIF_OP_OPEN 0 #define SNDIF_OP_CLOSE 1 #define SNDIF_OP_READ 2 #define SNDIF_OP_WRITE 3 -#define SNDIF_OP_IOCTL 4 +#define SNDIF_SET_VOLUME 4 +#define SNDIF_GET_VOLUME 5 #define SNDIF_MAX_PAGES_PER_REQUEST 10 -#define SNDIF_DEV_ID_CNT 5 +#define SNDIF_DEV_ID_CNT 2 /* * STATUS RETURN CODES. @@ -32,39 +85,47 @@ #define SNDIF_RSP_OKAY 0 struct alsa_hwparams { - snd_pcm_format_t format; - unsigned int channels; - unsigned int rate; + uint32_t format; + uint32_t channels; + uint32_t rate; }; +struct sndif_request_common { + uint64_t id; /* private guest value, echoed in resp */ + struct alsa_hwparams _pad1; + uint32_t _pad2; + uint32_t _pad3; +} __attribute__((__packed__)); + struct sndif_request_open { - struct alsa_hwparams hwparams; - unsigned int _pad1; uint64_t id; /* private guest value, echoed in resp */ - unsigned int _pad2; + struct alsa_hwparams hwparams; + uint32_t stream; + uint32_t _pad2; } __attribute__((__packed__)); struct sndif_request_rw { - struct alsa_hwparams _pad1; - unsigned int _pad2; uint64_t id; /* private guest value, echoed in resp */ - unsigned int len; + struct alsa_hwparams _pad1; + uint32_t len; + uint32_t _pad2; grant_ref_t gref[SNDIF_MAX_PAGES_PER_REQUEST]; } __attribute__((__packed__)); -struct sndif_request_common { - struct alsa_hwparams _pad1; - unsigned int _pad2; +struct sndif_request_volume { uint64_t id; /* private guest value, echoed in resp */ - unsigned int _pad3; + struct alsa_hwparams _pad1; + uint32_t left; + uint32_t right; } __attribute__((__packed__)); struct sndif_request { uint8_t operation; /* SNDIF_OP_??? */ union { + struct sndif_request_common common; struct sndif_request_open open; struct sndif_request_rw rw; - struct sndif_request_common common; + struct sndif_request_volume vol; } u; } __attribute__((__packed__)); diff --git a/sound/drivers/xen-sndfront.c b/sound/drivers/xen-sndfront.c index da048fc..4bb02ec 100644 --- a/sound/drivers/xen-sndfront.c +++ b/sound/drivers/xen-sndfront.c @@ -95,8 +95,7 @@ MODULE_PARM_DESC(model, "Soundcard model."); #define MIXER_ADDR_LAST MIXER_ADDR_MASTER_OUT struct vsnd_card { - struct sndfront_info *fr_info; - unsigned int dev_id; + unsigned int stream_id; grant_ref_t grefs[SNDIF_MAX_PAGES_PER_REQUEST]; unsigned char *buf; }; @@ -120,7 +119,13 @@ struct sndfront_info { unsigned int evtchn, irq; struct vsnd_card *vcard; int bret_code; +}; + +struct vsnd_sndfront { + int count; + spinlock_t lock; /* protect 'count' member */ struct platform_device *card_dev; + struct sndfront_info *infos[2]; }; #define GRANT_INVALID_REF 0 @@ -157,7 +162,6 @@ struct snd_virtualcard { spinlock_t mixer_lock; /* protect mixer settings */ int mixer_volume[MIXER_ADDR_LAST+1][2]; int capture_source[MIXER_ADDR_LAST+1][2]; - struct sndfront_info *fr_info; struct stream_info streams[2]; }; @@ -203,15 +207,33 @@ static struct snd_pcm_hardware virtualcard_pcm_hardware = { .fifo_size = 0, }; +static struct vsnd_sndfront snd_fronts; + static inline struct stream_info *get_vcard_stream(struct snd_virtualcard *virtualcard, - struct snd_pcm_substream *substream) { + struct snd_pcm_substream *substream) +{ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) return &virtualcard->streams[0]; else return &virtualcard->streams[1]; } +static inline +struct sndfront_info *get_sndfront_info(struct snd_pcm_substream *substream) +{ + struct sndfront_info *res = NULL; + int stream = substream->stream; + + spin_lock_irq(&snd_fronts.lock); + if ((stream == SNDRV_PCM_STREAM_PLAYBACK) && (snd_fronts.count > 0)) + res = snd_fronts.infos[0]; + else if ((stream == SNDRV_PCM_STREAM_CAPTURE) && (snd_fronts.count > 1)) + res = snd_fronts.infos[1]; + spin_unlock_irq(&snd_fronts.lock); + + return res; +} static unsigned long vmalloc_to_mfn(void *address) { @@ -231,7 +253,8 @@ static inline void flush_requests(struct sndfront_info *info) static int sndif_queue_request_open(struct sndfront_info *info, snd_pcm_format_t format, unsigned int channels, - unsigned int rate) + unsigned int rate, + unsigned int stream) { struct sndif_request *req; @@ -241,10 +264,11 @@ static int sndif_queue_request_open(struct sndfront_info *info, req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); req->operation = SNDIF_OP_OPEN; - req->u.open.id = info->vcard->dev_id; + req->u.open.id = info->vcard->stream_id; req->u.open.hwparams.format = format; req->u.open.hwparams.channels = channels; req->u.open.hwparams.rate = rate; + req->u.open.stream = stream; info->ring.req_prod_pvt++; flush_requests(info); @@ -261,7 +285,7 @@ static int sndif_queue_request_close(struct sndfront_info *info) req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); req->operation = SNDIF_OP_CLOSE; - req->u.open.id = info->vcard->dev_id; + req->u.open.id = info->vcard->stream_id; info->ring.req_prod_pvt++; @@ -282,7 +306,35 @@ static int sndif_queue_request_write(struct sndfront_info *info, req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); req->operation = SNDIF_OP_WRITE; - req->u.rw.id = info->vcard->dev_id; + req->u.rw.id = info->vcard->stream_id; + + req->u.rw.len = len; + + gref = info->vcard->grefs; + + for (i = 0; i < SNDIF_MAX_PAGES_PER_REQUEST; i++) + req->u.rw.gref[i] = gref[i]; + + info->ring.req_prod_pvt++; + + flush_requests(info); + return 0; +} + +static int sndif_queue_request_read(struct sndfront_info *info, + unsigned int len) +{ + struct sndif_request *req; + grant_ref_t *gref; + int i; + + if (unlikely(info->connected != SNDIF_STATE_CONNECTED)) + return 1; + + req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); + + req->operation = SNDIF_OP_READ; + req->u.rw.id = info->vcard->stream_id; req->u.rw.len = len; @@ -300,13 +352,17 @@ static int sndif_queue_request_write(struct sndfront_info *info, static int alsa_pcm_open(struct sndfront_info *info, snd_pcm_format_t format, unsigned int channels, - unsigned int rate) + unsigned int rate, + unsigned int stream) { unsigned long answer_tout; + if (!info) + return -EFAULT; + reinit_completion(&info->completion); - if (sndif_queue_request_open(info, format, channels, rate)) + if (sndif_queue_request_open(info, format, channels, rate, stream)) return -EIO; answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); @@ -321,6 +377,9 @@ static int alsa_pcm_close(struct sndfront_info *info) { unsigned long answer_tout; + if (!info) + return -EFAULT; + reinit_completion(&info->completion); if (sndif_queue_request_close(info)) @@ -340,6 +399,9 @@ static int alsa_pcm_write(struct sndfront_info *info, char __user *buf, unsigned char *shared_data; unsigned long answer_tout; + if (!info) + return -EFAULT; + shared_data = info->vcard->buf; if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) @@ -361,6 +423,35 @@ static int alsa_pcm_write(struct sndfront_info *info, char __user *buf, return info->bret_code; } +static int alsa_pcm_read(struct sndfront_info *info, char __user *buf, + int len) +{ + unsigned char *shared_data; + unsigned long answer_tout; + + if (!info) + return -EFAULT; + + shared_data = info->vcard->buf; + + if (len > PAGE_SIZE * SNDIF_MAX_PAGES_PER_REQUEST) + return -EFAULT; + + reinit_completion(&info->completion); + + if (sndif_queue_request_read(info, len)) + return -EIO; + + answer_tout = msecs_to_jiffies(VSND_WAIT_ANSWER_TOUT); + if (wait_for_completion_interruptible_timeout(&info->completion, + answer_tout) <= 0) + return -ETIMEDOUT; + + if (copy_to_user(buf, shared_data, len)) + return -EFAULT; + + return info->bret_code; +} static int alsa_pcm_silence(struct sndfront_info *info, int len) { unsigned char *shared_data; @@ -443,7 +534,7 @@ static void sndif_cleanup_vcard(struct sndfront_info *info) } static int sndif_add_virt_devices(struct sndfront_info *info, - unsigned int dev_id) + unsigned int stream_id) { int ret = 0; @@ -454,8 +545,7 @@ static int sndif_add_virt_devices(struct sndfront_info *info, if (!vcard) return -ENOMEM; - vcard->dev_id = dev_id; - vcard->fr_info = info; + vcard->stream_id = stream_id; info->vcard = vcard; @@ -517,6 +607,7 @@ static irqreturn_t sndif_interrupt(int irq, void *data) case SNDIF_OP_OPEN: case SNDIF_OP_CLOSE: case SNDIF_OP_WRITE: + case SNDIF_OP_READ: if (unlikely(bret->status != SNDIF_RSP_OKAY)) dev_dbg(&info->xbdev->dev, "snddev data request error: %x\n", @@ -674,16 +765,19 @@ static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream) if ((runtime->rate != vcard_stream->rate) || (runtime->channels != vcard_stream->channels) || (runtime->format != vcard_stream->format)) { + struct sndfront_info *info = get_sndfront_info(substream); + if (vcard_stream->opened) { - err = alsa_pcm_close(virtualcard->fr_info); + err = alsa_pcm_close(info); if (err) return err; /* if closed successfully */ vcard_stream->opened = false; } - err = alsa_pcm_open(virtualcard->fr_info, runtime->format, - runtime->channels, runtime->rate); + + err = alsa_pcm_open(info, runtime->format, runtime->channels, + runtime->rate, substream->stream); if (err) return err; @@ -696,26 +790,36 @@ static int virtualcard_pcm_prepare(struct snd_pcm_substream *substream) return 0; } -static -snd_pcm_uframes_t virtualcard_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t +virtualcard_pcm_playback_pointer(struct snd_pcm_substream *substream) { struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); snd_pcm_uframes_t buff_size = substream->runtime->buffer_size; - struct stream_info *vcard_stream; - vcard_stream = get_vcard_stream(virtualcard, substream); - - if (vcard_stream->crossed) { + if (virtualcard->streams[0].crossed) { snd_pcm_uframes_t hw_base = substream->runtime->hw_ptr_base; - hw_base += buff_size; if (hw_base >= substream->runtime->boundary) hw_base = 0; substream->runtime->hw_ptr_base = hw_base; - vcard_stream->crossed = 0; + virtualcard->streams[0].crossed = 0; } - return vcard_stream->position; + return virtualcard->streams[0].position; +} + +static snd_pcm_uframes_t +virtualcard_pcm_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + snd_pcm_uframes_t buff_size = substream->runtime->buffer_size, res; + +#ifdef MAX_CAPTURE_SIZE + res = (virtualcard->streams[1].position + MAX_CAPTURE_SIZE) % buff_size; +#else + res = (virtualcard->streams[1].position + buff_size / 2) % buff_size; +#endif + return res; } static int virtualcard_pcm_hw_params(struct snd_pcm_substream *substream, @@ -760,8 +864,9 @@ static int virtualcard_pcm_close(struct snd_pcm_substream *substream) { int err; struct snd_virtualcard *virtualcard = snd_pcm_substream_chip(substream); + struct sndfront_info *info = get_sndfront_info(substream); - err = alsa_pcm_close(virtualcard->fr_info); + err = alsa_pcm_close(info); if (err) return err; @@ -779,11 +884,11 @@ static int virtualcard_pcm_playback_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct stream_info *vcard_stream = &virtualcard->streams[0]; snd_pcm_uframes_t stream_pos = vcard_stream->position; + struct sndfront_info *info = get_sndfront_info(substream); vcard_stream->position = (stream_pos + count) % runtime->buffer_size; vcard_stream->crossed = count / runtime->buffer_size; - return alsa_pcm_write(virtualcard->fr_info, src, - frames_to_bytes(runtime, count)); + return alsa_pcm_write(info, src, frames_to_bytes(runtime, count)); } static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream, @@ -794,12 +899,12 @@ static int virtualcard_pcm_playback_silence(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct stream_info *vcard_stream = &virtualcard->streams[0]; snd_pcm_uframes_t stream_pos = vcard_stream->position; + struct sndfront_info *info = get_sndfront_info(substream); vcard_stream->position = (stream_pos + count) % runtime->buffer_size; vcard_stream->crossed = count / runtime->buffer_size; - return alsa_pcm_silence(virtualcard->fr_info, - frames_to_bytes(runtime, count)); + return alsa_pcm_silence(info, frames_to_bytes(runtime, count)); } static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream, @@ -811,11 +916,12 @@ static int virtualcard_pcm_capture_copy(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct stream_info *vcard_stream = &virtualcard->streams[1]; snd_pcm_uframes_t stream_pos = vcard_stream->position; + struct sndfront_info *info = get_sndfront_info(substream); vcard_stream->position = (stream_pos + count) % runtime->buffer_size; vcard_stream->crossed = count / runtime->buffer_size; - return 0; + return alsa_pcm_read(info, dst, frames_to_bytes(runtime, count)); } static struct snd_pcm_ops virtualcard_pcm_playback_ops = { @@ -826,7 +932,7 @@ static struct snd_pcm_ops virtualcard_pcm_playback_ops = { .hw_free = virtualcard_pcm_hw_free, .prepare = virtualcard_pcm_prepare, .trigger = virtualcard_pcm_trigger, - .pointer = virtualcard_pcm_pointer, + .pointer = virtualcard_pcm_playback_pointer, .copy = virtualcard_pcm_playback_copy, .silence = virtualcard_pcm_playback_silence, }; @@ -839,7 +945,7 @@ static struct snd_pcm_ops virtualcard_pcm_capture_ops = { .hw_free = virtualcard_pcm_hw_free, .prepare = virtualcard_pcm_prepare, .trigger = virtualcard_pcm_trigger, - .pointer = virtualcard_pcm_pointer, + .pointer = virtualcard_pcm_capture_pointer, .copy = virtualcard_pcm_capture_copy, }; @@ -1157,8 +1263,6 @@ static int snd_virtualcard_probe(struct platform_device *devptr) return err; virtualcard = card->private_data; virtualcard->card = card; - virtualcard->fr_info = - (*((struct sndfront_info **)devptr->dev.platform_data)); m = virtualcard_models[0]; @@ -1268,7 +1372,7 @@ static struct platform_driver snd_virtualcard_driver = { static int sndfront_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { - int err; + int err, count; struct sndfront_info *info; info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -1288,12 +1392,26 @@ static int sndfront_probe(struct xenbus_device *dev, if (err) goto err_free_info; - info->card_dev = platform_device_register_data(NULL, - SND_VIRTUALCARD_DRIVER, - -1, &info, sizeof(info)); - if (IS_ERR(info->card_dev)) { - err = -ENODEV; - goto err_free_info; + spin_lock_irq(&snd_fronts.lock); + snd_fronts.infos[snd_fronts.count] = info; + snd_fronts.count++; + count = snd_fronts.count; + spin_unlock_irq(&snd_fronts.lock); + + if (count == 1) { + struct platform_device *card_dev; + + card_dev = platform_device_register_data(NULL, + SND_VIRTUALCARD_DRIVER, + -1, NULL, 0); + snd_fronts.card_dev = card_dev; + if (IS_ERR(snd_fronts.card_dev)) { + spin_lock_irq(&snd_fronts.lock); + snd_fronts.count = 0; + spin_unlock_irq(&snd_fronts.lock); + err = -ENODEV; + goto err_free_info; + } } return 0; @@ -1349,7 +1467,7 @@ sndfront_closing(struct sndfront_info *info) */ static void sndfront_connect(struct sndfront_info *info) { - unsigned int dev_id; + unsigned int stream_id; int err; switch (info->connected) { @@ -1368,12 +1486,12 @@ static void sndfront_connect(struct sndfront_info *info) xenbus_switch_state(info->xbdev, XenbusStateConnected); - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "dev_id", "%u", - &dev_id, NULL); + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, "stream_id", "%u", + &stream_id, NULL); if (err) return; - err = sndif_add_virt_devices(info, dev_id); + err = sndif_add_virt_devices(info, stream_id); if (err) return; @@ -1415,11 +1533,19 @@ static void sndback_changed(struct xenbus_device *dev, static int sndfront_remove(struct xenbus_device *xbdev) { + int count; struct sndfront_info *info = dev_get_drvdata(&xbdev->dev); dev_dbg(&xbdev->dev, "%s removed", xbdev->nodename); - platform_device_unregister(info->card_dev); + spin_lock_irq(&snd_fronts.lock); + snd_fronts.count--; + count = snd_fronts.count; + snd_fronts.infos[count] = NULL; + spin_lock_irq(&snd_fronts.lock); + + if (!count) + platform_device_unregister(snd_fronts.card_dev); sndif_free(info, 0); @@ -1444,6 +1570,8 @@ static int __init xen_snd_front_init(void) { int ret = 0; + snd_fronts.count = 0; + spin_lock_init(&snd_fronts.lock); /*FIXME: xen_pv_domain() should be here, but ARM hardcoded to hvm*/ if (!xen_domain()) return -ENODEV; -- 1.9.1