2023-06-12 08:28:54

by Takashi Iwai

[permalink] [raw]
Subject: [PATCH 00/10] ALSA: Catch up MIDI 2.0 updates for UMP 1.1

Hi,

as the updated MIDI 2.0 spec has been published freshly [*], this is a
catch up to add the support for new specs, especially UMP v1.1
features, on Linux kernel.

The new UMP v1.1 introduced the concept of Function Blocks (FB), which
is a kind of superset of USB MIDI 2.0 Group Terminal Blocks (GTB).
The patch set adds the support for FB as the primary information
source while keeping the parse of GTB as fallback. Also UMP v1.1
supports the groupless messages, the protocol switch, static FBs, and
other new fundamental features, and those are supported as well.


Takashi

[*] https://www.midi.org/midi-articles/details-about-midi-2-0-midi-ci-profiles-and-property-exchange

===

Takashi Iwai (10):
ALSA: ump: Add more attributes to UMP EP and FB info
ALSA: ump: Support UMP Endpoint and Function Block parsing
ALSA: usb-audio: Parse UMP Endpoint and Function Blocks at first
ALSA: usb-audio: Add midi2_ump_probe option
ALSA: seq: ump: Handle groupless messages
ALSA: seq: ump: Handle FB info update
ALSA: seq: ump: Notify port changes to system port
ALSA: seq: ump: Notify UMP protocol change to sequencer
ALSA: ump: Add info flag bit for static blocks
ALSA: docs: Update MIDI 2.0 documentation for UMP 1.1 enhancement

Documentation/sound/designs/midi-2.0.rst | 39 +-
include/sound/ump.h | 89 +++++
include/sound/ump_msg.h | 225 +++++++++++
include/uapi/sound/asequencer.h | 5 +-
include/uapi/sound/asound.h | 21 +-
sound/core/seq/seq_system.c | 1 +
sound/core/seq/seq_ump_client.c | 79 +++-
sound/core/seq/seq_ump_convert.c | 3 +
sound/core/ump.c | 470 +++++++++++++++++++++++
sound/usb/midi2.c | 42 +-
10 files changed, 962 insertions(+), 12 deletions(-)

--
2.35.3



2023-06-12 08:42:03

by Takashi Iwai

[permalink] [raw]
Subject: [PATCH 10/10] ALSA: docs: Update MIDI 2.0 documentation for UMP 1.1 enhancement

There have been a few enhancements for the new UMP 1.1 features.
Update the documentation accordingly.

Signed-off-by: Takashi Iwai <[email protected]>
---
Documentation/sound/designs/midi-2.0.rst | 39 ++++++++++++++++++++----
1 file changed, 33 insertions(+), 6 deletions(-)

diff --git a/Documentation/sound/designs/midi-2.0.rst b/Documentation/sound/designs/midi-2.0.rst
index d55b0a4c6acb..27d0d3dea1b0 100644
--- a/Documentation/sound/designs/midi-2.0.rst
+++ b/Documentation/sound/designs/midi-2.0.rst
@@ -68,6 +68,15 @@ default instead of the MIDI 1.0 interface (at altset 0). You can
switch back to the binding with the old MIDI 1.0 interface by passing
`midi2_enable=0` option to snd-usb-audio driver module, too.

+The USB audio driver tries to query the UMP Endpoint and UMP Function
+Block information that are provided since UMP v1.1, and builds up the
+topology based on those information. When the device is older and
+doesn't respond to the new UMP inquiries, the driver falls back and
+builds the topology based on Group Terminal Block (GTB) information
+from the USB descriptor. Some device might be screwed up by the
+unexpected UMP command; in such a case, pass `midi2_probe=0` option to
+snd-usb-audio driver for skipping the UMP v1.1 inquiries.
+
When the MIDI 2.0 device is probed, the kernel creates a rawmidi
device for each UMP Endpoint of the device. Its device name is
`/dev/snd/umpC*D*` and different from the standard rawmidi device name
@@ -101,11 +110,15 @@ opening `/dev/snd/midiC*D*` will end up with opening the first
substream.

