[alsa-devel] [PATCH 0/6 v3] ALSA: firewire-digi00x: support MIDI functionality
Hi,
This commit updates my former post (patch 13, 15, 16):
[alsa-devel] [PATCH 00/25 v2] ALSA: support AMDTP variants http://mailman.alsa-project.org/pipermail/alsa-devel/2015-August/096739.html
Digi 00x driver may be newly available in Linux 4.4. This patchset adds ALSA MIDI ports to support physical MIDI ports and physical controls. I tested physical MIDI ports, while never tested physical controls because I'm not an owner of 'console' models.
The functionality for physical controls is written according to my assumption. When I investigated Digi 002 rack, Windows driver registers a certain address to the device, and adds virtual MIDI ports to the system. These ports are named as 'control'. When MIDI application sends MIDI messages to the port, asynchronous transactions start on IEEE 1394 bus. On the other hand, I have no way to confirm asynchronous transaction in opposite direction because the rack model doesn't have the functionality. In this time, I apply the same format of outgoing asynchronous transaction to incoming asynchronous transaction.
Takashi Sakamoto (6): firewire-digi00x: use in-kernel representation for the type of 8 bits firewire-digi00x: handle MIDI messages in isochronous packets firewire-digi00x: add support for MIDI ports corresponding to isochronous packet streaming firewire-digi00x: add support of asynchronous transaction for incoming MIDI messages from physical controls firewire-digi00x: add support of asynchronous transaction for outgoing MIDI messages to physical controls firewire-digi00x: add support for MIDI ports for physical controls
sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/amdtp-dot.c | 150 +++++++++++++++++++++---- sound/firewire/digi00x/digi00x-midi.c | 160 +++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-stream.c | 6 +- sound/firewire/digi00x/digi00x-transaction.c | 70 ++++++++++-- sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 15 ++- 7 files changed, 376 insertions(+), 31 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-midi.c
Original code for 'DoubleOhThree' encoding was written with '__u8' type, while the type is usually used to export something to userspace.
This commit replaces the type with 'u8'.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/amdtp-dot.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index e6731d3..c12ce4d 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -21,8 +21,8 @@ * Zammit in 2012, with reverse-engineering for Digi 003 Rack. */ struct dot_state { - __u8 carry; - __u8 idx; + u8 carry; + u8 idx; unsigned int off; };
@@ -47,25 +47,25 @@ struct amdtp_dot { #define BYTE_PER_SAMPLE (4) #define MAGIC_DOT_BYTE (2) #define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE) -static const __u8 dot_scrt(const __u8 idx, const unsigned int off) +static const u8 dot_scrt(const u8 idx, const unsigned int off) { /* * the length of the added pattern only depends on the lower nibble * of the last non-zero data */ - static const __u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14, - 12, 10, 8, 6, 4, 2, 0}; + static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14, + 12, 10, 8, 6, 4, 2, 0};
/* * the lower nibble of the salt. Interleaved sequence. * this is walked backwards according to len[] */ - static const __u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4, - 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf}; + static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4, + 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
/* circular list for the salt's hi nibble. */ - static const __u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4, - 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa}; + static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4, + 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
/* * start offset for upper nibble mapping. @@ -73,12 +73,12 @@ static const __u8 dot_scrt(const __u8 idx, const unsigned int off) * hir[] is not used and - coincidentally - the salt's hi nibble is * 0x09 regardless of the offset. */ - static const __u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4, - 3, 0x00, 14, 13, 8, 9, 10, 2}; + static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4, + 3, 0x00, 14, 13, 8, 9, 10, 2};
- const __u8 ln = idx & 0xf; - const __u8 hn = (idx >> 4) & 0xf; - const __u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15]; + const u8 ln = idx & 0xf; + const u8 hn = (idx >> 4) & 0xf; + const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
if (len[ln] < off) return 0x00; @@ -88,7 +88,7 @@ static const __u8 dot_scrt(const __u8 idx, const unsigned int off)
static void dot_encode_step(struct dot_state *state, __be32 *const buffer) { - __u8 * const data = (__u8 *) buffer; + u8 * const data = (u8 *) buffer;
if (data[MAGIC_DOT_BYTE] != 0x00) { state->off = 0;
In Digi 002/003 protocol, MIDI messages are transferred in the last data channel of data blocks. Although this data channel has a label of 0x80, it's not fully MIDI conformant data channel especially because the Counter field always zero independently of included MIDI bytes. The 4th byte of the data channel in LSB tells the number of included MIDI bytes. This byte also includes the number of MIDI port. Therefore, the data format in this data channel is: * 1st: 0x80 as label * 2nd: MIDI bytes * 3rd: 0 or MIDI bytes * 4th: the number of MIDI byte and the number of MIDI port
This commit adds support of MIDI messages in data block processing layer.
Like AM824 data format, this data channel has a capability to transfer more MIDI messages than the capability of phisical MIDI bus. Therefore, a throttle for data rate is required to prevent devices' internal buffer to overflow.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/amdtp-dot.c | 120 ++++++++++++++++++++++++++++++-- sound/firewire/digi00x/digi00x-stream.c | 4 +- sound/firewire/digi00x/digi00x.h | 9 ++- 3 files changed, 125 insertions(+), 8 deletions(-)
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index c12ce4d..b02a5e8c 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -17,6 +17,18 @@ #define AMDTP_FDF_AM824 0x00
/* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + +/* * The double-oh-three algorithm was discovered by Robin Gareus and Damien * Zammit in 2012, with reverse-engineering for Digi 003 Rack. */ @@ -31,6 +43,10 @@ struct amdtp_dot { struct dot_state state;
unsigned int midi_ports; + /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */ + struct snd_rawmidi_substream *midi[2]; + int midi_fifo_used[2]; + int midi_fifo_limit;
void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -99,7 +115,7 @@ static void dot_encode_step(struct dot_state *state, __be32 *const buffer) }
int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, unsigned int midi_ports) + unsigned int pcm_channels) { struct amdtp_dot *p = s->protocol; int err; @@ -118,7 +134,19 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, s->fdf = AMDTP_FDF_AM824 | s->sfc;
p->pcm_channels = pcm_channels; - p->midi_ports = midi_ports; + + if (s->direction == AMDTP_IN_STREAM) + p->midi_ports = DOT_MIDI_IN_PORTS; + else + p->midi_ports = DOT_MIDI_OUT_PORTS; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
return 0; } @@ -216,6 +244,81 @@ static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, } }
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_dot *p = s->protocol; + int used; + + used = p->midi_fifo_used[port]; + if (used == 0) + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + p->midi_fifo_used[port] = used; + + return used < p->midi_fifo_limit; +} + +static inline void midi_use_bytes(struct amdtp_stream *s, + unsigned int port, unsigned int count) +{ + struct amdtp_dot *p = s->protocol; + + p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count; +} + +static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[0]; + + len = 0; + if (port < p->midi_ports && + midi_ratelimit_per_packet(s, port) && + p->midi[port] != NULL) + len = snd_rawmidi_transmit(p->midi[port], b + 1, 2); + + if (len > 0) { + b[3] = (0x10 << port) | len; + midi_use_bytes(s, port, len); + } else { + b[1] = 0; + b[2] = 0; + b[3] = 0; + } + b[0] = 0x80; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port, len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + b = (u8 *)&buffer[0]; + port = b[3] >> 4; + len = b[3] & 0x0f; + + if (port < p->midi_ports && p->midi[port] && len > 0) + snd_rawmidi_receive(p->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { @@ -256,6 +359,15 @@ void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) } }
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi) +{ + struct amdtp_dot *p = s->protocol; + + if (port < p->midi_ports) + ACCESS_ONCE(p->midi[port]) = midi; +} + static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, @@ -273,7 +385,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; }
- /* A place holder for MIDI processing. */ + read_midi_messages(s, buffer, data_blocks);
return pcm_frames; } @@ -296,7 +408,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; }
- /* A place holder for MIDI processing. */ + write_midi_messages(s, buffer, data_blocks);
return pcm_frames; } diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 8aac31b..e9be162 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -199,7 +199,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
/* Keep resources for out-stream. */ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->rx_resources, @@ -210,7 +210,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
/* Keep resources for in-stream. */ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->tx_resources, diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index b960c86..88df67b 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -24,6 +24,7 @@ #include <sound/pcm_params.h> #include <sound/firewire.h> #include <sound/hwdep.h> +#include <sound/rawmidi.h>
#include "../lib.h" #include "../iso-resources.h" @@ -103,15 +104,19 @@ enum snd_dg00x_optical_mode { SND_DG00X_OPT_IFACE_MODE_COUNT, };
+#define DOT_MIDI_IN_PORTS 1 +#define DOT_MIDI_OUT_PORTS 2 + int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); + unsigned int pcm_channels); void amdtp_dot_reset(struct amdtp_stream *s); int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi);
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x); int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
This commit adds MIDI functionality to capture/playback MIDI messages from/to physical MIDI ports. These messages are transferred in isochronous packets.
When no substreams request AMDTP streams to run, this driver starts the streams at current sampling rate. When other substreams start at different sampling rate, the streams are stopped temporarily, then start again at requested sampling rate. This operation can generate missing MIDI bytes, thus it's preferable to start PCM substreams at favorite sampling rate in advance.
Digi 002/003 console also has a set of MIDI port for physical controls. These ports are added in later commits.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/digi00x-midi.c | 131 ++++++++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-stream.c | 2 + sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 2 + 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 sound/firewire/digi00x/digi00x-midi.c
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 28e3d13..1123e68 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,4 +1,4 @@ snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ digi00x-pcm.o digi00x-hwdep.o \ - digi00x-transaction.o digi00x.o + digi00x-transaction.o digi00x-midi.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c new file mode 100644 index 0000000..4e258ac --- /dev/null +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -0,0 +1,131 @@ +/* + * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family + * + * Copyright (c) 2014-2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "digi00x.h" + +static int midi_open(struct snd_rawmidi_substream *substream) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + int err; + + err = snd_dg00x_stream_lock_try(dg00x); + if (err < 0) + return err; + + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter++; + err = snd_dg00x_stream_start_duplex(dg00x, 0); + mutex_unlock(&dg00x->mutex); + if (err < 0) + snd_dg00x_stream_lock_release(dg00x); + + return err; +} + +static int midi_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dg00x *dg00x = substream->rmidi->private_data; + + mutex_lock(&dg00x->mutex); + dg00x->substreams_counter--; + snd_dg00x_stream_stop_duplex(dg00x); + mutex_unlock(&dg00x->mutex); + + snd_dg00x_stream_lock_release(dg00x); + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dg00x *dg00x = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number, substrm); + else + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dg00x *dg00x = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dg00x->lock, flags); + + if (up) + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number, substrm); + else + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dg00x->lock, flags); +} + +static struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_playback_trigger, +}; + +static void set_midi_substream_names(struct snd_dg00x *dg00x, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, subs->number + 1); + } +} + +int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + int err; + + err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, + DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", dg00x->card->shortname); + rmidi->private_data = dg00x; + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_capture_ops); + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + set_midi_substream_names(dg00x, str); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_playback_ops); + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + set_midi_substream_names(dg00x, str); + + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index e9be162..4d3b4eb 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -285,6 +285,8 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); if (err < 0) goto error; + if (rate == 0) + rate = curr_rate; if (curr_rate != rate || amdtp_streaming_error(&dg00x->tx_stream) || amdtp_streaming_error(&dg00x->rx_stream)) { diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 34937a2..bbe3be7 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -90,6 +90,10 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error;
+ err = snd_dg00x_create_midi_devices(dg00x); + if (err < 0) + goto error; + err = snd_dg00x_create_hwdep_device(dg00x); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 88df67b..89494d0 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -147,5 +147,7 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x); + int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x); #endif
Digi 00x series has two types of model; rack and console. The console models have physical controls. The model can transmit control messages. These control messages are transferred by asynchronous transactions to registered address.
This commit supports the asynchronous transaction.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/digi00x-transaction.c | 41 ++++++++++++++++++++++++++-- sound/firewire/digi00x/digi00x.h | 3 ++ 2 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c index 4937290..8bf5ed5 100644 --- a/sound/firewire/digi00x/digi00x-transaction.c +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -9,6 +9,28 @@ #include <sound/asound.h> #include "digi00x.h"
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf, + unsigned int length) +{ + struct snd_rawmidi_substream *substream; + unsigned int i; + unsigned int len; + u8 *b; + + substream = ACCESS_ONCE(dg00x->in_control); + if (substream == NULL) + return; + + length /= 4; + + for (i = 0; i < length; i++) { + b = (u8 *)&buf[i]; + len = b[3] & 0xf; + if (len > 0) + snd_rawmidi_receive(dg00x->in_control, b + 1, len); + } +} + static void handle_unknown_message(struct snd_dg00x *dg00x, unsigned long long offset, __be32 *buf) { @@ -31,6 +53,8 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
if (offset == dg00x->async_handler.offset) handle_unknown_message(dg00x, offset, buf); + else if (offset == dg00x->async_handler.offset + 4) + handle_midi_control(dg00x, buf, length);
fw_send_response(card, request, RCODE_COMPLETE); } @@ -39,14 +63,25 @@ int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x) { struct fw_device *device = fw_parent_device(dg00x->unit); __be32 data[2]; + int err;
/* Unknown. 4bytes. */ data[0] = cpu_to_be32((device->card->node_id << 16) | (dg00x->async_handler.offset >> 32)); data[1] = cpu_to_be32(dg00x->async_handler.offset); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, + &data, sizeof(data), 0); + if (err < 0) + return err; + + /* Asynchronous transactions for MIDI control message. */ + data[0] = cpu_to_be32((device->card->node_id << 16) | + (dg00x->async_handler.offset >> 32)); + data[1] = cpu_to_be32(dg00x->async_handler.offset + 4); return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, - &data, sizeof(data), 0); + DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR, + &data, sizeof(data), 0); }
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) @@ -57,7 +92,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) }; int err;
- dg00x->async_handler.length = 4; + dg00x->async_handler.length = 12; dg00x->async_handler.address_callback = handle_message; dg00x->async_handler.callback_data = dg00x;
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 89494d0..630f6aa 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -53,6 +53,9 @@ struct snd_dg00x { /* For asynchronous messages. */ struct fw_address_handler async_handler; u32 msg; + + /* For asynchronous MIDI controls. */ + struct snd_rawmidi_substream *in_control; };
#define DG00X_ADDR_BASE 0xffffe0000000ull
In previous commit, asynchronous transaction for incoming MIDI messages from physical controls is supported. The physical controls may be controlled by receiving MIDI messages at a certain address.
This commit supports asynchronous transaction for this purpose.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/digi00x-transaction.c | 29 ++++++++++++++++++++++++---- sound/firewire/digi00x/digi00x.h | 1 + 2 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c index 8bf5ed5..554324d 100644 --- a/sound/firewire/digi00x/digi00x-transaction.c +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -9,6 +9,18 @@ #include <sound/asound.h> #include "digi00x.h"
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf) +{ + int bytes; + + buf[0] = 0x80; + bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2); + if (bytes >= 0) + buf[3] = 0xc0 | bytes; + + return bytes; +} + static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf, unsigned int length) { @@ -102,15 +114,24 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) return err;
err = snd_dg00x_transaction_reregister(dg00x); - if (err < 0) { - fw_core_remove_address_handler(&dg00x->async_handler); - dg00x->async_handler.address_callback = NULL; - } + if (err < 0) + goto error; + + err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit, + DG00X_ADDR_BASE + DG00X_OFFSET_MMC, + 4, fill_midi_message); + if (err < 0) + goto error;
return err; +error: + fw_core_remove_address_handler(&dg00x->async_handler); + dg00x->async_handler.address_callback = NULL; + return err; }
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) { + snd_fw_async_midi_port_destroy(&dg00x->out_control); fw_core_remove_address_handler(&dg00x->async_handler); } diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 630f6aa..907e739 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -56,6 +56,7 @@ struct snd_dg00x {
/* For asynchronous MIDI controls. */ struct snd_rawmidi_substream *in_control; + struct snd_fw_async_midi_port out_control; };
#define DG00X_ADDR_BASE 0xffffe0000000ull
In former commits, asynchronous transactions are supported for physical controls. This commit adds a pair of MIDI ports for them.
This driver already adds diferrent number of ALSA MIDI ports for physical MIDI ports, and the number of in/out ports are different. As seeing as 'amidi' program in alsa-utils package, a pair of in/out MIDI ports is expected with the same name. Therefore, this commit adds a pair of new ports to the first.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/digi00x/digi00x-midi.c | 61 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 16 deletions(-)
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 4e258ac..9aa8b46 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -13,6 +13,10 @@ static int midi_open(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; int err;
+ /* This port is for asynchronous transaction. */ + if (substream->number == 0) + return 0; + err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) return err; @@ -31,6 +35,10 @@ static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ /* This port is for asynchronous transaction. */ + if (substream->number == 0) + return 0; + mutex_lock(&dg00x->mutex); dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); @@ -47,12 +55,20 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dg00x->lock, flags);
- if (up) - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number, substrm); - else - amdtp_dot_midi_trigger(&dg00x->tx_stream, - substrm->number, NULL); + /* This port is for asynchronous transaction. */ + if (substrm->number == 0) { + if (up) + dg00x->in_control = substrm; + else + dg00x->in_control = NULL; + } else { + if (up) + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, substrm); + else + amdtp_dot_midi_trigger(&dg00x->tx_stream, + substrm->number - 1, NULL); + }
spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -64,12 +80,19 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dg00x->lock, flags);
- if (up) - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number, substrm); - else - amdtp_dot_midi_trigger(&dg00x->rx_stream, - substrm->number, NULL); + /* This port is for asynchronous transaction. */ + if (substrm->number == 0) { + if (up) + snd_fw_async_midi_port_run(&dg00x->out_control, + substrm); + } else { + if (up) + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, substrm); + else + amdtp_dot_midi_trigger(&dg00x->rx_stream, + substrm->number - 1, NULL); + }
spin_unlock_irqrestore(&dg00x->lock, flags); } @@ -92,9 +115,15 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_substream *subs;
list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - dg00x->card->shortname, subs->number + 1); + if (subs->number > 0) + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, subs->number); + else + /* This port is for asynchronous transaction. */ + snprintf(subs->name, sizeof(subs->name), + "%s control", + dg00x->card->shortname); } }
@@ -105,7 +134,7 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) int err;
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0, - DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi); + DOT_MIDI_OUT_PORTS + 1, DOT_MIDI_IN_PORTS + 1, &rmidi); if (err < 0) return err;
On Sun, 11 Oct 2015 05:30:13 +0200, Takashi Sakamoto wrote:
Hi,
This commit updates my former post (patch 13, 15, 16):
[alsa-devel] [PATCH 00/25 v2] ALSA: support AMDTP variants http://mailman.alsa-project.org/pipermail/alsa-devel/2015-August/096739.html
Digi 00x driver may be newly available in Linux 4.4. This patchset adds ALSA MIDI ports to support physical MIDI ports and physical controls. I tested physical MIDI ports, while never tested physical controls because I'm not an owner of 'console' models.
The functionality for physical controls is written according to my assumption. When I investigated Digi 002 rack, Windows driver registers a certain address to the device, and adds virtual MIDI ports to the system. These ports are named as 'control'. When MIDI application sends MIDI messages to the port, asynchronous transactions start on IEEE 1394 bus. On the other hand, I have no way to confirm asynchronous transaction in opposite direction because the rack model doesn't have the functionality. In this time, I apply the same format of outgoing asynchronous transaction to incoming asynchronous transaction.
Takashi Sakamoto (6): firewire-digi00x: use in-kernel representation for the type of 8 bits firewire-digi00x: handle MIDI messages in isochronous packets firewire-digi00x: add support for MIDI ports corresponding to isochronous packet streaming firewire-digi00x: add support of asynchronous transaction for incoming MIDI messages from physical controls firewire-digi00x: add support of asynchronous transaction for outgoing MIDI messages to physical controls firewire-digi00x: add support for MIDI ports for physical controls
Thanks, applied all six patches now.
Takashi
sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/amdtp-dot.c | 150 +++++++++++++++++++++---- sound/firewire/digi00x/digi00x-midi.c | 160 +++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-stream.c | 6 +- sound/firewire/digi00x/digi00x-transaction.c | 70 ++++++++++-- sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 15 ++- 7 files changed, 376 insertions(+), 31 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-midi.c
-- 2.1.4
Hi,
On Oct 12 2015 01:27, Takashi Iwai wrote:
On Sun, 11 Oct 2015 05:30:13 +0200, Takashi Sakamoto wrote:
Hi,
This commit updates my former post (patch 13, 15, 16):
[alsa-devel] [PATCH 00/25 v2] ALSA: support AMDTP variants http://mailman.alsa-project.org/pipermail/alsa-devel/2015-August/096739.html
Digi 00x driver may be newly available in Linux 4.4. This patchset adds ALSA MIDI ports to support physical MIDI ports and physical controls. I tested physical MIDI ports, while never tested physical controls because I'm not an owner of 'console' models.
The functionality for physical controls is written according to my assumption. When I investigated Digi 002 rack, Windows driver registers a certain address to the device, and adds virtual MIDI ports to the system. These ports are named as 'control'. When MIDI application sends MIDI messages to the port, asynchronous transactions start on IEEE 1394 bus. On the other hand, I have no way to confirm asynchronous transaction in opposite direction because the rack model doesn't have the functionality. In this time, I apply the same format of outgoing asynchronous transaction to incoming asynchronous transaction.
Takashi Sakamoto (6): firewire-digi00x: use in-kernel representation for the type of 8 bits firewire-digi00x: handle MIDI messages in isochronous packets firewire-digi00x: add support for MIDI ports corresponding to isochronous packet streaming firewire-digi00x: add support of asynchronous transaction for incoming MIDI messages from physical controls firewire-digi00x: add support of asynchronous transaction for outgoing MIDI messages to physical controls firewire-digi00x: add support for MIDI ports for physical controls
Thanks, applied all six patches now.
Thanks. Now all patches for firewire-digi00x are pushed to upstream. It's a bit tough work to take me almost one year, so glad ;)
Thanks
Takashi Sakamoto
Takashi
sound/firewire/digi00x/Makefile | 2 +- sound/firewire/digi00x/amdtp-dot.c | 150 +++++++++++++++++++++---- sound/firewire/digi00x/digi00x-midi.c | 160 +++++++++++++++++++++++++++ sound/firewire/digi00x/digi00x-stream.c | 6 +- sound/firewire/digi00x/digi00x-transaction.c | 70 ++++++++++-- sound/firewire/digi00x/digi00x.c | 4 + sound/firewire/digi00x/digi00x.h | 15 ++- 7 files changed, 376 insertions(+), 31 deletions(-) create mode 100644 sound/firewire/digi00x/digi00x-midi.c
-- 2.1.4
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto