요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=================
MIDI 2.0 on Linux
=================
General
=======
MIDI 2.0 is an extended protocol for providing higher resolutions and
more fine controls over the legacy MIDI 1.0. The fundamental changes
introduced for supporting MIDI 2.0 are:
- Support of Universal MIDI Packet (UMP)
- Support of MIDI 2.0 protocol messages
- Transparent conversions between UMP and legacy MIDI 1.0 byte stream
- MIDI-CI for property and profile configurations
UMP is a new container format to hold all MIDI protocol 1.0 and MIDI
2.0 protocol messages. Unlike the former byte stream, it's 32bit
aligned, and each message can be put in a single packet. UMP can send
the events up to 16 "UMP Groups", where each UMP Group contain up to
16 MIDI channels.
MIDI 2.0 protocol is an extended protocol to achieve the higher
resolution and more controls over the old MIDI 1.0 protocol.
MIDI-CI is a high-level protocol that can talk with the MIDI device
for the flexible profiles and configurations. It's represented in the
form of special SysEx.
For Linux implementations, the kernel supports the UMP transport and
the encoding/decoding of MIDI protocols on UMP, while MIDI-CI is
supported in user-space over the standard SysEx.
As of this writing, only USB MIDI device supports the UMP and Linux
2.0 natively. The UMP support itself is pretty generic, hence it
could be used by other transport layers, although it could be
implemented differently (e.g. as a ALSA sequencer client), too.
The access to UMP devices are provided in two ways: the access via
rawmidi device and the access via ALSA sequencer API.
ALSA sequencer API was extended to allow the payload of UMP packets.
It's allowed to connect freely between MIDI 1.0 and MIDI 2.0 sequencer
clients, and the events are converted transparently.
Kernel Configuration
====================
The following new configs are added for supporting MIDI 2.0:
`CONFIG_SND_UMP`, `CONFIG_SND_UMP_LEGACY_RAWMIDI`,
`CONFIG_SND_SEQ_UMP`, `CONFIG_SND_SEQ_UMP_CLIENT`, and
`CONFIG_SND_USB_AUDIO_MIDI_V2`. The first visible one is
`CONFIG_SND_USB_AUDIO_MIDI_V2`, and when you choose it (to set `=y`),
the core support for UMP (`CONFIG_SND_UMP`) and the sequencer binding
(`CONFIG_SND_SEQ_UMP_CLIENT`) will be automatically selected.
Additionally, `CONFIG_SND_UMP_LEGACY_RAWMIDI=y` will enable the
support for the legacy raw MIDI device for UMP Endpoints.
Rawmidi Device with USB MIDI 2.0
================================
When a device supports MIDI 2.0, the USB-audio driver probes and uses
the MIDI 2.0 interface (that is found always at the altset 1) as
default instead of the MIDI 1.0 interface (at altset 0). You can
switch back to the binding with the old MIDI 1.0 interface by passing
`midi2_enable=0` option to snd-usb-audio driver module, too.
The USB audio driver tries to query the UMP Endpoint and UMP Function
Block information that are provided since UMP v1.1, and builds up the
topology based on those information. When the device is older and
doesn't respond to the new UMP inquiries, the driver falls back and
builds the topology based on Group Terminal Block (GTB) information
from the USB descriptor. Some device might be screwed up by the
unexpected UMP command; in such a case, pass `midi2_ump_probe=0`
option to snd-usb-audio driver for skipping the UMP v1.1 inquiries.
When the MIDI 2.0 device is probed, the kernel creates a rawmidi
device for each UMP Endpoint of the device. Its device name is
`/dev/snd/umpC*D*` and different from the standard rawmidi device name
`/dev/snd/midiC*D*` for MIDI 1.0, in order to avoid confusing the
legacy applications accessing mistakenly to UMP devices.
You can read and write UMP packet data directly from/to this UMP
rawmidi device. For example, reading via `hexdump` like below will
show the incoming UMP packets of the card 0 device 0 in the hex
format::
% hexdump -C /dev/snd/umpC0D0
00000000 01 07 b0 20 00 07 b0 20 64 3c 90 20 64 3c 80 20 |... ... d<. d<. |
Unlike the MIDI 1.0 byte stream, UMP is a 32bit packet, and the size
for reading or writing the device is also aligned to 32bit (which is 4
bytes).
The 32-bit words in the UMP packet payload are always in CPU native
endianness. Transport drivers are responsible to convert UMP words
from / to system endianness to required transport endianness / byte
order.
When `CONFIG_SND_UMP_LEGACY_RAWMIDI` is set, the driver creates
another standard raw MIDI device additionally as `/dev/snd/midiC*D*`.
This contains 16 substreams, and each substream corresponds to a
(0-based) UMP Group. Legacy applications can access to the specified
group via each substream in MIDI 1.0 byte stream format. With the
ALSA rawmidi API, you can open the arbitrary substream, while just
opening `/dev/snd/midiC*D*` will end up with opening the first
substream.
Each UMP Endpoint can provide the additional information, constructed
from the information inquired via UMP 1.1 Stream messages or USB MIDI
2.0 descriptors. And a UMP Endpoint may contain one or more UMP
Blocks, where UMP Block is an abstraction introduced in the ALSA UMP
implementations to represent the associations among UMP Groups. UMP
Block corresponds to Function Block in UMP 1.1 specification. When
UMP 1.1 Function Block information isn't available, it's filled
partially from Group Terminal Block (GTB) as defined in USB MIDI 2.0
specifications.
The information of UMP Endpoints and UMP Blocks are found in the proc
file `/proc/asound/card*/midi*`. For example::
% cat /proc/asound/card1/midi0
ProtoZOA MIDI
Type: UMP
EP Name: ProtoZOA
EP Product ID: ABCD12345678
UMP Version: 0x0000
Protocol Caps: 0x00000100
Protocol: 0x00000100
Num Blocks: 3
Block 0 (ProtoZOA Main)
Direction: bidirection
Active: Yes
Groups: 1-1
Is MIDI1: No
Block 1 (ProtoZOA Ext IN)
Direction: output
Active: Yes
Groups: 2-2
Is MIDI1: Yes (Low Speed)
....
Note that `Groups` field shown in the proc file above indicates the
1-based UMP Group numbers (from-to).
Those additional UMP Endpoint and UMP Block information can be
obtained via the new ioctls `SNDRV_UMP_IOCTL_ENDPOINT_INFO` and
`SNDRV_UMP_IOCTL_BLOCK_INFO`, respectively.
The rawmidi name and the UMP Endpoint name are usually identical, and
in the case of USB MIDI, it's taken from `iInterface` of the
corresponding USB MIDI interface descriptor. If it's not provided,
it's copied from `iProduct` of the USB device descriptor as a
fallback.
The Endpoint Product ID is a string field and supposed to be unique.
It's copied from `iSerialNumber` of the device for USB MIDI.
The protocol capabilities and the actual protocol bits are defined in
`asound.h`.
ALSA Sequencer with USB MIDI 2.0
================================
In addition to the rawmidi interfaces, ALSA sequencer interface
supports the new UMP MIDI 2.0 device, too. Now, each ALSA sequencer
client may set its MIDI version (0, 1 or 2) to declare itself being
either the legacy, UMP MIDI 1.0 or UMP MIDI 2.0 device, respectively.
The first, legacy client is the one that sends/receives the old
sequencer event as was. Meanwhile, UMP MIDI 1.0 and 2.0 clients send
and receive in the extended event record for UMP. The MIDI version is
seen in the new `midi_version` field of `snd_seq_client_info`.
A UMP packet can be sent/received in a sequencer event embedded by
specifying the new event flag bit `SNDRV_SEQ_EVENT_UMP`. When this
flag is set, the event has 16 byte (128 bit) data payload for holding
the UMP packet. Without the `SNDRV_SEQ_EVENT_UMP` bit flag, the event
is treated as a legacy event as it was (with max 12 byte data
payload).
With `SNDRV_SEQ_EVENT_UMP` flag set, the type field of a UMP sequencer
event is ignored (but it should be set to 0 as default).
The type of each client can be seen in `/proc/asound/seq/clients`.
For example::
% cat /proc/asound/seq/clients
Client info
cur clients : 3
....
Client 14 : "Midi Through" [Kernel Legacy]
Port 0 : "Midi Through Port-0" (RWe-)
Client 20 : "ProtoZOA" [Kernel UMP MIDI1]
UMP Endpoint: ProtoZOA
UMP Block 0: ProtoZOA Main [Active]
Groups: 1-1
UMP Block 1: ProtoZOA Ext IN [Active]
Groups: 2-2
UMP Block 2: ProtoZOA Ext OUT [Active]
Groups: 3-3
Port 0 : "MIDI 2.0" (RWeX) [In/Out]
Port 1 : "ProtoZOA Main" (RWeX) [In/Out]
Port 2 : "ProtoZOA Ext IN" (-We-) [Out]
Port 3 : "ProtoZOA Ext OUT" (R-e-) [In]
Here you can find two types of kernel clients, "Legacy" for client 14,
and "UMP MIDI1" for client 20, which is a USB MIDI 2.0 device.
A USB MIDI 2.0 client gives always the port 0 as "MIDI 2.0" and the
rest ports from 1 for each UMP Group (e.g. port 1 for Group 1).
In this example, the device has three active groups (Main, Ext IN and
Ext OUT), and those are exposed as sequencer ports from 1 to 3.
The "MIDI 2.0" port is for a UMP Endpoint, and its difference from
other UMP Group ports is that UMP Endpoint port sends the events from
the all ports on the device ("catch-all"), while each UMP Group port
sends only the events from the given UMP Group.
Also, UMP groupless messages (such as the UMP message type 0x0f) are
sent only to the UMP Endpoint port.
Note that, although each UMP sequencer client usually creates 16
ports, those ports that don't belong to any UMP Blocks (or belonging
to inactive UMP Blocks) are marked as inactive, and they don't appear
in the proc outputs. In the example above, the sequencer ports from 4
to 16 are present but not shown there.
The proc file above shows the UMP Block information, too. The same
entry (but with more detailed information) is found in the rawmidi
proc output.
When clients are connected between different MIDI versions, the events
are translated automatically depending on the client's version, not
only between the legacy and the UMP MIDI 1.0/2.0 types, but also
between UMP MIDI 1.0 and 2.0 types, too. For example, running
`aseqdump` program on the ProtoZOA Main port in the legacy mode will
give you the output like::
% aseqdump -p 20:1
Waiting for data. Press Ctrl+C to end.
Source Event Ch Data
20:1 Note on 0, note 60, velocity 100
20:1 Note off 0, note 60, velocity 100
20:1 Control change 0, controller 11, value 4
When you run `aseqdump` in MIDI 2.0 mode, it'll receive the high
precision data like::
% aseqdump -u 2 -p 20:1
Waiting for data. Press Ctrl+C to end.
Source Event Ch Data
20:1 Note on 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Note off 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Control change 0, controller 11, value 0x2000000
while the data is automatically converted by ALSA sequencer core.
Rawmidi API Extensions
======================
* The additional UMP Endpoint information can be obtained via the new
ioctl `SNDRV_UMP_IOCTL_ENDPOINT_INFO`. It contains the associated
card and device numbers, the bit flags, the protocols, the number of
UMP Blocks, the name string of the endpoint, etc.
The protocols are specified in two field, the protocol capabilities
and the current protocol. Both contain the bit flags specifying the
MIDI protocol version (`SNDRV_UMP_EP_INFO_PROTO_MIDI1` or
`SNDRV_UMP_EP_INFO_PROTO_MIDI2`) in the upper byte and the jitter
reduction timestamp (`SNDRV_UMP_EP_INFO_PROTO_JRTS_TX` and
`SNDRV_UMP_EP_INFO_PROTO_JRTS_RX`) in the lower byte.
A UMP Endpoint may contain up to 32 UMP Blocks, and the number of
the currently assigned blocks are shown in the Endpoint information.
* Each UMP Block information can be obtained via another new ioctl
`SNDRV_UMP_IOCTL_BLOCK_INFO`. The block ID number (0-based) has to
be passed for the block to query. The received data contains the
associated the direction of the block, the first associated group ID
(0-based) and the number of groups, the name string of the block,
etc.
The direction is either `SNDRV_UMP_DIR_INPUT`,
`SNDRV_UMP_DIR_OUTPUT` or `SNDRV_UMP_DIR_BIDIRECTION`.
* For the device supports UMP v1.1, the UMP MIDI protocol can be
switched via "Stream Configuration Request" message (UMP type 0x0f,
status 0x05). When UMP core receives such a message, it updates the
UMP EP info and the corresponding sequencer clients as well.
* The legacy rawmidi device number is found in the new `tied_device`
field of the rawmidi info.
On the other hand, the UMP rawmidi device number is found in
`tied_device` field of the legacy rawmidi info, too.
* Each substream of the legacy rawmidi may be enabled / disabled
dynamically depending on the UMP FB state.
When the selected substream is inactive, it's indicated by the bit
0x10 (`SNDRV_RAWMIDI_INFO_STREAM_INACTIVE`) in the `flags` field of
the legacy rawmidi info.
Control API Extensions
======================
* The new ioctl `SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE` is introduced for
querying the next UMP rawmidi device, while the existing ioctl
`SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE` queries only the legacy
rawmidi devices.
For setting the subdevice (substream number) to be opened, use the
ioctl `SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE` like the normal
rawmidi.
* Two new ioctls `SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO` and
`SNDRV_CTL_IOCTL_UMP_BLOCK_INFO` provide the UMP Endpoint and UMP
Block information of the specified UMP device via ALSA control API
without opening the actual (UMP) rawmidi device.
The `card` field is ignored upon inquiry, always tied with the card
of the control interface.
Sequencer API Extensions
========================
* `midi_version` field is added to `snd_seq_client_info` to indicate
the current MIDI version (either 0, 1 or 2) of each client.
When `midi_version` is 1 or 2, the alignment of read from a UMP
sequencer client is also changed from the former 28 bytes to 32
bytes for the extended payload. The alignment size for the write
isn't changed, but each event size may differ depending on the new
bit flag below.
* `SNDRV_SEQ_EVENT_UMP` flag bit is added for each sequencer event
flags. When this bit flag is set, the sequencer event is extended
to have a larger payload of 16 bytes instead of the legacy 12
bytes, and the event contains the UMP packet in the payload.
* The new sequencer port type bit (`SNDRV_SEQ_PORT_TYPE_MIDI_UMP`)
indicates the port being UMP-capable.
* The sequencer ports have new capability bits to indicate the
inactive ports (`SNDRV_SEQ_PORT_CAP_INACTIVE`) and the UMP Endpoint
port (`SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT`).
* The event conversion of ALSA sequencer clients can be suppressed the
new filter bit `SNDRV_SEQ_FILTER_NO_CONVERT` set to the client info.
For example, the kernel pass-through client (`snd-seq-dummy`) sets
this flag internally.
* The port information gained the new field `direction` to indicate
the direction of the port (either `SNDRV_SEQ_PORT_DIR_INPUT`,
`SNDRV_SEQ_PORT_DIR_OUTPUT` or `SNDRV_SEQ_PORT_DIR_BIDIRECTION`).
* Another additional field for the port information is `ump_group`
which specifies the associated UMP Group Number (1-based).
When it's non-zero, the UMP group field in the UMP packet updated
upon delivery to the specified group (corrected to be 0-based).
Each sequencer port is supposed to set this field if it's a port to
specific to a certain UMP group.
* Each client may set the additional event filter for UMP Groups in
`group_filter` bitmap. The filter consists of bitmap from 1-based
Group numbers. For example, when the bit 1 is set, messages from
Group 1 (i.e. the very first group) are filtered and not delivered.
The bit 0 is used for filtering UMP groupless messages.
* Two new ioctls are added for UMP-capable clients:
`SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO` and
`SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO`. They are used to get and set
either `snd_ump_endpoint_info` or `snd_ump_block_info` data
associated with the sequencer client. The USB MIDI driver provides
those information from the underlying UMP rawmidi, while a
user-space client may provide its own data via `*_SET` ioctl.
For an Endpoint data, pass 0 to the `type` field, while for a Block
data, pass the block number + 1 to the `type` field.
Setting the data for a kernel client shall result in an error.
* With UMP 1.1, Function Block information may be changed
dynamically. When the update of Function Block is received from the
device, ALSA sequencer core changes the corresponding sequencer port
name and attributes accordingly, and notifies the changes via the
announcement to the ALSA sequencer system port, similarly like the
normal port change notification.
* There are two extended event types for notifying the UMP Endpoint and
Function Block changes via the system announcement port:
type 68 (`SNDRV_SEQ_EVENT_UMP_EP_CHANGE`) and type 69
(`SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE`). They take the new type,
`snd_seq_ev_ump_notify` in the payload, indicating the client number
and the FB number that are changed.
MIDI2 USB Gadget Function Driver
================================
The latest kernel contains the support for USB MIDI 2.0 gadget
function driver, which can be used for prototyping and debugging MIDI
2.0 features.
`CONFIG_USB_GADGET`, `CONFIG_USB_CONFIGFS` and
`CONFIG_USB_CONFIGFS_F_MIDI2` need to be enabled for the MIDI2 gadget
driver.
In addition, for using a gadget driver, you need a working UDC driver.
In the example below, we use `dummy_hcd` driver (enabled via
`CONFIG_USB_DUMMY_HCD`) that is available on PC and VM for debugging
purpose. There are other UDC drivers depending on the platform, and
those can be used for a real device, instead, too.
At first, on a system to run the gadget, load `libcomposite` module::
% modprobe libcomposite
and you'll have `usb_gadget` subdirectory under configfs space
(typically `/sys/kernel/config` on modern OS). Then create a gadget
instance and add configurations there, for example::
% cd /sys/kernel/config
% mkdir usb_gadget/g1
% cd usb_gadget/g1
% mkdir configs/c.1
% mkdir functions/midi2.usb0
% echo 0x0004 > idProduct
% echo 0x17b3 > idVendor
% mkdir strings/0x409
% echo "ACME Enterprises" > strings/0x409/manufacturer
% echo "ACMESynth" > strings/0x409/product
% echo "ABCD12345" > strings/0x409/serialnumber
% mkdir configs/c.1/strings/0x409
% echo "Monosynth" > configs/c.1/strings/0x409/configuration
% echo 120 > configs/c.1/MaxPower
At this point, there must be a subdirectory `ep.0`, and that is the
configuration for a UMP Endpoint. You can fill the Endpoint
information like::
% echo "ACMESynth" > functions/midi2.usb0/iface_name
% echo "ACMESynth" > functions/midi2.usb0/ep.0/ep_name
% echo "ABCD12345" > functions/midi2.usb0/ep.0/product_id
% echo 0x0123 > functions/midi2.usb0/ep.0/family
% echo 0x4567 > functions/midi2.usb0/ep.0/model
% echo 0x123456 > functions/midi2.usb0/ep.0/manufacturer
% echo 0x12345678 > functions/midi2.usb0/ep.0/sw_revision
The default MIDI protocol can be set either 1 or 2::
% echo 2 > functions/midi2.usb0/ep.0/protocol
And, you can find a subdirectory `block.0` under this Endpoint
subdirectory. This defines the Function Block information::
% echo "Monosynth" > functions/midi2.usb0/ep.0/block.0/name
% echo 0 > functions/midi2.usb0/ep.0/block.0/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/num_groups
Finally, link the configuration and enable it::
% ln -s functions/midi2.usb0 configs/c.1
% echo dummy_udc.0 > UDC
where `dummy_udc.0` is an example case and it differs depending on the
system. You can find the UDC instances in `/sys/class/udc` and pass
the found name instead::
% ls /sys/class/udc
dummy_udc.0
Now, the MIDI 2.0 gadget device is enabled, and the gadget host
creates a new sound card instance containing a UMP rawmidi device by
`f_midi2` driver::
% cat /proc/asound/cards
....
1 [Gadget ]: f_midi2 - MIDI 2.0 Gadget
MIDI 2.0 Gadget
And on the connected host, a similar card should appear, too, but with
the card and device names given in the configfs above::
% cat /proc/asound/cards
....
2 [ACMESynth ]: USB-Audio - ACMESynth
ACME Enterprises ACMESynth at usb-dummy_hcd.0-1, high speed
You can play a MIDI file on the gadget side::
% aplaymidi -p 20:1 to_host.mid
and this will appear as an input from a MIDI device on the connected
host::
% aseqdump -p 20:0 -u 2
Vice versa, a playback on the connected host will work as an input on
the gadget, too.
Each Function Block may have different direction and UI-hint,
specified via `direction` and `ui_hint` attributes.
Passing `1` is for input-only, `2` for out-only and `3` for
bidirectional (the default value). For example::
% echo 2 > functions/midi2.usb0/ep.0/block.0/direction
% echo 2 > functions/midi2.usb0/ep.0/block.0/ui_hint
When you need more than one Function Blocks, you can create
subdirectories `block.1`, `block.2`, etc dynamically, and configure
them in the configuration procedure above before linking.
For example, to create a second Function Block for a keyboard::
% mkdir functions/midi2.usb0/ep.0/block.1
% echo "Keyboard" > functions/midi2.usb0/ep.0/block.1/name
% echo 1 > functions/midi2.usb0/ep.0/block.1/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.1/num_groups
% echo 1 > functions/midi2.usb0/ep.0/block.1/direction
% echo 1 > functions/midi2.usb0/ep.0/block.1/ui_hint
The `block.*` subdirectories can be removed dynamically, too (except
for `block.0` which is persistent).
For assigning a Function Block for MIDI 1.0 I/O, set up in `is_midi1`
attribute. 1 is for MIDI 1.0, and 2 is for MIDI 1.0 with low speed
connection::
% echo 2 > functions/midi2.usb0/ep.0/block.1/is_midi1
For disabling the processing of UMP Stream messages in the gadget
driver, pass `0` to `process_ump` attribute in the top-level config::
% echo 0 > functions/midi2.usb0/process_ump
The MIDI 1.0 interface at altset 0 is supported by the gadget driver,
too. When MIDI 1.0 interface is selected by the connected host, the
UMP I/O on the gadget is translated from/to USB MIDI 1.0 packets
accordingly while the gadget driver keeps communicating with the
user-space over UMP rawmidi.
MIDI 1.0 ports are set up from the config in each Function Block.
For example::
% echo 0 > functions/midi2.usb0/ep.0/block.0/midi1_first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/midi1_num_groups
The configuration above will enable the Group 1 (the index 0) for MIDI
1.0 interface. Note that those groups must be in the groups defined
for the Function Block itself.
The gadget driver supports more than one UMP Endpoints, too.
Similarly like the Function Blocks, you can create a new subdirectory
`ep.1` (but under the card top-level config) to enable a new Endpoint::
% mkdir functions/midi2.usb0/ep.1
and create a new Function Block there. For example, to create 4
Groups for the Function Block of this new Endpoint::
% mkdir functions/midi2.usb0/ep.1/block.0
% echo 4 > functions/midi2.usb0/ep.1/block.0/num_groups
Now, you'll have 4 rawmidi devices in total: the first two are UMP
rawmidi devices for Endpoint 0 and Endpoint 1, and other two for the
legacy MIDI 1.0 rawmidi devices corresponding to both EP 0 and EP 1.
The current altsetting on the gadget can be informed via a control
element "Operation Mode" with `RAWMIDI` iface. e.g. you can read it
via `amixer` program running on the gadget host like::
% amixer -c1 cget iface=RAWMIDI,name='Operation Mode'
; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
: values=2
The value (shown in the second returned line with `: values=`)
indicates 1 for MIDI 1.0 (altset 0), 2 for MIDI 2.0 (altset 1) and 0
for unset.
As of now, the configurations can't be changed after binding.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
UMP, MIDI 2.0과 Linux 역할
1-46MIDI 2.0은 기존 MIDI 1.0보다 높은 해상도와 세밀한 제어를 제공하는 확장 protocol이다. 핵심 변화는 Universal MIDI Packet(UMP), MIDI 2.0 protocol message, UMP와 MIDI 1.0 byte stream 사이의 투명한 변환, property·profile 구성을 위한 MIDI-CI 지원이다.
UMP는 MIDI 1.0과 MIDI 2.0 protocol message를 모두 담는 새 container 형식이다. 기존 byte stream과 달리 32bit 정렬이며 각 message를 단일 packet에 넣을 수 있다. 최대 16개 UMP Group으로 event를 보내고, 각 Group은 최대 16 MIDI channel을 포함한다.
MIDI 2.0 protocol은 MIDI 1.0을 확장해 해상도와 control을 높인다. MIDI-CI는 장치와 flexible profile·configuration을 협상하는 상위 protocol이며 특별한 SysEx 형태로 표현된다. Linux kernel은 UMP transport와 UMP상의 MIDI protocol encoding/decoding을 지원하고 MIDI-CI는 표준 SysEx 위에서 사용자 공간이 처리한다.
작성 시점에는 USB MIDI 장치만 UMP와 MIDI 2.0을 Linux에서 native로 지원한다. UMP 자체는 범용이므로 다른 transport layer에서도 사용할 수 있고 ALSA sequencer client처럼 다른 방식으로 구현할 수도 있다.
UMP 장치는 rawmidi 장치와 ALSA sequencer API 두 경로로 접근한다. Sequencer API는 UMP packet payload를 수용하도록 확장됐으며 MIDI 1.0과 MIDI 2.0 client를 자유롭게 연결하면 event를 투명하게 변환한다.
Protocol 계층과 kernel·user-space 책임을 구분한다.
=================
MIDI 2.0 on Linux
=================
General
=======
MIDI 2.0 is an extended protocol for providing higher resolutions and
more fine controls over the legacy MIDI 1.0. The fundamental changes
introduced for supporting MIDI 2.0 are:
- Support of Universal MIDI Packet (UMP)
- Support of MIDI 2.0 protocol messages
- Transparent conversions between UMP and legacy MIDI 1.0 byte stream
- MIDI-CI for property and profile configurations
UMP is a new container format to hold all MIDI protocol 1.0 and MIDI
2.0 protocol messages. Unlike the former byte stream, it's 32bit
aligned, and each message can be put in a single packet. UMP can send
the events up to 16 "UMP Groups", where each UMP Group contain up to
16 MIDI channels.
MIDI 2.0 protocol is an extended protocol to achieve the higher
resolution and more controls over the old MIDI 1.0 protocol.
MIDI-CI is a high-level protocol that can talk with the MIDI device
for the flexible profiles and configurations. It's represented in the
form of special SysEx.
For Linux implementations, the kernel supports the UMP transport and
the encoding/decoding of MIDI protocols on UMP, while MIDI-CI is
supported in user-space over the standard SysEx.
As of this writing, only USB MIDI device supports the UMP and Linux
2.0 natively. The UMP support itself is pretty generic, hence it
could be used by other transport layers, although it could be
implemented differently (e.g. as a ALSA sequencer client), too.
The access to UMP devices are provided in two ways: the access via
rawmidi device and the access via ALSA sequencer API.
ALSA sequencer API was extended to allow the payload of UMP packets.
It's allowed to connect freely between MIDI 1.0 and MIDI 2.0 sequencer
clients, and the events are converted transparently.
Kernel 설정
47-61MIDI 2.0 지원을 위해 `CONFIG_SND_UMP`, `CONFIG_SND_UMP_LEGACY_RAWMIDI`, `CONFIG_SND_SEQ_UMP`, `CONFIG_SND_SEQ_UMP_CLIENT`, `CONFIG_SND_USB_AUDIO_MIDI_V2`가 추가됐다.
사용자에게 보이는 첫 선택은 `CONFIG_SND_USB_AUDIO_MIDI_V2`다. 이를 `y`로 설정하면 UMP core인 `CONFIG_SND_UMP`와 sequencer binding인 `CONFIG_SND_SEQ_UMP_CLIENT`가 자동 선택된다. `CONFIG_SND_UMP_LEGACY_RAWMIDI=y`는 UMP Endpoint용 기존 raw MIDI 장치 호환 경로를 활성화한다.
직접 선택과 자동·호환 기능을 정리한다.
Kernel Configuration
====================
The following new configs are added for supporting MIDI 2.0:
`CONFIG_SND_UMP`, `CONFIG_SND_UMP_LEGACY_RAWMIDI`,
`CONFIG_SND_SEQ_UMP`, `CONFIG_SND_SEQ_UMP_CLIENT`, and
`CONFIG_SND_USB_AUDIO_MIDI_V2`. The first visible one is
`CONFIG_SND_USB_AUDIO_MIDI_V2`, and when you choose it (to set `=y`),
the core support for UMP (`CONFIG_SND_UMP`) and the sequencer binding
(`CONFIG_SND_SEQ_UMP_CLIENT`) will be automatically selected.
Additionally, `CONFIG_SND_UMP_LEGACY_RAWMIDI=y` will enable the
support for the legacy raw MIDI device for UMP Endpoints.
USB probe, UMP rawmidi와 legacy substream
62-120장치가 MIDI 2.0을 지원하면 USB-audio driver는 기본적으로 altset 0의 MIDI 1.0 interface 대신 항상 altset 1에 있는 MIDI 2.0 interface를 probe해 사용한다. `snd-usb-audio`에 `midi2_enable=0`을 주면 옛 MIDI 1.0 binding으로 되돌릴 수 있다.
Driver는 UMP v1.1부터 제공되는 UMP Endpoint와 Function Block 정보를 질의해 topology를 만든다. 오래된 장치가 새 질의에 응답하지 않으면 USB descriptor의 Group Terminal Block(GTB) 정보로 fallback한다. 예상하지 못한 UMP command 때문에 오동작하는 장치는 `midi2_ump_probe=0`으로 v1.1 질의를 건너뛸 수 있다.
MIDI 2.0 장치를 probe하면 UMP Endpoint마다 `/dev/snd/umpC*D*` rawmidi 장치를 만든다. MIDI 1.0의 `/dev/snd/midiC*D*`와 이름을 분리해 legacy 응용 프로그램이 UMP 장치를 실수로 열지 않게 한다. UMP rawmidi에서 packet을 직접 읽고 쓸 수 있다.
% hexdump -C /dev/snd/umpC0D0
00000000 01 07 b0 20 00 07 b0 20 64 3c 90 20 64 3c 80 20 |... ... d<. d<. |
UMP는 32bit packet이므로 read/write 크기도 4byte 단위로 정렬해야 한다. Payload의 32bit word는 항상 CPU native endianness다. Transport driver가 system endianness와 transport가 요구하는 endianness 또는 byte order 사이를 변환한다.
`CONFIG_SND_UMP_LEGACY_RAWMIDI`를 켜면 `/dev/snd/midiC*D*` 표준 raw MIDI 장치도 추가한다. 16개 substream이 0-based UMP Group에 각각 대응하며 legacy 응용 프로그램은 MIDI 1.0 byte stream으로 특정 Group에 접근한다. ALSA rawmidi API는 임의 substream을 열 수 있지만 device node만 열면 첫 substream을 연다.
UMP Endpoint에는 UMP v1.1 Stream message 또는 USB MIDI 2.0 descriptor에서 얻은 추가 정보가 있다. ALSA의 UMP Block은 여러 UMP Group의 연관을 나타내는 추상화이며 UMP v1.1 Function Block에 대응한다. Function Block 정보가 없으면 USB MIDI 2.0 GTB에서 일부를 채운다.
질의 지원 여부와 legacy 호환 설정에 따른 경로다.
장치 이름과 data 표현을 MIDI 1.0과 비교한다.
Rawmidi Device with USB MIDI 2.0
================================
When a device supports MIDI 2.0, the USB-audio driver probes and uses
the MIDI 2.0 interface (that is found always at the altset 1) as
default instead of the MIDI 1.0 interface (at altset 0). You can
switch back to the binding with the old MIDI 1.0 interface by passing
`midi2_enable=0` option to snd-usb-audio driver module, too.
The USB audio driver tries to query the UMP Endpoint and UMP Function
Block information that are provided since UMP v1.1, and builds up the
topology based on those information. When the device is older and
doesn't respond to the new UMP inquiries, the driver falls back and
builds the topology based on Group Terminal Block (GTB) information
from the USB descriptor. Some device might be screwed up by the
unexpected UMP command; in such a case, pass `midi2_ump_probe=0`
option to snd-usb-audio driver for skipping the UMP v1.1 inquiries.
When the MIDI 2.0 device is probed, the kernel creates a rawmidi
device for each UMP Endpoint of the device. Its device name is
`/dev/snd/umpC*D*` and different from the standard rawmidi device name
`/dev/snd/midiC*D*` for MIDI 1.0, in order to avoid confusing the
legacy applications accessing mistakenly to UMP devices.
You can read and write UMP packet data directly from/to this UMP
rawmidi device. For example, reading via `hexdump` like below will
show the incoming UMP packets of the card 0 device 0 in the hex
format::
% hexdump -C /dev/snd/umpC0D0
00000000 01 07 b0 20 00 07 b0 20 64 3c 90 20 64 3c 80 20 |... ... d<. d<. |
Unlike the MIDI 1.0 byte stream, UMP is a 32bit packet, and the size
for reading or writing the device is also aligned to 32bit (which is 4
bytes).
The 32-bit words in the UMP packet payload are always in CPU native
endianness. Transport drivers are responsible to convert UMP words
from / to system endianness to required transport endianness / byte
order.
When `CONFIG_SND_UMP_LEGACY_RAWMIDI` is set, the driver creates
another standard raw MIDI device additionally as `/dev/snd/midiC*D*`.
This contains 16 substreams, and each substream corresponds to a
(0-based) UMP Group. Legacy applications can access to the specified
group via each substream in MIDI 1.0 byte stream format. With the
ALSA rawmidi API, you can open the arbitrary substream, while just
opening `/dev/snd/midiC*D*` will end up with opening the first
substream.
Each UMP Endpoint can provide the additional information, constructed
from the information inquired via UMP 1.1 Stream messages or USB MIDI
2.0 descriptors. And a UMP Endpoint may contain one or more UMP
Blocks, where UMP Block is an abstraction introduced in the ALSA UMP
implementations to represent the associations among UMP Groups. UMP
Block corresponds to Function Block in UMP 1.1 specification. When
UMP 1.1 Function Block information isn't available, it's filled
partially from Group Terminal Block (GTB) as defined in USB MIDI 2.0
specifications.
Endpoint·Block 정보와 ioctl
121-168UMP Endpoint와 Block 정보는 `/proc/asound/card*/midi*`에서 볼 수 있다. 출력에는 Endpoint 이름·product ID·UMP version·protocol capability·현재 protocol·Block 수와 각 Block의 direction, active 상태, Group 범위, MIDI 1.0 여부가 나온다.
% cat /proc/asound/card1/midi0
ProtoZOA MIDI
Type: UMP
EP Name: ProtoZOA
EP Product ID: ABCD12345678
UMP Version: 0x0000
Protocol Caps: 0x00000100
Protocol: 0x00000100
Num Blocks: 3
Block 0 (ProtoZOA Main)
Direction: bidirection
Active: Yes
Groups: 1-1
Is MIDI1: No
Block 1 (ProtoZOA Ext IN)
Direction: output
Active: Yes
Groups: 2-2
Is MIDI1: Yes (Low Speed)
Proc의 `Groups`는 1-based UMP Group 번호 범위다. Endpoint와 Block 정보는 각각 `SNDRV_UMP_IOCTL_ENDPOINT_INFO`, `SNDRV_UMP_IOCTL_BLOCK_INFO` ioctl로 얻을 수 있다.
Rawmidi 이름과 Endpoint 이름은 보통 같다. USB MIDI에서는 해당 interface descriptor의 `iInterface`를 사용하고 없으면 USB device descriptor의 `iProduct`를 fallback으로 복사한다. Endpoint Product ID는 unique string이며 USB MIDI에서는 `iSerialNumber`에서 가져온다. Protocol capability와 실제 protocol bit는 `asound.h`에 정의된다.
Proc와 ioctl에서 노출하는 대표 field다.
The information of UMP Endpoints and UMP Blocks are found in the proc
file `/proc/asound/card*/midi*`. For example::
% cat /proc/asound/card1/midi0
ProtoZOA MIDI
Type: UMP
EP Name: ProtoZOA
EP Product ID: ABCD12345678
UMP Version: 0x0000
Protocol Caps: 0x00000100
Protocol: 0x00000100
Num Blocks: 3
Block 0 (ProtoZOA Main)
Direction: bidirection
Active: Yes
Groups: 1-1
Is MIDI1: No
Block 1 (ProtoZOA Ext IN)
Direction: output
Active: Yes
Groups: 2-2
Is MIDI1: Yes (Low Speed)
....
Note that `Groups` field shown in the proc file above indicates the
1-based UMP Group numbers (from-to).
Those additional UMP Endpoint and UMP Block information can be
obtained via the new ioctls `SNDRV_UMP_IOCTL_ENDPOINT_INFO` and
`SNDRV_UMP_IOCTL_BLOCK_INFO`, respectively.
The rawmidi name and the UMP Endpoint name are usually identical, and
in the case of USB MIDI, it's taken from `iInterface` of the
corresponding USB MIDI interface descriptor. If it's not provided,
it's copied from `iProduct` of the USB device descriptor as a
fallback.
The Endpoint Product ID is a string field and supposed to be unique.
It's copied from `iSerialNumber` of the device for USB MIDI.
The protocol capabilities and the actual protocol bits are defined in
`asound.h`.
ALSA Sequencer client·port와 자동 변환
169-262ALSA sequencer client는 `midi_version`을 0, 1, 2 중 하나로 설정해 legacy, UMP MIDI 1.0, UMP MIDI 2.0 장치임을 선언한다. Legacy client는 기존 sequencer event를 사용하고 UMP MIDI 1.0/2.0 client는 UMP용 확장 event record를 사용한다. 값은 `snd_seq_client_info.midi_version`에서 확인한다.
Sequencer event에 `SNDRV_SEQ_EVENT_UMP` flag를 설정하면 16byte(128bit) payload에 UMP packet을 담는다. Flag가 없으면 최대 12byte payload의 legacy event다. UMP event에서는 type field를 무시하지만 기본값 0으로 설정해야 한다.
% cat /proc/asound/seq/clients
Client 14 : "Midi Through" [Kernel Legacy]
Port 0 : "Midi Through Port-0" (RWe-)
Client 20 : "ProtoZOA" [Kernel UMP MIDI1]
UMP Endpoint: ProtoZOA
UMP Block 0: ProtoZOA Main [Active]
Groups: 1-1
UMP Block 1: ProtoZOA Ext IN [Active]
Groups: 2-2
UMP Block 2: ProtoZOA Ext OUT [Active]
Groups: 3-3
Port 0 : "MIDI 2.0" (RWeX) [In/Out]
Port 1 : "ProtoZOA Main" (RWeX) [In/Out]
Port 2 : "ProtoZOA Ext IN" (-We-) [Out]
Port 3 : "ProtoZOA Ext OUT" (R-e-) [In]
USB MIDI 2.0 client는 항상 port 0을 `MIDI 2.0` Endpoint port로 만들고, 각 UMP Group을 port 1부터 노출한다. Endpoint port는 장치 모든 port event를 받는 catch-all이고 Group port는 지정 Group event만 받는다. UMP type `0x0f` 같은 groupless message는 Endpoint port에만 전달한다.
UMP client는 보통 16개 port를 만들지만 어떤 Block에도 속하지 않거나 inactive Block에 속한 port는 inactive로 표시되어 proc에 나오지 않는다. 예의 port 4~16도 존재하지만 숨겨져 있다. Proc에는 Block 정보도 나오며 rawmidi proc 출력에서 더 자세히 볼 수 있다.
서로 다른 MIDI version의 client를 연결하면 legacy와 UMP MIDI 1.0/2.0 사이뿐 아니라 UMP MIDI 1.0과 2.0 사이도 client version에 맞춰 자동 변환한다. 같은 event를 legacy `aseqdump`로 보면 정수 velocity·controller 값이고 `-u 2`에서는 고정밀 16/32bit 값을 받는다.
% aseqdump -p 20:1
Source Event Ch Data
20:1 Note on 0, note 60, velocity 100
20:1 Note off 0, note 60, velocity 100
20:1 Control change 0, controller 11, value 4
% aseqdump -u 2 -p 20:1
Source Event Ch Data
20:1 Note on 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Note off 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Control change 0, controller 11, value 0x2000000
Endpoint catch-all과 Group 전용 port의 차이다.
Client version과 UMP flag에 따른 payload다.
ALSA Sequencer with USB MIDI 2.0
================================
In addition to the rawmidi interfaces, ALSA sequencer interface
supports the new UMP MIDI 2.0 device, too. Now, each ALSA sequencer
client may set its MIDI version (0, 1 or 2) to declare itself being
either the legacy, UMP MIDI 1.0 or UMP MIDI 2.0 device, respectively.
The first, legacy client is the one that sends/receives the old
sequencer event as was. Meanwhile, UMP MIDI 1.0 and 2.0 clients send
and receive in the extended event record for UMP. The MIDI version is
seen in the new `midi_version` field of `snd_seq_client_info`.
A UMP packet can be sent/received in a sequencer event embedded by
specifying the new event flag bit `SNDRV_SEQ_EVENT_UMP`. When this
flag is set, the event has 16 byte (128 bit) data payload for holding
the UMP packet. Without the `SNDRV_SEQ_EVENT_UMP` bit flag, the event
is treated as a legacy event as it was (with max 12 byte data
payload).
With `SNDRV_SEQ_EVENT_UMP` flag set, the type field of a UMP sequencer
event is ignored (but it should be set to 0 as default).
The type of each client can be seen in `/proc/asound/seq/clients`.
For example::
% cat /proc/asound/seq/clients
Client info
cur clients : 3
....
Client 14 : "Midi Through" [Kernel Legacy]
Port 0 : "Midi Through Port-0" (RWe-)
Client 20 : "ProtoZOA" [Kernel UMP MIDI1]
UMP Endpoint: ProtoZOA
UMP Block 0: ProtoZOA Main [Active]
Groups: 1-1
UMP Block 1: ProtoZOA Ext IN [Active]
Groups: 2-2
UMP Block 2: ProtoZOA Ext OUT [Active]
Groups: 3-3
Port 0 : "MIDI 2.0" (RWeX) [In/Out]
Port 1 : "ProtoZOA Main" (RWeX) [In/Out]
Port 2 : "ProtoZOA Ext IN" (-We-) [Out]
Port 3 : "ProtoZOA Ext OUT" (R-e-) [In]
Here you can find two types of kernel clients, "Legacy" for client 14,
and "UMP MIDI1" for client 20, which is a USB MIDI 2.0 device.
A USB MIDI 2.0 client gives always the port 0 as "MIDI 2.0" and the
rest ports from 1 for each UMP Group (e.g. port 1 for Group 1).
In this example, the device has three active groups (Main, Ext IN and
Ext OUT), and those are exposed as sequencer ports from 1 to 3.
The "MIDI 2.0" port is for a UMP Endpoint, and its difference from
other UMP Group ports is that UMP Endpoint port sends the events from
the all ports on the device ("catch-all"), while each UMP Group port
sends only the events from the given UMP Group.
Also, UMP groupless messages (such as the UMP message type 0x0f) are
sent only to the UMP Endpoint port.
Note that, although each UMP sequencer client usually creates 16
ports, those ports that don't belong to any UMP Blocks (or belonging
to inactive UMP Blocks) are marked as inactive, and they don't appear
in the proc outputs. In the example above, the sequencer ports from 4
to 16 are present but not shown there.
The proc file above shows the UMP Block information, too. The same
entry (but with more detailed information) is found in the rawmidi
proc output.
When clients are connected between different MIDI versions, the events
are translated automatically depending on the client's version, not
only between the legacy and the UMP MIDI 1.0/2.0 types, but also
between UMP MIDI 1.0 and 2.0 types, too. For example, running
`aseqdump` program on the ProtoZOA Main port in the legacy mode will
give you the output like::
% aseqdump -p 20:1
Waiting for data. Press Ctrl+C to end.
Source Event Ch Data
20:1 Note on 0, note 60, velocity 100
20:1 Note off 0, note 60, velocity 100
20:1 Control change 0, controller 11, value 4
When you run `aseqdump` in MIDI 2.0 mode, it'll receive the high
precision data like::
% aseqdump -u 2 -p 20:1
Waiting for data. Press Ctrl+C to end.
Source Event Ch Data
20:1 Note on 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Note off 0, note 60, velocity 0xc924, attr type = 0, data = 0x0
20:1 Control change 0, controller 11, value 0x2000000
while the data is automatically converted by ALSA sequencer core.
Rawmidi API 확장
263-307`SNDRV_UMP_IOCTL_ENDPOINT_INFO`는 연결 card·device 번호, flag, protocol, Block 수, Endpoint 이름 등을 반환한다. Protocol capability와 current protocol field는 상위 byte에 `SNDRV_UMP_EP_INFO_PROTO_MIDI1` 또는 `...MIDI2`, 하위 byte에 jitter reduction timestamp인 `...JRTS_TX`, `...JRTS_RX` bit를 담는다. Endpoint는 최대 32개 UMP Block을 가질 수 있고 현재 할당 수가 정보에 표시된다.
`SNDRV_UMP_IOCTL_BLOCK_INFO`는 0-based Block ID를 입력받아 direction, 0-based 첫 Group ID, Group 수, 이름 등을 반환한다. Direction은 `SNDRV_UMP_DIR_INPUT`, `SNDRV_UMP_DIR_OUTPUT`, `SNDRV_UMP_DIR_BIDIRECTION` 중 하나다.
UMP v1.1 장치는 UMP type `0x0f`, status `0x05`인 Stream Configuration Request로 MIDI protocol을 바꿀 수 있다. UMP core가 message를 받으면 Endpoint 정보와 대응 sequencer client도 갱신한다.
Rawmidi info의 새 `tied_device` field는 UMP 쪽에서 legacy rawmidi device 번호를, legacy 쪽에서는 UMP rawmidi 번호를 서로 가리킨다. Legacy rawmidi substream은 UMP Function Block 상태에 따라 동적으로 enable/disable될 수 있으며 inactive면 info `flags`에 bit `0x10`, 즉 `SNDRV_RAWMIDI_INFO_STREAM_INACTIVE`를 설정한다.
Endpoint·Block·연결 장치와 active 상태를 제공한다.
Rawmidi API Extensions
======================
* The additional UMP Endpoint information can be obtained via the new
ioctl `SNDRV_UMP_IOCTL_ENDPOINT_INFO`. It contains the associated
card and device numbers, the bit flags, the protocols, the number of
UMP Blocks, the name string of the endpoint, etc.
The protocols are specified in two field, the protocol capabilities
and the current protocol. Both contain the bit flags specifying the
MIDI protocol version (`SNDRV_UMP_EP_INFO_PROTO_MIDI1` or
`SNDRV_UMP_EP_INFO_PROTO_MIDI2`) in the upper byte and the jitter
reduction timestamp (`SNDRV_UMP_EP_INFO_PROTO_JRTS_TX` and
`SNDRV_UMP_EP_INFO_PROTO_JRTS_RX`) in the lower byte.
A UMP Endpoint may contain up to 32 UMP Blocks, and the number of
the currently assigned blocks are shown in the Endpoint information.
* Each UMP Block information can be obtained via another new ioctl
`SNDRV_UMP_IOCTL_BLOCK_INFO`. The block ID number (0-based) has to
be passed for the block to query. The received data contains the
associated the direction of the block, the first associated group ID
(0-based) and the number of groups, the name string of the block,
etc.
The direction is either `SNDRV_UMP_DIR_INPUT`,
`SNDRV_UMP_DIR_OUTPUT` or `SNDRV_UMP_DIR_BIDIRECTION`.
* For the device supports UMP v1.1, the UMP MIDI protocol can be
switched via "Stream Configuration Request" message (UMP type 0x0f,
status 0x05). When UMP core receives such a message, it updates the
UMP EP info and the corresponding sequencer clients as well.
* The legacy rawmidi device number is found in the new `tied_device`
field of the rawmidi info.
On the other hand, the UMP rawmidi device number is found in
`tied_device` field of the legacy rawmidi info, too.
* Each substream of the legacy rawmidi may be enabled / disabled
dynamically depending on the UMP FB state.
When the selected substream is inactive, it's indicated by the bit
0x10 (`SNDRV_RAWMIDI_INFO_STREAM_INACTIVE`) in the `flags` field of
the legacy rawmidi info.
Control API 확장
308-327`SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE`는 다음 UMP rawmidi device를 조회한다. 기존 `SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE`는 legacy rawmidi만 조회한다. 열 subdevice 번호 설정은 일반 rawmidi와 같이 `SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE`를 사용한다.
`SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO`와 `SNDRV_CTL_IOCTL_UMP_BLOCK_INFO`는 실제 UMP rawmidi device를 열지 않고 ALSA control API를 통해 지정 장치의 Endpoint와 Block 정보를 제공한다. 조회 시 `card` field는 무시하고 control interface의 card에 항상 연결한다.
장치 열거와 정보 조회 역할을 구분한다.
Control API Extensions
======================
* The new ioctl `SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE` is introduced for
querying the next UMP rawmidi device, while the existing ioctl
`SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE` queries only the legacy
rawmidi devices.
For setting the subdevice (substream number) to be opened, use the
ioctl `SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE` like the normal
rawmidi.
* Two new ioctls `SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO` and
`SNDRV_CTL_IOCTL_UMP_BLOCK_INFO` provide the UMP Endpoint and UMP
Block information of the specified UMP device via ALSA control API
without opening the actual (UMP) rawmidi device.
The `card` field is ignored upon inquiry, always tied with the card
of the control interface.
Sequencer API 확장
328-398`snd_seq_client_info`에 client의 현재 MIDI version 0/1/2를 나타내는 `midi_version`을 추가했다. 값이 1 또는 2면 UMP sequencer client read 정렬이 확장 payload를 위해 28byte에서 32byte로 바뀐다. Write 정렬은 같지만 event 크기는 `SNDRV_SEQ_EVENT_UMP` flag에 따라 달라진다. 이 flag가 있으면 legacy 12byte 대신 16byte payload에 UMP packet을 담는다.
새 port type `SNDRV_SEQ_PORT_TYPE_MIDI_UMP`는 UMP 가능 port를 나타낸다. Capability bit `SNDRV_SEQ_PORT_CAP_INACTIVE`와 `SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT`는 inactive port와 Endpoint port를 표시한다. Client info에 `SNDRV_SEQ_FILTER_NO_CONVERT` filter를 설정하면 event 변환을 막으며 kernel pass-through client `snd-seq-dummy`가 내부적으로 사용한다.
Port info의 `direction`은 `SNDRV_SEQ_PORT_DIR_INPUT`, `...OUTPUT`, `...BIDIRECTION` 중 하나다. `ump_group`은 연결된 1-based UMP Group 번호다. 0이 아니면 지정 Group으로 전달할 때 packet의 Group field를 0-based 값으로 보정한다.
Client는 `group_filter` bitmap으로 UMP Group event를 추가 filtering할 수 있다. 1-based Group 번호의 bit를 사용하며 bit 1은 첫 Group인 Group 1을 차단한다. Bit 0은 groupless message filtering에 쓴다.
UMP client용 `SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO`와 `SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO`는 client의 `snd_ump_endpoint_info` 또는 `snd_ump_block_info`를 조회·설정한다. Endpoint는 `type=0`, Block은 `type=block number + 1`을 전달한다. USB MIDI driver는 기반 UMP rawmidi 정보를 제공하고 user-space client는 `*_SET`으로 자체 정보를 제공할 수 있지만 kernel client 설정은 오류다.
UMP 1.1 Function Block 정보는 동적으로 변할 수 있다. Update를 받으면 sequencer core가 대응 port 이름과 attribute를 바꾸고 일반 port change처럼 system announcement port에 알린다. Endpoint와 Block 변경 event는 type 68 `SNDRV_SEQ_EVENT_UMP_EP_CHANGE`, type 69 `SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE`이며 payload의 `snd_seq_ev_ump_notify`가 변경된 client와 FB 번호를 나타낸다.
UMP client·event·port·notification을 구성하는 새 ABI다.
Sequencer API Extensions
========================
* `midi_version` field is added to `snd_seq_client_info` to indicate
the current MIDI version (either 0, 1 or 2) of each client.
When `midi_version` is 1 or 2, the alignment of read from a UMP
sequencer client is also changed from the former 28 bytes to 32
bytes for the extended payload. The alignment size for the write
isn't changed, but each event size may differ depending on the new
bit flag below.
* `SNDRV_SEQ_EVENT_UMP` flag bit is added for each sequencer event
flags. When this bit flag is set, the sequencer event is extended
to have a larger payload of 16 bytes instead of the legacy 12
bytes, and the event contains the UMP packet in the payload.
* The new sequencer port type bit (`SNDRV_SEQ_PORT_TYPE_MIDI_UMP`)
indicates the port being UMP-capable.
* The sequencer ports have new capability bits to indicate the
inactive ports (`SNDRV_SEQ_PORT_CAP_INACTIVE`) and the UMP Endpoint
port (`SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT`).
* The event conversion of ALSA sequencer clients can be suppressed the
new filter bit `SNDRV_SEQ_FILTER_NO_CONVERT` set to the client info.
For example, the kernel pass-through client (`snd-seq-dummy`) sets
this flag internally.
* The port information gained the new field `direction` to indicate
the direction of the port (either `SNDRV_SEQ_PORT_DIR_INPUT`,
`SNDRV_SEQ_PORT_DIR_OUTPUT` or `SNDRV_SEQ_PORT_DIR_BIDIRECTION`).
* Another additional field for the port information is `ump_group`
which specifies the associated UMP Group Number (1-based).
When it's non-zero, the UMP group field in the UMP packet updated
upon delivery to the specified group (corrected to be 0-based).
Each sequencer port is supposed to set this field if it's a port to
specific to a certain UMP group.
* Each client may set the additional event filter for UMP Groups in
`group_filter` bitmap. The filter consists of bitmap from 1-based
Group numbers. For example, when the bit 1 is set, messages from
Group 1 (i.e. the very first group) are filtered and not delivered.
The bit 0 is used for filtering UMP groupless messages.
* Two new ioctls are added for UMP-capable clients:
`SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO` and
`SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO`. They are used to get and set
either `snd_ump_endpoint_info` or `snd_ump_block_info` data
associated with the sequencer client. The USB MIDI driver provides
those information from the underlying UMP rawmidi, while a
user-space client may provide its own data via `*_SET` ioctl.
For an Endpoint data, pass 0 to the `type` field, while for a Block
data, pass the block number + 1 to the `type` field.
Setting the data for a kernel client shall result in an error.
* With UMP 1.1, Function Block information may be changed
dynamically. When the update of Function Block is received from the
device, ALSA sequencer core changes the corresponding sequencer port
name and attributes accordingly, and notifies the changes via the
announcement to the ALSA sequencer system port, similarly like the
normal port change notification.
* There are two extended event types for notifying the UMP Endpoint and
Function Block changes via the system announcement port:
type 68 (`SNDRV_SEQ_EVENT_UMP_EP_CHANGE`) and type 69
(`SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE`). They take the new type,
`snd_seq_ev_ump_notify` in the payload, indicating the client number
and the FB number that are changed.
USB MIDI 2.0 gadget 준비와 instance 생성
399-445최신 kernel의 USB MIDI 2.0 gadget function driver는 MIDI 2.0 기능 prototype과 debugging에 사용할 수 있다. `CONFIG_USB_GADGET`, `CONFIG_USB_CONFIGFS`, `CONFIG_USB_CONFIGFS_F_MIDI2`가 필요하고 동작하는 UDC driver도 있어야 한다. 예에서는 PC와 VM에서 debugging 가능한 `CONFIG_USB_DUMMY_HCD`의 `dummy_hcd`를 사용하지만 실제 장치는 플랫폼 UDC를 쓸 수 있다.
먼저 gadget 시스템에서 `libcomposite`를 적재하면 보통 `/sys/kernel/config`인 configfs에 `usb_gadget` directory가 생긴다. Gadget instance, configuration, `midi2.usb0` function과 USB 식별·문자열을 만든다.
% modprobe libcomposite
% cd /sys/kernel/config
% mkdir usb_gadget/g1
% cd usb_gadget/g1
% mkdir configs/c.1
% mkdir functions/midi2.usb0
% echo 0x0004 > idProduct
% echo 0x17b3 > idVendor
% mkdir strings/0x409
% echo "ACME Enterprises" > strings/0x409/manufacturer
% echo "ACMESynth" > strings/0x409/product
% echo "ABCD12345" > strings/0x409/serialnumber
% mkdir configs/c.1/strings/0x409
% echo "Monosynth" > configs/c.1/strings/0x409/configuration
% echo 120 > configs/c.1/MaxPower
Configfs에서 function과 USB descriptor 정보를 준비한다.
MIDI2 USB Gadget Function Driver
================================
The latest kernel contains the support for USB MIDI 2.0 gadget
function driver, which can be used for prototyping and debugging MIDI
2.0 features.
`CONFIG_USB_GADGET`, `CONFIG_USB_CONFIGFS` and
`CONFIG_USB_CONFIGFS_F_MIDI2` need to be enabled for the MIDI2 gadget
driver.
In addition, for using a gadget driver, you need a working UDC driver.
In the example below, we use `dummy_hcd` driver (enabled via
`CONFIG_USB_DUMMY_HCD`) that is available on PC and VM for debugging
purpose. There are other UDC drivers depending on the platform, and
those can be used for a real device, instead, too.
At first, on a system to run the gadget, load `libcomposite` module::
% modprobe libcomposite
and you'll have `usb_gadget` subdirectory under configfs space
(typically `/sys/kernel/config` on modern OS). Then create a gadget
instance and add configurations there, for example::
% cd /sys/kernel/config
% mkdir usb_gadget/g1
% cd usb_gadget/g1
% mkdir configs/c.1
% mkdir functions/midi2.usb0
% echo 0x0004 > idProduct
% echo 0x17b3 > idVendor
% mkdir strings/0x409
% echo "ACME Enterprises" > strings/0x409/manufacturer
% echo "ACMESynth" > strings/0x409/product
% echo "ABCD12345" > strings/0x409/serialnumber
% mkdir configs/c.1/strings/0x409
% echo "Monosynth" > configs/c.1/strings/0x409/configuration
% echo 120 > configs/c.1/MaxPower
At this point, there must be a subdirectory `ep.0`, and that is the
configuration for a UMP Endpoint. You can fill the Endpoint
information like::
Endpoint·Function Block 구성과 연결 시험
446-505`ep.0` directory가 UMP Endpoint 구성이다. Interface·Endpoint 이름, product ID, family, model, manufacturer, software revision을 설정하고 기본 MIDI protocol을 1 또는 2로 선택한다.
% echo "ACMESynth" > functions/midi2.usb0/iface_name
% echo "ACMESynth" > functions/midi2.usb0/ep.0/ep_name
% echo "ABCD12345" > functions/midi2.usb0/ep.0/product_id
% echo 0x0123 > functions/midi2.usb0/ep.0/family
% echo 0x4567 > functions/midi2.usb0/ep.0/model
% echo 0x123456 > functions/midi2.usb0/ep.0/manufacturer
% echo 0x12345678 > functions/midi2.usb0/ep.0/sw_revision
% echo 2 > functions/midi2.usb0/ep.0/protocol
Endpoint 아래 `block.0`에서 Function Block 이름, 첫 Group과 Group 수를 지정한다. Function을 configuration에 link한 뒤 UDC 이름을 써서 활성화한다. 실제 UDC instance 이름은 `/sys/class/udc`에서 찾는다.
% echo "Monosynth" > functions/midi2.usb0/ep.0/block.0/name
% echo 0 > functions/midi2.usb0/ep.0/block.0/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/num_groups
% ln -s functions/midi2.usb0 configs/c.1
% echo dummy_udc.0 > UDC
% ls /sys/class/udc
dummy_udc.0
활성화되면 gadget 쪽에는 `f_midi2 - MIDI 2.0 Gadget`, 연결 host에는 configfs 이름을 사용한 `USB-Audio - ACMESynth` sound card가 생긴다. Gadget에서 `aplaymidi -p 20:1 to_host.mid`를 실행하면 host의 `aseqdump -p 20:0 -u 2` 입력으로 보인다. 반대로 host playback도 gadget input으로 전달된다.
% aplaymidi -p 20:1 to_host.mid
% aseqdump -p 20:0 -u 2
Configfs 설정부터 양방향 sequencer data까지의 흐름이다.
% echo "ACMESynth" > functions/midi2.usb0/iface_name
% echo "ACMESynth" > functions/midi2.usb0/ep.0/ep_name
% echo "ABCD12345" > functions/midi2.usb0/ep.0/product_id
% echo 0x0123 > functions/midi2.usb0/ep.0/family
% echo 0x4567 > functions/midi2.usb0/ep.0/model
% echo 0x123456 > functions/midi2.usb0/ep.0/manufacturer
% echo 0x12345678 > functions/midi2.usb0/ep.0/sw_revision
The default MIDI protocol can be set either 1 or 2::
% echo 2 > functions/midi2.usb0/ep.0/protocol
And, you can find a subdirectory `block.0` under this Endpoint
subdirectory. This defines the Function Block information::
% echo "Monosynth" > functions/midi2.usb0/ep.0/block.0/name
% echo 0 > functions/midi2.usb0/ep.0/block.0/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/num_groups
Finally, link the configuration and enable it::
% ln -s functions/midi2.usb0 configs/c.1
% echo dummy_udc.0 > UDC
where `dummy_udc.0` is an example case and it differs depending on the
system. You can find the UDC instances in `/sys/class/udc` and pass
the found name instead::
% ls /sys/class/udc
dummy_udc.0
Now, the MIDI 2.0 gadget device is enabled, and the gadget host
creates a new sound card instance containing a UMP rawmidi device by
`f_midi2` driver::
% cat /proc/asound/cards
....
1 [Gadget ]: f_midi2 - MIDI 2.0 Gadget
MIDI 2.0 Gadget
And on the connected host, a similar card should appear, too, but with
the card and device names given in the configfs above::
% cat /proc/asound/cards
....
2 [ACMESynth ]: USB-Audio - ACMESynth
ACME Enterprises ACMESynth at usb-dummy_hcd.0-1, high speed
You can play a MIDI file on the gadget side::
% aplaymidi -p 20:1 to_host.mid
and this will appear as an input from a MIDI device on the connected
host::
% aseqdump -p 20:0 -u 2
Vice versa, a playback on the connected host will work as an input on
the gadget, too.
Function Block 속성과 MIDI 1.0 altset
506-555각 Function Block은 `direction`과 `ui_hint`를 따로 가질 수 있다. 값 1은 input-only, 2는 output-only, 3은 bidirectional이며 기본값은 3이다.
% echo 2 > functions/midi2.usb0/ep.0/block.0/direction
% echo 2 > functions/midi2.usb0/ep.0/block.0/ui_hint
Block이 여러 개 필요하면 link 전에 `block.1`, `block.2` 등을 동적으로 만들고 같은 방식으로 설정한다. 예의 두 번째 keyboard Block은 Group index 1 하나를 input-only로 사용한다. `block.*`는 동적으로 제거할 수도 있지만 `block.0`은 persistent라 제거할 수 없다.
% mkdir functions/midi2.usb0/ep.0/block.1
% echo "Keyboard" > functions/midi2.usb0/ep.0/block.1/name
% echo 1 > functions/midi2.usb0/ep.0/block.1/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.1/num_groups
% echo 1 > functions/midi2.usb0/ep.0/block.1/direction
% echo 1 > functions/midi2.usb0/ep.0/block.1/ui_hint
Function Block을 MIDI 1.0 I/O에 할당하려면 `is_midi1`을 설정한다. 1은 MIDI 1.0, 2는 low-speed MIDI 1.0 연결이다. Gadget driver의 UMP Stream message 처리를 끄려면 top-level `process_ump`에 0을 쓴다.
% echo 2 > functions/midi2.usb0/ep.0/block.1/is_midi1
% echo 0 > functions/midi2.usb0/process_ump
Gadget driver는 altset 0의 MIDI 1.0 interface도 지원한다. Host가 이를 선택하면 gadget driver는 USB MIDI 1.0 packet과 UMP I/O를 서로 변환하지만 사용자 공간과는 계속 UMP rawmidi로 통신한다. MIDI 1.0 port는 각 Function Block 설정에서 만들며 다음은 Group 1(index 0)을 활성화한다. 선택 Group은 반드시 Function Block 자체가 정의한 Group 범위 안에 있어야 한다.
% echo 0 > functions/midi2.usb0/ep.0/block.0/midi1_first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/midi1_num_groups
방향, MIDI 1.0 호환과 동적 생명주기다.
Each Function Block may have different direction and UI-hint,
specified via `direction` and `ui_hint` attributes.
Passing `1` is for input-only, `2` for out-only and `3` for
bidirectional (the default value). For example::
% echo 2 > functions/midi2.usb0/ep.0/block.0/direction
% echo 2 > functions/midi2.usb0/ep.0/block.0/ui_hint
When you need more than one Function Blocks, you can create
subdirectories `block.1`, `block.2`, etc dynamically, and configure
them in the configuration procedure above before linking.
For example, to create a second Function Block for a keyboard::
% mkdir functions/midi2.usb0/ep.0/block.1
% echo "Keyboard" > functions/midi2.usb0/ep.0/block.1/name
% echo 1 > functions/midi2.usb0/ep.0/block.1/first_group
% echo 1 > functions/midi2.usb0/ep.0/block.1/num_groups
% echo 1 > functions/midi2.usb0/ep.0/block.1/direction
% echo 1 > functions/midi2.usb0/ep.0/block.1/ui_hint
The `block.*` subdirectories can be removed dynamically, too (except
for `block.0` which is persistent).
For assigning a Function Block for MIDI 1.0 I/O, set up in `is_midi1`
attribute. 1 is for MIDI 1.0, and 2 is for MIDI 1.0 with low speed
connection::
% echo 2 > functions/midi2.usb0/ep.0/block.1/is_midi1
For disabling the processing of UMP Stream messages in the gadget
driver, pass `0` to `process_ump` attribute in the top-level config::
% echo 0 > functions/midi2.usb0/process_ump
The MIDI 1.0 interface at altset 0 is supported by the gadget driver,
too. When MIDI 1.0 interface is selected by the connected host, the
UMP I/O on the gadget is translated from/to USB MIDI 1.0 packets
accordingly while the gadget driver keeps communicating with the
user-space over UMP rawmidi.
MIDI 1.0 ports are set up from the config in each Function Block.
For example::
% echo 0 > functions/midi2.usb0/ep.0/block.0/midi1_first_group
% echo 1 > functions/midi2.usb0/ep.0/block.0/midi1_num_groups
The configuration above will enable the Group 1 (the index 0) for MIDI
1.0 interface. Note that those groups must be in the groups defined
for the Function Block itself.
여러 Endpoint와 Operation Mode
556-584Gadget driver는 UMP Endpoint를 여러 개 지원한다. Card top-level 아래 `ep.1`을 만들고 그 안에 Function Block을 생성한다. 예에서는 새 Endpoint Block에 4개 Group을 둔다.
% mkdir functions/midi2.usb0/ep.1
% mkdir functions/midi2.usb0/ep.1/block.0
% echo 4 > functions/midi2.usb0/ep.1/block.0/num_groups
이 구성은 rawmidi 장치 총 4개를 만든다. 앞의 두 개는 Endpoint 0과 1의 UMP rawmidi, 나머지 두 개는 각 Endpoint에 대응하는 legacy MIDI 1.0 rawmidi다.
Gadget의 현재 altsetting은 `RAWMIDI` iface의 `Operation Mode` control element로 알 수 있다. `amixer` 결과 `values=1`은 MIDI 1.0 altset 0, `2`는 MIDI 2.0 altset 1, `0`은 미설정이다. 현재는 binding 뒤 configuration을 바꿀 수 없다.
% amixer -c1 cget iface=RAWMIDI,name='Operation Mode'
; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
: values=2
두 Endpoint가 UMP와 legacy 장치를 각각 만든다.
The gadget driver supports more than one UMP Endpoints, too.
Similarly like the Function Blocks, you can create a new subdirectory
`ep.1` (but under the card top-level config) to enable a new Endpoint::
% mkdir functions/midi2.usb0/ep.1
and create a new Function Block there. For example, to create 4
Groups for the Function Block of this new Endpoint::
% mkdir functions/midi2.usb0/ep.1/block.0
% echo 4 > functions/midi2.usb0/ep.1/block.0/num_groups
Now, you'll have 4 rawmidi devices in total: the first two are UMP
rawmidi devices for Endpoint 0 and Endpoint 1, and other two for the
legacy MIDI 1.0 rawmidi devices corresponding to both EP 0 and EP 1.
The current altsetting on the gadget can be informed via a control
element "Operation Mode" with `RAWMIDI` iface. e.g. you can read it
via `amixer` program running on the gadget host like::
% amixer -c1 cget iface=RAWMIDI,name='Operation Mode'
; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
: values=2
The value (shown in the second returned line with `: values=`)
indicates 1 for MIDI 1.0 (altset 0), 2 for MIDI 2.0 (altset 1) and 0
for unset.
As of now, the configurations can't be changed after binding.
요약·해설
midi-2.0.rst:1-584Linux MIDI 2.0의 UMP rawmidi·sequencer ABI, Endpoint·Function Block 정보, MIDI 1.0/2.0 자동 변환과 configfs USB gadget 설정을 설명합니다.