/* 'Clock-based rate control mode' is just supported. */
 #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.
        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,
 }
 
 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;
        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;
 }
        }
 }
 
+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)
 {
        }
 }
 
+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,
                pcm_frames = 0;
        }
 
-       /* A place holder for MIDI processing. */
+       read_midi_messages(s, buffer, data_blocks);
 
        return pcm_frames;
 }
                pcm_frames = 0;
        }
 
-       /* A place holder for MIDI processing. */
+       write_midi_messages(s, buffer, data_blocks);
 
        return pcm_frames;
 }
 
 #include <sound/pcm_params.h>
 #include <sound/firewire.h>
 #include <sound/hwdep.h>
+#include <sound/rawmidi.h>
 
 #include "../lib.h"
 #include "../iso-resources.h"
        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);