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

Takashi Iwai tiwai at suse.de
Mon Jul 20 16:23:40 CEST 2015


On Sat, 11 Jul 2015 16:12:37 +0200,
Takashi Sakamoto wrote:
> 
> 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>

Using mmap for accessing this kind of message isn't good for various
reasons.  First off, it's volatile, and your implementation keeps only
a single record, thus it will be overridden at each input.

Second, the access is racy.

Third, the mmap implementation can be complex if a cache coherency
plays a role.  It's easy on x86, but for others...

And the rest... there must be something else I overlooked obviously
(I don't count a bug here, e.g. the lack of offset value check in vm
 fault handler :)


thanks,

Takashi

> ---
>  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
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


More information about the Alsa-devel mailing list