Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230523075358.9672-4-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+529 -49
+8
include/sound/rawmidi.h
··· 63 63 size_t avail_min; /* min avail for wakeup */ 64 64 size_t avail; /* max used buffer for wakeup */ 65 65 size_t xruns; /* over/underruns counter */ 66 + size_t align; /* alignment (0 = byte stream, 3 = UMP) */ 66 67 int buffer_ref; /* buffer reference count */ 67 68 /* misc */ 68 69 wait_queue_head_t sleep; ··· 148 147 struct snd_rawmidi **rmidi); 149 148 void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, 150 149 const struct snd_rawmidi_ops *ops); 150 + 151 + /* internal */ 152 + int snd_rawmidi_init(struct snd_rawmidi *rmidi, 153 + struct snd_card *card, char *id, int device, 154 + int output_count, int input_count, 155 + unsigned int info_flags); 156 + int snd_rawmidi_free(struct snd_rawmidi *rmidi); 151 157 152 158 /* callbacks */ 153 159
+118
include/sound/ump.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Universal MIDI Packet (UMP) Support 4 + */ 5 + #ifndef __SOUND_UMP_H 6 + #define __SOUND_UMP_H 7 + 8 + #include <sound/rawmidi.h> 9 + 10 + struct snd_ump_endpoint; 11 + struct snd_ump_block; 12 + 13 + struct snd_ump_endpoint { 14 + struct snd_rawmidi core; /* raw UMP access */ 15 + 16 + struct snd_ump_endpoint_info info; 17 + 18 + void *private_data; 19 + void (*private_free)(struct snd_ump_endpoint *ump); 20 + 21 + struct list_head block_list; /* list of snd_ump_block objects */ 22 + }; 23 + 24 + struct snd_ump_block { 25 + struct snd_ump_block_info info; 26 + struct snd_ump_endpoint *ump; 27 + 28 + void *private_data; 29 + void (*private_free)(struct snd_ump_block *blk); 30 + 31 + struct list_head list; 32 + }; 33 + 34 + #define rawmidi_to_ump(rmidi) container_of(rmidi, struct snd_ump_endpoint, core) 35 + 36 + int snd_ump_endpoint_new(struct snd_card *card, char *id, int device, 37 + int output, int input, 38 + struct snd_ump_endpoint **ump_ret); 39 + int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, 40 + unsigned int direction, unsigned int first_group, 41 + unsigned int num_groups, struct snd_ump_block **blk_ret); 42 + 43 + /* 44 + * Some definitions for UMP 45 + */ 46 + 47 + /* MIDI 2.0 Message Type */ 48 + enum { 49 + UMP_MSG_TYPE_UTILITY = 0x00, 50 + UMP_MSG_TYPE_SYSTEM = 0x01, 51 + UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE = 0x02, 52 + UMP_MSG_TYPE_DATA = 0x03, 53 + UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04, 54 + UMP_MSG_TYPE_EXTENDED_DATA = 0x05, 55 + }; 56 + 57 + /* MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */ 58 + enum { 59 + UMP_SYSEX_STATUS_SINGLE = 0, 60 + UMP_SYSEX_STATUS_START = 1, 61 + UMP_SYSEX_STATUS_CONTINUE = 2, 62 + UMP_SYSEX_STATUS_END = 3, 63 + }; 64 + 65 + /* 66 + * Helpers for retrieving / filling bits from UMP 67 + */ 68 + /* get the message type (4bit) from a UMP packet (header) */ 69 + static inline unsigned char ump_message_type(u32 data) 70 + { 71 + return data >> 28; 72 + } 73 + 74 + /* get the group number (0-based, 4bit) from a UMP packet (header) */ 75 + static inline unsigned char ump_message_group(u32 data) 76 + { 77 + return (data >> 24) & 0x0f; 78 + } 79 + 80 + /* get the MIDI status code (4bit) from a UMP packet (header) */ 81 + static inline unsigned char ump_message_status_code(u32 data) 82 + { 83 + return (data >> 20) & 0x0f; 84 + } 85 + 86 + /* get the MIDI channel number (0-based, 4bit) from a UMP packet (header) */ 87 + static inline unsigned char ump_message_channel(u32 data) 88 + { 89 + return (data >> 16) & 0x0f; 90 + } 91 + 92 + /* get the MIDI status + channel combo byte (8bit) from a UMP packet (header) */ 93 + static inline unsigned char ump_message_status_channel(u32 data) 94 + { 95 + return (data >> 16) & 0xff; 96 + } 97 + 98 + /* compose a UMP packet (header) from type, group and status values */ 99 + static inline u32 ump_compose(unsigned char type, unsigned char group, 100 + unsigned char status, unsigned char channel) 101 + { 102 + return ((u32)type << 28) | ((u32)group << 24) | ((u32)status << 20) | 103 + ((u32)channel << 16); 104 + } 105 + 106 + /* get SysEx message status (for both 7 and 8bits) from a UMP packet (header) */ 107 + static inline unsigned char ump_sysex_message_status(u32 data) 108 + { 109 + return (data >> 20) & 0xf; 110 + } 111 + 112 + /* get SysEx message length (for both 7 and 8bits) from a UMP packet (header) */ 113 + static inline unsigned char ump_sysex_message_length(u32 data) 114 + { 115 + return (data >> 16) & 0xf; 116 + } 117 + 118 + #endif /* __SOUND_UMP_H */
+56 -1
include/uapi/sound/asound.h
··· 708 708 * Raw MIDI section - /dev/snd/midi?? 709 709 */ 710 710 711 - #define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2) 711 + #define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 3) 712 712 713 713 enum { 714 714 SNDRV_RAWMIDI_STREAM_OUTPUT = 0, ··· 719 719 #define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 720 720 #define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 721 721 #define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 722 + #define SNDRV_RAWMIDI_INFO_UMP 0x00000008 722 723 723 724 struct snd_rawmidi_info { 724 725 unsigned int device; /* RO/WR (control): device number */ ··· 780 779 }; 781 780 #endif 782 781 782 + /* UMP EP Protocol / JRTS capability bits */ 783 + #define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 784 + #define SNDRV_UMP_EP_INFO_PROTO_MIDI1 0x0100 /* MIDI 1.0 */ 785 + #define SNDRV_UMP_EP_INFO_PROTO_MIDI2 0x0200 /* MIDI 2.0 */ 786 + #define SNDRV_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003 787 + #define SNDRV_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 /* JRTS Transmit */ 788 + #define SNDRV_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 /* JRTS Receive */ 789 + 790 + /* UMP Endpoint information */ 791 + struct snd_ump_endpoint_info { 792 + int card; /* card number */ 793 + int device; /* device number */ 794 + unsigned int flags; /* additional info */ 795 + unsigned int protocol_caps; /* protocol capabilities */ 796 + unsigned int protocol; /* current protocol */ 797 + unsigned int num_blocks; /* # of function blocks */ 798 + unsigned short version; /* UMP major/minor version */ 799 + unsigned short padding[7]; 800 + unsigned char name[128]; /* endpoint name string */ 801 + unsigned char product_id[128]; /* unique product id string */ 802 + unsigned char reserved[32]; 803 + } __packed; 804 + 805 + /* UMP direction */ 806 + #define SNDRV_UMP_DIR_INPUT 0x01 807 + #define SNDRV_UMP_DIR_OUTPUT 0x02 808 + #define SNDRV_UMP_DIR_BIDIRECTION 0x03 809 + 810 + /* UMP block info flags */ 811 + #define SNDRV_UMP_BLOCK_IS_MIDI1 (1U << 0) /* MIDI 1.0 port w/o restrict */ 812 + #define SNDRV_UMP_BLOCK_IS_LOWSPEED (1U << 1) /* 31.25Kbps B/W MIDI1 port */ 813 + 814 + /* UMP groups and blocks */ 815 + #define SNDRV_UMP_MAX_GROUPS 16 816 + #define SNDRV_UMP_MAX_BLOCKS 32 817 + 818 + /* UMP Block information */ 819 + struct snd_ump_block_info { 820 + int card; /* card number */ 821 + int device; /* device number */ 822 + unsigned char block_id; /* block ID (R/W) */ 823 + unsigned char direction; /* UMP direction */ 824 + unsigned char active; /* Activeness */ 825 + unsigned char first_group; /* first group ID */ 826 + unsigned char num_groups; /* number of groups */ 827 + unsigned char padding[3]; 828 + unsigned int flags; /* various info flags */ 829 + unsigned char name[128]; /* block name string */ 830 + unsigned char reserved[32]; 831 + } __packed; 832 + 783 833 #define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) 784 834 #define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) 785 835 #define SNDRV_RAWMIDI_IOCTL_USER_PVERSION _IOW('W', 0x02, int) ··· 838 786 #define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) 839 787 #define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) 840 788 #define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int) 789 + /* Additional ioctls for UMP rawmidi devices */ 790 + #define SNDRV_UMP_IOCTL_ENDPOINT_INFO _IOR('W', 0x40, struct snd_ump_endpoint_info) 791 + #define SNDRV_UMP_IOCTL_BLOCK_INFO _IOR('W', 0x41, struct snd_ump_block_info) 841 792 842 793 /* 843 794 * Timer section - /dev/snd/timer
+4
sound/core/Kconfig
··· 26 26 tristate 27 27 select SND_SEQ_DEVICE if SND_SEQUENCER != n 28 28 29 + config SND_UMP 30 + tristate 31 + select SND_RAWMIDI 32 + 29 33 config SND_COMPRESS_OFFLOAD 30 34 tristate 31 35
+2
sound/core/Makefile
··· 28 28 29 29 snd-ctl-led-objs := control_led.o 30 30 snd-rawmidi-objs := rawmidi.o 31 + snd-ump-objs := ump.o 31 32 snd-timer-objs := timer.o 32 33 snd-hrtimer-objs := hrtimer.o 33 34 snd-rtctimer-objs := rtctimer.o ··· 46 45 obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o 47 46 obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o 48 47 obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o 48 + obj-$(CONFIG_SND_UMP) += snd-ump.o 49 49 50 50 obj-$(CONFIG_SND_OSSEMUL) += oss/ 51 51 obj-$(CONFIG_SND_SEQUENCER) += seq/
+107 -48
sound/core/rawmidi.c
··· 21 21 #include <sound/control.h> 22 22 #include <sound/minors.h> 23 23 #include <sound/initval.h> 24 + #include <sound/ump.h> 24 25 25 26 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 26 27 MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA."); ··· 36 35 MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); 37 36 #endif /* CONFIG_SND_OSSEMUL */ 38 37 39 - static int snd_rawmidi_free(struct snd_rawmidi *rmidi); 40 38 static int snd_rawmidi_dev_free(struct snd_device *device); 41 39 static int snd_rawmidi_dev_register(struct snd_device *device); 42 40 static int snd_rawmidi_dev_disconnect(struct snd_device *device); ··· 72 72 }; 73 73 74 74 #define SNDRV_RAWMIDI_IOCTL_STATUS64 _IOWR('W', 0x20, struct snd_rawmidi_status64) 75 + 76 + #define rawmidi_is_ump(rmidi) \ 77 + (IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP)) 75 78 76 79 static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device) 77 80 { ··· 184 181 } 185 182 runtime->appl_ptr = runtime->hw_ptr = 0; 186 183 substream->runtime = runtime; 184 + if (rawmidi_is_ump(substream->rmidi)) 185 + runtime->align = 3; 187 186 return 0; 188 187 } 188 + 189 + /* get the current alignment (either 0 or 3) */ 190 + static inline int get_align(struct snd_rawmidi_runtime *runtime) 191 + { 192 + if (IS_ENABLED(CONFIG_SND_UMP)) 193 + return runtime->align; 194 + else 195 + return 0; 196 + } 197 + 198 + /* get the trimmed size with the current alignment */ 199 + #define get_aligned_size(runtime, size) ((size) & ~get_align(runtime)) 189 200 190 201 static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream) 191 202 { ··· 738 721 return -EINVAL; 739 722 if (params->avail_min < 1 || params->avail_min > params->buffer_size) 740 723 return -EINVAL; 724 + if (params->buffer_size & get_align(runtime)) 725 + return -EINVAL; 741 726 if (params->buffer_size != runtime->buffer_size) { 742 727 newbuf = kvzalloc(params->buffer_size, GFP_KERNEL); 743 728 if (!newbuf) ··· 1065 1046 struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec }; 1066 1047 int orig_count = src_count; 1067 1048 int frame_size = sizeof(struct snd_rawmidi_framing_tstamp); 1049 + int align = get_align(runtime); 1068 1050 1069 1051 BUILD_BUG_ON(frame_size != 0x20); 1070 1052 if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0)) 1071 1053 return -EINVAL; 1072 1054 1073 - while (src_count > 0) { 1055 + while (src_count > align) { 1074 1056 if ((int)(runtime->buffer_size - runtime->avail) < frame_size) { 1075 1057 runtime->xruns += src_count; 1076 1058 break; ··· 1079 1059 if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH) 1080 1060 frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH; 1081 1061 else { 1082 - frame.length = src_count; 1062 + frame.length = get_aligned_size(runtime, src_count); 1063 + if (!frame.length) 1064 + break; 1083 1065 memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH); 1084 1066 } 1085 1067 memcpy(frame.data, buffer, frame.length); ··· 1145 1123 goto unlock; 1146 1124 } 1147 1125 1126 + count = get_aligned_size(runtime, count); 1127 + if (!count) 1128 + goto unlock; 1129 + 1148 1130 if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) { 1149 1131 result = receive_with_tstamp_framing(substream, buffer, count, &ts64); 1150 1132 } else if (count == 1) { /* special case, faster code */ ··· 1168 1142 count1 = count; 1169 1143 if (count1 > (int)(runtime->buffer_size - runtime->avail)) 1170 1144 count1 = runtime->buffer_size - runtime->avail; 1145 + count1 = get_aligned_size(runtime, count1); 1146 + if (!count1) 1147 + goto unlock; 1171 1148 memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1); 1172 1149 runtime->hw_ptr += count1; 1173 1150 runtime->hw_ptr %= runtime->buffer_size; ··· 1371 1342 count1 = count; 1372 1343 if (count1 > (int)(runtime->buffer_size - runtime->avail)) 1373 1344 count1 = runtime->buffer_size - runtime->avail; 1345 + count1 = get_aligned_size(runtime, count1); 1346 + if (!count1) 1347 + goto __skip; 1374 1348 memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1); 1375 1349 count -= count1; 1376 1350 result += count1; 1377 1351 if (count > 0) { 1378 1352 if (count > (int)(runtime->buffer_size - runtime->avail - count1)) 1379 1353 count = runtime->buffer_size - runtime->avail - count1; 1354 + count = get_aligned_size(runtime, count); 1355 + if (!count) 1356 + goto __skip; 1380 1357 memcpy(buffer + count1, runtime->buffer, count); 1381 1358 result += count; 1382 1359 } ··· 1439 1404 return -EINVAL; 1440 1405 } 1441 1406 snd_BUG_ON(runtime->avail + count > runtime->buffer_size); 1407 + count = get_aligned_size(runtime, count); 1442 1408 runtime->hw_ptr += count; 1443 1409 runtime->hw_ptr %= runtime->buffer_size; 1444 1410 runtime->avail += count; ··· 1726 1690 1727 1691 rmidi = entry->private_data; 1728 1692 snd_iprintf(buffer, "%s\n\n", rmidi->name); 1693 + if (IS_ENABLED(CONFIG_SND_UMP)) 1694 + snd_iprintf(buffer, "Type: %s\n", 1695 + rawmidi_is_ump(rmidi) ? "UMP" : "Legacy"); 1729 1696 mutex_lock(&rmidi->open_mutex); 1730 1697 if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { 1731 1698 list_for_each_entry(substream, ··· 1839 1800 kfree(container_of(dev, struct snd_rawmidi, dev)); 1840 1801 } 1841 1802 1803 + /* used for both rawmidi and ump */ 1804 + int snd_rawmidi_init(struct snd_rawmidi *rmidi, 1805 + struct snd_card *card, char *id, int device, 1806 + int output_count, int input_count, 1807 + unsigned int info_flags) 1808 + { 1809 + int err; 1810 + static const struct snd_device_ops ops = { 1811 + .dev_free = snd_rawmidi_dev_free, 1812 + .dev_register = snd_rawmidi_dev_register, 1813 + .dev_disconnect = snd_rawmidi_dev_disconnect, 1814 + }; 1815 + 1816 + rmidi->card = card; 1817 + rmidi->device = device; 1818 + mutex_init(&rmidi->open_mutex); 1819 + init_waitqueue_head(&rmidi->open_wait); 1820 + INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams); 1821 + INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams); 1822 + rmidi->info_flags = info_flags; 1823 + 1824 + if (id != NULL) 1825 + strscpy(rmidi->id, id, sizeof(rmidi->id)); 1826 + 1827 + snd_device_initialize(&rmidi->dev, card); 1828 + rmidi->dev.release = release_rawmidi_device; 1829 + if (rawmidi_is_ump(rmidi)) 1830 + dev_set_name(&rmidi->dev, "umpC%iD%i", card->number, device); 1831 + else 1832 + dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device); 1833 + 1834 + err = snd_rawmidi_alloc_substreams(rmidi, 1835 + &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], 1836 + SNDRV_RAWMIDI_STREAM_INPUT, 1837 + input_count); 1838 + if (err < 0) 1839 + return err; 1840 + err = snd_rawmidi_alloc_substreams(rmidi, 1841 + &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], 1842 + SNDRV_RAWMIDI_STREAM_OUTPUT, 1843 + output_count); 1844 + if (err < 0) 1845 + return err; 1846 + err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops); 1847 + if (err < 0) 1848 + return err; 1849 + return 0; 1850 + } 1851 + EXPORT_SYMBOL_GPL(snd_rawmidi_init); 1852 + 1842 1853 /** 1843 1854 * snd_rawmidi_new - create a rawmidi instance 1844 1855 * @card: the card instance ··· 1909 1820 { 1910 1821 struct snd_rawmidi *rmidi; 1911 1822 int err; 1912 - static const struct snd_device_ops ops = { 1913 - .dev_free = snd_rawmidi_dev_free, 1914 - .dev_register = snd_rawmidi_dev_register, 1915 - .dev_disconnect = snd_rawmidi_dev_disconnect, 1916 - }; 1917 1823 1918 - if (snd_BUG_ON(!card)) 1919 - return -ENXIO; 1920 1824 if (rrawmidi) 1921 1825 *rrawmidi = NULL; 1922 1826 rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); 1923 1827 if (!rmidi) 1924 1828 return -ENOMEM; 1925 - rmidi->card = card; 1926 - rmidi->device = device; 1927 - mutex_init(&rmidi->open_mutex); 1928 - init_waitqueue_head(&rmidi->open_wait); 1929 - INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams); 1930 - INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams); 1931 - 1932 - if (id != NULL) 1933 - strscpy(rmidi->id, id, sizeof(rmidi->id)); 1934 - 1935 - snd_device_initialize(&rmidi->dev, card); 1936 - rmidi->dev.release = release_rawmidi_device; 1937 - dev_set_name(&rmidi->dev, "midiC%iD%i", card->number, device); 1938 - 1939 - err = snd_rawmidi_alloc_substreams(rmidi, 1940 - &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT], 1941 - SNDRV_RAWMIDI_STREAM_INPUT, 1942 - input_count); 1943 - if (err < 0) 1944 - goto error; 1945 - err = snd_rawmidi_alloc_substreams(rmidi, 1946 - &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT], 1947 - SNDRV_RAWMIDI_STREAM_OUTPUT, 1948 - output_count); 1949 - if (err < 0) 1950 - goto error; 1951 - err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops); 1952 - if (err < 0) 1953 - goto error; 1954 - 1829 + err = snd_rawmidi_init(rmidi, card, id, device, 1830 + output_count, input_count, 0); 1831 + if (err < 0) { 1832 + snd_rawmidi_free(rmidi); 1833 + return err; 1834 + } 1955 1835 if (rrawmidi) 1956 1836 *rrawmidi = rmidi; 1957 1837 return 0; 1958 - 1959 - error: 1960 - snd_rawmidi_free(rmidi); 1961 - return err; 1962 1838 } 1963 1839 EXPORT_SYMBOL(snd_rawmidi_new); 1964 1840 ··· 1938 1884 } 1939 1885 } 1940 1886 1941 - static int snd_rawmidi_free(struct snd_rawmidi *rmidi) 1887 + /* called from ump.c, too */ 1888 + int snd_rawmidi_free(struct snd_rawmidi *rmidi) 1942 1889 { 1943 1890 if (!rmidi) 1944 1891 return 0; ··· 1956 1901 put_device(&rmidi->dev); 1957 1902 return 0; 1958 1903 } 1904 + EXPORT_SYMBOL_GPL(snd_rawmidi_free); 1959 1905 1960 1906 static int snd_rawmidi_dev_free(struct snd_device *device) 1961 1907 { ··· 2007 1951 } 2008 1952 #ifdef CONFIG_SND_OSSEMUL 2009 1953 rmidi->ossreg = 0; 2010 - if ((int)rmidi->device == midi_map[rmidi->card->number]) { 1954 + if (!rawmidi_is_ump(rmidi) && 1955 + (int)rmidi->device == midi_map[rmidi->card->number]) { 2011 1956 if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, 2012 1957 rmidi->card, 0, &snd_rawmidi_f_ops, 2013 1958 rmidi) < 0) { ··· 2022 1965 #endif 2023 1966 } 2024 1967 } 2025 - if ((int)rmidi->device == amidi_map[rmidi->card->number]) { 1968 + if (!rawmidi_is_ump(rmidi) && 1969 + (int)rmidi->device == amidi_map[rmidi->card->number]) { 2026 1970 if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI, 2027 1971 rmidi->card, 1, &snd_rawmidi_f_ops, 2028 1972 rmidi) < 0) { ··· 2047 1989 } 2048 1990 rmidi->proc_entry = entry; 2049 1991 #if IS_ENABLED(CONFIG_SND_SEQUENCER) 2050 - if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ 1992 + /* no own registration mechanism? */ 1993 + if (!rmidi->ops || !rmidi->ops->dev_register) { 2051 1994 if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { 2052 1995 rmidi->seq_dev->private_data = rmidi; 2053 1996 rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
+4
sound/core/rawmidi_compat.c
··· 111 111 case SNDRV_RAWMIDI_IOCTL_INFO: 112 112 case SNDRV_RAWMIDI_IOCTL_DROP: 113 113 case SNDRV_RAWMIDI_IOCTL_DRAIN: 114 + #if IS_ENABLED(CONFIG_SND_UMP) 115 + case SNDRV_UMP_IOCTL_ENDPOINT_INFO: 116 + case SNDRV_UMP_IOCTL_BLOCK_INFO: 117 + #endif 114 118 return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp); 115 119 case SNDRV_RAWMIDI_IOCTL_PARAMS32: 116 120 return snd_rawmidi_ioctl_params_compat(rfile, argp);
+230
sound/core/ump.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Universal MIDI Packet (UMP) support 4 + */ 5 + 6 + #include <linux/list.h> 7 + #include <linux/slab.h> 8 + #include <linux/module.h> 9 + #include <linux/export.h> 10 + #include <linux/mm.h> 11 + #include <sound/core.h> 12 + #include <sound/rawmidi.h> 13 + #include <sound/ump.h> 14 + 15 + #define ump_err(ump, fmt, args...) dev_err(&(ump)->core.dev, fmt, ##args) 16 + #define ump_warn(ump, fmt, args...) dev_warn(&(ump)->core.dev, fmt, ##args) 17 + #define ump_info(ump, fmt, args...) dev_info(&(ump)->core.dev, fmt, ##args) 18 + #define ump_dbg(ump, fmt, args...) dev_dbg(&(ump)->core.dev, fmt, ##args) 19 + 20 + static int snd_ump_dev_register(struct snd_rawmidi *rmidi); 21 + static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi); 22 + static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd, 23 + void __user *argp); 24 + 25 + static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = { 26 + .dev_register = snd_ump_dev_register, 27 + .dev_unregister = snd_ump_dev_unregister, 28 + .ioctl = snd_ump_ioctl, 29 + }; 30 + 31 + static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi) 32 + { 33 + struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi); 34 + struct snd_ump_block *fb; 35 + 36 + while (!list_empty(&ump->block_list)) { 37 + fb = list_first_entry(&ump->block_list, struct snd_ump_block, 38 + list); 39 + list_del(&fb->list); 40 + if (fb->private_free) 41 + fb->private_free(fb); 42 + kfree(fb); 43 + } 44 + 45 + if (ump->private_free) 46 + ump->private_free(ump); 47 + } 48 + 49 + /** 50 + * snd_ump_endpoint_new - create a UMP Endpoint object 51 + * @card: the card instance 52 + * @id: the id string for rawmidi 53 + * @device: the device index for rawmidi 54 + * @output: 1 for enabling output 55 + * @input: 1 for enabling input 56 + * @ump_ret: the pointer to store the new UMP instance 57 + * 58 + * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi 59 + * instance with one input and/or one output rawmidi stream (either uni- 60 + * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks 61 + * that consist of one or multiple UMP Groups. 62 + * 63 + * Use snd_rawmidi_set_ops() to set the operators to the new instance. 64 + * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself 65 + * depending on the given @output and @input. 66 + * 67 + * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device 68 + * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is 69 + * created. 70 + * 71 + * Return: Zero if successful, or a negative error code on failure. 72 + */ 73 + int snd_ump_endpoint_new(struct snd_card *card, char *id, int device, 74 + int output, int input, 75 + struct snd_ump_endpoint **ump_ret) 76 + { 77 + unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP; 78 + struct snd_ump_endpoint *ump; 79 + int err; 80 + 81 + if (input) 82 + info_flags |= SNDRV_RAWMIDI_INFO_INPUT; 83 + if (output) 84 + info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; 85 + if (input && output) 86 + info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; 87 + 88 + ump = kzalloc(sizeof(*ump), GFP_KERNEL); 89 + if (!ump) 90 + return -ENOMEM; 91 + INIT_LIST_HEAD(&ump->block_list); 92 + err = snd_rawmidi_init(&ump->core, card, id, device, 93 + output, input, info_flags); 94 + if (err < 0) { 95 + snd_rawmidi_free(&ump->core); 96 + return err; 97 + } 98 + 99 + ump->info.card = card->number; 100 + ump->info.device = device; 101 + 102 + ump->core.private_free = snd_ump_endpoint_free; 103 + ump->core.ops = &snd_ump_rawmidi_ops; 104 + 105 + ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id); 106 + *ump_ret = ump; 107 + return 0; 108 + } 109 + EXPORT_SYMBOL_GPL(snd_ump_endpoint_new); 110 + 111 + /* 112 + * Device register / unregister hooks; 113 + * do nothing, placeholders for avoiding the default rawmidi handling 114 + */ 115 + static int snd_ump_dev_register(struct snd_rawmidi *rmidi) 116 + { 117 + return 0; 118 + } 119 + 120 + static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi) 121 + { 122 + return 0; 123 + } 124 + 125 + static struct snd_ump_block * 126 + snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id) 127 + { 128 + struct snd_ump_block *fb; 129 + 130 + list_for_each_entry(fb, &ump->block_list, list) { 131 + if (fb->info.block_id == id) 132 + return fb; 133 + } 134 + return NULL; 135 + } 136 + 137 + /** 138 + * snd_ump_block_new - Create a UMP block 139 + * @ump: UMP object 140 + * @blk: block ID number to create 141 + * @direction: direction (in/out/bidirection) 142 + * @first_group: the first group ID (0-based) 143 + * @num_groups: the number of groups in this block 144 + * @blk_ret: the pointer to store the resultant block object 145 + */ 146 + int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk, 147 + unsigned int direction, unsigned int first_group, 148 + unsigned int num_groups, struct snd_ump_block **blk_ret) 149 + { 150 + struct snd_ump_block *fb, *p; 151 + 152 + if (blk < 0 || blk >= SNDRV_UMP_MAX_BLOCKS) 153 + return -EINVAL; 154 + 155 + if (snd_ump_get_block(ump, blk)) 156 + return -EBUSY; 157 + 158 + fb = kzalloc(sizeof(*fb), GFP_KERNEL); 159 + if (!fb) 160 + return -ENOMEM; 161 + 162 + fb->ump = ump; 163 + fb->info.card = ump->info.card; 164 + fb->info.device = ump->info.device; 165 + fb->info.block_id = blk; 166 + if (blk >= ump->info.num_blocks) 167 + ump->info.num_blocks = blk + 1; 168 + fb->info.direction = direction; 169 + fb->info.active = 1; 170 + fb->info.first_group = first_group; 171 + fb->info.num_groups = num_groups; 172 + /* fill the default name, may be overwritten to a better name */ 173 + snprintf(fb->info.name, sizeof(fb->info.name), "Group %d-%d", 174 + first_group + 1, first_group + num_groups); 175 + 176 + /* put the entry in the ordered list */ 177 + list_for_each_entry(p, &ump->block_list, list) { 178 + if (p->info.block_id > blk) { 179 + list_add_tail(&fb->list, &p->list); 180 + goto added; 181 + } 182 + } 183 + list_add_tail(&fb->list, &ump->block_list); 184 + 185 + added: 186 + ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name); 187 + *blk_ret = fb; 188 + return 0; 189 + } 190 + EXPORT_SYMBOL_GPL(snd_ump_block_new); 191 + 192 + static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump, 193 + struct snd_ump_block_info __user *argp) 194 + { 195 + struct snd_ump_block *fb; 196 + unsigned char id; 197 + 198 + if (get_user(id, &argp->block_id)) 199 + return -EFAULT; 200 + fb = snd_ump_get_block(ump, id); 201 + if (!fb) 202 + return -ENOENT; 203 + if (copy_to_user(argp, &fb->info, sizeof(fb->info))) 204 + return -EFAULT; 205 + return 0; 206 + } 207 + 208 + /* 209 + * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl() 210 + */ 211 + static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd, 212 + void __user *argp) 213 + { 214 + struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi); 215 + 216 + switch (cmd) { 217 + case SNDRV_UMP_IOCTL_ENDPOINT_INFO: 218 + if (copy_to_user(argp, &ump->info, sizeof(ump->info))) 219 + return -EFAULT; 220 + return 0; 221 + case SNDRV_UMP_IOCTL_BLOCK_INFO: 222 + return snd_ump_ioctl_block(ump, argp); 223 + default: 224 + ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd); 225 + return -ENOTTY; 226 + } 227 + } 228 + 229 + MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver"); 230 + MODULE_LICENSE("GPL");