[RFC,26/37] ALSA: firewire-tascam: add MMAP support to show status and control message
diff mbox

Message ID 1436623968-10780-27-git-send-email-o-takashi@sakamocchi.jp
State New
Headers show

Commit Message

Takashi Sakamoto July 11, 2015, 2:12 p.m. UTC
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@linux-ipv6.org>
Signed-off-by: Takashi Sakamoto <o-takashi@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(-)

Comments

Takashi Iwai July 20, 2015, 2:23 p.m. UTC | #1
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@linux-ipv6.org>
> Signed-off-by: Takashi Sakamoto <o-takashi@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@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>
Clemens Ladisch July 21, 2015, 2:06 p.m. UTC | #2
Takashi Sakamoto wrote:
> Typically, such messages should be converted to MIDI control change
> messages for userspace applications.

It would be possible to have ALSA mixer controls for these, but this
would make sense only if these controls are suitable to be displayed in
a generic mixer application.  I guess this isn't the case for most of
them.

> 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.

I guess the Windows driver does not makes this information available in
any way?

If all the data is device-specific, the actual mapping chosen does not
really matter, because the mixer/control application needs to have
special code for parsing the data from this particular Linux driver
anyway.


Regards,
Clemens
Takashi Sakamoto July 22, 2015, 2:53 p.m. UTC | #3
On Jul 20 2015 23:23, Takashi Iwai wrote:
> 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@linux-ipv6.org>
>> Signed-off-by: Takashi Sakamoto <o-takashi@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...

Indeed. Now I investigate about the converter between the bitmap and
MIDI messages.

But anyway, input/output level should be exposed to userspace because it
doesn't matter to miss reading the information sometimes.

> 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 Sakamoto
Takashi Sakamoto July 22, 2015, 3:19 p.m. UTC | #4
On Jul 21 2015 23:06, Clemens Ladisch wrote:
> Takashi Sakamoto wrote:
>> Typically, such messages should be converted to MIDI control change
>> messages for userspace applications.
> 
> It would be possible to have ALSA mixer controls for these, but this
> would make sense only if these controls are suitable to be displayed in
> a generic mixer application.  I guess this isn't the case for most of
> them.

Indeed. The main purpose of these physical controls is to control DAW
softwares or the other devices. On the other hand, the major usage of
ALSA control elements is to control the device itself.

>> 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.
> 
> I guess the Windows driver does not makes this information available in
> any way?

Windows driver gives GUI to select the way to parse the control
information with three options. All of the options are based on MIDI
messaging. Therefore, the driver perform to convert to MIDI messages, I
think.

Furthermore, two of the options are somewhat compliant to Mackie
something. In Open Source Software, Ardour Gtk+ can handle the Mackie
something. In this reason, I think it's users' advantage to apply MIDI
converter for the control messages.

> If all the data is device-specific, the actual mapping chosen does not
> really matter, because the mixer/control application needs to have
> special code for parsing the data from this particular Linux driver
> anyway.

Exactly, with ignoring the updating interval.


Regards

Takashi Sakamoto

Patch
diff mbox

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