From c8b7f0b5b390ff4cae32566d53f4410e72de4573 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Tue, 31 Jul 2012 15:00:57 -0400 Subject: Move bootloader reset signal detection to Wirish. Use usb_cdcacm hooks to move the DTR edge and "1EAF" magic packet detection to usb_serial.cpp. We'll later be able to extend this system to support Leonardo-style reset signalling. Signed-off-by: Marti Bolivar --- libmaple/include/libmaple/libmaple.h | 1 - libmaple/usb/stm32f1/usb_cdcacm.c | 150 ++++++----------------------------- wirish/usb_serial.cpp | 116 +++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 127 deletions(-) diff --git a/libmaple/include/libmaple/libmaple.h b/libmaple/include/libmaple/libmaple.h index f1a595e..5abcaf9 100644 --- a/libmaple/include/libmaple/libmaple.h +++ b/libmaple/include/libmaple/libmaple.h @@ -48,7 +48,6 @@ extern "C" { */ #define USER_ADDR_ROM 0x08005000 #define USER_ADDR_RAM 0x20000C00 -#define STACK_TOP 0x20000800 #ifdef __cplusplus } diff --git a/libmaple/usb/stm32f1/usb_cdcacm.c b/libmaple/usb/stm32f1/usb_cdcacm.c index 44b2239..7a7d656 100644 --- a/libmaple/usb/stm32f1/usb_cdcacm.c +++ b/libmaple/usb/stm32f1/usb_cdcacm.c @@ -82,8 +82,6 @@ static uint8* usbGetStringDescriptor(uint16 length); static void usbSetConfiguration(void); static void usbSetDeviceAddress(void); -static void wait_reset(void); - /* * Descriptors */ @@ -263,13 +261,6 @@ ONE_DESCRIPTOR String_Descriptor[3] = { * Etc. */ -typedef enum { - DTR_UNSET, - DTR_HIGH, - DTR_NEGEDGE, - DTR_LOW -} RESET_STATE; - typedef struct { uint32 bitrate; uint8 format; @@ -284,7 +275,6 @@ USB_Line_Coding line_coding = { .paritytype = 0x00, .datatype = 0x08 }; -RESET_STATE reset_state = DTR_UNSET; static volatile uint8 vcomBufferRx[USB_CDCACM_RX_BUFLEN]; static volatile uint32 rx_offset = 0; @@ -492,64 +482,13 @@ static void vcomDataTxCb(void) { countTx = 0; } -#define EXC_RETURN 0xFFFFFFF9 -#define DEFAULT_CPSR 0x61000000 static void vcomDataRxCb(void) { - /* FIXME this is mad buggy */ - /* This following is safe since sizeof(vcomBufferRx) exceeds the * largest possible USB_CDCACM_RX_EPSIZE, and we set to NAK after * each data packet. Only when all bytes have been read is the RX * endpoint set back to VALID. */ newBytes = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP); usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_NAK); - - /* magic number, {0x31, 0x45, 0x41, 0x46} is "1EAF" */ - uint8 chkBuf[4]; - uint8 cmpBuf[4] = {0x31, 0x45, 0x41, 0x46}; - if (reset_state == DTR_NEGEDGE) { - reset_state = DTR_LOW; - - if (newBytes >= 4) { - unsigned int target = (unsigned int)wait_reset | 0x1; - - usb_copy_from_pma(chkBuf, 4, USB_CDCACM_RX_ADDR); - - int i; - USB_Bool cmpMatch = TRUE; - for (i = 0; i < 4; i++) { - if (chkBuf[i] != cmpBuf[i]) { - cmpMatch = FALSE; - } - } - - if (cmpMatch) { - asm volatile("mov r0, %[stack_top] \n\t" // Reset stack - "mov sp, r0 \n\t" - "mov r0, #1 \n\t" - "mov r1, %[target_addr] \n\t" - "mov r2, %[cpsr] \n\t" - "push {r2} \n\t" // Fake xPSR - "push {r1} \n\t" // PC target addr - "push {r0} \n\t" // Fake LR - "push {r0} \n\t" // Fake R12 - "push {r0} \n\t" // Fake R3 - "push {r0} \n\t" // Fake R2 - "push {r0} \n\t" // Fake R1 - "push {r0} \n\t" // Fake R0 - "mov lr, %[exc_return] \n\t" - "bx lr" - : - : [stack_top] "r" (STACK_TOP), - [target_addr] "r" (target), - [exc_return] "r" (EXC_RETURN), - [cpsr] "r" (DEFAULT_CPSR) - : "r0", "r1", "r2"); - /* should never get here */ - } - } - } - usb_copy_from_pma((uint8*)vcomBufferRx, newBytes, USB_CDCACM_RX_ADDR); if (rx_hook) { @@ -634,28 +573,27 @@ static void usbReset(void) { } static RESULT usbDataSetup(uint8 request) { - uint8 *(*CopyRoutine)(uint16); - CopyRoutine = NULL; + uint8* (*CopyRoutine)(uint16) = 0; if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { - /* Call the user hook first. */ - if (iface_setup_hook) { - uint8 req_copy = request; - iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy); - } - switch (request) { - case (USB_CDCACM_GET_LINE_CODING): + case USB_CDCACM_GET_LINE_CODING: CopyRoutine = vcomGetSetLineCoding; last_request = USB_CDCACM_GET_LINE_CODING; break; - case (USB_CDCACM_SET_LINE_CODING): + case USB_CDCACM_SET_LINE_CODING: CopyRoutine = vcomGetSetLineCoding; last_request = USB_CDCACM_SET_LINE_CODING; break; default: break; } + + /* Call the user hook. */ + if (iface_setup_hook) { + uint8 req_copy = request; + iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy); + } } if (CopyRoutine == NULL) { @@ -669,66 +607,32 @@ static RESULT usbDataSetup(uint8 request) { } static RESULT usbNoDataSetup(uint8 request) { + RESULT ret = USB_UNSUPPORT; uint8 new_signal; - /* we support set com feature but dont handle it */ if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { - /* Call the user hook first. */ - if (iface_setup_hook) { - uint8 req_copy = request; - iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy); - } - switch (request) { - case (USB_CDCACM_SET_COMM_FEATURE): - return USB_SUCCESS; - case (USB_CDCACM_SET_CONTROL_LINE_STATE): - /* to reset the board, pull both dtr and rts low - then pulse dtr by itself */ + case USB_CDCACM_SET_COMM_FEATURE: + /* We support set comm. feature, but don't handle it. */ + ret = USB_SUCCESS; + break; + case USB_CDCACM_SET_CONTROL_LINE_STATE: + /* Track changes to DTR and RTS. */ new_signal = (pInformation->USBwValues.bw.bb0 & (USB_CDCACM_CONTROL_LINE_DTR | USB_CDCACM_CONTROL_LINE_RTS)); line_dtr_rts = new_signal & 0x03; + ret = USB_SUCCESS; + break; + } - switch (reset_state) { - /* no default, covered enum */ - case DTR_UNSET: - if ((new_signal & USB_CDCACM_CONTROL_LINE_DTR) == 0 ) { - reset_state = DTR_LOW; - } else { - reset_state = DTR_HIGH; - } - break; - - case DTR_HIGH: - if ((new_signal & USB_CDCACM_CONTROL_LINE_DTR) == 0 ) { - reset_state = DTR_NEGEDGE; - } else { - reset_state = DTR_HIGH; - } - break; - - case DTR_NEGEDGE: - if ((new_signal & USB_CDCACM_CONTROL_LINE_DTR) == 0 ) { - reset_state = DTR_LOW; - } else { - reset_state = DTR_HIGH; - } - break; - - case DTR_LOW: - if ((new_signal & USB_CDCACM_CONTROL_LINE_DTR) == 0 ) { - reset_state = DTR_LOW; - } else { - reset_state = DTR_HIGH; - } - break; - } - - return USB_SUCCESS; + /* Call the user hook. */ + if (iface_setup_hook) { + uint8 req_copy = request; + iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy); } } - return USB_UNSUPPORT; + return ret; } static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting) { @@ -767,9 +671,3 @@ static void usbSetConfiguration(void) { static void usbSetDeviceAddress(void) { USBLIB->state = USB_ADDRESSED; } - -#define RESET_DELAY 100000 -static void wait_reset(void) { - delay_us(RESET_DELAY); - nvic_sys_reset(); -} diff --git a/wirish/usb_serial.cpp b/wirish/usb_serial.cpp index cfc6317..bf2ea4e 100644 --- a/wirish/usb_serial.cpp +++ b/wirish/usb_serial.cpp @@ -32,11 +32,23 @@ #include +#include #include #include #include +/* + * Hooks used for bootloader reset signalling + */ + +static void rxHook(unsigned, void*); +static void ifaceSetupHook(unsigned, void*); + +/* + * USBSerial interface + */ + #define USB_TIMEOUT 50 USBSerial::USBSerial(void) { @@ -48,12 +60,15 @@ USBSerial::USBSerial(void) { void USBSerial::begin(void) { #if BOARD_HAVE_SERIALUSB usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); + usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook); + usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook); #endif } void USBSerial::end(void) { #if BOARD_HAVE_SERIALUSB usb_cdcacm_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); + usb_cdcacm_remove_hooks(USB_CDCACM_HOOK_RX | USB_CDCACM_HOOK_IFACE_SETUP); #endif } @@ -126,3 +141,104 @@ uint8 USBSerial::getRTS(void) { #if BOARD_HAVE_SERIALUSB USBSerial SerialUSB; #endif + +/* + * Bootloader hook implementations + */ + +enum reset_state_t { + DTR_UNSET, + DTR_HIGH, + DTR_NEGEDGE, + DTR_LOW +}; + +static reset_state_t reset_state = DTR_UNSET; + +static void ifaceSetupHook(unsigned hook, void *requestvp) { + uint8 request = *(uint8*)requestvp; + + // Ignore requests we're not interested in. + if (request != USB_CDCACM_SET_CONTROL_LINE_STATE) { + return; + } + + // We need to see a negative edge on DTR before we start looking + // for the in-band magic reset byte sequence. + uint8 dtr = usb_cdcacm_get_dtr(); + switch (reset_state) { + case DTR_UNSET: + reset_state = dtr ? DTR_HIGH : DTR_LOW; + break; + case DTR_HIGH: + reset_state = dtr ? DTR_HIGH : DTR_NEGEDGE; + break; + case DTR_NEGEDGE: + reset_state = dtr ? DTR_HIGH : DTR_LOW; + break; + case DTR_LOW: + reset_state = dtr ? DTR_HIGH : DTR_LOW; + break; + } +} + +#define RESET_DELAY 100000 +static void wait_reset(void) { + delay_us(RESET_DELAY); + nvic_sys_reset(); +} + +#define STACK_TOP 0x20000800 +#define EXC_RETURN 0xFFFFFFF9 +#define DEFAULT_CPSR 0x61000000 +static void rxHook(unsigned hook, void *ignored) { + /* FIXME this is mad buggy; we need a new reset sequence. E.g. NAK + * after each RX means you can't reset if any bytes are waiting. */ + if (reset_state == DTR_NEGEDGE) { + reset_state = DTR_LOW; + + if (usb_cdcacm_data_available() >= 4) { + // The magic reset sequence is "1EAF". + uint8 cmpBuf[4] = {'1', 'E', 'A', 'F'}; + uint8 chkBuf[4]; + + // Peek at the waiting bytes, looking for reset sequence. + usb_cdcacm_peek(chkBuf, 4); + bool cmpMatch = true; + for (int i = 0; i < 4; i++) { + if (chkBuf[i] != cmpBuf[i]) { + cmpMatch = false; + break; + } + } + + // Got the magic sequence? Reset, presumably into the bootloader. + if (cmpMatch) { + // Return address is wait_reset, but we must set the thumb bit. + unsigned int target = (unsigned int)wait_reset | 0x1; + asm volatile("mov r0, %[stack_top] \n\t" // Reset stack + "mov sp, r0 \n\t" + "mov r0, #1 \n\t" + "mov r1, %[target_addr] \n\t" + "mov r2, %[cpsr] \n\t" + "push {r2} \n\t" // Fake xPSR + "push {r1} \n\t" // PC target addr + "push {r0} \n\t" // Fake LR + "push {r0} \n\t" // Fake R12 + "push {r0} \n\t" // Fake R3 + "push {r0} \n\t" // Fake R2 + "push {r0} \n\t" // Fake R1 + "push {r0} \n\t" // Fake R0 + "mov lr, %[exc_return] \n\t" + "bx lr" + : + : [stack_top] "r" (STACK_TOP), + [target_addr] "r" (target), + [exc_return] "r" (EXC_RETURN), + [cpsr] "r" (DEFAULT_CPSR) + : "r0", "r1", "r2"); + /* should never get here */ + } + } + } +} -- cgit v1.2.3