diff mbox series

[alsa-lib,2/4] seq: Add API helper functions for creating UMP Endpoint and Blocks

Message ID 20240619152855.6809-3-tiwai@suse.de (mailing list archive)
State New, archived
Headers show
Series Add API helper functions for creating UMP Endpoint and Blocks | expand

Commit Message

Takashi Iwai June 19, 2024, 3:28 p.m. UTC
For making it easer for applications to create a virtual UMP Endpoint
and UMP blocks, add two API helper functions.

snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint,
based on the given snd_ump_endpoint_info_t information.  The number of
(max) UMP groups belonging to this Endpoint has to be specified.
This function sets up the Endpoint info on the sequencer client, and
creates a MIDI 2.0 UMP port as well as UMP Group ports automatically.
The name of the sequencer client is updated from the Endpoint name,
too.

After creating a UMP Endpoint, create each UMP Block via
snd_seq_create_ump_block() function with a snd_ump_block_info_t info.
The associated groups for each block have to be specified there.
The port names and capability bits are updated accordingly after
setting each block information.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/seqmid.h    |   7 ++
 include/ump.h       |   6 ++
 src/seq/seq.c       |   6 +-
 src/seq/seq_local.h |   4 +
 src/seq/seqmid.c    | 249 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 271 insertions(+), 1 deletion(-)

Comments

Amadeusz Sławiński July 31, 2024, 8:46 a.m. UTC | #1
On 6/19/2024 5:28 PM, Takashi Iwai wrote:
> For making it easer for applications to create a virtual UMP Endpoint
> and UMP blocks, add two API helper functions.
> 
> snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint,
> based on the given snd_ump_endpoint_info_t information.  The number of
> (max) UMP groups belonging to this Endpoint has to be specified.
> This function sets up the Endpoint info on the sequencer client, and
> creates a MIDI 2.0 UMP port as well as UMP Group ports automatically.
> The name of the sequencer client is updated from the Endpoint name,
> too.
> 
> After creating a UMP Endpoint, create each UMP Block via
> snd_seq_create_ump_block() function with a snd_ump_block_info_t info.
> The associated groups for each block have to be specified there.
> The port names and capability bits are updated accordingly after
> setting each block information.
> 
> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> ---

...

