ALSA: seq: Support MIDI 2.0 UMP Endpoint port
[linux-2.6-microblaze.git] / sound / core / seq / seq_clientmgr.c
index 6508ce6..061b3e2 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;
 }