mirror of https://github.com/Desuuuu/klipper.git
lpc176x: Initial support for serial over usb
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
c812a40a37
commit
c381d03aad
|
@ -0,0 +1,453 @@
|
||||||
|
// Support for standard serial port over USB emulation
|
||||||
|
//
|
||||||
|
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <string.h> // memmove
|
||||||
|
#include "board/pgm.h" // PROGMEM
|
||||||
|
#include "board/usb_cdc.h" // usb_notify_setup
|
||||||
|
#include "byteorder.h" // cpu_to_le16
|
||||||
|
#include "command.h" // output
|
||||||
|
#include "generic/usbstd.h" // struct usb_device_descriptor
|
||||||
|
#include "generic/usbstd_cdc.h" // struct usb_cdc_header_descriptor
|
||||||
|
#include "sched.h" // sched_wake_task
|
||||||
|
|
||||||
|
// XXX - move to Kconfig
|
||||||
|
#define CONFIG_USB_VENDOR_ID 0x2341
|
||||||
|
#define CONFIG_USB_PRODUCT_ID 0xabcd
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Message block sending
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
static struct task_wake usb_bulk_in_wake;
|
||||||
|
static char transmit_buf[96];
|
||||||
|
static uint8_t transmit_pos;
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_notify_bulk_in(void)
|
||||||
|
{
|
||||||
|
sched_wake_task(&usb_bulk_in_wake);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_bulk_in_task(void)
|
||||||
|
{
|
||||||
|
if (!sched_check_wake(&usb_bulk_in_wake))
|
||||||
|
return;
|
||||||
|
uint_fast8_t tpos = transmit_pos;
|
||||||
|
if (!tpos)
|
||||||
|
return;
|
||||||
|
uint_fast8_t max_tpos = (tpos > USB_CDC_EP_BULK_IN_SIZE
|
||||||
|
? USB_CDC_EP_BULK_IN_SIZE : tpos);
|
||||||
|
int_fast8_t ret = usb_send_bulk_in(transmit_buf, max_tpos);
|
||||||
|
if (ret <= 0)
|
||||||
|
return;
|
||||||
|
uint_fast8_t needcopy = tpos - ret;
|
||||||
|
if (needcopy) {
|
||||||
|
memmove(transmit_buf, &transmit_buf[ret], needcopy);
|
||||||
|
usb_notify_bulk_in();
|
||||||
|
}
|
||||||
|
transmit_pos = needcopy;
|
||||||
|
}
|
||||||
|
DECL_TASK(usb_bulk_in_task);
|
||||||
|
|
||||||
|
// Encode and transmit a "response" message
|
||||||
|
void
|
||||||
|
console_sendf(const struct command_encoder *ce, va_list args)
|
||||||
|
{
|
||||||
|
// Verify space for message
|
||||||
|
uint_fast8_t tpos = transmit_pos, max_size = READP(ce->max_size);
|
||||||
|
if (tpos + max_size > sizeof(transmit_buf))
|
||||||
|
// Not enough space for message
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Generate message
|
||||||
|
char *buf = &transmit_buf[tpos];
|
||||||
|
uint8_t msglen = command_encodef(buf, ce, args);
|
||||||
|
command_add_frame(buf, msglen);
|
||||||
|
|
||||||
|
// Start message transmit
|
||||||
|
transmit_pos = tpos + msglen;
|
||||||
|
usb_notify_bulk_in();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Message block reading
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
static struct task_wake usb_bulk_out_wake;
|
||||||
|
static char receive_buf[128];
|
||||||
|
static uint8_t receive_pos;
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_notify_bulk_out(void)
|
||||||
|
{
|
||||||
|
sched_wake_task(&usb_bulk_out_wake);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_bulk_out_task(void)
|
||||||
|
{
|
||||||
|
if (!sched_check_wake(&usb_bulk_out_wake))
|
||||||
|
return;
|
||||||
|
// Process any existing message blocks
|
||||||
|
uint_fast8_t rpos = receive_pos;
|
||||||
|
uint8_t pop_count;
|
||||||
|
int_fast8_t ret = command_find_block(receive_buf, rpos, &pop_count);
|
||||||
|
if (ret > 0)
|
||||||
|
command_dispatch(receive_buf, pop_count);
|
||||||
|
if (ret) {
|
||||||
|
// Move buffer
|
||||||
|
uint_fast8_t needcopy = rpos - pop_count;
|
||||||
|
if (needcopy) {
|
||||||
|
memmove(receive_buf, &receive_buf[pop_count], needcopy);
|
||||||
|
usb_notify_bulk_out();
|
||||||
|
}
|
||||||
|
rpos = needcopy;
|
||||||
|
}
|
||||||
|
// Read more data
|
||||||
|
if (rpos + USB_CDC_EP_BULK_OUT_SIZE <= sizeof(receive_buf)) {
|
||||||
|
ret = usb_read_bulk_out(&receive_buf[rpos], USB_CDC_EP_BULK_OUT_SIZE);
|
||||||
|
if (ret > 0) {
|
||||||
|
rpos += ret;
|
||||||
|
usb_notify_bulk_out();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receive_pos = rpos;
|
||||||
|
}
|
||||||
|
DECL_TASK(usb_bulk_out_task);
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* USB descriptors
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
// XXX - move to Kconfig
|
||||||
|
#define USB_STR_MANUFACTURER u"Klipper"
|
||||||
|
#define USB_STR_PRODUCT u"Klipper firmware"
|
||||||
|
#define USB_STR_SERIAL u"12345"
|
||||||
|
|
||||||
|
// String descriptors
|
||||||
|
enum {
|
||||||
|
USB_STR_ID_MANUFACTURER = 1, USB_STR_ID_PRODUCT, USB_STR_ID_SERIAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIZE_cdc_string_langids (sizeof(cdc_string_langids) + 2)
|
||||||
|
|
||||||
|
static const struct usb_string_descriptor cdc_string_langids PROGMEM = {
|
||||||
|
.bLength = SIZE_cdc_string_langids,
|
||||||
|
.bDescriptorType = USB_DT_STRING,
|
||||||
|
.data = { cpu_to_le16(USB_LANGID_ENGLISH_US) },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIZE_cdc_string_manufacturer \
|
||||||
|
(sizeof(cdc_string_manufacturer) + sizeof(USB_STR_MANUFACTURER) - 2)
|
||||||
|
|
||||||
|
static const struct usb_string_descriptor cdc_string_manufacturer PROGMEM = {
|
||||||
|
.bLength = SIZE_cdc_string_manufacturer,
|
||||||
|
.bDescriptorType = USB_DT_STRING,
|
||||||
|
.data = USB_STR_MANUFACTURER,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIZE_cdc_string_product \
|
||||||
|
(sizeof(cdc_string_product) + sizeof(USB_STR_PRODUCT) - 2)
|
||||||
|
|
||||||
|
static const struct usb_string_descriptor cdc_string_product PROGMEM = {
|
||||||
|
.bLength = SIZE_cdc_string_product,
|
||||||
|
.bDescriptorType = USB_DT_STRING,
|
||||||
|
.data = USB_STR_PRODUCT,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SIZE_cdc_string_serial \
|
||||||
|
(sizeof(cdc_string_serial) + sizeof(USB_STR_SERIAL) - 2)
|
||||||
|
|
||||||
|
static const struct usb_string_descriptor cdc_string_serial PROGMEM = {
|
||||||
|
.bLength = SIZE_cdc_string_serial,
|
||||||
|
.bDescriptorType = USB_DT_STRING,
|
||||||
|
.data = USB_STR_SERIAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Device descriptor
|
||||||
|
static const struct usb_device_descriptor cdc_device_descriptor PROGMEM = {
|
||||||
|
.bLength = sizeof(cdc_device_descriptor),
|
||||||
|
.bDescriptorType = USB_DT_DEVICE,
|
||||||
|
.bcdUSB = cpu_to_le16(0x0200),
|
||||||
|
.bDeviceClass = USB_CLASS_COMM,
|
||||||
|
.bMaxPacketSize0 = USB_CDC_EP0_SIZE,
|
||||||
|
.idVendor = cpu_to_le16(CONFIG_USB_VENDOR_ID),
|
||||||
|
.idProduct = cpu_to_le16(CONFIG_USB_PRODUCT_ID),
|
||||||
|
.bcdDevice = cpu_to_le16(0x0100),
|
||||||
|
.iManufacturer = USB_STR_ID_MANUFACTURER,
|
||||||
|
.iProduct = USB_STR_ID_PRODUCT,
|
||||||
|
.iSerialNumber = USB_STR_ID_SERIAL,
|
||||||
|
.bNumConfigurations = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Config descriptor
|
||||||
|
static const struct config_s {
|
||||||
|
struct usb_config_descriptor config;
|
||||||
|
struct usb_interface_descriptor iface0;
|
||||||
|
struct usb_cdc_header_descriptor cdc_hdr;
|
||||||
|
struct usb_cdc_acm_descriptor cdc_acm;
|
||||||
|
struct usb_cdc_union_descriptor cdc_union;
|
||||||
|
struct usb_endpoint_descriptor ep1;
|
||||||
|
struct usb_interface_descriptor iface1;
|
||||||
|
struct usb_endpoint_descriptor ep2;
|
||||||
|
struct usb_endpoint_descriptor ep3;
|
||||||
|
} PACKED cdc_config_descriptor PROGMEM = {
|
||||||
|
.config = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.config),
|
||||||
|
.bDescriptorType = USB_DT_CONFIG,
|
||||||
|
.wTotalLength = cpu_to_le16(sizeof(cdc_config_descriptor)),
|
||||||
|
.bNumInterfaces = 2,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = 0xC0,
|
||||||
|
.bMaxPower = 50,
|
||||||
|
},
|
||||||
|
.iface0 = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.iface0),
|
||||||
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
|
.bInterfaceNumber = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_CLASS_COMM,
|
||||||
|
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||||
|
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
|
||||||
|
},
|
||||||
|
.cdc_hdr = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.cdc_hdr),
|
||||||
|
.bDescriptorType = USB_CDC_CS_INTERFACE,
|
||||||
|
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||||
|
.bcdCDC = 0x0110,
|
||||||
|
},
|
||||||
|
.cdc_acm = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.cdc_acm),
|
||||||
|
.bDescriptorType = USB_CDC_CS_INTERFACE,
|
||||||
|
.bDescriptorSubType = USB_CDC_ACM_TYPE,
|
||||||
|
.bmCapabilities = 0x06,
|
||||||
|
},
|
||||||
|
.cdc_union = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.cdc_union),
|
||||||
|
.bDescriptorType = USB_CDC_CS_INTERFACE,
|
||||||
|
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||||
|
.bMasterInterface0 = 0,
|
||||||
|
.bSlaveInterface0 = 1,
|
||||||
|
},
|
||||||
|
.ep1 = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.ep1),
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_CDC_EP_ACM | USB_DIR_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_ACM_SIZE),
|
||||||
|
.bInterval = 255,
|
||||||
|
},
|
||||||
|
.iface1 = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.iface1),
|
||||||
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0x0A,
|
||||||
|
},
|
||||||
|
.ep2 = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.ep2),
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_CDC_EP_BULK_OUT,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_BULK_OUT_SIZE),
|
||||||
|
},
|
||||||
|
.ep3 = {
|
||||||
|
.bLength = sizeof(cdc_config_descriptor.ep3),
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = USB_CDC_EP_BULK_IN | USB_DIR_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_BULK_IN_SIZE),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// List of available descriptors
|
||||||
|
static const struct descriptor_s {
|
||||||
|
uint_fast16_t wValue;
|
||||||
|
uint_fast16_t wIndex;
|
||||||
|
const void *desc;
|
||||||
|
uint_fast8_t size;
|
||||||
|
} cdc_descriptors[] PROGMEM = {
|
||||||
|
{ USB_DT_DEVICE<<8, 0x0000,
|
||||||
|
&cdc_device_descriptor, sizeof(cdc_device_descriptor) },
|
||||||
|
{ USB_DT_CONFIG<<8, 0x0000,
|
||||||
|
&cdc_config_descriptor, sizeof(cdc_config_descriptor) },
|
||||||
|
{ USB_DT_STRING<<8, 0x0000,
|
||||||
|
&cdc_string_langids, SIZE_cdc_string_langids },
|
||||||
|
{ (USB_DT_STRING<<8) | USB_STR_ID_MANUFACTURER, USB_LANGID_ENGLISH_US,
|
||||||
|
&cdc_string_manufacturer, SIZE_cdc_string_manufacturer },
|
||||||
|
{ (USB_DT_STRING<<8) | USB_STR_ID_PRODUCT, USB_LANGID_ENGLISH_US,
|
||||||
|
&cdc_string_product, SIZE_cdc_string_product },
|
||||||
|
{ (USB_DT_STRING<<8) | USB_STR_ID_SERIAL, USB_LANGID_ENGLISH_US,
|
||||||
|
&cdc_string_serial, SIZE_cdc_string_serial },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* USB endpoint 0 control message handling
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
// State tracking
|
||||||
|
enum {
|
||||||
|
US_READY, US_SEND, US_READ
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint_fast8_t usb_state;
|
||||||
|
static void *usb_xfer;
|
||||||
|
static uint_fast8_t usb_xfer_size;
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_do_stall(void)
|
||||||
|
{
|
||||||
|
usb_set_stall();
|
||||||
|
usb_state = US_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sending data from device to host
|
||||||
|
static void
|
||||||
|
usb_state_xfer(void)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
uint_fast8_t xs = usb_xfer_size;
|
||||||
|
if (xs > USB_CDC_EP0_SIZE)
|
||||||
|
xs = USB_CDC_EP0_SIZE;
|
||||||
|
int_fast8_t ret;
|
||||||
|
if (usb_state == US_SEND)
|
||||||
|
ret = usb_send_setup(usb_xfer, xs);
|
||||||
|
else
|
||||||
|
ret = usb_read_setup(usb_xfer, xs);
|
||||||
|
if (ret == xs) {
|
||||||
|
// Success
|
||||||
|
usb_xfer += xs;
|
||||||
|
usb_xfer_size -= xs;
|
||||||
|
if (!usb_xfer_size && xs < USB_CDC_EP0_SIZE) {
|
||||||
|
// Transfer completed successfully
|
||||||
|
if (usb_state == US_READ)
|
||||||
|
usb_send_setup(NULL, 0);
|
||||||
|
usb_state = US_READY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ret == -1)
|
||||||
|
// Interface busy - retry later
|
||||||
|
return;
|
||||||
|
// Error
|
||||||
|
usb_do_stall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_get_descriptor(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
// XXX - validate req
|
||||||
|
uint_fast8_t i;
|
||||||
|
for (i=0; i<ARRAY_SIZE(cdc_descriptors); i++) {
|
||||||
|
const struct descriptor_s *d = &cdc_descriptors[i];
|
||||||
|
if (READP(d->wValue) == req->wValue
|
||||||
|
&& READP(d->wIndex) == req->wIndex) {
|
||||||
|
usb_state = US_SEND;
|
||||||
|
usb_xfer = (void*)READP(d->desc);
|
||||||
|
usb_xfer_size = READP(d->size);
|
||||||
|
if (usb_xfer_size > req->wLength)
|
||||||
|
usb_xfer_size = req->wLength;
|
||||||
|
usb_state_xfer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usb_do_stall();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_set_address(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
usb_set_address(req->wValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_set_configuration(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
usb_set_configure();
|
||||||
|
usb_send_setup(NULL, 0);
|
||||||
|
usb_notify_bulk_in();
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct usb_cdc_line_coding line_coding;
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_set_line_coding(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
usb_state = US_READ;
|
||||||
|
usb_xfer = &line_coding;
|
||||||
|
usb_xfer_size = sizeof(line_coding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_get_line_coding(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
usb_state = US_SEND;
|
||||||
|
usb_xfer = &line_coding;
|
||||||
|
usb_xfer_size = sizeof(line_coding);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_req_line_state(struct usb_ctrlrequest *req)
|
||||||
|
{
|
||||||
|
usb_send_setup(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_state_ready(void)
|
||||||
|
{
|
||||||
|
struct usb_ctrlrequest req;
|
||||||
|
int_fast8_t ret = usb_read_setup(&req, sizeof(req));
|
||||||
|
if (ret != sizeof(req))
|
||||||
|
// XXX - should verify that packet was sent with a setup token
|
||||||
|
return;
|
||||||
|
switch (req.bRequest) {
|
||||||
|
case USB_REQ_GET_DESCRIPTOR: usb_req_get_descriptor(&req); break;
|
||||||
|
case USB_REQ_SET_ADDRESS: usb_req_set_address(&req); break;
|
||||||
|
case USB_REQ_SET_CONFIGURATION: usb_req_set_configuration(&req); break;
|
||||||
|
case USB_CDC_REQ_SET_LINE_CODING: usb_req_set_line_coding(&req); break;
|
||||||
|
case USB_CDC_REQ_GET_LINE_CODING: usb_req_get_line_coding(&req); break;
|
||||||
|
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: usb_req_line_state(&req); break;
|
||||||
|
default: usb_do_stall(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State tracking dispatch
|
||||||
|
static struct task_wake usb_setup_wake;
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_notify_setup(void)
|
||||||
|
{
|
||||||
|
sched_wake_task(&usb_setup_wake);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_setup_task(void)
|
||||||
|
{
|
||||||
|
if (!sched_check_wake(&usb_setup_wake))
|
||||||
|
return;
|
||||||
|
switch (usb_state) {
|
||||||
|
case US_READY: usb_state_ready(); break;
|
||||||
|
case US_SEND: usb_state_xfer(); break;
|
||||||
|
case US_READ: usb_state_xfer(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DECL_TASK(usb_setup_task);
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_shutdown(void)
|
||||||
|
{
|
||||||
|
usb_notify_bulk_in();
|
||||||
|
usb_notify_setup();
|
||||||
|
}
|
||||||
|
DECL_SHUTDOWN(usb_shutdown);
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef __GENERIC_USB_CDC_H
|
||||||
|
#define __GENERIC_USB_CDC_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint_fast8_t
|
||||||
|
|
||||||
|
enum {
|
||||||
|
USB_CDC_EP0_SIZE = 16,
|
||||||
|
|
||||||
|
// XXX - endpoint ids may need to changed per-board
|
||||||
|
USB_CDC_EP_ACM = 1,
|
||||||
|
USB_CDC_EP_ACM_SIZE = 8,
|
||||||
|
|
||||||
|
USB_CDC_EP_BULK_OUT = 2,
|
||||||
|
USB_CDC_EP_BULK_OUT_SIZE = 64,
|
||||||
|
|
||||||
|
USB_CDC_EP_BULK_IN = 5,
|
||||||
|
USB_CDC_EP_BULK_IN_SIZE = 64,
|
||||||
|
};
|
||||||
|
|
||||||
|
// callbacks provided by board specific code
|
||||||
|
int_fast8_t usb_read_bulk_out(void *data, uint_fast8_t max_len);
|
||||||
|
int_fast8_t usb_send_bulk_in(void *data, uint_fast8_t len);
|
||||||
|
int_fast8_t usb_read_setup(void *data, uint_fast8_t max_len);
|
||||||
|
int_fast8_t usb_send_setup(const void *data, uint_fast8_t len);
|
||||||
|
void usb_send_pgm_setup(void *data, uint_fast8_t len);
|
||||||
|
void usb_set_stall(void);
|
||||||
|
void usb_set_address(uint_fast8_t addr);
|
||||||
|
void usb_set_configure(void);
|
||||||
|
|
||||||
|
// usb_cdc.c
|
||||||
|
void usb_notify_bulk_in(void);
|
||||||
|
void usb_notify_bulk_out(void);
|
||||||
|
void usb_notify_setup(void);
|
||||||
|
|
||||||
|
#endif // usb_cdc.h
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Standard definitions for USB commands and data structures
|
||||||
|
#ifndef __GENERIC_USBSTD_H
|
||||||
|
#define __GENERIC_USBSTD_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint8_t
|
||||||
|
#include "compiler.h" // PACKED
|
||||||
|
|
||||||
|
#define USB_DIR_OUT 0 /* to device */
|
||||||
|
#define USB_DIR_IN 0x80 /* to host */
|
||||||
|
|
||||||
|
#define USB_REQ_GET_STATUS 0x00
|
||||||
|
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||||
|
#define USB_REQ_SET_FEATURE 0x03
|
||||||
|
#define USB_REQ_SET_ADDRESS 0x05
|
||||||
|
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||||
|
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||||
|
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||||
|
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||||
|
#define USB_REQ_GET_INTERFACE 0x0A
|
||||||
|
#define USB_REQ_SET_INTERFACE 0x0B
|
||||||
|
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||||
|
|
||||||
|
struct usb_ctrlrequest {
|
||||||
|
uint8_t bRequestType;
|
||||||
|
uint8_t bRequest;
|
||||||
|
uint16_t wValue;
|
||||||
|
uint16_t wIndex;
|
||||||
|
uint16_t wLength;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_DT_DEVICE 0x01
|
||||||
|
#define USB_DT_CONFIG 0x02
|
||||||
|
#define USB_DT_STRING 0x03
|
||||||
|
#define USB_DT_INTERFACE 0x04
|
||||||
|
#define USB_DT_ENDPOINT 0x05
|
||||||
|
#define USB_DT_DEVICE_QUALIFIER 0x06
|
||||||
|
#define USB_DT_OTHER_SPEED_CONFIG 0x07
|
||||||
|
#define USB_DT_ENDPOINT_COMPANION 0x30
|
||||||
|
|
||||||
|
struct usb_device_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
|
||||||
|
uint16_t bcdUSB;
|
||||||
|
uint8_t bDeviceClass;
|
||||||
|
uint8_t bDeviceSubClass;
|
||||||
|
uint8_t bDeviceProtocol;
|
||||||
|
uint8_t bMaxPacketSize0;
|
||||||
|
uint16_t idVendor;
|
||||||
|
uint16_t idProduct;
|
||||||
|
uint16_t bcdDevice;
|
||||||
|
uint8_t iManufacturer;
|
||||||
|
uint8_t iProduct;
|
||||||
|
uint8_t iSerialNumber;
|
||||||
|
uint8_t bNumConfigurations;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
|
||||||
|
#define USB_CLASS_AUDIO 1
|
||||||
|
#define USB_CLASS_COMM 2
|
||||||
|
#define USB_CLASS_HID 3
|
||||||
|
#define USB_CLASS_PHYSICAL 5
|
||||||
|
#define USB_CLASS_STILL_IMAGE 6
|
||||||
|
#define USB_CLASS_PRINTER 7
|
||||||
|
#define USB_CLASS_MASS_STORAGE 8
|
||||||
|
#define USB_CLASS_HUB 9
|
||||||
|
|
||||||
|
struct usb_config_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint8_t bNumInterfaces;
|
||||||
|
uint8_t bConfigurationValue;
|
||||||
|
uint8_t iConfiguration;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint8_t bMaxPower;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct usb_interface_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
|
||||||
|
uint8_t bInterfaceNumber;
|
||||||
|
uint8_t bAlternateSetting;
|
||||||
|
uint8_t bNumEndpoints;
|
||||||
|
uint8_t bInterfaceClass;
|
||||||
|
uint8_t bInterfaceSubClass;
|
||||||
|
uint8_t bInterfaceProtocol;
|
||||||
|
uint8_t iInterface;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct usb_endpoint_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint16_t wMaxPacketSize;
|
||||||
|
uint8_t bInterval;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
|
||||||
|
#define USB_ENDPOINT_DIR_MASK 0x80
|
||||||
|
|
||||||
|
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
|
||||||
|
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||||
|
#define USB_ENDPOINT_XFER_ISOC 1
|
||||||
|
#define USB_ENDPOINT_XFER_BULK 2
|
||||||
|
#define USB_ENDPOINT_XFER_INT 3
|
||||||
|
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
|
||||||
|
|
||||||
|
struct usb_string_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t data[];
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_LANGID_ENGLISH_US 0x0409
|
||||||
|
|
||||||
|
#endif // usbstd.h
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Standard definitions for USB CDC devices
|
||||||
|
#ifndef __GENERIC_USBSTD_CDC_H
|
||||||
|
#define __GENERIC_USBSTD_CDC_H
|
||||||
|
|
||||||
|
#define USB_CDC_SUBCLASS_ACM 0x02
|
||||||
|
|
||||||
|
#define USB_CDC_ACM_PROTO_AT_V25TER 1
|
||||||
|
|
||||||
|
struct usb_cdc_header_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint16_t bcdCDC;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_CDC_HEADER_TYPE 0x00
|
||||||
|
#define USB_CDC_ACM_TYPE 0x02
|
||||||
|
#define USB_CDC_UNION_TYPE 0x06
|
||||||
|
|
||||||
|
#define USB_CDC_CS_INTERFACE 0x24
|
||||||
|
#define USB_CDC_CS_ENDPOINT 0x25
|
||||||
|
|
||||||
|
struct usb_cdc_acm_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bmCapabilities;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
struct usb_cdc_union_descriptor {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bDescriptorSubType;
|
||||||
|
uint8_t bMasterInterface0;
|
||||||
|
uint8_t bSlaveInterface0;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#define USB_CDC_REQ_SET_LINE_CODING 0x20
|
||||||
|
#define USB_CDC_REQ_GET_LINE_CODING 0x21
|
||||||
|
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
|
||||||
|
|
||||||
|
struct usb_cdc_line_coding {
|
||||||
|
uint32_t dwDTERate;
|
||||||
|
uint8_t bCharFormat;
|
||||||
|
uint8_t bParityType;
|
||||||
|
uint8_t bDataBits;
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
|
#endif // usbstd_cdc.h
|
|
@ -25,7 +25,11 @@ config CLOCK_FREQ
|
||||||
default 25000000 if MACH_LPC1768 # 100000000 / 4
|
default 25000000 if MACH_LPC1768 # 100000000 / 4
|
||||||
default 30000000 if MACH_LPC1769 # 120000000 / 4
|
default 30000000 if MACH_LPC1769 # 120000000 / 4
|
||||||
|
|
||||||
|
config USBSERIAL
|
||||||
|
bool "Use USB for communication (instead of serial)"
|
||||||
|
default y
|
||||||
config SERIAL
|
config SERIAL
|
||||||
|
depends on !USBSERIAL
|
||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
config SERIAL_BAUD
|
config SERIAL_BAUD
|
||||||
|
|
|
@ -17,6 +17,7 @@ src-y += lpc176x/main.c lpc176x/timer.c lpc176x/gpio.c
|
||||||
src-y += generic/crc16_ccitt.c generic/alloc.c
|
src-y += generic/crc16_ccitt.c generic/alloc.c
|
||||||
src-y += generic/armcm_irq.c generic/timer_irq.c
|
src-y += generic/armcm_irq.c generic/timer_irq.c
|
||||||
src-y += ../lib/lpc176x/device/system_LPC17xx.c
|
src-y += ../lib/lpc176x/device/system_LPC17xx.c
|
||||||
|
src-$(CONFIG_USBSERIAL) += lpc176x/usbserial.c generic/usb_cdc.c
|
||||||
src-$(CONFIG_SERIAL) += lpc176x/serial.c generic/serial_irq.c
|
src-$(CONFIG_SERIAL) += lpc176x/serial.c generic/serial_irq.c
|
||||||
|
|
||||||
# Add the TOOLCHAIN_GCC_ARM files to the build
|
# Add the TOOLCHAIN_GCC_ARM files to the build
|
||||||
|
|
|
@ -38,7 +38,7 @@ timer_init(void)
|
||||||
// Disable timer
|
// Disable timer
|
||||||
LPC_TIM0->TCR = 0x02;
|
LPC_TIM0->TCR = 0x02;
|
||||||
// Enable interrupts
|
// Enable interrupts
|
||||||
NVIC_SetPriority(TIMER0_IRQn, 1);
|
NVIC_SetPriority(TIMER0_IRQn, 2);
|
||||||
NVIC_EnableIRQ(TIMER0_IRQn);
|
NVIC_EnableIRQ(TIMER0_IRQn);
|
||||||
LPC_TIM0->MCR = 0x01;
|
LPC_TIM0->MCR = 0x01;
|
||||||
// Clear counter value
|
// Clear counter value
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
// Hardware interface to USB on lpc176x
|
||||||
|
//
|
||||||
|
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
#include <string.h> // memcpy
|
||||||
|
#include "LPC17xx.h" // LPC_SC
|
||||||
|
#include "board/usb_cdc.h" // usb_notify_setup
|
||||||
|
#include "byteorder.h" // cpu_to_le32
|
||||||
|
#include "command.h" // output
|
||||||
|
#include "internal.h" // gpio_peripheral
|
||||||
|
#include "sched.h" // DECL_INIT
|
||||||
|
|
||||||
|
// Internal endpoint addresses
|
||||||
|
#define EP0OUT 0x00
|
||||||
|
#define EP0IN 0x01
|
||||||
|
#define EP1IN 0x03
|
||||||
|
#define EP2OUT 0x04
|
||||||
|
#define EP5IN 0x0b
|
||||||
|
|
||||||
|
// USB device interupt status flags
|
||||||
|
#define EP_SLOW (1<<2)
|
||||||
|
#define DEV_STAT (1<<3)
|
||||||
|
#define CCEMPTY (1<<4)
|
||||||
|
#define CDFULL (1<<5)
|
||||||
|
#define EP_RLZED (1<<8)
|
||||||
|
|
||||||
|
#define RD_EN (1<<0)
|
||||||
|
#define WR_EN (1<<1)
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_irq_disable(void)
|
||||||
|
{
|
||||||
|
NVIC_DisableIRQ(USB_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_irq_enable(void)
|
||||||
|
{
|
||||||
|
NVIC_EnableIRQ(USB_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usb_wait(uint32_t flag)
|
||||||
|
{
|
||||||
|
while (!(LPC_USB->USBDevIntSt & flag))
|
||||||
|
;
|
||||||
|
LPC_USB->USBDevIntClr = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Serial Interface Engine (SIE) functions
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
#define SIE_CMD_SELECT 0x00
|
||||||
|
#define SIE_CMD_SET_ENDPOINT_STATUS 0x40
|
||||||
|
#define SIE_CMD_SET_ADDRESS 0xD0
|
||||||
|
#define SIE_CMD_CONFIGURE 0xD8
|
||||||
|
#define SIE_CMD_SET_DEVICE_STATUS 0xFE
|
||||||
|
#define SIE_CMD_CLEAR_BUFFER 0xF2
|
||||||
|
#define SIE_CMD_VALIDATE_BUFFER 0xFA
|
||||||
|
|
||||||
|
static void
|
||||||
|
sie_cmd(uint32_t cmd)
|
||||||
|
{
|
||||||
|
LPC_USB->USBDevIntClr = CDFULL | CCEMPTY;
|
||||||
|
LPC_USB->USBCmdCode = 0x00000500 | (cmd << 16);
|
||||||
|
usb_wait(CCEMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sie_cmd_write(uint32_t cmd, uint32_t data)
|
||||||
|
{
|
||||||
|
sie_cmd(cmd);
|
||||||
|
LPC_USB->USBCmdCode = 0x00000100 | (data << 16);
|
||||||
|
usb_wait(CCEMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
sie_cmd_read(uint32_t cmd)
|
||||||
|
{
|
||||||
|
sie_cmd(cmd);
|
||||||
|
LPC_USB->USBCmdCode = 0x00000200 | (cmd << 16);
|
||||||
|
usb_wait(CDFULL);
|
||||||
|
return LPC_USB->USBCmdData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
sie_select_and_clear(uint32_t idx)
|
||||||
|
{
|
||||||
|
LPC_USB->USBEpIntClr = 1<<idx;
|
||||||
|
usb_wait(CDFULL);
|
||||||
|
return LPC_USB->USBCmdData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
* Interface
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
static int_fast8_t
|
||||||
|
usb_write_packet(uint32_t ep, const void *data, uint_fast8_t len)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
uint32_t sts = sie_cmd_read(SIE_CMD_SELECT | ep);
|
||||||
|
if (sts & 0x01) {
|
||||||
|
// Output buffers full
|
||||||
|
usb_irq_enable();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPC_USB->USBCtrl = WR_EN | ((ep/2) << 2);
|
||||||
|
LPC_USB->USBTxPLen = len;
|
||||||
|
if (!len)
|
||||||
|
LPC_USB->USBTxData = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i<DIV_ROUND_UP(len, 4); i++) {
|
||||||
|
uint32_t d;
|
||||||
|
memcpy(&d, data, sizeof(d));
|
||||||
|
data += sizeof(d);
|
||||||
|
LPC_USB->USBTxData = cpu_to_le32(d);
|
||||||
|
}
|
||||||
|
sie_cmd(SIE_CMD_VALIDATE_BUFFER);
|
||||||
|
usb_irq_enable();
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int_fast8_t
|
||||||
|
usb_read_packet(uint32_t ep, void *data, uint_fast8_t max_len)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
uint32_t sts = sie_cmd_read(SIE_CMD_SELECT | ep);
|
||||||
|
if (!(sts & 0x01)) {
|
||||||
|
// No data available
|
||||||
|
usb_irq_enable();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine packet size
|
||||||
|
LPC_USB->USBCtrl = RD_EN | ((ep/2) << 2);
|
||||||
|
uint32_t plen = LPC_USB->USBRxPLen;
|
||||||
|
while (!(plen & (1<<11)))
|
||||||
|
plen = LPC_USB->USBRxPLen;
|
||||||
|
plen &= 0x3FF;
|
||||||
|
if (plen > max_len)
|
||||||
|
// XXX - return error code? must keep reading?
|
||||||
|
plen = max_len;
|
||||||
|
// Copy data
|
||||||
|
uint32_t xfer = plen;
|
||||||
|
for (;;) {
|
||||||
|
uint32_t d = le32_to_cpu(LPC_USB->USBRxData);
|
||||||
|
if (xfer <= sizeof(d)) {
|
||||||
|
memcpy(data, &d, xfer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(data, &d, sizeof(d));
|
||||||
|
data += sizeof(d);
|
||||||
|
xfer -= sizeof(d);
|
||||||
|
}
|
||||||
|
// Clear space for next packet
|
||||||
|
sts = sie_cmd_read(SIE_CMD_CLEAR_BUFFER);
|
||||||
|
usb_irq_enable();
|
||||||
|
if (sts & 0x01)
|
||||||
|
// Packet overwritten
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return plen;
|
||||||
|
}
|
||||||
|
|
||||||
|
int_fast8_t
|
||||||
|
usb_read_bulk_out(void *data, uint_fast8_t max_len)
|
||||||
|
{
|
||||||
|
return usb_read_packet(EP2OUT, data, max_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int_fast8_t
|
||||||
|
usb_send_bulk_in(void *data, uint_fast8_t len)
|
||||||
|
{
|
||||||
|
return usb_write_packet(EP5IN, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int_fast8_t
|
||||||
|
usb_read_setup(void *data, uint_fast8_t max_len)
|
||||||
|
{
|
||||||
|
return usb_read_packet(EP0OUT, data, max_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int_fast8_t
|
||||||
|
usb_send_setup(const void *data, uint_fast8_t len)
|
||||||
|
{
|
||||||
|
return usb_write_packet(EP0IN, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_set_stall(void)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
sie_cmd_write(SIE_CMD_SET_ENDPOINT_STATUS | 0, (1<<7));
|
||||||
|
usb_irq_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_set_address(uint_fast8_t addr)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
sie_cmd_write(SIE_CMD_SET_ADDRESS, addr | (1<<7));
|
||||||
|
usb_irq_enable();
|
||||||
|
usb_send_setup(NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
realize_endpoint(uint32_t idx, uint32_t packet_size)
|
||||||
|
{
|
||||||
|
LPC_USB->USBDevIntClr = EP_RLZED;
|
||||||
|
LPC_USB->USBReEp |= 1<<idx;
|
||||||
|
LPC_USB->USBEpInd = idx;
|
||||||
|
LPC_USB->USBMaxPSize = packet_size;
|
||||||
|
usb_wait(EP_RLZED);
|
||||||
|
LPC_USB->USBEpIntEn |= 1<<idx;
|
||||||
|
sie_cmd_write(SIE_CMD_SET_ENDPOINT_STATUS | idx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usb_set_configure(void)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
realize_endpoint(EP1IN, USB_CDC_EP_ACM_SIZE);
|
||||||
|
realize_endpoint(EP2OUT, USB_CDC_EP_BULK_OUT_SIZE);
|
||||||
|
realize_endpoint(EP5IN, USB_CDC_EP_BULK_IN_SIZE);
|
||||||
|
sie_cmd_write(SIE_CMD_CONFIGURE, 1);
|
||||||
|
usb_irq_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
usbserial_init(void)
|
||||||
|
{
|
||||||
|
usb_irq_disable();
|
||||||
|
// enable power
|
||||||
|
LPC_SC->PCONP |= (1<<31);
|
||||||
|
// enable clock
|
||||||
|
LPC_USB->USBClkCtrl = 0x12;
|
||||||
|
while (LPC_USB->USBClkSt != 0x12)
|
||||||
|
;
|
||||||
|
// configure USBD+, USBD-, and USB Connect pins
|
||||||
|
gpio_peripheral(0, 29, 1, 0);
|
||||||
|
gpio_peripheral(0, 30, 1, 0);
|
||||||
|
gpio_peripheral(2, 9, 1, 0);
|
||||||
|
// setup endpoints
|
||||||
|
realize_endpoint(EP0OUT, USB_CDC_EP0_SIZE);
|
||||||
|
realize_endpoint(EP0IN, USB_CDC_EP0_SIZE);
|
||||||
|
sie_cmd_write(SIE_CMD_SET_DEVICE_STATUS, 1);
|
||||||
|
// enable irqs
|
||||||
|
LPC_USB->USBDevIntEn = DEV_STAT | EP_SLOW;
|
||||||
|
NVIC_SetPriority(USB_IRQn, 1);
|
||||||
|
usb_irq_enable();
|
||||||
|
}
|
||||||
|
DECL_INIT(usbserial_init);
|
||||||
|
|
||||||
|
void
|
||||||
|
usbserial_shutdown(void)
|
||||||
|
{
|
||||||
|
usb_irq_enable();
|
||||||
|
}
|
||||||
|
DECL_SHUTDOWN(usbserial_shutdown);
|
||||||
|
|
||||||
|
void __visible
|
||||||
|
USB_IRQHandler(void)
|
||||||
|
{
|
||||||
|
uint32_t udis = LPC_USB->USBDevIntSt;
|
||||||
|
if (udis & DEV_STAT) {
|
||||||
|
LPC_USB->USBDevIntClr = DEV_STAT;
|
||||||
|
// XXX - should handle reset and other states
|
||||||
|
}
|
||||||
|
if (udis & EP_SLOW) {
|
||||||
|
uint32_t ueis = LPC_USB->USBEpIntSt;
|
||||||
|
if (ueis & (1<<EP0OUT)) {
|
||||||
|
sie_select_and_clear(EP0OUT);
|
||||||
|
usb_notify_setup();
|
||||||
|
}
|
||||||
|
if (ueis & (1<<EP0IN)) {
|
||||||
|
sie_select_and_clear(EP0IN);
|
||||||
|
usb_notify_setup();
|
||||||
|
}
|
||||||
|
if (ueis & (1<<EP2OUT)) {
|
||||||
|
sie_select_and_clear(EP2OUT);
|
||||||
|
usb_notify_bulk_out();
|
||||||
|
}
|
||||||
|
if (ueis & (1<<EP5IN)) {
|
||||||
|
sie_select_and_clear(EP5IN);
|
||||||
|
usb_notify_bulk_in();
|
||||||
|
}
|
||||||
|
LPC_USB->USBDevIntClr = EP_SLOW;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue