olsndot/bluefnorf/usb.c
2020-04-11 15:51:25 +02:00

234 lines
5.6 KiB
C

/*
* Initialize USB ACM serial device, provide convenience
* function for serial communication.
*
* Mostly plucked together from some opencm3 example code.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
#include <libopencm3/cm3/nvic.h>
#include "usb.h"
#define STATUS_LED_PORT GPIOC
#define STATUS_LED_PIN GPIO13
#define USBD_PORT GPIOA
#define USBDM GPIO11
#define USBDP GPIO12
#define RX_ECHO 1
#define RX_BUF_LEN 256
static char rx_tmp[RX_BUF_LEN];
static char rx_buf[RX_BUF_LEN];
static size_t rx_tmp_len;
static uint8_t rx_buf_ready;
static usbd_device *usbd_dev;
/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];
static const struct usb_device_descriptor device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0xfe, // USB_CLASS_CDC,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x4001,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
static const struct usb_endpoint_descriptor comm_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x83,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct usb_endpoint_descriptor data_endp[] = {{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x01,
.bmAttributes = USB_ENDPOINT_ATTR_BULK,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct usb_interface_descriptor comm_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0xfe, // USB_CLASS_CDC,
.bInterfaceSubClass = 0x00, // USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = 0x00, // USB_CDC_PROTOCOL_AT,
.iInterface = 0,
.endpoint = comm_endp,
.extra = NULL, // &cdcacm_functional_descriptors,
.extralen = 0,
}};
static const struct usb_interface_descriptor data_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = data_endp,
}};
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = comm_iface,
}, {
.num_altsetting = 1,
.altsetting = data_iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"Chaos Computer Club Berlin e.V.",
"bluefnorf",
"bf2",
};
void usb_gpio_init() {
// Clock
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOC);
// D+
gpio_set_mode(USBD_PORT,
GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
USBDP);
// LED
gpio_set_mode(STATUS_LED_PORT,
GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL,
STATUS_LED_PIN);
}
void usb_status_led_toggle() {
gpio_toggle(STATUS_LED_PORT, STATUS_LED_PIN);
}
static void usb_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) {
char buf[64];
int len = usbd_ep_read_packet(usbd_dev, ep, buf, 64);
if (len) {
usbd_ep_write_packet(usbd_dev, 0x82, buf, len);
}
}
static enum usbd_request_return_codes usb_control_request_cb(usbd_device *usbd_dev, struct usb_setup_data *req,
uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) {
(void)usbd_dev;
(void)req;
(void)buf;
(void)len;
(void)complete;
return 0;
}
static void usb_set_config_cb(usbd_device *usbd_dev, uint16_t wValue) {
(void)wValue;
usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, usb_data_rx_cb);
usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL);
usbd_register_control_callback(
usbd_dev,
USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
usb_control_request_cb
);
}
usbd_device *usb_serial_init() {
// Initialize GPIO
usb_gpio_init();
// Initialize buffers
memset(rx_tmp, 0, RX_BUF_LEN);
memset(rx_buf, 0, RX_BUF_LEN);
rx_tmp_len = 0;
rx_buf_ready = 0;
// Pull down D+
gpio_clear(USBD_PORT, USBDP);
for (int i = 0; i < 0x10000; i++) {
__asm__("nop");
}
gpio_set(USBD_PORT, USBDP);
// Initialize usb device
usbd_dev = usbd_init(&st_usbfs_v1_usb_driver,
&device_descriptor,
&config,
usb_strings, 3,
usbd_control_buffer, sizeof(usbd_control_buffer));
usbd_register_set_config_callback(usbd_dev, usb_set_config_cb);
// Pull down
gpio_clear(USBD_PORT, USBDP);
nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
return usbd_dev;
}
void usb_lp_can_rx0_isr() {
usbd_poll(usbd_dev);
nvic_clear_pending_irq(NVIC_USB_LP_CAN_RX0_IRQ);
}