File UsbCdcLink.cpp
File List > demo-projets > stm32 > src > UsbCdcLink.cpp
Go to the documentation of this file.
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "Bsp.hpp"
#include "CdcUartTunnel.hpp"
#include "Esp32Manager.hpp"
#include "UsbCdcLink.h"
#include "usb_cdc.h"
#include "utils/Debug.hpp"
enum {
STRDESC_LANG,
STRDESC_MANUFACTURER,
STRDESC_PRODUCT,
STRDESC_CDC_IFACE,
STRDESC_DEBUG_IFACE,
STRDESC_SERIAL,
STRDESC_MAX,
};
enum {
INTERFACE_TUNNEL_COMM,
INTERFACE_TUNNEL_DATA,
INTERFACE_DEBUG_COMM,
INTERFACE_DEBUG_DATA,
INTERFACE_COUNT_ALL,
INTERFACE_COUNT_NODEBUG = INTERFACE_DEBUG_COMM,
};
extern "C" {
extern const struct usb_string_descriptor lang_desc;
extern const struct usb_string_descriptor manuf_desc_en;
extern const struct usb_string_descriptor prod_desc_en;
extern const struct usb_string_descriptor cdc_iface_desc_en;
extern const struct usb_string_descriptor debug_iface_desc_en;
extern struct usb_string_descriptor serial_number_desc_en;
};
static const struct usb_string_descriptor* const dtable[STRDESC_MAX] = {
&lang_desc,
&manuf_desc_en,
&prod_desc_en,
&cdc_iface_desc_en,
&debug_iface_desc_en,
&serial_number_desc_en,
};
#define DEBUG_DESCRIPTORS \
struct usb_iad_descriptor debug_comm_iad; \
struct usb_interface_descriptor debug_comm; \
struct usb_cdc_header_desc debug_cdc_hdr; \
struct usb_cdc_call_mgmt_desc debug_cdc_mgmt; \
struct usb_cdc_acm_desc debug_cdc_acm; \
struct usb_cdc_union_desc debug_cdc_union; \
struct usb_endpoint_descriptor debug_comm_ep; \
struct usb_interface_descriptor debug_data; \
struct usb_endpoint_descriptor debug_data_eprx; \
struct usb_endpoint_descriptor debug_data_eptx;
struct __debug_descriptors {
DEBUG_DESCRIPTORS
} __attribute__((packed));
static constexpr size_t DebugDescriptorsSize = sizeof(__debug_descriptors);
struct cdc_config {
struct usb_config_descriptor config;
struct usb_iad_descriptor tunnel_comm_iad;
struct usb_interface_descriptor tunnel_comm;
struct usb_cdc_header_desc tunnel_cdc_hdr;
struct usb_cdc_call_mgmt_desc tunnel_cdc_mgmt;
struct usb_cdc_acm_desc tunnel_cdc_acm;
struct usb_cdc_union_desc tunnel_cdc_union;
struct usb_endpoint_descriptor tunnel_comm_ep;
struct usb_interface_descriptor tunnel_data;
struct usb_endpoint_descriptor tunnel_data_eprx;
struct usb_endpoint_descriptor tunnel_data_eptx;
DEBUG_DESCRIPTORS
} __attribute__((packed));
static const struct usb_device_descriptor device_desc = {
.bLength = sizeof(struct usb_device_descriptor),
.bDescriptorType = USB_DTYPE_DEVICE,
.bcdUSB = VERSION_BCD(2, 0, 0),
.bDeviceClass = USB_CLASS_IAD,
.bDeviceSubClass = USB_SUBCLASS_IAD,
.bDeviceProtocol = USB_PROTO_IAD,
.bMaxPacketSize0 = CDC_EP0_SIZE,
.idVendor = 0x0483,
.idProduct = 0x5740,
.bcdDevice = VERSION_BCD(1, 0, 0),
.iManufacturer = STRDESC_MANUFACTURER,
.iProduct = STRDESC_PRODUCT,
.iSerialNumber = STRDESC_SERIAL,
.bNumConfigurations = 1,
};
static struct cdc_config config_desc = {
.config = {
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DTYPE_CONFIGURATION,
.wTotalLength = sizeof(struct cdc_config),
.bNumInterfaces = INTERFACE_COUNT_ALL,
.bConfigurationValue = 1,
.iConfiguration = NO_DESCRIPTOR,
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
.bMaxPower = USB_CFG_POWER_MA(100),
},
.tunnel_comm_iad = {
.bLength = sizeof(struct usb_iad_descriptor),
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
.bFirstInterface = INTERFACE_TUNNEL_COMM,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_CDC,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_PROTO_NONE,
.iFunction = NO_DESCRIPTOR,
},
.tunnel_comm = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = INTERFACE_TUNNEL_COMM,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = NO_DESCRIPTOR,
},
.tunnel_cdc_hdr = {
.bFunctionLength = sizeof(struct usb_cdc_header_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_HEADER,
.bcdCDC = VERSION_BCD(1, 1, 0),
},
.tunnel_cdc_mgmt = {
.bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT,
.bmCapabilities = 0,
.bDataInterface = INTERFACE_TUNNEL_DATA,
},
.tunnel_cdc_acm = {
.bFunctionLength = sizeof(struct usb_cdc_acm_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_ACM,
.bmCapabilities = 0,
},
.tunnel_cdc_union = {
.bFunctionLength = sizeof(struct usb_cdc_union_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_UNION,
.bMasterInterface0 = INTERFACE_TUNNEL_COMM,
.bSlaveInterface0 = INTERFACE_TUNNEL_DATA,
},
.tunnel_comm_ep = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_TUNNEL_NTF_EP,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = CDC_NTF_SZ,
.bInterval = 0xFF,
},
.tunnel_data = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = INTERFACE_TUNNEL_DATA,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = USB_SUBCLASS_NONE,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = STRDESC_CDC_IFACE,
},
.tunnel_data_eprx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_TUNNEL_RXD_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = CDC_DATA_SZ,
.bInterval = 0x01,
},
.tunnel_data_eptx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_TUNNEL_TXD_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = CDC_DATA_SZ,
.bInterval = 0x01,
},
.debug_comm_iad = {
.bLength = sizeof(struct usb_iad_descriptor),
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
.bFirstInterface = INTERFACE_DEBUG_COMM,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_CDC,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_PROTO_NONE,
.iFunction = NO_DESCRIPTOR,
},
.debug_comm = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = INTERFACE_DEBUG_COMM,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = NO_DESCRIPTOR,
},
.debug_cdc_hdr = {
.bFunctionLength = sizeof(struct usb_cdc_header_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_HEADER,
.bcdCDC = VERSION_BCD(1, 1, 0),
},
.debug_cdc_mgmt = {
.bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT,
.bmCapabilities = 0,
.bDataInterface = INTERFACE_DEBUG_DATA,
},
.debug_cdc_acm = {
.bFunctionLength = sizeof(struct usb_cdc_acm_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_ACM,
.bmCapabilities = 0,
},
.debug_cdc_union = {
.bFunctionLength = sizeof(struct usb_cdc_union_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_UNION,
.bMasterInterface0 = INTERFACE_DEBUG_COMM,
.bSlaveInterface0 = INTERFACE_DEBUG_DATA,
},
.debug_comm_ep = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_DEBUG_NTF_EP,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = CDC_NTF_SZ,
.bInterval = 0xFF,
},
.debug_data = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = INTERFACE_DEBUG_DATA,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = USB_SUBCLASS_NONE,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = STRDESC_DEBUG_IFACE,
},
.debug_data_eprx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_DEBUG_RXD_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = CDC_DATA_SZ,
.bInterval = 0x01,
},
.debug_data_eptx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = CDC_DEBUG_TXD_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = CDC_DATA_SZ,
.bInterval = 0x01,
},
};
usbd_device udev;
static uint32_t ubuf[0x20];
static bool enableDebugEp = false;
static struct usb_cdc_line_coding cdc_line_tunnel = {
.dwDTERate = 115200,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8,
};
static struct usb_cdc_line_coding cdc_line_debug = {
.dwDTERate = 115200,
.bCharFormat = USB_CDC_1_STOP_BITS,
.bParityType = USB_CDC_NO_PARITY,
.bDataBits = 8,
};
static usbd_respond cdc_getdesc(
usbd_ctlreq* req, void** address, uint16_t* length) {
const uint8_t dtype = req->wValue >> 8;
const uint8_t dnumber = req->wValue & 0xFF;
const void* desc;
uint16_t len = 0;
switch (dtype) {
case USB_DTYPE_DEVICE:
desc = &device_desc;
break;
case USB_DTYPE_CONFIGURATION:
desc = &config_desc;
len = config_desc.config.wTotalLength;
break;
case USB_DTYPE_STRING:
if (dnumber < STRDESC_MAX) {
desc = dtable[dnumber];
} else {
return usbd_fail;
}
break;
default:
return usbd_fail;
}
if (len == 0) {
len = ((struct usb_header_descriptor*)desc)->bLength;
}
*address = (void*)desc;
*length = len;
return usbd_ack;
};
static void tunnel_check_for_dfu_request(
const struct usb_cdc_line_coding* coding) {
#ifdef RBCX_SBOOT
if (coding->dwDTERate == 12345 && coding->bParityType == USB_CDC_EVEN_PARITY
&& coding->bCharFormat == USB_CDC_2_STOP_BITS) {
rebootToDfu();
}
#endif
}
static usbd_respond cdc_control_tunnel(usbd_device* dev, usbd_ctlreq* req) {
switch (req->bRequest) {
case USB_CDC_SET_CONTROL_LINE_STATE: {
const bool dtr = req->wValue & 0x01;
const bool rts = req->wValue & 0x02;
//DEBUG("CONTROL_LINE_STATE DTR %d RTS %d\n", (int)dtr, (int)rts);
sEsp32Manager.onSerialBreakInIrq(dtr, rts);
return usbd_ack;
}
case USB_CDC_SET_LINE_CODING: {
if (req->wLength < sizeof(cdc_line_tunnel))
return usbd_fail;
auto* newCoding = (struct usb_cdc_line_coding*)req->data;
tunnel_check_for_dfu_request(newCoding);
if (!tunnelOnSetLineCodingInIrq(cdc_line_tunnel, *newCoding))
return usbd_fail;
memcpy(&cdc_line_tunnel, req->data, sizeof(cdc_line_tunnel));
/*DEBUG("USB_CDC_SET_LINE_CODING %d %d %d %d\n",
cdc_line_tunnel.dwDTERate, cdc_line_tunnel.bCharFormat,
cdc_line_tunnel.bDataBits, cdc_line_tunnel.bParityType);*/
return usbd_ack;
}
case USB_CDC_GET_LINE_CODING:
dev->status.data_ptr = &cdc_line_tunnel;
dev->status.data_count = sizeof(cdc_line_tunnel);
return usbd_ack;
default:
return usbd_fail;
}
}
static usbd_respond cdc_control_debug(usbd_device* dev, usbd_ctlreq* req) {
switch (req->bRequest) {
case USB_CDC_SET_CONTROL_LINE_STATE: {
return usbd_ack;
}
case USB_CDC_SET_LINE_CODING: {
if (req->wLength < sizeof(cdc_line_debug))
return usbd_fail;
memcpy(&cdc_line_debug, req->data, sizeof(cdc_line_debug));
//DEBUG("USB_CDC_SET_LINE_CODING %d %d %d %d\n", cdc_line.dwDTERate,
// cdc_line.bCharFormat, cdc_line.bDataBits, cdc_line.bParityType);
return usbd_ack;
}
case USB_CDC_GET_LINE_CODING:
dev->status.data_ptr = &cdc_line_debug;
dev->status.data_count = sizeof(cdc_line_debug);
return usbd_ack;
default:
return usbd_fail;
}
}
static usbd_respond cdc_control(
usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType)
== (USB_REQ_INTERFACE | USB_REQ_CLASS)) {
switch (req->wIndex) {
case INTERFACE_TUNNEL_COMM:
return cdc_control_tunnel(dev, req);
case INTERFACE_DEBUG_COMM:
return cdc_control_debug(dev, req);
}
}
return usbd_fail;
}
static usbd_respond cdc_setconf(usbd_device* dev, uint8_t cfg) {
switch (cfg) {
case 0:
/* deconfiguring device */
usbd_ep_deconfig(dev, CDC_TUNNEL_NTF_EP);
usbd_ep_deconfig(dev, CDC_TUNNEL_TXD_EP);
usbd_ep_deconfig(dev, CDC_TUNNEL_RXD_EP);
if (enableDebugEp) {
usbd_ep_deconfig(dev, CDC_DEBUG_NTF_EP);
usbd_ep_deconfig(dev, CDC_DEBUG_TXD_EP);
usbd_ep_deconfig(dev, CDC_DEBUG_RXD_EP);
}
return usbd_ack;
case 1:
/* configuring device */
usbd_ep_config(dev, CDC_TUNNEL_RXD_EP,
USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, CDC_DATA_SZ);
usbd_ep_config(dev, CDC_TUNNEL_TXD_EP,
USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, CDC_DATA_SZ);
usbd_ep_config(
dev, CDC_TUNNEL_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
if (enableDebugEp) {
usbd_ep_config(dev, CDC_DEBUG_RXD_EP,
USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, CDC_DATA_SZ);
usbd_ep_config(dev, CDC_DEBUG_TXD_EP,
USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, CDC_DATA_SZ);
usbd_ep_config(
dev, CDC_DEBUG_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ);
}
return usbd_ack;
default:
return usbd_fail;
}
}
void cdcLinkInit() {
__HAL_RCC_USB_CLK_ENABLE();
// quickly charge Button capacitor
pinInit(button3Pin, GPIO_MODE_OUTPUT_PP, GPIO_PULLUP, GPIO_SPEED_FREQ_HIGH,
true);
pinWrite(button3Pin, 1);
std::array<uint32_t, 3> uid;
HAL_GetUID(uid.data());
char buf[9];
size_t sn_off = 0;
for (auto u : uid) {
snprintf(buf, sizeof(buf), "%08lx", u);
for (int i = 0; i < 8; ++i) {
serial_number_desc_en.wString[sn_off++] = buf[i];
}
}
// reinit button and check
for (volatile int i = 0; i < 5000; ++i)
;
pinInit(
button3Pin, GPIO_MODE_INPUT, GPIO_PULLUP, GPIO_SPEED_FREQ_LOW, true);
enableDebugEp = !pinRead(button3Pin)
|| (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk);
if (enableDebugEp) {
config_desc.config.bNumInterfaces = INTERFACE_COUNT_ALL;
config_desc.config.wTotalLength = sizeof(config_desc);
} else {
config_desc.config.bNumInterfaces = INTERFACE_COUNT_NODEBUG;
config_desc.config.wTotalLength
= sizeof(config_desc) - DebugDescriptorsSize;
}
usbd_init(&udev, &usbd_hw, CDC_EP0_SIZE, ubuf, sizeof(ubuf));
usbd_reg_config(&udev, cdc_setconf);
usbd_reg_control(&udev, cdc_control);
usbd_reg_descr(&udev, cdc_getdesc);
HAL_NVIC_SetPriority(usbLpIRQn, usbLpIRQnPrio, 0);
HAL_NVIC_EnableIRQ(usbLpIRQn);
usbd_enable(&udev, true);
usbd_connect(&udev, true);
}
bool cdcLinkIsDebugEpEnabled() { return enableDebugEp; }
extern "C" void USB_LP_IRQ_HANDLER(void) { usbd_poll(&udev); }