~ruther/stm32h747i-disco-usb-image-viewer

ref: 68abb4c6d986c3d7f3f04aa5c6610190960c1104 stm32h747i-disco-usb-image-viewer/firmware/src/usb_device_cdc.c -rw-r--r-- 25.8 KiB
68abb4c6 — Rutherther chore: tidy up rust code 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
#include "generic.h"
#include "stm32h747xx.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_cdc.h"
#include "registers.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

usb_class_header_t* usb_device_cdc_init(usb_device_t *device,
                uint16_t id_vendor, uint16_t id_product,
                char16_t *vendor_name, char16_t *product_name,
                uint16_t serial_number, char16_t* serial_name);

task_result_t usb_device_cdc_send_configuration(usb_device_t* device, usb_setup_command_t* cmd);
void usb_device_cdc_setup_endpoints(usb_device_t* device, uint8_t configuration);
void usb_device_cdc_reset_endpoints(usb_device_t* device);
void usb_device_cdc_reset_callback(usb_device_t* device);
task_result_t usb_device_cdc_setup_packet_callback(usb_device_t* device, usb_setup_command_t* cmd);
void usb_device_cdc_enumeration_done_callback(usb_device_t* device);
void usb_device_cdc_txfifo_empty_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_rxfifo_full_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_rx_done_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_tx_done_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nak_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_nyet_callback(usb_device_t* device, uint8_t endpoint);
void usb_device_cdc_received_data_callback(usb_device_t* device, packet_info_t* packet);

#define MAX_PACKET_SIZE 512

uint16_t usb_cdc_lang_descriptors[] = {
  USB_LANG_ENGLISH | (USB_SUBLANG_ENGLISH_US << 10)
};

usb_class_vtable_t USB_CLASS_CDC_ACM = {
  .init = usb_device_cdc_init,
  .send_configuration = usb_device_cdc_send_configuration,
  .setup_endpoints = usb_device_cdc_setup_endpoints,
  .reset_endpoints = usb_device_cdc_reset_endpoints,
  .reset_callback = usb_device_cdc_reset_callback,

  .setup_packet_callback = usb_device_cdc_setup_packet_callback,
  .enumeration_done_callback = usb_device_cdc_enumeration_done_callback,
  .txfifo_empty_callback = usb_device_cdc_txfifo_empty_callback,
  .rxfifo_full_callback = usb_device_cdc_rxfifo_full_callback,
  .rx_done_callback = usb_device_cdc_rx_done_callback,
  .tx_done_callback = usb_device_cdc_tx_done_callback,
  .nak_callback = usb_device_cdc_nak_callback,
  .nyet_callback = usb_device_cdc_nyet_callback,
  .received_data_callback = usb_device_cdc_received_data_callback,
};

static inline size_t strlen16(register const char16_t * string) {
    if (!string) return 0;
    register size_t len = 0;
    while(string[len++]);
    return len;
}

usb_class_header_t *usb_device_cdc_init(usb_device_t *device, uint16_t id_vendor,
                          uint16_t id_product, char16_t *vendor_name,
                          char16_t *product_name, uint16_t serial_number,
                          char16_t *serial_name) {
  usb_class_header_t* class = (usb_class_header_t*)calloc(1, sizeof(usb_device_cdc_t));

  class->device_descriptor.idProduct = id_product;
  class->device_descriptor.idVendor = id_vendor;
  class->device_descriptor.bcdDevice = serial_number;

  class->string_descriptor_zero.header.bDescriptorType = DESCRIPTOR_STRING;
  class->string_descriptor_zero.header.bLength = sizeof(usb_string_descriptor_zero_t);
  class->string_descriptor_zero.wLANGID = usb_cdc_lang_descriptors;

  uint8_t string_count = 0;

  if (vendor_name != NULL) {
    string_count++;
    class->device_descriptor.iManufacturer = string_count;
  }
  if (product_name != NULL) {
    string_count++;
    class->device_descriptor.iProduct = string_count;
  }
  if (serial_name != NULL) {
    string_count++;
    class->device_descriptor.iSerialNumber = string_count;
  }

  usb_unicode_string_descriptor_t *descriptor =
    (usb_unicode_string_descriptor_t*)malloc(sizeof(usb_unicode_string_descriptor_t) * string_count);
  class->string_descriptors = descriptor;

  if (vendor_name != NULL) {
    descriptor->header.bDescriptorType = DESCRIPTOR_STRING;
    descriptor->header.bLength = 2 + strlen16(vendor_name) * sizeof(char16_t);
    descriptor->bString = (uint16_t*)vendor_name;

    descriptor++;
  }
  if (product_name != NULL) {
    descriptor->header.bDescriptorType = DESCRIPTOR_STRING;
    descriptor->header.bLength = 2 + strlen16(product_name) * sizeof(char16_t);
    descriptor->bString = (uint16_t*)product_name;
    descriptor++;
  }
  if (serial_name != NULL) {
    descriptor->header.bDescriptorType = DESCRIPTOR_STRING;
    descriptor->header.bLength = 2 + strlen16(serial_name) * sizeof(char16_t);
    descriptor->bString = (uint16_t*)serial_name;
    descriptor++;
  }

  return class;
}

