Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: gadget: midi2: More flexible MIDI 1.0 configuration

This patch allows users to set up MIDI 1.0 ports more flexibly.
Namely, instead of the fixed mapping only from FB 0, now multiple
block definitions are applied to build up the MIDI 1.0 mapping.

The each block config has midi1_first_group and midi1_num_groups
attributes, and those specify which Groups are used for MIDI 1.0.
Those fields must be within the UMP Groups defined in the block
itself.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://lore.kernel.org/r/20230725062206.9674-8-tiwai@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Takashi Iwai and committed by
Greg Kroah-Hartman
a85ff0db 1b437d2f

+153 -61
+13 -11
Documentation/ABI/testing/configfs-usb-gadget-midi2
··· 39 39 40 40 The attributes: 41 41 42 - =============== =============================================== 43 - name Function Block name string 44 - direction 1: input, 2: output, 3: bidirectional 45 - first_group The first UMP Group number (0-15) 46 - num_groups The number of groups in this FB (1-16) 47 - ui_hint 0: unknown, 1: receiver, 2: sender, 3: both 48 - midi_ci_verison Supported MIDI-CI version number (8 bit) 49 - is_midi1 Legacy MIDI 1.0 device (0, 1 or 2) 50 - sysex8_streams Max number of SysEx8 streams (8 bit) 51 - active Active FB flag (0 or 1) 52 - =============== =============================================== 42 + ================= ============================================== 43 + name Function Block name string 44 + direction 1: input, 2: output, 3: bidirectional 45 + first_group The first UMP Group number (0-15) 46 + num_groups The number of groups in this FB (1-16) 47 + midi1_first_group The first UMP Group number for MIDI 1.0 (0-15) 48 + midi1_num_groups The number of groups for MIDI 1.0 (0-16) 49 + ui_hint 0: unknown, 1: receiver, 2: sender, 3: both 50 + midi_ci_verison Supported MIDI-CI version number (8 bit) 51 + is_midi1 Legacy MIDI 1.0 device (0, 1 or 2) 52 + sysex8_streams Max number of SysEx8 streams (8 bit) 53 + active Active FB flag (0 or 1) 54 + ================= ==============================================
+18 -16
Documentation/usb/gadget-testing.rst
··· 1009 1009 represents the Function Block for Block 0 information. 1010 1010 Its attributes are: 1011 1011 1012 - =============== =============================================== 1013 - name Function Block name string 1014 - direction Direction of this FB 1015 - 1: input, 2: output, or 3: bidirectional 1016 - first_group The first UMP Group number (0-15) 1017 - num_groups The number of groups in this FB (1-16) 1018 - ui_hint UI-hint of this FB 1019 - 0: unknown, 1: receiver, 2: sender, 3: both 1020 - midi_ci_verison Supported MIDI-CI version number (8 bit) 1021 - is_midi1 Legacy MIDI 1.0 device (0-2) 1022 - 0: MIDI 2.0 device, 1023 - 1: MIDI 1.0 without restriction, or 1024 - 2: MIDI 1.0 with low speed 1025 - sysex8_streams Max number of SysEx8 streams (8 bit) 1026 - active Bool flag for FB activity (0 or 1) 1027 - =============== =============================================== 1012 + ================= =============================================== 1013 + name Function Block name string 1014 + direction Direction of this FB 1015 + 1: input, 2: output, or 3: bidirectional 1016 + first_group The first UMP Group number (0-15) 1017 + num_groups The number of groups in this FB (1-16) 1018 + midi1_first_group The first UMP Group number for MIDI 1.0 (0-15) 1019 + midi1_num_groups The number of groups for MIDI 1.0 (0-16) 1020 + ui_hint UI-hint of this FB 1021 + 0: unknown, 1: receiver, 2: sender, 3: both 1022 + midi_ci_verison Supported MIDI-CI version number (8 bit) 1023 + is_midi1 Legacy MIDI 1.0 device (0-2) 1024 + 0: MIDI 2.0 device, 1025 + 1: MIDI 1.0 without restriction, or 1026 + 2: MIDI 1.0 with low speed 1027 + sysex8_streams Max number of SysEx8 streams (8 bit) 1028 + active Bool flag for FB activity (0 or 1) 1029 + ================= =============================================== 1028 1030 1029 1031 If multiple Function Blocks are required, you can add more Function 1030 1032 Blocks by creating subdirectories "block.<num>" with the corresponding
+120 -34
drivers/usb/gadget/function/f_midi2.c
··· 84 84 85 85 struct f_midi2_usb_ep ep_in; /* USB MIDI EP-in */ 86 86 struct f_midi2_usb_ep ep_out; /* USB MIDI EP-out */ 87 + 88 + u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */ 87 89 }; 88 90 89 91 /* indices for USB strings */ ··· 96 94 97 95 /* 1-based GTB id to string id */ 98 96 #define gtb_to_str_id(id) (STR_GTB1 + (id) - 1) 97 + 98 + /* mapping from MIDI 1.0 cable to UMP group */ 99 + struct midi1_cable_mapping { 100 + struct f_midi2_ep *ep; 101 + unsigned char block; 102 + unsigned char group; 103 + }; 99 104 100 105 /* operation mode */ 101 106 enum { ··· 121 112 struct f_midi2_usb_ep midi1_ep_in; 122 113 struct f_midi2_usb_ep midi1_ep_out; 123 114 115 + /* number of MIDI 1.0 I/O cables */ 116 + unsigned int num_midi1_in; 117 + unsigned int num_midi1_out; 118 + 124 119 /* conversion for MIDI 1.0 EP-in */ 125 120 struct f_midi2_midi1_port midi1_port[MAX_CABLES]; 126 121 /* conversion for MIDI 1.0 EP-out */ 127 122 struct ump_cvt_to_ump midi1_ump_cvt; 123 + /* mapping between cables and UMP groups */ 124 + struct midi1_cable_mapping in_cable_mapping[MAX_CABLES]; 125 + struct midi1_cable_mapping out_cable_mapping[MAX_CABLES]; 128 126 129 127 int midi_if; /* USB MIDI interface number */ 130 128 int operation_mode; /* current operation mode */ ··· 933 917 { 934 918 unsigned int cable, c; 935 919 936 - for (cable = 0; cable < midi2->midi2_eps[0].blks[0].info.num_groups; 937 - cable++) { 920 + for (cable = 0; cable < midi2->num_midi1_in; cable++) { 938 921 struct f_midi2_midi1_port *port = &midi2->midi1_port[cable]; 939 922 940 923 if (!port->pending) ··· 975 960 struct usb_request *req = NULL; 976 961 /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */ 977 962 unsigned char outbuf[12]; 978 - unsigned char group; 979 - int len, size, cable; 963 + unsigned char group, cable; 964 + int len, size; 980 965 u32 ump; 981 966 982 967 if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled) ··· 1001 986 &group); 1002 987 if (size <= 0) 1003 988 continue; 1004 - cable = group - ep->blks[0].info.first_group; 1005 - if (cable < 0 || cable >= ep->blks[0].info.num_groups) 989 + cable = ep->in_group_to_cable[group]; 990 + if (!cable) 1006 991 continue; 992 + cable--; /* to 0-base */ 1007 993 fill_midi1_pending_buf(midi2, cable, outbuf, size); 1008 994 } 1009 995 ··· 1041 1025 { 1042 1026 struct f_midi2_req_ctx *ctx = req->context; 1043 1027 struct f_midi2 *midi2 = ctx->usb_ep->card; 1044 - struct f_midi2_ep *ep = &midi2->midi2_eps[0]; 1028 + struct f_midi2_ep *ep; 1045 1029 struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt; 1046 1030 static const u8 midi1_packet_bytes[16] = { 1047 1031 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 1048 1032 }; 1049 - unsigned int group, bytes, c, len; 1033 + unsigned int group, cable, bytes, c, len; 1050 1034 int status = req->status; 1051 1035 const u8 *buf = req->buf; 1052 1036 ··· 1058 1042 1059 1043 len = req->actual >> 2; 1060 1044 for (; len; len--, buf += 4) { 1061 - group = *buf >> 4; 1062 - if (group >= ep->blks[0].info.num_groups) 1045 + cable = *buf >> 4; 1046 + ep = midi2->out_cable_mapping[cable].ep; 1047 + if (!ep) 1063 1048 continue; 1064 - group += ep->blks[0].info.first_group; 1049 + group = midi2->out_cable_mapping[cable].group; 1065 1050 bytes = midi1_packet_bytes[*buf & 0x0f]; 1066 1051 for (c = 0; c < bytes; c++) { 1067 1052 snd_ump_convert_to_ump(cvt, group, ep->info.protocol, ··· 1658 1641 1659 1642 static int append_midi1_in_jack(struct f_midi2 *midi2, 1660 1643 struct f_midi2_usb_config *config, 1644 + struct midi1_cable_mapping *map, 1661 1645 unsigned int type) 1662 1646 { 1663 1647 struct usb_midi_in_jack_descriptor *jack = ··· 1671 1653 jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; 1672 1654 jack->bJackType = type; 1673 1655 jack->bJackID = id; 1674 - jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? 1656 + /* use the corresponding block name as jack name */ 1657 + if (map->ep) 1658 + jack->iJack = map->ep->blks[map->block].string_id; 1675 1659 1676 1660 err = append_config(config, jack); 1677 1661 if (err < 0) ··· 1683 1663 1684 1664 static int append_midi1_out_jack(struct f_midi2 *midi2, 1685 1665 struct f_midi2_usb_config *config, 1666 + struct midi1_cable_mapping *map, 1686 1667 unsigned int type, unsigned int source) 1687 1668 { 1688 1669 struct usb_midi_out_jack_descriptor_1 *jack = ··· 1699 1678 jack->bNrInputPins = 1; 1700 1679 jack->pins[0].baSourceID = source; 1701 1680 jack->pins[0].baSourcePin = 0x01; 1702 - jack->iJack = midi2->strings[STR_GTB1].id; // TODO: better names? 1681 + /* use the corresponding block name as jack name */ 1682 + if (map->ep) 1683 + jack->iJack = map->ep->blks[map->block].string_id; 1703 1684 1704 1685 err = append_config(config, jack); 1705 1686 if (err < 0) ··· 1713 1690 struct f_midi2_usb_config *config, 1714 1691 int speed) 1715 1692 { 1716 - struct f_midi2_block *blk = &midi2->midi2_eps[0].blks[0]; 1717 1693 void **midi1_in_eps, **midi1_out_eps; 1718 1694 int i, jack, total; 1719 1695 int err; ··· 1746 1724 if (err < 0) 1747 1725 return err; 1748 1726 1749 - switch (blk->info.direction) { 1750 - case SNDRV_UMP_DIR_INPUT: 1751 - case SNDRV_UMP_DIR_OUTPUT: 1752 - midi2_midi1_if_desc.bNumEndpoints = 1; 1753 - break; 1754 - default: 1727 + if (midi2->num_midi1_in && midi2->num_midi1_out) 1755 1728 midi2_midi1_if_desc.bNumEndpoints = 2; 1756 - break; 1757 - } 1729 + else 1730 + midi2_midi1_if_desc.bNumEndpoints = 1; 1758 1731 1759 1732 err = append_configs(config, midi2_midi1_descs); 1760 1733 if (err < 0) 1761 1734 return err; 1762 1735 1763 1736 total = USB_DT_MS_HEADER_SIZE; 1764 - if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { 1737 + if (midi2->num_midi1_out) { 1765 1738 midi2_midi1_ep_out_class_desc.bLength = 1766 - USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); 1739 + USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out); 1767 1740 total += midi2_midi1_ep_out_class_desc.bLength; 1768 1741 midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack = 1769 - blk->info.num_groups; 1770 - total += blk->info.num_groups * 1742 + midi2->num_midi1_out; 1743 + total += midi2->num_midi1_out * 1771 1744 (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); 1772 - for (i = 0; i < blk->info.num_groups; i++) { 1745 + for (i = 0; i < midi2->num_midi1_out; i++) { 1773 1746 jack = append_midi1_in_jack(midi2, config, 1747 + &midi2->in_cable_mapping[i], 1774 1748 USB_MS_EMBEDDED); 1775 1749 if (jack < 0) 1776 1750 return jack; 1777 1751 midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack; 1778 1752 jack = append_midi1_out_jack(midi2, config, 1753 + &midi2->in_cable_mapping[i], 1779 1754 USB_MS_EXTERNAL, jack); 1780 1755 if (jack < 0) 1781 1756 return jack; 1782 1757 } 1783 1758 } 1784 1759 1785 - if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { 1760 + if (midi2->num_midi1_in) { 1786 1761 midi2_midi1_ep_in_class_desc.bLength = 1787 - USB_DT_MS_ENDPOINT_SIZE(blk->info.num_groups); 1762 + USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in); 1788 1763 total += midi2_midi1_ep_in_class_desc.bLength; 1789 1764 midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack = 1790 - blk->info.num_groups; 1791 - total += blk->info.num_groups * 1765 + midi2->num_midi1_in; 1766 + total += midi2->num_midi1_in * 1792 1767 (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1)); 1793 - for (i = 0; i < blk->info.num_groups; i++) { 1768 + for (i = 0; i < midi2->num_midi1_in; i++) { 1794 1769 jack = append_midi1_in_jack(midi2, config, 1770 + &midi2->out_cable_mapping[i], 1795 1771 USB_MS_EXTERNAL); 1796 1772 if (jack < 0) 1797 1773 return jack; 1798 1774 jack = append_midi1_out_jack(midi2, config, 1775 + &midi2->out_cable_mapping[i], 1799 1776 USB_MS_EMBEDDED, jack); 1800 1777 if (jack < 0) 1801 1778 return jack; ··· 1804 1783 1805 1784 midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total); 1806 1785 1807 - if (blk->info.direction != SNDRV_UMP_DIR_INPUT) { 1786 + if (midi2->num_midi1_out) { 1808 1787 err = append_configs(config, midi1_out_eps); 1809 1788 if (err < 0) 1810 1789 return err; 1811 1790 } 1812 - if (blk->info.direction != SNDRV_UMP_DIR_OUTPUT) { 1791 + if (midi2->num_midi1_in) { 1813 1792 err = append_configs(config, midi1_in_eps); 1814 1793 if (err < 0) 1815 1794 return err; ··· 2257 2236 F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3); 2258 2237 F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15); 2259 2238 F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16); 2239 + F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15); 2240 + F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16); 2260 2241 F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3); 2261 2242 F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1); 2262 2243 F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255); ··· 2288 2265 &f_midi2_block_opts_attr_direction, 2289 2266 &f_midi2_block_opts_attr_first_group, 2290 2267 &f_midi2_block_opts_attr_num_groups, 2268 + &f_midi2_block_opts_attr_midi1_first_group, 2269 + &f_midi2_block_opts_attr_midi1_num_groups, 2291 2270 &f_midi2_block_opts_attr_ui_hint, 2292 2271 &f_midi2_block_opts_attr_midi_ci_version, 2293 2272 &f_midi2_block_opts_attr_sysex8_streams, ··· 2669 2644 return ERR_PTR(ret); 2670 2645 } 2671 2646 2647 + /* set up the default MIDI1 (that is mandatory) */ 2648 + block_opts->info.midi1_num_groups = 1; 2649 + 2672 2650 config_group_init_type_name(&opts->func_inst.group, "", 2673 2651 &f_midi2_func_type); 2674 2652 ··· 2735 2707 i, j); 2736 2708 return -EINVAL; 2737 2709 } 2710 + 2711 + if (bp->midi1_num_groups) { 2712 + if (bp->midi1_first_group < bp->first_group || 2713 + bp->midi1_first_group + bp->midi1_num_groups > 2714 + bp->first_group + bp->num_groups) { 2715 + pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n", 2716 + i, j); 2717 + return -EINVAL; 2718 + } 2719 + } 2738 2720 } 2739 2721 } 2740 2722 if (!num_blks) { ··· 2753 2715 } 2754 2716 2755 2717 return num_eps; 2718 + } 2719 + 2720 + /* fill mapping between MIDI 1.0 cable and UMP EP/group */ 2721 + static void fill_midi1_cable_mapping(struct f_midi2 *midi2, 2722 + struct f_midi2_ep *ep, 2723 + int blk) 2724 + { 2725 + const struct f_midi2_block_info *binfo = &ep->blks[blk].info; 2726 + struct midi1_cable_mapping *map; 2727 + int i, group; 2728 + 2729 + if (!binfo->midi1_num_groups) 2730 + return; 2731 + if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) { 2732 + group = binfo->midi1_first_group; 2733 + map = midi2->in_cable_mapping + midi2->num_midi1_in; 2734 + for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { 2735 + if (midi2->num_midi1_in >= MAX_CABLES) 2736 + break; 2737 + map->ep = ep; 2738 + map->block = blk; 2739 + map->group = group; 2740 + midi2->num_midi1_in++; 2741 + /* store 1-based cable number */ 2742 + ep->in_group_to_cable[group] = midi2->num_midi1_in; 2743 + } 2744 + } 2745 + 2746 + if (binfo->direction != SNDRV_UMP_DIR_INPUT) { 2747 + group = binfo->midi1_first_group; 2748 + map = midi2->out_cable_mapping + midi2->num_midi1_out; 2749 + for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) { 2750 + if (midi2->num_midi1_out >= MAX_CABLES) 2751 + break; 2752 + map->ep = ep; 2753 + map->block = blk; 2754 + map->group = group; 2755 + midi2->num_midi1_out++; 2756 + } 2757 + } 2756 2758 } 2757 2759 2758 2760 /* gadget alloc callback */ ··· 2864 2786 bp = &ep->blks[blk]; 2865 2787 midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s = 2866 2788 ump_fb_name(&bp->info); 2789 + 2790 + fill_midi1_cable_mapping(midi2, ep, blk); 2867 2791 } 2792 + } 2793 + 2794 + if (!midi2->num_midi1_in && !midi2->num_midi1_out) { 2795 + pr_err("f_midi2: MIDI1 definition is missing\n"); 2796 + do_f_midi2_free(midi2, opts); 2797 + return ERR_PTR(-EINVAL); 2868 2798 } 2869 2799 2870 2800 return &midi2->func;
+2
drivers/usb/gadget/function/u_midi2.h
··· 18 18 unsigned int direction; /* FB direction: 1-3 */ 19 19 unsigned int first_group; /* first UMP group: 0-15 */ 20 20 unsigned int num_groups; /* number of UMP groups: 1-16 */ 21 + unsigned int midi1_first_group; /* first UMP group for MIDI 1.0 */ 22 + unsigned int midi1_num_groups; /* number of UMP groups for MIDI 1.0 */ 21 23 unsigned int ui_hint; /* UI-hint: 0-3 */ 22 24 unsigned int midi_ci_version; /* MIDI-CI version: 0-255 */ 23 25 unsigned int sysex8_streams; /* number of sysex8 streams: 0-255 */