330 lines
12 KiB
C
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;
|
|
}
|
|
|