void cdc_acm_configure(usb_device_t* device, uint16_t rx_queue_size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  usb_class_header_t* header = &cdc->header;

  cdc->endpoint_nak = false;
  cdc->lost_data = 0;

  queue_t* rx_queue = queue_malloc(1, rx_queue_size);
  cdc->rx_buffer = rx_queue;
  queue_init(rx_queue, 1, rx_queue_size);

  usb_device_descriptor_t device_descriptor =
    {
      .header = { .bDescriptorType = DESCRIPTOR_DEVICE, .bLength = sizeof(usb_device_descriptor_t) },
      .bcdUSB = 0x200,
      .bDeviceClass = USB_CLASS_CDC_CODE,
      .bDeviceSubClass = 0x00,
      .bDeviceProtocol = 0x00,
      .bMaxPacketSize0 = 64,
      .idVendor = header->device_descriptor.idVendor,
      .idProduct = header->device_descriptor.idProduct,
      .bcdDevice = 0x0000,
      .iManufacturer = header->device_descriptor.iManufacturer,
      .iProduct = header->device_descriptor.iProduct,
      .iSerialNumber = header->device_descriptor.iSerialNumber,
      .bNumConfigurations = 1,
    };
  header->device_descriptor = device_descriptor;

  usb_device_qualifier_t qualifier =
    {
      .header = { .bDescriptorType = DESCRIPTOR_DEVICE_QUALIFIER, .bLength = sizeof(usb_device_qualifier_t) },
      .bcdUSB = 0x20,
      .bDeviceClass = USB_CLASS_CDC_CODE,
      .bDeviceSubClass = 0x00,
      .bDeviceProtocol = 0x00,
      .bMaxPacketSize0 = 64,
      .bNumConfigurations = 0,
      .bReserved = 0
    };
  header->device_qualifier = qualifier;

  usb_configuration_descriptor_t configuration_descriptor =
    {
      .header = { .bDescriptorType = DESCRIPTOR_CONFIGURATION, .bLength = sizeof(usb_configuration_descriptor_t) },
      .bNumInterfaces = 2,
      .bConfigurationValue = 1,
      .iConfiguration = 0,
      .bmAttributes =
      {
        .self_powered = 0,
        .remote_wakeup = 0,
        .reserved1 = 1,
        .reserved_zeros = 0,
      },
      .bMaxPower = 50,
    };
  header->configuration_descriptor = configuration_descriptor;

  header->interfaces_count = 2;
  header->interfaces = malloc(2 * sizeof(usb_interface_t));

  static usb_endpoint_descriptor_t notification_endpoint =
  {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT,
              .bLength = sizeof(usb_endpoint_descriptor_t)},
   .bEndpointAddress = { .endpoint_number = 1, .direction = USB_ENDPOINT_IN, .reserved = 0 },
   .bInterval = 16,
   .bmAttributes = {
       .reserved_zeros = 0,
       .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC,
       .usage_type = USB_ENDPOINT_USAGE_DATA,
       .transfer_type = USB_ENDPOINT_TYPE_INTERRUPT,
   },
    .wMaxPacketSize = 64,
  };
  usb_interface_t communications_interface =
  {.interface_descriptor = {
       .header = {.bDescriptorType = DESCRIPTOR_INTERFACE,
                  .bLength = sizeof(usb_interface_descriptor_t)},
       .bInterfaceNumber = 0,
       .bAlternateSetting = 0,
       .bNumEndpoints = 1,
       .bInterfaceClass = USB_CLASS_CDC_CODE,
       .bInterfaceSubClass = USB_SUBCLASS_CDC_ACM_CODE,
       .bInterfaceProtocol = 0,
       .iInterface = 0
      },
    .endpoint_descriptors_count = 1,
    // NOTE: mind here, the endpoint is same for all devices,
    // so it's defined here as static variable, meaning it will
    // not be deallocated when this function exits.
    .endpoint_descriptors = &notification_endpoint,
  };
  static usb_endpoint_descriptor_t data_endpoints[2] = {
    {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT,
                .bLength = sizeof(usb_endpoint_descriptor_t)},
     .bEndpointAddress = { .endpoint_number = 2, .direction = USB_ENDPOINT_IN, .reserved = 0 },
     .bInterval = 1,
     .bmAttributes = {
       .reserved_zeros = 0,
       .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC,
       .usage_type = USB_ENDPOINT_USAGE_DATA,
       .transfer_type = USB_ENDPOINT_TYPE_BULK,
     },
     .wMaxPacketSize = MAX_PACKET_SIZE,
    },
    {.header = {.bDescriptorType = DESCRIPTOR_ENDPOINT,
                .bLength = sizeof(usb_endpoint_descriptor_t)},
     .bEndpointAddress = { .endpoint_number = 2, .direction = USB_ENDPOINT_OUT, .reserved = 0 },
     .bInterval = 1,
     .bmAttributes = {
       .reserved_zeros = 0,
       .synchronization_type = USB_ENDPOINT_SYNC_NO_SYNC,
       .usage_type = USB_ENDPOINT_USAGE_DATA,
       .transfer_type = USB_ENDPOINT_TYPE_BULK,
     },
     .wMaxPacketSize = MAX_PACKET_SIZE,
    },
  };
  usb_interface_t data_interface = {
    .interface_descriptor = {
      .header = {.bDescriptorType = DESCRIPTOR_INTERFACE,
                 .bLength = sizeof(usb_interface_descriptor_t)},
      .bInterfaceNumber = 1,
      .bAlternateSetting = 0,
      .bNumEndpoints = 2,
      .bInterfaceClass = USB_CLASS_DATA_CODE,
      .bInterfaceSubClass = 0x00,
      .bInterfaceProtocol = 0,
      .iInterface = 0
    },
    .endpoint_descriptors_count = 2,
    // NOTE: mind here, the endpoint is same for all devices,
    // so it's defined here as static variable, meaning it will
    // not be deallocated when this function exits.
    .endpoint_descriptors = data_endpoints,
  };

  cdc->functional_descriptors_count = 4;

  static usb_cdc_header_functional_decriptor_t header_function = {
    .header = { .bFunctionLength = sizeof(usb_cdc_header_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = HEADER_FUNCTIONAL_DESCRIPTOR_FUNCTIONAL_DESCRIPTOR },
    .bcdCDC = 0x0110,
  };
  static usb_cdc_acm_functional_decriptor_t acm_function = {
    .header = { .bFunctionLength = sizeof(usb_cdc_acm_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR },
    .bmCapabilities = 0x00,
  };
  static usb_cdc_union_functional_decriptor_t union_function = {
    .header = { .bFunctionLength = sizeof(usb_cdc_union_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = UNION_FUNCTIONAL_DESCRIPTOR },
    .bControlInterface = 0,
    .bSubordinateInterface0 = 1,
  };
  static usb_cdc_call_management_functional_decriptor_t call_function = {
    .header = { .bFunctionLength = sizeof(usb_cdc_call_management_functional_decriptor_t), .bDescriptorType = CS_INTERFACE, .bDescriptorSubType = CALL_MANAGEMENT_FUNCTIONAL_FUNCTIONAL_DESCRIPTOR },
    .bmCapabilities = 0x00
  };
  static usb_cdc_functional_descriptor_header_t *headers[4] =
    { &header_function.header, &acm_function.header, &union_function.header, &call_function.header };
  cdc->functional_descriptors = headers;

  header->interfaces[0] = communications_interface;
  header->interfaces[1] = data_interface;
}

uint16_t get_size(uint16_t size_to_send, uint16_t* remaining_size) {
  uint16_t size = size_to_send;

  if (*remaining_size < size_to_send) {
    size = *remaining_size;
    *remaining_size = 0;
  } else {
    *remaining_size -= size_to_send;
  }

  return size;
}

task_result_t usb_device_cdc_send_configuration(usb_device_t *device,
                                                usb_setup_command_t *cmd) {
  usb_device_cdc_t* dev = (usb_device_cdc_t*)device->class;
  USB_OTG_INEndpointTypeDef* enp0 = &device->in[0];
  volatile uint32_t* enp0fifo = &device->fifos[0].data[0];
  uint32_t sub_word_data;
  uint8_t  sub_word_count = 0;

  // first configure the size
  uint16_t size =
      sizeof(usb_configuration_descriptor_t) +
    dev->header.interfaces_count * sizeof(usb_interface_descriptor_t);

  for (uint8_t i = 0; i < dev->header.interfaces_count; i++) {
    usb_interface_t* interface = &dev->header.interfaces[i];
    size += interface->endpoint_descriptors_count * sizeof(usb_endpoint_descriptor_t);
  }

  for (uint8_t i = 0; i < dev->functional_descriptors_count; i++) {
    usb_cdc_functional_descriptor_header_t* descriptor = dev->functional_descriptors[i];
    size += descriptor->bFunctionLength;
  }

  dev->header.configuration_descriptor.wTotalLength = size;

  if (size > cmd->wLength) {
    size = cmd->wLength;
  }

  // TODO: what if there is not enough space for this?
  // I mean there should be... but that case should probably be handled to,
  // it depends a lot on how many functions and interfaces we do have...
  task_result_t result = usb_generic_setup_in_endpoint(enp0, size, 64);

  if (result != RES_OK) {
    return result;
  }

  // fill fifo with all configuration
  usb_generic_fill_fifo_words(enp0,
                              (uint8_t*)&dev->header.configuration_descriptor,
                              get_size(sizeof(usb_configuration_descriptor_t), &size),
                              enp0fifo, &sub_word_data, &sub_word_count);

  // NOTE: there is always one control interface and one data one.
  for (uint8_t i = 0; i < dev->header.interfaces_count; i++) {
    usb_interface_t* interface = &dev->header.interfaces[i];
    usb_generic_fill_fifo_words(enp0,
                                (uint8_t*)&interface->interface_descriptor,
                                get_size(sizeof(usb_interface_descriptor_t), &size),
                                enp0fifo, &sub_word_data, &sub_word_count);

    // Control interface has functional descriptors
    if (i == 0) {
      for (uint8_t j = 0; j < dev->functional_descriptors_count; j++) {
        usb_cdc_functional_descriptor_header_t* descriptor = dev->functional_descriptors[j];
        usb_generic_fill_fifo_words(enp0, (uint8_t *)descriptor,
                                    get_size(descriptor->bFunctionLength, &size),
                                    enp0fifo, &sub_word_data, &sub_word_count);
      }
    }

    for (uint8_t j = 0; j < interface->endpoint_descriptors_count; j++) {
      usb_generic_fill_fifo_words(enp0,
                                  (uint8_t*)&interface->endpoint_descriptors[j],
                                  get_size(sizeof(usb_endpoint_descriptor_t), &size),
                                  enp0fifo, &sub_word_data, &sub_word_count);
    }
  }

  // The fifo takes always 4 elements. We do not care what's written on the
  // last bytes, since the peripheral will know only about the first bytes
  // as per the size written to DIEPTSIZ
  if (sub_word_count > 0) {
    sub_word_count = 0;
    usb_generic_fill_fifo_words(enp0, (uint8_t *)&sub_word_data,
                                4, enp0fifo, &sub_word_data, &sub_word_count);
  }

  // After the fifo is filled...
  return result;
}

void usb_device_cdc_setup_endpoints(usb_device_t *device,
                                    uint8_t configuration) {
  // NOTE: currently it is assumed one notification endpoint (1),
  //       and data endpoint (2). This is valid for acm, if other
  //       are implemented in the future, this will have to be changed

  // FIFOs sizes
  uint16_t fifo_zero_pos = 0;
  uint16_t fifo_zero_size = reg_read_bits_pos(&device->core->DIEPTXF0_HNPTXFSIZ, USB_OTG_DIEPTXF_INEPTXFD_Pos, 0xFFFF);
  uint16_t curr_pos = fifo_zero_pos + fifo_zero_size;
  uint16_t sizes = 512 / 4;
  device->core->DIEPTXF[1] = (curr_pos << USB_OTG_DIEPTXF_INEPTXSA_Pos) | (sizes << USB_OTG_DIEPTXF_INEPTXFD_Pos);
  curr_pos += sizes;
  device->core->DIEPTXF[2] = (curr_pos << USB_OTG_DIEPTXF_INEPTXSA_Pos) | (sizes << USB_OTG_DIEPTXF_INEPTXFD_Pos);

  // Enable endpoint 1 IN
  //   it's the notification endpoint
  // Configure the endpoint (size, fifo, type)
  reg_write_bits_pos(&device->in[1].DIEPCTL, MAX_PACKET_SIZE, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL);
  reg_write_bits_pos(&device->in[1].DIEPCTL, 1, USB_OTG_DIEPCTL_TXFNUM_Pos, 0xF);
  reg_write_bits_pos(&device->in[1].DIEPCTL, 3, USB_OTG_DIEPCTL_EPTYP_Pos, 0x3);
  // Enable interrupts
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_IEPM_Pos + 1);
  // The in endpoint should be enabled only if there is something to send,
  // so not enablling.
  reg_set_bits_pos(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Pos, 1);

  // Enable endpoint 2 IN
  // Enable endpoint 2 OUT
  //   Those are the data endpoints, used for transferring
  //   serial data, directly, no protocol is utilized.
  // Configure the endpoint (size, fifo, type)
  reg_write_bits_pos(&device->in[2].DIEPCTL, MAX_PACKET_SIZE, USB_OTG_DIEPCTL_MPSIZ_Pos, 0x7FFUL);
  reg_write_bits_pos(&device->in[2].DIEPCTL, 2, USB_OTG_DIEPCTL_TXFNUM_Pos, 0xF);
  reg_write_bits_pos(&device->in[2].DIEPCTL, 2, USB_OTG_DIEPCTL_EPTYP_Pos, 0x3);
  reg_write_bits_pos(&device->out[2].DOEPCTL, 2, USB_OTG_DOEPCTL_EPTYP_Pos, 0x3);
  reg_write_bits_pos(&device->out[2].DOEPCTL, MAX_PACKET_SIZE, USB_OTG_DOEPCTL_MPSIZ_Pos, 0x7FFUL);
  /* reg_write_bits_pos(&device->out[2].DOEPTSIZ, MAX_PACKET_SIZE, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF); */
  reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF);
  reg_write_bits_pos(&device->out[2].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_PKTCNT_Pos, 0x3FF);
  // Enable interrupts
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_IEPM_Pos + 2);
  device->device->DAINTMSK |= 1 << (USB_OTG_DAINTMSK_OEPM_Pos + 2);
  // Enable the out endpoint
  reg_set_bits_pos(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Pos, 1);
  reg_set_bits_pos(&device->out[2].DOEPCTL, USB_OTG_DOEPCTL_USBAEP_Pos, 1);
  device->out[2].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
}

