aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-07-31 15:00:57 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-07-31 15:04:49 -0400
commitc8b7f0b5b390ff4cae32566d53f4410e72de4573 (patch)
treea28d849f8f06797e7e78f2cb6c30da1819ab9d2f
parent925446023aa23c2141d711c6e42cd2f75ce70a93 (diff)
downloadlibrambutan-c8b7f0b5b390ff4cae32566d53f4410e72de4573.tar.gz
librambutan-c8b7f0b5b390ff4cae32566d53f4410e72de4573.zip
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 <mbolivar@leaflabs.com>
-rw-r--r--libmaple/include/libmaple/libmaple.h1
-rw-r--r--libmaple/usb/stm32f1/usb_cdcacm.c150
-rw-r--r--wirish/usb_serial.cpp116
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 <string.h>
+#include <libmaple/nvic.h>
#include <libmaple/usb_cdcacm.h>
#include <libmaple/usb.h>
#include <wirish/wirish.h>
+/*
+ * 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 */
+ }
+ }
+ }
+}