2010-09-27 03:47:55 +02:00
|
|
|
/******************************************************************************
|
2010-05-28 05:37:48 +02:00
|
|
|
* The MIT License
|
|
|
|
*
|
2010-06-04 06:43:21 +02:00
|
|
|
* Copyright (c) 2010 Perry Hung.
|
2010-05-28 05:37:48 +02:00
|
|
|
*
|
2011-05-04 19:15:33 +02:00
|
|
|
* Permission is hereby granted, free of charge, to any person
|
|
|
|
* obtaining a copy of this software and associated documentation
|
|
|
|
* files (the "Software"), to deal in the Software without
|
|
|
|
* restriction, including without limitation the rights to use, copy,
|
|
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* of the Software, and to permit persons to whom the Software is
|
2010-05-28 05:37:48 +02:00
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
2011-05-04 19:15:33 +02:00
|
|
|
* The above copyright notice and this permission notice shall be
|
|
|
|
* included in all copies or substantial portions of the Software.
|
2010-05-28 05:37:48 +02:00
|
|
|
*
|
2011-05-04 19:15:33 +02:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
2010-09-27 03:47:55 +02:00
|
|
|
*****************************************************************************/
|
2010-05-28 05:37:48 +02:00
|
|
|
|
|
|
|
/**
|
2011-05-04 19:15:33 +02:00
|
|
|
* @brief USB virtual serial terminal
|
2010-05-28 05:37:48 +02:00
|
|
|
*/
|
|
|
|
|
Move public headers to include directories; related cleanups.
Move libmaple/*.h to (new) libmaple/include/libmaple/. The new
accepted way to include a libmaple header foo.h is with:
#include <libmaple/foo.h>
This is more polite in terms of the include namespace. It also allows
us to e.g. implement the Arduino SPI library at all (which has header
SPI.h; providing it was previously impossible on case-insensitive
filesystems due to libmaple's spi.h).
Similarly for Wirish.
The old include style (#include "header.h") is now deprecated.
libmaple/*.h:
- Change include guard #defines from _FOO_H_ to _LIBMAPLE_FOO_H_.
- Add license headers where they're missing
- Add conditional extern "C" { ... } blocks where they're missing
(they aren't always necessary, but we might was well do it against
the future, while we're at it.).
- Change includes from #include "foo.h" to #include <libmaple/foo.h>.
- Move includes after extern "C".
- Remove extra trailing newlines
Note that this doesn't include the headers under libmaple/usb/ or
libmaple/usb/usb_lib. These will get fixed later.
libmaple/*.c:
- Change includes from #include "foo.h" to #include <libmaple/foo.h>.
Makefile:
- Add I$(LIBMAPLE_PATH)/include/libmaple to GLOBAL_FLAGS. This allows
for users (including Wirish) to migrate their code, but should go
away ASAP, since it slows down compilation.
Wirish:
- Move wirish/**/*.h to (new) wirish/include/wirish/. This ignores
the USB headers, which, as usual, are getting handled after
everything else.
- Similarly generify wirish/boards/ structure. For each supported
board "foo", move wirish/boards/foo.h and wirish/boards/foo.cpp to
wirish/boards/foo/include/board/board.h and
wirish/boards/foo/board.cpp, respectively. Also remove the #ifdef
hacks around the .cpp files.
- wirish/rules.mk: put wirish/boards/foo/include in the include path
(and add wirish/boards/foo/board.cpp to the list of sources to be
compiled). This allows saying:
#include <board/board.h>
instead of the hack currently in place. We can allow the user to
override this setting later to make adding custom board definitions
easier.
- Disable -Werror in libmaple/rules.mk, as the current USB warnings
don't let the olimex_stm32_h103 board compile. We can re-enable
-Werror once we've moved the board-specific bits out of libmaple
proper.
libraries, examples:
- Update includes accordingly.
- Miscellaneous cosmetic fixups.
Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
2011-11-15 18:45:43 +01:00
|
|
|
#include <wirish/usb_serial.h>
|
|
|
|
|
2010-06-04 06:43:21 +02:00
|
|
|
#include <string.h>
|
2012-08-22 19:26:43 +02:00
|
|
|
#include <stdint.h>
|
2010-06-04 06:43:21 +02:00
|
|
|
|
2012-07-31 21:00:57 +02:00
|
|
|
#include <libmaple/nvic.h>
|
2011-11-16 19:26:34 +01:00
|
|
|
#include <libmaple/usb_cdcacm.h>
|
Move public headers to include directories; related cleanups.
Move libmaple/*.h to (new) libmaple/include/libmaple/. The new
accepted way to include a libmaple header foo.h is with:
#include <libmaple/foo.h>
This is more polite in terms of the include namespace. It also allows
us to e.g. implement the Arduino SPI library at all (which has header
SPI.h; providing it was previously impossible on case-insensitive
filesystems due to libmaple's spi.h).
Similarly for Wirish.
The old include style (#include "header.h") is now deprecated.
libmaple/*.h:
- Change include guard #defines from _FOO_H_ to _LIBMAPLE_FOO_H_.
- Add license headers where they're missing
- Add conditional extern "C" { ... } blocks where they're missing
(they aren't always necessary, but we might was well do it against
the future, while we're at it.).
- Change includes from #include "foo.h" to #include <libmaple/foo.h>.
- Move includes after extern "C".
- Remove extra trailing newlines
Note that this doesn't include the headers under libmaple/usb/ or
libmaple/usb/usb_lib. These will get fixed later.
libmaple/*.c:
- Change includes from #include "foo.h" to #include <libmaple/foo.h>.
Makefile:
- Add I$(LIBMAPLE_PATH)/include/libmaple to GLOBAL_FLAGS. This allows
for users (including Wirish) to migrate their code, but should go
away ASAP, since it slows down compilation.
Wirish:
- Move wirish/**/*.h to (new) wirish/include/wirish/. This ignores
the USB headers, which, as usual, are getting handled after
everything else.
- Similarly generify wirish/boards/ structure. For each supported
board "foo", move wirish/boards/foo.h and wirish/boards/foo.cpp to
wirish/boards/foo/include/board/board.h and
wirish/boards/foo/board.cpp, respectively. Also remove the #ifdef
hacks around the .cpp files.
- wirish/rules.mk: put wirish/boards/foo/include in the include path
(and add wirish/boards/foo/board.cpp to the list of sources to be
compiled). This allows saying:
#include <board/board.h>
instead of the hack currently in place. We can allow the user to
override this setting later to make adding custom board definitions
easier.
- Disable -Werror in libmaple/rules.mk, as the current USB warnings
don't let the olimex_stm32_h103 board compile. We can re-enable
-Werror once we've moved the board-specific bits out of libmaple
proper.
libraries, examples:
- Update includes accordingly.
- Miscellaneous cosmetic fixups.
Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
2011-11-15 18:45:43 +01:00
|
|
|
#include <libmaple/usb.h>
|
|
|
|
|
|
|
|
#include <wirish/wirish.h>
|
2010-05-28 05:37:48 +02:00
|
|
|
|
2012-07-31 21:00:57 +02:00
|
|
|
/*
|
|
|
|
* Hooks used for bootloader reset signalling
|
|
|
|
*/
|
|
|
|
|
2012-08-27 22:47:27 +02:00
|
|
|
#if BOARD_HAVE_SERIALUSB
|
2012-07-31 21:00:57 +02:00
|
|
|
static void rxHook(unsigned, void*);
|
|
|
|
static void ifaceSetupHook(unsigned, void*);
|
2012-08-27 22:47:27 +02:00
|
|
|
#endif
|
2012-07-31 21:00:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* USBSerial interface
|
|
|
|
*/
|
|
|
|
|
2010-07-21 04:03:52 +02:00
|
|
|
#define USB_TIMEOUT 50
|
2010-07-19 06:24:31 +02:00
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
USBSerial::USBSerial(void) {
|
2012-06-03 12:00:41 +02:00
|
|
|
#if !BOARD_HAVE_SERIALUSB
|
|
|
|
ASSERT(0);
|
|
|
|
#endif
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2010-07-19 06:24:31 +02:00
|
|
|
void USBSerial::begin(void) {
|
2012-06-03 12:00:41 +02:00
|
|
|
#if BOARD_HAVE_SERIALUSB
|
2011-06-15 22:43:48 +02:00
|
|
|
usb_cdcacm_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
|
2012-07-31 21:00:57 +02:00
|
|
|
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_RX, rxHook);
|
|
|
|
usb_cdcacm_set_hooks(USB_CDCACM_HOOK_IFACE_SETUP, ifaceSetupHook);
|
2012-06-03 12:00:41 +02:00
|
|
|
#endif
|
2010-07-19 06:24:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void USBSerial::end(void) {
|
2012-06-03 12:00:41 +02:00
|
|
|
#if BOARD_HAVE_SERIALUSB
|
2011-06-15 22:43:48 +02:00
|
|
|
usb_cdcacm_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT);
|
2012-07-31 21:00:57 +02:00
|
|
|
usb_cdcacm_remove_hooks(USB_CDCACM_HOOK_RX | USB_CDCACM_HOOK_IFACE_SETUP);
|
2012-06-03 12:00:41 +02:00
|
|
|
#endif
|
2010-07-19 06:24:31 +02:00
|
|
|
}
|
|
|
|
|
2010-06-04 06:43:21 +02:00
|
|
|
void USBSerial::write(uint8 ch) {
|
2012-07-25 23:19:41 +02:00
|
|
|
this->write(&ch, 1);
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2010-06-04 06:43:21 +02:00
|
|
|
void USBSerial::write(const char *str) {
|
2011-05-04 19:15:33 +02:00
|
|
|
this->write(str, strlen(str));
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
void USBSerial::write(const void *buf, uint32 len) {
|
2011-10-21 23:55:48 +02:00
|
|
|
if (!this->isConnected() || !buf) {
|
2010-07-23 02:33:53 +02:00
|
|
|
return;
|
|
|
|
}
|
2010-09-27 03:47:55 +02:00
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
uint32 txed = 0;
|
|
|
|
uint32 old_txed = 0;
|
2010-07-19 06:24:31 +02:00
|
|
|
uint32 start = millis();
|
2010-09-27 03:47:55 +02:00
|
|
|
|
2013-01-17 17:42:41 +01:00
|
|
|
uint32 sent = 0;
|
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
while (txed < len && (millis() - start < USB_TIMEOUT)) {
|
2013-01-17 17:42:41 +01:00
|
|
|
sent = usb_cdcacm_tx((const uint8*)buf + txed, len - txed);
|
|
|
|
txed += sent;
|
2011-05-04 19:15:33 +02:00
|
|
|
if (old_txed != txed) {
|
2010-07-19 06:24:31 +02:00
|
|
|
start = millis();
|
2011-05-04 19:15:33 +02:00
|
|
|
}
|
|
|
|
old_txed = txed;
|
2010-07-19 06:24:31 +02:00
|
|
|
}
|
2013-01-17 17:42:41 +01:00
|
|
|
|
|
|
|
|
|
|
|
if (sent == USB_CDCACM_TX_EPSIZE) {
|
|
|
|
while (usb_cdcacm_is_transmitting() != 0) {
|
|
|
|
}
|
|
|
|
/* flush out to avoid having the pc wait for more data */
|
|
|
|
usb_cdcacm_tx(NULL, 0);
|
|
|
|
}
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2010-06-04 06:43:21 +02:00
|
|
|
uint32 USBSerial::available(void) {
|
2011-09-23 23:56:24 +02:00
|
|
|
return usb_cdcacm_data_available();
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2010-06-04 06:43:21 +02:00
|
|
|
uint32 USBSerial::read(void *buf, uint32 len) {
|
2011-05-04 19:15:33 +02:00
|
|
|
if (!buf) {
|
2010-09-27 03:47:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2010-06-04 06:43:21 +02:00
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
uint32 rxed = 0;
|
|
|
|
while (rxed < len) {
|
2011-09-23 23:56:24 +02:00
|
|
|
rxed += usb_cdcacm_rx((uint8*)buf + rxed, len - rxed);
|
2010-12-14 04:09:12 +01:00
|
|
|
}
|
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
return rxed;
|
2010-05-28 05:37:48 +02:00
|
|
|
}
|
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
/* Blocks forever until 1 byte is received */
|
2010-06-04 06:43:21 +02:00
|
|
|
uint8 USBSerial::read(void) {
|
2011-09-23 23:56:24 +02:00
|
|
|
uint8 b;
|
|
|
|
this->read(&b, 1);
|
|
|
|
return b;
|
2010-06-04 06:43:21 +02:00
|
|
|
}
|
|
|
|
|
2010-07-19 06:24:31 +02:00
|
|
|
uint8 USBSerial::pending(void) {
|
2011-09-23 23:56:24 +02:00
|
|
|
return usb_cdcacm_get_pending();
|
2010-07-19 06:24:31 +02:00
|
|
|
}
|
|
|
|
|
2011-05-04 19:15:33 +02:00
|
|
|
uint8 USBSerial::isConnected(void) {
|
2011-10-21 23:55:48 +02:00
|
|
|
return usb_is_connected(USBLIB) && usb_is_configured(USBLIB);
|
2011-05-04 19:15:33 +02:00
|
|
|
}
|
|
|
|
|
2010-07-23 00:24:51 +02:00
|
|
|
uint8 USBSerial::getDTR(void) {
|
2011-09-23 23:56:24 +02:00
|
|
|
return usb_cdcacm_get_dtr();
|
2010-07-23 00:24:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
uint8 USBSerial::getRTS(void) {
|
2011-09-23 23:56:24 +02:00
|
|
|
return usb_cdcacm_get_rts();
|
2010-07-23 00:24:51 +02:00
|
|
|
}
|
|
|
|
|
2012-06-03 12:00:41 +02:00
|
|
|
#if BOARD_HAVE_SERIALUSB
|
2010-06-04 06:43:21 +02:00
|
|
|
USBSerial SerialUSB;
|
2012-06-03 12:00:41 +02:00
|
|
|
#endif
|
2012-07-31 21:00:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Bootloader hook implementations
|
|
|
|
*/
|
|
|
|
|
2012-08-27 22:47:27 +02:00
|
|
|
#if BOARD_HAVE_SERIALUSB
|
|
|
|
|
2012-07-31 21:00:57 +02:00
|
|
|
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".
|
2012-08-22 19:26:43 +02:00
|
|
|
static const uint8 magic[4] = {'1', 'E', 'A', 'F'};
|
2012-07-31 21:00:57 +02:00
|
|
|
uint8 chkBuf[4];
|
|
|
|
|
2012-08-22 19:26:43 +02:00
|
|
|
// Peek at the waiting bytes, looking for reset sequence,
|
|
|
|
// bailing on mismatch.
|
2012-07-31 21:00:57 +02:00
|
|
|
usb_cdcacm_peek(chkBuf, 4);
|
2012-08-22 19:26:43 +02:00
|
|
|
for (unsigned i = 0; i < sizeof(magic); i++) {
|
|
|
|
if (chkBuf[i] != magic[i]) {
|
|
|
|
return;
|
2012-07-31 21:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:26:43 +02:00
|
|
|
// Got the magic sequence -> reset, presumably into the bootloader.
|
|
|
|
// Return address is wait_reset, but we must set the thumb bit.
|
|
|
|
uintptr_t target = (uintptr_t)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");
|
|
|
|
/* Can't happen. */
|
|
|
|
ASSERT_FAULT(0);
|
2012-07-31 21:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-27 22:47:27 +02:00
|
|
|
|
|
|
|
#endif // BOARD_HAVE_SERIALUSB
|