zephyr/subsys/usb/device_next/class/usbd_midi2.c
Tomasz Moń b3eb6f2d11 usb: device_next: Reduce code size on Full-Speed only configuration
Allow compiler optimizations to remove High-Speed handling code. Knowing
that maximum operating speed is Full-Speed allows to reduce bulk buffers
from 512 to 64 bytes. More RAM optimizations are possible but this
commit only gets the low hanging fruits.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
2025-04-10 12:57:19 +02:00

794 lines
33 KiB
C

/*
* Copyright (c) 2024 Titouan Christophe
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT zephyr_midi2_device
#include <zephyr/drivers/usb/udc.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/usb/class/usbd_midi2.h>
#include <zephyr/usb/usbd.h>
#include "usbd_uac2_macros.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usbd_midi2, CONFIG_USBD_MIDI2_LOG_LEVEL);
#define MIDI1_ALTERNATE 0x00
#define MIDI2_ALTERNATE 0x01
UDC_BUF_POOL_DEFINE(usbd_midi_buf_pool, DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, 512U,
sizeof(struct udc_buf_info), NULL);
#define MIDI_QUEUE_SIZE 64
/* midi20 A.1 MS Class-Specific Interface Descriptor Types */
#define CS_GR_TRM_BLOCK 0x26
/* midi20 A.1 MS Class-Specific Interface Descriptor Subtypes */
#define MS_HEADER 0x01
/* midi20 A.2 MS Class-Specific Endpoint Descriptor Subtypes */
#define MS_GENERAL 0x01
#define MS_GENERAL_2_0 0x02
/* midi20 A.3 MS Class-Specific Group Terminal Block Descriptor Subtypes */
#define GR_TRM_BLOCK_HEADER 0x01
#define GR_TRM_BLOCK 0x02
/* midi20 A.6 Group Terminal Block Type */
#define GR_TRM_BIDIRECTIONAL 0x00
#define GR_TRM_INPUT_ONLY 0x01
#define GR_TRM_OUTPUT_ONLY 0x02
/* midi20 A.7 Group Terminal Default MIDI Protocol */
#define USE_MIDI_CI 0x00
#define MIDI_1_0_UP_TO_64_BITS 0x01
#define MIDI_1_0_UP_TO_64_BITS_JRTS 0x02
#define MIDI_1_0_UP_TO_128_BITS 0x03
#define MIDI_1_0_UP_TO_128_BITS_JRTS 0x04
#define MIDI_2_0 0x11
#define MIDI_2_0_JRTS 0x12
/* midi20: B.2.2 Class-specific AC Interface Descriptor */
struct usb_midi_cs_ac_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdADC;
uint16_t wTotalLength;
uint8_t bInCollection;
uint8_t baInterfaceNr1;
} __packed;
/* midi20 5.2.2.1 Class-Specific MS Interface Header Descriptor */
struct usb_midi_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdMSC;
uint16_t wTotalLength;
} __packed;
/* midi20 5.3.2 Class-Specific MIDI Streaming Data Endpoint Descriptor */
struct usb_midi_cs_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bNumGrpTrmBlock;
uint8_t baAssoGrpTrmBlkID[16];
} __packed;
/* midi20 5.4.1 Class Specific Group Terminal Block Header Descriptor */
struct usb_midi_grptrm_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t wTotalLength;
} __packed;
/* midi20 5.4.2.1 Group Terminal Block Descriptor */
struct usb_midi_grptrm_block_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bGrpTrmBlkID;
uint8_t bGrpTrmBlkType;
uint8_t nGroupTrm;
uint8_t nNumGroupTrm;
uint8_t iBlockItem;
uint8_t bMIDIProtocol;
uint16_t wMaxInputBandwidth;
uint16_t wMaxOutputBandwidth;
} __packed;
struct usbd_midi_descriptors {
struct usb_association_descriptor iad;
/* Standard AudioControl (AC) Interface Descriptor */
struct usb_if_descriptor if0_std;
struct usb_midi_cs_ac_header_descriptor if0_cs;
/* Empty MidiStreaming 1.0 on altsetting 0 */
struct usb_if_descriptor if1_0_std;
struct usb_midi_header_descriptor if1_0_ms_header;
struct usb_ep_descriptor if1_0_out_ep_fs;
struct usb_ep_descriptor if1_0_out_ep_hs;
struct usb_midi_cs_endpoint_descriptor if1_0_cs_out_ep;
struct usb_ep_descriptor if1_0_in_ep_fs;
struct usb_ep_descriptor if1_0_in_ep_hs;
struct usb_midi_cs_endpoint_descriptor if1_0_cs_in_ep;
/* MidiStreaming 2.0 on altsetting 1 */
struct usb_if_descriptor if1_1_std;
struct usb_midi_header_descriptor if1_1_ms_header;
struct usb_ep_descriptor if1_1_out_ep_fs;
struct usb_ep_descriptor if1_1_out_ep_hs;
struct usb_midi_cs_endpoint_descriptor if1_1_cs_out_ep;
struct usb_ep_descriptor if1_1_in_ep_fs;
struct usb_ep_descriptor if1_1_in_ep_hs;
struct usb_midi_cs_endpoint_descriptor if1_1_cs_in_ep;
/* MidiStreaming 2.0 Class-Specific Group Terminal Block Descriptors
* Retrievable by a Separate Get Request
*/
struct usb_midi_grptrm_header_descriptor grptrm_header;
struct usb_midi_grptrm_block_descriptor grptrm_blocks[16];
};
/* Device driver configuration */
struct usbd_midi_config {
struct usbd_midi_descriptors *desc;
struct usb_desc_header const **fs_descs;
struct usb_desc_header const **hs_descs;
};
/* Device driver data */
struct usbd_midi_data {
struct usbd_class_data *class_data;
struct k_work rx_work;
struct k_work tx_work;
uint8_t tx_queue_buf[MIDI_QUEUE_SIZE];
struct ring_buf tx_queue;
uint8_t altsetting;
struct usbd_midi_ops ops;
};
static void usbd_midi2_recv(const struct device *dev, struct net_buf *const buf)
{
struct usbd_midi_data *data = dev->data;
struct midi_ump ump;
LOG_HEXDUMP_DBG(buf->data, buf->len, "MIDI2 - Rx DATA");
while (buf->len >= 4) {
ump.data[0] = net_buf_pull_le32(buf);
for (size_t i = 1; i < UMP_NUM_WORDS(ump); i++) {
if (buf->len < 4) {
LOG_ERR("Incomplete UMP");
return;
}
ump.data[i] = net_buf_pull_le32(buf);
}
if (data->ops.rx_packet_cb) {
data->ops.rx_packet_cb(dev, ump);
}
}
if (buf->len) {
LOG_HEXDUMP_WRN(buf->data, buf->len, "Trailing data in Rx buffer");
}
}
static int usbd_midi_class_request(struct usbd_class_data *const class_data,
struct net_buf *const buf, const int err)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
const struct device *dev = usbd_class_get_private(class_data);
struct usbd_midi_data *data = dev->data;
struct udc_buf_info *info = udc_get_buf_info(buf);
LOG_DBG("MIDI2 request for %s ep=%02X len=%d err=%d",
dev->name, info->ep, buf->len, err);
if (err && err != -ECONNABORTED) {
LOG_ERR("Transfer error %d", err);
}
if (USB_EP_DIR_IS_OUT(info->ep)) {
usbd_midi2_recv(dev, buf);
k_work_submit(&data->rx_work);
} else {
LOG_HEXDUMP_DBG(buf->data, buf->len, "Tx DATA complete");
if (ring_buf_size_get(&data->tx_queue)) {
k_work_submit(&data->tx_work);
}
}
return usbd_ep_buf_free(uds_ctx, buf);
}
static void usbd_midi_class_update(struct usbd_class_data *const class_data,
const uint8_t iface, const uint8_t alternate)
{
const struct device *dev = usbd_class_get_private(class_data);
bool ready = false;
struct usbd_midi_data *data = dev->data;
LOG_DBG("update for %s: if=%u, alt=%u", dev->name, iface, alternate);
switch (alternate) {
case MIDI1_ALTERNATE:
data->altsetting = MIDI1_ALTERNATE;
LOG_WRN("%s set USB-MIDI1.0 altsetting (not implemented !)", dev->name);
break;
case MIDI2_ALTERNATE:
data->altsetting = MIDI2_ALTERNATE;
ready = true;
LOG_INF("%s set USB-MIDI2.0 altsetting", dev->name);
break;
}
if (data->ops.ready_cb) {
data->ops.ready_cb(dev, ready);
}
}
static void usbd_midi_class_enable(struct usbd_class_data *const class_data)
{
const struct device *dev = usbd_class_get_private(class_data);
struct usbd_midi_data *data = dev->data;
if (data->altsetting == MIDI2_ALTERNATE && data->ops.ready_cb) {
data->ops.ready_cb(dev, true);
}
LOG_DBG("Enable %s", dev->name);
k_work_submit(&data->rx_work);
}
static void usbd_midi_class_disable(struct usbd_class_data *const class_data)
{
const struct device *dev = usbd_class_get_private(class_data);
struct usbd_midi_data *data = dev->data;
if (data->ops.ready_cb) {
data->ops.ready_cb(dev, false);
}
LOG_DBG("Disable %s", dev->name);
k_work_cancel(&data->rx_work);
}
static void usbd_midi_class_suspended(struct usbd_class_data *const class_data)
{
const struct device *dev = usbd_class_get_private(class_data);
struct usbd_midi_data *data = dev->data;
if (data->ops.ready_cb) {
data->ops.ready_cb(dev, false);
}
LOG_DBG("Suspend %s", dev->name);
k_work_cancel(&data->rx_work);
}
static void usbd_midi_class_resumed(struct usbd_class_data *const class_data)
{
const struct device *dev = usbd_class_get_private(class_data);
struct usbd_midi_data *data = dev->data;
if (data->altsetting == MIDI2_ALTERNATE && data->ops.ready_cb) {
data->ops.ready_cb(dev, true);
}
LOG_DBG("Resume %s", dev->name);
k_work_submit(&data->rx_work);
}
static int usbd_midi_class_cth(struct usbd_class_data *const class_data,
const struct usb_setup_packet *const setup,
struct net_buf *const buf)
{
const struct device *dev = usbd_class_get_private(class_data);
const struct usbd_midi_config *config = dev->config;
struct usbd_midi_data *data = dev->data;
size_t head_len = config->desc->grptrm_header.bLength;
size_t total_len = sys_le16_to_cpu(config->desc->grptrm_header.wTotalLength);
LOG_DBG("Control to host for %s", dev->name);
LOG_DBG(" bmRequestType=%02X bRequest=%02X wValue=%04X wIndex=%04X wLength=%04X",
setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex,
setup->wLength);
/* Only support Group Terminal blocks retrieved with
* midi20 6. Class Specific Command: Group Terminal Blocks Descriptors Request
*/
if (data->altsetting != MIDI2_ALTERNATE ||
setup->bRequest != USB_SREQ_GET_DESCRIPTOR ||
setup->wValue != ((CS_GR_TRM_BLOCK << 8) | MIDI2_ALTERNATE)) {
errno = -ENOTSUP;
return 0;
}
/* Group terminal block header */
net_buf_add_mem(buf, (void *) &config->desc->grptrm_header,
MIN(head_len, setup->wLength));
/* Group terminal blocks */
if (setup->wLength > head_len && total_len > head_len) {
net_buf_add_mem(buf, (void *) &config->desc->grptrm_blocks,
MIN(total_len, setup->wLength) - head_len);
}
LOG_HEXDUMP_DBG(buf->data, buf->len, "Control to host");
return 0;
}
static int usbd_midi_class_init(struct usbd_class_data *const class_data)
{
const struct device *dev = usbd_class_get_private(class_data);
LOG_DBG("Init %s device class", dev->name);
return 0;
}
static void *usbd_midi_class_get_desc(struct usbd_class_data *const class_data,
const enum usbd_speed speed)
{
const struct device *dev = usbd_class_get_private(class_data);
const struct usbd_midi_config *config = dev->config;
LOG_DBG("Get descriptors for %s", dev->name);
if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
return config->hs_descs;
}
return config->fs_descs;
}
static struct usbd_class_api usbd_midi_class_api = {
.request = usbd_midi_class_request,
.update = usbd_midi_class_update,
.enable = usbd_midi_class_enable,
.disable = usbd_midi_class_disable,
.suspended = usbd_midi_class_suspended,
.resumed = usbd_midi_class_resumed,
.control_to_host = usbd_midi_class_cth,
.init = usbd_midi_class_init,
.get_desc = usbd_midi_class_get_desc,
};
static struct net_buf *usbd_midi_buf_alloc(uint8_t ep)
{
struct udc_buf_info *info;
struct net_buf *buf;
buf = net_buf_alloc(&usbd_midi_buf_pool, K_NO_WAIT);
if (!buf) {
return NULL;
}
info = udc_get_buf_info(buf);
info->ep = ep;
return buf;
}
static uint8_t usbd_midi_get_bulk_in(struct usbd_class_data *const class_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
const struct device *dev = usbd_class_get_private(class_data);
const struct usbd_midi_config *cfg = dev->config;
if (USBD_SUPPORTS_HIGH_SPEED &&
usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return cfg->desc->if1_1_in_ep_hs.bEndpointAddress;
}
return cfg->desc->if1_1_in_ep_fs.bEndpointAddress;
}
static uint8_t usbd_midi_get_bulk_out(struct usbd_class_data *const class_data)
{
struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
const struct device *dev = usbd_class_get_private(class_data);
const struct usbd_midi_config *cfg = dev->config;
if (USBD_SUPPORTS_HIGH_SPEED &&
usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
return cfg->desc->if1_1_out_ep_hs.bEndpointAddress;
}
return cfg->desc->if1_1_out_ep_fs.bEndpointAddress;
}
static void usbd_midi_rx_work(struct k_work *work)
{
struct usbd_midi_data *data = CONTAINER_OF(work, struct usbd_midi_data, rx_work);
struct net_buf *buf;
int ret;
buf = usbd_midi_buf_alloc(usbd_midi_get_bulk_out(data->class_data));
if (buf == NULL) {
LOG_WRN("Unable to allocate Rx net_buf");
return;
}
LOG_DBG("Enqueue Rx...");
ret = usbd_ep_enqueue(data->class_data, buf);
if (ret) {
LOG_ERR("Failed to enqueue Rx net_buf -> %d", ret);
net_buf_unref(buf);
}
}
static void usbd_midi_tx_work(struct k_work *work)
{
struct usbd_midi_data *data = CONTAINER_OF(work, struct usbd_midi_data, tx_work);
struct net_buf *buf;
int ret;
buf = usbd_midi_buf_alloc(usbd_midi_get_bulk_in(data->class_data));
if (buf == NULL) {
LOG_ERR("Unable to allocate Tx net_buf");
return;
}
net_buf_add(buf, ring_buf_get(&data->tx_queue, buf->data, buf->size));
LOG_HEXDUMP_DBG(buf->data, buf->len, "MIDI2 - Tx DATA");
ret = usbd_ep_enqueue(data->class_data, buf);
if (ret) {
LOG_ERR("Failed to enqueue Tx net_buf -> %d", ret);
net_buf_unref(buf);
}
}
static int usbd_midi_preinit(const struct device *dev)
{
struct usbd_midi_data *data = dev->data;
LOG_DBG("Init device %s", dev->name);
ring_buf_init(&data->tx_queue, MIDI_QUEUE_SIZE, data->tx_queue_buf);
k_work_init(&data->rx_work, usbd_midi_rx_work);
k_work_init(&data->tx_work, usbd_midi_tx_work);
return 0;
}
int usbd_midi_send(const struct device *dev, const struct midi_ump ump)
{
struct usbd_midi_data *data = dev->data;
size_t words = UMP_NUM_WORDS(ump);
size_t buflen = 4 * words;
uint32_t word;
LOG_DBG("Send MT=%X group=%X", UMP_MT(ump), UMP_GROUP(ump));
if (data->altsetting != MIDI2_ALTERNATE) {
LOG_WRN("MIDI2.0 is not enabled");
return -EIO;
}
if (buflen > ring_buf_space_get(&data->tx_queue)) {
LOG_WRN("Not enough space in tx queue");
return -ENOBUFS;
}
for (size_t i = 0; i < words; i++) {
word = sys_cpu_to_le32(ump.data[i]);
ring_buf_put(&data->tx_queue, (const uint8_t *)&word, 4);
}
k_work_submit(&data->tx_work);
return 0;
}
void usbd_midi_set_ops(const struct device *dev, const struct usbd_midi_ops *ops)
{
struct usbd_midi_data *data = dev->data;
if (ops == NULL) {
memset(&data->ops, 0, sizeof(struct usbd_midi_ops));
} else {
memcpy(&data->ops, ops, sizeof(struct usbd_midi_ops));
}
LOG_DBG("Set ops for %s to %p", dev->name, ops);
}
/* Group Terminal Block unique identification number, type and protocol
* see midi20 5.4.2 Group Terminal Block Descriptor
*/
#define GRPTRM_BLOCK_ID(node) UTIL_INC(DT_NODE_CHILD_IDX(node))
#define GRPTRM_BLOCK_TYPE(node) \
COND_CODE_1(DT_ENUM_HAS_VALUE(node, terminal_type, input_only), \
(GR_TRM_INPUT_ONLY), \
(COND_CODE_1(DT_ENUM_HAS_VALUE(node, terminal_type, output_only), \
(GR_TRM_OUTPUT_ONLY), \
(GR_TRM_BIDIRECTIONAL) \
)) \
)
#define GRPTRM_PROTOCOL(node) \
COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi2), \
(MIDI_2_0), \
(COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi1_up_to_64b), \
(MIDI_1_0_UP_TO_64_BITS), \
(COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi1_up_to_128b), \
(MIDI_1_0_UP_TO_128_BITS), \
(USE_MIDI_CI) \
)) \
)) \
)
/* Group Terminal Block unique identification number with a trailing comma
* if that block is bidirectional or of given terminal type; otherwise empty
*/
#define GRPTRM_BLOCK_ID_SEP_IF(node, ttype) \
IF_ENABLED( \
UTIL_OR(DT_ENUM_HAS_VALUE(node, terminal_type, bidirectional), \
DT_ENUM_HAS_VALUE(node, terminal_type, ttype)), \
(GRPTRM_BLOCK_ID(node),))
/* All unique identification numbers of output+bidir group terminal blocks */
#define GRPTRM_OUTPUT_BLOCK_IDS(n) \
DT_INST_FOREACH_CHILD_VARGS(n, GRPTRM_BLOCK_ID_SEP_IF, output_only)
/* All unique identification numbers of input+bidir group terminal blocks */
#define GRPTRM_INPUT_BLOCK_IDS(n) \
DT_INST_FOREACH_CHILD_VARGS(n, GRPTRM_BLOCK_ID_SEP_IF, input_only)
#define N_INPUTS(n) sizeof((uint8_t[]){GRPTRM_INPUT_BLOCK_IDS(n)})
#define N_OUTPUTS(n) sizeof((uint8_t[]){GRPTRM_OUTPUT_BLOCK_IDS(n)})
#define USBD_MIDI_VALIDATE_GRPTRM_BLOCK(node) \
BUILD_ASSERT(DT_REG_ADDR(node) < 16, \
"Group Terminal Block address must be within 0..15"); \
BUILD_ASSERT(DT_REG_ADDR(node) + DT_REG_SIZE(node) <= 16, \
"Too many Group Terminals in this Block");
#define USBD_MIDI_VALIDATE_INSTANCE(n) \
DT_INST_FOREACH_CHILD(n, USBD_MIDI_VALIDATE_GRPTRM_BLOCK)
#define USBD_MIDI2_INIT_GRPTRM_BLOCK_DESCRIPTOR(node) \
{ \
.bLength = sizeof(struct usb_midi_grptrm_block_descriptor), \
.bDescriptorType = CS_GR_TRM_BLOCK, \
.bDescriptorSubtype = GR_TRM_BLOCK, \
.bGrpTrmBlkID = GRPTRM_BLOCK_ID(node), \
.bGrpTrmBlkType = GRPTRM_BLOCK_TYPE(node), \
.nGroupTrm = DT_REG_ADDR(node), \
.nNumGroupTrm = DT_REG_SIZE(node), \
.iBlockItem = 0, \
.bMIDIProtocol = GRPTRM_PROTOCOL(node), \
.wMaxInputBandwidth = 0x0000, \
.wMaxOutputBandwidth = 0x0000, \
}
#define USBD_MIDI2_GRPTRM_TOTAL_LEN(n) \
sizeof(struct usb_midi_grptrm_header_descriptor) \
+ DT_INST_CHILD_NUM_STATUS_OKAY(n) \
* sizeof(struct usb_midi_grptrm_block_descriptor)
#define USBD_MIDI_DEFINE_DESCRIPTORS(n) \
static struct usbd_midi_descriptors usbd_midi_desc_##n = { \
.iad = { \
.bLength = sizeof(struct usb_association_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
.bFirstInterface = 0, \
.bInterfaceCount = 2, \
.bFunctionClass = AUDIO, \
.bFunctionSubClass = MIDISTREAMING, \
}, \
.if0_std = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 0, \
.bAlternateSetting = 0, \
.bNumEndpoints = 0, \
.bInterfaceClass = AUDIO, \
.bInterfaceSubClass = AUDIOCONTROL, \
}, \
.if0_cs = { \
.bLength = sizeof(struct usb_midi_cs_ac_header_descriptor), \
.bDescriptorType = USB_DESC_CS_INTERFACE, \
.bDescriptorSubtype = MS_HEADER, \
.bcdADC = sys_cpu_to_le16(0x0100), \
.wTotalLength = sizeof(struct usb_midi_cs_ac_header_descriptor), \
.bInCollection = 1, \
.baInterfaceNr1 = 1, \
}, \
.if1_0_std = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 1, \
.bAlternateSetting = MIDI1_ALTERNATE, \
.bNumEndpoints = 2, \
.bInterfaceClass = AUDIO, \
.bInterfaceSubClass = MIDISTREAMING, \
}, \
.if1_0_ms_header = { \
.bLength = sizeof(struct usb_midi_header_descriptor), \
.bDescriptorType = USB_DESC_CS_INTERFACE, \
.bDescriptorSubtype = MS_HEADER, \
.bcdMSC = sys_cpu_to_le16(0x0100), \
.wTotalLength = sys_cpu_to_le16( \
sizeof(struct usb_midi_header_descriptor) \
+ 2 * (sizeof(struct usb_ep_descriptor) + 4) \
), \
}, \
.if1_0_out_ep_fs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(64U), \
}, \
.if1_0_out_ep_hs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(512U), \
}, \
.if1_0_cs_out_ep = { \
.bLength = 4, \
.bDescriptorType = USB_DESC_CS_ENDPOINT, \
.bDescriptorSubtype = MS_GENERAL, \
.bNumGrpTrmBlock = 0, \
}, \
.if1_0_in_ep_fs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_IN_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(64U), \
}, \
.if1_0_in_ep_hs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_IN_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(512U), \
}, \
.if1_0_cs_in_ep = { \
.bLength = 4 + N_INPUTS(n), \
.bDescriptorType = USB_DESC_CS_ENDPOINT, \
.bDescriptorSubtype = MS_GENERAL, \
.bNumGrpTrmBlock = 0, \
}, \
.if1_1_std = { \
.bLength = sizeof(struct usb_if_descriptor), \
.bDescriptorType = USB_DESC_INTERFACE, \
.bInterfaceNumber = 1, \
.bAlternateSetting = MIDI2_ALTERNATE, \
.bNumEndpoints = 2, \
.bInterfaceClass = AUDIO, \
.bInterfaceSubClass = MIDISTREAMING, \
}, \
.if1_1_ms_header = { \
.bLength = sizeof(struct usb_midi_header_descriptor), \
.bDescriptorType = USB_DESC_CS_INTERFACE, \
.bDescriptorSubtype = MS_HEADER, \
.bcdMSC = sys_cpu_to_le16(0x0200), \
.wTotalLength = sys_cpu_to_le16( \
sizeof(struct usb_midi_header_descriptor)), \
}, \
.if1_1_out_ep_fs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(64U), \
}, \
.if1_1_out_ep_hs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(512U), \
}, \
.if1_1_cs_out_ep = { \
.bLength = 4 + N_OUTPUTS(n), \
.bDescriptorType = USB_DESC_CS_ENDPOINT, \
.bDescriptorSubtype = MS_GENERAL_2_0, \
.bNumGrpTrmBlock = N_OUTPUTS(n), \
.baAssoGrpTrmBlkID = {GRPTRM_OUTPUT_BLOCK_IDS(n)}, \
}, \
.if1_1_in_ep_fs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_IN_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(64U), \
}, \
.if1_1_in_ep_hs = { \
.bLength = sizeof(struct usb_ep_descriptor), \
.bDescriptorType = USB_DESC_ENDPOINT, \
.bEndpointAddress = n + FIRST_IN_EP_ADDR, \
.bmAttributes = USB_EP_TYPE_BULK, \
.wMaxPacketSize = sys_cpu_to_le16(512U), \
}, \
.if1_1_cs_in_ep = { \
.bLength = 4 + N_INPUTS(n), \
.bDescriptorType = USB_DESC_CS_ENDPOINT, \
.bDescriptorSubtype = MS_GENERAL_2_0, \
.bNumGrpTrmBlock = N_INPUTS(n), \
.baAssoGrpTrmBlkID = {GRPTRM_INPUT_BLOCK_IDS(n)}, \
}, \
.grptrm_header = { \
.bLength = sizeof(struct usb_midi_grptrm_header_descriptor), \
.bDescriptorType = CS_GR_TRM_BLOCK, \
.bDescriptorSubtype = GR_TRM_BLOCK_HEADER, \
.wTotalLength = sys_cpu_to_le16( \
USBD_MIDI2_GRPTRM_TOTAL_LEN(n) \
), \
}, \
.grptrm_blocks = { \
DT_INST_FOREACH_CHILD_SEP( \
n, USBD_MIDI2_INIT_GRPTRM_BLOCK_DESCRIPTOR, (,) \
) \
}, \
}; \
static const struct usb_desc_header *usbd_midi_desc_array_fs_##n[] = { \
(struct usb_desc_header *)&usbd_midi_desc_##n.iad, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if0_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if0_cs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_ms_header, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_out_ep_fs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_out_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_in_ep_fs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_in_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_ms_header, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_out_ep_fs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_out_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_in_ep_fs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_in_ep, \
NULL, \
}; \
static const struct usb_desc_header *usbd_midi_desc_array_hs_##n[] = { \
(struct usb_desc_header *)&usbd_midi_desc_##n.iad, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if0_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if0_cs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_ms_header, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_out_ep_hs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_out_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_in_ep_hs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_in_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_std, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_ms_header, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_out_ep_hs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_out_ep, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_in_ep_hs, \
(struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_in_ep, \
NULL, \
};
#define USBD_MIDI_DEFINE_DEVICE(n) \
USBD_MIDI_VALIDATE_INSTANCE(n) \
USBD_MIDI_DEFINE_DESCRIPTORS(n); \
USBD_DEFINE_CLASS(midi_##n, &usbd_midi_class_api, \
(void *)DEVICE_DT_GET(DT_DRV_INST(n)), NULL); \
static const struct usbd_midi_config usbd_midi_config_##n = { \
.desc = &usbd_midi_desc_##n, \
.fs_descs = usbd_midi_desc_array_fs_##n, \
.hs_descs = usbd_midi_desc_array_hs_##n, \
}; \
static struct usbd_midi_data usbd_midi_data_##n = { \
.class_data = &midi_##n, \
.altsetting = MIDI1_ALTERNATE, \
}; \
DEVICE_DT_INST_DEFINE(n, usbd_midi_preinit, NULL, \
&usbd_midi_data_##n, &usbd_midi_config_##n, \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
DT_INST_FOREACH_STATUS_OKAY(USBD_MIDI_DEFINE_DEVICE)