diff options
Diffstat (limited to 'sound/firewire/digi00x/amdtp-dot.c')
-rw-r--r-- | sound/firewire/digi00x/amdtp-dot.c | 120 |
1 files changed, 116 insertions, 4 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; } |