Commit e3a8a5b7 authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: rawmidi: UMP support



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.

Reviewed-by: default avatarJaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent fb3bd121
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -63,6 +63,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;
@@ -148,6 +149,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,

include/sound/ump.h

0 → 100644
+118 −0
Original line number Diff line number Diff line
/* 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 */
+56 −1
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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

+2 −0
Original line number Diff line number Diff line
@@ -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/
Loading