summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2023-05-23 09:53:33 +0200
committerTakashi Iwai <tiwai@suse.de>2023-05-23 12:11:07 +0200
commitd9c99876868c861afd0e9ce2cea407bbc446b3c9 (patch)
treedc764d59f023895f0e5bb314d82e079bb09f1bfe
parent51701400a94e999c3109c84f88273a9face51c4b (diff)
ALSA: usb-audio: Create UMP blocks from USB MIDI GTBs
USB MIDI spec defines the Group Terminal Blocks (GTB) that associate multiple UMP Groups. Those correspond to snd_ump_block entities in ALSA UMP abstraction, and now we create those UMP Block objects for each UMP Endpoint from the parsed GTB information. Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20230523075358.9672-13-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/usb/midi2.c100
1 files changed, 94 insertions, 6 deletions
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 790e4cd5d35c..5ffee06ac746 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -599,14 +599,8 @@ find_group_terminal_block(struct snd_usb_midi2_interface *umidi, int id)
static int parse_group_terminal_block(struct snd_usb_midi2_ump *rmidi,
const struct usb_ms20_gr_trm_block_descriptor *desc)
{
- struct snd_usb_audio *chip = rmidi->umidi->chip;
struct snd_ump_endpoint *ump = rmidi->ump;
- usb_audio_dbg(chip,
- "GTB id %d: groups = %d / %d, type = %d\n",
- desc->bGrpTrmBlkID, desc->nGroupTrm, desc->nNumGroupTrm,
- desc->bGrpTrmBlkType);
-
/* set default protocol */
switch (desc->bMIDIProtocol) {
case USB_MS_MIDI_PROTO_1_0_64:
@@ -798,6 +792,94 @@ static int find_matching_ep_partner(struct snd_usb_midi2_interface *umidi,
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)
+{
+ struct snd_usb_midi2_interface *umidi = rmidi->umidi;
+ const struct usb_ms20_gr_trm_block_descriptor *desc;
+ struct snd_ump_block *fb;
+ int type, err;
+
+ desc = find_group_terminal_block(umidi, blk);
+ if (!desc)
+ return 0;
+
+ usb_audio_dbg(umidi->chip,
+ "GTB %d: type=%d, group=%d/%d, protocol=%d, in bw=%d, out bw=%d\n",
+ blk, desc->bGrpTrmBlkType, desc->nGroupTrm,
+ desc->nNumGroupTrm, desc->bMIDIProtocol,
+ __le16_to_cpu(desc->wMaxInputBandwidth),
+ __le16_to_cpu(desc->wMaxOutputBandwidth));
+
+ /* assign the direction */
+ switch (desc->bGrpTrmBlkType) {
+ case USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL:
+ type = SNDRV_UMP_DIR_BIDIRECTION;
+ break;
+ case USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY:
+ type = SNDRV_UMP_DIR_INPUT;
+ break;
+ case USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY:
+ type = SNDRV_UMP_DIR_OUTPUT;
+ break;
+ default:
+ usb_audio_dbg(umidi->chip, "Unsupported GTB type %d\n",
+ desc->bGrpTrmBlkType);
+ return 0; /* unsupported */
+ }
+
+ /* guess work: set blk-1 as the (0-based) block ID */
+ err = snd_ump_block_new(rmidi->ump, blk - 1, type,
+ desc->nGroupTrm, desc->nNumGroupTrm,
+ &fb);
+ if (err == -EBUSY)
+ return 0; /* already present */
+ else if (err)
+ return err;
+
+ if (desc->iBlockItem)
+ usb_string(rmidi->dev, desc->iBlockItem,
+ fb->info.name, sizeof(fb->info.name));
+
+ if (__le16_to_cpu(desc->wMaxInputBandwidth) == 1 ||
+ __le16_to_cpu(desc->wMaxOutputBandwidth) == 1)
+ fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 |
+ SNDRV_UMP_BLOCK_IS_LOWSPEED;
+
+ usb_audio_dbg(umidi->chip,
+ "Created a UMP block %d from GTB, name=%s\n",
+ blk, fb->info.name);
+ return 0;
+}
+
+/* Create UMP blocks for each UMP EP */
+static int create_blocks_from_gtb(struct snd_usb_midi2_interface *umidi)
+{
+ struct snd_usb_midi2_ump *rmidi;
+ int i, blk, err, dir;
+
+ list_for_each_entry(rmidi, &umidi->rawmidi_list, list) {
+ if (!rmidi->ump)
+ continue;
+ /* Blocks have been already created? */
+ if (rmidi->ump->info.num_blocks)
+ continue;
+ /* loop over GTBs */
+ for (dir = 0; dir < 2; dir++) {
+ if (!rmidi->eps[dir])
+ continue;
+ for (i = 0; i < rmidi->eps[dir]->ms_ep->bNumGrpTrmBlock; i++) {
+ blk = rmidi->eps[dir]->ms_ep->baAssoGrpTrmBlkID[i];
+ err = create_gtb_block(rmidi, dir, blk);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
static void snd_usb_midi_v2_free(struct snd_usb_midi2_interface *umidi)
{
free_all_midi2_endpoints(umidi);
@@ -1009,6 +1091,12 @@ int snd_usb_midi_v2_create(struct snd_usb_audio *chip,
goto error;
}
+ err = create_blocks_from_gtb(umidi);
+ if (err < 0) {
+ usb_audio_err(chip, "Failed to create GTB blocks\n");
+ goto error;
+ }
+
set_fallback_rawmidi_names(umidi);
return 0;