Each UMP Endpoint can provide the additional information, constructed
-from USB MIDI 2.0 descriptors. And a UMP Endpoint may contain one or
-more UMP Blocks, where UMP Block is an abstraction introduced in the
-ALSA UMP implementations to represent the associations among UMP
-Groups. UMP Block corresponds to Group Terminal Block (GTB) in USB
-MIDI 2.0 specifications but provide a few more generic information.
+from the information inquired via UMP 1.1 Stream messages or USB MIDI
+2.0 descriptors. And a UMP Endpoint may contain one or more UMP
+Blocks, where UMP Block is an abstraction introduced in the ALSA UMP
+implementations to represent the associations among UMP Groups. UMP
+Block corresponds to Function Block in UMP 1.1 specification. When
+UMP 1.1 Function Block information isn't available, it's filled
+partially from Group Terminal Block (GTB) as defined in USB MIDI 2.0
+specifications.
+
The information of UMP Endpoints and UMP Blocks are found in the proc
file `/proc/asound/card*/midi*`. For example::

@@ -207,6 +220,8 @@ The "MIDI 2.0" port is for a UMP Endpoint, and its difference from
other UMP Group ports is that UMP Endpoint port sends the events from
the all ports on the device ("catch-all"), while each UMP Group port
sends only the events from the given UMP Group.
+Also, UMP groupless messages (such as the UMP message type 0x0f) are
+sent only to the UMP Endpoint port.

Note that, although each UMP sequencer client usually creates 16
ports, those ports that don't belong to any UMP Blocks (or belonging
@@ -273,6 +288,11 @@ Rawmidi API Extensions
The direction is either `SNDRV_UMP_DIR_INPUT`,
`SNDRV_UMP_DIR_OUTPUT` or `SNDRV_UMP_DIR_BIDIRECTION`.

+* For the device supports UMP v1.1, the UMP MIDI protocol can be
+ switched via "Stream Configuration Request" message (UMP type 0x0f,
+ status 0x05). When UMP core receives such a message, it updates the
+ UMP EP info and the corresponding sequencer clients as well.
+

Control API Extensions
======================
@@ -337,7 +357,7 @@ Sequencer API Extensions
`group_filter` bitmap. The filter consists of bitmap from 1-based
Group numbers. For example, when the bit 1 is set, messages from
Group 1 (i.e. the very first group) are filtered and not delivered.
- The bit 0 is reserved for future use.
+ The bit 0 is used for filtering UMP groupless messages.

* Two new ioctls are added for UMP-capable clients:
`SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO` and
@@ -349,3 +369,10 @@ Sequencer API Extensions
For an Endpoint data, pass 0 to the `type` field, while for a Block
data, pass the block number + 1 to the `type` field.
Setting the data for a kernel client shall result in an error.
+
+* With UMP 1.1, Function Block information may be changed
+ dynamically. When the update of Function Block is received from the
+ device, ALSA sequencer core changes the corresponding sequencer port
+ name and attributes accordingly, and notifies the changes via the
+ announcement to the ALSA sequencer system port, similarly like the
+ normal port change notification.
--
2.35.3


2023-06-12 08:53:00

by Takashi Iwai

[permalink] [raw]
Subject: [PATCH 02/10] ALSA: ump: Support UMP Endpoint and Function Block parsing

This patch adds the basic support for UMP Endpoint and UMP Function
Block parsing, which are extended in the new UMP v1.1 spec.
The patch provides a new helper function to perform the query of the
UMP Endpoint information and builds up the UMP blocks based on UMP
Function Block information. For the communication over the UMP
Endpoint, it opens the rawmidi device once internally, inquiries the
UMP Endpoint and Function Block info by sending new UMP Stream
messages, and waits for the response for each query.

The new UMP spec allows to update the FB info and change its
associated groups or its activeness on the fly, too. For catching it,
the UMP core keeps watching the incoming UMP messages, and
snd_ump_receive() handles the incoming UMP Stream messages to refresh
the FB info.

Signed-off-by: Takashi Iwai <[email protected]>
---
include/sound/ump.h | 83 +++++++++
include/sound/ump_msg.h | 225 ++++++++++++++++++++++++
sound/core/ump.c | 376 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 684 insertions(+)

