diff mbox series

[03/36] ALSA: rawmidi: UMP support

Message ID 20230519093114.28813-4-tiwai@suse.de (mailing list archive)
State Superseded
Headers show
Series ALSA: Add MIDI 2.0 support | expand

Commit Message

Takashi Iwai May 19, 2023, 9:30 a.m. UTC
This patch adds the support helpers for UMP (Universal MIDI Packet) in
ALSA core.

The basic design is that a rawmidi instance is assigned to each UMP
Endpoint.  A UMP Endpoint provides a UMP stream, typically
bidirectional (but can be also uni-directional, too), which may hold
up to 16 UMP Groups, where each UMP (input/output) Group corresponds
to the traditional MIDI I/O Endpoint.

Additionally, the ALSA UMP abstraction provides the multiple UMP
Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
metadata to hold the UMP Group clusters, and can represent the
functions assigned to each UMP Group.  A typical implementation of UMP
Block is the Group Terminal Blocks of USB MIDI 2.0 specification.

For distinguishing from the legacy byte-stream MIDI device, a new
device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
devices "midiC*D*".  The UMP instance can be identified by the new
rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.

A UMP rawmidi device reads/writes only in 4-bytes words alignment,
stored in CPU native endianness.

The transmit and receive functions take care of the input/out data
alignment, and may return zero or aligned size, and the params ioctl
may return -EINVAL when the given input/output buffer size isn't
aligned.

A few new UMP-specific ioctls are added for obtaining the new UMP
endpoint and block information.

As of this commit, no ALSA sequencer instance is attached to UMP
devices yet.  They will be supported by later patches.

Along with those changes, the protocol version for rawmidi is bumped
to 2.0.3.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/sound/rawmidi.h     |   8 ++
 include/sound/ump.h         | 118 ++++++++++++++++++
 include/uapi/sound/asound.h |  57 ++++++++-
 sound/core/Kconfig          |   4 +
 sound/core/Makefile         |   2 +
 sound/core/rawmidi.c        | 155 ++++++++++++++++--------
 sound/core/rawmidi_compat.c |   4 +
 sound/core/ump.c            | 231 ++++++++++++++++++++++++++++++++++++
 8 files changed, 530 insertions(+), 49 deletions(-)
 create mode 100644 include/sound/ump.h
 create mode 100644 sound/core/ump.c

Comments

Jaroslav Kysela May 22, 2023, 6:34 a.m. UTC | #1
On 19. 05. 23 11:30, Takashi Iwai wrote:
> This patch adds the support helpers for UMP (Universal MIDI Packet) in
> ALSA core.
> 
> The basic design is that a rawmidi instance is assigned to each UMP
> Endpoint.  A UMP Endpoint provides a UMP stream, typically
> bidirectional (but can be also uni-directional, too), which may hold
> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
> to the traditional MIDI I/O Endpoint.
> 
> Additionally, the ALSA UMP abstraction provides the multiple UMP
> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
> metadata to hold the UMP Group clusters, and can represent the
> functions assigned to each UMP Group.  A typical implementation of UMP
> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
> 
> For distinguishing from the legacy byte-stream MIDI device, a new
> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
> devices "midiC*D*".  The UMP instance can be identified by the new
> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
> 
> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
> stored in CPU native endianness.
> 
> The transmit and receive functions take care of the input/out data
> alignment, and may return zero or aligned size, and the params ioctl
> may return -EINVAL when the given input/output buffer size isn't
> aligned.
> 
> A few new UMP-specific ioctls are added for obtaining the new UMP
> endpoint and block information.
> 
> As of this commit, no ALSA sequencer instance is attached to UMP
> devices yet.  They will be supported by later patches.
> 
> Along with those changes, the protocol version for rawmidi is bumped
> to 2.0.3.
> 
> Signed-off-by: Takashi Iwai <tiwai@suse.de>

Reviewed-by: Jaroslav Kysela

except:

> +/* UMP Endpoint information */
> +struct snd_ump_endpoint_info {
> +	int card;			/* card number */
> +	int device;			/* device number */

I suspect that those two fields were added to enumerate devices in the control 
API. But this extension seems to be missing in your patches. There is only 
SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE implemented. Otherwise those two fields are 
not useful.

I also see the reference in the sequencer API 
SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO, but I don't get the use there, too.

> +/* UMP Block information */
> +struct snd_ump_block_info {
> +	int card;			/* card number */
> +	int device;			/* device number */

... dtto ...

					Jaroslav
Takashi Iwai May 22, 2023, 7:21 a.m. UTC | #2
On Mon, 22 May 2023 08:34:20 +0200,
Jaroslav Kysela wrote:
> 
> On 19. 05. 23 11:30, Takashi Iwai wrote:
> > This patch adds the support helpers for UMP (Universal MIDI Packet) in
> > ALSA core.
> > 
> > The basic design is that a rawmidi instance is assigned to each UMP
> > Endpoint.  A UMP Endpoint provides a UMP stream, typically
> > bidirectional (but can be also uni-directional, too), which may hold
> > up to 16 UMP Groups, where each UMP (input/output) Group corresponds
> > to the traditional MIDI I/O Endpoint.
> > 
> > Additionally, the ALSA UMP abstraction provides the multiple UMP
> > Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
> > metadata to hold the UMP Group clusters, and can represent the
> > functions assigned to each UMP Group.  A typical implementation of UMP
> > Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
> > 
> > For distinguishing from the legacy byte-stream MIDI device, a new
> > device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
> > devices "midiC*D*".  The UMP instance can be identified by the new
> > rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
> > 
> > A UMP rawmidi device reads/writes only in 4-bytes words alignment,
> > stored in CPU native endianness.
> > 
> > The transmit and receive functions take care of the input/out data
> > alignment, and may return zero or aligned size, and the params ioctl
> > may return -EINVAL when the given input/output buffer size isn't
> > aligned.
> > 
> > A few new UMP-specific ioctls are added for obtaining the new UMP
> > endpoint and block information.
> > 
> > As of this commit, no ALSA sequencer instance is attached to UMP
> > devices yet.  They will be supported by later patches.
> > 
> > Along with those changes, the protocol version for rawmidi is bumped
> > to 2.0.3.
> > 
> > Signed-off-by: Takashi Iwai <tiwai@suse.de>
> 
> Reviewed-by: Jaroslav Kysela
> 
> except:
> 
> > +/* UMP Endpoint information */
> > +struct snd_ump_endpoint_info {
> > +	int card;			/* card number */
> > +	int device;			/* device number */
> 
> I suspect that those two fields were added to enumerate devices in the
> control API. But this extension seems to be missing in your
> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
> implemented. Otherwise those two fields are not useful.

The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
snd_rawmidi_info is provided for (kernel) UMP implementation.

I took over those fields from snd_rawmidi_info, and they are supposed
to be referred rather from sequencer clients/ports (as well as from
the UMP rawmidi).  Then it could be useful when a user-space sequencer
client implements the UMP in future, and distinguish its identity.

But, it's no big deal to drop those, too.


thanks,

Takashi
Jaroslav Kysela May 22, 2023, 8:08 a.m. UTC | #3
On 22. 05. 23 9:21, Takashi Iwai wrote:
> On Mon, 22 May 2023 08:34:20 +0200,
> Jaroslav Kysela wrote:
>>
>> On 19. 05. 23 11:30, Takashi Iwai wrote:
>>> This patch adds the support helpers for UMP (Universal MIDI Packet) in
>>> ALSA core.
>>>
>>> The basic design is that a rawmidi instance is assigned to each UMP
>>> Endpoint.  A UMP Endpoint provides a UMP stream, typically
>>> bidirectional (but can be also uni-directional, too), which may hold
>>> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
>>> to the traditional MIDI I/O Endpoint.
>>>
>>> Additionally, the ALSA UMP abstraction provides the multiple UMP
>>> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
>>> metadata to hold the UMP Group clusters, and can represent the
>>> functions assigned to each UMP Group.  A typical implementation of UMP
>>> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
>>>
>>> For distinguishing from the legacy byte-stream MIDI device, a new
>>> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
>>> devices "midiC*D*".  The UMP instance can be identified by the new
>>> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
>>>
>>> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
>>> stored in CPU native endianness.
>>>
>>> The transmit and receive functions take care of the input/out data
>>> alignment, and may return zero or aligned size, and the params ioctl
>>> may return -EINVAL when the given input/output buffer size isn't
>>> aligned.
>>>
>>> A few new UMP-specific ioctls are added for obtaining the new UMP
>>> endpoint and block information.
>>>
>>> As of this commit, no ALSA sequencer instance is attached to UMP
>>> devices yet.  They will be supported by later patches.
>>>
>>> Along with those changes, the protocol version for rawmidi is bumped
>>> to 2.0.3.
>>>
>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>
>> Reviewed-by: Jaroslav Kysela
>>
>> except:
>>
>>> +/* UMP Endpoint information */
>>> +struct snd_ump_endpoint_info {
>>> +	int card;			/* card number */
>>> +	int device;			/* device number */
>>
>> I suspect that those two fields were added to enumerate devices in the
>> control API. But this extension seems to be missing in your
>> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
>> implemented. Otherwise those two fields are not useful.
> 
> The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
> snd_rawmidi_info is provided for (kernel) UMP implementation.

Right. My point was that an application may be able to evaluate the other UMP 
specific information from those new structures before the rawmidi device is 
opened. So the CTL API extension may make sense.

> I took over those fields from snd_rawmidi_info, and they are supposed
> to be referred rather from sequencer clients/ports (as well as from
> the UMP rawmidi).  Then it could be useful when a user-space sequencer
> client implements the UMP in future, and distinguish its identity.
> 
> But, it's no big deal to drop those, too.

Ok, keep them. Although this information seems to be redundant, it's harmless 
for now.

					Jaroslav
Takashi Iwai May 22, 2023, 10:27 a.m. UTC | #4
On Mon, 22 May 2023 10:08:24 +0200,
Jaroslav Kysela wrote:
> 
> On 22. 05. 23 9:21, Takashi Iwai wrote:
> > On Mon, 22 May 2023 08:34:20 +0200,
> > Jaroslav Kysela wrote:
> >> 
> >> On 19. 05. 23 11:30, Takashi Iwai wrote:
> >>> This patch adds the support helpers for UMP (Universal MIDI Packet) in
> >>> ALSA core.
> >>> 
> >>> The basic design is that a rawmidi instance is assigned to each UMP
> >>> Endpoint.  A UMP Endpoint provides a UMP stream, typically
> >>> bidirectional (but can be also uni-directional, too), which may hold
> >>> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
> >>> to the traditional MIDI I/O Endpoint.
> >>> 
> >>> Additionally, the ALSA UMP abstraction provides the multiple UMP
> >>> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
> >>> metadata to hold the UMP Group clusters, and can represent the
> >>> functions assigned to each UMP Group.  A typical implementation of UMP
> >>> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
> >>> 
> >>> For distinguishing from the legacy byte-stream MIDI device, a new
> >>> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
> >>> devices "midiC*D*".  The UMP instance can be identified by the new
> >>> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
> >>> 
> >>> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
> >>> stored in CPU native endianness.
> >>> 
> >>> The transmit and receive functions take care of the input/out data
> >>> alignment, and may return zero or aligned size, and the params ioctl
> >>> may return -EINVAL when the given input/output buffer size isn't
> >>> aligned.
> >>> 
> >>> A few new UMP-specific ioctls are added for obtaining the new UMP
> >>> endpoint and block information.
> >>> 
> >>> As of this commit, no ALSA sequencer instance is attached to UMP
> >>> devices yet.  They will be supported by later patches.
> >>> 
> >>> Along with those changes, the protocol version for rawmidi is bumped
> >>> to 2.0.3.
> >>> 
> >>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> >> 
> >> Reviewed-by: Jaroslav Kysela
> >> 
> >> except:
> >> 
> >>> +/* UMP Endpoint information */
> >>> +struct snd_ump_endpoint_info {
> >>> +	int card;			/* card number */
> >>> +	int device;			/* device number */
> >> 
> >> I suspect that those two fields were added to enumerate devices in the
> >> control API. But this extension seems to be missing in your
> >> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
> >> implemented. Otherwise those two fields are not useful.
> > 
> > The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
> > snd_rawmidi_info is provided for (kernel) UMP implementation.
> 
> Right. My point was that an application may be able to evaluate the
> other UMP specific information from those new structures before the
> rawmidi device is opened. So the CTL API extension may make sense.

Point taken, and indeed it might make more sense to change the ioctl
for looking at snd_ump_endpoint_info.  Will try to cook with it.

> > I took over those fields from snd_rawmidi_info, and they are supposed
> > to be referred rather from sequencer clients/ports (as well as from
> > the UMP rawmidi).  Then it could be useful when a user-space sequencer
> > client implements the UMP in future, and distinguish its identity.
> > 
> > But, it's no big deal to drop those, too.
> 
> Ok, keep them. Although this information seems to be redundant, it's
> harmless for now.

OK.


Takashi
Takashi Iwai May 22, 2023, 12:27 p.m. UTC | #5
On Mon, 22 May 2023 12:27:11 +0200,
Takashi Iwai wrote:
> 
> On Mon, 22 May 2023 10:08:24 +0200,
> Jaroslav Kysela wrote:
> > 
> > On 22. 05. 23 9:21, Takashi Iwai wrote:
> > > On Mon, 22 May 2023 08:34:20 +0200,
> > > Jaroslav Kysela wrote:
> > >> 
> > >> On 19. 05. 23 11:30, Takashi Iwai wrote:
> > >>> This patch adds the support helpers for UMP (Universal MIDI Packet) in
> > >>> ALSA core.
> > >>> 
> > >>> The basic design is that a rawmidi instance is assigned to each UMP
> > >>> Endpoint.  A UMP Endpoint provides a UMP stream, typically
> > >>> bidirectional (but can be also uni-directional, too), which may hold
> > >>> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
> > >>> to the traditional MIDI I/O Endpoint.
> > >>> 
> > >>> Additionally, the ALSA UMP abstraction provides the multiple UMP
> > >>> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
> > >>> metadata to hold the UMP Group clusters, and can represent the
> > >>> functions assigned to each UMP Group.  A typical implementation of UMP
> > >>> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
> > >>> 
> > >>> For distinguishing from the legacy byte-stream MIDI device, a new
> > >>> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
> > >>> devices "midiC*D*".  The UMP instance can be identified by the new
> > >>> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
> > >>> 
> > >>> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
> > >>> stored in CPU native endianness.
> > >>> 
> > >>> The transmit and receive functions take care of the input/out data
> > >>> alignment, and may return zero or aligned size, and the params ioctl
> > >>> may return -EINVAL when the given input/output buffer size isn't
> > >>> aligned.
> > >>> 
> > >>> A few new UMP-specific ioctls are added for obtaining the new UMP
> > >>> endpoint and block information.
> > >>> 
> > >>> As of this commit, no ALSA sequencer instance is attached to UMP
> > >>> devices yet.  They will be supported by later patches.
> > >>> 
> > >>> Along with those changes, the protocol version for rawmidi is bumped
> > >>> to 2.0.3.
> > >>> 
> > >>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> > >> 
> > >> Reviewed-by: Jaroslav Kysela
> > >> 
> > >> except:
> > >> 
> > >>> +/* UMP Endpoint information */
> > >>> +struct snd_ump_endpoint_info {
> > >>> +	int card;			/* card number */
> > >>> +	int device;			/* device number */
> > >> 
> > >> I suspect that those two fields were added to enumerate devices in the
> > >> control API. But this extension seems to be missing in your
> > >> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
> > >> implemented. Otherwise those two fields are not useful.
> > > 
> > > The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
> > > snd_rawmidi_info is provided for (kernel) UMP implementation.
> > 
> > Right. My point was that an application may be able to evaluate the
> > other UMP specific information from those new structures before the
> > rawmidi device is opened. So the CTL API extension may make sense.
> 
> Point taken, and indeed it might make more sense to change the ioctl
> for looking at snd_ump_endpoint_info.  Will try to cook with it.

FWIW, below is the patch to add two new ioctls for UMP EP/block
inquiries.  It's together with a slight change of ioctl callback.
Will be included in v2 patch set I'll submit later.

The latest patches are found in topic/midi20 branch of sound git
tree.


Takashi

-- 8< --
Subject: [PATCH] ALSA: ump: Add ioctls to inquiry UMP EP and Block info via
 control API

It'd be convenient to have ioctls to inquiry the UMP Endpoint and UMP
Block information directly via the control API without opening the
rawmidi interface, just like SNDRV_CTL_IOCTL_RAWMIDI_INFO.

This patch extends the rawmidi ioctl handler to support those; new
ioctls, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO and
SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, return the snd_ump_endpoint and
snd_ump_block data that is specified by the device field,
respectively.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 1e4a21036109..445653fc0025 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1178,6 +1178,8 @@ struct snd_ctl_tlv {
 #define SNDRV_CTL_IOCTL_RAWMIDI_INFO	_IOWR('U', 0x41, struct snd_rawmidi_info)
 #define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int)
 #define SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE	_IOWR('U', 0x43, int)
+#define SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO _IOWR('U', 0x44, struct snd_ump_endpoint_info)
+#define SNDRV_CTL_IOCTL_UMP_BLOCK_INFO	_IOWR('U', 0x44, struct snd_ump_block_info)
 #define SNDRV_CTL_IOCTL_POWER		_IOWR('U', 0xd0, int)
 #define SNDRV_CTL_IOCTL_POWER_STATE	_IOR('U', 0xd1, int)
 
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 9936ed282b85..ffb5b58105f4 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1043,6 +1043,28 @@ static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_SND_UMP)
+/* inquiry of UMP endpoint and block info via control API */
+static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
+				      void __user *argp)
+{
+	struct snd_ump_endpoint_info __user *info = argp;
+	struct snd_rawmidi *rmidi;
+	int device, ret;
+
+	if (get_user(device, &info->device))
+		return -EFAULT;
+	mutex_lock(&register_mutex);
+	rmidi = snd_rawmidi_search(card, device);
+	if (rmidi && rmidi->ops && rmidi->ops->ioctl)
+		ret = rmidi->ops->ioctl(rmidi, cmd, argp);
+	else
+		ret = -ENXIO;
+	mutex_unlock(&register_mutex);
+	return ret;
+}
+#endif
+
 static int snd_rawmidi_control_ioctl(struct snd_card *card,
 				     struct snd_ctl_file *control,
 				     unsigned int cmd,
@@ -1056,6 +1078,10 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
 #if IS_ENABLED(CONFIG_SND_UMP)
 	case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
 		return snd_rawmidi_next_device(card, argp, true);
+	case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
+		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
+	case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
+		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
 #endif
 	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
 	{
Takashi Iwai May 22, 2023, 4:22 p.m. UTC | #6
On Mon, 22 May 2023 14:27:37 +0200,
Takashi Iwai wrote:
> 
> On Mon, 22 May 2023 12:27:11 +0200,
> Takashi Iwai wrote:
> > 
> > On Mon, 22 May 2023 10:08:24 +0200,
> > Jaroslav Kysela wrote:
> > > 
> > > On 22. 05. 23 9:21, Takashi Iwai wrote:
> > > > On Mon, 22 May 2023 08:34:20 +0200,
> > > > Jaroslav Kysela wrote:
> > > >> 
> > > >> On 19. 05. 23 11:30, Takashi Iwai wrote:
> > > >>> This patch adds the support helpers for UMP (Universal MIDI Packet) in
> > > >>> ALSA core.
> > > >>> 
> > > >>> The basic design is that a rawmidi instance is assigned to each UMP
> > > >>> Endpoint.  A UMP Endpoint provides a UMP stream, typically
> > > >>> bidirectional (but can be also uni-directional, too), which may hold
> > > >>> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
> > > >>> to the traditional MIDI I/O Endpoint.
> > > >>> 
> > > >>> Additionally, the ALSA UMP abstraction provides the multiple UMP
> > > >>> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
> > > >>> metadata to hold the UMP Group clusters, and can represent the
> > > >>> functions assigned to each UMP Group.  A typical implementation of UMP
> > > >>> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
> > > >>> 
> > > >>> For distinguishing from the legacy byte-stream MIDI device, a new
> > > >>> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
> > > >>> devices "midiC*D*".  The UMP instance can be identified by the new
> > > >>> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
> > > >>> 
> > > >>> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
> > > >>> stored in CPU native endianness.
> > > >>> 
> > > >>> The transmit and receive functions take care of the input/out data
> > > >>> alignment, and may return zero or aligned size, and the params ioctl
> > > >>> may return -EINVAL when the given input/output buffer size isn't
> > > >>> aligned.
> > > >>> 
> > > >>> A few new UMP-specific ioctls are added for obtaining the new UMP
> > > >>> endpoint and block information.
> > > >>> 
> > > >>> As of this commit, no ALSA sequencer instance is attached to UMP
> > > >>> devices yet.  They will be supported by later patches.
> > > >>> 
> > > >>> Along with those changes, the protocol version for rawmidi is bumped
> > > >>> to 2.0.3.
> > > >>> 
> > > >>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
> > > >> 
> > > >> Reviewed-by: Jaroslav Kysela
> > > >> 
> > > >> except:
> > > >> 
> > > >>> +/* UMP Endpoint information */
> > > >>> +struct snd_ump_endpoint_info {
> > > >>> +	int card;			/* card number */
> > > >>> +	int device;			/* device number */
> > > >> 
> > > >> I suspect that those two fields were added to enumerate devices in the
> > > >> control API. But this extension seems to be missing in your
> > > >> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
> > > >> implemented. Otherwise those two fields are not useful.
> > > > 
> > > > The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
> > > > snd_rawmidi_info is provided for (kernel) UMP implementation.
> > > 
> > > Right. My point was that an application may be able to evaluate the
> > > other UMP specific information from those new structures before the
> > > rawmidi device is opened. So the CTL API extension may make sense.
> > 
> > Point taken, and indeed it might make more sense to change the ioctl
> > for looking at snd_ump_endpoint_info.  Will try to cook with it.
> 
> FWIW, below is the patch to add two new ioctls for UMP EP/block
> inquiries.  It's together with a slight change of ioctl callback.
> Will be included in v2 patch set I'll submit later.
> 
> The latest patches are found in topic/midi20 branch of sound git
> tree.

There was a typo.  The corrected version below.


Takashi

-- 8< --
From: Takashi Iwai <tiwai@suse.de>
Subject: [PATCH] ALSA: ump: Add ioctls to inquiry UMP EP and Block info via
 control API

It'd be convenient to have ioctls to inquiry the UMP Endpoint and UMP
Block information directly via the control API without opening the
rawmidi interface, just like SNDRV_CTL_IOCTL_RAWMIDI_INFO.

This patch extends the rawmidi ioctl handler to support those; new
ioctls, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO and
SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, return the snd_ump_endpoint and
snd_ump_block data that is specified by the device field,
respectively.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/uapi/sound/asound.h |  2 ++
 sound/core/rawmidi.c        | 26 ++++++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 1e4a21036109..5c5f41dd4001 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -1178,6 +1178,8 @@ struct snd_ctl_tlv {
 #define SNDRV_CTL_IOCTL_RAWMIDI_INFO	_IOWR('U', 0x41, struct snd_rawmidi_info)
 #define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int)
 #define SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE	_IOWR('U', 0x43, int)
+#define SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO _IOWR('U', 0x44, struct snd_ump_endpoint_info)
+#define SNDRV_CTL_IOCTL_UMP_BLOCK_INFO	_IOWR('U', 0x45, struct snd_ump_block_info)
 #define SNDRV_CTL_IOCTL_POWER		_IOWR('U', 0xd0, int)
 #define SNDRV_CTL_IOCTL_POWER_STATE	_IOR('U', 0xd1, int)
 
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 9936ed282b85..ffb5b58105f4 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1043,6 +1043,28 @@ static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
 	return 0;
 }
 
+#if IS_ENABLED(CONFIG_SND_UMP)
+/* inquiry of UMP endpoint and block info via control API */
+static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
+				      void __user *argp)
+{
+	struct snd_ump_endpoint_info __user *info = argp;
+	struct snd_rawmidi *rmidi;
+	int device, ret;
+
+	if (get_user(device, &info->device))
+		return -EFAULT;
+	mutex_lock(&register_mutex);
+	rmidi = snd_rawmidi_search(card, device);
+	if (rmidi && rmidi->ops && rmidi->ops->ioctl)
+		ret = rmidi->ops->ioctl(rmidi, cmd, argp);
+	else
+		ret = -ENXIO;
+	mutex_unlock(&register_mutex);
+	return ret;
+}
+#endif
+
 static int snd_rawmidi_control_ioctl(struct snd_card *card,
 				     struct snd_ctl_file *control,
 				     unsigned int cmd,
@@ -1056,6 +1078,10 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
 #if IS_ENABLED(CONFIG_SND_UMP)
 	case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
 		return snd_rawmidi_next_device(card, argp, true);
+	case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
+		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
+	case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
+		return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
 #endif
 	case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
 	{
Jaroslav Kysela May 22, 2023, 7:01 p.m. UTC | #7
On 22. 05. 23 18:22, Takashi Iwai wrote:
> On Mon, 22 May 2023 14:27:37 +0200,
> Takashi Iwai wrote:
>>
>> On Mon, 22 May 2023 12:27:11 +0200,
>> Takashi Iwai wrote:
>>>
>>> On Mon, 22 May 2023 10:08:24 +0200,
>>> Jaroslav Kysela wrote:
>>>>
>>>> On 22. 05. 23 9:21, Takashi Iwai wrote:
>>>>> On Mon, 22 May 2023 08:34:20 +0200,
>>>>> Jaroslav Kysela wrote:
>>>>>>
>>>>>> On 19. 05. 23 11:30, Takashi Iwai wrote:
>>>>>>> This patch adds the support helpers for UMP (Universal MIDI Packet) in
>>>>>>> ALSA core.
>>>>>>>
>>>>>>> The basic design is that a rawmidi instance is assigned to each UMP
>>>>>>> Endpoint.  A UMP Endpoint provides a UMP stream, typically
>>>>>>> bidirectional (but can be also uni-directional, too), which may hold
>>>>>>> up to 16 UMP Groups, where each UMP (input/output) Group corresponds
>>>>>>> to the traditional MIDI I/O Endpoint.
>>>>>>>
>>>>>>> Additionally, the ALSA UMP abstraction provides the multiple UMP
>>>>>>> Blocks that can be assigned to each UMP Endpoint.  A UMP Block is a
>>>>>>> metadata to hold the UMP Group clusters, and can represent the
>>>>>>> functions assigned to each UMP Group.  A typical implementation of UMP
>>>>>>> Block is the Group Terminal Blocks of USB MIDI 2.0 specification.
>>>>>>>
>>>>>>> For distinguishing from the legacy byte-stream MIDI device, a new
>>>>>>> device "umpC*D*" will be created, instead of the standard (MIDI 1.0)
>>>>>>> devices "midiC*D*".  The UMP instance can be identified by the new
>>>>>>> rawmidi info bit SNDRV_RAWMIDI_INFO_UMP, too.
>>>>>>>
>>>>>>> A UMP rawmidi device reads/writes only in 4-bytes words alignment,
>>>>>>> stored in CPU native endianness.
>>>>>>>
>>>>>>> The transmit and receive functions take care of the input/out data
>>>>>>> alignment, and may return zero or aligned size, and the params ioctl
>>>>>>> may return -EINVAL when the given input/output buffer size isn't
>>>>>>> aligned.
>>>>>>>
>>>>>>> A few new UMP-specific ioctls are added for obtaining the new UMP
>>>>>>> endpoint and block information.
>>>>>>>
>>>>>>> As of this commit, no ALSA sequencer instance is attached to UMP
>>>>>>> devices yet.  They will be supported by later patches.
>>>>>>>
>>>>>>> Along with those changes, the protocol version for rawmidi is bumped
>>>>>>> to 2.0.3.
>>>>>>>
>>>>>>> Signed-off-by: Takashi Iwai <tiwai@suse.de>
>>>>>>
>>>>>> Reviewed-by: Jaroslav Kysela
>>>>>>
>>>>>> except:
>>>>>>
>>>>>>> +/* UMP Endpoint information */
>>>>>>> +struct snd_ump_endpoint_info {
>>>>>>> +	int card;			/* card number */
>>>>>>> +	int device;			/* device number */
>>>>>>
>>>>>> I suspect that those two fields were added to enumerate devices in the
>>>>>> control API. But this extension seems to be missing in your
>>>>>> patches. There is only SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE
>>>>>> implemented. Otherwise those two fields are not useful.
>>>>>
>>>>> The SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE is looping over rawmidi, and
>>>>> snd_rawmidi_info is provided for (kernel) UMP implementation.
>>>>
>>>> Right. My point was that an application may be able to evaluate the
>>>> other UMP specific information from those new structures before the
>>>> rawmidi device is opened. So the CTL API extension may make sense.
>>>
>>> Point taken, and indeed it might make more sense to change the ioctl
>>> for looking at snd_ump_endpoint_info.  Will try to cook with it.
>>
>> FWIW, below is the patch to add two new ioctls for UMP EP/block
>> inquiries.  It's together with a slight change of ioctl callback.
>> Will be included in v2 patch set I'll submit later.
>>
>> The latest patches are found in topic/midi20 branch of sound git
>> tree.
> 
> There was a typo.  The corrected version below.
> 
> 
> Takashi
> 
> -- 8< --
> From: Takashi Iwai <tiwai@suse.de>
> Subject: [PATCH] ALSA: ump: Add ioctls to inquiry UMP EP and Block info via
>   control API
> 
> It'd be convenient to have ioctls to inquiry the UMP Endpoint and UMP
> Block information directly via the control API without opening the
> rawmidi interface, just like SNDRV_CTL_IOCTL_RAWMIDI_INFO.
> 
> This patch extends the rawmidi ioctl handler to support those; new
> ioctls, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO and
> SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, return the snd_ump_endpoint and
> snd_ump_block data that is specified by the device field,
> respectively.
> 
> Signed-off-by: Takashi Iwai <tiwai@suse.de>

Thanks. It looks good.

Suggested-by: Jaroslav Kysela <perex@perex.cz>
Reviewed-by: Jaroslav Kysela <perex@perex.cz>

					Jaroslav
diff mbox series

Patch

diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h
index a53cd063412c..4d8fa8ca0127 100644
--- a/include/sound/rawmidi.h
+++ b/include/sound/rawmidi.h
@@ -64,6 +64,7 @@  struct snd_rawmidi_runtime {
 	size_t avail_min;	/* min avail for wakeup */
 	size_t avail;		/* max used buffer for wakeup */
 	size_t xruns;		/* over/underruns counter */
+	size_t align;		/* alignment (0 = byte stream, 3 = UMP) */
 	int buffer_ref;		/* buffer reference count */
 	/* misc */
 	wait_queue_head_t sleep;
@@ -149,6 +150,13 @@  int snd_rawmidi_new(struct snd_card *card, char *id, int device,
 void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
 			 const struct snd_rawmidi_ops *ops);
 
+/* internal */
+int snd_rawmidi_init(struct snd_rawmidi *rmidi,
+		     struct snd_card *card, char *id, int device,
+		     int output_count, int input_count,
+		     unsigned int info_flags);
+int snd_rawmidi_free(struct snd_rawmidi *rmidi);
+
 /* callbacks */
 
 int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
diff --git a/include/sound/ump.h b/include/sound/ump.h
new file mode 100644
index 000000000000..8a3ac97cd1d3
--- /dev/null
+++ b/include/sound/ump.h
@@ -0,0 +1,118 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Universal MIDI Packet (UMP) Support
+ */
+#ifndef __SOUND_UMP_H
+#define __SOUND_UMP_H
+
+#include <sound/rawmidi.h>
+
+struct snd_ump_endpoint;
+struct snd_ump_block;
+
+struct snd_ump_endpoint {
+	struct snd_rawmidi core;	/* raw UMP access */
+
+	struct snd_ump_endpoint_info info;
+
+	void *private_data;
+	void (*private_free)(struct snd_ump_endpoint *ump);
+
+	struct list_head block_list;	/* list of snd_ump_block objects */
+};
+
+struct snd_ump_block {
+	struct snd_ump_block_info info;
+	struct snd_ump_endpoint *ump;
+
+	void *private_data;
+	void (*private_free)(struct snd_ump_block *blk);
+
+	struct list_head list;
+};
+
+#define rawmidi_to_ump(rmidi)	container_of(rmidi, struct snd_ump_endpoint, core)
+
+int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
+			 int output, int input,
+			 struct snd_ump_endpoint **ump_ret);
+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);
+
+/*
+ * Some definitions for UMP
+ */
+
+/* MIDI 2.0 Message Type */
+enum {
+	UMP_MSG_TYPE_UTILITY			= 0x00,
+	UMP_MSG_TYPE_SYSTEM			= 0x01,
+	UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE	= 0x02,
+	UMP_MSG_TYPE_DATA			= 0x03,
+	UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE	= 0x04,
+	UMP_MSG_TYPE_EXTENDED_DATA		= 0x05,
+};
+
+/* MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */
+enum {
+	UMP_SYSEX_STATUS_SINGLE			= 0,
+	UMP_SYSEX_STATUS_START			= 1,
+	UMP_SYSEX_STATUS_CONTINUE		= 2,
+	UMP_SYSEX_STATUS_END			= 3,
+};
+
+/*
+ * Helpers for retrieving / filling bits from UMP
+ */
+/* get the message type (4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_type(u32 data)
+{
+	return data >> 28;
+}
+
+/* get the group number (0-based, 4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_group(u32 data)
+{
+	return (data >> 24) & 0x0f;
+}
+
+/* get the MIDI status code (4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_status_code(u32 data)
+{
+	return (data >> 20) & 0x0f;
+}
+
+/* get the MIDI channel number (0-based, 4bit) from a UMP packet (header) */
+static inline unsigned char ump_message_channel(u32 data)
+{
+	return (data >> 16) & 0x0f;
+}
+
+/* get the MIDI status + channel combo byte (8bit) from a UMP packet (header) */
+static inline unsigned char ump_message_status_channel(u32 data)
+{
+	return (data >> 16) & 0xff;
+}
+
+/* compose a UMP packet (header) from type, group and status values */
+static inline u32 ump_compose(unsigned char type, unsigned char group,
+			      unsigned char status, unsigned char channel)
+{
+	return ((u32)type << 28) | ((u32)group << 24) | ((u32)status << 20) |
+		((u32)channel << 16);
+}
+
+/* get SysEx message status (for both 7 and 8bits) from a UMP packet (header) */
+static inline unsigned char ump_sysex_message_status(u32 data)
+{
+	return (data >> 20) & 0xf;
+}
+
+/* get SysEx message length (for both 7 and 8bits) from a UMP packet (header) */
+static inline unsigned char ump_sysex_message_length(u32 data)
+{
+	return (data >> 16) & 0xf;
+}
+
+#endif /* __SOUND_UMP_H */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 0aa955aa8246..b001df4b335e 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -708,7 +708,7 @@  enum {
  *  Raw MIDI section - /dev/snd/midi??
  */
 
-#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 2)
+#define SNDRV_RAWMIDI_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 3)
 
 enum {
 	SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
@@ -719,6 +719,7 @@  enum {
 #define SNDRV_RAWMIDI_INFO_OUTPUT		0x00000001
 #define SNDRV_RAWMIDI_INFO_INPUT		0x00000002
 #define SNDRV_RAWMIDI_INFO_DUPLEX		0x00000004
+#define SNDRV_RAWMIDI_INFO_UMP			0x00000008
 
 struct snd_rawmidi_info {
 	unsigned int device;		/* RO/WR (control): device number */
@@ -779,6 +780,57 @@  struct snd_rawmidi_status {
 };
 #endif
 
+/* UMP EP Protocol / JRTS capability bits */
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK	0x0300
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI1		0x0100 /* MIDI 1.0 */
+#define SNDRV_UMP_EP_INFO_PROTO_MIDI2		0x0200 /* MIDI 2.0 */
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_MASK	0x0003
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_TX		0x0001 /* JRTS Transmit */
+#define SNDRV_UMP_EP_INFO_PROTO_JRTS_RX		0x0002 /* JRTS Receive */
+
+/* UMP Endpoint information */
+struct snd_ump_endpoint_info {
+	int card;			/* card number */
+	int device;			/* device number */
+	unsigned int flags;		/* additional info */
+	unsigned int protocol_caps;	/* protocol capabilities */
+	unsigned int protocol;		/* current protocol */
+	unsigned int num_blocks;	/* # of function blocks */
+	unsigned short version;		/* UMP major/minor version */
+	unsigned short padding[7];
+	unsigned char name[128];	/* endpoint name string */
+	unsigned char product_id[128];	/* unique product id string */
+	unsigned char reserved[32];
+} __packed;
+
+/* UMP direction */
+#define SNDRV_UMP_DIR_INPUT		0x01
+#define SNDRV_UMP_DIR_OUTPUT		0x02
+#define SNDRV_UMP_DIR_BIDIRECTION	0x03
+
+/* UMP block info flags */
+#define SNDRV_UMP_BLOCK_IS_MIDI1	(1U << 0) /* MIDI 1.0 port w/o restrict */
+#define SNDRV_UMP_BLOCK_IS_LOWSPEED	(1U << 1) /* 31.25Kbps B/W MIDI1 port */
+
+/* UMP groups and blocks */
+#define SNDRV_UMP_MAX_GROUPS		16
+#define SNDRV_UMP_MAX_BLOCKS		32
+
+/* UMP Block information */
+struct snd_ump_block_info {
+	int card;			/* card number */
+	int device;			/* device number */
+	unsigned char block_id;		/* block ID (R/W) */
+	unsigned char direction;	/* UMP direction */
+	unsigned char active;		/* Activeness */
+	unsigned char first_group;	/* first group ID */
+	unsigned char num_groups;	/* number of groups */
+	unsigned char padding[3];
+	unsigned int flags;		/* various info flags */
+	unsigned char name[128];	/* block name string */
+	unsigned char reserved[32];
+} __packed;
+
 #define SNDRV_RAWMIDI_IOCTL_PVERSION	_IOR('W', 0x00, int)
 #define SNDRV_RAWMIDI_IOCTL_INFO	_IOR('W', 0x01, struct snd_rawmidi_info)
 #define SNDRV_RAWMIDI_IOCTL_USER_PVERSION _IOW('W', 0x02, int)
@@ -786,6 +838,9 @@  struct snd_rawmidi_status {
 #define SNDRV_RAWMIDI_IOCTL_STATUS	_IOWR('W', 0x20, struct snd_rawmidi_status)
 #define SNDRV_RAWMIDI_IOCTL_DROP	_IOW('W', 0x30, int)
 #define SNDRV_RAWMIDI_IOCTL_DRAIN	_IOW('W', 0x31, int)
+/* Additional ioctls for UMP rawmidi devices */
+#define SNDRV_UMP_IOCTL_ENDPOINT_INFO	_IOR('W', 0x40, struct snd_ump_endpoint_info)
+#define SNDRV_UMP_IOCTL_BLOCK_INFO	_IOR('W', 0x41, struct snd_ump_block_info)
 
 /*
  *  Timer section - /dev/snd/timer
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 12990d9a4dff..eb1c6c930de9 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -26,6 +26,10 @@  config SND_RAWMIDI
 	tristate
 	select SND_SEQ_DEVICE if SND_SEQUENCER != n
 
+config SND_UMP
+	tristate
+	select SND_RAWMIDI
+
 config SND_COMPRESS_OFFLOAD
 	tristate
 
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 2762f03d9b7b..562a05edbc50 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -28,6 +28,7 @@  snd-pcm-dmaengine-objs := pcm_dmaengine.o
 
 snd-ctl-led-objs  := control_led.o
 snd-rawmidi-objs  := rawmidi.o
+snd-ump-objs      := ump.o
 snd-timer-objs    := timer.o
 snd-hrtimer-objs  := hrtimer.o
 snd-rtctimer-objs := rtctimer.o
@@ -45,6 +46,7 @@  obj-$(CONFIG_SND_PCM)		+= snd-pcm.o
 obj-$(CONFIG_SND_DMAENGINE_PCM)	+= snd-pcm-dmaengine.o
 obj-$(CONFIG_SND_SEQ_DEVICE)	+= snd-seq-device.o
 obj-$(CONFIG_SND_RAWMIDI)	+= snd-rawmidi.o
+obj-$(CONFIG_SND_UMP)		+= snd-ump.o
 
 obj-$(CONFIG_SND_OSSEMUL)	+= oss/
 obj-$(CONFIG_SND_SEQUENCER)	+= seq/
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 1415f559b5d0..ef478fcacb42 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -21,6 +21,7 @@ 
 #include <sound/control.h>
 #include <sound/minors.h>
 #include <sound/initval.h>
+#include <sound/ump.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
@@ -35,7 +36,6 @@  module_param_array(amidi_map, int, NULL, 0444);
 MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
 #endif /* CONFIG_SND_OSSEMUL */
 
-static int snd_rawmidi_free(struct snd_rawmidi *rmidi);
 static int snd_rawmidi_dev_free(struct snd_device *device);
 static int snd_rawmidi_dev_register(struct snd_device *device);
 static int snd_rawmidi_dev_disconnect(struct snd_device *device);
@@ -73,6 +73,9 @@  struct snd_rawmidi_status64 {
 
 #define SNDRV_RAWMIDI_IOCTL_STATUS64	_IOWR('W', 0x20, struct snd_rawmidi_status64)
 
+#define rawmidi_is_ump(rmidi) \
+	(IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP))
+
 static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
 {
 	struct snd_rawmidi *rawmidi;
@@ -181,9 +184,23 @@  static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
 	}
 	runtime->appl_ptr = runtime->hw_ptr = 0;
 	substream->runtime = runtime;
+	if (rawmidi_is_ump(substream->rmidi))
+		runtime->align = 3;
 	return 0;
 }
 
+/* get the current alignment (either 0 or 3) */
+static inline int get_align(struct snd_rawmidi_runtime *runtime)
+{
+	if (IS_ENABLED(CONFIG_SND_UMP))
+		return runtime->align;
+	else
+		return 0;
+}
+
+/* get the trimmed size with the current alignment */
+#define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
+
 static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
 {
 	struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -721,6 +738,8 @@  static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
 		return -EINVAL;
 	if (params->avail_min < 1 || params->avail_min > params->buffer_size)
 		return -EINVAL;
+	if (params->buffer_size & get_align(runtime))
+		return -EINVAL;
 	if (params->buffer_size != runtime->buffer_size) {
 		newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
 		if (!newbuf)
@@ -1045,12 +1064,13 @@  static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
 	struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
 	int orig_count = src_count;
 	int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
+	int align = get_align(runtime);
 
 	BUILD_BUG_ON(frame_size != 0x20);
 	if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
 		return -EINVAL;
 
-	while (src_count > 0) {
+	while (src_count > align) {
 		if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
 			runtime->xruns += src_count;
 			break;
@@ -1058,7 +1078,9 @@  static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
 		if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
 			frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
 		else {
-			frame.length = src_count;
+			frame.length = get_aligned_size(runtime, src_count);
+			if (!frame.length)
+				break;
 			memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
 		}
 		memcpy(frame.data, buffer, frame.length);
@@ -1122,6 +1144,10 @@  int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 		goto unlock;
 	}
 
+	count = get_aligned_size(runtime, count);
+	if (!count)
+		goto unlock;
+
 	if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
 		result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
 	} else if (count == 1) {	/* special case, faster code */
@@ -1141,6 +1167,9 @@  int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
 			count1 = count;
 		if (count1 > (int)(runtime->buffer_size - runtime->avail))
 			count1 = runtime->buffer_size - runtime->avail;
+		count1 = get_aligned_size(runtime, count1);
+		if (!count1)
+			goto unlock;
 		memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
 		runtime->hw_ptr += count1;
 		runtime->hw_ptr %= runtime->buffer_size;
@@ -1341,12 +1370,18 @@  static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
 			count1 = count;
 		if (count1 > (int)(runtime->buffer_size - runtime->avail))
 			count1 = runtime->buffer_size - runtime->avail;
+		count1 = get_aligned_size(runtime, count1);
+		if (!count1)
+			goto __skip;
 		memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
 		count -= count1;
 		result += count1;
 		if (count > 0) {
 			if (count > (int)(runtime->buffer_size - runtime->avail - count1))
 				count = runtime->buffer_size - runtime->avail - count1;
+			count = get_aligned_size(runtime, count);
+			if (!count)
+				goto __skip;
 			memcpy(buffer + count1, runtime->buffer, count);
 			result += count;
 		}
@@ -1403,6 +1438,7 @@  static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
 		return -EINVAL;
 	}
 	snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
+	count = get_aligned_size(runtime, count);
 	runtime->hw_ptr += count;
 	runtime->hw_ptr %= runtime->buffer_size;
 	runtime->avail += count;
@@ -1689,6 +1725,9 @@  static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
 
 	rmidi = entry->private_data;
 	snd_iprintf(buffer, "%s\n\n", rmidi->name);
+	if (IS_ENABLED(CONFIG_SND_UMP))
+		snd_iprintf(buffer, "Type: %s\n",
+			    rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
 	mutex_lock(&rmidi->open_mutex);
 	if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
 		list_for_each_entry(substream,
@@ -1799,6 +1838,56 @@  static void release_rawmidi_device(struct device *dev)
 	kfree(container_of(dev, struct snd_rawmidi, dev));
 }
 
+/* used for both rawmidi and ump */
+int snd_rawmidi_init(struct snd_rawmidi *rmidi,
+		     struct snd_card *card, char *id, int device,
+		     int output_count, int input_count,
+		     unsigned int info_flags)
+{
+	int err;
+	static const struct snd_device_ops ops = {
+		.dev_free = snd_rawmidi_dev_free,
+		.dev_register = snd_rawmidi_dev_register,
+		.dev_disconnect = snd_rawmidi_dev_disconnect,
+	};
+
+	rmidi->card = card;
+	rmidi->device = device;
+	mutex_init(&rmidi->open_mutex);
+	init_waitqueue_head(&rmidi->open_wait);
+	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
+	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
+	rmidi->info_flags = info_flags;
+
+	if (id != NULL)
+		strscpy(rmidi->id, id, sizeof(rmidi->id));
+
+	snd_device_initialize(&rmidi->dev, card);
+	rmidi->dev.release = release_rawmidi_device;
+	if (rawmidi_is_ump(rmidi))
+		dev_set_name(&rmidi->dev, "umpC%iD%i", card->number, device);
+	else
+		dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);
+
+	err = snd_rawmidi_alloc_substreams(rmidi,
+					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+					   SNDRV_RAWMIDI_STREAM_INPUT,
+					   input_count);
+	if (err < 0)
+		return err;
+	err = snd_rawmidi_alloc_substreams(rmidi,
+					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+					   SNDRV_RAWMIDI_STREAM_OUTPUT,
+					   output_count);
+	if (err < 0)
+		return err;
+	err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
+	if (err < 0)
+		return err;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_rawmidi_init);
+
 /**
  * snd_rawmidi_new - create a rawmidi instance
  * @card: the card instance
@@ -1819,56 +1908,21 @@  int snd_rawmidi_new(struct snd_card *card, char *id, int device,
 {
 	struct snd_rawmidi *rmidi;
 	int err;
-	static const struct snd_device_ops ops = {
-		.dev_free = snd_rawmidi_dev_free,
-		.dev_register = snd_rawmidi_dev_register,
-		.dev_disconnect = snd_rawmidi_dev_disconnect,
-	};
 
-	if (snd_BUG_ON(!card))
-		return -ENXIO;
 	if (rrawmidi)
 		*rrawmidi = NULL;
 	rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
 	if (!rmidi)
 		return -ENOMEM;
-	rmidi->card = card;
-	rmidi->device = device;
-	mutex_init(&rmidi->open_mutex);
-	init_waitqueue_head(&rmidi->open_wait);
-	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
-	INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
-
-	if (id != NULL)
-		strscpy(rmidi->id, id, sizeof(rmidi->id));
-
-	snd_device_initialize(&rmidi->dev, card);
-	rmidi->dev.release = release_rawmidi_device;
-	dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device);
-
-	err = snd_rawmidi_alloc_substreams(rmidi,
-					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
-					   SNDRV_RAWMIDI_STREAM_INPUT,
-					   input_count);
-	if (err < 0)
-		goto error;
-	err = snd_rawmidi_alloc_substreams(rmidi,
-					   &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
-					   SNDRV_RAWMIDI_STREAM_OUTPUT,
-					   output_count);
-	if (err < 0)
-		goto error;
-	err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
-	if (err < 0)
-		goto error;
-
+	err = snd_rawmidi_init(rmidi, card, id, device,
+			       output_count, input_count, 0);
+	if (err < 0) {
+		snd_rawmidi_free(rmidi);
+		return err;
+	}
 	if (rrawmidi)
 		*rrawmidi = rmidi;
 	return 0;
-
- error:
-	snd_rawmidi_free(rmidi);
-	return err;
 }
 EXPORT_SYMBOL(snd_rawmidi_new);
 
@@ -1883,7 +1937,8 @@  static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
 	}
 }
 
-static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
+/* called from ump.c, too */
+int snd_rawmidi_free(struct snd_rawmidi *rmidi)
 {
 	if (!rmidi)
 		return 0;
@@ -1900,6 +1955,7 @@  static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
 	put_device(&rmidi->dev);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_rawmidi_free);
 
 static int snd_rawmidi_dev_free(struct snd_device *device)
 {
@@ -1950,7 +2006,8 @@  static int snd_rawmidi_dev_register(struct snd_device *device)
 	}
 #ifdef CONFIG_SND_OSSEMUL
 	rmidi->ossreg = 0;
-	if ((int)rmidi->device == midi_map[rmidi->card->number]) {
+	if (!rawmidi_is_ump(rmidi) &&
+	    (int)rmidi->device == midi_map[rmidi->card->number]) {
 		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
 					    rmidi->card, 0, &snd_rawmidi_f_ops,
 					    rmidi) < 0) {
@@ -1964,7 +2021,8 @@  static int snd_rawmidi_dev_register(struct snd_device *device)
 #endif
 		}
 	}
-	if ((int)rmidi->device == amidi_map[rmidi->card->number]) {
+	if (!rawmidi_is_ump(rmidi) &&
+	    (int)rmidi->device == amidi_map[rmidi->card->number]) {
 		if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
 					    rmidi->card, 1, &snd_rawmidi_f_ops,
 					    rmidi) < 0) {
@@ -1988,7 +2046,8 @@  static int snd_rawmidi_dev_register(struct snd_device *device)
 	}
 	rmidi->proc_entry = entry;
 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
-	if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
+	/* no own registration mechanism? */
+	if (!rmidi->ops || !rmidi->ops->dev_register) {
 		if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
 			rmidi->seq_dev->private_data = rmidi;
 			rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 68a93443583c..b81b30d82f88 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -111,6 +111,10 @@  static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
 	case SNDRV_RAWMIDI_IOCTL_INFO:
 	case SNDRV_RAWMIDI_IOCTL_DROP:
 	case SNDRV_RAWMIDI_IOCTL_DRAIN:
+#if IS_ENABLED(CONFIG_SND_UMP)
+	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+	case SNDRV_UMP_IOCTL_BLOCK_INFO:
+#endif
 		return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
 	case SNDRV_RAWMIDI_IOCTL_PARAMS32:
 		return snd_rawmidi_ioctl_params_compat(rfile, argp);
diff --git a/sound/core/ump.c b/sound/core/ump.c
new file mode 100644
index 000000000000..b49720e2e206
--- /dev/null
+++ b/sound/core/ump.c
@@ -0,0 +1,231 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal MIDI Packet (UMP) support
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/ump.h>
+
+#define ump_err(ump, fmt, args...)	dev_err(&(ump)->core.dev, fmt, ##args)
+#define ump_warn(ump, fmt, args...)	dev_warn(&(ump)->core.dev, fmt, ##args)
+#define ump_info(ump, fmt, args...)	dev_info(&(ump)->core.dev, fmt, ##args)
+#define ump_dbg(ump, fmt, args...)	dev_dbg(&(ump)->core.dev, fmt, ##args)
+
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
+static long snd_ump_ioctl(struct snd_rawmidi_file *rfile, unsigned int cmd,
+			  void __user *argp);
+
+static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
+	.dev_register = snd_ump_dev_register,
+	.dev_unregister = snd_ump_dev_unregister,
+	.ioctl = snd_ump_ioctl,
+};
+
+static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
+{
+	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+	struct snd_ump_block *fb;
+
+	while (!list_empty(&ump->block_list)) {
+		fb = list_first_entry(&ump->block_list, struct snd_ump_block,
+				      list);
+		list_del(&fb->list);
+		if (fb->private_free)
+			fb->private_free(fb);
+		kfree(fb);
+	}
+
+	if (ump->private_free)
+		ump->private_free(ump);
+}
+
+/**
+ * snd_ump_endpoint_new - create a UMP Endpoint object
+ * @card: the card instance
+ * @id: the id string for rawmidi
+ * @device: the device index for rawmidi
+ * @output: 1 for enabling output
+ * @input: 1 for enabling input
+ * @ump_ret: the pointer to store the new UMP instance
+ *
+ * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
+ * instance with one input and/or one output rawmidi stream (either uni-
+ * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
+ * that consist of one or multiple UMP Groups.
+ *
+ * Use snd_rawmidi_set_ops() to set the operators to the new instance.
+ * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
+ * depending on the given @output and @input.
+ *
+ * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
+ * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
+ * created.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
+			 int output, int input,
+			 struct snd_ump_endpoint **ump_ret)
+{
+	unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
+	struct snd_ump_endpoint *ump;
+	int err;
+
+	if (input)
+		info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+	if (output)
+		info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+	if (input && output)
+		info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	ump = kzalloc(sizeof(*ump), GFP_KERNEL);
+	if (!ump)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&ump->block_list);
+	err = snd_rawmidi_init(&ump->core, card, id, device,
+			       output, input, info_flags);
+	if (err < 0) {
+		snd_rawmidi_free(&ump->core);
+		return err;
+	}
+
+	ump->info.card = card->number;
+	ump->info.device = device;
+
+	ump->core.private_free = snd_ump_endpoint_free;
+	ump->core.ops = &snd_ump_rawmidi_ops;
+
+	ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
+	*ump_ret = ump;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
+
+/*
+ * Device register / unregister hooks;
+ *  do nothing, placeholders for avoiding the default rawmidi handling
+ */
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
+{
+	return 0;
+}
+
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
+{
+	return 0;
+}
+
+static struct snd_ump_block *
+snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
+{
+	struct snd_ump_block *fb;
+
+	list_for_each_entry(fb, &ump->block_list, list) {
+		if (fb->info.block_id == id)
+			return fb;
+	}
+	return NULL;
+}
+
+/**
+ * snd_ump_block_new - Create a UMP block
+ * @ump: UMP object
+ * @blk: block ID number to create
+ * @direction: direction (in/out/bidirection)
+ * @first_group: the first group ID (0-based)
+ * @num_groups: the number of groups in this block
+ * @blk_ret: the pointer to store the resultant block object
+ */
+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)
+{
+	struct snd_ump_block *fb, *p;
+
+	if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS)
+		return -EINVAL;
+
+	if (snd_ump_get_block(ump, blk))
+		return -EBUSY;
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (!fb)
+		return -ENOMEM;
+
+	fb->ump = ump;
+	fb->info.card = ump->info.card;
+	fb->info.device = ump->info.device;
+	fb->info.block_id = blk;
+	if (blk >= ump->info.num_blocks)
+		ump->info.num_blocks = blk + 1;
+	fb->info.direction = direction;
+	fb->info.active = 1;
+	fb->info.first_group = first_group;
+	fb->info.num_groups = num_groups;
+	/* fill the default name, may be overwritten to a better name */
+	snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d",
+		 first_group + 1, first_group + num_groups);
+
+	/* put the entry in the ordered list */
+	list_for_each_entry(p, &ump->block_list, list) {
+		if (p->info.block_id > blk) {
+			list_add_tail(&fb->list, &p->list);
+			goto added;
+		}
+	}
+	list_add_tail(&fb->list, &ump->block_list);
+
+ added:
+	ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
+	*blk_ret = fb;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_block_new);
+
+static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
+			       struct snd_ump_block_info __user *argp)
+{
+	struct snd_ump_block *fb;
+	unsigned char id;
+
+	if (get_user(id, &argp->block_id))
+		return -EFAULT;
+	fb = snd_ump_get_block(ump, id);
+	if (!fb)
+		return -ENOENT;
+	if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
+		return -EFAULT;
+	return 0;
+}
+
+/*
+ * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
+ */
+static long snd_ump_ioctl(struct snd_rawmidi_file *rfile, unsigned int cmd,
+			  void __user *argp)
+{
+	struct snd_rawmidi *rmidi = rfile->rmidi;
+	struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+
+	switch (cmd) {
+	case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+		if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
+			return -EFAULT;
+		return 0;
+	case SNDRV_UMP_IOCTL_BLOCK_INFO:
+		return snd_ump_ioctl_block(ump, argp);
+	default:
+		ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
+		return -ENOTTY;
+	}
+}
+
+MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
+MODULE_LICENSE("GPL");