ALSA: usb-audio: Parse UMP Endpoint and Function Blocks at first
authorTakashi Iwai <tiwai@suse.de>
Mon, 12 Jun 2023 08:10:47 +0000 (10:10 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 12 Jun 2023 16:22:28 +0000 (18:22 +0200)
Try to parse the UMP Endpoint and UMP Function Blocks for building the
topology at first.  Only when those are missing (e.g. on an older USB
MIDI 2.0 spec or a unidirectional endpoint), the driver still creates
blocks based on USB group terminal block information as fallback.

Link: https://lore.kernel.org/r/20230612081054.17200-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/usb/midi2.c

index 3417834..fad094e 100644 (file)
@@ -80,6 +80,7 @@ struct snd_usb_midi2_ump {
        struct snd_usb_midi2_endpoint *eps[2];  /* USB MIDI endpoints */
        int index;                              /* rawmidi device index */
        unsigned char usb_block_id;             /* USB GTB id used for finding a pair */
+       bool ump_parsed;                        /* Parsed UMP 1.1 EP/FB info*/
        struct list_head list;          /* list to umidi->rawmidi_list */
 };
 
@@ -786,6 +787,31 @@ static int find_matching_ep_partner(struct snd_usb_midi2_interface *umidi,
        return 0;
 }
 
+/* Call UMP helper to parse UMP endpoints;
+ * this needs to be called after starting the input streams for bi-directional
+ * communications
+ */
+static int parse_ump_endpoints(struct snd_usb_midi2_interface *umidi)
+{
+       struct snd_usb_midi2_ump *rmidi;
+       int err;
+
+       list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+               if (!rmidi->ump ||
+                   !(rmidi->ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
+                       continue;
+               err = snd_ump_parse_endpoint(rmidi->ump);
+               if (!err) {
+                       rmidi->ump_parsed = true;
+               } else {
+                       if (err == -ENOMEM)
+                               return err;
+                       /* fall back to GTB later */
+               }
+       }
+       return 0;
+}
+
 /* create a UMP block from a GTB entry */
 static int create_gtb_block(struct snd_usb_midi2_ump *rmidi, int dir, int blk)
 {
@@ -856,7 +882,7 @@ static int create_blocks_from_gtb(struct snd_usb_midi2_interface *umidi)
                if (!rmidi->ump)
                        continue;
                /* Blocks have been already created? */
-               if (rmidi->ump->info.num_blocks)
+               if (rmidi->ump_parsed || rmidi->ump->info.num_blocks)
                        continue;
                /* loop over GTBs */
                for (dir = 0; dir < 2; dir++) {
@@ -1110,6 +1136,12 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
                goto error;
        }
 
+       err = parse_ump_endpoints(umidi);
+       if (err < 0) {
+               usb_audio_err(chip, "Failed to parse UMP endpoint\n");
+               goto error;
+       }
+
        err = create_blocks_from_gtb(umidi);
        if (err < 0) {
                usb_audio_err(chip, "Failed to create GTB blocks\n");