diff --git a/include/sound/ump.h b/include/sound/ump.h
index e4fdf7cccf12..aef4748842d0 100644
--- a/include/sound/ump.h
+++ b/include/sound/ump.h
@@ -24,6 +24,13 @@ struct snd_ump_endpoint {
void *private_data;
void (*private_free)(struct snd_ump_endpoint *ump);

+ /* UMP Stream message processing */
+ u32 stream_wait_for; /* expected stream message status */
+ bool stream_finished; /* set when message has been processed */
+ bool parsed; /* UMP / FB parse finished? */
+ wait_queue_head_t stream_wait;
+ struct snd_rawmidi_file stream_rfile;
+
struct list_head block_list; /* list of snd_ump_block objects */

/* intermediate buffer for UMP input */
@@ -80,6 +87,7 @@ struct snd_ump_block {
int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
int output, int input,
struct snd_ump_endpoint **ump_ret);
+int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump);
int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
unsigned int direction, unsigned int first_group,
unsigned int num_groups, struct snd_ump_block **blk_ret);
@@ -109,6 +117,8 @@ enum {
UMP_MSG_TYPE_DATA = 0x03,
UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04,
UMP_MSG_TYPE_EXTENDED_DATA = 0x05,
+ UMP_MSG_TYPE_FLEX_DATA = 0x0d,
+ UMP_MSG_TYPE_STREAM = 0x0f,
};

/* MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */
@@ -119,6 +129,62 @@ enum {
UMP_SYSEX_STATUS_END = 3,
};

+/* UMP Utility Type Status (type 0x0) */
+enum {
+ UMP_UTILITY_MSG_STATUS_NOOP = 0x00,
+ UMP_UTILITY_MSG_STATUS_JR_CLOCK = 0x01,
+ UMP_UTILITY_MSG_STATUS_JR_TSTAMP = 0x02,
+ UMP_UTILITY_MSG_STATUS_DCTPQ = 0x03,
+ UMP_UTILITY_MSG_STATUS_DC = 0x04,
+};
+
+/* UMP Stream Message Status (type 0xf) */
+enum {
+ UMP_STREAM_MSG_STATUS_EP_DISCOVERY = 0x00,
+ UMP_STREAM_MSG_STATUS_EP_INFO = 0x01,
+ UMP_STREAM_MSG_STATUS_DEVICE_INFO = 0x02,
+ UMP_STREAM_MSG_STATUS_EP_NAME = 0x03,
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID = 0x04,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST = 0x05,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG = 0x06,
+ UMP_STREAM_MSG_STATUS_FB_DISCOVERY = 0x10,
+ UMP_STREAM_MSG_STATUS_FB_INFO = 0x11,
+ UMP_STREAM_MSG_STATUS_FB_NAME = 0x12,
+ UMP_STREAM_MSG_STATUS_START_CLIP = 0x20,
+ UMP_STREAM_MSG_STATUS_END_CLIP = 0x21,
+};
+
+/* UMP Endpoint Discovery filter bitmap */
+enum {
+ UMP_STREAM_MSG_REQUEST_EP_INFO = (1U << 0),
+ UMP_STREAM_MSG_REQUEST_DEVICE_INFO = (1U << 1),
+ UMP_STREAM_MSG_REQUEST_EP_NAME = (1U << 2),
+ UMP_STREAM_MSG_REQUEST_PRODUCT_ID = (1U << 3),
+ UMP_STREAM_MSG_REQUEST_STREAM_CFG = (1U << 4),
+};
+
+/* UMP Function Block Discovery filter bitmap */
+enum {
+ UMP_STREAM_MSG_REQUEST_FB_INFO = (1U << 0),
+ UMP_STREAM_MSG_REQUEST_FB_NAME = (1U << 1),
+};
+
+/* UMP Endpoint Info capability bits (used for protocol request/notify, too) */
+enum {
+ UMP_STREAM_MSG_EP_INFO_CAP_TXJR = (1U << 0), /* Sending JRTS */
+ UMP_STREAM_MSG_EP_INFO_CAP_RXJR = (1U << 1), /* Receiving JRTS */
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 = (1U << 8), /* MIDI 1.0 */
+ UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 = (1U << 9), /* MIDI 2.0 */
+};
+
+/* UMP EP / FB name string format; same as SysEx string handling */
+enum {
+ UMP_STREAM_MSG_FORMAT_SINGLE = 0,
+ UMP_STREAM_MSG_FORMAT_START = 1,
+ UMP_STREAM_MSG_FORMAT_CONTINUE = 2,
+ UMP_STREAM_MSG_FORMAT_END = 3,
+};
+
/*
* Helpers for retrieving / filling bits from UMP
*/
@@ -172,4 +238,21 @@ static inline unsigned char ump_sysex_message_length(u32 data)
return (data >> 16) & 0xf;
}