void usb_device_cdc_reset_endpoints(usb_device_t *device) {
  reg_set_bits(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_EPDIS_Msk);
  reg_set_bits(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_EPDIS_Msk);
  reg_set_bits(&device->out[2].DOEPCTL, USB_OTG_DOEPCTL_EPDIS_Msk);
  reg_clear_bits(&device->in[1].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);
  reg_clear_bits(&device->in[2].DIEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);
  reg_clear_bits(&device->out[2].DOEPCTL, USB_OTG_DIEPCTL_USBAEP_Msk);

  reg_clear_bits(&device->device->DAINTMSK,
                 (USB_OTG_DAINTMSK_IEPM_Msk << 1) |
                 (USB_OTG_DAINTMSK_IEPM_Msk << 2) |
                 (USB_OTG_DAINTMSK_OEPM_Msk << 2));

  usb_flush_tx_fifo(device->core, 1);
  usb_flush_tx_fifo(device->core, 2);
}

void usb_device_cdc_reset_callback(usb_device_t *device) {

  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  while (queue_dequeue(cdc->rx_buffer) != NULL);

  // NOTE: endpoint deinitialization and such is handled
  // by the usb device itself.
}

task_result_t usb_device_cdc_setup_packet_callback(usb_device_t *device,
                                                   usb_setup_command_t *cmd) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  cdc->got_setup = 1;
  reg_set_bits(&device->out[0].DOEPCTL, USB_OTG_DOEPCTL_STALL);
  reg_set_bits(&device->in[0].DIEPCTL, USB_OTG_DIEPCTL_STALL);
  return RES_OK;
  /* // TODO: only for some commands. For now, we just ignore them. */
  /* if (cmd->bRequest == 34) { */
  /*   task_result_t result = usb_generic_send(device->in, NULL, 0, &device->fifos[0].data[0]); */
  /*   if (result == RES_OK) { */
  /*     device->setup.stage = SETUP_STAGE_SENDING_ACK; */
  /*   } */
  /*   return result; */
  /* } */

  /* return RES_OK; */
}

