diff options
author | Andrew Shadura <andrewsh@debian.org> | 2014-10-06 08:35:56 +0200 |
---|---|---|
committer | Andrew Shadura <andrewsh@debian.org> | 2016-02-15 12:51:18 +0300 |
commit | 9de12366580cd1fe03b74bdda97541ead108bdd6 (patch) | |
tree | b56b5dde1f8bb10e89f0f07ab19f5be5f3e0d254 | |
parent | 2cb4f8fe0dd35b30c6d6c0071bed77c6eba70553 (diff) | |
parent | fbbebf2cfecf4c184a5076c07ba87e4224f008fc (diff) |
Imported Debian patch 0.4-1
-rw-r--r-- | Android.mk | 20 | ||||
-rw-r--r-- | HOWTO | 35 | ||||
-rw-r--r-- | I2C.txt | 94 | ||||
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | dev_table.c | 70 | ||||
-rw-r--r-- | i2c.c | 209 | ||||
-rw-r--r-- | init.c | 28 | ||||
-rw-r--r-- | init.h | 6 | ||||
-rw-r--r-- | main.c | 361 | ||||
-rw-r--r-- | parsers/Android.mk | 6 | ||||
-rw-r--r-- | parsers/hex.c | 8 | ||||
-rw-r--r-- | port.c | 59 | ||||
-rw-r--r-- | port.h | 75 | ||||
-rw-r--r-- | protocol.txt | 19 | ||||
-rw-r--r-- | serial.h | 37 | ||||
-rw-r--r-- | serial_posix.c | 329 | ||||
-rw-r--r-- | serial_w32.c | 343 | ||||
-rw-r--r-- | stm32.c | 1065 | ||||
-rw-r--r-- | stm32.h | 40 | ||||
-rw-r--r-- | stm32flash.1 | 110 | ||||
-rw-r--r-- | utils.c | 4 |
22 files changed, 2180 insertions, 754 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..7be3d00 --- /dev/null +++ b/Android.mk @@ -0,0 +1,20 @@ +TOP_LOCAL_PATH := $(call my-dir) + +include $(call all-named-subdir-makefiles, parsers) + +LOCAL_PATH := $(TOP_LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_MODULE := stm32flash +LOCAL_SRC_FILES := \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c \ + stm32.c \ + utils.c +LOCAL_STATIC_LIBRARIES := libparsers +include $(BUILD_EXECUTABLE) @@ -0,0 +1,35 @@ +Add new interfaces: +===================================================================== +Current version 0.4 supports the following interfaces: +- UART Windows (either "COMn" and "\\.\COMn"); +- UART posix/Linux (e.g. "/dev/ttyUSB0"); +- I2C Linux through standard driver "i2c-dev" (e.g. "/dev/i2c-n"). + +Starting from version 0.4, the back-end of stm32flash is modular and +ready to be expanded to support new interfaces. +I'm planning adding SPI on Linux through standard driver "spidev". +You are invited to contribute with more interfaces. + +To add a new interface you need to add a new file, populate the struct +port_interface (check at the end of files i2c.c, serial_posix.c and +serial_w32.c) and provide the relative functions to operate on the +interface: open/close, read/write, get_cfg_str and the optional gpio. +The include the new drive in Makefile and register the new struct +port_interface in file port.c in struct port_interface *ports[]. + +There are several USB-I2C adapter in the market, each providing its +own libraries to communicate with the I2C bus. +Could be interesting to provide as back-end a bridge between stm32flash +and such libraries (I have no plan on this item). + + +Add new STM32 devices: +===================================================================== +Add a new line in file dev_table.c, in table devices[]. +The fields of the table are listed in stm32.h, struct stm32_dev. + + +Cross compile on Linux host for Windows target with MinGW: +===================================================================== +I'm using a 64 bit Arch Linux machines, and I usually run: + make CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar @@ -0,0 +1,94 @@ +About I2C back-end communication in stm32flash +========================================================================== + +Starting from version v0.4, beside the serial communication port, +stm32flash adds support for I2C port to talk with STM32 bootloader. + +The current I2C back-end supports only the API provided by Linux kernel +driver "i2c-dev", so only I2C controllers with Linux kernel driver can be +used. +In Linux source code, most of the drivers for I2C and SMBUS controllers +are in + ./drivers/i2c/busses/ +Only I2C is supported by STM32 bootloader, so check the section below +about SMBUS. +No I2C support for Windows is available in stm32flash v0.4. + +Thanks to the new modular back-end, stm32flash can be easily extended to +support new back-ends and API. Check HOWTO file in stm32flash source code +for details. + +In the market there are several USB-to-I2C dongles; most of them are not +supported by kernel drivers. Manufacturer provide proprietary userspace +libraries using not standardized API. +These API and dongles could be supported in feature versions. + +There are currently 3 versions of STM32 bootloader for I2C communications: +- v1.0 using I2C clock stretching synchronization between host and STM32; +- v1.1 superset of v1.0, adds non stretching commands; +- v1.2 superset of v1.1, adds CRC command and compatibility with i2cdetect. +Details in ST application note AN2606. +All the bootloaders above are tested and working with stm32flash. + + +SMBUS controllers +========================================================================== + +Almost 50% of the drivers in Linux source code folder + ./drivers/i2c/busses/ +are for controllers that "only" support SMBUS protocol. They can NOT +operate with STM32 bootloader. +To identify if your controller supports I2C, use command: + i2cdetect -F n +where "n" is the number of the I2C interface (e.g. n=3 for "/dev/i2c-3"). +Controllers that supports I2C will report + I2C yes +Controller that support both I2C and SMBUS are ok. + +If you are interested on details about SMBUS protocol, you can download +the current specs from + http://smbus.org/specs/smbus20.pdf +and you can read the files in Linux source code + ./Documentation/i2c/i2c-protocol + ./Documentation/i2c/smbus-protocol + + +About bootloader v1.0 +========================================================================== + +Version v1.0 can have issues with some I2C controllers due to use of clock +stretching during commands that require long operations, like flash erase +and programming. + +Clock stretching is a technique to synchronize host and I2C device. When +I2C device wants to force a delay in the communication, it push "low" the +I2C clock; the I2C controller detects it and waits until I2C clock returns +"high". +Most I2C controllers set a "timeout" for clock stretching, ranging from +few milli-seconds to seconds depending on specific HW or SW driver. + +It is possible that the timeout in your I2C controller is smaller than the +delay required for flash erase or programming. In this case the I2C +controller will timeout and report error to stm32flash. +There is no possibility for stm32flash to retry, so it can only signal the +error and exit. + +To by-pass the issue with bootloader v1.0 you can modify the kernel driver +of your I2C controller. Not an easy job, since every controller has its own +way to handle the timeout. + +In my case I'm using the I2C controller integrated in the VGA port of my +laptop HP EliteBook 8460p. I built the 0.25$ VGA-to-I2C adapter reported in + http://www.paintyourdragon.com/?p=43 +To change the timeout of the I2C controller I had to modify the kernel file + drivers/gpu/drm/radeon/radeon_i2c.c +line 969 +- i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ ++ i2c->bit.timeout = msecs_to_jiffies(5000); /* 5s for STM32 */ +and recompile it. +Then + $> modprobe i2c-dev + $> chmod 666 /dev/i2c-7 + #> stm32flash -a 0x39 /dev/i2c-7 + +2014-09-16 Antonio Borneo @@ -3,7 +3,15 @@ CFLAGS += -Wall -g INSTALL = install -OBJS = main.o utils.o stm32.o serial_common.o serial_platform.o init.o +OBJS = dev_table.o \ + i2c.o \ + init.o \ + main.o \ + port.o \ + serial_common.o \ + serial_platform.o \ + stm32.o \ + utils.o LIBOBJS = parsers/parsers.a diff --git a/debian/changelog b/debian/changelog index 889b1df..73fdd97 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +stm32flash (0.4-1) unstable; urgency=medium + + * New upstream release (Closes: #763972). + + -- Andrew Shadura <andrewsh@debian.org> Mon, 06 Oct 2014 08:35:56 +0200 + stm32flash (0.3+dfsg-1) unstable; urgency=medium * Initial release (Closes: #735917). diff --git a/dev_table.c b/dev_table.c new file mode 100644 index 0000000..399cd9d --- /dev/null +++ b/dev_table.c @@ -0,0 +1,70 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com> + Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com> + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "stm32.h" + +/* + * Device table, corresponds to the "Bootloader device-dependant parameters" + * table in ST document AN2606. + * Note that the option bytes upper range is inclusive! + */ +const stm32_dev_t devices[] = { + /* F0 */ + {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x445, "STM32F042xx" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800}, + {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800}, + /* F1 */ + {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800}, + {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800}, + /* Note that F2 and F4 devices have sectors of different page sizes + and only the first sectors (of one page size) are included here */ + /* F2 */ + {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* F3 */ + {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x439, "STM32F302x4(6/8)" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + /* F4 */ + {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */ + {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + /* L0 */ + {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + /* L1 */ + {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000}, + {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + /* These are not (yet) in AN2606: */ + {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0}, + {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0}, + {0x0} +}; @@ -0,0 +1,209 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com> + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "serial.h" +#include "port.h" + + +#if defined(__WIN32__) || defined(__CYGWIN__) || defined(__APPLE__) + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + return PORT_ERR_NODEV; +} + +struct port_interface port_i2c = { + .name = "i2c", + .open = i2c_open, +}; + +#else + +#ifdef __ANDROID__ +#define I2C_SLAVE 0x0703 /* Use this slave address */ +#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */ +/* To determine what functionality is present */ +#define I2C_FUNC_I2C 0x00000001 +#else +#include <linux/i2c.h> +#include <linux/i2c-dev.h> +#endif + +#include <sys/ioctl.h> + +struct i2c_priv { + int fd; + int addr; +}; + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + struct i2c_priv *h; + int fd, addr, ret; + unsigned long funcs; + + /* 1. check device name match */ + if (strncmp(ops->device, "/dev/i2c-", strlen("/dev/i2c-"))) + return PORT_ERR_NODEV; + + /* 2. check options */ + addr = ops->bus_addr; + if (addr < 0x03 || addr > 0x77) { + fprintf(stderr, "I2C address out of range [0x03-0x77]\n"); + return PORT_ERR_UNKNOWN; + } + + /* 3. open it */ + h = calloc(sizeof(*h), 1); + if (h == NULL) { + fprintf(stderr, "End of memory\n"); + return PORT_ERR_UNKNOWN; + } + fd = open(ops->device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open special file \"%s\"\n", + ops->device); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 3.5. Check capabilities */ + ret = ioctl(fd, I2C_FUNCS, &funcs); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(funcs) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + if ((funcs & I2C_FUNC_I2C) == 0) { + fprintf(stderr, "Error: controller is not I2C, only SMBUS.\n"); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 4. set options */ + ret = ioctl(fd, I2C_SLAVE, addr); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(slave) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + h->fd = fd; + h->addr = addr; + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t i2c_close(struct port_interface *port) +{ + struct i2c_priv *h; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + close(h->fd); + free(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t i2c_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = read(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = write(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n, + int level) +{ + return PORT_ERR_OK; +} + +static const char *i2c_get_cfg_str(struct port_interface *port) +{ + struct i2c_priv *h; + static char str[11]; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return "INVALID"; + snprintf(str, sizeof(str), "addr 0x%2x", h->addr); + return str; +} + +static struct varlen_cmd i2c_cmd_get_reply[] = { + {0x10, 11}, + {0x11, 17}, + {0x12, 18}, + { /* sentinel */ } +}; + +struct port_interface port_i2c = { + .name = "i2c", + .flags = PORT_STRETCH_W, + .open = i2c_open, + .close = i2c_close, + .read = i2c_read, + .write = i2c_write, + .gpio = i2c_gpio, + .cmd_get_reply = i2c_cmd_get_reply, + .get_cfg_str = i2c_get_cfg_str, +}; + +#endif @@ -21,6 +21,7 @@ #include <ctype.h> #include <fcntl.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -29,6 +30,7 @@ #include "init.h" #include "serial.h" #include "stm32.h" +#include "port.h" struct gpio_list { struct gpio_list *next; @@ -105,7 +107,7 @@ static int release_gpio(int n) return write_to("/sys/class/gpio/unexport", num); } -static int gpio_sequence(serial_t *serial, const char *s, size_t l) +static int gpio_sequence(struct port_interface *port, const char *s, size_t l) { struct gpio_list *gpio_to_release = NULL, *to_free; int ret, level, gpio; @@ -154,7 +156,7 @@ static int gpio_sequence(serial_t *serial, const char *s, size_t l) } } if (gpio < 0) - ret = (serial_gpio(serial, -gpio, level) == SERIAL_ERR_OK); + ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); else ret = drive_gpio(gpio, level, &gpio_to_release); usleep(100000); @@ -170,7 +172,7 @@ static int gpio_sequence(serial_t *serial, const char *s, size_t l) return ret; } -static int gpio_bl_entry(serial_t *serial, const char *seq) +static int gpio_bl_entry(struct port_interface *port, const char *seq) { char *s; @@ -179,12 +181,12 @@ static int gpio_bl_entry(serial_t *serial, const char *seq) s = strchr(seq, ':'); if (s == NULL) - return gpio_sequence(serial, seq, strlen(seq)); + return gpio_sequence(port, seq, strlen(seq)); - return gpio_sequence(serial, seq, s - seq); + return gpio_sequence(port, seq, s - seq); } -static int gpio_bl_exit(serial_t *serial, const char *seq) +static int gpio_bl_exit(struct port_interface *port, const char *seq) { char *s; @@ -195,21 +197,23 @@ static int gpio_bl_exit(serial_t *serial, const char *seq) if (s == NULL || s[1] == '\0') return 1; - return gpio_sequence(serial, s + 1, strlen(s + 1)); + return gpio_sequence(port, s + 1, strlen(s + 1)); } -int init_bl_entry(serial_t *serial, const char *seq) +int init_bl_entry(struct port_interface *port, const char *seq) { if (seq) - return gpio_bl_entry(serial, seq); + return gpio_bl_entry(port, seq); return 1; } -int init_bl_exit(stm32_t *stm, serial_t *serial, const char *seq) +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) { if (seq) - return gpio_bl_exit(serial, seq); + return gpio_bl_exit(port, seq); - return stm32_reset_device(stm); + if (stm32_reset_device(stm) != STM32_ERR_OK) + return 0; + return 1; } @@ -22,10 +22,10 @@ #ifndef _INIT_H #define _INIT_H -#include "serial.h" #include "stm32.h" +#include "port.h" -int init_bl_entry(serial_t *serial, const char *seq); -int init_bl_exit(stm32_t *stm, serial_t *serial, const char *seq); +int init_bl_entry(struct port_interface *port, const char *seq); +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq); #endif @@ -33,38 +33,45 @@ #include "serial.h" #include "stm32.h" #include "parsers/parser.h" +#include "port.h" #include "parsers/binary.h" #include "parsers/hex.h" -#define VERSION "0.3" +#define VERSION "0.4" /* device globals */ -serial_t *serial = NULL; stm32_t *stm = NULL; void *p_st = NULL; parser_t *parser = NULL; /* settings */ -char *device = NULL; -serial_baud_t baudRate = SERIAL_BAUD_57600; -char *serial_mode = "8e1"; +struct port_options port_opts = { + .device = NULL, + .baudRate = SERIAL_BAUD_57600, + .serial_mode = "8e1", + .bus_addr = 0, + .rx_frame_max = STM32_MAX_RX_FRAME, + .tx_frame_max = STM32_MAX_TX_FRAME, +}; int rd = 0; int wr = 0; int wu = 0; int rp = 0; int ur = 0; int eraseOnly = 0; +int crc = 0; int npages = 0; int spage = 0; +int no_erase = 0; char verify = 0; int retry = 10; char exec_flag = 0; uint32_t execute = 0; char init_flag = 1; char force_binary = 0; -char reset_flag = 1; +char reset_flag = 0; char *filename; char *gpio_seq = NULL; uint32_t start_addr = 0; @@ -74,8 +81,42 @@ uint32_t readwrite_len = 0; int parse_options(int argc, char *argv[]); void show_help(char *name); +static int is_addr_in_ram(uint32_t addr) +{ + return addr >= stm->dev->ram_start && addr < stm->dev->ram_end; +} + +static int is_addr_in_flash(uint32_t addr) +{ + return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; +} + +static int flash_addr_to_page_floor(uint32_t addr) +{ + if (!is_addr_in_flash(addr)) + return 0; + + return (addr - stm->dev->fl_start) / stm->dev->fl_ps; +} + +static int flash_addr_to_page_ceil(uint32_t addr) +{ + if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) + return 0; + + return (addr + stm->dev->fl_ps - 1 - stm->dev->fl_start) + / stm->dev->fl_ps; +} + +static uint32_t flash_page_to_addr(int page) +{ + return stm->dev->fl_start + page * stm->dev->fl_ps; +} + int main(int argc, char* argv[]) { + struct port_interface *port = NULL; int ret = 1; + stm32_err_t s_err; parser_err_t perr; FILE *diag = stdout; @@ -134,31 +175,23 @@ int main(int argc, char* argv[]) { } } - serial = serial_open(device); - if (!serial) { - fprintf(stderr, "Failed to open serial port: "); - perror(device); + if (port_open(&port_opts, &port) != PORT_ERR_OK) { + fprintf(stderr, "Failed to open port: %s\n", port_opts.device); goto close; } - if (serial_setup( - serial, - baudRate, - serial_get_bits(serial_mode), - serial_get_parity(serial_mode), - serial_get_stopbit(serial_mode) - ) != SERIAL_ERR_OK) { - perror(device); + fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); + if (init_flag && init_bl_entry(port, gpio_seq) == 0) + goto close; + stm = stm32_init(port, init_flag); + if (!stm) goto close; - } - - fprintf(diag, "Serial Config: %s\n", serial_get_setup_str(serial)); - if (init_flag && init_bl_entry(serial, gpio_seq) == 0) goto close; - if (!(stm = stm32_init(serial, init_flag))) goto close; fprintf(diag, "Version : 0x%02x\n", stm->bl_version); - fprintf(diag, "Option 1 : 0x%02x\n", stm->option1); - fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); + if (port->flags & PORT_GVR_ETX) { + fprintf(diag, "Option 1 : 0x%02x\n", stm->option1); + fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); + } fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name); fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); fprintf(diag, "- Flash : %dKiB (sector size: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps); @@ -169,38 +202,84 @@ int main(int argc, char* argv[]) { uint32_t addr, start, end; unsigned int len; int failed = 0; + int first_page, num_pages; + + /* + * Cleanup addresses: + * + * Starting from options + * start_addr, readwrite_len, spage, npages + * and using device memory size, compute + * start, end, first_page, num_pages + */ + if (start_addr || readwrite_len) { + start = start_addr; + + if (is_addr_in_flash(start)) + end = stm->dev->fl_end; + else { + no_erase = 1; + if (is_addr_in_ram(start)) + end = stm->dev->ram_end; + else + end = start + sizeof(uint32_t); + } - if (rd) { - fprintf(diag, "\n"); + if (readwrite_len && (end > start + readwrite_len)) + end = start + readwrite_len; - if ((perr = parser->open(p_st, filename, 1)) != PARSER_ERR_OK) { - fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); - if (perr == PARSER_ERR_SYSTEM) perror(filename); + first_page = flash_addr_to_page_floor(start); + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + else + num_pages = flash_addr_to_page_ceil(end) - first_page; + } else if (!spage && !npages) { + start = stm->dev->fl_start; + end = stm->dev->fl_end; + first_page = 0; + num_pages = 0xff; /* mass erase */ + } else { + first_page = spage; + start = flash_page_to_addr(first_page); + if (start > stm->dev->fl_end) { + fprintf(stderr, "Address range exceeds flash size.\n"); goto close; } - if (start_addr || readwrite_len) { - start = start_addr; - if (readwrite_len) - end = start_addr + readwrite_len; - else + if (npages) { + num_pages = npages; + end = flash_page_to_addr(first_page + num_pages); + if (end > stm->dev->fl_end) end = stm->dev->fl_end; } else { - start = stm->dev->fl_start + (spage * stm->dev->fl_ps); end = stm->dev->fl_end; + num_pages = flash_addr_to_page_ceil(end) - first_page; } - addr = start; - if (start < stm->dev->fl_start || end > stm->dev->fl_end) { - fprintf(stderr, "Specified start & length are invalid\n"); + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + } + + if (rd) { + unsigned int max_len = port_opts.rx_frame_max; + + fprintf(diag, "Memory read\n"); + + perr = parser->open(p_st, filename, 1); + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) + perror(filename); goto close; } fflush(diag); + addr = start; while(addr < end) { uint32_t left = end - addr; - len = sizeof(buffer) > left ? left : sizeof(buffer); - if (!stm32_read_memory(stm, addr, buffer, len)) { + len = max_len > left ? left : max_len; + s_err = stm32_read_memory(stm, addr, buffer, len); + if (s_err != STM32_ERR_OK) { fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr); goto close; } @@ -236,29 +315,21 @@ int main(int argc, char* argv[]) { } else if (eraseOnly) { ret = 0; fprintf(stdout, "Erasing flash\n"); - if (start_addr || readwrite_len) { - if ((start_addr % stm->dev->fl_ps) != 0 - || (readwrite_len % stm->dev->fl_ps) != 0) { - fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); - ret = 1; - goto close; - } - spage = (start_addr - stm->dev->fl_start) / stm->dev->fl_ps; - if (readwrite_len) - npages = readwrite_len / stm->dev->fl_ps; - else - npages = (stm->dev->fl_end - stm->dev->fl_start) / stm->dev->fl_ps; - } - if (!spage && !npages) - npages = 0xff; /* mass erase */ + if (num_pages != 0xff && + (start != flash_page_to_addr(first_page) + || end != flash_page_to_addr(first_page + num_pages))) { + fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); + ret = 1; + goto close; + } - if (!stm32_erase_memory(stm, spage, npages)) { + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { fprintf(stderr, "Failed to erase memory\n"); ret = 1; goto close; } - } else if (wu) { fprintf(diag, "Write-unprotecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ @@ -267,49 +338,25 @@ int main(int argc, char* argv[]) { fprintf(diag, "Done.\n"); } else if (wr) { - fprintf(diag, "\n"); + fprintf(diag, "Write to memory\n"); off_t offset = 0; ssize_t r; unsigned int size; + unsigned int max_wlen, max_rlen; + + max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */ + max_wlen &= ~3; /* 32 bit aligned */ + + max_rlen = port_opts.rx_frame_max; + max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; /* Assume data from stdin is whole device */ - if (filename[0] == '-') - size = stm->dev->fl_end - stm->dev->fl_start; + if (filename[0] == '-' && filename[1] == '\0') + size = end - start; else size = parser->size(p_st); - if (start_addr || readwrite_len) { - start = start_addr; - spage = (start_addr - stm->dev->fl_start) / stm->dev->fl_ps; - if (readwrite_len) { - end = start_addr + readwrite_len; - npages = (end - stm->dev->fl_start + stm->dev->fl_ps - 1) / stm->dev->fl_ps - spage; - } else { - end = stm->dev->fl_end; - if (spage) - npages = (end - stm->dev->fl_start) / stm->dev->fl_ps - spage; - else - npages = 0xff; /* mass erase */ - } - } else if (!spage && !npages) { - start = stm->dev->fl_start; - end = stm->dev->fl_end; - npages = 0xff; /* mass erase */ - } else { - start = stm->dev->fl_start + (spage * stm->dev->fl_ps); - if (npages) - end = start + npages * stm->dev->fl_ps; - else - end = stm->dev->fl_end; - } - addr = start; - - if (start < stm->dev->fl_start || end > stm->dev->fl_end) { - fprintf(stderr, "Specified start & length are invalid\n"); - goto close; - } - // TODO: It is possible to write to non-page boundaries, by reading out flash // from partial pages and combining with the input data // if ((start % stm->dev->fl_ps) != 0 || (end % stm->dev->fl_ps) != 0) { @@ -319,15 +366,20 @@ int main(int argc, char* argv[]) { // TODO: If writes are not page aligned, we should probably read out existing flash // contents first, so it can be preserved and combined with new data - if (!stm32_erase_memory(stm, spage, npages)) { - fprintf(stderr, "Failed to erase memory\n"); - goto close; + if (!no_erase && num_pages) { + fprintf(diag, "Erasing memory\n"); + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + goto close; + } } fflush(diag); + addr = start; while(addr < end && offset < size) { uint32_t left = end - addr; - len = sizeof(buffer) > left ? left : sizeof(buffer); + len = max_wlen > left ? left : max_wlen; len = len > size - offset ? size - offset : len; if (parser->read(p_st, buffer, &len) != PARSER_ERR_OK) @@ -343,16 +395,26 @@ int main(int argc, char* argv[]) { } again: - if (!stm32_write_memory(stm, addr, buffer, len)) { + s_err = stm32_write_memory(stm, addr, buffer, len); + if (s_err != STM32_ERR_OK) { fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr); goto close; } if (verify) { uint8_t compare[len]; - if (!stm32_read_memory(stm, addr, compare, len)) { - fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr); - goto close; + unsigned int offset, rlen; + + offset = 0; + while (offset < len) { + rlen = len - offset; + rlen = rlen < max_rlen ? rlen : max_rlen; + s_err = stm32_read_memory(stm, addr + offset, compare + offset, rlen); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset); + goto close; + } + offset += rlen; } for(r = 0; r < len; ++r) @@ -388,6 +450,20 @@ int main(int argc, char* argv[]) { fprintf(diag, "Done.\n"); ret = 0; goto close; + } else if (crc) { + uint32_t crc_val = 0; + + fprintf(diag, "CRC computation\n"); + + s_err = stm32_crc_wrapper(stm, start, end - start, &crc_val); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read CRC\n"); + goto close; + } + fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end, + crc_val); + ret = 0; + goto close; } else ret = 0; @@ -398,7 +474,7 @@ close: fprintf(diag, "\nStarting execution at address 0x%08x... ", execute); fflush(diag); - if (stm32_go(stm, execute)) { + if (stm32_go(stm, execute) == STM32_ERR_OK) { reset_flag = 0; fprintf(diag, "done.\n"); } else @@ -408,29 +484,38 @@ close: if (stm && reset_flag) { fprintf(diag, "\nResetting device... "); fflush(diag); - if (init_bl_exit(stm, serial, gpio_seq)) + if (init_bl_exit(stm, port, gpio_seq)) fprintf(diag, "done.\n"); else fprintf(diag, "failed.\n"); } if (p_st ) parser->close(p_st); if (stm ) stm32_close (stm); - if (serial) serial_close (serial); + if (port) + port->close(port); fprintf(diag, "\n"); return ret; } -int parse_options(int argc, char *argv[]) { +int parse_options(int argc, char *argv[]) +{ int c; - while((c = getopt(argc, argv, "b:m:r:w:e:vn:g:jkfchuos:S:i:")) != -1) { + char *pLen; + + while ((c = getopt(argc, argv, "a:b:m:r:w:e:vn:g:jkfcChuos:S:F:i:R")) != -1) { switch(c) { + case 'a': + port_opts.bus_addr = strtoul(optarg, NULL, 0); + break; + case 'b': - baudRate = serial_get_baud(strtoul(optarg, NULL, 0)); - if (baudRate == SERIAL_BAUD_INVALID) { + port_opts.baudRate = serial_get_baud(strtoul(optarg, NULL, 0)); + if (port_opts.baudRate == SERIAL_BAUD_INVALID) { + serial_baud_t baudrate; fprintf(stderr, "Invalid baud rate, valid options are:\n"); - for(baudRate = SERIAL_BAUD_1200; baudRate != SERIAL_BAUD_INVALID; ++baudRate) - fprintf(stderr, " %d\n", serial_get_baud_int(baudRate)); + for (baudrate = SERIAL_BAUD_1200; baudrate != SERIAL_BAUD_INVALID; ++baudrate) + fprintf(stderr, " %d\n", serial_get_baud_int(baudrate)); return 1; } break; @@ -443,7 +528,7 @@ int parse_options(int argc, char *argv[]) { fprintf(stderr, "Invalid serial mode\n"); return 1; } - serial_mode = optarg; + port_opts.serial_mode = optarg; break; case 'r': @@ -469,6 +554,8 @@ int parse_options(int argc, char *argv[]) { fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); return 1; } + if (!npages) + no_erase = 1; break; case 'u': wu = 1; @@ -530,7 +617,6 @@ int parse_options(int argc, char *argv[]) { fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); return 1; } else { - char *pLen; start_addr = strtoul(optarg, &pLen, 0); if (*pLen == ':') { pLen++; @@ -542,6 +628,36 @@ int parse_options(int argc, char *argv[]) { } } break; + case 'F': + port_opts.rx_frame_max = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + port_opts.tx_frame_max = strtoul(pLen, NULL, 0); + } + if (port_opts.rx_frame_max < 0 + || port_opts.tx_frame_max < 0) { + fprintf(stderr, "ERROR: Invalid negative value for option -F\n"); + return 1; + } + if (port_opts.rx_frame_max == 0) + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + if (port_opts.tx_frame_max == 0) + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + if (port_opts.rx_frame_max < 20 + || port_opts.tx_frame_max < 5) { + fprintf(stderr, "ERROR: current code cannot work with small frames.\n"); + fprintf(stderr, "min(RX) = 20, min(TX) = 5\n"); + return 1; + } + if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) { + fprintf(stderr, "WARNING: Ignore RX length in option -F\n"); + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + } + if (port_opts.tx_frame_max > STM32_MAX_TX_FRAME) { + fprintf(stderr, "WARNING: Ignore TX length in option -F\n"); + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + } + break; case 'f': force_binary = 1; break; @@ -557,19 +673,27 @@ int parse_options(int argc, char *argv[]) { case 'i': gpio_seq = optarg; break; + + case 'R': + reset_flag = 1; + break; + + case 'C': + crc = 1; + break; } } for (c = optind; c < argc; ++c) { - if (device) { + if (port_opts.device) { fprintf(stderr, "ERROR: Invalid parameter specified\n"); show_help(argv[0]); return 1; } - device = argv[c]; + port_opts.device = argv[c]; } - if (device == NULL) { + if (port_opts.device == NULL) { fprintf(stderr, "ERROR: Device not specified\n"); show_help(argv[0]); return 1; @@ -586,11 +710,13 @@ int parse_options(int argc, char *argv[]) { void show_help(char *name) { fprintf(stderr, - "Usage: %s [-bvngfhc] [-[rw] filename] /dev/ttyS0\n" + "Usage: %s [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]\n" + " -a bus_address Bus address (e.g. for I2C port)\n" " -b rate Baud rate (default 57600)\n" " -m mode Serial port mode (default 8e1)\n" " -r filename Read flash to file (or - stdout)\n" " -w filename Write flash from file (or - stdout)\n" + " -C Compute CRC of flash content\n" " -u Disable the flash write-protection\n" " -j Enable the flash read-protection\n" " -k Disable the flash read-protection\n" @@ -601,6 +727,7 @@ void show_help(char *name) { " -g address Start execution at specified address (0 = flash start)\n" " -S address[:length] Specify start address and optionally length for\n" " read/write/erase operations\n" + " -F RX_length[:TX_length] Specify the max length of RX and TX frame\n" " -s start_page Flash at specified page (0 = flash start)\n" " -f Force binary parser\n" " -h Show this help\n" @@ -610,10 +737,13 @@ void show_help(char *name) { " -i GPIO_string GPIO sequence to enter/exit bootloader mode\n" " GPIO_string=[entry_seq][:[exit_seq]]\n" " sequence=[-]n[,sequence]\n" + " -R Reset device at exit.\n" "\n" "Examples:\n" " Get device information:\n" " %s /dev/ttyS0\n" + " or:\n" + " %s /dev/i2c-0\n" "\n" " Write with verify and then start execution:\n" " %s -w filename -v -g 0x0 /dev/ttyS0\n" @@ -637,6 +767,7 @@ void show_help(char *name) { name, name, name, + name, name ); } diff --git a/parsers/Android.mk b/parsers/Android.mk new file mode 100644 index 0000000..afec18c --- /dev/null +++ b/parsers/Android.mk @@ -0,0 +1,6 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libparsers +LOCAL_SRC_FILES := binary.c hex.c +include $(BUILD_STATIC_LIBRARY) diff --git a/parsers/hex.c b/parsers/hex.c index 76a8d64..3baf856 100644 --- a/parsers/hex.c +++ b/parsers/hex.c @@ -64,7 +64,7 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { char buffer[9]; unsigned int reclen, address, type; - uint8_t *record; + uint8_t *record = NULL; /* get the reclen, address, and type */ buffer[8] = 0; @@ -121,7 +121,11 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { switch(type) { case 0: - record[i] = c; + if (record != NULL) { + record[i] = c; + } else { + return PARSER_ERR_INVALID_FILE; + } break; case 2: @@ -0,0 +1,59 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com> + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <stdint.h> +#include <stdio.h> + +#include "serial.h" +#include "port.h" + + +extern struct port_interface port_serial; +extern struct port_interface port_i2c; + +static struct port_interface *ports[] = { + &port_serial, + &port_i2c, + NULL, +}; + + +port_err_t port_open(struct port_options *ops, struct port_interface **outport) +{ + int ret; + static struct port_interface **port; + + for (port = ports; *port; port++) { + ret = (*port)->open(*port, ops); + if (ret == PORT_ERR_NODEV) + continue; + if (ret == PORT_ERR_OK) + break; + fprintf(stderr, "Error probing interface \"%s\"\n", + (*port)->name); + } + if (*port == NULL) { + fprintf(stderr, "Cannot handle device \"%s\"\n", + ops->device); + return PORT_ERR_UNKNOWN; + } + + *outport = *port; + return PORT_ERR_OK; +} @@ -0,0 +1,75 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 Antonio Borneo <borneo.antonio@gmail.com> + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#ifndef _H_PORT +#define _H_PORT + +typedef enum { + PORT_ERR_OK = 0, + PORT_ERR_NODEV, /* No such device */ + PORT_ERR_TIMEDOUT, /* Operation timed out */ + PORT_ERR_UNKNOWN, +} port_err_t; + +/* flags */ +#define PORT_BYTE (1 << 0) /* byte (not frame) oriented */ +#define PORT_GVR_ETX (1 << 1) /* cmd GVR returns protection status */ +#define PORT_CMD_INIT (1 << 2) /* use INIT cmd to autodetect speed */ +#define PORT_RETRY (1 << 3) /* allowed read() retry after timeout */ +#define PORT_STRETCH_W (1 << 4) /* warning for no-stretching commands */ + +/* all options and flags used to open and configure an interface */ +struct port_options { + const char *device; + serial_baud_t baudRate; + const char *serial_mode; + int bus_addr; + int rx_frame_max; + int tx_frame_max; +}; + +/* + * Specify the length of reply for command GET + * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time + * consuming try-fail-timeout-retry operation. + * On byte-oriented protocols, i.e. UART, this information would be skipped + * after read the first byte, so not needed. + */ +struct varlen_cmd { + uint8_t version; + uint8_t length; +}; + +struct port_interface { + const char *name; + unsigned flags; + port_err_t (*open)(struct port_interface *port, struct port_options *ops); + port_err_t (*close)(struct port_interface *port); + port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level); + const char *(*get_cfg_str)(struct port_interface *port); + struct varlen_cmd *cmd_get_reply; + void *private; +}; + +port_err_t port_open(struct port_options *ops, struct port_interface **outport); + +#endif diff --git a/protocol.txt b/protocol.txt new file mode 100644 index 0000000..0391099 --- /dev/null +++ b/protocol.txt @@ -0,0 +1,19 @@ +The communication protocol used by ST bootloader is documented in following ST +application notes, depending on communication port. + +In current version of stm32flash are supported only UART and I2C ports. + +* AN3154: CAN protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf + +* AN3155: USART protocol used in the STM32(TM) bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf + +* AN4221: I2C protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf + +* AN4286: SPI protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf + +Boot mode selection for STM32 is documented in ST application note AN2606, available in ST website: + http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf @@ -21,8 +21,6 @@ #ifndef _SERIAL_H #define _SERIAL_H -#include <stdint.h> - typedef struct serial serial_t; typedef enum { @@ -74,40 +72,19 @@ typedef enum { } serial_stopbit_t; typedef enum { - SERIAL_ERR_OK = 0, - - SERIAL_ERR_SYSTEM, - SERIAL_ERR_UNKNOWN, - SERIAL_ERR_INVALID_BAUD, - SERIAL_ERR_INVALID_BITS, - SERIAL_ERR_INVALID_PARITY, - SERIAL_ERR_INVALID_STOPBIT, - SERIAL_ERR_NODATA -} serial_err_t; - -typedef enum { GPIO_RTS = 1, GPIO_DTR, GPIO_BRK, } serial_gpio_t; -serial_t* serial_open (const char *device); -void serial_close(serial_t *h); -void serial_flush(const serial_t *h); -serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bits_t bits, const serial_parity_t parity, const serial_stopbit_t stopbit); -serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len); -serial_err_t serial_read (const serial_t *h, void *buffer, unsigned int len); -const char* serial_get_setup_str(const serial_t *h); -serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level); - /* common helper functions */ -serial_baud_t serial_get_baud (const unsigned int baud); -unsigned int serial_get_baud_int (const serial_baud_t baud); -serial_bits_t serial_get_bits (const char *mode); -unsigned int serial_get_bits_int (const serial_bits_t bits); -serial_parity_t serial_get_parity (const char *mode); -char serial_get_parity_str (const serial_parity_t parity); -serial_stopbit_t serial_get_stopbit (const char *mode); +serial_baud_t serial_get_baud(const unsigned int baud); +unsigned int serial_get_baud_int(const serial_baud_t baud); +serial_bits_t serial_get_bits(const char *mode); +unsigned int serial_get_bits_int(const serial_bits_t bits); +serial_parity_t serial_get_parity(const char *mode); +char serial_get_parity_str(const serial_parity_t parity); +serial_stopbit_t serial_get_stopbit(const char *mode); unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit); #endif diff --git a/serial_posix.c b/serial_posix.c index 5a23991..46b8a94 100644 --- a/serial_posix.c +++ b/serial_posix.c @@ -17,30 +17,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#include <stdlib.h> #include <fcntl.h> -#include <unistd.h> -#include <termios.h> +#include <stdint.h> #include <stdio.h> -#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> #include <sys/ioctl.h> #include "serial.h" +#include "port.h" struct serial { - int fd; - struct termios oldtio; - struct termios newtio; - - char configured; - serial_baud_t baud; - serial_bits_t bits; - serial_parity_t parity; - serial_stopbit_t stopbit; + int fd; + struct termios oldtio; + struct termios newtio; + char setup_str[11]; }; -serial_t* serial_open(const char *device) { +static serial_t *serial_open(const char *device) +{ serial_t *h = calloc(sizeof(serial_t), 1); h->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); @@ -56,50 +53,52 @@ serial_t* serial_open(const char *device) { return h; } -void serial_close(serial_t *h) { - assert(h && h->fd > -1); +static void serial_flush(const serial_t *h) +{ + tcflush(h->fd, TCIFLUSH); +} +static void serial_close(serial_t *h) +{ serial_flush(h); tcsetattr(h->fd, TCSANOW, &h->oldtio); close(h->fd); free(h); } -void serial_flush(const serial_t *h) { - assert(h && h->fd > -1); - tcflush(h->fd, TCIFLUSH); -} +static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + speed_t port_baud; + tcflag_t port_bits; + tcflag_t port_parity; + tcflag_t port_stop; + struct termios settings; -serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bits_t bits, const serial_parity_t parity, const serial_stopbit_t stopbit) { - assert(h && h->fd > -1); - - speed_t port_baud; - tcflag_t port_bits; - tcflag_t port_parity; - tcflag_t port_stop; - - switch(baud) { - case SERIAL_BAUD_1200 : port_baud = B1200 ; break; - case SERIAL_BAUD_1800 : port_baud = B1800 ; break; - case SERIAL_BAUD_2400 : port_baud = B2400 ; break; - case SERIAL_BAUD_4800 : port_baud = B4800 ; break; - case SERIAL_BAUD_9600 : port_baud = B9600 ; break; - case SERIAL_BAUD_19200 : port_baud = B19200 ; break; - case SERIAL_BAUD_38400 : port_baud = B38400 ; break; - case SERIAL_BAUD_57600 : port_baud = B57600 ; break; - case SERIAL_BAUD_115200: port_baud = B115200; break; - case SERIAL_BAUD_230400: port_baud = B230400; break; + switch (baud) { + case SERIAL_BAUD_1200: port_baud = B1200; break; + case SERIAL_BAUD_1800: port_baud = B1800; break; + case SERIAL_BAUD_2400: port_baud = B2400; break; + case SERIAL_BAUD_4800: port_baud = B4800; break; + case SERIAL_BAUD_9600: port_baud = B9600; break; + case SERIAL_BAUD_19200: port_baud = B19200; break; + case SERIAL_BAUD_38400: port_baud = B38400; break; + case SERIAL_BAUD_57600: port_baud = B57600; break; + case SERIAL_BAUD_115200: port_baud = B115200; break; + case SERIAL_BAUD_230400: port_baud = B230400; break; #ifdef B460800 - case SERIAL_BAUD_460800: port_baud = B460800; break; + case SERIAL_BAUD_460800: port_baud = B460800; break; #endif /* B460800 */ #ifdef B921600 - case SERIAL_BAUD_921600: port_baud = B921600; break; + case SERIAL_BAUD_921600: port_baud = B921600; break; #endif /* B921600 */ #ifdef B500000 - case SERIAL_BAUD_500000: port_baud = B500000; break; + case SERIAL_BAUD_500000: port_baud = B500000; break; #endif /* B500000 */ #ifdef B576000 - case SERIAL_BAUD_576000: port_baud = B576000; break; + case SERIAL_BAUD_576000: port_baud = B576000; break; #endif /* B576000 */ #ifdef B1000000 case SERIAL_BAUD_1000000: port_baud = B1000000; break; @@ -113,45 +112,36 @@ serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bi case SERIAL_BAUD_INVALID: default: - return SERIAL_ERR_INVALID_BAUD; + return PORT_ERR_UNKNOWN; } - switch(bits) { + switch (bits) { case SERIAL_BITS_5: port_bits = CS5; break; case SERIAL_BITS_6: port_bits = CS6; break; case SERIAL_BITS_7: port_bits = CS7; break; case SERIAL_BITS_8: port_bits = CS8; break; default: - return SERIAL_ERR_INVALID_BITS; + return PORT_ERR_UNKNOWN; } - switch(parity) { - case SERIAL_PARITY_NONE: port_parity = 0; break; - case SERIAL_PARITY_EVEN: port_parity = INPCK | PARENB; break; - case SERIAL_PARITY_ODD : port_parity = INPCK | PARENB | PARODD; break; + switch (parity) { + case SERIAL_PARITY_NONE: port_parity = 0; break; + case SERIAL_PARITY_EVEN: port_parity = INPCK | PARENB; break; + case SERIAL_PARITY_ODD: port_parity = INPCK | PARENB | PARODD; break; default: - return SERIAL_ERR_INVALID_PARITY; + return PORT_ERR_UNKNOWN; } - switch(stopbit) { + switch (stopbit) { case SERIAL_STOPBIT_1: port_stop = 0; break; case SERIAL_STOPBIT_2: port_stop = CSTOPB; break; default: - return SERIAL_ERR_INVALID_STOPBIT; + return PORT_ERR_UNKNOWN; } - /* if the port is already configured, no need to do anything */ - if ( - h->configured && - h->baud == baud && - h->bits == bits && - h->parity == parity && - h->stopbit == stopbit - ) return SERIAL_ERR_OK; - /* reset the settings */ #ifndef __sun /* Used by GNU and BSD. Ignore __SVR4 in test. */ cfmakeraw(&h->newtio); @@ -183,111 +173,182 @@ serial_err_t serial_setup(serial_t *h, const serial_baud_t baud, const serial_bi CLOCAL | CREAD; - h->newtio.c_cc[VMIN ] = 0; - h->newtio.c_cc[VTIME] = 180; + h->newtio.c_cc[VMIN] = 0; + h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ /* set the settings */ serial_flush(h); if (tcsetattr(h->fd, TCSANOW, &h->newtio) != 0) - return SERIAL_ERR_SYSTEM; + return PORT_ERR_UNKNOWN; /* confirm they were set */ - struct termios settings; tcgetattr(h->fd, &settings); - if ( - settings.c_iflag != h->newtio.c_iflag || - settings.c_oflag != h->newtio.c_oflag || - settings.c_cflag != h->newtio.c_cflag || - settings.c_lflag != h->newtio.c_lflag - ) return SERIAL_ERR_UNKNOWN; - - h->configured = 1; - h->baud = baud; - h->bits = bits; - h->parity = parity; - h->stopbit = stopbit; - return SERIAL_ERR_OK; + if (settings.c_iflag != h->newtio.c_iflag || + settings.c_oflag != h->newtio.c_oflag || + settings.c_cflag != h->newtio.c_cflag || + settings.c_lflag != h->newtio.c_lflag) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit)); + return PORT_ERR_OK; } -serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len) { - assert(h && h->fd > -1 && h->configured); +static port_err_t serial_posix_open(struct port_interface *port, + struct port_options *ops) +{ + serial_t *h; + + /* 1. check device name match */ + if (strncmp(ops->device, "/dev/tty", strlen("/dev/tty"))) + return PORT_ERR_NODEV; + + /* 2. check options */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } - ssize_t r; - const uint8_t *pos = (const uint8_t*)buffer; + port->private = h; + return PORT_ERR_OK; +} - while(len > 0) { - r = write(h->fd, pos, len); - if (r < 1) return SERIAL_ERR_SYSTEM; +static port_err_t serial_posix_close(struct port_interface *port) +{ + serial_t *h; - len -= r; - pos += r; - } + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; - return SERIAL_ERR_OK; + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; } -serial_err_t serial_read(const serial_t *h, void *buffer, unsigned int len) { - assert(h && h->fd > -1 && h->configured); - +static port_err_t serial_posix_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; ssize_t r; - uint8_t *pos = (uint8_t*)buffer; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; - while(len > 0) { - r = read(h->fd, pos, len); - if (r == 0) return SERIAL_ERR_NODATA; - else if (r < 0) return SERIAL_ERR_SYSTEM; + while (nbyte) { + r = read(h->fd, pos, nbyte); + if (r == 0) + return PORT_ERR_TIMEDOUT; + if (r < 0) + return PORT_ERR_UNKNOWN; - len -= r; + nbyte -= r; pos += r; } - - return SERIAL_ERR_OK; + return PORT_ERR_OK; } -const char* serial_get_setup_str(const serial_t *h) { - static char str[11]; - if (!h->configured) - snprintf(str, sizeof(str), "INVALID"); - else - snprintf(str, sizeof(str), "%u %d%c%d", - serial_get_baud_int (h->baud ), - serial_get_bits_int (h->bits ), - serial_get_parity_str (h->parity ), - serial_get_stopbit_int(h->stopbit) - ); - - return str; +static port_err_t serial_posix_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + const uint8_t *pos = (const uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = write(h->fd, pos, nbyte); + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; } -serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level) { +static port_err_t serial_posix_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; int bit, lines; - switch(n) { - case GPIO_RTS: - bit = TIOCM_RTS; - break; + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; - case GPIO_DTR: - bit = TIOCM_DTR; - break; + switch (n) { + case GPIO_RTS: + bit = TIOCM_RTS; + break; - case GPIO_BRK: - if (level == 0) - return SERIAL_ERR_OK; - if (tcsendbreak(h->fd, 1)) - return SERIAL_ERR_SYSTEM; - return SERIAL_ERR_OK; + case GPIO_DTR: + bit = TIOCM_DTR; + break; - default: - return SERIAL_ERR_NODATA; + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (tcsendbreak(h->fd, 1)) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; } /* handle RTS/DTR */ if (ioctl(h->fd, TIOCMGET, &lines)) - return SERIAL_ERR_SYSTEM; + return PORT_ERR_UNKNOWN; lines = level ? lines | bit : lines & ~bit; if (ioctl(h->fd, TIOCMSET, &lines)) - return SERIAL_ERR_SYSTEM; + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_posix_get_cfg_str(struct port_interface *port) +{ + serial_t *h; - return SERIAL_ERR_OK; + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; } + +struct port_interface port_serial = { + .name = "serial_posix", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_posix_open, + .close = serial_posix_close, + .read = serial_posix_read, + .write = serial_posix_write, + .gpio = serial_posix_gpio, + .get_cfg_str = serial_posix_get_cfg_str, +}; diff --git a/serial_w32.c b/serial_w32.c index d5683ae..ceac3a3 100644 --- a/serial_w32.c +++ b/serial_w32.c @@ -18,55 +18,52 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#include <stdlib.h> #include <fcntl.h> -#include <unistd.h> +#include <stdint.h> #include <stdio.h> -#include <assert.h> - +#include <stdlib.h> +#include <string.h> +#include <unistd.h> #include <windows.h> +#include <ctype.h> #include "serial.h" +#include "port.h" struct serial { HANDLE fd; DCB oldtio; DCB newtio; - - char configured; - serial_baud_t baud; - serial_bits_t bits; - serial_parity_t parity; - serial_stopbit_t stopbit; + char setup_str[11]; }; -serial_t* serial_open(const char *device) +static serial_t *serial_open(const char *device) { serial_t *h = calloc(sizeof(serial_t), 1); + char *devName; - COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 3000, 0, 0}; + /* timeout in ms */ + COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 500, 0, 0}; /* Fix the device name if required */ - char *devName; if (strlen(device) > 4 && device[0] != '\\') { devName = calloc(1, strlen(device) + 5); sprintf(devName, "\\\\.\\%s", device); } else { - devName = device; + devName = (char *)device; } /* Create file handle for port */ - h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, - 0, /* Exclusive access */ - NULL, /* No security */ - OPEN_EXISTING, - 0, //FILE_FLAG_OVERLAPPED, - NULL); + h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, + 0, /* Exclusive access */ + NULL, /* No security */ + OPEN_EXISTING, + 0, /* No overlap */ + NULL); if (devName != device) free(devName); - + if (h->fd == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) fprintf(stderr, "File not found: %s\n", device); @@ -82,99 +79,85 @@ serial_t* serial_open(const char *device) GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */ GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */ - //PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); + /* PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); */ return h; } -void serial_close(serial_t *h) +static void serial_flush(const serial_t *h) { - assert(h && h->fd != INVALID_HANDLE_VALUE); + /* We shouldn't need to flush in non-overlapping (blocking) mode */ + /* tcflush(h->fd, TCIFLUSH); */ +} +static void serial_close(serial_t *h) +{ serial_flush(h); SetCommState(h->fd, &h->oldtio); CloseHandle(h->fd); free(h); } -void serial_flush(const serial_t *h) -{ - assert(h && (h->fd != INVALID_HANDLE_VALUE)); - /* We shouldn't need to flush in non-overlapping (blocking) mode */ - //tcflush(h->fd, TCIFLUSH); -} - -serial_err_t serial_setup(serial_t *h, - const serial_baud_t baud, - const serial_bits_t bits, - const serial_parity_t parity, - const serial_stopbit_t stopbit) +static port_err_t serial_setup(serial_t *h, + const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) { - assert(h && h->fd != INVALID_HANDLE_VALUE); - - switch(baud) { - case SERIAL_BAUD_1200 : h->newtio.BaudRate = CBR_1200 ; break; - //case SERIAL_BAUD_1800 : h->newtio.BaudRate = CBR_1800 ; break; - case SERIAL_BAUD_2400 : h->newtio.BaudRate = CBR_2400 ; break; - case SERIAL_BAUD_4800 : h->newtio.BaudRate = CBR_4800 ; break; - case SERIAL_BAUD_9600 : h->newtio.BaudRate = CBR_9600 ; break; - case SERIAL_BAUD_19200 : h->newtio.BaudRate = CBR_19200 ; break; - case SERIAL_BAUD_38400 : h->newtio.BaudRate = CBR_38400 ; break; - case SERIAL_BAUD_57600 : h->newtio.BaudRate = CBR_57600 ; break; - case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break; - case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break; - case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break; + switch (baud) { + case SERIAL_BAUD_1200: h->newtio.BaudRate = CBR_1200; break; + /* case SERIAL_BAUD_1800: h->newtio.BaudRate = CBR_1800; break; */ + case SERIAL_BAUD_2400: h->newtio.BaudRate = CBR_2400; break; + case SERIAL_BAUD_4800: h->newtio.BaudRate = CBR_4800; break; + case SERIAL_BAUD_9600: h->newtio.BaudRate = CBR_9600; break; + case SERIAL_BAUD_19200: h->newtio.BaudRate = CBR_19200; break; + case SERIAL_BAUD_38400: h->newtio.BaudRate = CBR_38400; break; + case SERIAL_BAUD_57600: h->newtio.BaudRate = CBR_57600; break; + case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break; + case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break; + case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break; /* These are not defined in WinBase.h and might work or not */ - case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break; - case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break; - case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break; - case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break; - case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break; + case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break; + case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break; + case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break; + case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break; + case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break; case SERIAL_BAUD_1000000: h->newtio.BaudRate = 1000000; break; case SERIAL_BAUD_1500000: h->newtio.BaudRate = 1500000; break; case SERIAL_BAUD_2000000: h->newtio.BaudRate = 2000000; break; - case SERIAL_BAUD_INVALID: + default: - return SERIAL_ERR_INVALID_BAUD; + return PORT_ERR_UNKNOWN; } - switch(bits) { + switch (bits) { case SERIAL_BITS_5: h->newtio.ByteSize = 5; break; case SERIAL_BITS_6: h->newtio.ByteSize = 6; break; case SERIAL_BITS_7: h->newtio.ByteSize = 7; break; case SERIAL_BITS_8: h->newtio.ByteSize = 8; break; default: - return SERIAL_ERR_INVALID_BITS; + return PORT_ERR_UNKNOWN; } - switch(parity) { + switch (parity) { case SERIAL_PARITY_NONE: h->newtio.Parity = NOPARITY; break; case SERIAL_PARITY_EVEN: h->newtio.Parity = EVENPARITY; break; - case SERIAL_PARITY_ODD : h->newtio.Parity = ODDPARITY; break; + case SERIAL_PARITY_ODD: h->newtio.Parity = ODDPARITY; break; default: - return SERIAL_ERR_INVALID_PARITY; + return PORT_ERR_UNKNOWN; } - switch(stopbit) { + switch (stopbit) { case SERIAL_STOPBIT_1: h->newtio.StopBits = ONESTOPBIT; break; case SERIAL_STOPBIT_2: h->newtio.StopBits = TWOSTOPBITS; break; default: - return SERIAL_ERR_INVALID_STOPBIT; + return PORT_ERR_UNKNOWN; } - /* if the port is already configured, no need to do anything */ - if ( - h->configured && - h->baud == baud && - h->bits == bits && - h->parity == parity && - h->stopbit == stopbit - ) return SERIAL_ERR_OK; - /* reset the settings */ h->newtio.fOutxCtsFlow = FALSE; h->newtio.fOutxDsrFlow = FALSE; @@ -186,99 +169,173 @@ serial_err_t serial_setup(serial_t *h, /* set the settings */ serial_flush(h); if (!SetCommState(h->fd, &h->newtio)) - return SERIAL_ERR_SYSTEM; - - h->configured = 1; - h->baud = baud; - h->bits = bits; - h->parity = parity; - h->stopbit = stopbit; - return SERIAL_ERR_OK; + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit) + ); + return PORT_ERR_OK; } -serial_err_t serial_write(const serial_t *h, const void *buffer, unsigned int len) +static port_err_t serial_w32_open(struct port_interface *port, + struct port_options *ops) { - assert(h && (h->fd != INVALID_HANDLE_VALUE) && h->configured); + serial_t *h; + + /* 1. check device name match */ + if (!(strlen(ops->device) == 4 + && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) + && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM")) + && isdigit(ops->device[strlen("\\\\.\\COM")]))) + return PORT_ERR_NODEV; + + /* 2. check options */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } - DWORD r; - uint8_t *pos = (uint8_t*)buffer; + port->private = h; + return PORT_ERR_OK; +} - while(len > 0) { - if(!WriteFile(h->fd, pos, len, &r, NULL)) - return SERIAL_ERR_SYSTEM; - if (r < 1) return SERIAL_ERR_SYSTEM; +static port_err_t serial_w32_close(struct port_interface *port) +{ + serial_t *h; - len -= r; - pos += r; - } + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; - return SERIAL_ERR_OK; + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; } -serial_err_t serial_read(const serial_t *h, void *buffer, unsigned int len) +static port_err_t serial_w32_read(struct port_interface *port, void *buf, + size_t nbyte) { - assert(h && (h->fd != INVALID_HANDLE_VALUE) && h->configured); - + serial_t *h; DWORD r; - uint8_t *pos = (uint8_t*)buffer; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; - while(len > 0) { - ReadFile(h->fd, pos, len, &r, NULL); - if (r == 0) return SERIAL_ERR_NODATA; - else if (r < 0) return SERIAL_ERR_SYSTEM; + while (nbyte) { + ReadFile(h->fd, pos, nbyte, &r, NULL); + if (r == 0) + return PORT_ERR_TIMEDOUT; + if (r < 0) + return PORT_ERR_UNKNOWN; - len -= r; + nbyte -= r; pos += r; } - - return SERIAL_ERR_OK; + return PORT_ERR_OK; } -const char* serial_get_setup_str(const serial_t *h) +static port_err_t serial_w32_write(struct port_interface *port, void *buf, + size_t nbyte) { - static char str[11]; - if (!h->configured) - snprintf(str, sizeof(str), "INVALID"); - else - snprintf(str, sizeof(str), "%u %d%c%d", - serial_get_baud_int (h->baud ), - serial_get_bits_int (h->bits ), - serial_get_parity_str (h->parity ), - serial_get_stopbit_int(h->stopbit) - ); - - return str; + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + if (!WriteFile(h->fd, pos, nbyte, &r, NULL)) + return PORT_ERR_UNKNOWN; + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; } -serial_err_t serial_gpio (const serial_t *h, serial_gpio_t n, int level) { +static port_err_t serial_w32_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; int bit; - switch(n) { - case GPIO_RTS: - bit = level ? SETRTS : CLRRTS; - break; - - case GPIO_DTR: - bit = level ? SETDTR : CLRDTR; - break; - - case GPIO_BRK: - if (level == 0) - return SERIAL_ERR_OK; - if (EscapeCommFunction(h->fd, SETBREAK) == 0) - return SERIAL_ERR_SYSTEM; - usleep(500000); - if (EscapeCommFunction(h->fd, CLRBREAK) == 0) - return SERIAL_ERR_SYSTEM; - return SERIAL_ERR_OK; - - default: - return SERIAL_ERR_NODATA; - } + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = level ? SETRTS : CLRRTS; + break; + + case GPIO_DTR: + bit = level ? SETDTR : CLRDTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (EscapeCommFunction(h->fd, SETBREAK) == 0) + return PORT_ERR_UNKNOWN; + usleep(500000); + if (EscapeCommFunction(h->fd, CLRBREAK) == 0) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } /* handle RTS/DTR */ if (EscapeCommFunction(h->fd, bit) == 0) - return SERIAL_ERR_SYSTEM; + return PORT_ERR_UNKNOWN; - return SERIAL_ERR_OK; + return PORT_ERR_OK; } + +static const char *serial_w32_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_w32", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_w32_open, + .close = serial_w32_close, + .read = serial_w32_read, + .write = serial_w32_write, + .gpio = serial_w32_gpio, + .get_cfg_str = serial_w32_get_cfg_str, +}; @@ -19,15 +19,20 @@ */ #include <stdlib.h> -#include <assert.h> +#include <stdint.h> #include <stdio.h> #include <string.h> +#include <time.h> +#include <unistd.h> #include "stm32.h" +#include "port.h" #include "utils.h" #define STM32_ACK 0x79 #define STM32_NACK 0x1F +#define STM32_BUSY 0x76 + #define STM32_CMD_INIT 0x7F #define STM32_CMD_GET 0x00 /* get the version and command supported */ #define STM32_CMD_GVR 0x01 /* get version and read protection status */ @@ -35,14 +40,31 @@ #define STM32_CMD_RM 0x11 /* read memory */ #define STM32_CMD_GO 0x21 /* go */ #define STM32_CMD_WM 0x31 /* write memory */ +#define STM32_CMD_WM_NS 0x32 /* no-stretch write memory */ #define STM32_CMD_ER 0x43 /* erase */ #define STM32_CMD_EE 0x44 /* extended erase */ +#define STM32_CMD_EE_NS 0x45 /* extended erase no-stretch */ #define STM32_CMD_WP 0x63 /* write protect */ +#define STM32_CMD_WP_NS 0x64 /* write protect no-stretch */ #define STM32_CMD_UW 0x73 /* write unprotect */ +#define STM32_CMD_UW_NS 0x74 /* write unprotect no-stretch */ #define STM32_CMD_RP 0x82 /* readout protect */ +#define STM32_CMD_RP_NS 0x83 /* readout protect no-stretch */ #define STM32_CMD_UR 0x92 /* readout unprotect */ +#define STM32_CMD_UR_NS 0x93 /* readout unprotect no-stretch */ +#define STM32_CMD_CRC 0xA1 /* compute CRC */ #define STM32_CMD_ERR 0xFF /* not a valid command */ +#define STM32_RESYNC_TIMEOUT 35 /* seconds */ +#define STM32_MASSERASE_TIMEOUT 35 /* seconds */ +#define STM32_SECTERASE_TIMEOUT 5 /* seconds */ +#define STM32_BLKWRITE_TIMEOUT 1 /* seconds */ +#define STM32_WUNPROT_TIMEOUT 1 /* seconds */ +#define STM32_WPROT_TIMEOUT 1 /* seconds */ +#define STM32_RPROT_TIMEOUT 1 /* seconds */ + +#define STM32_CMD_GET_LENGTH 17 /* bytes in the reply */ + struct stm32_cmd { uint8_t get; uint8_t gvr; @@ -55,6 +77,7 @@ struct stm32_cmd { uint8_t uw; uint8_t rp; uint8_t ur; + uint8_t crc; }; /* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0) @@ -72,129 +95,288 @@ static const uint8_t stm_reset_code[] = { static const uint32_t stm_reset_code_length = sizeof(stm_reset_code); -/* Device table, corresponds to the "Bootloader device-dependent parameters" - * table in ST document AN2606. - * Note that the option bytes upper range is inclusive! - */ -const stm32_dev_t devices[] = { - /* F0 */ - {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, - {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, - {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, - /* F1 */ - {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800}, - {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800}, - /* Note that F2 and F4 devices have sectors of different page sizes - and only the first sectors (of one page size) are included here */ - /* F2 */ - {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, - /* F3 */ - {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, - {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, - {0x439, "STM32F302" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, - {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, - /* F4 */ - {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, - /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */ - {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, - {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, - {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, - /* L0 */ - {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, - /* L1 */ - {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, - {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, - {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000}, - {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, - {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, - /* These are not (yet) in AN2606: */ - {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, - {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0}, - {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0}, - {0x0} -}; +extern const stm32_dev_t devices[]; + +static void stm32_warn_stretching(const char *f) +{ + fprintf(stderr, "Attention !!!\n"); + fprintf(stderr, "\tThis %s error could be caused by your I2C\n", f); + fprintf(stderr, "\tcontroller not accepting \"clock stretching\"\n"); + fprintf(stderr, "\tas required by bootloader.\n"); + fprintf(stderr, "\tCheck \"I2C.txt\" in stm32flash source code.\n"); +} + +static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, time_t timeout) +{ + struct port_interface *port = stm->port; + uint8_t byte; + port_err_t p_err; + time_t t0, t1; + + if (!(port->flags & PORT_RETRY)) + timeout = 0; -/* internal functions */ -uint8_t stm32_gen_cs(const uint32_t v); -void stm32_send_byte(const stm32_t *stm, uint8_t byte); -uint8_t stm32_read_byte(const stm32_t *stm); -char stm32_send_command(const stm32_t *stm, const uint8_t cmd); + if (timeout) + time(&t0); + do { + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_TIMEDOUT && timeout) { + time(&t1); + if (t1 < t0 + timeout) + continue; + } + + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to read ACK byte\n"); + return STM32_ERR_UNKNOWN; + } + + if (byte == STM32_ACK) + return STM32_ERR_OK; + if (byte == STM32_NACK) + return STM32_ERR_NACK; + if (byte != STM32_BUSY) { + fprintf(stderr, "Got byte 0x%02x instead of ACK\n", + byte); + return STM32_ERR_UNKNOWN; + } + } while (1); +} -uint8_t stm32_gen_cs(const uint32_t v) { - return ((v & 0xFF000000) >> 24) ^ - ((v & 0x00FF0000) >> 16) ^ - ((v & 0x0000FF00) >> 8) ^ - ((v & 0x000000FF) >> 0); +static stm32_err_t stm32_get_ack(const stm32_t *stm) +{ + return stm32_get_ack_timeout(stm, 0); } -void stm32_send_byte(const stm32_t *stm, uint8_t byte) { - serial_err_t err; - err = serial_write(stm->serial, &byte, 1); - if (err != SERIAL_ERR_OK) { - fprintf(stderr, "Failed to send byte: "); - perror("send_byte"); - exit(1); +static stm32_err_t stm32_send_command_timeout(const stm32_t *stm, + const uint8_t cmd, + time_t timeout) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + uint8_t buf[2]; + + buf[0] = cmd; + buf[1] = cmd ^ 0xFF; + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send command\n"); + return STM32_ERR_UNKNOWN; } + s_err = stm32_get_ack_timeout(stm, timeout); + if (s_err == STM32_ERR_OK) + return STM32_ERR_OK; + if (s_err == STM32_ERR_NACK) + fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd); + else + fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd); + return STM32_ERR_UNKNOWN; } -uint8_t stm32_read_byte(const stm32_t *stm) { - uint8_t byte; - serial_err_t err; - err = serial_read(stm->serial, &byte, 1); - if (err != SERIAL_ERR_OK) { - fprintf(stderr, "Failed to read byte: "); - perror("read_byte"); - exit(1); - } - return byte; +static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) +{ + return stm32_send_command_timeout(stm, cmd, 0); } -char stm32_send_command(const stm32_t *stm, const uint8_t cmd) { - int ret; +/* if we have lost sync, send a wrong command and expect a NACK */ +static stm32_err_t stm32_resync(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t buf[2], ack; + time_t t0, t1; + + time(&t0); + t1 = t0; + + buf[0] = STM32_CMD_ERR; + buf[1] = STM32_CMD_ERR ^ 0xFF; + while (t1 < t0 + STM32_RESYNC_TIMEOUT) { + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + usleep(500000); + time(&t1); + continue; + } + p_err = port->read(port, &ack, 1); + if (p_err != PORT_ERR_OK) { + time(&t1); + continue; + } + if (ack == STM32_NACK) + return STM32_ERR_OK; + time(&t1); + } + return STM32_ERR_UNKNOWN; +} - stm32_send_byte(stm, cmd); - stm32_send_byte(stm, cmd ^ 0xFF); - ret = stm32_read_byte(stm); - if (ret == STM32_ACK) { - return 1; - } else if (ret == STM32_NACK) { - fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd); - } else { - fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd); +/* + * some command receive reply frame with variable length, and length is + * embedded in reply frame itself. + * We can guess the length, but if we guess wrong the protocol gets out + * of sync. + * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte + * read for byte oriented interfaces (e.g. UART). + * + * to run safely, data buffer should be allocated for 256+1 bytes + * + * len is value of the first byte in the frame. + */ +static stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, uint8_t cmd, + uint8_t *data, unsigned int len) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (port->flags & PORT_BYTE) { + /* interface is UART-like */ + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + len = data[0]; + p_err = port->read(port, data + 1, len + 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; + } + + p_err = port->read(port, data, len + 2); + if (p_err == PORT_ERR_OK && len == data[0]) + return STM32_ERR_OK; + if (p_err != PORT_ERR_OK) { + /* restart with only one byte */ + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + } + + fprintf(stderr, "Re sync (len = %d)\n", data[0]); + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + len = data[0]; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, len + 2); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +/* + * Some interface, e.g. UART, requires a specific init sequence to let STM32 + * autodetect the interface speed. + * The sequence is only required one time after reset. + * stm32flash has command line flag "-c" to prevent sending the init sequence + * in case it was already sent before. + * User can easily forget adding "-c". In this case the bootloader would + * interpret the init sequence as part of a command message, then waiting for + * the rest of the message blocking the interface. + * This function sends the init sequence and, in case of timeout, recovers + * the interface. + */ +static stm32_err_t stm32_send_init_seq(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t byte, cmd = STM32_CMD_INIT; + + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_ACK) + return STM32_ERR_OK; + if (p_err == PORT_ERR_OK && byte == STM32_NACK) { + /* We could get error later, but let's continue, for now. */ + fprintf(stderr, + "Warning: the interface was not closed properly.\n"); + return STM32_ERR_OK; + } + if (p_err != PORT_ERR_TIMEDOUT) { + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; + } + + /* + * Check if previous STM32_CMD_INIT was taken as first byte + * of a command. Send a new byte, we should get back a NACK. + */ + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; } - return 0; + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_NACK) + return STM32_ERR_OK; + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; } -stm32_t* stm32_init(const serial_t *serial, const char init) { - uint8_t len, val; +/* find newer command by higher code */ +#define newer(prev, a) (((prev) == STM32_CMD_ERR) \ + ? (a) \ + : (((prev) > (a)) ? (prev) : (a))) + +stm32_t *stm32_init(struct port_interface *port, const char init) +{ + uint8_t len, val, buf[257]; stm32_t *stm; + int i, new_cmds; stm = calloc(sizeof(stm32_t), 1); stm->cmd = malloc(sizeof(stm32_cmd_t)); memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); - stm->serial = serial; + stm->port = port; - if (init) { - stm32_send_byte(stm, STM32_CMD_INIT); - if (stm32_read_byte(stm) != STM32_ACK) { - stm32_close(stm); - fprintf(stderr, "Failed to get init ACK from device\n"); + if ((port->flags & PORT_CMD_INIT) && init) + if (stm32_send_init_seq(stm) != STM32_ERR_OK) return NULL; - } + + /* get the version and read protection status */ + if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* From AN, only UART bootloader returns 3 bytes */ + len = (port->flags & PORT_GVR_ETX) ? 3 : 1; + if (port->read(port, buf, len) != PORT_ERR_OK) + return NULL; + stm->version = buf[0]; + stm->option1 = (port->flags & PORT_GVR_ETX) ? buf[1] : 0; + stm->option2 = (port->flags & PORT_GVR_ETX) ? buf[2] : 0; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; } /* get the bootloader information */ - if (!stm32_send_command(stm, STM32_CMD_GET)) return 0; - len = stm32_read_byte(stm) + 1; - stm->bl_version = stm32_read_byte(stm); --len; - while (len-- > 0) { - val = stm32_read_byte(stm); + len = STM32_CMD_GET_LENGTH; + if (port->cmd_get_reply) + for (i = 0; port->cmd_get_reply[i].length; i++) + if (stm->version == port->cmd_get_reply[i].version) { + len = port->cmd_get_reply[i].length; + break; + } + if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return NULL; + len = buf[0] + 1; + stm->bl_version = buf[1]; + new_cmds = 0; + for (i = 1; i < len; i++) { + val = buf[i + 1]; switch (val) { case STM32_CMD_GET: stm->cmd->get = val; break; @@ -207,26 +389,45 @@ stm32_t* stm32_init(const serial_t *serial, const char init) { case STM32_CMD_GO: stm->cmd->go = val; break; case STM32_CMD_WM: - stm->cmd->wm = val; break; + case STM32_CMD_WM_NS: + stm->cmd->wm = newer(stm->cmd->wm, val); + break; case STM32_CMD_ER: case STM32_CMD_EE: - stm->cmd->er = val; break; + case STM32_CMD_EE_NS: + stm->cmd->er = newer(stm->cmd->er, val); + break; case STM32_CMD_WP: - stm->cmd->wp = val; break; + case STM32_CMD_WP_NS: + stm->cmd->wp = newer(stm->cmd->wp, val); + break; case STM32_CMD_UW: - stm->cmd->uw = val; break; + case STM32_CMD_UW_NS: + stm->cmd->uw = newer(stm->cmd->uw, val); + break; case STM32_CMD_RP: - stm->cmd->rp = val; break; + case STM32_CMD_RP_NS: + stm->cmd->rp = newer(stm->cmd->rp, val); + break; case STM32_CMD_UR: - stm->cmd->ur = val; break; + case STM32_CMD_UR_NS: + stm->cmd->ur = newer(stm->cmd->ur, val); + break; + case STM32_CMD_CRC: + stm->cmd->crc = newer(stm->cmd->crc, val); + break; default: - fprintf(stderr, - "Seems this bootloader returns more then we " - "understand in the GET command.\n" - "We will skip unknown byte 0x%02x\n", val); + if (new_cmds++ == 0) + fprintf(stderr, + "GET returns unknown commands (0x%2x", + val); + else + fprintf(stderr, ", 0x%2x", val); } } - if (stm32_read_byte(stm) != STM32_ACK) { + if (new_cmds) + fprintf(stderr, ")\n"); + if (stm32_get_ack(stm) != STM32_ERR_OK) { stm32_close(stm); return NULL; } @@ -234,51 +435,35 @@ stm32_t* stm32_init(const serial_t *serial, const char init) { if (stm->cmd->get == STM32_CMD_ERR || stm->cmd->gvr == STM32_CMD_ERR || stm->cmd->gid == STM32_CMD_ERR) { - fprintf(stderr, "Error: bootloader did not returned correct " - "information from GET command\n"); - return NULL; - } - - /* get the version and read protection status */ - if (!stm32_send_command(stm, stm->cmd->gvr)) { - stm32_close(stm); - return NULL; - } - - stm->version = stm32_read_byte(stm); - stm->option1 = stm32_read_byte(stm); - stm->option2 = stm32_read_byte(stm); - if (stm32_read_byte(stm) != STM32_ACK) { - stm32_close(stm); + fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n"); return NULL; } /* get the device ID */ - if (!stm32_send_command(stm, stm->cmd->gid)) { + if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) { stm32_close(stm); return NULL; } - len = stm32_read_byte(stm) + 1; + len = buf[0] + 1; if (len < 2) { stm32_close(stm); fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len); return NULL; } - stm->pid = (stm32_read_byte(stm) << 8) | stm32_read_byte(stm); - len -= 2; - if (len > 0) { + stm->pid = (buf[1] << 8) | buf[2]; + if (len > 2) { fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len); - while (len-- > 0) - fprintf(stderr, " %02x", stm32_read_byte(stm)); + for (i = 2; i <= len ; i++) + fprintf(stderr, " %02x", buf[i]); fprintf(stderr, "\n"); } - if (stm32_read_byte(stm) != STM32_ACK) { + if (stm32_get_ack(stm) != STM32_ERR_OK) { stm32_close(stm); return NULL; } stm->dev = devices; - while(stm->dev->id != 0x00 && stm->dev->id != stm->pid) + while (stm->dev->id != 0x00 && stm->dev->id != stm->pid) ++stm->dev; if (!stm->dev->id) { @@ -290,264 +475,574 @@ stm32_t* stm32_init(const serial_t *serial, const char init) { return stm; } -void stm32_close(stm32_t *stm) { - if (stm) free(stm->cmd); +void stm32_close(stm32_t *stm) +{ + if (stm) + free(stm->cmd); free(stm); } -char stm32_read_memory(const stm32_t *stm, uint32_t address, uint8_t data[], unsigned int len) { - uint8_t cs; - unsigned int i; - assert(len > 0 && len < 257); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; - /* must be 32bit aligned */ - assert(address % 4 == 0); + if (!len) + return STM32_ERR_OK; - address = be_u32 (address); - cs = stm32_gen_cs(address); + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } if (stm->cmd->rm == STM32_CMD_ERR) { fprintf(stderr, "Error: READ command not implemented in bootloader.\n"); - return 0; + return STM32_ERR_NO_CMD; } - if (!stm32_send_command(stm, stm->cmd->rm)) return 0; - if (serial_write(stm->serial, &address, 4) != SERIAL_ERR_OK) - return 0; + if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; - stm32_send_byte(stm, cs); - if (stm32_read_byte(stm) != STM32_ACK) return 0; + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; - i = len - 1; - stm32_send_byte(stm, i); - stm32_send_byte(stm, i ^ 0xFF); - if (stm32_read_byte(stm) != STM32_ACK) return 0; + if (stm32_send_command(stm, len - 1) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; - if (serial_read(stm->serial, data, len) != SERIAL_ERR_OK) - return 0; + if (port->read(port, data, len) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; - return 1; + return STM32_ERR_OK; } -char stm32_write_memory(const stm32_t *stm, uint32_t address, const uint8_t data[], unsigned int len) { - uint8_t cs; - unsigned int i; - int c, extra; - assert(len > 0 && len < 257); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t cs, buf[256 + 2]; + unsigned int i, aligned_len; + stm32_err_t s_err; - /* must be 32bit aligned */ - assert(address % 4 == 0); + if (!len) + return STM32_ERR_OK; - address = be_u32 (address); - cs = stm32_gen_cs(address); + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + /* must be 32bit aligned */ + if (address & 0x3 || len & 0x3) { + fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } if (stm->cmd->wm == STM32_CMD_ERR) { fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n"); - return 0; + return STM32_ERR_NO_CMD; } /* send the address and checksum */ - if (!stm32_send_command(stm, stm->cmd->wm)) return 0; - if (serial_write(stm->serial, &address, 4) != SERIAL_ERR_OK) - return 0; - - stm32_send_byte(stm, cs); - if (stm32_read_byte(stm) != STM32_ACK) return 0; - - /* setup the cs and send the length */ - extra = len % 4; - cs = len - 1 + extra; - stm32_send_byte(stm, cs); - - /* write the data and build the checksum */ - for(i = 0; i < len; ++i) + if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + aligned_len = (len + 3) & ~3; + cs = aligned_len - 1; + buf[0] = aligned_len - 1; + for (i = 0; i < len; i++) { cs ^= data[i]; - - if (serial_write(stm->serial, data, len) != SERIAL_ERR_OK) - return 0; - - /* write the alignment padding */ - for(c = 0; c < extra; ++c) { - stm32_send_byte(stm, 0xFF); + buf[i + 1] = data[i]; + } + /* padding data */ + for (i = len; i < aligned_len; i++) { cs ^= 0xFF; + buf[i + 1] = 0xFF; } - - /* send the checksum */ - stm32_send_byte(stm, cs); - return stm32_read_byte(stm) == STM32_ACK; + buf[aligned_len + 1] = cs; + if (port->write(port, buf, aligned_len + 2) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wm != STM32_CMD_WM_NS) + stm32_warn_stretching("write"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } -char stm32_wunprot_memory(const stm32_t *stm) { +stm32_err_t stm32_wunprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + if (stm->cmd->uw == STM32_CMD_ERR) { fprintf(stderr, "Error: WRITE UNPROTECT command not implemented in bootloader.\n"); - return 0; + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->uw != STM32_CMD_UW_NS) + stm32_warn_stretching("WRITE UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->wp == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; } - if (!stm32_send_command(stm, stm->cmd->uw)) return 0; - if (!stm32_send_command(stm, 0x8C )) return 0; - return 1; + if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wp != STM32_CMD_WP_NS) + stm32_warn_stretching("WRITE PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } -char stm32_runprot_memory (const stm32_t *stm) { +stm32_err_t stm32_runprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + if (stm->cmd->ur == STM32_CMD_ERR) { - fprintf(stderr, "Error: READ UNPROTECT command not implemented in bootloader.\n"); - return 0; + fprintf(stderr, "Error: READOUT UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; } - if (!stm32_send_command(stm, stm->cmd->ur)) return 0; - if (!stm32_send_command(stm, 0x6D )) return 0; - return 1; + if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->ur != STM32_CMD_UR_NS) + stm32_warn_stretching("READOUT UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } -char stm32_readprot_memory(const stm32_t *stm) { +stm32_err_t stm32_readprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + if (stm->cmd->rp == STM32_CMD_ERR) { - fprintf(stderr, "Error: READ PROTECT command not implemented in bootloader.\n"); - return 0; + fprintf(stderr, "Error: READOUT PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; } - if (!stm32_send_command(stm, stm->cmd->rp)) return 0; - if (!stm32_send_command(stm, 0x7D )) return 0; - return 1; + if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->rp != STM32_CMD_RP_NS) + stm32_warn_stretching("READOUT PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } -char stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) { +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; if (!pages) - return 1; + return STM32_ERR_OK; if (stm->cmd->er == STM32_CMD_ERR) { fprintf(stderr, "Error: ERASE command not implemented in bootloader.\n"); - return 0; + return STM32_ERR_NO_CMD; } - if (!stm32_send_command(stm, stm->cmd->er)) { + if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { fprintf(stderr, "Can't initiate chip erase!\n"); - return 0; + return STM32_ERR_UNKNOWN; } - /* The erase command reported by the bootloader is either 0x43 or 0x44 */ + /* The erase command reported by the bootloader is either 0x43, 0x44 or 0x45 */ /* 0x44 is Extended Erase, a 2 byte based protocol and needs to be handled differently. */ - if (stm->cmd->er == STM32_CMD_EE) { - /* Not all chips using Extended Erase support mass erase */ - /* Currently known as not supporting mass erase is the Ultra Low Power STM32L15xx range */ - /* So if someone has not overridden the default, but uses one of these chips, take it out of */ - /* mass erase mode, so it will be done page by page. This maximum might not be correct either! */ - if (stm->pid == 0x416 && pages == 0xFF) pages = 0xF8; /* works for the STM32L152RB with 128Kb flash */ + /* 0x45 is clock no-stretching version of Extended Erase for I2C port. */ + if (stm->cmd->er != STM32_CMD_ER) { + /* Not all chips using Extended Erase support mass erase */ + /* Currently known as not supporting mass erase is the Ultra Low Power STM32L15xx range */ + /* So if someone has not overridden the default, but uses one of these chips, take it out of */ + /* mass erase mode, so it will be done page by page. This maximum might not be correct either! */ + if (stm->pid == 0x416 && pages == 0xFF) + pages = 0xF8; /* works for the STM32L152RB with 128Kb flash */ if (pages == 0xFF) { - stm32_send_byte(stm, 0xFF); - stm32_send_byte(stm, 0xFF); // 0xFFFF the magic number for mass erase - stm32_send_byte(stm, 0x00); // 0x00 the XOR of those two bytes as a checksum - if (stm32_read_byte(stm) != STM32_ACK) { + uint8_t buf[3]; + + /* 0xFFFF the magic number for mass erase */ + buf[0] = 0xFF; + buf[1] = 0xFF; + buf[2] = 0x00; /* checksum */ + if (port->write(port, buf, 3) != PORT_ERR_OK) { + fprintf(stderr, "Mass erase error.\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { fprintf(stderr, "Mass erase failed. Try specifying the number of pages to be erased.\n"); - return 0; + if (port->flags & PORT_STRETCH_W + && stm->cmd->er != STM32_CMD_EE_NS) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; } - return 1; + return STM32_ERR_OK; } uint16_t pg_num; uint8_t pg_byte; - uint8_t cs = 0; - + uint8_t cs = 0; + uint8_t *buf; + int i = 0; + + buf = malloc(2 + 2 * pages + 1); + if (!buf) + return STM32_ERR_UNKNOWN; + + /* Number of pages to be erased - 1, two bytes, MSB first */ pg_byte = (pages - 1) >> 8; - stm32_send_byte(stm, pg_byte); // Number of pages to be erased - 1, two bytes, MSB first + buf[i++] = pg_byte; cs ^= pg_byte; pg_byte = (pages - 1) & 0xFF; - stm32_send_byte(stm, pg_byte); + buf[i++] = pg_byte; cs ^= pg_byte; - + for (pg_num = spage; pg_num < spage + pages; pg_num++) { - pg_byte = pg_num >> 8; - cs ^= pg_byte; - stm32_send_byte(stm, pg_byte); - pg_byte = pg_num & 0xFF; - cs ^= pg_byte; - stm32_send_byte(stm, pg_byte); - } - stm32_send_byte(stm, cs); - - if (stm32_read_byte(stm) != STM32_ACK) { - fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n"); - return 0; - } - - return 1; + pg_byte = pg_num >> 8; + cs ^= pg_byte; + buf[i++] = pg_byte; + pg_byte = pg_num & 0xFF; + cs ^= pg_byte; + buf[i++] = pg_byte; + } + buf[i++] = cs; + p_err = port->write(port, buf, i); + free(buf); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Page-by-page erase error.\n"); + return STM32_ERR_UNKNOWN; + } + + s_err = stm32_get_ack_timeout(stm, STM32_SECTERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n"); + if (port->flags & PORT_STRETCH_W + && stm->cmd->er != STM32_CMD_EE_NS) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + + return STM32_ERR_OK; } /* And now the regular erase (0x43) for all other chips */ if (pages == 0xFF) { - return stm32_send_command(stm, 0xFF); + s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } else { uint8_t cs = 0; uint8_t pg_num; - stm32_send_byte(stm, pages-1); + uint8_t *buf; + int i = 0; + + buf = malloc(1 + pages + 1); + if (!buf) + return STM32_ERR_UNKNOWN; + + buf[i++] = pages - 1; cs ^= (pages-1); for (pg_num = spage; pg_num < (pages + spage); pg_num++) { - stm32_send_byte(stm, pg_num); + buf[i++] = pg_num; cs ^= pg_num; } - stm32_send_byte(stm, cs); - return stm32_read_byte(stm) == STM32_ACK; + buf[i++] = cs; + p_err = port->write(port, buf, i); + free(buf); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Erase failed.\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; } } -char stm32_run_raw_code(const stm32_t *stm, uint32_t target_address, const uint8_t *code, uint32_t code_size) +static stm32_err_t stm32_run_raw_code(const stm32_t *stm, + uint32_t target_address, + const uint8_t *code, uint32_t code_size) { uint32_t stack_le = le_u32(0x20002000); uint32_t code_address_le = le_u32(target_address + 8); uint32_t length = code_size + 8; - + uint8_t *mem, *pos; + uint32_t address, w; + /* Must be 32-bit aligned */ - assert(target_address % 4 == 0); + if (target_address & 0x3) { + fprintf(stderr, "Error: code address must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } - uint8_t *mem = malloc(length); + mem = malloc(length); if (!mem) - return 0; - + return STM32_ERR_UNKNOWN; + memcpy(mem, &stack_le, sizeof(uint32_t)); memcpy(mem + 4, &code_address_le, sizeof(uint32_t)); memcpy(mem + 8, code, code_size); - - uint8_t *pos = mem; - uint32_t address = target_address; - while(length > 0) { - - uint32_t w = length > 256 ? 256 : length; - if (!stm32_write_memory(stm, address, pos, w)) { + + pos = mem; + address = target_address; + while (length > 0) { + w = length > 256 ? 256 : length; + if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) { free(mem); - return 0; + return STM32_ERR_UNKNOWN; } - + address += w; pos += w; - length -=w; + length -= w; } - + free(mem); return stm32_go(stm, target_address); } -char stm32_go(const stm32_t *stm, uint32_t address) { - uint8_t cs; +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; - address = be_u32 (address); - cs = stm32_gen_cs(address); + if (stm->cmd->go == STM32_CMD_ERR) { + fprintf(stderr, "Error: GO command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } - if (stm->cmd->go == STM32_CMD_ERR) { - fprintf(stderr, "Error: GO command not implemented in bootloader.\n"); - return 0; - } + if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; - if (!stm32_send_command(stm, stm->cmd->go)) return 0; - serial_write(stm->serial, &address, 4); - serial_write(stm->serial, &cs , 1); + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; - return stm32_read_byte(stm) == STM32_ACK; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; } -char stm32_reset_device(const stm32_t *stm) { +stm32_err_t stm32_reset_device(const stm32_t *stm) +{ uint32_t target_address = stm->dev->ram_start; - + return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length); } +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc == STM32_CMD_ERR) { + fprintf(stderr, "Error: CRC command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = length >> 24; + buf[1] = (length >> 16) & 0xFF; + buf[2] = (length >> 8) & 0xFF; + buf[3] = length & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3])) + return STM32_ERR_UNKNOWN; + + *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return STM32_ERR_OK; +} + +/* + * CRC computed by STM32 is similar to the standard crc32_be() + * implemented, for example, in Linux kernel in ./lib/crc32.c + * But STM32 computes it on units of 32 bits word and swaps the + * bytes of the word before the computation. + * Due to byte swap, I cannot use any CRC available in existing + * libraries, so here is a simple not optimized implementation. + */ +#define CRCPOLY_BE 0x04c11db7 +#define CRC_MSBMASK 0x80000000 +#define CRC_INIT_VALUE 0xFFFFFFFF +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) +{ + int i; + uint32_t data; + + if (len & 0x3) { + fprintf(stderr, "Buffer length must be multiple of 4 bytes\n"); + return 0; + } + + while (len) { + data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + len -= 4; + + crc ^= data; + + for (i = 0; i < 32; i++) + if (crc & CRC_MSBMASK) + crc = (crc << 1) ^ CRCPOLY_BE; + else + crc = (crc << 1); + } + return crc; +} + +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + uint8_t buf[256]; + uint32_t start, total_len, len, current_crc; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc != STM32_CMD_ERR) + return stm32_crc_memory(stm, address, length, crc); + + start = address; + total_len = length; + current_crc = CRC_INIT_VALUE; + while (length) { + len = length > 256 ? 256 : length; + if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) { + fprintf(stderr, + "Failed to read memory at address 0x%08x, target write-protected?\n", + address); + return STM32_ERR_UNKNOWN; + } + current_crc = stm32_sw_crc(current_crc, buf, len); + length -= len; + address += len; + + fprintf(stderr, + "\rCRC address 0x%08x (%.2f%%) ", + address, + (100.0f / (float)total_len) * (float)(address - start) + ); + fflush(stderr); + } + fprintf(stderr, "Done.\n"); + *crc = current_crc; + return STM32_ERR_OK; +} @@ -24,12 +24,23 @@ #include <stdint.h> #include "serial.h" +#define STM32_MAX_RX_FRAME 256 /* cmd read memory */ +#define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */ + +typedef enum { + STM32_ERR_OK = 0, + STM32_ERR_UNKNOWN, /* Generic error */ + STM32_ERR_NACK, + STM32_ERR_NO_CMD, /* Command not available in bootloader */ +} stm32_err_t; + typedef struct stm32 stm32_t; typedef struct stm32_cmd stm32_cmd_t; typedef struct stm32_dev stm32_dev_t; struct stm32 { const serial_t *serial; + struct port_interface *port; uint8_t bl_version; uint8_t version; uint8_t option1, option2; @@ -49,16 +60,25 @@ struct stm32_dev { uint32_t mem_start, mem_end; }; -stm32_t* stm32_init (const serial_t *serial, const char init); -void stm32_close (stm32_t *stm); -char stm32_read_memory (const stm32_t *stm, uint32_t address, uint8_t data[], unsigned int len); -char stm32_write_memory (const stm32_t *stm, uint32_t address, const uint8_t data[], unsigned int len); -char stm32_wunprot_memory(const stm32_t *stm); -char stm32_erase_memory (const stm32_t *stm, uint8_t spage, uint8_t pages); -char stm32_go (const stm32_t *stm, uint32_t address); -char stm32_reset_device (const stm32_t *stm); -char stm32_readprot_memory (const stm32_t *stm); -char stm32_runprot_memory (const stm32_t *stm); +stm32_t *stm32_init(struct port_interface *port, const char init); +void stm32_close(stm32_t *stm); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_t *stm); +stm32_err_t stm32_wprot_memory(const stm32_t *stm); +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, + uint8_t pages); +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_t *stm); +stm32_err_t stm32_readprot_memory(const stm32_t *stm); +stm32_err_t stm32_runprot_memory(const stm32_t *stm); +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); #endif diff --git a/stm32flash.1 b/stm32flash.1 index 44e9ed7..d37292f 100644 --- a/stm32flash.1 +++ b/stm32flash.1 @@ -1,9 +1,11 @@ .TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command" .SH NAME -stm32flash \- UART flashing utility for STM32 and STM32W +stm32flash \- flashing utility for STM32 and STM32W through UART or I2C .SH SYNOPSIS .B stm32flash -.RB [ \-cfhjkouv ] +.RB [ \-cfhjkouvCR ] +.RB [ \-a +.IR bus_address ] .RB [ \-b .IR baud_rate ] .RB [ \-m @@ -21,10 +23,14 @@ stm32flash \- UART flashing utility for STM32 and STM32W .RB [ \-s .IR start_page ] .RB [ \-S -.IR address [: length ] +.IR address [: length ]] +.RB [ \-F +.IR RX_length [: TX_length ]] .RB [ \-i .IR GPIO_string ] -.I tty_device +.RI [ tty_device +.R | +.IR i2c_device ] .SH DESCRIPTION .B stm32flash @@ -39,6 +45,12 @@ to interact with the bootloader of STM32[W]. .SH OPTIONS .TP +.BI "\-a" " bus_address" +Specify address on bus for +.IR i2c_device . +This option is mandatory for I2C interface. + +.TP .BI "\-b" " baud_rate" Specify baud rate speed of .IR tty_device . @@ -104,7 +116,9 @@ Erase only. .BI "\-e" " num" Specify to erase only .I num -pages before writing the flash. Default is to erase the whole flash. +pages before writing the flash. Default is to erase the whole flash. With +.B \-e 0 +the flash would not be erased. .TP .B \-v @@ -126,7 +140,15 @@ Specify flash page offset (0 = flash start). .TP .BI "\-S" " address" "[:" "length" "]" -Specify start address and optionally length for read/write/erase operations. +Specify start address and optionally length for read/write/erase/crc operations. + +.TP +.BI "\-F" " RX_length" "[:" "TX_length" "]" +Specify the maximum frame size for the current interface. +Due to STM32 bootloader protocol, host will never handle frames bigger than +256 byte in RX or 258 byte in TX. +Due to current code, lowest limit in RX is 20 byte (to read a complete reply +of command GET). Minimum limit in TX is 5 byte, required by protocol. .TP .B \-f @@ -154,6 +176,25 @@ for the format of .I GPIO_string and further explanation). +.TP +.B \-C +Specify to compute CRC on memory content. +By default the CRC is computed on the whole flash content. +Use +.B "\-S" +to provide different memory address range. + +.TP +.B \-R +Specify to reset the device at exit. +This option is ignored if either +.BR "\-g" "," +.BR "\-j" "," +.B "\-k" +or +.B "\-u" +is also specified. + .SH BOOTLOADER GPIO SEQUENCE This feature is currently available on Linux host only. @@ -295,30 +336,65 @@ The original software package .B stm32flash is written by .I Geoffrey McRae <geoff@spacevs.com> -and available in http://stm32flash.googlecode.com . - -By November 2012 the project is migrated to -https://gitorious.org/stm32flash -and maintained by +and is since 2012 maintained by .IR "Tormod Volden <debian.tormod@gmail.com>" . -Extension to STM32W and man page are written by +Man page and extension to STM32W and I2C are written by .IR "Antonio Borneo <borneo.antonio@gmail.com>" . +Please report any bugs at the project homepage +http://stm32flash.googlecode.com . + .SH SEE ALSO .BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)." -The communication protocol used by ST bootloader is documented in ST -application note AN3155. Copy is include in the source code of this -software. Updated version of the application note is available in ST -website: +The communication protocol used by ST bootloader is documented in +following ST application notes, depending on communication port. +The current version of +.B stm32flash +only supports +.I UART +and +.I I2C +ports. .PD 0 .P +.IP \(bu 2 +AN3154: CAN protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf +.RE + +.P +.IP \(bu 2 +AN3155: USART protocol used in the STM32(TM) bootloader +.P +.RS http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf +.RE + +.P +.IP \(bu 2 +AN4221: I2C protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf +.RE + +.P +.IP \(bu 2 +AN4286: SPI protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf +.RE + .PD + Boot mode selection for STM32 is documented in ST application note -AN2606, available in ST website: +AN2606, available from the ST website: .PD 0 .P http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf @@ -17,7 +17,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +#include <stdint.h> #include "utils.h" /* detect CPU endian */ @@ -42,4 +42,4 @@ uint32_t le_u32(const uint32_t v) { ((v & 0x0000FF00) << 8) | ((v & 0x000000FF) << 24); return v; -}
\ No newline at end of file +} |