+/* For Stream Messages */
+static inline unsigned char ump_stream_message_format(u32 data)
+{
+ return (data >> 26) & 0x03;
+}
+
+static inline unsigned int ump_stream_message_status(u32 data)
+{
+ return (data >> 16) & 0x3ff;
+}
+
+static inline u32 ump_stream_compose(unsigned char status, unsigned short form)
+{
+ return (UMP_MSG_TYPE_STREAM << 28) | ((u32)form << 26) |
+ ((u32)status << 16);
+}
+
#endif /* __SOUND_UMP_H */
diff --git a/include/sound/ump_msg.h b/include/sound/ump_msg.h
index a594ef951b54..72f60ddfea75 100644
--- a/include/sound/ump_msg.h
+++ b/include/sound/ump_msg.h
@@ -537,4 +537,229 @@ union snd_ump_midi2_msg {
u32 raw[2];
};

+/* UMP Stream Message: Endpoint Discovery (128bit) */
+struct snd_ump_stream_msg_ep_discovery {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 ump_version_major:8;
+ u32 ump_version_minor:8;
+ /* 1 */
+ u32 reserved:24;
+ u32 filter_bitmap:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#else
+ /* 0 */
+ u32 ump_version_minor:8;
+ u32 ump_version_major:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 filter_bitmap:8;
+ u32 reserved:24;
+ /* 2-3 */
+ u32 reserved2[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Endpoint Info Notification (128bit) */
+struct snd_ump_stream_msg_ep_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 ump_version_major:8;
+ u32 ump_version_minor:8;
+ /* 1 */
+ u32 static_function_block:1;
+ u32 num_function_blocks:7;
+ u32 reserved:8;
+ u32 protocol:8;
+ u32 reserved2:6;
+ u32 jrts:2;
+ /* 2-3 */
+ u32 reserved3[2];
+#else
+ /* 0 */
+ u32 ump_version_minor:8;
+ u32 ump_version_major:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 jrts:2;
+ u32 reserved2:6;
+ u32 protocol:8;
+ u32 reserved:8;
+ u32 num_function_blocks:7;
+ u32 static_function_block:1;
+ /* 2-3 */
+ u32 reserved3[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Device Info Notification (128bit) */
+struct snd_ump_stream_msg_devince_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 reserved:16;
+ /* 1 */
+ u32 manufacture_id;
+ /* 2 */
+ u8 family_lsb;
+ u8 family_msb;
+ u8 model_lsb;
+ u8 model_msb;
+ /* 3 */
+ u32 sw_revision;
+#else
+ /* 0 */
+ u32 reserved:16;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 manufacture_id;
+ /* 2 */
+ u8 model_msb;
+ u8 model_lsb;
+ u8 family_msb;
+ u8 family_lsb;
+ /* 3 */
+ u32 sw_revision;
+#endif
+} __packed;
+
+/* UMP Stream Message: Stream Config Request / Notification (128bit) */
+struct snd_ump_stream_msg_stream_cfg {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 protocol:8;
+ u32 reserved:6;
+ u32 jrts:2;
+ /* 1-3 */
+ u32 reserved2[3];
+#else
+ /* 0 */
+ u32 jrts:2;
+ u32 reserved:6;
+ u32 protocol:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1-3 */
+ u32 reserved2[3];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Discovery (128bit) */
+struct snd_ump_stream_msg_fb_discovery {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 function_block_id:8;
+ u32 filter:8;
+ /* 1-3 */
+ u32 reserved[3];
+#else
+ /* 0 */
+ u32 filter:8;
+ u32 function_block_id:8;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1-3 */
+ u32 reserved[3];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Info Notification (128bit) */
+struct snd_ump_stream_msg_fb_info {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u32 type:4;
+ u32 format:2;
+ u32 status:10;
+ u32 active:1;
+ u32 function_block_id:7;
+ u32 reserved:2;
+ u32 ui_hint:2;
+ u32 midi_10:2;
+ u32 direction:2;
+ /* 1 */
+ u32 first_group:8;
+ u32 num_groups:8;
+ u32 midi_ci_version:8;
+ u32 sysex8_streams:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#else
+ /* 0 */
+ u32 direction:2;
+ u32 midi_10:2;
+ u32 ui_hint:2;
+ u32 reserved:2;
+ u32 function_block_id:7;
+ u32 active:1;
+ u32 status:10;
+ u32 format:2;
+ u32 type:4;
+ /* 1 */
+ u32 sysex8_streams:8;
+ u32 midi_ci_version:8;
+ u32 num_groups:8;
+ u32 first_group:8;
+ /* 2-3 */
+ u32 reserved2[2];
+#endif
+} __packed;
+
+/* UMP Stream Message: Function Block Name Notification (128bit) */
+struct snd_ump_stream_msg_fb_name {
+#ifdef __BIG_ENDIAN_BITFIELD
+ /* 0 */
+ u16 type:4;
+ u16 format:2;
+ u16 status:10;
+ u8 function_block_id;
+ u8 name0;
+ /* 1-3 */
+ u8 name[12];
+#else
+ /* 0 */
+ u8 name0;
+ u8 function_block_id;
+ u16 status:10;
+ u16 format:2;
+ u16 type:4;
+ /* 1-3 */
+ u8 name[12]; // FIXME: byte order
+#endif
+} __packed;
+
+/* MIDI 2.0 Stream Messages (128bit) */
+union snd_ump_stream_msg {
+ struct snd_ump_stream_msg_ep_discovery ep_discovery;
+ struct snd_ump_stream_msg_ep_info ep_info;
+ struct snd_ump_stream_msg_devince_info device_info;
+ struct snd_ump_stream_msg_stream_cfg stream_cfg;
+ struct snd_ump_stream_msg_fb_discovery fb_discovery;
+ struct snd_ump_stream_msg_fb_info fb_info;
+ struct snd_ump_stream_msg_fb_name fb_name;
+ u32 raw[4];
+};
+
#endif /* __SOUND_UMP_MSG_H */
diff --git a/sound/core/ump.c b/sound/core/ump.c
index 839873fb0f33..7df50f0affe9 100644
--- a/sound/core/ump.c
+++ b/sound/core/ump.c
@@ -30,6 +30,8 @@ static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
int up);
static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);

+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size);
#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
static int process_legacy_output(struct snd_ump_endpoint *ump,
u32 *buffer, int count);
@@ -133,6 +135,7 @@ int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
return -ENOMEM;
INIT_LIST_HEAD(&ump->block_list);
mutex_init(&ump->open_mutex);
+ init_waitqueue_head(&ump->stream_wait);
#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
spin_lock_init(&ump->legacy_locks[0]);
spin_lock_init(&ump->legacy_locks[1]);
@@ -302,6 +305,7 @@ int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
n = snd_ump_receive_ump_val(ump, *p++);
if (!n)
continue;
+ ump_handle_stream_msg(ump, ump->input_buf, n);
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (ump->seq_ops)
ump->seq_ops->input_receive(ump, ump->input_buf, n);
@@ -513,6 +517,378 @@ static void snd_ump_proc_read(struct snd_info_entry *entry,
}
}

