diff --git a/AUTHORS b/AUTHORS index d096f22..7894bb9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,3 +17,11 @@ Armin van der Togt Brian Silverman Georg Hofmann Luis Rodrigues +Jeff Epler +Alexander O. Anisimov +Seth LaForge +Johan Hellman +Matthias Weisser +Tilman Sauerbeck +Mateusz Spychała +Ernst Schwab diff --git a/HOWTO b/HOWTO index d8f32eb..4c466c7 100644 --- a/HOWTO +++ b/HOWTO @@ -33,3 +33,7 @@ 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 + +Cross compile on Linux 64 bits host for linux 32 bits target: +===================================================================== + make CFLAGS=-m32 LDFLAGS=-m32 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..16dcc41 --- /dev/null +++ b/INSTALL @@ -0,0 +1,16 @@ + +Building stm32flash + +A set of static makefiles is provided that should work on most operating +systems with a standard build environment, for instance GNU make and gcc. + +1. Build executable + + make + +2. Install executable and manual page (optional) + + make install + + The install location base can be set with the PREFIX flag (default + is /usr/local), e.g. make install PREFIX=/opt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile index 0328d55..81a5344 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,14 @@ PREFIX = /usr/local CFLAGS += -Wall -g +ifndef CC + $(error CC is not defined) +endif + +ifndef AR + $(error AR is not defined) +endif + INSTALL = install OBJS = dev_table.o \ @@ -19,7 +27,7 @@ all: stm32flash serial_platform.o: serial_posix.c serial_w32.c -parsers/parsers.a: +parsers/parsers.a: force cd parsers && $(MAKE) parsers.a stm32flash: $(OBJS) $(LIBOBJS) @@ -35,4 +43,6 @@ install: all $(INSTALL) -d $(DESTDIR)$(PREFIX)/share/man/man1 $(INSTALL) -m 644 stm32flash.1 $(DESTDIR)$(PREFIX)/share/man/man1 -.PHONY: all clean install +force: + +.PHONY: all clean install force diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c877c7b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,39 @@ + +SUBDIRS = parsers + +bin_PROGRAMS = stm32flash + +AUTOMAKE_OPTIONS = subdir-objects + + +stm32flash_SOURCES = \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c\ + stm32.c \ + utils.c + +stm32flash_LDADD = ${top_builddir}/parsers/parsers.la + +stm32flash_CFLAGS = \ + -g3 \ + -Os \ + -Wall \ + -Wextra \ + -I$(srcdir)/parsers + + +stm32flash_LDFLAGS = \ + -Wall \ + -g3 \ + -Wextra + +all: + + +.PHONY: all +.SILENT: all diff --git a/README.md b/README.md new file mode 100644 index 0000000..53541a4 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Since release [1.6.0](https://github.com/stm32duino/Arduino_Core_STM32/releases/tag/1.6.0) of the STM32 core, STM32CubeProgrammer is used to flash and replaced this tool. + +# stm32flash +Open source flash program for STM32 using the ST serial bootloader + +Forked from https://sourceforge.net/projects/stm32flash + +Branch **[master](https://github.com/stm32duino/stm32flash/tree/master)** sync from: +``` +Author: Patrick José Pereira 2018-03-16 18:07:25 +Committer: Tormod Volden 2018-04-17 00:18:23 +Follows: v0.5 +Precedes: + + Makefile: Check if CC and AR are defined + + Signed-off-by: Patrick José Pereira +``` + +Branch: **[Arduino_STM32](https://github.com/stm32duino/stm32flash/tree/Arduino_STM32)** +includes the stm32flash sources modified and provided by Roger Clark (@rogerclarkmelbourne) +with [rogerclarkmelbourne/Arduino_STM32](https://github.com/rogerclarkmelbourne/Arduino_STM32) diff --git a/TODO b/TODO index 41df614..09a1776 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,4 @@ -stm32: -- Add support for variable page size - AUTHORS: - Add contributors from Geoffrey's commits diff --git a/compiler.h b/compiler.h new file mode 100644 index 0000000..57e83ff --- /dev/null +++ b/compiler.h @@ -0,0 +1,38 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2017 Antonio Borneo + + 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_COMPILER +#define _H_COMPILER + +#if defined(__GNUC__) +#undef __unused +#undef __maybe_unused +#define __unused __attribute__ ((unused)) +#define __maybe_unused __attribute__ ((unused)) +#endif + +#ifndef __unused +#define __unused +#endif + +#ifndef __maybe_unused +#define __maybe_unused +#endif + +#endif /* _H_COMPILER */ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c05a9e0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,33 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.68]) +AC_INIT([stm32flash], [0.5], []) + +# Checks for programs. +AM_INIT_AUTOMAKE([foreign -Wall]) +AC_PROG_CC +AM_PROG_CC_C_O +AM_PROG_AR +AM_PROG_AS +LT_INIT +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. + +# Checks for libraries. + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + + +AC_CONFIG_FILES( + [ + Makefile + parsers/Makefile + ]) + +AC_OUTPUT diff --git a/dev_table.c b/dev_table.c index 399cd9d..5cdb515 100644 --- a/dev_table.c +++ b/dev_table.c @@ -1,7 +1,7 @@ /* stm32flash - Open Source ST STM32 flash program for *nix Copyright (C) 2010 Geoffrey McRae - Copyright (C) 2014 Antonio Borneo + Copyright (C) 2014-2015 Antonio Borneo This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -20,51 +20,102 @@ #include "stm32.h" +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 + +/* + * Page-size for page-by-page flash erase. + * Arrays are zero terminated; last non-zero value is automatically repeated + */ + +/* fixed size pages */ +static uint32_t p_128[] = { SZ_128, 0 }; +static uint32_t p_256[] = { SZ_256, 0 }; +static uint32_t p_1k[] = { SZ_1K, 0 }; +static uint32_t p_2k[] = { SZ_2K, 0 }; +static uint32_t p_128k[] = { SZ_128K, 0 }; +/* F2 and F4 page size */ +static uint32_t f2f4[] = { SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0 }; +/* F4 dual bank page size */ +static uint32_t f4db[] = { + SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, SZ_128K, SZ_128K, + SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0 +}; +/* F7 page size */ +static uint32_t f7[] = { SZ_32K, SZ_32K, SZ_32K, SZ_32K, SZ_128K, SZ_256K, 0 }; + /* * 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[] = { + /* ID "name" SRAM-address-range FLASH-address-range PPS PSize Option-byte-addr-range System-mem-addr-range Flags */ /* 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}, + {0x440, "STM32F030x8/F05xxx" , 0x20000800, 0x20002000, 0x08000000, 0x08010000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFEC00, 0x1FFFF800, 0}, + {0x444, "STM32F03xx4/6" , 0x20000800, 0x20001000, 0x08000000, 0x08008000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFEC00, 0x1FFFF800, 0}, + {0x442, "STM32F030xC/F09xxx" , 0x20001800, 0x20008000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, F_OBLL}, + {0x445, "STM32F04xxx/F070x6" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800, 0}, + {0x448, "STM32F070xB/F071xx/F72xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800, 0}, /* 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 */ + {0x412, "STM32F10xxx Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x410, "STM32F10xxx Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x414, "STM32F10xxx High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x420, "STM32F10xxx Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x428, "STM32F10xxx High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x418, "STM32F105xx/F107xx" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800, 0}, + {0x430, "STM32F10xxx XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800, 0}, /* F2 */ - {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + {0x411, "STM32F2xxxx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, /* 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}, + {0x432, "STM32F373xx/F378xx" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x422, "STM32F302xB(C)/F303xB(C)/F358xx" , 0x20001400, 0x2000A000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x439, "STM32F301xx/F302x4(6/8)/F318xx" , 0x20001800, 0x20004000, 0x08000000, 0x08010000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x438, "STM32F303x4(6/8)/F334xx/F328xx" , 0x20001800, 0x20003000, 0x08000000, 0x08010000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, + {0x446, "STM32F302xD(E)/F303xD(E)/F398xx" , 0x20001800, 0x20010000, 0x08000000, 0x08080000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, 0}, /* 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}, + {0x413, "STM32F40xxx/41xxx" , 0x20003000, 0x20020000, 0x08000000, 0x08100000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x419, "STM32F42xxx/43xxx" , 0x20003000, 0x20030000, 0x08000000, 0x08200000, 1, f4db , 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08040000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x458, "STM32F410xx" , 0x20003000, 0x20008000, 0x08000000, 0x08020000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x431, "STM32F411xx" , 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x441, "STM32F412xx" , 0x20003000, 0x20040000, 0x08000000, 0x08100000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x421, "STM32F446xx" , 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x434, "STM32F469xx/479xx" , 0x20003000, 0x20060000, 0x08000000, 0x08200000, 1, f4db , 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x463, "STM32F413xx/423xx" , 0x20003000, 0x20050000, 0x08000000, 0x08180000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + /* F7 */ + {0x452, "STM32F72xxx/73xxx" , 0x20004000, 0x20040000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, + {0x449, "STM32F74xxx/75xxx" , 0x20004000, 0x20050000, 0x08000000, 0x08100000, 1, f7 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, + {0x451, "STM32F76xxx/77xxx" , 0x20004000, 0x20080000, 0x08000000, 0x08200000, 1, f7 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, + /* H7 */ + {0x450, "STM32H74xxx/75xxx" , 0x20004100, 0x20020000, 0x08000000, 0x08200000, 1, p_128k, 0 , 0 , 0x1FF00000, 0x1FF1E800, 0}, /* L0 */ - {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x457, "STM32L01xxx/02xxx" , 0x20000800, 0x20000800, 0x08000000, 0x08004000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, + {0x425, "STM32L031xx/041xx" , 0x20001000, 0x20002000, 0x08000000, 0x08008000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, + {0x417, "STM32L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, + {0x447, "STM32L07xxx/08xxx" , 0x20002000, 0x20005000, 0x08000000, 0x08030000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF02000, 0}, /* 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}, + {0x416, "STM32L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, + {0x429, "STM32L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, + {0x427, "STM32L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08040000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF02000, F_NO_ME}, + {0x436, "STM32L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, p_256 , 0x1FF80000, 0x1FF8009F, 0x1FF00000, 0x1FF02000, 0}, + {0x437, "STM32L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08080000, 16, p_256 , 0x1FF80000, 0x1FF8009F, 0x1FF00000, 0x1FF02000, F_NO_ME}, + /* L4 */ + {0x435, "STM32L43xxx/44xxx" , 0x20003100, 0x2000C000, 0x08000000, 0x08040000, 1, p_2k , 0x1FFF7800, 0x1FFF780F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x462, "STM32L45xxx/46xxx" , 0x20003100, 0x20020000, 0x08000000, 0x08080000, 1, p_2k , 0x1FFF7800, 0x1FFF780F, 0x1FFF0000, 0x1FFF7000, F_PEMPTY}, + {0x415, "STM32L47xxx/48xxx" , 0x20003100, 0x20018000, 0x08000000, 0x08100000, 1, p_2k , 0x1FFF7800, 0x1FFFF80F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x461, "STM32L496xx/4A6xx" , 0x20003100, 0x20040000, 0x08000000, 0x08100000, 1, p_2k , 0x1FFF7800, 0x1FFFF80F, 0x1FFF0000, 0x1FFF7000, 0}, /* 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} + {0x641, "Medium_Density PL" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, + {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k , 0x08040800, 0x0804080F, 0x08040000, 0x08040800, 0}, + {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 4, p_2k , 0x08040800, 0x0804080F, 0x08040000, 0x08040800, 0}, + { /* sentinel */ } }; diff --git a/i2c.c b/i2c.c index 10e6bb1..bb99545 100644 --- a/i2c.c +++ b/i2c.c @@ -28,14 +28,15 @@ #include #include +#include "compiler.h" #include "serial.h" #include "port.h" #if !defined(__linux__) -static port_err_t i2c_open(struct port_interface *port, - struct port_options *ops) +static port_err_t i2c_open(struct port_interface __unused *port, + struct port_options __unused *ops) { return PORT_ERR_NODEV; } @@ -149,7 +150,7 @@ static port_err_t i2c_read(struct port_interface *port, void *buf, if (h == NULL) return PORT_ERR_UNKNOWN; ret = read(h->fd, buf, nbyte); - if (ret != nbyte) + if (ret != (int)nbyte) return PORT_ERR_UNKNOWN; return PORT_ERR_OK; } @@ -164,13 +165,14 @@ static port_err_t i2c_write(struct port_interface *port, void *buf, if (h == NULL) return PORT_ERR_UNKNOWN; ret = write(h->fd, buf, nbyte); - if (ret != nbyte) + if (ret != (int)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) +static port_err_t i2c_gpio(struct port_interface __unused *port, + serial_gpio_t __unused n, + int __unused level) { return PORT_ERR_OK; } @@ -194,11 +196,18 @@ static struct varlen_cmd i2c_cmd_get_reply[] = { { /* sentinel */ } }; +static port_err_t i2c_flush(struct port_interface __unused *port) +{ + /* We shouldn't need to flush I2C */ + return PORT_ERR_OK; +} + struct port_interface port_i2c = { .name = "i2c", .flags = PORT_STRETCH_W, .open = i2c_open, .close = i2c_close, + .flush = i2c_flush, .read = i2c_read, .write = i2c_write, .gpio = i2c_gpio, diff --git a/init.c b/init.c index 77a571b..3177607 100644 --- a/init.c +++ b/init.c @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -27,17 +28,24 @@ #include #include #include + +#include "compiler.h" #include "init.h" #include "serial.h" #include "stm32.h" #include "port.h" +#include "utils.h" + +extern FILE *diag; struct gpio_list { struct gpio_list *next; int gpio; + int input; /* 1 if direction of gpio should be changed back to input. */ + int exported; /* 0 if gpio should be unexported. */ }; - +#if defined(__linux__) static int write_to(const char *filename, const char *value) { int fd, ret; @@ -57,22 +65,45 @@ static int write_to(const char *filename, const char *value) return 1; } -#if !defined(__linux__) -static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) +static int read_from(const char *filename, char *buf, size_t len) { - fprintf(stderr, "GPIO control only available in Linux\n"); - return 0; + int fd, ret; + size_t n = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open file \"%s\"\n", filename); + return 0; + } + + do { + ret = read(fd, buf + n, len - n); + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + continue; /* try again */ + fprintf(stderr, "Error reading in file \"%s\"\n", filename); + close(fd); + return 0; + } + n += ret; + } while (n < len && ret); + + close(fd); + return n; } -#else + static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) { char num[16]; /* sized to carry MAX_INT */ char file[48]; /* sized to carry longest filename */ + char dir; struct stat buf; struct gpio_list *new; int ret; + int exported = 1; + int input = 0; - sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + sprintf(file, "/sys/class/gpio/gpio%d/value", n); ret = stat(file, &buf); if (ret) { /* file miss, GPIO not exported yet */ @@ -85,35 +116,75 @@ static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) fprintf(stderr, "GPIO %d not available\n", n); return 0; } + exported = 0; + } + + sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + ret = stat(file, &buf); + if (!ret) + if (read_from(file, &dir, sizeof(dir))) + if (dir == 'i') + input = 1; + + if (exported == 0 || input == 1) { new = (struct gpio_list *)malloc(sizeof(struct gpio_list)); if (new == NULL) { fprintf(stderr, "Out of memory\n"); return 0; } new->gpio = n; + new->exported = exported; + new->input = input; new->next = *gpio_to_release; *gpio_to_release = new; } return write_to(file, level ? "high" : "low"); } -#endif -static int release_gpio(int n) +static int release_gpio(int n, int input, int exported) { char num[16]; /* sized to carry MAX_INT */ + char file[48]; /* sized to carry longest filename */ sprintf(num, "%d", n); - return write_to("/sys/class/gpio/unexport", num); + if (input) { + sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + write_to(file, "in"); + } + if (!exported) + write_to("/sys/class/gpio/unexport", num); + + return 1; } +#else +static int drive_gpio(int __unused n, int __unused level, + struct gpio_list __unused **gpio_to_release) +{ + fprintf(stderr, "GPIO control only available in Linux\n"); + return 0; +} +#endif -static int gpio_sequence(struct port_interface *port, const char *s, size_t l) +static int gpio_sequence(struct port_interface *port, const char *seq, size_t len_seq) { - struct gpio_list *gpio_to_release = NULL, *to_free; - int ret, level, gpio; + struct gpio_list *gpio_to_release = NULL; +#if defined(__linux__) + struct gpio_list *to_free; +#endif + int ret = 0, level, gpio; + int sleep_time = 0; + int delimiter = 0; + const char *sig_str = NULL; + const char *s = seq; + size_t l = len_seq; + + fprintf(diag, "\nGPIO sequence start\n"); + while (ret == 0 && *s && l > 0) { + sig_str = NULL; + sleep_time = 0; + delimiter = 0; - ret = 1; - while (ret == 1 && *s && l > 0) { if (*s == '-') { level = 0; s++; @@ -127,48 +198,78 @@ static int gpio_sequence(struct port_interface *port, const char *s, size_t l) s++; l--; } - } else if (!strncmp(s, "rts", 3)) { + } else if (l >= 3 && !strncmp(s, "rts", 3)) { + sig_str = s; gpio = -GPIO_RTS; s += 3; l -= 3; - } else if (!strncmp(s, "dtr", 3)) { + } else if (l >= 3 && !strncmp(s, "dtr", 3)) { + sig_str = s; gpio = -GPIO_DTR; s += 3; l -= 3; - } else if (!strncmp(s, "brk", 3)) { + } else if (l >= 3 && !strncmp(s, "brk", 3)) { + sig_str = s; gpio = -GPIO_BRK; s += 3; l -= 3; - } else { - fprintf(stderr, "Character \'%c\' is not a digit\n", *s); - ret = 0; - break; - } - - if (*s && (l > 0)) { + } else if (*s && (l > 0)) { + delimiter = 1; + /* The ',' delimiter adds a 100 ms delay between signal toggles. + * i.e -rts,dtr will reset rts, wait 100 ms, set dtr. + * + * The '&' delimiter adds no delay between signal toggles. + * i.e -rts&dtr will reset rts and immediately set dtr. + * + * Example: -rts&dtr,,,rts,-dtr will reset rts and set dtr + * without delay, then wait 300 ms, set rts, wait 100 ms, reset dtr. + */ if (*s == ',') { s++; l--; + sleep_time = 100000; + } else if (*s == '&') { + s++; + l--; } else { - fprintf(stderr, "Character \'%c\' is not a separator\n", *s); - ret = 0; + fprintf(stderr, "Character \'%c\' is not a valid signal or separator\n", *s); + ret = 1; break; } + } else { + /* E.g. modifier without signal */ + fprintf(stderr, "Invalid sequence %.*s\n", (int) len_seq, seq); + ret = 1; + break; } - if (gpio < 0) - ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); - else - ret = drive_gpio(gpio, level, &gpio_to_release); - usleep(100000); - } + if (!delimiter) { /* actual gpio/port signal driving */ + if (gpio < 0) { + gpio = -gpio; + fprintf(diag, " setting port signal %.3s to %i... ", sig_str, level); + ret = (port->gpio(port, gpio, level) != PORT_ERR_OK); + printStatus(diag, ret); + } else { + fprintf(diag, " setting gpio %i to %i... ", gpio, level); + ret = (drive_gpio(gpio, level, &gpio_to_release) != 1); + printStatus(diag, ret); + } + } + + if (sleep_time) { + fprintf(diag, " delay %i us\n", sleep_time); + usleep(sleep_time); + } + } +#if defined(__linux__) while (gpio_to_release) { - release_gpio(gpio_to_release->gpio); + release_gpio(gpio_to_release->gpio, gpio_to_release->input, gpio_to_release->exported); to_free = gpio_to_release; gpio_to_release = gpio_to_release->next; free(to_free); } - usleep(500000); +#endif + fprintf(diag, "GPIO sequence end\n\n"); return ret; } @@ -186,7 +287,7 @@ static int gpio_bl_entry(struct port_interface *port, const char *seq) return gpio_sequence(port, seq, s - seq); } -static int gpio_bl_exit(struct port_interface *port, const char *seq) +int gpio_bl_exit(struct port_interface *port, const char *seq) { char *s; @@ -205,15 +306,13 @@ int init_bl_entry(struct port_interface *port, const char *seq) if (seq) return gpio_bl_entry(port, seq); - return 1; + return 0; } int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) { - if (seq) + if (seq && strchr(seq, ':')) return gpio_bl_exit(port, seq); - if (stm32_reset_device(stm) != STM32_ERR_OK) - return 0; - return 1; + return stm32_reset_device(stm); } diff --git a/init.h b/init.h index 6075b51..26992ac 100644 --- a/init.h +++ b/init.h @@ -27,5 +27,6 @@ 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); +int gpio_bl_exit(struct port_interface *port, const char *seq); #endif diff --git a/main.c b/main.c index 5105d21..b5a2b46 100644 --- a/main.c +++ b/main.c @@ -2,8 +2,8 @@ stm32flash - Open Source ST STM32 flash program for *nix Copyright 2010 Geoffrey McRae Copyright 2011 Steve Markgraf - Copyright 2012 Tormod Volden - Copyright 2013 Antonio Borneo + Copyright 2012-2016 Tormod Volden + Copyright 2013-2016 Antonio Borneo This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -27,6 +27,7 @@ #include #include #include +#include #include "init.h" #include "utils.h" @@ -38,13 +39,17 @@ #include "parsers/binary.h" #include "parsers/hex.h" -#define VERSION "0.4" +#if defined(__WIN32__) || defined(__CYGWIN__) +#include +#endif + +#define VERSION "STM32duino_0.5.1" /* device globals */ stm32_t *stm = NULL; - void *p_st = NULL; parser_t *parser = NULL; +struct port_interface *port = NULL; /* settings */ struct port_options port_opts = { @@ -55,13 +60,19 @@ struct port_options port_opts = { .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; + +enum actions { + ACT_NONE, + ACT_READ, + ACT_WRITE, + ACT_WRITE_UNPROTECT, + ACT_READ_PROTECT, + ACT_READ_UNPROTECT, + ACT_ERASE_ONLY, + ACT_CRC +}; + +enum actions action = ACT_NONE; int npages = 0; int spage = 0; int no_erase = 0; @@ -70,7 +81,9 @@ int retry = 10; char exec_flag = 0; uint32_t execute = 0; char init_flag = 1; +int use_stdinout = 0; char force_binary = 0; +FILE *diag; char reset_flag = 0; char *filename; char *gpio_seq = NULL; @@ -81,6 +94,36 @@ uint32_t readwrite_len = 0; int parse_options(int argc, char *argv[]); void show_help(char *name); +static const char *action2str(enum actions act) +{ + switch (act) { + case ACT_READ: + return "memory read"; + case ACT_WRITE: + return "memory write"; + case ACT_WRITE_UNPROTECT: + return "write unprotect"; + case ACT_READ_PROTECT: + return "read protect"; + case ACT_READ_UNPROTECT: + return "read unprotect"; + case ACT_ERASE_ONLY: + return "flash erase"; + case ACT_CRC: + return "memory crc"; + default: + return ""; + }; +} + +static void err_multi_action(enum actions new) +{ + fprintf(stderr, + "ERROR: Invalid options !\n" + "\tCan't execute \"%s\" and \"%s\" at the same time.\n", + action2str(action), action2str(new)); +} + static int is_addr_in_ram(uint32_t addr) { return addr >= stm->dev->ram_start && addr < stm->dev->ram_end; @@ -91,45 +134,130 @@ static int is_addr_in_flash(uint32_t addr) return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; } +static int is_addr_in_opt_bytes(uint32_t addr) +{ + /* option bytes upper range is inclusive in our device table */ + return addr >= stm->dev->opt_start && addr <= stm->dev->opt_end; +} + +static int is_addr_in_sysmem(uint32_t addr) +{ + return addr >= stm->dev->mem_start && addr < stm->dev->mem_end; +} + +/* returns the page that contains address "addr" */ static int flash_addr_to_page_floor(uint32_t addr) { + int page; + uint32_t *psize; + if (!is_addr_in_flash(addr)) return 0; - return (addr - stm->dev->fl_start) / stm->dev->fl_ps; + page = 0; + addr -= stm->dev->fl_start; + psize = stm->dev->fl_ps; + + while (addr >= psize[0]) { + addr -= psize[0]; + page++; + if (psize[1]) + psize++; + } + + return page; } -static int flash_addr_to_page_ceil(uint32_t addr) +/* returns the first page whose start addr is >= "addr" */ +int flash_addr_to_page_ceil(uint32_t addr) { + int page; + uint32_t *psize; + 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; + page = 0; + addr -= stm->dev->fl_start; + psize = stm->dev->fl_ps; + + while (addr >= psize[0]) { + addr -= psize[0]; + page++; + if (psize[1]) + psize++; + } + + return addr ? page + 1 : page; } +/* returns the lower address of flash page "page" */ static uint32_t flash_page_to_addr(int page) { - return stm->dev->fl_start + page * stm->dev->fl_ps; + int i; + uint32_t addr, *psize; + + addr = stm->dev->fl_start; + psize = stm->dev->fl_ps; + + for (i = 0; i < page; i++) { + addr += psize[0]; + if (psize[1]) + psize++; + } + + return addr; +} + + +#if defined(__WIN32__) || defined(__CYGWIN__) +BOOL CtrlHandler( DWORD fdwCtrlType ) +{ + fprintf(stderr, "\nCaught signal %lu\n",fdwCtrlType); + if (p_st && parser ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) port->close(port); + exit(1); +} +#else +void sighandler(int s){ + fprintf(stderr, "\nCaught signal %d\n",s); + if (p_st && parser ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) port->close(port); + exit(1); } +#endif 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; + diag = stdout; - fprintf(diag, "stm32flash " VERSION "\n\n"); - fprintf(diag, "http://stm32flash.googlecode.com/\n\n"); if (parse_options(argc, argv) != 0) goto close; - if (rd && filename[0] == '-') { + if (action == ACT_READ && use_stdinout) { diag = stderr; } - if (wr) { + fprintf(diag, "stm32flash " VERSION "\n\n"); + fprintf(diag, "https://github.com/stm32duino/stm32flash\n\n"); + +#if defined(__WIN32__) || defined(__CYGWIN__) + SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ); +#else + struct sigaction sigIntHandler; + + sigIntHandler.sa_handler = sighandler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + + sigaction(SIGINT, &sigIntHandler, NULL); +#endif + + if (action == ACT_WRITE) { /* first try hex */ if (!force_binary) { parser = &PARSER_HEX; @@ -181,8 +309,14 @@ int main(int argc, char* argv[]) { } fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); - if (init_flag && init_bl_entry(port, gpio_seq) == 0) + if (init_flag && init_bl_entry(port, gpio_seq)){ + ret = 1; + fprintf(stderr, "Failed to send boot enter sequence\n"); goto close; + } + + port->flush(port); + stm = stm32_init(port, init_flag); if (!stm) goto close; @@ -193,8 +327,8 @@ int main(int argc, char* argv[]) { 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); + fprintf(diag, "- RAM : Up to %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); + fprintf(diag, "- Flash : Up to %dKiB (size first sector: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps[0]); fprintf(diag, "- Option RAM : %db\n", stm->dev->opt_end - stm->dev->opt_start + 1); fprintf(diag, "- System RAM : %dKiB\n", (stm->dev->mem_end - stm->dev->mem_start) / 1024); @@ -221,8 +355,17 @@ int main(int argc, char* argv[]) { no_erase = 1; if (is_addr_in_ram(start)) end = stm->dev->ram_end; - else - end = start + sizeof(uint32_t); + else if (is_addr_in_opt_bytes(start)) + end = stm->dev->opt_end + 1; + else if (is_addr_in_sysmem(start)) + end = stm->dev->mem_end; + else { + /* Unknown territory */ + if (readwrite_len) + end = start + readwrite_len; + else + end = start + sizeof(uint32_t); + } } if (readwrite_len && (end > start + readwrite_len)) @@ -230,14 +373,14 @@ int main(int argc, char* argv[]) { first_page = flash_addr_to_page_floor(start); if (!first_page && end == stm->dev->fl_end) - num_pages = 0xff; /* mass erase */ + num_pages = STM32_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 */ + num_pages = STM32_MASS_ERASE; } else { first_page = spage; start = flash_page_to_addr(first_page); @@ -257,10 +400,10 @@ int main(int argc, char* argv[]) { } if (!first_page && end == stm->dev->fl_end) - num_pages = 0xff; /* mass erase */ + num_pages = STM32_MASS_ERASE; } - if (rd) { + if (action == ACT_READ) { unsigned int max_len = port_opts.rx_frame_max; fprintf(diag, "Memory read\n"); @@ -300,23 +443,33 @@ int main(int argc, char* argv[]) { fprintf(diag, "Done.\n"); ret = 0; goto close; - } else if (rp) { - fprintf(stdout, "Read-Protecting flash\n"); + } else if (action == ACT_READ_PROTECT) { + fprintf(diag, "Read-Protecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_readprot_memory(stm); - fprintf(stdout, "Done.\n"); - } else if (ur) { - fprintf(stdout, "Read-UnProtecting flash\n"); + s_err = stm32_readprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read-protect flash\n"); + goto close; + } + fprintf(diag, "Done.\n"); + ret = 0; + } else if (action == ACT_READ_UNPROTECT) { + fprintf(diag, "Read-UnProtecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_runprot_memory(stm); - fprintf(stdout, "Done.\n"); - } else if (eraseOnly) { + s_err = stm32_runprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read-unprotect flash\n"); + goto close; + } + fprintf(diag, "Done.\n"); + ret = 0; + } else if (action == ACT_ERASE_ONLY) { ret = 0; - fprintf(stdout, "Erasing flash\n"); + fprintf(diag, "Erasing flash\n"); - if (num_pages != 0xff && + if (num_pages != STM32_MASS_ERASE && (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"); @@ -330,18 +483,23 @@ int main(int argc, char* argv[]) { ret = 1; goto close; } - } else if (wu) { + ret = 0; + } else if (action == ACT_WRITE_UNPROTECT) { fprintf(diag, "Write-unprotecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_wunprot_memory(stm); + s_err = stm32_wunprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to write-unprotect flash\n"); + goto close; + } fprintf(diag, "Done.\n"); - - } else if (wr) { + ret = 0; + } else if (action == ACT_WRITE) { fprintf(diag, "Write to memory\n"); - off_t offset = 0; - ssize_t r; + unsigned int offset = 0; + unsigned int r; unsigned int size; unsigned int max_wlen, max_rlen; @@ -352,17 +510,17 @@ int main(int argc, char* argv[]) { max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; /* Assume data from stdin is whole device */ - if (filename[0] == '-' && filename[1] == '\0') + if (use_stdinout) size = end - start; else size = parser->size(p_st); // 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) { + // if ((start % stm->dev->fl_ps[i]) != 0 || (end % stm->dev->fl_ps[i]) != 0) { // fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); // goto close; - // } + // } // 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 @@ -386,14 +544,14 @@ int main(int argc, char* argv[]) { goto close; if (len == 0) { - if (filename[0] == '-') { + if (use_stdinout) { break; } else { fprintf(stderr, "Failed to read input file\n"); goto close; } } - + again: s_err = stm32_write_memory(stm, addr, buffer, len); if (s_err != STM32_ERR_OK) { @@ -450,7 +608,7 @@ int main(int argc, char* argv[]) { fprintf(diag, "Done.\n"); ret = 0; goto close; - } else if (crc) { + } else if (action == ACT_CRC) { uint32_t crc_val = 0; fprintf(diag, "CRC computation\n"); @@ -482,11 +640,17 @@ int main(int argc, char* argv[]) { } if (stm && reset_flag) { - fprintf(diag, "\nResetting device... "); + fprintf(diag, "\nResetting device... \n"); fflush(diag); - if (init_bl_exit(stm, port, gpio_seq)) - fprintf(diag, "done.\n"); - else fprintf(diag, "failed.\n"); + if (init_bl_exit(stm, port, gpio_seq)) { + ret = 1; + fprintf(diag, "Reset failed.\n"); + } else + fprintf(diag, "Reset done.\n"); + } else if (port) { + /* Always run exit sequence if present */ + if (gpio_seq && strchr(gpio_seq, ':')) + ret = gpio_bl_exit(port, gpio_seq) || ret; } if (p_st ) parser->close(p_st); @@ -533,14 +697,14 @@ int parse_options(int argc, char *argv[]) case 'r': case 'w': - rd = rd || c == 'r'; - wr = wr || c == 'w'; - if (rd && wr) { - fprintf(stderr, "ERROR: Invalid options, can't read & write at the same time\n"); + if (action != ACT_NONE) { + err_multi_action((c == 'r') ? ACT_READ : ACT_WRITE); return 1; } + action = (c == 'r') ? ACT_READ : ACT_WRITE; filename = optarg; - if (filename[0] == '-') { + if (filename[0] == '-' && filename[1] == '\0') { + use_stdinout = 1; force_binary = 1; } break; @@ -550,7 +714,7 @@ int parse_options(int argc, char *argv[]) return 1; } npages = strtoul(optarg, NULL, 0); - if (npages > 0xFF || npages < 0) { + if (npages > STM32_MAX_PAGES || npages < 0) { fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); return 1; } @@ -558,37 +722,37 @@ int parse_options(int argc, char *argv[]) no_erase = 1; break; case 'u': - wu = 1; - if (rd || wr) { - fprintf(stderr, "ERROR: Invalid options, can't write unprotect and read/write at the same time\n"); + if (action != ACT_NONE) { + err_multi_action(ACT_WRITE_UNPROTECT); return 1; } + action = ACT_WRITE_UNPROTECT; break; case 'j': - rp = 1; - if (rd || wr) { - fprintf(stderr, "ERROR: Invalid options, can't read protect and read/write at the same time\n"); + if (action != ACT_NONE) { + err_multi_action(ACT_READ_PROTECT); return 1; } + action = ACT_READ_PROTECT; break; case 'k': - ur = 1; - if (rd || wr) { - fprintf(stderr, "ERROR: Invalid options, can't read unprotect and read/write at the same time\n"); + if (action != ACT_NONE) { + err_multi_action(ACT_READ_UNPROTECT); return 1; } + action = ACT_READ_UNPROTECT; break; case 'o': - eraseOnly = 1; - if (rd || wr) { - fprintf(stderr, "ERROR: Invalid options, can't erase-only and read/write at the same time\n"); + if (action != ACT_NONE) { + err_multi_action(ACT_ERASE_ONLY); return 1; } - break; - + action = ACT_ERASE_ONLY; + break; + case 'v': verify = 1; break; @@ -644,9 +808,9 @@ int parse_options(int argc, char *argv[]) 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) { + || port_opts.tx_frame_max < 6) { fprintf(stderr, "ERROR: current code cannot work with small frames.\n"); - fprintf(stderr, "min(RX) = 20, min(TX) = 5\n"); + fprintf(stderr, "min(RX) = 20, min(TX) = 6\n"); return 1; } if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) { @@ -679,7 +843,11 @@ int parse_options(int argc, char *argv[]) break; case 'C': - crc = 1; + if (action != ACT_NONE) { + err_multi_action(ACT_CRC); + return 1; + } + action = ACT_CRC; break; } } @@ -699,7 +867,7 @@ int parse_options(int argc, char *argv[]) return 1; } - if (!wr && verify) { + if ((action != ACT_WRITE) && verify) { fprintf(stderr, "ERROR: Invalid usage, -v is only valid when writing\n"); show_help(argv[0]); return 1; @@ -734,10 +902,20 @@ void show_help(char *name) { " -c Resume the connection (don't send initial INIT)\n" " *Baud rate must be kept the same as the first init*\n" " This is useful if the reset fails\n" + " -R Reset device at exit.\n" " -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" + " sequence=[[-]signal]&|,[sequence]\n" + "\n" + "GPIO sequence:\n" + " The following signals can appear in a sequence:\n" + " Integer number representing GPIO pin\n" + " 'dtr', 'rts' or 'brk' representing serial port signal\n" + " The sequence can use the following delimiters:\n" + " ',' adds 100 ms delay between signals\n" + " '&' adds no delay between signals\n" + " The following modifiers can be prepended to a signal:\n" + " '-' reset signal (low) instead of setting it (high)\n" "\n" "Examples:\n" " Get device information:\n" @@ -758,9 +936,14 @@ void show_help(char *name) { " %s -g 0x0 /dev/ttyS0\n" "\n" " GPIO sequence:\n" - " - entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high\n" - " - exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high\n" - " %s -i -3,-2,2:3,-2,2 /dev/ttyS0\n", + " - entry sequence: GPIO_3=low, GPIO_2=low, 100ms delay, GPIO_2=high\n" + " - exit sequence: GPIO_3=high, GPIO_2=low, 300ms delay, GPIO_2=high\n" + " %s -i '-3&-2,2:3&-2,,,2' /dev/ttyS0\n" + " GPIO sequence adding delay after port opening:\n" + " - entry sequence: delay 500ms\n" + " - exit sequence: rts=high, dtr=low, 300ms delay, GPIO_2=high\n" + " %s -R -i ',,,,,:rts&-dtr,,,2' /dev/ttyS0\n", + name, name, name, name, diff --git a/parsers/Makefile.am b/parsers/Makefile.am new file mode 100644 index 0000000..033d49b --- /dev/null +++ b/parsers/Makefile.am @@ -0,0 +1,11 @@ +noinst_LTLIBRARIES = parsers.la + + +parsers_la_SOURCES = binary.c hex.c + +parsers_la_CXXFLAGS = -Wall -g + + +parsers_la_LDFLAGS = -module -avoid-version + + diff --git a/parsers/binary.c b/parsers/binary.c index f491952..a1a30b4 100644 --- a/parsers/binary.c +++ b/parsers/binary.c @@ -39,7 +39,7 @@ void* binary_init() { parser_err_t binary_open(void *storage, const char *filename, const char write) { binary_t *st = storage; if (write) { - if (filename[0] == '-') + if (filename[0] == '-' && filename[1] == '\0') st->fd = 1; else st->fd = open( @@ -57,7 +57,7 @@ parser_err_t binary_open(void *storage, const char *filename, const char write) ); st->stat.st_size = 0; } else { - if (filename[0] == '-') { + if (filename[0] == '-' && filename[1] == '\0') { st->fd = 0; } else { if (stat(filename, &st->stat) != 0) @@ -92,11 +92,13 @@ unsigned int binary_size(void *storage) { parser_err_t binary_read(void *storage, void *data, unsigned int *len) { binary_t *st = storage; unsigned int left = *len; + unsigned char *d = data; + if (st->write) return PARSER_ERR_WRONLY; ssize_t r; while(left > 0) { - r = read(st->fd, data, left); + r = read(st->fd, d, left); /* If there is no data to read at all, return OK, but with zero read */ if (r == 0 && left == *len) { *len = 0; @@ -104,7 +106,7 @@ parser_err_t binary_read(void *storage, void *data, unsigned int *len) { } if (r <= 0) return PARSER_ERR_SYSTEM; left -= r; - data += r; + d += r; } *len = *len - left; @@ -113,16 +115,18 @@ parser_err_t binary_read(void *storage, void *data, unsigned int *len) { parser_err_t binary_write(void *storage, void *data, unsigned int len) { binary_t *st = storage; + unsigned char *d = data; + if (!st->write) return PARSER_ERR_RDONLY; ssize_t r; while(len > 0) { - r = write(st->fd, data, len); + r = write(st->fd, d, len); if (r < 1) return PARSER_ERR_SYSTEM; st->stat.st_size += r; len -= r; - data += r; + d += r; } return PARSER_ERR_OK; diff --git a/parsers/hex.c b/parsers/hex.c index 3baf856..b3eaddf 100644 --- a/parsers/hex.c +++ b/parsers/hex.c @@ -27,12 +27,13 @@ #include #include "hex.h" +#include "../compiler.h" #include "../utils.h" typedef struct { size_t data_len, offset; uint8_t *data; - uint8_t base; + uint32_t base; } hex_t; void* hex_init() { @@ -45,9 +46,9 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { return PARSER_ERR_RDONLY; } else { char mark; - int i, fd; + int fd; uint8_t checksum; - unsigned int c; + unsigned int c, i; uint32_t base = 0; unsigned int last_address = 0x0; @@ -105,7 +106,7 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { /* extended linear address record */ case 4: - base = address; + base = 0; break; } @@ -152,12 +153,18 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { return PARSER_ERR_OK; /* address record */ + case 4: base = base << 12; + /* fall-through */ case 2: base = base << 4; - case 4: base = be_u32(base); /* Reset last_address since our base changed */ last_address = 0; - if (st->base == 0) { + /* Only assign the program's base address once, and only + * do so if we haven't seen any data records yet. + * If there are any data records before address records, + * the program's base address must be zero. + */ + if (st->base == 0 && st->data_len == 0) { st->base = base; break; } @@ -168,11 +175,11 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { return PARSER_ERR_INVALID_FILE; } - /* if there is a gap, enlarge and fill with zeros */ + /* if there is a gap, enlarge and fill with 0xff */ unsigned int len = base - st->base; if (len > st->data_len) { st->data = realloc(st->data, len); - memset(&st->data[st->data_len], 0, len - st->data_len); + memset(&st->data[st->data_len], 0xff, len - st->data_len); st->data_len = len; } break; @@ -208,7 +215,7 @@ parser_err_t hex_read(void *storage, void *data, unsigned int *len) { return PARSER_ERR_OK; } -parser_err_t hex_write(void *storage, void *data, unsigned int len) { +parser_err_t hex_write(void __unused *storage, void __unused *data, unsigned int __unused len) { return PARSER_ERR_RDONLY; } diff --git a/port.h b/port.h index 290f034..4e728d7 100644 --- a/port.h +++ b/port.h @@ -62,6 +62,7 @@ struct port_interface { 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 (*flush)(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); diff --git a/serial_posix.c b/serial_posix.c index 30b5ff8..00625b8 100644 --- a/serial_posix.c +++ b/serial_posix.c @@ -26,10 +26,18 @@ #include #include #include +#include +#include #include "serial.h" #include "port.h" +#ifndef TERMIOS_TIMEOUT_MS +#define TERMIOS_TIMEOUT_MS 500 +#endif + +#define TERMIOS_TIMEOUT ((TERMIOS_TIMEOUT_MS)/100) + struct serial { int fd; struct termios oldtio; @@ -46,6 +54,13 @@ static serial_t *serial_open(const char *device) free(h); return NULL; } + + if(lockf(h->fd,F_TLOCK,0) != 0) + { + fprintf(stderr, "Error: %s is already open\n", device); + free(h); + return NULL; + } fcntl(h->fd, F_SETFL, 0); tcgetattr(h->fd, &h->oldtio); @@ -63,6 +78,7 @@ static void serial_close(serial_t *h) { serial_flush(h); tcsetattr(h->fd, TCSANOW, &h->oldtio); + lockf(h->fd, F_ULOCK, 0); close(h->fd); free(h); } @@ -128,8 +144,8 @@ static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, 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; + case SERIAL_PARITY_EVEN: port_parity = PARENB; break; + case SERIAL_PARITY_ODD: port_parity = PARENB | PARODD; break; default: return PORT_ERR_UNKNOWN; @@ -173,9 +189,11 @@ static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, port_stop | CLOCAL | CREAD; + if ( port_parity != 0 ) + h->newtio.c_iflag |= INPCK; h->newtio.c_cc[VMIN] = 0; - h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ + h->newtio.c_cc[VTIME] = TERMIOS_TIMEOUT; /* in units of 0.1 s */ /* set the settings */ serial_flush(h); @@ -198,30 +216,12 @@ static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, return PORT_ERR_OK; } -static int startswith(const char *haystack, const char *needle) { - return strncmp(haystack, needle, strlen(needle)) == 0; -} - -static int is_tty(const char *path) { - char resolved[PATH_MAX]; - - if(!realpath(path, resolved)) return 0; - - if(startswith(resolved, "/dev/tty")) return 1; - - return 0; -} - static port_err_t serial_posix_open(struct port_interface *port, struct port_options *ops) { serial_t *h; - /* 1. check device name match */ - if (!is_tty(ops->device)) - return PORT_ERR_NODEV; - - /* 2. check options */ + /* 1. check options */ if (ops->baudRate == SERIAL_BAUD_INVALID) return PORT_ERR_UNKNOWN; if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) @@ -231,11 +231,15 @@ static port_err_t serial_posix_open(struct port_interface *port, if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) return PORT_ERR_UNKNOWN; - /* 3. open it */ + /* 2. open it */ h = serial_open(ops->device); if (h == NULL) return PORT_ERR_UNKNOWN; + /* 3. check for tty (but only warn) */ + if (!isatty(h->fd)) + fprintf(stderr, "Warning: Not a tty: %s\n", ops->device); + /* 4. set options */ if (serial_setup(h, ops->baudRate, serial_get_bits(ops->serial_mode), @@ -357,11 +361,24 @@ static const char *serial_posix_get_cfg_str(struct port_interface *port) return h ? h->setup_str : "INVALID"; } +static port_err_t serial_posix_flush(struct port_interface *port) +{ + serial_t *h; + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_flush(h); + + return PORT_ERR_OK; +} + 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, + .flush = serial_posix_flush, .read = serial_posix_read, .write = serial_posix_write, .gpio = serial_posix_gpio, diff --git a/serial_w32.c b/serial_w32.c index ceac3a3..97f7f24 100644 --- a/serial_w32.c +++ b/serial_w32.c @@ -27,6 +27,7 @@ #include #include +#include "compiler.h" #include "serial.h" #include "port.h" @@ -67,6 +68,7 @@ static serial_t *serial_open(const char *device) if (h->fd == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) fprintf(stderr, "File not found: %s\n", device); + free(h); return NULL; } @@ -76,6 +78,9 @@ static serial_t *serial_open(const char *device) SetCommMask(h->fd, EV_ERR); /* Notify us of error events */ + /* DCBlength should be initialized before calling GetCommState */ + h->oldtio.DCBlength = sizeof(DCB); + h->newtio.DCBlength = sizeof(DCB); GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */ GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */ @@ -84,7 +89,7 @@ static serial_t *serial_open(const char *device) return h; } -static void serial_flush(const serial_t *h) +static void serial_flush(const serial_t __unused *h) { /* We shouldn't need to flush in non-overlapping (blocking) mode */ /* tcflush(h->fd, TCIFLUSH); */ @@ -161,10 +166,15 @@ static port_err_t serial_setup(serial_t *h, /* reset the settings */ h->newtio.fOutxCtsFlow = FALSE; h->newtio.fOutxDsrFlow = FALSE; + h->newtio.fDtrControl = DTR_CONTROL_DISABLE; + h->newtio.fDsrSensitivity = FALSE; + h->newtio.fTXContinueOnXoff = FALSE; h->newtio.fOutX = FALSE; h->newtio.fInX = FALSE; - h->newtio.fNull = 0; - h->newtio.fAbortOnError = 0; + h->newtio.fErrorChar = FALSE; + h->newtio.fNull = FALSE; + h->newtio.fRtsControl = RTS_CONTROL_DISABLE; + h->newtio.fAbortOnError = FALSE; /* set the settings */ serial_flush(h); @@ -186,8 +196,7 @@ static port_err_t serial_w32_open(struct port_interface *port, serial_t *h; /* 1. check device name match */ - if (!(strlen(ops->device) == 4 - && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) + if (!(!strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM")) && isdigit(ops->device[strlen("\\\\.\\COM")]))) return PORT_ERR_NODEV; @@ -249,8 +258,6 @@ static port_err_t serial_w32_read(struct port_interface *port, void *buf, ReadFile(h->fd, pos, nbyte, &r, NULL); if (r == 0) return PORT_ERR_TIMEDOUT; - if (r < 0) - return PORT_ERR_UNKNOWN; nbyte -= r; pos += r; @@ -329,11 +336,24 @@ static const char *serial_w32_get_cfg_str(struct port_interface *port) return h ? h->setup_str : "INVALID"; } +static port_err_t serial_w32_flush(struct port_interface *port) +{ + serial_t *h; + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_flush(h); + + return PORT_ERR_OK; +} + 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, + .flush = serial_w32_flush, .read = serial_w32_read, .write = serial_w32_write, .gpio = serial_w32_gpio, diff --git a/stm32.c b/stm32.c index 74047d2..96c0e81 100644 --- a/stm32.c +++ b/stm32.c @@ -57,7 +57,7 @@ #define STM32_RESYNC_TIMEOUT 35 /* seconds */ #define STM32_MASSERASE_TIMEOUT 35 /* seconds */ -#define STM32_SECTERASE_TIMEOUT 5 /* seconds */ +#define STM32_PAGEERASE_TIMEOUT 5 /* seconds */ #define STM32_BLKWRITE_TIMEOUT 1 /* seconds */ #define STM32_WUNPROT_TIMEOUT 1 /* seconds */ #define STM32_WPROT_TIMEOUT 1 /* seconds */ @@ -95,8 +95,85 @@ static const uint8_t stm_reset_code[] = { static const uint32_t stm_reset_code_length = sizeof(stm_reset_code); +/* RM0360, Empty check + * On STM32F070x6 and STM32F030xC devices only, internal empty check flag is + * implemented to allow easy programming of the virgin devices by the boot loader. This flag is + * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the + * flag is set, the device is considered as empty and System memory (boot loader) is selected + * instead of the Main Flash as a boot space to allow user to program the Flash memory. + * This flag is updated only during Option bytes loading: it is set when the content of the + * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power + * on or setting of OBL_LAUNCH bit in FLASH_CR register is needed to clear this flag after + * programming of a virgin device to execute user code after System reset. + */ +static const uint8_t stm_obl_launch_code[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x10, 0x20, 0x02, 0x40, // address: FLASH_CR = 40022010 + 0x00, 0x20, 0x00, 0x00 // value: OBL_LAUNCH = 00002000 +}; + +static const uint32_t stm_obl_launch_code_length = sizeof(stm_obl_launch_code); + +/* RM0394, Empty check + * On STM32L452 (and possibly all STM32L45xxx/46xxx) internal empty check flag is + * implemented to allow easy programming of the virgin devices by the boot loader. This flag is + * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the + * flag is set, the device is considered as empty and System memory (boot loader) is selected + * instead of the Main Flash as a boot space to allow user to program the Flash memory. + * This flag is updated only during Option bytes loading: it is set when the content of the + * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power + * on or setting of OBL_LAUNCH bit in FLASH_CR register or a toggle of PEMPTY bit in FLASH_SR + * register is needed to clear this flag after after programming of a virgin device to execute + * user code after System reset. + * In STM32L45xxx/46xxx the register FLASH_CR could be locked and a special SW sequence is + * required for unlocking it. If a previous unsuccessful unlock has happened, a reset is + * required before the unlock. Due to such complications, toggling the PEMPTY bit in FLASH_SR + * seams the most reasonable choice. + * The code below check first word in flash and flag PEMPTY. If they do not match, then it + * toggles PEMPTY. At last, it resets. + */ + +static const uint8_t stm_pempty_launch_code[] = { + 0x08, 0x48, // ldr r0, [pc, #32] ; () + 0x00, 0x68, // ldr r0, [r0, #0] + 0x01, 0x30, // adds r0, #1 + 0x41, 0x1e, // subs r1, r0, #1 + 0x88, 0x41, // sbcs r0, r1 + + 0x07, 0x49, // ldr r1, [pc, #28] ; () + 0x07, 0x4a, // ldr r2, [pc, #28] ; () + 0x0b, 0x68, // ldr r3, [r1, #0] + 0x13, 0x40, // ands r3, r2 + 0x5c, 0x1e, // subs r4, r3, #1 + 0xa3, 0x41, // sbcs r3, r4 + + 0x98, 0x42, // cmp r0, r3 + 0x00, 0xd1, // bne.n skip1 + + 0x0a, 0x60, // str r2, [r1, #0] + + 0x04, 0x48, // skip1: ldr r0, [pc, #16] ; () + 0x05, 0x49, // ldr r1, [pc, #16] ; () + 0x01, 0x60, // str r1, [r0, #0] + + 0xfe, 0xe7, // endless: b.n endless + + 0x00, 0x00, 0x00, 0x08, // .word 0x08000000 + 0x10, 0x20, 0x02, 0x40, // .word 0x40022010 + 0x00, 0x00, 0x02, 0x00, // .word 0x00020000 + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 = VECTKEY | SYSRESETREQ +}; + +static const uint32_t stm_pempty_launch_code_length = sizeof(stm_pempty_launch_code); + extern const stm32_dev_t devices[]; +int flash_addr_to_page_ceil(uint32_t addr); + static void stm32_warn_stretching(const char *f) { fprintf(stderr, "Attention !!!\n"); @@ -540,8 +617,8 @@ stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, } /* must be 32bit aligned */ - if (address & 0x3 || len & 0x3) { - fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n"); + if (address & 0x3) { + fprintf(stderr, "Error: WRITE address must be 4 byte aligned\n"); return STM32_ERR_UNKNOWN; } @@ -582,7 +659,7 @@ stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->wm != STM32_CMD_WM_NS) stm32_warn_stretching("write"); return STM32_ERR_UNKNOWN; @@ -604,12 +681,12 @@ stm32_err_t stm32_wunprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT); - if (s_err == STM32_NACK) { + if (s_err == STM32_ERR_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 + if ((port->flags & PORT_STRETCH_W) && stm->cmd->uw != STM32_CMD_UW_NS) stm32_warn_stretching("WRITE UNPROTECT"); return STM32_ERR_UNKNOWN; @@ -631,12 +708,12 @@ stm32_err_t stm32_wprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT); - if (s_err == STM32_NACK) { + if (s_err == STM32_ERR_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 + if ((port->flags & PORT_STRETCH_W) && stm->cmd->wp != STM32_CMD_WP_NS) stm32_warn_stretching("WRITE PROTECT"); return STM32_ERR_UNKNOWN; @@ -658,12 +735,12 @@ stm32_err_t stm32_runprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); - if (s_err == STM32_NACK) { + if (s_err == STM32_ERR_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 + if ((port->flags & PORT_STRETCH_W) && stm->cmd->ur != STM32_CMD_UR_NS) stm32_warn_stretching("READOUT UNPROTECT"); return STM32_ERR_UNKNOWN; @@ -685,12 +762,12 @@ stm32_err_t stm32_readprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT); - if (s_err == STM32_NACK) { + if (s_err == STM32_ERR_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 + if ((port->flags & PORT_STRETCH_W) && stm->cmd->rp != STM32_CMD_RP_NS) stm32_warn_stretching("READOUT PROTECT"); return STM32_ERR_UNKNOWN; @@ -698,119 +775,68 @@ stm32_err_t stm32_readprot_memory(const stm32_t *stm) return STM32_ERR_OK; } -stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) +static stm32_err_t stm32_mass_erase(const stm32_t *stm) { struct port_interface *port = stm->port; stm32_err_t s_err; - port_err_t p_err; - - if (!pages) - return STM32_ERR_OK; - - if (stm->cmd->er == STM32_CMD_ERR) { - fprintf(stderr, "Error: ERASE command not implemented in bootloader.\n"); - return STM32_ERR_NO_CMD; - } + uint8_t buf[3]; if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { - fprintf(stderr, "Can't initiate chip erase!\n"); + fprintf(stderr, "Can't initiate chip mass erase!\n"); return STM32_ERR_UNKNOWN; } - /* 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. */ - /* 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) { - 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"); - 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; - } - - uint16_t pg_num; - uint8_t pg_byte; - 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; - buf[i++] = pg_byte; - cs ^= pg_byte; - pg_byte = (pages - 1) & 0xFF; - 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; - 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, pages * 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) { + /* regular erase (0x43) */ + if (stm->cmd->er == STM32_CMD_ER) { 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"); + stm32_warn_stretching("mass erase"); return STM32_ERR_UNKNOWN; } return STM32_ERR_OK; - } else { - uint8_t cs = 0; - uint8_t pg_num; - uint8_t *buf; - int i = 0; + } + + /* extended erase */ + buf[0] = 0xFF; /* 0xFFFF the magic number for mass erase */ + 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"); + if ((port->flags & PORT_STRETCH_W) + && stm->cmd->er != STM32_CMD_EE_NS) + stm32_warn_stretching("mass erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +static stm32_err_t stm32_pages_erase(const stm32_t *stm, uint32_t spage, uint32_t pages) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + uint32_t pg_num; + uint8_t pg_byte; + uint8_t cs = 0; + uint8_t *buf; + int i = 0; + /* 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. */ + /* 0x45 is clock no-stretching version of Extended Erase for I2C port. */ + if (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + fprintf(stderr, "Can't initiate chip mass erase!\n"); + return STM32_ERR_UNKNOWN; + } + + /* regular erase (0x43) */ + if (stm->cmd->er == STM32_CMD_ER) { buf = malloc(1 + pages + 1); if (!buf) return STM32_ERR_UNKNOWN; @@ -828,7 +854,7 @@ stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) fprintf(stderr, "Erase failed.\n"); return STM32_ERR_UNKNOWN; } - s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + s_err = stm32_get_ack_timeout(stm, pages * STM32_PAGEERASE_TIMEOUT); if (s_err != STM32_ERR_OK) { if (port->flags & PORT_STRETCH_W) stm32_warn_stretching("erase"); @@ -836,6 +862,92 @@ stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) } return STM32_ERR_OK; } + + /* extended erase */ + 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; + buf[i++] = pg_byte; + cs ^= pg_byte; + pg_byte = (pages - 1) & 0xFF; + 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; + 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, pages * STM32_PAGEERASE_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; +} + +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint32_t spage, uint32_t pages) +{ + uint32_t n; + stm32_err_t s_err; + + if (!pages || spage > STM32_MAX_PAGES || + ((pages != STM32_MASS_ERASE) && ((spage + pages) > STM32_MAX_PAGES))) + return STM32_ERR_OK; + + if (stm->cmd->er == STM32_CMD_ERR) { + fprintf(stderr, "Error: ERASE command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (pages == STM32_MASS_ERASE) { + /* + * Not all chips support mass erase. + * Mass erase can be obtained executing a "readout protect" + * followed by "readout un-protect". This method is not + * suggested because can hang the target if a debug SWD/JTAG + * is connected. When the target enters in "readout + * protection" mode it will consider the debug connection as + * a tentative of intrusion and will hang. + * Erasing the flash page-by-page is the safer way to go. + */ + if (!(stm->dev->flags & F_NO_ME)) + return stm32_mass_erase(stm); + + pages = flash_addr_to_page_ceil(stm->dev->fl_end); + } + + /* + * Some device, like STM32L152, cannot erase more than 512 pages in + * one command. Split the call. + */ + while (pages) { + n = (pages <= 512) ? pages : 512; + s_err = stm32_pages_erase(stm, spage, n); + if (s_err != STM32_ERR_OK) + return s_err; + spage += n; + pages -= n; + } + return STM32_ERR_OK; } static stm32_err_t stm32_run_raw_code(const stm32_t *stm, @@ -843,7 +955,7 @@ static stm32_err_t stm32_run_raw_code(const stm32_t *stm, 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 code_address_le = le_u32(target_address + 8 + 1); // thumb mode address (!) uint32_t length = code_size + 8; uint8_t *mem, *pos; uint32_t address, w; @@ -910,7 +1022,15 @@ 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); + if (stm->dev->flags & F_OBLL) { + /* set the OBL_LAUNCH bit to reset device (see RM0360, 2.5) */ + return stm32_run_raw_code(stm, target_address, stm_obl_launch_code, stm_obl_launch_code_length); + } else if (stm->dev->flags & F_PEMPTY) { + /* clear the PEMPTY bit to reset the device (see RM0394) */ + return stm32_run_raw_code(stm, target_address, stm_pempty_launch_code, stm_pempty_launch_code_length); + } else { + 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, @@ -919,7 +1039,7 @@ stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, struct port_interface *port = stm->port; uint8_t buf[5]; - if (address & 0x3 || length & 0x3) { + if ((address & 0x3) || (length & 0x3)) { fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); return STM32_ERR_UNKNOWN; } @@ -1012,7 +1132,7 @@ stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint8_t buf[256]; uint32_t start, total_len, len, current_crc; - if (address & 0x3 || length & 0x3) { + if ((address & 0x3) || (length & 0x3)) { fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); return STM32_ERR_UNKNOWN; } diff --git a/stm32.h b/stm32.h index 1688fcb..eb9e79e 100644 --- a/stm32.h +++ b/stm32.h @@ -27,6 +27,9 @@ #define STM32_MAX_RX_FRAME 256 /* cmd read memory */ #define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */ +#define STM32_MAX_PAGES 0x0000ffff +#define STM32_MASS_ERASE 0x00100000 /* > 2 x max_pages */ + typedef enum { STM32_ERR_OK = 0, STM32_ERR_UNKNOWN, /* Generic error */ @@ -34,6 +37,12 @@ typedef enum { STM32_ERR_NO_CMD, /* Command not available in bootloader */ } stm32_err_t; +typedef enum { + F_NO_ME = 1 << 0, /* Mass-Erase not supported */ + F_OBLL = 1 << 1, /* OBL_LAUNCH required */ + F_PEMPTY = 1 << 2, /* clear PEMPTY bit required */ +} flags_t; + typedef struct stm32 stm32_t; typedef struct stm32_cmd stm32_cmd_t; typedef struct stm32_dev stm32_dev_t; @@ -55,9 +64,10 @@ struct stm32_dev { uint32_t ram_start, ram_end; uint32_t fl_start, fl_end; uint16_t fl_pps; // pages per sector - uint16_t fl_ps; // page size + uint32_t *fl_ps; // page size uint32_t opt_start, opt_end; uint32_t mem_start, mem_end; + uint32_t flags; }; stm32_t *stm32_init(struct port_interface *port, const char init); @@ -68,8 +78,8 @@ 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_erase_memory(const stm32_t *stm, uint32_t spage, + uint32_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); diff --git a/stm32flash.1 b/stm32flash.1 index d37292f..6870ae0 100644 --- a/stm32flash.1 +++ b/stm32flash.1 @@ -1,6 +1,6 @@ -.TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command" +.TH STM32FLASH 1 "2015\-11\-25" STM32FLASH "User command" .SH NAME -stm32flash \- flashing utility for STM32 and STM32W through UART or I2C +stm32flash \- flashing utility for STM32 through UART or I2C .SH SYNOPSIS .B stm32flash .RB [ \-cfhjkouvCR ] @@ -29,19 +29,21 @@ stm32flash \- flashing utility for STM32 and STM32W through UART or I2C .RB [ \-i .IR GPIO_string ] .RI [ tty_device -.R | +| .IR i2c_device ] .SH DESCRIPTION .B stm32flash -reads or writes the flash memory of STM32 and STM32W. +reads or writes the flash memory of STM32. -It requires the STM32[W] to embed a bootloader compliant with ST -application note AN3155. +It requires the STM32 to embed a bootloader compliant with ST +application note AN3155 or AN4221. .B stm32flash uses the serial port .I tty_device -to interact with the bootloader of STM32[W]. +or the i2c port +.I i2c_device +to interact with the bootloader of STM32. .SH OPTIONS .TP @@ -55,7 +57,7 @@ This option is mandatory for I2C interface. Specify baud rate speed of .IR tty_device . Please notice that the ST bootloader can automatically detect the baud rate, -as explaned in chapter 2 of AN3155. +as explained in chapter 2 of AN3155. This option could be required together with option .B "\-c" or if following interaction with bootloader is expected. @@ -68,7 +70,7 @@ Specify the format of UART data. .I mode is a three characters long string where each character specifies, in this strict order, character size, parity and stop bits. -The only values currenly used are +The only values currently used are .I 8e1 for standard STM32 bootloader and .I 8n1 @@ -78,27 +80,27 @@ Default is .TP .BI "\-r" " filename" -Specify to read the STM32[W] flash and write its content in +Specify to read the STM32 flash and write its content in .I filename in raw binary format (see below .BR "FORMAT CONVERSION" ). .TP .BI "\-w" " filename" -Specify to write the STM32[W] flash with the content of +Specify to write the STM32 flash with the content of .IR filename . File format can be either raw binary or intel hex (see below .BR "FORMAT CONVERSION" ). The file format is automatically detected. To by\-pass format detection and force binary mode (e.g. to -write an intel hex content in STM32[W] flash), use +write an intel hex content in STM32 flash), use .B \-f option. .TP .B \-u -Specify to disable write\-protection from STM32[W] flash. -The STM32[W] will be reset after this operation. +Specify to disable write\-protection from STM32 flash. +The STM32 will be reset after this operation. .TP .B \-j @@ -167,9 +169,9 @@ existing connection. This is useful if the reset fails. .TP .BI "\-i" " GPIO_string" -Specify the GPIO sequences on the host to force STM32[W] to enter and +Specify the GPIO sequences on the host to force STM32 to enter and exit bootloader mode. GPIO can either be real GPIO connected from host to -STM32[W] beside the UART connection, or UART's modem signals used as +STM32 beside the UART connection, or UART's modem signals used as GPIO. (See below .B BOOTLOADER GPIO SEQUENCE for the format of @@ -224,8 +226,9 @@ The format of .RS GPIO_string = [entry sequence][:[exit sequence]] .P -sequence = [\-]n[,sequence] +sequence = [[\-]signal]&|,[sequence] .RE +.PD .P In the above sequences, negative numbers correspond to GPIO at "low" level; numbers without sign correspond to GPIO at "high" level. @@ -236,6 +239,20 @@ The string "brk" forces the UART to send a BREAK sequence on TX line; after BREAK the UART is returned in normal "non\-break" mode. Note: the string "\-brk" has no effect and is ignored. .PD +.P +The ',' delimiter adds 100 ms of delay between signal toggles, whereas +the '&' delimiter adds no delay. +An empty signal, thus repeated ',' delimiters, can be used to insert larger +delays in multiples of 100 ms. +E.g. "rts,,,,\-dtr" will set RTS, then wait 400 ms, then reset DTR. +"rts&\-dtr" will set RTS and reset DTR without delay. You can use ',' delimiters +alone to simply add a delay between opening port and starting to flash. +.DP +.P +Note that since version 0.6, an exit sequence will always be executed if +specified, regardless of the -R option, to ensure the signals are reset. +If -R is specified, but no exit sequence, a software-triggered reset will +be performed. .PD 0 As example, let's suppose the following connection between host and STM32: @@ -255,7 +272,7 @@ The corresponding string for .I GPIO_string is "4,\-5,\-3,3". -To exit from bootloade and run the application program, the sequence is: +To exit from bootloader and run the application program, the sequence is: put GPIO_4="low"; then send reset pulse. The corresponding string for .I GPIO_string @@ -271,15 +288,30 @@ As example, supposing GPIO_3 connected to PA5 and GPIO_2 to STM32W's reset. The command: .PD 0 .RS -stm32flash \-i \-3,\-2,2:3,\-2,2 /dev/ttyS0 +stm32flash \-i '\-3&\-2,2:3&\-2,,,2' /dev/ttyS0 +.RE +provides: +.IP \(bu 2 +entry sequence: GPIO_3=low, GPIO_2=low, 100 ms delay, GPIO_2=high +.IP \(bu 2 +exit sequence: GPIO_3=high, GPIO_2=low, 300 ms delay, GPIO_2=high +.PD + + +GPIO sequence to bring delays on start after port opening. +The command: +.PD 0 +.RS +stm32flash \-i ',,,,,:rts&\-dtr,,,2' /dev/ttyS0\n", .RE provides: .IP \(bu 2 -entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high +entry sequence: delay 500 ms .IP \(bu 2 -exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high +exit sequence: RTS=high, DTR=low, 300 ms delay, GPIO_2=high .PD + .SH EXAMPLES Get device information: .RS @@ -343,7 +375,7 @@ Man page and extension to STM32W and I2C are written by .IR "Antonio Borneo " . Please report any bugs at the project homepage -http://stm32flash.googlecode.com . +http://stm32flash.sourceforge.net . .SH SEE ALSO .BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)." diff --git a/utils.c b/utils.c index 271bb3e..4bfba17 100644 --- a/utils.c +++ b/utils.c @@ -43,3 +43,10 @@ uint32_t le_u32(const uint32_t v) { ((v & 0x000000FF) << 24); return v; } + +void printStatus(FILE *fd, int condition){ + if(condition) + fprintf(fd, "Error!\n"); + else + fprintf(fd, "OK\n"); +} diff --git a/utils.h b/utils.h index a8d37d2..07395d3 100644 --- a/utils.h +++ b/utils.h @@ -22,9 +22,12 @@ #define _H_UTILS #include +#include char cpu_le(); uint32_t be_u32(const uint32_t v); uint32_t le_u32(const uint32_t v); +void printStatus(FILE *fd, int condition); + #endif