Message ID | 1436623968-10780-27-git-send-email-o-takashi@sakamocchi.jp (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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 >
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
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
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
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
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(-)