+/*
+ * UMP endpoint and function block handling
+ */
+
+/* open / close UMP streams for the internal stream msg communication */
+static int ump_request_open(struct snd_ump_endpoint *ump)
+{
+ return snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &ump->stream_rfile);
+}
+
+static void ump_request_close(struct snd_ump_endpoint *ump)
+{
+ snd_rawmidi_kernel_release(&ump->stream_rfile);
+}
+
+/* request a command and wait for the given response;
+ * @req1 and @req2 are u32 commands
+ * @reply is the expected UMP stream status
+ */
+static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
+ u32 reply)
+{
+ u32 buf[4];
+
+ ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
+ __func__, req1, req2, reply);
+ memset(buf, 0, sizeof(buf));
+ buf[0] = req1;
+ buf[1] = req2;
+ ump->stream_finished = 0;
+ ump->stream_wait_for = reply;
+ snd_rawmidi_kernel_write(ump->stream_rfile.output,
+ (unsigned char *)&buf, 16);
+ wait_event_timeout(ump->stream_wait, ump->stream_finished,
+ msecs_to_jiffies(500));
+ if (!READ_ONCE(ump->stream_finished)) {
+ ump_dbg(ump, "%s: request timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+ ump->stream_finished = 0;
+ ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+ return 0;
+}
+
+/* append the received letters via UMP packet to the given string buffer;
+ * return 1 if the full string is received or 0 to continue
+ */
+static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
+ int maxsize, const u32 *buf, int offset)
+{
+ unsigned char format;
+ int c;
+
+ format = ump_stream_message_format(buf[0]);
+ if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_START) {
+ c = 0;
+ } else {
+ c = strlen(dest);
+ if (c >= maxsize - 1)
+ return 1;
+ }
+
+ for (; offset < 16; offset++) {
+ dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
+ if (!dest[c])
+ break;
+ if (++c >= maxsize - 1)
+ break;
+ }
+ dest[c] = 0;
+ return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_END);
+}
+
+/* handle EP info stream message; update the UMP attributes */
+static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.version = (buf->ep_info.ump_version_major << 8) |
+ buf->ep_info.ump_version_minor;
+ ump->info.num_blocks = buf->ep_info.num_function_blocks;
+ if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
+ ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
+ ump->info.num_blocks);
+ ump->info.num_blocks = 1;
+ }
+
+ ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
+ buf->ep_info.jrts;
+
+ ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
+ ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
+ return 1; /* finished */
+}
+
+/* handle EP device info stream message; update the UMP attributes */
+static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
+ ump->info.family_id = (buf->device_info.family_msb << 8) |
+ buf->device_info.family_lsb;
+ ump->info.model_id = (buf->device_info.model_msb << 8) |
+ buf->device_info.model_lsb;
+ ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
+ ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
+ ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
+ ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
+ ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%02x%02x%02x%02x\n",
+ ump->info.manufacturer_id,
+ ump->info.family_id,
+ ump->info.model_id,
+ ump->info.sw_revision[0],
+ ump->info.sw_revision[1],
+ ump->info.sw_revision[2],
+ ump->info.sw_revision[3]);
+ return 1; /* finished */
+}
+
+/* handle EP name stream message; update the UMP name string */
+static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
+ buf->raw, 2);
+}
+
+/* handle EP product id stream message; update the UMP product_id string */
+static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ return ump_append_string(ump, ump->info.product_id,
+ sizeof(ump->info.product_id),
+ buf->raw, 2);
+}
+
+/* handle EP stream config message; update the UMP protocol */
+static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.protocol =
+ (buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
+ ump_dbg(ump, "Current protocol = %x (caps = %x)\n",
+ ump->info.protocol, ump->info.protocol_caps);
+ return 1; /* finished */
+}
+
+/* Extract Function Block info from UMP packet */
+static void fill_fb_info(struct snd_ump_endpoint *ump,
+ struct snd_ump_block_info *info,
+ const union snd_ump_stream_msg *buf)
+{
+ info->direction = buf->fb_info.direction;
+ info->ui_hint = buf->fb_info.ui_hint;
+ info->first_group = buf->fb_info.first_group;
+ info->num_groups = buf->fb_info.num_groups;
+ info->flags = buf->fb_info.midi_10;
+ info->active = buf->fb_info.active;
+ info->midi_ci_version = buf->fb_info.midi_ci_version;
+ info->sysex8_streams = buf->fb_info.sysex8_streams;
+
+ ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
+ info->block_id, info->direction, info->active,
+ info->first_group, info->num_groups, info->midi_ci_version,
+ info->sysex8_streams, info->flags);
+}
+
+/* handle FB info message; update FB info if the block is present */
+static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+
+ blk = buf->fb_info.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+ if (fb) {
+ fill_fb_info(ump, &fb->info, buf);
+ } else if (ump->parsed) {
+ /* complain only if updated after parsing */
+ ump_info(ump, "Function Block Info Update for non-existing block %d\n",
+ blk);
+ return -ENODEV;
+ }
+ return 1; /* finished */
+}
+
+/* handle FB name message; update the FB name string */
+static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+
+ blk = buf->fb_name.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+ if (!fb)
+ return -ENODEV;
+
+ return ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
+ buf->raw, 3);
+}
+
+static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
+{
+ struct snd_ump_block *fb;
+ unsigned char direction, first_group, num_groups;
+ const union snd_ump_stream_msg *buf =
+ (const union snd_ump_stream_msg *)ump->input_buf;
+ u32 msg;
+ int err;
+
+ /* query the FB info once */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get FB info for block %d\n", blk);
+ return err;
+ }
+
+ /* the last input must be the FB info */
+ if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
+ ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw);
+ return -EINVAL;
+ }
+
+ direction = buf->fb_info.direction;
+ first_group = buf->fb_info.first_group;
+ num_groups = buf->fb_info.num_groups;
+
+ err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
+ &fb);
+ if (err < 0)
+ return err;
+
+ fill_fb_info(ump, &fb->info, buf);
+
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
+ if (err)
+ ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
+
+ return 0;
+}
+
+/* handle stream messages, called from snd_ump_receive() */
+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size)
+{
+ const union snd_ump_stream_msg *msg;
+ unsigned int status;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(*msg) != 16);
+ ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
+ buf[0], buf[1], buf[2], buf[3]);
+
+ if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
+ return;
+
+ msg = (const union snd_ump_stream_msg *)buf;
+ status = ump_stream_message_status(*buf);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_INFO:
+ ret = ump_handle_ep_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
+ ret = ump_handle_device_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_EP_NAME:
+ ret = ump_handle_ep_name_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
+ ret = ump_handle_product_id_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG:
+ ret = ump_handle_stream_cfg_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_INFO:
+ ret = ump_handle_fb_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_NAME:
+ ret = ump_handle_fb_name_msg(ump, msg);
+ break;
+ default:
+ return;
+ }
+
+ /* when the message has been processed fully, wake up */
+ if (ret > 0 && ump->stream_wait_for == status) {
+ WRITE_ONCE(ump->stream_finished, 1);
+ wake_up(&ump->stream_wait);
+ }
+}
+
+/**
+ * snd_ump_parse_endpoint - parse endpoint and create function blocks
+ * @ump: UMP object
+ *
+ * Returns 0 for successful parse, -ENODEV if device doesn't respond
+ * (or the query is unsupported), or other error code for serious errors.
+ */
+int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
+{
+ int blk, err;
+ u32 msg;
+
+ if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
+ return -ENODEV;
+
+ err = ump_request_open(ump);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to open rawmidi device: %d\n", err);
+ return err;
+ }
+
+ /* Check Endpoint Information */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
+ 0x0101; /* UMP version 1.1 */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
+ UMP_STREAM_MSG_STATUS_EP_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get UMP EP info\n");
+ goto error;
+ }
+
+ /* Request Endpoint Device Info */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
+ UMP_STREAM_MSG_STATUS_DEVICE_INFO);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP device info\n");
+
+ /* Request Endpoint Name */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
+ UMP_STREAM_MSG_STATUS_EP_NAME);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP name string\n");
+
+ /* Request Endpoint Product ID */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP product ID string\n");
+
+ /* Get the current stream configuration */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP stream config\n");
+
+ /* Query and create blocks from Function Blocks */
+ for (blk = 0; blk < ump->info.num_blocks; blk++) {
+ err = create_block_from_fb_info(ump, blk);
+ if (err < 0)
+ continue;
+ }
+
+ error:
+ ump->parsed = true;
+ ump_request_close(ump);
+ if (err == -ETIMEDOUT)
+ err = -ENODEV;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
+
#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
/*
* Legacy rawmidi support
--
2.35.3


2023-06-12 09:01:34

by Takashi Iwai

[permalink] [raw]
Subject: [PATCH 07/10] ALSA: seq: ump: Notify port changes to system port

For allowing applications to track the FB active changes, this patch
adds the notification from the system port at each time a FB change is
handled and the active flag or re-grouping happens.

Signed-off-by: Takashi Iwai <[email protected]>
---
sound/core/seq/seq_system.c | 1 +
sound/core/seq/seq_ump_client.c | 3 +++
2 files changed, 4 insertions(+)

diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index 32c2d9b57751..80267290190d 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -85,6 +85,7 @@ void snd_seq_system_broadcast(int client, int port, int type)
ev.type = type;
snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
}
+EXPORT_SYMBOL_GPL(snd_seq_system_broadcast);

/* entry points for broadcasting system events */
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)
diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c
index 2f93d76b05ce..901a670dcb36 100644
--- a/sound/core/seq/seq_ump_client.c
+++ b/sound/core/seq/seq_ump_client.c
@@ -13,6 +13,7 @@
#include <sound/seq_kernel.h>
#include <sound/seq_device.h>
#include "seq_clientmgr.h"
+#include "seq_system.h"

struct seq_ump_client;
struct seq_ump_group;
@@ -273,6 +274,8 @@ static void update_port_infos(struct seq_ump_client *client)
new);
if (err < 0)
goto error;
+ /* notify to system port */
+ snd_seq_system_client_ev_port_change(client->seq_client, i);
}
error:
kfree(new);
--
2.35.3