1 /*
2 * Copyright (c) 2024 Titouan Christophe
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT zephyr_midi2_device
8
9 #include <zephyr/drivers/usb/udc.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/sys/ring_buffer.h>
12 #include <zephyr/usb/class/usbd_midi2.h>
13 #include <zephyr/usb/usbd.h>
14
15 #include "usbd_uac2_macros.h"
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(usbd_midi2, CONFIG_USBD_MIDI2_LOG_LEVEL);
19
20 #define MIDI1_ALTERNATE 0x00
21 #define MIDI2_ALTERNATE 0x01
22
23 UDC_BUF_POOL_DEFINE(usbd_midi_buf_pool, DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, 512U,
24 sizeof(struct udc_buf_info), NULL);
25
26 #define MIDI_QUEUE_SIZE 64
27
28 /* midi20 A.1 MS Class-Specific Interface Descriptor Types */
29 #define CS_GR_TRM_BLOCK 0x26
30 /* midi20 A.1 MS Class-Specific Interface Descriptor Subtypes */
31 #define MS_HEADER 0x01
32
33 /* midi20 A.2 MS Class-Specific Endpoint Descriptor Subtypes */
34 #define MS_GENERAL 0x01
35 #define MS_GENERAL_2_0 0x02
36
37 /* midi20 A.3 MS Class-Specific Group Terminal Block Descriptor Subtypes */
38 #define GR_TRM_BLOCK_HEADER 0x01
39 #define GR_TRM_BLOCK 0x02
40
41 /* midi20 A.6 Group Terminal Block Type */
42 #define GR_TRM_BIDIRECTIONAL 0x00
43 #define GR_TRM_INPUT_ONLY 0x01
44 #define GR_TRM_OUTPUT_ONLY 0x02
45
46 /* midi20 A.7 Group Terminal Default MIDI Protocol */
47 #define USE_MIDI_CI 0x00
48 #define MIDI_1_0_UP_TO_64_BITS 0x01
49 #define MIDI_1_0_UP_TO_64_BITS_JRTS 0x02
50 #define MIDI_1_0_UP_TO_128_BITS 0x03
51 #define MIDI_1_0_UP_TO_128_BITS_JRTS 0x04
52 #define MIDI_2_0 0x11
53 #define MIDI_2_0_JRTS 0x12
54
55 /* midi20: B.2.2 Class-specific AC Interface Descriptor */
56 struct usb_midi_cs_ac_header_descriptor {
57 uint8_t bLength;
58 uint8_t bDescriptorType;
59 uint8_t bDescriptorSubtype;
60 uint16_t bcdADC;
61 uint16_t wTotalLength;
62 uint8_t bInCollection;
63 uint8_t baInterfaceNr1;
64 } __packed;
65
66 /* midi20 5.2.2.1 Class-Specific MS Interface Header Descriptor */
67 struct usb_midi_header_descriptor {
68 uint8_t bLength;
69 uint8_t bDescriptorType;
70 uint8_t bDescriptorSubtype;
71 uint16_t bcdMSC;
72 uint16_t wTotalLength;
73 } __packed;
74
75 /* midi20 5.3.2 Class-Specific MIDI Streaming Data Endpoint Descriptor */
76 struct usb_midi_cs_endpoint_descriptor {
77 uint8_t bLength;
78 uint8_t bDescriptorType;
79 uint8_t bDescriptorSubtype;
80 uint8_t bNumGrpTrmBlock;
81 uint8_t baAssoGrpTrmBlkID[16];
82 } __packed;
83
84 /* midi20 5.4.1 Class Specific Group Terminal Block Header Descriptor */
85 struct usb_midi_grptrm_header_descriptor {
86 uint8_t bLength;
87 uint8_t bDescriptorType;
88 uint8_t bDescriptorSubtype;
89 uint16_t wTotalLength;
90 } __packed;
91
92 /* midi20 5.4.2.1 Group Terminal Block Descriptor */
93 struct usb_midi_grptrm_block_descriptor {
94 uint8_t bLength;
95 uint8_t bDescriptorType;
96 uint8_t bDescriptorSubtype;
97 uint8_t bGrpTrmBlkID;
98 uint8_t bGrpTrmBlkType;
99 uint8_t nGroupTrm;
100 uint8_t nNumGroupTrm;
101 uint8_t iBlockItem;
102 uint8_t bMIDIProtocol;
103 uint16_t wMaxInputBandwidth;
104 uint16_t wMaxOutputBandwidth;
105 } __packed;
106
107 struct usbd_midi_descriptors {
108 struct usb_association_descriptor iad;
109
110 /* Standard AudioControl (AC) Interface Descriptor */
111 struct usb_if_descriptor if0_std;
112 struct usb_midi_cs_ac_header_descriptor if0_cs;
113
114 /* Empty MidiStreaming 1.0 on altsetting 0 */
115 struct usb_if_descriptor if1_0_std;
116 struct usb_midi_header_descriptor if1_0_ms_header;
117 struct usb_ep_descriptor if1_0_out_ep_fs;
118 struct usb_ep_descriptor if1_0_out_ep_hs;
119 struct usb_midi_cs_endpoint_descriptor if1_0_cs_out_ep;
120 struct usb_ep_descriptor if1_0_in_ep_fs;
121 struct usb_ep_descriptor if1_0_in_ep_hs;
122 struct usb_midi_cs_endpoint_descriptor if1_0_cs_in_ep;
123
124 /* MidiStreaming 2.0 on altsetting 1 */
125 struct usb_if_descriptor if1_1_std;
126 struct usb_midi_header_descriptor if1_1_ms_header;
127 struct usb_ep_descriptor if1_1_out_ep_fs;
128 struct usb_ep_descriptor if1_1_out_ep_hs;
129 struct usb_midi_cs_endpoint_descriptor if1_1_cs_out_ep;
130 struct usb_ep_descriptor if1_1_in_ep_fs;
131 struct usb_ep_descriptor if1_1_in_ep_hs;
132 struct usb_midi_cs_endpoint_descriptor if1_1_cs_in_ep;
133
134 /* MidiStreaming 2.0 Class-Specific Group Terminal Block Descriptors
135 * Retrievable by a Separate Get Request
136 */
137 struct usb_midi_grptrm_header_descriptor grptrm_header;
138 struct usb_midi_grptrm_block_descriptor grptrm_blocks[16];
139 };
140
141 /* Device driver configuration */
142 struct usbd_midi_config {
143 struct usbd_midi_descriptors *desc;
144 struct usb_desc_header const **fs_descs;
145 struct usb_desc_header const **hs_descs;
146 };
147
148 /* Device driver data */
149 struct usbd_midi_data {
150 struct usbd_class_data *class_data;
151 struct k_work rx_work;
152 struct k_work tx_work;
153 uint8_t tx_queue_buf[MIDI_QUEUE_SIZE];
154 struct ring_buf tx_queue;
155 uint8_t altsetting;
156 struct usbd_midi_ops ops;
157 };
158
usbd_midi2_recv(const struct device * dev,struct net_buf * const buf)159 static void usbd_midi2_recv(const struct device *dev, struct net_buf *const buf)
160 {
161 struct usbd_midi_data *data = dev->data;
162 struct midi_ump ump;
163
164 LOG_HEXDUMP_DBG(buf->data, buf->len, "MIDI2 - Rx DATA");
165 while (buf->len >= 4) {
166 ump.data[0] = net_buf_pull_le32(buf);
167 for (size_t i = 1; i < UMP_NUM_WORDS(ump); i++) {
168 if (buf->len < 4) {
169 LOG_ERR("Incomplete UMP");
170 return;
171 }
172 ump.data[i] = net_buf_pull_le32(buf);
173 }
174
175 if (data->ops.rx_packet_cb) {
176 data->ops.rx_packet_cb(dev, ump);
177 }
178 }
179
180 if (buf->len) {
181 LOG_HEXDUMP_WRN(buf->data, buf->len, "Trailing data in Rx buffer");
182 }
183 }
184
usbd_midi_class_request(struct usbd_class_data * const class_data,struct net_buf * const buf,const int err)185 static int usbd_midi_class_request(struct usbd_class_data *const class_data,
186 struct net_buf *const buf, const int err)
187 {
188 struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
189 const struct device *dev = usbd_class_get_private(class_data);
190 struct usbd_midi_data *data = dev->data;
191 struct udc_buf_info *info = udc_get_buf_info(buf);
192
193 LOG_DBG("MIDI2 request for %s ep=%02X len=%d err=%d",
194 dev->name, info->ep, buf->len, err);
195
196 if (err && err != -ECONNABORTED) {
197 LOG_ERR("Transfer error %d", err);
198 }
199 if (USB_EP_DIR_IS_OUT(info->ep)) {
200 usbd_midi2_recv(dev, buf);
201 k_work_submit(&data->rx_work);
202 } else {
203 LOG_HEXDUMP_DBG(buf->data, buf->len, "Tx DATA complete");
204 if (ring_buf_size_get(&data->tx_queue)) {
205 k_work_submit(&data->tx_work);
206 }
207 }
208
209 return usbd_ep_buf_free(uds_ctx, buf);
210 }
211
usbd_midi_class_update(struct usbd_class_data * const class_data,const uint8_t iface,const uint8_t alternate)212 static void usbd_midi_class_update(struct usbd_class_data *const class_data,
213 const uint8_t iface, const uint8_t alternate)
214 {
215 const struct device *dev = usbd_class_get_private(class_data);
216 bool ready = false;
217 struct usbd_midi_data *data = dev->data;
218
219 LOG_DBG("update for %s: if=%u, alt=%u", dev->name, iface, alternate);
220
221 switch (alternate) {
222 case MIDI1_ALTERNATE:
223 data->altsetting = MIDI1_ALTERNATE;
224 LOG_WRN("%s set USB-MIDI1.0 altsetting (not implemented !)", dev->name);
225 break;
226 case MIDI2_ALTERNATE:
227 data->altsetting = MIDI2_ALTERNATE;
228 ready = true;
229 LOG_INF("%s set USB-MIDI2.0 altsetting", dev->name);
230 break;
231 }
232
233 if (data->ops.ready_cb) {
234 data->ops.ready_cb(dev, ready);
235 }
236 }
237
usbd_midi_class_enable(struct usbd_class_data * const class_data)238 static void usbd_midi_class_enable(struct usbd_class_data *const class_data)
239 {
240 const struct device *dev = usbd_class_get_private(class_data);
241 struct usbd_midi_data *data = dev->data;
242
243 if (data->altsetting == MIDI2_ALTERNATE && data->ops.ready_cb) {
244 data->ops.ready_cb(dev, true);
245 }
246
247 LOG_DBG("Enable %s", dev->name);
248 k_work_submit(&data->rx_work);
249 }
250
usbd_midi_class_disable(struct usbd_class_data * const class_data)251 static void usbd_midi_class_disable(struct usbd_class_data *const class_data)
252 {
253 const struct device *dev = usbd_class_get_private(class_data);
254 struct usbd_midi_data *data = dev->data;
255
256 if (data->ops.ready_cb) {
257 data->ops.ready_cb(dev, false);
258 }
259
260 LOG_DBG("Disable %s", dev->name);
261 k_work_cancel(&data->rx_work);
262 }
263
usbd_midi_class_suspended(struct usbd_class_data * const class_data)264 static void usbd_midi_class_suspended(struct usbd_class_data *const class_data)
265 {
266 const struct device *dev = usbd_class_get_private(class_data);
267 struct usbd_midi_data *data = dev->data;
268
269 if (data->ops.ready_cb) {
270 data->ops.ready_cb(dev, false);
271 }
272
273 LOG_DBG("Suspend %s", dev->name);
274 k_work_cancel(&data->rx_work);
275 }
276
usbd_midi_class_resumed(struct usbd_class_data * const class_data)277 static void usbd_midi_class_resumed(struct usbd_class_data *const class_data)
278 {
279 const struct device *dev = usbd_class_get_private(class_data);
280 struct usbd_midi_data *data = dev->data;
281
282 if (data->altsetting == MIDI2_ALTERNATE && data->ops.ready_cb) {
283 data->ops.ready_cb(dev, true);
284 }
285
286 LOG_DBG("Resume %s", dev->name);
287 k_work_submit(&data->rx_work);
288 }
289
usbd_midi_class_cth(struct usbd_class_data * const class_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)290 static int usbd_midi_class_cth(struct usbd_class_data *const class_data,
291 const struct usb_setup_packet *const setup,
292 struct net_buf *const buf)
293 {
294 const struct device *dev = usbd_class_get_private(class_data);
295 const struct usbd_midi_config *config = dev->config;
296 struct usbd_midi_data *data = dev->data;
297
298 size_t head_len = config->desc->grptrm_header.bLength;
299 size_t total_len = sys_le16_to_cpu(config->desc->grptrm_header.wTotalLength);
300
301 LOG_DBG("Control to host for %s", dev->name);
302 LOG_DBG(" bmRequestType=%02X bRequest=%02X wValue=%04X wIndex=%04X wLength=%04X",
303 setup->bmRequestType, setup->bRequest, setup->wValue, setup->wIndex,
304 setup->wLength);
305
306 /* Only support Group Terminal blocks retrieved with
307 * midi20 6. Class Specific Command: Group Terminal Blocks Descriptors Request
308 */
309 if (data->altsetting != MIDI2_ALTERNATE ||
310 setup->bRequest != USB_SREQ_GET_DESCRIPTOR ||
311 setup->wValue != ((CS_GR_TRM_BLOCK << 8) | MIDI2_ALTERNATE)) {
312 errno = -ENOTSUP;
313 return 0;
314 }
315
316 /* Group terminal block header */
317 net_buf_add_mem(buf, (void *) &config->desc->grptrm_header,
318 MIN(head_len, setup->wLength));
319
320 /* Group terminal blocks */
321 if (setup->wLength > head_len && total_len > head_len) {
322 net_buf_add_mem(buf, (void *) &config->desc->grptrm_blocks,
323 MIN(total_len, setup->wLength) - head_len);
324 }
325 LOG_HEXDUMP_DBG(buf->data, buf->len, "Control to host");
326
327 return 0;
328 }
329
usbd_midi_class_init(struct usbd_class_data * const class_data)330 static int usbd_midi_class_init(struct usbd_class_data *const class_data)
331 {
332 const struct device *dev = usbd_class_get_private(class_data);
333
334 LOG_DBG("Init %s device class", dev->name);
335
336 return 0;
337 }
338
usbd_midi_class_get_desc(struct usbd_class_data * const class_data,const enum usbd_speed speed)339 static void *usbd_midi_class_get_desc(struct usbd_class_data *const class_data,
340 const enum usbd_speed speed)
341 {
342 const struct device *dev = usbd_class_get_private(class_data);
343 const struct usbd_midi_config *config = dev->config;
344
345 LOG_DBG("Get descriptors for %s", dev->name);
346
347 return (speed == USBD_SPEED_HS) ? config->hs_descs : config->fs_descs;
348 }
349
350
351 static struct usbd_class_api usbd_midi_class_api = {
352 .request = usbd_midi_class_request,
353 .update = usbd_midi_class_update,
354 .enable = usbd_midi_class_enable,
355 .disable = usbd_midi_class_disable,
356 .suspended = usbd_midi_class_suspended,
357 .resumed = usbd_midi_class_resumed,
358 .control_to_host = usbd_midi_class_cth,
359 .init = usbd_midi_class_init,
360 .get_desc = usbd_midi_class_get_desc,
361 };
362
usbd_midi_buf_alloc(uint8_t ep)363 static struct net_buf *usbd_midi_buf_alloc(uint8_t ep)
364 {
365 struct udc_buf_info *info;
366 struct net_buf *buf;
367
368 buf = net_buf_alloc(&usbd_midi_buf_pool, K_NO_WAIT);
369 if (!buf) {
370 return NULL;
371 }
372
373 info = udc_get_buf_info(buf);
374 info->ep = ep;
375
376 return buf;
377 }
378
usbd_midi_get_bulk_in(struct usbd_class_data * const class_data)379 static uint8_t usbd_midi_get_bulk_in(struct usbd_class_data *const class_data)
380 {
381 struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
382 const struct device *dev = usbd_class_get_private(class_data);
383 const struct usbd_midi_config *cfg = dev->config;
384
385 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
386 return cfg->desc->if1_1_in_ep_hs.bEndpointAddress;
387 }
388
389 return cfg->desc->if1_1_in_ep_fs.bEndpointAddress;
390 }
391
usbd_midi_get_bulk_out(struct usbd_class_data * const class_data)392 static uint8_t usbd_midi_get_bulk_out(struct usbd_class_data *const class_data)
393 {
394 struct usbd_context *uds_ctx = usbd_class_get_ctx(class_data);
395 const struct device *dev = usbd_class_get_private(class_data);
396 const struct usbd_midi_config *cfg = dev->config;
397
398 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
399 return cfg->desc->if1_1_out_ep_hs.bEndpointAddress;
400 }
401
402 return cfg->desc->if1_1_out_ep_fs.bEndpointAddress;
403 }
404
usbd_midi_rx_work(struct k_work * work)405 static void usbd_midi_rx_work(struct k_work *work)
406 {
407 struct usbd_midi_data *data = CONTAINER_OF(work, struct usbd_midi_data, rx_work);
408 struct net_buf *buf;
409 int ret;
410
411 buf = usbd_midi_buf_alloc(usbd_midi_get_bulk_out(data->class_data));
412 if (buf == NULL) {
413 LOG_WRN("Unable to allocate Rx net_buf");
414 return;
415 }
416
417 LOG_DBG("Enqueue Rx...");
418 ret = usbd_ep_enqueue(data->class_data, buf);
419 if (ret) {
420 LOG_ERR("Failed to enqueue Rx net_buf -> %d", ret);
421 net_buf_unref(buf);
422 }
423 }
424
usbd_midi_tx_work(struct k_work * work)425 static void usbd_midi_tx_work(struct k_work *work)
426 {
427 struct usbd_midi_data *data = CONTAINER_OF(work, struct usbd_midi_data, tx_work);
428 struct net_buf *buf;
429 int ret;
430
431 buf = usbd_midi_buf_alloc(usbd_midi_get_bulk_in(data->class_data));
432 if (buf == NULL) {
433 LOG_ERR("Unable to allocate Tx net_buf");
434 return;
435 }
436
437 net_buf_add(buf, ring_buf_get(&data->tx_queue, buf->data, buf->size));
438 LOG_HEXDUMP_DBG(buf->data, buf->len, "MIDI2 - Tx DATA");
439
440 ret = usbd_ep_enqueue(data->class_data, buf);
441 if (ret) {
442 LOG_ERR("Failed to enqueue Tx net_buf -> %d", ret);
443 net_buf_unref(buf);
444 }
445 }
446
usbd_midi_preinit(const struct device * dev)447 static int usbd_midi_preinit(const struct device *dev)
448 {
449 struct usbd_midi_data *data = dev->data;
450
451 LOG_DBG("Init device %s", dev->name);
452 ring_buf_init(&data->tx_queue, MIDI_QUEUE_SIZE, data->tx_queue_buf);
453 k_work_init(&data->rx_work, usbd_midi_rx_work);
454 k_work_init(&data->tx_work, usbd_midi_tx_work);
455
456 return 0;
457 }
458
usbd_midi_send(const struct device * dev,const struct midi_ump ump)459 int usbd_midi_send(const struct device *dev, const struct midi_ump ump)
460 {
461 struct usbd_midi_data *data = dev->data;
462 size_t words = UMP_NUM_WORDS(ump);
463 size_t buflen = 4 * words;
464 uint32_t word;
465
466 LOG_DBG("Send MT=%X group=%X", UMP_MT(ump), UMP_GROUP(ump));
467 if (data->altsetting != MIDI2_ALTERNATE) {
468 LOG_WRN("MIDI2.0 is not enabled");
469 return -EIO;
470 }
471
472 if (buflen > ring_buf_space_get(&data->tx_queue)) {
473 LOG_WRN("Not enough space in tx queue");
474 return -ENOBUFS;
475 }
476
477 for (size_t i = 0; i < words; i++) {
478 word = sys_cpu_to_le32(ump.data[i]);
479 ring_buf_put(&data->tx_queue, (const uint8_t *)&word, 4);
480 }
481 k_work_submit(&data->tx_work);
482
483 return 0;
484 }
485
usbd_midi_set_ops(const struct device * dev,const struct usbd_midi_ops * ops)486 void usbd_midi_set_ops(const struct device *dev, const struct usbd_midi_ops *ops)
487 {
488 struct usbd_midi_data *data = dev->data;
489
490 if (ops == NULL) {
491 memset(&data->ops, 0, sizeof(struct usbd_midi_ops));
492 } else {
493 memcpy(&data->ops, ops, sizeof(struct usbd_midi_ops));
494 }
495
496 LOG_DBG("Set ops for %s to %p", dev->name, ops);
497 }
498
499 /* Group Terminal Block unique identification number, type and protocol
500 * see midi20 5.4.2 Group Terminal Block Descriptor
501 */
502 #define GRPTRM_BLOCK_ID(node) UTIL_INC(DT_NODE_CHILD_IDX(node))
503 #define GRPTRM_BLOCK_TYPE(node) \
504 COND_CODE_1(DT_ENUM_HAS_VALUE(node, terminal_type, input_only), \
505 (GR_TRM_INPUT_ONLY), \
506 (COND_CODE_1(DT_ENUM_HAS_VALUE(node, terminal_type, output_only), \
507 (GR_TRM_OUTPUT_ONLY), \
508 (GR_TRM_BIDIRECTIONAL) \
509 )) \
510 )
511 #define GRPTRM_PROTOCOL(node) \
512 COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi2), \
513 (MIDI_2_0), \
514 (COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi1_up_to_64b), \
515 (MIDI_1_0_UP_TO_64_BITS), \
516 (COND_CODE_1(DT_ENUM_HAS_VALUE(node, protocol, midi1_up_to_128b), \
517 (MIDI_1_0_UP_TO_128_BITS), \
518 (USE_MIDI_CI) \
519 )) \
520 )) \
521 )
522
523 /* Group Terminal Block unique identification number with a trailing comma
524 * if that block is bidirectional or of given terminal type; otherwise empty
525 */
526 #define GRPTRM_BLOCK_ID_SEP_IF(node, ttype) \
527 IF_ENABLED( \
528 UTIL_OR(DT_ENUM_HAS_VALUE(node, terminal_type, bidirectional), \
529 DT_ENUM_HAS_VALUE(node, terminal_type, ttype)), \
530 (GRPTRM_BLOCK_ID(node),))
531
532 /* All unique identification numbers of output+bidir group terminal blocks */
533 #define GRPTRM_OUTPUT_BLOCK_IDS(n) \
534 DT_INST_FOREACH_CHILD_VARGS(n, GRPTRM_BLOCK_ID_SEP_IF, output_only)
535
536 /* All unique identification numbers of input+bidir group terminal blocks */
537 #define GRPTRM_INPUT_BLOCK_IDS(n) \
538 DT_INST_FOREACH_CHILD_VARGS(n, GRPTRM_BLOCK_ID_SEP_IF, input_only)
539
540 #define N_INPUTS(n) sizeof((uint8_t[]){GRPTRM_INPUT_BLOCK_IDS(n)})
541 #define N_OUTPUTS(n) sizeof((uint8_t[]){GRPTRM_OUTPUT_BLOCK_IDS(n)})
542
543 #define USBD_MIDI_VALIDATE_GRPTRM_BLOCK(node) \
544 BUILD_ASSERT(DT_REG_ADDR(node) < 16, \
545 "Group Terminal Block address must be within 0..15"); \
546 BUILD_ASSERT(DT_REG_ADDR(node) + DT_REG_SIZE(node) <= 16, \
547 "Too many Group Terminals in this Block");
548
549 #define USBD_MIDI_VALIDATE_INSTANCE(n) \
550 DT_INST_FOREACH_CHILD(n, USBD_MIDI_VALIDATE_GRPTRM_BLOCK)
551
552 #define USBD_MIDI2_INIT_GRPTRM_BLOCK_DESCRIPTOR(node) \
553 { \
554 .bLength = sizeof(struct usb_midi_grptrm_block_descriptor), \
555 .bDescriptorType = CS_GR_TRM_BLOCK, \
556 .bDescriptorSubtype = GR_TRM_BLOCK, \
557 .bGrpTrmBlkID = GRPTRM_BLOCK_ID(node), \
558 .bGrpTrmBlkType = GRPTRM_BLOCK_TYPE(node), \
559 .nGroupTrm = DT_REG_ADDR(node), \
560 .nNumGroupTrm = DT_REG_SIZE(node), \
561 .iBlockItem = 0, \
562 .bMIDIProtocol = GRPTRM_PROTOCOL(node), \
563 .wMaxInputBandwidth = 0x0000, \
564 .wMaxOutputBandwidth = 0x0000, \
565 }
566
567 #define USBD_MIDI2_GRPTRM_TOTAL_LEN(n) \
568 sizeof(struct usb_midi_grptrm_header_descriptor) \
569 + DT_INST_CHILD_NUM_STATUS_OKAY(n) \
570 * sizeof(struct usb_midi_grptrm_block_descriptor)
571
572 #define USBD_MIDI_DEFINE_DESCRIPTORS(n) \
573 static struct usbd_midi_descriptors usbd_midi_desc_##n = { \
574 .iad = { \
575 .bLength = sizeof(struct usb_association_descriptor), \
576 .bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
577 .bFirstInterface = 0, \
578 .bInterfaceCount = 2, \
579 .bFunctionClass = AUDIO, \
580 .bFunctionSubClass = MIDISTREAMING, \
581 }, \
582 .if0_std = { \
583 .bLength = sizeof(struct usb_if_descriptor), \
584 .bDescriptorType = USB_DESC_INTERFACE, \
585 .bInterfaceNumber = 0, \
586 .bAlternateSetting = 0, \
587 .bNumEndpoints = 0, \
588 .bInterfaceClass = AUDIO, \
589 .bInterfaceSubClass = AUDIOCONTROL, \
590 }, \
591 .if0_cs = { \
592 .bLength = sizeof(struct usb_midi_cs_ac_header_descriptor), \
593 .bDescriptorType = USB_DESC_CS_INTERFACE, \
594 .bDescriptorSubtype = MS_HEADER, \
595 .bcdADC = sys_cpu_to_le16(0x0100), \
596 .wTotalLength = sizeof(struct usb_midi_cs_ac_header_descriptor), \
597 .bInCollection = 1, \
598 .baInterfaceNr1 = 1, \
599 }, \
600 .if1_0_std = { \
601 .bLength = sizeof(struct usb_if_descriptor), \
602 .bDescriptorType = USB_DESC_INTERFACE, \
603 .bInterfaceNumber = 1, \
604 .bAlternateSetting = MIDI1_ALTERNATE, \
605 .bNumEndpoints = 2, \
606 .bInterfaceClass = AUDIO, \
607 .bInterfaceSubClass = MIDISTREAMING, \
608 }, \
609 .if1_0_ms_header = { \
610 .bLength = sizeof(struct usb_midi_header_descriptor), \
611 .bDescriptorType = USB_DESC_CS_INTERFACE, \
612 .bDescriptorSubtype = MS_HEADER, \
613 .bcdMSC = sys_cpu_to_le16(0x0100), \
614 .wTotalLength = sys_cpu_to_le16( \
615 sizeof(struct usb_midi_header_descriptor) \
616 + 2 * (sizeof(struct usb_ep_descriptor) + 4) \
617 ), \
618 }, \
619 .if1_0_out_ep_fs = { \
620 .bLength = sizeof(struct usb_ep_descriptor), \
621 .bDescriptorType = USB_DESC_ENDPOINT, \
622 .bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
623 .bmAttributes = USB_EP_TYPE_BULK, \
624 .wMaxPacketSize = sys_cpu_to_le16(64U), \
625 }, \
626 .if1_0_out_ep_hs = { \
627 .bLength = sizeof(struct usb_ep_descriptor), \
628 .bDescriptorType = USB_DESC_ENDPOINT, \
629 .bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
630 .bmAttributes = USB_EP_TYPE_BULK, \
631 .wMaxPacketSize = sys_cpu_to_le16(512U), \
632 }, \
633 .if1_0_cs_out_ep = { \
634 .bLength = 4, \
635 .bDescriptorType = USB_DESC_CS_ENDPOINT, \
636 .bDescriptorSubtype = MS_GENERAL, \
637 .bNumGrpTrmBlock = 0, \
638 }, \
639 .if1_0_in_ep_fs = { \
640 .bLength = sizeof(struct usb_ep_descriptor), \
641 .bDescriptorType = USB_DESC_ENDPOINT, \
642 .bEndpointAddress = n + FIRST_IN_EP_ADDR, \
643 .bmAttributes = USB_EP_TYPE_BULK, \
644 .wMaxPacketSize = sys_cpu_to_le16(64U), \
645 }, \
646 .if1_0_in_ep_hs = { \
647 .bLength = sizeof(struct usb_ep_descriptor), \
648 .bDescriptorType = USB_DESC_ENDPOINT, \
649 .bEndpointAddress = n + FIRST_IN_EP_ADDR, \
650 .bmAttributes = USB_EP_TYPE_BULK, \
651 .wMaxPacketSize = sys_cpu_to_le16(512U), \
652 }, \
653 .if1_0_cs_in_ep = { \
654 .bLength = 4 + N_INPUTS(n), \
655 .bDescriptorType = USB_DESC_CS_ENDPOINT, \
656 .bDescriptorSubtype = MS_GENERAL, \
657 .bNumGrpTrmBlock = 0, \
658 }, \
659 .if1_1_std = { \
660 .bLength = sizeof(struct usb_if_descriptor), \
661 .bDescriptorType = USB_DESC_INTERFACE, \
662 .bInterfaceNumber = 1, \
663 .bAlternateSetting = MIDI2_ALTERNATE, \
664 .bNumEndpoints = 2, \
665 .bInterfaceClass = AUDIO, \
666 .bInterfaceSubClass = MIDISTREAMING, \
667 }, \
668 .if1_1_ms_header = { \
669 .bLength = sizeof(struct usb_midi_header_descriptor), \
670 .bDescriptorType = USB_DESC_CS_INTERFACE, \
671 .bDescriptorSubtype = MS_HEADER, \
672 .bcdMSC = sys_cpu_to_le16(0x0200), \
673 .wTotalLength = sys_cpu_to_le16( \
674 sizeof(struct usb_midi_header_descriptor)), \
675 }, \
676 .if1_1_out_ep_fs = { \
677 .bLength = sizeof(struct usb_ep_descriptor), \
678 .bDescriptorType = USB_DESC_ENDPOINT, \
679 .bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
680 .bmAttributes = USB_EP_TYPE_BULK, \
681 .wMaxPacketSize = sys_cpu_to_le16(64U), \
682 }, \
683 .if1_1_out_ep_hs = { \
684 .bLength = sizeof(struct usb_ep_descriptor), \
685 .bDescriptorType = USB_DESC_ENDPOINT, \
686 .bEndpointAddress = n + FIRST_OUT_EP_ADDR, \
687 .bmAttributes = USB_EP_TYPE_BULK, \
688 .wMaxPacketSize = sys_cpu_to_le16(512U), \
689 }, \
690 .if1_1_cs_out_ep = { \
691 .bLength = 4 + N_OUTPUTS(n), \
692 .bDescriptorType = USB_DESC_CS_ENDPOINT, \
693 .bDescriptorSubtype = MS_GENERAL_2_0, \
694 .bNumGrpTrmBlock = N_OUTPUTS(n), \
695 .baAssoGrpTrmBlkID = {GRPTRM_OUTPUT_BLOCK_IDS(n)}, \
696 }, \
697 .if1_1_in_ep_fs = { \
698 .bLength = sizeof(struct usb_ep_descriptor), \
699 .bDescriptorType = USB_DESC_ENDPOINT, \
700 .bEndpointAddress = n + FIRST_IN_EP_ADDR, \
701 .bmAttributes = USB_EP_TYPE_BULK, \
702 .wMaxPacketSize = sys_cpu_to_le16(64U), \
703 }, \
704 .if1_1_in_ep_hs = { \
705 .bLength = sizeof(struct usb_ep_descriptor), \
706 .bDescriptorType = USB_DESC_ENDPOINT, \
707 .bEndpointAddress = n + FIRST_IN_EP_ADDR, \
708 .bmAttributes = USB_EP_TYPE_BULK, \
709 .wMaxPacketSize = sys_cpu_to_le16(512U), \
710 }, \
711 .if1_1_cs_in_ep = { \
712 .bLength = 4 + N_INPUTS(n), \
713 .bDescriptorType = USB_DESC_CS_ENDPOINT, \
714 .bDescriptorSubtype = MS_GENERAL_2_0, \
715 .bNumGrpTrmBlock = N_INPUTS(n), \
716 .baAssoGrpTrmBlkID = {GRPTRM_INPUT_BLOCK_IDS(n)}, \
717 }, \
718 .grptrm_header = { \
719 .bLength = sizeof(struct usb_midi_grptrm_header_descriptor), \
720 .bDescriptorType = CS_GR_TRM_BLOCK, \
721 .bDescriptorSubtype = GR_TRM_BLOCK_HEADER, \
722 .wTotalLength = sys_cpu_to_le16( \
723 USBD_MIDI2_GRPTRM_TOTAL_LEN(n) \
724 ), \
725 }, \
726 .grptrm_blocks = { \
727 DT_INST_FOREACH_CHILD_SEP( \
728 n, USBD_MIDI2_INIT_GRPTRM_BLOCK_DESCRIPTOR, (,) \
729 ) \
730 }, \
731 }; \
732 static const struct usb_desc_header *usbd_midi_desc_array_fs_##n[] = { \
733 (struct usb_desc_header *)&usbd_midi_desc_##n.iad, \
734 (struct usb_desc_header *)&usbd_midi_desc_##n.if0_std, \
735 (struct usb_desc_header *)&usbd_midi_desc_##n.if0_cs, \
736 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_std, \
737 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_ms_header, \
738 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_out_ep_fs, \
739 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_out_ep, \
740 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_in_ep_fs, \
741 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_in_ep, \
742 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_std, \
743 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_ms_header, \
744 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_out_ep_fs, \
745 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_out_ep, \
746 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_in_ep_fs, \
747 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_in_ep, \
748 NULL, \
749 }; \
750 static const struct usb_desc_header *usbd_midi_desc_array_hs_##n[] = { \
751 (struct usb_desc_header *)&usbd_midi_desc_##n.iad, \
752 (struct usb_desc_header *)&usbd_midi_desc_##n.if0_std, \
753 (struct usb_desc_header *)&usbd_midi_desc_##n.if0_cs, \
754 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_std, \
755 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_ms_header, \
756 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_out_ep_hs, \
757 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_out_ep, \
758 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_in_ep_hs, \
759 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_0_cs_in_ep, \
760 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_std, \
761 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_ms_header, \
762 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_out_ep_hs, \
763 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_out_ep, \
764 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_in_ep_hs, \
765 (struct usb_desc_header *)&usbd_midi_desc_##n.if1_1_cs_in_ep, \
766 NULL, \
767 };
768
769 #define USBD_MIDI_DEFINE_DEVICE(n) \
770 USBD_MIDI_VALIDATE_INSTANCE(n) \
771 USBD_MIDI_DEFINE_DESCRIPTORS(n); \
772 USBD_DEFINE_CLASS(midi_##n, &usbd_midi_class_api, \
773 (void *)DEVICE_DT_GET(DT_DRV_INST(n)), NULL); \
774 static const struct usbd_midi_config usbd_midi_config_##n = { \
775 .desc = &usbd_midi_desc_##n, \
776 .fs_descs = usbd_midi_desc_array_fs_##n, \
777 .hs_descs = usbd_midi_desc_array_hs_##n, \
778 }; \
779 static struct usbd_midi_data usbd_midi_data_##n = { \
780 .class_data = &midi_##n, \
781 .altsetting = MIDI1_ALTERNATE, \
782 }; \
783 DEVICE_DT_INST_DEFINE(n, usbd_midi_preinit, NULL, \
784 &usbd_midi_data_##n, &usbd_midi_config_##n, \
785 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
786
787 DT_INST_FOREACH_STATUS_OKAY(USBD_MIDI_DEFINE_DEVICE)
788