From 8d190cdbb9c4e4377d0c33424e453c5ac166eed8 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sun, 10 Jun 2012 16:40:07 +1200 Subject: Renamed platforms to 'native' and 'libftdi' and moved into 'platforms' dir. --- src/platforms/native/Makefile.inc | 32 ++ src/platforms/native/blackmagic.ld | 29 ++ src/platforms/native/cdcacm.c | 581 +++++++++++++++++++++++++++++++++++++ src/platforms/native/gdb_if.c | 86 ++++++ src/platforms/native/jtagtap.c | 89 ++++++ src/platforms/native/platform.c | 254 ++++++++++++++++ src/platforms/native/platform.h | 162 +++++++++++ src/platforms/native/swdptap.c | 155 ++++++++++ src/platforms/native/traceswo.c | 190 ++++++++++++ src/platforms/native/traceswo.h | 27 ++ src/platforms/native/usbdfu.c | 329 +++++++++++++++++++++ src/platforms/native/usbuart.c | 140 +++++++++ src/platforms/native/usbuart.h | 33 +++ 13 files changed, 2107 insertions(+) create mode 100644 src/platforms/native/Makefile.inc create mode 100644 src/platforms/native/blackmagic.ld create mode 100644 src/platforms/native/cdcacm.c create mode 100644 src/platforms/native/gdb_if.c create mode 100644 src/platforms/native/jtagtap.c create mode 100644 src/platforms/native/platform.c create mode 100644 src/platforms/native/platform.h create mode 100644 src/platforms/native/swdptap.c create mode 100644 src/platforms/native/traceswo.c create mode 100644 src/platforms/native/traceswo.h create mode 100644 src/platforms/native/usbdfu.c create mode 100644 src/platforms/native/usbuart.c create mode 100644 src/platforms/native/usbuart.h (limited to 'src/platforms/native') diff --git a/src/platforms/native/Makefile.inc b/src/platforms/native/Makefile.inc new file mode 100644 index 0000000..3bddde9 --- /dev/null +++ b/src/platforms/native/Makefile.inc @@ -0,0 +1,32 @@ +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +CFLAGS += -Istm32/include -mcpu=cortex-m3 -mthumb -DSTM32F1 +LDFLAGS_BOOT = -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ + -Wl,-T,$(PLATFORM_DIR)/blackmagic.ld -nostartfiles -lc -lnosys -Wl,-Map=mapfile \ + -mthumb -mcpu=cortex-m3 -Wl,-gc-sections +LDFLAGS = $(LDFLAGS_BOOT) -Wl,-Ttext=0x8002000 + +SRC += cdcacm.c \ + platform.c \ + traceswo.c \ + usbuart.c \ + +all: blackmagic.bin blackmagic_dfu.bin blackmagic_dfu.hex + +blackmagic.bin: blackmagic + $(OBJCOPY) -O binary $^ $@ + +blackmagic_dfu: usbdfu.o + $(CC) $^ -o $@ $(LDFLAGS_BOOT) + +blackmagic_dfu.bin: blackmagic_dfu + $(OBJCOPY) -O binary $^ $@ + +blackmagic_dfu.hex: blackmagic_dfu + $(OBJCOPY) -O ihex $^ $@ + +host_clean: + -rm blackmagic.bin blackmagic_dfu blackmagic_dfu.bin blackmagic_dfu.hex + diff --git a/src/platforms/native/blackmagic.ld b/src/platforms/native/blackmagic.ld new file mode 100644 index 0000000..9755309 --- /dev/null +++ b/src/platforms/native/blackmagic.ld @@ -0,0 +1,29 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script from libopenstm32. */ +INCLUDE libopencm3_stm32f1.ld + diff --git a/src/platforms/native/cdcacm.c b/src/platforms/native/cdcacm.c new file mode 100644 index 0000000..ebdef18 --- /dev/null +++ b/src/platforms/native/cdcacm.c @@ -0,0 +1,581 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements a the USB Communications Device Class - Abstract + * Control Model (CDC-ACM) as defined in CDC PSTN subclass 1.2. + * A Device Firmware Upgrade (DFU 1.1) class interface is provided for + * field firmware upgrade. + * + * The device's unique id is used as the USB serial number string. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "traceswo.h" +#include "usbuart.h" + +#define DFU_IF_NO 4 + +static char *get_dev_unique_id(char *serial_no); + +static int configured; +static int cdcacm_gdb_dtr = 1; + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0xEF, /* Miscellaneous Device */ + .bDeviceSubClass = 2, /* Common Class */ + .bDeviceProtocol = 1, /* Interface Association */ + .bMaxPacketSize0 = 64, + .idVendor = 0x1D50, + .idProduct = 0x6018, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +/* This notification endpoint isn't implemented. According to CDC spec its + * optional, but its absence causes a NULL pointer dereference in Linux cdc_acm + * driver. */ +static const struct usb_endpoint_descriptor gdb_comm_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x82, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 255, +}}; + +static const struct usb_endpoint_descriptor gdb_data_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x81, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}}; + +static const struct { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__((packed)) gdb_cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + .bcdCDC = 0x0110, + }, + .call_mgmt = { + .bFunctionLength = + sizeof(struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 1, + }, + .acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + .bmCapabilities = 2, /* SET_LINE_CODING supported */ + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + .bControlInterface = 0, + .bSubordinateInterface0 = 1, + } +}; + +static const struct usb_interface_descriptor gdb_comm_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = 4, + + .endpoint = gdb_comm_endp, + + .extra = &gdb_cdcacm_functional_descriptors, + .extralen = sizeof(gdb_cdcacm_functional_descriptors) +}}; + +static const struct usb_interface_descriptor gdb_data_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + + .endpoint = gdb_data_endp, +}}; + +static const struct usb_iface_assoc_descriptor gdb_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 0, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTOCOL_AT, + .iFunction = 0, +}; + +/* Serial ACM interface */ +static const struct usb_endpoint_descriptor uart_comm_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x84, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 255, +}}; + +static const struct usb_endpoint_descriptor uart_data_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x03, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x83, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = CDCACM_PACKET_SIZE, + .bInterval = 1, +}}; + +static const struct { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__((packed)) uart_cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + .bcdCDC = 0x0110, + }, + .call_mgmt = { + .bFunctionLength = + sizeof(struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 3, + }, + .acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + .bmCapabilities = 2, /* SET_LINE_CODING supported*/ + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + .bControlInterface = 2, + .bSubordinateInterface0 = 3, + } +}; + +static const struct usb_interface_descriptor uart_comm_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 2, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = 5, + + .endpoint = uart_comm_endp, + + .extra = &uart_cdcacm_functional_descriptors, + .extralen = sizeof(uart_cdcacm_functional_descriptors) +}}; + +static const struct usb_interface_descriptor uart_data_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 3, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + + .endpoint = uart_data_endp, +}}; + +static const struct usb_iface_assoc_descriptor uart_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 2, + .bInterfaceCount = 2, + .bFunctionClass = USB_CLASS_CDC, + .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, + .bFunctionProtocol = USB_CDC_PROTOCOL_AT, + .iFunction = 0, +}; + +const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof(struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x011A, +}; + +const struct usb_interface_descriptor dfu_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = DFU_IF_NO, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 1, + .iInterface = 6, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), +}; + +static const struct usb_iface_assoc_descriptor dfu_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 4, + .bInterfaceCount = 1, + .bFunctionClass = 0xFE, + .bFunctionSubClass = 1, + .bFunctionProtocol = 1, + .iFunction = 6, +}; + +static const struct usb_endpoint_descriptor trace_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x85, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 0, +}}; + +const struct usb_interface_descriptor trace_iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 5, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0xFF, + .bInterfaceProtocol = 0xFF, + .iInterface = 7, + + .endpoint = trace_endp, +}; + +static const struct usb_iface_assoc_descriptor trace_assoc = { + .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + .bFirstInterface = 5, + .bInterfaceCount = 1, + .bFunctionClass = 0xFF, + .bFunctionSubClass = 0xFF, + .bFunctionProtocol = 0xFF, + .iFunction = 7, +}; + +static const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .iface_assoc = &gdb_assoc, + .altsetting = gdb_comm_iface, +}, { + .num_altsetting = 1, + .altsetting = gdb_data_iface, +}, { + .num_altsetting = 1, + .iface_assoc = &uart_assoc, + .altsetting = uart_comm_iface, +}, { + .num_altsetting = 1, + .altsetting = uart_data_iface, +}, { + .num_altsetting = 1, + .iface_assoc = &dfu_assoc, + .altsetting = &dfu_iface, +}, { + .num_altsetting = 1, + .iface_assoc = &trace_assoc, + .altsetting = &trace_iface, +}}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 6, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + + .interface = ifaces, +}; + +char serial_no[9]; + +static const char *usb_strings[] = { + "x", + "Black Sphere Technologies", + "Black Magic Probe", + serial_no, + "Black Magic GDB Server", + "Black Magic UART Port", + "Black Magic Firmware Upgrade", + "Black Magic Trace Capture", +}; + +static void dfu_detach_complete(struct usb_setup_data *req) +{ + (void)req; + + /* Disconnect USB cable */ + gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, 0, USB_PU_PIN); + + /* Assert boot-request pin */ + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); + gpio_clear(GPIOB, GPIO12); + + /* Reset core to enter bootloader */ + scb_reset_core(); +} + +static int cdcacm_control_request(struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, void (**complete)(struct usb_setup_data *req)) +{ + (void)complete; + (void)buf; + (void)len; + + switch(req->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + /* Ignore if not for GDB interface */ + if(req->wIndex != 0) + return 1; + + cdcacm_gdb_dtr = req->wValue & 1; + + return 1; + case USB_CDC_REQ_SET_LINE_CODING: + if(*len < sizeof(struct usb_cdc_line_coding)) + return 0; + + switch(req->wIndex) { + case 2: + usbuart_set_line_coding((struct usb_cdc_line_coding*)*buf); + case 0: + return 1; /* Ignore on GDB Port */ + default: + return 0; + } + case DFU_GETSTATUS: + if(req->wIndex == DFU_IF_NO) { + (*buf)[0] = DFU_STATUS_OK; + (*buf)[1] = 0; + (*buf)[2] = 0; + (*buf)[3] = 0; + (*buf)[4] = STATE_APP_IDLE; + (*buf)[5] = 0; /* iString not used here */ + *len = 6; + + return 1; + } + case DFU_DETACH: + if(req->wIndex == DFU_IF_NO) { + *complete = dfu_detach_complete; + return 1; + } + return 0; + } + return 0; +} + +int cdcacm_get_config(void) +{ + return configured; +} + +int cdcacm_get_dtr(void) +{ + return cdcacm_gdb_dtr; +} + +static void cdcacm_set_config(u16 wValue) +{ + configured = wValue; + + /* GDB interface */ + usbd_ep_setup(0x01, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); + usbd_ep_setup(0x81, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, NULL); + usbd_ep_setup(0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + + /* Serial interface */ + usbd_ep_setup(0x03, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, usbuart_usb_out_cb); + usbd_ep_setup(0x83, USB_ENDPOINT_ATTR_BULK, CDCACM_PACKET_SIZE, usbuart_usb_in_cb); + usbd_ep_setup(0x84, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + + /* Trace interface */ + usbd_ep_setup(0x85, USB_ENDPOINT_ATTR_BULK, 64, trace_buf_drain); + + usbd_register_control_callback( + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + cdcacm_control_request); + + /* Notify the host that DCD is asserted. + * Allows the use of /dev/tty* devices on *BSD/MacOS + */ + char buf[10]; + struct usb_cdc_notification *notif = (void*)buf; + /* We echo signals back to host as notification */ + notif->bmRequestType = 0xA1; + notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; + notif->wValue = 0; + notif->wIndex = 0; + notif->wLength = 2; + buf[8] = 3; /* DCD | DSR */ + buf[9] = 0; + usbd_ep_write_packet(0x82, buf, 10); + notif->wIndex = 2; + usbd_ep_write_packet(0x84, buf, 10); +} + +/* We need a special large control buffer for this device: */ +uint8_t usbd_control_buffer[256]; + +void cdcacm_init(void) +{ + void exti15_10_isr(void); + + get_dev_unique_id(serial_no); + + usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings); + usbd_set_control_buffer_size(sizeof(usbd_control_buffer)); + usbd_register_set_config_callback(cdcacm_set_config); + + nvic_set_priority(NVIC_USB_LP_CAN_RX0_IRQ, IRQ_PRI_USB); + nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); + nvic_set_priority(USB_VBUS_IRQ, IRQ_PRI_USB_VBUS); + nvic_enable_irq(USB_VBUS_IRQ); + + gpio_set(USB_VBUS_PORT, USB_VBUS_PIN); + gpio_set(USB_PU_PORT, USB_PU_PIN); + + gpio_set_mode(USB_VBUS_PORT, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, USB_VBUS_PIN); + + /* Configure EXTI for USB VBUS monitor */ + exti_select_source(USB_VBUS_PIN, USB_VBUS_PORT); + exti_set_trigger(USB_VBUS_PIN, EXTI_TRIGGER_BOTH); + exti_enable_request(USB_VBUS_PIN); + + exti15_10_isr(); +} + +void usb_lp_can_rx0_isr(void) +{ + usbd_poll(); +} + +void exti15_10_isr(void) +{ + if (gpio_get(USB_VBUS_PORT, USB_VBUS_PIN)) { + /* Drive pull-up high if VBUS connected */ + gpio_set_mode(USB_PU_PORT, GPIO_MODE_OUTPUT_10_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, USB_PU_PIN); + } else { + /* Allow pull-up to float if VBUS disconnected */ + gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_FLOAT, USB_PU_PIN); + } + + exti_reset_request(USB_VBUS_PIN); +} + +static char *get_dev_unique_id(char *s) +{ + volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8; + uint32_t unique_id = *unique_id_p + + *(unique_id_p + 1) + + *(unique_id_p + 2); + int i; + + /* Fetch serial number from chip's unique ID */ + for(i = 0; i < 8; i++) { + s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0'; + } + for(i = 0; i < 8; i++) + if(s[i] > '9') + s[i] += 'A' - '9' - 1; + s[8] = 0; + + return s; +} + diff --git a/src/platforms/native/gdb_if.c b/src/platforms/native/gdb_if.c new file mode 100644 index 0000000..aa43293 --- /dev/null +++ b/src/platforms/native/gdb_if.c @@ -0,0 +1,86 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements a transparent channel over which the GDB Remote + * Serial Debugging protocol is implemented. This implementation for STM32 + * uses the USB CDC-ACM device bulk endpoints to implement the channel. + */ +#include "platform.h" +#include + +#include "gdb_if.h" + +static uint32_t count_out; +static uint32_t count_in; +static uint32_t out_ptr; +static uint8_t buffer_out[CDCACM_PACKET_SIZE]; +static uint8_t buffer_in[CDCACM_PACKET_SIZE]; + +void gdb_if_putchar(unsigned char c, int flush) +{ + buffer_in[count_in++] = c; + if(flush || (count_in == CDCACM_PACKET_SIZE)) { + /* Refuse to send if USB isn't configured, and + * don't bother if nobody's listening */ + if((cdcacm_get_config() != 1) || !cdcacm_get_dtr()) { + count_in = 0; + return; + } + while(usbd_ep_write_packet(1, buffer_in, count_in) <= 0); + count_in = 0; + } +} + +unsigned char gdb_if_getchar(void) +{ + while(!(out_ptr < count_out)) { + /* Detach if port closed */ + if(!cdcacm_get_dtr()) + return 0x04; + + while(cdcacm_get_config() != 1); + count_out = usbd_ep_read_packet(1, buffer_out, + CDCACM_PACKET_SIZE); + out_ptr = 0; + } + + return buffer_out[out_ptr++]; +} + +unsigned char gdb_if_getchar_to(int timeout) +{ + timeout_counter = timeout/100; + + if(!(out_ptr < count_out)) do { + /* Detach if port closed */ + if(!cdcacm_get_dtr()) + return 0x04; + + count_out = usbd_ep_read_packet(1, buffer_out, + CDCACM_PACKET_SIZE); + out_ptr = 0; + } while(timeout_counter && !(out_ptr < count_out)); + + if(out_ptr < count_out) + return gdb_if_getchar(); + + return -1; +} + diff --git a/src/platforms/native/jtagtap.c b/src/platforms/native/jtagtap.c new file mode 100644 index 0000000..3703279 --- /dev/null +++ b/src/platforms/native/jtagtap.c @@ -0,0 +1,89 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements the low-level JTAG TAP interface. */ + +#include + +#include "general.h" + +#include "jtagtap.h" + +int jtagtap_init(void) +{ + /* This needs some fixing... */ + /* Toggle required to sort out line drivers... */ + gpio_port_write(GPIOA, 0x8100); + gpio_port_write(GPIOB, 0x2000); + + gpio_port_write(GPIOA, 0x8180); + gpio_port_write(GPIOB, 0x2002); + + gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN); + + /* Go to JTAG mode for SWJ-DP */ + for(int i = 0; i <= 50; i++) jtagtap_next(1, 0); /* Reset SW-DP */ + jtagtap_tms_seq(0xE73C, 16); /* SWD to JTAG sequence */ + jtagtap_soft_reset(); + + return 0; +} + +void jtagtap_reset(void) +{ + volatile int i; + gpio_clear(GPIOB, GPIO1); + for(i = 0; i < 10000; i++) asm("nop"); + gpio_set(GPIOB, GPIO1); + jtagtap_soft_reset(); +} + +void jtagtap_srst(void) +{ + volatile int i; + gpio_set(GPIOA, GPIO2); + for(i = 0; i < 10000; i++) asm("nop"); + gpio_clear(GPIOA, GPIO2); +} + +inline uint8_t jtagtap_next(uint8_t dTMS, uint8_t dTDO) +{ + uint8_t ret; + + gpio_set_val(JTAG_PORT, TMS_PIN, dTMS); + gpio_set_val(JTAG_PORT, TDI_PIN, dTDO); + gpio_set(JTAG_PORT, TCK_PIN); + ret = gpio_get(JTAG_PORT, TDO_PIN); + gpio_clear(JTAG_PORT, TCK_PIN); + + DEBUG("jtagtap_next(TMS = %d, TDO = %d) = %d\n", dTMS, dTDO, ret); + + return ret; +} + + + +#define PROVIDE_GENERIC_JTAGTAP_TMS_SEQ +#define PROVIDE_GENERIC_JTAGTAP_TDI_TDO_SEQ +#define PROVIDE_GENERIC_JTAGTAP_TDI_SEQ + +#include "jtagtap_generic.c" + diff --git a/src/platforms/native/platform.c b/src/platforms/native/platform.c new file mode 100644 index 0000000..4aff924 --- /dev/null +++ b/src/platforms/native/platform.c @@ -0,0 +1,254 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements the platform specific functions for the STM32 + * implementation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "jtag_scan.h" +#include "usbuart.h" + +#include + +uint8_t running_status; +volatile uint32_t timeout_counter; + +jmp_buf fatal_error_jmpbuf; + +void morse(const char *msg, char repeat); +static void morse_update(void); + +static void adc_init(void); + +/* Pins PB[7:5] are used to detect hardware revision. + * 000 - Original production build. + * 001 - Mini production build. + */ +int platform_hwversion(void) +{ + static int hwversion = -1; + if (hwversion == -1) { + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, + GPIO7 | GPIO6 | GPIO5); + gpio_clear(GPIOB, GPIO7 | GPIO6 | GPIO5); + hwversion = gpio_get(GPIOB, GPIO7 | GPIO6 | GPIO5) >> 5; + } + return hwversion; +} + +int platform_init(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + /* Enable peripherals */ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM2EN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPDEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN); + + /* Setup GPIO ports */ + gpio_clear(USB_PU_PORT, USB_PU_PIN); + gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, + USB_PU_PIN); + + gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, + TMS_PIN | TCK_PIN | TDI_PIN); + + gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, + LED_UART | LED_IDLE_RUN | LED_ERROR); + + /* FIXME: This pin in intended to be input, but the TXS0108 fails + * to release the device from reset if this floats. */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO7); + + /* Setup heartbeat timer */ + systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8); + systick_set_reload(900000); /* Interrupt us at 10 Hz */ + SCB_SHPR(11) &= ~((15 << 4) & 0xff); + SCB_SHPR(11) |= ((14 << 4) & 0xff); + systick_interrupt_enable(); + systick_counter_enable(); + + usbuart_init(); + + if (platform_hwversion() > 0) { + adc_init(); + } else { + gpio_clear(GPIOB, GPIO0); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, GPIO0); + } + + SCB_VTOR = 0x2000; // Relocate interrupt vector table here + + cdcacm_init(); + + jtag_scan(); + + return 0; +} + +void sys_tick_handler(void) +{ + if(running_status) + gpio_toggle(LED_PORT, LED_IDLE_RUN); + + if(timeout_counter) + timeout_counter--; + + morse_update(); +} + + +/* Morse code patterns and lengths */ +static const struct { + uint16_t code; + uint8_t bits; +} morse_letter[] = { + { 0b00011101, 8}, // 'A' .- + { 0b000101010111, 12}, // 'B' -... + { 0b00010111010111, 14}, // 'C' -.-. + { 0b0001010111, 10}, // 'D' -.. + { 0b0001, 4}, // 'E' . + { 0b000101110101, 12}, // 'F' ..-. + { 0b000101110111, 12}, // 'G' --. + { 0b0001010101, 10}, // 'H' .... + { 0b000101, 6}, // 'I' .. + {0b0001110111011101, 16}, // 'J' .--- + { 0b000111010111, 12}, // 'K' -.- + { 0b000101011101, 12}, // 'L' .-.. + { 0b0001110111, 10}, // 'M' -- + { 0b00010111, 8}, // 'N' -. + { 0b00011101110111, 14}, // 'O' --- + { 0b00010111011101, 14}, // 'P' .--. + {0b0001110101110111, 16}, // 'Q' --.- + { 0b0001011101, 10}, // 'R' .-. + { 0b00010101, 8}, // 'S' ... + { 0b000111, 6}, // 'T' - + { 0b0001110101, 10}, // 'U' ..- + { 0b000111010101, 12}, // 'V' ...- + { 0b000111011101, 12}, // 'W' .-- + { 0b00011101010111, 14}, // 'X' -..- + {0b0001110111010111, 16}, // 'Y' -.-- + { 0b00010101110111, 14}, // 'Z' --.. +}; + + +const char *morse_msg; +static const char * volatile morse_ptr; +static char morse_repeat; + +void morse(const char *msg, char repeat) +{ + morse_msg = morse_ptr = msg; + morse_repeat = repeat; + SET_ERROR_STATE(0); +} + +static void morse_update(void) +{ + static uint16_t code; + static uint8_t bits; + + if(!morse_ptr) return; + + if(!bits) { + char c = *morse_ptr++; + if(!c) { + if(morse_repeat) { + morse_ptr = morse_msg; + c = *morse_ptr++; + } else { + morse_ptr = 0; + return; + } + } + if((c >= 'A') && (c <= 'Z')) { + c -= 'A'; + code = morse_letter[c].code; + bits = morse_letter[c].bits; + } else { + code = 0; bits = 4; + } + } + SET_ERROR_STATE(code & 1); + code >>= 1; bits--; +} + +static void adc_init(void) +{ + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_ADC1EN); + + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_ANALOG, GPIO0); + + adc_off(ADC1); + adc_disable_scan_mode(ADC1); + adc_set_single_conversion_mode(ADC1); + adc_enable_discontinous_mode_regular(ADC1); + adc_disable_external_trigger_regular(ADC1); + adc_set_right_aligned(ADC1); + adc_set_conversion_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC); + + adc_on(ADC1); + + /* Wait for ADC starting up. */ + for (int i = 0; i < 800000; i++) /* Wait a bit. */ + __asm__("nop"); + + adc_reset_calibration(ADC1); + adc_calibration(ADC1); +} + +const char *platform_target_voltage(void) +{ + if (platform_hwversion() == 0) + return gpio_get(GPIOB, GPIO0) ? "OK" : "ABSENT!"; + + static char ret[] = "0.0V"; + const u8 channel = 8; + adc_set_regular_sequence(ADC1, 1, (u8*)&channel); + + adc_on(ADC1); + + /* Wait for end of conversion. */ + while (!(ADC_SR(ADC1) & ADC_SR_EOC)); + + u32 val = ADC_DR(ADC1) * 99; /* 0-4095 */ + ret[0] = '0' + val / 81910; + ret[2] = '0' + (val / 8191) % 10; + + return ret; +} diff --git a/src/platforms/native/platform.h b/src/platforms/native/platform.h new file mode 100644 index 0000000..56e0b77 --- /dev/null +++ b/src/platforms/native/platform.h @@ -0,0 +1,162 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements the platform specific functions for the STM32 + * implementation. + */ +#ifndef __PLATFORM_H +#define __PLATFORM_H + +#include + +#include +#include + +#include "gdb_packet.h" + +#define INLINE_GPIO +#define CDCACM_PACKET_SIZE 64 +#define PLATFORM_HAS_TRACESWO + +/* Important pin mappings for STM32 implementation: + * + * LED0 = PB2 (Yellow LED : Running) + * LED1 = PB10 (Yellow LED : Idle) + * LED2 = PB11 (Red LED : Error) + * + * TPWR = RB0 (input) -- analogue on mini design ADC1, ch8 + * nTRST = PB1 + * SRST_OUT = PA2 + * TDI = PA3 + * TMS = PA4 (input for SWDP) + * TCK = PA5 + * TDO = PA6 (input) + * nSRST = PA7 (input) + * + * USB cable pull-up: PA8 + * USB VBUS detect: PB13 -- New on mini design. + * Enable pull up for compatibility. + * Force DFU mode button: PB12 + */ + +/* Hardware definitions... */ +#define JTAG_PORT GPIOA +#define TDI_PIN GPIO3 +#define TMS_PIN GPIO4 +#define TCK_PIN GPIO5 +#define TDO_PIN GPIO6 + +#define SWDP_PORT JTAG_PORT +#define SWDIO_PIN TMS_PIN +#define SWCLK_PIN TCK_PIN + +#define USB_PU_PORT GPIOA +#define USB_PU_PIN GPIO8 + +#define USB_VBUS_PORT GPIOB +#define USB_VBUS_PIN GPIO13 +#define USB_VBUS_IRQ NVIC_EXTI15_10_IRQ + +#define LED_PORT GPIOB +#define LED_UART GPIO2 +#define LED_IDLE_RUN GPIO10 +#define LED_ERROR GPIO11 + +/* Interrupt priorities. Low numbers are high priority. + * For now USART1 preempts USB which may spin while buffer is drained. + * TIM3 is used for traceswo capture and must be highest priority. + */ +#define IRQ_PRI_USB (2 << 4) +#define IRQ_PRI_USART1 (1 << 4) +#define IRQ_PRI_USB_VBUS (14 << 4) +#define IRQ_PRI_TIM3 (0 << 4) + +#define DEBUG(...) + +extern uint8_t running_status; +extern volatile uint32_t timeout_counter; + +extern jmp_buf fatal_error_jmpbuf; + +extern const char *morse_msg; + +#define gpio_set_val(port, pin, val) do { \ + if(val) \ + gpio_set((port), (pin)); \ + else \ + gpio_clear((port), (pin)); \ +} while(0) + +#define SET_RUN_STATE(state) {running_status = (state);} +#define SET_IDLE_STATE(state) {gpio_set_val(LED_PORT, LED_IDLE_RUN, state);} +#define SET_ERROR_STATE(state) {gpio_set_val(LED_PORT, LED_ERROR, state);} + +#define PLATFORM_SET_FATAL_ERROR_RECOVERY() {setjmp(fatal_error_jmpbuf);} +#define PLATFORM_FATAL_ERROR(error) { \ + if(running_status) gdb_putpacketz("X1D"); \ + else gdb_putpacketz("EFF"); \ + running_status = 0; \ + TARGET_LIST_FREE(); \ + cur_target = last_target = NULL; \ + morse("TARGET LOST.", 1); \ + longjmp(fatal_error_jmpbuf, (error)); \ +} + +int platform_init(void); +void morse(const char *msg, char repeat); +const char *platform_target_voltage(void); +int platform_hwversion(void); + +/* */ +void cdcacm_init(void); +/* Returns current usb configuration, or 0 if not configured. */ +int cdcacm_get_config(void); +int cdcacm_get_dtr(void); + +/* */ +void uart_usb_buf_drain(uint8_t ep); + +/* Use newlib provided integer only stdio functions */ +#define sscanf siscanf +#define sprintf siprintf +#define vasprintf vasiprintf + +#ifdef INLINE_GPIO +static inline void _gpio_set(u32 gpioport, u16 gpios) +{ + GPIO_BSRR(gpioport) = gpios; +} +#define gpio_set _gpio_set + +static inline void _gpio_clear(u32 gpioport, u16 gpios) +{ + GPIO_BRR(gpioport) = gpios; +} +#define gpio_clear _gpio_clear + +static inline u16 _gpio_get(u32 gpioport, u16 gpios) +{ + return (u16)GPIO_IDR(gpioport) & gpios; +} +#define gpio_get _gpio_get +#endif + +#endif + diff --git a/src/platforms/native/swdptap.c b/src/platforms/native/swdptap.c new file mode 100644 index 0000000..8ac78b2 --- /dev/null +++ b/src/platforms/native/swdptap.c @@ -0,0 +1,155 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2011 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements the low-level SW-DP interface. */ + +#include + +#include "general.h" + +#include "swdptap.h" + +#include "gdb_packet.h" + +static void swdptap_turnaround(uint8_t dir) +{ + static uint8_t olddir = 0; + + DEBUG("%s", dir ? "\n-> ":"\n<- "); + + /* Don't turnaround if direction not changing */ + if(dir == olddir) return; + olddir = dir; + + if(dir) + gpio_set_mode(SWDP_PORT, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_FLOAT, SWDIO_PIN); + gpio_set(SWDP_PORT, SWCLK_PIN); + gpio_clear(SWDP_PORT, SWCLK_PIN); + if(!dir) + gpio_set_mode(SWDP_PORT, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, SWDIO_PIN); +} + +static uint8_t swdptap_bit_in(void) +{ + uint8_t ret; + + ret = gpio_get(SWDP_PORT, SWDIO_PIN); + gpio_set(SWDP_PORT, SWCLK_PIN); + gpio_clear(SWDP_PORT, SWCLK_PIN); + + DEBUG("%d", ret?1:0); + + return ret; +} + +static void swdptap_bit_out(uint8_t val) +{ + DEBUG("%d", val); + + gpio_set_val(SWDP_PORT, SWDIO_PIN, val); + gpio_set(SWDP_PORT, SWCLK_PIN); + gpio_clear(SWDP_PORT, SWCLK_PIN); +} + +int swdptap_init(void) +{ + /* This must be investigated in more detail. + * As described in STM32 Reference Manual... */ + swdptap_reset(); + swdptap_seq_out(0xE79E, 16); /* 0b0111100111100111 */ + swdptap_reset(); + swdptap_seq_out(0, 16); + + return 0; +} + + +void swdptap_reset(void) +{ + swdptap_turnaround(0); + /* 50 clocks with TMS high */ + for(int i = 0; i < 50; i++) swdptap_bit_out(1); +} + + +uint32_t swdptap_seq_in(int ticks) +{ + uint32_t index = 1; + uint32_t ret = 0; + + swdptap_turnaround(1); + + while(ticks--) { + if(swdptap_bit_in()) ret |= index; + index <<= 1; + } + + return ret; +} + + +uint8_t swdptap_seq_in_parity(uint32_t *ret, int ticks) +{ + uint32_t index = 1; + uint8_t parity = 0; + *ret = 0; + + swdptap_turnaround(1); + + while(ticks--) { + if(swdptap_bit_in()) { + *ret |= index; + parity ^= 1; + } + index <<= 1; + } + if(swdptap_bit_in()) parity ^= 1; + + return parity; +} + + +void swdptap_seq_out(uint32_t MS, int ticks) +{ + swdptap_turnaround(0); + + while(ticks--) { + swdptap_bit_out(MS & 1); + MS >>= 1; + } +} + + +void swdptap_seq_out_parity(uint32_t MS, int ticks) +{ + uint8_t parity = 0; + + swdptap_turnaround(0); + + while(ticks--) { + swdptap_bit_out(MS & 1); + parity ^= MS; + MS >>= 1; + } + swdptap_bit_out(parity & 1); +} + diff --git a/src/platforms/native/traceswo.c b/src/platforms/native/traceswo.c new file mode 100644 index 0000000..926a057 --- /dev/null +++ b/src/platforms/native/traceswo.c @@ -0,0 +1,190 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file implements capture of the TRACESWO output. + * + * ARM DDI 0403D - ARMv7M Architecture Reference Manual + * ARM DDI 0337I - Cortex-M3 Technical Reference Manual + * ARM DDI 0314H - CoreSight Components Technical Reference Manual + */ + +/* TDO/TRACESWO signal comes into pin PA6/TIM3_CH1 + * Manchester coding is assumed on TRACESWO, so bit timing can be detected. + * The idea is to use TIM3 input capture modes to capture pulse timings. + * These can be capture directly to RAM by DMA. + * The core can then process the buffer to extract the frame. + */ +#include "general.h" + +#include +#include +#include + +#include + +#include + +void traceswo_init(void) +{ + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_TIM3EN); + + timer_reset(TIM3); + + /* Refer to ST doc RM0008 - STM32F10xx Reference Manual. + * Section 14.3.4 - 14.3.6 (General Purpose Timer - Input Capture) + * + * CCR1 captures cycle time, CCR2 captures high time + */ + + /* Use TI1 as capture input for CH1 and CH2 */ + timer_ic_set_input(TIM3, TIM_IC1, TIM_IC_IN_TI1); + timer_ic_set_input(TIM3, TIM_IC2, TIM_IC_IN_TI1); + + /* Capture CH1 on rising edge, CH2 on falling edge */ + timer_ic_set_polarity(TIM3, TIM_IC1, TIM_IC_RISING); + timer_ic_set_polarity(TIM3, TIM_IC2, TIM_IC_FALLING); + + /* Trigger on Filtered Timer Input 1 (TI1FP1) */ + timer_slave_set_trigger(TIM3, TIM_SMCR_TS_IT1FP1); + + /* Slave reset mode: reset counter on trigger */ + timer_slave_set_mode(TIM3, TIM_SMCR_SMS_RM); + + /* Enable capture interrupt */ + nvic_set_priority(NVIC_TIM3_IRQ, IRQ_PRI_TIM3); + nvic_enable_irq(NVIC_TIM3_IRQ); + timer_enable_irq(TIM3, TIM_DIER_CC1IE); + + /* Enable the capture channels */ + timer_ic_enable(TIM3, TIM_IC1); + timer_ic_enable(TIM3, TIM_IC2); + + timer_enable_counter(TIM3); +} + +static uint8_t trace_usb_buf[64]; +static uint8_t trace_usb_buf_size; + +void trace_buf_push(uint8_t *buf, int len) +{ + if (usbd_ep_write_packet(0x85, buf, len) != len) { + if (trace_usb_buf_size + len > 64) { + /* Stall if upstream to too slow. */ + usbd_ep_stall_set(0x85, 1); + trace_usb_buf_size = 0; + return; + } + memcpy(trace_usb_buf + trace_usb_buf_size, buf, len); + trace_usb_buf_size += len; + } +} + +void trace_buf_drain(uint8_t ep) +{ + if (!trace_usb_buf_size) + return; + + usbd_ep_write_packet(ep, trace_usb_buf, trace_usb_buf_size); + trace_usb_buf_size = 0; +} + +#define ALLOWED_DUTY_ERROR 5 + +void tim3_isr(void) +{ + uint16_t sr = TIM_SR(TIM3); + uint16_t duty, cycle; + static uint16_t bt; + static uint8_t lastbit; + static uint8_t decbuf[17]; + static uint8_t decbuf_pos; + static uint8_t halfbit; + static uint8_t notstart; + + /* Reset decoder state if capture overflowed */ + if (sr & (TIM_SR_CC1OF | TIM_SR_UIF)) { + timer_clear_flag(TIM3, TIM_SR_CC1OF | TIM_SR_UIF); + if (!(sr & (TIM_SR_CC2IF | TIM_SR_CC1IF))) + goto flush_and_reset; + } + + cycle = TIM_CCR1(TIM3); + duty = TIM_CCR2(TIM3); + + /* Reset decoder state if crazy shit happened */ + if ((bt && (((duty / bt) > 2) || ((duty / bt) == 0))) || (duty == 0)) + goto flush_and_reset; + + if(!(sr & TIM_SR_CC1IF)) notstart = 1; + + if (!bt) { + if (notstart) { + notstart = 0; + return; + } + /* First bit, sync decoder */ + duty -= ALLOWED_DUTY_ERROR; + if (((cycle / duty) != 2) && + ((cycle / duty) != 3)) + return; + bt = duty; + lastbit = 1; + halfbit = 0; + timer_set_period(TIM3, duty * 6); + timer_clear_flag(TIM3, TIM_SR_UIF); + timer_enable_irq(TIM3, TIM_DIER_UIE); + } else { + /* If high time is extended we need to flip the bit */ + if ((duty / bt) > 1) { + if (!halfbit) /* lost sync somehow */ + goto flush_and_reset; + halfbit = 0; + lastbit ^= 1; + } + decbuf[decbuf_pos >> 3] |= lastbit << (decbuf_pos & 7); + decbuf_pos++; + } + + if (!(sr & TIM_SR_CC1IF) || (((cycle - duty) / bt) > 2)) + goto flush_and_reset; + + if (((cycle - duty) / bt) > 1) { + /* If low time extended we need to pack another bit. */ + if (halfbit) /* this is a valid stop-bit or we lost sync */ + goto flush_and_reset; + halfbit = 1; + lastbit ^= 1; + decbuf[decbuf_pos >> 3] |= lastbit << (decbuf_pos & 7); + decbuf_pos++; + } + + if (decbuf_pos < 128) + return; + +flush_and_reset: + timer_set_period(TIM3, -1); + timer_disable_irq(TIM3, TIM_DIER_UIE); + trace_buf_push(decbuf, decbuf_pos >> 3); + bt = 0; + decbuf_pos = 0; + memset(decbuf, 0, sizeof(decbuf)); +} + + diff --git a/src/platforms/native/traceswo.h b/src/platforms/native/traceswo.h new file mode 100644 index 0000000..c5bc0e5 --- /dev/null +++ b/src/platforms/native/traceswo.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __TRACESWO_H +#define __TRACESWO_H + +void traceswo_init(void); +void trace_buf_drain(uint8_t ep); + +#endif + diff --git a/src/platforms/native/usbdfu.c b/src/platforms/native/usbdfu.c new file mode 100644 index 0000000..9520c7a --- /dev/null +++ b/src/platforms/native/usbdfu.c @@ -0,0 +1,329 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define APP_ADDRESS 0x08002000 + +/* Commands sent with wBlockNum == 0 as per ST implementation. */ +#define CMD_SETADDR 0x21 +#define CMD_ERASE 0x41 + +#define FLASH_OBP_RDP 0x1FFFF800 +#define FLASH_OBP_WRP10 0x1FFFF808 + +#define FLASH_OBP_RDP_KEY 0x5aa5 + +/* We need a special large control buffer for this device: */ +u8 usbd_control_buffer[1024]; + +static enum dfu_state usbdfu_state = STATE_DFU_IDLE; + +static char *get_dev_unique_id(char *serial_no); + +static struct { + u8 buf[sizeof(usbd_control_buffer)]; + u16 len; + u32 addr; + u16 blocknum; +} prog; + +const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1D50, + .idProduct = 0x6017, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +const struct usb_dfu_descriptor dfu_function = { + .bLength = sizeof(struct usb_dfu_descriptor), + .bDescriptorType = DFU_FUNCTIONAL, + .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, + .wDetachTimeout = 255, + .wTransferSize = 1024, + .bcdDFUVersion = 0x011A, +}; + +const struct usb_interface_descriptor iface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */ + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 2, + + /* The ST Microelectronics DfuSe application needs this string. + * The format isn't documented... */ + .iInterface = 4, + + .extra = &dfu_function, + .extralen = sizeof(dfu_function), +}; + +const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .altsetting = &iface, +}}; + +const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0xC0, + .bMaxPower = 0x32, + + .interface = ifaces, +}; + +static char serial_no[9]; + +static const char *usb_strings[] = { + "x", + "Black Sphere Technologies", + "Black Magic Probe (Upgrade)", + serial_no, + /* This string is used by ST Microelectronics' DfuSe utility */ + "@Internal Flash /0x08000000/8*001Ka,120*001Kg" +}; + +static u8 usbdfu_getstatus(u32 *bwPollTimeout) +{ + switch(usbdfu_state) { + case STATE_DFU_DNLOAD_SYNC: + usbdfu_state = STATE_DFU_DNBUSY; + *bwPollTimeout = 100; + return DFU_STATUS_OK; + + case STATE_DFU_MANIFEST_SYNC: + /* Device will reset when read is complete */ + usbdfu_state = STATE_DFU_MANIFEST; + return DFU_STATUS_OK; + + default: + return DFU_STATUS_OK; + } +} + +static void usbdfu_getstatus_complete(struct usb_setup_data *req) +{ + int i; + (void)req; + + switch(usbdfu_state) { + case STATE_DFU_DNBUSY: + + flash_unlock(); + if(prog.blocknum == 0) { + if ((*(u32*)(prog.buf+1) < 0x8002000) || + (*(u32*)(prog.buf+1) >= 0x8020000)) { + usbd_ep_stall_set(0, 1); + return; + } + switch(prog.buf[0]) { + case CMD_ERASE: + flash_erase_page(*(u32*)(prog.buf+1)); + case CMD_SETADDR: + prog.addr = *(u32*)(prog.buf+1); + } + } else { + u32 baseaddr = prog.addr + + ((prog.blocknum - 2) * + dfu_function.wTransferSize); + for(i = 0; i < prog.len; i += 2) + flash_program_half_word(baseaddr + i, + *(u16*)(prog.buf+i)); + } + flash_lock(); + + /* We jump straight to dfuDNLOAD-IDLE, + * skipping dfuDNLOAD-SYNC + */ + usbdfu_state = STATE_DFU_DNLOAD_IDLE; + return; + + case STATE_DFU_MANIFEST: + /* USB device must detach, we just reset... */ + scb_reset_system(); + return; /* Will never return */ + default: + return; + } +} + +static int usbdfu_control_request(struct usb_setup_data *req, u8 **buf, + u16 *len, void (**complete)(struct usb_setup_data *req)) +{ + + if((req->bmRequestType & 0x7F) != 0x21) + return 0; /* Only accept class request */ + + switch(req->bRequest) { + case DFU_DNLOAD: + if((len == NULL) || (*len == 0)) { + usbdfu_state = STATE_DFU_MANIFEST_SYNC; + return 1; + } else { + /* Copy download data for use on GET_STATUS */ + prog.blocknum = req->wValue; + prog.len = *len; + memcpy(prog.buf, *buf, *len); + usbdfu_state = STATE_DFU_DNLOAD_SYNC; + return 1; + } + case DFU_CLRSTATUS: + /* Clear error and return to dfuIDLE */ + if(usbdfu_state == STATE_DFU_ERROR) + usbdfu_state = STATE_DFU_IDLE; + return 1; + case DFU_ABORT: + /* Abort returns to dfuIDLE state */ + usbdfu_state = STATE_DFU_IDLE; + return 1; + case DFU_UPLOAD: + /* Upload not supported for now */ + return 0; + case DFU_GETSTATUS: { + u32 bwPollTimeout = 0; /* 24-bit integer in DFU class spec */ + + (*buf)[0] = usbdfu_getstatus(&bwPollTimeout); + (*buf)[1] = bwPollTimeout & 0xFF; + (*buf)[2] = (bwPollTimeout >> 8) & 0xFF; + (*buf)[3] = (bwPollTimeout >> 16) & 0xFF; + (*buf)[4] = usbdfu_state; + (*buf)[5] = 0; /* iString not used here */ + *len = 6; + + *complete = usbdfu_getstatus_complete; + + return 1; + } + case DFU_GETSTATE: + /* Return state with no state transision */ + *buf[0] = usbdfu_state; + *len = 1; + return 1; + } + + return 0; +} + +int main(void) +{ + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); + if(gpio_get(GPIOB, GPIO12)) { + /* Boot the application if it's valid */ + if((*(volatile u32*)APP_ADDRESS & 0x2FFE0000) == 0x20000000) { + /* Set vector table base address */ + SCB_VTOR = APP_ADDRESS & 0xFFFF; + /* Initialise master stack pointer */ + asm volatile ("msr msp, %0"::"g" + (*(volatile u32*)APP_ADDRESS)); + /* Jump to application */ + (*(void(**)())(APP_ADDRESS + 4))(); + } + } + + if ((FLASH_WRPR & 0x03) != 0x00) { + flash_unlock(); + FLASH_CR = 0; + flash_erase_option_bytes(); + flash_program_option_bytes(FLASH_OBP_RDP, FLASH_OBP_RDP_KEY); + flash_program_option_bytes(FLASH_OBP_WRP10, 0x03FC); + } + + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); + + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, 0, GPIO8); + + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO11); + systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8); + systick_set_reload(900000); + systick_interrupt_enable(); + systick_counter_enable(); + gpio_set_mode(GPIOB, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_FLOAT, GPIO2 | GPIO10); + + get_dev_unique_id(serial_no); + + usbd_init(&stm32f103_usb_driver, &dev, &config, usb_strings); + usbd_set_control_buffer_size(sizeof(usbd_control_buffer)); + usbd_register_control_callback( + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + usbdfu_control_request); + + gpio_set(GPIOA, GPIO8); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO8); + + while (1) + usbd_poll(); +} + +static char *get_dev_unique_id(char *s) +{ + volatile uint32_t *unique_id_p = (volatile uint32_t *)0x1FFFF7E8; + uint32_t unique_id = *unique_id_p + + *(unique_id_p + 1) + + *(unique_id_p + 2); + int i; + + /* Fetch serial number from chip's unique ID */ + for(i = 0; i < 8; i++) { + s[7-i] = ((unique_id >> (4*i)) & 0xF) + '0'; + } + for(i = 0; i < 8; i++) + if(s[i] > '9') + s[i] += 'A' - '9' - 1; + s[8] = 0; + + return s; +} + +void sys_tick_handler() +{ + gpio_toggle(GPIOB, GPIO11); /* LED2 on/off */ +} + diff --git a/src/platforms/native/usbuart.c b/src/platforms/native/usbuart.c new file mode 100644 index 0000000..2fac6ee --- /dev/null +++ b/src/platforms/native/usbuart.c @@ -0,0 +1,140 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" + +void usbuart_init(void) +{ + /* On mini hardware, UART and SWD share connector pins. + * Don't enable UART if we're being debugged. */ + if ((platform_hwversion() == 1) && (SCS_DEMCR & SCS_DEMCR_TRCENA)) + return; + + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_USART1EN); + + /* UART1 TX to 'alternate function output push-pull' */ + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9); + + /* Setup UART parameters. */ + usart_set_baudrate(USART1, 38400); + usart_set_databits(USART1, 8); + usart_set_stopbits(USART1, USART_STOPBITS_1); + usart_set_mode(USART1, USART_MODE_TX_RX); + usart_set_parity(USART1, USART_PARITY_NONE); + usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE); + + /* Finally enable the USART. */ + usart_enable(USART1); + + /* Enable interrupts */ + USART1_CR1 |= USART_CR1_RXNEIE; + nvic_set_priority(NVIC_USART1_IRQ, IRQ_PRI_USART1); + nvic_enable_irq(NVIC_USART1_IRQ); +} + +void usbuart_set_line_coding(struct usb_cdc_line_coding *coding) +{ + usart_set_baudrate(USART1, coding->dwDTERate); + usart_set_databits(USART1, coding->bDataBits); + switch(coding->bCharFormat) { + case 0: + usart_set_stopbits(USART1, USART_STOPBITS_1); + break; + case 1: + usart_set_stopbits(USART1, USART_STOPBITS_1_5); + break; + case 2: + usart_set_stopbits(USART1, USART_STOPBITS_2); + break; + } + switch(coding->bParityType) { + case 0: + usart_set_parity(USART1, USART_PARITY_NONE); + break; + case 1: + usart_set_parity(USART1, USART_PARITY_ODD); + break; + case 2: + usart_set_parity(USART1, USART_PARITY_EVEN); + break; + } +} + +void usbuart_usb_out_cb(uint8_t ep) +{ + (void)ep; + + char buf[CDCACM_PACKET_SIZE]; + int len = usbd_ep_read_packet(0x03, buf, CDCACM_PACKET_SIZE); + + /* Don't bother if uart is disabled. + * This will be the case on mini while we're being debugged. + */ + if(!(RCC_APB2ENR & RCC_APB2ENR_USART1EN)) + return; + + gpio_set(LED_PORT, LED_UART); + for(int i = 0; i < len; i++) + usart_send_blocking(USART1, buf[i]); + gpio_clear(LED_PORT, LED_UART); +} + +static uint8_t uart_usb_buf[CDCACM_PACKET_SIZE]; +static uint8_t uart_usb_buf_size; + +void usbuart_usb_in_cb(uint8_t ep) +{ + if (!uart_usb_buf_size) { + gpio_clear(LED_PORT, LED_UART); + return; + } + + usbd_ep_write_packet(ep, uart_usb_buf, uart_usb_buf_size); + uart_usb_buf_size = 0; +} + +void usart1_isr(void) +{ + char c = usart_recv(USART1); + + gpio_set(LED_PORT, LED_UART); + + /* Try to send now */ + if (usbd_ep_write_packet(0x83, &c, 1) == 1) + return; + + /* We failed, so queue for later */ + if (uart_usb_buf_size == CDCACM_PACKET_SIZE) { + /* Drop if the buffer's full: we have no flow control */ + return; + } + + uart_usb_buf[uart_usb_buf_size++] = c; +} + diff --git a/src/platforms/native/usbuart.h b/src/platforms/native/usbuart.h new file mode 100644 index 0000000..0bfa7d6 --- /dev/null +++ b/src/platforms/native/usbuart.h @@ -0,0 +1,33 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2012 Black Sphere Technologies Ltd. + * Written by Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __USBUART_H +#define __USBUART_H + +#include +#include "general.h" + +void usbuart_init(void); + +void usbuart_set_line_coding(struct usb_cdc_line_coding *coding); +void usbuart_usb_out_cb(uint8_t ep); +void usbuart_usb_in_cb(uint8_t ep); + +#endif + -- cgit v1.2.3