#include <sound/asound.h>
 
 /** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3)
 
 /**
  * definition of sequencer event types
 #define SNDRV_SEQ_PRIORITY_HIGH                (1<<4)  /* event should be processed before others */
 #define SNDRV_SEQ_PRIORITY_MASK                (1<<4)
 
+#define SNDRV_SEQ_EVENT_UMP            (1<<5)  /* event holds a UMP packet */
 
        /* note event */
 struct snd_seq_ev_note {
        struct snd_seq_event *event;            /* quoted event */
 } __attribute__((packed));
 
+union snd_seq_event_data { /* event data... */
+       struct snd_seq_ev_note note;
+       struct snd_seq_ev_ctrl control;
+       struct snd_seq_ev_raw8 raw8;
+       struct snd_seq_ev_raw32 raw32;
+       struct snd_seq_ev_ext ext;
+       struct snd_seq_ev_queue_control queue;
+       union snd_seq_timestamp time;
+       struct snd_seq_addr addr;
+       struct snd_seq_connect connect;
+       struct snd_seq_result result;
+       struct snd_seq_ev_quote quote;
+};
 
        /* sequencer event */
 struct snd_seq_event {
        unsigned char queue;            /* schedule queue */
        union snd_seq_timestamp time;   /* schedule time */
 
-
        struct snd_seq_addr source;     /* source address */
        struct snd_seq_addr dest;       /* destination address */
 
-       union {                         /* event data... */
-               struct snd_seq_ev_note note;
-               struct snd_seq_ev_ctrl control;
-               struct snd_seq_ev_raw8 raw8;
-               struct snd_seq_ev_raw32 raw32;
-               struct snd_seq_ev_ext ext;
-               struct snd_seq_ev_queue_control queue;
-               union snd_seq_timestamp time;
-               struct snd_seq_addr addr;
-               struct snd_seq_connect connect;
-               struct snd_seq_result result;
-               struct snd_seq_ev_quote quote;
-       } data;
+       union snd_seq_event_data data;
 };
 
+       /* (compatible) event for UMP-capable clients */
+struct snd_seq_ump_event {
+       snd_seq_event_type_t type;      /* event type */
+       unsigned char flags;            /* event flags */
+       char tag;
+       unsigned char queue;            /* schedule queue */
+       union snd_seq_timestamp time;   /* schedule time */
+       struct snd_seq_addr source;     /* source address */
+       struct snd_seq_addr dest;       /* destination address */
+
+       union {
+               union snd_seq_event_data data;
+               unsigned int ump[4];
+       };
+};
 
 /*
  * bounce event - stored as variable size data
        int event_lost;                 /* number of lost events */
        int card;                       /* RO: card number[kernel] */
        int pid;                        /* RO: pid[user] */
-       char reserved[56];              /* for future use */
+       unsigned int midi_version;      /* MIDI version */
+       char reserved[52];              /* for future use */
 };
 
+/* MIDI version numbers in client info */
+#define SNDRV_SEQ_CLIENT_LEGACY_MIDI           0       /* Legacy client */
+#define SNDRV_SEQ_CLIENT_UMP_MIDI_1_0          1       /* UMP MIDI 1.0 */
+#define SNDRV_SEQ_CLIENT_UMP_MIDI_2_0          2       /* UMP MIDI 2.0 */
 
 /* client pool size */
 struct snd_seq_client_pool {
 
        return 0;
 }
 
+static bool event_is_compatible(const struct snd_seq_client *client,
+                               const struct snd_seq_event *ev)
+{
+       if (snd_seq_ev_is_ump(ev) && !client->midi_version)
+               return false;
+       if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev))
+               return false;
+       return true;
+}
 
 /* handle client read() */
 /* possible error values:
 {
        struct snd_seq_client *client = file->private_data;
        struct snd_seq_fifo *fifo;
+       size_t aligned_size;
        int err;
        long result = 0;
        struct snd_seq_event_cell *cell;
        err = 0;
        snd_seq_fifo_lock(fifo);
 
+       if (client->midi_version > 0)
+               aligned_size = sizeof(struct snd_seq_ump_event);
+       else
+               aligned_size = sizeof(struct snd_seq_event);
+
        /* while data available in queue */
