ALSA: seq: Support MIDI 2.0 UMP Endpoint port
authorTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 07:53:48 +0000 (09:53 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 23 May 2023 10:11:24 +0000 (12:11 +0200)
This is an extension to ALSA sequencer infrastructure to support the
MIDI 2.0 UMP Endpoint port.  It's a "catch-all" port that is supposed
to be present for each UMP Endpoint.  When this port is read via
subscription, it sends any events from all ports (UMP Groups) found in
the same client.

A UMP Endpoint port can be created with the new capability bit
SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT.  Although the port assignment isn't
strictly defined, it should be the port number 0.

Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-28-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/uapi/sound/asequencer.h
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_clientmgr.h

index c6ca6609790b93652d1051665a300ede69402af0..67532c46b115b86919fe00d391d0f6721fe6849f 100644 (file)
@@ -428,6 +428,7 @@ struct snd_seq_remove_events {
 #define SNDRV_SEQ_PORT_CAP_SUBS_WRITE  (1<<6)  /* allow write subscription */
 #define SNDRV_SEQ_PORT_CAP_NO_EXPORT   (1<<7)  /* routing not allowed */
 #define SNDRV_SEQ_PORT_CAP_INACTIVE    (1<<8)  /* inactive port */
+#define SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT        (1<<9)  /* MIDI 2.0 UMP Endpoint port */
 
        /* port type */
 #define SNDRV_SEQ_PORT_TYPE_SPECIFIC   (1<<0)  /* hardware specific */
index 6508ce63f76195c3707464d32a2f35aeaa09fe82..061b3e2bece1464cfee7834ba9159cfc82460948 100644 (file)
@@ -239,6 +239,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
        mutex_init(&client->ports_mutex);
        INIT_LIST_HEAD(&client->ports_list_head);
        mutex_init(&client->ioctl_mutex);
+       client->ump_endpoint_port = -1;
 
        /* find free slot in the client table */
        spin_lock_irq(&clients_lock);
@@ -680,20 +681,17 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
 /*
  * send the event to all subscribers:
  */
-static int deliver_to_subscribers(struct snd_seq_client *client,
-                                 struct snd_seq_event *event,
-                                 int atomic, int hop)
+static int __deliver_to_subscribers(struct snd_seq_client *client,
+                                   struct snd_seq_event *event,
+                                   struct snd_seq_client_port *src_port,
+                                   int atomic, int hop)
 {
        struct snd_seq_subscribers *subs;
        int err, result = 0, num_ev = 0;
-       struct snd_seq_client_port *src_port;
        union __snd_seq_event event_saved;
        size_t saved_size;
        struct snd_seq_port_subs_info *grp;
 
-       src_port = snd_seq_port_use_ptr(client, event->source.port);
-       if (src_port == NULL)
-               return -EINVAL; /* invalid source port */
        /* save original event record */
        saved_size = snd_seq_event_packet_size(event);
        memcpy(&event_saved, event, saved_size);
@@ -733,6 +731,31 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
        return (result < 0) ? result : num_ev;
 }
 
+static int deliver_to_subscribers(struct snd_seq_client *client,
+                                 struct snd_seq_event *event,
+                                 int atomic, int hop)
+{
+       struct snd_seq_client_port *src_port;
+       int ret = 0, ret2;
+
+       src_port = snd_seq_port_use_ptr(client, event->source.port);
+       if (src_port) {
+               ret = __deliver_to_subscribers(client, event, src_port, atomic, hop);
+               snd_seq_port_unlock(src_port);
+       }
+
+       if (client->ump_endpoint_port < 0 ||
+           event->source.port == client->ump_endpoint_port)
+               return ret;
+
+       src_port = snd_seq_port_use_ptr(client, client->ump_endpoint_port);
+       if (!src_port)
+               return ret;
+       ret2 = __deliver_to_subscribers(client, event, src_port, atomic, hop);
+       snd_seq_port_unlock(src_port);
+       return ret2 < 0 ? ret2 : ret;
+}
+
 /* deliver an event to the destination port(s).
  * if the event is to subscribers or broadcast, the event is dispatched
  * to multiple targets.
@@ -1257,6 +1280,9 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
                return -EPERM;
        if (client->type == USER_CLIENT && info->kernel)
                return -EINVAL;
+       if ((info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) &&
+           client->ump_endpoint_port >= 0)
+               return -EBUSY;
 
        if (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT)
                port_idx = info->addr.port;
@@ -1286,6 +1312,8 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
        info->addr = port->addr;
 
        snd_seq_set_port_info(port, info);
+       if (info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT)
+               client->ump_endpoint_port = port->addr.port;
        snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
        snd_seq_port_unlock(port);
 
@@ -1305,8 +1333,11 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
                return -EPERM;
 
        err = snd_seq_delete_port(client, info->addr.port);
-       if (err >= 0)
+       if (err >= 0) {
+               if (client->ump_endpoint_port == info->addr.port)
+                       client->ump_endpoint_port = -1;
                snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
+       }
        return err;
 }
 
index 5657f80918352f08c62e040f7cc4f1a483956491..bb973d36ce78ad6e89410672bd4189174df96758 100644 (file)
@@ -50,6 +50,7 @@ struct snd_seq_client {
        struct mutex ports_mutex;
        struct mutex ioctl_mutex;
        int convert32;          /* convert 32->64bit */
+       int ump_endpoint_port;
 
        /* output pool */
        struct snd_seq_pool *pool;              /* memory pool for this client */