void usb_device_cdc_enumeration_done_callback(usb_device_t *device) {
  // Now receiving send, receive calls.
  // Nothing to do.
}

typedef enum {
  CDC_TX_FIFO_EMPTY,
  CDC_RX_FIFO_FULL,
  CDC_RX_DONE,
  CDC_TX_DONE,
  CDC_RX_DATA,
  CDC_TX_DATA,

  CDC_RX_REQ,
  CDC_TX_REQ,
} cdc_event_t;

#define DATA_ENDPOINT 2

task_result_t cdc_transmit(usb_device_t *device, uint8_t* data, uint16_t* size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;

  USB_OTG_INEndpointTypeDef* enp = &device->in[DATA_ENDPOINT];
  volatile uint32_t* fifo = device->fifos[DATA_ENDPOINT].data;

  uint32_t fifo_size = ((enp->DTXFSTS & (USB_OTG_DTXFSTS_INEPTFSAV_Msk)) >> USB_OTG_DTXFSTS_INEPTFSAV_Pos);

  if (fifo_size * 4 < *size) {
    *size = fifo_size * 4;
  }

  task_result_t result = usb_generic_setup_in_endpoint(enp, *size, MAX_PACKET_SIZE);

  if (result != RES_OK) {
    return result;
  }

  cdc->tx_state = CDC_TX_STATE_SENDING;

  usb_generic_fill_fifo(enp, data, *size, fifo);

  return RES_OK;
}

