[PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400
Hi,
Fireface 400 uses asynchronous transaction mechanism to deliver event of hardware knob control as well as received MIDI messages. Current implementation doesn't distinguish them, thus all arrived messages are processed for MIDI message.
This patchset adds the parser to distinguish them, and deliver knob control event to user space via ALSA hwdep character device. The implementation works well as long as I tested with the patches for libhitaki library:
https://github.com/alsa-project/libhitaki/tree/topic/ff/400-msg
I note that Fireface 400 transmits no asynchronous transaction when it is not configured by block write transaction to offset 0x000080100514, which turn off HOST led from red.
Takashi Sakamoto (6): ALSA: fireface: rename callback functions ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400 ALSA: fireface: update UAPI for data of knob control ALSA: fireface: add local framework to message parser ALSA: fireface: implement message parser for Fireface 400
include/uapi/sound/firewire.h | 24 +++ sound/firewire/fireface/ff-hwdep.c | 41 ++-- sound/firewire/fireface/ff-protocol-former.c | 186 +++++++++++++++---- sound/firewire/fireface/ff-protocol-latter.c | 6 +- sound/firewire/fireface/ff-transaction.c | 17 +- sound/firewire/fireface/ff.c | 10 + sound/firewire/fireface/ff.h | 9 +- 7 files changed, 239 insertions(+), 54 deletions(-)
It's cleared that Fireface 400 transmits quadlet message for two purposes at least; received MIDI messages and notification of knob control operation. Nevertheless current implementation uses callback function name just for MIDI messages.
This commit renames the callback functions.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 12 ++++++------ sound/firewire/fireface/ff-protocol-latter.c | 6 +++--- sound/firewire/fireface/ff-transaction.c | 12 +++++------- sound/firewire/fireface/ff.h | 4 ++-- 4 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8900ffe517ed..16afcb334e3c 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff) // address. // A write transaction to clear registered higher 4 bytes of destination address // has an effect to suppress asynchronous transaction from device. -static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length) { int i;
@@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, }
const struct snd_ff_protocol snd_ff_protocol_ff800 = { - .handle_midi_msg = ff800_handle_midi_msg, + .handle_msg = ff800_handle_midi_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, @@ -553,8 +553,8 @@ static void ff400_finish_session(struct snd_ff *ff) // input attenuation. This driver allocates destination address with '0000'0000 // in its lower offset and expects userspace application to configure the // register for it. -static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length) { int i;
@@ -592,7 +592,7 @@ static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset, }
const struct snd_ff_protocol snd_ff_protocol_ff400 = { - .handle_midi_msg = ff400_handle_midi_msg, + .handle_msg = ff400_handle_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 76c3eab36d4e..e7a066fb1ead 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer // input attenuation. This driver allocates for the first option // (0x'....'....'0000'0000) and expects userspace application to configure the // register for it. -static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length) +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length) { u32 data = le32_to_cpu(*buf); unsigned int index = (data & 0x000000f0) >> 4; @@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff, }
const struct snd_ff_protocol snd_ff_protocol_latter = { - .handle_midi_msg = latter_handle_midi_msg, + .handle_msg = latter_handle_midi_msg, .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index ee7122c461d4..764c772a0b1e 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -125,10 +125,9 @@ static void transmit_midi1_msg(struct work_struct *work) transmit_midi_msg(ff, 1); }
-static void handle_midi_msg(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) +static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode, + int destination, int source, int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) { struct snd_ff *ff = callback_data; __le32 *buf = data; @@ -136,8 +135,7 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE);
offset -= ff->async_handler.offset; - ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, - length); + ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length); }
static int allocate_own_address(struct snd_ff *ff, int i) @@ -146,7 +144,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) int err;
ff->async_handler.length = ff->spec->midi_addr_range; - ff->async_handler.address_callback = handle_midi_msg; + ff->async_handler.address_callback = handle_msg; ff->async_handler.callback_data = ff;
midi_msg_region.start = 0x000100000000ull * i; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 0535f0b58b67..0358b444bd01 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -110,8 +110,8 @@ enum snd_ff_clock_src { };
struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, - __le32 *buf, size_t length); + void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, + size_t length); int (*fill_midi_msg)(struct snd_ff *ff, struct snd_rawmidi_substream *substream, unsigned int port);
The time stamp of isochronous cycle at which asynchronous transaction is sent is perhaps useful somehow. A commit b2405aa948b9 ("firewire: add kernel API to access packet structure in request structure for AR context") adds kernel API to retrieve the time stamp in inner structure of request subaction.
This commit changes local framework to handle message delivered by the asynchronous transaction so that time stamp is picked up by the kernel API.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 4 ++-- sound/firewire/fireface/ff-protocol-latter.c | 2 +- sound/firewire/fireface/ff-transaction.c | 3 ++- sound/firewire/fireface/ff.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 16afcb334e3c..22d6aee52de3 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -403,7 +403,7 @@ static void ff800_finish_session(struct snd_ff *ff) // A write transaction to clear registered higher 4 bytes of destination address // has an effect to suppress asynchronous transaction from device. static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, - size_t length) + size_t length, u32 tstamp) { int i;
@@ -554,7 +554,7 @@ static void ff400_finish_session(struct snd_ff *ff) // in its lower offset and expects userspace application to configure the // register for it. static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, - size_t length) + size_t length, u32 tstamp) { int i;
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index e7a066fb1ead..9947e0c2e0aa 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -394,7 +394,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer // (0x'....'....'0000'0000) and expects userspace application to configure the // register for it. static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, - size_t length) + size_t length, u32 tstamp) { u32 data = le32_to_cpu(*buf); unsigned int index = (data & 0x000000f0) >> 4; diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 764c772a0b1e..79f733d8c98b 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -131,11 +131,12 @@ static void handle_msg(struct fw_card *card, struct fw_request *request, int tco { struct snd_ff *ff = callback_data; __le32 *buf = data; + u32 tstamp = fw_request_get_timestamp(request);
fw_send_response(card, request, RCODE_COMPLETE);
offset -= ff->async_handler.offset; - ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length); + ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp); }
static int allocate_own_address(struct snd_ff *ff, int i) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 0358b444bd01..f430ebe157b3 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -111,7 +111,7 @@ enum snd_ff_clock_src {
struct snd_ff_protocol { void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, - size_t length); + size_t length, u32 tstamp); int (*fill_midi_msg)(struct snd_ff *ff, struct snd_rawmidi_substream *substream, unsigned int port);
This is minor code refactoring to add helper function to parse MIDI message bytes in quadlet message.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 47 +++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 22d6aee52de3..d2cc9961b973 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -534,6 +534,22 @@ static void ff400_finish_session(struct snd_ff *ff) FF400_ISOC_COMM_STOP, ®, sizeof(reg), 0); }
+static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) +{ + struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]); + + if (substream != NULL) { + u8 byte = (quad >> (16 * port)) & 0x000000ff; + + snd_rawmidi_receive(substream, &byte, 1); + } +} + +#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 +#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff +#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 +#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 + // For Fireface 400, lower 4 bytes of destination address is configured by bit // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can // select one of 4 options: @@ -560,34 +576,11 @@ static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le3
for (i = 0; i < length / 4; i++) { u32 quad = le32_to_cpu(buf[i]); - u8 byte; - unsigned int index; - struct snd_rawmidi_substream *substream;
- /* Message in first port. */ - /* - * This value may represent the index of this unit when the same - * units are on the same IEEE 1394 bus. This driver doesn't use - * it. - */ - index = (quad >> 8) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[0]); - if (substream != NULL) { - byte = quad & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } - - /* Message in second port. */ - index = (quad >> 24) & 0xff; - if (index > 0) { - substream = READ_ONCE(ff->tx_midi_substreams[1]); - if (substream != NULL) { - byte = (quad >> 16) & 0xff; - snd_rawmidi_receive(substream, &byte, 1); - } - } + if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) + parse_midi_msg(ff, quad, 0); + else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) + parse_midi_msg(ff, quad, 1); } }
This commit adds a new event of knob control specific to RME Fireface 400.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- include/uapi/sound/firewire.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h index 3532ac7046d7..50917581dd2b 100644 --- a/include/uapi/sound/firewire.h +++ b/include/uapi/sound/firewire.h @@ -14,6 +14,7 @@ #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479 #define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL 0x7473636d #define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE 0x4d545244 +#define SNDRV_FIREWIRE_EVENT_FF400_MESSAGE 0x4f6c6761
struct snd_firewire_event_common { unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */ @@ -72,6 +73,28 @@ struct snd_firewire_event_motu_register_dsp_change { __u32 changes[]; /* Encoded event for change of register DSP. */ };
+/** + * struct snd_firewire_event_ff400_message - the container for message from Fireface 400 when + * operating hardware knob. + * + * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE. + * @messages.message: The messages expressing hardware knob operation. + * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous + * transaction was sent to deliver the message. It has 16 bit unsigned integer + * value. The higher 3 bits of value expresses the lower three bits of second + * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle + * field up to 7999. + * + * The structure expresses message transmitted by Fireface 400 when operating hardware knob. + */ +struct snd_firewire_event_ff400_message { + unsigned int type; + struct { + __u32 message; + __u32 tstamp; + } messages[]; +}; + union snd_firewire_event { struct snd_firewire_event_common common; struct snd_firewire_event_lock_status lock_status; @@ -81,6 +104,7 @@ union snd_firewire_event { struct snd_firewire_event_tascam_control tascam_control; struct snd_firewire_event_motu_notification motu_notification; struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change; + struct snd_firewire_event_ff400_message ff400_message; };
This commit adds local framework to message parser. This is preparation for future work to pass event of knob control for Fireface 400 to user space.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-hwdep.c | 41 +++++++++++++++++------- sound/firewire/fireface/ff-transaction.c | 4 +++ sound/firewire/fireface/ff.c | 10 ++++++ sound/firewire/fireface/ff.h | 5 +++ 4 files changed, 49 insertions(+), 11 deletions(-)
diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c index ea64a2a41eea..8a741b3b0436 100644 --- a/sound/firewire/fireface/ff-hwdep.c +++ b/sound/firewire/fireface/ff-hwdep.c @@ -15,16 +15,23 @@
#include "ff.h"
+static bool has_msg(struct snd_ff *ff) +{ + if (ff->spec->protocol->has_msg) + return ff->spec->protocol->has_msg(ff); + else + return 0; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { struct snd_ff *ff = hwdep->private_data; DEFINE_WAIT(wait); - union snd_firewire_event event;
spin_lock_irq(&ff->lock);
- while (!ff->dev_lock_changed) { + while (!ff->dev_lock_changed && !has_msg(ff)) { prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&ff->lock); schedule(); @@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&ff->lock); }
- memset(&event, 0, sizeof(event)); - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = (ff->dev_lock_count > 0); - ff->dev_lock_changed = false; + if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) { + struct snd_firewire_event_lock_status ev = { + .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS, + .status = (ff->dev_lock_count > 0), + };
- count = min_t(long, count, sizeof(event.lock_status)); + ff->dev_lock_changed = false;
- spin_unlock_irq(&ff->lock); + spin_unlock_irq(&ff->lock);
- if (copy_to_user(buf, &event, count)) - return -EFAULT; + if (copy_to_user(buf, &ev, sizeof(ev))) + return -EFAULT; + count = sizeof(ev); + } else if (has_msg(ff)) { + // NOTE: Acquired spin lock should be released before accessing to user space in the + // callback since the access can cause page fault. + count = ff->spec->protocol->copy_msg_to_user(ff, buf, count); + spin_unlock_irq(&ff->lock); + } else { + spin_unlock_irq(&ff->lock); + + count = 0; + }
return count; } @@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &ff->hwdep_wait, wait);
spin_lock_irq(&ff->lock); - if (ff->dev_lock_changed) + if (ff->dev_lock_changed || has_msg(ff)) events = EPOLLIN | EPOLLRDNORM; else events = 0; diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c index 79f733d8c98b..6b89e39f4a43 100644 --- a/sound/firewire/fireface/ff-transaction.c +++ b/sound/firewire/fireface/ff-transaction.c @@ -132,11 +132,15 @@ static void handle_msg(struct fw_card *card, struct fw_request *request, int tco struct snd_ff *ff = callback_data; __le32 *buf = data; u32 tstamp = fw_request_get_timestamp(request); + unsigned long flag;
fw_send_response(card, request, RCODE_COMPLETE);
offset -= ff->async_handler.offset; + + spin_lock_irqsave(&ff->lock, flag); ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp); + spin_unlock_irqrestore(&ff->lock, flag); }
static int allocate_own_address(struct snd_ff *ff, int i) diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index 7bf51d062021..448e972028d9 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card) snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff);
+ kfree(ff->msg_parser); + mutex_destroy(&ff->mutex); fw_unit_put(ff->unit); } @@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e if (err < 0) goto error;
+ if (ff->spec->protocol->msg_parser_size > 0) { + ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL); + if (!ff->msg_parser) { + err = -ENOMEM; + goto error; + } + } + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index f430ebe157b3..7e42f5778a8a 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -97,6 +97,8 @@ struct snd_ff { wait_queue_head_t hwdep_wait;
struct amdtp_domain domain; + + void *msg_parser; };
enum snd_ff_clock_src { @@ -110,6 +112,9 @@ enum snd_ff_clock_src { };
struct snd_ff_protocol { + size_t msg_parser_size; + bool (*has_msg)(struct snd_ff *ff); + long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count); void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf, size_t length, u32 tstamp); int (*fill_midi_msg)(struct snd_ff *ff,
This commit implements message parser for Fireface 400 to pass data of knob control to user space. The parser has FIFO which can store maximum 32 events without no overrun detection since it doesn't matter to lose the event.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/fireface/ff-protocol-former.c | 143 ++++++++++++++++++- 1 file changed, 137 insertions(+), 6 deletions(-)
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index d2cc9961b973..f58008762fe6 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -545,10 +545,23 @@ static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) } }
-#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 -#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff -#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 -#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 +#define FF400_QUEUE_SIZE 32 + +struct ff400_msg_parser { + struct { + u32 msg; + u32 tstamp; + } msgs[FF400_QUEUE_SIZE]; + size_t push_pos; + size_t pull_pos; +}; + +static bool ff400_has_msg(struct snd_ff *ff) +{ + struct ff400_msg_parser *parser = ff->msg_parser; + + return (parser->push_pos != parser->pull_pos); +}
// For Fireface 400, lower 4 bytes of destination address is configured by bit // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can @@ -569,22 +582,140 @@ static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port) // input attenuation. This driver allocates destination address with '0000'0000 // in its lower offset and expects userspace application to configure the // register for it. + +// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of +// stereo physical port. +// - 0: Microphone input 0/1 +// - 1: Line input 0/1 +// - [2-4]: Line output 0-5 +// - 5: Headphone output 0/1 +// - 6: S/PDIF output 0/1 +// - [7-10]: ADAT output 0-7 +// +// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone +// input: +// +// - 0: 0.0 dB +// - 10: +10.0 dB +// - 11: +11.0 dB +// - 12: +12.0 dB +// - ... +// - 63: +63.0 dB: +// - 64: +64.0 dB: +// - 65: +65.0 dB: +// +// For signal level of line input: +// +// - 0: 0.0 dB +// - 1: +0.5 dB +// - 2: +1.0 dB +// - 3: +1.5 dB +// - ... +// - 34: +17.0 dB: +// - 35: +17.5 dB: +// - 36: +18.0 dB: +// +// For signal level of any type of output: +// +// - 63: -infinite +// - 62: -58.0 dB +// - 61: -56.0 dB +// - 60: -54.0 dB +// - 59: -53.0 dB +// - 58: -52.0 dB +// - ... +// - 7: -1.0 dB +// - 6: 0.0 dB +// - 5: +1.0 dB +// - ... +// - 2: +4.0 dB +// - 1: +5.0 dB +// - 0: +6.0 dB +// +// When the message is not for signal level operation, it's for MIDI bytes. When matching to +// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When +// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000. +#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000 +#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000 +#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000 +#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000 +#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00 +#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100 +#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff +#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000 +#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000 + static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, size_t length, u32 tstamp) { + bool need_hwdep_wake_up = false; int i;
for (i = 0; i < length / 4; i++) { u32 quad = le32_to_cpu(buf[i]);
- if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) + if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) { + struct ff400_msg_parser *parser = ff->msg_parser; + + parser->msgs[parser->push_pos].msg = quad; + parser->msgs[parser->push_pos].tstamp = tstamp; + ++parser->push_pos; + if (parser->push_pos >= FF400_QUEUE_SIZE) + parser->push_pos = 0; + + need_hwdep_wake_up = true; + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) { parse_midi_msg(ff, quad, 0); - else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) + } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) { parse_midi_msg(ff, quad, 1); + } } + + if (need_hwdep_wake_up) + wake_up(&ff->hwdep_wait); +} + +static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count) +{ + struct ff400_msg_parser *parser = ff->msg_parser; + u32 type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE; + long consumed = 0; + + if (count < 8) + return 0; + + spin_unlock_irq(&ff->lock); + + if (copy_to_user(buf, &type, sizeof(type))) + return -EFAULT; + + spin_lock_irq(&ff->lock); + + count -= sizeof(type); + consumed += sizeof(type); + + while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) { + spin_unlock_irq(&ff->lock); + + if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos, + sizeof(*parser->msgs))) + return -EFAULT; + + spin_lock_irq(&ff->lock); + ++parser->pull_pos; + if (parser->pull_pos >= FF400_QUEUE_SIZE) + parser->pull_pos = 0; + count -= sizeof(*parser->msgs); + consumed += sizeof(*parser->msgs); + } + + return consumed; }
const struct snd_ff_protocol snd_ff_protocol_ff400 = { + .msg_parser_size = sizeof(struct ff400_msg_parser), + .has_msg = ff400_has_msg, + .copy_msg_to_user = ff400_copy_msg_to_user, .handle_msg = ff400_handle_msg, .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock,
On Thu, 12 Jan 2023 13:09:48 +0100, Takashi Sakamoto wrote:
Hi,
Fireface 400 uses asynchronous transaction mechanism to deliver event of hardware knob control as well as received MIDI messages. Current implementation doesn't distinguish them, thus all arrived messages are processed for MIDI message.
This patchset adds the parser to distinguish them, and deliver knob control event to user space via ALSA hwdep character device. The implementation works well as long as I tested with the patches for libhitaki library:
https://github.com/alsa-project/libhitaki/tree/topic/ff/400-msg
I note that Fireface 400 transmits no asynchronous transaction when it is not configured by block write transaction to offset 0x000080100514, which turn off HOST led from red.
Takashi Sakamoto (6): ALSA: fireface: rename callback functions ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400 ALSA: fireface: update UAPI for data of knob control ALSA: fireface: add local framework to message parser ALSA: fireface: implement message parser for Fireface 400
Applied now to for-next branch. Thanks.
Takashi
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto