aboutsummaryrefslogtreecommitdiff
path: root/src/platforms/native
diff options
context:
space:
mode:
Diffstat (limited to 'src/platforms/native')
-rw-r--r--src/platforms/native/Makefile.inc32
-rw-r--r--src/platforms/native/blackmagic.ld29
-rw-r--r--src/platforms/native/cdcacm.c581
-rw-r--r--src/platforms/native/gdb_if.c86
-rw-r--r--src/platforms/native/jtagtap.c89
-rw-r--r--src/platforms/native/platform.c254
-rw-r--r--src/platforms/native/platform.h162
-rw-r--r--src/platforms/native/swdptap.c155
-rw-r--r--src/platforms/native/traceswo.c190
-rw-r--r--src/platforms/native/traceswo.h27
-rw-r--r--src/platforms/native/usbdfu.c329
-rw-r--r--src/platforms/native/usbuart.c140
-rw-r--r--src/platforms/native/usbuart.h33
13 files changed, 2107 insertions, 0 deletions
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 <tommi@viadmin.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 <libopencm3/stm32/f1/rcc.h>
+#include <libopencm3/stm32/nvic.h>
+#include <libopencm3/stm32/exti.h>
+#include <libopencm3/stm32/f1/gpio.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/cdc.h>
+#include <libopencm3/stm32/f1/scb.h>
+#include <libopencm3/usb/dfu.h>
+#include <stdlib.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 <libopencm3/usb/usbd.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the low-level JTAG TAP interface. */
+
+#include <stdio.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the platform specific functions for the STM32
+ * implementation.
+ */
+
+#include <libopencm3/stm32/f1/rcc.h>
+#include <libopencm3/stm32/systick.h>
+#include <libopencm3/stm32/f1/scb.h>
+#include <libopencm3/stm32/nvic.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/stm32/f1/adc.h>
+
+#include "platform.h"
+#include "jtag_scan.h"
+#include "usbuart.h"
+
+#include <ctype.h>
+
+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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the platform specific functions for the STM32
+ * implementation.
+ */
+#ifndef __PLATFORM_H
+#define __PLATFORM_H
+
+#include <libopencm3/stm32/f1/gpio.h>
+
+#include <setjmp.h>
+#include <alloca.h>
+
+#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);
+
+/* <cdcacm.c> */
+void cdcacm_init(void);
+/* Returns current usb configuration, or 0 if not configured. */
+int cdcacm_get_config(void);
+int cdcacm_get_dtr(void);
+
+/* <platform.h> */
+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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements the low-level SW-DP interface. */
+
+#include <stdio.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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 <libopencm3/stm32/nvic.h>
+#include <libopencm3/stm32/timer.h>
+#include <libopencm3/stm32/f1/rcc.h>
+
+#include <libopencm3/usb/usbd.h>
+
+#include <string.h>
+
+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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <libopencm3/stm32/systick.h>
+#include <libopencm3/stm32/f1/rcc.h>
+#include <libopencm3/stm32/f1/gpio.h>
+#include <libopencm3/stm32/f1/flash.h>
+#include <libopencm3/stm32/f1/scb.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/dfu.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopencm3/stm32/f1/rcc.h>
+#include <libopencm3/stm32/f1/gpio.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/nvic.h>
+#include <libopencm3/cm3/scs.h>
+#include <libopencm3/usb/usbd.h>
+#include <libopencm3/usb/cdc.h>
+
+#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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#ifndef __USBUART_H
+#define __USBUART_H
+
+#include <libopencm3/usb/cdc.h>
+#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
+