|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 3/3] xen-sndfront: add capture support
From: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>
Now both play and capture is supported.
Signed-off-by: Iurii Konovalenko <iurii.konovalenko@xxxxxxxxxxxxxxx>
Signed-off-by: Oleksandr Dmytryshyn <oleksandr.dmytryshyn@xxxxxxxxxxxxxxx>
---
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 <xen/interface/grant_table.h>
/*
+ * 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
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |