[alsa-devel] [RFC][PATCH 26/37] ALSA: firewire-tascam: add MMAP support to show status and control message

Takashi Sakamoto o-takashi at sakamocchi.jp
Sat Jul 11 16:12:37 CEST 2015


TASCAM FireWire series transfers status and control messages in
isochronous packets. One packet includes one message, and the
message is periodic every 64 packets. As a result, the messages
construct image with 64 quadlets. For example:

00 00000000, 32 00000000
01 00000000, 33 00000000
02 00000000, 34 00000000
03 00020000, 35 00000000
04 00000000, 36 25ea0000
05 ffff0000, 37 27dfff00
06 ffffffff, 38 1d9d0000
07 ffffffff, 39 00000000
08 ffffffff, 40 25ea0000
09 ffffffff, 41 27dfff00
10 00000000, 42 00000000
11 00000000, 43 00000000
12 00000000, 44 00000000
13 00000000, 45 00000000
14 00000000, 46 00000000
15 00010000, 47 00000000
16 00012e00, 48 00000000
17 00010400, 49 00000000
18 00011e00, 50 00000000
19 00011200, 51 00000000
20 00014900, 52 01010101
21 00011e00, 53 00000000
22 00010c00, 54 25ea0000
23 00013000, 55 27dfff00
24 00000000, 56 00000000
25 00000000, 57 00000000
26 00000000, 58 00000000
27 00000000, 59 00000001
28 00000000, 60 00000000
29 00000000, 61 00000000
30 00000000, 62 00000000
31 00000000, 63 00000000

Quadlet 00-15 show control messages. Quadlet 16-23 show analog input
level. Quadlet 24-31 shows digital ADAT input level. Quadlet 32-33
shows digital S/PDIF input level. Quadlet 34-35 is unknown. Quadlet
36-43 shows analog output level. The other quadlets are unknown.

This image is updated every 1 msec or less, depending on current
sampling transfer frequency.

This commit adds MMAP support to show this image via hwdep interface.
An userspace application can map a page frame to its virtual address
space with read-only flag. This driver write every control and status
messages into the page frame, and the userspace application can parse
them.

The control messages are both of edge-triggered/level-trigerred,
according to physical implementation. For example, the control message
corresponding to toggle switch acts as edge-trigger, while the control
message corresponding to level fader acts as level-trigger. These control
messages are updated every 1 msec or less, alghough the scheduling
granuality in Linux operating system is not always so small. This may
cause the userspace parser misses some messages when many tasks are
running without voluntary programs.

Typically, such messages should be converted to MIDI control change
messages for userspace applications. Thus, this driver should have
a converter with appropriate MIDI map. Currently I have no good idea
for the map and it is not implemented yet. Developers helps are
required.

Cc: Yoshifuji Hideaki <yoshfuji at linux-ipv6.org>
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 include/uapi/sound/firewire.h         |  4 ++++
 sound/firewire/tascam/amdtp-tascam.c  |  4 +++-
 sound/firewire/tascam/tascam-hwdep.c  | 30 ++++++++++++++++++++++++++++++
 sound/firewire/tascam/tascam-stream.c | 15 +++++++++++++++
 sound/firewire/tascam/tascam.h        |  3 +++
 5 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index db79a12..c5cb86d 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -79,4 +79,8 @@ struct snd_firewire_get_info {
  * Returns -EBUSY if the driver is already streaming.
  */
 
+struct snd_firewire_tascam_status {
+	__u32 status[64];
+};
+
 #endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
index 22c2eaf..ca54b85 100644
--- a/sound/firewire/tascam/amdtp-tascam.c
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -155,14 +155,16 @@ void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
 static void read_control_messages(struct amdtp_stream *s,
 				  __be32 *buffer, unsigned int data_blocks)
 {
+	struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);
 	unsigned int first, index = 0;
 	unsigned int i;
 
 	first = (buffer[0] >> 15) % 64;
 
-	/* TODO */
 	for (i = 0; i < data_blocks; i++) {
 		index = be32_to_cpu(buffer[0]) % 64;
+		tscm->status->status[index] =
+				be32_to_cpu(buffer[s->data_block_quadlets - 1]);
 		buffer += s->data_block_quadlets;
 	}
 }
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
index 4b539f5..fe51fee 100644
--- a/sound/firewire/tascam/tascam-hwdep.c
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -175,12 +175,42 @@ static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 #define hwdep_compat_ioctl NULL
 #endif
 
+static int hwdep_vm_fault(struct vm_area_struct *area, struct vm_fault *vmf)
+{
+	struct snd_tscm *tscm = area->vm_private_data;
+	void *virt_addr;
+	struct page *page;
+
+	/* The page is already allocated by streaming layer. */
+	virt_addr = (char *)(tscm->status) + (vmf->pgoff << PAGE_SHIFT);
+	page = virt_to_page(virt_addr);
+	get_page(page);
+	vmf->page = page;
+
+	return 0;
+}
+
+static const struct vm_operations_struct hwdep_vm_ops = {
+	.fault		= hwdep_vm_fault,
+};
+
+static int hwdep_mmap(struct snd_hwdep *hwdep, struct file *filp,
+		      struct vm_area_struct *area)
+{
+	area->vm_ops = &hwdep_vm_ops;
+	area->vm_flags = VM_READ | VM_RAND_READ | VM_DONTEXPAND;
+	area->vm_private_data = hwdep->private_data;
+
+	return 0;
+}
+
 static const struct snd_hwdep_ops hwdep_ops = {
 	.read		= hwdep_read,
 	.release	= hwdep_release,
 	.poll		= hwdep_poll,
 	.ioctl		= hwdep_ioctl,
 	.ioctl_compat	= hwdep_compat_ioctl,
+	.mmap		= hwdep_mmap,
 };
 
 int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 23124fb..83f437e 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -314,6 +314,7 @@ error:
 int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
 {
 	unsigned int pcm_channels;
+	unsigned int size;
 	int err;
 
 	/* For out-stream. */
@@ -344,6 +345,15 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
 	if (err < 0)
 		amdtp_stream_destroy(&tscm->rx_stream);
 
+	/* For control messages. */
+	size = sizeof(struct snd_firewire_tascam_status);
+	tscm->status = snd_malloc_pages(size, GFP_KERNEL);
+	if (tscm->status == NULL) {
+		amdtp_stream_destroy(&tscm->tx_stream);
+		amdtp_stream_destroy(&tscm->rx_stream);
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
@@ -363,11 +373,16 @@ void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
  */
 void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
 {
+	unsigned int size;
+
 	amdtp_stream_destroy(&tscm->rx_stream);
 	amdtp_stream_destroy(&tscm->tx_stream);
 
 	fw_iso_resources_destroy(&tscm->rx_resources);
 	fw_iso_resources_destroy(&tscm->tx_resources);
+
+	size = sizeof(struct snd_firewire_tascam_status);
+	snd_free_pages(tscm->status, size);
 }
 
 int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index 053a8bf..0340693 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -70,6 +70,9 @@ struct snd_tscm {
 	int dev_lock_count;
 	bool dev_lock_changed;
 	wait_queue_head_t hwdep_wait;
+
+	/* For control messages. */
+	struct snd_firewire_tascam_status *status;
 };
 
 #define TSCM_ADDR_BASE			0xffff00000000ull
-- 
2.1.4



More information about the Alsa-devel mailing list