void cdc_process_tx(usb_device_t *device, cdc_event_t event) {
  switch (event) {
  case CDC_TX_REQ:
    break;
  case CDC_TX_DONE:
  case CDC_TX_FIFO_EMPTY:
    // Try reading from the queue. If size is 0,
    // notify application?
    break;
  default:
    // Should not happen.
    break;
  }
}

void cdc_process_rx(usb_device_t *device, cdc_event_t event) {
  /* usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class; */
  /* queue_t* queue = cdc->rx_buffer; */
  /* switch (event) { */
  /* case CDC_RX_REQ: */
  /*   // TODO: read from peripheral fifo to queue */
  /*   if (queue_space(queue) > 0 && cdc->pending_rx_bytes > 0) { */
  /*     cdc_read(device); */
  /*   } */
  /*   break; */
  /* case CDC_RX_DONE: */
  /* case CDC_RX_FIFO_FULL: */
  /*   // Try reading from peripheral to the queue, */
  /*   // if there is space. Check for space first, */
  /*   // when data are read, we don't have where to */
  /*   // store them! */

  /*   if (queue_space(queue) > 0) { */
  /*     cdc_read(device); */
  /*   } */
  /*   break; */
  /*   break; */
  /* default: */
  /*   // Should not happen. */
  /*   break; */
  /* } */
}