-       while (count >= sizeof(struct snd_seq_event)) {
+       while (count >= aligned_size) {
                int nonblock;
 
                nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
                err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
                if (err < 0)
                        break;
+               if (!event_is_compatible(client, &cell->event)) {
+                       snd_seq_cell_free(cell);
+                       cell = NULL;
+                       continue;
+               }
                if (snd_seq_ev_is_variable(&cell->event)) {
-                       struct snd_seq_event tmpev;
-                       tmpev = cell->event;
+                       struct snd_seq_ump_event tmpev;
+
+                       memcpy(&tmpev, &cell->event, aligned_size);
                        tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
-                       if (copy_to_user(buf, &tmpev, sizeof(struct snd_seq_event))) {
+                       if (copy_to_user(buf, &tmpev, aligned_size)) {
                                err = -EFAULT;
                                break;
                        }
-                       count -= sizeof(struct snd_seq_event);
-                       buf += sizeof(struct snd_seq_event);
+                       count -= aligned_size;
+                       buf += aligned_size;
                        err = snd_seq_expand_var_event(&cell->event, count,
                                                       (char __force *)buf, 0,
-                                                      sizeof(struct snd_seq_event));
+                                                      aligned_size);
                        if (err < 0)
                                break;
                        result += err;
                        count -= err;
                        buf += err;
                } else {
-                       if (copy_to_user(buf, &cell->event, sizeof(struct snd_seq_event))) {
+                       if (copy_to_user(buf, &cell->event, aligned_size)) {
                                err = -EFAULT;
                                break;
                        }
-                       count -= sizeof(struct snd_seq_event);
-                       buf += sizeof(struct snd_seq_event);
+                       count -= aligned_size;
+                       buf += aligned_size;
                }
                snd_seq_cell_free(cell);
                cell = NULL; /* to be sure */
-               result += sizeof(struct snd_seq_event);
+               result += aligned_size;
        }
 
        if (err < 0) {
 {
        struct snd_seq_subscribers *subs;
        int err, result = 0, num_ev = 0;
-       struct snd_seq_event event_saved;
        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 */
-       event_saved = *event;
+       saved_size = snd_seq_event_packet_size(event);
+       memcpy(&event_saved, event, saved_size);
        grp = &src_port->c_src;
        
        /* lock list */
                }
                num_ev++;
                /* restore original event record */
-               *event = event_saved;
+               memcpy(event, &event_saved, saved_size);
        }
        if (atomic)
                read_unlock(&grp->list_lock);
        else
                up_read(&grp->list_mutex);
-       *event = event_saved; /* restore */
-       snd_seq_port_unlock(src_port);
+       memcpy(event, &event_saved, saved_size);
        return (result < 0) ? result : num_ev;
 }
 
                return -EINVAL;
        }
 