> +			if (*blknames) {
> +				strlcat(blknames, ", ", sizeof(blknames));
> +				strlcat(blknames, (const char *)bp->name,
> +					sizeof(blknames));

FYI, this seems to introduce build problems on systems that do not have 
strlcpy:

During build:
seqmid.c: In function ‘update_group_ports’:
seqmid.c:672:33: warning: implicit declaration of function ‘strlcat’; 
did you mean ‘strncat’? [-Wimplicit-function-declaration]
   672 |                                 strlcat(blknames, ", ", 
sizeof(blknames));
       |                                 ^~~~~~~
       |                                 strncat

And then during linking:
/usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports':
/home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined 
reference to `strlcat'
/usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673: 
undefined reference to `strlcat'
collect2: error: ld returned 1 exit status
Takashi Iwai July 31, 2024, 9:21 a.m. UTC | #2
On Wed, 31 Jul 2024 10:46:08 +0200,
Amadeusz Sławiński wrote:
> 
> On 6/19/2024 5:28 PM, Takashi Iwai wrote:
> > For making it easer for applications to create a virtual UMP Endpoint
> > and UMP blocks, add two API helper functions.
> > 
> > snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint,
> > based on the given snd_ump_endpoint_info_t information.  The number of
> > (max) UMP groups belonging to this Endpoint has to be specified.
> > This function sets up the Endpoint info on the sequencer client, and
> > creates a MIDI 2.0 UMP port as well as UMP Group ports automatically.
> > The name of the sequencer client is updated from the Endpoint name,
> > too.
> > 
> > After creating a UMP Endpoint, create each UMP Block via
> > snd_seq_create_ump_block() function with a snd_ump_block_info_t info.
> > The associated groups for each block have to be specified there.
> > The port names and capability bits are updated accordingly after
> > setting each block information.
> > 
> > Signed-off-by: Takashi Iwai <tiwai@suse.de>
> > ---
> 
> ...
> 
> > +			if (*blknames) {
> > +				strlcat(blknames, ", ", sizeof(blknames));
> > +				strlcat(blknames, (const char *)bp->name,
> > +					sizeof(blknames));
> 
> FYI, this seems to introduce build problems on systems that do not
> have strlcpy:
> 
> During build:
> seqmid.c: In function ‘update_group_ports’:
> seqmid.c:672:33: warning: implicit declaration of function
> ‘strlcat’; did you mean ‘strncat’?
> [-Wimplicit-function-declaration]
>   672 |                                 strlcat(blknames, ", ",
> sizeof(blknames));
>       |                                 ^~~~~~~
>       |                                 strncat
> 
> And then during linking:
> /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports':
> /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined
> reference to `strlcat'
> /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673:
> undefined reference to `strlcat'
> collect2: error: ld returned 1 exit status

Thanks, I'll modify it to avoid strlcat() like below.


Takashi

-- 8< --
Subject: [PATCH] seq: Avoid strlcat()

strlcat() isn't available in every system, so better to avoid it.
Rewrite the code without strlcat().

Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks")
Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 src/seq/seqmid.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
index 08c62d5c24b8..b30db4075254 100644
--- a/src/seq/seqmid.c
+++ b/src/seq/seqmid.c
@@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
 		char blknames[64];
 		char name[64];
 		unsigned int caps = 0;
+		int len;
 
 		blknames[0] = 0;
 		for (b = 0; b < ep->num_blocks; b++) {
@@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
 
 			if (!*bp->name)
 				continue;
-			if (*blknames) {
-				strlcat(blknames, ", ", sizeof(blknames));
-				strlcat(blknames, (const char *)bp->name,
-					sizeof(blknames));
-			} else {
+			len = strlen(blknames);
+			if (len)
+				snprintf(blknames + len, sizeof(blknames) - len,
+					 ", %s", bp->name);
+			else
 				snd_strlcpy(blknames, (const char *)bp->name,
 					    sizeof(blknames));
-			}
 		}
 
 		if (!*blknames)
Amadeusz Sławiński July 31, 2024, 9:29 a.m. UTC | #3
On 7/31/2024 11:21 AM, Takashi Iwai wrote:
> On Wed, 31 Jul 2024 10:46:08 +0200,
> Amadeusz Sławiński wrote:
>>
>> On 6/19/2024 5:28 PM, Takashi Iwai wrote:
>>> For making it easer for applications to create a virtual UMP Endpoint
>>> and UMP blocks, add two API helper functions.
>>>
>>> snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint,
>>> based on the given snd_ump_endpoint_info_t information.  The number of
>>> (max) UMP groups belonging to this Endpoint has to be specified.
>>> This function sets up the Endpoint info on the sequencer client, and
>>> creates a MIDI 2.0 UMP port as well as UMP Group ports automatically.
>>> The name of the sequencer client is updated from the Endpoint name,
>>> too.
>>>
>>> After creating a UMP Endpoint, create each UMP Block via
>>> snd_seq_create_ump_block() function with a snd_ump_block_info_t info.
>>> The associated groups for each block have to be specified there.
>>> The port names and capability bits are updated accordingly after
>>> setting each block information.
>>>
>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>> ---
>>
>> ...
>>
>>> +			if (*blknames) {
>>> +				strlcat(blknames, ", ", sizeof(blknames));
>>> +				strlcat(blknames, (const char *)bp->name,
>>> +					sizeof(blknames));
>>
>> FYI, this seems to introduce build problems on systems that do not
>> have strlcpy:
>>
>> During build:
>> seqmid.c: In function ‘update_group_ports’:
>> seqmid.c:672:33: warning: implicit declaration of function
>> ‘strlcat’; did you mean ‘strncat’?
>> [-Wimplicit-function-declaration]
>>    672 |                                 strlcat(blknames, ", ",
>> sizeof(blknames));
>>        |                                 ^~~~~~~
>>        |                                 strncat
>>
>> And then during linking:
>> /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports':
>> /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined
>> reference to `strlcat'
>> /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673:
>> undefined reference to `strlcat'
>> collect2: error: ld returned 1 exit status
> 
> Thanks, I'll modify it to avoid strlcat() like below.
> 
> 
> Takashi
> 
> -- 8< --
> Subject: [PATCH] seq: Avoid strlcat()
> 
> strlcat() isn't available in every system, so better to avoid it.
> Rewrite the code without strlcat().
> 
> Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks")
> Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com
> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> ---
>   src/seq/seqmid.c | 12 ++++++------
>   1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
> index 08c62d5c24b8..b30db4075254 100644
> --- a/src/seq/seqmid.c
> +++ b/src/seq/seqmid.c
> @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
>   		char blknames[64];
>   		char name[64];
>   		unsigned int caps = 0;
> +		int len;
>   
>   		blknames[0] = 0;
>   		for (b = 0; b < ep->num_blocks; b++) {
> @@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
>   
>   			if (!*bp->name)
>   				continue;
> -			if (*blknames) {
> -				strlcat(blknames, ", ", sizeof(blknames));
> -				strlcat(blknames, (const char *)bp->name,
> -					sizeof(blknames));
> -			} else {
> +			len = strlen(blknames);
> +			if (len)
> +				snprintf(blknames + len, sizeof(blknames) - len,
> +					 ", %s", bp->name);
> +			else
>   				snd_strlcpy(blknames, (const char *)bp->name,
>   					    sizeof(blknames));
> -			}
>   		}
>   
>   		if (!*blknames)

Builds now, but still gives warning:

seqmid.c: In function ‘update_group_ports’:
seqmid.c:675:45: warning: ‘%s’ directive output may be truncated writing 
up to 127 bytes into a region of size 61 [-Wformat-truncation=]
   675 |                                          ", %s", bp->name);
       |                                             ^~
In file included from /usr/include/stdio.h:894,
                  from ../../include/local.h:28,
                  from seq_local.h:26,
                  from seqmid.c:23:
In function ‘snprintf’,
     inlined from ‘update_group_ports’ at seqmid.c:674:5:
/usr/include/x86_64-linux-gnu/bits/stdio2.h:71:10: note: 
‘__builtin___snprintf_chk’ output between 3 and 130 bytes into a 
destination of size 63
    71 |   return __builtin___snprintf_chk (__s, __n, 
__USE_FORTIFY_LEVEL - 1,
       | 
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    72 |                                    __glibc_objsize (__s), __fmt,
       |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    73 |                                    __va_arg_pack ());
       |                                    ~~~~~~~~~~~~~~~~~
Takashi Iwai July 31, 2024, 9:34 a.m. UTC | #4
On Wed, 31 Jul 2024 11:29:41 +0200,
Amadeusz Sławiński wrote:
> 
> On 7/31/2024 11:21 AM, Takashi Iwai wrote:
> > On Wed, 31 Jul 2024 10:46:08 +0200,
> > Amadeusz Sławiński wrote:
> >> 
> >> On 6/19/2024 5:28 PM, Takashi Iwai wrote:
> >>> For making it easer for applications to create a virtual UMP Endpoint
> >>> and UMP blocks, add two API helper functions.
> >>> 
> >>> snd_seq_create_ump_endpoint() creates (unsurprisingly) a UMP Endpoint,
> >>> based on the given snd_ump_endpoint_info_t information.  The number of
> >>> (max) UMP groups belonging to this Endpoint has to be specified.
> >>> This function sets up the Endpoint info on the sequencer client, and
> >>> creates a MIDI 2.0 UMP port as well as UMP Group ports automatically.
> >>> The name of the sequencer client is updated from the Endpoint name,
> >>> too.
> >>> 
> >>> After creating a UMP Endpoint, create each UMP Block via
> >>> snd_seq_create_ump_block() function with a snd_ump_block_info_t info.
> >>> The associated groups for each block have to be specified there.
> >>> The port names and capability bits are updated accordingly after
> >>> setting each block information.
> >>> 
> >>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> >>> ---
> >> 
> >> ...
> >> 
> >>> +			if (*blknames) {
> >>> +				strlcat(blknames, ", ", sizeof(blknames));
> >>> +				strlcat(blknames, (const char *)bp->name,
> >>> +					sizeof(blknames));
> >> 
> >> FYI, this seems to introduce build problems on systems that do not
> >> have strlcpy:
> >> 
> >> During build:
> >> seqmid.c: In function ‘update_group_ports’:
> >> seqmid.c:672:33: warning: implicit declaration of function
> >> ‘strlcat’; did you mean ‘strncat’?
> >> [-Wimplicit-function-declaration]
> >>    672 |                                 strlcat(blknames, ", ",
> >> sizeof(blknames));
> >>        |                                 ^~~~~~~
> >>        |                                 strncat
> >> 
> >> And then during linking:
> >> /usr/bin/ld: seq/.libs/libseq.a(seqmid.o): in function `update_group_ports':
> >> /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:672: undefined
> >> reference to `strlcat'
> >> /usr/bin/ld: /home/amade/workdir/avs/alsa-lib/src/seq/seqmid.c:673:
> >> undefined reference to `strlcat'
> >> collect2: error: ld returned 1 exit status
> > 
> > Thanks, I'll modify it to avoid strlcat() like below.
> > 
> > 
> > Takashi
> > 
> > -- 8< --
> > Subject: [PATCH] seq: Avoid strlcat()
> > 
> > strlcat() isn't available in every system, so better to avoid it.
> > Rewrite the code without strlcat().
> > 
> > Fixes: 6167b8ce3e80 ("seq: Add API helper functions for creating UMP Endpoint and Blocks")
> > Link: https://lore.kernel.org/0796c157-1ac3-47a3-9d54-ba86f59d64d5@linux.intel.com
> > Signed-off-by: Takashi Iwai <tiwai@suse.de>
> > ---
> >   src/seq/seqmid.c | 12 ++++++------
> >   1 file changed, 6 insertions(+), 6 deletions(-)
> > 
> > diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
> > index 08c62d5c24b8..b30db4075254 100644
> > --- a/src/seq/seqmid.c
> > +++ b/src/seq/seqmid.c
> > @@ -635,6 +635,7 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
> >   		char blknames[64];
> >   		char name[64];
> >   		unsigned int caps = 0;
> > +		int len;
> >     		blknames[0] = 0;
> >   		for (b = 0; b < ep->num_blocks; b++) {
> > @@ -668,14 +669,13 @@ static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
> >     			if (!*bp->name)
> >   				continue;
> > -			if (*blknames) {
> > -				strlcat(blknames, ", ", sizeof(blknames));
> > -				strlcat(blknames, (const char *)bp->name,
> > -					sizeof(blknames));
> > -			} else {
> > +			len = strlen(blknames);
> > +			if (len)
> > +				snprintf(blknames + len, sizeof(blknames) - len,
> > +					 ", %s", bp->name);
> > +			else
> >   				snd_strlcpy(blknames, (const char *)bp->name,
> >   					    sizeof(blknames));
> > -			}
> >   		}
> >     		if (!*blknames)
> 
> Builds now, but still gives warning:
> 
> seqmid.c: In function ‘update_group_ports’:
> seqmid.c:675:45: warning: ‘%s’ directive output may be truncated
> writing up to 127 bytes into a region of size 61
> [-Wformat-truncation=]
>   675 |                                          ", %s", bp->name);
>       |                                             ^~
> In file included from /usr/include/stdio.h:894,
>                  from ../../include/local.h:28,
>                  from seq_local.h:26,
>                  from seqmid.c:23:
> In function ‘snprintf’,
>     inlined from ‘update_group_ports’ at seqmid.c:674:5:
> /usr/include/x86_64-linux-gnu/bits/stdio2.h:71:10: note:
> ‘__builtin___snprintf_chk’ output between 3 and 130 bytes into a
> destination of size 63
>    71 |   return __builtin___snprintf_chk (__s, __n,
> __USE_FORTIFY_LEVEL - 1,
>       | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    72 |                                    __glibc_objsize (__s), __fmt,
>       |                                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    73 |                                    __va_arg_pack ());
>       |                                    ~~~~~~~~~~~~~~~~~

The compiler gives too much false positives.


Takashi
diff mbox series

Patch

diff --git a/include/seqmid.h b/include/seqmid.h
index 4464c2d3af0b..bf968a5b2c7b 100644
--- a/include/seqmid.h
+++ b/include/seqmid.h
@@ -520,6 +520,13 @@  int snd_seq_reset_pool_input(snd_seq_t *seq);
 	((ev)->type = SND_SEQ_EVENT_SYSEX,\
 	 snd_seq_ev_set_variable(ev, datalen, dataptr))
 
+/* Helper API functions for UMP endpoint and block creations */
+int snd_seq_create_ump_endpoint(snd_seq_t *seq,
+				const snd_ump_endpoint_info_t *info,
+				unsigned int num_groups);
+int snd_seq_create_ump_block(snd_seq_t *seq, int blkid,
+			     const snd_ump_block_info_t *info);
+
 /** \} */
 
 #ifdef __cplusplus
diff --git a/include/ump.h b/include/ump.h
index 45b3ad270db7..01363a329fa7 100644
--- a/include/ump.h
+++ b/include/ump.h
@@ -69,6 +69,9 @@  enum _snd_ump_direction {
 /** Bit flag for JRTS in Receive */
 #define SND_UMP_EP_INFO_PROTO_JRTS_RX		0x0002
 
+/** Default version passed to UMP Endpoint info */
+#define SND_UMP_EP_INFO_DEFAULT_VERSION		0x0101
+
 size_t snd_ump_endpoint_info_sizeof(void);
 /** \hideinitializer
  * \brief allocate an invalid #snd_ump_endpoint_info_t using standard alloca
@@ -125,6 +128,9 @@  enum _snd_ump_block_ui_hint {
 	SND_UMP_BLOCK_UI_HINT_BOTH =		0x03,
 };
 
+/** Default MIDI CI version passed to UMP Block info */
+#define SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION	0x01
+
 size_t snd_ump_block_info_sizeof(void);
 /** \hideinitializer
  * \brief allocate an invalid #snd_ump_block_info_t using standard alloca
diff --git a/src/seq/seq.c b/src/seq/seq.c
index 5eac4848b9c7..ff0468140177 100644
--- a/src/seq/seq.c
+++ b/src/seq/seq.c
@@ -1042,7 +1042,8 @@  int _snd_seq_open_lconf(snd_seq_t **seqp, const char *name,
  */
 int snd_seq_close(snd_seq_t *seq)
 {
-	int err;
+	int i, err;
+
 	assert(seq);
 	err = seq->ops->close(seq);
 	if (seq->dl_handle)
@@ -1051,6 +1052,9 @@  int snd_seq_close(snd_seq_t *seq)
 	free(seq->ibuf);
 	free(seq->tmpbuf);
 	free(seq->name);
+	free(seq->ump_ep);
+	for (i = 0; i < 16; i++)
+		free(seq->ump_blks[i]);
 	free(seq);
 	return err;
 }
diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h
index 468248062638..263029702739 100644
--- a/src/seq/seq_local.h
+++ b/src/seq/seq_local.h
@@ -94,6 +94,10 @@  struct _snd_seq {
 	size_t tmpbufsize;		/* size of errbuf */
 	size_t packet_size;		/* input packet alignment size */
 	int midi_version;	/* current protocol version */
+
+	unsigned int num_ump_groups;		/* number of UMP groups */
+	snd_ump_endpoint_info_t *ump_ep;	/* optional UMP info */
+	snd_ump_block_info_t *ump_blks[16];	/* optional UMP block info */
 };
 
 int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode);
diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c
index 9ec93ee8ade1..d7eac6cafa0e 100644
--- a/src/seq/seqmid.c
+++ b/src/seq/seqmid.c
@@ -493,3 +493,252 @@  int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg)
 	return 0;
 }
 
+/**
+ * \brief create a UMP Endpoint for the given sequencer client
+ * \param seq sequencer handle
+ * \param info UMP Endpoint information to initialize
+ * \param num_groups max number of groups in the endpoint
+ * \return 0 on success or negative error code
+ *
+ * This function initializes the sequencer client to the corresponding
+ * MIDI 2.0 mode (either MIDI 1.0 or MIDI 2.0 protocol) depending on the
+ * given snd_ump_endpoint_info_t info.
+ *
+ * This function should be called right after opening a sequencer client.
+ * The client name is updated from the UMP Endpoint name, and a primary
+ * MIDI 2.0 UMP port and each UMP Group port are created.
+ * The application should pass each UMP block info via succeeding
+ * snd_seq_create_ump_block() call.
+ */
+int snd_seq_create_ump_endpoint(snd_seq_t *seq,
+				const snd_ump_endpoint_info_t *info,
+				unsigned int num_groups)
+{
+	int err, version;
+	unsigned int i;
+	snd_seq_port_info_t *pinfo;
+
+	if (seq->ump_ep)
+		return -EBUSY;
+
+	if (num_groups < 1 || num_groups > SND_UMP_MAX_GROUPS)
+		return -EINVAL;
+
+	if (!(info->protocol_caps & info->protocol)) {
+		SNDERR("Inconsistent UMP protocol_caps and protocol\n");
+		return -EINVAL;
+	}
+
+	if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI2) {
+		version = SND_SEQ_CLIENT_UMP_MIDI_2_0;
+	} else if (info->protocol & SND_UMP_EP_INFO_PROTO_MIDI1) {
+		version = SND_SEQ_CLIENT_UMP_MIDI_1_0;
+	} else {
+		SNDERR("Invalid UMP protocol set 0x%x\n", info->protocol);
+		return -EINVAL;
+	}
+
+	err = snd_seq_set_client_midi_version(seq, version);
+	if (err < 0) {
+		SNDERR("Failed to set to MIDI protocol 0x%x\n", version);
+		return err;
+	}
+
+	seq->ump_ep = malloc(sizeof(*info));
+	if (!seq->ump_ep)
+		return -ENOMEM;
+
+	*seq->ump_ep = *info;
+	if (!seq->ump_ep->version)
+		seq->ump_ep->version = SND_UMP_EP_INFO_DEFAULT_VERSION;
+
+	if (info->name) {
+		err = snd_seq_set_client_name(seq, (const char *)info->name);
+		if (err < 0)
+			goto error_free;
+	}
+
+	err = snd_seq_set_ump_endpoint_info(seq, seq->ump_ep);
+	if (err < 0) {
+		SNDERR("Failed to set UMP EP info\n");
+		goto error_free;
+	}
+
+	snd_seq_port_info_alloca(&pinfo);
+
+	snd_seq_port_info_set_port(pinfo, 0);
+	snd_seq_port_info_set_port_specified(pinfo, 1);
+	snd_seq_port_info_set_name(pinfo, "MIDI 2.0");
+	snd_seq_port_info_set_capability(pinfo,
+					 SNDRV_SEQ_PORT_CAP_READ |
+					 SNDRV_SEQ_PORT_CAP_SYNC_READ |
+					 SNDRV_SEQ_PORT_CAP_SUBS_READ |
+					 SNDRV_SEQ_PORT_CAP_WRITE |
+					 SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+					 SNDRV_SEQ_PORT_CAP_SUBS_WRITE |
+					 SNDRV_SEQ_PORT_CAP_DUPLEX);
+	snd_seq_port_info_set_type(pinfo,
+				   SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+				   SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+				   SND_SEQ_PORT_TYPE_APPLICATION |
+				   SNDRV_SEQ_PORT_TYPE_PORT);
+	snd_seq_port_info_set_ump_group(pinfo,
+					SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+				   SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+				   SND_SEQ_PORT_TYPE_APPLICATION |
+				   SNDRV_SEQ_PORT_TYPE_PORT);
+	err = snd_seq_create_port(seq, pinfo);
+	if (err < 0) {
+		SNDERR("Failed to create MIDI 2.0 port\n");
+		goto error_free;
+	}
+
+	for (i = 0; i < num_groups; i++) {
+		char name[32];
+
+		snd_seq_port_info_set_port(pinfo, i + 1);
+		snd_seq_port_info_set_port_specified(pinfo, 1);
+		sprintf(name, "Group %d", i + 1);
+		snd_seq_port_info_set_capability(pinfo, 0); /* set later */
+		snd_seq_port_info_set_name(pinfo, name);
+		snd_seq_port_info_set_ump_group(pinfo, i + 1);
+		err = snd_seq_create_port(seq, pinfo);
+		if (err < 0) {
+			SNDERR("Failed to create Group port %d\n", i + 1);
+			goto error;
+		}
+	}
+
+	seq->num_ump_groups = num_groups;
+	return 0;
+
+ error:
+	/* delete all ports including port 0 */
+	for (i = 0; i <= num_groups; i++)
+		snd_seq_delete_port(seq, i);
+ error_free:
+	free(seq->ump_ep);
+	seq->ump_ep = NULL;
+	return err;
+}
+
+/* update each port name and capability from the block list */
+static void update_group_ports(snd_seq_t *seq, snd_ump_endpoint_info_t *ep)
+{
+	unsigned int i, b;
+	snd_seq_port_info_t *pinfo;
+	snd_ump_block_info_t *bp;
+
+	snd_seq_port_info_alloca(&pinfo);
+
+	for (i = 0; i < seq->num_ump_groups; i++) {
+		char blknames[64];
+		char name[64];
+		unsigned int caps = 0;
+
+		blknames[0] = 0;
+		for (b = 0; b < ep->num_blocks; b++) {
+			bp = seq->ump_blks[b];
+			if (!bp)
+				continue;
+			if (i < bp->first_group ||
+			    i >= bp->first_group + bp->num_groups)
+				continue;
+			switch (bp->direction) {
+			case SNDRV_UMP_DIR_INPUT:
+				caps |= SNDRV_SEQ_PORT_CAP_READ |
+					SNDRV_SEQ_PORT_CAP_SYNC_READ |
+					SNDRV_SEQ_PORT_CAP_SUBS_READ;
+				break;
+			case SNDRV_UMP_DIR_OUTPUT:
+				caps |= SNDRV_SEQ_PORT_CAP_WRITE |
+					SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+					SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+				break;
+			case SNDRV_UMP_DIR_BIDIRECTION:
+				caps |= SNDRV_SEQ_PORT_CAP_READ |
+					SNDRV_SEQ_PORT_CAP_SYNC_READ |
+					SNDRV_SEQ_PORT_CAP_SUBS_READ |
+					SNDRV_SEQ_PORT_CAP_WRITE |
+					SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+					SNDRV_SEQ_PORT_CAP_SUBS_WRITE |
+					SNDRV_SEQ_PORT_CAP_DUPLEX;
+				break;
+			}
+
+			if (!*bp->name)
+				continue;
+			if (*blknames) {
+				strlcat(blknames, ", ", sizeof(blknames));
+				strlcat(blknames, (const char *)bp->name,
+					sizeof(blknames));
+			} else {
+				snd_strlcpy(blknames, (const char *)bp->name,
+					    sizeof(blknames));
+			}
+		}
+
+		if (!*blknames)
+			continue;
+
+		snprintf(name, sizeof(name), "Group %d (%s)", i + 1, blknames);
+		if (snd_seq_get_port_info(seq, i + 1, pinfo) < 0)
+			continue;
+
+		if (strcmp(name, snd_seq_port_info_get_name(pinfo)) ||
+		    snd_seq_port_info_get_capability(pinfo) != caps) {
+			snd_seq_port_info_set_name(pinfo, name);
+			snd_seq_port_info_set_capability(pinfo, caps);
+			snd_seq_set_port_info(seq, i + 1, pinfo);
+		}
+	}
+}
+
+/**
+ * \brief create a UMP block for the given sequencer client
+ * \param seq sequencer handle
+ * \param blkid 0-based block id
+ * \param info UMP block info to initialize
+ * \return 0 on success or negative error code
+ *
+ * This function sets up the UMP block info of the given block id.
+ * The sequencer port name is updated accordingly with the associated
+ * block name automatically.
+ */
+int snd_seq_create_ump_block(snd_seq_t *seq, int blkid,
+			     const snd_ump_block_info_t *info)
+{
+	snd_ump_block_info_t *bp;
+	snd_ump_endpoint_info_t *ep = seq->ump_ep;
+	int err;
+
+	if (!ep)
+		return -EINVAL;
+	if (info->first_group >= seq->num_ump_groups ||
+	    info->first_group + info->num_groups > seq->num_ump_groups)
+		return -EINVAL;
+	if (blkid < 0 || blkid >= (int)ep->num_blocks)
+		return -EINVAL;
+
+	if (seq->ump_blks[blkid])
+		return -EBUSY;
+	seq->ump_blks[blkid] = bp = malloc(sizeof(*info));
+	if (!bp)
+		return -ENOMEM;
+	*bp = *info;
+
+	if (!bp->midi_ci_version)
+		bp->midi_ci_version = SND_UMP_BLOCK_INFO_DEFAULT_MIDI_CI_VERSION;
+	bp->active = 1;
+
+	err = snd_seq_set_ump_block_info(seq, blkid, bp);
+	if (err < 0) {
+		SNDERR("Failed to set UMP EP info\n");
+		free(bp);
+		seq->ump_blks[blkid] = NULL;
+		return err;
+	}
+
+	update_group_ports(seq, ep);
+	return 0;
+}