void usb_device_cdc_txfifo_empty_callback(usb_device_t *device,
                                          uint8_t endpoint) {
  cdc_process_tx(device, CDC_TX_FIFO_EMPTY);
}
void usb_device_cdc_rxfifo_full_callback(usb_device_t *device,
                                          uint8_t endpoint) {
  cdc_process_rx(device, CDC_RX_FIFO_FULL);
}
void usb_device_cdc_rx_done_callback(usb_device_t *device,
                                           uint8_t endpoint) {
  cdc_process_rx(device, CDC_RX_DONE);
}

void usb_device_cdc_tx_done_callback(usb_device_t *device,
                                           uint8_t endpoint) {
  cdc_process_tx(device, CDC_TX_DONE);
}

void usb_device_cdc_enable_endpoint_maybe(usb_device_t* device, usb_device_cdc_t *cdc) {
  if (queue_space(cdc->rx_buffer) >= MAX_PACKET_SIZE) {
    reg_write_bits_pos(&device->out[DATA_ENDPOINT].DOEPTSIZ, MAX_PACKET_SIZE, USB_OTG_DOEPTSIZ_XFRSIZ_Pos, 0xFFFF);
    reg_write_bits_pos(&device->out[DATA_ENDPOINT].DOEPTSIZ, 1, USB_OTG_DOEPTSIZ_PKTCNT_Pos, 0x3FF);
    device->out[DATA_ENDPOINT].DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK;
    cdc->endpoint_nak = false;
  }
}

