ihsm-strain-gage-controller-fw/src/usb_if.c
2023-06-06 13:10:06 +02:00

330 lines
12 KiB
C

#include <global.h>
#include <usb_if.h>
#define USB_EP0_SIZE 8
#define USB_CDC_RX_EP 0x01
#define USB_CDC_TX_EP 0x81
#define USB_CDC_NTF_EP 0x82
#define USB_CDC_NTF_SIZE 0x08
static usbd_respond usb_getdesc (usbd_ctlreq *req, void **address, uint16_t *length);
static usbd_respond usb_control(usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback);
static void usb_cobs_rx(usbd_device *dev, uint8_t event, uint8_t ep);
static int usb_cobs_output(char c);
static void usb_cobs_tx(usbd_device *dev, uint8_t event, uint8_t ep);
static void cdc_endpoint_handler(usbd_device *dev, uint8_t event, uint8_t ep);
struct cdc_config {
struct usb_config_descriptor config;
struct usb_iad_descriptor comm_iad;
struct usb_interface_descriptor comm;
struct usb_cdc_header_desc cdc_hdr;
struct usb_cdc_call_mgmt_desc cdc_mgmt;
struct usb_cdc_acm_desc cdc_acm;
struct usb_cdc_union_desc cdc_union;
struct usb_endpoint_descriptor comm_ep;
struct usb_interface_descriptor data;
struct usb_endpoint_descriptor data_eprx;
struct usb_endpoint_descriptor data_eptx;
} __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 = USB_EP0_SIZE,
.idVendor = 0x0483,
.idProduct = 0x5740,
.bcdDevice = VERSION_BCD(1,0,0),
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = INTSERIALNO_DESCRIPTOR,
.bNumConfigurations = 1,
};
/* Device configuration descriptor */
static const struct cdc_config config_desc = {
.config = {
.bLength = sizeof(struct usb_config_descriptor),
.bDescriptorType = USB_DTYPE_CONFIGURATION,
.wTotalLength = sizeof(struct cdc_config),
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.iConfiguration = NO_DESCRIPTOR,
.bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
.bMaxPower = USB_CFG_POWER_MA(100),
},
.comm_iad = {
.bLength = sizeof(struct usb_iad_descriptor),
.bDescriptorType = USB_DTYPE_INTERFASEASSOC,
.bFirstInterface = 0,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_CDC,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_PROTO_NONE,
.iFunction = NO_DESCRIPTOR,
},
.comm = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = NO_DESCRIPTOR,
},
.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),
},
.cdc_mgmt = {
.bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT,
.bmCapabilities = 0,
.bDataInterface = 1,
},
.cdc_acm = {
.bFunctionLength = sizeof(struct usb_cdc_acm_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_ACM,
.bmCapabilities = 0,
},
.cdc_union = {
.bFunctionLength = sizeof(struct usb_cdc_union_desc),
.bDescriptorType = USB_DTYPE_CS_INTERFACE,
.bDescriptorSubType = USB_DTYPE_CDC_UNION,
.bMasterInterface0 = 0,
.bSlaveInterface0 = 1,
},
.comm_ep = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = USB_CDC_NTF_EP,
.bmAttributes = USB_EPTYPE_INTERRUPT,
.wMaxPacketSize = USB_CDC_NTF_SIZE,
.bInterval = 0xFF,
},
.data = {
.bLength = sizeof(struct usb_interface_descriptor),
.bDescriptorType = USB_DTYPE_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = USB_SUBCLASS_NONE,
.bInterfaceProtocol = USB_PROTO_NONE,
.iInterface = NO_DESCRIPTOR,
},
.data_eprx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = USB_CDC_RX_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = USB_CDC_DATA_SIZE,
.bInterval = 0x01,
},
.data_eptx = {
.bLength = sizeof(struct usb_endpoint_descriptor),
.bDescriptorType = USB_DTYPE_ENDPOINT,
.bEndpointAddress = USB_CDC_TX_EP,
.bmAttributes = USB_EPTYPE_BULK,
.wMaxPacketSize = USB_CDC_DATA_SIZE,
.bInterval = 0x01,
},
};
static const struct usb_string_descriptor lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
static const struct usb_string_descriptor manuf_desc_en = USB_STRING_DESC("TU Darmstadt, KOM / emergenCITY");
static const struct usb_string_descriptor prod_desc_en = USB_STRING_DESC("IHSM rotor tester");
struct usb_state st_usb;
static usbd_respond usb_getdesc (usbd_ctlreq *req, void **address, uint16_t *length) {
const uint8_t dtype = req->wValue >> 8;
const uint8_t dnumber = req->wValue & 0xFF;
switch (dtype) {
case USB_DTYPE_DEVICE: *address = (void **)&device_desc; *length = device_desc.bLength; return usbd_ack;
case USB_DTYPE_CONFIGURATION: *address = (void **)&config_desc; *length = sizeof(config_desc); return usbd_ack;
case USB_DTYPE_STRING: break;
default: return usbd_fail;
}
switch (dnumber) {
case 0: *address = (void **)&lang_desc; *length = lang_desc.bLength; return usbd_ack;
case 1: *address = (void **)&manuf_desc_en; *length = manuf_desc_en.bLength; return usbd_ack;
case 2: *address = (void **)&prod_desc_en; *length = prod_desc_en.bLength; return usbd_ack;
default: return usbd_fail;
}
}
static usbd_respond usb_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)
&& req->wIndex == 0 ) {
switch (req->bRequest) {
case USB_CDC_SET_CONTROL_LINE_STATE:
return usbd_ack;
case USB_CDC_SET_LINE_CODING:
memcpy(&st_usb.line, req->data, sizeof(st_usb.line));
return usbd_ack;
case USB_CDC_GET_LINE_CODING:
dev->status.data_ptr = &st_usb.line;
dev->status.data_count = sizeof(st_usb.line);
return usbd_ack;
default:
st_usb.error = ERR_PROTOCOL;
st_usb.last_error = sys_time_us;
return usbd_fail;
}
}
return usbd_fail;
}
static void usb_cobs_rx(usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t buf[USB_CDC_DATA_SIZE];
int nread = usbd_ep_read(dev, USB_CDC_RX_EP, buf, USB_CDC_DATA_SIZE);
for (int i=0; i<nread; i++) {
int rc = cobs_decode_incremental(&st_usb.cobs_state, (char*)st_usb.usb_pkbuf, sizeof(st_usb.usb_pkbuf), buf[i]);
if (rc < 0) { /* Framing error */
st_usb.error = ERR_PROTOCOL;
st_usb.last_error = sys_time_us;
st_usb.cobs_synchronized = false;
} else if (rc > 0 && st_usb.cobs_synchronized) { /* Packet complete */
handle_usb_packet(st_usb.usb_pkbuf, rc);
} /* else rc == 0 -> packet in progress */
if (buf[i] == 0) {
st_usb.cobs_synchronized = true;
}
}
}
static int usb_cobs_output(char c) {
size_t pos = st_usb.txbuf_wrpos;
st_usb.txbuf[pos] = c;
pos++;
if (pos >= sizeof(st_usb.txbuf)) {
pos = 0;
}
if (pos == st_usb.txbuf_rdpos) {
return ERR_BUFFER_OVERFLOW;
}
st_usb.txbuf_wrpos = pos;
return ERR_SUCCESS;
}
ErrorCode usb_write_response(void *data, size_t len) {
ErrorCode rc = cobs_encode_usart(usb_cobs_output, data, len);
if (rc) {
st_usb.error = rc;
st_usb.last_error = sys_time_us;
}
return rc;
}
static void usb_cobs_tx(usbd_device *dev, uint8_t event, uint8_t ep) {
uint8_t buf[USB_CDC_DATA_SIZE];
size_t rdpos = st_usb.txbuf_rdpos;
size_t wrpos = st_usb.txbuf_wrpos;
size_t nread = 0;
while (rdpos != wrpos && nread < USB_CDC_DATA_SIZE) {
buf[nread] = st_usb.txbuf[rdpos];
nread++;
rdpos++;
if (rdpos == sizeof(st_usb.txbuf)) {
rdpos = 0;
}
}
if (nread) {
usbd_ep_write(dev, ep, buf, nread);
st_usb.txbuf_rdpos = rdpos;
}
}
static void cdc_endpoint_handler(usbd_device *dev, uint8_t event, uint8_t ep) {
usb_cobs_rx(dev, event, ep);
usb_cobs_tx(dev, event, ep);
}
static usbd_respond usb_setconf (usbd_device *dev, uint8_t cfg) {
switch (cfg) {
case 0:
/* deconfiguring device */
usbd_ep_deconfig(dev, USB_CDC_NTF_EP);
usbd_ep_deconfig(dev, USB_CDC_TX_EP);
usbd_ep_deconfig(dev, USB_CDC_RX_EP);
usbd_reg_endpoint(dev, USB_CDC_RX_EP, 0);
usbd_reg_endpoint(dev, USB_CDC_TX_EP, 0);
st_usb.error = ERR_SUCCESS;
st_usb.connected = false;
return usbd_ack;
case 1:
/* configuring device */
usbd_ep_config(dev, USB_CDC_RX_EP, USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, USB_CDC_DATA_SIZE);
usbd_ep_config(dev, USB_CDC_TX_EP, USB_EPTYPE_BULK /*| USB_EPTYPE_DBLBUF*/, USB_CDC_DATA_SIZE);
usbd_ep_config(dev, USB_CDC_NTF_EP, USB_EPTYPE_INTERRUPT, USB_CDC_NTF_SIZE);
usbd_reg_endpoint(dev, USB_CDC_RX_EP, cdc_endpoint_handler);
usbd_reg_endpoint(dev, USB_CDC_TX_EP, cdc_endpoint_handler);
usbd_ep_write(dev, USB_CDC_TX_EP, 0, 0);
cobs_decode_incremental_initialize(&st_usb.cobs_state);
st_usb.cobs_synchronized = false;
st_usb.line.dwDTERate = 115200;
st_usb.line.bCharFormat = USB_CDC_1_STOP_BITS;
st_usb.line.bParityType = USB_CDC_NO_PARITY;
st_usb.line.bDataBits = 8;
st_usb.error = ERR_SUCCESS;
st_usb.connected = true;
return usbd_ack;
default:
st_usb.error = ERR_PROTOCOL;
st_usb.last_error = sys_time_us;
return usbd_fail;
}
}
void usb_init() {
memset(&st_usb, 0, sizeof(st_usb));
usbd_init(&st_usb.usb_dev, &usbd_hw, USB_EP0_SIZE, st_usb.usb_buf, sizeof(st_usb.usb_buf));
usbd_reg_config(&st_usb.usb_dev, usb_setconf);
usbd_reg_control(&st_usb.usb_dev, usb_control);
usbd_reg_descr(&st_usb.usb_dev, usb_getdesc);
usbd_enable(&st_usb.usb_dev, true);
usbd_connect(&st_usb.usb_dev, true);
}
void USB_IRQHandler() {
usbd_poll(&st_usb.usb_dev);
USB->ISTR = 0;
}