ALSA: ump: Handle MIDI 1.0 Function Block in MIDI 2.0 protocol
authorTakashi Iwai <tiwai@suse.de>
Tue, 6 Aug 2024 07:00:23 +0000 (09:00 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 6 Aug 2024 07:01:23 +0000 (09:01 +0200)
The UMP v1.1 spec says in the section 6.2.1:
"If a UMP Endpoint declares MIDI 2.0 Protocol but a Function Block
represents a MIDI 1.0 connection, then may optionally be used for
messages to/from that Function Block."

It implies that the driver can (and should) keep MIDI 1.0 CVM
exceptionally for those FBs even if UMP Endpoint is running in MIDI
2.0 protocol, and the current driver lacks of it.

This patch extends the sequencer port info to indicate a MIDI 1.0
port, and tries to send/receive MIDI 1.0 CVM as is when this port is
the source or sink.  The sequencer port flag is set by the driver at
parsing FBs and GTBs although application can set it to its own
user-space clients, too.

Link: https://patch.msgid.link/20240806070024.14301-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/ump.h
include/uapi/sound/asequencer.h
sound/core/seq/seq_ports.c
sound/core/seq/seq_ports.h
sound/core/seq/seq_ump_client.c
sound/core/seq/seq_ump_convert.c
sound/core/ump.c

index 7f68056acdffed813e4d40a97fc9cdddfee68ae2..7484f62fb234e37e74e677d4ddc5b28f7bd09e18 100644 (file)
@@ -18,6 +18,7 @@ struct snd_ump_group {
        unsigned int dir_bits;          /* directions */
        bool active;                    /* activeness */
        bool valid;                     /* valid group (referred by blocks) */
+       bool is_midi1;                  /* belongs to a MIDI1 FB */
        char name[64];                  /* group name */
 };
 
index 39b37edcf81316b678158eca3e2572b22e5d7f84..bc30c1f2a10945964edbb1783f68dd7772b532ce 100644 (file)
@@ -461,6 +461,8 @@ struct snd_seq_remove_events {
 #define SNDRV_SEQ_PORT_FLG_TIMESTAMP   (1<<1)
 #define SNDRV_SEQ_PORT_FLG_TIME_REAL   (1<<2)
 
+#define SNDRV_SEQ_PORT_FLG_IS_MIDI1    (1<<3)  /* Keep MIDI 1.0 protocol */
+
 /* port direction */
 #define SNDRV_SEQ_PORT_DIR_UNKNOWN     0
 #define SNDRV_SEQ_PORT_DIR_INPUT       1
index ca631ca4f2c64ebf46292b99a2c6c48128e293cf..535290f24eedbb16c6a711ee9917d82d761144d5 100644 (file)
@@ -362,6 +362,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
                        port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
        }
 
+       port->is_midi1 = !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1);
+
        return 0;
 }
 
index b111382f697aa6c6ce7b9bc0d2ff68deda804855..20a26bffecb78528a4e7e1d1f7e144d1c7bf24c7 100644 (file)
@@ -87,6 +87,8 @@ struct snd_seq_client_port {
        unsigned char direction;
        unsigned char ump_group;
 
+       bool is_midi1;  /* keep MIDI 1.0 protocol */
+
 #if IS_ENABLED(CONFIG_SND_SEQ_UMP)
        struct snd_seq_ump_midi2_bank midi2_bank[16]; /* per channel */
 #endif
index 637154bd1a3fcc07d0175cf0dbbd13ff22aeed3b..e5d3f4d206bf6ac8912fdd0e06a781210e5a8755 100644 (file)
@@ -189,6 +189,8 @@ static void fill_port_info(struct snd_seq_port_info *port,
        port->ump_group = group->group + 1;
        if (!group->active)
                port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE;
+       if (group->is_midi1)
+               port->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
        port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
                SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
                SNDRV_SEQ_PORT_TYPE_HARDWARE |
@@ -223,7 +225,7 @@ static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
                return -ENOMEM;
 
        fill_port_info(port, client, group);
-       port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+       port->flags |= SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
        memset(&pcallbacks, 0, sizeof(pcallbacks));
        pcallbacks.owner = THIS_MODULE;
        pcallbacks.private_data = client;
index d9dacfbe4a9ae776cdbab06aab2f1ff96a52088a..90656980ae26074e90662e1fc162b5bdf890a958 100644 (file)
@@ -595,12 +595,13 @@ int snd_seq_deliver_from_ump(struct snd_seq_client *source,
        type = ump_message_type(ump_ev->ump[0]);
 
        if (snd_seq_client_is_ump(dest)) {
-               if (snd_seq_client_is_midi2(dest) &&
-                   type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
+               bool is_midi2 = snd_seq_client_is_midi2(dest) &&
+                       !dest_port->is_midi1;
+
+               if (is_midi2 && type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
                        return cvt_ump_midi1_to_midi2(dest, dest_port,
                                                      event, atomic, hop);
-               else if (!snd_seq_client_is_midi2(dest) &&
-                        type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)
+               else if (!is_midi2 && type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)
                        return cvt_ump_midi2_to_midi1(dest, dest_port,
                                                      event, atomic, hop);
                /* non-EP port and different group is set? */
@@ -1254,7 +1255,7 @@ int snd_seq_deliver_to_ump(struct snd_seq_client *source,
                return 0; /* group filtered - skip the event */
        if (event->type == SNDRV_SEQ_EVENT_SYSEX)
                return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop);
-       else if (snd_seq_client_is_midi2(dest))
+       else if (snd_seq_client_is_midi2(dest) && !dest_port->is_midi1)
                return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop);
        else
                return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop);
index e39e9cda4912fcb20d11941c3d9e8730b2c741f4..c7c3581bbbbc2f5f489cbfafc3401d57d6e1663c 100644 (file)
@@ -538,6 +538,7 @@ static void update_group_attrs(struct snd_ump_endpoint *ump)
                group->active = 0;
                group->group = i;
                group->valid = false;
+               group->is_midi1 = false;
        }
 
        list_for_each_entry(fb, &ump->block_list, list) {
@@ -548,6 +549,8 @@ static void update_group_attrs(struct snd_ump_endpoint *ump)
                        group->valid = true;
                        if (fb->info.active)
                                group->active = 1;
+                       if (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1)
+                               group->is_midi1 = true;
                        switch (fb->info.direction) {
                        case SNDRV_UMP_DIR_INPUT:
                                group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT);
@@ -1156,6 +1159,7 @@ static int process_legacy_output(struct snd_ump_endpoint *ump,
        struct snd_rawmidi_substream *substream;
        struct ump_cvt_to_ump *ctx;
        const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
+       unsigned int protocol;
        unsigned char c;
        int group, size = 0;
 
@@ -1168,9 +1172,13 @@ static int process_legacy_output(struct snd_ump_endpoint *ump,
                if (!substream)
                        continue;
                ctx = &ump->out_cvts[group];
+               protocol = ump->info.protocol;
+               if ((protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) &&
+                   ump->groups[group].is_midi1)
+                       protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
                while (!ctx->ump_bytes &&
                       snd_rawmidi_transmit(substream, &c, 1) > 0)
-                       snd_ump_convert_to_ump(ctx, group, ump->info.protocol, c);
+                       snd_ump_convert_to_ump(ctx, group, protocol, c);
                if (ctx->ump_bytes && ctx->ump_bytes <= count) {
                        size = ctx->ump_bytes;
                        memcpy(buffer, ctx->ump, size);