-       if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+       if (!snd_seq_ev_is_ump(&cell->event) &&
+           cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
                /* NOTE event:
                 * the event cell is re-used as a NOTE-OFF event and
                 * enqueued again.
                /* add the duration time */
                switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
                case SNDRV_SEQ_TIME_STAMP_TICK:
-                       ev->time.tick += ev->data.note.duration;
+                       cell->event.time.tick += ev->data.note.duration;
                        break;
                case SNDRV_SEQ_TIME_STAMP_REAL:
                        /* unit for duration is ms */
 
        /* direct event processing without enqueued */
        if (snd_seq_ev_is_direct(event)) {
-               if (event->type == SNDRV_SEQ_EVENT_NOTE)
+               if (!snd_seq_ev_is_ump(event) &&
+                   event->type == SNDRV_SEQ_EVENT_NOTE)
                        return -EINVAL; /* this event must be enqueued! */
                return snd_seq_deliver_event(client, event, atomic, hop);
        }
        struct snd_seq_client *client = file->private_data;
        int written = 0, len;
        int err, handled;
-       struct snd_seq_event event;
+       union __snd_seq_event __event;
+       struct snd_seq_event *ev = &__event.legacy;
 
        if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
                return -ENXIO;
        err = -EINVAL;
        while (count >= sizeof(struct snd_seq_event)) {
                /* Read in the event header from the user */
-               len = sizeof(event);
-               if (copy_from_user(&event, buf, len)) {
+               len = sizeof(struct snd_seq_event);
+               if (copy_from_user(ev, buf, len)) {
                        err = -EFAULT;
                        break;
                }
-               event.source.client = client->number;   /* fill in client number */
+               /* read in the rest bytes for UMP events */
+               if (snd_seq_ev_is_ump(ev)) {
+                       if (count < sizeof(struct snd_seq_ump_event))
+                               break;
+                       if (copy_from_user((char *)ev + len, buf + len,
+                                          sizeof(struct snd_seq_ump_event) - len)) {
+                               err = -EFAULT;
+                               break;
+                       }
+                       len = sizeof(struct snd_seq_ump_event);
+               }
+
+               ev->source.client = client->number;     /* fill in client number */
                /* Check for extension data length */
-               if (check_event_type_and_length(&event)) {
+               if (check_event_type_and_length(ev)) {
                        err = -EINVAL;
                        break;
                }
 
-               /* check for special events */
-               if (event.type == SNDRV_SEQ_EVENT_NONE)
-                       goto __skip_event;
-               else if (snd_seq_ev_is_reserved(&event)) {
+               if (!event_is_compatible(client, ev)) {
                        err = -EINVAL;
                        break;
                }
 
-               if (snd_seq_ev_is_variable(&event)) {
-                       int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+               /* check for special events */
+               if (!snd_seq_ev_is_ump(ev)) {
+                       if (ev->type == SNDRV_SEQ_EVENT_NONE)
+                               goto __skip_event;
+                       else if (snd_seq_ev_is_reserved(ev)) {
+                               err = -EINVAL;
+                               break;
+                       }
+               }
+
+               if (snd_seq_ev_is_variable(ev)) {
+                       int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
                        if ((size_t)(extlen + len) > count) {
                                /* back out, will get an error this time or next */
                                err = -EINVAL;
                                break;
                        }
                        /* set user space pointer */
-                       event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
-                       event.data.ext.ptr = (char __force *)buf
-                                               + sizeof(struct snd_seq_event);
+                       ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+                       ev->data.ext.ptr = (char __force *)buf + len;
                        len += extlen; /* increment data length */
                } else {
 #ifdef CONFIG_COMPAT
-                       if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
-                               void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]);
-                               event.data.ext.ptr = ptr;
-                       }
+                       if (client->convert32 && snd_seq_ev_is_varusr(ev))
+                               ev->data.ext.ptr =
+                                       (void __force *)compat_ptr(ev->data.raw32.d[1]);
 #endif
                }
 
                /* ok, enqueue it */
-               err = snd_seq_client_enqueue_event(client, &event, file,
+               err = snd_seq_client_enqueue_event(client, ev, file,
                                                   !(file->f_flags & O_NONBLOCK),
                                                   0, 0, &client->ioctl_mutex);
                if (err < 0)
        else
                info->card = -1;
 
+       info->midi_version = cptr->midi_version;
        memset(info->reserved, 0, sizeof(info->reserved));
 }
 
        if (client->type != client_info->type)
                return -EINVAL;
 
+       /* check validity of midi_version field */
+       if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3) &&
+           client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
+               return -EINVAL;
+
        /* fill the info fields */
        if (client_info->name[0])
                strscpy(client->name, client_info->name, sizeof(client->name));
 
        client->filter = client_info->filter;
        client->event_lost = client_info->event_lost;
+       if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
+               client->midi_version = client_info->midi_version;
        memcpy(client->event_filter, client_info->event_filter, 32);
 
        return 0;
        if (snd_BUG_ON(!ev))
                return -EINVAL;
 
-       if (ev->type == SNDRV_SEQ_EVENT_NONE)
-               return 0; /* ignore this */
-       if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
-               return -EINVAL; /* quoted events can't be enqueued */
+       if (!snd_seq_ev_is_ump(ev)) {
+               if (ev->type == SNDRV_SEQ_EVENT_NONE)
+                       return 0; /* ignore this */
+               if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+                       return -EINVAL; /* quoted events can't be enqueued */
+       }
 
        /* fill in client number */
        ev->source.client = client;
        mutex_unlock(&client->ports_mutex);
 }
 
+static const char *midi_version_string(unsigned int version)
+{
+       switch (version) {
+       case SNDRV_SEQ_CLIENT_LEGACY_MIDI:
+               return "Legacy";
+       case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0:
+               return "UMP MIDI1";
+       case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0:
+               return "UMP MIDI2";
+       default:
+               return "Unknown";
+       }
+}
 
 /* exported to seq_info.c */
 void snd_seq_info_clients_read(struct snd_info_entry *entry, 
                        continue;
                }
 
-               snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+               snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n",
                            c, client->name,
-                           client->type == USER_CLIENT ? "User" : "Kernel");
+                           client->type == USER_CLIENT ? "User" : "Kernel",
+                           midi_version_string(client->midi_version));
                snd_seq_info_dump_ports(buffer, client);
                if (snd_seq_write_pool_allocated(client)) {
                        snd_iprintf(buffer, "  Output pool :\n");