|
| 1 | +# MicroPython USB CDC module |
| 2 | +# MIT license; Copyright (c) 2022 Martin Fischer |
| 3 | +from .device import ( |
| 4 | + USBInterface, |
| 5 | + get_usbdevice |
| 6 | +) |
| 7 | +from .utils import ( |
| 8 | + endpoint_descriptor, |
| 9 | + EP_OUT_FLAG |
| 10 | +) |
| 11 | +from micropython import const |
| 12 | +import ustruct |
| 13 | + |
| 14 | +_DEV_CLASS_MISC = const(0xef) |
| 15 | +_CS_DESC_TYPE = const(0x24) # CS Interface type communication descriptor |
| 16 | +_ITF_ASSOCIATION_DESC_TYPE = const(0xb) # Interface Association descriptor |
| 17 | + |
| 18 | +# CDC control interface definitions |
| 19 | +_CDC_ITF_CONTROL_CLASS = const(2) |
| 20 | +_CDC_ITF_CONTROL_SUBCLASS = const(2) # Abstract Control Mode |
| 21 | +_CDC_ITF_CONTROL_PROT = const(0) # no protocol |
| 22 | + |
| 23 | +# CDC data interface definitions |
| 24 | +_CDC_ITF_DATA_CLASS = const(0xa) |
| 25 | +_CDC_ITF_DATA_SUBCLASS = const(0) |
| 26 | +_CDC_ITF_DATA_PROT = const(0) # no protocol |
| 27 | + |
| 28 | + |
| 29 | +def setup_CDC_device(): |
| 30 | + # CDC is a composite device, consisting of multiple interfaces |
| 31 | + # (CDC control and CDC data) |
| 32 | + # therefore we have to make sure that the association descriptor |
| 33 | + # is set and that it associates both interfaces to the logical cdc class |
| 34 | + usb_device = get_usbdevice() |
| 35 | + usb_device.device_class = _DEV_CLASS_MISC |
| 36 | + usb_device.device_subclass = 2 |
| 37 | + usb_device.device_protocol = 1 # Itf association descriptor |
| 38 | + |
| 39 | + |
| 40 | +class CDCControlInterface(USBInterface): |
| 41 | + # Implements the CDC Control Interface |
| 42 | + |
| 43 | + def __init__(self, interface_str): |
| 44 | + super().__init__(_CDC_ITF_CONTROL_CLASS, _CDC_ITF_CONTROL_SUBCLASS, |
| 45 | + _CDC_ITF_CONTROL_PROT) |
| 46 | + |
| 47 | + def get_itf_descriptor(self, num_eps, itf_idx, str_idx): |
| 48 | + # CDC needs a Interface Association Descriptor (IAD) |
| 49 | + # first interface is zero, two interfaces in total |
| 50 | + desc = ustruct.pack("<BBBBBBBB", 8, _ITF_ASSOCIATION_DESC_TYPE, itf_idx, 2, |
| 51 | + _CDC_ITF_CONTROL_CLASS, _CDC_ITF_CONTROL_SUBCLASS, |
| 52 | + _CDC_ITF_CONTROL_PROT, 0) # "IAD" |
| 53 | + |
| 54 | + itf, strs = super().get_itf_descriptor(num_eps, itf_idx, str_idx) |
| 55 | + desc += itf |
| 56 | + # Append the CDC class-specific interface descriptor |
| 57 | + # see also USB spec document CDC120-track, p20 |
| 58 | + desc += ustruct.pack("<BBBH", 5, _CS_DESC_TYPE, 0, 0x0120) # "Header" |
| 59 | + desc += ustruct.pack("<BBBBB", 5, _CS_DESC_TYPE, 1, 0, 1) # "Call Management" |
| 60 | + desc += ustruct.pack("<BBBB", 4, _CS_DESC_TYPE, 2, 2) # "Abstract Control" |
| 61 | + desc += ustruct.pack("<BBBH", 5, _CS_DESC_TYPE, 6, itf_idx, 1) # "Union" |
| 62 | + return desc, strs |
| 63 | + |
| 64 | + def get_endpoint_descriptors(self, ep_addr, str_idx): |
| 65 | + self.ep_in = endpoint_descriptor((ep_addr + 1) | EP_OUT_FLAG, "interrupt", 8, 16) |
| 66 | + return (self.ep_in, [], ((ep_addr+1) | EP_OUT_FLAG,)) |
| 67 | + |
| 68 | + |
| 69 | +class CDCDataInterface(USBInterface): |
| 70 | + # Implements the CDC Data Interface |
| 71 | + |
| 72 | + def __init__(self, interface_str): |
| 73 | + super().__init__(_CDC_ITF_DATA_CLASS, _CDC_ITF_DATA_SUBCLASS, |
| 74 | + _CDC_ITF_DATA_PROT) |
| 75 | + |
| 76 | + def get_endpoint_descriptors(self, ep_addr, str_idx): |
| 77 | + # XXX OUT = 0x00 but is defined as 0x80? |
| 78 | + self.ep_in = (ep_addr + 2) | EP_OUT_FLAG |
| 79 | + self.ep_out = (ep_addr + 2) & ~EP_OUT_FLAG |
| 80 | + print("cdc in={} out={}".format(self.ep_in, self.ep_out)) |
| 81 | + # one IN / OUT Endpoint |
| 82 | + e_out = endpoint_descriptor(self.ep_out, "bulk", 64, 0) |
| 83 | + e_in = endpoint_descriptor(self.ep_in, "bulk", 64, 0) |
| 84 | + desc = e_out + e_in |
| 85 | + return (desc, [], (self.ep_out, self.ep_in)) |
| 86 | + |
0 commit comments