Skip to content

Commit 9dc5d31

Browse files
takaswietiwai
authored andcommitted
ALSA: firewire-digi00x: handle MIDI messages in isochronous packets
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> Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 17385a3 commit 9dc5d31

3 files changed

Lines changed: 125 additions & 8 deletions

File tree

sound/firewire/digi00x/amdtp-dot.c

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@
1616
/* 'Clock-based rate control mode' is just supported. */
1717
#define AMDTP_FDF_AM824 0x00
1818

19+
/*
20+
* Nominally 3125 bytes/second, but the MIDI port's clock might be
21+
* 1% too slow, and the bus clock 100 ppm too fast.
22+
*/
23+
#define MIDI_BYTES_PER_SECOND 3093
24+
25+
/*
26+
* Several devices look only at the first eight data blocks.
27+
* In any case, this is more than enough for the MIDI data rate.
28+
*/
29+
#define MAX_MIDI_RX_BLOCKS 8
30+
1931
/*
2032
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
2133
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
@@ -31,6 +43,10 @@ struct amdtp_dot {
3143
struct dot_state state;
3244

3345
unsigned int midi_ports;
46+
/* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
47+
struct snd_rawmidi_substream *midi[2];
48+
int midi_fifo_used[2];
49+
int midi_fifo_limit;
3450

3551
void (*transfer_samples)(struct amdtp_stream *s,
3652
struct snd_pcm_substream *pcm,
@@ -99,7 +115,7 @@ static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
99115
}
100116

101117
int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
102-
unsigned int pcm_channels, unsigned int midi_ports)
118+
unsigned int pcm_channels)
103119
{
104120
struct amdtp_dot *p = s->protocol;
105121
int err;
@@ -118,7 +134,19 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
118134
s->fdf = AMDTP_FDF_AM824 | s->sfc;
119135

120136
p->pcm_channels = pcm_channels;
121-
p->midi_ports = midi_ports;
137+
138+
if (s->direction == AMDTP_IN_STREAM)
139+
p->midi_ports = DOT_MIDI_IN_PORTS;
140+
else
141+
p->midi_ports = DOT_MIDI_OUT_PORTS;
142+
143+
/*
144+
* We do not know the actual MIDI FIFO size of most devices. Just
145+
* assume two bytes, i.e., one byte can be received over the bus while
146+
* the previous one is transmitted over MIDI.
147+
* (The value here is adjusted for midi_ratelimit_per_packet().)
148+
*/
149+
p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
122150

123151
return 0;
124152
}
@@ -216,6 +244,81 @@ static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
216244
}
217245
}
218246

247+
static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
248+
{
249+
struct amdtp_dot *p = s->protocol;
250+
int used;
251+
252+
used = p->midi_fifo_used[port];
253+
if (used == 0)
254+
return true;
255+
256+
used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
257+
used = max(used, 0);
258+
p->midi_fifo_used[port] = used;
259+
260+
return used < p->midi_fifo_limit;
261+
}
262+
263+
static inline void midi_use_bytes(struct amdtp_stream *s,
264+
unsigned int port, unsigned int count)
265+
{
266+
struct amdtp_dot *p = s->protocol;
267+
268+
p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
269+
}
270+
271+
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
272+
unsigned int data_blocks)
273+
{
274+
struct amdtp_dot *p = s->protocol;
275+
unsigned int f, port;
276+
int len;
277+
u8 *b;
278+
279+
for (f = 0; f < data_blocks; f++) {
280+
port = (s->data_block_counter + f) % 8;
281+
b = (u8 *)&buffer[0];
282+
283+
len = 0;
284+
if (port < p->midi_ports &&
285+
midi_ratelimit_per_packet(s, port) &&
286+
p->midi[port] != NULL)
287+
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
288+
289+
if (len > 0) {
290+
b[3] = (0x10 << port) | len;
291+
midi_use_bytes(s, port, len);
292+
} else {
293+
b[1] = 0;
294+
b[2] = 0;
295+
b[3] = 0;
296+
}
297+
b[0] = 0x80;
298+
299+
buffer += s->data_block_quadlets;
300+
}
301+
}
302+
303+
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
304+
unsigned int data_blocks)
305+
{
306+
struct amdtp_dot *p = s->protocol;
307+
unsigned int f, port, len;
308+
u8 *b;
309+
310+
for (f = 0; f < data_blocks; f++) {
311+
b = (u8 *)&buffer[0];
312+
port = b[3] >> 4;
313+
len = b[3] & 0x0f;
314+
315+
if (port < p->midi_ports && p->midi[port] && len > 0)
316+
snd_rawmidi_receive(p->midi[port], b + 1, len);
317+
318+
buffer += s->data_block_quadlets;
319+
}
320+
}
321+
219322
int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
220323
struct snd_pcm_runtime *runtime)
221324
{
@@ -256,6 +359,15 @@ void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
256359
}
257360
}
258361

362+
void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
363+
struct snd_rawmidi_substream *midi)
364+
{
365+
struct amdtp_dot *p = s->protocol;
366+
367+
if (port < p->midi_ports)
368+
ACCESS_ONCE(p->midi[port]) = midi;
369+
}
370+
259371
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
260372
__be32 *buffer,
261373
unsigned int data_blocks,
@@ -273,7 +385,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
273385
pcm_frames = 0;
274386
}
275387

276-
/* A place holder for MIDI processing. */
388+
read_midi_messages(s, buffer, data_blocks);
277389

278390
return pcm_frames;
279391
}
@@ -296,7 +408,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
296408
pcm_frames = 0;
297409
}
298410

299-
/* A place holder for MIDI processing. */
411+
write_midi_messages(s, buffer, data_blocks);
300412

301413
return pcm_frames;
302414
}

sound/firewire/digi00x/digi00x-stream.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
199199

200200
/* Keep resources for out-stream. */
201201
err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
202-
snd_dg00x_stream_pcm_channels[i], 0);
202+
snd_dg00x_stream_pcm_channels[i]);
203203
if (err < 0)
204204
return err;
205205
err = fw_iso_resources_allocate(&dg00x->rx_resources,
@@ -210,7 +210,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
210210

211211
/* Keep resources for in-stream. */
212212
err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
213-
snd_dg00x_stream_pcm_channels[i], 0);
213+
snd_dg00x_stream_pcm_channels[i]);
214214
if (err < 0)
215215
return err;
216216
err = fw_iso_resources_allocate(&dg00x->tx_resources,

sound/firewire/digi00x/digi00x.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <sound/pcm_params.h>
2525
#include <sound/firewire.h>
2626
#include <sound/hwdep.h>
27+
#include <sound/rawmidi.h>
2728

2829
#include "../lib.h"
2930
#include "../iso-resources.h"
@@ -103,15 +104,19 @@ enum snd_dg00x_optical_mode {
103104
SND_DG00X_OPT_IFACE_MODE_COUNT,
104105
};
105106

107+
#define DOT_MIDI_IN_PORTS 1
108+
#define DOT_MIDI_OUT_PORTS 2
109+
106110
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
107111
enum amdtp_stream_direction dir);
108112
int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
109-
unsigned int pcm_channels,
110-
unsigned int midi_ports);
113+
unsigned int pcm_channels);
111114
void amdtp_dot_reset(struct amdtp_stream *s);
112115
int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
113116
struct snd_pcm_runtime *runtime);
114117
void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
118+
void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
119+
struct snd_rawmidi_substream *midi);
115120

116121
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
117122
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);

0 commit comments

Comments
 (0)