struct snd_ump_endpoint;
 struct snd_ump_block;
+struct snd_ump_ops;
 
 struct snd_ump_endpoint {
        struct snd_rawmidi core;        /* raw UMP access */
 
        struct snd_ump_endpoint_info info;
 
+       const struct snd_ump_ops *ops;  /* UMP ops set by the driver */
+       struct snd_rawmidi_substream *substreams[2];    /* opened substreams */
+
        void *private_data;
        void (*private_free)(struct snd_ump_endpoint *ump);
 
        struct list_head block_list;    /* list of snd_ump_block objects */
 };
 
+/* ops filled by UMP drivers */
+struct snd_ump_ops {
+       int (*open)(struct snd_ump_endpoint *ump, int dir);
+       void (*close)(struct snd_ump_endpoint *ump, int dir);
+       void (*trigger)(struct snd_ump_endpoint *ump, int dir, int up);
+       void (*drain)(struct snd_ump_endpoint *ump, int dir);
+};
+
 struct snd_ump_block {
        struct snd_ump_block_info info;
        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);
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count);
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count);
 
 /*
  * Some definitions for UMP
 
                          void __user *argp);
 static void snd_ump_proc_read(struct snd_info_entry *entry,
                              struct snd_info_buffer *buffer);
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
+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 const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
        .dev_register = snd_ump_dev_register,
        .proc_read = snd_ump_proc_read,
 };
 
+static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
+       .open = snd_ump_rawmidi_open,
+       .close = snd_ump_rawmidi_close,
+       .trigger = snd_ump_rawmidi_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
+       .open = snd_ump_rawmidi_open,
+       .close = snd_ump_rawmidi_close,
+       .trigger = snd_ump_rawmidi_trigger,
+       .drain = snd_ump_rawmidi_drain,
+};
+
 static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
 {
        struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
 
        ump->core.private_free = snd_ump_endpoint_free;
        ump->core.ops = &snd_ump_rawmidi_ops;
+       if (input)
+               snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
+                                   &snd_ump_rawmidi_input_ops);
+       if (output)
+               snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                                   &snd_ump_rawmidi_output_ops);
 
        ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
        *ump_ret = ump;
        return NULL;
 }
 
+/*
+ * rawmidi ops for UMP endpoint
+ */
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+       int dir = substream->stream;
+       int err;
+
+       if (ump->substreams[dir])
+               return -EBUSY;
+       err = ump->ops->open(ump, dir);
+       if (err < 0)
+               return err;
+       ump->substreams[dir] = substream;
+       return 0;
+}
+
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+       int dir = substream->stream;
+
+       ump->substreams[dir] = NULL;
+       ump->ops->close(ump, dir);
+       return 0;
+}
+
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+                                   int up)
+{
+       struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+       int dir = substream->stream;
+
+       ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
+{
+       struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+
+       if (ump->ops->drain)
+               ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+/**
+ * snd_ump_receive - transfer UMP packets from the device
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to submit the received UMP packets from the device
+ * to user-space.  It's essentially a wrapper of rawmidi_receive().
+ * The data to receive is in CPU-native endianness.
+ */
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
+{
+       struct snd_rawmidi_substream *substream =
+               ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+       if (!substream)
+               return 0;
+       return snd_rawmidi_receive(substream, (const char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive);
+
+/**
+ * snd_ump_transmit - transmit UMP packets
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to obtain the UMP packets from user-space to the
+ * device.  It's essentially a wrapper of rawmidi_transmit().
+ * The data to transmit is in CPU-native endianness.
+ */
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
+{
+       struct snd_rawmidi_substream *substream =
+               ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+       if (!substream)
+               return -ENODEV;
+       return snd_rawmidi_transmit(substream, (char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_transmit);
+
 /**
  * snd_ump_block_new - Create a UMP block
  * @ump: UMP object
 
        struct usb_device *dev;
        const struct usb_ms20_endpoint_descriptor *ms_ep; /* reference to EP descriptor */
        struct snd_usb_midi2_endpoint *pair;    /* bidirectional pair EP */
-       struct snd_usb_midi2_ump *rmidi;        /* assigned UMP EP */
+       struct snd_usb_midi2_ump *rmidi;        /* assigned UMP EP pair */
+       struct snd_ump_endpoint *ump;           /* assigned UMP EP */
        int direction;                  /* direction (STR_IN/OUT) */
        unsigned int endpoint;          /* EP number */
        unsigned int pipe;              /* URB pipe */
 {
        int count;
 
-       if (ep->substream)
-               count = snd_rawmidi_transmit(ep->substream,
-                                            urb->transfer_buffer,
-                                            ep->packets);
-       else
-               count = -ENODEV;
+       count = snd_ump_transmit(ep->ump, urb->transfer_buffer,
+                                ep->packets);
        if (count < 0) {
                dev_dbg(&ep->dev->dev, "rawmidi transmit error %d\n", count);
                return count;
        len &= ~3; /* align UMP */
        if (len > ep->packets)
                len = ep->packets;
-       if (len > 0 && ep->substream) {
+       if (len > 0) {
                le32_to_cpu_array((u32 *)urb->transfer_buffer, len >> 2);
-               snd_rawmidi_receive(ep->substream, urb->transfer_buffer, len);
+               snd_ump_receive(ep->ump, (u32 *)urb->transfer_buffer, len);
        }
  dequeue:
        set_bit(ctx->index, &ep->urb_free);
 }
 
 static struct snd_usb_midi2_endpoint *
-substream_to_endpoint(struct snd_rawmidi_substream *substream)
+ump_to_endpoint(struct snd_ump_endpoint *ump, int dir)
 {
-       struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
        struct snd_usb_midi2_ump *rmidi = ump->private_data;
 
-       return rmidi->eps[substream->stream];
+       return rmidi->eps[dir];
 }
 
-/* rawmidi open callback */
-static int snd_usb_midi_v2_open(struct snd_rawmidi_substream *substream)
+/* ump open callback */
+static int snd_usb_midi_v2_open(struct snd_ump_endpoint *ump, int dir)
 {
-       struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+       struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
        int err = 0;
 
        if (!ep || !ep->endpoint)
                return -ENODEV;
        if (ep->disconnected)
                return -EIO;
-       if (ep->substream)
-               return -EBUSY;
        if (ep->direction == STR_OUT) {
                err = alloc_midi_urbs(ep);
                if (err)
                        return err;
        }
-       spin_lock_irq(&ep->lock);
-       ep->substream = substream;
-       spin_unlock_irq(&ep->lock);
        return 0;
 }
 
-/* rawmidi close callback */
-static int snd_usb_midi_v2_close(struct snd_rawmidi_substream *substream)
+/* ump close callback */
+static void snd_usb_midi_v2_close(struct snd_ump_endpoint *ump, int dir)
 {
-       struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+       struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
 
-       spin_lock_irq(&ep->lock);
-       ep->substream = NULL;
-       spin_unlock_irq(&ep->lock);
        if (ep->direction == STR_OUT) {
                kill_midi_urbs(ep, false);
                drain_urb_queue(ep);
                free_midi_urbs(ep);
        }
-       return 0;
 }
 
-/* rawmidi trigger callback */
-static void snd_usb_midi_v2_trigger(struct snd_rawmidi_substream *substream,
+/* ump trigger callback */
+static void snd_usb_midi_v2_trigger(struct snd_ump_endpoint *ump, int dir,
                                    int up)
 {
-       struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+       struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
 
        atomic_set(&ep->running, up);
        if (up && ep->direction == STR_OUT && !ep->disconnected)
                submit_io_urbs(ep);
 }
 
-/* rawmidi drain callback */
-static void snd_usb_midi_v2_drain(struct snd_rawmidi_substream *substream)
+/* ump drain callback */
+static void snd_usb_midi_v2_drain(struct snd_ump_endpoint *ump, int dir)
 {
-       struct snd_usb_midi2_endpoint *ep = substream_to_endpoint(substream);
+       struct snd_usb_midi2_endpoint *ep = ump_to_endpoint(ump, dir);
 
        drain_urb_queue(ep);
 }
        return err;
 }
 
-static const struct snd_rawmidi_ops output_ops = {
+static const struct snd_ump_ops snd_usb_midi_v2_ump_ops = {
        .open = snd_usb_midi_v2_open,
        .close = snd_usb_midi_v2_close,
        .trigger = snd_usb_midi_v2_trigger,
        .drain = snd_usb_midi_v2_drain,
 };
 
-static const struct snd_rawmidi_ops input_ops = {
-       .open = snd_usb_midi_v2_open,
-       .close = snd_usb_midi_v2_close,
-       .trigger = snd_usb_midi_v2_trigger,
-};
-
 /* create a USB MIDI 2.0 endpoint object */
 static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi,
                                 struct usb_host_endpoint *hostep,
        umidi->chip->num_rawmidis++;
 
        ump->private_data = rmidi;
-
-       if (input)
-               snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
-                                   &input_ops);
-       if (output)
-               snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
-                                   &output_ops);
+       ump->ops = &snd_usb_midi_v2_ump_ops;
 
        rmidi->eps[STR_IN] = ep_in;
        rmidi->eps[STR_OUT] = ep_out;
        if (ep_in) {
                ep_in->pair = ep_out;
                ep_in->rmidi = rmidi;
+               ep_in->ump = ump;
        }
        if (ep_out) {
                ep_out->pair = ep_in;
                ep_out->rmidi = rmidi;
+               ep_out->ump = ump;
        }
 
        list_add_tail(&rmidi->list, &umidi->rawmidi_list);