void usb_device_cdc_received_data_callback(usb_device_t *device, packet_info_t *packet) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  uint8_t data[2*MAX_PACKET_SIZE];
  uint16_t len = packet->byte_count;

  volatile uint32_t* fifo = &device->fifos[packet->endpoint_num].data[0];

  if (packet->packet_status == PACKET_OUT_DATA_PACKET_RCVD) {
    if (cdc->receive_location_callback != NULL) {
      uint8_t* receive_location = cdc->receive_location_callback(device, len);
      if (receive_location != NULL) {
        usb_generic_read(receive_location, len, fifo);
        return;
      }
    }

    usb_generic_read(data, len, fifo);

    if (cdc->receive_callback != NULL) {
      uint16_t app_read = cdc->receive_callback(device, data, len);
      len -= app_read;
    }

    // TODO: could be optimized until pointer has to wrap
    if (queue_space(cdc->rx_buffer) < len) {
      cdc->lost_data += len - queue_space(cdc->rx_buffer);
    }

    for (uint16_t i = 0; i < len; i++) {
       queue_enqueue(cdc->rx_buffer, (void*)&data[i]);
    }
  } else if(packet->packet_status == PACKET_OUT_TRANSFER_COMPLETED) {
    if (!(device->out[DATA_ENDPOINT].DOEPINT & USB_OTG_DOEPINT_NAK)) {
      usb_device_cdc_enable_endpoint_maybe(device, cdc);
    }
  } else {
    usb_generic_read(data, packet->byte_count, fifo);
  }
}

void usb_device_cdc_nak_callback(usb_device_t *device, uint8_t endpoint) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  if (queue_space(cdc->rx_buffer) > MAX_PACKET_SIZE) {
    usb_device_cdc_enable_endpoint_maybe(device, cdc);
  } else {
    cdc->endpoint_nak = true;
  }
}
void usb_device_cdc_nyet_callback(usb_device_t *device, uint8_t endpoint) {
  // Nothing to do for now
}

void cdc_data_set_receive_callback(usb_device_t *device,
                                   cdc_receive_callback_t callback) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  cdc->receive_callback = callback;
}

void cdc_data_set_receive_location_callback(usb_device_t *device,
                                   cdc_receive_location_callback_t callback) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  cdc->receive_location_callback = callback;
}

int32_t cdc_data_send_blocking(usb_device_t *device, uint8_t *data, uint16_t size) {
  int32_t sent = 0;
  if (size == 0) {
    size = strlen((char*)data);
  }

  while (size > 0) {
    int32_t res = cdc_data_send(device, data, size);

    if (res == -1) {
      return -1;
    }

    size -= res;
    data += res;
    sent += res;
  }

  return sent;
}

int32_t cdc_data_send(usb_device_t *device, uint8_t* data, uint16_t size) {
  if (size == 0) {
    size = strlen((char*)data);
  }

  task_result_t result = cdc_transmit(device, data, &size);

  // If would block, just send back nothing was sent
  // That means no transaction has begun.
  if (result == RES_WOULD_BLOCK) {
    size = 0;
  } else if (result == RES_ERROR) {
    size = -1;
  }

  return size;
}

uint16_t cdc_data_receive(usb_device_t *device, uint8_t* buffer,
                          uint16_t max_size) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  uint16_t size = max_size;

  for (uint16_t i = 0; i < max_size; i++) {
    if (!queue_dequeue_safely(cdc->rx_buffer, buffer + i)) {
      size = i;
      break;
    }
  }

  if (cdc->endpoint_nak) {
    usb_device_cdc_enable_endpoint_maybe(device, cdc);
  }

  return size;
}

bool cdc_got_params(usb_device_t *device) {
  usb_device_cdc_t* cdc = (usb_device_cdc_t*)device->class;
  return cdc->got_setup != 0;
}
Do not follow this link