From 7e06070ade5c72fa794dd823ed0669df1f3408d2 Mon Sep 17 00:00:00 2001 From: "roger@rogerclark.net" Date: Sat, 7 May 2016 08:02:54 +1000 Subject: [PATCH] Started to add linux 64 bit support, by copying the linux folder to linux64 and changeing platform.txt. Also copied scr folder into each platform's folder to allow the src to be distributed even if the tools are split up when using the Board Manager --- STM32F1/platform.txt | 23 +- tools/{ => linux}/src/build_dfu-util.sh | 0 tools/{ => linux}/src/dfu-util/AUTHORS | 60 +- tools/{ => linux}/src/dfu-util/COPYING | 680 ++-- tools/{ => linux}/src/dfu-util/ChangeLog | 186 +- tools/{ => linux}/src/dfu-util/DEVICES.txt | 40 +- tools/{ => linux}/src/dfu-util/Makefile.am | 6 +- tools/{ => linux}/src/dfu-util/README | 40 +- tools/{ => linux}/src/dfu-util/TODO | 28 +- tools/{ => linux}/src/dfu-util/autogen.sh | 0 tools/{ => linux}/src/dfu-util/configure.ac | 82 +- .../src/dfu-util/device-logs/README | 154 +- .../src/dfu-util/device-logs/dsonano.lsusb | 120 +- .../src/dfu-util/device-logs/lpclink.log | 118 +- .../src/dfu-util/device-logs/lpclink.lsusb | 116 +- .../src/dfu-util/device-logs/lpclink2.log | 118 +- .../src/dfu-util/device-logs/lpclink2.lsusb | 406 +-- .../src/dfu-util/device-logs/opc-20.lsusb | 120 +- .../openmoko-freerunner-dfumode.lsusb | 218 +- .../device-logs/openmoko-freerunner.lsusb | 358 +-- .../device-logs/openmoko-neo1973.lsusb | 364 +-- .../src/dfu-util/device-logs/openpcd.lsusb | 120 +- .../device-logs/qi-hardware-atusb.lsusb | 118 +- .../src/dfu-util/device-logs/simtrace.lsusb | 140 +- .../src/dfu-util/device-logs/sparkcore.lsusb | 120 +- .../device-logs/stm32f107.bin-download | 96 +- .../src/dfu-util/device-logs/stm32f107.lsusb | 120 +- .../device-logs/stm32f4discovery.bin-download | 72 +- .../device-logs/stm32f4discovery.lsusb | 160 +- .../dfu-util/device-logs/tdk-bluetooth.lsusb | 538 ++-- tools/{ => linux}/src/dfu-util/dfuse-pack.py | 242 +- .../src/dfu-util/doc/40-dfuse.rules | 8 +- .../{ => linux}/src/dfu-util/doc/Makefile.am | 4 +- .../src/dfu-util/doc/SPEC-differences.txt | 42 +- tools/{ => linux}/src/dfu-util/doc/dfu-util.1 | 496 +-- .../src/dfu-util/msvc/README_msvc.txt | 20 +- .../src/dfu-util/msvc/dfu-suffix_2010.vcxproj | 198 +- .../src/dfu-util/msvc/dfu-util_2010.sln | 108 +- .../src/dfu-util/msvc/dfu-util_2010.vcxproj | 238 +- .../{ => linux}/src/dfu-util/src/Makefile.am | 56 +- tools/{ => linux}/src/dfu-util/src/dfu.c | 714 ++--- tools/{ => linux}/src/dfu-util/src/dfu.h | 266 +- tools/{ => linux}/src/dfu-util/src/dfu_file.c | 888 +++--- tools/{ => linux}/src/dfu-util/src/dfu_file.h | 120 +- tools/{ => linux}/src/dfu-util/src/dfu_load.c | 392 +-- tools/{ => linux}/src/dfu-util/src/dfu_load.h | 14 +- tools/{ => linux}/src/dfu-util/src/dfu_util.c | 692 ++-- tools/{ => linux}/src/dfu-util/src/dfu_util.h | 72 +- tools/{ => linux}/src/dfu-util/src/dfuse.c | 1304 ++++---- tools/{ => linux}/src/dfu-util/src/dfuse.h | 70 +- .../{ => linux}/src/dfu-util/src/dfuse_mem.c | 396 +-- .../{ => linux}/src/dfu-util/src/dfuse_mem.h | 88 +- tools/{ => linux}/src/dfu-util/src/main.c | 1398 ++++---- tools/{ => linux}/src/dfu-util/src/portable.h | 144 +- tools/{ => linux}/src/dfu-util/src/prefix.c | 352 +- tools/{ => linux}/src/dfu-util/src/quirks.c | 112 +- tools/{ => linux}/src/dfu-util/src/quirks.h | 54 +- tools/{ => linux}/src/dfu-util/src/suffix.c | 352 +- tools/{ => linux}/src/dfu-util/src/usb_dfu.h | 198 +- tools/{ => linux}/src/dfu-util/www/build.html | 294 +- .../src/dfu-util/www/dfu-util.1.html | 822 ++--- tools/{ => linux}/src/dfu-util/www/dfuse.html | 270 +- tools/{ => linux}/src/dfu-util/www/index.html | 238 +- tools/{ => linux}/src/dfu-util/www/simple.css | 112 +- tools/{ => linux}/src/maple_loader/README.md | 0 tools/{ => linux}/src/maple_loader/build.xml | 146 +- .../maple_loader/build/built-jar.properties | 8 +- .../build/classes/CliTemplate/CliMain.class | Bin .../classes/CliTemplate/DFUUploader.class | Bin .../classes/CliTemplate/ExecCommand.class | Bin .../build/classes/processing/app/Base.class | Bin .../classes/processing/app/Preferences.class | Bin .../build/classes/processing/app/Serial.class | Bin .../processing/app/SerialException.class | Bin .../app/debug/MessageConsumer.class | Bin .../processing/app/debug/MessageSiphon.class | Bin .../app/debug/RunnerException.class | Bin .../processing/app/helpers/ProcessUtils.class | Bin .../src/maple_loader/dist/README.TXT | 2 +- .../src/maple_loader/dist/lib/jssc.jar | Bin .../src/maple_loader/dist/maple_loader.jar | Bin .../src/maple_loader/jars/jssc.jar | Bin .../{ => linux}/src/maple_loader/manifest.mf | 0 .../src/maple_loader/nbproject/build-impl.xml | 2826 ++++++++--------- .../nbproject/genfiles.properties | 16 +- .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 12 +- .../nbproject/private/private.xml | 20 +- .../maple_loader/nbproject/project.properties | 158 +- .../src/maple_loader/nbproject/project.xml | 30 +- .../maple_loader/src/CliTemplate/CliMain.java | 0 .../src/CliTemplate/DFUUploader.java | 0 .../src/CliTemplate/ExecCommand.java | 0 .../maple_loader/src/processing/app/Base.java | 0 .../src/processing/app/Preferences.java | 0 .../src/processing/app/Serial.java | 0 .../src/processing/app/SerialException.java | 0 .../processing/app/debug/MessageConsumer.java | 0 .../processing/app/debug/MessageSiphon.java | 0 .../processing/app/debug/RunnerException.java | 0 .../processing/app/helpers/ProcessUtils.java | 0 .../src/stm32flash_serial/src/AUTHORS | 0 .../src/stm32flash_serial/src/Android.mk | 0 .../src/stm32flash_serial/src/HOWTO | 0 .../src/stm32flash_serial/src/I2C.txt | 0 .../src/stm32flash_serial/src/Makefile | 0 .../src/stm32flash_serial/src/TODO | 0 .../src/stm32flash_serial/src/dev_table.c | 0 .../src/stm32flash_serial/src/gpl-2.0.txt | 0 .../src/stm32flash_serial/src/i2c.c | 0 .../src/stm32flash_serial/src/init.c | 0 .../src/stm32flash_serial/src/init.h | 0 .../src/stm32flash_serial/src/main.c | 0 .../stm32flash_serial/src/parsers/Android.mk | 0 .../stm32flash_serial/src/parsers/Makefile | 0 .../stm32flash_serial/src/parsers/binary.c | 0 .../stm32flash_serial/src/parsers/binary.h | 0 .../src/stm32flash_serial/src/parsers/hex.c | 0 .../src/stm32flash_serial/src/parsers/hex.h | 0 .../stm32flash_serial/src/parsers/parser.h | 0 .../src/stm32flash_serial/src/port.c | 0 .../src/stm32flash_serial/src/port.h | 0 .../src/stm32flash_serial/src/protocol.txt | 0 .../src/stm32flash_serial/src/serial.h | 0 .../src/stm32flash_serial/src/serial_common.c | 0 .../stm32flash_serial/src/serial_platform.c | 0 .../src/stm32flash_serial/src/serial_posix.c | 0 .../src/stm32flash_serial/src/serial_w32.c | 0 .../src/stm32flash_serial/src/stm32.c | 0 .../src/stm32flash_serial/src/stm32.h | 0 .../src/stm32flash_serial/src/stm32flash.1 | 0 .../src/stm32flash_serial/src/utils.c | 0 .../src/stm32flash_serial/src/utils.h | 0 .../src/upload-reset/upload-reset.c | 0 tools/linux64/45-maple.rules | 5 + tools/linux64/49-stlinkv1.rules | 11 + tools/linux64/49-stlinkv2-1.rules | 12 + tools/linux64/49-stlinkv2.rules | 12 + tools/linux64/dfu-util/dfu-prefix | Bin 0 -> 47440 bytes tools/linux64/dfu-util/dfu-suffix | Bin 0 -> 47528 bytes tools/linux64/dfu-util/dfu-util | Bin 0 -> 148808 bytes tools/linux64/install.sh | 24 + tools/linux64/maple_upload | 40 + tools/linux64/readme.txt | 1 + tools/linux64/serial_upload | 2 + tools/linux64/src/build_dfu-util.sh | 15 + tools/linux64/src/dfu-util/AUTHORS | 30 + tools/linux64/src/dfu-util/COPYING | 340 ++ tools/linux64/src/dfu-util/ChangeLog | 93 + tools/linux64/src/dfu-util/DEVICES.txt | 20 + tools/linux64/src/dfu-util/Makefile.am | 3 + tools/linux64/src/dfu-util/README | 20 + tools/linux64/src/dfu-util/TODO | 14 + tools/linux64/src/dfu-util/autogen.sh | 2 + tools/linux64/src/dfu-util/configure.ac | 41 + tools/linux64/src/dfu-util/device-logs/README | 77 + .../src/dfu-util/device-logs/dsonano.lsusb | 60 + .../src/dfu-util/device-logs/lpclink.log | 59 + .../src/dfu-util/device-logs/lpclink.lsusb | 58 + .../src/dfu-util/device-logs/lpclink2.log | 59 + .../src/dfu-util/device-logs/lpclink2.lsusb | 203 ++ .../src/dfu-util/device-logs/opc-20.lsusb | 60 + .../openmoko-freerunner-dfumode.lsusb | 109 + .../device-logs/openmoko-freerunner.lsusb | 179 ++ .../device-logs/openmoko-neo1973.lsusb | 182 ++ .../src/dfu-util/device-logs/openpcd.lsusb | 60 + .../device-logs/qi-hardware-atusb.lsusb | 59 + .../src/dfu-util/device-logs/simtrace.lsusb | 70 + .../src/dfu-util/device-logs/sparkcore.lsusb | 60 + .../device-logs/stm32f107.bin-download | 48 + .../src/dfu-util/device-logs/stm32f107.lsusb | 60 + .../device-logs/stm32f4discovery.bin-download | 36 + .../device-logs/stm32f4discovery.lsusb | 80 + .../dfu-util/device-logs/tdk-bluetooth.lsusb | 269 ++ tools/linux64/src/dfu-util/dfuse-pack.py | 121 + tools/linux64/src/dfu-util/doc/40-dfuse.rules | 4 + tools/linux64/src/dfu-util/doc/Makefile.am | 2 + .../src/dfu-util/doc/SPEC-differences.txt | 21 + tools/linux64/src/dfu-util/doc/dfu-util.1 | 248 ++ .../linux64/src/dfu-util/msvc/README_msvc.txt | 10 + .../src/dfu-util/msvc/dfu-suffix_2010.vcxproj | 100 + .../src/dfu-util/msvc/dfu-util_2010.sln | 54 + .../src/dfu-util/msvc/dfu-util_2010.vcxproj | 120 + tools/linux64/src/dfu-util/src/Makefile.am | 28 + tools/linux64/src/dfu-util/src/dfu.c | 357 +++ tools/linux64/src/dfu-util/src/dfu.h | 133 + tools/linux64/src/dfu-util/src/dfu_file.c | 444 +++ tools/linux64/src/dfu-util/src/dfu_file.h | 60 + tools/linux64/src/dfu-util/src/dfu_load.c | 196 ++ tools/linux64/src/dfu-util/src/dfu_load.h | 7 + tools/linux64/src/dfu-util/src/dfu_util.c | 346 ++ tools/linux64/src/dfu-util/src/dfu_util.h | 36 + tools/linux64/src/dfu-util/src/dfuse.c | 652 ++++ tools/linux64/src/dfu-util/src/dfuse.h | 35 + tools/linux64/src/dfu-util/src/dfuse_mem.c | 198 ++ tools/linux64/src/dfu-util/src/dfuse_mem.h | 44 + tools/linux64/src/dfu-util/src/main.c | 699 ++++ tools/linux64/src/dfu-util/src/portable.h | 72 + tools/linux64/src/dfu-util/src/prefix.c | 176 + tools/linux64/src/dfu-util/src/quirks.c | 56 + tools/linux64/src/dfu-util/src/quirks.h | 27 + tools/linux64/src/dfu-util/src/suffix.c | 176 + tools/linux64/src/dfu-util/src/usb_dfu.h | 99 + tools/linux64/src/dfu-util/www/build.html | 147 + .../linux64/src/dfu-util/www/dfu-util.1.html | 411 +++ tools/linux64/src/dfu-util/www/dfuse.html | 135 + tools/linux64/src/dfu-util/www/index.html | 119 + tools/linux64/src/dfu-util/www/simple.css | 56 + tools/linux64/src/maple_loader/README.md | 5 + tools/linux64/src/maple_loader/build.xml | 73 + .../maple_loader/build/built-jar.properties | 4 + .../build/classes/CliTemplate/CliMain.class | Bin 0 -> 1753 bytes .../classes/CliTemplate/DFUUploader.class | Bin 0 -> 7476 bytes .../classes/CliTemplate/ExecCommand.class | Bin 0 -> 3243 bytes .../build/classes/processing/app/Base.class | Bin 0 -> 639 bytes .../classes/processing/app/Preferences.class | Bin 0 -> 2215 bytes .../build/classes/processing/app/Serial.class | Bin 0 -> 7732 bytes .../processing/app/SerialException.class | Bin 0 -> 735 bytes .../app/debug/MessageConsumer.class | Bin 0 -> 174 bytes .../processing/app/debug/MessageSiphon.class | Bin 0 -> 2325 bytes .../app/debug/RunnerException.class | Bin 0 -> 2509 bytes .../processing/app/helpers/ProcessUtils.class | Bin 0 -> 1399 bytes .../linux64/src/maple_loader/dist/README.TXT | 32 + .../src/maple_loader/dist/lib/jssc.jar | Bin 0 -> 152418 bytes .../src/maple_loader/dist/maple_loader.jar | Bin 0 -> 32791 bytes tools/linux64/src/maple_loader/jars/jssc.jar | Bin 0 -> 152418 bytes tools/linux64/src/maple_loader/manifest.mf | 3 + .../src/maple_loader/nbproject/build-impl.xml | 1413 +++++++++ .../nbproject/genfiles.properties | 8 + .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../nbproject/private/private.xml | 10 + .../maple_loader/nbproject/project.properties | 79 + .../src/maple_loader/nbproject/project.xml | 15 + .../maple_loader/src/CliTemplate/CliMain.java | 60 + .../src/CliTemplate/DFUUploader.java | 345 ++ .../src/CliTemplate/ExecCommand.java | 119 + .../maple_loader/src/processing/app/Base.java | 53 + .../src/processing/app/Preferences.java | 157 + .../src/processing/app/Serial.java | 527 +++ .../src/processing/app/SerialException.java | 39 + .../processing/app/debug/MessageConsumer.java | 42 + .../processing/app/debug/MessageSiphon.java | 104 + .../processing/app/debug/RunnerException.java | 161 + .../processing/app/helpers/ProcessUtils.java | 32 + .../linux64/src/stm32flash_serial/src/AUTHORS | 19 + .../src/stm32flash_serial/src/Android.mk | 20 + tools/linux64/src/stm32flash_serial/src/HOWTO | 35 + .../linux64/src/stm32flash_serial/src/I2C.txt | 94 + .../src/stm32flash_serial/src/Makefile | 38 + tools/linux64/src/stm32flash_serial/src/TODO | 7 + .../src/stm32flash_serial/src/dev_table.c | 70 + .../src/stm32flash_serial/src/gpl-2.0.txt | 339 ++ tools/linux64/src/stm32flash_serial/src/i2c.c | 209 ++ .../linux64/src/stm32flash_serial/src/init.c | 219 ++ .../linux64/src/stm32flash_serial/src/init.h | 31 + .../linux64/src/stm32flash_serial/src/main.c | 774 +++++ .../stm32flash_serial/src/parsers/Android.mk | 6 + .../stm32flash_serial/src/parsers/Makefile | 12 + .../stm32flash_serial/src/parsers/binary.c | 140 + .../stm32flash_serial/src/parsers/binary.h | 27 + .../src/stm32flash_serial/src/parsers/hex.c | 224 ++ .../src/stm32flash_serial/src/parsers/hex.h | 27 + .../stm32flash_serial/src/parsers/parser.h | 56 + .../linux64/src/stm32flash_serial/src/port.c | 59 + .../linux64/src/stm32flash_serial/src/port.h | 75 + .../src/stm32flash_serial/src/protocol.txt | 19 + .../src/stm32flash_serial/src/serial.h | 90 + .../src/stm32flash_serial/src/serial_common.c | 154 + .../stm32flash_serial/src/serial_platform.c | 5 + .../src/stm32flash_serial/src/serial_posix.c | 395 +++ .../src/stm32flash_serial/src/serial_w32.c | 341 ++ .../linux64/src/stm32flash_serial/src/stm32.c | 1048 ++++++ .../linux64/src/stm32flash_serial/src/stm32.h | 84 + .../src/stm32flash_serial/src/stm32flash.1 | 407 +++ .../linux64/src/stm32flash_serial/src/utils.c | 45 + .../linux64/src/stm32flash_serial/src/utils.h | 30 + tools/linux64/src/upload-reset/upload-reset.c | 161 + tools/linux64/stlink/st-flash | Bin 0 -> 172752 bytes tools/linux64/stlink/st-info | Bin 0 -> 170300 bytes tools/linux64/stlink/st-term | Bin 0 -> 143235 bytes tools/linux64/stlink/st-util | Bin 0 -> 231592 bytes tools/linux64/stlink_upload | 45 + tools/linux64/stm32flash/stm32flash | Bin 0 -> 88981 bytes tools/linux64/upload-reset | Bin 0 -> 7946 bytes tools/linux64/upload_router | 112 + tools/macosx/src/build_dfu-util.sh | 15 + tools/macosx/src/dfu-util/AUTHORS | 30 + tools/macosx/src/dfu-util/COPYING | 340 ++ tools/macosx/src/dfu-util/ChangeLog | 93 + tools/macosx/src/dfu-util/DEVICES.txt | 20 + tools/macosx/src/dfu-util/Makefile.am | 3 + tools/macosx/src/dfu-util/README | 20 + tools/macosx/src/dfu-util/TODO | 14 + tools/macosx/src/dfu-util/autogen.sh | 2 + tools/macosx/src/dfu-util/configure.ac | 41 + tools/macosx/src/dfu-util/device-logs/README | 77 + .../src/dfu-util/device-logs/dsonano.lsusb | 60 + .../src/dfu-util/device-logs/lpclink.log | 59 + .../src/dfu-util/device-logs/lpclink.lsusb | 58 + .../src/dfu-util/device-logs/lpclink2.log | 59 + .../src/dfu-util/device-logs/lpclink2.lsusb | 203 ++ .../src/dfu-util/device-logs/opc-20.lsusb | 60 + .../openmoko-freerunner-dfumode.lsusb | 109 + .../device-logs/openmoko-freerunner.lsusb | 179 ++ .../device-logs/openmoko-neo1973.lsusb | 182 ++ .../src/dfu-util/device-logs/openpcd.lsusb | 60 + .../device-logs/qi-hardware-atusb.lsusb | 59 + .../src/dfu-util/device-logs/simtrace.lsusb | 70 + .../src/dfu-util/device-logs/sparkcore.lsusb | 60 + .../device-logs/stm32f107.bin-download | 48 + .../src/dfu-util/device-logs/stm32f107.lsusb | 60 + .../device-logs/stm32f4discovery.bin-download | 36 + .../device-logs/stm32f4discovery.lsusb | 80 + .../dfu-util/device-logs/tdk-bluetooth.lsusb | 269 ++ tools/macosx/src/dfu-util/dfuse-pack.py | 121 + tools/macosx/src/dfu-util/doc/40-dfuse.rules | 4 + tools/macosx/src/dfu-util/doc/Makefile.am | 2 + .../src/dfu-util/doc/SPEC-differences.txt | 21 + tools/macosx/src/dfu-util/doc/dfu-util.1 | 248 ++ .../macosx/src/dfu-util/msvc/README_msvc.txt | 10 + .../src/dfu-util/msvc/dfu-suffix_2010.vcxproj | 100 + .../src/dfu-util/msvc/dfu-util_2010.sln | 54 + .../src/dfu-util/msvc/dfu-util_2010.vcxproj | 120 + tools/macosx/src/dfu-util/src/Makefile.am | 28 + tools/macosx/src/dfu-util/src/dfu.c | 357 +++ tools/macosx/src/dfu-util/src/dfu.h | 133 + tools/macosx/src/dfu-util/src/dfu_file.c | 444 +++ tools/macosx/src/dfu-util/src/dfu_file.h | 60 + tools/macosx/src/dfu-util/src/dfu_load.c | 196 ++ tools/macosx/src/dfu-util/src/dfu_load.h | 7 + tools/macosx/src/dfu-util/src/dfu_util.c | 346 ++ tools/macosx/src/dfu-util/src/dfu_util.h | 36 + tools/macosx/src/dfu-util/src/dfuse.c | 652 ++++ tools/macosx/src/dfu-util/src/dfuse.h | 35 + tools/macosx/src/dfu-util/src/dfuse_mem.c | 198 ++ tools/macosx/src/dfu-util/src/dfuse_mem.h | 44 + tools/macosx/src/dfu-util/src/main.c | 699 ++++ tools/macosx/src/dfu-util/src/portable.h | 72 + tools/macosx/src/dfu-util/src/prefix.c | 176 + tools/macosx/src/dfu-util/src/quirks.c | 56 + tools/macosx/src/dfu-util/src/quirks.h | 27 + tools/macosx/src/dfu-util/src/suffix.c | 176 + tools/macosx/src/dfu-util/src/usb_dfu.h | 99 + tools/macosx/src/dfu-util/www/build.html | 147 + tools/macosx/src/dfu-util/www/dfu-util.1.html | 411 +++ tools/macosx/src/dfu-util/www/dfuse.html | 135 + tools/macosx/src/dfu-util/www/index.html | 119 + tools/macosx/src/dfu-util/www/simple.css | 56 + tools/macosx/src/maple_loader/README.md | 5 + tools/macosx/src/maple_loader/build.xml | 73 + .../maple_loader/build/built-jar.properties | 4 + .../build/classes/CliTemplate/CliMain.class | Bin 0 -> 1753 bytes .../classes/CliTemplate/DFUUploader.class | Bin 0 -> 7476 bytes .../classes/CliTemplate/ExecCommand.class | Bin 0 -> 3243 bytes .../build/classes/processing/app/Base.class | Bin 0 -> 639 bytes .../classes/processing/app/Preferences.class | Bin 0 -> 2215 bytes .../build/classes/processing/app/Serial.class | Bin 0 -> 7732 bytes .../processing/app/SerialException.class | Bin 0 -> 735 bytes .../app/debug/MessageConsumer.class | Bin 0 -> 174 bytes .../processing/app/debug/MessageSiphon.class | Bin 0 -> 2325 bytes .../app/debug/RunnerException.class | Bin 0 -> 2509 bytes .../processing/app/helpers/ProcessUtils.class | Bin 0 -> 1399 bytes tools/macosx/src/maple_loader/dist/README.TXT | 32 + .../macosx/src/maple_loader/dist/lib/jssc.jar | Bin 0 -> 152418 bytes .../src/maple_loader/dist/maple_loader.jar | Bin 0 -> 32791 bytes tools/macosx/src/maple_loader/jars/jssc.jar | Bin 0 -> 152418 bytes tools/macosx/src/maple_loader/manifest.mf | 3 + .../src/maple_loader/nbproject/build-impl.xml | 1413 +++++++++ .../nbproject/genfiles.properties | 8 + .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../nbproject/private/private.xml | 10 + .../maple_loader/nbproject/project.properties | 79 + .../src/maple_loader/nbproject/project.xml | 15 + .../maple_loader/src/CliTemplate/CliMain.java | 60 + .../src/CliTemplate/DFUUploader.java | 345 ++ .../src/CliTemplate/ExecCommand.java | 119 + .../maple_loader/src/processing/app/Base.java | 53 + .../src/processing/app/Preferences.java | 157 + .../src/processing/app/Serial.java | 527 +++ .../src/processing/app/SerialException.java | 39 + .../processing/app/debug/MessageConsumer.java | 42 + .../processing/app/debug/MessageSiphon.java | 104 + .../processing/app/debug/RunnerException.java | 161 + .../processing/app/helpers/ProcessUtils.java | 32 + .../macosx/src/stm32flash_serial/src/AUTHORS | 19 + .../src/stm32flash_serial/src/Android.mk | 20 + tools/macosx/src/stm32flash_serial/src/HOWTO | 35 + .../macosx/src/stm32flash_serial/src/I2C.txt | 94 + .../macosx/src/stm32flash_serial/src/Makefile | 38 + tools/macosx/src/stm32flash_serial/src/TODO | 7 + .../src/stm32flash_serial/src/dev_table.c | 70 + .../src/stm32flash_serial/src/gpl-2.0.txt | 339 ++ tools/macosx/src/stm32flash_serial/src/i2c.c | 209 ++ tools/macosx/src/stm32flash_serial/src/init.c | 219 ++ tools/macosx/src/stm32flash_serial/src/init.h | 31 + tools/macosx/src/stm32flash_serial/src/main.c | 774 +++++ .../stm32flash_serial/src/parsers/Android.mk | 6 + .../stm32flash_serial/src/parsers/Makefile | 12 + .../stm32flash_serial/src/parsers/binary.c | 140 + .../stm32flash_serial/src/parsers/binary.h | 27 + .../src/stm32flash_serial/src/parsers/hex.c | 224 ++ .../src/stm32flash_serial/src/parsers/hex.h | 27 + .../stm32flash_serial/src/parsers/parser.h | 56 + tools/macosx/src/stm32flash_serial/src/port.c | 59 + tools/macosx/src/stm32flash_serial/src/port.h | 75 + .../src/stm32flash_serial/src/protocol.txt | 19 + .../macosx/src/stm32flash_serial/src/serial.h | 90 + .../src/stm32flash_serial/src/serial_common.c | 154 + .../stm32flash_serial/src/serial_platform.c | 5 + .../src/stm32flash_serial/src/serial_posix.c | 395 +++ .../src/stm32flash_serial/src/serial_w32.c | 341 ++ .../macosx/src/stm32flash_serial/src/stm32.c | 1048 ++++++ .../macosx/src/stm32flash_serial/src/stm32.h | 84 + .../src/stm32flash_serial/src/stm32flash.1 | 407 +++ .../macosx/src/stm32flash_serial/src/utils.c | 45 + .../macosx/src/stm32flash_serial/src/utils.h | 30 + tools/macosx/src/upload-reset/upload-reset.c | 161 + tools/src/texane-stlink | 1 - tools/win/debugging.bat | 21 - tools/win/src/build_dfu-util.sh | 15 + tools/win/src/dfu-util/AUTHORS | 30 + tools/win/src/dfu-util/COPYING | 340 ++ tools/win/src/dfu-util/ChangeLog | 93 + tools/win/src/dfu-util/DEVICES.txt | 20 + tools/win/src/dfu-util/Makefile.am | 3 + tools/win/src/dfu-util/README | 20 + tools/win/src/dfu-util/TODO | 14 + tools/win/src/dfu-util/autogen.sh | 2 + tools/win/src/dfu-util/configure.ac | 41 + tools/win/src/dfu-util/device-logs/README | 77 + .../src/dfu-util/device-logs/dsonano.lsusb | 60 + .../win/src/dfu-util/device-logs/lpclink.log | 59 + .../src/dfu-util/device-logs/lpclink.lsusb | 58 + .../win/src/dfu-util/device-logs/lpclink2.log | 59 + .../src/dfu-util/device-logs/lpclink2.lsusb | 203 ++ .../win/src/dfu-util/device-logs/opc-20.lsusb | 60 + .../openmoko-freerunner-dfumode.lsusb | 109 + .../device-logs/openmoko-freerunner.lsusb | 179 ++ .../device-logs/openmoko-neo1973.lsusb | 182 ++ .../src/dfu-util/device-logs/openpcd.lsusb | 60 + .../device-logs/qi-hardware-atusb.lsusb | 59 + .../src/dfu-util/device-logs/simtrace.lsusb | 70 + .../src/dfu-util/device-logs/sparkcore.lsusb | 60 + .../device-logs/stm32f107.bin-download | 48 + .../src/dfu-util/device-logs/stm32f107.lsusb | 60 + .../device-logs/stm32f4discovery.bin-download | 36 + .../device-logs/stm32f4discovery.lsusb | 80 + .../dfu-util/device-logs/tdk-bluetooth.lsusb | 269 ++ tools/win/src/dfu-util/dfuse-pack.py | 121 + tools/win/src/dfu-util/doc/40-dfuse.rules | 4 + tools/win/src/dfu-util/doc/Makefile.am | 2 + .../win/src/dfu-util/doc/SPEC-differences.txt | 21 + tools/win/src/dfu-util/doc/dfu-util.1 | 248 ++ tools/win/src/dfu-util/msvc/README_msvc.txt | 10 + .../src/dfu-util/msvc/dfu-suffix_2010.vcxproj | 100 + tools/win/src/dfu-util/msvc/dfu-util_2010.sln | 54 + .../src/dfu-util/msvc/dfu-util_2010.vcxproj | 120 + tools/win/src/dfu-util/src/Makefile.am | 28 + tools/win/src/dfu-util/src/dfu.c | 357 +++ tools/win/src/dfu-util/src/dfu.h | 133 + tools/win/src/dfu-util/src/dfu_file.c | 444 +++ tools/win/src/dfu-util/src/dfu_file.h | 60 + tools/win/src/dfu-util/src/dfu_load.c | 196 ++ tools/win/src/dfu-util/src/dfu_load.h | 7 + tools/win/src/dfu-util/src/dfu_util.c | 346 ++ tools/win/src/dfu-util/src/dfu_util.h | 36 + tools/win/src/dfu-util/src/dfuse.c | 652 ++++ tools/win/src/dfu-util/src/dfuse.h | 35 + tools/win/src/dfu-util/src/dfuse_mem.c | 198 ++ tools/win/src/dfu-util/src/dfuse_mem.h | 44 + tools/win/src/dfu-util/src/main.c | 699 ++++ tools/win/src/dfu-util/src/portable.h | 72 + tools/win/src/dfu-util/src/prefix.c | 176 + tools/win/src/dfu-util/src/quirks.c | 56 + tools/win/src/dfu-util/src/quirks.h | 27 + tools/win/src/dfu-util/src/suffix.c | 176 + tools/win/src/dfu-util/src/usb_dfu.h | 99 + tools/win/src/dfu-util/www/build.html | 147 + tools/win/src/dfu-util/www/dfu-util.1.html | 411 +++ tools/win/src/dfu-util/www/dfuse.html | 135 + tools/win/src/dfu-util/www/index.html | 119 + tools/win/src/dfu-util/www/simple.css | 56 + tools/win/src/maple_loader/README.md | 5 + tools/win/src/maple_loader/build.xml | 73 + .../maple_loader/build/built-jar.properties | 4 + .../build/classes/CliTemplate/CliMain.class | Bin 0 -> 1753 bytes .../classes/CliTemplate/DFUUploader.class | Bin 0 -> 7476 bytes .../classes/CliTemplate/ExecCommand.class | Bin 0 -> 3243 bytes .../build/classes/processing/app/Base.class | Bin 0 -> 639 bytes .../classes/processing/app/Preferences.class | Bin 0 -> 2215 bytes .../build/classes/processing/app/Serial.class | Bin 0 -> 7732 bytes .../processing/app/SerialException.class | Bin 0 -> 735 bytes .../app/debug/MessageConsumer.class | Bin 0 -> 174 bytes .../processing/app/debug/MessageSiphon.class | Bin 0 -> 2325 bytes .../app/debug/RunnerException.class | Bin 0 -> 2509 bytes .../processing/app/helpers/ProcessUtils.class | Bin 0 -> 1399 bytes tools/win/src/maple_loader/dist/README.TXT | 32 + tools/win/src/maple_loader/dist/lib/jssc.jar | Bin 0 -> 152418 bytes .../src/maple_loader/dist/maple_loader.jar | Bin 0 -> 32791 bytes tools/win/src/maple_loader/jars/jssc.jar | Bin 0 -> 152418 bytes tools/win/src/maple_loader/manifest.mf | 3 + .../src/maple_loader/nbproject/build-impl.xml | 1413 +++++++++ .../nbproject/genfiles.properties | 8 + .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../nbproject/private/private.xml | 10 + .../maple_loader/nbproject/project.properties | 79 + .../src/maple_loader/nbproject/project.xml | 15 + .../maple_loader/src/CliTemplate/CliMain.java | 60 + .../src/CliTemplate/DFUUploader.java | 345 ++ .../src/CliTemplate/ExecCommand.java | 119 + .../maple_loader/src/processing/app/Base.java | 53 + .../src/processing/app/Preferences.java | 157 + .../src/processing/app/Serial.java | 527 +++ .../src/processing/app/SerialException.java | 39 + .../processing/app/debug/MessageConsumer.java | 42 + .../processing/app/debug/MessageSiphon.java | 104 + .../processing/app/debug/RunnerException.java | 161 + .../processing/app/helpers/ProcessUtils.java | 32 + tools/win/src/stm32flash_serial/src/AUTHORS | 19 + .../win/src/stm32flash_serial/src/Android.mk | 20 + tools/win/src/stm32flash_serial/src/HOWTO | 35 + tools/win/src/stm32flash_serial/src/I2C.txt | 94 + tools/win/src/stm32flash_serial/src/Makefile | 38 + tools/win/src/stm32flash_serial/src/TODO | 7 + .../win/src/stm32flash_serial/src/dev_table.c | 70 + .../win/src/stm32flash_serial/src/gpl-2.0.txt | 339 ++ tools/win/src/stm32flash_serial/src/i2c.c | 209 ++ tools/win/src/stm32flash_serial/src/init.c | 219 ++ tools/win/src/stm32flash_serial/src/init.h | 31 + tools/win/src/stm32flash_serial/src/main.c | 774 +++++ .../stm32flash_serial/src/parsers/Android.mk | 6 + .../stm32flash_serial/src/parsers/Makefile | 12 + .../stm32flash_serial/src/parsers/binary.c | 140 + .../stm32flash_serial/src/parsers/binary.h | 27 + .../src/stm32flash_serial/src/parsers/hex.c | 224 ++ .../src/stm32flash_serial/src/parsers/hex.h | 27 + .../stm32flash_serial/src/parsers/parser.h | 56 + tools/win/src/stm32flash_serial/src/port.c | 59 + tools/win/src/stm32flash_serial/src/port.h | 75 + .../src/stm32flash_serial/src/protocol.txt | 19 + tools/win/src/stm32flash_serial/src/serial.h | 90 + .../src/stm32flash_serial/src/serial_common.c | 154 + .../stm32flash_serial/src/serial_platform.c | 5 + .../src/stm32flash_serial/src/serial_posix.c | 395 +++ .../src/stm32flash_serial/src/serial_w32.c | 341 ++ tools/win/src/stm32flash_serial/src/stm32.c | 1048 ++++++ tools/win/src/stm32flash_serial/src/stm32.h | 84 + .../src/stm32flash_serial/src/stm32flash.1 | 407 +++ tools/win/src/stm32flash_serial/src/utils.c | 45 + tools/win/src/stm32flash_serial/src/utils.h | 30 + tools/win/src/upload-reset/upload-reset.c | 161 + tools/win/upload_router.bat | 56 - 555 files changed, 58520 insertions(+), 9441 deletions(-) rename tools/{ => linux}/src/build_dfu-util.sh (100%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/AUTHORS (93%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/COPYING (98%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/ChangeLog (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/DEVICES.txt (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/Makefile.am (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/README (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/TODO (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/autogen.sh (100%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/configure.ac (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/README (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/dsonano.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/lpclink.log (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/lpclink.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/lpclink2.log (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/lpclink2.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/opc-20.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/openmoko-freerunner.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/openmoko-neo1973.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/openpcd.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/qi-hardware-atusb.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/simtrace.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/sparkcore.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/stm32f107.bin-download (98%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/stm32f107.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/stm32f4discovery.bin-download (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/stm32f4discovery.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/device-logs/tdk-bluetooth.lsusb (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/dfuse-pack.py (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/doc/40-dfuse.rules (98%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/doc/Makefile.am (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/doc/SPEC-differences.txt (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/doc/dfu-util.1 (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/msvc/README_msvc.txt (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/msvc/dfu-suffix_2010.vcxproj (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/msvc/dfu-util_2010.sln (98%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/msvc/dfu-util_2010.vcxproj (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/Makefile.am (94%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu.h (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_file.c (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_file.h (95%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_load.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_load.h (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_util.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfu_util.h (95%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfuse.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfuse.h (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfuse_mem.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/dfuse_mem.h (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/main.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/portable.h (95%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/prefix.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/quirks.c (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/quirks.h (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/suffix.c (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/src/usb_dfu.h (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/www/build.html (96%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/www/dfu-util.1.html (95%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/www/dfuse.html (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/www/index.html (97%) mode change 100755 => 100644 rename tools/{ => linux}/src/dfu-util/www/simple.css (92%) mode change 100755 => 100644 rename tools/{ => linux}/src/maple_loader/README.md (100%) rename tools/{ => linux}/src/maple_loader/build.xml (97%) rename tools/{ => linux}/src/maple_loader/build/built-jar.properties (96%) rename tools/{ => linux}/src/maple_loader/build/classes/CliTemplate/CliMain.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/CliTemplate/DFUUploader.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/CliTemplate/ExecCommand.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/Base.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/Preferences.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/Serial.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/SerialException.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/debug/RunnerException.class (100%) rename tools/{ => linux}/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class (100%) rename tools/{ => linux}/src/maple_loader/dist/README.TXT (99%) rename tools/{ => linux}/src/maple_loader/dist/lib/jssc.jar (100%) rename tools/{ => linux}/src/maple_loader/dist/maple_loader.jar (100%) rename tools/{ => linux}/src/maple_loader/jars/jssc.jar (100%) rename tools/{ => linux}/src/maple_loader/manifest.mf (100%) rename tools/{ => linux}/src/maple_loader/nbproject/build-impl.xml (98%) rename tools/{ => linux}/src/maple_loader/nbproject/genfiles.properties (98%) rename tools/{ => linux}/src/maple_loader/nbproject/private/config.properties (100%) rename tools/{ => linux}/src/maple_loader/nbproject/private/private.properties (96%) rename tools/{ => linux}/src/maple_loader/nbproject/private/private.xml (98%) rename tools/{ => linux}/src/maple_loader/nbproject/project.properties (96%) rename tools/{ => linux}/src/maple_loader/nbproject/project.xml (97%) rename tools/{ => linux}/src/maple_loader/src/CliTemplate/CliMain.java (100%) rename tools/{ => linux}/src/maple_loader/src/CliTemplate/DFUUploader.java (100%) rename tools/{ => linux}/src/maple_loader/src/CliTemplate/ExecCommand.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/Base.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/Preferences.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/Serial.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/SerialException.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/debug/MessageConsumer.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/debug/MessageSiphon.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/debug/RunnerException.java (100%) rename tools/{ => linux}/src/maple_loader/src/processing/app/helpers/ProcessUtils.java (100%) rename tools/{ => linux}/src/stm32flash_serial/src/AUTHORS (100%) rename tools/{ => linux}/src/stm32flash_serial/src/Android.mk (100%) rename tools/{ => linux}/src/stm32flash_serial/src/HOWTO (100%) rename tools/{ => linux}/src/stm32flash_serial/src/I2C.txt (100%) rename tools/{ => linux}/src/stm32flash_serial/src/Makefile (100%) rename tools/{ => linux}/src/stm32flash_serial/src/TODO (100%) rename tools/{ => linux}/src/stm32flash_serial/src/dev_table.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/gpl-2.0.txt (100%) rename tools/{ => linux}/src/stm32flash_serial/src/i2c.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/init.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/init.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/main.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/Android.mk (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/Makefile (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/binary.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/binary.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/hex.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/hex.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/parsers/parser.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/port.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/port.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/protocol.txt (100%) rename tools/{ => linux}/src/stm32flash_serial/src/serial.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/serial_common.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/serial_platform.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/serial_posix.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/serial_w32.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/stm32.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/stm32.h (100%) rename tools/{ => linux}/src/stm32flash_serial/src/stm32flash.1 (100%) rename tools/{ => linux}/src/stm32flash_serial/src/utils.c (100%) rename tools/{ => linux}/src/stm32flash_serial/src/utils.h (100%) rename tools/{ => linux}/src/upload-reset/upload-reset.c (100%) create mode 100644 tools/linux64/45-maple.rules create mode 100644 tools/linux64/49-stlinkv1.rules create mode 100644 tools/linux64/49-stlinkv2-1.rules create mode 100644 tools/linux64/49-stlinkv2.rules create mode 100644 tools/linux64/dfu-util/dfu-prefix create mode 100644 tools/linux64/dfu-util/dfu-suffix create mode 100644 tools/linux64/dfu-util/dfu-util create mode 100644 tools/linux64/install.sh create mode 100644 tools/linux64/maple_upload create mode 100644 tools/linux64/readme.txt create mode 100644 tools/linux64/serial_upload create mode 100644 tools/linux64/src/build_dfu-util.sh create mode 100644 tools/linux64/src/dfu-util/AUTHORS create mode 100644 tools/linux64/src/dfu-util/COPYING create mode 100644 tools/linux64/src/dfu-util/ChangeLog create mode 100644 tools/linux64/src/dfu-util/DEVICES.txt create mode 100644 tools/linux64/src/dfu-util/Makefile.am create mode 100644 tools/linux64/src/dfu-util/README create mode 100644 tools/linux64/src/dfu-util/TODO create mode 100644 tools/linux64/src/dfu-util/autogen.sh create mode 100644 tools/linux64/src/dfu-util/configure.ac create mode 100644 tools/linux64/src/dfu-util/device-logs/README create mode 100644 tools/linux64/src/dfu-util/device-logs/dsonano.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/lpclink.log create mode 100644 tools/linux64/src/dfu-util/device-logs/lpclink.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/lpclink2.log create mode 100644 tools/linux64/src/dfu-util/device-logs/lpclink2.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/opc-20.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/openmoko-freerunner.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/openmoko-neo1973.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/openpcd.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/qi-hardware-atusb.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/simtrace.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/sparkcore.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/stm32f107.bin-download create mode 100644 tools/linux64/src/dfu-util/device-logs/stm32f107.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/stm32f4discovery.bin-download create mode 100644 tools/linux64/src/dfu-util/device-logs/stm32f4discovery.lsusb create mode 100644 tools/linux64/src/dfu-util/device-logs/tdk-bluetooth.lsusb create mode 100644 tools/linux64/src/dfu-util/dfuse-pack.py create mode 100644 tools/linux64/src/dfu-util/doc/40-dfuse.rules create mode 100644 tools/linux64/src/dfu-util/doc/Makefile.am create mode 100644 tools/linux64/src/dfu-util/doc/SPEC-differences.txt create mode 100644 tools/linux64/src/dfu-util/doc/dfu-util.1 create mode 100644 tools/linux64/src/dfu-util/msvc/README_msvc.txt create mode 100644 tools/linux64/src/dfu-util/msvc/dfu-suffix_2010.vcxproj create mode 100644 tools/linux64/src/dfu-util/msvc/dfu-util_2010.sln create mode 100644 tools/linux64/src/dfu-util/msvc/dfu-util_2010.vcxproj create mode 100644 tools/linux64/src/dfu-util/src/Makefile.am create mode 100644 tools/linux64/src/dfu-util/src/dfu.c create mode 100644 tools/linux64/src/dfu-util/src/dfu.h create mode 100644 tools/linux64/src/dfu-util/src/dfu_file.c create mode 100644 tools/linux64/src/dfu-util/src/dfu_file.h create mode 100644 tools/linux64/src/dfu-util/src/dfu_load.c create mode 100644 tools/linux64/src/dfu-util/src/dfu_load.h create mode 100644 tools/linux64/src/dfu-util/src/dfu_util.c create mode 100644 tools/linux64/src/dfu-util/src/dfu_util.h create mode 100644 tools/linux64/src/dfu-util/src/dfuse.c create mode 100644 tools/linux64/src/dfu-util/src/dfuse.h create mode 100644 tools/linux64/src/dfu-util/src/dfuse_mem.c create mode 100644 tools/linux64/src/dfu-util/src/dfuse_mem.h create mode 100644 tools/linux64/src/dfu-util/src/main.c create mode 100644 tools/linux64/src/dfu-util/src/portable.h create mode 100644 tools/linux64/src/dfu-util/src/prefix.c create mode 100644 tools/linux64/src/dfu-util/src/quirks.c create mode 100644 tools/linux64/src/dfu-util/src/quirks.h create mode 100644 tools/linux64/src/dfu-util/src/suffix.c create mode 100644 tools/linux64/src/dfu-util/src/usb_dfu.h create mode 100644 tools/linux64/src/dfu-util/www/build.html create mode 100644 tools/linux64/src/dfu-util/www/dfu-util.1.html create mode 100644 tools/linux64/src/dfu-util/www/dfuse.html create mode 100644 tools/linux64/src/dfu-util/www/index.html create mode 100644 tools/linux64/src/dfu-util/www/simple.css create mode 100644 tools/linux64/src/maple_loader/README.md create mode 100644 tools/linux64/src/maple_loader/build.xml create mode 100644 tools/linux64/src/maple_loader/build/built-jar.properties create mode 100644 tools/linux64/src/maple_loader/build/classes/CliTemplate/CliMain.class create mode 100644 tools/linux64/src/maple_loader/build/classes/CliTemplate/DFUUploader.class create mode 100644 tools/linux64/src/maple_loader/build/classes/CliTemplate/ExecCommand.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/Base.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/Preferences.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/Serial.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/SerialException.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/debug/RunnerException.class create mode 100644 tools/linux64/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class create mode 100644 tools/linux64/src/maple_loader/dist/README.TXT create mode 100644 tools/linux64/src/maple_loader/dist/lib/jssc.jar create mode 100644 tools/linux64/src/maple_loader/dist/maple_loader.jar create mode 100644 tools/linux64/src/maple_loader/jars/jssc.jar create mode 100644 tools/linux64/src/maple_loader/manifest.mf create mode 100644 tools/linux64/src/maple_loader/nbproject/build-impl.xml create mode 100644 tools/linux64/src/maple_loader/nbproject/genfiles.properties create mode 100644 tools/linux64/src/maple_loader/nbproject/private/config.properties create mode 100644 tools/linux64/src/maple_loader/nbproject/private/private.properties create mode 100644 tools/linux64/src/maple_loader/nbproject/private/private.xml create mode 100644 tools/linux64/src/maple_loader/nbproject/project.properties create mode 100644 tools/linux64/src/maple_loader/nbproject/project.xml create mode 100644 tools/linux64/src/maple_loader/src/CliTemplate/CliMain.java create mode 100644 tools/linux64/src/maple_loader/src/CliTemplate/DFUUploader.java create mode 100644 tools/linux64/src/maple_loader/src/CliTemplate/ExecCommand.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/Base.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/Preferences.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/Serial.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/SerialException.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/debug/MessageConsumer.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/debug/MessageSiphon.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/debug/RunnerException.java create mode 100644 tools/linux64/src/maple_loader/src/processing/app/helpers/ProcessUtils.java create mode 100644 tools/linux64/src/stm32flash_serial/src/AUTHORS create mode 100644 tools/linux64/src/stm32flash_serial/src/Android.mk create mode 100644 tools/linux64/src/stm32flash_serial/src/HOWTO create mode 100644 tools/linux64/src/stm32flash_serial/src/I2C.txt create mode 100644 tools/linux64/src/stm32flash_serial/src/Makefile create mode 100644 tools/linux64/src/stm32flash_serial/src/TODO create mode 100644 tools/linux64/src/stm32flash_serial/src/dev_table.c create mode 100644 tools/linux64/src/stm32flash_serial/src/gpl-2.0.txt create mode 100644 tools/linux64/src/stm32flash_serial/src/i2c.c create mode 100644 tools/linux64/src/stm32flash_serial/src/init.c create mode 100644 tools/linux64/src/stm32flash_serial/src/init.h create mode 100644 tools/linux64/src/stm32flash_serial/src/main.c create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/Android.mk create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/Makefile create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/binary.c create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/binary.h create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/hex.c create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/hex.h create mode 100644 tools/linux64/src/stm32flash_serial/src/parsers/parser.h create mode 100644 tools/linux64/src/stm32flash_serial/src/port.c create mode 100644 tools/linux64/src/stm32flash_serial/src/port.h create mode 100644 tools/linux64/src/stm32flash_serial/src/protocol.txt create mode 100644 tools/linux64/src/stm32flash_serial/src/serial.h create mode 100644 tools/linux64/src/stm32flash_serial/src/serial_common.c create mode 100644 tools/linux64/src/stm32flash_serial/src/serial_platform.c create mode 100644 tools/linux64/src/stm32flash_serial/src/serial_posix.c create mode 100644 tools/linux64/src/stm32flash_serial/src/serial_w32.c create mode 100644 tools/linux64/src/stm32flash_serial/src/stm32.c create mode 100644 tools/linux64/src/stm32flash_serial/src/stm32.h create mode 100644 tools/linux64/src/stm32flash_serial/src/stm32flash.1 create mode 100644 tools/linux64/src/stm32flash_serial/src/utils.c create mode 100644 tools/linux64/src/stm32flash_serial/src/utils.h create mode 100644 tools/linux64/src/upload-reset/upload-reset.c create mode 100644 tools/linux64/stlink/st-flash create mode 100644 tools/linux64/stlink/st-info create mode 100644 tools/linux64/stlink/st-term create mode 100644 tools/linux64/stlink/st-util create mode 100644 tools/linux64/stlink_upload create mode 100644 tools/linux64/stm32flash/stm32flash create mode 100644 tools/linux64/upload-reset create mode 100644 tools/linux64/upload_router create mode 100644 tools/macosx/src/build_dfu-util.sh create mode 100644 tools/macosx/src/dfu-util/AUTHORS create mode 100644 tools/macosx/src/dfu-util/COPYING create mode 100644 tools/macosx/src/dfu-util/ChangeLog create mode 100644 tools/macosx/src/dfu-util/DEVICES.txt create mode 100644 tools/macosx/src/dfu-util/Makefile.am create mode 100644 tools/macosx/src/dfu-util/README create mode 100644 tools/macosx/src/dfu-util/TODO create mode 100644 tools/macosx/src/dfu-util/autogen.sh create mode 100644 tools/macosx/src/dfu-util/configure.ac create mode 100644 tools/macosx/src/dfu-util/device-logs/README create mode 100644 tools/macosx/src/dfu-util/device-logs/dsonano.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/lpclink.log create mode 100644 tools/macosx/src/dfu-util/device-logs/lpclink.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/lpclink2.log create mode 100644 tools/macosx/src/dfu-util/device-logs/lpclink2.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/opc-20.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/openmoko-freerunner.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/openmoko-neo1973.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/openpcd.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/qi-hardware-atusb.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/simtrace.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/sparkcore.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/stm32f107.bin-download create mode 100644 tools/macosx/src/dfu-util/device-logs/stm32f107.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/stm32f4discovery.bin-download create mode 100644 tools/macosx/src/dfu-util/device-logs/stm32f4discovery.lsusb create mode 100644 tools/macosx/src/dfu-util/device-logs/tdk-bluetooth.lsusb create mode 100644 tools/macosx/src/dfu-util/dfuse-pack.py create mode 100644 tools/macosx/src/dfu-util/doc/40-dfuse.rules create mode 100644 tools/macosx/src/dfu-util/doc/Makefile.am create mode 100644 tools/macosx/src/dfu-util/doc/SPEC-differences.txt create mode 100644 tools/macosx/src/dfu-util/doc/dfu-util.1 create mode 100644 tools/macosx/src/dfu-util/msvc/README_msvc.txt create mode 100644 tools/macosx/src/dfu-util/msvc/dfu-suffix_2010.vcxproj create mode 100644 tools/macosx/src/dfu-util/msvc/dfu-util_2010.sln create mode 100644 tools/macosx/src/dfu-util/msvc/dfu-util_2010.vcxproj create mode 100644 tools/macosx/src/dfu-util/src/Makefile.am create mode 100644 tools/macosx/src/dfu-util/src/dfu.c create mode 100644 tools/macosx/src/dfu-util/src/dfu.h create mode 100644 tools/macosx/src/dfu-util/src/dfu_file.c create mode 100644 tools/macosx/src/dfu-util/src/dfu_file.h create mode 100644 tools/macosx/src/dfu-util/src/dfu_load.c create mode 100644 tools/macosx/src/dfu-util/src/dfu_load.h create mode 100644 tools/macosx/src/dfu-util/src/dfu_util.c create mode 100644 tools/macosx/src/dfu-util/src/dfu_util.h create mode 100644 tools/macosx/src/dfu-util/src/dfuse.c create mode 100644 tools/macosx/src/dfu-util/src/dfuse.h create mode 100644 tools/macosx/src/dfu-util/src/dfuse_mem.c create mode 100644 tools/macosx/src/dfu-util/src/dfuse_mem.h create mode 100644 tools/macosx/src/dfu-util/src/main.c create mode 100644 tools/macosx/src/dfu-util/src/portable.h create mode 100644 tools/macosx/src/dfu-util/src/prefix.c create mode 100644 tools/macosx/src/dfu-util/src/quirks.c create mode 100644 tools/macosx/src/dfu-util/src/quirks.h create mode 100644 tools/macosx/src/dfu-util/src/suffix.c create mode 100644 tools/macosx/src/dfu-util/src/usb_dfu.h create mode 100644 tools/macosx/src/dfu-util/www/build.html create mode 100644 tools/macosx/src/dfu-util/www/dfu-util.1.html create mode 100644 tools/macosx/src/dfu-util/www/dfuse.html create mode 100644 tools/macosx/src/dfu-util/www/index.html create mode 100644 tools/macosx/src/dfu-util/www/simple.css create mode 100644 tools/macosx/src/maple_loader/README.md create mode 100644 tools/macosx/src/maple_loader/build.xml create mode 100644 tools/macosx/src/maple_loader/build/built-jar.properties create mode 100644 tools/macosx/src/maple_loader/build/classes/CliTemplate/CliMain.class create mode 100644 tools/macosx/src/maple_loader/build/classes/CliTemplate/DFUUploader.class create mode 100644 tools/macosx/src/maple_loader/build/classes/CliTemplate/ExecCommand.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/Base.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/Preferences.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/Serial.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/SerialException.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/debug/RunnerException.class create mode 100644 tools/macosx/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class create mode 100644 tools/macosx/src/maple_loader/dist/README.TXT create mode 100644 tools/macosx/src/maple_loader/dist/lib/jssc.jar create mode 100644 tools/macosx/src/maple_loader/dist/maple_loader.jar create mode 100644 tools/macosx/src/maple_loader/jars/jssc.jar create mode 100644 tools/macosx/src/maple_loader/manifest.mf create mode 100644 tools/macosx/src/maple_loader/nbproject/build-impl.xml create mode 100644 tools/macosx/src/maple_loader/nbproject/genfiles.properties create mode 100644 tools/macosx/src/maple_loader/nbproject/private/config.properties create mode 100644 tools/macosx/src/maple_loader/nbproject/private/private.properties create mode 100644 tools/macosx/src/maple_loader/nbproject/private/private.xml create mode 100644 tools/macosx/src/maple_loader/nbproject/project.properties create mode 100644 tools/macosx/src/maple_loader/nbproject/project.xml create mode 100644 tools/macosx/src/maple_loader/src/CliTemplate/CliMain.java create mode 100644 tools/macosx/src/maple_loader/src/CliTemplate/DFUUploader.java create mode 100644 tools/macosx/src/maple_loader/src/CliTemplate/ExecCommand.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/Base.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/Preferences.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/Serial.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/SerialException.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/debug/MessageConsumer.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/debug/MessageSiphon.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/debug/RunnerException.java create mode 100644 tools/macosx/src/maple_loader/src/processing/app/helpers/ProcessUtils.java create mode 100644 tools/macosx/src/stm32flash_serial/src/AUTHORS create mode 100644 tools/macosx/src/stm32flash_serial/src/Android.mk create mode 100644 tools/macosx/src/stm32flash_serial/src/HOWTO create mode 100644 tools/macosx/src/stm32flash_serial/src/I2C.txt create mode 100644 tools/macosx/src/stm32flash_serial/src/Makefile create mode 100644 tools/macosx/src/stm32flash_serial/src/TODO create mode 100644 tools/macosx/src/stm32flash_serial/src/dev_table.c create mode 100644 tools/macosx/src/stm32flash_serial/src/gpl-2.0.txt create mode 100644 tools/macosx/src/stm32flash_serial/src/i2c.c create mode 100644 tools/macosx/src/stm32flash_serial/src/init.c create mode 100644 tools/macosx/src/stm32flash_serial/src/init.h create mode 100644 tools/macosx/src/stm32flash_serial/src/main.c create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/Android.mk create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/Makefile create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/binary.c create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/binary.h create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/hex.c create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/hex.h create mode 100644 tools/macosx/src/stm32flash_serial/src/parsers/parser.h create mode 100644 tools/macosx/src/stm32flash_serial/src/port.c create mode 100644 tools/macosx/src/stm32flash_serial/src/port.h create mode 100644 tools/macosx/src/stm32flash_serial/src/protocol.txt create mode 100644 tools/macosx/src/stm32flash_serial/src/serial.h create mode 100644 tools/macosx/src/stm32flash_serial/src/serial_common.c create mode 100644 tools/macosx/src/stm32flash_serial/src/serial_platform.c create mode 100644 tools/macosx/src/stm32flash_serial/src/serial_posix.c create mode 100644 tools/macosx/src/stm32flash_serial/src/serial_w32.c create mode 100644 tools/macosx/src/stm32flash_serial/src/stm32.c create mode 100644 tools/macosx/src/stm32flash_serial/src/stm32.h create mode 100644 tools/macosx/src/stm32flash_serial/src/stm32flash.1 create mode 100644 tools/macosx/src/stm32flash_serial/src/utils.c create mode 100644 tools/macosx/src/stm32flash_serial/src/utils.h create mode 100644 tools/macosx/src/upload-reset/upload-reset.c delete mode 160000 tools/src/texane-stlink delete mode 100644 tools/win/debugging.bat create mode 100644 tools/win/src/build_dfu-util.sh create mode 100644 tools/win/src/dfu-util/AUTHORS create mode 100644 tools/win/src/dfu-util/COPYING create mode 100644 tools/win/src/dfu-util/ChangeLog create mode 100644 tools/win/src/dfu-util/DEVICES.txt create mode 100644 tools/win/src/dfu-util/Makefile.am create mode 100644 tools/win/src/dfu-util/README create mode 100644 tools/win/src/dfu-util/TODO create mode 100644 tools/win/src/dfu-util/autogen.sh create mode 100644 tools/win/src/dfu-util/configure.ac create mode 100644 tools/win/src/dfu-util/device-logs/README create mode 100644 tools/win/src/dfu-util/device-logs/dsonano.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/lpclink.log create mode 100644 tools/win/src/dfu-util/device-logs/lpclink.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/lpclink2.log create mode 100644 tools/win/src/dfu-util/device-logs/lpclink2.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/opc-20.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/openmoko-freerunner.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/openmoko-neo1973.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/openpcd.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/qi-hardware-atusb.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/simtrace.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/sparkcore.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/stm32f107.bin-download create mode 100644 tools/win/src/dfu-util/device-logs/stm32f107.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/stm32f4discovery.bin-download create mode 100644 tools/win/src/dfu-util/device-logs/stm32f4discovery.lsusb create mode 100644 tools/win/src/dfu-util/device-logs/tdk-bluetooth.lsusb create mode 100644 tools/win/src/dfu-util/dfuse-pack.py create mode 100644 tools/win/src/dfu-util/doc/40-dfuse.rules create mode 100644 tools/win/src/dfu-util/doc/Makefile.am create mode 100644 tools/win/src/dfu-util/doc/SPEC-differences.txt create mode 100644 tools/win/src/dfu-util/doc/dfu-util.1 create mode 100644 tools/win/src/dfu-util/msvc/README_msvc.txt create mode 100644 tools/win/src/dfu-util/msvc/dfu-suffix_2010.vcxproj create mode 100644 tools/win/src/dfu-util/msvc/dfu-util_2010.sln create mode 100644 tools/win/src/dfu-util/msvc/dfu-util_2010.vcxproj create mode 100644 tools/win/src/dfu-util/src/Makefile.am create mode 100644 tools/win/src/dfu-util/src/dfu.c create mode 100644 tools/win/src/dfu-util/src/dfu.h create mode 100644 tools/win/src/dfu-util/src/dfu_file.c create mode 100644 tools/win/src/dfu-util/src/dfu_file.h create mode 100644 tools/win/src/dfu-util/src/dfu_load.c create mode 100644 tools/win/src/dfu-util/src/dfu_load.h create mode 100644 tools/win/src/dfu-util/src/dfu_util.c create mode 100644 tools/win/src/dfu-util/src/dfu_util.h create mode 100644 tools/win/src/dfu-util/src/dfuse.c create mode 100644 tools/win/src/dfu-util/src/dfuse.h create mode 100644 tools/win/src/dfu-util/src/dfuse_mem.c create mode 100644 tools/win/src/dfu-util/src/dfuse_mem.h create mode 100644 tools/win/src/dfu-util/src/main.c create mode 100644 tools/win/src/dfu-util/src/portable.h create mode 100644 tools/win/src/dfu-util/src/prefix.c create mode 100644 tools/win/src/dfu-util/src/quirks.c create mode 100644 tools/win/src/dfu-util/src/quirks.h create mode 100644 tools/win/src/dfu-util/src/suffix.c create mode 100644 tools/win/src/dfu-util/src/usb_dfu.h create mode 100644 tools/win/src/dfu-util/www/build.html create mode 100644 tools/win/src/dfu-util/www/dfu-util.1.html create mode 100644 tools/win/src/dfu-util/www/dfuse.html create mode 100644 tools/win/src/dfu-util/www/index.html create mode 100644 tools/win/src/dfu-util/www/simple.css create mode 100644 tools/win/src/maple_loader/README.md create mode 100644 tools/win/src/maple_loader/build.xml create mode 100644 tools/win/src/maple_loader/build/built-jar.properties create mode 100644 tools/win/src/maple_loader/build/classes/CliTemplate/CliMain.class create mode 100644 tools/win/src/maple_loader/build/classes/CliTemplate/DFUUploader.class create mode 100644 tools/win/src/maple_loader/build/classes/CliTemplate/ExecCommand.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/Base.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/Preferences.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/Serial.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/SerialException.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/debug/RunnerException.class create mode 100644 tools/win/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class create mode 100644 tools/win/src/maple_loader/dist/README.TXT create mode 100644 tools/win/src/maple_loader/dist/lib/jssc.jar create mode 100644 tools/win/src/maple_loader/dist/maple_loader.jar create mode 100644 tools/win/src/maple_loader/jars/jssc.jar create mode 100644 tools/win/src/maple_loader/manifest.mf create mode 100644 tools/win/src/maple_loader/nbproject/build-impl.xml create mode 100644 tools/win/src/maple_loader/nbproject/genfiles.properties create mode 100644 tools/win/src/maple_loader/nbproject/private/config.properties create mode 100644 tools/win/src/maple_loader/nbproject/private/private.properties create mode 100644 tools/win/src/maple_loader/nbproject/private/private.xml create mode 100644 tools/win/src/maple_loader/nbproject/project.properties create mode 100644 tools/win/src/maple_loader/nbproject/project.xml create mode 100644 tools/win/src/maple_loader/src/CliTemplate/CliMain.java create mode 100644 tools/win/src/maple_loader/src/CliTemplate/DFUUploader.java create mode 100644 tools/win/src/maple_loader/src/CliTemplate/ExecCommand.java create mode 100644 tools/win/src/maple_loader/src/processing/app/Base.java create mode 100644 tools/win/src/maple_loader/src/processing/app/Preferences.java create mode 100644 tools/win/src/maple_loader/src/processing/app/Serial.java create mode 100644 tools/win/src/maple_loader/src/processing/app/SerialException.java create mode 100644 tools/win/src/maple_loader/src/processing/app/debug/MessageConsumer.java create mode 100644 tools/win/src/maple_loader/src/processing/app/debug/MessageSiphon.java create mode 100644 tools/win/src/maple_loader/src/processing/app/debug/RunnerException.java create mode 100644 tools/win/src/maple_loader/src/processing/app/helpers/ProcessUtils.java create mode 100644 tools/win/src/stm32flash_serial/src/AUTHORS create mode 100644 tools/win/src/stm32flash_serial/src/Android.mk create mode 100644 tools/win/src/stm32flash_serial/src/HOWTO create mode 100644 tools/win/src/stm32flash_serial/src/I2C.txt create mode 100644 tools/win/src/stm32flash_serial/src/Makefile create mode 100644 tools/win/src/stm32flash_serial/src/TODO create mode 100644 tools/win/src/stm32flash_serial/src/dev_table.c create mode 100644 tools/win/src/stm32flash_serial/src/gpl-2.0.txt create mode 100644 tools/win/src/stm32flash_serial/src/i2c.c create mode 100644 tools/win/src/stm32flash_serial/src/init.c create mode 100644 tools/win/src/stm32flash_serial/src/init.h create mode 100644 tools/win/src/stm32flash_serial/src/main.c create mode 100644 tools/win/src/stm32flash_serial/src/parsers/Android.mk create mode 100644 tools/win/src/stm32flash_serial/src/parsers/Makefile create mode 100644 tools/win/src/stm32flash_serial/src/parsers/binary.c create mode 100644 tools/win/src/stm32flash_serial/src/parsers/binary.h create mode 100644 tools/win/src/stm32flash_serial/src/parsers/hex.c create mode 100644 tools/win/src/stm32flash_serial/src/parsers/hex.h create mode 100644 tools/win/src/stm32flash_serial/src/parsers/parser.h create mode 100644 tools/win/src/stm32flash_serial/src/port.c create mode 100644 tools/win/src/stm32flash_serial/src/port.h create mode 100644 tools/win/src/stm32flash_serial/src/protocol.txt create mode 100644 tools/win/src/stm32flash_serial/src/serial.h create mode 100644 tools/win/src/stm32flash_serial/src/serial_common.c create mode 100644 tools/win/src/stm32flash_serial/src/serial_platform.c create mode 100644 tools/win/src/stm32flash_serial/src/serial_posix.c create mode 100644 tools/win/src/stm32flash_serial/src/serial_w32.c create mode 100644 tools/win/src/stm32flash_serial/src/stm32.c create mode 100644 tools/win/src/stm32flash_serial/src/stm32.h create mode 100644 tools/win/src/stm32flash_serial/src/stm32flash.1 create mode 100644 tools/win/src/stm32flash_serial/src/utils.c create mode 100644 tools/win/src/stm32flash_serial/src/utils.h create mode 100644 tools/win/src/upload-reset/upload-reset.c delete mode 100644 tools/win/upload_router.bat diff --git a/STM32F1/platform.txt b/STM32F1/platform.txt index caca7fe..24864c1 100644 --- a/STM32F1/platform.txt +++ b/STM32F1/platform.txt @@ -107,15 +107,13 @@ recipe.output.save_file={build.project_name}.{build.variant}.bin # Uploader tools # ------------------- - - # Upload using Maple bootloader over DFU tools.maple_upload.cmd=maple_upload tools.maple_upload.cmd.windows=maple_upload.bat tools.maple_upload.path={runtime.hardware.path}/tools/win tools.maple_upload.path.macosx={runtime.hardware.path}/tools/macosx -tools.maple_upload.path.linux={runtime.hardware.path}/tools/linux - +tools.maple_upload.path.linux={runtime.hardware.path}/tools/linux +tools.maple_upload.path.linux64={runtime.hardware.path}/tools/linux64 tools.maple_upload.upload.params.verbose=-d tools.maple_upload.upload.params.quiet= tools.maple_upload.upload.pattern="{path}/{cmd}" {serial.port.file} {upload.altID} {upload.usbID} "{build.path}/{build.project_name}.bin" @@ -129,29 +127,18 @@ tools.serial_upload.cmd.macosx=serial_upload tools.serial_upload.path={runtime.hardware.path}/tools/win tools.serial_upload.path.macosx={runtime.hardware.path}/tools/macosx tools.serial_upload.path.linux={runtime.hardware.path}/tools/linux - +tools.serial_upload.path.linux64={runtime.hardware.path}/tools/linux64 tools.serial_upload.upload.params.verbose=-d tools.serial_upload.upload.params.quiet=n tools.serial_upload.upload.pattern="{path}/{cmd}" {serial.port.file} {upload.altID} {upload.usbID} "{build.path}/{build.project_name}.bin" - -tools.upload_router.cmd=upload_router -tools.upload_router.cmd.windows=upload_router.bat -tools.upload_router.path={runtime.hardware.path}/tools/win -tools.upload_router.path.macosx={runtime.hardware.path}/tools/macosx -tools.upload_router.path.linux={runtime.hardware.path}/tools/linux - -tools.upload_router.upload.params.verbose=-d -tools.upload_router.upload.params.quiet= -tools.upload_router.upload.pattern="{path}/{cmd}" {serial.port.file} {upload.altID} {upload.usbID} "{build.path}/{build.project_name}" {upload.protocol} {build.debuggingMode} "{runtime.ide.path}/hardware/tools/{build.gcc_ver}/bin/" - # stlink upload tools.stlink_upload.cmd=stlink_upload tools.stlink_upload.cmd.windows=stlink_upload.bat tools.stlink_upload.path.windows={runtime.hardware.path}/tools/win tools.stlink_upload.path.macosx={runtime.hardware.path}/tools/macosx tools.stlink_upload.path.linux={runtime.hardware.path}/tools/linux - +tools.stlink_upload.path.linux64={runtime.hardware.path}/tools/linux64 tools.stlink_upload.upload.params.verbose=-d tools.stlink_upload.upload.params.quiet= tools.stlink_upload.upload.pattern="{path}/{cmd}" {serial.port.file} {upload.altID} {upload.usbID} "{build.path}/{build.project_name}.bin" @@ -162,4 +149,4 @@ tools.bmp_upload.path={runtime.tools.arm-none-eabi-gcc.path}/bin/ tools.bmp_upload.upload.speed=230400 tools.bmp_upload.upload.params.verbose= tools.bmp_upload.upload.params.quiet=-q --batch-silent -tools.bmp_upload.upload.pattern="{path}{cmd}" -cd "{build.path}" -b {upload.speed} {upload.verbose} -ex "set debug remote 0" -ex "set target-async off" -ex "set remotetimeout 60" -ex "set mem inaccessible-by-default off" -ex "set confirm off" -ex "set height 0" -ex "target extended-remote {serial.port}" -ex "monitor swdp_scan" -ex "attach 1" -ex "x/wx 0x8000004" -ex "monitor erase_mass" -ex "echo 0x8000004 expect 0xffffffff after erase\n" -ex "x/wx 0x8000004" -ex "file {build.project_name}.elf" -ex "load" -ex "x/wx 0x08000004" -ex "tbreak main" -ex "run" -ex "echo \n\n\nUpload finished!" -ex "quit" +tools.bmp_upload.upload.pattern="{path}{cmd}" -cd "{build.path}" -b {upload.speed} {upload.verbose} -ex "set debug remote 0" -ex "set target-async off" -ex "set remotetimeout 60" -ex "set mem inaccessible-by-default off" -ex "set confirm off" -ex "set height 0" -ex "target extended-remote {serial.port}" -ex "monitor swdp_scan" -ex "attach 1" -ex "x/wx 0x8000004" -ex "monitor erase_mass" -ex "echo 0x8000004 expect 0xffffffff after erase\n" -ex "x/wx 0x8000004" -ex "file {build.project_name}.elf" -ex "load" -ex "x/wx 0x08000004" -ex "tbreak main" -ex "run" -ex "echo \n\n\nUpload finished!" -ex "quit" \ No newline at end of file diff --git a/tools/src/build_dfu-util.sh b/tools/linux/src/build_dfu-util.sh old mode 100755 new mode 100644 similarity index 100% rename from tools/src/build_dfu-util.sh rename to tools/linux/src/build_dfu-util.sh diff --git a/tools/src/dfu-util/AUTHORS b/tools/linux/src/dfu-util/AUTHORS old mode 100755 new mode 100644 similarity index 93% rename from tools/src/dfu-util/AUTHORS rename to tools/linux/src/dfu-util/AUTHORS index bc16c9c..1b36c73 --- a/tools/src/dfu-util/AUTHORS +++ b/tools/linux/src/dfu-util/AUTHORS @@ -1,30 +1,30 @@ -Authors ordered by first contribution. - -Harald Welte -Werner Almesberger -Michael Lauer -Jim Huang -Stefan Schmidt -Daniel Willmann -Mike Frysinger -Uwe Hermann -C. Scott Ananian -Bernard Blackham -Holger Freyther -Marc Singer -James Perkins -Tommi Keisala -Pascal Schweizer -Bradley Scott -Uwe Bonnes -Andrey Smirnov -Jussi Timperi -Hans Petter Selasky -Bo Shen -Henrique de Almeida Mendonca -Bernd Krumboeck -Dennis Meier -Veli-Pekka Peltola -Dave Hylands -Michael Grzeschik -Paul Fertser +Authors ordered by first contribution. + +Harald Welte +Werner Almesberger +Michael Lauer +Jim Huang +Stefan Schmidt +Daniel Willmann +Mike Frysinger +Uwe Hermann +C. Scott Ananian +Bernard Blackham +Holger Freyther +Marc Singer +James Perkins +Tommi Keisala +Pascal Schweizer +Bradley Scott +Uwe Bonnes +Andrey Smirnov +Jussi Timperi +Hans Petter Selasky +Bo Shen +Henrique de Almeida Mendonca +Bernd Krumboeck +Dennis Meier +Veli-Pekka Peltola +Dave Hylands +Michael Grzeschik +Paul Fertser diff --git a/tools/src/dfu-util/COPYING b/tools/linux/src/dfu-util/COPYING old mode 100755 new mode 100644 similarity index 98% rename from tools/src/dfu-util/COPYING rename to tools/linux/src/dfu-util/COPYING index fbdd65f..d60c31a --- a/tools/src/dfu-util/COPYING +++ b/tools/linux/src/dfu-util/COPYING @@ -1,340 +1,340 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General -Public License instead of this License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/tools/src/dfu-util/ChangeLog b/tools/linux/src/dfu-util/ChangeLog old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/ChangeLog rename to tools/linux/src/dfu-util/ChangeLog index 35a1da7..37f1add --- a/tools/src/dfu-util/ChangeLog +++ b/tools/linux/src/dfu-util/ChangeLog @@ -1,93 +1,93 @@ -0.8: - o New, separate dfu-prefix tool (Uwe Bonnes) - o Allow filtering on serial number (Uwe Bonnes) - o Improved VID/PID/serial filtering (Bradley Scott) - o Support reading firmware from stdin (Tormod Volden) - o Warn if missing DFU suffix (Tormod Volden) - o Improved progress bar (Hans Petter Selasky) - o Fix dfuse leave option (Uwe Bonnes) - o Major code rework (Hans Petter Selasky) - o MS Visual Studio build support (Henrique Mendonca) - o dfuse-pack.py tool for .dfu files (Antonio Galeo) - o Many other fixes from many people - -2014-09-13: Tormod Volden - -0.7: - o Support for TI Stellaris devices (Tommi Keisala) - o Fix libusb detection on MacOSX (Marc Singer) - o Fix libusb detection on FreeBSD (Tormod Volden) - o Improved DfuSe support (Tormod Volden) - o Support all special commands (leave, unprotect, mass-erase) - o Arbitrary upload lengths - o "force" option for various possible (dangerous) overrides - -2012-10-07: Tormod Volden - -0.6: - o Add detach mode (Stefan Schmidt) - o Check return value on all libusb calls (Tormod Volden) - o Fix segmentation fault with -s option (Tormod Volden) - o Add DFU suffix manipulation tool (Stefan Schmidt) - o Port to Windows: (Tormod Volden, some parts based on work from Satz - Klauer) - o Port file handling to stdio streams - o Sleep() macros - o C99 types - o Pack structs - o Detect DfuSe device correctly on big-endian architectures (Tormod - Volden) - o Add dfuse progress indication on download (Tormod Volden) - o Cleanup: gcc pedantic, gcc extension, ... (Tormod Volden) - o Rely on page size from functional descriptor. Please report if you get - an error about it. (Tormod Volden) - o Add quirk for Maple since it reports wrong DFU version (Tormod Volden) - -2012-04-22: Stefan Schmidt - -0.5: - o DfuSe extension support for ST devices (Tormod Volden) - o Add initial support for bitWillDetach flag from DFU 1.1 (Tormod - Volden) - o Internal cleanup and some manual page fixes (Tormod Volden) - -2011-11-02: Stefan Schmidt - -0.4: - o Rework to use libusb-1.0 (Stefan Schmidt) - o DFU suffix support (Tormod Volden, Stefan Schmidt) - o Sspeed up DFU downloads directly into memory (Bernard Blackham) - o More flexible -d vid:pid parsing (Tormod Volden) - o Many bug fixes and cleanups - -2011-07-20: Stefan Schmidt - -0.3: - o quirks: Add OpenOCD to the poll timeout quirk table. - -2010-12-22: Stefan Schmidt - -0.2: - o Fix some typos on the website and the README (Antonio Ospite, Uwe - Hermann) - o Remove build rule for a static binary. We can use autotools for this. - (Mike Frysinger) - o Fix infinite loop in download error path (C. Scott Ananian) - o Break out to show the 'finished' in upload (C. Scott Ananian) - o Add GPLv2+ headers (Harald Welte) - o Remove dead code (commands.[ch]) remnescent of dfu-programmer (Harald - Welte) - o Simple quirk system with Openmoko quirk for missing bwPollTimeout (Tormod Volden) - o New default (1024) and clamping of transfer size (Tormod Volden) - o Verify sending of completion packet (Tormod Volden) - o Look for DFU functional descriptor among all descriptors (Tormod - Volden) - o Print out in which direction we are transferring data - o Abort in upload if the file already exists - -2010-11-17 Stefan Schmidt - -0.1: - Initial release - -2010-05-23 Stefan Schmidt +0.8: + o New, separate dfu-prefix tool (Uwe Bonnes) + o Allow filtering on serial number (Uwe Bonnes) + o Improved VID/PID/serial filtering (Bradley Scott) + o Support reading firmware from stdin (Tormod Volden) + o Warn if missing DFU suffix (Tormod Volden) + o Improved progress bar (Hans Petter Selasky) + o Fix dfuse leave option (Uwe Bonnes) + o Major code rework (Hans Petter Selasky) + o MS Visual Studio build support (Henrique Mendonca) + o dfuse-pack.py tool for .dfu files (Antonio Galeo) + o Many other fixes from many people + +2014-09-13: Tormod Volden + +0.7: + o Support for TI Stellaris devices (Tommi Keisala) + o Fix libusb detection on MacOSX (Marc Singer) + o Fix libusb detection on FreeBSD (Tormod Volden) + o Improved DfuSe support (Tormod Volden) + o Support all special commands (leave, unprotect, mass-erase) + o Arbitrary upload lengths + o "force" option for various possible (dangerous) overrides + +2012-10-07: Tormod Volden + +0.6: + o Add detach mode (Stefan Schmidt) + o Check return value on all libusb calls (Tormod Volden) + o Fix segmentation fault with -s option (Tormod Volden) + o Add DFU suffix manipulation tool (Stefan Schmidt) + o Port to Windows: (Tormod Volden, some parts based on work from Satz + Klauer) + o Port file handling to stdio streams + o Sleep() macros + o C99 types + o Pack structs + o Detect DfuSe device correctly on big-endian architectures (Tormod + Volden) + o Add dfuse progress indication on download (Tormod Volden) + o Cleanup: gcc pedantic, gcc extension, ... (Tormod Volden) + o Rely on page size from functional descriptor. Please report if you get + an error about it. (Tormod Volden) + o Add quirk for Maple since it reports wrong DFU version (Tormod Volden) + +2012-04-22: Stefan Schmidt + +0.5: + o DfuSe extension support for ST devices (Tormod Volden) + o Add initial support for bitWillDetach flag from DFU 1.1 (Tormod + Volden) + o Internal cleanup and some manual page fixes (Tormod Volden) + +2011-11-02: Stefan Schmidt + +0.4: + o Rework to use libusb-1.0 (Stefan Schmidt) + o DFU suffix support (Tormod Volden, Stefan Schmidt) + o Sspeed up DFU downloads directly into memory (Bernard Blackham) + o More flexible -d vid:pid parsing (Tormod Volden) + o Many bug fixes and cleanups + +2011-07-20: Stefan Schmidt + +0.3: + o quirks: Add OpenOCD to the poll timeout quirk table. + +2010-12-22: Stefan Schmidt + +0.2: + o Fix some typos on the website and the README (Antonio Ospite, Uwe + Hermann) + o Remove build rule for a static binary. We can use autotools for this. + (Mike Frysinger) + o Fix infinite loop in download error path (C. Scott Ananian) + o Break out to show the 'finished' in upload (C. Scott Ananian) + o Add GPLv2+ headers (Harald Welte) + o Remove dead code (commands.[ch]) remnescent of dfu-programmer (Harald + Welte) + o Simple quirk system with Openmoko quirk for missing bwPollTimeout (Tormod Volden) + o New default (1024) and clamping of transfer size (Tormod Volden) + o Verify sending of completion packet (Tormod Volden) + o Look for DFU functional descriptor among all descriptors (Tormod + Volden) + o Print out in which direction we are transferring data + o Abort in upload if the file already exists + +2010-11-17 Stefan Schmidt + +0.1: + Initial release + +2010-05-23 Stefan Schmidt diff --git a/tools/src/dfu-util/DEVICES.txt b/tools/linux/src/dfu-util/DEVICES.txt old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/DEVICES.txt rename to tools/linux/src/dfu-util/DEVICES.txt index 682e62c..bdd9f1f --- a/tools/src/dfu-util/DEVICES.txt +++ b/tools/linux/src/dfu-util/DEVICES.txt @@ -1,20 +1,20 @@ -List of supported software and hardware products: - -Software user (bootloader, etc) -------------------------------- -- Sam7DFU: http://www.openpcd.org/Sam7dfu -- U-boot: DFU patches -- Barebox: http://www.barebox.org/ -- Leaflabs: http://code.google.com/p/leaflabs/ -- Blackmagic DFU - -Products using DFU ------------------- -- OpenPCD (sam7dfu) -- Openmoko Neo 1973 and Freerunner (u-boot with DFU patches) -- Leaflabs Maple -- ATUSB from Qi Hardware -- STM32F105/7, STM32F2/F3/F4 in System Bootloader -- Blackmagic debug probe -- NXP LPC31xx/LPC43XX, e.g. LPC-Link and LPC-Link2, need binaries - with LPC prefix and encoding (LPC-Link) +List of supported software and hardware products: + +Software user (bootloader, etc) +------------------------------- +- Sam7DFU: http://www.openpcd.org/Sam7dfu +- U-boot: DFU patches +- Barebox: http://www.barebox.org/ +- Leaflabs: http://code.google.com/p/leaflabs/ +- Blackmagic DFU + +Products using DFU +------------------ +- OpenPCD (sam7dfu) +- Openmoko Neo 1973 and Freerunner (u-boot with DFU patches) +- Leaflabs Maple +- ATUSB from Qi Hardware +- STM32F105/7, STM32F2/F3/F4 in System Bootloader +- Blackmagic debug probe +- NXP LPC31xx/LPC43XX, e.g. LPC-Link and LPC-Link2, need binaries + with LPC prefix and encoding (LPC-Link) diff --git a/tools/src/dfu-util/Makefile.am b/tools/linux/src/dfu-util/Makefile.am old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/Makefile.am rename to tools/linux/src/dfu-util/Makefile.am index 7318dd8..641dda5 --- a/tools/src/dfu-util/Makefile.am +++ b/tools/linux/src/dfu-util/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = src doc - -EXTRA_DIST = autogen.sh TODO DEVICES.txt dfuse-pack.py +SUBDIRS = src doc + +EXTRA_DIST = autogen.sh TODO DEVICES.txt dfuse-pack.py diff --git a/tools/src/dfu-util/README b/tools/linux/src/dfu-util/README old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/README rename to tools/linux/src/dfu-util/README index acda06a..0f8f262 --- a/tools/src/dfu-util/README +++ b/tools/linux/src/dfu-util/README @@ -1,20 +1,20 @@ -Dfu-util - Device Firmware Upgrade Utilities - -Dfu-util is the host side implementation of the DFU 1.0 [1] and DFU 1.1 [2] -specification of the USB forum. - -DFU is intended to download and upload firmware to devices connected over -USB. It ranges from small devices like micro-controller boards up to mobile -phones. With dfu-util you are able to download firmware to your device or -upload firmware from it. - -dfu-util has been tested with Openmoko Neo1973 and Freerunner and many -other devices. - -[1] DFU 1.0 spec: http://www.usb.org/developers/devclass_docs/usbdfu10.pdf -[2] DFU 1.1 spec: http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf - -The official website is: - - http://dfu-util.gnumonks.org/ - +Dfu-util - Device Firmware Upgrade Utilities + +Dfu-util is the host side implementation of the DFU 1.0 [1] and DFU 1.1 [2] +specification of the USB forum. + +DFU is intended to download and upload firmware to devices connected over +USB. It ranges from small devices like micro-controller boards up to mobile +phones. With dfu-util you are able to download firmware to your device or +upload firmware from it. + +dfu-util has been tested with Openmoko Neo1973 and Freerunner and many +other devices. + +[1] DFU 1.0 spec: http://www.usb.org/developers/devclass_docs/usbdfu10.pdf +[2] DFU 1.1 spec: http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf + +The official website is: + + http://dfu-util.gnumonks.org/ + diff --git a/tools/src/dfu-util/TODO b/tools/linux/src/dfu-util/TODO old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/TODO rename to tools/linux/src/dfu-util/TODO index 7574512..900c30c --- a/tools/src/dfu-util/TODO +++ b/tools/linux/src/dfu-util/TODO @@ -1,14 +1,14 @@ -DfuSe: -- Do erase and write in two separate passes when downloading -- Skip "Set Address" command when downloading contiguous blocks -- Implement "Get Commands" command - -Devices: -- Research iPhone/iPod/iPad support - Heavily modified dfu-util fork here: - https://github.com/planetbeing/xpwn/tree/master/dfu-util -- Test against Niftylights - -Non-Code: -- Logo -- Re-License as LGPL for usage as library? +DfuSe: +- Do erase and write in two separate passes when downloading +- Skip "Set Address" command when downloading contiguous blocks +- Implement "Get Commands" command + +Devices: +- Research iPhone/iPod/iPad support + Heavily modified dfu-util fork here: + https://github.com/planetbeing/xpwn/tree/master/dfu-util +- Test against Niftylights + +Non-Code: +- Logo +- Re-License as LGPL for usage as library? diff --git a/tools/src/dfu-util/autogen.sh b/tools/linux/src/dfu-util/autogen.sh old mode 100755 new mode 100644 similarity index 100% rename from tools/src/dfu-util/autogen.sh rename to tools/linux/src/dfu-util/autogen.sh diff --git a/tools/src/dfu-util/configure.ac b/tools/linux/src/dfu-util/configure.ac old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/configure.ac rename to tools/linux/src/dfu-util/configure.ac index 8810f56..8622114 --- a/tools/src/dfu-util/configure.ac +++ b/tools/linux/src/dfu-util/configure.ac @@ -1,41 +1,41 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) -AC_INIT([dfu-util],[0.8],[dfu-util@lists.gnumonks.org],,[http://dfu-util.gnumonks.org]) -AC_CONFIG_AUX_DIR(m4) -AM_INIT_AUTOMAKE([foreign]) -AC_CONFIG_HEADERS([config.h]) - -# Test for new silent rules and enable only if they are available -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -# Checks for programs. -AC_PROG_CC - -# Checks for libraries. -# On FreeBSD the libusb-1.0 is called libusb and resides in system location -AC_CHECK_LIB([usb], [libusb_init],, [native_libusb=no],) -AS_IF([test x$native_libusb = xno], [ - PKG_CHECK_MODULES([USB], [libusb-1.0 >= 1.0.0],, - AC_MSG_ERROR([*** Required libusb-1.0 >= 1.0.0 not installed ***])) -]) -AC_CHECK_LIB([usbpath],[usb_path2devnum],,,-lusb) - -LIBS="$LIBS $USB_LIBS" -CFLAGS="$CFLAGS $USB_CFLAGS" - -# Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS([usbpath.h windows.h sysexits.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_TYPE_SIZE_T - -# Checks for library functions. -AC_FUNC_MEMCMP -AC_CHECK_FUNCS([ftruncate getpagesize nanosleep err]) - -AC_CONFIG_FILES(Makefile src/Makefile doc/Makefile) -AC_OUTPUT +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([dfu-util],[0.8],[dfu-util@lists.gnumonks.org],,[http://dfu-util.gnumonks.org]) +AC_CONFIG_AUX_DIR(m4) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([config.h]) + +# Test for new silent rules and enable only if they are available +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +# On FreeBSD the libusb-1.0 is called libusb and resides in system location +AC_CHECK_LIB([usb], [libusb_init],, [native_libusb=no],) +AS_IF([test x$native_libusb = xno], [ + PKG_CHECK_MODULES([USB], [libusb-1.0 >= 1.0.0],, + AC_MSG_ERROR([*** Required libusb-1.0 >= 1.0.0 not installed ***])) +]) +AC_CHECK_LIB([usbpath],[usb_path2devnum],,,-lusb) + +LIBS="$LIBS $USB_LIBS" +CFLAGS="$CFLAGS $USB_CFLAGS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([usbpath.h windows.h sysexits.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([ftruncate getpagesize nanosleep err]) + +AC_CONFIG_FILES(Makefile src/Makefile doc/Makefile) +AC_OUTPUT diff --git a/tools/src/dfu-util/device-logs/README b/tools/linux/src/dfu-util/device-logs/README old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/device-logs/README rename to tools/linux/src/dfu-util/device-logs/README index c1de3e7..00d3d1a --- a/tools/src/dfu-util/device-logs/README +++ b/tools/linux/src/dfu-util/device-logs/README @@ -1,77 +1,77 @@ -Device: -------- -qi-hardware-atusb: -- Qi Hardware ben-wpan -- DFU implementation: - http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw/usb -- Tester: Stefan Schmidt - -openpcd: -- OpenPCD RFID reader -- DFU implementation: SAM7DFU - http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git -- Tester: Stefan Schmidt - -simtrace: -- Sysmocom SimTrace -- DFU implementation: SAM7DFU - http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git -- Tester: Stefan Schmidt - -openmoko-freerunner: -- Openmoko Freerunner -- DFU implementation: Old U-Boot - http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches -- Tester: Stefan Schmidt - -openmoko-neo1973: -- Openmoko Neo1073 -- DFU implementation: Old U-Boot - http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches -- Tester: Stefan Schmidt - -tdk-bluetooth: -- TDK Corp. Bluetooth Adapter -- DFU implementation: closed soure -- Only upload has been tested -- Tester: Stefan Schmidt - -stm32f107: -- STM32 microcontrollers with built-in (ROM) DFU loader -- DFU implementation: Closed source but probably similar to the one - in their USB device libraries. Some relevant application notes: - http://www.st.com -> AN3156 and AN2606 -- Tested by Uwe Bonnes - -stm32f4discovery: -- STM32 microcontroller board with built-in (ROM) DFU loader -- DFU implementation: Closed source, probably similar to stm32f107. -- Tested by Joe Rothweiler - -dso-nano: -- DSO Nano pocket oscilloscope -- DFU implementation: Based on ST Microelectronics USB FS Library 1.0 - http://dsonano.googlecode.com/files/DS0201_OpenSource.rar -- Tester: Tormod Volden - -opc-20: -- Custom devices based on STM32F1xx -- DFU implementation: ST Microelectronics USB FS Device Library 3.1.0 - http://www.st.com -> um0424.zip -- Tester: Tormod Volden - -lpc-link, lpclink2: -- NXP LPCXpresso debug adapters -- Proprietary DFU implementation, uses special download files with - LPC prefix and encoding of the target firmware code -- Tested by Uwe Bonnes - -Adding the lsusb output and a download log of your device here helps -us to avoid regressions for hardware we cannot test while working on -the code. To extract the lsusb output use this command: -sudo lsusb -v -d $USBID > $DEVICE.lsusb -Prepare a description snippet as above, and send it to us. A log -(copy-paste of the command window) of a firmware download is also -nice, please use the double verbose option -v -v and include the -command line in the log file. - +Device: +------- +qi-hardware-atusb: +- Qi Hardware ben-wpan +- DFU implementation: + http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw/usb +- Tester: Stefan Schmidt + +openpcd: +- OpenPCD RFID reader +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +simtrace: +- Sysmocom SimTrace +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +openmoko-freerunner: +- Openmoko Freerunner +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +openmoko-neo1973: +- Openmoko Neo1073 +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +tdk-bluetooth: +- TDK Corp. Bluetooth Adapter +- DFU implementation: closed soure +- Only upload has been tested +- Tester: Stefan Schmidt + +stm32f107: +- STM32 microcontrollers with built-in (ROM) DFU loader +- DFU implementation: Closed source but probably similar to the one + in their USB device libraries. Some relevant application notes: + http://www.st.com -> AN3156 and AN2606 +- Tested by Uwe Bonnes + +stm32f4discovery: +- STM32 microcontroller board with built-in (ROM) DFU loader +- DFU implementation: Closed source, probably similar to stm32f107. +- Tested by Joe Rothweiler + +dso-nano: +- DSO Nano pocket oscilloscope +- DFU implementation: Based on ST Microelectronics USB FS Library 1.0 + http://dsonano.googlecode.com/files/DS0201_OpenSource.rar +- Tester: Tormod Volden + +opc-20: +- Custom devices based on STM32F1xx +- DFU implementation: ST Microelectronics USB FS Device Library 3.1.0 + http://www.st.com -> um0424.zip +- Tester: Tormod Volden + +lpc-link, lpclink2: +- NXP LPCXpresso debug adapters +- Proprietary DFU implementation, uses special download files with + LPC prefix and encoding of the target firmware code +- Tested by Uwe Bonnes + +Adding the lsusb output and a download log of your device here helps +us to avoid regressions for hardware we cannot test while working on +the code. To extract the lsusb output use this command: +sudo lsusb -v -d $USBID > $DEVICE.lsusb +Prepare a description snippet as above, and send it to us. A log +(copy-paste of the command window) of a firmware download is also +nice, please use the double verbose option -v -v and include the +command line in the log file. + diff --git a/tools/src/dfu-util/device-logs/dsonano.lsusb b/tools/linux/src/dfu-util/device-logs/dsonano.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/dsonano.lsusb rename to tools/linux/src/dfu-util/device-logs/dsonano.lsusb index 44a5d28..140a7bc --- a/tools/src/dfu-util/device-logs/dsonano.lsusb +++ b/tools/linux/src/dfu-util/device-logs/dsonano.lsusb @@ -1,60 +1,60 @@ - -Bus 002 Device 004: ID 0483:df11 SGS Thomson Microelectronics -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x0483 SGS Thomson Microelectronics - idProduct 0xdf11 - bcdDevice 1.1a - iManufacturer 1 STMicroelectronics - iProduct 2 STM32 DFU - iSerial 3 001 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 36 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 64mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 0 - iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 0 - iInterface 5 @SPI Flash : M25P64/0x00000000/64*064Kg,64*064Kg - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 11 - Will Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 255 milliseconds - wTransferSize 1024 bytes - bcdDFUVersion 1.1a -Device Status: 0x0000 - (Bus Powered) + +Bus 002 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 1.1a + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 001 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 5 @SPI Flash : M25P64/0x00000000/64*064Kg,64*064Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/lpclink.log b/tools/linux/src/dfu-util/device-logs/lpclink.log old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/lpclink.log rename to tools/linux/src/dfu-util/device-logs/lpclink.log index f2a04f7..7de4dd3 --- a/tools/src/dfu-util/device-logs/lpclink.log +++ b/tools/linux/src/dfu-util/device-logs/lpclink.log @@ -1,59 +1,59 @@ -(The on-board LPC3154 has some encryption key set and LPCXpressoWIN.enc -is encrypted.) - -$ lsusb | grep NXP -Bus 003 Device 011: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link - -$ dfu-util -v -v -v -R -D /opt/lpc/lpcxpresso/bin/LPCXpressoWIN.enc - -dfu-util 0.7 - -Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. -Copyright 2010-2012 Tormod Volden and Stefan Schmidt -This program is Free Software and has ABSOLUTELY NO WARRANTY -Please report bugs to dfu-util@lists.gnumonks.org - -dfu-util: Invalid DFU suffix signature -dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! -Deducing device DFU version from functional descriptor length -Opening DFU capable USB device... -ID 0471:df55 -Run-time device DFU version 0100 -Claiming USB DFU Runtime Interface... -Determining device status: -state = dfuIDLE, status = 0 -dfu-util: WARNING: Runtime device already in DFU state ?!? -Claiming USB DFU Interface... -Setting Alternate Setting #0 ... -Determining device status: -state = dfuIDLE, status = 0 -dfuIDLE, continuing -DFU mode device DFU version 0100 -Device returned transfer size 2048 -Copying data from PC to DFU device -Download [ ] 0% 0 bytes -Download [= ] 6% 2048 bytes -Download [=== ] 13% 4096 bytes -Download [==== ] 19% 6144 bytes -Download [====== ] 26% 8192 bytes -Download [======== ] 32% 10240 bytes -Download [========= ] 39% 12288 bytes -Download [=========== ] 45% 14336 bytes -Download [============= ] 52% 16384 bytes -Download [============== ] 59% 18432 bytes -Download [================ ] 65% 20480 bytes -Download [================== ] 72% 22528 bytes -Download [=================== ] 78% 24576 bytes -Download [===================== ] 85% 26624 bytes -Download [====================== ] 91% 28672 bytes -Download [======================== ] 98% 29192 bytes -Download [=========================] 100% 29192 bytes -Download done. -Sent a total of 29192 bytes -state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present -Done! -dfu-util: can't detach -Resetting USB to switch back to runtime mode - -$ lsusb | grep NXP -Bus 003 Device 012: ID 1fc9:0009 NXP Semiconductors +(The on-board LPC3154 has some encryption key set and LPCXpressoWIN.enc +is encrypted.) + +$ lsusb | grep NXP +Bus 003 Device 011: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link + +$ dfu-util -v -v -v -R -D /opt/lpc/lpcxpresso/bin/LPCXpressoWIN.enc + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Deducing device DFU version from functional descriptor length +Opening DFU capable USB device... +ID 0471:df55 +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 6% 2048 bytes +Download [=== ] 13% 4096 bytes +Download [==== ] 19% 6144 bytes +Download [====== ] 26% 8192 bytes +Download [======== ] 32% 10240 bytes +Download [========= ] 39% 12288 bytes +Download [=========== ] 45% 14336 bytes +Download [============= ] 52% 16384 bytes +Download [============== ] 59% 18432 bytes +Download [================ ] 65% 20480 bytes +Download [================== ] 72% 22528 bytes +Download [=================== ] 78% 24576 bytes +Download [===================== ] 85% 26624 bytes +Download [====================== ] 91% 28672 bytes +Download [======================== ] 98% 29192 bytes +Download [=========================] 100% 29192 bytes +Download done. +Sent a total of 29192 bytes +state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present +Done! +dfu-util: can't detach +Resetting USB to switch back to runtime mode + +$ lsusb | grep NXP +Bus 003 Device 012: ID 1fc9:0009 NXP Semiconductors diff --git a/tools/src/dfu-util/device-logs/lpclink.lsusb b/tools/linux/src/dfu-util/device-logs/lpclink.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/lpclink.lsusb rename to tools/linux/src/dfu-util/device-logs/lpclink.lsusb index c80d2b7..867b2a2 --- a/tools/src/dfu-util/device-logs/lpclink.lsusb +++ b/tools/linux/src/dfu-util/device-logs/lpclink.lsusb @@ -1,58 +1,58 @@ - -Bus 003 Device 008: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 2.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x0471 Philips (or NXP) - idProduct 0xdf55 LPCXpresso LPC-Link - bcdDevice 0.01 - iManufacturer 0 - iProduct 0 - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 25 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 0 - iInterface 0 - Device Firmware Upgrade Interface Descriptor: - bLength 7 - bDescriptorType 33 - bmAttributes 1 - Will Not Detach - Manifestation Intolerant - Upload Unsupported - Download Supported - wDetachTimeout 65535 milliseconds - wTransferSize 2048 bytes -Device Qualifier (for other device speed): - bLength 10 - bDescriptorType 6 - bcdUSB 2.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - bNumConfigurations 1 -Device Status: 0x0000 - (Bus Powered) + +Bus 003 Device 008: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0471 Philips (or NXP) + idProduct 0xdf55 LPCXpresso LPC-Link + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 25 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 1 + Will Not Detach + Manifestation Intolerant + Upload Unsupported + Download Supported + wDetachTimeout 65535 milliseconds + wTransferSize 2048 bytes +Device Qualifier (for other device speed): + bLength 10 + bDescriptorType 6 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + bNumConfigurations 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/lpclink2.log b/tools/linux/src/dfu-util/device-logs/lpclink2.log old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/lpclink2.log rename to tools/linux/src/dfu-util/device-logs/lpclink2.log index f33e671..4681eff --- a/tools/src/dfu-util/device-logs/lpclink2.log +++ b/tools/linux/src/dfu-util/device-logs/lpclink2.log @@ -1,59 +1,59 @@ -$ lsusb | grep NXP -Bus 003 Device 013: ID 1fc9:000c NXP Semiconductors - -$ dfu-util -D ~/devel/dfu-util/firmware.bin.qthdr - -dfu-util 0.7 - -Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. -Copyright 2010-2012 Tormod Volden and Stefan Schmidt -This program is Free Software and has ABSOLUTELY NO WARRANTY -Please report bugs to dfu-util@lists.gnumonks.org - -dfu-util: Invalid DFU suffix signature -dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! -Possible unencryptes NXP LPC DFU prefix with the following properties -Payload length: 39 kiByte -Opening DFU capable USB device... -ID 1fc9:000c -Run-time device DFU version 0100 -Claiming USB DFU Runtime Interface... -Determining device status: -state = dfuIDLE, status = 0 -dfu-util: WARNING: Runtime device already in DFU state ?!? -Claiming USB DFU Interface... -Setting Alternate Setting #0 ... -Determining device status: -state = dfuIDLE, status = 0 -dfuIDLE, continuing -DFU mode device DFU version 0100 -Device returned transfer size 2048 -Copying data from PC to DFU device -Download [ ] 0% 0 bytes -Download [= ] 4% 2048 bytes -Download [== ] 9% 4096 bytes -Download [=== ] 14% 6144 bytes -Download [==== ] 19% 8192 bytes -Download [====== ] 24% 10240 bytes -Download [======= ] 28% 12288 bytes -Download [======== ] 33% 14336 bytes -Download [========= ] 38% 16384 bytes -Download [========== ] 43% 18432 bytes -Download [============ ] 48% 20480 bytes -Download [============= ] 53% 22528 bytes -Download [============== ] 57% 24576 bytes -Download [=============== ] 62% 26624 bytes -Download [================ ] 67% 28672 bytes -Download [================== ] 72% 30720 bytes -Download [=================== ] 77% 32768 bytes -Download [==================== ] 82% 34816 bytes -Download [===================== ] 86% 36864 bytes -Download [====================== ] 91% 38912 bytes -Download [======================== ] 96% 40356 bytes -Download [=========================] 100% 40356 bytes -Download done. -Sent a total of 40356 bytes -dfu-util: unable to read DFU status - -$ lsusb | grep NXP -Bus 003 Device 014: ID 1fc9:0018 NXP Semiconductors +$ lsusb | grep NXP +Bus 003 Device 013: ID 1fc9:000c NXP Semiconductors + +$ dfu-util -D ~/devel/dfu-util/firmware.bin.qthdr + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Possible unencryptes NXP LPC DFU prefix with the following properties +Payload length: 39 kiByte +Opening DFU capable USB device... +ID 1fc9:000c +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 4% 2048 bytes +Download [== ] 9% 4096 bytes +Download [=== ] 14% 6144 bytes +Download [==== ] 19% 8192 bytes +Download [====== ] 24% 10240 bytes +Download [======= ] 28% 12288 bytes +Download [======== ] 33% 14336 bytes +Download [========= ] 38% 16384 bytes +Download [========== ] 43% 18432 bytes +Download [============ ] 48% 20480 bytes +Download [============= ] 53% 22528 bytes +Download [============== ] 57% 24576 bytes +Download [=============== ] 62% 26624 bytes +Download [================ ] 67% 28672 bytes +Download [================== ] 72% 30720 bytes +Download [=================== ] 77% 32768 bytes +Download [==================== ] 82% 34816 bytes +Download [===================== ] 86% 36864 bytes +Download [====================== ] 91% 38912 bytes +Download [======================== ] 96% 40356 bytes +Download [=========================] 100% 40356 bytes +Download done. +Sent a total of 40356 bytes +dfu-util: unable to read DFU status + +$ lsusb | grep NXP +Bus 003 Device 014: ID 1fc9:0018 NXP Semiconductors diff --git a/tools/src/dfu-util/device-logs/lpclink2.lsusb b/tools/linux/src/dfu-util/device-logs/lpclink2.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/lpclink2.lsusb rename to tools/linux/src/dfu-util/device-logs/lpclink2.lsusb index d8d6e0c..b833fca --- a/tools/src/dfu-util/device-logs/lpclink2.lsusb +++ b/tools/linux/src/dfu-util/device-logs/lpclink2.lsusb @@ -1,203 +1,203 @@ - -Bus 003 Device 007: ID 0c72:000c PEAK System PCAN-USB -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 16 - idVendor 0x0c72 PEAK System - idProduct 0x000c PCAN-USB - bcdDevice 1c.ff - iManufacturer 0 - iProduct 3 VER1:PEAK -VER2:02.8.01 -DAT :06.05.2004 -TIME:09:35:37 - ... - iSerial 0 - bNumConfigurations 3 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 46 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 200mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 4 - bInterfaceClass 0 (Defined at Interface level) - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 20 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x01 EP 1 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 20 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 46 - bNumInterfaces 1 - bConfigurationValue 2 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 394mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 4 - bInterfaceClass 0 (Defined at Interface level) - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 20 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x01 EP 1 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 20 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 46 - bNumInterfaces 1 - bConfigurationValue 3 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 200mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 4 - bInterfaceClass 0 (Defined at Interface level) - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x01 EP 1 OUT - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 -Device Status: 0x0001 - Self Powered + +Bus 003 Device 007: ID 0c72:000c PEAK System PCAN-USB +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x0c72 PEAK System + idProduct 0x000c PCAN-USB + bcdDevice 1c.ff + iManufacturer 0 + iProduct 3 VER1:PEAK +VER2:02.8.01 +DAT :06.05.2004 +TIME:09:35:37 + ... + iSerial 0 + bNumConfigurations 3 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 2 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 394mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 3 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0001 + Self Powered diff --git a/tools/src/dfu-util/device-logs/opc-20.lsusb b/tools/linux/src/dfu-util/device-logs/opc-20.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/opc-20.lsusb rename to tools/linux/src/dfu-util/device-logs/opc-20.lsusb index 25ad12f..580df90 --- a/tools/src/dfu-util/device-logs/opc-20.lsusb +++ b/tools/linux/src/dfu-util/device-logs/opc-20.lsusb @@ -1,60 +1,60 @@ - -Bus 001 Device 004: ID 0483:df11 SGS Thomson Microelectronics -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x0483 SGS Thomson Microelectronics - idProduct 0xdf11 - bcdDevice 2.00 - iManufacturer 1 STMicroelectronics - iProduct 2 STM32 DFU - iSerial 3 ÿÿÿÿÿÿÿÿÿÿÿÿ - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 36 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xc0 - Self Powered - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 5 @SPI Flash : M25P64/0x00000000/128*64Kg - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 11 - Will Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 255 milliseconds - wTransferSize 1024 bytes - bcdDFUVersion 1a.01 -Device Status: 0x0001 - Self Powered + +Bus 001 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 2.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 ÿÿÿÿÿÿÿÿÿÿÿÿ + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : M25P64/0x00000000/128*64Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1a.01 +Device Status: 0x0001 + Self Powered diff --git a/tools/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb b/tools/linux/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb rename to tools/linux/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb index 64bd018..4c0abfb --- a/tools/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb +++ b/tools/linux/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb @@ -1,109 +1,109 @@ -Bus 003 Device 017: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 16 - idVendor 0x1d50 OpenMoko, Inc. - idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader - bcdDevice 0.00 - iManufacturer 1 OpenMoko, Inc - iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 - iSerial 3 0000000 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 81 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 7 USB Device Firmware Upgrade - bmAttributes 0x80 - (Bus Powered) - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 8 RAM 0x32000000 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 9 u-boot - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 2 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 10 u-boot_env - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 3 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 11 kernel - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 4 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 12 splash - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 5 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 13 factory - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 6 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 14 rootfs - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 7 - Will Not Detach - Manifestation Tolerant - Upload Supported - Download Supported - wDetachTimeout 65280 milliseconds - wTransferSize 4096 bytes - bcdDFUVersion 1.00 -Device Status: 0x0a00 - (Bus Powered) +Bus 003 Device 017: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 81 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 7 USB Device Firmware Upgrade + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 8 RAM 0x32000000 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 9 u-boot + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 10 u-boot_env + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 11 kernel + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 4 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 12 splash + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 5 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 13 factory + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 6 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 14 rootfs + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 +Device Status: 0x0a00 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/openmoko-freerunner.lsusb b/tools/linux/src/dfu-util/device-logs/openmoko-freerunner.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/openmoko-freerunner.lsusb rename to tools/linux/src/dfu-util/device-logs/openmoko-freerunner.lsusb index cf148e5..835708d --- a/tools/src/dfu-util/device-logs/openmoko-freerunner.lsusb +++ b/tools/linux/src/dfu-util/device-logs/openmoko-freerunner.lsusb @@ -1,179 +1,179 @@ -Bus 005 Device 033: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.10 - bDeviceClass 2 Communications - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 16 - idVendor 0x1d50 OpenMoko, Inc. - idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader - bcdDevice 0.00 - iManufacturer 1 OpenMoko, Inc - iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 - iSerial 3 0000000 - bNumConfigurations 2 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 85 - bNumInterfaces 3 - bConfigurationValue 1 - iConfiguration 4 TTY via USB - bmAttributes 0x80 - (Bus Powered) - MaxPower 500mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 2 Communications - bInterfaceSubClass 2 Abstract (modem) - bInterfaceProtocol 1 AT-commands (v.25ter) - iInterface 6 Control Interface - CDC Header: - bcdCDC 0.6e - CDC Call Management: - bmCapabilities 0x00 - bDataInterface 1 - CDC ACM: - bmCapabilities 0x00 - CDC Union: - bMasterInterface 0 - bSlaveInterface 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 10 CDC Data - bInterfaceSubClass 0 Unused - bInterfaceProtocol 0 - iInterface 5 Bulk Data Interface - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 2 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 1 - iInterface 7 USB Device Firmware Upgrade - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 7 - Will Not Detach - Manifestation Tolerant - Upload Supported - Download Supported - wDetachTimeout 65280 milliseconds - wTransferSize 4096 bytes - bcdDFUVersion 1.00 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 67 - bNumInterfaces 2 - bConfigurationValue 2 - iConfiguration 4 TTY via USB - bmAttributes 0x80 - (Bus Powered) - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 2 Communications - bInterfaceSubClass 2 Abstract (modem) - bInterfaceProtocol 1 AT-commands (v.25ter) - iInterface 6 Control Interface - CDC Header: - bcdCDC 0.6e - CDC Call Management: - bmCapabilities 0x00 - bDataInterface 1 - CDC ACM: - bmCapabilities 0x00 - CDC Union: - bMasterInterface 0 - bSlaveInterface 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 10 CDC Data - bInterfaceSubClass 0 Unused - bInterfaceProtocol 0 - iInterface 5 Bulk Data Interface - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 -Device Status: 0x9a00 - (Bus Powered) +Bus 005 Device 033: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 USB Device Firmware Upgrade + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x9a00 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/openmoko-neo1973.lsusb b/tools/linux/src/dfu-util/device-logs/openmoko-neo1973.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/openmoko-neo1973.lsusb rename to tools/linux/src/dfu-util/device-logs/openmoko-neo1973.lsusb index dc1fe7c..0778950 --- a/tools/src/dfu-util/device-logs/openmoko-neo1973.lsusb +++ b/tools/linux/src/dfu-util/device-logs/openmoko-neo1973.lsusb @@ -1,182 +1,182 @@ - -Bus 006 Device 020: ID 1457:5119 First International Computer, Inc. OpenMoko Neo1973 u-boot cdc_acm serial port -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.10 - bDeviceClass 2 Communications - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 16 - idVendor 0x1457 First International Computer, Inc. - idProduct 0x5119 OpenMoko Neo1973 u-boot cdc_acm serial port - bcdDevice 0.00 - iManufacturer 1 - iProduct 2 - iSerial 3 - bNumConfigurations 2 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 85 - bNumInterfaces 3 - bConfigurationValue 1 - iConfiguration 4 - bmAttributes 0x80 - (Bus Powered) - MaxPower 500mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 2 Communications - bInterfaceSubClass 2 Abstract (modem) - bInterfaceProtocol 1 AT-commands (v.25ter) - iInterface 6 - CDC Header: - bcdCDC 0.6e - CDC Call Management: - bmCapabilities 0x00 - bDataInterface 1 - CDC ACM: - bmCapabilities 0x00 - CDC Union: - bMasterInterface 0 - bSlaveInterface 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 10 CDC Data - bInterfaceSubClass 0 Unused - bInterfaceProtocol 0 - iInterface 5 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 2 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 1 - iInterface 7 - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 7 - Will Not Detach - Manifestation Tolerant - Upload Supported - Download Supported - wDetachTimeout 65280 milliseconds - wTransferSize 4096 bytes - bcdDFUVersion 1.00 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 67 - bNumInterfaces 2 - bConfigurationValue 2 - iConfiguration 4 - bmAttributes 0x80 - (Bus Powered) - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 2 Communications - bInterfaceSubClass 2 Abstract (modem) - bInterfaceProtocol 1 AT-commands (v.25ter) - iInterface 6 - CDC Header: - bcdCDC 0.6e - CDC Call Management: - bmCapabilities 0x00 - bDataInterface 1 - CDC ACM: - bmCapabilities 0x00 - CDC Union: - bMasterInterface 0 - bSlaveInterface 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 10 CDC Data - bInterfaceSubClass 0 Unused - bInterfaceProtocol 0 - iInterface 5 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 255 -Device Status: 0x0006 - (Bus Powered) - Remote Wakeup Enabled - Test Mode + +Bus 006 Device 020: ID 1457:5119 First International Computer, Inc. OpenMoko Neo1973 u-boot cdc_acm serial port +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1457 First International Computer, Inc. + idProduct 0x5119 OpenMoko Neo1973 u-boot cdc_acm serial port + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x0006 + (Bus Powered) + Remote Wakeup Enabled + Test Mode diff --git a/tools/src/dfu-util/device-logs/openpcd.lsusb b/tools/linux/src/dfu-util/device-logs/openpcd.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/openpcd.lsusb rename to tools/linux/src/dfu-util/device-logs/openpcd.lsusb index 8618647..f6255a9 --- a/tools/src/dfu-util/device-logs/openpcd.lsusb +++ b/tools/linux/src/dfu-util/device-logs/openpcd.lsusb @@ -1,60 +1,60 @@ - -Bus 006 Device 016: ID 16c0:076b VOTI OpenPCD 13.56MHz RFID Reader -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 8 - idVendor 0x16c0 VOTI - idProduct 0x076b OpenPCD 13.56MHz RFID Reader - bcdDevice 0.00 - iManufacturer 1 - iProduct 2 OpenPCD RFID Simulator - DFU Mode - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 36 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 200mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 0 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 0 - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 3 - Will Not Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 65280 milliseconds - wTransferSize 256 bytes - bcdDFUVersion 1.00 -Device Status: 0x0000 - (Bus Powered) + +Bus 006 Device 016: ID 16c0:076b VOTI OpenPCD 13.56MHz RFID Reader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x076b OpenPCD 13.56MHz RFID Reader + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 OpenPCD RFID Simulator - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/qi-hardware-atusb.lsusb b/tools/linux/src/dfu-util/device-logs/qi-hardware-atusb.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/qi-hardware-atusb.lsusb rename to tools/linux/src/dfu-util/device-logs/qi-hardware-atusb.lsusb index d1da57a..bfc1701 --- a/tools/src/dfu-util/device-logs/qi-hardware-atusb.lsusb +++ b/tools/linux/src/dfu-util/device-logs/qi-hardware-atusb.lsusb @@ -1,59 +1,59 @@ - -Bus 006 Device 013: ID 20b7:1540 Qi Hardware ben-wpan, AT86RF230-based -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 2.00 - bDeviceClass 255 Vendor Specific Class - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x20b7 Qi Hardware - idProduct 0x1540 ben-wpan, AT86RF230-based - bcdDevice 0.01 - iManufacturer 0 - iProduct 0 - iSerial 1 4630333438371508231a - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 34 - bNumInterfaces 2 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 40mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 255 Vendor Specific Class - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 0 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 1 - iInterface 0 -Device Status: 0x0000 - (Bus Powered) + +Bus 006 Device 013: ID 20b7:1540 Qi Hardware ben-wpan, AT86RF230-based +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x20b7 Qi Hardware + idProduct 0x1540 ben-wpan, AT86RF230-based + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 1 4630333438371508231a + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 2 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 40mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/simtrace.lsusb b/tools/linux/src/dfu-util/device-logs/simtrace.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/simtrace.lsusb rename to tools/linux/src/dfu-util/device-logs/simtrace.lsusb index fd51539..578ddf0 --- a/tools/src/dfu-util/device-logs/simtrace.lsusb +++ b/tools/linux/src/dfu-util/device-logs/simtrace.lsusb @@ -1,70 +1,70 @@ - -Bus 006 Device 017: ID 16c0:0762 VOTI -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 8 - idVendor 0x16c0 VOTI - idProduct 0x0762 - bcdDevice 0.00 - iManufacturer 1 sysmocom - systems for mobile communications GmbH - iProduct 2 SimTrace SIM Sniffer - DFU Mode - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 45 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 3 SimTrace DFU Configuration - bmAttributes 0x80 - (Bus Powered) - MaxPower 200mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 4 SimTrace DFU Interface - Application Partition - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 5 SimTrace DFU Interface - Bootloader Partition - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 2 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 6 SimTrace DFU Interface - RAM - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 3 - Will Not Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 65280 milliseconds - wTransferSize 256 bytes - bcdDFUVersion 1.00 -Device Status: 0x0000 - (Bus Powered) + +Bus 006 Device 017: ID 16c0:0762 VOTI +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x0762 + bcdDevice 0.00 + iManufacturer 1 sysmocom - systems for mobile communications GmbH + iProduct 2 SimTrace SIM Sniffer - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 45 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 3 SimTrace DFU Configuration + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 SimTrace DFU Interface - Application Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 SimTrace DFU Interface - Bootloader Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 SimTrace DFU Interface - RAM + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/sparkcore.lsusb b/tools/linux/src/dfu-util/device-logs/sparkcore.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/sparkcore.lsusb rename to tools/linux/src/dfu-util/device-logs/sparkcore.lsusb index 79c529b..b6029ff --- a/tools/src/dfu-util/device-logs/sparkcore.lsusb +++ b/tools/linux/src/dfu-util/device-logs/sparkcore.lsusb @@ -1,60 +1,60 @@ - -Bus 001 Device 008: ID 1d50:607f OpenMoko, Inc. -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x1d50 OpenMoko, Inc. - idProduct 0x607f - bcdDevice 2.00 - iManufacturer 1 Spark Devices - iProduct 2 CORE DFU - iSerial 3 8D80527B5055 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 36 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xc0 - Self Powered - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 4 @Internal Flash /0x08000000/20*001Ka,108*001Kg - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 5 @SPI Flash : SST25x/0x00000000/512*04Kg - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 11 - Will Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 255 milliseconds - wTransferSize 1024 bytes - bcdDFUVersion 1.1a -Device Status: 0x0001 - Self Powered + +Bus 001 Device 008: ID 1d50:607f OpenMoko, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x607f + bcdDevice 2.00 + iManufacturer 1 Spark Devices + iProduct 2 CORE DFU + iSerial 3 8D80527B5055 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/20*001Ka,108*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : SST25x/0x00000000/512*04Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/src/dfu-util/device-logs/stm32f107.bin-download b/tools/linux/src/dfu-util/device-logs/stm32f107.bin-download old mode 100755 new mode 100644 similarity index 98% rename from tools/src/dfu-util/device-logs/stm32f107.bin-download rename to tools/linux/src/dfu-util/device-logs/stm32f107.bin-download index 4d803fb..45b714f --- a/tools/src/dfu-util/device-logs/stm32f107.bin-download +++ b/tools/linux/src/dfu-util/device-logs/stm32f107.bin-download @@ -1,48 +1,48 @@ -> src/dfu-util --intf 0 --alt 0 -v -v -v -s 0x8000000 -D test3 -dfu-util 0.4 - -(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. -(C) 2010-2011 Tormod Volden (DfuSe support) -This program is Free Software and has ABSOLUTELY NO WARRANTY - -dfu-util does currently only support DFU version 1.0 - -Opening DFU USB device... ID 0483:df11 -Run-time device DFU version 011a -Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/128*002Kg" -Claiming USB DFU Interface... -Setting Alternate Setting #0 ... -Determining device status: state = dfuIDLE, status = 0 -dfuIDLE, continuing -DFU mode device DFU version 011a -Device returned transfer size 2048 -No valid DFU suffix signature -Warning: File has no DFU suffix -DfuSe interface name: "Internal Flash " -Memory segment at 0x08000000 128 x 2048 = 262144 (rew) -Uploading to address = 0x08000000, size = 16384 -Erasing page size 2048 at address 0x08000000, page starting at 0x08000000 - Download from image offset 00000000 to memory 08000000-080007ff, size 2048 - Setting address pointer to 0x08000000 -Erasing page size 2048 at address 0x08000800, page starting at 0x08000800 - Download from image offset 00000800 to memory 08000800-08000fff, size 2048 - Setting address pointer to 0x08000800 -Erasing page size 2048 at address 0x08001000, page starting at 0x08001000 - Download from image offset 00001000 to memory 08001000-080017ff, size 2048 - Setting address pointer to 0x08001000 -Erasing page size 2048 at address 0x08001800, page starting at 0x08001800 - Download from image offset 00001800 to memory 08001800-08001fff, size 2048 - Setting address pointer to 0x08001800 -Erasing page size 2048 at address 0x08002000, page starting at 0x08002000 - Download from image offset 00002000 to memory 08002000-080027ff, size 2048 - Setting address pointer to 0x08002000 -Erasing page size 2048 at address 0x08002800, page starting at 0x08002800 - Download from image offset 00002800 to memory 08002800-08002fff, size 2048 - Setting address pointer to 0x08002800 -Erasing page size 2048 at address 0x08003000, page starting at 0x08003000 - Download from image offset 00003000 to memory 08003000-080037ff, size 2048 - Setting address pointer to 0x08003000 -Erasing page size 2048 at address 0x08003800, page starting at 0x08003800 - Download from image offset 00003800 to memory 08003800-08003fff, size 2048 - Setting address pointer to 0x08003800 - +> src/dfu-util --intf 0 --alt 0 -v -v -v -s 0x8000000 -D test3 +dfu-util 0.4 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Opening DFU USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/128*002Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +No valid DFU suffix signature +Warning: File has no DFU suffix +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 128 x 2048 = 262144 (rew) +Uploading to address = 0x08000000, size = 16384 +Erasing page size 2048 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 +Erasing page size 2048 at address 0x08000800, page starting at 0x08000800 + Download from image offset 00000800 to memory 08000800-08000fff, size 2048 + Setting address pointer to 0x08000800 +Erasing page size 2048 at address 0x08001000, page starting at 0x08001000 + Download from image offset 00001000 to memory 08001000-080017ff, size 2048 + Setting address pointer to 0x08001000 +Erasing page size 2048 at address 0x08001800, page starting at 0x08001800 + Download from image offset 00001800 to memory 08001800-08001fff, size 2048 + Setting address pointer to 0x08001800 +Erasing page size 2048 at address 0x08002000, page starting at 0x08002000 + Download from image offset 00002000 to memory 08002000-080027ff, size 2048 + Setting address pointer to 0x08002000 +Erasing page size 2048 at address 0x08002800, page starting at 0x08002800 + Download from image offset 00002800 to memory 08002800-08002fff, size 2048 + Setting address pointer to 0x08002800 +Erasing page size 2048 at address 0x08003000, page starting at 0x08003000 + Download from image offset 00003000 to memory 08003000-080037ff, size 2048 + Setting address pointer to 0x08003000 +Erasing page size 2048 at address 0x08003800, page starting at 0x08003800 + Download from image offset 00003800 to memory 08003800-08003fff, size 2048 + Setting address pointer to 0x08003800 + diff --git a/tools/src/dfu-util/device-logs/stm32f107.lsusb b/tools/linux/src/dfu-util/device-logs/stm32f107.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/stm32f107.lsusb rename to tools/linux/src/dfu-util/device-logs/stm32f107.lsusb index 89d8d38..14b45cd --- a/tools/src/dfu-util/device-logs/stm32f107.lsusb +++ b/tools/linux/src/dfu-util/device-logs/stm32f107.lsusb @@ -1,60 +1,60 @@ - -Bus 001 Device 028: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x0483 SGS Thomson Microelectronics - idProduct 0xdf11 STM Device in DFU Mode - bcdDevice 20.00 - iManufacturer 1 STMicroelectronics - iProduct 2 STM32 0x418 DFU Bootloader - iSerial 3 STM32 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 36 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xc0 - Self Powered - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 4 @Internal Flash /0x08000000/128*002Kg - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 5 @Option Bytes /0x1FFFF800/01*016 g - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 11 - Will Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 255 milliseconds - wTransferSize 2048 bytes - bcdDFUVersion 1.1a -Device Status: 0x0000 - (Bus Powered) + +Bus 001 Device 028: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 20.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 0x418 DFU Bootloader + iSerial 3 STM32 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/128*002Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFF800/01*016 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/device-logs/stm32f4discovery.bin-download b/tools/linux/src/dfu-util/device-logs/stm32f4discovery.bin-download old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/stm32f4discovery.bin-download rename to tools/linux/src/dfu-util/device-logs/stm32f4discovery.bin-download index d5a2679..96e1722 --- a/tools/src/dfu-util/device-logs/stm32f4discovery.bin-download +++ b/tools/linux/src/dfu-util/device-logs/stm32f4discovery.bin-download @@ -1,36 +1,36 @@ -dfu-util --device 0483:df11 --alt 0 \ - --dfuse-address 0x08000000 \ - -v -v -v \ - --download arm/iotoggle.bin -No valid DFU suffix signature -Warning: File has no DFU suffix -dfu-util 0.5 - -(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. -(C) 2010-2011 Tormod Volden (DfuSe support) -This program is Free Software and has ABSOLUTELY NO WARRANTY - -dfu-util does currently only support DFU version 1.0 - -Filter on vendor = 0x0483 product = 0xdf11 -Opening DFU capable USB device... ID 0483:df11 -Run-time device DFU version 011a -Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" -Claiming USB DFU Interface... -Setting Alternate Setting #0 ... -Determining device status: state = dfuERROR, status = 10 -dfuERROR, clearing status -Determining device status: state = dfuIDLE, status = 0 -dfuIDLE, continuing -DFU mode device DFU version 011a -Device returned transfer size 2048 -DfuSe interface name: "Internal Flash " -Memory segment at 0x08000000 4 x 16384 = 65536 (rew) -Memory segment at 0x08010000 1 x 65536 = 65536 (rew) -Memory segment at 0x08020000 7 x 131072 = 917504 (rew) -Uploading to address = 0x08000000, size = 2308 -Erasing page size 16384 at address 0x08000000, page starting at 0x08000000 - Download from image offset 00000000 to memory 08000000-080007ff, size 2048 - Setting address pointer to 0x08000000 - Download from image offset 00000800 to memory 08000800-08000903, size 260 - Setting address pointer to 0x08000800 +dfu-util --device 0483:df11 --alt 0 \ + --dfuse-address 0x08000000 \ + -v -v -v \ + --download arm/iotoggle.bin +No valid DFU suffix signature +Warning: File has no DFU suffix +dfu-util 0.5 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Filter on vendor = 0x0483 product = 0xdf11 +Opening DFU capable USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuERROR, status = 10 +dfuERROR, clearing status +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 4 x 16384 = 65536 (rew) +Memory segment at 0x08010000 1 x 65536 = 65536 (rew) +Memory segment at 0x08020000 7 x 131072 = 917504 (rew) +Uploading to address = 0x08000000, size = 2308 +Erasing page size 16384 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 + Download from image offset 00000800 to memory 08000800-08000903, size 260 + Setting address pointer to 0x08000800 diff --git a/tools/src/dfu-util/device-logs/stm32f4discovery.lsusb b/tools/linux/src/dfu-util/device-logs/stm32f4discovery.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/stm32f4discovery.lsusb rename to tools/linux/src/dfu-util/device-logs/stm32f4discovery.lsusb index c78530c..0b870de --- a/tools/src/dfu-util/device-logs/stm32f4discovery.lsusb +++ b/tools/linux/src/dfu-util/device-logs/stm32f4discovery.lsusb @@ -1,80 +1,80 @@ - -Bus 001 Device 010: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.00 - bDeviceClass 0 (Defined at Interface level) - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 64 - idVendor 0x0483 SGS Thomson Microelectronics - idProduct 0xdf11 STM Device in DFU Mode - bcdDevice 21.00 - iManufacturer 1 STMicroelectronics - iProduct 2 STM32 BOOTLOADER - iSerial 3 315A28A0B956 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 54 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xc0 - Self Powered - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 1 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 5 @Option Bytes /0x1FFFC000/01*016 g - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 2 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 6 @OTP Memory /0x1FFF7800/01*512 g,01*016 g - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 3 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 2 - iInterface 7 @Device Feature/0xFFFF0000/01*004 g - Device Firmware Upgrade Interface Descriptor: - bLength 9 - bDescriptorType 33 - bmAttributes 11 - Will Detach - Manifestation Intolerant - Upload Supported - Download Supported - wDetachTimeout 255 milliseconds - wTransferSize 2048 bytes - bcdDFUVersion 1.1a -Device Status: 0x0001 - Self Powered + +Bus 001 Device 010: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 21.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 BOOTLOADER + iSerial 3 315A28A0B956 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 54 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFC000/01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 @OTP Memory /0x1FFF7800/01*512 g,01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 7 @Device Feature/0xFFFF0000/01*004 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/src/dfu-util/device-logs/tdk-bluetooth.lsusb b/tools/linux/src/dfu-util/device-logs/tdk-bluetooth.lsusb old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/device-logs/tdk-bluetooth.lsusb rename to tools/linux/src/dfu-util/device-logs/tdk-bluetooth.lsusb index ac07bb7..c0cface --- a/tools/src/dfu-util/device-logs/tdk-bluetooth.lsusb +++ b/tools/linux/src/dfu-util/device-logs/tdk-bluetooth.lsusb @@ -1,269 +1,269 @@ - -Bus 006 Device 014: ID 04bf:0320 TDK Corp. Bluetooth Adapter -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 2.00 - bDeviceClass 224 Wireless - bDeviceSubClass 1 Radio Frequency - bDeviceProtocol 1 Bluetooth - bMaxPacketSize0 64 - idVendor 0x04bf TDK Corp. - idProduct 0x0320 Bluetooth Adapter - bcdDevice 26.52 - iManufacturer 1 Ezurio - iProduct 2 Turbo Bluetooth Adapter - iSerial 3 008098D4FFBD - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 193 - bNumInterfaces 3 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 64mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 3 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0010 1x 16 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0040 1x 64 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0000 1x 0 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0000 1x 0 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 1 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0009 1x 9 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0009 1x 9 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 2 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0011 1x 17 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0011 1x 17 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 3 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0019 1x 25 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0019 1x 25 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 4 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0021 1x 33 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0021 1x 33 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 5 - bNumEndpoints 2 - bInterfaceClass 224 Wireless - bInterfaceSubClass 1 Radio Frequency - bInterfaceProtocol 1 Bluetooth - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x03 EP 3 OUT - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0031 1x 49 bytes - bInterval 1 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x83 EP 3 IN - bmAttributes 1 - Transfer Type Isochronous - Synch Type None - Usage Type Data - wMaxPacketSize 0x0031 1x 49 bytes - bInterval 1 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 2 - bAlternateSetting 0 - bNumEndpoints 0 - bInterfaceClass 254 Application Specific Interface - bInterfaceSubClass 1 Device Firmware Update - bInterfaceProtocol 0 - iInterface 0 - Device Firmware Upgrade Interface Descriptor: - bLength 7 - bDescriptorType 33 - bmAttributes 7 - Will Not Detach - Manifestation Tolerant - Upload Supported - Download Supported - wDetachTimeout 5000 milliseconds - wTransferSize 1023 bytes -Device Status: 0x0000 - (Bus Powered) + +Bus 006 Device 014: ID 04bf:0320 TDK Corp. Bluetooth Adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 224 Wireless + bDeviceSubClass 1 Radio Frequency + bDeviceProtocol 1 Bluetooth + bMaxPacketSize0 64 + idVendor 0x04bf TDK Corp. + idProduct 0x0320 Bluetooth Adapter + bcdDevice 26.52 + iManufacturer 1 Ezurio + iProduct 2 Turbo Bluetooth Adapter + iSerial 3 008098D4FFBD + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 193 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 1 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 2 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 3 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 4 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 5 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 5000 milliseconds + wTransferSize 1023 bytes +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/src/dfu-util/dfuse-pack.py b/tools/linux/src/dfu-util/dfuse-pack.py old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/dfuse-pack.py rename to tools/linux/src/dfu-util/dfuse-pack.py index 022df6a..875cc5c --- a/tools/src/dfu-util/dfuse-pack.py +++ b/tools/linux/src/dfu-util/dfuse-pack.py @@ -1,121 +1,121 @@ -#!/usr/bin/python - -# Written by Antonio Galea - 2010/11/18 -# Distributed under Gnu LGPL 3.0 -# see http://www.gnu.org/licenses/lgpl-3.0.txt - -import sys,struct,zlib,os -from optparse import OptionParser - -DEFAULT_DEVICE="0x0483:0xdf11" - -def named(tuple,names): - return dict(zip(names.split(),tuple)) -def consume(fmt,data,names): - n = struct.calcsize(fmt) - return named(struct.unpack(fmt,data[:n]),names),data[n:] -def cstring(string): - return string.split('\0',1)[0] -def compute_crc(data): - return 0xFFFFFFFF & -zlib.crc32(data) -1 - -def parse(file,dump_images=False): - print 'File: "%s"' % file - data = open(file,'rb').read() - crc = compute_crc(data[:-4]) - prefix, data = consume('<5sBIB',data,'signature version size targets') - print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix - for t in range(prefix['targets']): - tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements') - tprefix['num'] = t - if tprefix['named']: - tprefix['name'] = cstring(tprefix['name']) - else: - tprefix['name'] = '' - print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix - tsize = tprefix['size'] - target, data = data[:tsize], data[tsize:] - for e in range(tprefix['elements']): - eprefix, target = consume('<2I',target,'address size') - eprefix['num'] = e - print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix - esize = eprefix['size'] - image, target = target[:esize], target[esize:] - if dump_images: - out = '%s.target%d.image%d.bin' % (file,t,e) - open(out,'wb').write(image) - print ' DUMPED IMAGE TO "%s"' % out - if len(target): - print "target %d: PARSE ERROR" % t - suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc') - print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix - if crc != suffix['crc']: - print "CRC ERROR: computed crc32 is 0x%08x" % crc - data = data[16:] - if data: - print "PARSE ERROR" - -def build(file,targets,device=DEFAULT_DEVICE): - data = '' - for t,target in enumerate(targets): - tdata = '' - for image in target: - tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data'] - tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata - data += tdata - data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data - v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1)) - data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16) - crc = compute_crc(data) - data += struct.pack(' and -Harald Welte . Over time, nearly complete -support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. -.SH LICENCE -.B dfu-util -is covered by the GNU General Public License (GPL), version 2 or later. -.SH COPYRIGHT -This manual page was originally written by Uwe Hermann , -and is now part of the dfu-util project. +.TH DFU-UTIL 1 "September 23, 2012" +.SH NAME +dfu-util \- Device firmware update (DFU) USB programmer +.SH SYNOPSIS +.\" Listing devices +.HP +.B dfu-util +.B \-l +.RB [\| \-v \|] +.RB [\| \-d +.IR vid:pid [\|, vid:pid \|]\|] +.RB [\| \-p +.IR path \|] +.RB [\| \-c +.IR configuration \|] +.RB [\| \-i +.IR interface \|] +.RB [\| \-a +.IR alt-intf \|] +.RB [\| \-S +.IR serial [\|, serial \|]\|] +.\" Download to or upload from device +.HP +.B dfu-util +.RB [\| \-v \|] +.RB [\| \-d +.IR vid:pid [\|, vid:pid \|]\|] +.RB [\| \-p +.IR path \|] +.RB [\| \-c +.IR configuration \|] +.RB [\| \-i +.IR interface \|] +.RB [\| \-a +.IR alt-intf \|] +.RB [\| \-S +.IR serial [\|, serial \|]\|] +.RB [\| \-t +.IR size \|] +.RB [\| \-Z +.IR size \|] +.RB [\| \-s +.IR address \|] +.RB [\| \-R \|] +.RB [\| \-D \||\| \-U +.IR file \|] +.\" --help and --version +.HP +.B dfu-util +.RB [\| \-hV \|] +.SH DESCRIPTION +.B dfu-util +is a program that implements the host (computer) side of the USB DFU +(Universal Serial Bus Device Firmware Upgrade) protocol. +.sp +dfu-util communicates with devices that implement the device side of the +USB DFU protocol, and is often used to upgrade the firmware of such +devices. +.SH OPTIONS +.TP +.B "\-l, \-\-list" +List the currently attached DFU capable USB devices. +.TP +.BR "\-d, \-\-device" " [\fIRun-Time VENDOR\fP]:[\fIRun-Time PRODUCT\fP][,[\fIDFU Mode VENDOR\fP]:[\fIDFU Mode PRODUCT\fP]]" +.RS +Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device +to work with. \fBVENDOR\fP and \fBPRODUCT\fP are hexadecimal numbers (no prefix +needed), "*" (match any), or "-" (match nothing). By default, any DFU capable +device in either run-time or DFU mode will be considered. +.sp +If you only have one standards-compliant DFU device attached to your computer, +this parameter is optional. However, as soon as you have multiple DFU devices +connected, dfu-util will detect this and abort, asking you to specify which +device to use. +.sp +If only run-time IDs are specified (e.g. "\fB--device 1457:51ab\fP"), then in +addition to the specified run-time IDs, any DFU mode devices will also be +considered. This is beneficial to allow a DFU capable device to be found +again after a switch to DFU mode, since the vendor and/or product ID of a +device usually changes in DFU mode. +.sp +If only DFU mode IDs are specified (e.g. "\fB--device ,951:26\fP"), then all +run-time devices will be ignored, making it easy to target a specific device in +DFU mode. +.sp +If both run-time and DFU mode IDs are specified (e.g. "\fB--device +1457:51ab,:2bc\fP"), then unspecified DFU mode components will use the run-time +value specified. +.sp +Examples: +.TP +.B "--device 1457:51ab,951:26" +.br +Work with a device in run-time mode with +vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 +and product ID 0x0026 +.sp +.TP +.B "--device 1457:51ab,:2bc" +.br +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc +.sp +.TP +.B "--device 1457:51ab" +.br +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with any vendor and product ID +.sp +.TP +.B "--device ,951:26" +.br +Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 +.sp +.TP +.B "--device *,-" +.br +Work with any device in run-time mode, and ignore any device in DFU mode +.sp +.TP +.B "--device ," +.br +Ignore any device in run-time mode, and Work with any device in DFU mode +.RE +.TP +.BR "\-p, \-\-path" " BUS-PORT. ... .PORT" +Specify the path to the DFU device. +.TP +.BR "\-c, \-\-cfg" " CONFIG-NR" +Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. +.TP +.BR "\-i, \-\-intf" " INTF-NR" +Specify the DFU interface number. +.TP +.BR "\-a, \-\-alt" " ALT" +Specify the altsetting of the DFU interface by name or by number. +.TP +.BR "\-S, \-\-serial" " [\fIRun-Time SERIAL\fP][,[\fIDFU Mode SERIAL\fP]]" +Specify the run-time and DFU mode serial numbers used to further restrict +device matches. If multiple, identical DFU devices are simultaneously +connected to a system then vendor and product ID will be insufficient for +targeting a single device. In this situation, it may be possible to use this +parameter to specify a serial number which also must match. +.sp +If only a single serial number is specified, then the same serial number is +used in both run-time and DFU mode. An empty serial number will match any +serial number in the corresponding mode. +.TP +.B "\-t, \-\-transfer-size" " SIZE" +Specify the number of bytes per USB transfer. The optimal value is +usually determined automatically so this option is rarely useful. If +you need to use this option for a device, please report it as a bug. +.TP +.B "\-Z, \-\-upload-size" " SIZE" +Specify the expected upload size, in bytes. +.TP +.BR "\-U, \-\-upload" " FILE" +Read firmware from device into +.BR FILE . +.TP +.BR "\-D, \-\-download" " FILE" +Write firmware from +.B FILE +into device. When FILE is \-, the firmware is read from stdin. +.TP +.B "\-R, \-\-reset" +Issue USB reset signalling after upload or download has finished. +.TP +.BR "\-s, \-\-dfuse-address" " address" +Specify target address for raw binary download/upload on DfuSe devices. Do +.B not +use this for downloading DfuSe (.dfu) files. Modifiers can be added +to the address, separated by a colon, to perform special DfuSE commands such +as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. +.TP +.B "\-v, \-\-verbose" +Print more information about dfu-util's operation. A second +.B -v +will turn on verbose logging of USB requests. Repeat this option to further +increase verbosity. +.TP +.B "\-h, \-\-help" +Show a help text and exit. +.TP +.B "\-V, \-\-version" +Show version information and exit. +.SH EXAMPLES +.SS Using dfu-util in the OpenMoko project +(with the Neo1973 hardware) +.PP +Flashing the rootfs: +.br +.B " $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2" +.PP +Flashing the kernel: +.br +.B " $ dfu-util -a kernel -R -D /path/to/uImage" +.PP +Flashing the bootloader: +.br +.B " $ dfu-util -a u-boot -R -D /path/to/u-boot.bin" +.PP +Copying a kernel into RAM: +.br +.B " $ dfu-util -a 0 -R -D /path/to/uImage" +.sp +Once this has finished, the kernel will be available at the default load +address of 0x32000000 in Neo1973 RAM. +.B Note: +You cannot transfer more than 2MB of data into RAM using this method. +.sp +.SS Using dfu-util with a DfuSe device +.PP +Flashing a +.B .dfu +(special DfuSe format) file to the device: +.br +.B " $ dfu-util -a 0 -D /path/to/dfuse-image.dfu" +.PP +Reading out 1 KB of flash starting at address 0x8000000: +.br +.B " $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin" +.PP +Flashing a binary file to address 0x8004000 of device memory and +ask the device to leave DFU mode: +.br +.B " $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin" +.\" There are no bugs of course +.SH BUGS +Please report any bugs to the dfu-util mailing list at +.BR dfu-util@lists.gnumonks.org . +Please use the +.IR --verbose " option (repeated as necessary) to provide more" +information in your bug report. +.SH SEE ALSO +The dfu-util home page is +.B http://dfu-util.gnumonks.org +.SH HISTORY +dfu-util was originally written for the OpenMoko project by +Weston Schmidt and +Harald Welte . Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +.SH LICENCE +.B dfu-util +is covered by the GNU General Public License (GPL), version 2 or later. +.SH COPYRIGHT +This manual page was originally written by Uwe Hermann , +and is now part of the dfu-util project. diff --git a/tools/src/dfu-util/msvc/README_msvc.txt b/tools/linux/src/dfu-util/msvc/README_msvc.txt old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/msvc/README_msvc.txt rename to tools/linux/src/dfu-util/msvc/README_msvc.txt index 25aeeed..6e68ec6 --- a/tools/src/dfu-util/msvc/README_msvc.txt +++ b/tools/linux/src/dfu-util/msvc/README_msvc.txt @@ -1,10 +1,10 @@ -# (C) Roger Meier -# (C) Pascal Schweizer -# msvc folder is GPL-2.0+, LGPL-2.1+, BSD-3-Clause or MIT license(SPDX) - -Building dfu-util native on Windows with Visual Studio - -3rd party dependencies: -- libusbx ( git clone https://github.com/libusbx/libusbx.git ) - - getopt (part of libusbx: libusbx/examples/getopt) - +# (C) Roger Meier +# (C) Pascal Schweizer +# msvc folder is GPL-2.0+, LGPL-2.1+, BSD-3-Clause or MIT license(SPDX) + +Building dfu-util native on Windows with Visual Studio + +3rd party dependencies: +- libusbx ( git clone https://github.com/libusbx/libusbx.git ) + - getopt (part of libusbx: libusbx/examples/getopt) + diff --git a/tools/src/dfu-util/msvc/dfu-suffix_2010.vcxproj b/tools/linux/src/dfu-util/msvc/dfu-suffix_2010.vcxproj old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/msvc/dfu-suffix_2010.vcxproj rename to tools/linux/src/dfu-util/msvc/dfu-suffix_2010.vcxproj index 87b7ead..0c316c2 --- a/tools/src/dfu-util/msvc/dfu-suffix_2010.vcxproj +++ b/tools/linux/src/dfu-util/msvc/dfu-suffix_2010.vcxproj @@ -1,100 +1,100 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA} - dfusuffix - dfu-suffix - - - - Application - true - MultiByte - - - Application - false - true - MultiByte - - - - - - - - - - - - - $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) - $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib - $(ExecutablePath) - - - $(ExecutablePath) - $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreadedDebug - - - true - - - - - Level3 - MaxSpeed - true - true - HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - true - - - - - - - - - - - - - {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} - - - {349ee8f9-7d25-4909-aaf5-ff3fade72187} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA} + dfusuffix + dfu-suffix + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + \ No newline at end of file diff --git a/tools/src/dfu-util/msvc/dfu-util_2010.sln b/tools/linux/src/dfu-util/msvc/dfu-util_2010.sln old mode 100755 new mode 100644 similarity index 98% rename from tools/src/dfu-util/msvc/dfu-util_2010.sln rename to tools/linux/src/dfu-util/msvc/dfu-util_2010.sln index e6c932c..ef79723 --- a/tools/src/dfu-util/msvc/dfu-util_2010.sln +++ b/tools/linux/src/dfu-util/msvc/dfu-util_2010.sln @@ -1,54 +1,54 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-util", "dfu-util_2010.vcxproj", "{0E071A60-7EF2-4427-BAA8-9143CACB5BCB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4F8746D-B27E-4806-95E5-2052174E923B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-suffix", "dfu-suffix_2010.vcxproj", "{8F7600A2-3B37-4956-B39B-A1D43EF29EDA}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt_2010", "..\..\libusbx\msvc\getopt_2010.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\..\libusbx\msvc\libusb_static_2010.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.ActiveCfg = Debug|Win32 - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.Build.0 = Debug|Win32 - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|x64.ActiveCfg = Debug|Win32 - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.ActiveCfg = Release|Win32 - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.Build.0 = Release|Win32 - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|x64.ActiveCfg = Release|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.ActiveCfg = Debug|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.Build.0 = Debug|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|x64.ActiveCfg = Debug|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.ActiveCfg = Release|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.Build.0 = Release|Win32 - {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|x64.ActiveCfg = Release|Win32 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 - {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 - {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-util", "dfu-util_2010.vcxproj", "{0E071A60-7EF2-4427-BAA8-9143CACB5BCB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4F8746D-B27E-4806-95E5-2052174E923B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-suffix", "dfu-suffix_2010.vcxproj", "{8F7600A2-3B37-4956-B39B-A1D43EF29EDA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt_2010", "..\..\libusbx\msvc\getopt_2010.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\..\libusbx\msvc\libusb_static_2010.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.Build.0 = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|x64.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.ActiveCfg = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.Build.0 = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|x64.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.Build.0 = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|x64.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.Build.0 = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|x64.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/src/dfu-util/msvc/dfu-util_2010.vcxproj b/tools/linux/src/dfu-util/msvc/dfu-util_2010.vcxproj old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/msvc/dfu-util_2010.vcxproj rename to tools/linux/src/dfu-util/msvc/dfu-util_2010.vcxproj index cb07b64..17a8bee --- a/tools/src/dfu-util/msvc/dfu-util_2010.vcxproj +++ b/tools/linux/src/dfu-util/msvc/dfu-util_2010.vcxproj @@ -1,120 +1,120 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0E071A60-7EF2-4427-BAA8-9143CACB5BCB} - dfuutil - dfu-util - - - - Application - true - MultiByte - - - Application - false - true - MultiByte - - - - - - - - - - - - - $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) - $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib - $(ExecutablePath) - - - $(ExecutablePath) - $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) - $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - - - - Level3 - Disabled - HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreadedDebug - - - true - - - - - - - - - Level3 - MaxSpeed - true - true - HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - MultiThreaded - - - true - true - true - - - copy $(SolutionDir)..\$(Platform)\$(Configuration)\dll\libusb-1.0.dll $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ - - - - - - - - - - - - - - - - - - - - - - - - - - {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} - - - {349ee8f9-7d25-4909-aaf5-ff3fade72187} - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB} + dfuutil + dfu-util + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + copy $(SolutionDir)..\$(Platform)\$(Configuration)\dll\libusb-1.0.dll $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + \ No newline at end of file diff --git a/tools/src/dfu-util/src/Makefile.am b/tools/linux/src/dfu-util/src/Makefile.am old mode 100755 new mode 100644 similarity index 94% rename from tools/src/dfu-util/src/Makefile.am rename to tools/linux/src/dfu-util/src/Makefile.am index b1f810a..70179c4 --- a/tools/src/dfu-util/src/Makefile.am +++ b/tools/linux/src/dfu-util/src/Makefile.am @@ -1,28 +1,28 @@ -AM_CFLAGS = -Wall -Wextra - -bin_PROGRAMS = dfu-util dfu-suffix dfu-prefix -dfu_util_SOURCES = main.c \ - portable.h \ - dfu_load.c \ - dfu_load.h \ - dfu_util.c \ - dfu_util.h \ - dfuse.c \ - dfuse.h \ - dfuse_mem.c \ - dfuse_mem.h \ - dfu.c \ - dfu.h \ - usb_dfu.h \ - dfu_file.c \ - dfu_file.h \ - quirks.c \ - quirks.h - -dfu_suffix_SOURCES = suffix.c \ - dfu_file.h \ - dfu_file.c - -dfu_prefix_SOURCES = prefix.c \ - dfu_file.h \ - dfu_file.c +AM_CFLAGS = -Wall -Wextra + +bin_PROGRAMS = dfu-util dfu-suffix dfu-prefix +dfu_util_SOURCES = main.c \ + portable.h \ + dfu_load.c \ + dfu_load.h \ + dfu_util.c \ + dfu_util.h \ + dfuse.c \ + dfuse.h \ + dfuse_mem.c \ + dfuse_mem.h \ + dfu.c \ + dfu.h \ + usb_dfu.h \ + dfu_file.c \ + dfu_file.h \ + quirks.c \ + quirks.h + +dfu_suffix_SOURCES = suffix.c \ + dfu_file.h \ + dfu_file.c + +dfu_prefix_SOURCES = prefix.c \ + dfu_file.h \ + dfu_file.c diff --git a/tools/src/dfu-util/src/dfu.c b/tools/linux/src/dfu-util/src/dfu.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfu.c rename to tools/linux/src/dfu-util/src/dfu.c index 050ad50..14d7673 --- a/tools/src/dfu-util/src/dfu.c +++ b/tools/linux/src/dfu-util/src/dfu.c @@ -1,357 +1,357 @@ -/* - * Low-level DFU communication routines, originally taken from - * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ - * (part of dfu-programmer). - * - * Copyright 2005-2006 Weston Schmidt - * Copyright 2011-2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include - -#include - -#include "portable.h" -#include "dfu.h" -#include "quirks.h" - -static int dfu_timeout = 5000; /* 5 seconds - default */ - -/* - * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * timeout - the timeout in ms the USB device should wait for a pending - * USB reset before giving up and terminating the operation - * - * returns 0 or < 0 on error - */ -int dfu_detach( libusb_device_handle *device, - const unsigned short interface, - const unsigned short timeout ) -{ - return libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_DETACH, - /* wValue */ timeout, - /* wIndex */ interface, - /* Data */ NULL, - /* wLength */ 0, - dfu_timeout ); -} - - -/* - * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * length - the total number of bytes to transfer to the USB - * device - must be less than wTransferSize - * data - the data to transfer - * - * returns the number of bytes written or < 0 on error - */ -int dfu_download( libusb_device_handle *device, - const unsigned short interface, - const unsigned short length, - const unsigned short transaction, - unsigned char* data ) -{ - int status; - - status = libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_DNLOAD, - /* wValue */ transaction, - /* wIndex */ interface, - /* Data */ data, - /* wLength */ length, - dfu_timeout ); - return status; -} - - -/* - * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * length - the maximum number of bytes to receive from the USB - * device - must be less than wTransferSize - * data - the buffer to put the received data in - * - * returns the number of bytes received or < 0 on error - */ -int dfu_upload( libusb_device_handle *device, - const unsigned short interface, - const unsigned short length, - const unsigned short transaction, - unsigned char* data ) -{ - int status; - - status = libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_UPLOAD, - /* wValue */ transaction, - /* wIndex */ interface, - /* Data */ data, - /* wLength */ length, - dfu_timeout ); - return status; -} - - -/* - * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * status - the data structure to be populated with the results - * - * return the number of bytes read in or < 0 on an error - */ -int dfu_get_status( struct dfu_if *dif, struct dfu_status *status ) -{ - unsigned char buffer[6]; - int result; - - /* Initialize the status data structure */ - status->bStatus = DFU_STATUS_ERROR_UNKNOWN; - status->bwPollTimeout = 0; - status->bState = STATE_DFU_ERROR; - status->iString = 0; - - result = libusb_control_transfer( dif->dev_handle, - /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_GETSTATUS, - /* wValue */ 0, - /* wIndex */ dif->interface, - /* Data */ buffer, - /* wLength */ 6, - dfu_timeout ); - - if( 6 == result ) { - status->bStatus = buffer[0]; - if (dif->quirks & QUIRK_POLLTIMEOUT) - status->bwPollTimeout = DEFAULT_POLLTIMEOUT; - else - status->bwPollTimeout = ((0xff & buffer[3]) << 16) | - ((0xff & buffer[2]) << 8) | - (0xff & buffer[1]); - status->bState = buffer[4]; - status->iString = buffer[5]; - } - - return result; -} - - -/* - * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * - * return 0 or < 0 on an error - */ -int dfu_clear_status( libusb_device_handle *device, - const unsigned short interface ) -{ - return libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_OUT| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_CLRSTATUS, - /* wValue */ 0, - /* wIndex */ interface, - /* Data */ NULL, - /* wLength */ 0, - dfu_timeout ); -} - - -/* - * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * length - the maximum number of bytes to receive from the USB - * device - must be less than wTransferSize - * data - the buffer to put the received data in - * - * returns the state or < 0 on error - */ -int dfu_get_state( libusb_device_handle *device, - const unsigned short interface ) -{ - int result; - unsigned char buffer[1]; - - result = libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_GETSTATE, - /* wValue */ 0, - /* wIndex */ interface, - /* Data */ buffer, - /* wLength */ 1, - dfu_timeout ); - - /* Return the error if there is one. */ - if (result < 1) - return -1; - - /* Return the state. */ - return buffer[0]; -} - - -/* - * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) - * - * device - the usb_dev_handle to communicate with - * interface - the interface to communicate with - * - * returns 0 or < 0 on an error - */ -int dfu_abort( libusb_device_handle *device, - const unsigned short interface ) -{ - return libusb_control_transfer( device, - /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_ABORT, - /* wValue */ 0, - /* wIndex */ interface, - /* Data */ NULL, - /* wLength */ 0, - dfu_timeout ); -} - - -const char* dfu_state_to_string( int state ) -{ - const char *message; - - switch (state) { - case STATE_APP_IDLE: - message = "appIDLE"; - break; - case STATE_APP_DETACH: - message = "appDETACH"; - break; - case STATE_DFU_IDLE: - message = "dfuIDLE"; - break; - case STATE_DFU_DOWNLOAD_SYNC: - message = "dfuDNLOAD-SYNC"; - break; - case STATE_DFU_DOWNLOAD_BUSY: - message = "dfuDNBUSY"; - break; - case STATE_DFU_DOWNLOAD_IDLE: - message = "dfuDNLOAD-IDLE"; - break; - case STATE_DFU_MANIFEST_SYNC: - message = "dfuMANIFEST-SYNC"; - break; - case STATE_DFU_MANIFEST: - message = "dfuMANIFEST"; - break; - case STATE_DFU_MANIFEST_WAIT_RESET: - message = "dfuMANIFEST-WAIT-RESET"; - break; - case STATE_DFU_UPLOAD_IDLE: - message = "dfuUPLOAD-IDLE"; - break; - case STATE_DFU_ERROR: - message = "dfuERROR"; - break; - default: - message = NULL; - break; - } - - return message; -} - -/* Chapter 6.1.2 */ -static const char *dfu_status_names[] = { - /* DFU_STATUS_OK */ - "No error condition is present", - /* DFU_STATUS_errTARGET */ - "File is not targeted for use by this device", - /* DFU_STATUS_errFILE */ - "File is for this device but fails some vendor-specific test", - /* DFU_STATUS_errWRITE */ - "Device is unable to write memory", - /* DFU_STATUS_errERASE */ - "Memory erase function failed", - /* DFU_STATUS_errCHECK_ERASED */ - "Memory erase check failed", - /* DFU_STATUS_errPROG */ - "Program memory function failed", - /* DFU_STATUS_errVERIFY */ - "Programmed memory failed verification", - /* DFU_STATUS_errADDRESS */ - "Cannot program memory due to received address that is out of range", - /* DFU_STATUS_errNOTDONE */ - "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", - /* DFU_STATUS_errFIRMWARE */ - "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", - /* DFU_STATUS_errVENDOR */ - "iString indicates a vendor specific error", - /* DFU_STATUS_errUSBR */ - "Device detected unexpected USB reset signalling", - /* DFU_STATUS_errPOR */ - "Device detected unexpected power on reset", - /* DFU_STATUS_errUNKNOWN */ - "Something went wrong, but the device does not know what it was", - /* DFU_STATUS_errSTALLEDPKT */ - "Device stalled an unexpected request" -}; - - -const char *dfu_status_to_string(int status) -{ - if (status > DFU_STATUS_errSTALLEDPKT) - return "INVALID"; - return dfu_status_names[status]; -} - -int dfu_abort_to_idle(struct dfu_if *dif) -{ - int ret; - struct dfu_status dst; - - ret = dfu_abort(dif->dev_handle, dif->interface); - if (ret < 0) { - errx(EX_IOERR, "Error sending dfu abort request"); - exit(1); - } - ret = dfu_get_status(dif, &dst); - if (ret < 0) { - errx(EX_IOERR, "Error during abort get_status"); - exit(1); - } - if (dst.bState != DFU_STATE_dfuIDLE) { - errx(EX_IOERR, "Failed to enter idle state on abort"); - exit(1); - } - milli_sleep(dst.bwPollTimeout); - return ret; -} +/* + * Low-level DFU communication routines, originally taken from + * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ + * (part of dfu-programmer). + * + * Copyright 2005-2006 Weston Schmidt + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "quirks.h" + +static int dfu_timeout = 5000; /* 5 seconds - default */ + +/* + * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * timeout - the timeout in ms the USB device should wait for a pending + * USB reset before giving up and terminating the operation + * + * returns 0 or < 0 on error + */ +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DETACH, + /* wValue */ timeout, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the total number of bytes to transfer to the USB + * device - must be less than wTransferSize + * data - the data to transfer + * + * returns the number of bytes written or < 0 on error + */ +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the number of bytes received or < 0 on error + */ +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * status - the data structure to be populated with the results + * + * return the number of bytes read in or < 0 on an error + */ +int dfu_get_status( struct dfu_if *dif, struct dfu_status *status ) +{ + unsigned char buffer[6]; + int result; + + /* Initialize the status data structure */ + status->bStatus = DFU_STATUS_ERROR_UNKNOWN; + status->bwPollTimeout = 0; + status->bState = STATE_DFU_ERROR; + status->iString = 0; + + result = libusb_control_transfer( dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATUS, + /* wValue */ 0, + /* wIndex */ dif->interface, + /* Data */ buffer, + /* wLength */ 6, + dfu_timeout ); + + if( 6 == result ) { + status->bStatus = buffer[0]; + if (dif->quirks & QUIRK_POLLTIMEOUT) + status->bwPollTimeout = DEFAULT_POLLTIMEOUT; + else + status->bwPollTimeout = ((0xff & buffer[3]) << 16) | + ((0xff & buffer[2]) << 8) | + (0xff & buffer[1]); + status->bState = buffer[4]; + status->iString = buffer[5]; + } + + return result; +} + + +/* + * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * return 0 or < 0 on an error + */ +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_CLRSTATUS, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the state or < 0 on error + */ +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ) +{ + int result; + unsigned char buffer[1]; + + result = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATE, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ buffer, + /* wLength */ 1, + dfu_timeout ); + + /* Return the error if there is one. */ + if (result < 1) + return -1; + + /* Return the state. */ + return buffer[0]; +} + + +/* + * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * returns 0 or < 0 on an error + */ +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_ABORT, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +const char* dfu_state_to_string( int state ) +{ + const char *message; + + switch (state) { + case STATE_APP_IDLE: + message = "appIDLE"; + break; + case STATE_APP_DETACH: + message = "appDETACH"; + break; + case STATE_DFU_IDLE: + message = "dfuIDLE"; + break; + case STATE_DFU_DOWNLOAD_SYNC: + message = "dfuDNLOAD-SYNC"; + break; + case STATE_DFU_DOWNLOAD_BUSY: + message = "dfuDNBUSY"; + break; + case STATE_DFU_DOWNLOAD_IDLE: + message = "dfuDNLOAD-IDLE"; + break; + case STATE_DFU_MANIFEST_SYNC: + message = "dfuMANIFEST-SYNC"; + break; + case STATE_DFU_MANIFEST: + message = "dfuMANIFEST"; + break; + case STATE_DFU_MANIFEST_WAIT_RESET: + message = "dfuMANIFEST-WAIT-RESET"; + break; + case STATE_DFU_UPLOAD_IDLE: + message = "dfuUPLOAD-IDLE"; + break; + case STATE_DFU_ERROR: + message = "dfuERROR"; + break; + default: + message = NULL; + break; + } + + return message; +} + +/* Chapter 6.1.2 */ +static const char *dfu_status_names[] = { + /* DFU_STATUS_OK */ + "No error condition is present", + /* DFU_STATUS_errTARGET */ + "File is not targeted for use by this device", + /* DFU_STATUS_errFILE */ + "File is for this device but fails some vendor-specific test", + /* DFU_STATUS_errWRITE */ + "Device is unable to write memory", + /* DFU_STATUS_errERASE */ + "Memory erase function failed", + /* DFU_STATUS_errCHECK_ERASED */ + "Memory erase check failed", + /* DFU_STATUS_errPROG */ + "Program memory function failed", + /* DFU_STATUS_errVERIFY */ + "Programmed memory failed verification", + /* DFU_STATUS_errADDRESS */ + "Cannot program memory due to received address that is out of range", + /* DFU_STATUS_errNOTDONE */ + "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", + /* DFU_STATUS_errFIRMWARE */ + "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", + /* DFU_STATUS_errVENDOR */ + "iString indicates a vendor specific error", + /* DFU_STATUS_errUSBR */ + "Device detected unexpected USB reset signalling", + /* DFU_STATUS_errPOR */ + "Device detected unexpected power on reset", + /* DFU_STATUS_errUNKNOWN */ + "Something went wrong, but the device does not know what it was", + /* DFU_STATUS_errSTALLEDPKT */ + "Device stalled an unexpected request" +}; + + +const char *dfu_status_to_string(int status) +{ + if (status > DFU_STATUS_errSTALLEDPKT) + return "INVALID"; + return dfu_status_names[status]; +} + +int dfu_abort_to_idle(struct dfu_if *dif) +{ + int ret; + struct dfu_status dst; + + ret = dfu_abort(dif->dev_handle, dif->interface); + if (ret < 0) { + errx(EX_IOERR, "Error sending dfu abort request"); + exit(1); + } + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during abort get_status"); + exit(1); + } + if (dst.bState != DFU_STATE_dfuIDLE) { + errx(EX_IOERR, "Failed to enter idle state on abort"); + exit(1); + } + milli_sleep(dst.bwPollTimeout); + return ret; +} diff --git a/tools/src/dfu-util/src/dfu.h b/tools/linux/src/dfu-util/src/dfu.h old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/dfu.h rename to tools/linux/src/dfu-util/src/dfu.h index 39f18c3..8e3caeb --- a/tools/src/dfu-util/src/dfu.h +++ b/tools/linux/src/dfu-util/src/dfu.h @@ -1,133 +1,133 @@ -/* - * dfu-programmer - * - * $Id: dfu.h,v 1.2 2005/09/25 01:27:42 schmidtw Exp $ - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef DFU_H -#define DFU_H - -#include -#include "usb_dfu.h" - -/* DFU states */ -#define STATE_APP_IDLE 0x00 -#define STATE_APP_DETACH 0x01 -#define STATE_DFU_IDLE 0x02 -#define STATE_DFU_DOWNLOAD_SYNC 0x03 -#define STATE_DFU_DOWNLOAD_BUSY 0x04 -#define STATE_DFU_DOWNLOAD_IDLE 0x05 -#define STATE_DFU_MANIFEST_SYNC 0x06 -#define STATE_DFU_MANIFEST 0x07 -#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 -#define STATE_DFU_UPLOAD_IDLE 0x09 -#define STATE_DFU_ERROR 0x0a - - -/* DFU status */ -#define DFU_STATUS_OK 0x00 -#define DFU_STATUS_ERROR_TARGET 0x01 -#define DFU_STATUS_ERROR_FILE 0x02 -#define DFU_STATUS_ERROR_WRITE 0x03 -#define DFU_STATUS_ERROR_ERASE 0x04 -#define DFU_STATUS_ERROR_CHECK_ERASED 0x05 -#define DFU_STATUS_ERROR_PROG 0x06 -#define DFU_STATUS_ERROR_VERIFY 0x07 -#define DFU_STATUS_ERROR_ADDRESS 0x08 -#define DFU_STATUS_ERROR_NOTDONE 0x09 -#define DFU_STATUS_ERROR_FIRMWARE 0x0a -#define DFU_STATUS_ERROR_VENDOR 0x0b -#define DFU_STATUS_ERROR_USBR 0x0c -#define DFU_STATUS_ERROR_POR 0x0d -#define DFU_STATUS_ERROR_UNKNOWN 0x0e -#define DFU_STATUS_ERROR_STALLEDPKT 0x0f - -/* DFU commands */ -#define DFU_DETACH 0 -#define DFU_DNLOAD 1 -#define DFU_UPLOAD 2 -#define DFU_GETSTATUS 3 -#define DFU_CLRSTATUS 4 -#define DFU_GETSTATE 5 -#define DFU_ABORT 6 - -/* DFU interface */ -#define DFU_IFF_DFU 0x0001 /* DFU Mode, (not Runtime) */ - -/* This is based off of DFU_GETSTATUS - * - * 1 unsigned byte bStatus - * 3 unsigned byte bwPollTimeout - * 1 unsigned byte bState - * 1 unsigned byte iString -*/ - -struct dfu_status { - unsigned char bStatus; - unsigned int bwPollTimeout; - unsigned char bState; - unsigned char iString; -}; - -struct dfu_if { - struct usb_dfu_func_descriptor func_dfu; - uint16_t quirks; - uint16_t busnum; - uint16_t devnum; - uint16_t vendor; - uint16_t product; - uint16_t bcdDevice; - uint8_t configuration; - uint8_t interface; - uint8_t altsetting; - uint8_t flags; - uint8_t bMaxPacketSize0; - char *alt_name; - char *serial_name; - libusb_device *dev; - libusb_device_handle *dev_handle; - struct dfu_if *next; -}; - -int dfu_detach( libusb_device_handle *device, - const unsigned short interface, - const unsigned short timeout ); -int dfu_download( libusb_device_handle *device, - const unsigned short interface, - const unsigned short length, - const unsigned short transaction, - unsigned char* data ); -int dfu_upload( libusb_device_handle *device, - const unsigned short interface, - const unsigned short length, - const unsigned short transaction, - unsigned char* data ); -int dfu_get_status( struct dfu_if *dif, - struct dfu_status *status ); -int dfu_clear_status( libusb_device_handle *device, - const unsigned short interface ); -int dfu_get_state( libusb_device_handle *device, - const unsigned short interface ); -int dfu_abort( libusb_device_handle *device, - const unsigned short interface ); -int dfu_abort_to_idle( struct dfu_if *dif); - -const char *dfu_state_to_string( int state ); - -const char *dfu_status_to_string( int status ); - -#endif /* DFU_H */ +/* + * dfu-programmer + * + * $Id: dfu.h,v 1.2 2005/09/25 01:27:42 schmidtw Exp $ + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFU_H +#define DFU_H + +#include +#include "usb_dfu.h" + +/* DFU states */ +#define STATE_APP_IDLE 0x00 +#define STATE_APP_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + + +/* DFU status */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_ERROR_TARGET 0x01 +#define DFU_STATUS_ERROR_FILE 0x02 +#define DFU_STATUS_ERROR_WRITE 0x03 +#define DFU_STATUS_ERROR_ERASE 0x04 +#define DFU_STATUS_ERROR_CHECK_ERASED 0x05 +#define DFU_STATUS_ERROR_PROG 0x06 +#define DFU_STATUS_ERROR_VERIFY 0x07 +#define DFU_STATUS_ERROR_ADDRESS 0x08 +#define DFU_STATUS_ERROR_NOTDONE 0x09 +#define DFU_STATUS_ERROR_FIRMWARE 0x0a +#define DFU_STATUS_ERROR_VENDOR 0x0b +#define DFU_STATUS_ERROR_USBR 0x0c +#define DFU_STATUS_ERROR_POR 0x0d +#define DFU_STATUS_ERROR_UNKNOWN 0x0e +#define DFU_STATUS_ERROR_STALLEDPKT 0x0f + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +/* DFU interface */ +#define DFU_IFF_DFU 0x0001 /* DFU Mode, (not Runtime) */ + +/* This is based off of DFU_GETSTATUS + * + * 1 unsigned byte bStatus + * 3 unsigned byte bwPollTimeout + * 1 unsigned byte bState + * 1 unsigned byte iString +*/ + +struct dfu_status { + unsigned char bStatus; + unsigned int bwPollTimeout; + unsigned char bState; + unsigned char iString; +}; + +struct dfu_if { + struct usb_dfu_func_descriptor func_dfu; + uint16_t quirks; + uint16_t busnum; + uint16_t devnum; + uint16_t vendor; + uint16_t product; + uint16_t bcdDevice; + uint8_t configuration; + uint8_t interface; + uint8_t altsetting; + uint8_t flags; + uint8_t bMaxPacketSize0; + char *alt_name; + char *serial_name; + libusb_device *dev; + libusb_device_handle *dev_handle; + struct dfu_if *next; +}; + +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ); +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_get_status( struct dfu_if *dif, + struct dfu_status *status ); +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ); +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort_to_idle( struct dfu_if *dif); + +const char *dfu_state_to_string( int state ); + +const char *dfu_status_to_string( int status ); + +#endif /* DFU_H */ diff --git a/tools/src/dfu-util/src/dfu_file.c b/tools/linux/src/dfu-util/src/dfu_file.c old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/dfu_file.c rename to tools/linux/src/dfu-util/src/dfu_file.c index 2f5a585..7c897d4 --- a/tools/src/dfu-util/src/dfu_file.c +++ b/tools/linux/src/dfu-util/src/dfu_file.c @@ -1,444 +1,444 @@ -/* - * Load or store DFU files including suffix and prefix - * - * Copyright 2014 Tormod Volden - * Copyright 2012 Stefan Schmidt - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "portable.h" -#include "dfu_file.h" - -#define DFU_SUFFIX_LENGTH 16 -#define LMDFU_PREFIX_LENGTH 8 -#define LPCDFU_PREFIX_LENGTH 16 -#define PROGRESS_BAR_WIDTH 25 -#define STDIN_CHUNK_SIZE 65536 - -static const unsigned long crc32_table[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; - -static uint32_t crc32_byte(uint32_t accum, uint8_t delta) -{ - return crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); -} - -static int probe_prefix(struct dfu_file *file) -{ - uint8_t *prefix = file->firmware; - - if (file->size.total < LMDFU_PREFIX_LENGTH) - return 1; - if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { - file->prefix_type = LMDFU_PREFIX; - file->size.prefix = LMDFU_PREFIX_LENGTH; - file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); - } - else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f)== 0x3f)) { - file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; - file->size.prefix = LPCDFU_PREFIX_LENGTH; - } - - if (file->size.prefix + file->size.suffix > file->size.total) - return 1; - return 0; -} - -void dfu_progress_bar(const char *desc, unsigned long long curr, - unsigned long long max) -{ - static char buf[PROGRESS_BAR_WIDTH + 1]; - static unsigned long long last_progress = -1; - static time_t last_time; - time_t curr_time = time(NULL); - unsigned long long progress; - unsigned long long x; - - /* check for not known maximum */ - if (max < curr) - max = curr + 1; - /* make none out of none give zero */ - if (max == 0 && curr == 0) - max = 1; - - /* compute completion */ - progress = (PROGRESS_BAR_WIDTH * curr) / max; - if (progress > PROGRESS_BAR_WIDTH) - progress = PROGRESS_BAR_WIDTH; - if (progress == last_progress && - curr_time == last_time) - return; - last_progress = progress; - last_time = curr_time; - - for (x = 0; x != PROGRESS_BAR_WIDTH; x++) { - if (x < progress) - buf[x] = '='; - else - buf[x] = ' '; - } - buf[x] = 0; - - printf("\r%s\t[%s] %3lld%% %12lld bytes", desc, buf, - (100ULL * curr) / max, curr); - - if (progress == PROGRESS_BAR_WIDTH) - printf("\n%s done.\n", desc); -} - -void *dfu_malloc(size_t size) -{ - void *ptr = malloc(size); - if (ptr == NULL) - errx(EX_SOFTWARE, "Cannot allocate memory of size %d bytes", (int)size); - return (ptr); -} - -uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size) -{ - int x; - - /* compute CRC */ - for (x = 0; x != size; x++) - crc = crc32_byte(crc, ((uint8_t *)buf)[x]); - - /* write data */ - if (write(f, buf, size) != size) - err(EX_IOERR, "Could not write %d bytes to file %d", size, f); - - return (crc); -} - -void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix) -{ - off_t offset; - int f; - int i; - int res; - - file->size.prefix = 0; - file->size.suffix = 0; - - /* default values, if no valid suffix is found */ - file->bcdDFU = 0; - file->idVendor = 0xffff; /* wildcard value */ - file->idProduct = 0xffff; /* wildcard value */ - file->bcdDevice = 0xffff; /* wildcard value */ - - /* default values, if no valid prefix is found */ - file->lmdfu_address = 0; - - free(file->firmware); - - if (!strcmp(file->name, "-")) { - int read_bytes; - -#ifdef WIN32 - _setmode( _fileno( stdin ), _O_BINARY ); -#endif - file->firmware = (uint8_t*) dfu_malloc(STDIN_CHUNK_SIZE); - read_bytes = fread(file->firmware, 1, STDIN_CHUNK_SIZE, stdin); - file->size.total = read_bytes; - while (read_bytes == STDIN_CHUNK_SIZE) { - file->firmware = (uint8_t*) realloc(file->firmware, file->size.total + STDIN_CHUNK_SIZE); - if (!file->firmware) - err(EX_IOERR, "Could not allocate firmware buffer"); - read_bytes = fread(file->firmware + file->size.total, 1, STDIN_CHUNK_SIZE, stdin); - file->size.total += read_bytes; - } - if (verbose) - printf("Read %i bytes from stdin\n", file->size.total); - /* Never require suffix when reading from stdin */ - check_suffix = MAYBE_SUFFIX; - } else { - f = open(file->name, O_RDONLY | O_BINARY); - if (f < 0) - err(EX_IOERR, "Could not open file %s for reading", file->name); - - offset = lseek(f, 0, SEEK_END); - - if ((int)offset < 0 || (int)offset != offset) - err(EX_IOERR, "File size is too big"); - - if (lseek(f, 0, SEEK_SET) != 0) - err(EX_IOERR, "Could not seek to beginning"); - - file->size.total = offset; - file->firmware = dfu_malloc(file->size.total); - - if (read(f, file->firmware, file->size.total) != file->size.total) { - err(EX_IOERR, "Could not read %d bytes from %s", - file->size.total, file->name); - } - close(f); - } - - /* Check for possible DFU file suffix by trying to parse one */ - { - uint32_t crc = 0xffffffff; - const uint8_t *dfusuffix; - int missing_suffix = 0; - const char *reason; - - if (file->size.total < DFU_SUFFIX_LENGTH) { - reason = "File too short for DFU suffix"; - missing_suffix = 1; - goto checked; - } - - dfusuffix = file->firmware + file->size.total - - DFU_SUFFIX_LENGTH; - - for (i = 0; i < file->size.total - 4; i++) - crc = crc32_byte(crc, file->firmware[i]); - - if (dfusuffix[10] != 'D' || - dfusuffix[9] != 'F' || - dfusuffix[8] != 'U') { - reason = "Invalid DFU suffix signature"; - missing_suffix = 1; - goto checked; - } - - file->dwCRC = (dfusuffix[15] << 24) + - (dfusuffix[14] << 16) + - (dfusuffix[13] << 8) + - dfusuffix[12]; - - if (file->dwCRC != crc) { - reason = "DFU suffix CRC does not match"; - missing_suffix = 1; - goto checked; - } - - /* At this point we believe we have a DFU suffix - so we require further checks to succeed */ - - file->bcdDFU = (dfusuffix[7] << 8) + dfusuffix[6]; - - if (verbose) - printf("DFU suffix version %x\n", file->bcdDFU); - - file->size.suffix = dfusuffix[11]; - - if (file->size.suffix < DFU_SUFFIX_LENGTH) { - errx(EX_IOERR, "Unsupported DFU suffix length %d", - file->size.suffix); - } - - if (file->size.suffix > file->size.total) { - errx(EX_IOERR, "Invalid DFU suffix length %d", - file->size.suffix); - } - - file->idVendor = (dfusuffix[5] << 8) + dfusuffix[4]; - file->idProduct = (dfusuffix[3] << 8) + dfusuffix[2]; - file->bcdDevice = (dfusuffix[1] << 8) + dfusuffix[0]; - -checked: - if (missing_suffix) { - if (check_suffix == NEEDS_SUFFIX) { - warnx("%s", reason); - errx(EX_IOERR, "Valid DFU suffix needed"); - } else if (check_suffix == MAYBE_SUFFIX) { - warnx("%s", reason); - warnx("A valid DFU suffix will be required in " - "a future dfu-util release!!!"); - } - } else { - if (check_suffix == NO_SUFFIX) { - errx(EX_SOFTWARE, "Please remove existing DFU suffix before adding a new one.\n"); - } - } - } - res = probe_prefix(file); - if ((res || file->size.prefix == 0) && check_prefix == NEEDS_PREFIX) - errx(EX_IOERR, "Valid DFU prefix needed"); - if (file->size.prefix && check_prefix == NO_PREFIX) - errx(EX_IOERR, "A prefix already exists, please delete it first"); - if (file->size.prefix && verbose) { - uint8_t *data = file->firmware; - if (file->prefix_type == LMDFU_PREFIX) - printf("Possible TI Stellaris DFU prefix with " - "the following properties\n" - "Address: 0x%08x\n" - "Payload length: %d\n", - file->lmdfu_address, - data[4] | (data[5] << 8) | - (data[6] << 16) | (data[7] << 14)); - else if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) - printf("Possible unencrypted NXP LPC DFU prefix with " - "the following properties\n" - "Payload length: %d kiByte\n", - data[2] >>1 | (data[3] << 7) ); - else - errx(EX_IOERR, "Unknown DFU prefix type"); - } -} - -void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix) -{ - uint32_t crc = 0xffffffff; - int f; - - f = open(file->name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0666); - if (f < 0) - err(EX_IOERR, "Could not open file %s for writing", file->name); - - /* write prefix, if any */ - if (write_prefix) { - if (file->prefix_type == LMDFU_PREFIX) { - uint8_t lmdfu_prefix[LMDFU_PREFIX_LENGTH]; - uint32_t addr = file->lmdfu_address / 1024; - - /* lmdfu_dfu_prefix payload length excludes prefix and suffix */ - uint32_t len = file->size.total - - file->size.prefix - file->size.suffix; - - lmdfu_prefix[0] = 0x01; /* STELLARIS_DFU_PROG */ - lmdfu_prefix[1] = 0x00; /* Reserved */ - lmdfu_prefix[2] = (uint8_t)(addr & 0xff); - lmdfu_prefix[3] = (uint8_t)(addr >> 8); - lmdfu_prefix[4] = (uint8_t)(len & 0xff); - lmdfu_prefix[5] = (uint8_t)(len >> 8) & 0xff; - lmdfu_prefix[6] = (uint8_t)(len >> 16) & 0xff; - lmdfu_prefix[7] = (uint8_t)(len >> 24); - - crc = dfu_file_write_crc(f, crc, lmdfu_prefix, LMDFU_PREFIX_LENGTH); - } - if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { - uint8_t lpcdfu_prefix[LPCDFU_PREFIX_LENGTH] = {0}; - int i; - - /* Payload is firmware and prefix rounded to 512 bytes */ - uint32_t len = (file->size.total - file->size.suffix + 511) /512; - - lpcdfu_prefix[0] = 0x1a; /* Unencypted*/ - lpcdfu_prefix[1] = 0x3f; /* Reserved */ - lpcdfu_prefix[2] = (uint8_t)(len & 0xff); - lpcdfu_prefix[3] = (uint8_t)((len >> 8) & 0xff); - for (i = 12; i < LPCDFU_PREFIX_LENGTH; i++) - lpcdfu_prefix[i] = 0xff; - - crc = dfu_file_write_crc(f, crc, lpcdfu_prefix, LPCDFU_PREFIX_LENGTH); - } - } - /* write firmware binary */ - crc = dfu_file_write_crc(f, crc, file->firmware + file->size.prefix, - file->size.total - file->size.prefix - file->size.suffix); - - /* write suffix, if any */ - if (write_suffix) { - uint8_t dfusuffix[DFU_SUFFIX_LENGTH]; - - dfusuffix[0] = file->bcdDevice & 0xff; - dfusuffix[1] = file->bcdDevice >> 8; - dfusuffix[2] = file->idProduct & 0xff; - dfusuffix[3] = file->idProduct >> 8; - dfusuffix[4] = file->idVendor & 0xff; - dfusuffix[5] = file->idVendor >> 8; - dfusuffix[6] = file->bcdDFU & 0xff; - dfusuffix[7] = file->bcdDFU >> 8; - dfusuffix[8] = 'U'; - dfusuffix[9] = 'F'; - dfusuffix[10] = 'D'; - dfusuffix[11] = DFU_SUFFIX_LENGTH; - - crc = dfu_file_write_crc(f, crc, dfusuffix, - DFU_SUFFIX_LENGTH - 4); - - dfusuffix[12] = crc; - dfusuffix[13] = crc >> 8; - dfusuffix[14] = crc >> 16; - dfusuffix[15] = crc >> 24; - - crc = dfu_file_write_crc(f, crc, dfusuffix + 12, 4); - } - close(f); -} - -void show_suffix_and_prefix(struct dfu_file *file) -{ - if (file->size.prefix == LMDFU_PREFIX_LENGTH) { - printf("The file %s contains a TI Stellaris DFU prefix with the following properties:\n", file->name); - printf("Address:\t0x%08x\n", file->lmdfu_address); - } else if (file->size.prefix == LPCDFU_PREFIX_LENGTH) { - uint8_t * prefix = file->firmware; - printf("The file %s contains a NXP unencrypted LPC DFU prefix with the following properties:\n", file->name); - printf("Size:\t%5d kiB\n", prefix[2]>>1|prefix[3]<<7); - } else if (file->size.prefix != 0) { - printf("The file %s contains an unknown prefix\n", file->name); - } - if (file->size.suffix > 0) { - printf("The file %s contains a DFU suffix with the following properties:\n", file->name); - printf("BCD device:\t0x%04X\n", file->bcdDevice); - printf("Product ID:\t0x%04X\n",file->idProduct); - printf("Vendor ID:\t0x%04X\n", file->idVendor); - printf("BCD DFU:\t0x%04X\n", file->bcdDFU); - printf("Length:\t\t%i\n", file->size.suffix); - printf("CRC:\t\t0x%08X\n", file->dwCRC); - } -} +/* + * Load or store DFU files including suffix and prefix + * + * Copyright 2014 Tormod Volden + * Copyright 2012 Stefan Schmidt + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +#define DFU_SUFFIX_LENGTH 16 +#define LMDFU_PREFIX_LENGTH 8 +#define LPCDFU_PREFIX_LENGTH 16 +#define PROGRESS_BAR_WIDTH 25 +#define STDIN_CHUNK_SIZE 65536 + +static const unsigned long crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +static uint32_t crc32_byte(uint32_t accum, uint8_t delta) +{ + return crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); +} + +static int probe_prefix(struct dfu_file *file) +{ + uint8_t *prefix = file->firmware; + + if (file->size.total < LMDFU_PREFIX_LENGTH) + return 1; + if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { + file->prefix_type = LMDFU_PREFIX; + file->size.prefix = LMDFU_PREFIX_LENGTH; + file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); + } + else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f)== 0x3f)) { + file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; + file->size.prefix = LPCDFU_PREFIX_LENGTH; + } + + if (file->size.prefix + file->size.suffix > file->size.total) + return 1; + return 0; +} + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max) +{ + static char buf[PROGRESS_BAR_WIDTH + 1]; + static unsigned long long last_progress = -1; + static time_t last_time; + time_t curr_time = time(NULL); + unsigned long long progress; + unsigned long long x; + + /* check for not known maximum */ + if (max < curr) + max = curr + 1; + /* make none out of none give zero */ + if (max == 0 && curr == 0) + max = 1; + + /* compute completion */ + progress = (PROGRESS_BAR_WIDTH * curr) / max; + if (progress > PROGRESS_BAR_WIDTH) + progress = PROGRESS_BAR_WIDTH; + if (progress == last_progress && + curr_time == last_time) + return; + last_progress = progress; + last_time = curr_time; + + for (x = 0; x != PROGRESS_BAR_WIDTH; x++) { + if (x < progress) + buf[x] = '='; + else + buf[x] = ' '; + } + buf[x] = 0; + + printf("\r%s\t[%s] %3lld%% %12lld bytes", desc, buf, + (100ULL * curr) / max, curr); + + if (progress == PROGRESS_BAR_WIDTH) + printf("\n%s done.\n", desc); +} + +void *dfu_malloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL) + errx(EX_SOFTWARE, "Cannot allocate memory of size %d bytes", (int)size); + return (ptr); +} + +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size) +{ + int x; + + /* compute CRC */ + for (x = 0; x != size; x++) + crc = crc32_byte(crc, ((uint8_t *)buf)[x]); + + /* write data */ + if (write(f, buf, size) != size) + err(EX_IOERR, "Could not write %d bytes to file %d", size, f); + + return (crc); +} + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix) +{ + off_t offset; + int f; + int i; + int res; + + file->size.prefix = 0; + file->size.suffix = 0; + + /* default values, if no valid suffix is found */ + file->bcdDFU = 0; + file->idVendor = 0xffff; /* wildcard value */ + file->idProduct = 0xffff; /* wildcard value */ + file->bcdDevice = 0xffff; /* wildcard value */ + + /* default values, if no valid prefix is found */ + file->lmdfu_address = 0; + + free(file->firmware); + + if (!strcmp(file->name, "-")) { + int read_bytes; + +#ifdef WIN32 + _setmode( _fileno( stdin ), _O_BINARY ); +#endif + file->firmware = (uint8_t*) dfu_malloc(STDIN_CHUNK_SIZE); + read_bytes = fread(file->firmware, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total = read_bytes; + while (read_bytes == STDIN_CHUNK_SIZE) { + file->firmware = (uint8_t*) realloc(file->firmware, file->size.total + STDIN_CHUNK_SIZE); + if (!file->firmware) + err(EX_IOERR, "Could not allocate firmware buffer"); + read_bytes = fread(file->firmware + file->size.total, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total += read_bytes; + } + if (verbose) + printf("Read %i bytes from stdin\n", file->size.total); + /* Never require suffix when reading from stdin */ + check_suffix = MAYBE_SUFFIX; + } else { + f = open(file->name, O_RDONLY | O_BINARY); + if (f < 0) + err(EX_IOERR, "Could not open file %s for reading", file->name); + + offset = lseek(f, 0, SEEK_END); + + if ((int)offset < 0 || (int)offset != offset) + err(EX_IOERR, "File size is too big"); + + if (lseek(f, 0, SEEK_SET) != 0) + err(EX_IOERR, "Could not seek to beginning"); + + file->size.total = offset; + file->firmware = dfu_malloc(file->size.total); + + if (read(f, file->firmware, file->size.total) != file->size.total) { + err(EX_IOERR, "Could not read %d bytes from %s", + file->size.total, file->name); + } + close(f); + } + + /* Check for possible DFU file suffix by trying to parse one */ + { + uint32_t crc = 0xffffffff; + const uint8_t *dfusuffix; + int missing_suffix = 0; + const char *reason; + + if (file->size.total < DFU_SUFFIX_LENGTH) { + reason = "File too short for DFU suffix"; + missing_suffix = 1; + goto checked; + } + + dfusuffix = file->firmware + file->size.total - + DFU_SUFFIX_LENGTH; + + for (i = 0; i < file->size.total - 4; i++) + crc = crc32_byte(crc, file->firmware[i]); + + if (dfusuffix[10] != 'D' || + dfusuffix[9] != 'F' || + dfusuffix[8] != 'U') { + reason = "Invalid DFU suffix signature"; + missing_suffix = 1; + goto checked; + } + + file->dwCRC = (dfusuffix[15] << 24) + + (dfusuffix[14] << 16) + + (dfusuffix[13] << 8) + + dfusuffix[12]; + + if (file->dwCRC != crc) { + reason = "DFU suffix CRC does not match"; + missing_suffix = 1; + goto checked; + } + + /* At this point we believe we have a DFU suffix + so we require further checks to succeed */ + + file->bcdDFU = (dfusuffix[7] << 8) + dfusuffix[6]; + + if (verbose) + printf("DFU suffix version %x\n", file->bcdDFU); + + file->size.suffix = dfusuffix[11]; + + if (file->size.suffix < DFU_SUFFIX_LENGTH) { + errx(EX_IOERR, "Unsupported DFU suffix length %d", + file->size.suffix); + } + + if (file->size.suffix > file->size.total) { + errx(EX_IOERR, "Invalid DFU suffix length %d", + file->size.suffix); + } + + file->idVendor = (dfusuffix[5] << 8) + dfusuffix[4]; + file->idProduct = (dfusuffix[3] << 8) + dfusuffix[2]; + file->bcdDevice = (dfusuffix[1] << 8) + dfusuffix[0]; + +checked: + if (missing_suffix) { + if (check_suffix == NEEDS_SUFFIX) { + warnx("%s", reason); + errx(EX_IOERR, "Valid DFU suffix needed"); + } else if (check_suffix == MAYBE_SUFFIX) { + warnx("%s", reason); + warnx("A valid DFU suffix will be required in " + "a future dfu-util release!!!"); + } + } else { + if (check_suffix == NO_SUFFIX) { + errx(EX_SOFTWARE, "Please remove existing DFU suffix before adding a new one.\n"); + } + } + } + res = probe_prefix(file); + if ((res || file->size.prefix == 0) && check_prefix == NEEDS_PREFIX) + errx(EX_IOERR, "Valid DFU prefix needed"); + if (file->size.prefix && check_prefix == NO_PREFIX) + errx(EX_IOERR, "A prefix already exists, please delete it first"); + if (file->size.prefix && verbose) { + uint8_t *data = file->firmware; + if (file->prefix_type == LMDFU_PREFIX) + printf("Possible TI Stellaris DFU prefix with " + "the following properties\n" + "Address: 0x%08x\n" + "Payload length: %d\n", + file->lmdfu_address, + data[4] | (data[5] << 8) | + (data[6] << 16) | (data[7] << 14)); + else if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) + printf("Possible unencrypted NXP LPC DFU prefix with " + "the following properties\n" + "Payload length: %d kiByte\n", + data[2] >>1 | (data[3] << 7) ); + else + errx(EX_IOERR, "Unknown DFU prefix type"); + } +} + +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix) +{ + uint32_t crc = 0xffffffff; + int f; + + f = open(file->name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0666); + if (f < 0) + err(EX_IOERR, "Could not open file %s for writing", file->name); + + /* write prefix, if any */ + if (write_prefix) { + if (file->prefix_type == LMDFU_PREFIX) { + uint8_t lmdfu_prefix[LMDFU_PREFIX_LENGTH]; + uint32_t addr = file->lmdfu_address / 1024; + + /* lmdfu_dfu_prefix payload length excludes prefix and suffix */ + uint32_t len = file->size.total - + file->size.prefix - file->size.suffix; + + lmdfu_prefix[0] = 0x01; /* STELLARIS_DFU_PROG */ + lmdfu_prefix[1] = 0x00; /* Reserved */ + lmdfu_prefix[2] = (uint8_t)(addr & 0xff); + lmdfu_prefix[3] = (uint8_t)(addr >> 8); + lmdfu_prefix[4] = (uint8_t)(len & 0xff); + lmdfu_prefix[5] = (uint8_t)(len >> 8) & 0xff; + lmdfu_prefix[6] = (uint8_t)(len >> 16) & 0xff; + lmdfu_prefix[7] = (uint8_t)(len >> 24); + + crc = dfu_file_write_crc(f, crc, lmdfu_prefix, LMDFU_PREFIX_LENGTH); + } + if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { + uint8_t lpcdfu_prefix[LPCDFU_PREFIX_LENGTH] = {0}; + int i; + + /* Payload is firmware and prefix rounded to 512 bytes */ + uint32_t len = (file->size.total - file->size.suffix + 511) /512; + + lpcdfu_prefix[0] = 0x1a; /* Unencypted*/ + lpcdfu_prefix[1] = 0x3f; /* Reserved */ + lpcdfu_prefix[2] = (uint8_t)(len & 0xff); + lpcdfu_prefix[3] = (uint8_t)((len >> 8) & 0xff); + for (i = 12; i < LPCDFU_PREFIX_LENGTH; i++) + lpcdfu_prefix[i] = 0xff; + + crc = dfu_file_write_crc(f, crc, lpcdfu_prefix, LPCDFU_PREFIX_LENGTH); + } + } + /* write firmware binary */ + crc = dfu_file_write_crc(f, crc, file->firmware + file->size.prefix, + file->size.total - file->size.prefix - file->size.suffix); + + /* write suffix, if any */ + if (write_suffix) { + uint8_t dfusuffix[DFU_SUFFIX_LENGTH]; + + dfusuffix[0] = file->bcdDevice & 0xff; + dfusuffix[1] = file->bcdDevice >> 8; + dfusuffix[2] = file->idProduct & 0xff; + dfusuffix[3] = file->idProduct >> 8; + dfusuffix[4] = file->idVendor & 0xff; + dfusuffix[5] = file->idVendor >> 8; + dfusuffix[6] = file->bcdDFU & 0xff; + dfusuffix[7] = file->bcdDFU >> 8; + dfusuffix[8] = 'U'; + dfusuffix[9] = 'F'; + dfusuffix[10] = 'D'; + dfusuffix[11] = DFU_SUFFIX_LENGTH; + + crc = dfu_file_write_crc(f, crc, dfusuffix, + DFU_SUFFIX_LENGTH - 4); + + dfusuffix[12] = crc; + dfusuffix[13] = crc >> 8; + dfusuffix[14] = crc >> 16; + dfusuffix[15] = crc >> 24; + + crc = dfu_file_write_crc(f, crc, dfusuffix + 12, 4); + } + close(f); +} + +void show_suffix_and_prefix(struct dfu_file *file) +{ + if (file->size.prefix == LMDFU_PREFIX_LENGTH) { + printf("The file %s contains a TI Stellaris DFU prefix with the following properties:\n", file->name); + printf("Address:\t0x%08x\n", file->lmdfu_address); + } else if (file->size.prefix == LPCDFU_PREFIX_LENGTH) { + uint8_t * prefix = file->firmware; + printf("The file %s contains a NXP unencrypted LPC DFU prefix with the following properties:\n", file->name); + printf("Size:\t%5d kiB\n", prefix[2]>>1|prefix[3]<<7); + } else if (file->size.prefix != 0) { + printf("The file %s contains an unknown prefix\n", file->name); + } + if (file->size.suffix > 0) { + printf("The file %s contains a DFU suffix with the following properties:\n", file->name); + printf("BCD device:\t0x%04X\n", file->bcdDevice); + printf("Product ID:\t0x%04X\n",file->idProduct); + printf("Vendor ID:\t0x%04X\n", file->idVendor); + printf("BCD DFU:\t0x%04X\n", file->bcdDFU); + printf("Length:\t\t%i\n", file->size.suffix); + printf("CRC:\t\t0x%08X\n", file->dwCRC); + } +} diff --git a/tools/src/dfu-util/src/dfu_file.h b/tools/linux/src/dfu-util/src/dfu_file.h old mode 100755 new mode 100644 similarity index 95% rename from tools/src/dfu-util/src/dfu_file.h rename to tools/linux/src/dfu-util/src/dfu_file.h index 31c90b8..abebd44 --- a/tools/src/dfu-util/src/dfu_file.h +++ b/tools/linux/src/dfu-util/src/dfu_file.h @@ -1,60 +1,60 @@ - -#ifndef DFU_FILE_H -#define DFU_FILE_H - -#include - -struct dfu_file { - /* File name */ - const char *name; - /* Pointer to file loaded into memory */ - uint8_t *firmware; - /* Different sizes */ - struct { - int total; - int prefix; - int suffix; - } size; - /* From prefix fields */ - uint32_t lmdfu_address; - /* From prefix fields */ - uint32_t prefix_type; - - /* From DFU suffix fields */ - uint32_t dwCRC; - uint16_t bcdDFU; - uint16_t idVendor; - uint16_t idProduct; - uint16_t bcdDevice; -}; - -enum suffix_req { - NO_SUFFIX, - NEEDS_SUFFIX, - MAYBE_SUFFIX -}; - -enum prefix_req { - NO_PREFIX, - NEEDS_PREFIX, - MAYBE_PREFIX -}; - -enum prefix_type { - ZERO_PREFIX, - LMDFU_PREFIX, - LPCDFU_UNENCRYPTED_PREFIX -}; - -extern int verbose; - -void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix); -void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix); - -void dfu_progress_bar(const char *desc, unsigned long long curr, - unsigned long long max); -void *dfu_malloc(size_t size); -uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size); -void show_suffix_and_prefix(struct dfu_file *file); - -#endif /* DFU_FILE_H */ + +#ifndef DFU_FILE_H +#define DFU_FILE_H + +#include + +struct dfu_file { + /* File name */ + const char *name; + /* Pointer to file loaded into memory */ + uint8_t *firmware; + /* Different sizes */ + struct { + int total; + int prefix; + int suffix; + } size; + /* From prefix fields */ + uint32_t lmdfu_address; + /* From prefix fields */ + uint32_t prefix_type; + + /* From DFU suffix fields */ + uint32_t dwCRC; + uint16_t bcdDFU; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; +}; + +enum suffix_req { + NO_SUFFIX, + NEEDS_SUFFIX, + MAYBE_SUFFIX +}; + +enum prefix_req { + NO_PREFIX, + NEEDS_PREFIX, + MAYBE_PREFIX +}; + +enum prefix_type { + ZERO_PREFIX, + LMDFU_PREFIX, + LPCDFU_UNENCRYPTED_PREFIX +}; + +extern int verbose; + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix); +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix); + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max); +void *dfu_malloc(size_t size); +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size); +void show_suffix_and_prefix(struct dfu_file *file); + +#endif /* DFU_FILE_H */ diff --git a/tools/src/dfu-util/src/dfu_load.c b/tools/linux/src/dfu-util/src/dfu_load.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfu_load.c rename to tools/linux/src/dfu-util/src/dfu_load.c index d4ff51c..64f7009 --- a/tools/src/dfu-util/src/dfu_load.c +++ b/tools/linux/src/dfu-util/src/dfu_load.c @@ -1,196 +1,196 @@ -/* - * DFU transfer routines - * - * This is supposed to be a general DFU implementation, as specified in the - * USB DFU 1.0 and 1.1 specification. - * - * The code was originally intended to interface with a USB device running the - * "sam7dfu" firmware (see http://www.openpcd.org/) on an AT91SAM7 processor. - * - * Copyright 2007-2008 Harald Welte - * Copyright 2013 Hans Petter Selasky - * Copyright 2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include - -#include - -#include "portable.h" -#include "dfu.h" -#include "usb_dfu.h" -#include "dfu_file.h" -#include "dfu_load.h" -#include "quirks.h" - -int dfuload_do_upload(struct dfu_if *dif, int xfer_size, - int expected_size, int fd) -{ - int total_bytes = 0; - unsigned short transaction = 0; - unsigned char *buf; - int ret; - - buf = dfu_malloc(xfer_size); - - printf("Copying data from DFU device to PC\n"); - dfu_progress_bar("Upload", 0, 1); - - while (1) { - int rc; - rc = dfu_upload(dif->dev_handle, dif->interface, - xfer_size, transaction++, buf); - if (rc < 0) { - warnx("Error during upload"); - ret = rc; - goto out_free; - } - - dfu_file_write_crc(fd, 0, buf, rc); - total_bytes += rc; - - if (total_bytes < 0) - errx(EX_SOFTWARE, "Received too many bytes (wraparound)"); - - if (rc < xfer_size) { - /* last block, return */ - ret = total_bytes; - break; - } - dfu_progress_bar("Upload", total_bytes, expected_size); - } - ret = 0; - -out_free: - dfu_progress_bar("Upload", total_bytes, total_bytes); - if (total_bytes == 0) - printf("\nFailed.\n"); - free(buf); - if (verbose) - printf("Received a total of %i bytes\n", total_bytes); - if (expected_size != 0 && total_bytes != expected_size) - errx(EX_SOFTWARE, "Unexpected number of bytes uploaded from device"); - return ret; -} - -int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) -{ - int bytes_sent; - int expected_size; - unsigned char *buf; - unsigned short transaction = 0; - struct dfu_status dst; - int ret; - - printf("Copying data from PC to DFU device\n"); - - buf = file->firmware; - expected_size = file->size.total - file->size.suffix; - bytes_sent = 0; - - dfu_progress_bar("Download", 0, 1); - while (bytes_sent < expected_size) { - int bytes_left; - int chunk_size; - - bytes_left = expected_size - bytes_sent; - if (bytes_left < xfer_size) - chunk_size = bytes_left; - else - chunk_size = xfer_size; - - ret = dfu_download(dif->dev_handle, dif->interface, - chunk_size, transaction++, chunk_size ? buf : NULL); - if (ret < 0) { - warnx("Error during download"); - goto out; - } - bytes_sent += chunk_size; - buf += chunk_size; - - do { - ret = dfu_get_status(dif, &dst); - if (ret < 0) { - errx(EX_IOERR, "Error during download get_status"); - goto out; - } - - if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE || - dst.bState == DFU_STATE_dfuERROR) - break; - - /* Wait while device executes flashing */ - milli_sleep(dst.bwPollTimeout); - - } while (1); - if (dst.bStatus != DFU_STATUS_OK) { - printf(" failed!\n"); - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, - dfu_state_to_string(dst.bState), dst.bStatus, - dfu_status_to_string(dst.bStatus)); - ret = -1; - goto out; - } - dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left); - } - - /* send one zero sized download request to signalize end */ - ret = dfu_download(dif->dev_handle, dif->interface, - 0, transaction, NULL); - if (ret < 0) { - errx(EX_IOERR, "Error sending completion packet"); - goto out; - } - - dfu_progress_bar("Download", bytes_sent, bytes_sent); - - if (verbose) - printf("Sent a total of %i bytes\n", bytes_sent); - -get_status: - /* Transition to MANIFEST_SYNC state */ - ret = dfu_get_status(dif, &dst); - if (ret < 0) { - warnx("unable to read DFU status after completion"); - goto out; - } - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, - dfu_state_to_string(dst.bState), dst.bStatus, - dfu_status_to_string(dst.bStatus)); - - milli_sleep(dst.bwPollTimeout); - - /* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */ - switch (dst.bState) { - case DFU_STATE_dfuMANIFEST_SYNC: - case DFU_STATE_dfuMANIFEST: - /* some devices (e.g. TAS1020b) need some time before we - * can obtain the status */ - milli_sleep(1000); - goto get_status; - break; - case DFU_STATE_dfuIDLE: - break; - } - printf("Done!\n"); - -out: - return bytes_sent; -} +/* + * DFU transfer routines + * + * This is supposed to be a general DFU implementation, as specified in the + * USB DFU 1.0 and 1.1 specification. + * + * The code was originally intended to interface with a USB device running the + * "sam7dfu" firmware (see http://www.openpcd.org/) on an AT91SAM7 processor. + * + * Copyright 2007-2008 Harald Welte + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "quirks.h" + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, + int expected_size, int fd) +{ + int total_bytes = 0; + unsigned short transaction = 0; + unsigned char *buf; + int ret; + + buf = dfu_malloc(xfer_size); + + printf("Copying data from DFU device to PC\n"); + dfu_progress_bar("Upload", 0, 1); + + while (1) { + int rc; + rc = dfu_upload(dif->dev_handle, dif->interface, + xfer_size, transaction++, buf); + if (rc < 0) { + warnx("Error during upload"); + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes (wraparound)"); + + if (rc < xfer_size) { + /* last block, return */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, expected_size); + } + ret = 0; + +out_free: + dfu_progress_bar("Upload", total_bytes, total_bytes); + if (total_bytes == 0) + printf("\nFailed.\n"); + free(buf); + if (verbose) + printf("Received a total of %i bytes\n", total_bytes); + if (expected_size != 0 && total_bytes != expected_size) + errx(EX_SOFTWARE, "Unexpected number of bytes uploaded from device"); + return ret; +} + +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) +{ + int bytes_sent; + int expected_size; + unsigned char *buf; + unsigned short transaction = 0; + struct dfu_status dst; + int ret; + + printf("Copying data from PC to DFU device\n"); + + buf = file->firmware; + expected_size = file->size.total - file->size.suffix; + bytes_sent = 0; + + dfu_progress_bar("Download", 0, 1); + while (bytes_sent < expected_size) { + int bytes_left; + int chunk_size; + + bytes_left = expected_size - bytes_sent; + if (bytes_left < xfer_size) + chunk_size = bytes_left; + else + chunk_size = xfer_size; + + ret = dfu_download(dif->dev_handle, dif->interface, + chunk_size, transaction++, chunk_size ? buf : NULL); + if (ret < 0) { + warnx("Error during download"); + goto out; + } + bytes_sent += chunk_size; + buf += chunk_size; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + goto out; + } + + if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE || + dst.bState == DFU_STATE_dfuERROR) + break; + + /* Wait while device executes flashing */ + milli_sleep(dst.bwPollTimeout); + + } while (1); + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + ret = -1; + goto out; + } + dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left); + } + + /* send one zero sized download request to signalize end */ + ret = dfu_download(dif->dev_handle, dif->interface, + 0, transaction, NULL); + if (ret < 0) { + errx(EX_IOERR, "Error sending completion packet"); + goto out; + } + + dfu_progress_bar("Download", bytes_sent, bytes_sent); + + if (verbose) + printf("Sent a total of %i bytes\n", bytes_sent); + +get_status: + /* Transition to MANIFEST_SYNC state */ + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + warnx("unable to read DFU status after completion"); + goto out; + } + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + + milli_sleep(dst.bwPollTimeout); + + /* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */ + switch (dst.bState) { + case DFU_STATE_dfuMANIFEST_SYNC: + case DFU_STATE_dfuMANIFEST: + /* some devices (e.g. TAS1020b) need some time before we + * can obtain the status */ + milli_sleep(1000); + goto get_status; + break; + case DFU_STATE_dfuIDLE: + break; + } + printf("Done!\n"); + +out: + return bytes_sent; +} diff --git a/tools/src/dfu-util/src/dfu_load.h b/tools/linux/src/dfu-util/src/dfu_load.h old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/dfu_load.h rename to tools/linux/src/dfu-util/src/dfu_load.h index fe363aa..be23e9b --- a/tools/src/dfu-util/src/dfu_load.h +++ b/tools/linux/src/dfu-util/src/dfu_load.h @@ -1,7 +1,7 @@ -#ifndef DFU_LOAD_H -#define DFU_LOAD_H - -int dfuload_do_upload(struct dfu_if *dif, int xfer_size, int expected_size, int fd); -int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file); - -#endif /* DFU_LOAD_H */ +#ifndef DFU_LOAD_H +#define DFU_LOAD_H + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, int expected_size, int fd); +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file); + +#endif /* DFU_LOAD_H */ diff --git a/tools/src/dfu-util/src/dfu_util.c b/tools/linux/src/dfu-util/src/dfu_util.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfu_util.c rename to tools/linux/src/dfu-util/src/dfu_util.c index d9ad2d0..b94c7cc --- a/tools/src/dfu-util/src/dfu_util.c +++ b/tools/linux/src/dfu-util/src/dfu_util.c @@ -1,346 +1,346 @@ -/* - * Functions for detecting DFU USB entities - * - * Written by Harald Welte - * Copyright 2007-2008 by OpenMoko, Inc. - * Copyright 2013 Hans Petter Selasky - * - * Based on existing code of dfu-programmer-0.4 - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include "portable.h" -#include "dfu.h" -#include "usb_dfu.h" -#include "dfu_file.h" -#include "dfu_load.h" -#include "dfu_util.h" -#include "dfuse.h" -#include "quirks.h" - -#ifdef HAVE_USBPATH_H -#include -#endif - -/* - * Look for a descriptor in a concatenated descriptor list. Will - * return upon the first match of the given descriptor type. Returns length of - * found descriptor, limited to res_size - */ -static int find_descriptor(const uint8_t *desc_list, int list_len, - uint8_t desc_type, void *res_buf, int res_size) -{ - int p = 0; - - if (list_len < 2) - return (-1); - - while (p + 1 < list_len) { - int desclen; - - desclen = (int) desc_list[p]; - if (desclen == 0) { - warnx("Invalid descriptor list"); - return -1; - } - if (desc_list[p + 1] == desc_type) { - if (desclen > res_size) - desclen = res_size; - if (p + desclen > list_len) - desclen = list_len - p; - memcpy(res_buf, &desc_list[p], desclen); - return desclen; - } - p += (int) desc_list[p]; - } - return -1; -} - -static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) -{ - struct usb_dfu_func_descriptor func_dfu; - libusb_device_handle *devh; - struct dfu_if *pdfu; - struct libusb_config_descriptor *cfg; - const struct libusb_interface_descriptor *intf; - const struct libusb_interface *uif; - char alt_name[MAX_DESC_STR_LEN + 1]; - char serial_name[MAX_DESC_STR_LEN + 1]; - int cfg_idx; - int intf_idx; - int alt_idx; - int ret; - int has_dfu; - - for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { - memset(&func_dfu, 0, sizeof(func_dfu)); - has_dfu = 0; - - ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); - if (ret != 0) - return; - if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { - libusb_free_config_descriptor(cfg); - continue; - } - - /* - * In some cases, noticably FreeBSD if uid != 0, - * the configuration descriptors are empty - */ - if (!cfg) - return; - - ret = find_descriptor(cfg->extra, cfg->extra_length, - USB_DT_DFU, &func_dfu, sizeof(func_dfu)); - if (ret > -1) - goto found_dfu; - - for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; - intf_idx++) { - uif = &cfg->interface[intf_idx]; - if (!uif) - break; - - for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; - alt_idx++) { - intf = &uif->altsetting[alt_idx]; - - ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, - &func_dfu, sizeof(func_dfu)); - if (ret > -1) - goto found_dfu; - - if (intf->bInterfaceClass != 0xfe || - intf->bInterfaceSubClass != 1) - continue; - - has_dfu = 1; - } - } - if (has_dfu) { - /* - * Finally try to retrieve it requesting the - * device directly This is not supported on - * all devices for non-standard types - */ - if (libusb_open(dev, &devh) == 0) { - ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, - (void *)&func_dfu, sizeof(func_dfu)); - libusb_close(devh); - if (ret > -1) - goto found_dfu; - } - warnx("Device has DFU interface, " - "but has no DFU functional descriptor"); - - /* fake version 1.0 */ - func_dfu.bLength = 7; - func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); - goto found_dfu; - } - libusb_free_config_descriptor(cfg); - continue; - -found_dfu: - if (func_dfu.bLength == 7) { - printf("Deducing device DFU version from functional descriptor " - "length\n"); - func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); - } else if (func_dfu.bLength < 9) { - printf("Error obtaining DFU functional descriptor\n"); - printf("Please report this as a bug!\n"); - printf("Warning: Assuming DFU version 1.0\n"); - func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); - printf("Warning: Transfer size can not be detected\n"); - func_dfu.wTransferSize = 0; - } - - for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; - intf_idx++) { - if (match_iface_index > -1 && match_iface_index != intf_idx) - continue; - - uif = &cfg->interface[intf_idx]; - if (!uif) - break; - - for (alt_idx = 0; - alt_idx < uif->num_altsetting; alt_idx++) { - int dfu_mode; - - intf = &uif->altsetting[alt_idx]; - - if (intf->bInterfaceClass != 0xfe || - intf->bInterfaceSubClass != 1) - continue; - - dfu_mode = (intf->bInterfaceProtocol == 2); - /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ - if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) - dfu_mode = 1; - - if (dfu_mode && - match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) - continue; - - if (dfu_mode) { - if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || - (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { - continue; - } - } else { - if ((match_vendor >= 0 && match_vendor != desc->idVendor) || - (match_product >= 0 && match_product != desc->idProduct)) { - continue; - } - } - - if (libusb_open(dev, &devh)) { - warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); - break; - } - if (intf->iInterface != 0) - ret = libusb_get_string_descriptor_ascii(devh, - intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); - else - ret = -1; - if (ret < 1) - strcpy(alt_name, "UNKNOWN"); - if (desc->iSerialNumber != 0) - ret = libusb_get_string_descriptor_ascii(devh, - desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); - else - ret = -1; - if (ret < 1) - strcpy(serial_name, "UNKNOWN"); - libusb_close(devh); - - if (dfu_mode && - match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) - continue; - - if (dfu_mode) { - if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) - continue; - } else { - if (match_serial != NULL && strcmp(match_serial, serial_name)) - continue; - } - - pdfu = dfu_malloc(sizeof(*pdfu)); - - memset(pdfu, 0, sizeof(*pdfu)); - - pdfu->func_dfu = func_dfu; - pdfu->dev = libusb_ref_device(dev); - pdfu->quirks = get_quirks(desc->idVendor, - desc->idProduct, desc->bcdDevice); - pdfu->vendor = desc->idVendor; - pdfu->product = desc->idProduct; - pdfu->bcdDevice = desc->bcdDevice; - pdfu->configuration = cfg->bConfigurationValue; - pdfu->interface = intf->bInterfaceNumber; - pdfu->altsetting = intf->bAlternateSetting; - pdfu->devnum = libusb_get_device_address(dev); - pdfu->busnum = libusb_get_bus_number(dev); - pdfu->alt_name = strdup(alt_name); - if (pdfu->alt_name == NULL) - errx(EX_SOFTWARE, "Out of memory"); - pdfu->serial_name = strdup(serial_name); - if (pdfu->serial_name == NULL) - errx(EX_SOFTWARE, "Out of memory"); - if (dfu_mode) - pdfu->flags |= DFU_IFF_DFU; - if (pdfu->quirks & QUIRK_FORCE_DFU11) { - pdfu->func_dfu.bcdDFUVersion = - libusb_cpu_to_le16(0x0110); - } - pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; - - /* queue into list */ - pdfu->next = dfu_root; - dfu_root = pdfu; - } - } - libusb_free_config_descriptor(cfg); - } -} - -void probe_devices(libusb_context *ctx) -{ - libusb_device **list; - ssize_t num_devs; - ssize_t i; - - num_devs = libusb_get_device_list(ctx, &list); - for (i = 0; i < num_devs; ++i) { - struct libusb_device_descriptor desc; - struct libusb_device *dev = list[i]; - - if (match_bus > -1 && match_bus != libusb_get_bus_number(dev)) - continue; - if (match_device > -1 && match_device != libusb_get_device_address(dev)) - continue; - if (libusb_get_device_descriptor(dev, &desc)) - continue; - probe_configuration(dev, &desc); - } - libusb_free_device_list(list, 0); -} - -void disconnect_devices(void) -{ - struct dfu_if *pdfu; - struct dfu_if *prev = NULL; - - for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) { - free(prev); - libusb_unref_device(pdfu->dev); - free(pdfu->alt_name); - free(pdfu->serial_name); - prev = pdfu; - } - free(prev); - dfu_root = NULL; -} - -void print_dfu_if(struct dfu_if *dfu_if) -{ - printf("Found %s: [%04x:%04x] ver=%04x, devnum=%u, cfg=%u, intf=%u, " - "alt=%u, name=\"%s\", serial=\"%s\"\n", - dfu_if->flags & DFU_IFF_DFU ? "DFU" : "Runtime", - dfu_if->vendor, dfu_if->product, - dfu_if->bcdDevice, dfu_if->devnum, - dfu_if->configuration, dfu_if->interface, - dfu_if->altsetting, dfu_if->alt_name, - dfu_if->serial_name); -} - -/* Walk the device tree and print out DFU devices */ -void list_dfu_interfaces(void) -{ - struct dfu_if *pdfu; - - for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) - print_dfu_if(pdfu); -} +/* + * Functions for detecting DFU USB entities + * + * Written by Harald Welte + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013 Hans Petter Selasky + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +/* + * Look for a descriptor in a concatenated descriptor list. Will + * return upon the first match of the given descriptor type. Returns length of + * found descriptor, limited to res_size + */ +static int find_descriptor(const uint8_t *desc_list, int list_len, + uint8_t desc_type, void *res_buf, int res_size) +{ + int p = 0; + + if (list_len < 2) + return (-1); + + while (p + 1 < list_len) { + int desclen; + + desclen = (int) desc_list[p]; + if (desclen == 0) { + warnx("Invalid descriptor list"); + return -1; + } + if (desc_list[p + 1] == desc_type) { + if (desclen > res_size) + desclen = res_size; + if (p + desclen > list_len) + desclen = list_len - p; + memcpy(res_buf, &desc_list[p], desclen); + return desclen; + } + p += (int) desc_list[p]; + } + return -1; +} + +static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) +{ + struct usb_dfu_func_descriptor func_dfu; + libusb_device_handle *devh; + struct dfu_if *pdfu; + struct libusb_config_descriptor *cfg; + const struct libusb_interface_descriptor *intf; + const struct libusb_interface *uif; + char alt_name[MAX_DESC_STR_LEN + 1]; + char serial_name[MAX_DESC_STR_LEN + 1]; + int cfg_idx; + int intf_idx; + int alt_idx; + int ret; + int has_dfu; + + for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { + memset(&func_dfu, 0, sizeof(func_dfu)); + has_dfu = 0; + + ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); + if (ret != 0) + return; + if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { + libusb_free_config_descriptor(cfg); + continue; + } + + /* + * In some cases, noticably FreeBSD if uid != 0, + * the configuration descriptors are empty + */ + if (!cfg) + return; + + ret = find_descriptor(cfg->extra, cfg->extra_length, + USB_DT_DFU, &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; + alt_idx++) { + intf = &uif->altsetting[alt_idx]; + + ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, + &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + has_dfu = 1; + } + } + if (has_dfu) { + /* + * Finally try to retrieve it requesting the + * device directly This is not supported on + * all devices for non-standard types + */ + if (libusb_open(dev, &devh) == 0) { + ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, + (void *)&func_dfu, sizeof(func_dfu)); + libusb_close(devh); + if (ret > -1) + goto found_dfu; + } + warnx("Device has DFU interface, " + "but has no DFU functional descriptor"); + + /* fake version 1.0 */ + func_dfu.bLength = 7; + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + goto found_dfu; + } + libusb_free_config_descriptor(cfg); + continue; + +found_dfu: + if (func_dfu.bLength == 7) { + printf("Deducing device DFU version from functional descriptor " + "length\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + } else if (func_dfu.bLength < 9) { + printf("Error obtaining DFU functional descriptor\n"); + printf("Please report this as a bug!\n"); + printf("Warning: Assuming DFU version 1.0\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + printf("Warning: Transfer size can not be detected\n"); + func_dfu.wTransferSize = 0; + } + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + if (match_iface_index > -1 && match_iface_index != intf_idx) + continue; + + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; + alt_idx < uif->num_altsetting; alt_idx++) { + int dfu_mode; + + intf = &uif->altsetting[alt_idx]; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + dfu_mode = (intf->bInterfaceProtocol == 2); + /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ + if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) + dfu_mode = 1; + + if (dfu_mode && + match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) + continue; + + if (dfu_mode) { + if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || + (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { + continue; + } + } else { + if ((match_vendor >= 0 && match_vendor != desc->idVendor) || + (match_product >= 0 && match_product != desc->idProduct)) { + continue; + } + } + + if (libusb_open(dev, &devh)) { + warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); + break; + } + if (intf->iInterface != 0) + ret = libusb_get_string_descriptor_ascii(devh, + intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(alt_name, "UNKNOWN"); + if (desc->iSerialNumber != 0) + ret = libusb_get_string_descriptor_ascii(devh, + desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(serial_name, "UNKNOWN"); + libusb_close(devh); + + if (dfu_mode && + match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) + continue; + + if (dfu_mode) { + if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) + continue; + } else { + if (match_serial != NULL && strcmp(match_serial, serial_name)) + continue; + } + + pdfu = dfu_malloc(sizeof(*pdfu)); + + memset(pdfu, 0, sizeof(*pdfu)); + + pdfu->func_dfu = func_dfu; + pdfu->dev = libusb_ref_device(dev); + pdfu->quirks = get_quirks(desc->idVendor, + desc->idProduct, desc->bcdDevice); + pdfu->vendor = desc->idVendor; + pdfu->product = desc->idProduct; + pdfu->bcdDevice = desc->bcdDevice; + pdfu->configuration = cfg->bConfigurationValue; + pdfu->interface = intf->bInterfaceNumber; + pdfu->altsetting = intf->bAlternateSetting; + pdfu->devnum = libusb_get_device_address(dev); + pdfu->busnum = libusb_get_bus_number(dev); + pdfu->alt_name = strdup(alt_name); + if (pdfu->alt_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + pdfu->serial_name = strdup(serial_name); + if (pdfu->serial_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + if (dfu_mode) + pdfu->flags |= DFU_IFF_DFU; + if (pdfu->quirks & QUIRK_FORCE_DFU11) { + pdfu->func_dfu.bcdDFUVersion = + libusb_cpu_to_le16(0x0110); + } + pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; + + /* queue into list */ + pdfu->next = dfu_root; + dfu_root = pdfu; + } + } + libusb_free_config_descriptor(cfg); + } +} + +void probe_devices(libusb_context *ctx) +{ + libusb_device **list; + ssize_t num_devs; + ssize_t i; + + num_devs = libusb_get_device_list(ctx, &list); + for (i = 0; i < num_devs; ++i) { + struct libusb_device_descriptor desc; + struct libusb_device *dev = list[i]; + + if (match_bus > -1 && match_bus != libusb_get_bus_number(dev)) + continue; + if (match_device > -1 && match_device != libusb_get_device_address(dev)) + continue; + if (libusb_get_device_descriptor(dev, &desc)) + continue; + probe_configuration(dev, &desc); + } + libusb_free_device_list(list, 0); +} + +void disconnect_devices(void) +{ + struct dfu_if *pdfu; + struct dfu_if *prev = NULL; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) { + free(prev); + libusb_unref_device(pdfu->dev); + free(pdfu->alt_name); + free(pdfu->serial_name); + prev = pdfu; + } + free(prev); + dfu_root = NULL; +} + +void print_dfu_if(struct dfu_if *dfu_if) +{ + printf("Found %s: [%04x:%04x] ver=%04x, devnum=%u, cfg=%u, intf=%u, " + "alt=%u, name=\"%s\", serial=\"%s\"\n", + dfu_if->flags & DFU_IFF_DFU ? "DFU" : "Runtime", + dfu_if->vendor, dfu_if->product, + dfu_if->bcdDevice, dfu_if->devnum, + dfu_if->configuration, dfu_if->interface, + dfu_if->altsetting, dfu_if->alt_name, + dfu_if->serial_name); +} + +/* Walk the device tree and print out DFU devices */ +void list_dfu_interfaces(void) +{ + struct dfu_if *pdfu; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) + print_dfu_if(pdfu); +} diff --git a/tools/src/dfu-util/src/dfu_util.h b/tools/linux/src/dfu-util/src/dfu_util.h old mode 100755 new mode 100644 similarity index 95% rename from tools/src/dfu-util/src/dfu_util.h rename to tools/linux/src/dfu-util/src/dfu_util.h index 24e50c1..fc0c19d --- a/tools/src/dfu-util/src/dfu_util.h +++ b/tools/linux/src/dfu-util/src/dfu_util.h @@ -1,36 +1,36 @@ -#ifndef DFU_UTIL_H -#define DFU_UTIL_H - -/* USB string descriptor should contain max 126 UTF-16 characters - * but 253 would even accomodate any UTF-8 encoding */ -#define MAX_DESC_STR_LEN 253 - -enum mode { - MODE_NONE, - MODE_VERSION, - MODE_LIST, - MODE_DETACH, - MODE_UPLOAD, - MODE_DOWNLOAD -}; - -extern struct dfu_if *dfu_root; -extern int match_bus; -extern int match_device; -extern int match_vendor; -extern int match_product; -extern int match_vendor_dfu; -extern int match_product_dfu; -extern int match_config_index; -extern int match_iface_index; -extern int match_iface_alt_index; -extern const char *match_iface_alt_name; -extern const char *match_serial; -extern const char *match_serial_dfu; - -void probe_devices(libusb_context *); -void disconnect_devices(void); -void print_dfu_if(struct dfu_if *); -void list_dfu_interfaces(void); - -#endif /* DFU_UTIL_H */ +#ifndef DFU_UTIL_H +#define DFU_UTIL_H + +/* USB string descriptor should contain max 126 UTF-16 characters + * but 253 would even accomodate any UTF-8 encoding */ +#define MAX_DESC_STR_LEN 253 + +enum mode { + MODE_NONE, + MODE_VERSION, + MODE_LIST, + MODE_DETACH, + MODE_UPLOAD, + MODE_DOWNLOAD +}; + +extern struct dfu_if *dfu_root; +extern int match_bus; +extern int match_device; +extern int match_vendor; +extern int match_product; +extern int match_vendor_dfu; +extern int match_product_dfu; +extern int match_config_index; +extern int match_iface_index; +extern int match_iface_alt_index; +extern const char *match_iface_alt_name; +extern const char *match_serial; +extern const char *match_serial_dfu; + +void probe_devices(libusb_context *); +void disconnect_devices(void); +void print_dfu_if(struct dfu_if *); +void list_dfu_interfaces(void); + +#endif /* DFU_UTIL_H */ diff --git a/tools/src/dfu-util/src/dfuse.c b/tools/linux/src/dfu-util/src/dfuse.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfuse.c rename to tools/linux/src/dfu-util/src/dfuse.c index 39bc72d..fce29fe --- a/tools/src/dfu-util/src/dfuse.c +++ b/tools/linux/src/dfu-util/src/dfuse.c @@ -1,652 +1,652 @@ -/* - * DfuSe specific functions - * - * This implements the ST Microsystems DFU extensions (DfuSe) - * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) - * The DfuSe file format is described in ST document UM0391. - * - * Copyright 2010-2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include - -#include "portable.h" -#include "dfu.h" -#include "usb_dfu.h" -#include "dfu_file.h" -#include "dfuse.h" -#include "dfuse_mem.h" - -#define DFU_TIMEOUT 5000 - -extern int verbose; -static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ -static struct memsegment *mem_layout; -static unsigned int dfuse_address = 0; -static unsigned int dfuse_length = 0; -static int dfuse_force = 0; -static int dfuse_leave = 0; -static int dfuse_unprotect = 0; -static int dfuse_mass_erase = 0; - -unsigned int quad2uint(unsigned char *p) -{ - return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); -} - -void dfuse_parse_options(const char *options) -{ - char *end; - const char *endword; - unsigned int number; - - /* address, possibly empty, must be first */ - if (*options != ':') { - endword = strchr(options, ':'); - if (!endword) - endword = options + strlen(options); /* GNU strchrnul */ - - number = strtoul(options, &end, 0); - if (end == endword) { - dfuse_address = number; - } else { - errx(EX_IOERR, "Invalid dfuse address: %s", options); - } - options = endword; - } - - while (*options) { - if (*options == ':') { - options++; - continue; - } - endword = strchr(options, ':'); - if (!endword) - endword = options + strlen(options); - - if (!strncmp(options, "force", endword - options)) { - dfuse_force++; - options += 5; - continue; - } - if (!strncmp(options, "leave", endword - options)) { - dfuse_leave = 1; - options += 5; - continue; - } - if (!strncmp(options, "unprotect", endword - options)) { - dfuse_unprotect = 1; - options += 9; - continue; - } - if (!strncmp(options, "mass-erase", endword - options)) { - dfuse_mass_erase = 1; - options += 10; - continue; - } - - /* any valid number is interpreted as upload length */ - number = strtoul(options, &end, 0); - if (end == endword) { - dfuse_length = number; - } else { - errx(EX_IOERR, "Invalid dfuse modifier: %s", options); - } - options = endword; - } -} - -/* DFU_UPLOAD request for DfuSe 1.1a */ -int dfuse_upload(struct dfu_if *dif, const unsigned short length, - unsigned char *data, unsigned short transaction) -{ - int status; - - status = libusb_control_transfer(dif->dev_handle, - /* bmRequestType */ LIBUSB_ENDPOINT_IN | - LIBUSB_REQUEST_TYPE_CLASS | - LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_UPLOAD, - /* wValue */ transaction, - /* wIndex */ dif->interface, - /* Data */ data, - /* wLength */ length, - DFU_TIMEOUT); - if (status < 0) { - errx(EX_IOERR, "%s: libusb_control_msg returned %d", - __FUNCTION__, status); - } - return status; -} - -/* DFU_DNLOAD request for DfuSe 1.1a */ -int dfuse_download(struct dfu_if *dif, const unsigned short length, - unsigned char *data, unsigned short transaction) -{ - int status; - - status = libusb_control_transfer(dif->dev_handle, - /* bmRequestType */ LIBUSB_ENDPOINT_OUT | - LIBUSB_REQUEST_TYPE_CLASS | - LIBUSB_RECIPIENT_INTERFACE, - /* bRequest */ DFU_DNLOAD, - /* wValue */ transaction, - /* wIndex */ dif->interface, - /* Data */ data, - /* wLength */ length, - DFU_TIMEOUT); - if (status < 0) { - errx(EX_IOERR, "%s: libusb_control_transfer returned %d", - __FUNCTION__, status); - } - return status; -} - -/* DfuSe only commands */ -/* Leaves the device in dfuDNLOAD-IDLE state */ -int dfuse_special_command(struct dfu_if *dif, unsigned int address, - enum dfuse_command command) -{ - const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", - "MASS_ERASE", "READ_UNPROTECT"}; - unsigned char buf[5]; - int length; - int ret; - struct dfu_status dst; - int firstpoll = 1; - - if (command == ERASE_PAGE) { - struct memsegment *segment; - int page_size; - - segment = find_segment(mem_layout, address); - if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { - errx(EX_IOERR, "Page at 0x%08x can not be erased", - address); - } - page_size = segment->pagesize; - if (verbose > 1) - printf("Erasing page size %i at address 0x%08x, page " - "starting at 0x%08x\n", page_size, address, - address & ~(page_size - 1)); - buf[0] = 0x41; /* Erase command */ - length = 5; - last_erased_page = address & ~(page_size - 1); - } else if (command == SET_ADDRESS) { - if (verbose > 2) - printf(" Setting address pointer to 0x%08x\n", - address); - buf[0] = 0x21; /* Set Address Pointer command */ - length = 5; - } else if (command == MASS_ERASE) { - buf[0] = 0x41; /* Mass erase command when length = 1 */ - length = 1; - } else if (command == READ_UNPROTECT) { - buf[0] = 0x92; - length = 1; - } else { - errx(EX_IOERR, "Non-supported special command %d", command); - } - buf[1] = address & 0xff; - buf[2] = (address >> 8) & 0xff; - buf[3] = (address >> 16) & 0xff; - buf[4] = (address >> 24) & 0xff; - - ret = dfuse_download(dif, length, buf, 0); - if (ret < 0) { - errx(EX_IOERR, "Error during special command \"%s\" download", - dfuse_command_name[command]); - } - do { - ret = dfu_get_status(dif, &dst); - if (ret < 0) { - errx(EX_IOERR, "Error during special command \"%s\" get_status", - dfuse_command_name[command]); - } - if (firstpoll) { - firstpoll = 0; - if (dst.bState != DFU_STATE_dfuDNBUSY) { - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, - dfu_state_to_string(dst.bState), dst.bStatus, - dfu_status_to_string(dst.bStatus)); - errx(EX_IOERR, "Wrong state after command \"%s\" download", - dfuse_command_name[command]); - } - } - /* wait while command is executed */ - if (verbose) - printf(" Poll timeout %i ms\n", dst.bwPollTimeout); - milli_sleep(dst.bwPollTimeout); - if (command == READ_UNPROTECT) - return ret; - } while (dst.bState == DFU_STATE_dfuDNBUSY); - - if (dst.bStatus != DFU_STATUS_OK) { - errx(EX_IOERR, "%s not correctly executed", - dfuse_command_name[command]); - } - return ret; -} - -int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, - int transaction) -{ - int bytes_sent; - struct dfu_status dst; - int ret; - - ret = dfuse_download(dif, size, size ? data : NULL, transaction); - if (ret < 0) { - errx(EX_IOERR, "Error during download"); - return ret; - } - bytes_sent = ret; - - do { - ret = dfu_get_status(dif, &dst); - if (ret < 0) { - errx(EX_IOERR, "Error during download get_status"); - return ret; - } - milli_sleep(dst.bwPollTimeout); - } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && - dst.bState != DFU_STATE_dfuERROR && - dst.bState != DFU_STATE_dfuMANIFEST); - - if (dst.bState == DFU_STATE_dfuMANIFEST) - printf("Transitioning to dfuMANIFEST state\n"); - - if (dst.bStatus != DFU_STATUS_OK) { - printf(" failed!\n"); - printf("state(%u) = %s, status(%u) = %s\n", dst.bState, - dfu_state_to_string(dst.bState), dst.bStatus, - dfu_status_to_string(dst.bStatus)); - return -1; - } - return bytes_sent; -} - -int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, - const char *dfuse_options) -{ - int total_bytes = 0; - int upload_limit = 0; - unsigned char *buf; - int transaction; - int ret; - - buf = dfu_malloc(xfer_size); - - if (dfuse_options) - dfuse_parse_options(dfuse_options); - if (dfuse_length) - upload_limit = dfuse_length; - if (dfuse_address) { - struct memsegment *segment; - - mem_layout = parse_memory_layout((char *)dif->alt_name); - if (!mem_layout) - errx(EX_IOERR, "Failed to parse memory layout"); - - segment = find_segment(mem_layout, dfuse_address); - if (!dfuse_force && - (!segment || !(segment->memtype & DFUSE_READABLE))) - errx(EX_IOERR, "Page at 0x%08x is not readable", - dfuse_address); - - if (!upload_limit) { - upload_limit = segment->end - dfuse_address + 1; - printf("Limiting upload to end of memory segment, " - "%i bytes\n", upload_limit); - } - dfuse_special_command(dif, dfuse_address, SET_ADDRESS); - dfu_abort_to_idle(dif); - } else { - /* Boot loader decides the start address, unknown to us */ - /* Use a short length to lower risk of running out of bounds */ - if (!upload_limit) - upload_limit = 0x4000; - printf("Limiting default upload to %i bytes\n", upload_limit); - } - - dfu_progress_bar("Upload", 0, 1); - - transaction = 2; - while (1) { - int rc; - - /* last chunk can be smaller than original xfer_size */ - if (upload_limit - total_bytes < xfer_size) - xfer_size = upload_limit - total_bytes; - rc = dfuse_upload(dif, xfer_size, buf, transaction++); - if (rc < 0) { - ret = rc; - goto out_free; - } - - dfu_file_write_crc(fd, 0, buf, rc); - total_bytes += rc; - - if (total_bytes < 0) - errx(EX_SOFTWARE, "Received too many bytes"); - - if (rc < xfer_size || total_bytes >= upload_limit) { - /* last block, return successfully */ - ret = total_bytes; - break; - } - dfu_progress_bar("Upload", total_bytes, upload_limit); - } - - dfu_progress_bar("Upload", total_bytes, total_bytes); - - dfu_abort_to_idle(dif); - if (dfuse_leave) { - dfuse_special_command(dif, dfuse_address, SET_ADDRESS); - dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ - } - - out_free: - free(buf); - - return ret; -} - -/* Writes an element of any size to the device, taking care of page erases */ -/* returns 0 on success, otherwise -EINVAL */ -int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, - unsigned int dwElementSize, unsigned char *data, - int xfer_size) -{ - int p; - int ret; - struct memsegment *segment; - - /* Check at least that we can write to the last address */ - segment = - find_segment(mem_layout, dwElementAddress + dwElementSize - 1); - if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { - errx(EX_IOERR, "Last page at 0x%08x is not writeable", - dwElementAddress + dwElementSize - 1); - } - - dfu_progress_bar("Download", 0, 1); - - for (p = 0; p < (int)dwElementSize; p += xfer_size) { - int page_size; - unsigned int erase_address; - unsigned int address = dwElementAddress + p; - int chunk_size = xfer_size; - - segment = find_segment(mem_layout, address); - if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { - errx(EX_IOERR, "Page at 0x%08x is not writeable", - address); - } - page_size = segment->pagesize; - - /* check if this is the last chunk */ - if (p + chunk_size > (int)dwElementSize) - chunk_size = dwElementSize - p; - - /* Erase only for flash memory downloads */ - if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) { - /* erase all involved pages */ - for (erase_address = address; - erase_address < address + chunk_size; - erase_address += page_size) - if ((erase_address & ~(page_size - 1)) != - last_erased_page) - dfuse_special_command(dif, - erase_address, - ERASE_PAGE); - - if (((address + chunk_size - 1) & ~(page_size - 1)) != - last_erased_page) { - if (verbose > 2) - printf(" Chunk extends into next page," - " erase it as well\n"); - dfuse_special_command(dif, - address + chunk_size - 1, - ERASE_PAGE); - } - } - - if (verbose) { - printf(" Download from image offset " - "%08x to memory %08x-%08x, size %i\n", - p, address, address + chunk_size - 1, - chunk_size); - } else { - dfu_progress_bar("Download", p, dwElementSize); - } - - dfuse_special_command(dif, address, SET_ADDRESS); - - /* transaction = 2 for no address offset */ - ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2); - if (ret != chunk_size) { - errx(EX_IOERR, "Failed to write whole chunk: " - "%i of %i bytes", ret, chunk_size); - return -EINVAL; - } - } - if (!verbose) - dfu_progress_bar("Download", dwElementSize, dwElementSize); - return 0; -} - -static void -dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) -{ - if (size > *rem) { - errx(EX_IOERR, "Corrupt DfuSe file: " - "Cannot read %d bytes from %d bytes", size, *rem); - } - if (dst != NULL) - memcpy(dst, *src, size); - (*src) += size; - (*rem) -= size; -} - -/* Download raw binary file to DfuSe device */ -int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, - struct dfu_file *file, unsigned int start_address) -{ - unsigned int dwElementAddress; - unsigned int dwElementSize; - unsigned char *data; - int ret; - - dwElementAddress = start_address; - dwElementSize = file->size.total - - file->size.suffix - file->size.prefix; - - printf("Downloading to address = 0x%08x, size = %i\n", - dwElementAddress, dwElementSize); - - data = file->firmware + file->size.prefix; - - ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, - xfer_size); - if (ret != 0) - goto out_free; - - printf("File downloaded successfully\n"); - ret = dwElementSize; - - out_free: - return ret; -} - -/* Parse a DfuSe file and download contents to device */ -int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, - struct dfu_file *file) -{ - uint8_t dfuprefix[11]; - uint8_t targetprefix[274]; - uint8_t elementheader[8]; - int image; - int element; - int bTargets; - int bAlternateSetting; - int dwNbElements; - unsigned int dwElementAddress; - unsigned int dwElementSize; - uint8_t *data; - int ret; - int rem; - int bFirstAddressSaved = 0; - - rem = file->size.total - file->size.prefix - file->size.suffix; - data = file->firmware + file->size.prefix; - - /* Must be larger than a minimal DfuSe header and suffix */ - if (rem < (int)(sizeof(dfuprefix) + - sizeof(targetprefix) + sizeof(elementheader))) { - errx(EX_SOFTWARE, "File too small for a DfuSe file"); - } - - dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); - - if (strncmp((char *)dfuprefix, "DfuSe", 5)) { - errx(EX_IOERR, "No valid DfuSe signature"); - return -EINVAL; - } - if (dfuprefix[5] != 0x01) { - errx(EX_IOERR, "DFU format revision %i not supported", - dfuprefix[5]); - return -EINVAL; - } - bTargets = dfuprefix[10]; - printf("file contains %i DFU images\n", bTargets); - - for (image = 1; image <= bTargets; image++) { - printf("parsing DFU image %i\n", image); - dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); - if (strncmp((char *)targetprefix, "Target", 6)) { - errx(EX_IOERR, "No valid target signature"); - return -EINVAL; - } - bAlternateSetting = targetprefix[6]; - dwNbElements = quad2uint((unsigned char *)targetprefix + 270); - printf("image for alternate setting %i, ", bAlternateSetting); - printf("(%i elements, ", dwNbElements); - printf("total size = %i)\n", - quad2uint((unsigned char *)targetprefix + 266)); - if (bAlternateSetting != dif->altsetting) - printf("Warning: Image does not match current alternate" - " setting.\n" - "Please rerun with the correct -a option setting" - " to download this image!\n"); - for (element = 1; element <= dwNbElements; element++) { - printf("parsing element %i, ", element); - dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); - dwElementAddress = - quad2uint((unsigned char *)elementheader); - dwElementSize = - quad2uint((unsigned char *)elementheader + 4); - printf("address = 0x%08x, ", dwElementAddress); - printf("size = %i\n", dwElementSize); - - if (!bFirstAddressSaved) { - bFirstAddressSaved = 1; - dfuse_address = dwElementAddress; - } - /* sanity check */ - if ((int)dwElementSize > rem) - errx(EX_SOFTWARE, "File too small for element size"); - - if (bAlternateSetting == dif->altsetting) { - ret = dfuse_dnload_element(dif, dwElementAddress, - dwElementSize, data, xfer_size); - } else { - ret = 0; - } - - /* advance read pointer */ - dfuse_memcpy(NULL, &data, &rem, dwElementSize); - - if (ret != 0) - return ret; - } - } - - if (rem != 0) - warnx("%d bytes leftover", rem); - - printf("done parsing DfuSe file\n"); - - return 0; -} - -int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, - const char *dfuse_options) -{ - int ret; - - if (dfuse_options) - dfuse_parse_options(dfuse_options); - mem_layout = parse_memory_layout((char *)dif->alt_name); - if (!mem_layout) { - errx(EX_IOERR, "Failed to parse memory layout"); - } - if (dfuse_unprotect) { - if (!dfuse_force) { - errx(EX_IOERR, "The read unprotect command " - "will erase the flash memory" - "and can only be used with force\n"); - } - dfuse_special_command(dif, 0, READ_UNPROTECT); - printf("Device disconnects, erases flash and resets now\n"); - exit(0); - } - if (dfuse_mass_erase) { - if (!dfuse_force) { - errx(EX_IOERR, "The mass erase command " - "can only be used with force"); - } - printf("Performing mass erase, this can take a moment\n"); - dfuse_special_command(dif, 0, MASS_ERASE); - } - if (dfuse_address) { - if (file->bcdDFU == 0x11a) { - errx(EX_IOERR, "This is a DfuSe file, not " - "meant for raw download"); - } - ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); - } else { - if (file->bcdDFU != 0x11a) { - warnx("Only DfuSe file version 1.1a is supported"); - errx(EX_IOERR, "(for raw binary download, use the " - "--dfuse-address option)"); - } - ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); - } - free_segment_list(mem_layout); - - dfu_abort_to_idle(dif); - - if (dfuse_leave) { - dfuse_special_command(dif, dfuse_address, SET_ADDRESS); - dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ - } - return ret; -} +/* + * DfuSe specific functions + * + * This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) + * The DfuSe file format is described in ST document UM0391. + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfuse.h" +#include "dfuse_mem.h" + +#define DFU_TIMEOUT 5000 + +extern int verbose; +static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ +static struct memsegment *mem_layout; +static unsigned int dfuse_address = 0; +static unsigned int dfuse_length = 0; +static int dfuse_force = 0; +static int dfuse_leave = 0; +static int dfuse_unprotect = 0; +static int dfuse_mass_erase = 0; + +unsigned int quad2uint(unsigned char *p) +{ + return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); +} + +void dfuse_parse_options(const char *options) +{ + char *end; + const char *endword; + unsigned int number; + + /* address, possibly empty, must be first */ + if (*options != ':') { + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); /* GNU strchrnul */ + + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_address = number; + } else { + errx(EX_IOERR, "Invalid dfuse address: %s", options); + } + options = endword; + } + + while (*options) { + if (*options == ':') { + options++; + continue; + } + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); + + if (!strncmp(options, "force", endword - options)) { + dfuse_force++; + options += 5; + continue; + } + if (!strncmp(options, "leave", endword - options)) { + dfuse_leave = 1; + options += 5; + continue; + } + if (!strncmp(options, "unprotect", endword - options)) { + dfuse_unprotect = 1; + options += 9; + continue; + } + if (!strncmp(options, "mass-erase", endword - options)) { + dfuse_mass_erase = 1; + options += 10; + continue; + } + + /* any valid number is interpreted as upload length */ + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_length = number; + } else { + errx(EX_IOERR, "Invalid dfuse modifier: %s", options); + } + options = endword; + } +} + +/* DFU_UPLOAD request for DfuSe 1.1a */ +int dfuse_upload(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_msg returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DFU_DNLOAD request for DfuSe 1.1a */ +int dfuse_download(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_transfer returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DfuSe only commands */ +/* Leaves the device in dfuDNLOAD-IDLE state */ +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command) +{ + const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", + "MASS_ERASE", "READ_UNPROTECT"}; + unsigned char buf[5]; + int length; + int ret; + struct dfu_status dst; + int firstpoll = 1; + + if (command == ERASE_PAGE) { + struct memsegment *segment; + int page_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { + errx(EX_IOERR, "Page at 0x%08x can not be erased", + address); + } + page_size = segment->pagesize; + if (verbose > 1) + printf("Erasing page size %i at address 0x%08x, page " + "starting at 0x%08x\n", page_size, address, + address & ~(page_size - 1)); + buf[0] = 0x41; /* Erase command */ + length = 5; + last_erased_page = address & ~(page_size - 1); + } else if (command == SET_ADDRESS) { + if (verbose > 2) + printf(" Setting address pointer to 0x%08x\n", + address); + buf[0] = 0x21; /* Set Address Pointer command */ + length = 5; + } else if (command == MASS_ERASE) { + buf[0] = 0x41; /* Mass erase command when length = 1 */ + length = 1; + } else if (command == READ_UNPROTECT) { + buf[0] = 0x92; + length = 1; + } else { + errx(EX_IOERR, "Non-supported special command %d", command); + } + buf[1] = address & 0xff; + buf[2] = (address >> 8) & 0xff; + buf[3] = (address >> 16) & 0xff; + buf[4] = (address >> 24) & 0xff; + + ret = dfuse_download(dif, length, buf, 0); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" download", + dfuse_command_name[command]); + } + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" get_status", + dfuse_command_name[command]); + } + if (firstpoll) { + firstpoll = 0; + if (dst.bState != DFU_STATE_dfuDNBUSY) { + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + errx(EX_IOERR, "Wrong state after command \"%s\" download", + dfuse_command_name[command]); + } + } + /* wait while command is executed */ + if (verbose) + printf(" Poll timeout %i ms\n", dst.bwPollTimeout); + milli_sleep(dst.bwPollTimeout); + if (command == READ_UNPROTECT) + return ret; + } while (dst.bState == DFU_STATE_dfuDNBUSY); + + if (dst.bStatus != DFU_STATUS_OK) { + errx(EX_IOERR, "%s not correctly executed", + dfuse_command_name[command]); + } + return ret; +} + +int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, + int transaction) +{ + int bytes_sent; + struct dfu_status dst; + int ret; + + ret = dfuse_download(dif, size, size ? data : NULL, transaction); + if (ret < 0) { + errx(EX_IOERR, "Error during download"); + return ret; + } + bytes_sent = ret; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + return ret; + } + milli_sleep(dst.bwPollTimeout); + } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && + dst.bState != DFU_STATE_dfuERROR && + dst.bState != DFU_STATE_dfuMANIFEST); + + if (dst.bState == DFU_STATE_dfuMANIFEST) + printf("Transitioning to dfuMANIFEST state\n"); + + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + return -1; + } + return bytes_sent; +} + +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options) +{ + int total_bytes = 0; + int upload_limit = 0; + unsigned char *buf; + int transaction; + int ret; + + buf = dfu_malloc(xfer_size); + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + if (dfuse_length) + upload_limit = dfuse_length; + if (dfuse_address) { + struct memsegment *segment; + + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) + errx(EX_IOERR, "Failed to parse memory layout"); + + segment = find_segment(mem_layout, dfuse_address); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_READABLE))) + errx(EX_IOERR, "Page at 0x%08x is not readable", + dfuse_address); + + if (!upload_limit) { + upload_limit = segment->end - dfuse_address + 1; + printf("Limiting upload to end of memory segment, " + "%i bytes\n", upload_limit); + } + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfu_abort_to_idle(dif); + } else { + /* Boot loader decides the start address, unknown to us */ + /* Use a short length to lower risk of running out of bounds */ + if (!upload_limit) + upload_limit = 0x4000; + printf("Limiting default upload to %i bytes\n", upload_limit); + } + + dfu_progress_bar("Upload", 0, 1); + + transaction = 2; + while (1) { + int rc; + + /* last chunk can be smaller than original xfer_size */ + if (upload_limit - total_bytes < xfer_size) + xfer_size = upload_limit - total_bytes; + rc = dfuse_upload(dif, xfer_size, buf, transaction++); + if (rc < 0) { + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes"); + + if (rc < xfer_size || total_bytes >= upload_limit) { + /* last block, return successfully */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, upload_limit); + } + + dfu_progress_bar("Upload", total_bytes, total_bytes); + + dfu_abort_to_idle(dif); + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + + out_free: + free(buf); + + return ret; +} + +/* Writes an element of any size to the device, taking care of page erases */ +/* returns 0 on success, otherwise -EINVAL */ +int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, + unsigned int dwElementSize, unsigned char *data, + int xfer_size) +{ + int p; + int ret; + struct memsegment *segment; + + /* Check at least that we can write to the last address */ + segment = + find_segment(mem_layout, dwElementAddress + dwElementSize - 1); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Last page at 0x%08x is not writeable", + dwElementAddress + dwElementSize - 1); + } + + dfu_progress_bar("Download", 0, 1); + + for (p = 0; p < (int)dwElementSize; p += xfer_size) { + int page_size; + unsigned int erase_address; + unsigned int address = dwElementAddress + p; + int chunk_size = xfer_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Page at 0x%08x is not writeable", + address); + } + page_size = segment->pagesize; + + /* check if this is the last chunk */ + if (p + chunk_size > (int)dwElementSize) + chunk_size = dwElementSize - p; + + /* Erase only for flash memory downloads */ + if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) { + /* erase all involved pages */ + for (erase_address = address; + erase_address < address + chunk_size; + erase_address += page_size) + if ((erase_address & ~(page_size - 1)) != + last_erased_page) + dfuse_special_command(dif, + erase_address, + ERASE_PAGE); + + if (((address + chunk_size - 1) & ~(page_size - 1)) != + last_erased_page) { + if (verbose > 2) + printf(" Chunk extends into next page," + " erase it as well\n"); + dfuse_special_command(dif, + address + chunk_size - 1, + ERASE_PAGE); + } + } + + if (verbose) { + printf(" Download from image offset " + "%08x to memory %08x-%08x, size %i\n", + p, address, address + chunk_size - 1, + chunk_size); + } else { + dfu_progress_bar("Download", p, dwElementSize); + } + + dfuse_special_command(dif, address, SET_ADDRESS); + + /* transaction = 2 for no address offset */ + ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2); + if (ret != chunk_size) { + errx(EX_IOERR, "Failed to write whole chunk: " + "%i of %i bytes", ret, chunk_size); + return -EINVAL; + } + } + if (!verbose) + dfu_progress_bar("Download", dwElementSize, dwElementSize); + return 0; +} + +static void +dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) +{ + if (size > *rem) { + errx(EX_IOERR, "Corrupt DfuSe file: " + "Cannot read %d bytes from %d bytes", size, *rem); + } + if (dst != NULL) + memcpy(dst, *src, size); + (*src) += size; + (*rem) -= size; +} + +/* Download raw binary file to DfuSe device */ +int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file, unsigned int start_address) +{ + unsigned int dwElementAddress; + unsigned int dwElementSize; + unsigned char *data; + int ret; + + dwElementAddress = start_address; + dwElementSize = file->size.total - + file->size.suffix - file->size.prefix; + + printf("Downloading to address = 0x%08x, size = %i\n", + dwElementAddress, dwElementSize); + + data = file->firmware + file->size.prefix; + + ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, + xfer_size); + if (ret != 0) + goto out_free; + + printf("File downloaded successfully\n"); + ret = dwElementSize; + + out_free: + return ret; +} + +/* Parse a DfuSe file and download contents to device */ +int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file) +{ + uint8_t dfuprefix[11]; + uint8_t targetprefix[274]; + uint8_t elementheader[8]; + int image; + int element; + int bTargets; + int bAlternateSetting; + int dwNbElements; + unsigned int dwElementAddress; + unsigned int dwElementSize; + uint8_t *data; + int ret; + int rem; + int bFirstAddressSaved = 0; + + rem = file->size.total - file->size.prefix - file->size.suffix; + data = file->firmware + file->size.prefix; + + /* Must be larger than a minimal DfuSe header and suffix */ + if (rem < (int)(sizeof(dfuprefix) + + sizeof(targetprefix) + sizeof(elementheader))) { + errx(EX_SOFTWARE, "File too small for a DfuSe file"); + } + + dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); + + if (strncmp((char *)dfuprefix, "DfuSe", 5)) { + errx(EX_IOERR, "No valid DfuSe signature"); + return -EINVAL; + } + if (dfuprefix[5] != 0x01) { + errx(EX_IOERR, "DFU format revision %i not supported", + dfuprefix[5]); + return -EINVAL; + } + bTargets = dfuprefix[10]; + printf("file contains %i DFU images\n", bTargets); + + for (image = 1; image <= bTargets; image++) { + printf("parsing DFU image %i\n", image); + dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); + if (strncmp((char *)targetprefix, "Target", 6)) { + errx(EX_IOERR, "No valid target signature"); + return -EINVAL; + } + bAlternateSetting = targetprefix[6]; + dwNbElements = quad2uint((unsigned char *)targetprefix + 270); + printf("image for alternate setting %i, ", bAlternateSetting); + printf("(%i elements, ", dwNbElements); + printf("total size = %i)\n", + quad2uint((unsigned char *)targetprefix + 266)); + if (bAlternateSetting != dif->altsetting) + printf("Warning: Image does not match current alternate" + " setting.\n" + "Please rerun with the correct -a option setting" + " to download this image!\n"); + for (element = 1; element <= dwNbElements; element++) { + printf("parsing element %i, ", element); + dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); + dwElementAddress = + quad2uint((unsigned char *)elementheader); + dwElementSize = + quad2uint((unsigned char *)elementheader + 4); + printf("address = 0x%08x, ", dwElementAddress); + printf("size = %i\n", dwElementSize); + + if (!bFirstAddressSaved) { + bFirstAddressSaved = 1; + dfuse_address = dwElementAddress; + } + /* sanity check */ + if ((int)dwElementSize > rem) + errx(EX_SOFTWARE, "File too small for element size"); + + if (bAlternateSetting == dif->altsetting) { + ret = dfuse_dnload_element(dif, dwElementAddress, + dwElementSize, data, xfer_size); + } else { + ret = 0; + } + + /* advance read pointer */ + dfuse_memcpy(NULL, &data, &rem, dwElementSize); + + if (ret != 0) + return ret; + } + } + + if (rem != 0) + warnx("%d bytes leftover", rem); + + printf("done parsing DfuSe file\n"); + + return 0; +} + +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options) +{ + int ret; + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) { + errx(EX_IOERR, "Failed to parse memory layout"); + } + if (dfuse_unprotect) { + if (!dfuse_force) { + errx(EX_IOERR, "The read unprotect command " + "will erase the flash memory" + "and can only be used with force\n"); + } + dfuse_special_command(dif, 0, READ_UNPROTECT); + printf("Device disconnects, erases flash and resets now\n"); + exit(0); + } + if (dfuse_mass_erase) { + if (!dfuse_force) { + errx(EX_IOERR, "The mass erase command " + "can only be used with force"); + } + printf("Performing mass erase, this can take a moment\n"); + dfuse_special_command(dif, 0, MASS_ERASE); + } + if (dfuse_address) { + if (file->bcdDFU == 0x11a) { + errx(EX_IOERR, "This is a DfuSe file, not " + "meant for raw download"); + } + ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); + } else { + if (file->bcdDFU != 0x11a) { + warnx("Only DfuSe file version 1.1a is supported"); + errx(EX_IOERR, "(for raw binary download, use the " + "--dfuse-address option)"); + } + ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); + } + free_segment_list(mem_layout); + + dfu_abort_to_idle(dif); + + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + return ret; +} diff --git a/tools/src/dfu-util/src/dfuse.h b/tools/linux/src/dfu-util/src/dfuse.h old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/dfuse.h rename to tools/linux/src/dfu-util/src/dfuse.h index e82f058..ed1108c --- a/tools/src/dfu-util/src/dfuse.h +++ b/tools/linux/src/dfu-util/src/dfuse.h @@ -1,35 +1,35 @@ -/* This implements the ST Microsystems DFU extensions (DfuSe) - * as per the DfuSe 1.1a specification (Document UM0391) - * - * (C) 2010-2012 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef DFUSE_H -#define DFUSE_H - -#include "dfu.h" - -enum dfuse_command { SET_ADDRESS, ERASE_PAGE, MASS_ERASE, READ_UNPROTECT }; - -int dfuse_special_command(struct dfu_if *dif, unsigned int address, - enum dfuse_command command); -int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, - const char *dfuse_options); -int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, - const char *dfuse_options); - -#endif /* DFUSE_H */ +/* This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (Document UM0391) + * + * (C) 2010-2012 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_H +#define DFUSE_H + +#include "dfu.h" + +enum dfuse_command { SET_ADDRESS, ERASE_PAGE, MASS_ERASE, READ_UNPROTECT }; + +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command); +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options); +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options); + +#endif /* DFUSE_H */ diff --git a/tools/src/dfu-util/src/dfuse_mem.c b/tools/linux/src/dfu-util/src/dfuse_mem.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfuse_mem.c rename to tools/linux/src/dfu-util/src/dfuse_mem.c index ae339fb..a91aacf --- a/tools/src/dfu-util/src/dfuse_mem.c +++ b/tools/linux/src/dfu-util/src/dfuse_mem.c @@ -1,198 +1,198 @@ -/* - * Helper functions for reading the memory map of a device - * following the ST DfuSe 1.1a specification. - * - * Copyright 2011-2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include - -#include "portable.h" -#include "dfu_file.h" -#include "dfuse_mem.h" - -int add_segment(struct memsegment **segment_list, struct memsegment segment) -{ - struct memsegment *new_element; - - new_element = dfu_malloc(sizeof(struct memsegment)); - *new_element = segment; - new_element->next = NULL; - - if (*segment_list == NULL) - /* list can be empty on first call */ - *segment_list = new_element; - else { - struct memsegment *next_element; - - /* find last element in list */ - next_element = *segment_list; - while (next_element->next != NULL) - next_element = next_element->next; - next_element->next = new_element; - } - return 0; -} - -struct memsegment *find_segment(struct memsegment *segment_list, - unsigned int address) -{ - while (segment_list != NULL) { - if (segment_list->start <= address && - segment_list->end >= address) - return segment_list; - segment_list = segment_list->next; - } - return NULL; -} - -void free_segment_list(struct memsegment *segment_list) -{ - struct memsegment *next_element; - - while (segment_list->next != NULL) { - next_element = segment_list->next; - free(segment_list); - segment_list = next_element; - } - free(segment_list); -} - -/* Parse memory map from interface descriptor string - * encoded as per ST document UM0424 section 4.3.2. - */ -struct memsegment *parse_memory_layout(char *intf_desc) -{ - - char multiplier, memtype; - unsigned int address; - int sectors, size; - char *name, *typestring; - int ret; - int count = 0; - char separator; - int scanned; - struct memsegment *segment_list = NULL; - struct memsegment segment; - - name = dfu_malloc(strlen(intf_desc)); - - ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned); - if (ret < 1) { - free(name); - warnx("Could not read name, sscanf returned %d", ret); - return NULL; - } - printf("DfuSe interface name: \"%s\"\n", name); - - intf_desc += scanned; - typestring = dfu_malloc(strlen(intf_desc)); - - while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned), - ret > 0) { - - intf_desc += scanned; - while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n", - §ors, &size, &multiplier, typestring, - &scanned), ret > 2) { - intf_desc += scanned; - - count++; - memtype = 0; - if (ret == 4) { - if (strlen(typestring) == 1 - && typestring[0] != '/') - memtype = typestring[0]; - else { - warnx("Parsing type identifier '%s' " - "failed for segment %i", - typestring, count); - continue; - } - } - - /* Quirk for STM32F4 devices */ - if (strcmp(name, "Device Feature") == 0) - memtype = 'e'; - - switch (multiplier) { - case 'B': - break; - case 'K': - size *= 1024; - break; - case 'M': - size *= 1024 * 1024; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'g': - if (!memtype) { - warnx("Non-valid multiplier '%c', " - "interpreted as type " - "identifier instead", - multiplier); - memtype = multiplier; - break; - } - /* fallthrough if memtype was already set */ - default: - warnx("Non-valid multiplier '%c', " - "assuming bytes", multiplier); - } - - if (!memtype) { - warnx("No valid type for segment %d\n", count); - continue; - } - - segment.start = address; - segment.end = address + sectors * size - 1; - segment.pagesize = size; - segment.memtype = memtype & 7; - add_segment(&segment_list, segment); - - if (verbose) - printf("Memory segment at 0x%08x %3d x %4d = " - "%5d (%s%s%s)\n", - address, sectors, size, sectors * size, - memtype & DFUSE_READABLE ? "r" : "", - memtype & DFUSE_ERASABLE ? "e" : "", - memtype & DFUSE_WRITEABLE ? "w" : ""); - - address += sectors * size; - - separator = *intf_desc; - if (separator == ',') - intf_desc += 1; - else - break; - } /* while per segment */ - - } /* while per address */ - free(name); - free(typestring); - - return segment_list; -} +/* + * Helper functions for reading the memory map of a device + * following the ST DfuSe 1.1a specification. + * + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" +#include "dfuse_mem.h" + +int add_segment(struct memsegment **segment_list, struct memsegment segment) +{ + struct memsegment *new_element; + + new_element = dfu_malloc(sizeof(struct memsegment)); + *new_element = segment; + new_element->next = NULL; + + if (*segment_list == NULL) + /* list can be empty on first call */ + *segment_list = new_element; + else { + struct memsegment *next_element; + + /* find last element in list */ + next_element = *segment_list; + while (next_element->next != NULL) + next_element = next_element->next; + next_element->next = new_element; + } + return 0; +} + +struct memsegment *find_segment(struct memsegment *segment_list, + unsigned int address) +{ + while (segment_list != NULL) { + if (segment_list->start <= address && + segment_list->end >= address) + return segment_list; + segment_list = segment_list->next; + } + return NULL; +} + +void free_segment_list(struct memsegment *segment_list) +{ + struct memsegment *next_element; + + while (segment_list->next != NULL) { + next_element = segment_list->next; + free(segment_list); + segment_list = next_element; + } + free(segment_list); +} + +/* Parse memory map from interface descriptor string + * encoded as per ST document UM0424 section 4.3.2. + */ +struct memsegment *parse_memory_layout(char *intf_desc) +{ + + char multiplier, memtype; + unsigned int address; + int sectors, size; + char *name, *typestring; + int ret; + int count = 0; + char separator; + int scanned; + struct memsegment *segment_list = NULL; + struct memsegment segment; + + name = dfu_malloc(strlen(intf_desc)); + + ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned); + if (ret < 1) { + free(name); + warnx("Could not read name, sscanf returned %d", ret); + return NULL; + } + printf("DfuSe interface name: \"%s\"\n", name); + + intf_desc += scanned; + typestring = dfu_malloc(strlen(intf_desc)); + + while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned), + ret > 0) { + + intf_desc += scanned; + while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n", + §ors, &size, &multiplier, typestring, + &scanned), ret > 2) { + intf_desc += scanned; + + count++; + memtype = 0; + if (ret == 4) { + if (strlen(typestring) == 1 + && typestring[0] != '/') + memtype = typestring[0]; + else { + warnx("Parsing type identifier '%s' " + "failed for segment %i", + typestring, count); + continue; + } + } + + /* Quirk for STM32F4 devices */ + if (strcmp(name, "Device Feature") == 0) + memtype = 'e'; + + switch (multiplier) { + case 'B': + break; + case 'K': + size *= 1024; + break; + case 'M': + size *= 1024 * 1024; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + if (!memtype) { + warnx("Non-valid multiplier '%c', " + "interpreted as type " + "identifier instead", + multiplier); + memtype = multiplier; + break; + } + /* fallthrough if memtype was already set */ + default: + warnx("Non-valid multiplier '%c', " + "assuming bytes", multiplier); + } + + if (!memtype) { + warnx("No valid type for segment %d\n", count); + continue; + } + + segment.start = address; + segment.end = address + sectors * size - 1; + segment.pagesize = size; + segment.memtype = memtype & 7; + add_segment(&segment_list, segment); + + if (verbose) + printf("Memory segment at 0x%08x %3d x %4d = " + "%5d (%s%s%s)\n", + address, sectors, size, sectors * size, + memtype & DFUSE_READABLE ? "r" : "", + memtype & DFUSE_ERASABLE ? "e" : "", + memtype & DFUSE_WRITEABLE ? "w" : ""); + + address += sectors * size; + + separator = *intf_desc; + if (separator == ',') + intf_desc += 1; + else + break; + } /* while per segment */ + + } /* while per address */ + free(name); + free(typestring); + + return segment_list; +} diff --git a/tools/src/dfu-util/src/dfuse_mem.h b/tools/linux/src/dfu-util/src/dfuse_mem.h old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/dfuse_mem.h rename to tools/linux/src/dfu-util/src/dfuse_mem.h index 8fe7cc4..0181f0c --- a/tools/src/dfu-util/src/dfuse_mem.h +++ b/tools/linux/src/dfu-util/src/dfuse_mem.h @@ -1,44 +1,44 @@ -/* Helper functions for reading the memory map in a device - * following the ST DfuSe 1.1a specification. - * - * (C) 2011 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef DFUSE_MEM_H -#define DFUSE_MEM_H - -#define DFUSE_READABLE 1 -#define DFUSE_ERASABLE 2 -#define DFUSE_WRITEABLE 4 - -struct memsegment { - unsigned int start; - unsigned int end; - int pagesize; - int memtype; - struct memsegment *next; -}; - -int add_segment(struct memsegment **list, struct memsegment new_element); - -struct memsegment *find_segment(struct memsegment *list, unsigned int address); - -void free_segment_list(struct memsegment *list); - -struct memsegment *parse_memory_layout(char *intf_desc_str); - -#endif /* DFUSE_MEM_H */ +/* Helper functions for reading the memory map in a device + * following the ST DfuSe 1.1a specification. + * + * (C) 2011 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_MEM_H +#define DFUSE_MEM_H + +#define DFUSE_READABLE 1 +#define DFUSE_ERASABLE 2 +#define DFUSE_WRITEABLE 4 + +struct memsegment { + unsigned int start; + unsigned int end; + int pagesize; + int memtype; + struct memsegment *next; +}; + +int add_segment(struct memsegment **list, struct memsegment new_element); + +struct memsegment *find_segment(struct memsegment *list, unsigned int address); + +void free_segment_list(struct memsegment *list); + +struct memsegment *parse_memory_layout(char *intf_desc_str); + +#endif /* DFUSE_MEM_H */ diff --git a/tools/src/dfu-util/src/main.c b/tools/linux/src/dfu-util/src/main.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/main.c rename to tools/linux/src/dfu-util/src/main.c index f59d27d..acaed2f --- a/tools/src/dfu-util/src/main.c +++ b/tools/linux/src/dfu-util/src/main.c @@ -1,699 +1,699 @@ -/* - * dfu-util - * - * Copyright 2007-2008 by OpenMoko, Inc. - * Copyright 2013-2014 Hans Petter Selasky - * - * Written by Harald Welte - * - * Based on existing code of dfu-programmer-0.4 - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "portable.h" -#include "dfu.h" -#include "usb_dfu.h" -#include "dfu_file.h" -#include "dfu_load.h" -#include "dfu_util.h" -#include "dfuse.h" -#include "quirks.h" - -#ifdef HAVE_USBPATH_H -#include -#endif - -int verbose = 0; - -struct dfu_if *dfu_root = NULL; - -int match_bus = -1; -int match_device = -1; -int match_vendor = -1; -int match_product = -1; -int match_vendor_dfu = -1; -int match_product_dfu = -1; -int match_config_index = -1; -int match_iface_index = -1; -int match_iface_alt_index = -1; -const char *match_iface_alt_name = NULL; -const char *match_serial = NULL; -const char *match_serial_dfu = NULL; - -static int parse_match_value(const char *str, int default_value) -{ - char *remainder; - int value; - - if (str == NULL) { - value = default_value; - } else if (*str == '*') { - value = -1; /* Match anything */ - } else if (*str == '-') { - value = 0x10000; /* Impossible vendor/product ID */ - } else { - value = strtoul(str, &remainder, 16); - if (remainder == str) { - value = default_value; - } - } - return value; -} - -static void parse_vendprod(const char *str) -{ - const char *comma; - const char *colon; - - /* Default to match any DFU device in runtime or DFU mode */ - match_vendor = -1; - match_product = -1; - match_vendor_dfu = -1; - match_product_dfu = -1; - - comma = strchr(str, ','); - if (comma == str) { - /* DFU mode vendor/product being specified without any runtime - * vendor/product specification, so don't match any runtime device */ - match_vendor = match_product = 0x10000; - } else { - colon = strchr(str, ':'); - if (colon != NULL) { - ++colon; - if ((comma != NULL) && (colon > comma)) { - colon = NULL; - } - } - match_vendor = parse_match_value(str, match_vendor); - match_product = parse_match_value(colon, match_product); - if (comma != NULL) { - /* Both runtime and DFU mode vendor/product specifications are - * available, so default DFU mode match components to the given - * runtime match components */ - match_vendor_dfu = match_vendor; - match_product_dfu = match_product; - } - } - if (comma != NULL) { - ++comma; - colon = strchr(comma, ':'); - if (colon != NULL) { - ++colon; - } - match_vendor_dfu = parse_match_value(comma, match_vendor_dfu); - match_product_dfu = parse_match_value(colon, match_product_dfu); - } -} - -static void parse_serial(char *str) -{ - char *comma; - - match_serial = str; - comma = strchr(str, ','); - if (comma == NULL) { - match_serial_dfu = match_serial; - } else { - *comma++ = 0; - match_serial_dfu = comma; - } - if (*match_serial == 0) match_serial = NULL; - if (*match_serial_dfu == 0) match_serial_dfu = NULL; -} - -#ifdef HAVE_USBPATH_H - -static int resolve_device_path(char *path) -{ - int res; - - res = usb_path2devnum(path); - if (res < 0) - return -EINVAL; - if (!res) - return 0; - - match_bus = atoi(path); - match_device = res; - - return 0; -} - -#else /* HAVE_USBPATH_H */ - -static int resolve_device_path(char *path) -{ - (void)path; /* Eliminate unused variable warning */ - errx(EX_SOFTWARE, "USB device paths are not supported by this dfu-util.\n"); -} - -#endif /* !HAVE_USBPATH_H */ - -static void help(void) -{ - fprintf(stderr, "Usage: dfu-util [options] ...\n" - " -h --help\t\t\tPrint this help message\n" - " -V --version\t\t\tPrint the version number\n" - " -v --verbose\t\t\tPrint verbose debug statements\n" - " -l --list\t\t\tList currently attached DFU capable devices\n"); - fprintf(stderr, " -e --detach\t\t\tDetach currently attached DFU capable devices\n" - " -E --detach-delay seconds\tTime to wait before reopening a device after detach\n" - " -d --device :[,:]\n" - "\t\t\t\tSpecify Vendor/Product ID(s) of DFU device\n" - " -p --path \tSpecify path to DFU device\n" - " -c --cfg \t\tSpecify the Configuration of DFU device\n" - " -i --intf \t\tSpecify the DFU Interface number\n" - " -S --serial [,]\n" - "\t\t\t\tSpecify Serial String of DFU device\n" - " -a --alt \t\tSpecify the Altsetting of the DFU Interface\n" - "\t\t\t\tby name or by number\n"); - fprintf(stderr, " -t --transfer-size \tSpecify the number of bytes per USB Transfer\n" - " -U --upload \t\tRead firmware from device into \n" - " -Z --upload-size \tSpecify the expected upload size in bytes\n" - " -D --download \t\tWrite firmware from into device\n" - " -R --reset\t\t\tIssue USB Reset signalling once we're finished\n" - " -s --dfuse-address
\tST DfuSe mode, specify target address for\n" - "\t\t\t\traw file download or upload. Not applicable for\n" - "\t\t\t\tDfuSe file (.dfu) downloads\n" - ); - exit(EX_USAGE); -} - -static void print_version(void) -{ - printf(PACKAGE_STRING "\n\n"); - printf("Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\n" - "Copyright 2010-2014 Tormod Volden and Stefan Schmidt\n" - "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" - "Please report bugs to " PACKAGE_BUGREPORT "\n\n"); -} - -static struct option opts[] = { - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'V' }, - { "verbose", 0, 0, 'v' }, - { "list", 0, 0, 'l' }, - { "detach", 0, 0, 'e' }, - { "detach-delay", 1, 0, 'E' }, - { "device", 1, 0, 'd' }, - { "path", 1, 0, 'p' }, - { "configuration", 1, 0, 'c' }, - { "cfg", 1, 0, 'c' }, - { "interface", 1, 0, 'i' }, - { "intf", 1, 0, 'i' }, - { "altsetting", 1, 0, 'a' }, - { "alt", 1, 0, 'a' }, - { "serial", 1, 0, 'S' }, - { "transfer-size", 1, 0, 't' }, - { "upload", 1, 0, 'U' }, - { "upload-size", 1, 0, 'Z' }, - { "download", 1, 0, 'D' }, - { "reset", 0, 0, 'R' }, - { "dfuse-address", 1, 0, 's' }, - { 0, 0, 0, 0 } -}; - -int main(int argc, char **argv) -{ - int expected_size = 0; - unsigned int transfer_size = 0; - enum mode mode = MODE_NONE; - struct dfu_status status; - libusb_context *ctx; - struct dfu_file file; - char *end; - int final_reset = 0; - int ret; - int dfuse_device = 0; - int fd; - const char *dfuse_options = NULL; - int detach_delay = 5; - uint16_t runtime_vendor; - uint16_t runtime_product; - - memset(&file, 0, sizeof(file)); - - /* make sure all prints are flushed */ - setvbuf(stdout, NULL, _IONBF, 0); - - while (1) { - int c, option_index = 0; - c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, - &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - help(); - break; - case 'V': - mode = MODE_VERSION; - break; - case 'v': - verbose++; - break; - case 'l': - mode = MODE_LIST; - break; - case 'e': - mode = MODE_DETACH; - match_iface_alt_index = 0; - match_iface_index = 0; - break; - case 'E': - detach_delay = atoi(optarg); - break; - case 'd': - parse_vendprod(optarg); - break; - case 'p': - /* Parse device path */ - ret = resolve_device_path(optarg); - if (ret < 0) - errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); - if (!ret) - errx(EX_SOFTWARE, "Cannot find '%s'", optarg); - break; - case 'c': - /* Configuration */ - match_config_index = atoi(optarg); - break; - case 'i': - /* Interface */ - match_iface_index = atoi(optarg); - break; - case 'a': - /* Interface Alternate Setting */ - match_iface_alt_index = strtoul(optarg, &end, 0); - if (*end) { - match_iface_alt_name = optarg; - match_iface_alt_index = -1; - } - break; - case 'S': - parse_serial(optarg); - break; - case 't': - transfer_size = atoi(optarg); - break; - case 'U': - mode = MODE_UPLOAD; - file.name = optarg; - break; - case 'Z': - expected_size = atoi(optarg); - break; - case 'D': - mode = MODE_DOWNLOAD; - file.name = optarg; - break; - case 'R': - final_reset = 1; - break; - case 's': - dfuse_options = optarg; - break; - default: - help(); - break; - } - } - - print_version(); - if (mode == MODE_VERSION) { - exit(0); - } - - if (mode == MODE_NONE) { - fprintf(stderr, "You need to specify one of -D or -U\n"); - help(); - } - - if (match_config_index == 0) { - /* Handle "-c 0" (unconfigured device) as don't care */ - match_config_index = -1; - } - - if (mode == MODE_DOWNLOAD) { - dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); - /* If the user didn't specify product and/or vendor IDs to match, - * use any IDs from the file suffix for device matching */ - if (match_vendor < 0 && file.idVendor != 0xffff) { - match_vendor = file.idVendor; - printf("Match vendor ID from file: %04x\n", match_vendor); - } - if (match_product < 0 && file.idProduct != 0xffff) { - match_product = file.idProduct; - printf("Match product ID from file: %04x\n", match_product); - } - } - - ret = libusb_init(&ctx); - if (ret) - errx(EX_IOERR, "unable to initialize libusb: %i", ret); - - if (verbose > 2) { - libusb_set_debug(ctx, 255); - } - - probe_devices(ctx); - - if (mode == MODE_LIST) { - list_dfu_interfaces(); - exit(0); - } - - if (dfu_root == NULL) { - errx(EX_IOERR, "No DFU capable USB device available"); - } else if (dfu_root->next != NULL) { - /* We cannot safely support more than one DFU capable device - * with same vendor/product ID, since during DFU we need to do - * a USB bus reset, after which the target device will get a - * new address */ - errx(EX_IOERR, "More than one DFU capable USB device found! " - "Try `--list' and specify the serial number " - "or disconnect all but one device\n"); - } - - /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ - - printf("Opening DFU capable USB device...\n"); - ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); - if (ret || !dfu_root->dev_handle) - errx(EX_IOERR, "Cannot open device"); - - printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); - - printf("Run-time device DFU version %04x\n", - libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); - - /* Transition from run-Time mode to DFU mode */ - if (!(dfu_root->flags & DFU_IFF_DFU)) { - int err; - /* In the 'first round' during runtime mode, there can only be one - * DFU Interface descriptor according to the DFU Spec. */ - - /* FIXME: check if the selected device really has only one */ - - runtime_vendor = dfu_root->vendor; - runtime_product = dfu_root->product; - - printf("Claiming USB DFU Runtime Interface...\n"); - if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { - errx(EX_IOERR, "Cannot claim interface %d", - dfu_root->interface); - } - - if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { - errx(EX_IOERR, "Cannot set alt interface zero"); - } - - printf("Determining device status: "); - - err = dfu_get_status(dfu_root, &status); - if (err == LIBUSB_ERROR_PIPE) { - printf("Device does not implement get_status, assuming appIDLE\n"); - status.bStatus = DFU_STATUS_OK; - status.bwPollTimeout = 0; - status.bState = DFU_STATE_appIDLE; - status.iString = 0; - } else if (err < 0) { - errx(EX_IOERR, "error get_status"); - } else { - printf("state = %s, status = %d\n", - dfu_state_to_string(status.bState), status.bStatus); - } - milli_sleep(status.bwPollTimeout); - - switch (status.bState) { - case DFU_STATE_appIDLE: - case DFU_STATE_appDETACH: - printf("Device really in Runtime Mode, send DFU " - "detach request...\n"); - if (dfu_detach(dfu_root->dev_handle, - dfu_root->interface, 1000) < 0) { - warnx("error detaching"); - } - if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { - printf("Device will detach and reattach...\n"); - } else { - printf("Resetting USB...\n"); - ret = libusb_reset_device(dfu_root->dev_handle); - if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) - errx(EX_IOERR, "error resetting " - "after detach"); - } - break; - case DFU_STATE_dfuERROR: - printf("dfuERROR, clearing status\n"); - if (dfu_clear_status(dfu_root->dev_handle, - dfu_root->interface) < 0) { - errx(EX_IOERR, "error clear_status"); - } - /* fall through */ - default: - warnx("WARNING: Runtime device already in DFU state ?!?"); - libusb_release_interface(dfu_root->dev_handle, - dfu_root->interface); - goto dfustate; - } - libusb_release_interface(dfu_root->dev_handle, - dfu_root->interface); - libusb_close(dfu_root->dev_handle); - dfu_root->dev_handle = NULL; - - if (mode == MODE_DETACH) { - libusb_exit(ctx); - exit(0); - } - - /* keeping handles open might prevent re-enumeration */ - disconnect_devices(); - - milli_sleep(detach_delay * 1000); - - /* Change match vendor and product to impossible values to force - * only DFU mode matches in the following probe */ - match_vendor = match_product = 0x10000; - - probe_devices(ctx); - - if (dfu_root == NULL) { - errx(EX_IOERR, "Lost device after RESET?"); - } else if (dfu_root->next != NULL) { - errx(EX_IOERR, "More than one DFU capable USB device found! " - "Try `--list' and specify the serial number " - "or disconnect all but one device"); - } - - /* Check for DFU mode device */ - if (!(dfu_root->flags | DFU_IFF_DFU)) - errx(EX_SOFTWARE, "Device is not in DFU mode"); - - printf("Opening DFU USB Device...\n"); - ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); - if (ret || !dfu_root->dev_handle) { - errx(EX_IOERR, "Cannot open device"); - } - } else { - /* we're already in DFU mode, so we can skip the detach/reset - * procedure */ - /* If a match vendor/product was specified, use that as the runtime - * vendor/product, otherwise use the DFU mode vendor/product */ - runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; - runtime_product = match_product < 0 ? dfu_root->product : match_product; - } - -dfustate: -#if 0 - printf("Setting Configuration %u...\n", dfu_root->configuration); - if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { - errx(EX_IOERR, "Cannot set configuration"); - } -#endif - printf("Claiming USB DFU Interface...\n"); - if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { - errx(EX_IOERR, "Cannot claim interface"); - } - - printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); - if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { - errx(EX_IOERR, "Cannot set alternate interface"); - } - -status_again: - printf("Determining device status: "); - if (dfu_get_status(dfu_root, &status ) < 0) { - errx(EX_IOERR, "error get_status"); - } - printf("state = %s, status = %d\n", - dfu_state_to_string(status.bState), status.bStatus); - - milli_sleep(status.bwPollTimeout); - - switch (status.bState) { - case DFU_STATE_appIDLE: - case DFU_STATE_appDETACH: - errx(EX_IOERR, "Device still in Runtime Mode!"); - break; - case DFU_STATE_dfuERROR: - printf("dfuERROR, clearing status\n"); - if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { - errx(EX_IOERR, "error clear_status"); - } - goto status_again; - break; - case DFU_STATE_dfuDNLOAD_IDLE: - case DFU_STATE_dfuUPLOAD_IDLE: - printf("aborting previous incomplete transfer\n"); - if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { - errx(EX_IOERR, "can't send DFU_ABORT"); - } - goto status_again; - break; - case DFU_STATE_dfuIDLE: - printf("dfuIDLE, continuing\n"); - break; - default: - break; - } - - if (DFU_STATUS_OK != status.bStatus ) { - printf("WARNING: DFU Status: '%s'\n", - dfu_status_to_string(status.bStatus)); - /* Clear our status & try again. */ - if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) - errx(EX_IOERR, "USB communication error"); - if (dfu_get_status(dfu_root, &status) < 0) - errx(EX_IOERR, "USB communication error"); - if (DFU_STATUS_OK != status.bStatus) - errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); - - milli_sleep(status.bwPollTimeout); - } - - printf("DFU mode device DFU version %04x\n", - libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); - - if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) - dfuse_device = 1; - - /* If not overridden by the user */ - if (!transfer_size) { - transfer_size = libusb_le16_to_cpu( - dfu_root->func_dfu.wTransferSize); - if (transfer_size) { - printf("Device returned transfer size %i\n", - transfer_size); - } else { - errx(EX_IOERR, "Transfer size must be specified"); - } - } - -#ifdef HAVE_GETPAGESIZE -/* autotools lie when cross-compiling for Windows using mingw32/64 */ -#ifndef __MINGW32__ - /* limitation of Linux usbdevio */ - if ((int)transfer_size > getpagesize()) { - transfer_size = getpagesize(); - printf("Limited transfer size to %i\n", transfer_size); - } -#endif /* __MINGW32__ */ -#endif /* HAVE_GETPAGESIZE */ - - if (transfer_size < dfu_root->bMaxPacketSize0) { - transfer_size = dfu_root->bMaxPacketSize0; - printf("Adjusted transfer size to %i\n", transfer_size); - } - - switch (mode) { - case MODE_UPLOAD: - /* open for "exclusive" writing */ - fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); - if (fd < 0) - err(EX_IOERR, "Cannot open file %s for writing", file.name); - - if (dfuse_device || dfuse_options) { - if (dfuse_do_upload(dfu_root, transfer_size, fd, - dfuse_options) < 0) - exit(1); - } else { - if (dfuload_do_upload(dfu_root, transfer_size, - expected_size, fd) < 0) { - exit(1); - } - } - close(fd); - break; - - case MODE_DOWNLOAD: - if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || - (file.idProduct != 0xffff && file.idProduct != runtime_product)) && - ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || - (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { - errx(EX_IOERR, "Error: File ID %04x:%04x does " - "not match device (%04x:%04x or %04x:%04x)", - file.idVendor, file.idProduct, - runtime_vendor, runtime_product, - dfu_root->vendor, dfu_root->product); - } - if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { - if (dfuse_do_dnload(dfu_root, transfer_size, &file, - dfuse_options) < 0) - exit(1); - } else { - if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) - exit(1); - } - break; - case MODE_DETACH: - if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { - warnx("can't detach"); - } - break; - default: - errx(EX_IOERR, "Unsupported mode: %u", mode); - break; - } - - if (final_reset) { - if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { - /* Even if detach failed, just carry on to leave the - device in a known state */ - warnx("can't detach"); - } - printf("Resetting USB to switch back to runtime mode\n"); - ret = libusb_reset_device(dfu_root->dev_handle); - if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { - errx(EX_IOERR, "error resetting after download"); - } - } - - libusb_close(dfu_root->dev_handle); - dfu_root->dev_handle = NULL; - libusb_exit(ctx); - - return (0); -} +/* + * dfu-util + * + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013-2014 Hans Petter Selasky + * + * Written by Harald Welte + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +int verbose = 0; + +struct dfu_if *dfu_root = NULL; + +int match_bus = -1; +int match_device = -1; +int match_vendor = -1; +int match_product = -1; +int match_vendor_dfu = -1; +int match_product_dfu = -1; +int match_config_index = -1; +int match_iface_index = -1; +int match_iface_alt_index = -1; +const char *match_iface_alt_name = NULL; +const char *match_serial = NULL; +const char *match_serial_dfu = NULL; + +static int parse_match_value(const char *str, int default_value) +{ + char *remainder; + int value; + + if (str == NULL) { + value = default_value; + } else if (*str == '*') { + value = -1; /* Match anything */ + } else if (*str == '-') { + value = 0x10000; /* Impossible vendor/product ID */ + } else { + value = strtoul(str, &remainder, 16); + if (remainder == str) { + value = default_value; + } + } + return value; +} + +static void parse_vendprod(const char *str) +{ + const char *comma; + const char *colon; + + /* Default to match any DFU device in runtime or DFU mode */ + match_vendor = -1; + match_product = -1; + match_vendor_dfu = -1; + match_product_dfu = -1; + + comma = strchr(str, ','); + if (comma == str) { + /* DFU mode vendor/product being specified without any runtime + * vendor/product specification, so don't match any runtime device */ + match_vendor = match_product = 0x10000; + } else { + colon = strchr(str, ':'); + if (colon != NULL) { + ++colon; + if ((comma != NULL) && (colon > comma)) { + colon = NULL; + } + } + match_vendor = parse_match_value(str, match_vendor); + match_product = parse_match_value(colon, match_product); + if (comma != NULL) { + /* Both runtime and DFU mode vendor/product specifications are + * available, so default DFU mode match components to the given + * runtime match components */ + match_vendor_dfu = match_vendor; + match_product_dfu = match_product; + } + } + if (comma != NULL) { + ++comma; + colon = strchr(comma, ':'); + if (colon != NULL) { + ++colon; + } + match_vendor_dfu = parse_match_value(comma, match_vendor_dfu); + match_product_dfu = parse_match_value(colon, match_product_dfu); + } +} + +static void parse_serial(char *str) +{ + char *comma; + + match_serial = str; + comma = strchr(str, ','); + if (comma == NULL) { + match_serial_dfu = match_serial; + } else { + *comma++ = 0; + match_serial_dfu = comma; + } + if (*match_serial == 0) match_serial = NULL; + if (*match_serial_dfu == 0) match_serial_dfu = NULL; +} + +#ifdef HAVE_USBPATH_H + +static int resolve_device_path(char *path) +{ + int res; + + res = usb_path2devnum(path); + if (res < 0) + return -EINVAL; + if (!res) + return 0; + + match_bus = atoi(path); + match_device = res; + + return 0; +} + +#else /* HAVE_USBPATH_H */ + +static int resolve_device_path(char *path) +{ + (void)path; /* Eliminate unused variable warning */ + errx(EX_SOFTWARE, "USB device paths are not supported by this dfu-util.\n"); +} + +#endif /* !HAVE_USBPATH_H */ + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-util [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -v --verbose\t\t\tPrint verbose debug statements\n" + " -l --list\t\t\tList currently attached DFU capable devices\n"); + fprintf(stderr, " -e --detach\t\t\tDetach currently attached DFU capable devices\n" + " -E --detach-delay seconds\tTime to wait before reopening a device after detach\n" + " -d --device :[,:]\n" + "\t\t\t\tSpecify Vendor/Product ID(s) of DFU device\n" + " -p --path \tSpecify path to DFU device\n" + " -c --cfg \t\tSpecify the Configuration of DFU device\n" + " -i --intf \t\tSpecify the DFU Interface number\n" + " -S --serial [,]\n" + "\t\t\t\tSpecify Serial String of DFU device\n" + " -a --alt \t\tSpecify the Altsetting of the DFU Interface\n" + "\t\t\t\tby name or by number\n"); + fprintf(stderr, " -t --transfer-size \tSpecify the number of bytes per USB Transfer\n" + " -U --upload \t\tRead firmware from device into \n" + " -Z --upload-size \tSpecify the expected upload size in bytes\n" + " -D --download \t\tWrite firmware from into device\n" + " -R --reset\t\t\tIssue USB Reset signalling once we're finished\n" + " -s --dfuse-address
\tST DfuSe mode, specify target address for\n" + "\t\t\t\traw file download or upload. Not applicable for\n" + "\t\t\t\tDfuSe file (.dfu) downloads\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf(PACKAGE_STRING "\n\n"); + printf("Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\n" + "Copyright 2010-2014 Tormod Volden and Stefan Schmidt\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to " PACKAGE_BUGREPORT "\n\n"); +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "verbose", 0, 0, 'v' }, + { "list", 0, 0, 'l' }, + { "detach", 0, 0, 'e' }, + { "detach-delay", 1, 0, 'E' }, + { "device", 1, 0, 'd' }, + { "path", 1, 0, 'p' }, + { "configuration", 1, 0, 'c' }, + { "cfg", 1, 0, 'c' }, + { "interface", 1, 0, 'i' }, + { "intf", 1, 0, 'i' }, + { "altsetting", 1, 0, 'a' }, + { "alt", 1, 0, 'a' }, + { "serial", 1, 0, 'S' }, + { "transfer-size", 1, 0, 't' }, + { "upload", 1, 0, 'U' }, + { "upload-size", 1, 0, 'Z' }, + { "download", 1, 0, 'D' }, + { "reset", 0, 0, 'R' }, + { "dfuse-address", 1, 0, 's' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int expected_size = 0; + unsigned int transfer_size = 0; + enum mode mode = MODE_NONE; + struct dfu_status status; + libusb_context *ctx; + struct dfu_file file; + char *end; + int final_reset = 0; + int ret; + int dfuse_device = 0; + int fd; + const char *dfuse_options = NULL; + int detach_delay = 5; + uint16_t runtime_vendor; + uint16_t runtime_product; + + memset(&file, 0, sizeof(file)); + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + mode = MODE_VERSION; + break; + case 'v': + verbose++; + break; + case 'l': + mode = MODE_LIST; + break; + case 'e': + mode = MODE_DETACH; + match_iface_alt_index = 0; + match_iface_index = 0; + break; + case 'E': + detach_delay = atoi(optarg); + break; + case 'd': + parse_vendprod(optarg); + break; + case 'p': + /* Parse device path */ + ret = resolve_device_path(optarg); + if (ret < 0) + errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); + if (!ret) + errx(EX_SOFTWARE, "Cannot find '%s'", optarg); + break; + case 'c': + /* Configuration */ + match_config_index = atoi(optarg); + break; + case 'i': + /* Interface */ + match_iface_index = atoi(optarg); + break; + case 'a': + /* Interface Alternate Setting */ + match_iface_alt_index = strtoul(optarg, &end, 0); + if (*end) { + match_iface_alt_name = optarg; + match_iface_alt_index = -1; + } + break; + case 'S': + parse_serial(optarg); + break; + case 't': + transfer_size = atoi(optarg); + break; + case 'U': + mode = MODE_UPLOAD; + file.name = optarg; + break; + case 'Z': + expected_size = atoi(optarg); + break; + case 'D': + mode = MODE_DOWNLOAD; + file.name = optarg; + break; + case 'R': + final_reset = 1; + break; + case 's': + dfuse_options = optarg; + break; + default: + help(); + break; + } + } + + print_version(); + if (mode == MODE_VERSION) { + exit(0); + } + + if (mode == MODE_NONE) { + fprintf(stderr, "You need to specify one of -D or -U\n"); + help(); + } + + if (match_config_index == 0) { + /* Handle "-c 0" (unconfigured device) as don't care */ + match_config_index = -1; + } + + if (mode == MODE_DOWNLOAD) { + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + /* If the user didn't specify product and/or vendor IDs to match, + * use any IDs from the file suffix for device matching */ + if (match_vendor < 0 && file.idVendor != 0xffff) { + match_vendor = file.idVendor; + printf("Match vendor ID from file: %04x\n", match_vendor); + } + if (match_product < 0 && file.idProduct != 0xffff) { + match_product = file.idProduct; + printf("Match product ID from file: %04x\n", match_product); + } + } + + ret = libusb_init(&ctx); + if (ret) + errx(EX_IOERR, "unable to initialize libusb: %i", ret); + + if (verbose > 2) { + libusb_set_debug(ctx, 255); + } + + probe_devices(ctx); + + if (mode == MODE_LIST) { + list_dfu_interfaces(); + exit(0); + } + + if (dfu_root == NULL) { + errx(EX_IOERR, "No DFU capable USB device available"); + } else if (dfu_root->next != NULL) { + /* We cannot safely support more than one DFU capable device + * with same vendor/product ID, since during DFU we need to do + * a USB bus reset, after which the target device will get a + * new address */ + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device\n"); + } + + /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ + + printf("Opening DFU capable USB device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) + errx(EX_IOERR, "Cannot open device"); + + printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); + + printf("Run-time device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + /* Transition from run-Time mode to DFU mode */ + if (!(dfu_root->flags & DFU_IFF_DFU)) { + int err; + /* In the 'first round' during runtime mode, there can only be one + * DFU Interface descriptor according to the DFU Spec. */ + + /* FIXME: check if the selected device really has only one */ + + runtime_vendor = dfu_root->vendor; + runtime_product = dfu_root->product; + + printf("Claiming USB DFU Runtime Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface %d", + dfu_root->interface); + } + + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { + errx(EX_IOERR, "Cannot set alt interface zero"); + } + + printf("Determining device status: "); + + err = dfu_get_status(dfu_root, &status); + if (err == LIBUSB_ERROR_PIPE) { + printf("Device does not implement get_status, assuming appIDLE\n"); + status.bStatus = DFU_STATUS_OK; + status.bwPollTimeout = 0; + status.bState = DFU_STATE_appIDLE; + status.iString = 0; + } else if (err < 0) { + errx(EX_IOERR, "error get_status"); + } else { + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + } + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + printf("Device really in Runtime Mode, send DFU " + "detach request...\n"); + if (dfu_detach(dfu_root->dev_handle, + dfu_root->interface, 1000) < 0) { + warnx("error detaching"); + } + if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { + printf("Device will detach and reattach...\n"); + } else { + printf("Resetting USB...\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) + errx(EX_IOERR, "error resetting " + "after detach"); + } + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, + dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + /* fall through */ + default: + warnx("WARNING: Runtime device already in DFU state ?!?"); + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + goto dfustate; + } + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + + if (mode == MODE_DETACH) { + libusb_exit(ctx); + exit(0); + } + + /* keeping handles open might prevent re-enumeration */ + disconnect_devices(); + + milli_sleep(detach_delay * 1000); + + /* Change match vendor and product to impossible values to force + * only DFU mode matches in the following probe */ + match_vendor = match_product = 0x10000; + + probe_devices(ctx); + + if (dfu_root == NULL) { + errx(EX_IOERR, "Lost device after RESET?"); + } else if (dfu_root->next != NULL) { + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device"); + } + + /* Check for DFU mode device */ + if (!(dfu_root->flags | DFU_IFF_DFU)) + errx(EX_SOFTWARE, "Device is not in DFU mode"); + + printf("Opening DFU USB Device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) { + errx(EX_IOERR, "Cannot open device"); + } + } else { + /* we're already in DFU mode, so we can skip the detach/reset + * procedure */ + /* If a match vendor/product was specified, use that as the runtime + * vendor/product, otherwise use the DFU mode vendor/product */ + runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; + runtime_product = match_product < 0 ? dfu_root->product : match_product; + } + +dfustate: +#if 0 + printf("Setting Configuration %u...\n", dfu_root->configuration); + if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { + errx(EX_IOERR, "Cannot set configuration"); + } +#endif + printf("Claiming USB DFU Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface"); + } + + printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { + errx(EX_IOERR, "Cannot set alternate interface"); + } + +status_again: + printf("Determining device status: "); + if (dfu_get_status(dfu_root, &status ) < 0) { + errx(EX_IOERR, "error get_status"); + } + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + errx(EX_IOERR, "Device still in Runtime Mode!"); + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + goto status_again; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + case DFU_STATE_dfuUPLOAD_IDLE: + printf("aborting previous incomplete transfer\n"); + if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "can't send DFU_ABORT"); + } + goto status_again; + break; + case DFU_STATE_dfuIDLE: + printf("dfuIDLE, continuing\n"); + break; + default: + break; + } + + if (DFU_STATUS_OK != status.bStatus ) { + printf("WARNING: DFU Status: '%s'\n", + dfu_status_to_string(status.bStatus)); + /* Clear our status & try again. */ + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) + errx(EX_IOERR, "USB communication error"); + if (dfu_get_status(dfu_root, &status) < 0) + errx(EX_IOERR, "USB communication error"); + if (DFU_STATUS_OK != status.bStatus) + errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); + + milli_sleep(status.bwPollTimeout); + } + + printf("DFU mode device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) + dfuse_device = 1; + + /* If not overridden by the user */ + if (!transfer_size) { + transfer_size = libusb_le16_to_cpu( + dfu_root->func_dfu.wTransferSize); + if (transfer_size) { + printf("Device returned transfer size %i\n", + transfer_size); + } else { + errx(EX_IOERR, "Transfer size must be specified"); + } + } + +#ifdef HAVE_GETPAGESIZE +/* autotools lie when cross-compiling for Windows using mingw32/64 */ +#ifndef __MINGW32__ + /* limitation of Linux usbdevio */ + if ((int)transfer_size > getpagesize()) { + transfer_size = getpagesize(); + printf("Limited transfer size to %i\n", transfer_size); + } +#endif /* __MINGW32__ */ +#endif /* HAVE_GETPAGESIZE */ + + if (transfer_size < dfu_root->bMaxPacketSize0) { + transfer_size = dfu_root->bMaxPacketSize0; + printf("Adjusted transfer size to %i\n", transfer_size); + } + + switch (mode) { + case MODE_UPLOAD: + /* open for "exclusive" writing */ + fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); + if (fd < 0) + err(EX_IOERR, "Cannot open file %s for writing", file.name); + + if (dfuse_device || dfuse_options) { + if (dfuse_do_upload(dfu_root, transfer_size, fd, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_upload(dfu_root, transfer_size, + expected_size, fd) < 0) { + exit(1); + } + } + close(fd); + break; + + case MODE_DOWNLOAD: + if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || + (file.idProduct != 0xffff && file.idProduct != runtime_product)) && + ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || + (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { + errx(EX_IOERR, "Error: File ID %04x:%04x does " + "not match device (%04x:%04x or %04x:%04x)", + file.idVendor, file.idProduct, + runtime_vendor, runtime_product, + dfu_root->vendor, dfu_root->product); + } + if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { + if (dfuse_do_dnload(dfu_root, transfer_size, &file, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) + exit(1); + } + break; + case MODE_DETACH: + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + warnx("can't detach"); + } + break; + default: + errx(EX_IOERR, "Unsupported mode: %u", mode); + break; + } + + if (final_reset) { + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + /* Even if detach failed, just carry on to leave the + device in a known state */ + warnx("can't detach"); + } + printf("Resetting USB to switch back to runtime mode\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { + errx(EX_IOERR, "error resetting after download"); + } + } + + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + libusb_exit(ctx); + + return (0); +} diff --git a/tools/src/dfu-util/src/portable.h b/tools/linux/src/dfu-util/src/portable.h old mode 100755 new mode 100644 similarity index 95% rename from tools/src/dfu-util/src/portable.h rename to tools/linux/src/dfu-util/src/portable.h index 2609232..cf8d5df --- a/tools/src/dfu-util/src/portable.h +++ b/tools/linux/src/dfu-util/src/portable.h @@ -1,72 +1,72 @@ - -#ifndef PORTABLE_H -#define PORTABLE_H - -#ifdef HAVE_CONFIG_H -# include "config.h" -#else -# define PACKAGE "dfu-util" -# define PACKAGE_VERSION "0.8-msvc" -# define PACKAGE_STRING "dfu-util 0.8-msvc" -# define PACKAGE_BUGREPORT "dfu-util@lists.gnumonks.org" -#endif /* HAVE_CONFIG_H */ - -#ifdef HAVE_FTRUNCATE -# include -#else -# include -#endif /* HAVE_FTRUNCATE */ - -#ifdef HAVE_NANOSLEEP -# include -# define milli_sleep(msec) do {\ - if (msec) {\ - struct timespec nanosleepDelay = { (msec) / 1000, ((msec) % 1000) * 1000000 };\ - nanosleep(&nanosleepDelay, NULL);\ - } } while (0) -#elif defined HAVE_WINDOWS_H -# define milli_sleep(msec) do {\ - if (msec) {\ - Sleep(msec);\ - } } while (0) -#else -# error "Can't get no sleep! Please report" -#endif /* HAVE_NANOSLEEP */ - -#ifdef HAVE_ERR -# include -#else -# include -# include -# define warnx(...) do {\ - fprintf(stderr, __VA_ARGS__);\ - fprintf(stderr, "\n"); } while (0) -# define errx(eval, ...) do {\ - warnx(__VA_ARGS__);\ - exit(eval); } while (0) -# define warn(...) do {\ - fprintf(stderr, "%s: ", strerror(errno));\ - warnx(__VA_ARGS__); } while (0) -# define err(eval, ...) do {\ - warn(__VA_ARGS__);\ - exit(eval); } while (0) -#endif /* HAVE_ERR */ - -#ifdef HAVE_SYSEXITS_H -# include -#else -# define EX_OK 0 /* successful termination */ -# define EX_USAGE 64 /* command line usage error */ -# define EX_SOFTWARE 70 /* internal software error */ -# define EX_IOERR 74 /* input/output error */ -#endif /* HAVE_SYSEXITS_H */ - -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -#ifndef off_t -# define off_t long int -#endif - -#endif /* PORTABLE_H */ + +#ifndef PORTABLE_H +#define PORTABLE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# define PACKAGE "dfu-util" +# define PACKAGE_VERSION "0.8-msvc" +# define PACKAGE_STRING "dfu-util 0.8-msvc" +# define PACKAGE_BUGREPORT "dfu-util@lists.gnumonks.org" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_FTRUNCATE +# include +#else +# include +#endif /* HAVE_FTRUNCATE */ + +#ifdef HAVE_NANOSLEEP +# include +# define milli_sleep(msec) do {\ + if (msec) {\ + struct timespec nanosleepDelay = { (msec) / 1000, ((msec) % 1000) * 1000000 };\ + nanosleep(&nanosleepDelay, NULL);\ + } } while (0) +#elif defined HAVE_WINDOWS_H +# define milli_sleep(msec) do {\ + if (msec) {\ + Sleep(msec);\ + } } while (0) +#else +# error "Can't get no sleep! Please report" +#endif /* HAVE_NANOSLEEP */ + +#ifdef HAVE_ERR +# include +#else +# include +# include +# define warnx(...) do {\ + fprintf(stderr, __VA_ARGS__);\ + fprintf(stderr, "\n"); } while (0) +# define errx(eval, ...) do {\ + warnx(__VA_ARGS__);\ + exit(eval); } while (0) +# define warn(...) do {\ + fprintf(stderr, "%s: ", strerror(errno));\ + warnx(__VA_ARGS__); } while (0) +# define err(eval, ...) do {\ + warn(__VA_ARGS__);\ + exit(eval); } while (0) +#endif /* HAVE_ERR */ + +#ifdef HAVE_SYSEXITS_H +# include +#else +# define EX_OK 0 /* successful termination */ +# define EX_USAGE 64 /* command line usage error */ +# define EX_SOFTWARE 70 /* internal software error */ +# define EX_IOERR 74 /* input/output error */ +#endif /* HAVE_SYSEXITS_H */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifndef off_t +# define off_t long int +#endif + +#endif /* PORTABLE_H */ diff --git a/tools/src/dfu-util/src/prefix.c b/tools/linux/src/dfu-util/src/prefix.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/prefix.c rename to tools/linux/src/dfu-util/src/prefix.c index 05692f9..be8e3fa --- a/tools/src/dfu-util/src/prefix.c +++ b/tools/linux/src/dfu-util/src/prefix.c @@ -1,176 +1,176 @@ -/* - * dfu-prefix - * - * Copyright 2011-2012 Stefan Schmidt - * Copyright 2013 Hans Petter Selasky - * Copyright 2014 Uwe Bonnes - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include "portable.h" -#include "dfu_file.h" - -enum mode { - MODE_NONE, - MODE_ADD, - MODE_DEL, - MODE_CHECK -}; - -int verbose; - -static void help(void) -{ - fprintf(stderr, "Usage: dfu-prefix [options] ...\n" - " -h --help\t\t\tPrint this help message\n" - " -V --version\t\t\tPrint the version number\n" - " -c --check \t\tCheck DFU prefix of \n" - " -D --delete \t\tDelete DFU prefix from \n" - " -a --add \t\tAdd DFU prefix to \n" - "In combination with -a:\n" - ); - fprintf(stderr, " -s --stellaris-address
Add TI Stellaris address prefix to \n" - "In combination with -D or -c:\n" - " -T --stellaris\t\tAct on TI Stellaris address prefix of \n" - "In combination with -a or -D or -c:\n" - " -L --lpc-prefix\t\tUse NXP LPC DFU prefix format\n" - ); - exit(EX_USAGE); -} - -static void print_version(void) -{ - printf("dfu-prefix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); - printf("Copyright 2011-2012 Stefan Schmidt, 2014 Uwe Bonnes\n" - "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" - "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); - -} - -static struct option opts[] = { - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'V' }, - { "check", 1, 0, 'c' }, - { "add", 1, 0, 'a' }, - { "delete", 1, 0, 'D' }, - { "stellaris-address", 1, 0, 's' }, - { "stellaris", 0, 0, 'T' }, - { "LPC", 0, 0, 'L' }, -}; -int main(int argc, char **argv) -{ - struct dfu_file file; - enum mode mode = MODE_NONE; - enum prefix_type type = ZERO_PREFIX; - uint32_t lmdfu_flash_address = 0; - char *end; - - /* make sure all prints are flushed */ - setvbuf(stdout, NULL, _IONBF, 0); - - print_version(); - - memset(&file, 0, sizeof(file)); - - while (1) { - int c, option_index = 0; - c = getopt_long(argc, argv, "hVc:a:D:p:v:d:s:TL", opts, - &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - help(); - break; - case 'V': - exit(0); - break; - case 'D': - file.name = optarg; - mode = MODE_DEL; - break; - case 'c': - file.name = optarg; - mode = MODE_CHECK; - break; - case 'a': - file.name = optarg; - mode = MODE_ADD; - break; - case 's': - lmdfu_flash_address = strtoul(optarg, &end, 0); - if (*end) { - errx(EX_IOERR, "Invalid lmdfu " - "address: %s", optarg); - } - /* fall-through */ - case 'T': - type = LMDFU_PREFIX; - break; - case 'L': - type = LPCDFU_UNENCRYPTED_PREFIX; - break; - default: - help(); - break; - } - } - - if (!file.name) { - fprintf(stderr, "You need to specify a filename\n"); - help(); - } - - switch(mode) { - case MODE_ADD: - if (type == ZERO_PREFIX) - errx(EX_IOERR, "Prefix type must be specified"); - dfu_load_file(&file, MAYBE_SUFFIX, NO_PREFIX); - file.lmdfu_address = lmdfu_flash_address; - file.prefix_type = type; - printf("Adding prefix to file\n"); - dfu_store_file(&file, file.size.suffix != 0, 1); - break; - - case MODE_CHECK: - dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); - show_suffix_and_prefix(&file); - if (type > ZERO_PREFIX && file.prefix_type != type) - errx(EX_IOERR, "No prefix of requested type"); - break; - - case MODE_DEL: - dfu_load_file(&file, MAYBE_SUFFIX, NEEDS_PREFIX); - if (type > ZERO_PREFIX && file.prefix_type != type) - errx(EX_IOERR, "No prefix of requested type"); - printf("Removing prefix from file\n"); - /* if there was a suffix, rewrite it */ - dfu_store_file(&file, file.size.suffix != 0, 0); - break; - - default: - help(); - break; - } - return (0); -} +/* + * dfu-prefix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Uwe Bonnes + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-prefix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU prefix of \n" + " -D --delete \t\tDelete DFU prefix from \n" + " -a --add \t\tAdd DFU prefix to \n" + "In combination with -a:\n" + ); + fprintf(stderr, " -s --stellaris-address
Add TI Stellaris address prefix to \n" + "In combination with -D or -c:\n" + " -T --stellaris\t\tAct on TI Stellaris address prefix of \n" + "In combination with -a or -D or -c:\n" + " -L --lpc-prefix\t\tUse NXP LPC DFU prefix format\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-prefix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2014 Uwe Bonnes\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "stellaris-address", 1, 0, 's' }, + { "stellaris", 0, 0, 'T' }, + { "LPC", 0, 0, 'L' }, +}; +int main(int argc, char **argv) +{ + struct dfu_file file; + enum mode mode = MODE_NONE; + enum prefix_type type = ZERO_PREFIX; + uint32_t lmdfu_flash_address = 0; + char *end; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:s:TL", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + case 's': + lmdfu_flash_address = strtoul(optarg, &end, 0); + if (*end) { + errx(EX_IOERR, "Invalid lmdfu " + "address: %s", optarg); + } + /* fall-through */ + case 'T': + type = LMDFU_PREFIX; + break; + case 'L': + type = LPCDFU_UNENCRYPTED_PREFIX; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + if (type == ZERO_PREFIX) + errx(EX_IOERR, "Prefix type must be specified"); + dfu_load_file(&file, MAYBE_SUFFIX, NO_PREFIX); + file.lmdfu_address = lmdfu_flash_address; + file.prefix_type = type; + printf("Adding prefix to file\n"); + dfu_store_file(&file, file.size.suffix != 0, 1); + break; + + case MODE_CHECK: + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + break; + + case MODE_DEL: + dfu_load_file(&file, MAYBE_SUFFIX, NEEDS_PREFIX); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + printf("Removing prefix from file\n"); + /* if there was a suffix, rewrite it */ + dfu_store_file(&file, file.size.suffix != 0, 0); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/src/dfu-util/src/quirks.c b/tools/linux/src/dfu-util/src/quirks.c old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/quirks.c rename to tools/linux/src/dfu-util/src/quirks.c index c90e585..de394a6 --- a/tools/src/dfu-util/src/quirks.c +++ b/tools/linux/src/dfu-util/src/quirks.c @@ -1,56 +1,56 @@ -/* - * Simple quirk system for dfu-util - * - * Copyright 2010-2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include "quirks.h" - -uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) -{ - uint16_t quirks = 0; - - /* Device returns bogus bwPollTimeout values */ - if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) && - product >= PRODUCT_FREERUNNER_FIRST && - product <= PRODUCT_FREERUNNER_LAST) - quirks |= QUIRK_POLLTIMEOUT; - - if (vendor == VENDOR_VOTI && - product == PRODUCT_OPENPCD) - quirks |= QUIRK_POLLTIMEOUT; - - /* Reports wrong DFU version in DFU descriptor */ - if (vendor == VENDOR_LEAFLABS && - product == PRODUCT_MAPLE3 && - bcdDevice == 0x0200) - quirks |= QUIRK_FORCE_DFU11; - - /* old devices(bcdDevice == 0) return bogus bwPollTimeout values */ - if (vendor == VENDOR_SIEMENS && - (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) && - bcdDevice == 0) - quirks |= QUIRK_POLLTIMEOUT; - - /* M-Audio Transit returns bogus bwPollTimeout values */ - if (vendor == VENDOR_MIDIMAN && - product == PRODUCT_TRANSIT) - quirks |= QUIRK_POLLTIMEOUT; - - return (quirks); -} +/* + * Simple quirk system for dfu-util + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "quirks.h" + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) +{ + uint16_t quirks = 0; + + /* Device returns bogus bwPollTimeout values */ + if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) && + product >= PRODUCT_FREERUNNER_FIRST && + product <= PRODUCT_FREERUNNER_LAST) + quirks |= QUIRK_POLLTIMEOUT; + + if (vendor == VENDOR_VOTI && + product == PRODUCT_OPENPCD) + quirks |= QUIRK_POLLTIMEOUT; + + /* Reports wrong DFU version in DFU descriptor */ + if (vendor == VENDOR_LEAFLABS && + product == PRODUCT_MAPLE3 && + bcdDevice == 0x0200) + quirks |= QUIRK_FORCE_DFU11; + + /* old devices(bcdDevice == 0) return bogus bwPollTimeout values */ + if (vendor == VENDOR_SIEMENS && + (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) && + bcdDevice == 0) + quirks |= QUIRK_POLLTIMEOUT; + + /* M-Audio Transit returns bogus bwPollTimeout values */ + if (vendor == VENDOR_MIDIMAN && + product == PRODUCT_TRANSIT) + quirks |= QUIRK_POLLTIMEOUT; + + return (quirks); +} diff --git a/tools/src/dfu-util/src/quirks.h b/tools/linux/src/dfu-util/src/quirks.h old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/src/quirks.h rename to tools/linux/src/dfu-util/src/quirks.h index 3193f25..0e4b3ec --- a/tools/src/dfu-util/src/quirks.h +++ b/tools/linux/src/dfu-util/src/quirks.h @@ -1,27 +1,27 @@ -#ifndef DFU_QUIRKS_H -#define DFU_QUIRKS_H - -#define VENDOR_OPENMOKO 0x1d50 /* Openmoko Freerunner / GTA02 */ -#define VENDOR_FIC 0x1457 /* Openmoko Freerunner / GTA02 */ -#define VENDOR_VOTI 0x16c0 /* OpenPCD Reader */ -#define VENDOR_LEAFLABS 0x1eaf /* Maple */ -#define VENDOR_SIEMENS 0x0908 /* Siemens AG */ -#define VENDOR_MIDIMAN 0x0763 /* Midiman */ - -#define PRODUCT_FREERUNNER_FIRST 0x5117 -#define PRODUCT_FREERUNNER_LAST 0x5126 -#define PRODUCT_OPENPCD 0x076b -#define PRODUCT_MAPLE3 0x0003 /* rev 3 and 5 */ -#define PRODUCT_PXM40 0x02c4 /* Siemens AG, PXM 40 */ -#define PRODUCT_PXM50 0x02c5 /* Siemens AG, PXM 50 */ -#define PRODUCT_TRANSIT 0x2806 /* M-Audio Transit (Midiman) */ - -#define QUIRK_POLLTIMEOUT (1<<0) -#define QUIRK_FORCE_DFU11 (1<<1) - -/* Fallback value, works for OpenMoko */ -#define DEFAULT_POLLTIMEOUT 5 - -uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice); - -#endif /* DFU_QUIRKS_H */ +#ifndef DFU_QUIRKS_H +#define DFU_QUIRKS_H + +#define VENDOR_OPENMOKO 0x1d50 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_FIC 0x1457 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_VOTI 0x16c0 /* OpenPCD Reader */ +#define VENDOR_LEAFLABS 0x1eaf /* Maple */ +#define VENDOR_SIEMENS 0x0908 /* Siemens AG */ +#define VENDOR_MIDIMAN 0x0763 /* Midiman */ + +#define PRODUCT_FREERUNNER_FIRST 0x5117 +#define PRODUCT_FREERUNNER_LAST 0x5126 +#define PRODUCT_OPENPCD 0x076b +#define PRODUCT_MAPLE3 0x0003 /* rev 3 and 5 */ +#define PRODUCT_PXM40 0x02c4 /* Siemens AG, PXM 40 */ +#define PRODUCT_PXM50 0x02c5 /* Siemens AG, PXM 50 */ +#define PRODUCT_TRANSIT 0x2806 /* M-Audio Transit (Midiman) */ + +#define QUIRK_POLLTIMEOUT (1<<0) +#define QUIRK_FORCE_DFU11 (1<<1) + +/* Fallback value, works for OpenMoko */ +#define DEFAULT_POLLTIMEOUT 5 + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice); + +#endif /* DFU_QUIRKS_H */ diff --git a/tools/src/dfu-util/src/suffix.c b/tools/linux/src/dfu-util/src/suffix.c old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/suffix.c rename to tools/linux/src/dfu-util/src/suffix.c index ba02a01..0df248f --- a/tools/src/dfu-util/src/suffix.c +++ b/tools/linux/src/dfu-util/src/suffix.c @@ -1,176 +1,176 @@ -/* - * dfu-suffix - * - * Copyright 2011-2012 Stefan Schmidt - * Copyright 2013 Hans Petter Selasky - * Copyright 2014 Tormod Volden - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include "portable.h" -#include "dfu_file.h" - -enum mode { - MODE_NONE, - MODE_ADD, - MODE_DEL, - MODE_CHECK -}; - -int verbose; - -static void help(void) -{ - fprintf(stderr, "Usage: dfu-suffix [options] ...\n" - " -h --help\t\t\tPrint this help message\n" - " -V --version\t\t\tPrint the version number\n" - " -c --check \t\tCheck DFU suffix of \n" - " -a --add \t\tAdd DFU suffix to \n" - " -D --delete \t\tDelete DFU suffix from \n" - " -p --pid \t\tAdd product ID into DFU suffix in \n" - " -v --vid \t\tAdd vendor ID into DFU suffix in \n" - " -d --did \t\tAdd device ID into DFU suffix in \n" - " -S --spec \t\tAdd DFU specification ID into DFU suffix in \n" - ); - exit(EX_USAGE); -} - -static void print_version(void) -{ - printf("dfu-suffix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); - printf("Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden\n" - "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" - "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); - -} - -static struct option opts[] = { - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'V' }, - { "check", 1, 0, 'c' }, - { "add", 1, 0, 'a' }, - { "delete", 1, 0, 'D' }, - { "pid", 1, 0, 'p' }, - { "vid", 1, 0, 'v' }, - { "did", 1, 0, 'd' }, - { "spec", 1, 0, 'S' }, -}; - -int main(int argc, char **argv) -{ - struct dfu_file file; - int pid, vid, did, spec; - enum mode mode = MODE_NONE; - - /* make sure all prints are flushed */ - setvbuf(stdout, NULL, _IONBF, 0); - - print_version(); - - pid = vid = did = 0xffff; - spec = 0x0100; /* Default to bcdDFU version 1.0 */ - memset(&file, 0, sizeof(file)); - - while (1) { - int c, option_index = 0; - c = getopt_long(argc, argv, "hVc:a:D:p:v:d:S:s:T", opts, - &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - help(); - break; - case 'V': - exit(0); - break; - case 'D': - file.name = optarg; - mode = MODE_DEL; - break; - case 'p': - pid = strtol(optarg, NULL, 16); - break; - case 'v': - vid = strtol(optarg, NULL, 16); - break; - case 'd': - did = strtol(optarg, NULL, 16); - break; - case 'S': - spec = strtol(optarg, NULL, 16); - break; - case 'c': - file.name = optarg; - mode = MODE_CHECK; - break; - case 'a': - file.name = optarg; - mode = MODE_ADD; - break; - default: - help(); - break; - } - } - - if (!file.name) { - fprintf(stderr, "You need to specify a filename\n"); - help(); - } - - if (spec != 0x0100 && spec != 0x011a) { - fprintf(stderr, "Only DFU specification 0x0100 and 0x011a supported\n"); - help(); - } - - switch(mode) { - case MODE_ADD: - dfu_load_file(&file, NO_SUFFIX, MAYBE_PREFIX); - file.idVendor = vid; - file.idProduct = pid; - file.bcdDevice = did; - file.bcdDFU = spec; - /* always write suffix, rewrite prefix if there was one */ - dfu_store_file(&file, 1, file.size.prefix != 0); - printf("Suffix successfully added to file\n"); - break; - - case MODE_CHECK: - dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); - show_suffix_and_prefix(&file); - break; - - case MODE_DEL: - dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); - dfu_store_file(&file, 0, file.size.prefix != 0); - if (file.size.suffix) /* had a suffix */ - printf("Suffix successfully removed from file\n"); - break; - - default: - help(); - break; - } - return (0); -} +/* + * dfu-suffix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-suffix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU suffix of \n" + " -a --add \t\tAdd DFU suffix to \n" + " -D --delete \t\tDelete DFU suffix from \n" + " -p --pid \t\tAdd product ID into DFU suffix in \n" + " -v --vid \t\tAdd vendor ID into DFU suffix in \n" + " -d --did \t\tAdd device ID into DFU suffix in \n" + " -S --spec \t\tAdd DFU specification ID into DFU suffix in \n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-suffix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "pid", 1, 0, 'p' }, + { "vid", 1, 0, 'v' }, + { "did", 1, 0, 'd' }, + { "spec", 1, 0, 'S' }, +}; + +int main(int argc, char **argv) +{ + struct dfu_file file; + int pid, vid, did, spec; + enum mode mode = MODE_NONE; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + pid = vid = did = 0xffff; + spec = 0x0100; /* Default to bcdDFU version 1.0 */ + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:S:s:T", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'p': + pid = strtol(optarg, NULL, 16); + break; + case 'v': + vid = strtol(optarg, NULL, 16); + break; + case 'd': + did = strtol(optarg, NULL, 16); + break; + case 'S': + spec = strtol(optarg, NULL, 16); + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + if (spec != 0x0100 && spec != 0x011a) { + fprintf(stderr, "Only DFU specification 0x0100 and 0x011a supported\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + dfu_load_file(&file, NO_SUFFIX, MAYBE_PREFIX); + file.idVendor = vid; + file.idProduct = pid; + file.bcdDevice = did; + file.bcdDFU = spec; + /* always write suffix, rewrite prefix if there was one */ + dfu_store_file(&file, 1, file.size.prefix != 0); + printf("Suffix successfully added to file\n"); + break; + + case MODE_CHECK: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + break; + + case MODE_DEL: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + dfu_store_file(&file, 0, file.size.prefix != 0); + if (file.size.suffix) /* had a suffix */ + printf("Suffix successfully removed from file\n"); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/src/dfu-util/src/usb_dfu.h b/tools/linux/src/dfu-util/src/usb_dfu.h old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/src/usb_dfu.h rename to tools/linux/src/dfu-util/src/usb_dfu.h index f6fe270..660bedc --- a/tools/src/dfu-util/src/usb_dfu.h +++ b/tools/linux/src/dfu-util/src/usb_dfu.h @@ -1,99 +1,99 @@ -#ifndef USB_DFU_H -#define USB_DFU_H -/* USB Device Firmware Update Implementation for OpenPCD - * (C) 2006 by Harald Welte - * - * Protocol definitions for USB DFU - * - * This ought to be compliant to the USB DFU Spec 1.0 as available from - * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include - -#define USB_DT_DFU 0x21 - -#ifdef _MSC_VER -# pragma pack(push) -# pragma pack(1) -#endif /* _MSC_VER */ -struct usb_dfu_func_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bmAttributes; -#define USB_DFU_CAN_DOWNLOAD (1 << 0) -#define USB_DFU_CAN_UPLOAD (1 << 1) -#define USB_DFU_MANIFEST_TOL (1 << 2) -#define USB_DFU_WILL_DETACH (1 << 3) - uint16_t wDetachTimeOut; - uint16_t wTransferSize; - uint16_t bcdDFUVersion; -#ifdef _MSC_VER -}; -# pragma pack(pop) -#elif defined __GNUC__ -} __attribute__ ((packed)); -#else - #warning "No way to pack struct on this compiler? This will break!" -#endif /* _MSC_VER */ - -#define USB_DT_DFU_SIZE 9 - -#define USB_TYPE_DFU (LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE) - -/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ -#define USB_REQ_DFU_DETACH 0x00 -#define USB_REQ_DFU_DNLOAD 0x01 -#define USB_REQ_DFU_UPLOAD 0x02 -#define USB_REQ_DFU_GETSTATUS 0x03 -#define USB_REQ_DFU_CLRSTATUS 0x04 -#define USB_REQ_DFU_GETSTATE 0x05 -#define USB_REQ_DFU_ABORT 0x06 - -/* DFU_GETSTATUS bStatus values (Section 6.1.2, DFU Rev 1.1) */ -#define DFU_STATUS_OK 0x00 -#define DFU_STATUS_errTARGET 0x01 -#define DFU_STATUS_errFILE 0x02 -#define DFU_STATUS_errWRITE 0x03 -#define DFU_STATUS_errERASE 0x04 -#define DFU_STATUS_errCHECK_ERASED 0x05 -#define DFU_STATUS_errPROG 0x06 -#define DFU_STATUS_errVERIFY 0x07 -#define DFU_STATUS_errADDRESS 0x08 -#define DFU_STATUS_errNOTDONE 0x09 -#define DFU_STATUS_errFIRMWARE 0x0a -#define DFU_STATUS_errVENDOR 0x0b -#define DFU_STATUS_errUSBR 0x0c -#define DFU_STATUS_errPOR 0x0d -#define DFU_STATUS_errUNKNOWN 0x0e -#define DFU_STATUS_errSTALLEDPKT 0x0f - -enum dfu_state { - DFU_STATE_appIDLE = 0, - DFU_STATE_appDETACH = 1, - DFU_STATE_dfuIDLE = 2, - DFU_STATE_dfuDNLOAD_SYNC = 3, - DFU_STATE_dfuDNBUSY = 4, - DFU_STATE_dfuDNLOAD_IDLE = 5, - DFU_STATE_dfuMANIFEST_SYNC = 6, - DFU_STATE_dfuMANIFEST = 7, - DFU_STATE_dfuMANIFEST_WAIT_RST = 8, - DFU_STATE_dfuUPLOAD_IDLE = 9, - DFU_STATE_dfuERROR = 10 -}; - -#endif /* USB_DFU_H */ +#ifndef USB_DFU_H +#define USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * Protocol definitions for USB DFU + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define USB_DT_DFU 0x21 + +#ifdef _MSC_VER +# pragma pack(push) +# pragma pack(1) +#endif /* _MSC_VER */ +struct usb_dfu_func_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bmAttributes; +#define USB_DFU_CAN_DOWNLOAD (1 << 0) +#define USB_DFU_CAN_UPLOAD (1 << 1) +#define USB_DFU_MANIFEST_TOL (1 << 2) +#define USB_DFU_WILL_DETACH (1 << 3) + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +#ifdef _MSC_VER +}; +# pragma pack(pop) +#elif defined __GNUC__ +} __attribute__ ((packed)); +#else + #warning "No way to pack struct on this compiler? This will break!" +#endif /* _MSC_VER */ + +#define USB_DT_DFU_SIZE 9 + +#define USB_TYPE_DFU (LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE) + +/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +/* DFU_GETSTATUS bStatus values (Section 6.1.2, DFU Rev 1.1) */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10 +}; + +#endif /* USB_DFU_H */ diff --git a/tools/src/dfu-util/www/build.html b/tools/linux/src/dfu-util/www/build.html old mode 100755 new mode 100644 similarity index 96% rename from tools/src/dfu-util/www/build.html rename to tools/linux/src/dfu-util/www/build.html index 2e6de0c..f3036e4 --- a/tools/src/dfu-util/www/build.html +++ b/tools/linux/src/dfu-util/www/build.html @@ -1,147 +1,147 @@ - - - - - - - Building dfu-util from source - - - - - - - - - -
-

How to build dfu-util from source

- -

Prerequisites for building from git

-

Mac OS X

-

-First install MacPorts (and if you are on 10.6 or older, the Java Developer Package) and then run: -

-
-	sudo port install libusb-devel git-core
-
- -

FreeBSD

-
-	sudo pkg_add -r git pkgconf
-
- -

Ubuntu and Debian and derivatives

-
-	sudo apt-get build-dep dfu-util
-	sudo apt-get install libusb-1.0-0-dev
-
- -

Get the source code and build it

-

-The first time you will have to clone the git repository: -

-
-	git clone git://gitorious.org/dfu-util/dfu-util.git
-	cd dfu-util
-
-

-If you later want to update to latest git version, just run this: -

-
-	make maintainer-clean
-	git pull
-
-

-To build the source: -

-
-	./autogen.sh
-	./configure  # on most systems
-	make
-
- -

-If you are building on Mac OS X, replace the ./configure command with: -

-
-	./configure --libdir=/opt/local/lib --includedir=/opt/local/include  # on MacOSX only
-
- -

-Your dfu-util binary will be inside the src folder. Use it from there, or install it to /usr/local/bin by running "sudo make install". -

- -

Cross-building for Windows

- -

-Windows binaries can be built in a MinGW -environment, on a Windows computer or cross-hosted in another OS. -To build it on a Debian or Ubuntu host, first install build dependencies: -

-
-	sudo apt-get build-dep libusb-1.0-0 dfu-util
-	sudo apt-get install mingw32
-
- -

-The below example builds dfu-util 0.8 and libusb 1.0.19 from unpacked release -tarballs. If you instead build from git, you will have to run "./autogen.sh" -before running the "./configure" steps. -

- -
-mkdir -p build
-cd libusb-1.0.19
-PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
-    --host=i586-mingw32msvc --prefix=$PWD/../build
-# WINVER workaround needed for 1.0.19 only
-make CFLAGS="-DWINVER=0x0501"
-make install
-cd ..
-
-cd dfu-util-0.8
-PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
-    --host=i586-mingw32msvc --prefix=$PWD/../build
-make
-make install
-cd ..
-
-The build files will now be in build/bin. -

- -

Building on Windows using MinGW

-This assumes using release tarballs or having run ./autogen.sh on -the git sources. -
-cd libusb-1.0.19
-./configure --prefix=$HOME
-# WINVER workaround needed for 1.0.19 only
-# MKDIR_P setting should not really be needed...
-make CFLAGS="-DWINVER=0x0501" MKDIR_P="mkdir -p"
-make install
-cd ..
-
-cd dfu-util-0.8
-./configure USB_CFLAGS="-I$HOME/include/libusb-1.0" \
-            USB_LIBS="-L $HOME/lib -lusb-1.0" PKG_CONFIG=true
-make
-make install
-cd ..
-
-To link libusb statically into dfu-util.exe use instead of "make": -
-make LDFLAGS=-static
-
-The built executables (and DLL) will now be under $HOME/bin. - -

-[Back to dfu-util main page] -

- -
- - + + + + + + + Building dfu-util from source + + + + + + + + + +
+

How to build dfu-util from source

+ +

Prerequisites for building from git

+

Mac OS X

+

+First install MacPorts (and if you are on 10.6 or older, the Java Developer Package) and then run: +

+
+	sudo port install libusb-devel git-core
+
+ +

FreeBSD

+
+	sudo pkg_add -r git pkgconf
+
+ +

Ubuntu and Debian and derivatives

+
+	sudo apt-get build-dep dfu-util
+	sudo apt-get install libusb-1.0-0-dev
+
+ +

Get the source code and build it

+

+The first time you will have to clone the git repository: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	cd dfu-util
+
+

+If you later want to update to latest git version, just run this: +

+
+	make maintainer-clean
+	git pull
+
+

+To build the source: +

+
+	./autogen.sh
+	./configure  # on most systems
+	make
+
+ +

+If you are building on Mac OS X, replace the ./configure command with: +

+
+	./configure --libdir=/opt/local/lib --includedir=/opt/local/include  # on MacOSX only
+
+ +

+Your dfu-util binary will be inside the src folder. Use it from there, or install it to /usr/local/bin by running "sudo make install". +

+ +

Cross-building for Windows

+ +

+Windows binaries can be built in a MinGW +environment, on a Windows computer or cross-hosted in another OS. +To build it on a Debian or Ubuntu host, first install build dependencies: +

+
+	sudo apt-get build-dep libusb-1.0-0 dfu-util
+	sudo apt-get install mingw32
+
+ +

+The below example builds dfu-util 0.8 and libusb 1.0.19 from unpacked release +tarballs. If you instead build from git, you will have to run "./autogen.sh" +before running the "./configure" steps. +

+ +
+mkdir -p build
+cd libusb-1.0.19
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+# WINVER workaround needed for 1.0.19 only
+make CFLAGS="-DWINVER=0x0501"
+make install
+cd ..
+
+cd dfu-util-0.8
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+make
+make install
+cd ..
+
+The build files will now be in build/bin. +

+ +

Building on Windows using MinGW

+This assumes using release tarballs or having run ./autogen.sh on +the git sources. +
+cd libusb-1.0.19
+./configure --prefix=$HOME
+# WINVER workaround needed for 1.0.19 only
+# MKDIR_P setting should not really be needed...
+make CFLAGS="-DWINVER=0x0501" MKDIR_P="mkdir -p"
+make install
+cd ..
+
+cd dfu-util-0.8
+./configure USB_CFLAGS="-I$HOME/include/libusb-1.0" \
+            USB_LIBS="-L $HOME/lib -lusb-1.0" PKG_CONFIG=true
+make
+make install
+cd ..
+
+To link libusb statically into dfu-util.exe use instead of "make": +
+make LDFLAGS=-static
+
+The built executables (and DLL) will now be under $HOME/bin. + +

+[Back to dfu-util main page] +

+ +
+ + diff --git a/tools/src/dfu-util/www/dfu-util.1.html b/tools/linux/src/dfu-util/www/dfu-util.1.html old mode 100755 new mode 100644 similarity index 95% rename from tools/src/dfu-util/www/dfu-util.1.html rename to tools/linux/src/dfu-util/www/dfu-util.1.html index 667a369..62ca40b --- a/tools/src/dfu-util/www/dfu-util.1.html +++ b/tools/linux/src/dfu-util/www/dfu-util.1.html @@ -1,411 +1,411 @@ - - -Man page of DFU-UTIL - - -

DFU-UTIL(1)

- -  -

NAME

- -dfu-util - Device firmware update (DFU) USB programmer -  -

SYNOPSIS

- - -
-
-dfu-util - --l - -[-v] - -[-d - -vid:pid[,vid:pid]] - -[-p - -path] - -[-c - -configuration] - -[-i - -interface] - -[-a - -alt-intf] - -[-S - -serial[,serial]] - - -
-dfu-util - -[-v] - -[-d - -vid:pid[,vid:pid]] - -[-p - -path] - -[-c - -configuration] - -[-i - -interface] - -[-a - -alt-intf] - -[-S - -serial[,serial]] - -[-t - -size] - -[-Z - -size] - -[-s - -address] - -[-R] - -[-D|-U - -file] - - -
-dfu-util - -[-hV] - -
-  -

DESCRIPTION

- -dfu-util - -is a program that implements the host (computer) side of the USB DFU -(Universal Serial Bus Device Firmware Upgrade) protocol. -

-dfu-util communicates with devices that implement the device side of the -USB DFU protocol, and is often used to upgrade the firmware of such -devices. -  -

OPTIONS

- -
-
-l, --list - -
-List the currently attached DFU capable USB devices. -
-d, --device [Run-Time VENDOR]:[Run-Time PRODUCT][,[DFU Mode VENDOR]:[DFU Mode PRODUCT]] - -
-
-Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device -to work with. VENDOR and PRODUCT are hexadecimal numbers (no prefix -needed), "*" (match any), or "-" (match nothing). By default, any DFU capable -device in either run-time or DFU mode will be considered. -

-If you only have one standards-compliant DFU device attached to your computer, -this parameter is optional. However, as soon as you have multiple DFU devices -connected, dfu-util will detect this and abort, asking you to specify which -device to use. -

-If only run-time IDs are specified (e.g. "--device 1457:51ab"), then in -addition to the specified run-time IDs, any DFU mode devices will also be -considered. This is beneficial to allow a DFU capable device to be found -again after a switch to DFU mode, since the vendor and/or product ID of a -device usually changes in DFU mode. -

-If only DFU mode IDs are specified (e.g. "--device ,951:26"), then all -run-time devices will be ignored, making it easy to target a specific device in -DFU mode. -

-If both run-time and DFU mode IDs are specified (e.g. "--device -1457:51ab,:2bc"), then unspecified DFU mode components will use the run-time -value specified. -

-Examples: -

-
--device 1457:51ab,951:26 - -
-
- -Work with a device in run-time mode with -vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 -and product ID 0x0026 -

-

--device 1457:51ab,:2bc - -
-
- -Work with a device in run-time mode with vendor ID 0x1457 and product ID -0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc -

-

--device 1457:51ab - -
-
- -Work with a device in run-time mode with vendor ID 0x1457 and product ID -0x51ab, or in DFU mode with any vendor and product ID -

-

--device ,951:26 - -
-
- -Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 -

-

--device *,- - -
-
- -Work with any device in run-time mode, and ignore any device in DFU mode -

-

--device , - -
-
- -Ignore any device in run-time mode, and Work with any device in DFU mode -
-
- -
-p, --path BUS-PORT. ... .PORT - -
-Specify the path to the DFU device. -
-c, --cfg CONFIG-NR - -
-Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. -
-i, --intf INTF-NR - -
-Specify the DFU interface number. -
-a, --alt ALT - -
-Specify the altsetting of the DFU interface by name or by number. -
-S, --serial [Run-Time SERIAL][,[DFU Mode SERIAL]] - -
-Specify the run-time and DFU mode serial numbers used to further restrict -device matches. If multiple, identical DFU devices are simultaneously -connected to a system then vendor and product ID will be insufficient for -targeting a single device. In this situation, it may be possible to use this -parameter to specify a serial number which also must match. -

-If only a single serial number is specified, then the same serial number is -used in both run-time and DFU mode. An empty serial number will match any -serial number in the corresponding mode. -

-t, --transfer-size SIZE - -
-Specify the number of bytes per USB transfer. The optimal value is -usually determined automatically so this option is rarely useful. If -you need to use this option for a device, please report it as a bug. -
-Z, --upload-size SIZE - -
-Specify the expected upload size, in bytes. -
-U, --upload FILE - -
-Read firmware from device into -FILE. - -
-D, --download FILE - -
-Write firmware from -FILE - -into device. -
-R, --reset - -
-Issue USB reset signalling after upload or download has finished. -
-s, --dfuse-address address - -
-Specify target address for raw binary download/upload on DfuSe devices. Do -not - -use this for downloading DfuSe (.dfu) files. Modifiers can be added -to the address, separated by a colon, to perform special DfuSE commands such -as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. -
-v, --verbose - -
-Print more information about dfu-util's operation. A second --v - -will turn on verbose logging of USB requests. Repeat this option to further -increase verbosity. -
-h, --help - -
-Show a help text and exit. -
-V, --version - -
-Show version information and exit. -
-  -

EXAMPLES

- -  -

Using dfu-util in the OpenMoko project

- -(with the Neo1973 hardware) -

- -Flashing the rootfs: -
- - $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2 - -

- -Flashing the kernel: -
- - $ dfu-util -a kernel -R -D /path/to/uImage - -

- -Flashing the bootloader: -
- - $ dfu-util -a u-boot -R -D /path/to/u-boot.bin - -

- -Copying a kernel into RAM: -
- - $ dfu-util -a 0 -R -D /path/to/uImage - -

-Once this has finished, the kernel will be available at the default load -address of 0x32000000 in Neo1973 RAM. -Note: - -You cannot transfer more than 2MB of data into RAM using this method. -

-  -

Using dfu-util with a DfuSe device

- -

- -Flashing a -.dfu - -(special DfuSe format) file to the device: -
- - $ dfu-util -a 0 -D /path/to/dfuse-image.dfu - -

- -Reading out 1 KB of flash starting at address 0x8000000: -
- - $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin - -

- -Flashing a binary file to address 0x8004000 of device memory and -ask the device to leave DFU mode: -
- - $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin - - -  -

BUGS

- -Please report any bugs to the dfu-util mailing list at -dfu-util@lists.gnumonks.org. - -Please use the ---verbose option (repeated as necessary) to provide more - -information in your bug report. -  -

SEE ALSO

- -The dfu-util home page is -http://dfu-util.gnumonks.org - -  -

HISTORY

- -dfu-util was originally written for the OpenMoko project by -Weston Schmidt <weston_schmidt@yahoo.com> and -Harald Welte <hwelte@hmw-consulting.de>. Over time, nearly complete -support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. -  -

LICENCE

- -dfu-util - -is covered by the GNU General Public License (GPL), version 2 or later. -  -

COPYRIGHT

- -This manual page was originally written by Uwe Hermann <uwe@hermann-uwe.de>, -and is now part of the dfu-util project. -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
OPTIONS
-
EXAMPLES
-
-
Using dfu-util in the OpenMoko project
-
Using dfu-util with a DfuSe device
-
-
BUGS
-
SEE ALSO
-
HISTORY
-
LICENCE
-
COPYRIGHT
-
-
-This document was created by man2html, -using the doc/dfu-util.1 manual page from dfu-util 0.8.
-Time: 14:40:57 GMT, September 13, 2014 - - + + +Man page of DFU-UTIL + + +

DFU-UTIL(1)

+ +  +

NAME

+ +dfu-util - Device firmware update (DFU) USB programmer +  +

SYNOPSIS

+ + +
+
+dfu-util + +-l + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + + +
+dfu-util + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + +[-t + +size] + +[-Z + +size] + +[-s + +address] + +[-R] + +[-D|-U + +file] + + +
+dfu-util + +[-hV] + +
+  +

DESCRIPTION

+ +dfu-util + +is a program that implements the host (computer) side of the USB DFU +(Universal Serial Bus Device Firmware Upgrade) protocol. +

+dfu-util communicates with devices that implement the device side of the +USB DFU protocol, and is often used to upgrade the firmware of such +devices. +  +

OPTIONS

+ +
+
-l, --list + +
+List the currently attached DFU capable USB devices. +
-d, --device [Run-Time VENDOR]:[Run-Time PRODUCT][,[DFU Mode VENDOR]:[DFU Mode PRODUCT]] + +
+
+Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device +to work with. VENDOR and PRODUCT are hexadecimal numbers (no prefix +needed), "*" (match any), or "-" (match nothing). By default, any DFU capable +device in either run-time or DFU mode will be considered. +

+If you only have one standards-compliant DFU device attached to your computer, +this parameter is optional. However, as soon as you have multiple DFU devices +connected, dfu-util will detect this and abort, asking you to specify which +device to use. +

+If only run-time IDs are specified (e.g. "--device 1457:51ab"), then in +addition to the specified run-time IDs, any DFU mode devices will also be +considered. This is beneficial to allow a DFU capable device to be found +again after a switch to DFU mode, since the vendor and/or product ID of a +device usually changes in DFU mode. +

+If only DFU mode IDs are specified (e.g. "--device ,951:26"), then all +run-time devices will be ignored, making it easy to target a specific device in +DFU mode. +

+If both run-time and DFU mode IDs are specified (e.g. "--device +1457:51ab,:2bc"), then unspecified DFU mode components will use the run-time +value specified. +

+Examples: +

+
--device 1457:51ab,951:26 + +
+
+ +Work with a device in run-time mode with +vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 +and product ID 0x0026 +

+

--device 1457:51ab,:2bc + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc +

+

--device 1457:51ab + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with any vendor and product ID +

+

--device ,951:26 + +
+
+ +Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 +

+

--device *,- + +
+
+ +Work with any device in run-time mode, and ignore any device in DFU mode +

+

--device , + +
+
+ +Ignore any device in run-time mode, and Work with any device in DFU mode +
+
+ +
-p, --path BUS-PORT. ... .PORT + +
+Specify the path to the DFU device. +
-c, --cfg CONFIG-NR + +
+Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. +
-i, --intf INTF-NR + +
+Specify the DFU interface number. +
-a, --alt ALT + +
+Specify the altsetting of the DFU interface by name or by number. +
-S, --serial [Run-Time SERIAL][,[DFU Mode SERIAL]] + +
+Specify the run-time and DFU mode serial numbers used to further restrict +device matches. If multiple, identical DFU devices are simultaneously +connected to a system then vendor and product ID will be insufficient for +targeting a single device. In this situation, it may be possible to use this +parameter to specify a serial number which also must match. +

+If only a single serial number is specified, then the same serial number is +used in both run-time and DFU mode. An empty serial number will match any +serial number in the corresponding mode. +

-t, --transfer-size SIZE + +
+Specify the number of bytes per USB transfer. The optimal value is +usually determined automatically so this option is rarely useful. If +you need to use this option for a device, please report it as a bug. +
-Z, --upload-size SIZE + +
+Specify the expected upload size, in bytes. +
-U, --upload FILE + +
+Read firmware from device into +FILE. + +
-D, --download FILE + +
+Write firmware from +FILE + +into device. +
-R, --reset + +
+Issue USB reset signalling after upload or download has finished. +
-s, --dfuse-address address + +
+Specify target address for raw binary download/upload on DfuSe devices. Do +not + +use this for downloading DfuSe (.dfu) files. Modifiers can be added +to the address, separated by a colon, to perform special DfuSE commands such +as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. +
-v, --verbose + +
+Print more information about dfu-util's operation. A second +-v + +will turn on verbose logging of USB requests. Repeat this option to further +increase verbosity. +
-h, --help + +
+Show a help text and exit. +
-V, --version + +
+Show version information and exit. +
+  +

EXAMPLES

+ +  +

Using dfu-util in the OpenMoko project

+ +(with the Neo1973 hardware) +

+ +Flashing the rootfs: +
+ + $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2 + +

+ +Flashing the kernel: +
+ + $ dfu-util -a kernel -R -D /path/to/uImage + +

+ +Flashing the bootloader: +
+ + $ dfu-util -a u-boot -R -D /path/to/u-boot.bin + +

+ +Copying a kernel into RAM: +
+ + $ dfu-util -a 0 -R -D /path/to/uImage + +

+Once this has finished, the kernel will be available at the default load +address of 0x32000000 in Neo1973 RAM. +Note: + +You cannot transfer more than 2MB of data into RAM using this method. +

+  +

Using dfu-util with a DfuSe device

+ +

+ +Flashing a +.dfu + +(special DfuSe format) file to the device: +
+ + $ dfu-util -a 0 -D /path/to/dfuse-image.dfu + +

+ +Reading out 1 KB of flash starting at address 0x8000000: +
+ + $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin + +

+ +Flashing a binary file to address 0x8004000 of device memory and +ask the device to leave DFU mode: +
+ + $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin + + +  +

BUGS

+ +Please report any bugs to the dfu-util mailing list at +dfu-util@lists.gnumonks.org. + +Please use the +--verbose option (repeated as necessary) to provide more + +information in your bug report. +  +

SEE ALSO

+ +The dfu-util home page is +http://dfu-util.gnumonks.org + +  +

HISTORY

+ +dfu-util was originally written for the OpenMoko project by +Weston Schmidt <weston_schmidt@yahoo.com> and +Harald Welte <hwelte@hmw-consulting.de>. Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +  +

LICENCE

+ +dfu-util + +is covered by the GNU General Public License (GPL), version 2 or later. +  +

COPYRIGHT

+ +This manual page was originally written by Uwe Hermann <uwe@hermann-uwe.de>, +and is now part of the dfu-util project. +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
OPTIONS
+
EXAMPLES
+
+
Using dfu-util in the OpenMoko project
+
Using dfu-util with a DfuSe device
+
+
BUGS
+
SEE ALSO
+
HISTORY
+
LICENCE
+
COPYRIGHT
+
+
+This document was created by man2html, +using the doc/dfu-util.1 manual page from dfu-util 0.8.
+Time: 14:40:57 GMT, September 13, 2014 + + diff --git a/tools/src/dfu-util/www/dfuse.html b/tools/linux/src/dfu-util/www/dfuse.html old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/www/dfuse.html rename to tools/linux/src/dfu-util/www/dfuse.html index ebb9784..35e4ffa --- a/tools/src/dfu-util/www/dfuse.html +++ b/tools/linux/src/dfu-util/www/dfuse.html @@ -1,135 +1,135 @@ - - - - - - DfuSe and dfu-util - - - - - - - - - -
-

Using dfu-util with DfuSe devices

-

DfuSe

-

- DfuSe (DFU with ST Microsystems extensions) is a protocol based on - DFU 1.1. However, in expanding the functionality of the DFU protocol, - ST Microsystems broke all compatibility with the DFU 1.1 standard. - DfuSe devices report the DFU version as "1.1a". -

-

- DfuSe can be used to download firmware and other data - from a host computer to a conforming device (or upload in the - opposite direction) over USB similar to standard DFU. -

-

- The main difference from standard DFU is that the target address in - the device (flash) memory is specified by the host, so that a - download can be performed to parts of the device memory. The host - program is also responsible for erasing flash pages before they - are written to. -

-

.dfu files

-

- A special file format is defined by ST Microsystems to carry firmware - for DfuSe devices. The file contains target information such as address - and alternate interface information in addition to the binary data. - Several blocks of binary data can be combined in one .dfu file. -

-

Alternate interfaces

-

- Different memory locations of the device may have different - characteristics that the host program (dfu-util) has to take - into considerations, such as flash memory page size, read-only - versus read-write segments, the need to erase, and so on. - These parameters are reported by the device in the string - descriptors meant for describing the USB interfaces. - The host program decodes these strings to build a memory map of - the device. Different memory units or address spaces are listed - in separate alternate interface settings that must be selected - according to the memory unit to access. -

-

- Note that dfu-util leaves it to the user to select alternate - interface. When parsing a .dfu file it will skip file segments - not matching the selected alternate interface. Also, some - DfuSe device firmware implementations ignore the setting of - alternate interface and deduct the memory unit from the - address, since they have no address space overlap. -

-

DfuSe special commands

-

- DfuSe special commands are used by the host program during normal - downloads or uploads, such as SET_ADDRESS and ERASE_PAGE. Also - the normal DFU_DNLOAD and DFU_UPLOAD commands have special - implementations in DfuSe. - Many DfuSe devices also support commands to leave DFU mode, - read unprotect the flash memory or mass erase the flash memory. - dfu-util (from version 0.7) - supports adding "leave", "unprotect", or "mass-erase" - to the -s option argument to send such requests in combination - with a download request. These modifiers are separated with a colon. -

-

- Some DfuSe devices have their DfuSe bootloader running from flash - memory. Erasing the whole flash memory would therefore destroy - the DfuSe bootloader itself and practically brick the device - for most users. Any use of modifiers such as "unprotect" - and "mass-erase" therefore needs to be combined with the "force" - modifer. This is not included in the examples, to not encourage - ignorant users to copy and paste such instructions and shoot - themselves in the foot. -

-

- Devices based on for instance STM32F103 all run the bootloader - from flash, since there is no USB bootloader in ROM. -

-

- For instance STM32F107, STM32F2xx and STM32F4xx devices have a - DfuSe bootloader in ROM, so the flash can be erased while - keeping the device available for USB DFU transfers as long - as the device designers use this built-in bootloader and have - not implemented another DfuSe bootloader in flash that the user is - dependent upon. -

-

- Well-written bootloaders running from flash will report their - own memory region as read-only and not eraseable, but this does - not prevent dfu-util from sending a "unprotect" or "mass-erase" - request which overrides this, if the user insists. -

-

Example usage

-

- Flashing a .dfu (special DfuSe format) file to the device: -

-
-         $ dfu-util -a 0 -D /path/to/dfuse-image.dfu
-	
-

- Reading out 1 KB of flash starting at address 0x8000000: -

-
-         $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin
-	
-

- Flashing a binary file to address 0x8004000 of device memory and ask - the device to leave DFU mode: -

-
-         $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin
-	
-

- [Back to dfu-util main page] -

- -
- - + + + + + + DfuSe and dfu-util + + + + + + + + + +
+

Using dfu-util with DfuSe devices

+

DfuSe

+

+ DfuSe (DFU with ST Microsystems extensions) is a protocol based on + DFU 1.1. However, in expanding the functionality of the DFU protocol, + ST Microsystems broke all compatibility with the DFU 1.1 standard. + DfuSe devices report the DFU version as "1.1a". +

+

+ DfuSe can be used to download firmware and other data + from a host computer to a conforming device (or upload in the + opposite direction) over USB similar to standard DFU. +

+

+ The main difference from standard DFU is that the target address in + the device (flash) memory is specified by the host, so that a + download can be performed to parts of the device memory. The host + program is also responsible for erasing flash pages before they + are written to. +

+

.dfu files

+

+ A special file format is defined by ST Microsystems to carry firmware + for DfuSe devices. The file contains target information such as address + and alternate interface information in addition to the binary data. + Several blocks of binary data can be combined in one .dfu file. +

+

Alternate interfaces

+

+ Different memory locations of the device may have different + characteristics that the host program (dfu-util) has to take + into considerations, such as flash memory page size, read-only + versus read-write segments, the need to erase, and so on. + These parameters are reported by the device in the string + descriptors meant for describing the USB interfaces. + The host program decodes these strings to build a memory map of + the device. Different memory units or address spaces are listed + in separate alternate interface settings that must be selected + according to the memory unit to access. +

+

+ Note that dfu-util leaves it to the user to select alternate + interface. When parsing a .dfu file it will skip file segments + not matching the selected alternate interface. Also, some + DfuSe device firmware implementations ignore the setting of + alternate interface and deduct the memory unit from the + address, since they have no address space overlap. +

+

DfuSe special commands

+

+ DfuSe special commands are used by the host program during normal + downloads or uploads, such as SET_ADDRESS and ERASE_PAGE. Also + the normal DFU_DNLOAD and DFU_UPLOAD commands have special + implementations in DfuSe. + Many DfuSe devices also support commands to leave DFU mode, + read unprotect the flash memory or mass erase the flash memory. + dfu-util (from version 0.7) + supports adding "leave", "unprotect", or "mass-erase" + to the -s option argument to send such requests in combination + with a download request. These modifiers are separated with a colon. +

+

+ Some DfuSe devices have their DfuSe bootloader running from flash + memory. Erasing the whole flash memory would therefore destroy + the DfuSe bootloader itself and practically brick the device + for most users. Any use of modifiers such as "unprotect" + and "mass-erase" therefore needs to be combined with the "force" + modifer. This is not included in the examples, to not encourage + ignorant users to copy and paste such instructions and shoot + themselves in the foot. +

+

+ Devices based on for instance STM32F103 all run the bootloader + from flash, since there is no USB bootloader in ROM. +

+

+ For instance STM32F107, STM32F2xx and STM32F4xx devices have a + DfuSe bootloader in ROM, so the flash can be erased while + keeping the device available for USB DFU transfers as long + as the device designers use this built-in bootloader and have + not implemented another DfuSe bootloader in flash that the user is + dependent upon. +

+

+ Well-written bootloaders running from flash will report their + own memory region as read-only and not eraseable, but this does + not prevent dfu-util from sending a "unprotect" or "mass-erase" + request which overrides this, if the user insists. +

+

Example usage

+

+ Flashing a .dfu (special DfuSe format) file to the device: +

+
+         $ dfu-util -a 0 -D /path/to/dfuse-image.dfu
+	
+

+ Reading out 1 KB of flash starting at address 0x8000000: +

+
+         $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin
+	
+

+ Flashing a binary file to address 0x8004000 of device memory and ask + the device to leave DFU mode: +

+
+         $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin
+	
+

+ [Back to dfu-util main page] +

+ +
+ + diff --git a/tools/src/dfu-util/www/index.html b/tools/linux/src/dfu-util/www/index.html old mode 100755 new mode 100644 similarity index 97% rename from tools/src/dfu-util/www/index.html rename to tools/linux/src/dfu-util/www/index.html index 9bb3223..108ddaf --- a/tools/src/dfu-util/www/index.html +++ b/tools/linux/src/dfu-util/www/index.html @@ -1,119 +1,119 @@ - - - - - - dfu-util Homepage - - - - - - - - - -
-

dfu-util - Device Firmware Upgrade Utilities

-

Description

-

- dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. - - DFU is intended to download and upload firmware to/from devices connected - over USB. It ranges from small devices like micro-controller boards - to mobile phones. Using dfu-util you can download firmware to your - DFU-enabled device or upload firmware from it. dfu-util has been - tested with the Openmoko Neo1973 and Freerunner and many other devices. -

-

- See the manual page for examples of use. -

-

Supported Devices

- -

Releases

-

- Releases of the dfu-util software can be found in the - releases folder. - The current release is 0.8. -

-

- We offer binaries for Microsoft Windows and some other platforms. - dfu-util uses libusb 1.0 to access your device, so - on Windows you have to register the device with the WinUSB driver - (alternatively libusb-win32 or libusbK), please see the libusbx wiki - for more details. -

-

- Mac OS X users can also get dfu-util from Homebrew with "brew install dfu-util" or from MacPorts. -

-

- Most Linux distributions ship dfu-util in binary packages for those - who do not want to compile dfu-util from source. - On Debian, Ubuntu, Fedora and Gentoo you can install it through the - normal software package tools. For other distributions -(namely OpenSuSe, Mandriva, and CentOS) Holger Freyther was kind enough to -provide binary packages through the Open Build Service. -

-

Development

-

- Development happens in a GIT repository. Browse it via the web -interface or clone it with: -

-
-	git clone git://gitorious.org/dfu-util/dfu-util.git
-	
-

- See our build instructions for how to - build the source on different platforms. -

-

License

-

- This software is licensed under the GPL version 2. -

-

Contact

-

- If you have questions about the development or use of dfu-util please - send an e-mail to our dedicated -mailing list for dfu-util. -

-

People

-

- dfu-util was originally written by - Harald Welte partially based on code from - dfu-programmer 0.4 and is currently maintained by Stefan Schmidt and - Tormod Volden. -

- -
- - + + + + + + dfu-util Homepage + + + + + + + + + +
+

dfu-util - Device Firmware Upgrade Utilities

+

Description

+

+ dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. + + DFU is intended to download and upload firmware to/from devices connected + over USB. It ranges from small devices like micro-controller boards + to mobile phones. Using dfu-util you can download firmware to your + DFU-enabled device or upload firmware from it. dfu-util has been + tested with the Openmoko Neo1973 and Freerunner and many other devices. +

+

+ See the manual page for examples of use. +

+

Supported Devices

+ +

Releases

+

+ Releases of the dfu-util software can be found in the + releases folder. + The current release is 0.8. +

+

+ We offer binaries for Microsoft Windows and some other platforms. + dfu-util uses libusb 1.0 to access your device, so + on Windows you have to register the device with the WinUSB driver + (alternatively libusb-win32 or libusbK), please see the libusbx wiki + for more details. +

+

+ Mac OS X users can also get dfu-util from Homebrew with "brew install dfu-util" or from MacPorts. +

+

+ Most Linux distributions ship dfu-util in binary packages for those + who do not want to compile dfu-util from source. + On Debian, Ubuntu, Fedora and Gentoo you can install it through the + normal software package tools. For other distributions +(namely OpenSuSe, Mandriva, and CentOS) Holger Freyther was kind enough to +provide binary packages through the Open Build Service. +

+

Development

+

+ Development happens in a GIT repository. Browse it via the web +interface or clone it with: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	
+

+ See our build instructions for how to + build the source on different platforms. +

+

License

+

+ This software is licensed under the GPL version 2. +

+

Contact

+

+ If you have questions about the development or use of dfu-util please + send an e-mail to our dedicated +mailing list for dfu-util. +

+

People

+

+ dfu-util was originally written by + Harald Welte partially based on code from + dfu-programmer 0.4 and is currently maintained by Stefan Schmidt and + Tormod Volden. +

+ +
+ + diff --git a/tools/src/dfu-util/www/simple.css b/tools/linux/src/dfu-util/www/simple.css old mode 100755 new mode 100644 similarity index 92% rename from tools/src/dfu-util/www/simple.css rename to tools/linux/src/dfu-util/www/simple.css index 10b7c1d..98100bc --- a/tools/src/dfu-util/www/simple.css +++ b/tools/linux/src/dfu-util/www/simple.css @@ -1,56 +1,56 @@ -body { - margin: 10px; - font-size: 0.82em; - background-color: #EEE; -} - -h1 { - clear: both; - padding: 0 0 12px 0; - margin: 0; - font-size: 2em; - font-weight: bold; -} - -h2 { - clear: both; - margin: 0; - font-size: 1.5em; - font-weight: normal; -} - -h3 { - clear: both; - margin: 15px 0 0 0; - font-size: 1.0em; - font-weight: bold; -} - -p { - line-height: 20px; - padding: 8px 0 8px 0; - margin: 0; - font-size: 1.1em; -} - -pre { - white-space: pre-wrap; - background-color: #CCC; - padding: 3px; -} - -a:hover { - background-color: #DDD; -} - -#middlebox { - width: 600px; - margin: 0px auto; - text-align: left; -} - -#footer { - height: 100px; - padding: 28px 3px 0 0; - margin: 20px 0 20px 0; -} +body { + margin: 10px; + font-size: 0.82em; + background-color: #EEE; +} + +h1 { + clear: both; + padding: 0 0 12px 0; + margin: 0; + font-size: 2em; + font-weight: bold; +} + +h2 { + clear: both; + margin: 0; + font-size: 1.5em; + font-weight: normal; +} + +h3 { + clear: both; + margin: 15px 0 0 0; + font-size: 1.0em; + font-weight: bold; +} + +p { + line-height: 20px; + padding: 8px 0 8px 0; + margin: 0; + font-size: 1.1em; +} + +pre { + white-space: pre-wrap; + background-color: #CCC; + padding: 3px; +} + +a:hover { + background-color: #DDD; +} + +#middlebox { + width: 600px; + margin: 0px auto; + text-align: left; +} + +#footer { + height: 100px; + padding: 28px 3px 0 0; + margin: 20px 0 20px 0; +} diff --git a/tools/src/maple_loader/README.md b/tools/linux/src/maple_loader/README.md similarity index 100% rename from tools/src/maple_loader/README.md rename to tools/linux/src/maple_loader/README.md diff --git a/tools/src/maple_loader/build.xml b/tools/linux/src/maple_loader/build.xml similarity index 97% rename from tools/src/maple_loader/build.xml rename to tools/linux/src/maple_loader/build.xml index 63ae9c2..80bdd6f 100644 --- a/tools/src/maple_loader/build.xml +++ b/tools/linux/src/maple_loader/build.xml @@ -1,73 +1,73 @@ - - - - - - - - - - - Builds, tests, and runs the project maple_loader. - - - + + + + + + + + + + + Builds, tests, and runs the project maple_loader. + + + diff --git a/tools/src/maple_loader/build/built-jar.properties b/tools/linux/src/maple_loader/build/built-jar.properties similarity index 96% rename from tools/src/maple_loader/build/built-jar.properties rename to tools/linux/src/maple_loader/build/built-jar.properties index 94002ae..10752d5 100644 --- a/tools/src/maple_loader/build/built-jar.properties +++ b/tools/linux/src/maple_loader/build/built-jar.properties @@ -1,4 +1,4 @@ -#Mon, 20 Jul 2015 11:21:26 +1000 - - -C\:\\Users\\rclark\\Desktop\\maple-asp-master\\installer\\maple_loader= +#Mon, 20 Jul 2015 11:21:26 +1000 + + +C\:\\Users\\rclark\\Desktop\\maple-asp-master\\installer\\maple_loader= diff --git a/tools/src/maple_loader/build/classes/CliTemplate/CliMain.class b/tools/linux/src/maple_loader/build/classes/CliTemplate/CliMain.class similarity index 100% rename from tools/src/maple_loader/build/classes/CliTemplate/CliMain.class rename to tools/linux/src/maple_loader/build/classes/CliTemplate/CliMain.class diff --git a/tools/src/maple_loader/build/classes/CliTemplate/DFUUploader.class b/tools/linux/src/maple_loader/build/classes/CliTemplate/DFUUploader.class similarity index 100% rename from tools/src/maple_loader/build/classes/CliTemplate/DFUUploader.class rename to tools/linux/src/maple_loader/build/classes/CliTemplate/DFUUploader.class diff --git a/tools/src/maple_loader/build/classes/CliTemplate/ExecCommand.class b/tools/linux/src/maple_loader/build/classes/CliTemplate/ExecCommand.class similarity index 100% rename from tools/src/maple_loader/build/classes/CliTemplate/ExecCommand.class rename to tools/linux/src/maple_loader/build/classes/CliTemplate/ExecCommand.class diff --git a/tools/src/maple_loader/build/classes/processing/app/Base.class b/tools/linux/src/maple_loader/build/classes/processing/app/Base.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/Base.class rename to tools/linux/src/maple_loader/build/classes/processing/app/Base.class diff --git a/tools/src/maple_loader/build/classes/processing/app/Preferences.class b/tools/linux/src/maple_loader/build/classes/processing/app/Preferences.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/Preferences.class rename to tools/linux/src/maple_loader/build/classes/processing/app/Preferences.class diff --git a/tools/src/maple_loader/build/classes/processing/app/Serial.class b/tools/linux/src/maple_loader/build/classes/processing/app/Serial.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/Serial.class rename to tools/linux/src/maple_loader/build/classes/processing/app/Serial.class diff --git a/tools/src/maple_loader/build/classes/processing/app/SerialException.class b/tools/linux/src/maple_loader/build/classes/processing/app/SerialException.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/SerialException.class rename to tools/linux/src/maple_loader/build/classes/processing/app/SerialException.class diff --git a/tools/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class b/tools/linux/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class rename to tools/linux/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class diff --git a/tools/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class b/tools/linux/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class rename to tools/linux/src/maple_loader/build/classes/processing/app/debug/MessageSiphon.class diff --git a/tools/src/maple_loader/build/classes/processing/app/debug/RunnerException.class b/tools/linux/src/maple_loader/build/classes/processing/app/debug/RunnerException.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/debug/RunnerException.class rename to tools/linux/src/maple_loader/build/classes/processing/app/debug/RunnerException.class diff --git a/tools/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class b/tools/linux/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class similarity index 100% rename from tools/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class rename to tools/linux/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class diff --git a/tools/src/maple_loader/dist/README.TXT b/tools/linux/src/maple_loader/dist/README.TXT similarity index 99% rename from tools/src/maple_loader/dist/README.TXT rename to tools/linux/src/maple_loader/dist/README.TXT index 795e0cf..255b89c 100644 --- a/tools/src/maple_loader/dist/README.TXT +++ b/tools/linux/src/maple_loader/dist/README.TXT @@ -29,4 +29,4 @@ the projects runtime path. * To set a main class in a standard Java project, right-click the project node in the Projects window and choose Properties. Then click Run and enter the class name in the Main Class field. Alternatively, you can manually type the -class name in the manifest Main-Class element. +class name in the manifest Main-Class element. diff --git a/tools/src/maple_loader/dist/lib/jssc.jar b/tools/linux/src/maple_loader/dist/lib/jssc.jar similarity index 100% rename from tools/src/maple_loader/dist/lib/jssc.jar rename to tools/linux/src/maple_loader/dist/lib/jssc.jar diff --git a/tools/src/maple_loader/dist/maple_loader.jar b/tools/linux/src/maple_loader/dist/maple_loader.jar similarity index 100% rename from tools/src/maple_loader/dist/maple_loader.jar rename to tools/linux/src/maple_loader/dist/maple_loader.jar diff --git a/tools/src/maple_loader/jars/jssc.jar b/tools/linux/src/maple_loader/jars/jssc.jar similarity index 100% rename from tools/src/maple_loader/jars/jssc.jar rename to tools/linux/src/maple_loader/jars/jssc.jar diff --git a/tools/src/maple_loader/manifest.mf b/tools/linux/src/maple_loader/manifest.mf similarity index 100% rename from tools/src/maple_loader/manifest.mf rename to tools/linux/src/maple_loader/manifest.mf diff --git a/tools/src/maple_loader/nbproject/build-impl.xml b/tools/linux/src/maple_loader/nbproject/build-impl.xml similarity index 98% rename from tools/src/maple_loader/nbproject/build-impl.xml rename to tools/linux/src/maple_loader/nbproject/build-impl.xml index 080006a..a66f349 100644 --- a/tools/src/maple_loader/nbproject/build-impl.xml +++ b/tools/linux/src/maple_loader/nbproject/build-impl.xml @@ -1,1413 +1,1413 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.src.dir - Must set build.dir - Must set dist.dir - Must set build.classes.dir - Must set dist.javadoc.dir - Must set build.test.classes.dir - Must set build.test.results.dir - Must set build.classes.excludes - Must set dist.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No tests executed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set JVM to use for profiling in profiler.info.jvm - Must set profiler agent JVM arguments in profiler.info.jvmargs.agent - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: - - java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - Must select one file in the IDE or set run.class - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set debug.class - - - - - Must select one file in the IDE or set debug.class - - - - - Must set fix.includes - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - Must select one file in the IDE or set profile.class - This target only works when run from inside the NetBeans IDE. - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - This target only works when run from inside the NetBeans IDE. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select one file in the IDE or set run.class - - - - - - Must select some files in the IDE or set test.includes - - - - - Must select one file in the IDE or set run.class - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must select some files in the IDE or set javac.includes - - - - - - - - - - - - - - - - - - - - Some tests failed; see details above. - - - - - - - - - Must select some files in the IDE or set test.includes - - - - Some tests failed; see details above. - - - - Must select some files in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - Some tests failed; see details above. - - - - - Must select one file in the IDE or set test.class - - - - Must select one file in the IDE or set test.class - Must select some method in the IDE or set test.method - - - - - - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - Must select one file in the IDE or set applet.url - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/src/maple_loader/nbproject/genfiles.properties b/tools/linux/src/maple_loader/nbproject/genfiles.properties similarity index 98% rename from tools/src/maple_loader/nbproject/genfiles.properties rename to tools/linux/src/maple_loader/nbproject/genfiles.properties index 1007bfc..c136721 100644 --- a/tools/src/maple_loader/nbproject/genfiles.properties +++ b/tools/linux/src/maple_loader/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=2e6a03ba -build.xml.script.CRC32=4676ee6b -build.xml.stylesheet.CRC32=8064a381@1.75.2.48 -# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. -# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=2e6a03ba -nbproject/build-impl.xml.script.CRC32=392b3f79 -nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 +build.xml.data.CRC32=2e6a03ba +build.xml.script.CRC32=4676ee6b +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=2e6a03ba +nbproject/build-impl.xml.script.CRC32=392b3f79 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/src/maple_loader/nbproject/private/config.properties b/tools/linux/src/maple_loader/nbproject/private/config.properties similarity index 100% rename from tools/src/maple_loader/nbproject/private/config.properties rename to tools/linux/src/maple_loader/nbproject/private/config.properties diff --git a/tools/src/maple_loader/nbproject/private/private.properties b/tools/linux/src/maple_loader/nbproject/private/private.properties similarity index 96% rename from tools/src/maple_loader/nbproject/private/private.properties rename to tools/linux/src/maple_loader/nbproject/private/private.properties index ab05185..e5c9f10 100644 --- a/tools/src/maple_loader/nbproject/private/private.properties +++ b/tools/linux/src/maple_loader/nbproject/private/private.properties @@ -1,6 +1,6 @@ -compile.on.save=true -do.depend=false -do.jar=true -javac.debug=true -javadoc.preview=true -user.properties.file=C:\\Users\\rclark\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\rclark\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties diff --git a/tools/src/maple_loader/nbproject/private/private.xml b/tools/linux/src/maple_loader/nbproject/private/private.xml similarity index 98% rename from tools/src/maple_loader/nbproject/private/private.xml rename to tools/linux/src/maple_loader/nbproject/private/private.xml index 9feb3fe..a1bbd60 100644 --- a/tools/src/maple_loader/nbproject/private/private.xml +++ b/tools/linux/src/maple_loader/nbproject/private/private.xml @@ -1,10 +1,10 @@ - - - - - - file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/CliMain.java - file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/DFUUploader.java - - - + + + + + + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/CliMain.java + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/DFUUploader.java + + + diff --git a/tools/src/maple_loader/nbproject/project.properties b/tools/linux/src/maple_loader/nbproject/project.properties similarity index 96% rename from tools/src/maple_loader/nbproject/project.properties rename to tools/linux/src/maple_loader/nbproject/project.properties index 984cc6a..7f48d71 100644 --- a/tools/src/maple_loader/nbproject/project.properties +++ b/tools/linux/src/maple_loader/nbproject/project.properties @@ -1,79 +1,79 @@ -annotation.processing.enabled=true -annotation.processing.enabled.in.editor=false -annotation.processing.processors.list= -annotation.processing.run.all.processors=true -annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output -application.title=maple_loader -application.vendor=bob -build.classes.dir=${build.dir}/classes -build.classes.excludes=**/*.java,**/*.form -# This directory is removed when the project is cleaned: -build.dir=build -build.generated.dir=${build.dir}/generated -build.generated.sources.dir=${build.dir}/generated-sources -# Only compile against the classpath explicitly listed here: -build.sysclasspath=ignore -build.test.classes.dir=${build.dir}/test/classes -build.test.results.dir=${build.dir}/test/results -# Uncomment to specify the preferred debugger connection transport: -#debug.transport=dt_socket -debug.classpath=\ - ${run.classpath} -debug.test.classpath=\ - ${run.test.classpath} -# Files in build.classes.dir which should be excluded from distribution jar -dist.archive.excludes= -# This directory is removed when the project is cleaned: -dist.dir=dist -dist.jar=${dist.dir}/maple_loader.jar -dist.javadoc.dir=${dist.dir}/javadoc -endorsed.classpath= -excludes= -file.reference.jssc.jar=dist/lib/jssc.jar -file.reference.jssc.jar-1=jars/jssc.jar -includes=** -jar.compress=false -javac.classpath=\ - ${file.reference.jssc.jar}:\ - ${file.reference.jssc.jar-1} -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.7 -javac.target=1.7 -javac.test.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -javac.test.processorpath=\ - ${javac.test.classpath} -javadoc.additionalparam= -javadoc.author=false -javadoc.encoding=${source.encoding} -javadoc.noindex=false -javadoc.nonavbar=false -javadoc.notree=false -javadoc.private=false -javadoc.splitindex=true -javadoc.use=true -javadoc.version=false -javadoc.windowtitle= -main.class=CliTemplate.CliMain -manifest.file=manifest.mf -meta.inf.dir=${src.dir}/META-INF -mkdist.disabled=false -platform.active=default_platform -run.classpath=\ - ${javac.classpath}:\ - ${build.classes.dir} -# Space-separated list of JVM arguments used when running the project. -# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. -# To set system properties for unit tests define test-sys-prop.name=value: -run.jvmargs= -run.test.classpath=\ - ${javac.test.classpath}:\ - ${build.test.classes.dir} -source.encoding=UTF-8 -src.dir=src -test.src.dir=test +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=maple_loader +application.vendor=bob +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/maple_loader.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jssc.jar=dist/lib/jssc.jar +file.reference.jssc.jar-1=jars/jssc.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.jssc.jar}:\ + ${file.reference.jssc.jar-1} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=CliTemplate.CliMain +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/src/maple_loader/nbproject/project.xml b/tools/linux/src/maple_loader/nbproject/project.xml similarity index 97% rename from tools/src/maple_loader/nbproject/project.xml rename to tools/linux/src/maple_loader/nbproject/project.xml index 076fe22..92218a9 100644 --- a/tools/src/maple_loader/nbproject/project.xml +++ b/tools/linux/src/maple_loader/nbproject/project.xml @@ -1,15 +1,15 @@ - - - org.netbeans.modules.java.j2seproject - - - maple_loader - - - - - - - - - + + + org.netbeans.modules.java.j2seproject + + + maple_loader + + + + + + + + + diff --git a/tools/src/maple_loader/src/CliTemplate/CliMain.java b/tools/linux/src/maple_loader/src/CliTemplate/CliMain.java similarity index 100% rename from tools/src/maple_loader/src/CliTemplate/CliMain.java rename to tools/linux/src/maple_loader/src/CliTemplate/CliMain.java diff --git a/tools/src/maple_loader/src/CliTemplate/DFUUploader.java b/tools/linux/src/maple_loader/src/CliTemplate/DFUUploader.java similarity index 100% rename from tools/src/maple_loader/src/CliTemplate/DFUUploader.java rename to tools/linux/src/maple_loader/src/CliTemplate/DFUUploader.java diff --git a/tools/src/maple_loader/src/CliTemplate/ExecCommand.java b/tools/linux/src/maple_loader/src/CliTemplate/ExecCommand.java similarity index 100% rename from tools/src/maple_loader/src/CliTemplate/ExecCommand.java rename to tools/linux/src/maple_loader/src/CliTemplate/ExecCommand.java diff --git a/tools/src/maple_loader/src/processing/app/Base.java b/tools/linux/src/maple_loader/src/processing/app/Base.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/Base.java rename to tools/linux/src/maple_loader/src/processing/app/Base.java diff --git a/tools/src/maple_loader/src/processing/app/Preferences.java b/tools/linux/src/maple_loader/src/processing/app/Preferences.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/Preferences.java rename to tools/linux/src/maple_loader/src/processing/app/Preferences.java diff --git a/tools/src/maple_loader/src/processing/app/Serial.java b/tools/linux/src/maple_loader/src/processing/app/Serial.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/Serial.java rename to tools/linux/src/maple_loader/src/processing/app/Serial.java diff --git a/tools/src/maple_loader/src/processing/app/SerialException.java b/tools/linux/src/maple_loader/src/processing/app/SerialException.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/SerialException.java rename to tools/linux/src/maple_loader/src/processing/app/SerialException.java diff --git a/tools/src/maple_loader/src/processing/app/debug/MessageConsumer.java b/tools/linux/src/maple_loader/src/processing/app/debug/MessageConsumer.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/debug/MessageConsumer.java rename to tools/linux/src/maple_loader/src/processing/app/debug/MessageConsumer.java diff --git a/tools/src/maple_loader/src/processing/app/debug/MessageSiphon.java b/tools/linux/src/maple_loader/src/processing/app/debug/MessageSiphon.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/debug/MessageSiphon.java rename to tools/linux/src/maple_loader/src/processing/app/debug/MessageSiphon.java diff --git a/tools/src/maple_loader/src/processing/app/debug/RunnerException.java b/tools/linux/src/maple_loader/src/processing/app/debug/RunnerException.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/debug/RunnerException.java rename to tools/linux/src/maple_loader/src/processing/app/debug/RunnerException.java diff --git a/tools/src/maple_loader/src/processing/app/helpers/ProcessUtils.java b/tools/linux/src/maple_loader/src/processing/app/helpers/ProcessUtils.java similarity index 100% rename from tools/src/maple_loader/src/processing/app/helpers/ProcessUtils.java rename to tools/linux/src/maple_loader/src/processing/app/helpers/ProcessUtils.java diff --git a/tools/src/stm32flash_serial/src/AUTHORS b/tools/linux/src/stm32flash_serial/src/AUTHORS similarity index 100% rename from tools/src/stm32flash_serial/src/AUTHORS rename to tools/linux/src/stm32flash_serial/src/AUTHORS diff --git a/tools/src/stm32flash_serial/src/Android.mk b/tools/linux/src/stm32flash_serial/src/Android.mk similarity index 100% rename from tools/src/stm32flash_serial/src/Android.mk rename to tools/linux/src/stm32flash_serial/src/Android.mk diff --git a/tools/src/stm32flash_serial/src/HOWTO b/tools/linux/src/stm32flash_serial/src/HOWTO similarity index 100% rename from tools/src/stm32flash_serial/src/HOWTO rename to tools/linux/src/stm32flash_serial/src/HOWTO diff --git a/tools/src/stm32flash_serial/src/I2C.txt b/tools/linux/src/stm32flash_serial/src/I2C.txt similarity index 100% rename from tools/src/stm32flash_serial/src/I2C.txt rename to tools/linux/src/stm32flash_serial/src/I2C.txt diff --git a/tools/src/stm32flash_serial/src/Makefile b/tools/linux/src/stm32flash_serial/src/Makefile similarity index 100% rename from tools/src/stm32flash_serial/src/Makefile rename to tools/linux/src/stm32flash_serial/src/Makefile diff --git a/tools/src/stm32flash_serial/src/TODO b/tools/linux/src/stm32flash_serial/src/TODO similarity index 100% rename from tools/src/stm32flash_serial/src/TODO rename to tools/linux/src/stm32flash_serial/src/TODO diff --git a/tools/src/stm32flash_serial/src/dev_table.c b/tools/linux/src/stm32flash_serial/src/dev_table.c similarity index 100% rename from tools/src/stm32flash_serial/src/dev_table.c rename to tools/linux/src/stm32flash_serial/src/dev_table.c diff --git a/tools/src/stm32flash_serial/src/gpl-2.0.txt b/tools/linux/src/stm32flash_serial/src/gpl-2.0.txt similarity index 100% rename from tools/src/stm32flash_serial/src/gpl-2.0.txt rename to tools/linux/src/stm32flash_serial/src/gpl-2.0.txt diff --git a/tools/src/stm32flash_serial/src/i2c.c b/tools/linux/src/stm32flash_serial/src/i2c.c similarity index 100% rename from tools/src/stm32flash_serial/src/i2c.c rename to tools/linux/src/stm32flash_serial/src/i2c.c diff --git a/tools/src/stm32flash_serial/src/init.c b/tools/linux/src/stm32flash_serial/src/init.c similarity index 100% rename from tools/src/stm32flash_serial/src/init.c rename to tools/linux/src/stm32flash_serial/src/init.c diff --git a/tools/src/stm32flash_serial/src/init.h b/tools/linux/src/stm32flash_serial/src/init.h similarity index 100% rename from tools/src/stm32flash_serial/src/init.h rename to tools/linux/src/stm32flash_serial/src/init.h diff --git a/tools/src/stm32flash_serial/src/main.c b/tools/linux/src/stm32flash_serial/src/main.c similarity index 100% rename from tools/src/stm32flash_serial/src/main.c rename to tools/linux/src/stm32flash_serial/src/main.c diff --git a/tools/src/stm32flash_serial/src/parsers/Android.mk b/tools/linux/src/stm32flash_serial/src/parsers/Android.mk similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/Android.mk rename to tools/linux/src/stm32flash_serial/src/parsers/Android.mk diff --git a/tools/src/stm32flash_serial/src/parsers/Makefile b/tools/linux/src/stm32flash_serial/src/parsers/Makefile similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/Makefile rename to tools/linux/src/stm32flash_serial/src/parsers/Makefile diff --git a/tools/src/stm32flash_serial/src/parsers/binary.c b/tools/linux/src/stm32flash_serial/src/parsers/binary.c similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/binary.c rename to tools/linux/src/stm32flash_serial/src/parsers/binary.c diff --git a/tools/src/stm32flash_serial/src/parsers/binary.h b/tools/linux/src/stm32flash_serial/src/parsers/binary.h similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/binary.h rename to tools/linux/src/stm32flash_serial/src/parsers/binary.h diff --git a/tools/src/stm32flash_serial/src/parsers/hex.c b/tools/linux/src/stm32flash_serial/src/parsers/hex.c similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/hex.c rename to tools/linux/src/stm32flash_serial/src/parsers/hex.c diff --git a/tools/src/stm32flash_serial/src/parsers/hex.h b/tools/linux/src/stm32flash_serial/src/parsers/hex.h similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/hex.h rename to tools/linux/src/stm32flash_serial/src/parsers/hex.h diff --git a/tools/src/stm32flash_serial/src/parsers/parser.h b/tools/linux/src/stm32flash_serial/src/parsers/parser.h similarity index 100% rename from tools/src/stm32flash_serial/src/parsers/parser.h rename to tools/linux/src/stm32flash_serial/src/parsers/parser.h diff --git a/tools/src/stm32flash_serial/src/port.c b/tools/linux/src/stm32flash_serial/src/port.c similarity index 100% rename from tools/src/stm32flash_serial/src/port.c rename to tools/linux/src/stm32flash_serial/src/port.c diff --git a/tools/src/stm32flash_serial/src/port.h b/tools/linux/src/stm32flash_serial/src/port.h similarity index 100% rename from tools/src/stm32flash_serial/src/port.h rename to tools/linux/src/stm32flash_serial/src/port.h diff --git a/tools/src/stm32flash_serial/src/protocol.txt b/tools/linux/src/stm32flash_serial/src/protocol.txt similarity index 100% rename from tools/src/stm32flash_serial/src/protocol.txt rename to tools/linux/src/stm32flash_serial/src/protocol.txt diff --git a/tools/src/stm32flash_serial/src/serial.h b/tools/linux/src/stm32flash_serial/src/serial.h similarity index 100% rename from tools/src/stm32flash_serial/src/serial.h rename to tools/linux/src/stm32flash_serial/src/serial.h diff --git a/tools/src/stm32flash_serial/src/serial_common.c b/tools/linux/src/stm32flash_serial/src/serial_common.c similarity index 100% rename from tools/src/stm32flash_serial/src/serial_common.c rename to tools/linux/src/stm32flash_serial/src/serial_common.c diff --git a/tools/src/stm32flash_serial/src/serial_platform.c b/tools/linux/src/stm32flash_serial/src/serial_platform.c similarity index 100% rename from tools/src/stm32flash_serial/src/serial_platform.c rename to tools/linux/src/stm32flash_serial/src/serial_platform.c diff --git a/tools/src/stm32flash_serial/src/serial_posix.c b/tools/linux/src/stm32flash_serial/src/serial_posix.c similarity index 100% rename from tools/src/stm32flash_serial/src/serial_posix.c rename to tools/linux/src/stm32flash_serial/src/serial_posix.c diff --git a/tools/src/stm32flash_serial/src/serial_w32.c b/tools/linux/src/stm32flash_serial/src/serial_w32.c similarity index 100% rename from tools/src/stm32flash_serial/src/serial_w32.c rename to tools/linux/src/stm32flash_serial/src/serial_w32.c diff --git a/tools/src/stm32flash_serial/src/stm32.c b/tools/linux/src/stm32flash_serial/src/stm32.c similarity index 100% rename from tools/src/stm32flash_serial/src/stm32.c rename to tools/linux/src/stm32flash_serial/src/stm32.c diff --git a/tools/src/stm32flash_serial/src/stm32.h b/tools/linux/src/stm32flash_serial/src/stm32.h similarity index 100% rename from tools/src/stm32flash_serial/src/stm32.h rename to tools/linux/src/stm32flash_serial/src/stm32.h diff --git a/tools/src/stm32flash_serial/src/stm32flash.1 b/tools/linux/src/stm32flash_serial/src/stm32flash.1 similarity index 100% rename from tools/src/stm32flash_serial/src/stm32flash.1 rename to tools/linux/src/stm32flash_serial/src/stm32flash.1 diff --git a/tools/src/stm32flash_serial/src/utils.c b/tools/linux/src/stm32flash_serial/src/utils.c similarity index 100% rename from tools/src/stm32flash_serial/src/utils.c rename to tools/linux/src/stm32flash_serial/src/utils.c diff --git a/tools/src/stm32flash_serial/src/utils.h b/tools/linux/src/stm32flash_serial/src/utils.h similarity index 100% rename from tools/src/stm32flash_serial/src/utils.h rename to tools/linux/src/stm32flash_serial/src/utils.h diff --git a/tools/src/upload-reset/upload-reset.c b/tools/linux/src/upload-reset/upload-reset.c similarity index 100% rename from tools/src/upload-reset/upload-reset.c rename to tools/linux/src/upload-reset/upload-reset.c diff --git a/tools/linux64/45-maple.rules b/tools/linux64/45-maple.rules new file mode 100644 index 0000000..d1bda5f --- /dev/null +++ b/tools/linux64/45-maple.rules @@ -0,0 +1,5 @@ +ATTRS{idProduct}=="1001", ATTRS{idVendor}=="0110", MODE="664", GROUP="plugdev" +ATTRS{idProduct}=="1002", ATTRS{idVendor}=="0110", MODE="664", GROUP="plugdev" +ATTRS{idProduct}=="0003", ATTRS{idVendor}=="1eaf", MODE="664", GROUP="plugdev" SYMLINK+="maple" +ATTRS{idProduct}=="0004", ATTRS{idVendor}=="1eaf", MODE="664", GROUP="plugdev" SYMLINK+="maple" + diff --git a/tools/linux64/49-stlinkv1.rules b/tools/linux64/49-stlinkv1.rules new file mode 100644 index 0000000..d474d6a --- /dev/null +++ b/tools/linux64/49-stlinkv1.rules @@ -0,0 +1,11 @@ +# stm32 discovery boards, with onboard st/linkv1 +# ie, STM32VL + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3744", \ + MODE:="0666", \ + SYMLINK+="stlinkv1_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/tools/linux64/49-stlinkv2-1.rules b/tools/linux64/49-stlinkv2-1.rules new file mode 100644 index 0000000..a5a79b9 --- /dev/null +++ b/tools/linux64/49-stlinkv2-1.rules @@ -0,0 +1,12 @@ +# stm32 nucleo boards, with onboard st/linkv2-1 +# ie, STM32F0, STM32F4. +# STM32VL has st/linkv1, which is quite different + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", \ + MODE:="0666", \ + SYMLINK+="stlinkv2-1_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/tools/linux64/49-stlinkv2.rules b/tools/linux64/49-stlinkv2.rules new file mode 100644 index 0000000..a11215c --- /dev/null +++ b/tools/linux64/49-stlinkv2.rules @@ -0,0 +1,12 @@ +# stm32 discovery boards, with onboard st/linkv2 +# ie, STM32L, STM32F4. +# STM32VL has st/linkv1, which is quite different + +SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", \ + MODE:="0666", \ + SYMLINK+="stlinkv2_%n" + +# If you share your linux system with other users, or just don't like the +# idea of write permission for everybody, you can replace MODE:="0666" with +# OWNER:="yourusername" to create the device owned by you, or with +# GROUP:="somegroupname" and mange access using standard unix groups. diff --git a/tools/linux64/dfu-util/dfu-prefix b/tools/linux64/dfu-util/dfu-prefix new file mode 100644 index 0000000000000000000000000000000000000000..88e2d04d6d3904e7467d3db63fe5d1c3cd85213a GIT binary patch literal 47440 zcmeIbd3;pW`9FT{OqQ7mSs;jElK~P5LP8SuO(u}Q009Cd5EKj{nUF{}GZPkzLJjdl zN>SV@ZT$dst8LYyRt3}mZn(4`0g>WvU25V|sSDzo@B4l3Id>+Lq@T9GS3m#!Jdpb= z=XsvL> zgbO7JxKbh-PM|YLdlb}qbdP$eN-tIEX?RpQrlGDM&G4^4)vv(j0Y%zFAadj}2z9af z?*+<;Z%#7>Hm7+cqG6TE78+{3h0vp)|7=Q7f{Rpn-L=bURX+_CDMnRQt{OLHR8?78 zRb@@X`n2^E#-)uLlkTrg&yw|~eag<7Unon`?dS$YpL^qvF(C5~UU~D;!(R?PxZuQy zbKd=H#Md7$8OppQ{LzN2SEO5PHhSC0>x;ks_!~a(rvu6hOA;o2x^2J%w|?0RtZvhO zHs7cf-H78~#ta;s3fDehc_+{Dr4fFuJN& zeK-7)ZulHNUD=80hX0Fh_&0UKU)Bwu@zPbfS9QbB1HUICEPwh+U_$8eJQFziMPd9} z#jgnCS1A6MLsk<;A5HYlLroWkA$51pH!MNqx zDP2Q>;_{NpDpBt%sj8|i75+e3WetzD4FOT*_xaX~hwJ2X#Um5U;a@Z;prB$_lpQx?#!J03yc2z^Ufc$!tOg~wCeW19y1cgDK z4y~@Ptx@b^F)MHO^clrj>7&!fSa=Z2N*{|x5H|JCZeb=Tm=h6}!+Zm?g8^+lPX+o` zIyrOvN%4ADR>pD$d0Ej7{g`hP?|^1HJ8h+eoI4D?~*IpJQ34x)6R3%>-N@!Vm42%_01_5c#bk{P+<5 zju5_%5oYfU;r9%Y-xI>}FUYDY@Bh%`g3 z?Wm*|k!Fas9g_4M(st5&B|V)qL#l0$q$iPPD7Ed7bQWobP}^onUqG6n(^eiW@xl!NZLl4A<^cR^w(oSGZfkqB>gFAhCrK;^arHb z^=+rW2P5Te((LlKw`Bn|Y?pyDcIm z6(Zi|oMdn2p}g&7$qBicAGcl#r}wWM=n)6XaA>K<;UK#!^HA$FWJ&zMRm|Euz(ekE zDzF1!r1|~NyuZuao|7E0-;=#k9M}apZ}S_?pBw=TBBFINoNPSX9>3*5P>roQjb}%# z8`7Tq)cW%vxh-}JTKm8tWVNu&gC6L&CZT}VCr}-Dza07A?SW)R=J8gxXLj=u@3x#| z$L0wlzNra1*;27>J#?~LWLQ-LMd&gPvL7RdHBM+-VzOf-oAG9{rx(Fc>A z3U+3D>lG-{+qf|~LBwyF4_0>LcecP4INIa4+z;XG#*dBorUl5!Z8{Ubg@YvXxO4+e z(;AN^IM{|Ng1C;q0VTm1;p%tPL7%agrj-q8bO zSH(ANr$dcT7G1m|dwKSX>`StXpO9Ks5qF~|C^`_GGZEGs798x4c5S^nSctNZ=Ie

*o>PBZq|qN8B-Rb^`=Ij~}!Jg$S{4 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 literal 0 HcmV?d00001 diff --git a/tools/linux64/dfu-util/dfu-suffix b/tools/linux64/dfu-util/dfu-suffix new file mode 100644 index 0000000000000000000000000000000000000000..e96df29485e168b14d7ef6a128b98897557f9c50 GIT binary patch literal 47528 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

m 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 MAK=A3@NwJ!za>UQ{r~^~ literal 0 HcmV?d00001 diff --git a/tools/linux64/install.sh b/tools/linux64/install.sh new file mode 100644 index 0000000..29ddb2f --- /dev/null +++ b/tools/linux64/install.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if sudo [ -w /etc/udev/rules.d ]; then + echo "Copying Maple-specific udev rules..." + sudo cp -v 45-maple.rules /etc/udev/rules.d/45-maple.rules + sudo chown root:root /etc/udev/rules.d/45-maple.rules + sudo chmod 644 /etc/udev/rules.d/45-maple.rules + sudo cp -v 49-stlinkv1.rules /etc/udev/rules.d/49-stlinkv1.rules + sudo chown root:root /etc/udev/rules.d/49-stlinkv1.rules + sudo chmod 644 /etc/udev/rules.d/49-stlinkv1.rules + sudo cp -v 49-stlinkv2.rules /etc/udev/rules.d/49-stlinkv2.rules + sudo chown root:root /etc/udev/rules.d/49-stlinkv2.rules + sudo chmod 644 /etc/udev/rules.d/49-stlinkv2.rules + sudo cp -v 49-stlinkv2-1.rules /etc/udev/rules.d/49-stlinkv2-1.rules + sudo chown root:root /etc/udev/rules.d/49-stlinkv2-1.rules + sudo chmod 644 /etc/udev/rules.d/49-stlinkv2-1.rules + echo "Reloading udev rules" + sudo udevadm control --reload-rules + echo "Adding current user to dialout group" + sudo adduser $USER dialout +else + echo "Couldn't copy to /etc/udev/rules.d/; you probably have to run this script as root? Or your distribution of Linux doesn't include udev; try running the IDE itself as root." +fi + diff --git a/tools/linux64/maple_upload b/tools/linux64/maple_upload new file mode 100644 index 0000000..e799f3a --- /dev/null +++ b/tools/linux64/maple_upload @@ -0,0 +1,40 @@ +#!/bin/bash + +#set -e + + + +if [ $# -lt 4 ]; then + echo "Usage: $0 $# " >&2 + exit 1 +fi +dummy_port="$1"; altID="$2"; usbID="$3"; binfile="$4"; dummy_port_fullpath="/dev/$1" +if [ $# -eq 5 ]; then + dfuse_addr="--dfuse-address $5" +else + dfuse_addr="" +fi + + +# Get the directory where the script is running. +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# ----------------- IMPORTANT ----------------- +# The 2nd parameter to upload-reset is the delay after resetting before it exits +# This value is in milliseonds +# You may need to tune this to your system +# 750ms to 1500ms seems to work on my Mac + + +"${DIR}/upload-reset" ${dummy_port_fullpath} 750 + + +#DFU_UTIL=$(dirname $0)/dfu-util/dfu-util +DFU_UTIL=/usr/bin/dfu-util +DFU_UTIL=${DIR}/dfu-util/dfu-util +if [ ! -x "${DFU_UTIL}" ]; then + echo "$0: error: cannot find ${DFU_UTIL}" >&2 + exit 2 +fi + +"${DFU_UTIL}" -d ${usbID} -a ${altID} -D ${binfile} ${dfuse_addr} -R diff --git a/tools/linux64/readme.txt b/tools/linux64/readme.txt new file mode 100644 index 0000000..2d13beb --- /dev/null +++ b/tools/linux64/readme.txt @@ -0,0 +1 @@ +The maple upload script needs its rights to be set to 755 \ No newline at end of file diff --git a/tools/linux64/serial_upload b/tools/linux64/serial_upload new file mode 100644 index 0000000..05d17c6 --- /dev/null +++ b/tools/linux64/serial_upload @@ -0,0 +1,2 @@ +#!/bin/bash +$(dirname $0)/stm32flash/stm32flash -g 0x8000000 -b 230400 -w "$4" /dev/"$1" diff --git a/tools/linux64/src/build_dfu-util.sh b/tools/linux64/src/build_dfu-util.sh new file mode 100644 index 0000000..3563f57 --- /dev/null +++ b/tools/linux64/src/build_dfu-util.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo apt-get build-dep dfu-util +sudo apt-get install build-essentials +sudo apt-get install libusb-1.0-0-dev +sudo apt-get install autoconf automake autotools-dev + +cd dfu-util +./autogen.sh +./configure +make +cp src/dfu-util ../../linux/dfu-util +cp src/dfu-suffix ../../linux/dfu-util +cp src/dfu-prefix ../../linux/dfu-util + diff --git a/tools/linux64/src/dfu-util/AUTHORS b/tools/linux64/src/dfu-util/AUTHORS new file mode 100644 index 0000000..1b36c73 --- /dev/null +++ b/tools/linux64/src/dfu-util/AUTHORS @@ -0,0 +1,30 @@ +Authors ordered by first contribution. + +Harald Welte +Werner Almesberger +Michael Lauer +Jim Huang +Stefan Schmidt +Daniel Willmann +Mike Frysinger +Uwe Hermann +C. Scott Ananian +Bernard Blackham +Holger Freyther +Marc Singer +James Perkins +Tommi Keisala +Pascal Schweizer +Bradley Scott +Uwe Bonnes +Andrey Smirnov +Jussi Timperi +Hans Petter Selasky +Bo Shen +Henrique de Almeida Mendonca +Bernd Krumboeck +Dennis Meier +Veli-Pekka Peltola +Dave Hylands +Michael Grzeschik +Paul Fertser diff --git a/tools/linux64/src/dfu-util/COPYING b/tools/linux64/src/dfu-util/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/tools/linux64/src/dfu-util/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/tools/linux64/src/dfu-util/ChangeLog b/tools/linux64/src/dfu-util/ChangeLog new file mode 100644 index 0000000..37f1add --- /dev/null +++ b/tools/linux64/src/dfu-util/ChangeLog @@ -0,0 +1,93 @@ +0.8: + o New, separate dfu-prefix tool (Uwe Bonnes) + o Allow filtering on serial number (Uwe Bonnes) + o Improved VID/PID/serial filtering (Bradley Scott) + o Support reading firmware from stdin (Tormod Volden) + o Warn if missing DFU suffix (Tormod Volden) + o Improved progress bar (Hans Petter Selasky) + o Fix dfuse leave option (Uwe Bonnes) + o Major code rework (Hans Petter Selasky) + o MS Visual Studio build support (Henrique Mendonca) + o dfuse-pack.py tool for .dfu files (Antonio Galeo) + o Many other fixes from many people + +2014-09-13: Tormod Volden + +0.7: + o Support for TI Stellaris devices (Tommi Keisala) + o Fix libusb detection on MacOSX (Marc Singer) + o Fix libusb detection on FreeBSD (Tormod Volden) + o Improved DfuSe support (Tormod Volden) + o Support all special commands (leave, unprotect, mass-erase) + o Arbitrary upload lengths + o "force" option for various possible (dangerous) overrides + +2012-10-07: Tormod Volden + +0.6: + o Add detach mode (Stefan Schmidt) + o Check return value on all libusb calls (Tormod Volden) + o Fix segmentation fault with -s option (Tormod Volden) + o Add DFU suffix manipulation tool (Stefan Schmidt) + o Port to Windows: (Tormod Volden, some parts based on work from Satz + Klauer) + o Port file handling to stdio streams + o Sleep() macros + o C99 types + o Pack structs + o Detect DfuSe device correctly on big-endian architectures (Tormod + Volden) + o Add dfuse progress indication on download (Tormod Volden) + o Cleanup: gcc pedantic, gcc extension, ... (Tormod Volden) + o Rely on page size from functional descriptor. Please report if you get + an error about it. (Tormod Volden) + o Add quirk for Maple since it reports wrong DFU version (Tormod Volden) + +2012-04-22: Stefan Schmidt + +0.5: + o DfuSe extension support for ST devices (Tormod Volden) + o Add initial support for bitWillDetach flag from DFU 1.1 (Tormod + Volden) + o Internal cleanup and some manual page fixes (Tormod Volden) + +2011-11-02: Stefan Schmidt + +0.4: + o Rework to use libusb-1.0 (Stefan Schmidt) + o DFU suffix support (Tormod Volden, Stefan Schmidt) + o Sspeed up DFU downloads directly into memory (Bernard Blackham) + o More flexible -d vid:pid parsing (Tormod Volden) + o Many bug fixes and cleanups + +2011-07-20: Stefan Schmidt + +0.3: + o quirks: Add OpenOCD to the poll timeout quirk table. + +2010-12-22: Stefan Schmidt + +0.2: + o Fix some typos on the website and the README (Antonio Ospite, Uwe + Hermann) + o Remove build rule for a static binary. We can use autotools for this. + (Mike Frysinger) + o Fix infinite loop in download error path (C. Scott Ananian) + o Break out to show the 'finished' in upload (C. Scott Ananian) + o Add GPLv2+ headers (Harald Welte) + o Remove dead code (commands.[ch]) remnescent of dfu-programmer (Harald + Welte) + o Simple quirk system with Openmoko quirk for missing bwPollTimeout (Tormod Volden) + o New default (1024) and clamping of transfer size (Tormod Volden) + o Verify sending of completion packet (Tormod Volden) + o Look for DFU functional descriptor among all descriptors (Tormod + Volden) + o Print out in which direction we are transferring data + o Abort in upload if the file already exists + +2010-11-17 Stefan Schmidt + +0.1: + Initial release + +2010-05-23 Stefan Schmidt diff --git a/tools/linux64/src/dfu-util/DEVICES.txt b/tools/linux64/src/dfu-util/DEVICES.txt new file mode 100644 index 0000000..bdd9f1f --- /dev/null +++ b/tools/linux64/src/dfu-util/DEVICES.txt @@ -0,0 +1,20 @@ +List of supported software and hardware products: + +Software user (bootloader, etc) +------------------------------- +- Sam7DFU: http://www.openpcd.org/Sam7dfu +- U-boot: DFU patches +- Barebox: http://www.barebox.org/ +- Leaflabs: http://code.google.com/p/leaflabs/ +- Blackmagic DFU + +Products using DFU +------------------ +- OpenPCD (sam7dfu) +- Openmoko Neo 1973 and Freerunner (u-boot with DFU patches) +- Leaflabs Maple +- ATUSB from Qi Hardware +- STM32F105/7, STM32F2/F3/F4 in System Bootloader +- Blackmagic debug probe +- NXP LPC31xx/LPC43XX, e.g. LPC-Link and LPC-Link2, need binaries + with LPC prefix and encoding (LPC-Link) diff --git a/tools/linux64/src/dfu-util/Makefile.am b/tools/linux64/src/dfu-util/Makefile.am new file mode 100644 index 0000000..641dda5 --- /dev/null +++ b/tools/linux64/src/dfu-util/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src doc + +EXTRA_DIST = autogen.sh TODO DEVICES.txt dfuse-pack.py diff --git a/tools/linux64/src/dfu-util/README b/tools/linux64/src/dfu-util/README new file mode 100644 index 0000000..0f8f262 --- /dev/null +++ b/tools/linux64/src/dfu-util/README @@ -0,0 +1,20 @@ +Dfu-util - Device Firmware Upgrade Utilities + +Dfu-util is the host side implementation of the DFU 1.0 [1] and DFU 1.1 [2] +specification of the USB forum. + +DFU is intended to download and upload firmware to devices connected over +USB. It ranges from small devices like micro-controller boards up to mobile +phones. With dfu-util you are able to download firmware to your device or +upload firmware from it. + +dfu-util has been tested with Openmoko Neo1973 and Freerunner and many +other devices. + +[1] DFU 1.0 spec: http://www.usb.org/developers/devclass_docs/usbdfu10.pdf +[2] DFU 1.1 spec: http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf + +The official website is: + + http://dfu-util.gnumonks.org/ + diff --git a/tools/linux64/src/dfu-util/TODO b/tools/linux64/src/dfu-util/TODO new file mode 100644 index 0000000..900c30c --- /dev/null +++ b/tools/linux64/src/dfu-util/TODO @@ -0,0 +1,14 @@ +DfuSe: +- Do erase and write in two separate passes when downloading +- Skip "Set Address" command when downloading contiguous blocks +- Implement "Get Commands" command + +Devices: +- Research iPhone/iPod/iPad support + Heavily modified dfu-util fork here: + https://github.com/planetbeing/xpwn/tree/master/dfu-util +- Test against Niftylights + +Non-Code: +- Logo +- Re-License as LGPL for usage as library? diff --git a/tools/linux64/src/dfu-util/autogen.sh b/tools/linux64/src/dfu-util/autogen.sh new file mode 100644 index 0000000..e67aed3 --- /dev/null +++ b/tools/linux64/src/dfu-util/autogen.sh @@ -0,0 +1,2 @@ +#! /bin/sh +autoreconf -v -i diff --git a/tools/linux64/src/dfu-util/configure.ac b/tools/linux64/src/dfu-util/configure.ac new file mode 100644 index 0000000..8622114 --- /dev/null +++ b/tools/linux64/src/dfu-util/configure.ac @@ -0,0 +1,41 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([dfu-util],[0.8],[dfu-util@lists.gnumonks.org],,[http://dfu-util.gnumonks.org]) +AC_CONFIG_AUX_DIR(m4) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([config.h]) + +# Test for new silent rules and enable only if they are available +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +# On FreeBSD the libusb-1.0 is called libusb and resides in system location +AC_CHECK_LIB([usb], [libusb_init],, [native_libusb=no],) +AS_IF([test x$native_libusb = xno], [ + PKG_CHECK_MODULES([USB], [libusb-1.0 >= 1.0.0],, + AC_MSG_ERROR([*** Required libusb-1.0 >= 1.0.0 not installed ***])) +]) +AC_CHECK_LIB([usbpath],[usb_path2devnum],,,-lusb) + +LIBS="$LIBS $USB_LIBS" +CFLAGS="$CFLAGS $USB_CFLAGS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([usbpath.h windows.h sysexits.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([ftruncate getpagesize nanosleep err]) + +AC_CONFIG_FILES(Makefile src/Makefile doc/Makefile) +AC_OUTPUT diff --git a/tools/linux64/src/dfu-util/device-logs/README b/tools/linux64/src/dfu-util/device-logs/README new file mode 100644 index 0000000..00d3d1a --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/README @@ -0,0 +1,77 @@ +Device: +------- +qi-hardware-atusb: +- Qi Hardware ben-wpan +- DFU implementation: + http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw/usb +- Tester: Stefan Schmidt + +openpcd: +- OpenPCD RFID reader +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +simtrace: +- Sysmocom SimTrace +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +openmoko-freerunner: +- Openmoko Freerunner +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +openmoko-neo1973: +- Openmoko Neo1073 +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +tdk-bluetooth: +- TDK Corp. Bluetooth Adapter +- DFU implementation: closed soure +- Only upload has been tested +- Tester: Stefan Schmidt + +stm32f107: +- STM32 microcontrollers with built-in (ROM) DFU loader +- DFU implementation: Closed source but probably similar to the one + in their USB device libraries. Some relevant application notes: + http://www.st.com -> AN3156 and AN2606 +- Tested by Uwe Bonnes + +stm32f4discovery: +- STM32 microcontroller board with built-in (ROM) DFU loader +- DFU implementation: Closed source, probably similar to stm32f107. +- Tested by Joe Rothweiler + +dso-nano: +- DSO Nano pocket oscilloscope +- DFU implementation: Based on ST Microelectronics USB FS Library 1.0 + http://dsonano.googlecode.com/files/DS0201_OpenSource.rar +- Tester: Tormod Volden + +opc-20: +- Custom devices based on STM32F1xx +- DFU implementation: ST Microelectronics USB FS Device Library 3.1.0 + http://www.st.com -> um0424.zip +- Tester: Tormod Volden + +lpc-link, lpclink2: +- NXP LPCXpresso debug adapters +- Proprietary DFU implementation, uses special download files with + LPC prefix and encoding of the target firmware code +- Tested by Uwe Bonnes + +Adding the lsusb output and a download log of your device here helps +us to avoid regressions for hardware we cannot test while working on +the code. To extract the lsusb output use this command: +sudo lsusb -v -d $USBID > $DEVICE.lsusb +Prepare a description snippet as above, and send it to us. A log +(copy-paste of the command window) of a firmware download is also +nice, please use the double verbose option -v -v and include the +command line in the log file. + diff --git a/tools/linux64/src/dfu-util/device-logs/dsonano.lsusb b/tools/linux64/src/dfu-util/device-logs/dsonano.lsusb new file mode 100644 index 0000000..140a7bc --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/dsonano.lsusb @@ -0,0 +1,60 @@ + +Bus 002 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 1.1a + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 001 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 5 @SPI Flash : M25P64/0x00000000/64*064Kg,64*064Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/lpclink.log b/tools/linux64/src/dfu-util/device-logs/lpclink.log new file mode 100644 index 0000000..7de4dd3 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/lpclink.log @@ -0,0 +1,59 @@ +(The on-board LPC3154 has some encryption key set and LPCXpressoWIN.enc +is encrypted.) + +$ lsusb | grep NXP +Bus 003 Device 011: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link + +$ dfu-util -v -v -v -R -D /opt/lpc/lpcxpresso/bin/LPCXpressoWIN.enc + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Deducing device DFU version from functional descriptor length +Opening DFU capable USB device... +ID 0471:df55 +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 6% 2048 bytes +Download [=== ] 13% 4096 bytes +Download [==== ] 19% 6144 bytes +Download [====== ] 26% 8192 bytes +Download [======== ] 32% 10240 bytes +Download [========= ] 39% 12288 bytes +Download [=========== ] 45% 14336 bytes +Download [============= ] 52% 16384 bytes +Download [============== ] 59% 18432 bytes +Download [================ ] 65% 20480 bytes +Download [================== ] 72% 22528 bytes +Download [=================== ] 78% 24576 bytes +Download [===================== ] 85% 26624 bytes +Download [====================== ] 91% 28672 bytes +Download [======================== ] 98% 29192 bytes +Download [=========================] 100% 29192 bytes +Download done. +Sent a total of 29192 bytes +state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present +Done! +dfu-util: can't detach +Resetting USB to switch back to runtime mode + +$ lsusb | grep NXP +Bus 003 Device 012: ID 1fc9:0009 NXP Semiconductors diff --git a/tools/linux64/src/dfu-util/device-logs/lpclink.lsusb b/tools/linux64/src/dfu-util/device-logs/lpclink.lsusb new file mode 100644 index 0000000..867b2a2 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/lpclink.lsusb @@ -0,0 +1,58 @@ + +Bus 003 Device 008: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0471 Philips (or NXP) + idProduct 0xdf55 LPCXpresso LPC-Link + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 25 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 1 + Will Not Detach + Manifestation Intolerant + Upload Unsupported + Download Supported + wDetachTimeout 65535 milliseconds + wTransferSize 2048 bytes +Device Qualifier (for other device speed): + bLength 10 + bDescriptorType 6 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + bNumConfigurations 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/lpclink2.log b/tools/linux64/src/dfu-util/device-logs/lpclink2.log new file mode 100644 index 0000000..4681eff --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/lpclink2.log @@ -0,0 +1,59 @@ +$ lsusb | grep NXP +Bus 003 Device 013: ID 1fc9:000c NXP Semiconductors + +$ dfu-util -D ~/devel/dfu-util/firmware.bin.qthdr + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Possible unencryptes NXP LPC DFU prefix with the following properties +Payload length: 39 kiByte +Opening DFU capable USB device... +ID 1fc9:000c +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 4% 2048 bytes +Download [== ] 9% 4096 bytes +Download [=== ] 14% 6144 bytes +Download [==== ] 19% 8192 bytes +Download [====== ] 24% 10240 bytes +Download [======= ] 28% 12288 bytes +Download [======== ] 33% 14336 bytes +Download [========= ] 38% 16384 bytes +Download [========== ] 43% 18432 bytes +Download [============ ] 48% 20480 bytes +Download [============= ] 53% 22528 bytes +Download [============== ] 57% 24576 bytes +Download [=============== ] 62% 26624 bytes +Download [================ ] 67% 28672 bytes +Download [================== ] 72% 30720 bytes +Download [=================== ] 77% 32768 bytes +Download [==================== ] 82% 34816 bytes +Download [===================== ] 86% 36864 bytes +Download [====================== ] 91% 38912 bytes +Download [======================== ] 96% 40356 bytes +Download [=========================] 100% 40356 bytes +Download done. +Sent a total of 40356 bytes +dfu-util: unable to read DFU status + +$ lsusb | grep NXP +Bus 003 Device 014: ID 1fc9:0018 NXP Semiconductors diff --git a/tools/linux64/src/dfu-util/device-logs/lpclink2.lsusb b/tools/linux64/src/dfu-util/device-logs/lpclink2.lsusb new file mode 100644 index 0000000..b833fca --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/lpclink2.lsusb @@ -0,0 +1,203 @@ + +Bus 003 Device 007: ID 0c72:000c PEAK System PCAN-USB +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x0c72 PEAK System + idProduct 0x000c PCAN-USB + bcdDevice 1c.ff + iManufacturer 0 + iProduct 3 VER1:PEAK +VER2:02.8.01 +DAT :06.05.2004 +TIME:09:35:37 + ... + iSerial 0 + bNumConfigurations 3 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 2 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 394mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 3 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0001 + Self Powered diff --git a/tools/linux64/src/dfu-util/device-logs/opc-20.lsusb b/tools/linux64/src/dfu-util/device-logs/opc-20.lsusb new file mode 100644 index 0000000..580df90 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/opc-20.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 2.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 ÿÿÿÿÿÿÿÿÿÿÿÿ + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : M25P64/0x00000000/128*64Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1a.01 +Device Status: 0x0001 + Self Powered diff --git a/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb b/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb new file mode 100644 index 0000000..4c0abfb --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb @@ -0,0 +1,109 @@ +Bus 003 Device 017: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 81 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 7 USB Device Firmware Upgrade + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 8 RAM 0x32000000 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 9 u-boot + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 10 u-boot_env + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 11 kernel + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 4 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 12 splash + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 5 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 13 factory + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 6 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 14 rootfs + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 +Device Status: 0x0a00 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner.lsusb b/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner.lsusb new file mode 100644 index 0000000..835708d --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/openmoko-freerunner.lsusb @@ -0,0 +1,179 @@ +Bus 005 Device 033: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 USB Device Firmware Upgrade + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x9a00 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/openmoko-neo1973.lsusb b/tools/linux64/src/dfu-util/device-logs/openmoko-neo1973.lsusb new file mode 100644 index 0000000..0778950 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/openmoko-neo1973.lsusb @@ -0,0 +1,182 @@ + +Bus 006 Device 020: ID 1457:5119 First International Computer, Inc. OpenMoko Neo1973 u-boot cdc_acm serial port +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1457 First International Computer, Inc. + idProduct 0x5119 OpenMoko Neo1973 u-boot cdc_acm serial port + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x0006 + (Bus Powered) + Remote Wakeup Enabled + Test Mode diff --git a/tools/linux64/src/dfu-util/device-logs/openpcd.lsusb b/tools/linux64/src/dfu-util/device-logs/openpcd.lsusb new file mode 100644 index 0000000..f6255a9 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/openpcd.lsusb @@ -0,0 +1,60 @@ + +Bus 006 Device 016: ID 16c0:076b VOTI OpenPCD 13.56MHz RFID Reader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x076b OpenPCD 13.56MHz RFID Reader + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 OpenPCD RFID Simulator - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/qi-hardware-atusb.lsusb b/tools/linux64/src/dfu-util/device-logs/qi-hardware-atusb.lsusb new file mode 100644 index 0000000..bfc1701 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/qi-hardware-atusb.lsusb @@ -0,0 +1,59 @@ + +Bus 006 Device 013: ID 20b7:1540 Qi Hardware ben-wpan, AT86RF230-based +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x20b7 Qi Hardware + idProduct 0x1540 ben-wpan, AT86RF230-based + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 1 4630333438371508231a + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 2 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 40mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/simtrace.lsusb b/tools/linux64/src/dfu-util/device-logs/simtrace.lsusb new file mode 100644 index 0000000..578ddf0 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/simtrace.lsusb @@ -0,0 +1,70 @@ + +Bus 006 Device 017: ID 16c0:0762 VOTI +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x0762 + bcdDevice 0.00 + iManufacturer 1 sysmocom - systems for mobile communications GmbH + iProduct 2 SimTrace SIM Sniffer - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 45 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 3 SimTrace DFU Configuration + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 SimTrace DFU Interface - Application Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 SimTrace DFU Interface - Bootloader Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 SimTrace DFU Interface - RAM + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/sparkcore.lsusb b/tools/linux64/src/dfu-util/device-logs/sparkcore.lsusb new file mode 100644 index 0000000..b6029ff --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/sparkcore.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 008: ID 1d50:607f OpenMoko, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x607f + bcdDevice 2.00 + iManufacturer 1 Spark Devices + iProduct 2 CORE DFU + iSerial 3 8D80527B5055 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/20*001Ka,108*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : SST25x/0x00000000/512*04Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/linux64/src/dfu-util/device-logs/stm32f107.bin-download b/tools/linux64/src/dfu-util/device-logs/stm32f107.bin-download new file mode 100644 index 0000000..45b714f --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/stm32f107.bin-download @@ -0,0 +1,48 @@ +> src/dfu-util --intf 0 --alt 0 -v -v -v -s 0x8000000 -D test3 +dfu-util 0.4 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Opening DFU USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/128*002Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +No valid DFU suffix signature +Warning: File has no DFU suffix +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 128 x 2048 = 262144 (rew) +Uploading to address = 0x08000000, size = 16384 +Erasing page size 2048 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 +Erasing page size 2048 at address 0x08000800, page starting at 0x08000800 + Download from image offset 00000800 to memory 08000800-08000fff, size 2048 + Setting address pointer to 0x08000800 +Erasing page size 2048 at address 0x08001000, page starting at 0x08001000 + Download from image offset 00001000 to memory 08001000-080017ff, size 2048 + Setting address pointer to 0x08001000 +Erasing page size 2048 at address 0x08001800, page starting at 0x08001800 + Download from image offset 00001800 to memory 08001800-08001fff, size 2048 + Setting address pointer to 0x08001800 +Erasing page size 2048 at address 0x08002000, page starting at 0x08002000 + Download from image offset 00002000 to memory 08002000-080027ff, size 2048 + Setting address pointer to 0x08002000 +Erasing page size 2048 at address 0x08002800, page starting at 0x08002800 + Download from image offset 00002800 to memory 08002800-08002fff, size 2048 + Setting address pointer to 0x08002800 +Erasing page size 2048 at address 0x08003000, page starting at 0x08003000 + Download from image offset 00003000 to memory 08003000-080037ff, size 2048 + Setting address pointer to 0x08003000 +Erasing page size 2048 at address 0x08003800, page starting at 0x08003800 + Download from image offset 00003800 to memory 08003800-08003fff, size 2048 + Setting address pointer to 0x08003800 + diff --git a/tools/linux64/src/dfu-util/device-logs/stm32f107.lsusb b/tools/linux64/src/dfu-util/device-logs/stm32f107.lsusb new file mode 100644 index 0000000..14b45cd --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/stm32f107.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 028: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 20.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 0x418 DFU Bootloader + iSerial 3 STM32 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/128*002Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFF800/01*016 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.bin-download b/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.bin-download new file mode 100644 index 0000000..96e1722 --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.bin-download @@ -0,0 +1,36 @@ +dfu-util --device 0483:df11 --alt 0 \ + --dfuse-address 0x08000000 \ + -v -v -v \ + --download arm/iotoggle.bin +No valid DFU suffix signature +Warning: File has no DFU suffix +dfu-util 0.5 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Filter on vendor = 0x0483 product = 0xdf11 +Opening DFU capable USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuERROR, status = 10 +dfuERROR, clearing status +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 4 x 16384 = 65536 (rew) +Memory segment at 0x08010000 1 x 65536 = 65536 (rew) +Memory segment at 0x08020000 7 x 131072 = 917504 (rew) +Uploading to address = 0x08000000, size = 2308 +Erasing page size 16384 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 + Download from image offset 00000800 to memory 08000800-08000903, size 260 + Setting address pointer to 0x08000800 diff --git a/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.lsusb b/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.lsusb new file mode 100644 index 0000000..0b870de --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/stm32f4discovery.lsusb @@ -0,0 +1,80 @@ + +Bus 001 Device 010: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 21.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 BOOTLOADER + iSerial 3 315A28A0B956 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 54 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFC000/01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 @OTP Memory /0x1FFF7800/01*512 g,01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 7 @Device Feature/0xFFFF0000/01*004 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/linux64/src/dfu-util/device-logs/tdk-bluetooth.lsusb b/tools/linux64/src/dfu-util/device-logs/tdk-bluetooth.lsusb new file mode 100644 index 0000000..c0cface --- /dev/null +++ b/tools/linux64/src/dfu-util/device-logs/tdk-bluetooth.lsusb @@ -0,0 +1,269 @@ + +Bus 006 Device 014: ID 04bf:0320 TDK Corp. Bluetooth Adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 224 Wireless + bDeviceSubClass 1 Radio Frequency + bDeviceProtocol 1 Bluetooth + bMaxPacketSize0 64 + idVendor 0x04bf TDK Corp. + idProduct 0x0320 Bluetooth Adapter + bcdDevice 26.52 + iManufacturer 1 Ezurio + iProduct 2 Turbo Bluetooth Adapter + iSerial 3 008098D4FFBD + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 193 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 1 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 2 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 3 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 4 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 5 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 5000 milliseconds + wTransferSize 1023 bytes +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/linux64/src/dfu-util/dfuse-pack.py b/tools/linux64/src/dfu-util/dfuse-pack.py new file mode 100644 index 0000000..875cc5c --- /dev/null +++ b/tools/linux64/src/dfu-util/dfuse-pack.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +# Written by Antonio Galea - 2010/11/18 +# Distributed under Gnu LGPL 3.0 +# see http://www.gnu.org/licenses/lgpl-3.0.txt + +import sys,struct,zlib,os +from optparse import OptionParser + +DEFAULT_DEVICE="0x0483:0xdf11" + +def named(tuple,names): + return dict(zip(names.split(),tuple)) +def consume(fmt,data,names): + n = struct.calcsize(fmt) + return named(struct.unpack(fmt,data[:n]),names),data[n:] +def cstring(string): + return string.split('\0',1)[0] +def compute_crc(data): + return 0xFFFFFFFF & -zlib.crc32(data) -1 + +def parse(file,dump_images=False): + print 'File: "%s"' % file + data = open(file,'rb').read() + crc = compute_crc(data[:-4]) + prefix, data = consume('<5sBIB',data,'signature version size targets') + print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix + for t in range(prefix['targets']): + tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements') + tprefix['num'] = t + if tprefix['named']: + tprefix['name'] = cstring(tprefix['name']) + else: + tprefix['name'] = '' + print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix + tsize = tprefix['size'] + target, data = data[:tsize], data[tsize:] + for e in range(tprefix['elements']): + eprefix, target = consume('<2I',target,'address size') + eprefix['num'] = e + print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix + esize = eprefix['size'] + image, target = target[:esize], target[esize:] + if dump_images: + out = '%s.target%d.image%d.bin' % (file,t,e) + open(out,'wb').write(image) + print ' DUMPED IMAGE TO "%s"' % out + if len(target): + print "target %d: PARSE ERROR" % t + suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc') + print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix + if crc != suffix['crc']: + print "CRC ERROR: computed crc32 is 0x%08x" % crc + data = data[16:] + if data: + print "PARSE ERROR" + +def build(file,targets,device=DEFAULT_DEVICE): + data = '' + for t,target in enumerate(targets): + tdata = '' + for image in target: + tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data'] + tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata + data += tdata + data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data + v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1)) + data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16) + crc = compute_crc(data) + data += struct.pack(' and +Harald Welte . Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +.SH LICENCE +.B dfu-util +is covered by the GNU General Public License (GPL), version 2 or later. +.SH COPYRIGHT +This manual page was originally written by Uwe Hermann , +and is now part of the dfu-util project. diff --git a/tools/linux64/src/dfu-util/msvc/README_msvc.txt b/tools/linux64/src/dfu-util/msvc/README_msvc.txt new file mode 100644 index 0000000..6e68ec6 --- /dev/null +++ b/tools/linux64/src/dfu-util/msvc/README_msvc.txt @@ -0,0 +1,10 @@ +# (C) Roger Meier +# (C) Pascal Schweizer +# msvc folder is GPL-2.0+, LGPL-2.1+, BSD-3-Clause or MIT license(SPDX) + +Building dfu-util native on Windows with Visual Studio + +3rd party dependencies: +- libusbx ( git clone https://github.com/libusbx/libusbx.git ) + - getopt (part of libusbx: libusbx/examples/getopt) + diff --git a/tools/linux64/src/dfu-util/msvc/dfu-suffix_2010.vcxproj b/tools/linux64/src/dfu-util/msvc/dfu-suffix_2010.vcxproj new file mode 100644 index 0000000..0c316c2 --- /dev/null +++ b/tools/linux64/src/dfu-util/msvc/dfu-suffix_2010.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA} + dfusuffix + dfu-suffix + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/linux64/src/dfu-util/msvc/dfu-util_2010.sln b/tools/linux64/src/dfu-util/msvc/dfu-util_2010.sln new file mode 100644 index 0000000..ef79723 --- /dev/null +++ b/tools/linux64/src/dfu-util/msvc/dfu-util_2010.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-util", "dfu-util_2010.vcxproj", "{0E071A60-7EF2-4427-BAA8-9143CACB5BCB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4F8746D-B27E-4806-95E5-2052174E923B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-suffix", "dfu-suffix_2010.vcxproj", "{8F7600A2-3B37-4956-B39B-A1D43EF29EDA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt_2010", "..\..\libusbx\msvc\getopt_2010.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\..\libusbx\msvc\libusb_static_2010.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.Build.0 = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|x64.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.ActiveCfg = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.Build.0 = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|x64.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.Build.0 = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|x64.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.Build.0 = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|x64.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/linux64/src/dfu-util/msvc/dfu-util_2010.vcxproj b/tools/linux64/src/dfu-util/msvc/dfu-util_2010.vcxproj new file mode 100644 index 0000000..17a8bee --- /dev/null +++ b/tools/linux64/src/dfu-util/msvc/dfu-util_2010.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB} + dfuutil + dfu-util + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + copy $(SolutionDir)..\$(Platform)\$(Configuration)\dll\libusb-1.0.dll $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/linux64/src/dfu-util/src/Makefile.am b/tools/linux64/src/dfu-util/src/Makefile.am new file mode 100644 index 0000000..70179c4 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/Makefile.am @@ -0,0 +1,28 @@ +AM_CFLAGS = -Wall -Wextra + +bin_PROGRAMS = dfu-util dfu-suffix dfu-prefix +dfu_util_SOURCES = main.c \ + portable.h \ + dfu_load.c \ + dfu_load.h \ + dfu_util.c \ + dfu_util.h \ + dfuse.c \ + dfuse.h \ + dfuse_mem.c \ + dfuse_mem.h \ + dfu.c \ + dfu.h \ + usb_dfu.h \ + dfu_file.c \ + dfu_file.h \ + quirks.c \ + quirks.h + +dfu_suffix_SOURCES = suffix.c \ + dfu_file.h \ + dfu_file.c + +dfu_prefix_SOURCES = prefix.c \ + dfu_file.h \ + dfu_file.c diff --git a/tools/linux64/src/dfu-util/src/dfu.c b/tools/linux64/src/dfu-util/src/dfu.c new file mode 100644 index 0000000..14d7673 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu.c @@ -0,0 +1,357 @@ +/* + * Low-level DFU communication routines, originally taken from + * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ + * (part of dfu-programmer). + * + * Copyright 2005-2006 Weston Schmidt + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "quirks.h" + +static int dfu_timeout = 5000; /* 5 seconds - default */ + +/* + * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * timeout - the timeout in ms the USB device should wait for a pending + * USB reset before giving up and terminating the operation + * + * returns 0 or < 0 on error + */ +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DETACH, + /* wValue */ timeout, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the total number of bytes to transfer to the USB + * device - must be less than wTransferSize + * data - the data to transfer + * + * returns the number of bytes written or < 0 on error + */ +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the number of bytes received or < 0 on error + */ +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * status - the data structure to be populated with the results + * + * return the number of bytes read in or < 0 on an error + */ +int dfu_get_status( struct dfu_if *dif, struct dfu_status *status ) +{ + unsigned char buffer[6]; + int result; + + /* Initialize the status data structure */ + status->bStatus = DFU_STATUS_ERROR_UNKNOWN; + status->bwPollTimeout = 0; + status->bState = STATE_DFU_ERROR; + status->iString = 0; + + result = libusb_control_transfer( dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATUS, + /* wValue */ 0, + /* wIndex */ dif->interface, + /* Data */ buffer, + /* wLength */ 6, + dfu_timeout ); + + if( 6 == result ) { + status->bStatus = buffer[0]; + if (dif->quirks & QUIRK_POLLTIMEOUT) + status->bwPollTimeout = DEFAULT_POLLTIMEOUT; + else + status->bwPollTimeout = ((0xff & buffer[3]) << 16) | + ((0xff & buffer[2]) << 8) | + (0xff & buffer[1]); + status->bState = buffer[4]; + status->iString = buffer[5]; + } + + return result; +} + + +/* + * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * return 0 or < 0 on an error + */ +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_CLRSTATUS, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the state or < 0 on error + */ +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ) +{ + int result; + unsigned char buffer[1]; + + result = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATE, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ buffer, + /* wLength */ 1, + dfu_timeout ); + + /* Return the error if there is one. */ + if (result < 1) + return -1; + + /* Return the state. */ + return buffer[0]; +} + + +/* + * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * returns 0 or < 0 on an error + */ +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_ABORT, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +const char* dfu_state_to_string( int state ) +{ + const char *message; + + switch (state) { + case STATE_APP_IDLE: + message = "appIDLE"; + break; + case STATE_APP_DETACH: + message = "appDETACH"; + break; + case STATE_DFU_IDLE: + message = "dfuIDLE"; + break; + case STATE_DFU_DOWNLOAD_SYNC: + message = "dfuDNLOAD-SYNC"; + break; + case STATE_DFU_DOWNLOAD_BUSY: + message = "dfuDNBUSY"; + break; + case STATE_DFU_DOWNLOAD_IDLE: + message = "dfuDNLOAD-IDLE"; + break; + case STATE_DFU_MANIFEST_SYNC: + message = "dfuMANIFEST-SYNC"; + break; + case STATE_DFU_MANIFEST: + message = "dfuMANIFEST"; + break; + case STATE_DFU_MANIFEST_WAIT_RESET: + message = "dfuMANIFEST-WAIT-RESET"; + break; + case STATE_DFU_UPLOAD_IDLE: + message = "dfuUPLOAD-IDLE"; + break; + case STATE_DFU_ERROR: + message = "dfuERROR"; + break; + default: + message = NULL; + break; + } + + return message; +} + +/* Chapter 6.1.2 */ +static const char *dfu_status_names[] = { + /* DFU_STATUS_OK */ + "No error condition is present", + /* DFU_STATUS_errTARGET */ + "File is not targeted for use by this device", + /* DFU_STATUS_errFILE */ + "File is for this device but fails some vendor-specific test", + /* DFU_STATUS_errWRITE */ + "Device is unable to write memory", + /* DFU_STATUS_errERASE */ + "Memory erase function failed", + /* DFU_STATUS_errCHECK_ERASED */ + "Memory erase check failed", + /* DFU_STATUS_errPROG */ + "Program memory function failed", + /* DFU_STATUS_errVERIFY */ + "Programmed memory failed verification", + /* DFU_STATUS_errADDRESS */ + "Cannot program memory due to received address that is out of range", + /* DFU_STATUS_errNOTDONE */ + "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", + /* DFU_STATUS_errFIRMWARE */ + "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", + /* DFU_STATUS_errVENDOR */ + "iString indicates a vendor specific error", + /* DFU_STATUS_errUSBR */ + "Device detected unexpected USB reset signalling", + /* DFU_STATUS_errPOR */ + "Device detected unexpected power on reset", + /* DFU_STATUS_errUNKNOWN */ + "Something went wrong, but the device does not know what it was", + /* DFU_STATUS_errSTALLEDPKT */ + "Device stalled an unexpected request" +}; + + +const char *dfu_status_to_string(int status) +{ + if (status > DFU_STATUS_errSTALLEDPKT) + return "INVALID"; + return dfu_status_names[status]; +} + +int dfu_abort_to_idle(struct dfu_if *dif) +{ + int ret; + struct dfu_status dst; + + ret = dfu_abort(dif->dev_handle, dif->interface); + if (ret < 0) { + errx(EX_IOERR, "Error sending dfu abort request"); + exit(1); + } + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during abort get_status"); + exit(1); + } + if (dst.bState != DFU_STATE_dfuIDLE) { + errx(EX_IOERR, "Failed to enter idle state on abort"); + exit(1); + } + milli_sleep(dst.bwPollTimeout); + return ret; +} diff --git a/tools/linux64/src/dfu-util/src/dfu.h b/tools/linux64/src/dfu-util/src/dfu.h new file mode 100644 index 0000000..8e3caeb --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu.h @@ -0,0 +1,133 @@ +/* + * dfu-programmer + * + * $Id: dfu.h,v 1.2 2005/09/25 01:27:42 schmidtw Exp $ + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFU_H +#define DFU_H + +#include +#include "usb_dfu.h" + +/* DFU states */ +#define STATE_APP_IDLE 0x00 +#define STATE_APP_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + + +/* DFU status */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_ERROR_TARGET 0x01 +#define DFU_STATUS_ERROR_FILE 0x02 +#define DFU_STATUS_ERROR_WRITE 0x03 +#define DFU_STATUS_ERROR_ERASE 0x04 +#define DFU_STATUS_ERROR_CHECK_ERASED 0x05 +#define DFU_STATUS_ERROR_PROG 0x06 +#define DFU_STATUS_ERROR_VERIFY 0x07 +#define DFU_STATUS_ERROR_ADDRESS 0x08 +#define DFU_STATUS_ERROR_NOTDONE 0x09 +#define DFU_STATUS_ERROR_FIRMWARE 0x0a +#define DFU_STATUS_ERROR_VENDOR 0x0b +#define DFU_STATUS_ERROR_USBR 0x0c +#define DFU_STATUS_ERROR_POR 0x0d +#define DFU_STATUS_ERROR_UNKNOWN 0x0e +#define DFU_STATUS_ERROR_STALLEDPKT 0x0f + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +/* DFU interface */ +#define DFU_IFF_DFU 0x0001 /* DFU Mode, (not Runtime) */ + +/* This is based off of DFU_GETSTATUS + * + * 1 unsigned byte bStatus + * 3 unsigned byte bwPollTimeout + * 1 unsigned byte bState + * 1 unsigned byte iString +*/ + +struct dfu_status { + unsigned char bStatus; + unsigned int bwPollTimeout; + unsigned char bState; + unsigned char iString; +}; + +struct dfu_if { + struct usb_dfu_func_descriptor func_dfu; + uint16_t quirks; + uint16_t busnum; + uint16_t devnum; + uint16_t vendor; + uint16_t product; + uint16_t bcdDevice; + uint8_t configuration; + uint8_t interface; + uint8_t altsetting; + uint8_t flags; + uint8_t bMaxPacketSize0; + char *alt_name; + char *serial_name; + libusb_device *dev; + libusb_device_handle *dev_handle; + struct dfu_if *next; +}; + +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ); +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_get_status( struct dfu_if *dif, + struct dfu_status *status ); +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ); +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort_to_idle( struct dfu_if *dif); + +const char *dfu_state_to_string( int state ); + +const char *dfu_status_to_string( int status ); + +#endif /* DFU_H */ diff --git a/tools/linux64/src/dfu-util/src/dfu_file.c b/tools/linux64/src/dfu-util/src/dfu_file.c new file mode 100644 index 0000000..7c897d4 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_file.c @@ -0,0 +1,444 @@ +/* + * Load or store DFU files including suffix and prefix + * + * Copyright 2014 Tormod Volden + * Copyright 2012 Stefan Schmidt + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +#define DFU_SUFFIX_LENGTH 16 +#define LMDFU_PREFIX_LENGTH 8 +#define LPCDFU_PREFIX_LENGTH 16 +#define PROGRESS_BAR_WIDTH 25 +#define STDIN_CHUNK_SIZE 65536 + +static const unsigned long crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +static uint32_t crc32_byte(uint32_t accum, uint8_t delta) +{ + return crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); +} + +static int probe_prefix(struct dfu_file *file) +{ + uint8_t *prefix = file->firmware; + + if (file->size.total < LMDFU_PREFIX_LENGTH) + return 1; + if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { + file->prefix_type = LMDFU_PREFIX; + file->size.prefix = LMDFU_PREFIX_LENGTH; + file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); + } + else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f)== 0x3f)) { + file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; + file->size.prefix = LPCDFU_PREFIX_LENGTH; + } + + if (file->size.prefix + file->size.suffix > file->size.total) + return 1; + return 0; +} + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max) +{ + static char buf[PROGRESS_BAR_WIDTH + 1]; + static unsigned long long last_progress = -1; + static time_t last_time; + time_t curr_time = time(NULL); + unsigned long long progress; + unsigned long long x; + + /* check for not known maximum */ + if (max < curr) + max = curr + 1; + /* make none out of none give zero */ + if (max == 0 && curr == 0) + max = 1; + + /* compute completion */ + progress = (PROGRESS_BAR_WIDTH * curr) / max; + if (progress > PROGRESS_BAR_WIDTH) + progress = PROGRESS_BAR_WIDTH; + if (progress == last_progress && + curr_time == last_time) + return; + last_progress = progress; + last_time = curr_time; + + for (x = 0; x != PROGRESS_BAR_WIDTH; x++) { + if (x < progress) + buf[x] = '='; + else + buf[x] = ' '; + } + buf[x] = 0; + + printf("\r%s\t[%s] %3lld%% %12lld bytes", desc, buf, + (100ULL * curr) / max, curr); + + if (progress == PROGRESS_BAR_WIDTH) + printf("\n%s done.\n", desc); +} + +void *dfu_malloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL) + errx(EX_SOFTWARE, "Cannot allocate memory of size %d bytes", (int)size); + return (ptr); +} + +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size) +{ + int x; + + /* compute CRC */ + for (x = 0; x != size; x++) + crc = crc32_byte(crc, ((uint8_t *)buf)[x]); + + /* write data */ + if (write(f, buf, size) != size) + err(EX_IOERR, "Could not write %d bytes to file %d", size, f); + + return (crc); +} + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix) +{ + off_t offset; + int f; + int i; + int res; + + file->size.prefix = 0; + file->size.suffix = 0; + + /* default values, if no valid suffix is found */ + file->bcdDFU = 0; + file->idVendor = 0xffff; /* wildcard value */ + file->idProduct = 0xffff; /* wildcard value */ + file->bcdDevice = 0xffff; /* wildcard value */ + + /* default values, if no valid prefix is found */ + file->lmdfu_address = 0; + + free(file->firmware); + + if (!strcmp(file->name, "-")) { + int read_bytes; + +#ifdef WIN32 + _setmode( _fileno( stdin ), _O_BINARY ); +#endif + file->firmware = (uint8_t*) dfu_malloc(STDIN_CHUNK_SIZE); + read_bytes = fread(file->firmware, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total = read_bytes; + while (read_bytes == STDIN_CHUNK_SIZE) { + file->firmware = (uint8_t*) realloc(file->firmware, file->size.total + STDIN_CHUNK_SIZE); + if (!file->firmware) + err(EX_IOERR, "Could not allocate firmware buffer"); + read_bytes = fread(file->firmware + file->size.total, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total += read_bytes; + } + if (verbose) + printf("Read %i bytes from stdin\n", file->size.total); + /* Never require suffix when reading from stdin */ + check_suffix = MAYBE_SUFFIX; + } else { + f = open(file->name, O_RDONLY | O_BINARY); + if (f < 0) + err(EX_IOERR, "Could not open file %s for reading", file->name); + + offset = lseek(f, 0, SEEK_END); + + if ((int)offset < 0 || (int)offset != offset) + err(EX_IOERR, "File size is too big"); + + if (lseek(f, 0, SEEK_SET) != 0) + err(EX_IOERR, "Could not seek to beginning"); + + file->size.total = offset; + file->firmware = dfu_malloc(file->size.total); + + if (read(f, file->firmware, file->size.total) != file->size.total) { + err(EX_IOERR, "Could not read %d bytes from %s", + file->size.total, file->name); + } + close(f); + } + + /* Check for possible DFU file suffix by trying to parse one */ + { + uint32_t crc = 0xffffffff; + const uint8_t *dfusuffix; + int missing_suffix = 0; + const char *reason; + + if (file->size.total < DFU_SUFFIX_LENGTH) { + reason = "File too short for DFU suffix"; + missing_suffix = 1; + goto checked; + } + + dfusuffix = file->firmware + file->size.total - + DFU_SUFFIX_LENGTH; + + for (i = 0; i < file->size.total - 4; i++) + crc = crc32_byte(crc, file->firmware[i]); + + if (dfusuffix[10] != 'D' || + dfusuffix[9] != 'F' || + dfusuffix[8] != 'U') { + reason = "Invalid DFU suffix signature"; + missing_suffix = 1; + goto checked; + } + + file->dwCRC = (dfusuffix[15] << 24) + + (dfusuffix[14] << 16) + + (dfusuffix[13] << 8) + + dfusuffix[12]; + + if (file->dwCRC != crc) { + reason = "DFU suffix CRC does not match"; + missing_suffix = 1; + goto checked; + } + + /* At this point we believe we have a DFU suffix + so we require further checks to succeed */ + + file->bcdDFU = (dfusuffix[7] << 8) + dfusuffix[6]; + + if (verbose) + printf("DFU suffix version %x\n", file->bcdDFU); + + file->size.suffix = dfusuffix[11]; + + if (file->size.suffix < DFU_SUFFIX_LENGTH) { + errx(EX_IOERR, "Unsupported DFU suffix length %d", + file->size.suffix); + } + + if (file->size.suffix > file->size.total) { + errx(EX_IOERR, "Invalid DFU suffix length %d", + file->size.suffix); + } + + file->idVendor = (dfusuffix[5] << 8) + dfusuffix[4]; + file->idProduct = (dfusuffix[3] << 8) + dfusuffix[2]; + file->bcdDevice = (dfusuffix[1] << 8) + dfusuffix[0]; + +checked: + if (missing_suffix) { + if (check_suffix == NEEDS_SUFFIX) { + warnx("%s", reason); + errx(EX_IOERR, "Valid DFU suffix needed"); + } else if (check_suffix == MAYBE_SUFFIX) { + warnx("%s", reason); + warnx("A valid DFU suffix will be required in " + "a future dfu-util release!!!"); + } + } else { + if (check_suffix == NO_SUFFIX) { + errx(EX_SOFTWARE, "Please remove existing DFU suffix before adding a new one.\n"); + } + } + } + res = probe_prefix(file); + if ((res || file->size.prefix == 0) && check_prefix == NEEDS_PREFIX) + errx(EX_IOERR, "Valid DFU prefix needed"); + if (file->size.prefix && check_prefix == NO_PREFIX) + errx(EX_IOERR, "A prefix already exists, please delete it first"); + if (file->size.prefix && verbose) { + uint8_t *data = file->firmware; + if (file->prefix_type == LMDFU_PREFIX) + printf("Possible TI Stellaris DFU prefix with " + "the following properties\n" + "Address: 0x%08x\n" + "Payload length: %d\n", + file->lmdfu_address, + data[4] | (data[5] << 8) | + (data[6] << 16) | (data[7] << 14)); + else if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) + printf("Possible unencrypted NXP LPC DFU prefix with " + "the following properties\n" + "Payload length: %d kiByte\n", + data[2] >>1 | (data[3] << 7) ); + else + errx(EX_IOERR, "Unknown DFU prefix type"); + } +} + +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix) +{ + uint32_t crc = 0xffffffff; + int f; + + f = open(file->name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0666); + if (f < 0) + err(EX_IOERR, "Could not open file %s for writing", file->name); + + /* write prefix, if any */ + if (write_prefix) { + if (file->prefix_type == LMDFU_PREFIX) { + uint8_t lmdfu_prefix[LMDFU_PREFIX_LENGTH]; + uint32_t addr = file->lmdfu_address / 1024; + + /* lmdfu_dfu_prefix payload length excludes prefix and suffix */ + uint32_t len = file->size.total - + file->size.prefix - file->size.suffix; + + lmdfu_prefix[0] = 0x01; /* STELLARIS_DFU_PROG */ + lmdfu_prefix[1] = 0x00; /* Reserved */ + lmdfu_prefix[2] = (uint8_t)(addr & 0xff); + lmdfu_prefix[3] = (uint8_t)(addr >> 8); + lmdfu_prefix[4] = (uint8_t)(len & 0xff); + lmdfu_prefix[5] = (uint8_t)(len >> 8) & 0xff; + lmdfu_prefix[6] = (uint8_t)(len >> 16) & 0xff; + lmdfu_prefix[7] = (uint8_t)(len >> 24); + + crc = dfu_file_write_crc(f, crc, lmdfu_prefix, LMDFU_PREFIX_LENGTH); + } + if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { + uint8_t lpcdfu_prefix[LPCDFU_PREFIX_LENGTH] = {0}; + int i; + + /* Payload is firmware and prefix rounded to 512 bytes */ + uint32_t len = (file->size.total - file->size.suffix + 511) /512; + + lpcdfu_prefix[0] = 0x1a; /* Unencypted*/ + lpcdfu_prefix[1] = 0x3f; /* Reserved */ + lpcdfu_prefix[2] = (uint8_t)(len & 0xff); + lpcdfu_prefix[3] = (uint8_t)((len >> 8) & 0xff); + for (i = 12; i < LPCDFU_PREFIX_LENGTH; i++) + lpcdfu_prefix[i] = 0xff; + + crc = dfu_file_write_crc(f, crc, lpcdfu_prefix, LPCDFU_PREFIX_LENGTH); + } + } + /* write firmware binary */ + crc = dfu_file_write_crc(f, crc, file->firmware + file->size.prefix, + file->size.total - file->size.prefix - file->size.suffix); + + /* write suffix, if any */ + if (write_suffix) { + uint8_t dfusuffix[DFU_SUFFIX_LENGTH]; + + dfusuffix[0] = file->bcdDevice & 0xff; + dfusuffix[1] = file->bcdDevice >> 8; + dfusuffix[2] = file->idProduct & 0xff; + dfusuffix[3] = file->idProduct >> 8; + dfusuffix[4] = file->idVendor & 0xff; + dfusuffix[5] = file->idVendor >> 8; + dfusuffix[6] = file->bcdDFU & 0xff; + dfusuffix[7] = file->bcdDFU >> 8; + dfusuffix[8] = 'U'; + dfusuffix[9] = 'F'; + dfusuffix[10] = 'D'; + dfusuffix[11] = DFU_SUFFIX_LENGTH; + + crc = dfu_file_write_crc(f, crc, dfusuffix, + DFU_SUFFIX_LENGTH - 4); + + dfusuffix[12] = crc; + dfusuffix[13] = crc >> 8; + dfusuffix[14] = crc >> 16; + dfusuffix[15] = crc >> 24; + + crc = dfu_file_write_crc(f, crc, dfusuffix + 12, 4); + } + close(f); +} + +void show_suffix_and_prefix(struct dfu_file *file) +{ + if (file->size.prefix == LMDFU_PREFIX_LENGTH) { + printf("The file %s contains a TI Stellaris DFU prefix with the following properties:\n", file->name); + printf("Address:\t0x%08x\n", file->lmdfu_address); + } else if (file->size.prefix == LPCDFU_PREFIX_LENGTH) { + uint8_t * prefix = file->firmware; + printf("The file %s contains a NXP unencrypted LPC DFU prefix with the following properties:\n", file->name); + printf("Size:\t%5d kiB\n", prefix[2]>>1|prefix[3]<<7); + } else if (file->size.prefix != 0) { + printf("The file %s contains an unknown prefix\n", file->name); + } + if (file->size.suffix > 0) { + printf("The file %s contains a DFU suffix with the following properties:\n", file->name); + printf("BCD device:\t0x%04X\n", file->bcdDevice); + printf("Product ID:\t0x%04X\n",file->idProduct); + printf("Vendor ID:\t0x%04X\n", file->idVendor); + printf("BCD DFU:\t0x%04X\n", file->bcdDFU); + printf("Length:\t\t%i\n", file->size.suffix); + printf("CRC:\t\t0x%08X\n", file->dwCRC); + } +} diff --git a/tools/linux64/src/dfu-util/src/dfu_file.h b/tools/linux64/src/dfu-util/src/dfu_file.h new file mode 100644 index 0000000..abebd44 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_file.h @@ -0,0 +1,60 @@ + +#ifndef DFU_FILE_H +#define DFU_FILE_H + +#include + +struct dfu_file { + /* File name */ + const char *name; + /* Pointer to file loaded into memory */ + uint8_t *firmware; + /* Different sizes */ + struct { + int total; + int prefix; + int suffix; + } size; + /* From prefix fields */ + uint32_t lmdfu_address; + /* From prefix fields */ + uint32_t prefix_type; + + /* From DFU suffix fields */ + uint32_t dwCRC; + uint16_t bcdDFU; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; +}; + +enum suffix_req { + NO_SUFFIX, + NEEDS_SUFFIX, + MAYBE_SUFFIX +}; + +enum prefix_req { + NO_PREFIX, + NEEDS_PREFIX, + MAYBE_PREFIX +}; + +enum prefix_type { + ZERO_PREFIX, + LMDFU_PREFIX, + LPCDFU_UNENCRYPTED_PREFIX +}; + +extern int verbose; + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix); +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix); + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max); +void *dfu_malloc(size_t size); +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size); +void show_suffix_and_prefix(struct dfu_file *file); + +#endif /* DFU_FILE_H */ diff --git a/tools/linux64/src/dfu-util/src/dfu_load.c b/tools/linux64/src/dfu-util/src/dfu_load.c new file mode 100644 index 0000000..64f7009 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_load.c @@ -0,0 +1,196 @@ +/* + * DFU transfer routines + * + * This is supposed to be a general DFU implementation, as specified in the + * USB DFU 1.0 and 1.1 specification. + * + * The code was originally intended to interface with a USB device running the + * "sam7dfu" firmware (see http://www.openpcd.org/) on an AT91SAM7 processor. + * + * Copyright 2007-2008 Harald Welte + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "quirks.h" + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, + int expected_size, int fd) +{ + int total_bytes = 0; + unsigned short transaction = 0; + unsigned char *buf; + int ret; + + buf = dfu_malloc(xfer_size); + + printf("Copying data from DFU device to PC\n"); + dfu_progress_bar("Upload", 0, 1); + + while (1) { + int rc; + rc = dfu_upload(dif->dev_handle, dif->interface, + xfer_size, transaction++, buf); + if (rc < 0) { + warnx("Error during upload"); + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes (wraparound)"); + + if (rc < xfer_size) { + /* last block, return */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, expected_size); + } + ret = 0; + +out_free: + dfu_progress_bar("Upload", total_bytes, total_bytes); + if (total_bytes == 0) + printf("\nFailed.\n"); + free(buf); + if (verbose) + printf("Received a total of %i bytes\n", total_bytes); + if (expected_size != 0 && total_bytes != expected_size) + errx(EX_SOFTWARE, "Unexpected number of bytes uploaded from device"); + return ret; +} + +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) +{ + int bytes_sent; + int expected_size; + unsigned char *buf; + unsigned short transaction = 0; + struct dfu_status dst; + int ret; + + printf("Copying data from PC to DFU device\n"); + + buf = file->firmware; + expected_size = file->size.total - file->size.suffix; + bytes_sent = 0; + + dfu_progress_bar("Download", 0, 1); + while (bytes_sent < expected_size) { + int bytes_left; + int chunk_size; + + bytes_left = expected_size - bytes_sent; + if (bytes_left < xfer_size) + chunk_size = bytes_left; + else + chunk_size = xfer_size; + + ret = dfu_download(dif->dev_handle, dif->interface, + chunk_size, transaction++, chunk_size ? buf : NULL); + if (ret < 0) { + warnx("Error during download"); + goto out; + } + bytes_sent += chunk_size; + buf += chunk_size; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + goto out; + } + + if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE || + dst.bState == DFU_STATE_dfuERROR) + break; + + /* Wait while device executes flashing */ + milli_sleep(dst.bwPollTimeout); + + } while (1); + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + ret = -1; + goto out; + } + dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left); + } + + /* send one zero sized download request to signalize end */ + ret = dfu_download(dif->dev_handle, dif->interface, + 0, transaction, NULL); + if (ret < 0) { + errx(EX_IOERR, "Error sending completion packet"); + goto out; + } + + dfu_progress_bar("Download", bytes_sent, bytes_sent); + + if (verbose) + printf("Sent a total of %i bytes\n", bytes_sent); + +get_status: + /* Transition to MANIFEST_SYNC state */ + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + warnx("unable to read DFU status after completion"); + goto out; + } + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + + milli_sleep(dst.bwPollTimeout); + + /* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */ + switch (dst.bState) { + case DFU_STATE_dfuMANIFEST_SYNC: + case DFU_STATE_dfuMANIFEST: + /* some devices (e.g. TAS1020b) need some time before we + * can obtain the status */ + milli_sleep(1000); + goto get_status; + break; + case DFU_STATE_dfuIDLE: + break; + } + printf("Done!\n"); + +out: + return bytes_sent; +} diff --git a/tools/linux64/src/dfu-util/src/dfu_load.h b/tools/linux64/src/dfu-util/src/dfu_load.h new file mode 100644 index 0000000..be23e9b --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_load.h @@ -0,0 +1,7 @@ +#ifndef DFU_LOAD_H +#define DFU_LOAD_H + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, int expected_size, int fd); +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file); + +#endif /* DFU_LOAD_H */ diff --git a/tools/linux64/src/dfu-util/src/dfu_util.c b/tools/linux64/src/dfu-util/src/dfu_util.c new file mode 100644 index 0000000..b94c7cc --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_util.c @@ -0,0 +1,346 @@ +/* + * Functions for detecting DFU USB entities + * + * Written by Harald Welte + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013 Hans Petter Selasky + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +/* + * Look for a descriptor in a concatenated descriptor list. Will + * return upon the first match of the given descriptor type. Returns length of + * found descriptor, limited to res_size + */ +static int find_descriptor(const uint8_t *desc_list, int list_len, + uint8_t desc_type, void *res_buf, int res_size) +{ + int p = 0; + + if (list_len < 2) + return (-1); + + while (p + 1 < list_len) { + int desclen; + + desclen = (int) desc_list[p]; + if (desclen == 0) { + warnx("Invalid descriptor list"); + return -1; + } + if (desc_list[p + 1] == desc_type) { + if (desclen > res_size) + desclen = res_size; + if (p + desclen > list_len) + desclen = list_len - p; + memcpy(res_buf, &desc_list[p], desclen); + return desclen; + } + p += (int) desc_list[p]; + } + return -1; +} + +static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) +{ + struct usb_dfu_func_descriptor func_dfu; + libusb_device_handle *devh; + struct dfu_if *pdfu; + struct libusb_config_descriptor *cfg; + const struct libusb_interface_descriptor *intf; + const struct libusb_interface *uif; + char alt_name[MAX_DESC_STR_LEN + 1]; + char serial_name[MAX_DESC_STR_LEN + 1]; + int cfg_idx; + int intf_idx; + int alt_idx; + int ret; + int has_dfu; + + for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { + memset(&func_dfu, 0, sizeof(func_dfu)); + has_dfu = 0; + + ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); + if (ret != 0) + return; + if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { + libusb_free_config_descriptor(cfg); + continue; + } + + /* + * In some cases, noticably FreeBSD if uid != 0, + * the configuration descriptors are empty + */ + if (!cfg) + return; + + ret = find_descriptor(cfg->extra, cfg->extra_length, + USB_DT_DFU, &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; + alt_idx++) { + intf = &uif->altsetting[alt_idx]; + + ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, + &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + has_dfu = 1; + } + } + if (has_dfu) { + /* + * Finally try to retrieve it requesting the + * device directly This is not supported on + * all devices for non-standard types + */ + if (libusb_open(dev, &devh) == 0) { + ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, + (void *)&func_dfu, sizeof(func_dfu)); + libusb_close(devh); + if (ret > -1) + goto found_dfu; + } + warnx("Device has DFU interface, " + "but has no DFU functional descriptor"); + + /* fake version 1.0 */ + func_dfu.bLength = 7; + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + goto found_dfu; + } + libusb_free_config_descriptor(cfg); + continue; + +found_dfu: + if (func_dfu.bLength == 7) { + printf("Deducing device DFU version from functional descriptor " + "length\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + } else if (func_dfu.bLength < 9) { + printf("Error obtaining DFU functional descriptor\n"); + printf("Please report this as a bug!\n"); + printf("Warning: Assuming DFU version 1.0\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + printf("Warning: Transfer size can not be detected\n"); + func_dfu.wTransferSize = 0; + } + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + if (match_iface_index > -1 && match_iface_index != intf_idx) + continue; + + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; + alt_idx < uif->num_altsetting; alt_idx++) { + int dfu_mode; + + intf = &uif->altsetting[alt_idx]; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + dfu_mode = (intf->bInterfaceProtocol == 2); + /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ + if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) + dfu_mode = 1; + + if (dfu_mode && + match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) + continue; + + if (dfu_mode) { + if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || + (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { + continue; + } + } else { + if ((match_vendor >= 0 && match_vendor != desc->idVendor) || + (match_product >= 0 && match_product != desc->idProduct)) { + continue; + } + } + + if (libusb_open(dev, &devh)) { + warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); + break; + } + if (intf->iInterface != 0) + ret = libusb_get_string_descriptor_ascii(devh, + intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(alt_name, "UNKNOWN"); + if (desc->iSerialNumber != 0) + ret = libusb_get_string_descriptor_ascii(devh, + desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(serial_name, "UNKNOWN"); + libusb_close(devh); + + if (dfu_mode && + match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) + continue; + + if (dfu_mode) { + if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) + continue; + } else { + if (match_serial != NULL && strcmp(match_serial, serial_name)) + continue; + } + + pdfu = dfu_malloc(sizeof(*pdfu)); + + memset(pdfu, 0, sizeof(*pdfu)); + + pdfu->func_dfu = func_dfu; + pdfu->dev = libusb_ref_device(dev); + pdfu->quirks = get_quirks(desc->idVendor, + desc->idProduct, desc->bcdDevice); + pdfu->vendor = desc->idVendor; + pdfu->product = desc->idProduct; + pdfu->bcdDevice = desc->bcdDevice; + pdfu->configuration = cfg->bConfigurationValue; + pdfu->interface = intf->bInterfaceNumber; + pdfu->altsetting = intf->bAlternateSetting; + pdfu->devnum = libusb_get_device_address(dev); + pdfu->busnum = libusb_get_bus_number(dev); + pdfu->alt_name = strdup(alt_name); + if (pdfu->alt_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + pdfu->serial_name = strdup(serial_name); + if (pdfu->serial_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + if (dfu_mode) + pdfu->flags |= DFU_IFF_DFU; + if (pdfu->quirks & QUIRK_FORCE_DFU11) { + pdfu->func_dfu.bcdDFUVersion = + libusb_cpu_to_le16(0x0110); + } + pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; + + /* queue into list */ + pdfu->next = dfu_root; + dfu_root = pdfu; + } + } + libusb_free_config_descriptor(cfg); + } +} + +void probe_devices(libusb_context *ctx) +{ + libusb_device **list; + ssize_t num_devs; + ssize_t i; + + num_devs = libusb_get_device_list(ctx, &list); + for (i = 0; i < num_devs; ++i) { + struct libusb_device_descriptor desc; + struct libusb_device *dev = list[i]; + + if (match_bus > -1 && match_bus != libusb_get_bus_number(dev)) + continue; + if (match_device > -1 && match_device != libusb_get_device_address(dev)) + continue; + if (libusb_get_device_descriptor(dev, &desc)) + continue; + probe_configuration(dev, &desc); + } + libusb_free_device_list(list, 0); +} + +void disconnect_devices(void) +{ + struct dfu_if *pdfu; + struct dfu_if *prev = NULL; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) { + free(prev); + libusb_unref_device(pdfu->dev); + free(pdfu->alt_name); + free(pdfu->serial_name); + prev = pdfu; + } + free(prev); + dfu_root = NULL; +} + +void print_dfu_if(struct dfu_if *dfu_if) +{ + printf("Found %s: [%04x:%04x] ver=%04x, devnum=%u, cfg=%u, intf=%u, " + "alt=%u, name=\"%s\", serial=\"%s\"\n", + dfu_if->flags & DFU_IFF_DFU ? "DFU" : "Runtime", + dfu_if->vendor, dfu_if->product, + dfu_if->bcdDevice, dfu_if->devnum, + dfu_if->configuration, dfu_if->interface, + dfu_if->altsetting, dfu_if->alt_name, + dfu_if->serial_name); +} + +/* Walk the device tree and print out DFU devices */ +void list_dfu_interfaces(void) +{ + struct dfu_if *pdfu; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) + print_dfu_if(pdfu); +} diff --git a/tools/linux64/src/dfu-util/src/dfu_util.h b/tools/linux64/src/dfu-util/src/dfu_util.h new file mode 100644 index 0000000..fc0c19d --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfu_util.h @@ -0,0 +1,36 @@ +#ifndef DFU_UTIL_H +#define DFU_UTIL_H + +/* USB string descriptor should contain max 126 UTF-16 characters + * but 253 would even accomodate any UTF-8 encoding */ +#define MAX_DESC_STR_LEN 253 + +enum mode { + MODE_NONE, + MODE_VERSION, + MODE_LIST, + MODE_DETACH, + MODE_UPLOAD, + MODE_DOWNLOAD +}; + +extern struct dfu_if *dfu_root; +extern int match_bus; +extern int match_device; +extern int match_vendor; +extern int match_product; +extern int match_vendor_dfu; +extern int match_product_dfu; +extern int match_config_index; +extern int match_iface_index; +extern int match_iface_alt_index; +extern const char *match_iface_alt_name; +extern const char *match_serial; +extern const char *match_serial_dfu; + +void probe_devices(libusb_context *); +void disconnect_devices(void); +void print_dfu_if(struct dfu_if *); +void list_dfu_interfaces(void); + +#endif /* DFU_UTIL_H */ diff --git a/tools/linux64/src/dfu-util/src/dfuse.c b/tools/linux64/src/dfu-util/src/dfuse.c new file mode 100644 index 0000000..fce29fe --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfuse.c @@ -0,0 +1,652 @@ +/* + * DfuSe specific functions + * + * This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) + * The DfuSe file format is described in ST document UM0391. + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfuse.h" +#include "dfuse_mem.h" + +#define DFU_TIMEOUT 5000 + +extern int verbose; +static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ +static struct memsegment *mem_layout; +static unsigned int dfuse_address = 0; +static unsigned int dfuse_length = 0; +static int dfuse_force = 0; +static int dfuse_leave = 0; +static int dfuse_unprotect = 0; +static int dfuse_mass_erase = 0; + +unsigned int quad2uint(unsigned char *p) +{ + return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); +} + +void dfuse_parse_options(const char *options) +{ + char *end; + const char *endword; + unsigned int number; + + /* address, possibly empty, must be first */ + if (*options != ':') { + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); /* GNU strchrnul */ + + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_address = number; + } else { + errx(EX_IOERR, "Invalid dfuse address: %s", options); + } + options = endword; + } + + while (*options) { + if (*options == ':') { + options++; + continue; + } + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); + + if (!strncmp(options, "force", endword - options)) { + dfuse_force++; + options += 5; + continue; + } + if (!strncmp(options, "leave", endword - options)) { + dfuse_leave = 1; + options += 5; + continue; + } + if (!strncmp(options, "unprotect", endword - options)) { + dfuse_unprotect = 1; + options += 9; + continue; + } + if (!strncmp(options, "mass-erase", endword - options)) { + dfuse_mass_erase = 1; + options += 10; + continue; + } + + /* any valid number is interpreted as upload length */ + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_length = number; + } else { + errx(EX_IOERR, "Invalid dfuse modifier: %s", options); + } + options = endword; + } +} + +/* DFU_UPLOAD request for DfuSe 1.1a */ +int dfuse_upload(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_msg returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DFU_DNLOAD request for DfuSe 1.1a */ +int dfuse_download(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_transfer returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DfuSe only commands */ +/* Leaves the device in dfuDNLOAD-IDLE state */ +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command) +{ + const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", + "MASS_ERASE", "READ_UNPROTECT"}; + unsigned char buf[5]; + int length; + int ret; + struct dfu_status dst; + int firstpoll = 1; + + if (command == ERASE_PAGE) { + struct memsegment *segment; + int page_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { + errx(EX_IOERR, "Page at 0x%08x can not be erased", + address); + } + page_size = segment->pagesize; + if (verbose > 1) + printf("Erasing page size %i at address 0x%08x, page " + "starting at 0x%08x\n", page_size, address, + address & ~(page_size - 1)); + buf[0] = 0x41; /* Erase command */ + length = 5; + last_erased_page = address & ~(page_size - 1); + } else if (command == SET_ADDRESS) { + if (verbose > 2) + printf(" Setting address pointer to 0x%08x\n", + address); + buf[0] = 0x21; /* Set Address Pointer command */ + length = 5; + } else if (command == MASS_ERASE) { + buf[0] = 0x41; /* Mass erase command when length = 1 */ + length = 1; + } else if (command == READ_UNPROTECT) { + buf[0] = 0x92; + length = 1; + } else { + errx(EX_IOERR, "Non-supported special command %d", command); + } + buf[1] = address & 0xff; + buf[2] = (address >> 8) & 0xff; + buf[3] = (address >> 16) & 0xff; + buf[4] = (address >> 24) & 0xff; + + ret = dfuse_download(dif, length, buf, 0); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" download", + dfuse_command_name[command]); + } + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" get_status", + dfuse_command_name[command]); + } + if (firstpoll) { + firstpoll = 0; + if (dst.bState != DFU_STATE_dfuDNBUSY) { + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + errx(EX_IOERR, "Wrong state after command \"%s\" download", + dfuse_command_name[command]); + } + } + /* wait while command is executed */ + if (verbose) + printf(" Poll timeout %i ms\n", dst.bwPollTimeout); + milli_sleep(dst.bwPollTimeout); + if (command == READ_UNPROTECT) + return ret; + } while (dst.bState == DFU_STATE_dfuDNBUSY); + + if (dst.bStatus != DFU_STATUS_OK) { + errx(EX_IOERR, "%s not correctly executed", + dfuse_command_name[command]); + } + return ret; +} + +int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, + int transaction) +{ + int bytes_sent; + struct dfu_status dst; + int ret; + + ret = dfuse_download(dif, size, size ? data : NULL, transaction); + if (ret < 0) { + errx(EX_IOERR, "Error during download"); + return ret; + } + bytes_sent = ret; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + return ret; + } + milli_sleep(dst.bwPollTimeout); + } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && + dst.bState != DFU_STATE_dfuERROR && + dst.bState != DFU_STATE_dfuMANIFEST); + + if (dst.bState == DFU_STATE_dfuMANIFEST) + printf("Transitioning to dfuMANIFEST state\n"); + + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + return -1; + } + return bytes_sent; +} + +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options) +{ + int total_bytes = 0; + int upload_limit = 0; + unsigned char *buf; + int transaction; + int ret; + + buf = dfu_malloc(xfer_size); + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + if (dfuse_length) + upload_limit = dfuse_length; + if (dfuse_address) { + struct memsegment *segment; + + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) + errx(EX_IOERR, "Failed to parse memory layout"); + + segment = find_segment(mem_layout, dfuse_address); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_READABLE))) + errx(EX_IOERR, "Page at 0x%08x is not readable", + dfuse_address); + + if (!upload_limit) { + upload_limit = segment->end - dfuse_address + 1; + printf("Limiting upload to end of memory segment, " + "%i bytes\n", upload_limit); + } + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfu_abort_to_idle(dif); + } else { + /* Boot loader decides the start address, unknown to us */ + /* Use a short length to lower risk of running out of bounds */ + if (!upload_limit) + upload_limit = 0x4000; + printf("Limiting default upload to %i bytes\n", upload_limit); + } + + dfu_progress_bar("Upload", 0, 1); + + transaction = 2; + while (1) { + int rc; + + /* last chunk can be smaller than original xfer_size */ + if (upload_limit - total_bytes < xfer_size) + xfer_size = upload_limit - total_bytes; + rc = dfuse_upload(dif, xfer_size, buf, transaction++); + if (rc < 0) { + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes"); + + if (rc < xfer_size || total_bytes >= upload_limit) { + /* last block, return successfully */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, upload_limit); + } + + dfu_progress_bar("Upload", total_bytes, total_bytes); + + dfu_abort_to_idle(dif); + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + + out_free: + free(buf); + + return ret; +} + +/* Writes an element of any size to the device, taking care of page erases */ +/* returns 0 on success, otherwise -EINVAL */ +int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, + unsigned int dwElementSize, unsigned char *data, + int xfer_size) +{ + int p; + int ret; + struct memsegment *segment; + + /* Check at least that we can write to the last address */ + segment = + find_segment(mem_layout, dwElementAddress + dwElementSize - 1); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Last page at 0x%08x is not writeable", + dwElementAddress + dwElementSize - 1); + } + + dfu_progress_bar("Download", 0, 1); + + for (p = 0; p < (int)dwElementSize; p += xfer_size) { + int page_size; + unsigned int erase_address; + unsigned int address = dwElementAddress + p; + int chunk_size = xfer_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Page at 0x%08x is not writeable", + address); + } + page_size = segment->pagesize; + + /* check if this is the last chunk */ + if (p + chunk_size > (int)dwElementSize) + chunk_size = dwElementSize - p; + + /* Erase only for flash memory downloads */ + if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) { + /* erase all involved pages */ + for (erase_address = address; + erase_address < address + chunk_size; + erase_address += page_size) + if ((erase_address & ~(page_size - 1)) != + last_erased_page) + dfuse_special_command(dif, + erase_address, + ERASE_PAGE); + + if (((address + chunk_size - 1) & ~(page_size - 1)) != + last_erased_page) { + if (verbose > 2) + printf(" Chunk extends into next page," + " erase it as well\n"); + dfuse_special_command(dif, + address + chunk_size - 1, + ERASE_PAGE); + } + } + + if (verbose) { + printf(" Download from image offset " + "%08x to memory %08x-%08x, size %i\n", + p, address, address + chunk_size - 1, + chunk_size); + } else { + dfu_progress_bar("Download", p, dwElementSize); + } + + dfuse_special_command(dif, address, SET_ADDRESS); + + /* transaction = 2 for no address offset */ + ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2); + if (ret != chunk_size) { + errx(EX_IOERR, "Failed to write whole chunk: " + "%i of %i bytes", ret, chunk_size); + return -EINVAL; + } + } + if (!verbose) + dfu_progress_bar("Download", dwElementSize, dwElementSize); + return 0; +} + +static void +dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) +{ + if (size > *rem) { + errx(EX_IOERR, "Corrupt DfuSe file: " + "Cannot read %d bytes from %d bytes", size, *rem); + } + if (dst != NULL) + memcpy(dst, *src, size); + (*src) += size; + (*rem) -= size; +} + +/* Download raw binary file to DfuSe device */ +int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file, unsigned int start_address) +{ + unsigned int dwElementAddress; + unsigned int dwElementSize; + unsigned char *data; + int ret; + + dwElementAddress = start_address; + dwElementSize = file->size.total - + file->size.suffix - file->size.prefix; + + printf("Downloading to address = 0x%08x, size = %i\n", + dwElementAddress, dwElementSize); + + data = file->firmware + file->size.prefix; + + ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, + xfer_size); + if (ret != 0) + goto out_free; + + printf("File downloaded successfully\n"); + ret = dwElementSize; + + out_free: + return ret; +} + +/* Parse a DfuSe file and download contents to device */ +int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file) +{ + uint8_t dfuprefix[11]; + uint8_t targetprefix[274]; + uint8_t elementheader[8]; + int image; + int element; + int bTargets; + int bAlternateSetting; + int dwNbElements; + unsigned int dwElementAddress; + unsigned int dwElementSize; + uint8_t *data; + int ret; + int rem; + int bFirstAddressSaved = 0; + + rem = file->size.total - file->size.prefix - file->size.suffix; + data = file->firmware + file->size.prefix; + + /* Must be larger than a minimal DfuSe header and suffix */ + if (rem < (int)(sizeof(dfuprefix) + + sizeof(targetprefix) + sizeof(elementheader))) { + errx(EX_SOFTWARE, "File too small for a DfuSe file"); + } + + dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); + + if (strncmp((char *)dfuprefix, "DfuSe", 5)) { + errx(EX_IOERR, "No valid DfuSe signature"); + return -EINVAL; + } + if (dfuprefix[5] != 0x01) { + errx(EX_IOERR, "DFU format revision %i not supported", + dfuprefix[5]); + return -EINVAL; + } + bTargets = dfuprefix[10]; + printf("file contains %i DFU images\n", bTargets); + + for (image = 1; image <= bTargets; image++) { + printf("parsing DFU image %i\n", image); + dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); + if (strncmp((char *)targetprefix, "Target", 6)) { + errx(EX_IOERR, "No valid target signature"); + return -EINVAL; + } + bAlternateSetting = targetprefix[6]; + dwNbElements = quad2uint((unsigned char *)targetprefix + 270); + printf("image for alternate setting %i, ", bAlternateSetting); + printf("(%i elements, ", dwNbElements); + printf("total size = %i)\n", + quad2uint((unsigned char *)targetprefix + 266)); + if (bAlternateSetting != dif->altsetting) + printf("Warning: Image does not match current alternate" + " setting.\n" + "Please rerun with the correct -a option setting" + " to download this image!\n"); + for (element = 1; element <= dwNbElements; element++) { + printf("parsing element %i, ", element); + dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); + dwElementAddress = + quad2uint((unsigned char *)elementheader); + dwElementSize = + quad2uint((unsigned char *)elementheader + 4); + printf("address = 0x%08x, ", dwElementAddress); + printf("size = %i\n", dwElementSize); + + if (!bFirstAddressSaved) { + bFirstAddressSaved = 1; + dfuse_address = dwElementAddress; + } + /* sanity check */ + if ((int)dwElementSize > rem) + errx(EX_SOFTWARE, "File too small for element size"); + + if (bAlternateSetting == dif->altsetting) { + ret = dfuse_dnload_element(dif, dwElementAddress, + dwElementSize, data, xfer_size); + } else { + ret = 0; + } + + /* advance read pointer */ + dfuse_memcpy(NULL, &data, &rem, dwElementSize); + + if (ret != 0) + return ret; + } + } + + if (rem != 0) + warnx("%d bytes leftover", rem); + + printf("done parsing DfuSe file\n"); + + return 0; +} + +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options) +{ + int ret; + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) { + errx(EX_IOERR, "Failed to parse memory layout"); + } + if (dfuse_unprotect) { + if (!dfuse_force) { + errx(EX_IOERR, "The read unprotect command " + "will erase the flash memory" + "and can only be used with force\n"); + } + dfuse_special_command(dif, 0, READ_UNPROTECT); + printf("Device disconnects, erases flash and resets now\n"); + exit(0); + } + if (dfuse_mass_erase) { + if (!dfuse_force) { + errx(EX_IOERR, "The mass erase command " + "can only be used with force"); + } + printf("Performing mass erase, this can take a moment\n"); + dfuse_special_command(dif, 0, MASS_ERASE); + } + if (dfuse_address) { + if (file->bcdDFU == 0x11a) { + errx(EX_IOERR, "This is a DfuSe file, not " + "meant for raw download"); + } + ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); + } else { + if (file->bcdDFU != 0x11a) { + warnx("Only DfuSe file version 1.1a is supported"); + errx(EX_IOERR, "(for raw binary download, use the " + "--dfuse-address option)"); + } + ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); + } + free_segment_list(mem_layout); + + dfu_abort_to_idle(dif); + + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + return ret; +} diff --git a/tools/linux64/src/dfu-util/src/dfuse.h b/tools/linux64/src/dfu-util/src/dfuse.h new file mode 100644 index 0000000..ed1108c --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfuse.h @@ -0,0 +1,35 @@ +/* This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (Document UM0391) + * + * (C) 2010-2012 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_H +#define DFUSE_H + +#include "dfu.h" + +enum dfuse_command { SET_ADDRESS, ERASE_PAGE, MASS_ERASE, READ_UNPROTECT }; + +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command); +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options); +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options); + +#endif /* DFUSE_H */ diff --git a/tools/linux64/src/dfu-util/src/dfuse_mem.c b/tools/linux64/src/dfu-util/src/dfuse_mem.c new file mode 100644 index 0000000..a91aacf --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfuse_mem.c @@ -0,0 +1,198 @@ +/* + * Helper functions for reading the memory map of a device + * following the ST DfuSe 1.1a specification. + * + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" +#include "dfuse_mem.h" + +int add_segment(struct memsegment **segment_list, struct memsegment segment) +{ + struct memsegment *new_element; + + new_element = dfu_malloc(sizeof(struct memsegment)); + *new_element = segment; + new_element->next = NULL; + + if (*segment_list == NULL) + /* list can be empty on first call */ + *segment_list = new_element; + else { + struct memsegment *next_element; + + /* find last element in list */ + next_element = *segment_list; + while (next_element->next != NULL) + next_element = next_element->next; + next_element->next = new_element; + } + return 0; +} + +struct memsegment *find_segment(struct memsegment *segment_list, + unsigned int address) +{ + while (segment_list != NULL) { + if (segment_list->start <= address && + segment_list->end >= address) + return segment_list; + segment_list = segment_list->next; + } + return NULL; +} + +void free_segment_list(struct memsegment *segment_list) +{ + struct memsegment *next_element; + + while (segment_list->next != NULL) { + next_element = segment_list->next; + free(segment_list); + segment_list = next_element; + } + free(segment_list); +} + +/* Parse memory map from interface descriptor string + * encoded as per ST document UM0424 section 4.3.2. + */ +struct memsegment *parse_memory_layout(char *intf_desc) +{ + + char multiplier, memtype; + unsigned int address; + int sectors, size; + char *name, *typestring; + int ret; + int count = 0; + char separator; + int scanned; + struct memsegment *segment_list = NULL; + struct memsegment segment; + + name = dfu_malloc(strlen(intf_desc)); + + ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned); + if (ret < 1) { + free(name); + warnx("Could not read name, sscanf returned %d", ret); + return NULL; + } + printf("DfuSe interface name: \"%s\"\n", name); + + intf_desc += scanned; + typestring = dfu_malloc(strlen(intf_desc)); + + while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned), + ret > 0) { + + intf_desc += scanned; + while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n", + §ors, &size, &multiplier, typestring, + &scanned), ret > 2) { + intf_desc += scanned; + + count++; + memtype = 0; + if (ret == 4) { + if (strlen(typestring) == 1 + && typestring[0] != '/') + memtype = typestring[0]; + else { + warnx("Parsing type identifier '%s' " + "failed for segment %i", + typestring, count); + continue; + } + } + + /* Quirk for STM32F4 devices */ + if (strcmp(name, "Device Feature") == 0) + memtype = 'e'; + + switch (multiplier) { + case 'B': + break; + case 'K': + size *= 1024; + break; + case 'M': + size *= 1024 * 1024; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + if (!memtype) { + warnx("Non-valid multiplier '%c', " + "interpreted as type " + "identifier instead", + multiplier); + memtype = multiplier; + break; + } + /* fallthrough if memtype was already set */ + default: + warnx("Non-valid multiplier '%c', " + "assuming bytes", multiplier); + } + + if (!memtype) { + warnx("No valid type for segment %d\n", count); + continue; + } + + segment.start = address; + segment.end = address + sectors * size - 1; + segment.pagesize = size; + segment.memtype = memtype & 7; + add_segment(&segment_list, segment); + + if (verbose) + printf("Memory segment at 0x%08x %3d x %4d = " + "%5d (%s%s%s)\n", + address, sectors, size, sectors * size, + memtype & DFUSE_READABLE ? "r" : "", + memtype & DFUSE_ERASABLE ? "e" : "", + memtype & DFUSE_WRITEABLE ? "w" : ""); + + address += sectors * size; + + separator = *intf_desc; + if (separator == ',') + intf_desc += 1; + else + break; + } /* while per segment */ + + } /* while per address */ + free(name); + free(typestring); + + return segment_list; +} diff --git a/tools/linux64/src/dfu-util/src/dfuse_mem.h b/tools/linux64/src/dfu-util/src/dfuse_mem.h new file mode 100644 index 0000000..0181f0c --- /dev/null +++ b/tools/linux64/src/dfu-util/src/dfuse_mem.h @@ -0,0 +1,44 @@ +/* Helper functions for reading the memory map in a device + * following the ST DfuSe 1.1a specification. + * + * (C) 2011 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_MEM_H +#define DFUSE_MEM_H + +#define DFUSE_READABLE 1 +#define DFUSE_ERASABLE 2 +#define DFUSE_WRITEABLE 4 + +struct memsegment { + unsigned int start; + unsigned int end; + int pagesize; + int memtype; + struct memsegment *next; +}; + +int add_segment(struct memsegment **list, struct memsegment new_element); + +struct memsegment *find_segment(struct memsegment *list, unsigned int address); + +void free_segment_list(struct memsegment *list); + +struct memsegment *parse_memory_layout(char *intf_desc_str); + +#endif /* DFUSE_MEM_H */ diff --git a/tools/linux64/src/dfu-util/src/main.c b/tools/linux64/src/dfu-util/src/main.c new file mode 100644 index 0000000..acaed2f --- /dev/null +++ b/tools/linux64/src/dfu-util/src/main.c @@ -0,0 +1,699 @@ +/* + * dfu-util + * + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013-2014 Hans Petter Selasky + * + * Written by Harald Welte + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +int verbose = 0; + +struct dfu_if *dfu_root = NULL; + +int match_bus = -1; +int match_device = -1; +int match_vendor = -1; +int match_product = -1; +int match_vendor_dfu = -1; +int match_product_dfu = -1; +int match_config_index = -1; +int match_iface_index = -1; +int match_iface_alt_index = -1; +const char *match_iface_alt_name = NULL; +const char *match_serial = NULL; +const char *match_serial_dfu = NULL; + +static int parse_match_value(const char *str, int default_value) +{ + char *remainder; + int value; + + if (str == NULL) { + value = default_value; + } else if (*str == '*') { + value = -1; /* Match anything */ + } else if (*str == '-') { + value = 0x10000; /* Impossible vendor/product ID */ + } else { + value = strtoul(str, &remainder, 16); + if (remainder == str) { + value = default_value; + } + } + return value; +} + +static void parse_vendprod(const char *str) +{ + const char *comma; + const char *colon; + + /* Default to match any DFU device in runtime or DFU mode */ + match_vendor = -1; + match_product = -1; + match_vendor_dfu = -1; + match_product_dfu = -1; + + comma = strchr(str, ','); + if (comma == str) { + /* DFU mode vendor/product being specified without any runtime + * vendor/product specification, so don't match any runtime device */ + match_vendor = match_product = 0x10000; + } else { + colon = strchr(str, ':'); + if (colon != NULL) { + ++colon; + if ((comma != NULL) && (colon > comma)) { + colon = NULL; + } + } + match_vendor = parse_match_value(str, match_vendor); + match_product = parse_match_value(colon, match_product); + if (comma != NULL) { + /* Both runtime and DFU mode vendor/product specifications are + * available, so default DFU mode match components to the given + * runtime match components */ + match_vendor_dfu = match_vendor; + match_product_dfu = match_product; + } + } + if (comma != NULL) { + ++comma; + colon = strchr(comma, ':'); + if (colon != NULL) { + ++colon; + } + match_vendor_dfu = parse_match_value(comma, match_vendor_dfu); + match_product_dfu = parse_match_value(colon, match_product_dfu); + } +} + +static void parse_serial(char *str) +{ + char *comma; + + match_serial = str; + comma = strchr(str, ','); + if (comma == NULL) { + match_serial_dfu = match_serial; + } else { + *comma++ = 0; + match_serial_dfu = comma; + } + if (*match_serial == 0) match_serial = NULL; + if (*match_serial_dfu == 0) match_serial_dfu = NULL; +} + +#ifdef HAVE_USBPATH_H + +static int resolve_device_path(char *path) +{ + int res; + + res = usb_path2devnum(path); + if (res < 0) + return -EINVAL; + if (!res) + return 0; + + match_bus = atoi(path); + match_device = res; + + return 0; +} + +#else /* HAVE_USBPATH_H */ + +static int resolve_device_path(char *path) +{ + (void)path; /* Eliminate unused variable warning */ + errx(EX_SOFTWARE, "USB device paths are not supported by this dfu-util.\n"); +} + +#endif /* !HAVE_USBPATH_H */ + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-util [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -v --verbose\t\t\tPrint verbose debug statements\n" + " -l --list\t\t\tList currently attached DFU capable devices\n"); + fprintf(stderr, " -e --detach\t\t\tDetach currently attached DFU capable devices\n" + " -E --detach-delay seconds\tTime to wait before reopening a device after detach\n" + " -d --device :[,:]\n" + "\t\t\t\tSpecify Vendor/Product ID(s) of DFU device\n" + " -p --path \tSpecify path to DFU device\n" + " -c --cfg \t\tSpecify the Configuration of DFU device\n" + " -i --intf \t\tSpecify the DFU Interface number\n" + " -S --serial [,]\n" + "\t\t\t\tSpecify Serial String of DFU device\n" + " -a --alt \t\tSpecify the Altsetting of the DFU Interface\n" + "\t\t\t\tby name or by number\n"); + fprintf(stderr, " -t --transfer-size \tSpecify the number of bytes per USB Transfer\n" + " -U --upload \t\tRead firmware from device into \n" + " -Z --upload-size \tSpecify the expected upload size in bytes\n" + " -D --download \t\tWrite firmware from into device\n" + " -R --reset\t\t\tIssue USB Reset signalling once we're finished\n" + " -s --dfuse-address
\tST DfuSe mode, specify target address for\n" + "\t\t\t\traw file download or upload. Not applicable for\n" + "\t\t\t\tDfuSe file (.dfu) downloads\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf(PACKAGE_STRING "\n\n"); + printf("Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\n" + "Copyright 2010-2014 Tormod Volden and Stefan Schmidt\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to " PACKAGE_BUGREPORT "\n\n"); +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "verbose", 0, 0, 'v' }, + { "list", 0, 0, 'l' }, + { "detach", 0, 0, 'e' }, + { "detach-delay", 1, 0, 'E' }, + { "device", 1, 0, 'd' }, + { "path", 1, 0, 'p' }, + { "configuration", 1, 0, 'c' }, + { "cfg", 1, 0, 'c' }, + { "interface", 1, 0, 'i' }, + { "intf", 1, 0, 'i' }, + { "altsetting", 1, 0, 'a' }, + { "alt", 1, 0, 'a' }, + { "serial", 1, 0, 'S' }, + { "transfer-size", 1, 0, 't' }, + { "upload", 1, 0, 'U' }, + { "upload-size", 1, 0, 'Z' }, + { "download", 1, 0, 'D' }, + { "reset", 0, 0, 'R' }, + { "dfuse-address", 1, 0, 's' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int expected_size = 0; + unsigned int transfer_size = 0; + enum mode mode = MODE_NONE; + struct dfu_status status; + libusb_context *ctx; + struct dfu_file file; + char *end; + int final_reset = 0; + int ret; + int dfuse_device = 0; + int fd; + const char *dfuse_options = NULL; + int detach_delay = 5; + uint16_t runtime_vendor; + uint16_t runtime_product; + + memset(&file, 0, sizeof(file)); + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + mode = MODE_VERSION; + break; + case 'v': + verbose++; + break; + case 'l': + mode = MODE_LIST; + break; + case 'e': + mode = MODE_DETACH; + match_iface_alt_index = 0; + match_iface_index = 0; + break; + case 'E': + detach_delay = atoi(optarg); + break; + case 'd': + parse_vendprod(optarg); + break; + case 'p': + /* Parse device path */ + ret = resolve_device_path(optarg); + if (ret < 0) + errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); + if (!ret) + errx(EX_SOFTWARE, "Cannot find '%s'", optarg); + break; + case 'c': + /* Configuration */ + match_config_index = atoi(optarg); + break; + case 'i': + /* Interface */ + match_iface_index = atoi(optarg); + break; + case 'a': + /* Interface Alternate Setting */ + match_iface_alt_index = strtoul(optarg, &end, 0); + if (*end) { + match_iface_alt_name = optarg; + match_iface_alt_index = -1; + } + break; + case 'S': + parse_serial(optarg); + break; + case 't': + transfer_size = atoi(optarg); + break; + case 'U': + mode = MODE_UPLOAD; + file.name = optarg; + break; + case 'Z': + expected_size = atoi(optarg); + break; + case 'D': + mode = MODE_DOWNLOAD; + file.name = optarg; + break; + case 'R': + final_reset = 1; + break; + case 's': + dfuse_options = optarg; + break; + default: + help(); + break; + } + } + + print_version(); + if (mode == MODE_VERSION) { + exit(0); + } + + if (mode == MODE_NONE) { + fprintf(stderr, "You need to specify one of -D or -U\n"); + help(); + } + + if (match_config_index == 0) { + /* Handle "-c 0" (unconfigured device) as don't care */ + match_config_index = -1; + } + + if (mode == MODE_DOWNLOAD) { + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + /* If the user didn't specify product and/or vendor IDs to match, + * use any IDs from the file suffix for device matching */ + if (match_vendor < 0 && file.idVendor != 0xffff) { + match_vendor = file.idVendor; + printf("Match vendor ID from file: %04x\n", match_vendor); + } + if (match_product < 0 && file.idProduct != 0xffff) { + match_product = file.idProduct; + printf("Match product ID from file: %04x\n", match_product); + } + } + + ret = libusb_init(&ctx); + if (ret) + errx(EX_IOERR, "unable to initialize libusb: %i", ret); + + if (verbose > 2) { + libusb_set_debug(ctx, 255); + } + + probe_devices(ctx); + + if (mode == MODE_LIST) { + list_dfu_interfaces(); + exit(0); + } + + if (dfu_root == NULL) { + errx(EX_IOERR, "No DFU capable USB device available"); + } else if (dfu_root->next != NULL) { + /* We cannot safely support more than one DFU capable device + * with same vendor/product ID, since during DFU we need to do + * a USB bus reset, after which the target device will get a + * new address */ + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device\n"); + } + + /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ + + printf("Opening DFU capable USB device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) + errx(EX_IOERR, "Cannot open device"); + + printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); + + printf("Run-time device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + /* Transition from run-Time mode to DFU mode */ + if (!(dfu_root->flags & DFU_IFF_DFU)) { + int err; + /* In the 'first round' during runtime mode, there can only be one + * DFU Interface descriptor according to the DFU Spec. */ + + /* FIXME: check if the selected device really has only one */ + + runtime_vendor = dfu_root->vendor; + runtime_product = dfu_root->product; + + printf("Claiming USB DFU Runtime Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface %d", + dfu_root->interface); + } + + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { + errx(EX_IOERR, "Cannot set alt interface zero"); + } + + printf("Determining device status: "); + + err = dfu_get_status(dfu_root, &status); + if (err == LIBUSB_ERROR_PIPE) { + printf("Device does not implement get_status, assuming appIDLE\n"); + status.bStatus = DFU_STATUS_OK; + status.bwPollTimeout = 0; + status.bState = DFU_STATE_appIDLE; + status.iString = 0; + } else if (err < 0) { + errx(EX_IOERR, "error get_status"); + } else { + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + } + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + printf("Device really in Runtime Mode, send DFU " + "detach request...\n"); + if (dfu_detach(dfu_root->dev_handle, + dfu_root->interface, 1000) < 0) { + warnx("error detaching"); + } + if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { + printf("Device will detach and reattach...\n"); + } else { + printf("Resetting USB...\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) + errx(EX_IOERR, "error resetting " + "after detach"); + } + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, + dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + /* fall through */ + default: + warnx("WARNING: Runtime device already in DFU state ?!?"); + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + goto dfustate; + } + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + + if (mode == MODE_DETACH) { + libusb_exit(ctx); + exit(0); + } + + /* keeping handles open might prevent re-enumeration */ + disconnect_devices(); + + milli_sleep(detach_delay * 1000); + + /* Change match vendor and product to impossible values to force + * only DFU mode matches in the following probe */ + match_vendor = match_product = 0x10000; + + probe_devices(ctx); + + if (dfu_root == NULL) { + errx(EX_IOERR, "Lost device after RESET?"); + } else if (dfu_root->next != NULL) { + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device"); + } + + /* Check for DFU mode device */ + if (!(dfu_root->flags | DFU_IFF_DFU)) + errx(EX_SOFTWARE, "Device is not in DFU mode"); + + printf("Opening DFU USB Device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) { + errx(EX_IOERR, "Cannot open device"); + } + } else { + /* we're already in DFU mode, so we can skip the detach/reset + * procedure */ + /* If a match vendor/product was specified, use that as the runtime + * vendor/product, otherwise use the DFU mode vendor/product */ + runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; + runtime_product = match_product < 0 ? dfu_root->product : match_product; + } + +dfustate: +#if 0 + printf("Setting Configuration %u...\n", dfu_root->configuration); + if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { + errx(EX_IOERR, "Cannot set configuration"); + } +#endif + printf("Claiming USB DFU Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface"); + } + + printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { + errx(EX_IOERR, "Cannot set alternate interface"); + } + +status_again: + printf("Determining device status: "); + if (dfu_get_status(dfu_root, &status ) < 0) { + errx(EX_IOERR, "error get_status"); + } + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + errx(EX_IOERR, "Device still in Runtime Mode!"); + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + goto status_again; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + case DFU_STATE_dfuUPLOAD_IDLE: + printf("aborting previous incomplete transfer\n"); + if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "can't send DFU_ABORT"); + } + goto status_again; + break; + case DFU_STATE_dfuIDLE: + printf("dfuIDLE, continuing\n"); + break; + default: + break; + } + + if (DFU_STATUS_OK != status.bStatus ) { + printf("WARNING: DFU Status: '%s'\n", + dfu_status_to_string(status.bStatus)); + /* Clear our status & try again. */ + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) + errx(EX_IOERR, "USB communication error"); + if (dfu_get_status(dfu_root, &status) < 0) + errx(EX_IOERR, "USB communication error"); + if (DFU_STATUS_OK != status.bStatus) + errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); + + milli_sleep(status.bwPollTimeout); + } + + printf("DFU mode device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) + dfuse_device = 1; + + /* If not overridden by the user */ + if (!transfer_size) { + transfer_size = libusb_le16_to_cpu( + dfu_root->func_dfu.wTransferSize); + if (transfer_size) { + printf("Device returned transfer size %i\n", + transfer_size); + } else { + errx(EX_IOERR, "Transfer size must be specified"); + } + } + +#ifdef HAVE_GETPAGESIZE +/* autotools lie when cross-compiling for Windows using mingw32/64 */ +#ifndef __MINGW32__ + /* limitation of Linux usbdevio */ + if ((int)transfer_size > getpagesize()) { + transfer_size = getpagesize(); + printf("Limited transfer size to %i\n", transfer_size); + } +#endif /* __MINGW32__ */ +#endif /* HAVE_GETPAGESIZE */ + + if (transfer_size < dfu_root->bMaxPacketSize0) { + transfer_size = dfu_root->bMaxPacketSize0; + printf("Adjusted transfer size to %i\n", transfer_size); + } + + switch (mode) { + case MODE_UPLOAD: + /* open for "exclusive" writing */ + fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); + if (fd < 0) + err(EX_IOERR, "Cannot open file %s for writing", file.name); + + if (dfuse_device || dfuse_options) { + if (dfuse_do_upload(dfu_root, transfer_size, fd, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_upload(dfu_root, transfer_size, + expected_size, fd) < 0) { + exit(1); + } + } + close(fd); + break; + + case MODE_DOWNLOAD: + if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || + (file.idProduct != 0xffff && file.idProduct != runtime_product)) && + ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || + (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { + errx(EX_IOERR, "Error: File ID %04x:%04x does " + "not match device (%04x:%04x or %04x:%04x)", + file.idVendor, file.idProduct, + runtime_vendor, runtime_product, + dfu_root->vendor, dfu_root->product); + } + if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { + if (dfuse_do_dnload(dfu_root, transfer_size, &file, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) + exit(1); + } + break; + case MODE_DETACH: + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + warnx("can't detach"); + } + break; + default: + errx(EX_IOERR, "Unsupported mode: %u", mode); + break; + } + + if (final_reset) { + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + /* Even if detach failed, just carry on to leave the + device in a known state */ + warnx("can't detach"); + } + printf("Resetting USB to switch back to runtime mode\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { + errx(EX_IOERR, "error resetting after download"); + } + } + + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + libusb_exit(ctx); + + return (0); +} diff --git a/tools/linux64/src/dfu-util/src/portable.h b/tools/linux64/src/dfu-util/src/portable.h new file mode 100644 index 0000000..cf8d5df --- /dev/null +++ b/tools/linux64/src/dfu-util/src/portable.h @@ -0,0 +1,72 @@ + +#ifndef PORTABLE_H +#define PORTABLE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# define PACKAGE "dfu-util" +# define PACKAGE_VERSION "0.8-msvc" +# define PACKAGE_STRING "dfu-util 0.8-msvc" +# define PACKAGE_BUGREPORT "dfu-util@lists.gnumonks.org" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_FTRUNCATE +# include +#else +# include +#endif /* HAVE_FTRUNCATE */ + +#ifdef HAVE_NANOSLEEP +# include +# define milli_sleep(msec) do {\ + if (msec) {\ + struct timespec nanosleepDelay = { (msec) / 1000, ((msec) % 1000) * 1000000 };\ + nanosleep(&nanosleepDelay, NULL);\ + } } while (0) +#elif defined HAVE_WINDOWS_H +# define milli_sleep(msec) do {\ + if (msec) {\ + Sleep(msec);\ + } } while (0) +#else +# error "Can't get no sleep! Please report" +#endif /* HAVE_NANOSLEEP */ + +#ifdef HAVE_ERR +# include +#else +# include +# include +# define warnx(...) do {\ + fprintf(stderr, __VA_ARGS__);\ + fprintf(stderr, "\n"); } while (0) +# define errx(eval, ...) do {\ + warnx(__VA_ARGS__);\ + exit(eval); } while (0) +# define warn(...) do {\ + fprintf(stderr, "%s: ", strerror(errno));\ + warnx(__VA_ARGS__); } while (0) +# define err(eval, ...) do {\ + warn(__VA_ARGS__);\ + exit(eval); } while (0) +#endif /* HAVE_ERR */ + +#ifdef HAVE_SYSEXITS_H +# include +#else +# define EX_OK 0 /* successful termination */ +# define EX_USAGE 64 /* command line usage error */ +# define EX_SOFTWARE 70 /* internal software error */ +# define EX_IOERR 74 /* input/output error */ +#endif /* HAVE_SYSEXITS_H */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifndef off_t +# define off_t long int +#endif + +#endif /* PORTABLE_H */ diff --git a/tools/linux64/src/dfu-util/src/prefix.c b/tools/linux64/src/dfu-util/src/prefix.c new file mode 100644 index 0000000..be8e3fa --- /dev/null +++ b/tools/linux64/src/dfu-util/src/prefix.c @@ -0,0 +1,176 @@ +/* + * dfu-prefix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Uwe Bonnes + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-prefix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU prefix of \n" + " -D --delete \t\tDelete DFU prefix from \n" + " -a --add \t\tAdd DFU prefix to \n" + "In combination with -a:\n" + ); + fprintf(stderr, " -s --stellaris-address
Add TI Stellaris address prefix to \n" + "In combination with -D or -c:\n" + " -T --stellaris\t\tAct on TI Stellaris address prefix of \n" + "In combination with -a or -D or -c:\n" + " -L --lpc-prefix\t\tUse NXP LPC DFU prefix format\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-prefix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2014 Uwe Bonnes\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "stellaris-address", 1, 0, 's' }, + { "stellaris", 0, 0, 'T' }, + { "LPC", 0, 0, 'L' }, +}; +int main(int argc, char **argv) +{ + struct dfu_file file; + enum mode mode = MODE_NONE; + enum prefix_type type = ZERO_PREFIX; + uint32_t lmdfu_flash_address = 0; + char *end; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:s:TL", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + case 's': + lmdfu_flash_address = strtoul(optarg, &end, 0); + if (*end) { + errx(EX_IOERR, "Invalid lmdfu " + "address: %s", optarg); + } + /* fall-through */ + case 'T': + type = LMDFU_PREFIX; + break; + case 'L': + type = LPCDFU_UNENCRYPTED_PREFIX; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + if (type == ZERO_PREFIX) + errx(EX_IOERR, "Prefix type must be specified"); + dfu_load_file(&file, MAYBE_SUFFIX, NO_PREFIX); + file.lmdfu_address = lmdfu_flash_address; + file.prefix_type = type; + printf("Adding prefix to file\n"); + dfu_store_file(&file, file.size.suffix != 0, 1); + break; + + case MODE_CHECK: + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + break; + + case MODE_DEL: + dfu_load_file(&file, MAYBE_SUFFIX, NEEDS_PREFIX); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + printf("Removing prefix from file\n"); + /* if there was a suffix, rewrite it */ + dfu_store_file(&file, file.size.suffix != 0, 0); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/linux64/src/dfu-util/src/quirks.c b/tools/linux64/src/dfu-util/src/quirks.c new file mode 100644 index 0000000..de394a6 --- /dev/null +++ b/tools/linux64/src/dfu-util/src/quirks.c @@ -0,0 +1,56 @@ +/* + * Simple quirk system for dfu-util + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "quirks.h" + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) +{ + uint16_t quirks = 0; + + /* Device returns bogus bwPollTimeout values */ + if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) && + product >= PRODUCT_FREERUNNER_FIRST && + product <= PRODUCT_FREERUNNER_LAST) + quirks |= QUIRK_POLLTIMEOUT; + + if (vendor == VENDOR_VOTI && + product == PRODUCT_OPENPCD) + quirks |= QUIRK_POLLTIMEOUT; + + /* Reports wrong DFU version in DFU descriptor */ + if (vendor == VENDOR_LEAFLABS && + product == PRODUCT_MAPLE3 && + bcdDevice == 0x0200) + quirks |= QUIRK_FORCE_DFU11; + + /* old devices(bcdDevice == 0) return bogus bwPollTimeout values */ + if (vendor == VENDOR_SIEMENS && + (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) && + bcdDevice == 0) + quirks |= QUIRK_POLLTIMEOUT; + + /* M-Audio Transit returns bogus bwPollTimeout values */ + if (vendor == VENDOR_MIDIMAN && + product == PRODUCT_TRANSIT) + quirks |= QUIRK_POLLTIMEOUT; + + return (quirks); +} diff --git a/tools/linux64/src/dfu-util/src/quirks.h b/tools/linux64/src/dfu-util/src/quirks.h new file mode 100644 index 0000000..0e4b3ec --- /dev/null +++ b/tools/linux64/src/dfu-util/src/quirks.h @@ -0,0 +1,27 @@ +#ifndef DFU_QUIRKS_H +#define DFU_QUIRKS_H + +#define VENDOR_OPENMOKO 0x1d50 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_FIC 0x1457 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_VOTI 0x16c0 /* OpenPCD Reader */ +#define VENDOR_LEAFLABS 0x1eaf /* Maple */ +#define VENDOR_SIEMENS 0x0908 /* Siemens AG */ +#define VENDOR_MIDIMAN 0x0763 /* Midiman */ + +#define PRODUCT_FREERUNNER_FIRST 0x5117 +#define PRODUCT_FREERUNNER_LAST 0x5126 +#define PRODUCT_OPENPCD 0x076b +#define PRODUCT_MAPLE3 0x0003 /* rev 3 and 5 */ +#define PRODUCT_PXM40 0x02c4 /* Siemens AG, PXM 40 */ +#define PRODUCT_PXM50 0x02c5 /* Siemens AG, PXM 50 */ +#define PRODUCT_TRANSIT 0x2806 /* M-Audio Transit (Midiman) */ + +#define QUIRK_POLLTIMEOUT (1<<0) +#define QUIRK_FORCE_DFU11 (1<<1) + +/* Fallback value, works for OpenMoko */ +#define DEFAULT_POLLTIMEOUT 5 + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice); + +#endif /* DFU_QUIRKS_H */ diff --git a/tools/linux64/src/dfu-util/src/suffix.c b/tools/linux64/src/dfu-util/src/suffix.c new file mode 100644 index 0000000..0df248f --- /dev/null +++ b/tools/linux64/src/dfu-util/src/suffix.c @@ -0,0 +1,176 @@ +/* + * dfu-suffix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-suffix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU suffix of \n" + " -a --add \t\tAdd DFU suffix to \n" + " -D --delete \t\tDelete DFU suffix from \n" + " -p --pid \t\tAdd product ID into DFU suffix in \n" + " -v --vid \t\tAdd vendor ID into DFU suffix in \n" + " -d --did \t\tAdd device ID into DFU suffix in \n" + " -S --spec \t\tAdd DFU specification ID into DFU suffix in \n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-suffix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "pid", 1, 0, 'p' }, + { "vid", 1, 0, 'v' }, + { "did", 1, 0, 'd' }, + { "spec", 1, 0, 'S' }, +}; + +int main(int argc, char **argv) +{ + struct dfu_file file; + int pid, vid, did, spec; + enum mode mode = MODE_NONE; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + pid = vid = did = 0xffff; + spec = 0x0100; /* Default to bcdDFU version 1.0 */ + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:S:s:T", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'p': + pid = strtol(optarg, NULL, 16); + break; + case 'v': + vid = strtol(optarg, NULL, 16); + break; + case 'd': + did = strtol(optarg, NULL, 16); + break; + case 'S': + spec = strtol(optarg, NULL, 16); + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + if (spec != 0x0100 && spec != 0x011a) { + fprintf(stderr, "Only DFU specification 0x0100 and 0x011a supported\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + dfu_load_file(&file, NO_SUFFIX, MAYBE_PREFIX); + file.idVendor = vid; + file.idProduct = pid; + file.bcdDevice = did; + file.bcdDFU = spec; + /* always write suffix, rewrite prefix if there was one */ + dfu_store_file(&file, 1, file.size.prefix != 0); + printf("Suffix successfully added to file\n"); + break; + + case MODE_CHECK: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + break; + + case MODE_DEL: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + dfu_store_file(&file, 0, file.size.prefix != 0); + if (file.size.suffix) /* had a suffix */ + printf("Suffix successfully removed from file\n"); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/linux64/src/dfu-util/src/usb_dfu.h b/tools/linux64/src/dfu-util/src/usb_dfu.h new file mode 100644 index 0000000..660bedc --- /dev/null +++ b/tools/linux64/src/dfu-util/src/usb_dfu.h @@ -0,0 +1,99 @@ +#ifndef USB_DFU_H +#define USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * Protocol definitions for USB DFU + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define USB_DT_DFU 0x21 + +#ifdef _MSC_VER +# pragma pack(push) +# pragma pack(1) +#endif /* _MSC_VER */ +struct usb_dfu_func_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bmAttributes; +#define USB_DFU_CAN_DOWNLOAD (1 << 0) +#define USB_DFU_CAN_UPLOAD (1 << 1) +#define USB_DFU_MANIFEST_TOL (1 << 2) +#define USB_DFU_WILL_DETACH (1 << 3) + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +#ifdef _MSC_VER +}; +# pragma pack(pop) +#elif defined __GNUC__ +} __attribute__ ((packed)); +#else + #warning "No way to pack struct on this compiler? This will break!" +#endif /* _MSC_VER */ + +#define USB_DT_DFU_SIZE 9 + +#define USB_TYPE_DFU (LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE) + +/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +/* DFU_GETSTATUS bStatus values (Section 6.1.2, DFU Rev 1.1) */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10 +}; + +#endif /* USB_DFU_H */ diff --git a/tools/linux64/src/dfu-util/www/build.html b/tools/linux64/src/dfu-util/www/build.html new file mode 100644 index 0000000..f3036e4 --- /dev/null +++ b/tools/linux64/src/dfu-util/www/build.html @@ -0,0 +1,147 @@ + + + + + + + Building dfu-util from source + + + + + + + + + +
+

How to build dfu-util from source

+ +

Prerequisites for building from git

+

Mac OS X

+

+First install MacPorts (and if you are on 10.6 or older, the Java Developer Package) and then run: +

+
+	sudo port install libusb-devel git-core
+
+ +

FreeBSD

+
+	sudo pkg_add -r git pkgconf
+
+ +

Ubuntu and Debian and derivatives

+
+	sudo apt-get build-dep dfu-util
+	sudo apt-get install libusb-1.0-0-dev
+
+ +

Get the source code and build it

+

+The first time you will have to clone the git repository: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	cd dfu-util
+
+

+If you later want to update to latest git version, just run this: +

+
+	make maintainer-clean
+	git pull
+
+

+To build the source: +

+
+	./autogen.sh
+	./configure  # on most systems
+	make
+
+ +

+If you are building on Mac OS X, replace the ./configure command with: +

+
+	./configure --libdir=/opt/local/lib --includedir=/opt/local/include  # on MacOSX only
+
+ +

+Your dfu-util binary will be inside the src folder. Use it from there, or install it to /usr/local/bin by running "sudo make install". +

+ +

Cross-building for Windows

+ +

+Windows binaries can be built in a MinGW +environment, on a Windows computer or cross-hosted in another OS. +To build it on a Debian or Ubuntu host, first install build dependencies: +

+
+	sudo apt-get build-dep libusb-1.0-0 dfu-util
+	sudo apt-get install mingw32
+
+ +

+The below example builds dfu-util 0.8 and libusb 1.0.19 from unpacked release +tarballs. If you instead build from git, you will have to run "./autogen.sh" +before running the "./configure" steps. +

+ +
+mkdir -p build
+cd libusb-1.0.19
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+# WINVER workaround needed for 1.0.19 only
+make CFLAGS="-DWINVER=0x0501"
+make install
+cd ..
+
+cd dfu-util-0.8
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+make
+make install
+cd ..
+
+The build files will now be in build/bin. +

+ +

Building on Windows using MinGW

+This assumes using release tarballs or having run ./autogen.sh on +the git sources. +
+cd libusb-1.0.19
+./configure --prefix=$HOME
+# WINVER workaround needed for 1.0.19 only
+# MKDIR_P setting should not really be needed...
+make CFLAGS="-DWINVER=0x0501" MKDIR_P="mkdir -p"
+make install
+cd ..
+
+cd dfu-util-0.8
+./configure USB_CFLAGS="-I$HOME/include/libusb-1.0" \
+            USB_LIBS="-L $HOME/lib -lusb-1.0" PKG_CONFIG=true
+make
+make install
+cd ..
+
+To link libusb statically into dfu-util.exe use instead of "make": +
+make LDFLAGS=-static
+
+The built executables (and DLL) will now be under $HOME/bin. + +

+[Back to dfu-util main page] +

+ +
+ + diff --git a/tools/linux64/src/dfu-util/www/dfu-util.1.html b/tools/linux64/src/dfu-util/www/dfu-util.1.html new file mode 100644 index 0000000..62ca40b --- /dev/null +++ b/tools/linux64/src/dfu-util/www/dfu-util.1.html @@ -0,0 +1,411 @@ + + +Man page of DFU-UTIL + + +

DFU-UTIL(1)

+ +  +

NAME

+ +dfu-util - Device firmware update (DFU) USB programmer +  +

SYNOPSIS

+ + +
+
+dfu-util + +-l + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + + +
+dfu-util + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + +[-t + +size] + +[-Z + +size] + +[-s + +address] + +[-R] + +[-D|-U + +file] + + +
+dfu-util + +[-hV] + +
+  +

DESCRIPTION

+ +dfu-util + +is a program that implements the host (computer) side of the USB DFU +(Universal Serial Bus Device Firmware Upgrade) protocol. +

+dfu-util communicates with devices that implement the device side of the +USB DFU protocol, and is often used to upgrade the firmware of such +devices. +  +

OPTIONS

+ +
+
-l, --list + +
+List the currently attached DFU capable USB devices. +
-d, --device [Run-Time VENDOR]:[Run-Time PRODUCT][,[DFU Mode VENDOR]:[DFU Mode PRODUCT]] + +
+
+Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device +to work with. VENDOR and PRODUCT are hexadecimal numbers (no prefix +needed), "*" (match any), or "-" (match nothing). By default, any DFU capable +device in either run-time or DFU mode will be considered. +

+If you only have one standards-compliant DFU device attached to your computer, +this parameter is optional. However, as soon as you have multiple DFU devices +connected, dfu-util will detect this and abort, asking you to specify which +device to use. +

+If only run-time IDs are specified (e.g. "--device 1457:51ab"), then in +addition to the specified run-time IDs, any DFU mode devices will also be +considered. This is beneficial to allow a DFU capable device to be found +again after a switch to DFU mode, since the vendor and/or product ID of a +device usually changes in DFU mode. +

+If only DFU mode IDs are specified (e.g. "--device ,951:26"), then all +run-time devices will be ignored, making it easy to target a specific device in +DFU mode. +

+If both run-time and DFU mode IDs are specified (e.g. "--device +1457:51ab,:2bc"), then unspecified DFU mode components will use the run-time +value specified. +

+Examples: +

+
--device 1457:51ab,951:26 + +
+
+ +Work with a device in run-time mode with +vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 +and product ID 0x0026 +

+

--device 1457:51ab,:2bc + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc +

+

--device 1457:51ab + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with any vendor and product ID +

+

--device ,951:26 + +
+
+ +Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 +

+

--device *,- + +
+
+ +Work with any device in run-time mode, and ignore any device in DFU mode +

+

--device , + +
+
+ +Ignore any device in run-time mode, and Work with any device in DFU mode +
+
+ +
-p, --path BUS-PORT. ... .PORT + +
+Specify the path to the DFU device. +
-c, --cfg CONFIG-NR + +
+Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. +
-i, --intf INTF-NR + +
+Specify the DFU interface number. +
-a, --alt ALT + +
+Specify the altsetting of the DFU interface by name or by number. +
-S, --serial [Run-Time SERIAL][,[DFU Mode SERIAL]] + +
+Specify the run-time and DFU mode serial numbers used to further restrict +device matches. If multiple, identical DFU devices are simultaneously +connected to a system then vendor and product ID will be insufficient for +targeting a single device. In this situation, it may be possible to use this +parameter to specify a serial number which also must match. +

+If only a single serial number is specified, then the same serial number is +used in both run-time and DFU mode. An empty serial number will match any +serial number in the corresponding mode. +

-t, --transfer-size SIZE + +
+Specify the number of bytes per USB transfer. The optimal value is +usually determined automatically so this option is rarely useful. If +you need to use this option for a device, please report it as a bug. +
-Z, --upload-size SIZE + +
+Specify the expected upload size, in bytes. +
-U, --upload FILE + +
+Read firmware from device into +FILE. + +
-D, --download FILE + +
+Write firmware from +FILE + +into device. +
-R, --reset + +
+Issue USB reset signalling after upload or download has finished. +
-s, --dfuse-address address + +
+Specify target address for raw binary download/upload on DfuSe devices. Do +not + +use this for downloading DfuSe (.dfu) files. Modifiers can be added +to the address, separated by a colon, to perform special DfuSE commands such +as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. +
-v, --verbose + +
+Print more information about dfu-util's operation. A second +-v + +will turn on verbose logging of USB requests. Repeat this option to further +increase verbosity. +
-h, --help + +
+Show a help text and exit. +
-V, --version + +
+Show version information and exit. +
+  +

EXAMPLES

+ +  +

Using dfu-util in the OpenMoko project

+ +(with the Neo1973 hardware) +

+ +Flashing the rootfs: +
+ + $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2 + +

+ +Flashing the kernel: +
+ + $ dfu-util -a kernel -R -D /path/to/uImage + +

+ +Flashing the bootloader: +
+ + $ dfu-util -a u-boot -R -D /path/to/u-boot.bin + +

+ +Copying a kernel into RAM: +
+ + $ dfu-util -a 0 -R -D /path/to/uImage + +

+Once this has finished, the kernel will be available at the default load +address of 0x32000000 in Neo1973 RAM. +Note: + +You cannot transfer more than 2MB of data into RAM using this method. +

+  +

Using dfu-util with a DfuSe device

+ +

+ +Flashing a +.dfu + +(special DfuSe format) file to the device: +
+ + $ dfu-util -a 0 -D /path/to/dfuse-image.dfu + +

+ +Reading out 1 KB of flash starting at address 0x8000000: +
+ + $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin + +

+ +Flashing a binary file to address 0x8004000 of device memory and +ask the device to leave DFU mode: +
+ + $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin + + +  +

BUGS

+ +Please report any bugs to the dfu-util mailing list at +dfu-util@lists.gnumonks.org. + +Please use the +--verbose option (repeated as necessary) to provide more + +information in your bug report. +  +

SEE ALSO

+ +The dfu-util home page is +http://dfu-util.gnumonks.org + +  +

HISTORY

+ +dfu-util was originally written for the OpenMoko project by +Weston Schmidt <weston_schmidt@yahoo.com> and +Harald Welte <hwelte@hmw-consulting.de>. Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +  +

LICENCE

+ +dfu-util + +is covered by the GNU General Public License (GPL), version 2 or later. +  +

COPYRIGHT

+ +This manual page was originally written by Uwe Hermann <uwe@hermann-uwe.de>, +and is now part of the dfu-util project. +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
OPTIONS
+
EXAMPLES
+
+
Using dfu-util in the OpenMoko project
+
Using dfu-util with a DfuSe device
+
+
BUGS
+
SEE ALSO
+
HISTORY
+
LICENCE
+
COPYRIGHT
+
+
+This document was created by man2html, +using the doc/dfu-util.1 manual page from dfu-util 0.8.
+Time: 14:40:57 GMT, September 13, 2014 + + diff --git a/tools/linux64/src/dfu-util/www/dfuse.html b/tools/linux64/src/dfu-util/www/dfuse.html new file mode 100644 index 0000000..35e4ffa --- /dev/null +++ b/tools/linux64/src/dfu-util/www/dfuse.html @@ -0,0 +1,135 @@ + + + + + + DfuSe and dfu-util + + + + + + + + + +
+

Using dfu-util with DfuSe devices

+

DfuSe

+

+ DfuSe (DFU with ST Microsystems extensions) is a protocol based on + DFU 1.1. However, in expanding the functionality of the DFU protocol, + ST Microsystems broke all compatibility with the DFU 1.1 standard. + DfuSe devices report the DFU version as "1.1a". +

+

+ DfuSe can be used to download firmware and other data + from a host computer to a conforming device (or upload in the + opposite direction) over USB similar to standard DFU. +

+

+ The main difference from standard DFU is that the target address in + the device (flash) memory is specified by the host, so that a + download can be performed to parts of the device memory. The host + program is also responsible for erasing flash pages before they + are written to. +

+

.dfu files

+

+ A special file format is defined by ST Microsystems to carry firmware + for DfuSe devices. The file contains target information such as address + and alternate interface information in addition to the binary data. + Several blocks of binary data can be combined in one .dfu file. +

+

Alternate interfaces

+

+ Different memory locations of the device may have different + characteristics that the host program (dfu-util) has to take + into considerations, such as flash memory page size, read-only + versus read-write segments, the need to erase, and so on. + These parameters are reported by the device in the string + descriptors meant for describing the USB interfaces. + The host program decodes these strings to build a memory map of + the device. Different memory units or address spaces are listed + in separate alternate interface settings that must be selected + according to the memory unit to access. +

+

+ Note that dfu-util leaves it to the user to select alternate + interface. When parsing a .dfu file it will skip file segments + not matching the selected alternate interface. Also, some + DfuSe device firmware implementations ignore the setting of + alternate interface and deduct the memory unit from the + address, since they have no address space overlap. +

+

DfuSe special commands

+

+ DfuSe special commands are used by the host program during normal + downloads or uploads, such as SET_ADDRESS and ERASE_PAGE. Also + the normal DFU_DNLOAD and DFU_UPLOAD commands have special + implementations in DfuSe. + Many DfuSe devices also support commands to leave DFU mode, + read unprotect the flash memory or mass erase the flash memory. + dfu-util (from version 0.7) + supports adding "leave", "unprotect", or "mass-erase" + to the -s option argument to send such requests in combination + with a download request. These modifiers are separated with a colon. +

+

+ Some DfuSe devices have their DfuSe bootloader running from flash + memory. Erasing the whole flash memory would therefore destroy + the DfuSe bootloader itself and practically brick the device + for most users. Any use of modifiers such as "unprotect" + and "mass-erase" therefore needs to be combined with the "force" + modifer. This is not included in the examples, to not encourage + ignorant users to copy and paste such instructions and shoot + themselves in the foot. +

+

+ Devices based on for instance STM32F103 all run the bootloader + from flash, since there is no USB bootloader in ROM. +

+

+ For instance STM32F107, STM32F2xx and STM32F4xx devices have a + DfuSe bootloader in ROM, so the flash can be erased while + keeping the device available for USB DFU transfers as long + as the device designers use this built-in bootloader and have + not implemented another DfuSe bootloader in flash that the user is + dependent upon. +

+

+ Well-written bootloaders running from flash will report their + own memory region as read-only and not eraseable, but this does + not prevent dfu-util from sending a "unprotect" or "mass-erase" + request which overrides this, if the user insists. +

+

Example usage

+

+ Flashing a .dfu (special DfuSe format) file to the device: +

+
+         $ dfu-util -a 0 -D /path/to/dfuse-image.dfu
+	
+

+ Reading out 1 KB of flash starting at address 0x8000000: +

+
+         $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin
+	
+

+ Flashing a binary file to address 0x8004000 of device memory and ask + the device to leave DFU mode: +

+
+         $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin
+	
+

+ [Back to dfu-util main page] +

+ +
+ + diff --git a/tools/linux64/src/dfu-util/www/index.html b/tools/linux64/src/dfu-util/www/index.html new file mode 100644 index 0000000..108ddaf --- /dev/null +++ b/tools/linux64/src/dfu-util/www/index.html @@ -0,0 +1,119 @@ + + + + + + dfu-util Homepage + + + + + + + + + +
+

dfu-util - Device Firmware Upgrade Utilities

+

Description

+

+ dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. + + DFU is intended to download and upload firmware to/from devices connected + over USB. It ranges from small devices like micro-controller boards + to mobile phones. Using dfu-util you can download firmware to your + DFU-enabled device or upload firmware from it. dfu-util has been + tested with the Openmoko Neo1973 and Freerunner and many other devices. +

+

+ See the manual page for examples of use. +

+

Supported Devices

+ +

Releases

+

+ Releases of the dfu-util software can be found in the + releases folder. + The current release is 0.8. +

+

+ We offer binaries for Microsoft Windows and some other platforms. + dfu-util uses libusb 1.0 to access your device, so + on Windows you have to register the device with the WinUSB driver + (alternatively libusb-win32 or libusbK), please see the libusbx wiki + for more details. +

+

+ Mac OS X users can also get dfu-util from Homebrew with "brew install dfu-util" or from MacPorts. +

+

+ Most Linux distributions ship dfu-util in binary packages for those + who do not want to compile dfu-util from source. + On Debian, Ubuntu, Fedora and Gentoo you can install it through the + normal software package tools. For other distributions +(namely OpenSuSe, Mandriva, and CentOS) Holger Freyther was kind enough to +provide binary packages through the Open Build Service. +

+

Development

+

+ Development happens in a GIT repository. Browse it via the web +interface or clone it with: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	
+

+ See our build instructions for how to + build the source on different platforms. +

+

License

+

+ This software is licensed under the GPL version 2. +

+

Contact

+

+ If you have questions about the development or use of dfu-util please + send an e-mail to our dedicated +mailing list for dfu-util. +

+

People

+

+ dfu-util was originally written by + Harald Welte partially based on code from + dfu-programmer 0.4 and is currently maintained by Stefan Schmidt and + Tormod Volden. +

+ +
+ + diff --git a/tools/linux64/src/dfu-util/www/simple.css b/tools/linux64/src/dfu-util/www/simple.css new file mode 100644 index 0000000..98100bc --- /dev/null +++ b/tools/linux64/src/dfu-util/www/simple.css @@ -0,0 +1,56 @@ +body { + margin: 10px; + font-size: 0.82em; + background-color: #EEE; +} + +h1 { + clear: both; + padding: 0 0 12px 0; + margin: 0; + font-size: 2em; + font-weight: bold; +} + +h2 { + clear: both; + margin: 0; + font-size: 1.5em; + font-weight: normal; +} + +h3 { + clear: both; + margin: 15px 0 0 0; + font-size: 1.0em; + font-weight: bold; +} + +p { + line-height: 20px; + padding: 8px 0 8px 0; + margin: 0; + font-size: 1.1em; +} + +pre { + white-space: pre-wrap; + background-color: #CCC; + padding: 3px; +} + +a:hover { + background-color: #DDD; +} + +#middlebox { + width: 600px; + margin: 0px auto; + text-align: left; +} + +#footer { + height: 100px; + padding: 28px 3px 0 0; + margin: 20px 0 20px 0; +} diff --git a/tools/linux64/src/maple_loader/README.md b/tools/linux64/src/maple_loader/README.md new file mode 100644 index 0000000..c6c9379 --- /dev/null +++ b/tools/linux64/src/maple_loader/README.md @@ -0,0 +1,5 @@ +These files build the maple_loader.jar file used on Windows to reset the Sketch via USB Serial, so that the bootloader will run in dfu upload mode, ready for a new sketch to be uploaded + +The files were written by @bobC (github) and have been slightly modified by me (Roger Clark), so that dfu-util no longer attempts to reset the board after upload. +This change to dfu-util's reset command line argument, was required because dfu-util was showing errors on some Windows systems, because the bootloader had reset its self after upload, +before dfu-util had chance to tell it to reset. \ No newline at end of file diff --git a/tools/linux64/src/maple_loader/build.xml b/tools/linux64/src/maple_loader/build.xml new file mode 100644 index 0000000..80bdd6f --- /dev/null +++ b/tools/linux64/src/maple_loader/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project maple_loader. + + + diff --git a/tools/linux64/src/maple_loader/build/built-jar.properties b/tools/linux64/src/maple_loader/build/built-jar.properties new file mode 100644 index 0000000..10752d5 --- /dev/null +++ b/tools/linux64/src/maple_loader/build/built-jar.properties @@ -0,0 +1,4 @@ +#Mon, 20 Jul 2015 11:21:26 +1000 + + +C\:\\Users\\rclark\\Desktop\\maple-asp-master\\installer\\maple_loader= diff --git a/tools/linux64/src/maple_loader/build/classes/CliTemplate/CliMain.class b/tools/linux64/src/maple_loader/build/classes/CliTemplate/CliMain.class new file mode 100644 index 0000000000000000000000000000000000000000..37ee63000b20df7528829288b7f0fbeb28eb3b98 GIT binary patch literal 1753 zcmZ`(?^7F96g^8e*(Ke!Bot_BEkSJ?Qc5V;R-jfb6dMgcG>{5a@g;erOE=l=X2Z~N zI-?()v44qQZ9mYCj{X7uQR;cgB4LP`%)9&UJMY|k&%L{U{r%gY0A}FDk-}_0<}h#K zP8=WNu8EK0_!ysNj62~BB)v{_}O_60{U^nDSt6rA=x+pvJBnxgu zI3?lRY9BiF;IZA*FjQD@>~&f991+MIpO=N*m?v4CR>N@SQDIl?iJT)EwOldq?M7{0 z1=Gko)OB=!zaqWBcH4J_UnBKkkE}dUI6;1gw$&`>7YQ%hjU`)E zajLr0+VC7#RHd&Wbu912rH#WQ=6<>uh{}^?;k8ROkTtMLX(_0Nc+qY771^P*!g?oF zA{H)T)WDX7NBCMpqAol~Zg=9P_ogOq82HA*x7cPXO-U`CNso0H_|AernPCsT6gqfD ziB{N*&{q|KDBD5PLIqWlAG&3^RonI{{SmYoxSoP)usq+b`J%ok9YH>;)vkUrROWE2 z1fx)i@Km${ciWdu8CZ~@l4rt$J?WR-CIgLhcf3nE3pG4u+m5>OUsEmEN|O=MNK4;$ z{j^)Dw0vJy$I{IU`M8_2<0+8CF&@gCoHaGY7pQPWHk+L4obWud${p#; z27L@k4K-ZtakV#DocMerkTo@*!#%F$RY--p=emv*?bquh)s{kq%`Mr24f>z_x0oWk zLCTs8mRW_UvAgFo<0m=Ux^{yCxgX?0=#uN#1q+Prgb%YlQtp0*JB^+1Ngr)XjWN8< zkD&VXK~wJ&*EDxIT8%cEeGTnp`*@6NtnD1eJAAe}0KAL$xa-GdT%jioXby1(eJ`?+ z=|WbYUd~3RSF*9`IsFy-l92=G$>_B=h$W4rsZ9R>O+0xjarp;jEnTgr6Z)^pp1H0d zXD8G;z>`EW8R7an1~*^h+*~vjP3Z?1diV=2&c$9~Bza!{1F6kODt3Si#W#5C02f~} zu__!6^)Mo=87N_h>m}%{*ksZcy$P0*Vi}_>IX literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/CliTemplate/DFUUploader.class b/tools/linux64/src/maple_loader/build/classes/CliTemplate/DFUUploader.class new file mode 100644 index 0000000000000000000000000000000000000000..77087b052fbd03f77b9e1417f2754196beaf1844 GIT binary patch literal 7476 zcma)B3wRvWb^ed`F{9OB*=x(TY-84dU$P}(9tLFNha?+hWLZeEjj_Ruv?FU^wY$vD z+L$CXEoquZOPhvJOhO1r!679nP>>BSq)o~rNg+*>5?)D~SMwxINTCfcvWyg3T8V{8Iu^JYwRX_4%a)7U0X; z`xos!rp@Cfo-pxb0$;&bO?*v<{?)`&`u?m@vQ{@1J9`JZyR`4 zCm%8JTmp;qjOXj{0$w!nodg$u*TnZs{AY}M>7R50-^ULO{80D)NShy*rWRhRvj5A( zPfYyO#LrCp+{7XR)Ebg7q%I+roRmPH z%t+u8sn^$;+RW0|q$#uYWe!h}h6MJ@TvO&Huvz9C(rC&89a?C}q67xzWK$N?WO9lr zDN~l1a;hn(nR2=zOAT3O$Z|pbuUx2t#AN*c--bhEv-KO#s}ZpZc3u-bBe z+BxVBQJ>}0Y=67JKwf&d*lGK=AXY34c6F+ue0%8X?RLI&4J*`fmupjZLBlknRgm!5 zRMi<>S>N%zV%~Ss6-9YtgXeX-1)nMpMX2=^9!H`A&M)Ejh#=Lhq& z50BVJE$xioH7uA_+TEEulnu=(Fo#_l|8U+RaSd5PDiU{s8P2Hd_c;Z}?=5B&KZ$}v zM`e0*9@UsP6+r#LB#&>Xx=r;JEF$iJXODI|8T&A`>LO=JN5O38T4$)}JAuX>;q3%X zC4&Vw>&Rs?&X7WP-O4KQ6#$A$(79Qk=S^5>v*+nSv-eK7qt5n5+wFoAg2E`Vfvc+) z_l6YVoy>xt?#kwiy!iZZftzb>FAgj6(tS<|mb$9My69lBpqErkh8w2dg9>|2p_rja zqe#o9U5^y?9KYygc`rj&8o1MtCdL}UoRWz`4peN^_)az=uxjb)yqgISV2qOK_5u2E zS{Ra|iT+&C8*;W13B*(t+gg`%{BJd(q91n?y|zds39pmfCy9 zwU(@s)s{3%izTgch9zsXIaAJ}o2plGW5}15Qn)hYY)j73vm&IfC2QqeZYiw^C}+ue zay}7V8ncUjZa*#R_?EQE8H_r+BtM&VkOZ=1lPX)&oUvrHv68WUF55l#X06lg=*p zQ^RgHo$^PVD*1E)F?cz_5Zc=HmrNK@BLd@Aq}fhsn`T?n+-YG9$H=}N+05Zo3r$bC z8K;o4JttKt=JO=KlTO(_PjU+>-yL-bWSb=yYYd96*|K@7 zCEc{HY**`srVLU^B~S=H6F4ex%g+AxR46C4)utNhC8>r6uVR=-IcJARW9VUxdu4~2&HffIc!OfoNveuOL}pTB(~%d>9eF?1_YopQkLvg z18l@$Lv~rRTQ0R^kLL2Zf#=^ASC|RQ6F~%GY;f$+Fy=5kSh834k)Lw8Hdk1(pRaPI zo@px!Bw@**lD7a7V@S;&4`S33OVavw`V_xSTL>_DGw3j!I9`hBDd#QGknphJ)MZGln8zRf)YdkFuYT!8l>JFg`~N)c1yR*>2SExlfjGoz_6PN;8{ z@nvn=Qk1}~&Ap`!^LG#=w&t1?ODRQ+%S1@j3(o!jWOZc=ObYIh>twmpa}T;1hFvCs zdGAonObRhO2CpKf-^nE(42{ZiG;}r84L$BqyAd-5d*CG9IYao zr>jeR!-B3VB} zbt86~&X_e+^jOOJ0}QL%-AqR7V3yE~omB@8j8G;Q#|jz8$uq33*rr8xguG-P=dCnv z98iEvx_bF?U9=68WZyu)ZoN#m>N_(aMIQ28pZ+k?ZXq9hGt^NYK|@dZj1^Wn5`}d_ zZNbUgq)LuwMGNkA+(~VDfZ^v##n*}z)zw$o{L_r_@W2?8;L6Vt%`oA$DlZL_AXOTs zjiXv52__#|zIGL)OC;cq8G*G})`fl|P0OO`sj6tprSnTbnKGf~n>{fVhUIlSsQ5@Uu6=bTo=MKf#&H^h;dRS@2{=3!bd^f+tf4pYu_~LGC%kehsdK z$v(?Mw%2i_l}~0k#F~yl?hBT@i)}m@T#0wHZ-oo+9$e2i!5`th6r`KC^BLi@u4&PO zsA*a?hR8j9t$~V2@Ckmq$IuptNrg*GVz>!^9O$Jz)wl+K!u3%eu%CTaO6=(&H&cQh zvTfA~ME4v+EEykzu{vUm!DMF)wPQ$(q3%{Jt*o%7U2z0P^eCc%j*&pe@S2;d^xIa^ zZyWx!q~A8YpS>FV8MQDGc^#dRO=>lk)EuRbHQ%D%3EJiZP05pvV+M_ptlz#mSwDuE zMqFh}#|9-44%U9Wf{FaLR$UUldE7v8@ z;4RzCvck2EwH1YH%L3MLUt`IOrOQ<2&MXP1gQrfN#VLYKeIHIWH4+48^XS#db9gj8 zEVu(l@i{Cfb`*_&jnmm)h+1qTpu5pP@Ge9TPQfKuik)c2E?j`!*n&$ryB7o4$KHO} zWXx4;eGC%*L&Rm8k`GY!2=0N42gt{#$;21P!XM%q5qJ`TFLgL1N!Do#ah)v1yJR!o zBl~c@9Kw6$dfXuIC%%KKD`Mg<{ zoIOq`M-2Q;xPUrU^O2ubA=c#&N7$FS)rtnKR5b~^PwTHs`yhGq2WTCB%LKDXdv`fxe@w-;CN z8K*UdvAN{)D=?3G-pJ99UsH?{jFGy+pq?oq{L5A<{j9CPgCmO zQtqADfx9UAvy^=|k?=Xn{sN`nL+^i{@#^mb|Mk&p%S--?aChj*2=|tI`zyT}ly-z? z)=+{;kAIv!jox8kjT2{|pf(c3nKO|X=T+4oVo*OeH)FRp+qLP@W}G*x(FHntyIJM0 zsD5sS{)%j*-7KOb(-e6S9olqivn6stnT1=A<3h6Oeq0oCs;3fdRy_ok1&?4sTWnQh z>>+GpxJh2z7H^D2N}C(w+{0I8E9uzZ^Z;sZMO_dEkKo+MTBCu}kA51*Eaj$?;LrrtK=1TNWg0)2ZLDZ2j{299B;29$}xU8TX==-Na> z;t1yG%H0=4)+UaQzuZ=Lbo?caE|>0Ut9u+v${G`%Mq{0xzb8DuEqZkPdEI##wcqh&>(efs#~q9ceWFTeLF5*l;Ia7DPrEKfEpir$WyO((E_Px8uRuph+U#^}AY{g3bx zeobtg#osYDRWSKUj(|}kh6l;~G1TK2X5k?;;bELbOK-rVj8b1F{vLyaCm460WWxOl zk?|yv`8A^PY21&mbLJa(5#Pr5@GMVy9zSOUdI{g*i7(=1K3~E2nbvXaq+tV)3;EoLs-T0Zi7K-z-Jhp>Zsc| znQV6_$!aHFyfo;5o9O z39sU{3NM2E%m^Do#EF7hoFfTrkb1PsEbNon{EwMAI4BMHD4$1U9&VQf zxSN^qm@L9$vII}csd!3G$2Vmuo{{BvPL|;XKEEp~$%ZB(e-(Zs%|v|*f5%>f*Q6D1 z@JAQnuazh9*U7p3eeyK^(xdq~PuyoqT!@onAs0-XgD(YKh@zcLRxZSd{+@sfadNd4 z$BBD`fDF;jjL8$jNUyyz+80&O9x0(+tIRIckF(T|S0i#Vz~5BdJ|F>xljA%jI4aUlU>Rb!L!&wvzl8Vy<){CEKuEF2>pXQSltvfsN9Ot#S$ar4IwL3wvdE gVEnnEj%Bm4B-0QQCdfFVb&17``F4Wm)Zia~4@M(Y761SM literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/CliTemplate/ExecCommand.class b/tools/linux64/src/maple_loader/build/classes/CliTemplate/ExecCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..ad95f79845d3d1b0c1d2245d06800c06c37e8c08 GIT binary patch literal 3243 zcma)8TW}NS75;u{rC%0{F$RGJfhdV%e8DI8>iD5`@f$5 zobR0TowIuL-k)v*cpTT`Xu((=I*^sf#Sz9>9Ca|&Fun>Cm{fx$aYoHgOYE&f9t8WB885cO{+|kW;2R=9EkUaYA6t zxaF3-gRbkihn%vV6KE)zS;x-R9@UxCS+nR_jxEqKP;{NFSt?ofMA|48(>ZgjJdr+9 zwr$frxFEMnAiCSKEw5J~+}d_bK6Ts5zpt3hVZm@qR|ani9V8zv@!I)W%#c?OJOT$+=a;zC}UmqFTH zV?2m#49>}jGqW>Tz-F`Y~^#NtK+Bm8Ih%QT*6BdKiBapPV4xEf|14!9gks$#A`a{ z@XG~EOL@W!{XfN|<5vp$<4XIl@r1y`E52HiR*4sN{06^O&o1luom%}~A^U^!dF!H2 zD^XI%PIT$`gmS;(++*icuIZIs+sviT?L5CEVnbtR%&aGIMaNbAQQ~zS*HBSGI4w|b zPFr3o%ewDLQ42%)KGpnwaR@x>bL>*NV3MQti;1n4$l66m0`9Y3S?L9V^{s99bBPti zYM$^W{kFu44jVbv=n~phecIK=?x>ZlXGk{bIX;gBT9@>f1N} z$}HW*mNCrshI|tYLYF9FnPqJE{mK z6v5<_kuRG=8sTcI_HMSA5xSb;dhj19djx zgIwuVNxpfKJAoeTq6D>sPebyj48*Bh2#;QeHrR2v^LDou?v5lP3GEWLbtWQRJ<(*e zf=G3W&O+awjNXI{;rNA!n4AB{_U$(j6Lz106kD zQoG$933o>m(M04D+LKx$+SMbkLcfL8v|OK*6|8A&;84NZ3K}m&srv7{uV7t!_!`!$ zA<=OI8+x=kZ0?ENLepq6QbF=MHdUc*PDbzC!ULm8t%5BK%T^^+-9G4VqguC2$XRSv z?^4RCl#+d@H{31f=Ks|bo16b95kK-*#ME?Wi}orWyo%<=MqhnX3br`OQNu>~<~ z!&bE7QSLj@&bw{cO->APH$n~^B_ocrNuMSU&XNPqbG}F>yu$2WVpiuUeHr^{VGsLq zFEg+gf5ATd4SjeEAI97GDBeLo-o?l9Zw%lr25}EVLSR_bVN^8Wgh=3&*u*y84MX%` zOk|K1Pa!8xV%+D@n^;ebO|-NJQ8KKHcJ`8!^~AT2wl%CJmk)3iA;wL#yocN}=!=rm zsAZ=w7lFkWN-j#AF{10@>MGfK5Qh*EQAXq=#2X`m>TOK%GvKaT^-~|apcui&=nq*a zBJ^oLv0UaiZxDxR?JYdV=nWIg+nC~)ZJ0LRK?z4_TOvanswS#BMyTT`PmW;CJe`P2 z9FtfjahxMVgyy@5G|gijx0-$0j#Q*>R literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/processing/app/Base.class b/tools/linux64/src/maple_loader/build/classes/processing/app/Base.class new file mode 100644 index 0000000000000000000000000000000000000000..4aa0bde02049fbb6c4e035d952edc3f002cba1e1 GIT binary patch literal 639 zcmaKp%TB^T6o&tSLZKAVg7^Ck!G)N(aDg#K6B84>Bp6NH4Q13pXq&cpc^a2)+_+KW z!Uyo7jHgH%(vSU{TL#iY#anNj3gnP^@bs|Q|HhKMoyE2H|Q1LIN$1qrOT-y|$C#{PD zcbvjD_e7B)F1=G}RqY$fA^jOcvg~v7YM(o8fhs+!)VyWT%68K=#jXsB#RI`gjbC#e zacB&x?X6h6Aymxhn8h5Cwpqozjs+|-MB1iwk7gM916!fasl&rcO_+WcD&Kj&XfPx$ zgn#7Pj&S`uhHS3%^Q!E-G+r_P8+DsnY1AJLE^ZIc>Fe~e?9mh|{hDBfK@&rxJwZ`{ zPDW>B`33BWE+Oc&M_X_PNxJLb3Ft$LqB#07KqPp)PT9fA7@=2$4doe;&^=V;0ZRCh ec&BR#29XFt##T^2{g)*ApJcUD!jNgz82kc&eSc^G literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/processing/app/Preferences.class b/tools/linux64/src/maple_loader/build/classes/processing/app/Preferences.class new file mode 100644 index 0000000000000000000000000000000000000000..89cf0100455af1f41500150e545c8bbd12583674 GIT binary patch literal 2215 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 L#S!xqDulu}Wzd50 literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class b/tools/linux64/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class new file mode 100644 index 0000000000000000000000000000000000000000..37250e7704487822639cf03f4e4a333da994be50 GIT binary patch literal 174 zcmX^0Z`VEs1_omWPId-%b_Nbc2KL<4;^M^gR7M6-4WF#UvPAuy#JqI<;F6-uymV{L zFh&Nh;QZ2}36TD<=a zee$u-T}#4J*3vf~eDmkHmbdTB3`x3-T|Q)=efH(-y}z^fnf&_KPd@_~$Jc3`K|#YE z4ezEAM=^~U<~s2b<`r2`3FpOF@DY|1aeE`vQp>rz{0}ik26Y+<5P_3@WA4aH*G_Nr)yS)eXYYsjn$=fJOWF4tYZhC37l!#w}YMI5E!%pT%fP% z+dxal=PHf9X`?)_MwAB1vTAyk>pLES*6{_t)bW){_Z9doX zp*cQw*xW5Rg|$DEB4^Hd92&M1T~J--)m&ee8#S=#_5-Loy058<-UTH+9!Y~sI?QMy zn1HJE?|PQwSw7)TWD82zQS}XvWu(;85YEQY?D>`vXiP_vZ;Q0~d|YUa5UKy|5#2V& zD&;$eAPhYZ*KvUzx$BPrxE*8^HuS-Abd74{xS?(;X{fBvh&yULy=i|kxTlNf7 z;w+!=GZ1(g31n24^DB^1jm|Zu+BsO+^IQ|O%i=s~j=OJ>nhpw19x}OH;50efeMVe!jVSW-ua2gkw zO`dcdm+%Iz;Y~`x9D}Hok8;QOw0WI5DNf}bBYzQNVV2?>S|_O6VZ79L80C_z<4CD^ zfI;%wK6*-f=pC-(g-nK|j=mppG=?XC|CSTX_X(zo5p(&F87o^tr z9?2)rD19-q?V1mm3%@)Ksv5%;9Ag$^m_dKA7Fna(JET`I5v((~5v)b52P@x^V2u+I z;12HvxQ;$d({BZrQKIK8eOE9=eg?Pczk&k!BCY2zk3}rN#66z3f(qph*T;Ae;5fm_32#8&VW5Z_DWZjif-x5zCr=eMcl9{`-9>GD2Q?oaJ}>;w=qVZ)>>5-{ha@ zC)IHMcf={5(&)lhg-58CS>Z0m^@zIs;_yKL*=f}RVTnhAt9^(L80hAo19jc~p+^+7 zg8`@6lwrath(FM+v2>NRYR{Ye%`#rtqAGQjaxxcZ8#Y@_{ZRoV0mQotV5n6@E2k~%3N36at+NEB6-iZpQsd%&@c zA5A4Tuz*j%h6Rru8x|m8Sz^Hl;6tJQ&z#FqWkoT zMK-PSux61%52o=};bEPJ4IUoy@DZt?IbKWPfebnC6t{hn9-@}T2Yu_~Sl zGS`A;(5nc_7S3xI4+ZI)kPtM>f{*(rM}FtPJ8DRlR@G}9dYyph>Z9JP2VFsL?zKCu zDyTtN_S)_8v47OBl|St_n|^2OMb&Tjf>smk3khDIvfC>O3%k1#=<|Tx>kS#<)kHgK zXjWC=*OG0dpELW%Wb8=5OE#92@hTEq^Lvj*RhcgoV`foZtZvN4!3NHxs#B^^M5P>9 z#6Ic;$!!E-EuFg8jjdt}>NO(^_{eLkgxg9tthcQM)l3P5vl+EEYKIxMb2NuOg3i=~ zV?W*`vu(6lFaE{aZ}mG>e_NJeexjyJ+@KC+DaXSt9#$Nhp^QVbbjhJtc(}~N9L@9U zjzjO$ilE#7r(*=ojSoOn4?$!Z7 z;WTsKk=&3Rg9MWTqKiRx`z2eXhyHnb=qBD$(nntT@ZiJ1n(dZyi&;I%$qF{UC!H*ZrjZjn!nbMU#8yrqi8@^Z z7ANbLa`yv3Ot{OtLrS@&0qJgTK!&?GAk)q21C%6d3}|Yc!W&3B(JFM-5bOg~yMk1$ zgJ%Pt9!joCs2OqPBd&bJm5;dciVGAn>4qeHT@mhpkduXk3~Aa0QVb>|>$in8Y$GW< z(s|e;cI4cq+w=y+aADG0O0+EJXju0z(*J;Z*ls316FrtPj(j(ijG#i)trbON%6yG8 z6E^EUU=Cs+W&~nJ&00h|hn*h>F@B!_d6EcHqPNxNG01h)Y!c)dKt8(wXIWW>;Zp|t)R=_w{k$&(nS<|sy#QGvY!8-p}wg9Yy*v>)mJ{n9+<-P@KEO7OK70uz15(}q literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class b/tools/linux64/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..27eca62622c61bc21ae557714ca4fa2d4c1eb22b GIT binary patch literal 1399 zcmah}Sy$6Q7`@XLQbO2@A}XTd)|Mr1C|W=TH?$PBD#zs|q@zUJBqpiLyT8ImUwqXU z+>ZPJe~HJR;5jZgC15~3=A6vTcjucs_gj+BKRzr2ID)Gxx-g_-7$Yil91Mp;3J#}n zB#omgnlP&1n2Hu04~MZdPNZ?N6$Olk_Jo2-1*Zg>$1U3mCIyn&+)aVhv{R7+9YxEQ zrFv~ade@DGDv?alF^%d?!?Qv=0#m`F^CTYS6zNcsy3f#h~aIPY8?ejDTV~wVGj91iJrnHr%gOLdgON zcR^s=pJY>Yt6GsBIg`?U97foXP^UC~)^LgUtdH=SyBg)_de`w7c_jW%WRiq!<-jl> zlDv^E1^oi*xu>Rd1Iw{}1*d73ow{er^H!MRu6VjeLU|f8=vHt>!xW|!oYim+=QV7_ zHi6EKDP*Pw82{Y?4Hs}xp!c^!L>%QHWS!A)371*^>01}(XG)~Aor>e%qmUrTHOw;8oku}GFUSD$z#b&AmmXn=E66?~`-!w6+5R1hf&=0k Rugelj(J99_&vgQWzW|+SRY?E< literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/dist/README.TXT b/tools/linux64/src/maple_loader/dist/README.TXT new file mode 100644 index 0000000..255b89c --- /dev/null +++ b/tools/linux64/src/maple_loader/dist/README.TXT @@ -0,0 +1,32 @@ +======================== +BUILD OUTPUT DESCRIPTION +======================== + +When you build an Java application project that has a main class, the IDE +automatically copies all of the JAR +files on the projects classpath to your projects dist/lib folder. The IDE +also adds each of the JAR files to the Class-Path element in the application +JAR files manifest file (MANIFEST.MF). + +To run the project from the command line, go to the dist folder and +type the following: + +java -jar "maple_loader.jar" + +To distribute this project, zip up the dist folder (including the lib folder) +and distribute the ZIP file. + +Notes: + +* If two JAR files on the project classpath have the same name, only the first +JAR file is copied to the lib folder. +* Only JAR files are copied to the lib folder. +If the classpath contains other types of files or folders, these files (folders) +are not copied. +* If a library on the projects classpath also has a Class-Path element +specified in the manifest,the content of the Class-Path element has to be on +the projects runtime path. +* To set a main class in a standard Java project, right-click the project node +in the Projects window and choose Properties. Then click Run and enter the +class name in the Main Class field. Alternatively, you can manually type the +class name in the manifest Main-Class element. diff --git a/tools/linux64/src/maple_loader/dist/lib/jssc.jar b/tools/linux64/src/maple_loader/dist/lib/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 zcmaHT18`>1wq`oEZQHhO+v+&^W81cEcWiZRCmkDql8&7Yrr*2s-prl*=APQCPSvTe z*50d5t#uZUvK$yVEC>iR2#6Qprzpt(b-{pugD6O-i!jJ2N-}*;f`BOhZzwc~=U=D= z6|qmkUwHXnLjOzu4OI|Pl#!HBQ)g6=RETbb9b`rpeir`Rmn1;dohRM40XRh>Mt4kb z@Z1tBlx2{9_*_u-ob{eE-=>h~;KdD!RTs5{X)3(2&ES2+7z5UiV$*0k${Xaj_ z7zlIf&Z1#8GiZX$gb-Qf$_@82dkk#dxGvW_ht70E#1zZps6mP1QFuY&gFyFEBM8}i zVgca}CI{Fpi|26vCBVPR3h`F}8y6QdrhgByvo>}4HxRhLT>cYcXYJtT`EP)T{{^%+ zF*9;>G5U8{g#W_2INF&wTmQTL{~PLI?O^Wc@qeJu|M_xxN7J#=;Xy#AFhM|w|8o6L zfz>RWtxfC{O`Yu-;>tkLc->o`%yiy`Kwb4EM5Z?u=4_n#Qwi)*!hNJOA>AF5 z^zr{LmCAkK#-!x*07E4(VZnb) zN+rOsz=kS7x8Q;*a9xfCl!!7+23kZJCIKCDFEyeJQ-E$pjeb(GSF=HGfEtao1dB`8QX;ou32_^v}k(* zU-871Evj>?CBAdJB^^U%dT1YA-*f~u2R1Z6Q9Ek5{HH9XTx}|mLdF_A;`gqJiOCn% z7$!sPSxc8u9!1WKB))n#x5};}3bfdx)yql6GR}H(Z(A~J1G$HKQ}!~JEapX7((R&Q zHrf)?EoiH#Qo@Oc2dj_bGUP;q3q4aFieR4W7sYNRyS&@m<6DRHsOxSiQJWdH-63>ti<13Ucz}+CW-p;h8Eh{haYarYos9yx)`)*D^ul`hm&S~3;39}i zd)c~3P_09Yt-6Jl*LuntHB9fZ6@(tv0%RNN1nC=JrE&xDp&e7#R6MtA7>de%u{LR{ zyE8X&PB3Qn4tM?k5Mc44dPGq89E4#)W@)R>{SK!2+v^j9aYTJvCnDAGY zw&qm}C22kqQk0WwQe-bwyS0)ZZddQ{SK?TAlpKr~YC42P9D0p%Qfx<90d06osGLPC z_naOg&BV7-3n^^%TDY~;?5e*dtEr-Msi>vu5y)T2(jyQ`(oxNS=}>P;M(|=QSiX?K z&gMXw+0e%MhLN~4TS@E3MDw?tRWHWL8H~$0v{~qdj8_&eCSdWH9K6Ki#a!r;Y7i45 zJ-C3T6v%yL^r zSe99yK;Mc+(&ek7+DZ{iEEV_G>(cOy6N}0>(GAX|2GKR_ZVq=>>-?{jwP;J8t54kc z6u}uVm2i2o>r6w&5umMhswfvFg<0G|t_9Ff?L5nUgcqD$UtnA8H-+8yoI3BH`;1&3V5yY%l%%8RHKsupZ8S3KI+Bt6cmWmohh8K{f;wIz=TRM|35=ojU^6`aj zWiKW?h1HX{_+S=lF+WA~K_GubP1>VDlxTAc6yYYdB+p_GrbJyqX2wa$)tKGtf5W!Cjx}k7{WtOMxxt9G=W9U3Ma>G}oT=MFPtPwrQECYr_(;jc~C)8Q4-yJ;w6PU?cTHW%tEueYR` zL`CjuaG{LL25%aeNcUR1wd<7G&yUdS9J753or>w4vW*24V7X{;=r%$2U9Y+#@df=c z&Mq9C5v`MfDcDRB%@U#FTNpZDlNmU~k3h-cz-|xj1I+`|E#~71oNkn2jY5tdu_iwR zz7$G-qtGObPBqKr%|`bNeCDb$ijqi?O!eTge~sovJm+peyj!V%@c5NCK$Iwq?kW~_ z$f#JHYo+nv&n{LnHl(@8?5GtLTkUG?NBn~ASZoZu_$FtRkW%;5zyVKlHB;v4<>u#Q z4@+ZU3sR#2rGcb3rYQNG#a#w)2{yFJ=h5kn;AoJ{S5)w|wiG8^6&)gUhkdq`>0ZRE zbG(t_)7>bJ-kJJP8)}BvbR1)^lf*6|Y|D?BQCSiI@At_x;%>3@5k&(+wk?Q#pV#7l z(CEvRGJ0;%%y0gtiHL9rgLb|VuL^c{^DP0F7W>?58viI|dI^!Gh8Y%t1vJr1B733q zOgV8m!l@F+a-Sy^{f(-zLZYfMqlGqIGqzxv+Vcw&+!pc`7V2&Y_K009gC zIg4=G2v-d?N5PLGg2;udAYJ8P)C9n6M$?D0^`g15G-|2fyIh)5H}OrwQN5#46+T5| z0kuP*;>O*qpje7nOJR>JdMcCCA9HkT!Bfo1h3GU1Cj$N+AE#|onMit7;_I~Tkj!)g zMcf6J05!ZRdSZ~X#&i&juxRnNWUFCcJt~8P2moDf$^Km7X%+n^P5nx5=v_29v6u&m z*Np=Mi=fw9HACTiGHUPwxEu5o^?l>Ts<`ARI}jdpSVSo>}!$al{pJJFd?I@^1cysbm48~kN~go&-5T1KNo`?TKs^s&rQ7kZWC zyC|4Fd7bfsZ`iFhx!5wxTXF17d&}FC>=w=@cXt)?!;3(N_Hm<`eY?$_i{Y+QK3Jh= zWn_PPrhDSb1&**WryYvPMtIL-5q`}V#ShF0%P*!Ll>K8tqPanE;;ShTD5#)zhST$; zQ(bauh0za>hZvdXe~wQrZVoS^!{A9Z(!v~#48w@ zj${=oh|(uOu;}qEUkPgksNS+QvA~+Bl_sL z+h9%SW;Zf2QDeLh9;NB$ryMZHm8Y0qYzU)a=-c{kIi1THtCloz)3%6(&+snTsBI_>+mJNBVEbG-ZjP#_n8iTxu#5b(4gE5#Q6-GJ^BA3$KhwC4fr2K7(q zW7x8Q6Ah4iqXRg7uU`yecAs@ud6E9@qrBf4d&v?q~D(qH@g$T;F;%^hc~n zIN=L^MCbs((iY7)Q>9I~NLo7x=SOf*&yAfoPe=ibGp2Z)ZfI{dlp%F^y)(&wAn*X) zUUKh1{pmF(#>~&wOtMLt;I&B{xj57(#w@H+s5HRsg4OjXv`#uOp?(Lmx64X?Bj5}6 zPOllx(yK@0cG6_{l?k=tT*vJ+As5npR&V2_lke{v--rKTTXT#C?j7b!S`Fo@Df;!OSG_+2*H^;1WbROcgUjuW z|BrPL*5fw6Xfsc3ie7`02~+`Bm=4b=O)=$t+_!RcF9S<4=#xl8Dyj{yZta5IWAD+? zcLAXro0Qcq^Hk0j#w0GwDvV1+Jf6fdbuuW4Y&pLRpV$*6K6#6w!f$2dNud?#|ho@4HQmC~1?t?R$b#hqln<(| zar{B}{~OEn|0S${j%kz~ zon6UU{t?O;$cy|(Aey^k(}GzmbhEd<$Q2n3r2>RA)whw8Caw~k4MDn5Ilx2;?*0wm zJUxB!;R#BeL_-~xnyC4S34e_{73wuowD5E!*6#3VF!lW+_OTK07}Q z^v|M%AW`((gMxs-L4tsg{12i?xLY{5s#`f*n3(_X_)J|-2~7q0bM|STVailb7?~!z zRM@%|i*cs|+PTg;wKOe~pTPo5Zjy7|AhPcdo$t+0EW_9Z@!*V5s)BcwgIs&L#?3-& zUYFTvF1Fp=tfybs*A*bF<87i6sw7FMqzv^q5SQ3$%<(WJloneUZ~4I>SMB@X2q!a> zS!R%(-5Crv8G);PCN}YXGXsXKFL?$-@#Q#!odTPT<#$~cR%NYC$MQ)o8_Wpb(~Ia% zZsY6OZo=-`W|60H%0C~B1LWFaH#DQ@2|Ars=;~}k%Y9bHC1}Fi%EY!^VHxotIc&4L zT9p^=^r*fEnB@T>*jes$peQFYD`(-~EOF2cgijdcd=%B`ELa#!e*)hpXMA{O5>&Q%4wO}be z8%5;@u)(wrB*n#Gd92zYwb4?UYAgw3>}}z+4k#FK+UP(EoGDt(hRJyk7y?}a z6*i5<={)rr^rs$}M!SZU96~e&53Y3*4{eU+P+^PgLR<#M&Ri2d-n!W^o!A&~F$J_- zu0#*|p{=6A{LEk3Gtnv%jC4|eY`Y16P0nA?obpELw=lgJ`c$@DDRF;O1ruT1} z-?Ad<267&f1{(7N;0c99BOJPF(50Oo_IQUipGi~|9#L3* zOW|&rj2&-d(Z65=X1UO0a{I1mwnTY2Ky7{3ZOlv);+j;@UgEz`w9QREAbyMa(7%uLkg2F7G^*tB&>9~v_S>AM z%^}w+tVy+L4|06`CU2q0fB@D;f1Z6PlEU?G`|ZV_mQXppE+=(hs%MrFBZ#)M3ricD zEBzst7=x=KF3woy6GjH#g^5SgaB#$VymdCCK){)l}J~ zCdB_RL#>P5r5xNYykKJNo#PQCAZ_HKyw>G+eymKjIMj~fa;=&!$2o8PmMjb1B&W|% zj>}Pq?Yxy4-mXkb4-Dmnp(C$NRc0y@5iJSTKt+}AVu}B7@P&QH7=f!U>06AG!B4Cq zVh4=JMI^G`HsD3vMh+mz`L+s=e9<)tuMZ|7dEKAJ$>XE!asDvel870%Q;$2=F+MpD zhl08kFSdE4y;`x5Ybhap)me9G zZ)~+IxWKHAgHL71JLLuUne(G=PNm4&VCr>uncKgA+43;@ih4P38+2Lm94kw-onUs1 zW+z2%JYi2|`z-73NIcy5X^ghh?};S+HZOm~9LH-49!t9)ZNGbI^E#v@WGdk5%Ntv{ zALV#=KBN;!OmXAsB&E_iG51sCJUXh*wM$)WI?@q)S~ZN}!w%W<`(8+vQ1=DBoEHwg z+$aL)$&njQc!LTD>q$T#8~DpK3F}6)f%#%~uu;O1(FouV`CTjZ$LdAMaPWCZc!DCJ zkG)@UJcdYGWaob1JMp!fv8=m*kWp&R{;A+=^Ut|iJ8sD4{Yc9p7Q!2xiv4NhW>tNh zt+Ld2_+VMU_XbrO2pn~qGUgE0GEY`&)#$V!HJY80u5sT6FU>Slg2Qh*gxSJ^2t|>9 zPfe&kZ>YY+d~S_u3k1mq5^n&JyMQG85=ds@h=)47!DK z*oH+^H@JR;j*D1MN%_N$KsnExzF47ots6o=j0~RB-80)4=pX%@-r84t!_SEikirwhTF5~g??&E)55@+4S#bV#`p3xLbffqP_mWokOdwMwoF zk;N?BlB@SvPa~w0l~?$QEE9D%#_|LdAKB6jQfCi!mZbs_RC7IazuIf`TJO8BN9$Ctz zKNDibOWr7)VD=z=jmVPJdKgQ|33@q8*$Mg^gVdz6{2}02pt>iY9g+6On4=EORJNqL z*E98xw9$`A+t*%D8vhCu)lbFa=ki9C*Ixe`QVR`vmjW5bxB^x7IN5?j@b}54X~*(u zYfsksLB?5}R9_-48*ZD(=@ruo4p+=F9BdEVEV_W~ zuzA>4(>idjW8AOZwQ=z>le=s}Z(IuYp_wl?!#A8Q#ku12z931)18?3a#5YRCyNUcV zM?gtHPR={eduU&X)>(IlaK$ZVZ=eSdp6i9j;flz~r9bp*9w|&w2vzZ|k-At*0_Q_r zebKf!+Ysvlh|n0s7>W7T2`r5UhDqM?xJ9p{%^sv~ z$$P2ti{(ti8DrEQ`ZCLeVzADb4lLd(JVQPy7#A^a84WNp;1%>aU1vn*tir<8=s00p zP|1wwIAA&Y_f;Yskn}4t`o}0;Z}zRx`!&brhP`jgdHoT$NI%Qt3yRX5eW|Wa$j%n@ z&tGnllFCikd^l2_r#t|d1pWYs=Ss0@DHac7j;$KzP|I{KfI80C^UgS?&=mC)N_qqS z7sNlisD|y=MmE?WAlf`2AQ=Dp!TpC@nx_lni*JDuu>0H2y(vpSr_?&TRG~b#FnFUymD)29_5dat7IcTJ zA`t5(V0U_UcG|%zptOH?`nm7$W7lW*&yLTo9g*LC-=X+&CH?IZhp7@Hq0eh$!o-Jv zm!|&sk;J1NXM*e(_I{y~D88)S?;LjfRtJoP zy?SUy4^d3Fp-lHcf$R<~G$WI@n)jD%$zQL-CPHqJaIY*A31r#alOaaWF@J_x4Bdr< z2Q zVPigNg+3=reS1zQUbUPl)PC3=ASk@Y0KR65KgOs&FVMd3qqrcFh5AyzY^#7l{q5iFQ<_tCMVW2&#X`IEA=R2n@!hbAWp znAkqJioBW_?qJA}4}qYvFA4gY(dXN>Czu5V;OBi0D;_d4X5@vi-daH0KwzDB^S4s6 zTF|EEZK@Q)Nf4J(h4UV3#J`KOpSSj)28hU!qTnPMe2V~Q7#iBrY2wSpk3(o5J8bm4 zo}U-zpY3I{dq3PaFxxLWDt6#O;r6zjDsS~;0K4SF#lr%t0-J0dDULFy&fdbu&4KnY zeyH@@SsaO3pu+v_r_iLd{x`qtFK1riozUyDZr#Loli2H0*64dx77V*MmBEnGTTusi zsAunRAojUlT#D^1aD8AjGP&_1><#1O$dJQ4$eF#ONQ~_^a&u+U*&**12oP-#u%8l^ zvmF!uia)l)_|aPt<>UNYfUaZg`!!FT5BtLd(t|^)K-?DDZvnXusUZ6$7kGB0Tz4}$ zqp^X#`23xG_bbz+>H3{k+oi+HoK2rabq~2+$Q!n*&o~Bq=f-G)wT>!Bzd!MPbqsaA zeqfwkbv3c)Pe696076Lcq91X3#K8FLuTL#JYU>{W{8=r#d z#nQ_4jrBDF3?9qERO<^Hzjm5}d{#@-Tb?HUS9Idj-J!dJPu& zgC~C*_@0k1mY-VO?Y18H%ewu$?RA%%vxHaMw@z{-(#NMu?O(qZ5r%z3&bMQq zDQlMyHkkPp>2VphKls)J`FK~K?aWJfco(itdM61EJy+uH5hEJ}lr_2!r}6dhhpcCL z$WY_RwZ#5xOc$!Q>m+Vq;z^Fvh>OEN%m3cNiAM@`kDnk$$AgWtM+{=IYF1N-mqZqd z*{&3x#Dk&Y(j}>FX=BtwULuWBhrVB4C5g0VN$i1#r$pTkir;B-_e6W@@!M7QvLMf}{9Wfe?{gv?`5ys7s22$Cb@V2{5|^k-ysJz{E94y^VU2UPG3wOGAFmBtyt%j{R}P`9w^ps5o50if_6Y>4M5G zHov^EnYP2(s#*MJP^FFh;rTkuZh!SdMp1faa3lxkJ>L*q<_f)c_O|l|qFpMdIv`&fj|J zdjxK(>V%Fl3e0Rm2^AFQz8E$2QJEn>Y;1JEc>yKU;oWCh*w_5M z4}q^sxrfNiMxa3m{4{Ou8>4!UD%JKhw2tb z&8?1wT*P10u=4EK))ASl_xppIye)>;TwdTDe72aUD6mj!&rdhzM46?(=z}cz7-?+t z+_m0zo3H&fJYT6Iv2l7d_7r-^+Jy~hz#ct=xpq_EZ06SKvB#y0itq?YniB66GiwaR zY4|8ADsm!afhxoAc9RTd<&;}*=N53IQOe7)uvxv^)NhH!;;=Mu4G!KlpC5*oZDt1U zRWos-5W)V4ajIvKM`AP&a#f*}b?XXpSU$t-(!k0Y!OSy6b3Aj!+pb?Ccz_O2PBr5GX_hfUSaetvQ;Yw@W@ZB{@V zsED`Mdjb^c%6!avNQ>|c?~k+Yr$hW z9z}KR4i|F_?OTOnrMihnn05@qQ`pAh=_2e#yB5I;sTOHaEU%uK()GqO(il~@u~L^* z4&p*QdgrIzdnp7ZlU@6_xyCz;jJXGdA)XaSQlYnm72oRwn#ZXiqQ`e=Mxsg_%8F~^ z%AeA7LdFG$Sm#*_7<_ZubsZ^?J>8<}7MGyq!iyhYJc_DUe+(ozMxb>ycf6WtR%LI7 zq3y~_R@HBCaUdFFSDD+)MhcG~9$S)#W5kRa>9WGazuS({qarmM+V7}Grw;2<74LA% zu%uKnk;Lj;o(8{pDtnt~x{{l)H-syOcvB)CrulJ4P>X zuCAX}ITgiRTKrOpd zWpUf$O9gQ2O(+C*NS9B$xQ(pMOZYed__sTGr(~9- zh>EW{9YX-z*16Y4UK83{c8=UTn6ja9zkgdc*VUsf+3>~rWcT=Zirn&LC~2D-e|7Xg zxH;z!Cjv5RF4?aXa8EM1lJN~kSIT?T06b{8V>&^{n7Qh&*5RyuwUlO6-6D-_J+V%Y z-^%Qsv6OQH&8jm)F2A}5`e2ypNYH`(BK&X{$p5$`BR2loML_cQvT1=EP* zRrvC;AgK)*J$vMIC;0v@LlJ@ITc~U1cdn#A`4}&n?p=qjwk;_-C}YSs*;bc(w>?JA z)Z%d3e`D$pNW}htZ*4++oWzrRGPP#g#b)bbs~(r+Vbk#Da6Z#WX`EKEy?cLOF387F*O8ymvZAsxlhHpvXP4r;RBZ4gF)eS+kz4J2L;}T#r*Rc&_^U-L^1&5 z;g{Wm$$Sdu8#J(l^X;PWx_Mztc-OIn2*htoQV9Bu1n&c3ez#>|7xcp(?}HgAW%uAA z9Pmpr0O{c`o@IWweIX&_Gbrwp1W2?FBntVAvi(ppxF~R{3s{u=f`6DZxG!*?c!tCJ zGU{6Wn&kLwiP>eVf9-d_4Oo>Ff_n49`@oy;Z(ZmI`|!d0fSM0zSqK3A_@3eSd60bl zgYotAB>DRK?`^->=r7BKMt{iOqk$X|CcYCo{g5fnNffVQ@LtIuhp&*8A4fl-RSa&R zy+ZPbQiqU;gjx!@S)vJ&QIM5%*G(8POwZ8nUjD*1Pzj@KM=PQ6-fjgl%T&L zLV%8>sFUUhJ|b7;vjt}s-j`0w&=+Zu%Sq35eL^x!Cww@+KJV|lQ}CA>l#LTdU5z3 zOTNq+L?)+r3I<2MV3V5KkZCC=DK~`AympuJg-pNl@`RDO~4yG|7u8_%L->(%W@ zW`BPqxzjq`k~DN{t(d^dq{SPoOpNh4wL#uqpPHh5^sSssLvz14j=jh0$N87C4H3y-Hqg^VbL!{! z@=zyXbRA?R5n{r14ESZR18NR@a{Nrl>$;;cxbh$HnuO&LY3<5{K9$$V#c*Rdj}AH9 za9SGc0$qX)kpjGC3L3iSmf!@z`_9HqI5$B$%(GR@asuRf^wHK))J1F(G_6sr=UB%r z67m%U#mV6kt;Afp=iZ|Q+4MUK!Ho;BwoK_lyrD}{rga-A|Z z@^A!ue(0^pU5KJw!oaXLY>fgyCHiM@8pSe$Qyyg_scq#H+7c^)Nz@WbdKbP(i)dFH z4SrE>LjMr6H)o`UV&t(yB%$y_KKrn%N)pV1c}QhJDTYI&ec+lP#4vnE5Lz*;qKU9; zHV+9heC{an`Nu|Kg|l(8W*3TN5OO04IDKefCXHn$vdyp^nu)*;e?K#}l_j`zfH&ro z&%jQ;I2^-M*a5m?G2&CjMtl#KMG4y?tbU3@2v}f;DJOleYoWWwfqi5>Vg59j(D5l zN7ZQJj*}9PKAhJRoxACw{1wr7{%Ul$HG$K8_~Jk!t6j+F2>UHK z`YQ;XPA1M8LPT@_*x2$8rNy`W0iBuZiiz$*o(j@a&y=~P(ll#}KEBF2(h7OK#az7; z0qLa#6)4)Dv#_Q22y5#IDa$Lh8+>%mYP~io&w-Fw9sQyTQw%O;o}kV`QR<&;sc1qG z6#2!Fw{SM-{iSXP*1UTBAVI$3|?FNahLFv(w9lnh0ml$%xsxK-OZqXLGuga^n zSW^oJxx6;(#>)*sb{r;k!F!%<+Ci;^N5HxR_J^vqBn4B7Q}u|t?2)q*MgFMu8=Vbw zc-Ne^^D_tTPmtCJ&+KK^)^CivTgv5({Ib0%Esg7X`+21vMK_hqGpInbW>1A?Pps4` zjGa#~TKfB&;1-kM9+Cqh=4m+Q=@@8YCY3bLESnKJ=II*c8;)~Hqkc~@TnmoS>axy7 zYQt2#n&YH>vd#(C0m>y1UfEDKCr@dScihg$S*CGSle+^kA4P1~*nzY*Dc-4^bvFkr zGkEoV2VFT59gnjM8neO`ksDFWHL z5hi%2GPEtKv3-#$Rj*b0B+W9%;3*||?zMK%9S9a=> zeeWgMP3D*}*%>QrZG)(b5%zV^~)`+8-VzWHWSHARiys(#IO6 zVwB~vNJ0K^&40NHFbhFUhg?ImQFqBtUs^V*J9IH4UgD(vzJKoM@x+Z_JZ=t@;8zut z3Y$DdvA=3M9&<($L?Ypo3$EZ|Bl%@6ntvJ24?F$?>`qXa5bOwy3Zau4?`T7Q++`~< z0rZYV*qe{H}QH**f;{?~vnX6D=`r+Zgx z<5C%zOJ2I8IHQ)~<%D_%pW6PTgOj=BF+^ZL^i2lH=cEi>5A1U+{P?H*I3X6f=h007 z)+4n#@K(8T%x5tXudc?l#!F*3i07APoZ=$loTwJV6%5FaVfQGynY*B4gLUqL)1z8T zr}}TF>r{JN8J964<2oYOu55Xp*-1#sc_MuCU97WDd*~4((^Y7>=~=B_duA3* z5_vJ^Bfhy}1PX!{)+m9vx^ot8D-vI;3v+z~T7qnY7HwC*e0>?@0Qx)8@$z^fHX{TMV{2|Ca& zJ2UtUxcLZT;4!~Rt25PdR(=uE$i!M^;Fk79>g2)3=8`Yn7S1?4=9BI_#~YuU!K3EV z=8(DG85B~b{<((|O;ippiCUGTg_^-As5QE6n@lN8ozgtBj;}MRLb=D7a^@nYBW0FkL{z@h9TrcEBgjtXi3g!V1ZNd>3oMN1n~qbaVWk zp7WAt@|j<$nZLiNGk;fQ{@zFT;1%rA`%|7Qzkl9g&WL8;E9=SgQ}NGD-}pVL!EdiL zM9)tdEw7!z9R9DiD;TnIb}?6~*jA+-?C^yy?Y62Hnr=2v( z;Yyq(NDHEcclrX>M>E$6VmqHRfiYmy&q8j|jBZ*1jUv?*xs~dE=!iS)%OH8blTA zjJPQ&a;(=2jIa2z9fY8bMlruxpB!CL9vhnD8Vls!+>^>Bno7<2vvYVGt(r=$x*|b0 z%Ey{!D`|l=$rYX!y*yij41e0>XHHGd^keLN$Epv*d%f8~JH< zqzd}PqpW=tL?kO8IXS)BH__YE$T20P?yN#<7DoJxJ9!q3&dxE1q)8HX`=25-Q>HIo zs)AHNwBcPMfd@cWc-A6?X8`I2)6gGp)lZ~38arWQT_?Vkbv+Yx~Kuf>V~>;w2m zg~BamQwdE3eRQs66V5^pRudTl;)lJ;as;BZ7)eMf1_+II3w;ZLc* zy`jTg9JdH@RXc{>hDd0 zk+^)>&q`<{KjUm>vVX0*NgAwk*sIBK6z5gY*a2oeoEG<@>WETn&e1BInJZ_SHCYM8Xc*ChF@rn zyLnu+pplW$f-Qu78U03GdW*A44UYb%$cVRTW^7lpZMlx@k(*sQ)}C^<2Lj)QmYZ$S znKn6YidQ))jh}H2A8rWX=SSgX-Y)a=w%A}3!Cv@J*P)i}V1#g(nql~YdDcZ^RCo^g z{lv9kfj9=w5UEM6i`Hbp?>FhB5nb3PT$*R-+v*ci{%IT3g{piIiL@8lDldg&P;|E5 z6R28@i^PKDOoS2?SX8$jC(JDEF`ZaG-+I)PhehWJ5DCJsT!D;0GDLzP!Sng;c>zQ} z_^w|?31WFpKtZ5?gkV%w(e1a`kjPXNHD;*;p~#U4_X&*aO$v{mfQ(cE6#%XoR~NY~ zu?zTiTyYG`mUHebF|OnCk`AvFx#kuWrREk)hBkm{D>AI%V;oqeYuXKQ{#vX+fgf6L;NL>6nU+XuQOZj}e*lGlKDF zpauK;{7==KS~=%*soBM!-tg%p76?;1f;<;A+8hn%s+z?UT^7y3xfoAVCUdu4OgHsR z1P#V$oQt5C=|hI!o^TtuRM)KF)&lXLFdIAqdou<{bqA82*Q5pnzdh-|;yDf`bJ7o+ zraGj`xH&Y;9I^1A z#2RlrQC??npE5dC6BKr_@Uign@Wcb-gX06!1E8iX4WKfVkevTd1JJ_elhZiZ-*K+L zDo^-7$lz&a;pA%V=Gm0*0pV#jV$+9xN^EP%|u@TbNBXFV8yzdnn@V>~^;k*~a6qUL=iO@B{pdTv zc{ftxU1p;#nPk%Vu*i4s%)rDw!9#i%l0p#PDT+Nj2q!*c>(&{53NI%HQ*-dN#DSj} z9F2aLuIB;YZaB-w#GVQ7k=k$0%cycGvhUkSjn;25&xE!GCR3S{ZoBqmvXWMd1z#iP zwMngIGX#%75$@C5cjw+`r);|IsvZy)ZI1_`C6=|HUaTEamJF+b&~4WxyJMP3G~4O{ z#J8?w-s<4L@IRg>bxt$s#7YE>H`A>3UTBb~c_0|F$6%}Tx7dK&oX$v%hRhMU2u*6} z;ruS$+R2TOBfvZsV0{yRGc+J@jhyd&4%N42;o0p*w-pv0_hj zKmHi6r%&jycB9L|bVqT;M>SkUv;zZhd^JNlw^qpx4aZ!4_bxbBkQd}XGnKoc(0Gd z$G@kr@n#rOgzb3(XXs=EWWn;@j0v#pu+6;{1~Ld?4o5gCcxR(HMts*l%|LB?Fx$E5Vj+?pcYiyl(_kt#AZ6H2&?rQaL~(@fBGYVNQLRTz z*yvMEvs~sM+kVNV#F>6lTSK7s?4^wY(0g=FfEiJ4v4`}L#?f=BWe98qQl+aTCJ9OqOpop97HHVf~LUWAS|NWVyeGNJTk4NQeP zQry7quYmU{02M)ZDSVlNcFErTK`%mop}v3(5>ehrg$gO|DM9Zl?YVga9je{RzjoBG zVGis_(iulB3O`kxhX4Sz> zTQ*@yf7L&@m}juatP)@P@ejE0kABPlIb3lx^FX`Mzi=9;YhM|EEc?V=uGusNxDy;0 z@=xILmRmJHK`QmfOU+iDjZRj4hW4`#XOg6xGgPRLlmB!!cYd?EOCU=jqFFuU`e!=^ z;hi@_{dazo8wLaf?_X`ef0(fU>H_xD*mgnFK>xCD%28<3qn>Y-)RPdg&hfvoSZ)iZ zKt~8ISEf$=(WH>>D%Yjw&ZV4xETWPLkQ}QzglFqnju71AM#HvP2H+EU-c3+_Vm^9s zSzB8(v-w6BLd_BVg@o`cU!;@rhIf&F2M4cMLZQ`b_G{be~uHUDKg{5Wk zGz0Y1AA~|0tetVwqyrjr7oBj?OnFKUYNKULJQYX0FtF71Q<3qS2>7^(NjBCwNJM5k zB;*E9axb+@(mJiD_$(ANCf2iE;4HlcAM612Y{$lhBO?c>%{GjIMy z0b9l1Uek}kxtCCU&+xL!a93MBXA7QM-yK$a9G)^1Dwc%^J=@f9x5m#VWg~skjcA}g z8$!olIZnk;QwL+ul9(*c*}aly5^NeTEIUmRVz#(C43bm@SibY|OwmBFnZL0;r5SsM z6)scjh#*9oRAjP!5MQ9zLF_b$JgqY(<*6HIi;ifQurw(*oPce*Ve_0r<%7>4$vckd zrJ#jpBGaJA&%x5>qvgM!22Lu?>4q6Qkx7AI=&bdAwz*{`{@zy@yG%A)Kp9sf?z zF!hnuAZ@wlEr$2QEAX=g(0+hXOV(x;Vt;Y5MMb|RMy6Qw`>loB0fNmzWrTu2rl;5p zHp4A5b5#XoJw2X|`Ybc3&QxQ&b<$=F$Acu3t%d2tZ0v2`9yp+@gG{iBJkbPWv zG{WRX{lSlg*%_pkWZ4PWObMQEi7kfVJGCX~>z)$TMOhsxCEp3Ju+PaVV>)bxhkEA? zmi!}Mgp0e&8Lb$yBA+N)t#$?2eG5{%#gH+T9jP$-ERQrN?yV`c5i~ zziMwZ7!nAH75GM;?9g)JG}3ZvEwFmakt8c6JGVTew5d08^n+Un%S<9+Ge)q5+UXJO z^xCoSsg=Lr=sM-}%&prP-FSOYcgtxzE*;oz&@Z0j1;5)v|B@M;SGfEDCzo`*jtHbq z1-m9zh|bUnvxL1~Ey^17S&m`ef!KI(b~FJ%LAP$PPLGE?!thASr24pcoO882-P8%w`Ei67^z3OpHd}3@JK}z(#x)8xJM(;%(97RoW}gidx>Y7Ioj_ znLPpV>7L}3LC${|7CU9gA46xTKjPBkn?I3}>8mpsi5F+s;WQI>!S63I2xn*~9zCIz z)kD2c!NhrJD{p9d=SKO1`4uw7v8mn>!{6rP=6n;iZ^&#i4zKpT+rf7(?z;OHjH{et z>wz>hHIzu!Qv(F_E=gQ;h3`<7x2_yQcCosi=9cX5bY_8CarMASev~UOZZ!70Aw@}^ z7FK^7bnW#u9`n!D2x}vIBOr*2(OmC9qALdq4!Nb3fhyQFs zA;G0)4FV`wFr&>~8CWD$k9n$Q`&tO4HT9jbo%bhS_YJ*tV-#jDvqN_olaGJ zg0v5N`k}rgiD&SqH8kBCjT%}V0fw6|g(aB!Yys8gdih94gGhaLRHKa*N8>;Yb|i2W zb14hw7qxlcJIEu|qkKa8EOHxvxuxfNjl8qDUr@CQpOFWort>95ueHWP$szhQAgF7g zTBU85p=^2%bmiM0?)qYmF6y5++NcpRRbkrr4Pz|`-M#r^_6H4;Sb9Qu2QCHzpZ!-x zA$m|+b4$ANe4Ub+9NOG1=>tDTPNKf>{ug0q6;x-?Wo?265AG1$-Gh5@_uvrR-Ge&> zcXxMpcXvCuLvTBX14Gq+^M5lnHFx`>yKmmEw|YOd_F5ju`Zjr11S{zEhl^)j1@EWv@CNaSe>mS%S0>dH{zHvKw?ayLq>pg zzUE>=!pe~~hHkA}2*1@UhdUu_q0p)|-*|B0U48V14Q1C64>ZSMg8DRJ(eH=1%_Cy# z-yX&07rbhUL{4_VpfI);Lxm?aLK?7ug=W0FO4&tvfbB=NSIXuWnmd`+pULLe&tkuq z%H|isP5Ye`uXn^Anpmr*-b5y9R84Qh-cfgiXi8;r8Wn-Qo&La9@|`G=;oo>$oHkzX z$q`>#;w=*ZW335@OszyRX38YVgu#n;wwwQ{R72W(ngsG%#U^&0SK@E0cV0iRKXS&* zPNa>J&>-fgc?nV|+4#Wg6RTS>XQQaBAq(y)E{s+;wK5s%cchjsm^(L0cSKTEo?8UX z9{ZJ7l_ssFOGLtLV6}o-{S8gMUA=YPIg6=I)0LVFE(ecGun7)tAc=|I#i!~#g3HjOZYeh!x4N<4&*iLB z$9QQIn^3&JvsjT^4i<7lXqOMQYnMf6d1)PNE}Og4CK9J6$ductRcH4hq_~PpNAM+b zXg{>US7F*{OB{X6bPu?jSgtBYwfeE1wKOiGnANuZJNuIjGCF~U3y?AehKh@)(cP)X z-DbGA54a{IE6Scd3T{nxI76}m3NIUnP$8gp*|S>CPS<2=_h7xm`1X$Mgd63zB%|ZK zB3V_W@}(^3r->{I-RpN##G0~sNb%2NA_sN_!udjf!YQZrSBX${V1+!Lzc-Gg46dYXH-)rI^ivYtHT$KNHh+~uqQ>1nb^5x1 z-xT&Ti5_ku$&XtQR)o8+L=3%-*7P0W?z^BqBwEQg$ZlC4TaFBoEA~DRBS;5+v>zd8 z`Dq2b`KN{@gaOWT#0>%Jyv@vfhYV9&yL&-~R3iX^fOdBMg7s352O26gVa$n=P?3+4 zQZ^}V{>_InC^P{hCE+4?EX>UNh8$DfAG-WF&H<+jZuJWEH>-9~RnM8^-=+vLKQ9#B z>6QpTg0_uV`C4p@F&r`Wm%%(Kxju>7R^2a$c$vpz?6#do8G~%L;gMz@{r-Dogxt4M zzxSpKXu2%c2@h45pOv8ar0r1)gO6VPtqUd9zDjw(&x5^#Z5|damJ>bmrhT_FKAxpHcA2Hjcye;@7 zcIJ&A+`nwK-eeivdE#eu`^ikL9Fq~ZY>7vYO4;v6SozP3zJTth44E?L8B`^~P9oTw zPnepR-gaG)36c9wYJjv*YgkmqDebBPo+zS0zf=RRgwuQq{l`wL>9@y~V>X^YPLWb@ z=i=1(!y-+QOu-V|u*0O$;e+833I z#WM+OwBk^B{FP>1EU}_@jLqZ=L~}SQi#f?-JMyJ z)D<^7##Rl4!!K$@Kl3Y+Xwc*A8f5f%-41!enfK^DAR>y;fBHa>grqf#(MANo%}&Lh zOl7fm?)dw>!Wxou{uUNq2%NRS+XNLf{#n-;nSYu@r$)@%Ij4Wfw=JP*|tiY$0ZO)F^JgAiuF$P^b zR~o|(9`BdoU@V?HYf2KMTDn43tZ^up)36cmu?|}4!H2(1Bu;do%c%@?)69f-hT;}X zao3hXf`UnFkVbXXM^rI~f}!v#+nC6yr|?%IKIJ~cbx)bM$qW&t^fI`GXssZm z`WL_GZQW&U-!EDZc|{^gNmkmU$hv131g>bvMA0SC@+Y1tqJ5$PF2n?;+-DCtbS8%c zK+QUCobbZ}_92;vqkYo}vY&rxr~Xb!V@}mM1tjTbkX5Ia{(T(4Ojq>#_}ul4x-*E* z&2o_rC4Z0CWvi%)SZ;uE3-;|Ni>V(4kL26Wx@Z+Q#1P(5d{1SpRUvZlt&`e-;=kAG zqdY@E#-0L=mOeB|aA0eG@E>*(?7kQ!hxytL5XOIk3%e4N-ct++h&UPu2$Ijc_y0LZ zeU?_Ls$z_6Ok4&=PPP`mnVjvLbx=LjmXmw!e<#Q#1Rzlb<2JAu4rzTy`NmjjL?r!< zR=Jcyrpe7b)Kqjt;rB>7d}s<&A+(?xS-f~r3P;dFG95S^%bgL14^pM#a-!y6E0b%h z{hfy_bjNT_hlc8J|C4P{#wcd6AL#0kkJtZS`$>-XHSY<4`+kMVs!cC}qQrlyDiY1z ziQj~?FIHwmF8XMR1#VJe(^Ny@D!ve9#O2hCkO6X}>UI`tk>~JR;_g+34P2P~w+Bck z3BD1ND+~Lbevyms8;`7lT*Ac=-B%Y@UCDZfCYJ^U)5-P2?%RV_LTet4yo+6j%iP2s zG_E6$BYDn_XU!IwsA*qlpr)|6cCjI6*QpFG)l6t$^tfdEwTaI;a$7x+=$et$aCKEZqTL zy+{Epfp-2eXFclj&EOeOE=cAmv9<@im=ibBYcBwt2cdFmGVo|raC|(aFRS9OFnT_% zgX|mM^gFK2CsH`gy%c;sifUb~whR1uL8$IKCzfx!IgQBF_T$~xgIENug5ay2rX6s0 zFZea$D6x{hRG+?^?LE<@?UfrxcgL2DXUe!#$NkiY)q5>Z>h|$jyNmUi&=@)T?YZHB zWP0D__K9$lIj$m3705T1vNoHu%}BnR(30AYu5~Z2jYN$%GH75Ot+L zn{4kupK6a_l&}zp@Z{>UP>vYLO@3oNU1Yy_EHpnt}E@!F+q=Wwi1HBWH{yw@|d6&Ag&SRe?vd$P%>Tl3bJmz>_+f5gY_1gy7Q+5 z=888EVqfjR^;QO+(B9q~wQlZS$29_L!@!Nj{Iz9Zrd?9ZDw&9TJ?;0UK0llhLNoBRNrK+XP3}SZDgLVgLeWop1=~=dJBll< z`4<6GlKU9M98Z3Fz5!B2hJR5b0>sDg!H|x}mQbRqvZ--D4Onh$h3 zI1G_Eg%vtVLf>zZc)f_M>|KYy9xwnJ7D%3l50qC{&?DjI*YI6x15w{CI53}}2N~6uwE>uK}fJMa9#2iegCzwpz5Sm~joy zy4NNy#gJ#j+hQr%qthZ;2s3W`EXwzyVT1mI!FEl5Wyyom>QR1@sq)EzLwFNYipKKL zj87cgqfrxgdMhwy{GQiKIrGzp5<_ooedRcAgagraZ${}&hRVsC<;_8l2Qw}-$URh- z`TAf&s8@E1#7Zw*+9xW+C*OhL^jV6=#@eug^xNGiKAKK;JyXJTnA`>KRrJs+MaNda zWNj1k_m)?gw-aPcC->JMZ<QE~QS3!(MdOf= z%ZL0tsQa82Vfm)JRcC6X=iF$n?~1JZOB2qAJk|49V>LW+lH#8~wvvfI_Mrd5+|k0v z?X||pwXX}(NbQbsv7^87UXD`dD%*Vv{st71@Y|j>}c(~=;yWi_x|6E<|g|K(W zj`!YLxsb2H9=#QdoyZRgP~F?Y&jhFPupOJiwXAcFP(&Pa)v&-XP}`6z(<@pbpedPy^xV^~X?L|&p% z7`DKpE~u-fW2({L>oC5@FXr;O^=1f9c~cQ?-OM$ZFv)YQjs2?B?CI=Sd6ZHr^AAK} zY8#CQb-Jjh1$|ju44RfE5BQ^XDR?8_{q&DRIo9z#5pKIfcC@uBG{Kj4 zFP4fW?!;R)8R+92h4o>7^u}Enfmw}%ZuV^3@H=T@8fh52@+GR)iI(iTIb8MLl*bAs zXrVC84CVZ=-8yTAZo+Kov43Wb#$CRQXi0FT>9Tqs8Dqys#s4@$2ZaXGYXqb}%*VNS z=bL}A&q!Z*k>5r9b6xsAj?|kd4}s$-zaO z&82RMb*B#rtR?fy(#4~GDZa7pQV@$Ze|I8S$-U%2+>|%2>J{ICCG8X&TY#|}raaY| z4^2?kC-;G$7la2Q;vyBFAGn;mcQtO>+aGi6kT&~XP zk-Jn6m`fY34v_tp5Kz$P3tInkl+4|5>ymcJY$(xJPTsiAvtR8wCHlfhKM6Af#p8BZ zPuRIK)Fnep50QPEGk_pn>km0e?W1H&Wymhvu=p%cW7OC(bLrTbxQa>jm`!Hzz?l(p ze?aK8;ij2S*q6y&L@EoF1Z;Ig|<~x(b#Qx|5W4J0jX?iy?Wzv&bfv@2T=dT zwb;90+#TA{Az8Gg>aURYZ77*Y=#K)Q(f8026BOcr?4!tOK(&Glv4-rIqFzYB^zZhC zI*?tzG8dgF3x+qHV4g#qGK8wbU^ov*>tQt!J*@`5`~1VR!*rIIx_0LMAZqxMyg|7X z>lNd_WaCK4Km~x_fW`+e+0FMVG}4zEY>7R_XQzawps2=V295e>z*So=d+N{HjCzO8 z=IhV+8)`bce^8f;CpXPS zfMqV_4(u@DR^FN=(k0Z@g~EIa<3|91TDAw-sX^!sPN%czu44BC%13bKZz{5h*|V?Q zX%FHyjFeSr zl*`aRnB`qi`rmcynCr(_DhBxzHqU&*wt~0l`n|d%+;nE$k-XeaW2_6jX=|Ci6jTl9 zkJy0;F*+`TUCd_Q zyRtT!hMN>N&%O~?&aFl>Wg;#uDfv%xp7Q23(ave-DA|Kk?dp{4b_t}hXTMhd5xe=7 z=4}}l+ ztjVmyEpgk25M9#=@;O=F&~w? zT%S#+%EMQ_?f*LTzJ9#JKwK%-E|6Jqr5GylRAB3#2C!${p_CnU=5a3x*oe#=8P#~+ z6z^=a+r;vdr5ZAR8N^<_FC&RLQ_kXxSfa3uX_}=Htx_Tkc>Q?JSm*`mu?FxXr<9Ab}LiVaC-m zR>OFJz8H_xP2(;4*oh0eM4qylx6cZYjOU3TSMJh-g#A(*A&)LSF7{h zy)jNWZ%NORhRW8>x~w)8OvC2!^K;?2FMaiKi%PqDLRthaEfQfl4{Xy`_n8x{1Y1xa z_7WHKcGuVw+9#__ZXvMX3$U92Oo!Pj=wo*V31;&&fP6W(fvyX4>C8N@@E&;lFLsy)kgS{G3Jfj6I~kIr2Q5 zpfA&%m4z8ryQlgYG8~yq8F?~@Gln&g+wOr(<4hW>g>jtX^V~QT@LaxPQ{daT78e_v zO`VIwN$XMM%c?L-GuA5@B~jwNsG`QF2n+Gn~w}oj>;OuR>^qw9t*>E&bdjcHxC*EBMrry~y)*}e%wSzNQ%?zn!nwDhi{yBvQ>a0yzS#BqORea8Tw8E>y zYJ*&Zy@Y6iTJ)3p1t&=-#a`OGK9oD!rHNdjc0vLA-5y~@bXPDH(dK>+EMHzz>(!t~ z+?)NDv#DgBtgQ>X{HE{qTh8iM?$Z=wYr=3gBTd1&c6odJCh<^5PFOG!BymR5>;1P1 zp-Ior#zOHm)weNE=@UTed}|*y$^$*n2?5CwCoXOMnN!^n_m#lX|0! zjkf?s4&~1oq7A;h;ux6D@TfvnU^zFIcrD@2H9E952jrk`B=@~SbzpOBL&*pq5Qp9O zC~HSL`!Lz`U=?{TYB5sCRzX`|evuy9izzf$ zNiP`^tUx+F%8#89$Gld+iKN26&Wi9scjww5088`eVZiQrj>@&}~3LY1rLFPmQ|-Kt~vwY?aqEVCaPL3o#y$;MMN2U1S^HAk1}H0wMuUV^No zhi-FBA_s(NWkNgba6+kN8oJ`yB_0*uTy;8EEnO`yLBc>rF6a2TCv*s3I13`x6=`7n zVt;(eT?}P(%$w%#E8>eu_{sJuodjyXRBgVd(WEsH{0U=jAjw8@88GRd9KV-u4@|O< z8D9n#xRFg6@*5Cirs~QyUwQxfgaXwIo%IsN+OfK7TuPcLOp#w#6(;6MP^iJr!v& zfM4aPE*mVpF{ubn{=^NQo&@$TD1u^Hy-wOb{8{`?`3{C%UAg%x&cXDMJ%0;JfFJM`FifTdPKc++`c{>{vd7R+)3E`ymJcRbv)dWb_Sj-)KMN1n>B|(T-EQJ$9l-YLfC{$&#Bm}#O_oE+|fEW zvR6_Zw3LK*zDTY7i;SnjV_=_Kyz3L*!$X#XG|o91G_u#j3{L+DyuTH*)m$M^+^~DB zO;0rL7|q?mq9a^C@IvAgjB(&?+QQIHjr5dJ>d0cVNpe<-=kFe|qVHXMiSDHq8W~Ky zta?rP;KwjRDd6={@`&ywgl1i^yh`-e&3T??GFRvdOz$UN-g`R3=^-e*mYT?ua=kcw zh>G(TU;Es#V@x5dxO3hLQ{9^=;kDTC@D*^-Le|0mf&UKBZV}k#20ODqDCmpYSE1lo z^;~h@EWKw0m?sNrtWUh-{3j&5Z}^-D`GkZdwEq_*bpC&k5ZOgdBBf?kNSfMmEC}Tr zsb~_1vDOF8BdxXi{6l+LGkmJHG0({9gNi%x8PDGQI7Wt94uLwma09 zud6$I_F$zG(r&fZIV8906-Hk#5O-Kv6i#aQ#GP~2iqXNBs(h0FXyYWv2^ zn;w*%hE%&c3FNU65y8n5Un&&h3J3NJqIf|m+dnu?KsQ51cjz{|AQBw2w$?6;POQan z?~Gi&3cq$o#XVJLIDm(>J!hsses1fHIE6LDELcj&X_PK~3|z1**tG54y0pMh%s)4&`~HU+BIpeV9SO8B*n8I`HmB?!j_RnQ+FW=R`rj zqvgGMZ%lnL+*o({17beyNpK|TfEGe?2*@-0qdA8u#KDYJ(t;7`!uusLa$+p^P$=wA zR0rXG_q+1;qpmH})8AZeHZWIMrYjIQQJtcPqURP^7c|1Fe%k&kI)pf`PxCA2cZB2H z&m>vz^H{x1LBF?`FoS5H)7%W=%bLcm_6|$C*5?|7UmBnn>s~NuhFQw*lIP`U^qIM` z{di8xbsa~@fW6*mP+n2Uca?uLw}@{?t+fVRb)Tq+YD2(h+5hg;CWbg50uW-eUuLfAlwT%1D62K?SX8*#n_lyEy zX5@>l1hAKB|6{|8OQ^BTx8-Ng$k3vfFMdGI`nYGhuQrVYhy!xRoYJ#22M%^y5`=aM zwVS8~{jB8yrZE)4c4a?!Gns2$DN+pSkvQq@PsPX%O6`yTG5Ng6Bc}w z%(J6xsz~%eGO!VgG;(%&d-D51-b{j)e|+~s>8WV{x|G-S1K8GK}%kVOU}9W({F#WMEmZt?lgWqi~B6gU)u}e*Q2^Lcoeo89R5zM?nPlo7OQ7i&p26my7dk4V>|mF6JlI zef;V`zZ%Cf+D1H6vThP+zRO}1o%e`roh!eue*!M=Y5i!XNb9)HTA=s)%i+RoYsF7e zA0jV1OZz0uCc2m;Npp6{OF%K$kIfW!3SVPf?!ZAP!%iod8!%&UZ;MQ=+7SwK`=kZ* zfprq&c<&4JV91V^N*&Uju|0?+0Z?GYHcyRy|7TKf$dD5mItY)j`J>D#d}yFtGFZdA zB52}VxrAG;g?ho~8`1k%y8uyixTPQo$P*@HYr4PX#*>qEUD`4~#vEFSUNm1gvMqfm z#KZIZR^U;%L>qtd{`jbzD%k>Wn?xxLtT8v&7KeX-gIU0O&JVYPK$t$wyM{gn#q70q zhW$5lD9#6Sb}%)3;V+=jyWZ$adt6KZL$NN=()A>l9sDx)X6U(nW=Bb&0-JsMxk*7z zPQ_1C=yiYLrA6?cnW0kXe{188m>x(dJMaTMhb8a_-_9Y3Wxer@mL`K8J!;61CM)0% zAoRs>N9nnZwA5sAH~TpT>I{Dv5u$VlpD~H!f_@1Y;qs-jpi~GB@bEBDM9aIN8C~um z2iMQOz#q)*h~wgfnFXH#K3D3n8zGg2usoF~2eYEAa~~|~;(_Fzz{hJt16ZD!Y+{YH z-5WoT;D;UW7;X0w9R>oZVJ84PY;`x4-5DaYob#?!UL6rNob#Mi9TVbw@=ep?s5$rr zis`2qRKT)`_w0!GF>w;M`RlaPZYrQUM)#)pHQW4b+Nm8z_prDV3(RElsO76_hFi>KozrNa;ax9J*>iqu{U4$;RVVh zpd4#fPe(>kV&c4t8@pG2{p6-2QF@?5d}-r-gd$ zH{DxO@@g<$OYQd#e1m~Oai%Z(ruj;CUq;ZGNxs-vYE;$rdl4R~u#Y4wEh!>|9JuNE z*S0|0eh_uRNdCyg-Te1Znap4vq7OfLJtoDz=X^X%lHzrx7J5sd*lPwjaix;{BRMZx+Kz_B5rw`mtW(Th7t+VtQqz z!3p>U|D&2;HORLOqS2+8xI6M&rao;iKsht@$*!Z6yr+E7RNmRW3KaU83pp3nI2?kFmNQ8aWv_(n4=$Zy_{`@l_*`9&{q z7J=BA*AMQ%%{{7ti8QhHrZN*5Ul1fKD6a2dIiv`Y8N}^Wd%%Ci6m!1H=PiCv(_VV+yg)@f027o=> zwd&x+B}2U$%leB+?4gzxJy-ldpTUTb}PyzUL&Vn<>pTRx)>Xw~jJQJOq^1bpG!3 zA~L0w2kkSG6BS7sD@^_2U*K(QenN}%HBZi8#dKU2HcHW12ia;NK4Cvl6O|j zs^+2QoWstOxUCKvt}RTyP>=-5V<*+-WkGhykt`COgT1=8^YPY>!sC^s) zX#CC9m~H+MYLSgZruh$^fEAOBkj4lQUA?-Oj{zrS0LTqUd|M6y&4{PfQ?KMVMDGUA zsSC$)|67Qo4((_G`GfDd3cQ2&8TSq+ebVub7BlB%Sx$td(Fn_45=wq%0t2%?nk|dTLb#Tl5pOVhVcR1N?@j%wM!zMs!$qY#%6XwO-K1NTQ8Vr;Yf1Kzb2pHoA|1F!>zQ;1E}wLywI-9h6sljQ5BYT@3rA?&@-L+bSYQt;T@2Xi{D)W1l)sg`8 zp?@gt&Upx)WUE_+t{^_RG6Q-aBWeC3HP1cs9fiZcBX8gL84?Lf<59EQM_EYLeW)}m z)C&cUd{fSr_;ICBc*i4n8^@{85iBr|)$==K;+)wv<9nQ9^u~HlDvu zl%3@+}M5WlPz< zFkDwd_&zX?e8o;U*+-JnSWL_PY&Bj+isaG!&kpmvown0LY|zwd zvOHZ)53^0ya)Fk4SQ7F*7hMYs2eo$cj7{awl;e!!cHY8uA3R%(2@7Hz#LmoM03Te~ z-KwS0{hg+@-t|-ne-9`>2BB&*ef0Y6R7VT1b*>Acp3*l3 zm;z$3lbA2BoBZY6wUhWT*UDWsghaDsj$+PXTw1==pg;KwpI}A9y_Ret|0172Ey!9? zNThY=+hbm`v@f)a&)b32V?5;+>D<$NDD778f&t^7>WK6rdBLIdOZxGb^;RC7kO=q> z=5mJ1Zxj--9b>j{!ZScsm64_8tE3j`>nSIH}X?K!6bIn zRJ$ZCAmoYKZ5$9JQW9hS3g4Wl%v50(JGR2L+8 z0*Y=ASLdRrEnf^1Mt_>B&4U;9-;+2Yu4?~YY6m*r1yZ7~>1W)bo}Yzs*Xdtk{3-n0 zM_z>}xH+BoIc}m_)IBxdTZ4`b<) zjy_MX9}ZeI6&!xvWX%G+>>Z6A;o9^89Sv%Lm-OSg*-Wn_<8DKTE{HN%H=* z&KdSmcJGm?4q4ve?`HzP$816WLhl6(Zx+(S-&SIL_gB zu2M)@$M5Eq)v}B?Bj{SjsMdUE0UV`)2|32`F8Zmn8Ku5)j?Z&jK&uyi{4!XekZOgS3U^b!!;Q8{}pkMqCg$|5Yz zlOdIAe^`1R>!tuw@&buZA3jS4pMsMW!FM7e&Zf*m3jpAA=AV$C=jhVjHTrcd{gB4o z^%sXvFI=#GLgBU03iddjDp*&9U?)6&D@4Kxn#55LzVNL)Ds&_55w8Vr*849XP{2uy zw@tM5$`wQL(OaIs61iam`>FRoxuCx(Mrr+Lj$;}p1O(ClE*|vY;rf&gUg`R~s7mzo zzRrKkCPE6t*f>Uf~_n|cOjhZ4XT&?5AFF#1jJ z1}eP{DRGkif{zzRvRpTTf>!RO&&pN}~SNmK%kjKUSq@Q^|0d#D)W~IHq-O5H( z|1DpT@Q&Q*k*kjAa)^-`tP$*tm8nq3`4o_=)fBPCPk7J?1=*HG&+X~S9nm!lq#(%0 z{FOYidR%XYY!$HKIAid-I6>^dTYfr%_gGXTI1|EAZcmyhYn;ojSSefvq6chFOPV#H zTqDp8y4>Nu}H+kv;vehRjOJ`*;!e_-1Oo z-wX}Rgs+jq35KES8=qosk(&cb!DHG5%Q?-Nl3#xajQdAp>m~Q?eH3-LaTxgj(NjB# zK7NP7wQlFtF60*FK>%AS+bQmY%_!`pQLbANM-QHr^~wuVWqdL%L>|i=NS1ZWKVY#W zv#OTHG041*et4}W>Z9qrwN`>j&NNe0~8rthnV5@(mbJRp2 zJ>xVRl$@lS9=c&MQ32f=N_9h?mA82^YR&t?XS1Pus|O@^6MYn|-||plLc-Lw96B{? z?ljwi3aGz#YzKB$706+36{{(0IyS?}D|)Wu*Y5TdP6+IDHZah5L9tH?^yhKYv|Z11 zI2LP8JqF7hdyCuuJNL|2H``d$CZ12!rGi~9b@!;?|K}alZ~P2tpEso{I7^zd{PZpbr7{nvwySC)as9x~H60b?31#-!J% z;k$d%5&~Dj)AsxY75hI(q+UVn;CzHzpJ>)fi}%;OX=WV=y%G$9C;o30n7oPSqH+?$ z;OB*%gu8(XXO+$!)e`(ieq7C@L)fkyP%wgGh>+I5M~$2~(z^-p^X!9W_!ocAfZiec z@ZpC!^2mVu`3F!?&a-u*%vbtZTe~1Lfoms&yRTPU03g(BYn$@UsMKW zO99oJ=@Lae5hrDciIVD^qJ(_#XdxB+g7V_=v9SF362(%|m|j@MfG?F*0s{djWzC}U z;yWJ{Cwxv&2Kugc87=Q;@s#MZ)WnQcPy(*}$~oTsmGi2V>9r9?C5{(XFLkNeRG<0l zgbDTjt;dTVE3F$n2Z{Xo&262NveHWxUS@W3*cXUH*PlE^-@&TO}$A&m)8=g8dDYpD8b96 z?MXZ&wH?B~5Tuc_NzJc3Vg0!0y3jm{{p=9Ta9J~kfA2){?f8Psztli8u;3Q?F8_&p zgqnf-HThu9qV>wf0>=9V-4=f~YgqSWYXoHXpxv#O^M@6~+;J-r3UY+L=VS%LYfQ&_ z>b8A<+F7cw))q6DEg#hfArxVXPGX*MP_dAe(4b?+Psgrtc@_L56G3LCQtjMhr3Yuf z_2>8%K7{O*nDjq{rn2Zn;+lqH?@(yNjcmCvIF!jldIl|7892>0V86&S1l4~qVO);V z#I3h)MynU;&-j}{c~iwX4i&0gq)|@lB;CfQNc#Mm=Gh+0rDd|#%e~=YR9l6TJqJ2D z*@`wMcS%eg+^um5exdT!rIsu5K_gNnAQ*~xc+-ccC(52zTCml>zb+YUya-F^k!XuqjEF30_#+ZDw-C8tD#I=LNwXo z@!b>-fy*jx0RG4r{E?erK6vI~jSpL)yst0;C8oL_RFcVDRtH5rx!icTAMWt-=beq? z7L_|v(^EfR^mIKKPLyF}eSgGW8diQVOV+f!Bjj5aRliHa!e>e*HrwopfswK=#b%%I zp}*-4vYyL}CXQ4o z3$EX2x!p(fUPvH}38=c?fC!KZD!YXy$VF>fIK{KG{ z>mko|&-trEB->^fbWofl;Z&vxuYF`jjhU)f!&{9R9ijI*6k5PuXB@8wcNlmocp?(L zc5=C^PF>rfB~kTF_{c`8t3LZp+L1^Jp`c0h&?ihAF#2Mzh!ci}M&r@2{KpM$<1v1D z$rNt*iq~T!q{P_LfhcKpVb>9{)ftuF05yrp&2hyfwkHG&FT-e^K6_X7Ipc`02%iI;8QW{KwJF z@m_MCSBKO7%Oef8N`~aRNM;e|c@ZV@)MxOtr(yV*lqBc5klZ^jMavbXxz#1=jtBt4 z7RDy@QZBUyc?OkpZ|&hXpRv6!LEP$!kHxoAX+RD8>5*ufDm>|v`S~)t0M<5!thJG& z2;C_gv1b@|Absqk%j=Rhdi+xW6wunturV7ZFoOMR*y zN3tGh3dKgM-ckI;%x1`mpgKy`K&W=^GW=JQ+4)kJu!ydsYbg10D!nJldf8!2yz{h- zenpnG`rXK6`?!pLM^=<})b<*m+8p-Os2(ED(jz}~D$;z@rimNXvefE;?8w+mwibyz zN5@|M*J?Aa#gOs|o@KP!R2;iaOti^_SU?cm8|@U zJxO+;3SV*{m21TCzsnH%R;M_?Y^9sNgs1BtiG=RE905#ULDsPvqC0>9Yt)kE+M;c; zAlKMTHS&x`yCENZ>&XqvOjm@FU69L=AU33dj!cbPv7rzoP=hq5$ z!g^!_(#}@fwaBi<4`=k<{)vXx_zv^#DBn^D5DFj1$3Oa(QW_Y&20gpPlQfaga^d=Q zvFtUuj=07u<3H0B$xIv zeuK+LrZ?V6{xwsAC6&f6dRdkMghLkZ=V;J-=3mFklydZ~lj|@Y26gL%z3q6r!;eh3$COjFymOJ!VYDzHpp6u;s;zOWL_o1VQI#e1D(l~OQH{wsFJ&qwcf zm|Nj{JrPL`CA=a_;Ag|3dQPI*KYMj|Oh!nhSMJpIH}|Uv^Ec_@FLON(f?zYUU`I*K z0SOy8s>TI>Ubpn25DfAmv^IKX$ch~l1cCr0cX(&TeVZ}8PHAyp8XdM;ZfQ@? z<+9NwOC#W!!{*n^E@6)+zp87;FD5xS+C_IX;JJj^Z@^Iflo>mBTLv}rhcmnAz&!iH z2eF`Wu@j|_|8@BuRNzDY^vuknh{_>{f{GwBAnVXNj;YF)jC%AjKHT4G9dnl>IjxEoG_ z1cVh2@*ZxDUqsx>NX9+Zo0zZh_s|jpfHh+(fOt605BYZflpz{U-yM&nuqv_L&S)1t zc}EA;!^;Qk-QsY1&fjLxJBmTrZoG7^RI=UU`&ioXiQd)uO^M#q@}WxS+m=)3Vy9kFt`@Dar$u4=<5%Gni;cSy{IW-X zMxNoWTkHCkrJy$RsfoFOSAmMYw26Cs%HkXm56}S&2KKp>9}913^v?GwRQ(rWXB`x0 z(END_7CcyR3+@^S9w1n73GObzT^Dx`E{j8OcX#*Tu8X@YwtKu+cU5=4ySl5Is;TLz z>gV}us^`-^{eAsw72DdXL~KsH?1>)7!KC8_-jnlUUTBzaiTFNJ&sgPBdYhdv^>Eeh z1YG#sh4?YMTG?BTMIH(t%=Q6SR?o^wD8#<>-@1rs?mS4wqBvkzE3siKdM4TG$Dc1G zv%kmNH7&=9rGgZP4gOe@ASzXD4QdKRH$7{3jGLANf}1B^g%scD8@Y%^4-LpkXb*I57lep!`dq@$RdRM9afNSug(i>h;;IJR>@k(K6;$Ch zl|HmqJP)I@)j3GzOp#!3#EjW5D8$q*YevH20Bo>myF&-}Fi>Vq%V3p3yPyhgCp;_j zCvAbB6>tHOF`9YQ?Gj7)fw%0->a7LAK7&wfLg2_L@(^KU_v#empgMYzkia z;2snbz1{hdjJ1tKv|Z-WifCN&&h4|OVG=|e{Pvy91xu7$q0NP^#TrJ?#Nos9uc-Dg z7$;;(S;LpvO4?!VImq>hN;`88#U@T+{5HicI;uE1Y2#$PpUVdep#8PG&9k#bR`H_* zNXvfe-N5^mMM~jJA6godh`$-vjMVf@RO}QsfhH8sm;NvNMWpS?o2bRXc8P(En=miK zPVyrcwjnoWT?dX|fQI(j`M4V}LuHPtacZWIKf6*9vkRX8<^;ysxvuTgw@%CE)Xw2( z-)Fvnz<)OQIq_aG+#LXimKOaJQi-@JG~v1h4bJazi=Ai!zc{^3Cbrx!FNV-w4^^<0 z`qn(^3Or=`qb#j2xw_eh3nD}K#poIy|FvE6qF7lnizTw^-*}BDwtjdqyd0E_vByA5 zO}85wcU#B2!D^;oSm3^Fv8c(mCh^TIbyMohIUy;-ROw7QQA5FPasO2CEK}C3>JhCZ z6d}g7xnNsDWU+eOK&czX_KO}g)EOAGHL#bg_M$MfmpKDqB$Im0xwRGC6m?}=SROgN z8{9iqvoG;NTu`rDP?_J>qwmk?dkQqFT)0_tXTu5}Pn`o&JMb_#=7 zY9}dN*uy}mQvUD`*%w@FjCvpQO_sb1&*KQ``N~X+Pug*{zDB}iS$qHmdyqXS=24v%0YKO?&+51{CPU7Y)_$iWWSnYrVA0T>_aT3 z7)&2iK2n)|c;OI6^6ehLDssu2l*6{p!oKR~0t|~ouX(ZXg593WpCi*ig?KuHMsgQM z>MWCB=XNqsY9gO~cElv5&)009f0dUxZ0YO|lrts5>%>GyIs@kce$>uuBLK%~^LUX< ze81<{yQkyZc~HqTGz!y++cyTYkN;vXcS`wG;2+VWodeHsr=pK?%uC^NYOyMa_svnn ziTq47`LiFOZEoPw8!|g_$xWr$PwTkLqRheU5NPVQ@uAMq_MNNtZexW5iP<%dJZoyZrka zF;=d&S$u=ZXrOdll9(vASNb0)I3~#WSa>BM4W0n<-EPg%wW!ZU?@4#432jF@FVG7b z$_7#6VM4;{{;@Atzaf5bzfdd_hJ*e%{?rx6D|3J@2xdXa!V_%R60>ywCr1PfNfkM{ zFt_On*ZG-I-KVIWatbC|*u`zJFF=P@sb6z%aQm!_5mgEIKYve8%0*fnE3sTlRi zPXG1B-J$YJy+XCZ0{7gV#7n^2#iQ0#-x^)~T4~JH13ZZ~Rv3kJ-SFZCOG>tj6bWH% zA8OVf3?dVtyW%`#_Gsi6e7iKnzUDc(T2$CC+5r1@urQKEsW#tiU22A0?!D=Uxo2D{ zKw&nW`31QKiEkSyMQjNyMXFcfJIfKB~RE_v`1HHZTTYXBf*cNAIOBEsQ)|b zu0i$oV15_(_WuS0L-GG)-3C0||H0h?fd9OdxA&t?w@f2sFiw&#cKZNJJ^Ar2N~e}< z6yD&>p8=T&Aq(vfa-jmP)giiMs`Z=+jpNs8AgIl|3yU~Y_u%%xt zM^mbb{~n<4NPR+wrL>=Jz1+Myq^bV_T?RDqZjHBs)}5z>9@m|xJM9mr{uBXeys~3W z275LpDb>II>dj6fbC0ftsSF?YhjO*(e6%M@ZPNv2qVY{hLJD;G1?PN17$=))di{io zPX8R-sZD9;DpevhQ4^Gi8_oPEin%*3aA9e19-WakdXf@DU(_*YYWWO_l?|1<(4k13 zlJ8;{NS%kMJkex62Z{1H({si2Kkc=6r&MmsiLgUfXQ|L9%dq?$Ee78RU}1wg0pW4%P;-Mtu#fKKz@&{pv}r@k^M68c%&*2 zvl|F8G@dLb7kU97x4l5F4N%TnJ=-)Ua-5dv5wGreY&g#`z05Eg%o>f1+q8H&NcYxx zV14ybo|Pi~pebTd2NVbs!x1UN*|4k28vGrv%9TjE81#N9;#ffSh(RHbjxGfF#BhgJ zc%`U~BBYD*T>k^`86R2~2#sWAoWN4047#fS^VHqT@Je0#olhKRZQ2E;GoG~#aSUoQ zX5X&dt5u~ZdMrMVn|1J&DK9Q=dR`6S*%a6EvADF)gSAQS-s^f5)pf8 zv_2bMRDG^Qf#CnNFZSA<|1*r2_eM5%_kNq0mw6l2j#Ke60S0z|e`}%*c_Xu){?urJ zT4a#_AW-_o9Cumt%F>Dg6u(bZda?ZnAy@TFxK>5LXV_3YuPA;1M9n?U`k`8NGe0U6 zJeM=P@g`N3cs0!$2@N71-d9!HfAs7Rtd_a*;PT}hw;+!lZjfv$}ErKvT`eE~2d?~1dQ=j|dbpLptgAUY*#)vb5&APk> z??S#m{0sT8gVIa)qffM|s+SF(u0W3FE8)Ygw5@F}G6^Mo9}Rj!JUgL93sb6MsI8|N zLTu5;Hp;tV>4IDrZ06z8k3!m*P@BMm)9TW5Wdl;z6KZ~p1zcVPpEw6fgTIs{Xz)aI zd*L*_p;EF$Vq&V|4p!pw{y4AS?~ujjO(psDzX{41u{VT%BY)c|2lWLQ_OLlfcQvVI zoUqp@r>RvlY-92S!x31Jl#D4<>Va67N`+sJ@s{9RQDlK!uOZ6m0KyC3B zG#m8y!pCJ+JJ}RKxuDnl!G-^%>Xa2=d?G?6h@$rx0=diT|9j@_fhc@qA6K|@`1-8~ z*%`=V^nM-y#3LA2Lus63^=MbYxVD%1FNof3M&P_^#!rvD_X1+wkyU<|gi5pZYIk;J zio;e@m`T81$1Hsbp31T2b-)^RirA!XTdHp=Vk%X-WpA-2lRLPjQw_2P;TyR##L1>p? zDB^RVK?d@-j7&>v@Nl?^G))<@AGRY}(GR}fII396BnRx*>2Q-HY7}HY0!Os;;FxWs zPs2(wn#f(Kj%W$+Qdb&S!!y!oF<&4$1C^$)@KB8bdx||e@HAG=&mqMt5Ec}hP^33U z(ue4M0{DYtOke%WK|ygvLf+*uduw}v`oceo3;rbIu|zEePz*l|Lnr|Nj zGYdC?iD$l$BHH>4SI!dhOwA|I!NB-N{SKoc6*$;#2`CD#XNJb8`7rV4w7%FWx(oZ{ zUP@hF$*!w$^;S$njkc?Lbx*3r=md-;m`;|fq1l*gh99Drym^zqt)l$DRyvXNRWOau zT_F6HgOcj;%dVm1{y4CAJ7%$c6l-MPWYt-X`bb<4Fg*WKwI(Mhm}$AkiSMlUS$1tQ z@}l#$wTdCd4GydCX{~5aX{$)NZ{2*m4K1Zf73D#L{fC7Wbe^75oIHEB-fF;1dFAQ| zw~LD9WATk2pZS@jc(yLDdGtmj!tbt+^(gKG?n`2X{YFBjkL#Kd=sr?KLbA_07Ih@f zYxRa&rs9B(mNC|A;~C+Bs2tA$qL+`-`?s~xoY&c86Fe```&7fecI^pbI3cDl@d7zP z=~v-&9@nnNAC5Nda5%psPLgaE;K}Nj1Zq%+ zjb_qgQGe{3l%J^!e{QhwHvZ~HoR+01Swcp>EaLwl`wv2iN%1ZG3um}{?`IdRjDQ*` z5p!-fGNgiy-4!6=fQZU1jqX#j8`YehlI2%v)y%)-_QpIJmf|?KLP|{V0#Sx_+G)NM z-BQD^r`VV`q~&K?u`OWMbW;7-dY9{b=5}q?(_n;IBJV{!iU7iuNJ1#OEk1_cy;OCQ z_W}(8zH*$9tO!^V1UgcbJGrbcgq+aL=P1u_l~i<}abDj{IT3U=d|Yqd7>R24blfVJ zjtCxTCcWMaru15;zYv-3rY^i3SNedDcBF;A(LRVXcZ{WF7soptoxD7#!m7?G&-``h zXWgKRY6Z0v^JHZ5@_-7{1d=uqvdD1kp%%wq9Q&X%&904-A2L$)*UiA*rd5R{IG^Es zH0hG67d^wocZz)xnhCkJtwIZnI$~kWE70s56QP`bAH@O(d5=anGWs)NX zDPUHa9V#&K9x6hB zQT(BWNd`aj+pXtBVTwH4w$W)l&Zl<8=hN67p+g%pj@wK10nHeC)K>m>OtwbV3pmWMB*l~N-pgtz!30!_D@M#Y0(DL=tZv+_1 z?(g8fAhC7P0h2$&cDIaU*^7{*la{j&R^emb>yIIz<`1LK!isa-k@#&`J6>YDOJJsH z3v+7!bl-XD9b5R_t$vO#5j0H?q8i*9s4J`n>(lrN_+jfzu$__(+K$o;+rXK;T)VO0+^DMBi_bM78V zt*ehpf5%&~MB}Qmr0LJjZ-4Id?vGZ~vf3YfZ)!eNc=WkM;THhjLd*AHNr6JY&7_2b zKjSo{Q3MgM?J0U}aKJ7fBD?(=##{rdKx>96mMbl%F|gBjUt+;#S9-sLHZ)Jd1s?W* zw?Ml3!zB4O?%*9yY#zF1dfC)42e9JZWS(BbaE?#HFlpggp8${zPQ-0+_^!r!f9i9h zp`$mJySK3=D*s!wq@y9q#svDynM3rgC}RG^@&QyN7iK%@s(x2LHLpl3D*IKOtZB?V!1G}9 zj1b%rS*hB3&_zIULjd^i*o}8H>D(sPbzISIX6CvZ7dBCzae`Zb*SB9rXVTtz3X6HD zn>>(HW>x_ji&?WrI^Qv~_t=EC>oTxck>*hH^AFx-`MRQW8UjN!s!yC7zs;yKFO+9v zvy?4S{SPDcVI%l7`w5~jIu53iey)Y-S!NY&AfwL)WW#69eISl6$HQ2(-Rhm!ig~ZW z36srgXfu}dCgb%F6D#mtwzXeGCY%+f>c0z5|3vlH^3Zn9>j(z$r_vduB$M?(tUXM8 z;RHe$JB%=989Vfw=F4X19ylm&`Fd>0lVUK6598 z9@4N7pPA19AT4U~{ZL%EP|&zTBMFsqjk}0>;2Zsh(@pp*8{~eoJnCKI z7%|{(nm9QXEXqiU?~vCgKm z2%ZG;68=3sYdGg)OCAN(Hqj35$YCV zXOJm+6M!740)B^WM5b)GXI6vtnBc0x$0NYUJmC0)x%4V;_}Z+N6-ed=`w~k8(ME=o zg-*u3uh~6%g$gyguUHnFZyBq(Sj|ufzEJGJxK?n%nvH`8))j( z9$MG+cOMKnSow_`8=kIifmpuOs7bq0nfq`nMK)xEuG)R_euEHw=E*}xoHZ>*=3wf%*+URtF{+y_L45G zT)5B-hTBcRqBf{4_2h*gQ*)Z3)S++2?;8Mz-FaD1H9wTHh;W-ZWLAY(mxi~mH zH?RZVwmi#6)c#>G85GX$evIP^{xSV^x-l=~2x%(8SN!ehx#wT*FxfF2;J0DEchv89 zP>(;sg!Skg+>PTukosR--yCFBtz{wS-^+Ti1P2)hPb z`EzqJ{TtiB_1?OeKx>f7@1~UW`l!h!V`sn7!v>7K&;9D1ts0psgg2HXT^CEt_cEC2 z+>wS(N@mgV4iPo1{naZ{7E15__5Bi#zm=+PK(1@5PZQhUE+mb{J%a3v-A-XGjciiW zyh^|AS9b^^?9CfI7#Q@vX_R885M{2GKA!qpPO1vyJ~bG?24I6KM#{-DX5pojkq()z zm-b#RZ2!Ww*z&CRb~|ggbK8nz>84}^AOB5cUFqt(z`dJg18GgM6$YLOfOAx`d><96 zFJm^)9Gr90&DS$bU3}zL9l1cPfk;}ly%+u`GfX{`=6kT% zc1t@hq<{e`%WodGo`0t1y>Wqg4oPBMoh0mk`aYms7CR7zV5$=2Um>G3D}fbpyX1^8 zL+n++WFZ#xBt>a+2m4YI1c~g;Fi^8+edvDBWt(ziM`V2%#-9^UY<5ZVvw! zj=!4mL6n?xPTNFj8)c-Z{{!U=8gz7_Kd>0l$>uLb&6LjCemSPc(gL^MV9ST@nHk(r z47@8_0M7Bch5;xHgBbosaklbE3kQ)0AnyB)cI3Y0<|84cB{1BknqXj0%Q!IR(e>eG@&SllQk++^Vy>$&RTggK$%KJ>_Cc^p<9p2KK~`D)X>9Ai;r=N zDiD@cKwjw7{w=r52K-D<3>&%f69o6B;`P5s=$R1>+k}3CfiZn2?bQF1wA&h)7}z@- zc(DDiU1=R0Ojxb$tlmxMI`$5#8gAW+UQK4~6yf#vh+!zkJk$i^uY_e4S!z7R$sFvP z6hxuaFVv=l#HLz}z)>ns$vR4o6?Ro5`T*un^YR469*QD zT4|;_+Loz2raXBO1_03J%RZe+P4gP&+qQsUj`JnoJ8=IGb-$(Nc)jF>0y%h4r*Q=b zJ&HMto9ffgw6;+o=d%qXW9V2sWlvc(@QMjOX~oCL;^aFWu70C3%ul8Ma*a~XRP=|1 z0=wzw*_myLgq;@iyEobqMhcyQ#NFI_S5ed^H4ITRN+4&9~A18Wa)s;j?)#K;@$6q`1c5i1W8u6Q^TIpohuQWo2X6O}_njNV<3 z0JX#<8Tn$P!y9Ngeo<&h!Yv1CTuLCscnX1rOQ;qyp}j6s0>U3Ldx((M@Er za!*`oAKC`<*o53=`P^d;RA*Ck`*NPBd{q)|r?C(h$V`o$N2|vRA*|~8PN;7}>2@OA z#dHPDL-?!}TKYlfDLlAS%E1Rye3^TWEUNX(9A^Krs&qgVYrt8Y?62rkGJkCAnXu4W_=WMHk=qg_RAHRceS&9VbI0XPnTH^}?u0!PFKGSbJ4W5) z3L^Nk#k8-vsk^TjnC(q~5YAr0VWck5qN3V?p`vN&j@2oau|gujXVy6X^a1V_z#s?BoEkp0#!#_fikQH6-pvKCnYu z33~?P*H#3Ns~zifaVF+*UPE*E#9wB(6pjAmWYT=dJJ{e#9mUh&a>8ou0Ayhxvmg%G9WW&}5Pu zQ4o*cS}$Ny!#`&j?8&zj1%aK}ggb`=bgPhtGcFj%+Q1Dd8ok=CHLpY8CN;~`N1tLK zkU2{VZ6eYHjon^BoZE+&7mIA<9~RkIH-{kh&B$v#1y9&I&p|!OBt7#LKlAa*j}FhU z!l9nwgr7_M<-d4LA9`{e#@6Za^>{=X~`ik7b zYG9S1I1G?&jdl3QIEOrI;W-2@_@msnLi0N;%L-d~r&%O^d*>A0ioc*|1bW!yeXBQ# z&bbsm<6ps*UuV(o8;O3h~7PAUy4- zxfZbEt4vl0J$UI%#HAkI%4`#$8g^Pis9t}+)O=1a3M8Xg%Ysm72PbFIYU{C<%(WIj z#jg;W&u``3nhh~^Z9f}tNc#*q^gd<)OpD{pg*o+f@)zrrW;+Y%j+I|~dk;7qmEB-5Y( zuA?Z=_A^gGk&Q=1$=)sjC*0$gg60a$Ndxg=+nIZu9;_|T{tD(D0J3u}Pj=4mCIcBp zaN5oCu!bGti)PO{K~)H*`x=js+h>=o)3d?p=xvg$OYe?GfiAxd3jGXmclg&t98YiU zQa+Fh`pWmNFK=+EhC#%wJo`Koe5p1)f7s))>~`F`-4@s}Cz*zqcdeIkPE3Gjm{kq$ zB;^h36n^wXXKFcC(SX-2>qWNT6u%`}SJkm#B{-||nH5;?41fm${5D25p;e+U;x;&d zhlTQ_tu431$1~sk)6)B^J=YtSr^~Q?HqcW!$f7gUTJunWk~f{8{%?I6MBe zL;xc=lb>rn8Hs~QW`vG3eu%Ro@;eW?yA8e|9{+jMIm2|q-7;_#@ebsB0cPKjlJPE6 zk+Id-yL8o_la6w!0ws{pL5hM#>TN)u8qCu(fnl{swhA3Kry7u~K8PYkhVAH%8w`*Eg}fZHZ}5iWjd8SC zv~-%T(R)v3KN2Qg(of?Zw#oyPl1~yKPZeh zD}nKJUU$Hf0NiinXW5yO=Ui;xP~oYdzRPpNaBKJ?qP)J*$dV7e(T+0t`|dyEQg3DT_97azCR0km8@{z=EZ;o5TZ0!T7nE$#D;!I zfWSWk-k^L4(Kd)*PYHk?UDo~WQY*8W!vKIRoP0vHocci*#=~ED202uh_y`SNoF?4; z-+0~=-*?Q>n@jV-UP`IX#!NhfupgZguJ5#>{<5?lN_X;aAO!+PLuudr{28MA|-Zi$&U-4W!x ztScy7S`Y^|F1NPT>lncF?O*R^Eq4b1B_!gn;Ga3 zMMw0pTtyBd?G5Uf#{^ufDTLkdvh&vo)HF?|4usDtXpUk*Uln5Izv7JinSr|>s_T2S zO^|w|j8mPI@KjqTz38rWX0VL)Iq{2mA@H3M>l?nnH&Xqp5JdJS;Z^ub9!j+Z9(35h z=q8SxfQ&i>0CIBOzx(N9Mm~!C!;ng6*b>L{8b(svdBnNm-MLfPKsoAP_KR-B&jR^j zj+TACeQF(Q&;Q9WkFn_l=uRCPdwmloiaP6@=$o2`d*~W`arKUKY(ZETt9t4kFxs`6k?&`VKRP$a5{7XHC;z6^T`qhzf)@e=xtkpcV^m+|ZW+pN%^#TFLKHM%nz1WIyMay{#ds zVMi3{E629QwqTZTv3a$EmEP5!tc1v`v&gcwy8Bb`Yb&4>x;_iX*_&UiP_GI_3of(! zLT(DPB0p^QZwtY;43Bz`M+QE2n7x(#{?K7MkA77QtEezjY*qt+J=BFmixC19&B3YO z@aSp`3|L=Dv7A1mZ!g(r(>;yfo-*hDGuDE7S~rk-LZiWnrrkzbPzgvEk9N~OtMj{% z)&UV%WdGr#Pytn)F4_2V1vYqw^iEFJFR8x@2K-3TplyE{Cz9RP{d=pSgBJ-;hp%K`%UO1_t~akZuAe3V?H+lPY8L~|#jNwf0-xtyjV*&#g`7WtGKO=g+iNV9JN zyXlh}yg)JG&> z#}rqOg~BtFZxKnzB5h%K#dpZ~WrG+#8E|YGX*yooof@OiJ3YM<0!+TEuNbc~NSnt! zu1I$LSE5LeNH{1e5~LVTvBuxPukM+|?QZgRFliWbOU&S-|M}DU(A7nn~|?@Iav)YyW(gl3WSG%G&fxgf!H?=~hF4DZn{a2M=S)8u$v*Y}+R zyzPW?s;PKdz+tSnHlIF$$vbqXDINxNL_=bqIBLVF6nMM7xAO~%w>g?Kl#_`5|6E>icWiymF;yB z(B)2`ZI>VSM9#$+dtRkhO@lUFx{#C^wfxh8o9lgClo%Em0`lP0Y|(t^^uc zG|a^_CdAh_wG90)y5N7jf0D($%T@MB!@$t~|2W2b&EWqa8G=R*xMnHU-n8iv!R+5> zS|S{G0|S518m17XZ_8tM{Nx#t2&XwMj$=Kn=w1 zo;_rwLWd6~)_$#x+TBOk3Y~ow zwoDLszd&q#p`(#)s_Jf>DO2N`G)_Yh586E#V4D86wccX(QY@eBUa4KXI=^^mpx0*X zU4uo3?(+0we#X-&-(t2>WOUTmvg0n5?E=LAUH@XKwj0$jn^ayHK7;()nMhgt(H zA64~hf@Si{)pTM~CzfAclc+nnr3J@Y4JH<|d6TM?)ZdK&r+GY-rI%DTgIX_ywn!to zQJ27C1gZ`7?k${MZOwEFg<_k|6g$;-u4<{JoZ!^$BHR3b4rsG{Q=}A=erR-+@2>KS zHUDZ6xTtX&M%RND&~nN!ugter*gwoeZ|+S3$PDdnLSD+ad361n5qolq6Ufi6TnNmd z*VxLrDkWDWQ8B^s{#TE4`8Smn)v#T+P?&;TpLMjFEj3G=zMsxr_NK~*K^^%M4p-Se z^Lf`u^5H*VtQc-+WDo zZV0`V`oyz@VBC&eC6MS|?GwBKt)}um)^C-H2e3SB?R2UQ2EXFf>~xMhTzr_4zv06I zrR=VU!P~>7FP=yGxw!|0~eat25?=%>?X2=p5w6gt3 z-qQ%^ZM=>Bt8{jUGDsBDLI_fJzbdEp{$X>a|F!3Mi7#JEzl3)@bK9c!OLnwN80joR zfx=~=a?BPs0_fBc;z=dqrTnAL+X$@#!V&&M6qF4z{f4wmGM6vZqk~A15&G>`i*l2f|$ZylZOLHf7KWw^)9oCuj;Skup3Act)Mn9JuS#&<9Bx( zuIF6D4ZYQQ1PL1yw@h*lA^EZ8ysm7xbT!YfxRM1nF{VKqO}-02BVlZ%?Jw{w$xvMU zxDAkMH(cZ0*(#N@W{*2JBDGF7y~#YAD5^D-A(SgQ?pmqcQzG_>*+SiB)W68-RFWloLK8}5y|=bb02FI5-HVU~&-n5Dy2T?H5hq^btpwD#AX^&P*s zTj8b;ibm2LBts($Is*+QA%SSgQU`HLbd`UjaF)9}Ngk7KQ}!e}crVDOU1UuEaqhYV zZLbD3jg4g`aSE6Ad^7mc>8slLT0_HtC@H+);0|!t{qh>tk^ZOsLyp?|-Up++=X06X z4+MIt^PF{vlZtmXXEW_X=;AgV>>ZK;vtPxpAQ{Tv#=+U70^)6Ee7}EB6V6rMGRNfb z<8T-$5PCnznVwPThh2H$Rp;a+piWoC@4(5hlISQS+`5gxRWH?%xR~lDw$Ex{Y^Frr zK91;*e>~)L!2I5XR;&H=IT(rE4gK%*r#Ms${|!)_tRBcshO=WDJXpqs+e&o1^c=zr z7=)lW`gsdpC?*IfaJ`NgO;{Nh8&9|JJ_lZE2ozMgP>ppgn)wS)q zPG+&uKMb9QGn|B$?lK(*y2OVcI77I!kXE1sy(D?ZhIUlXUmb*!5-~pUgw*#0ztqoZ z|K>d;zqDzbD1SciE%n?!>K{XeB#i z5ZoK8$pP#7utieX*o)Bshvjqj_@qxq*@W#H_S1oBX7ertb6;!?mQcNURLlRK;Q37z zM$g7Nyrh$29GA(tU(jA6L?=|Xk#hpd+ZvyJt6M>J=m`r#Mc@hJZ}`=V4*l+x+4GIx z@k50RhEiydwtmMBta@htZ)&QsNBFABK`x5;=nKD*&f?8pO&M2zGNzJBBxM6;%qK0o z&8jt(aS>i0f?H965Q zs0RphVa@H7flieNw??+CBz>)BwNf*onqg8WBFR}MX{gEu1eZT1sC;ES;Y|qVV?^mB zeRX;1X2|Y9=>zq02?fJ~%+P1M($nIfrKdom($ik~L|G0@k+;~ZnHCB`bzyqJAkSRP zx4Pk?@0fj@eZM|di@hKDlONYc?XiaH83glBr;C)(=_`gCiFC_*zw*Z7+`D0B^*yP6 z>rPr277F!hm!fxL5aXya6Y#21wSWA#Eo4nkHlW$L@#nymK}$baWD!hwat=f&U^VNd zuj)KLYW3@s_s;cN+lqVl6!mqQdSOjy^!~wsUc2-B5}+{*2tKjR;#GcMTpn`&F`$eg zenP5sHwO5hn52HO8}ciat<|h;Zcm~?dGMqT>xRNmpFhfsxS+p(g^n_4mttYFVWcCsdLhJ+mU}Qo#A@x25~%{`9bR*Y;fYC`YS=harcS)R+eQkJQLk{ z>$=r77Pb$6Tyx3;eQz&o$I?QT#pP>^tcF_PztpBatvwfxYX-G-PQ7 zH}p{OnPIU{J*uW(-C)DINfDn{ljJJ>3aO`<0cFKNdGL5=l7p{2O`9A~ejW%!{YDO- zaAFYJK8WaHKy_iP%bO>RTElCSbLtUv$80)$OTPMj8esVc%`R-~59tWmZ*)i6u>foH zXqw1h<>{vbj3eN9Pyny1AFexrqPjs?Gu6~IpF++af_nLyDv%~|*W~0jc+X#b%e&<7 z`Egru6X>y67pS{s!JNrRPVLl@egduciY@Uv@@kB)O#zmX<*%*~P#ycuxHR4lq~G+tzy36YMz(%kns7p!aU;Q zaCMAcMmFePlPpfI3KWEfr-)BF-}?mEgk~8%XRQ3OrZ)GkE3rberB3!#inWxK8+$u) zJ|5cgVP$z91kFue`c~oQo_#rdp^u^r;|Tr7UnALjE&N$A-(wLmkKCJUHF?(-kSHRx z@{;RycHROJtg@HYZ^qs-KJP@05^$6HCnww1I>j?c{qfU#Go-7pX3>T{F>A6%m{@;& zF1`^e#?+TJ+Vm4;2;&)jBUUe0os@KqReM9FVwSmhjILq+1uzZ9Yh#<=cyB2X+j=os z+9ufNKiD3@M96b8O3GVsu zLT>YiXDUJEb}aZ(!CM+;@YHf0I=OVpZlh+w^F|F14O*=Z+a%#<#6iDX|Kp+5fP(^IF7Zs$9u#ehjPtA+;C*6nU; zh-o8N(Y`Krv~m18t)FDo(Q}PstCX_c&6VlsAMGFa(W`gBmsW@n1ez?oF~{e#J@0AD%9F3<9M3 zpTpjpv>97Ot{Jg^`t=FJ3u`$3^7V)RFH z2{1C8+=<6m*sVZ*d=c3m34NG93;y;cP^@@=d#NY+IO^?7EQoN>>tDtB?;pBmW3Hq> zbRGK!D-vJL78h@!P@DyLaJ^}uyt<;iR>IH*NlX%o%}uXOJ~5ois&11P_Lwm^n4!G( z<9e8&yjr51DFzU$Zf7$%P$T&9BluAgns?`vZ0*i_pXpq+;WfB#NqB68Ep>1_tOFPttTfpYF)zQaip~=chc~aYLH@|&74p7k}G>1ylrmZ zD(?fdW-IK`5XCb4x*Vh|)xoPD&b@=GY1mOS7Ms31xLI5OH^R;`rjjVi);NuKV6 zE{(gpTjTEDxO?N)xHsgvVYV`Dz#VE{?@q_fLAuQ zxk!jve>w&i9_pO#djKXaB6R=x8Rsw!uh$7)x5%` zsQgGDMy)vfvDN-(IuHyI5V`qGtGK_Ap_sW7G$CBbgrPZMLAyhAvi^Zp10P{WakHGb z`Geie@1?1ALVr;*OtfY}ME`vVwsuCMYU<0HqbB*kS#$n_`S_oLmY$!Inr2$h7Jt6} z+{!IkVX!bnqyl2_Ic15-uSj6ba6E}jB@Wv{^K3QtNy*7G;&fDyYs+8+8cCT3Of4Ut_>bicF z+Z|uI(bNAC9{rUdJ-?-g{o%@<%#s#H1*wRcYe_yVdckM`-igUn+93LsZJ8wdp(PJG z^(2Qpbgj68af@-~512t8PRS@t`%^AeWgSyGfBab4lA4$|v1(GJV2Oq`dQjqY1tp$qdIyZ%S&lk*Kofu6Z8qWsKPyR`d!@#89IF8JJ5< z#e5*qe7rJPI9qDTHLN0!Y7HS`Pj4&LQ&ISpKuRo|zmFUdP||Is_tx~aPam+-hqWnp zZnydy%zF&oRxoSjn2Tj9{(i0TPpimT8=tE8pbi`aE`~n7Wje>*68`O9vb4zWXU)|? zLB6klS?Mn72sCO!tBu3EeB4OPq2+3+Qv#PQF^1O-zVI~$^txNs)qq$r$_EJPla^RMgN`X`M=8oi; z(Qb6goucz7LjF2#&_$^Ug!-RFn~ zG6VP7h+}EW3t7(F%+8WdszO_ZuQhqUk!{SmTtXK)WRBm>VQI^ba*-h@i%hKkNeeo3 z=XqfdluA3n<9OCS+%a9`(i(m1=6se<4uS5pvpCI*)6Du4R#Ze#ajMC(abyg!^AMXj&#L(M46i7aD zR2MZ5mo=tmc_0xVRC)mXn^4hXBuF~h*TkNiFG0J-m$HZXc<->XkOSv@%c_R6BFlJB z;{_Pma|mXd_cs%ZSUQ8sP#zd8y#*)-v$p*~!Ci9b8QH-``L-FqqvBAbb{GfloZeLX z2QG=Qrk_^7LuEuq8ga*@+Y6b^r8ulncCPxm^ungLYQ%XW+s>h`qrjGD zRK6q7DXXdcr8BH^MUzAqZ9pt&c7izhr@j6CtM9u0!l=;42|b2{Iq>fz+hvmsUemO~%Ai9bRFj zyDBgQ_eMa0xL7d8g>#!pWY#k+#)QR9@e)ripAaTS4zK1;);?lMMKK8*3m8QM5>YHU zo~>dcmE6K5G>uA;d_P&i$}ymG=p@8Vil61?imUJi^Txt9jdGzLv&|V!%9fa!6ASYj!OtXgR=Aad=P}F00 zVq+^TwNB=_v52)>FOOB#{;nAF;l+qz%%ta|J%}~G*Hwa$Bp5C}+NXY?=s*!`sUF~6 zz0?VSfSmaKia2n(DV+G&W_omv!1V-o|40Hyj8XvA3mc^ZC2}d^n*4Z#K1UD1#o#gn z_=8yn|=u#*p{gFwvIF3EF=ddXS6#zh9_dt<8$E7hA7QZ1nj#$qa zIHVeWWgIRzHU7nMvS+DO)?h5)-m^`%+_2w*=dtDh3K#S|v&i*7Riigm_MoP0nKvlA ziWAP0utWns2u-*|{=NTJC?oU|w+(4*Hw=P=M9}rL68-%zBgEcJ&~BcZdSrPRd>%aLu(e(j+uvT0A_dYAD1l#i$vi9+wzkjEj;9JukDcCj(TOOaN7C4IU8!J zE&q9BOmFAz#IWF*8loLt{)zEn`f;x3F!oHB4FBZGl_P7}<=QruEaz7H;&e;5xcGzQ zxX^ZIo~i?UcRf+lTjP=NGvqc7s6OHnM3h1PIn@IV9(kkMZ}f-WvIolmJgoz--^H#D zj7^DW%6O^xyqP~`gWpkGGv=xvlog6F$E%Al`^`|pSTk{gvN(eyxm^kTa+^JEX>z%Y zueLoio`B6>{Nz>e>`%jsBSwPUctcqcZn=&-)tEZkaV184+?x5H^GT!0s2}sISji#J zKi)NL^AgJ=9Cdf8U-MBIz}rtz41bUoo%Zh@#nZTR`21UC{wEU8t}|Hobh5-2mVz3c z88iM!-#PjwF0>de^@3_j_cTEOXgw;QRQUSUOPd(jhapxMz!{IoeJVdaHH=~Gm8 z?u|pjUJ`}-W6Z=>Wt@l_{^zu1a>Tpy{Ci1?VIs15RwrXQQJQY_%flu`RHClkyMEEF z+>+^w7;iOqweYN<%v(vkp~zq5^>On9Em!bBGJ-vJhkc;)jkM7W+yzhEJ9M3M<;EAm zy$0f>KnA({H{s3+FLAipoLH>CQ2jyQ!J2H4&iD{_+aIPjJhNI@9r8DuAQ*_X4`>$Z z;2jz5Gf^w8Q(^TTO*tS~Yo-hN7bEBqPo`R$NJ*%^~!{hC0& z9R<2%Ba1O5;jqfAjdQzM^$oa^f^6189dg^$tYK7~@WKN_*5=Y0ex4Up7Qn|1d0a&< z5RgJA@{G8-X=F&mqG!!E2BfS02vTZ{8}V zs^CNJ8lgMJ^NBAHdr0Z^NCcnD#U?Tx)wlaYYPW6zZWkli(3Cpo?7Gk4dOs+G10$xO zWAHek_=(WkzSHjrLl|>T_0aX3xBky(SU)5FAr?q>(PhWX3WmIEpbvkNl2zs1BsD~K z7KHnjn^JDI6!P26`GhGPPE@9L7kDs80Uii@6AM)3TVpae6-n%Y1Xl z>lJ*JDHaNcXKtMnyIEBWny6F?*#s-2LmCra3-`$RmLq3`Jy$#?xY#DZb%zxL;;hVO zWYUp!G2z{x=D`#grp0SdoAXnB0PNC*ij~OH09mv=v^h)eoV+0ufGOIX!fqp~-w8BK zp^)&M6R99SiQi9Dfm3G)#n+w7gZNX?#`N1x9%-Gk=?{L)w^wp2+1Yngs}0J=aE8by z99I;B_@OjFpL=K^a*?j6jHi((0l7U?(pp@NrSS6-forkb+qW$W!(kH%v^mxSveyZ= z{MR+|qi*a~!r80jD>+$9E0X!#t>uksJK~(ONX~B;gxT;t9U;h(Cf5Qc0|P1)>i^@S zq@MfSx(g8KQj<>o|JNrGMP5LF#6P{<43-?}>q(sizH8lk3(KQ}?1W~_jSBd*6byQv zI&GgSqfcPr$l-+@-&J`>PcNsC6`amdtd8^X^lPg5ZpAA=vBR3?&$Gw8G_V=mZ_X&_ zHsn+rMy}^EU^eirBqGm1u_X#{T?;K|h=HFy{09)Y(f5)Go-U6C5F%;yfHBp^gv=XQ zg@+V`0rEmx1Pre#B6?Dpb=&m1^9@JrTe5NjevSSHsl(_&v~CB+Zw;+7A-4K*n)XBv zlEn9{wx(^?zUx0b9z3?Y zfYsJs<6D;pL9`DSH2z<~rLv|s5NLX4!9?tR@mV|0Xcz2#Zj_!qaLAcEWR!nCx**<5 z{0Px{5@(F}TF$)s-?hMVeQq`<20c8YexIjog|EEJ`F@)J$aUE93cTmeY4XVir5{WS z*lVVCQdHq2=r=AzGp{i@yQk#H+tCqF{;o)fv>KB#b0 z3?9)ueo~m0bJKahlGbS?M3}JivQ+8W&}~9}NM)i5c1UHEbx(nb^NrYCFIKm6|8YuL-Aox|aH*nC zSE=3u*M)Dz*ke$sI)rIOU0YI9k$LB*=A+-#=^aN)_06wQB&P!pH+!|xKW{}{wff5A ziq3di28B}pveu=eGA4{^J6~V>WVO-$Z9XYU$WlSEShJd?snRc%7QMo4B{-6&i?Mdb zaNm6;Lu%AmUf!8Qb>X_kNwb3Tx6Wz#9LVQeFS>q(j$*zLva@VlV#6oYf0sjk4MA-d ze>r^qg9-LUGy(e>L~>#JA19T*v^lXl4tJb$wmWNe`EE zXr`g~maH!LnqV_>o-%hljN^Xs1I`%yF8ACDNol-9TI2GJ#aJUEROvIWCW+*007}KW z!?Vh|vzy&(`5nw60|YG2!X$LPg~q}*W%#eY?`aH}+a?np%j4OsmC+6Nrj*z?4p`lD z=!)=Lu5|Mp{nF*q8IuldkfF>W{R_ImOB}kvXFk8Yp>0hhb$6)1sM@ @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/dist/maple_loader.jar b/tools/linux64/src/maple_loader/dist/maple_loader.jar new file mode 100644 index 0000000000000000000000000000000000000000..e1f9965c1ff0fe14384717ea4559c7f59e9a1ca6 GIT binary patch literal 32791 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 VEwG5^!#_X%TL6{zhrDw%{eMvMy}$qf literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/jars/jssc.jar b/tools/linux64/src/maple_loader/jars/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 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 @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/linux64/src/maple_loader/manifest.mf b/tools/linux64/src/maple_loader/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/tools/linux64/src/maple_loader/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/linux64/src/maple_loader/nbproject/build-impl.xml b/tools/linux64/src/maple_loader/nbproject/build-impl.xml new file mode 100644 index 0000000..a66f349 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/build-impl.xml @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/linux64/src/maple_loader/nbproject/genfiles.properties b/tools/linux64/src/maple_loader/nbproject/genfiles.properties new file mode 100644 index 0000000..c136721 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=2e6a03ba +build.xml.script.CRC32=4676ee6b +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=2e6a03ba +nbproject/build-impl.xml.script.CRC32=392b3f79 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/linux64/src/maple_loader/nbproject/private/config.properties b/tools/linux64/src/maple_loader/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/tools/linux64/src/maple_loader/nbproject/private/private.properties b/tools/linux64/src/maple_loader/nbproject/private/private.properties new file mode 100644 index 0000000..e5c9f10 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\rclark\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties diff --git a/tools/linux64/src/maple_loader/nbproject/private/private.xml b/tools/linux64/src/maple_loader/nbproject/private/private.xml new file mode 100644 index 0000000..a1bbd60 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/private/private.xml @@ -0,0 +1,10 @@ + + + + + + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/CliMain.java + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/DFUUploader.java + + + diff --git a/tools/linux64/src/maple_loader/nbproject/project.properties b/tools/linux64/src/maple_loader/nbproject/project.properties new file mode 100644 index 0000000..7f48d71 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/project.properties @@ -0,0 +1,79 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=maple_loader +application.vendor=bob +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/maple_loader.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jssc.jar=dist/lib/jssc.jar +file.reference.jssc.jar-1=jars/jssc.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.jssc.jar}:\ + ${file.reference.jssc.jar-1} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=CliTemplate.CliMain +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/linux64/src/maple_loader/nbproject/project.xml b/tools/linux64/src/maple_loader/nbproject/project.xml new file mode 100644 index 0000000..92218a9 --- /dev/null +++ b/tools/linux64/src/maple_loader/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + maple_loader + + + + + + + + + diff --git a/tools/linux64/src/maple_loader/src/CliTemplate/CliMain.java b/tools/linux64/src/maple_loader/src/CliTemplate/CliMain.java new file mode 100644 index 0000000..c7dc9f0 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/CliTemplate/CliMain.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import processing.app.Preferences; + +//import processing.app.I18n; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class CliMain { + + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + + String comPort = args[0]; // + String altIf = args[1]; // + String usbID = args[2]; // "1EAF:0003"; + String binFile = args[3]; // bin file + + System.out.println("maple_loader v0.1"); + + Preferences.set ("serial.port",comPort); + Preferences.set ("serial.parity","N"); + Preferences.setInteger ("serial.databits", 8); + Preferences.setInteger ("serial.debug_rate",9600); + Preferences.setInteger ("serial.stopbits",1); + + Preferences.setInteger ("programDelay",1200); + + Preferences.set ("upload.usbID", usbID); + Preferences.set ("upload.altID", altIf); + Preferences.setBoolean ("upload.auto_reset", true); + Preferences.setBoolean ("upload.verbose", false); + + // + DFUUploader dfuUploader = new DFUUploader(); + try { + //dfuUploader.uploadViaDFU(binFile); + dfuUploader.uploadViaDFU(binFile); + } catch (Exception e) + { + System.err.print (MessageFormat.format("an error occurred! {0}\n", e.getMessage())); + } + } +} diff --git a/tools/linux64/src/maple_loader/src/CliTemplate/DFUUploader.java b/tools/linux64/src/maple_loader/src/CliTemplate/DFUUploader.java new file mode 100644 index 0000000..3dee0b4 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/CliTemplate/DFUUploader.java @@ -0,0 +1,345 @@ +/* + DFUUploader - uploader implementation using DFU + + Copyright (c) 2010 + Andrew Meyer + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package CliTemplate; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import processing.app.Preferences; +import processing.app.Serial; +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; + +/** + * + * @author bob + */ +public class DFUUploader implements MessageConsumer { + + boolean firstErrorFound; + boolean secondErrorFound; + // part of the PdeMessageConsumer interface + boolean notFoundError; + boolean verbose; + RunnerException exception; + + static final String SUPER_BADNESS = + "Compiler error!"; + + public boolean uploadUsingPreferences(String binPath, boolean verbose) + throws RunnerException { + + this.verbose = verbose; + + return uploadViaDFU(binPath); + } + + // works with old and new versions of dfu-util + private boolean found_device (String dfuData, String usbID) + { + return dfuData.contains(("Found DFU: [0x"+usbID.substring(0,4)).toUpperCase()) || + dfuData.contains(("Found DFU: ["+usbID.substring(0,4)).toUpperCase()); + } + + public boolean uploadViaDFU (String binPath) + throws RunnerException { + + this.verbose = Preferences.getBoolean ("upload.verbose"); + + /* todo, check for size overruns! */ + String fileType="bin"; + + if (fileType.equals("bin")) { + String usbID = Preferences.get("upload.usbID"); + if (usbID == null) { + /* fall back on default */ + /* this isnt great because is default Avrdude or dfu-util? */ + usbID = Preferences.get("upload.usbID"); + } + + /* if auto-reset, then emit the reset pulse on dtr/rts */ + if (Preferences.get("upload.auto_reset") != null) { + if (Preferences.get("upload.auto_reset").toLowerCase().equals("true")) { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + } else { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + + String dfuList = new String(); + List commandCheck = new ArrayList(); + commandCheck.add("dfu-util"); + commandCheck.add("-l"); + long startChecking = System.currentTimeMillis(); + System.out.println("Searching for DFU device [" + usbID + "]..."); + do { + try { + Thread.sleep(100); + } catch (InterruptedException e) {} + dfuList = executeCheckCommand(commandCheck); + //System.out.println(dfuList); + } + while ( !found_device (dfuList.toUpperCase(), usbID) && (System.currentTimeMillis() - startChecking < 7000)); + + if ( !found_device (dfuList.toUpperCase(), usbID) ) + { + System.out.println(dfuList); + System.err.println("Couldn't find the DFU device: [" + usbID + "]"); + return false; + } + System.out.println("Found it!"); + + /* todo, add handle to let user choose altIf at upload time! */ + String altIf = Preferences.get("upload.altID"); + + List commandDownloader = new ArrayList(); + commandDownloader.add("dfu-util"); + commandDownloader.add("-a "+altIf); + commandDownloader.add("-R"); + commandDownloader.add("-d "+usbID); + commandDownloader.add("-D"+ binPath); //"./thisbin.bin"); + + return executeUploadCommand(commandDownloader); + } + + System.err.println("Only .bin files are supported at this time"); + return false; + } + + /* we need to ensure both RTS and DTR are low to start, + then pulse DTR on its own. This is the reset signal + maple responds to + */ + private void emitResetPulse() throws RunnerException { + + /* wait a while for the device to reboot */ + int programDelay = Preferences.getInteger("programDelay"); + + try { + Serial serialPort = new Serial(); + + // try to toggle DTR/RTS (old scheme) + serialPort.setRTS(false); + serialPort.setDTR(false); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + + // try magic number + serialPort.setRTS(true); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.write("1EAF"); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.dispose(); + + } catch(Exception e) { + System.err.println("Reset via USB Serial Failed! Did you select the right serial port?\nAssuming the board is in perpetual bootloader mode and continuing to attempt dfu programming...\n"); + } + } + + protected String executeCheckCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + String returnStr = new String(); + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(process.getInputStream())); + BufferedReader stdError = new BufferedReader(new + InputStreamReader(process.getErrorStream())); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean busy = true; + while (busy) { + try { + result = process.waitFor(); + busy = false; + } catch (InterruptedException intExc) { + } + } + + String s; + while ((s = stdInput.readLine()) != null) { + returnStr += s + "\n"; + } + + process.destroy(); + + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) return "Error!"; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + } + + return returnStr; // ? true : false; + + } + + // Need to overload this from Uploader to use the system-wide dfu-util + protected boolean executeUploadCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + new MessageSiphon(process.getInputStream(), this); + new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean compiling = true; + while (compiling) { + try { + result = process.waitFor(); + compiling = false; + } catch (InterruptedException intExc) { + } + } + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) + return false; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + //editor.error(exception); + //PdeBase.openURL(BUGS_URL); + //throw new PdeException(SUPER_BADNESS); + } + + return (result == 0); // ? true : false; + + } + + // deal with messages from dfu-util... + public void message(String s) { + + if(s.indexOf("dfu-util - (C) ") != -1) { return; } + if(s.indexOf("This program is Free Software and has ABSOLUTELY NO WARRANTY") != -1) { return; } + + if(s.indexOf("No DFU capable USB device found") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: No Maple found"); + return; + } + + if(s.indexOf("Operation not perimitted") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: Insufficient privilages"); + return; + } + + // else just print everything... + System.out.print(s); + } + +} diff --git a/tools/linux64/src/maple_loader/src/CliTemplate/ExecCommand.java b/tools/linux64/src/maple_loader/src/CliTemplate/ExecCommand.java new file mode 100644 index 0000000..3d6c106 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/CliTemplate/ExecCommand.java @@ -0,0 +1,119 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class ExecCommand implements MessageConsumer { + + private boolean verbose = true; + private boolean firstErrorFound; + private boolean secondErrorFound; + private RunnerException exception; + + /** + * Either succeeds or throws a RunnerException fit for public consumption. + * + * @param command + * @throws RunnerException + */ + public void execAsynchronously(String[] command) throws RunnerException { + + // eliminate any empty array entries + List stringList = new ArrayList<>(); + for (String string : command) { + string = string.trim(); + if (string.length() != 0) + stringList.add(string); + } + command = stringList.toArray(new String[stringList.size()]); + if (command.length == 0) + return; + int result = 0; + + if (verbose) { + for (String c : command) + System.out.print(c + " "); + System.out.println(); + } + + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + + Process process; + try { + process = ProcessUtils.exec(command); + } catch (IOException e) { + RunnerException re = new RunnerException(e.getMessage()); + re.hideStackTrace(); + throw re; + } + + MessageSiphon in = new MessageSiphon(process.getInputStream(), this); + MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + boolean compiling = true; + while (compiling) { + try { + in.join(); + err.join(); + result = process.waitFor(); + //System.out.println("result is " + result); + compiling = false; + } catch (InterruptedException ignored) { } + } + + // an error was queued up by message(), barf this back to compile(), + // which will barf it back to Editor. if you're having trouble + // discerning the imagery, consider how cows regurgitate their food + // to digest it, and the fact that they have five stomaches. + // + //System.out.println("throwing up " + exception); + if (exception != null) + throw exception; + + if (result > 1) { + // a failure in the tool (e.g. unable to locate a sub-executable) + System.err.println(MessageFormat.format("{0} returned {1}", command[0], result)); + } + + if (result != 0) { + RunnerException re = new RunnerException(MessageFormat.format("exit code: {0}", result)); + re.hideStackTrace(); + throw re; + } + } + + /** + * Part of the MessageConsumer interface, this is called + * whenever a piece (usually a line) of error message is spewed + * out from the compiler. The errors are parsed for their contents + * and line number, which is then reported back to Editor. + * @param s + */ + @Override + public void message(String s) { + int i; + + + System.err.print(s); + } + +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/Base.java b/tools/linux64/src/maple_loader/src/processing/app/Base.java new file mode 100644 index 0000000..c3a174d --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/Base.java @@ -0,0 +1,53 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-10 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + + +/** + * The base class for the main processing application. + * Primary role of this class is for platform identification and + * general interaction with the system (launching URLs, loading + * files and images, etc) that comes from that. + */ +public class Base { + + /** + * returns true if running on windows. + */ + static public boolean isWindows() { + //return PApplet.platform == PConstants.WINDOWS; + return System.getProperty("os.name").indexOf("Windows") != -1; + } + + + /** + * true if running on linux. + */ + static public boolean isLinux() { + //return PApplet.platform == PConstants.LINUX; + return System.getProperty("os.name").indexOf("Linux") != -1; + } + + + +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/Preferences.java b/tools/linux64/src/maple_loader/src/processing/app/Preferences.java new file mode 100644 index 0000000..6368e38 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/Preferences.java @@ -0,0 +1,157 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-09 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import java.io.*; +import java.util.*; + + +/** + * Storage class for user preferences and environment settings. + *

+ * This class no longer uses the Properties class, since + * properties files are iso8859-1, which is highly likely to + * be a problem when trying to save sketch folders and locations. + *

+ * The GUI portion in here is really ugly, as it uses exact layout. This was + * done in frustration one evening (and pre-Swing), but that's long since past, + * and it should all be moved to a proper swing layout like BoxLayout. + *

+ * This is very poorly put together, that the preferences panel and the actual + * preferences i/o is part of the same code. But there hasn't yet been a + * compelling reason to bother with the separation aside from concern about + * being lectured by strangers who feel that it doesn't look like what they + * learned in CS class. + *

+ * Would also be possible to change this to use the Java Preferences API. + * Some useful articles + * here and + * here. + * However, haven't implemented this yet for lack of time, but more + * importantly, because it would entail writing to the registry (on Windows), + * or an obscure file location (on Mac OS X) and make it far more difficult to + * find the preferences to tweak them by hand (no! stay out of regedit!) + * or to reset the preferences by simply deleting the preferences.txt file. + */ +public class Preferences { + + // what to call the feller + + static final String PREFS_FILE = "preferences.txt"; + + + // prompt text stuff + + static final String PROMPT_YES = "Yes"; + static final String PROMPT_NO = "No"; + static final String PROMPT_CANCEL = "Cancel"; + static final String PROMPT_OK = "OK"; + static final String PROMPT_BROWSE = "Browse"; + + /** + * Standardized width for buttons. Mac OS X 10.3 wants 70 as its default, + * Windows XP needs 66, and my Ubuntu machine needs 80+, so 80 seems proper. + */ + static public int BUTTON_WIDTH = 80; + + /** + * Standardized button height. Mac OS X 10.3 (Java 1.4) wants 29, + * presumably because it now includes the blue border, where it didn't + * in Java 1.3. Windows XP only wants 23 (not sure what default Linux + * would be). Because of the disparity, on Mac OS X, it will be set + * inside a static block. + */ + static public int BUTTON_HEIGHT = 24; + + // value for the size bars, buttons, etc + + static final int GRID_SIZE = 33; + + + // indents and spacing standards. these probably need to be modified + // per platform as well, since macosx is so huge, windows is smaller, + // and linux is all over the map + + static final int GUI_BIG = 13; + static final int GUI_BETWEEN = 10; + static final int GUI_SMALL = 6; + + + + // data model + + static Hashtable table = new Hashtable();; + static File preferencesFile; + + + static protected void init(String commandLinePrefs) { + + + } + + + public Preferences() { + + } + + // ................................................................. + + // ................................................................. + + // ................................................................. + + // ................................................................. + + + + static public String get(String attribute) { + return (String) table.get(attribute); + } + + static public void set(String attribute, String value) { + table.put(attribute, value); + } + + + static public boolean getBoolean(String attribute) { + String value = get(attribute); + return (new Boolean(value)).booleanValue(); + } + + + static public void setBoolean(String attribute, boolean value) { + set(attribute, value ? "true" : "false"); + } + + + static public int getInteger(String attribute) { + return Integer.parseInt(get(attribute)); + } + + + static public void setInteger(String key, int value) { + set(key, String.valueOf(value)); + } + +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/Serial.java b/tools/linux64/src/maple_loader/src/processing/app/Serial.java new file mode 100644 index 0000000..04566a7 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/Serial.java @@ -0,0 +1,527 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + PSerial - class for serial port goodness + Part of the Processing project - http://processing.org + + Copyright (c) 2004 Ben Fry & Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.app; +//import processing.core.*; + + +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; +import jssc.SerialPortList; +import processing.app.debug.MessageConsumer; + + +public class Serial implements SerialPortEventListener { + + //PApplet parent; + + // properties can be passed in for default values + // otherwise defaults to 9600 N81 + + // these could be made static, which might be a solution + // for the classloading problem.. because if code ran again, + // the static class would have an object that could be closed + + SerialPort port; + + int rate; + int parity; + int databits; + int stopbits; + boolean monitor = false; + + // read buffer and streams + + InputStream input; + OutputStream output; + + byte buffer[] = new byte[32768]; + int bufferIndex; + int bufferLast; + + MessageConsumer consumer; + + public Serial(boolean monitor) throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + this.monitor = monitor; + } + + public Serial() throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(int irate) throws SerialException { + this(Preferences.get("serial.port"), irate, + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate) throws SerialException { + this(iname, irate, Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname) throws SerialException { + this(iname, Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate, + char iparity, int idatabits, float istopbits) + throws SerialException { + //if (port != null) port.close(); + //this.parent = parent; + //parent.attach(this); + + this.rate = irate; + + parity = SerialPort.PARITY_NONE; + if (iparity == 'E') parity = SerialPort.PARITY_EVEN; + if (iparity == 'O') parity = SerialPort.PARITY_ODD; + + this.databits = idatabits; + + stopbits = SerialPort.STOPBITS_1; + if (istopbits == 1.5f) stopbits = SerialPort.STOPBITS_1_5; + if (istopbits == 2) stopbits = SerialPort.STOPBITS_2; + + try { + port = new SerialPort(iname); + port.openPort(); + port.setParams(rate, databits, stopbits, parity, true, true); + port.addEventListener(this); + } catch (Exception e) { + throw new SerialException(MessageFormat.format("Error opening serial port ''{0}''.", iname), e); + } + + if (port == null) { + throw new SerialException("Serial port '" + iname + "' not found. Did you select the right one from the Tools > Serial Port menu?"); + } + } + + + public void setup() { + //parent.registerCall(this, DISPOSE); + } + + public void dispose() throws IOException { + if (port != null) { + try { + if (port.isOpened()) { + port.closePort(); // close the port + } + } catch (SerialPortException e) { + throw new IOException(e); + } finally { + port = null; + } + } + } + + public void addListener(MessageConsumer consumer) { + this.consumer = consumer; + } + + public synchronized void serialEvent(SerialPortEvent serialEvent) { + if (serialEvent.isRXCHAR()) { + try { + byte[] buf = port.readBytes(serialEvent.getEventValue()); + if (buf.length > 0) { + if (bufferLast == buffer.length) { + byte temp[] = new byte[bufferLast << 1]; + System.arraycopy(buffer, 0, temp, 0, bufferLast); + buffer = temp; + } + if (monitor) { + System.out.print(new String(buf)); + } + if (this.consumer != null) { + this.consumer.message(new String(buf)); + } + } + } catch (SerialPortException e) { + errorMessage("serialEvent", e); + } + } + } + + + /** + * Returns the number of bytes that have been read from serial + * and are waiting to be dealt with by the user. + */ + public synchronized int available() { + return (bufferLast - bufferIndex); + } + + + /** + * Ignore all the bytes read so far and empty the buffer. + */ + public synchronized void clear() { + bufferLast = 0; + bufferIndex = 0; + } + + + /** + * Returns a number between 0 and 255 for the next byte that's + * waiting in the buffer. + * Returns -1 if there was no byte (although the user should + * first check available() to see if things are ready to avoid this) + */ + public synchronized int read() { + if (bufferIndex == bufferLast) return -1; + + int outgoing = buffer[bufferIndex++] & 0xff; + if (bufferIndex == bufferLast) { // rewind + bufferIndex = 0; + bufferLast = 0; + } + return outgoing; + } + + + /** + * Returns the next byte in the buffer as a char. + * Returns -1, or 0xffff, if nothing is there. + */ + public synchronized char readChar() { + if (bufferIndex == bufferLast) return (char)(-1); + return (char) read(); + } + + + /** + * Return a byte array of anything that's in the serial buffer. + * Not particularly memory/speed efficient, because it creates + * a byte array on each read, but it's easier to use than + * readBytes(byte b[]) (see below). + */ + public synchronized byte[] readBytes() { + if (bufferIndex == bufferLast) return null; + + int length = bufferLast - bufferIndex; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Grab whatever is in the serial buffer, and stuff it into a + * byte buffer passed in by the user. This is more memory/time + * efficient than readBytes() returning a byte[] array. + *

+ * Returns an int for how many bytes were read. If more bytes + * are available than can fit into the byte array, only those + * that will fit are read. + */ + public synchronized int readBytes(byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + + int length = bufferLast - bufferIndex; + if (length > outgoing.length) length = outgoing.length; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Reads from the serial port into a buffer of bytes up to and + * including a particular character. If the character isn't in + * the serial buffer, then 'null' is returned. + */ + public synchronized byte[] readBytesUntil(int interesting) { + if (bufferIndex == bufferLast) return null; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return null; + + int length = found - bufferIndex + 1; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Reads from the serial port into a buffer of bytes until a + * particular character. If the character isn't in the serial + * buffer, then 'null' is returned. + *

+ * If outgoing[] is not big enough, then -1 is returned, + * and an error message is printed on the console. + * If nothing is in the buffer, zero is returned. + * If 'interesting' byte is not in the buffer, then 0 is returned. + */ + public synchronized int readBytesUntil(int interesting, byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return 0; + + int length = found - bufferIndex + 1; + if (length > outgoing.length) { + System.err.println("readBytesUntil() byte buffer is" + + " too small for the " + length + + " bytes up to and including char " + interesting); + return -1; + } + //byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Return whatever has been read from the serial port so far + * as a String. It assumes that the incoming characters are ASCII. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readString() { + if (bufferIndex == bufferLast) return null; + return new String(readBytes()); + } + + + /** + * Combination of readBytesUntil and readString. See caveats in + * each function. Returns null if it still hasn't found what + * you're looking for. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readStringUntil(int interesting) { + byte b[] = readBytesUntil(interesting); + if (b == null) return null; + return new String(b); + } + + + /** + * This will handle both ints, bytes and chars transparently. + */ + public void write(int what) { // will also cover char + try { + port.writeInt(what & 0xff); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + public void write(byte bytes[]) { + try { + port.writeBytes(bytes); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + /** + * Write a String to the output. Note that this doesn't account + * for Unicode (two bytes per char), nor will it send UTF8 + * characters.. It assumes that you mean to send a byte buffer + * (most often the case for networking and serial i/o) and + * will only use the bottom 8 bits of each char in the string. + * (Meaning that internally it uses String.getBytes) + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public void write(String what) { + write(what.getBytes()); + } + + public void setDTR(boolean state) { + try { + port.setDTR(state); + } catch (SerialPortException e) { + errorMessage("setDTR", e); + } + } + + public void setRTS(boolean state) { + try { + port.setRTS(state); + } catch (SerialPortException e) { + errorMessage("setRTS", e); + } + } + + static public List list() { + return Arrays.asList(SerialPortList.getPortNames()); + } + + + /** + * General error reporting, all corraled here just in case + * I think of something slightly more intelligent to do. + */ + static public void errorMessage(String where, Throwable e) { + System.err.println("Error inside Serial." + where + "()"); + e.printStackTrace(); + } +} + + + /* + class SerialMenuListener implements ItemListener { + //public SerialMenuListener() { } + + public void itemStateChanged(ItemEvent e) { + int count = serialMenu.getItemCount(); + for (int i = 0; i < count; i++) { + ((CheckboxMenuItem)serialMenu.getItem(i)).setState(false); + } + CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); + item.setState(true); + String name = item.getLabel(); + //System.out.println(item.getLabel()); + PdeBase.properties.put("serial.port", name); + //System.out.println("set to " + get("serial.port")); + } + } + */ + + + /* + protected Vector buildPortList() { + // get list of names for serial ports + // have the default port checked (if present) + Vector list = new Vector(); + + //SerialMenuListener listener = new SerialMenuListener(); + boolean problem = false; + + // if this is failing, it may be because + // lib/javax.comm.properties is missing. + // java is weird about how it searches for java.comm.properties + // so it tends to be very fragile. i.e. quotes in the CLASSPATH + // environment variable will hose things. + try { + //System.out.println("building port list"); + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + //System.out.println(portId); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + //if (portId.getName().equals(port)) { + String name = portId.getName(); + //CheckboxMenuItem mi = + //new CheckboxMenuItem(name, name.equals(defaultName)); + + //mi.addItemListener(listener); + //serialMenu.add(mi); + list.addElement(name); + } + } + } catch (UnsatisfiedLinkError e) { + e.printStackTrace(); + problem = true; + + } catch (Exception e) { + System.out.println("exception building serial menu"); + e.printStackTrace(); + } + + //if (serialMenu.getItemCount() == 0) { + //System.out.println("dimming serial menu"); + //serialMenu.setEnabled(false); + //} + + // only warn them if this is the first time + if (problem && PdeBase.firstTime) { + JOptionPane.showMessageDialog(this, //frame, + "Serial port support not installed.\n" + + "Check the readme for instructions\n" + + "if you need to use the serial port. ", + "Serial Port Warning", + JOptionPane.WARNING_MESSAGE); + } + return list; + } + */ + + + diff --git a/tools/linux64/src/maple_loader/src/processing/app/SerialException.java b/tools/linux64/src/maple_loader/src/processing/app/SerialException.java new file mode 100644 index 0000000..525c240 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/SerialException.java @@ -0,0 +1,39 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) 2007 David A. Mellis + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +public class SerialException extends Exception { + public SerialException() { + super(); + } + + public SerialException(String message) { + super(message); + } + + public SerialException(String message, Throwable cause) { + super(message, cause); + } + + public SerialException(Throwable cause) { + super(cause); + } +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/debug/MessageConsumer.java b/tools/linux64/src/maple_loader/src/processing/app/debug/MessageConsumer.java new file mode 100644 index 0000000..5e20429 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/debug/MessageConsumer.java @@ -0,0 +1,42 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * Interface for dealing with parser/compiler output. + *

+ * Different instances of MessageStream need to do different things with + * messages. In particular, a stream instance used for parsing output from + * the compiler compiler has to interpret its messages differently than one + * parsing output from the runtime. + *

+ * Classes which consume messages and do something with them + * should implement this interface. + */ +public interface MessageConsumer { + + public void message(String s); + +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/debug/MessageSiphon.java b/tools/linux64/src/maple_loader/src/processing/app/debug/MessageSiphon.java new file mode 100644 index 0000000..26901c3 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/debug/MessageSiphon.java @@ -0,0 +1,104 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.SocketException; + +/** + * Slurps up messages from compiler. + */ +public class MessageSiphon implements Runnable { + + private final BufferedReader streamReader; + private final MessageConsumer consumer; + + private Thread thread; + private boolean canRun; + + public MessageSiphon(InputStream stream, MessageConsumer consumer) { + this.streamReader = new BufferedReader(new InputStreamReader(stream)); + this.consumer = consumer; + this.canRun = true; + + thread = new Thread(this); + // don't set priority too low, otherwise exceptions won't + // bubble up in time (i.e. compile errors have a weird delay) + //thread.setPriority(Thread.MIN_PRIORITY); + thread.setPriority(Thread.MAX_PRIORITY - 1); + thread.start(); + } + + + public void run() { + try { + // process data until we hit EOF; this will happily block + // (effectively sleeping the thread) until new data comes in. + // when the program is finally done, null will come through. + // + String currentLine; + while (canRun && (currentLine = streamReader.readLine()) != null) { + // \n is added again because readLine() strips it out + //EditorConsole.systemOut.println("messaging in"); + consumer.message(currentLine + "\n"); + //EditorConsole.systemOut.println("messaging out"); + } + //EditorConsole.systemOut.println("messaging thread done"); + } catch (NullPointerException npe) { + // Fairly common exception during shutdown + } catch (SocketException e) { + // socket has been close while we were wainting for data. nothing to see here, move along + } catch (Exception e) { + // On Linux and sometimes on Mac OS X, a "bad file descriptor" + // message comes up when closing an applet that's run externally. + // That message just gets supressed here.. + String mess = e.getMessage(); + if ((mess != null) && + (mess.indexOf("Bad file descriptor") != -1)) { + //if (e.getMessage().indexOf("Bad file descriptor") == -1) { + //System.err.println("MessageSiphon err " + e); + //e.printStackTrace(); + } else { + e.printStackTrace(); + } + } finally { + thread = null; + } + } + + // Wait until the MessageSiphon thread is complete. + public void join() throws java.lang.InterruptedException { + // Grab a temp copy in case another thread nulls the "thread" + // member variable + Thread t = thread; + if (t != null) t.join(); + } + + public void stop() { + this.canRun = false; + } + +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/debug/RunnerException.java b/tools/linux64/src/maple_loader/src/processing/app/debug/RunnerException.java new file mode 100644 index 0000000..0a67d1e --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/debug/RunnerException.java @@ -0,0 +1,161 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-08 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * An exception with a line number attached that occurs + * during either compile time or run time. + */ +@SuppressWarnings("serial") +public class RunnerException extends Exception { + protected String message; + protected int codeIndex; + protected int codeLine; + protected int codeColumn; + protected boolean showStackTrace; + + + public RunnerException(String message) { + this(message, true); + } + + public RunnerException(String message, boolean showStackTrace) { + this(message, -1, -1, -1, showStackTrace); + } + + public RunnerException(String message, int file, int line) { + this(message, file, line, -1, true); + } + + + public RunnerException(String message, int file, int line, int column) { + this(message, file, line, column, true); + } + + + public RunnerException(String message, int file, int line, int column, + boolean showStackTrace) { + this.message = message; + this.codeIndex = file; + this.codeLine = line; + this.codeColumn = column; + this.showStackTrace = showStackTrace; + } + + + public RunnerException(Exception e) { + super(e); + this.showStackTrace = true; + } + + /** + * Override getMessage() in Throwable, so that I can set + * the message text outside the constructor. + */ + public String getMessage() { + return message; + } + + + public void setMessage(String message) { + this.message = message; + } + + + public int getCodeIndex() { + return codeIndex; + } + + + public void setCodeIndex(int index) { + codeIndex = index; + } + + + public boolean hasCodeIndex() { + return codeIndex != -1; + } + + + public int getCodeLine() { + return codeLine; + } + + + public void setCodeLine(int line) { + this.codeLine = line; + } + + + public boolean hasCodeLine() { + return codeLine != -1; + } + + + public void setCodeColumn(int column) { + this.codeColumn = column; + } + + + public int getCodeColumn() { + return codeColumn; + } + + + public void showStackTrace() { + showStackTrace = true; + } + + + public void hideStackTrace() { + showStackTrace = false; + } + + + /** + * Nix the java.lang crap out of an exception message + * because it scares the children. + *

+ * This function must be static to be used with super() + * in each of the constructors above. + */ + /* + static public final String massage(String msg) { + if (msg.indexOf("java.lang.") == 0) { + //int dot = msg.lastIndexOf('.'); + msg = msg.substring("java.lang.".length()); + } + return msg; + //return (dot == -1) ? msg : msg.substring(dot+1); + } + */ + + + public void printStackTrace() { + if (showStackTrace) { + super.printStackTrace(); + } + } +} diff --git a/tools/linux64/src/maple_loader/src/processing/app/helpers/ProcessUtils.java b/tools/linux64/src/maple_loader/src/processing/app/helpers/ProcessUtils.java new file mode 100644 index 0000000..c023f58 --- /dev/null +++ b/tools/linux64/src/maple_loader/src/processing/app/helpers/ProcessUtils.java @@ -0,0 +1,32 @@ +package processing.app.helpers; + +//import processing.app.Base; + +import java.io.IOException; +import java.util.Map; + +import processing.app.Base; + +public class ProcessUtils { + + public static Process exec(String[] command) throws IOException { + // No problems on linux and mac + if (!Base.isWindows()) { + return Runtime.getRuntime().exec(command); + } + + // Brutal hack to workaround windows command line parsing. + // http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly + // http://msdn.microsoft.com/en-us/library/a1y7w461.aspx + // http://bugs.sun.com/view_bug.do?bug_id=6468220 + // http://bugs.sun.com/view_bug.do?bug_id=6518827 + String[] cmdLine = new String[command.length]; + for (int i = 0; i < command.length; i++) + cmdLine[i] = command[i].replace("\"", "\\\""); + + ProcessBuilder pb = new ProcessBuilder(cmdLine); + Map env = pb.environment(); + env.put("CYGWIN", "nodosfilewarning"); + return pb.start(); + } +} diff --git a/tools/linux64/src/stm32flash_serial/src/AUTHORS b/tools/linux64/src/stm32flash_serial/src/AUTHORS new file mode 100644 index 0000000..d096f22 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/AUTHORS @@ -0,0 +1,19 @@ +Authors ordered by first contribution. + +Geoffrey McRae +Bret Olmsted +Tormod Volden +Jakob Malm +Reuben Dowle +Matthias Kubisch +Paul Fertser +Daniel Strnad +Jérémie Rapin +Christian Pointner +Mats Erik Andersson +Alexey Borovik +Antonio Borneo +Armin van der Togt +Brian Silverman +Georg Hofmann +Luis Rodrigues diff --git a/tools/linux64/src/stm32flash_serial/src/Android.mk b/tools/linux64/src/stm32flash_serial/src/Android.mk new file mode 100644 index 0000000..7be3d00 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/Android.mk @@ -0,0 +1,20 @@ +TOP_LOCAL_PATH := $(call my-dir) + +include $(call all-named-subdir-makefiles, parsers) + +LOCAL_PATH := $(TOP_LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_MODULE := stm32flash +LOCAL_SRC_FILES := \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c \ + stm32.c \ + utils.c +LOCAL_STATIC_LIBRARIES := libparsers +include $(BUILD_EXECUTABLE) diff --git a/tools/linux64/src/stm32flash_serial/src/HOWTO b/tools/linux64/src/stm32flash_serial/src/HOWTO new file mode 100644 index 0000000..d8f32eb --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/HOWTO @@ -0,0 +1,35 @@ +Add new interfaces: +===================================================================== +Current version 0.4 supports the following interfaces: +- UART Windows (either "COMn" and "\\.\COMn"); +- UART posix/Linux (e.g. "/dev/ttyUSB0"); +- I2C Linux through standard driver "i2c-dev" (e.g. "/dev/i2c-n"). + +Starting from version 0.4, the back-end of stm32flash is modular and +ready to be expanded to support new interfaces. +I'm planning adding SPI on Linux through standard driver "spidev". +You are invited to contribute with more interfaces. + +To add a new interface you need to add a new file, populate the struct +port_interface (check at the end of files i2c.c, serial_posix.c and +serial_w32.c) and provide the relative functions to operate on the +interface: open/close, read/write, get_cfg_str and the optional gpio. +The include the new drive in Makefile and register the new struct +port_interface in file port.c in struct port_interface *ports[]. + +There are several USB-I2C adapter in the market, each providing its +own libraries to communicate with the I2C bus. +Could be interesting to provide as back-end a bridge between stm32flash +and such libraries (I have no plan on this item). + + +Add new STM32 devices: +===================================================================== +Add a new line in file dev_table.c, in table devices[]. +The fields of the table are listed in stm32.h, struct stm32_dev. + + +Cross compile on Linux host for Windows target with MinGW: +===================================================================== +I'm using a 64 bit Arch Linux machines, and I usually run: + make CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar diff --git a/tools/linux64/src/stm32flash_serial/src/I2C.txt b/tools/linux64/src/stm32flash_serial/src/I2C.txt new file mode 100644 index 0000000..4c05ff6 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/I2C.txt @@ -0,0 +1,94 @@ +About I2C back-end communication in stm32flash +========================================================================== + +Starting from version v0.4, beside the serial communication port, +stm32flash adds support for I2C port to talk with STM32 bootloader. + +The current I2C back-end supports only the API provided by Linux kernel +driver "i2c-dev", so only I2C controllers with Linux kernel driver can be +used. +In Linux source code, most of the drivers for I2C and SMBUS controllers +are in + ./drivers/i2c/busses/ +Only I2C is supported by STM32 bootloader, so check the section below +about SMBUS. +No I2C support for Windows is available in stm32flash v0.4. + +Thanks to the new modular back-end, stm32flash can be easily extended to +support new back-ends and API. Check HOWTO file in stm32flash source code +for details. + +In the market there are several USB-to-I2C dongles; most of them are not +supported by kernel drivers. Manufacturer provide proprietary userspace +libraries using not standardized API. +These API and dongles could be supported in feature versions. + +There are currently 3 versions of STM32 bootloader for I2C communications: +- v1.0 using I2C clock stretching synchronization between host and STM32; +- v1.1 superset of v1.0, adds non stretching commands; +- v1.2 superset of v1.1, adds CRC command and compatibility with i2cdetect. +Details in ST application note AN2606. +All the bootloaders above are tested and working with stm32flash. + + +SMBUS controllers +========================================================================== + +Almost 50% of the drivers in Linux source code folder + ./drivers/i2c/busses/ +are for controllers that "only" support SMBUS protocol. They can NOT +operate with STM32 bootloader. +To identify if your controller supports I2C, use command: + i2cdetect -F n +where "n" is the number of the I2C interface (e.g. n=3 for "/dev/i2c-3"). +Controllers that supports I2C will report + I2C yes +Controller that support both I2C and SMBUS are ok. + +If you are interested on details about SMBUS protocol, you can download +the current specs from + http://smbus.org/specs/smbus20.pdf +and you can read the files in Linux source code + ./Documentation/i2c/i2c-protocol + ./Documentation/i2c/smbus-protocol + + +About bootloader v1.0 +========================================================================== + +Version v1.0 can have issues with some I2C controllers due to use of clock +stretching during commands that require long operations, like flash erase +and programming. + +Clock stretching is a technique to synchronize host and I2C device. When +I2C device wants to force a delay in the communication, it push "low" the +I2C clock; the I2C controller detects it and waits until I2C clock returns +"high". +Most I2C controllers set a "timeout" for clock stretching, ranging from +few milli-seconds to seconds depending on specific HW or SW driver. + +It is possible that the timeout in your I2C controller is smaller than the +delay required for flash erase or programming. In this case the I2C +controller will timeout and report error to stm32flash. +There is no possibility for stm32flash to retry, so it can only signal the +error and exit. + +To by-pass the issue with bootloader v1.0 you can modify the kernel driver +of your I2C controller. Not an easy job, since every controller has its own +way to handle the timeout. + +In my case I'm using the I2C controller integrated in the VGA port of my +laptop HP EliteBook 8460p. I built the 0.25$ VGA-to-I2C adapter reported in + http://www.paintyourdragon.com/?p=43 +To change the timeout of the I2C controller I had to modify the kernel file + drivers/gpu/drm/radeon/radeon_i2c.c +line 969 +- i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ ++ i2c->bit.timeout = msecs_to_jiffies(5000); /* 5s for STM32 */ +and recompile it. +Then + $> modprobe i2c-dev + $> chmod 666 /dev/i2c-7 + #> stm32flash -a 0x39 /dev/i2c-7 + +2014-09-16 Antonio Borneo diff --git a/tools/linux64/src/stm32flash_serial/src/Makefile b/tools/linux64/src/stm32flash_serial/src/Makefile new file mode 100644 index 0000000..0328d55 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/Makefile @@ -0,0 +1,38 @@ +PREFIX = /usr/local +CFLAGS += -Wall -g + +INSTALL = install + +OBJS = dev_table.o \ + i2c.o \ + init.o \ + main.o \ + port.o \ + serial_common.o \ + serial_platform.o \ + stm32.o \ + utils.o + +LIBOBJS = parsers/parsers.a + +all: stm32flash + +serial_platform.o: serial_posix.c serial_w32.c + +parsers/parsers.a: + cd parsers && $(MAKE) parsers.a + +stm32flash: $(OBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBOBJS) + +clean: + rm -f $(OBJS) stm32flash + cd parsers && $(MAKE) $@ + +install: all + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 755 stm32flash $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -d $(DESTDIR)$(PREFIX)/share/man/man1 + $(INSTALL) -m 644 stm32flash.1 $(DESTDIR)$(PREFIX)/share/man/man1 + +.PHONY: all clean install diff --git a/tools/linux64/src/stm32flash_serial/src/TODO b/tools/linux64/src/stm32flash_serial/src/TODO new file mode 100644 index 0000000..41df614 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/TODO @@ -0,0 +1,7 @@ + +stm32: +- Add support for variable page size + +AUTHORS: +- Add contributors from Geoffrey's commits + diff --git a/tools/linux64/src/stm32flash_serial/src/dev_table.c b/tools/linux64/src/stm32flash_serial/src/dev_table.c new file mode 100644 index 0000000..399cd9d --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/dev_table.c @@ -0,0 +1,70 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2014 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. +*/ + +#include "stm32.h" + +/* + * Device table, corresponds to the "Bootloader device-dependant parameters" + * table in ST document AN2606. + * Note that the option bytes upper range is inclusive! + */ +const stm32_dev_t devices[] = { + /* F0 */ + {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x445, "STM32F042xx" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800}, + {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800}, + /* F1 */ + {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800}, + {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800}, + /* Note that F2 and F4 devices have sectors of different page sizes + and only the first sectors (of one page size) are included here */ + /* F2 */ + {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* F3 */ + {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x439, "STM32F302x4(6/8)" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + /* F4 */ + {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */ + {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + /* L0 */ + {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + /* L1 */ + {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000}, + {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + /* These are not (yet) in AN2606: */ + {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0}, + {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0}, + {0x0} +}; diff --git a/tools/linux64/src/stm32flash_serial/src/gpl-2.0.txt b/tools/linux64/src/stm32flash_serial/src/gpl-2.0.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/gpl-2.0.txt @@ -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/tools/linux64/src/stm32flash_serial/src/i2c.c b/tools/linux64/src/stm32flash_serial/src/i2c.c new file mode 100644 index 0000000..10e6bb1 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/i2c.c @@ -0,0 +1,209 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + + +#if !defined(__linux__) + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + return PORT_ERR_NODEV; +} + +struct port_interface port_i2c = { + .name = "i2c", + .open = i2c_open, +}; + +#else + +#ifdef __ANDROID__ +#define I2C_SLAVE 0x0703 /* Use this slave address */ +#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */ +/* To determine what functionality is present */ +#define I2C_FUNC_I2C 0x00000001 +#else +#include +#include +#endif + +#include + +struct i2c_priv { + int fd; + int addr; +}; + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + struct i2c_priv *h; + int fd, addr, ret; + unsigned long funcs; + + /* 1. check device name match */ + if (strncmp(ops->device, "/dev/i2c-", strlen("/dev/i2c-"))) + return PORT_ERR_NODEV; + + /* 2. check options */ + addr = ops->bus_addr; + if (addr < 0x03 || addr > 0x77) { + fprintf(stderr, "I2C address out of range [0x03-0x77]\n"); + return PORT_ERR_UNKNOWN; + } + + /* 3. open it */ + h = calloc(sizeof(*h), 1); + if (h == NULL) { + fprintf(stderr, "End of memory\n"); + return PORT_ERR_UNKNOWN; + } + fd = open(ops->device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open special file \"%s\"\n", + ops->device); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 3.5. Check capabilities */ + ret = ioctl(fd, I2C_FUNCS, &funcs); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(funcs) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + if ((funcs & I2C_FUNC_I2C) == 0) { + fprintf(stderr, "Error: controller is not I2C, only SMBUS.\n"); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 4. set options */ + ret = ioctl(fd, I2C_SLAVE, addr); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(slave) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + h->fd = fd; + h->addr = addr; + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t i2c_close(struct port_interface *port) +{ + struct i2c_priv *h; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + close(h->fd); + free(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t i2c_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = read(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = write(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n, + int level) +{ + return PORT_ERR_OK; +} + +static const char *i2c_get_cfg_str(struct port_interface *port) +{ + struct i2c_priv *h; + static char str[11]; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return "INVALID"; + snprintf(str, sizeof(str), "addr 0x%2x", h->addr); + return str; +} + +static struct varlen_cmd i2c_cmd_get_reply[] = { + {0x10, 11}, + {0x11, 17}, + {0x12, 18}, + { /* sentinel */ } +}; + +struct port_interface port_i2c = { + .name = "i2c", + .flags = PORT_STRETCH_W, + .open = i2c_open, + .close = i2c_close, + .read = i2c_read, + .write = i2c_write, + .gpio = i2c_gpio, + .cmd_get_reply = i2c_cmd_get_reply, + .get_cfg_str = i2c_get_cfg_str, +}; + +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/init.c b/tools/linux64/src/stm32flash_serial/src/init.c new file mode 100644 index 0000000..77a571b --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/init.c @@ -0,0 +1,219 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "init.h" +#include "serial.h" +#include "stm32.h" +#include "port.h" + +struct gpio_list { + struct gpio_list *next; + int gpio; +}; + + +static int write_to(const char *filename, const char *value) +{ + int fd, ret; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open file \"%s\"\n", filename); + return 0; + } + ret = write(fd, value, strlen(value)); + if (ret < 0) { + fprintf(stderr, "Error writing in file \"%s\"\n", filename); + close(fd); + return 0; + } + close(fd); + return 1; +} + +#if !defined(__linux__) +static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) +{ + fprintf(stderr, "GPIO control only available in Linux\n"); + return 0; +} +#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 */ + struct stat buf; + struct gpio_list *new; + int ret; + + sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + ret = stat(file, &buf); + if (ret) { + /* file miss, GPIO not exported yet */ + sprintf(num, "%d", n); + ret = write_to("/sys/class/gpio/export", num); + if (!ret) + return 0; + ret = stat(file, &buf); + if (ret) { + fprintf(stderr, "GPIO %d not available\n", n); + return 0; + } + new = (struct gpio_list *)malloc(sizeof(struct gpio_list)); + if (new == NULL) { + fprintf(stderr, "Out of memory\n"); + return 0; + } + new->gpio = n; + new->next = *gpio_to_release; + *gpio_to_release = new; + } + + return write_to(file, level ? "high" : "low"); +} +#endif + +static int release_gpio(int n) +{ + char num[16]; /* sized to carry MAX_INT */ + + sprintf(num, "%d", n); + return write_to("/sys/class/gpio/unexport", num); +} + +static int gpio_sequence(struct port_interface *port, const char *s, size_t l) +{ + struct gpio_list *gpio_to_release = NULL, *to_free; + int ret, level, gpio; + + ret = 1; + while (ret == 1 && *s && l > 0) { + if (*s == '-') { + level = 0; + s++; + l--; + } else + level = 1; + + if (isdigit(*s)) { + gpio = atoi(s); + while (isdigit(*s)) { + s++; + l--; + } + } else if (!strncmp(s, "rts", 3)) { + gpio = -GPIO_RTS; + s += 3; + l -= 3; + } else if (!strncmp(s, "dtr", 3)) { + gpio = -GPIO_DTR; + s += 3; + l -= 3; + } else if (!strncmp(s, "brk", 3)) { + 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)) { + if (*s == ',') { + s++; + l--; + } else { + fprintf(stderr, "Character \'%c\' is not a separator\n", *s); + ret = 0; + break; + } + } + if (gpio < 0) + ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); + else + ret = drive_gpio(gpio, level, &gpio_to_release); + usleep(100000); + } + + while (gpio_to_release) { + release_gpio(gpio_to_release->gpio); + to_free = gpio_to_release; + gpio_to_release = gpio_to_release->next; + free(to_free); + } + usleep(500000); + return ret; +} + +static int gpio_bl_entry(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL || seq[0] == ':') + return 1; + + s = strchr(seq, ':'); + if (s == NULL) + return gpio_sequence(port, seq, strlen(seq)); + + return gpio_sequence(port, seq, s - seq); +} + +static int gpio_bl_exit(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL) + return 1; + + s = strchr(seq, ':'); + if (s == NULL || s[1] == '\0') + return 1; + + return gpio_sequence(port, s + 1, strlen(s + 1)); +} + +int init_bl_entry(struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_entry(port, seq); + + return 1; +} + +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_exit(port, seq); + + if (stm32_reset_device(stm) != STM32_ERR_OK) + return 0; + return 1; +} diff --git a/tools/linux64/src/stm32flash_serial/src/init.h b/tools/linux64/src/stm32flash_serial/src/init.h new file mode 100644 index 0000000..6075b51 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/init.h @@ -0,0 +1,31 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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 _INIT_H +#define _INIT_H + +#include "stm32.h" +#include "port.h" + +int init_bl_entry(struct port_interface *port, const char *seq); +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq); + +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/main.c b/tools/linux64/src/stm32flash_serial/src/main.c new file mode 100644 index 0000000..f081d61 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/main.c @@ -0,0 +1,774 @@ +/* + 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 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "utils.h" +#include "serial.h" +#include "stm32.h" +#include "parsers/parser.h" +#include "port.h" + +#include "parsers/binary.h" +#include "parsers/hex.h" + +#define VERSION "Arduino_STM32_0.9" + +/* device globals */ +stm32_t *stm = NULL; + +void *p_st = NULL; +parser_t *parser = NULL; + +/* settings */ +struct port_options port_opts = { + .device = NULL, + .baudRate = SERIAL_BAUD_57600, + .serial_mode = "8e1", + .bus_addr = 0, + .rx_frame_max = STM32_MAX_RX_FRAME, + .tx_frame_max = STM32_MAX_TX_FRAME, +}; +int rd = 0; +int wr = 0; +int wu = 0; +int rp = 0; +int ur = 0; +int eraseOnly = 0; +int crc = 0; +int npages = 0; +int spage = 0; +int no_erase = 0; +char verify = 0; +int retry = 10; +char exec_flag = 0; +uint32_t execute = 0; +char init_flag = 1; +char force_binary = 0; +char reset_flag = 0; +char *filename; +char *gpio_seq = NULL; +uint32_t start_addr = 0; +uint32_t readwrite_len = 0; + +/* functions */ +int parse_options(int argc, char *argv[]); +void show_help(char *name); + +static int is_addr_in_ram(uint32_t addr) +{ + return addr >= stm->dev->ram_start && addr < stm->dev->ram_end; +} + +static int is_addr_in_flash(uint32_t addr) +{ + return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; +} + +static int flash_addr_to_page_floor(uint32_t addr) +{ + if (!is_addr_in_flash(addr)) + return 0; + + return (addr - stm->dev->fl_start) / stm->dev->fl_ps; +} + +static int flash_addr_to_page_ceil(uint32_t addr) +{ + if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) + return 0; + + return (addr + stm->dev->fl_ps - 1 - stm->dev->fl_start) + / stm->dev->fl_ps; +} + +static uint32_t flash_page_to_addr(int page) +{ + return stm->dev->fl_start + page * stm->dev->fl_ps; +} + +int main(int argc, char* argv[]) { + struct port_interface *port = NULL; + int ret = 1; + stm32_err_t s_err; + parser_err_t perr; + FILE *diag = stdout; + + fprintf(diag, "stm32flash " VERSION "\n\n"); + fprintf(diag, "http://github.com/rogerclarkmelbourne/arduino_stm32\n\n"); + if (parse_options(argc, argv) != 0) + goto close; + + if (rd && filename[0] == '-') { + diag = stderr; + } + + if (wr) { + /* first try hex */ + if (!force_binary) { + parser = &PARSER_HEX; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (force_binary || (perr = parser->open(p_st, filename, 0)) != PARSER_ERR_OK) { + if (force_binary || perr == PARSER_ERR_INVALID_FILE) { + if (!force_binary) { + parser->close(p_st); + p_st = NULL; + } + + /* now try binary */ + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + perr = parser->open(p_st, filename, 0); + } + + /* if still have an error, fail */ + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) perror(filename); + goto close; + } + } + + fprintf(diag, "Using Parser : %s\n", parser->name); + } else { + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (port_open(&port_opts, &port) != PORT_ERR_OK) { + fprintf(stderr, "Failed to open port: %s\n", port_opts.device); + goto close; + } + + fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); + if (init_flag && init_bl_entry(port, gpio_seq) == 0) + goto close; + stm = stm32_init(port, init_flag); + if (!stm) + goto close; + + fprintf(diag, "Version : 0x%02x\n", stm->bl_version); + if (port->flags & PORT_GVR_ETX) { + fprintf(diag, "Option 1 : 0x%02x\n", stm->option1); + fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); + } + fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name); + fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); + fprintf(diag, "- Flash : %dKiB (sector size: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps); + 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); + + uint8_t buffer[256]; + uint32_t addr, start, end; + unsigned int len; + int failed = 0; + int first_page, num_pages; + + /* + * Cleanup addresses: + * + * Starting from options + * start_addr, readwrite_len, spage, npages + * and using device memory size, compute + * start, end, first_page, num_pages + */ + if (start_addr || readwrite_len) { + start = start_addr; + + if (is_addr_in_flash(start)) + end = stm->dev->fl_end; + else { + no_erase = 1; + if (is_addr_in_ram(start)) + end = stm->dev->ram_end; + else + end = start + sizeof(uint32_t); + } + + if (readwrite_len && (end > start + readwrite_len)) + end = start + readwrite_len; + + first_page = flash_addr_to_page_floor(start); + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + else + num_pages = flash_addr_to_page_ceil(end) - first_page; + } else if (!spage && !npages) { + start = stm->dev->fl_start; + end = stm->dev->fl_end; + first_page = 0; + num_pages = 0xff; /* mass erase */ + } else { + first_page = spage; + start = flash_page_to_addr(first_page); + if (start > stm->dev->fl_end) { + fprintf(stderr, "Address range exceeds flash size.\n"); + goto close; + } + + if (npages) { + num_pages = npages; + end = flash_page_to_addr(first_page + num_pages); + if (end > stm->dev->fl_end) + end = stm->dev->fl_end; + } else { + end = stm->dev->fl_end; + num_pages = flash_addr_to_page_ceil(end) - first_page; + } + + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + } + + if (rd) { + unsigned int max_len = port_opts.rx_frame_max; + + fprintf(diag, "Memory read\n"); + + perr = parser->open(p_st, filename, 1); + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) + perror(filename); + goto close; + } + + fflush(diag); + addr = start; + while(addr < end) { + uint32_t left = end - addr; + len = max_len > left ? left : max_len; + s_err = stm32_read_memory(stm, addr, buffer, len); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr); + goto close; + } + if (parser->write(p_st, buffer, len) != PARSER_ERR_OK) + { + fprintf(stderr, "Failed to write data to file\n"); + goto close; + } + addr += len; + + fprintf(diag, + "\rRead address 0x%08x (%.2f%%) ", + addr, + (100.0f / (float)(end - start)) * (float)(addr - start) + ); + fflush(diag); + } + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (rp) { + fprintf(stdout, "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"); + /* 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) { + ret = 0; + fprintf(stdout, "Erasing flash\n"); + + if (num_pages != 0xff && + (start != flash_page_to_addr(first_page) + || end != flash_page_to_addr(first_page + num_pages))) { + fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); + ret = 1; + goto close; + } + + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + ret = 1; + goto close; + } + } else if (wu) { + fprintf(diag, "Write-unprotecting flash\n"); + /* the device automatically performs a reset after the sending the ACK */ + reset_flag = 0; + stm32_wunprot_memory(stm); + fprintf(diag, "Done.\n"); + + } else if (wr) { + fprintf(diag, "Write to memory\n"); + + off_t offset = 0; + ssize_t r; + unsigned int size; + unsigned int max_wlen, max_rlen; + + max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */ + max_wlen &= ~3; /* 32 bit aligned */ + + max_rlen = port_opts.rx_frame_max; + max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; + + /* Assume data from stdin is whole device */ + if (filename[0] == '-' && filename[1] == '\0') + 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) { + // 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 + if (!no_erase && num_pages) { + fprintf(diag, "Erasing memory\n"); + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + goto close; + } + } + + fflush(diag); + addr = start; + while(addr < end && offset < size) { + uint32_t left = end - addr; + len = max_wlen > left ? left : max_wlen; + len = len > size - offset ? size - offset : len; + + if (parser->read(p_st, buffer, &len) != PARSER_ERR_OK) + goto close; + + if (len == 0) { + if (filename[0] == '-') { + 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) { + fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr); + goto close; + } + + if (verify) { + uint8_t compare[len]; + unsigned int offset, rlen; + + offset = 0; + while (offset < len) { + rlen = len - offset; + rlen = rlen < max_rlen ? rlen : max_rlen; + s_err = stm32_read_memory(stm, addr + offset, compare + offset, rlen); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset); + goto close; + } + offset += rlen; + } + + for(r = 0; r < len; ++r) + if (buffer[r] != compare[r]) { + if (failed == retry) { + fprintf(stderr, "Failed to verify at address 0x%08x, expected 0x%02x and found 0x%02x\n", + (uint32_t)(addr + r), + buffer [r], + compare[r] + ); + goto close; + } + ++failed; + goto again; + } + + failed = 0; + } + + addr += len; + offset += len; + + fprintf(diag, + "\rWrote %saddress 0x%08x (%.2f%%) ", + verify ? "and verified " : "", + addr, + (100.0f / size) * offset + ); + fflush(diag); + + } + + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (crc) { + uint32_t crc_val = 0; + + fprintf(diag, "CRC computation\n"); + + s_err = stm32_crc_wrapper(stm, start, end - start, &crc_val); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read CRC\n"); + goto close; + } + fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end, + crc_val); + ret = 0; + goto close; + } else + ret = 0; + +close: + if (stm && exec_flag && ret == 0) { + if (execute == 0) + execute = stm->dev->fl_start; + + fprintf(diag, "\nStarting execution at address 0x%08x... ", execute); + fflush(diag); + if (stm32_go(stm, execute) == STM32_ERR_OK) { + reset_flag = 0; + fprintf(diag, "done.\n"); + } else + fprintf(diag, "failed.\n"); + } + + if (stm && reset_flag) { + fprintf(diag, "\nResetting device... "); + fflush(diag); + if (init_bl_exit(stm, port, gpio_seq)) + fprintf(diag, "done.\n"); + else fprintf(diag, "failed.\n"); + } + + if (p_st ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) + port->close(port); + + fprintf(diag, "\n"); + return ret; +} + +int parse_options(int argc, char *argv[]) +{ + int c; + char *pLen; + + while ((c = getopt(argc, argv, "a:b:m:r:w:e:vn:g:jkfcChuos:S:F:i:R")) != -1) { + switch(c) { + case 'a': + port_opts.bus_addr = strtoul(optarg, NULL, 0); + break; + + case 'b': + port_opts.baudRate = serial_get_baud(strtoul(optarg, NULL, 0)); + if (port_opts.baudRate == SERIAL_BAUD_INVALID) { + serial_baud_t baudrate; + fprintf(stderr, "Invalid baud rate, valid options are:\n"); + for (baudrate = SERIAL_BAUD_1200; baudrate != SERIAL_BAUD_INVALID; ++baudrate) + fprintf(stderr, " %d\n", serial_get_baud_int(baudrate)); + return 1; + } + break; + + case 'm': + if (strlen(optarg) != 3 + || serial_get_bits(optarg) == SERIAL_BITS_INVALID + || serial_get_parity(optarg) == SERIAL_PARITY_INVALID + || serial_get_stopbit(optarg) == SERIAL_STOPBIT_INVALID) { + fprintf(stderr, "Invalid serial mode\n"); + return 1; + } + port_opts.serial_mode = optarg; + break; + + 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"); + return 1; + } + filename = optarg; + if (filename[0] == '-') { + force_binary = 1; + } + break; + case 'e': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + npages = strtoul(optarg, NULL, 0); + if (npages > 0xFF || npages < 0) { + fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); + return 1; + } + if (!npages) + no_erase = 1; + break; + case 'u': + wu = 1; + if (rd || wr) { + fprintf(stderr, "ERROR: Invalid options, can't write unprotect and read/write at the same time\n"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + break; + + case 'v': + verify = 1; + break; + + case 'n': + retry = strtoul(optarg, NULL, 0); + break; + + case 'g': + exec_flag = 1; + execute = strtoul(optarg, NULL, 0); + if (execute % 4 != 0) { + fprintf(stderr, "ERROR: Execution address must be word-aligned\n"); + return 1; + } + break; + case 's': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + spage = strtoul(optarg, NULL, 0); + break; + case 'S': + if (spage || npages) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } else { + start_addr = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + readwrite_len = strtoul(pLen, NULL, 0); + if (readwrite_len == 0) { + fprintf(stderr, "ERROR: Invalid options, can't specify zero length\n"); + return 1; + } + } + } + break; + case 'F': + port_opts.rx_frame_max = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + port_opts.tx_frame_max = strtoul(pLen, NULL, 0); + } + if (port_opts.rx_frame_max < 0 + || port_opts.tx_frame_max < 0) { + fprintf(stderr, "ERROR: Invalid negative value for option -F\n"); + return 1; + } + if (port_opts.rx_frame_max == 0) + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + if (port_opts.tx_frame_max == 0) + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + if (port_opts.rx_frame_max < 20 + || port_opts.tx_frame_max < 5) { + fprintf(stderr, "ERROR: current code cannot work with small frames.\n"); + fprintf(stderr, "min(RX) = 20, min(TX) = 5\n"); + return 1; + } + if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) { + fprintf(stderr, "WARNING: Ignore RX length in option -F\n"); + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + } + if (port_opts.tx_frame_max > STM32_MAX_TX_FRAME) { + fprintf(stderr, "WARNING: Ignore TX length in option -F\n"); + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + } + break; + case 'f': + force_binary = 1; + break; + + case 'c': + init_flag = 0; + break; + + case 'h': + show_help(argv[0]); + exit(0); + + case 'i': + gpio_seq = optarg; + break; + + case 'R': + reset_flag = 1; + break; + + case 'C': + crc = 1; + break; + } + } + + for (c = optind; c < argc; ++c) { + if (port_opts.device) { + fprintf(stderr, "ERROR: Invalid parameter specified\n"); + show_help(argv[0]); + return 1; + } + port_opts.device = argv[c]; + } + + if (port_opts.device == NULL) { + fprintf(stderr, "ERROR: Device not specified\n"); + show_help(argv[0]); + return 1; + } + + if (!wr && verify) { + fprintf(stderr, "ERROR: Invalid usage, -v is only valid when writing\n"); + show_help(argv[0]); + return 1; + } + + return 0; +} + +void show_help(char *name) { + fprintf(stderr, + "Usage: %s [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]\n" + " -a bus_address Bus address (e.g. for I2C port)\n" + " -b rate Baud rate (default 57600)\n" + " -m mode Serial port mode (default 8e1)\n" + " -r filename Read flash to file (or - stdout)\n" + " -w filename Write flash from file (or - stdout)\n" + " -C Compute CRC of flash content\n" + " -u Disable the flash write-protection\n" + " -j Enable the flash read-protection\n" + " -k Disable the flash read-protection\n" + " -o Erase only\n" + " -e n Only erase n pages before writing the flash\n" + " -v Verify writes\n" + " -n count Retry failed writes up to count times (default 10)\n" + " -g address Start execution at specified address (0 = flash start)\n" + " -S address[:length] Specify start address and optionally length for\n" + " read/write/erase operations\n" + " -F RX_length[:TX_length] Specify the max length of RX and TX frame\n" + " -s start_page Flash at specified page (0 = flash start)\n" + " -f Force binary parser\n" + " -h Show this help\n" + " -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" + " -i GPIO_string GPIO sequence to enter/exit bootloader mode\n" + " GPIO_string=[entry_seq][:[exit_seq]]\n" + " sequence=[-]n[,sequence]\n" + " -R Reset device at exit.\n" + "\n" + "Examples:\n" + " Get device information:\n" + " %s /dev/ttyS0\n" + " or:\n" + " %s /dev/i2c-0\n" + "\n" + " Write with verify and then start execution:\n" + " %s -w filename -v -g 0x0 /dev/ttyS0\n" + "\n" + " Read flash to file:\n" + " %s -r filename /dev/ttyS0\n" + "\n" + " Read 100 bytes of flash from 0x1000 to stdout:\n" + " %s -r - -S 0x1000:100 /dev/ttyS0\n" + "\n" + " Start execution:\n" + " %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", + name, + name, + name, + name, + name, + name, + name, + name + ); +} + diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/Android.mk b/tools/linux64/src/stm32flash_serial/src/parsers/Android.mk new file mode 100644 index 0000000..afec18c --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/Android.mk @@ -0,0 +1,6 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libparsers +LOCAL_SRC_FILES := binary.c hex.c +include $(BUILD_STATIC_LIBRARY) diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/Makefile b/tools/linux64/src/stm32flash_serial/src/parsers/Makefile new file mode 100644 index 0000000..bb7df1e --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/Makefile @@ -0,0 +1,12 @@ + +CFLAGS += -Wall -g + +all: parsers.a + +parsers.a: binary.o hex.o + $(AR) rc $@ binary.o hex.o + +clean: + rm -f *.o parsers.a + +.PHONY: all clean diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/binary.c b/tools/linux64/src/stm32flash_serial/src/parsers/binary.c new file mode 100644 index 0000000..f491952 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/binary.c @@ -0,0 +1,140 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include + +#include "binary.h" + +typedef struct { + int fd; + char write; + struct stat stat; +} binary_t; + +void* binary_init() { + return calloc(sizeof(binary_t), 1); +} + +parser_err_t binary_open(void *storage, const char *filename, const char write) { + binary_t *st = storage; + if (write) { + if (filename[0] == '-') + st->fd = 1; + else + st->fd = open( + filename, +#ifndef __WIN32__ + O_WRONLY | O_CREAT | O_TRUNC, +#else + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, +#endif +#ifndef __WIN32__ + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH +#else + 0 +#endif + ); + st->stat.st_size = 0; + } else { + if (filename[0] == '-') { + st->fd = 0; + } else { + if (stat(filename, &st->stat) != 0) + return PARSER_ERR_INVALID_FILE; + st->fd = open(filename, +#ifndef __WIN32__ + O_RDONLY +#else + O_RDONLY | O_BINARY +#endif + ); + } + } + + st->write = write; + return st->fd == -1 ? PARSER_ERR_SYSTEM : PARSER_ERR_OK; +} + +parser_err_t binary_close(void *storage) { + binary_t *st = storage; + + if (st->fd) close(st->fd); + free(st); + return PARSER_ERR_OK; +} + +unsigned int binary_size(void *storage) { + binary_t *st = storage; + return st->stat.st_size; +} + +parser_err_t binary_read(void *storage, void *data, unsigned int *len) { + binary_t *st = storage; + unsigned int left = *len; + if (st->write) return PARSER_ERR_WRONLY; + + ssize_t r; + while(left > 0) { + r = read(st->fd, data, left); + /* If there is no data to read at all, return OK, but with zero read */ + if (r == 0 && left == *len) { + *len = 0; + return PARSER_ERR_OK; + } + if (r <= 0) return PARSER_ERR_SYSTEM; + left -= r; + data += r; + } + + *len = *len - left; + return PARSER_ERR_OK; +} + +parser_err_t binary_write(void *storage, void *data, unsigned int len) { + binary_t *st = storage; + if (!st->write) return PARSER_ERR_RDONLY; + + ssize_t r; + while(len > 0) { + r = write(st->fd, data, len); + if (r < 1) return PARSER_ERR_SYSTEM; + st->stat.st_size += r; + + len -= r; + data += r; + } + + return PARSER_ERR_OK; +} + +parser_t PARSER_BINARY = { + "Raw BINARY", + binary_init, + binary_open, + binary_close, + binary_size, + binary_read, + binary_write +}; + diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/binary.h b/tools/linux64/src/stm32flash_serial/src/parsers/binary.h new file mode 100644 index 0000000..d989acf --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/binary.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_BINARY_H +#define _PARSER_BINARY_H + +#include "parser.h" + +extern parser_t PARSER_BINARY; +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/hex.c b/tools/linux64/src/stm32flash_serial/src/parsers/hex.c new file mode 100644 index 0000000..3baf856 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/hex.c @@ -0,0 +1,224 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#include "hex.h" +#include "../utils.h" + +typedef struct { + size_t data_len, offset; + uint8_t *data; + uint8_t base; +} hex_t; + +void* hex_init() { + return calloc(sizeof(hex_t), 1); +} + +parser_err_t hex_open(void *storage, const char *filename, const char write) { + hex_t *st = storage; + if (write) { + return PARSER_ERR_RDONLY; + } else { + char mark; + int i, fd; + uint8_t checksum; + unsigned int c; + uint32_t base = 0; + unsigned int last_address = 0x0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return PARSER_ERR_SYSTEM; + + /* read in the file */ + + while(read(fd, &mark, 1) != 0) { + if (mark == '\n' || mark == '\r') continue; + if (mark != ':') + return PARSER_ERR_INVALID_FILE; + + char buffer[9]; + unsigned int reclen, address, type; + uint8_t *record = NULL; + + /* get the reclen, address, and type */ + buffer[8] = 0; + if (read(fd, &buffer, 8) != 8) return PARSER_ERR_INVALID_FILE; + if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* setup the checksum */ + checksum = + reclen + + ((address & 0xFF00) >> 8) + + ((address & 0x00FF) >> 0) + + type; + + switch(type) { + /* data record */ + case 0: + c = address - last_address; + st->data = realloc(st->data, st->data_len + c + reclen); + + /* if there is a gap, set it to 0xff and increment the length */ + if (c > 0) { + memset(&st->data[st->data_len], 0xff, c); + st->data_len += c; + } + + last_address = address + reclen; + record = &st->data[st->data_len]; + st->data_len += reclen; + break; + + /* extended segment address record */ + case 2: + base = 0; + break; + + /* extended linear address record */ + case 4: + base = address; + break; + } + + buffer[2] = 0; + for(i = 0; i < reclen; ++i) { + if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* add the byte to the checksum */ + checksum += c; + + switch(type) { + case 0: + if (record != NULL) { + record[i] = c; + } else { + return PARSER_ERR_INVALID_FILE; + } + break; + + case 2: + case 4: + base = (base << 8) | c; + break; + } + } + + /* read, scan, and verify the checksum */ + if ( + read(fd, &buffer, 2 ) != 2 || + sscanf(buffer, "%2x", &c) != 1 || + (uint8_t)(checksum + c) != 0x00 + ) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + switch(type) { + /* EOF */ + case 1: + close(fd); + return PARSER_ERR_OK; + + /* address record */ + 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) { + st->base = base; + break; + } + + /* we cant cope with files out of order */ + if (base < st->base) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* if there is a gap, enlarge and fill with zeros */ + 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); + st->data_len = len; + } + break; + } + } + + close(fd); + return PARSER_ERR_OK; + } +} + +parser_err_t hex_close(void *storage) { + hex_t *st = storage; + if (st) free(st->data); + free(st); + return PARSER_ERR_OK; +} + +unsigned int hex_size(void *storage) { + hex_t *st = storage; + return st->data_len; +} + +parser_err_t hex_read(void *storage, void *data, unsigned int *len) { + hex_t *st = storage; + unsigned int left = st->data_len - st->offset; + unsigned int get = left > *len ? *len : left; + + memcpy(data, &st->data[st->offset], get); + st->offset += get; + + *len = get; + return PARSER_ERR_OK; +} + +parser_err_t hex_write(void *storage, void *data, unsigned int len) { + return PARSER_ERR_RDONLY; +} + +parser_t PARSER_HEX = { + "Intel HEX", + hex_init, + hex_open, + hex_close, + hex_size, + hex_read, + hex_write +}; + diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/hex.h b/tools/linux64/src/stm32flash_serial/src/parsers/hex.h new file mode 100644 index 0000000..02413c9 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/hex.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_HEX_H +#define _PARSER_HEX_H + +#include "parser.h" + +extern parser_t PARSER_HEX; +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/parsers/parser.h b/tools/linux64/src/stm32flash_serial/src/parsers/parser.h new file mode 100644 index 0000000..c2fae3c --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/parsers/parser.h @@ -0,0 +1,56 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_PARSER +#define _H_PARSER + +enum parser_err { + PARSER_ERR_OK, + PARSER_ERR_SYSTEM, + PARSER_ERR_INVALID_FILE, + PARSER_ERR_WRONLY, + PARSER_ERR_RDONLY +}; +typedef enum parser_err parser_err_t; + +struct parser { + const char *name; + void* (*init )(); /* initialise the parser */ + parser_err_t (*open )(void *storage, const char *filename, const char write); /* open the file for read|write */ + parser_err_t (*close)(void *storage); /* close and free the parser */ + unsigned int (*size )(void *storage); /* get the total data size */ + parser_err_t (*read )(void *storage, void *data, unsigned int *len); /* read a block of data */ + parser_err_t (*write)(void *storage, void *data, unsigned int len); /* write a block of data */ +}; +typedef struct parser parser_t; + +static inline const char* parser_errstr(parser_err_t err) { + switch(err) { + case PARSER_ERR_OK : return "OK"; + case PARSER_ERR_SYSTEM : return "System Error"; + case PARSER_ERR_INVALID_FILE: return "Invalid File"; + case PARSER_ERR_WRONLY : return "Parser can only write"; + case PARSER_ERR_RDONLY : return "Parser can only read"; + default: + return "Unknown Error"; + } +} + +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/port.c b/tools/linux64/src/stm32flash_serial/src/port.c new file mode 100644 index 0000000..08e58cc --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/port.c @@ -0,0 +1,59 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + +#include +#include + +#include "serial.h" +#include "port.h" + + +extern struct port_interface port_serial; +extern struct port_interface port_i2c; + +static struct port_interface *ports[] = { + &port_serial, + &port_i2c, + NULL, +}; + + +port_err_t port_open(struct port_options *ops, struct port_interface **outport) +{ + int ret; + static struct port_interface **port; + + for (port = ports; *port; port++) { + ret = (*port)->open(*port, ops); + if (ret == PORT_ERR_NODEV) + continue; + if (ret == PORT_ERR_OK) + break; + fprintf(stderr, "Error probing interface \"%s\"\n", + (*port)->name); + } + if (*port == NULL) { + fprintf(stderr, "Cannot handle device \"%s\"\n", + ops->device); + return PORT_ERR_UNKNOWN; + } + + *outport = *port; + return PORT_ERR_OK; +} diff --git a/tools/linux64/src/stm32flash_serial/src/port.h b/tools/linux64/src/stm32flash_serial/src/port.h new file mode 100644 index 0000000..290f034 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/port.h @@ -0,0 +1,75 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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_PORT +#define _H_PORT + +typedef enum { + PORT_ERR_OK = 0, + PORT_ERR_NODEV, /* No such device */ + PORT_ERR_TIMEDOUT, /* Operation timed out */ + PORT_ERR_UNKNOWN, +} port_err_t; + +/* flags */ +#define PORT_BYTE (1 << 0) /* byte (not frame) oriented */ +#define PORT_GVR_ETX (1 << 1) /* cmd GVR returns protection status */ +#define PORT_CMD_INIT (1 << 2) /* use INIT cmd to autodetect speed */ +#define PORT_RETRY (1 << 3) /* allowed read() retry after timeout */ +#define PORT_STRETCH_W (1 << 4) /* warning for no-stretching commands */ + +/* all options and flags used to open and configure an interface */ +struct port_options { + const char *device; + serial_baud_t baudRate; + const char *serial_mode; + int bus_addr; + int rx_frame_max; + int tx_frame_max; +}; + +/* + * Specify the length of reply for command GET + * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time + * consuming try-fail-timeout-retry operation. + * On byte-oriented protocols, i.e. UART, this information would be skipped + * after read the first byte, so not needed. + */ +struct varlen_cmd { + uint8_t version; + uint8_t length; +}; + +struct port_interface { + const char *name; + unsigned flags; + port_err_t (*open)(struct port_interface *port, struct port_options *ops); + port_err_t (*close)(struct port_interface *port); + port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level); + const char *(*get_cfg_str)(struct port_interface *port); + struct varlen_cmd *cmd_get_reply; + void *private; +}; + +port_err_t port_open(struct port_options *ops, struct port_interface **outport); + +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/protocol.txt b/tools/linux64/src/stm32flash_serial/src/protocol.txt new file mode 100644 index 0000000..0391099 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/protocol.txt @@ -0,0 +1,19 @@ +The communication protocol used by ST bootloader is documented in following ST +application notes, depending on communication port. + +In current version of stm32flash are supported only UART and I2C ports. + +* AN3154: CAN protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf + +* AN3155: USART protocol used in the STM32(TM) bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf + +* AN4221: I2C protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf + +* AN4286: SPI protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf + +Boot mode selection for STM32 is documented in ST application note AN2606, available in ST website: + http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf diff --git a/tools/linux64/src/stm32flash_serial/src/serial.h b/tools/linux64/src/stm32flash_serial/src/serial.h new file mode 100644 index 0000000..227ba16 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/serial.h @@ -0,0 +1,90 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _SERIAL_H +#define _SERIAL_H + +typedef struct serial serial_t; + +typedef enum { + SERIAL_PARITY_NONE, + SERIAL_PARITY_EVEN, + SERIAL_PARITY_ODD, + + SERIAL_PARITY_INVALID +} serial_parity_t; + +typedef enum { + SERIAL_BITS_5, + SERIAL_BITS_6, + SERIAL_BITS_7, + SERIAL_BITS_8, + + SERIAL_BITS_INVALID +} serial_bits_t; + +typedef enum { + SERIAL_BAUD_1200, + SERIAL_BAUD_1800, + SERIAL_BAUD_2400, + SERIAL_BAUD_4800, + SERIAL_BAUD_9600, + SERIAL_BAUD_19200, + SERIAL_BAUD_38400, + SERIAL_BAUD_57600, + SERIAL_BAUD_115200, + SERIAL_BAUD_128000, + SERIAL_BAUD_230400, + SERIAL_BAUD_256000, + SERIAL_BAUD_460800, + SERIAL_BAUD_500000, + SERIAL_BAUD_576000, + SERIAL_BAUD_921600, + SERIAL_BAUD_1000000, + SERIAL_BAUD_1500000, + SERIAL_BAUD_2000000, + + SERIAL_BAUD_INVALID +} serial_baud_t; + +typedef enum { + SERIAL_STOPBIT_1, + SERIAL_STOPBIT_2, + + SERIAL_STOPBIT_INVALID +} serial_stopbit_t; + +typedef enum { + GPIO_RTS = 1, + GPIO_DTR, + GPIO_BRK, +} serial_gpio_t; + +/* common helper functions */ +serial_baud_t serial_get_baud(const unsigned int baud); +unsigned int serial_get_baud_int(const serial_baud_t baud); +serial_bits_t serial_get_bits(const char *mode); +unsigned int serial_get_bits_int(const serial_bits_t bits); +serial_parity_t serial_get_parity(const char *mode); +char serial_get_parity_str(const serial_parity_t parity); +serial_stopbit_t serial_get_stopbit(const char *mode); +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit); + +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/serial_common.c b/tools/linux64/src/stm32flash_serial/src/serial_common.c new file mode 100644 index 0000000..43e48e1 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/serial_common.c @@ -0,0 +1,154 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "serial.h" + +serial_baud_t serial_get_baud(const unsigned int baud) { + switch(baud) { + case 1200: return SERIAL_BAUD_1200 ; + case 1800: return SERIAL_BAUD_1800 ; + case 2400: return SERIAL_BAUD_2400 ; + case 4800: return SERIAL_BAUD_4800 ; + case 9600: return SERIAL_BAUD_9600 ; + case 19200: return SERIAL_BAUD_19200 ; + case 38400: return SERIAL_BAUD_38400 ; + case 57600: return SERIAL_BAUD_57600 ; + case 115200: return SERIAL_BAUD_115200; + case 128000: return SERIAL_BAUD_128000; + case 230400: return SERIAL_BAUD_230400; + case 256000: return SERIAL_BAUD_256000; + case 460800: return SERIAL_BAUD_460800; + case 500000: return SERIAL_BAUD_500000; + case 576000: return SERIAL_BAUD_576000; + case 921600: return SERIAL_BAUD_921600; + case 1000000: return SERIAL_BAUD_1000000; + case 1500000: return SERIAL_BAUD_1500000; + case 2000000: return SERIAL_BAUD_2000000; + + default: + return SERIAL_BAUD_INVALID; + } +} + +unsigned int serial_get_baud_int(const serial_baud_t baud) { + switch(baud) { + case SERIAL_BAUD_1200 : return 1200 ; + case SERIAL_BAUD_1800 : return 1800 ; + case SERIAL_BAUD_2400 : return 2400 ; + case SERIAL_BAUD_4800 : return 4800 ; + case SERIAL_BAUD_9600 : return 9600 ; + case SERIAL_BAUD_19200 : return 19200 ; + case SERIAL_BAUD_38400 : return 38400 ; + case SERIAL_BAUD_57600 : return 57600 ; + case SERIAL_BAUD_115200: return 115200; + case SERIAL_BAUD_128000: return 128000; + case SERIAL_BAUD_230400: return 230400; + case SERIAL_BAUD_256000: return 256000; + case SERIAL_BAUD_460800: return 460800; + case SERIAL_BAUD_500000: return 500000; + case SERIAL_BAUD_576000: return 576000; + case SERIAL_BAUD_921600: return 921600; + case SERIAL_BAUD_1000000: return 1000000; + case SERIAL_BAUD_1500000: return 1500000; + case SERIAL_BAUD_2000000: return 2000000; + + case SERIAL_BAUD_INVALID: + default: + return 0; + } +} + +serial_bits_t serial_get_bits(const char *mode) { + if (!mode) + return SERIAL_BITS_INVALID; + switch(mode[0]) { + case '5': return SERIAL_BITS_5; + case '6': return SERIAL_BITS_6; + case '7': return SERIAL_BITS_7; + case '8': return SERIAL_BITS_8; + + default: + return SERIAL_BITS_INVALID; + } +} + +unsigned int serial_get_bits_int(const serial_bits_t bits) { + switch(bits) { + case SERIAL_BITS_5: return 5; + case SERIAL_BITS_6: return 6; + case SERIAL_BITS_7: return 7; + case SERIAL_BITS_8: return 8; + + default: + return 0; + } +} + +serial_parity_t serial_get_parity(const char *mode) { + if (!mode || !mode[0]) + return SERIAL_PARITY_INVALID; + switch(mode[1]) { + case 'N': + case 'n': + return SERIAL_PARITY_NONE; + case 'E': + case 'e': + return SERIAL_PARITY_EVEN; + case 'O': + case 'o': + return SERIAL_PARITY_ODD; + + default: + return SERIAL_PARITY_INVALID; + } +} + +char serial_get_parity_str(const serial_parity_t parity) { + switch(parity) { + case SERIAL_PARITY_NONE: return 'N'; + case SERIAL_PARITY_EVEN: return 'E'; + case SERIAL_PARITY_ODD : return 'O'; + + default: + return ' '; + } +} + +serial_stopbit_t serial_get_stopbit(const char *mode) { + if (!mode || !mode[0] || !mode[1]) + return SERIAL_STOPBIT_INVALID; + switch(mode[2]) { + case '1': return SERIAL_STOPBIT_1; + case '2': return SERIAL_STOPBIT_2; + + default: + return SERIAL_STOPBIT_INVALID; + } +} + +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit) { + switch(stopbit) { + case SERIAL_STOPBIT_1: return 1; + case SERIAL_STOPBIT_2: return 2; + + default: + return 0; + } +} + diff --git a/tools/linux64/src/stm32flash_serial/src/serial_platform.c b/tools/linux64/src/stm32flash_serial/src/serial_platform.c new file mode 100644 index 0000000..98e2569 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/serial_platform.c @@ -0,0 +1,5 @@ +#if defined(__WIN32__) || defined(__CYGWIN__) +# include "serial_w32.c" +#else +# include "serial_posix.c" +#endif diff --git a/tools/linux64/src/stm32flash_serial/src/serial_posix.c b/tools/linux64/src/stm32flash_serial/src/serial_posix.c new file mode 100644 index 0000000..284b35b --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/serial_posix.c @@ -0,0 +1,395 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + int fd; + struct termios oldtio; + struct termios newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + + h->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (h->fd < 0) { + free(h); + return NULL; + } + fcntl(h->fd, F_SETFL, 0); + + tcgetattr(h->fd, &h->oldtio); + tcgetattr(h->fd, &h->newtio); + + return h; +} + +static void serial_flush(const serial_t *h) +{ + tcflush(h->fd, TCIFLUSH); +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + tcsetattr(h->fd, TCSANOW, &h->oldtio); + close(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + speed_t port_baud; + tcflag_t port_bits; + tcflag_t port_parity; + tcflag_t port_stop; + struct termios settings; + + switch (baud) { + case SERIAL_BAUD_1200: port_baud = B1200; break; + case SERIAL_BAUD_1800: port_baud = B1800; break; + case SERIAL_BAUD_2400: port_baud = B2400; break; + case SERIAL_BAUD_4800: port_baud = B4800; break; + case SERIAL_BAUD_9600: port_baud = B9600; break; + case SERIAL_BAUD_19200: port_baud = B19200; break; + case SERIAL_BAUD_38400: port_baud = B38400; break; + case SERIAL_BAUD_57600: port_baud = B57600; break; + case SERIAL_BAUD_115200: port_baud = B115200; break; + case SERIAL_BAUD_230400: port_baud = B230400; break; +#ifdef B460800 + case SERIAL_BAUD_460800: port_baud = B460800; break; +#endif /* B460800 */ +#ifdef B921600 + case SERIAL_BAUD_921600: port_baud = B921600; break; +#endif /* B921600 */ +#ifdef B500000 + case SERIAL_BAUD_500000: port_baud = B500000; break; +#endif /* B500000 */ +#ifdef B576000 + case SERIAL_BAUD_576000: port_baud = B576000; break; +#endif /* B576000 */ +#ifdef B1000000 + case SERIAL_BAUD_1000000: port_baud = B1000000; break; +#endif /* B1000000 */ +#ifdef B1500000 + case SERIAL_BAUD_1500000: port_baud = B1500000; break; +#endif /* B1500000 */ +#ifdef B2000000 + case SERIAL_BAUD_2000000: port_baud = B2000000; break; +#endif /* B2000000 */ + + case SERIAL_BAUD_INVALID: + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: port_bits = CS5; break; + case SERIAL_BITS_6: port_bits = CS6; break; + case SERIAL_BITS_7: port_bits = CS7; break; + case SERIAL_BITS_8: port_bits = CS8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: port_parity = 0; break; + case SERIAL_PARITY_EVEN: port_parity = PARENB; break; + case SERIAL_PARITY_ODD: port_parity = PARENB | PARODD; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: port_stop = 0; break; + case SERIAL_STOPBIT_2: port_stop = CSTOPB; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ +#ifndef __sun /* Used by GNU and BSD. Ignore __SVR4 in test. */ + cfmakeraw(&h->newtio); +#else /* __sun */ + h->newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR + | IGNCR | ICRNL | IXON); + if (port_parity) + h->newtio.c_iflag |= INPCK; + + h->newtio.c_oflag &= ~OPOST; + h->newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + h->newtio.c_cflag &= ~(CSIZE | PARENB); + h->newtio.c_cflag |= CS8; +#endif /* __sun */ +#ifdef __QNXNTO__ + h->newtio.c_cflag &= ~(CSIZE | IHFLOW | OHFLOW); +#else + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); +#endif + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); + h->newtio.c_iflag &= ~(IXON | IXOFF | IXANY | IGNPAR); + h->newtio.c_lflag &= ~(ECHOK | ECHOCTL | ECHOKE); + h->newtio.c_oflag &= ~(OPOST | ONLCR); + + /* setup the new settings */ + cfsetispeed(&h->newtio, port_baud); + cfsetospeed(&h->newtio, port_baud); + h->newtio.c_cflag |= + port_parity | + port_bits | + port_stop | + CLOCAL | + CREAD; + + h->newtio.c_cc[VMIN] = 0; + h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ + + /* set the settings */ + serial_flush(h); + if (tcsetattr(h->fd, TCSANOW, &h->newtio) != 0) + return PORT_ERR_UNKNOWN; + +/* this check fails on CDC-ACM devices, bits 16 and 17 of cflag differ! + * it has been disabled below for now -jcw, 2015-11-09 + if (settings.c_cflag != h->newtio.c_cflag) + fprintf(stderr, "c_cflag mismatch %lx\n", + settings.c_cflag ^ h->newtio.c_cflag); + */ + + /* confirm they were set */ + tcgetattr(h->fd, &settings); + if (settings.c_iflag != h->newtio.c_iflag || + settings.c_oflag != h->newtio.c_oflag || + //settings.c_cflag != h->newtio.c_cflag || + settings.c_lflag != h->newtio.c_lflag) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit)); + return PORT_ERR_OK; +} + +/* + * Roger clark. + * This function is no longer used. But has just been commented out in case it needs + * to be reinstated in the future + +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; + + + /* + * Roger Clark + * Commented out this check, because on OSX some devices are /dev/cu + * and some users use symbolic links to devices, hence the name may not even start + * with /dev + + if(startswith(resolved, "/dev/tty")) return 1; + + return 0; + */ + + return 1; +} + +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 */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = read(h->fd, pos, nbyte); + if (r == 0) + return PORT_ERR_TIMEDOUT; + if (r < 0) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + const uint8_t *pos = (const uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = write(h->fd, pos, nbyte); + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit, lines; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = TIOCM_RTS; + break; + + case GPIO_DTR: + bit = TIOCM_DTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (tcsendbreak(h->fd, 1)) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (ioctl(h->fd, TIOCMGET, &lines)) + return PORT_ERR_UNKNOWN; + lines = level ? lines | bit : lines & ~bit; + if (ioctl(h->fd, TIOCMSET, &lines)) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_posix_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_posix", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_posix_open, + .close = serial_posix_close, + .read = serial_posix_read, + .write = serial_posix_write, + .gpio = serial_posix_gpio, + .get_cfg_str = serial_posix_get_cfg_str, +}; diff --git a/tools/linux64/src/stm32flash_serial/src/serial_w32.c b/tools/linux64/src/stm32flash_serial/src/serial_w32.c new file mode 100644 index 0000000..56772c0 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/serial_w32.c @@ -0,0 +1,341 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2010 Gareth McMullin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + HANDLE fd; + DCB oldtio; + DCB newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + char *devName; + + /* timeout in ms */ + COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 500, 0, 0}; + + /* Fix the device name if required */ + if (strlen(device) > 4 && device[0] != '\\') { + devName = calloc(1, strlen(device) + 5); + sprintf(devName, "\\\\.\\%s", device); + } else { + devName = (char *)device; + } + + /* Create file handle for port */ + h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, + 0, /* Exclusive access */ + NULL, /* No security */ + OPEN_EXISTING, + 0, /* No overlap */ + NULL); + + if (devName != device) + free(devName); + + if (h->fd == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + fprintf(stderr, "File not found: %s\n", device); + return NULL; + } + + SetupComm(h->fd, 4096, 4096); /* Set input and output buffer size */ + + SetCommTimeouts(h->fd, &timeouts); + + SetCommMask(h->fd, EV_ERR); /* Notify us of error events */ + + GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */ + GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */ + + /* PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); */ + + return h; +} + +static void serial_flush(const serial_t *h) +{ + /* We shouldn't need to flush in non-overlapping (blocking) mode */ + /* tcflush(h->fd, TCIFLUSH); */ +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + SetCommState(h->fd, &h->oldtio); + CloseHandle(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, + const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + switch (baud) { + case SERIAL_BAUD_1200: h->newtio.BaudRate = CBR_1200; break; + /* case SERIAL_BAUD_1800: h->newtio.BaudRate = CBR_1800; break; */ + case SERIAL_BAUD_2400: h->newtio.BaudRate = CBR_2400; break; + case SERIAL_BAUD_4800: h->newtio.BaudRate = CBR_4800; break; + case SERIAL_BAUD_9600: h->newtio.BaudRate = CBR_9600; break; + case SERIAL_BAUD_19200: h->newtio.BaudRate = CBR_19200; break; + case SERIAL_BAUD_38400: h->newtio.BaudRate = CBR_38400; break; + case SERIAL_BAUD_57600: h->newtio.BaudRate = CBR_57600; break; + case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break; + case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break; + case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break; + /* These are not defined in WinBase.h and might work or not */ + case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break; + case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break; + case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break; + case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break; + case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break; + case SERIAL_BAUD_1000000: h->newtio.BaudRate = 1000000; break; + case SERIAL_BAUD_1500000: h->newtio.BaudRate = 1500000; break; + case SERIAL_BAUD_2000000: h->newtio.BaudRate = 2000000; break; + case SERIAL_BAUD_INVALID: + + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: h->newtio.ByteSize = 5; break; + case SERIAL_BITS_6: h->newtio.ByteSize = 6; break; + case SERIAL_BITS_7: h->newtio.ByteSize = 7; break; + case SERIAL_BITS_8: h->newtio.ByteSize = 8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: h->newtio.Parity = NOPARITY; break; + case SERIAL_PARITY_EVEN: h->newtio.Parity = EVENPARITY; break; + case SERIAL_PARITY_ODD: h->newtio.Parity = ODDPARITY; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: h->newtio.StopBits = ONESTOPBIT; break; + case SERIAL_STOPBIT_2: h->newtio.StopBits = TWOSTOPBITS; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ + h->newtio.fOutxCtsFlow = FALSE; + h->newtio.fOutxDsrFlow = FALSE; + h->newtio.fOutX = FALSE; + h->newtio.fInX = FALSE; + h->newtio.fNull = 0; + h->newtio.fAbortOnError = 0; + + /* set the settings */ + serial_flush(h); + if (!SetCommState(h->fd, &h->newtio)) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit) + ); + return PORT_ERR_OK; +} + +static port_err_t serial_w32_open(struct port_interface *port, + struct port_options *ops) +{ + serial_t *h; + + /* 1. check device name match */ + if (!((strlen(ops->device) == 4 || strlen(ops->device) == 5) + && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) + && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM")) + && isdigit(ops->device[strlen("\\\\.\\COM")]))) + return PORT_ERR_NODEV; + + /* 2. check options */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + 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; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + if (!WriteFile(h->fd, pos, nbyte, &r, NULL)) + return PORT_ERR_UNKNOWN; + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = level ? SETRTS : CLRRTS; + break; + + case GPIO_DTR: + bit = level ? SETDTR : CLRDTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (EscapeCommFunction(h->fd, SETBREAK) == 0) + return PORT_ERR_UNKNOWN; + usleep(500000); + if (EscapeCommFunction(h->fd, CLRBREAK) == 0) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (EscapeCommFunction(h->fd, bit) == 0) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_w32_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_w32", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_w32_open, + .close = serial_w32_close, + .read = serial_w32_read, + .write = serial_w32_write, + .gpio = serial_w32_gpio, + .get_cfg_str = serial_w32_get_cfg_str, +}; diff --git a/tools/linux64/src/stm32flash_serial/src/stm32.c b/tools/linux64/src/stm32flash_serial/src/stm32.c new file mode 100644 index 0000000..74047d2 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/stm32.c @@ -0,0 +1,1048 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright 2010 Geoffrey McRae + Copyright 2012-2014 Tormod Volden + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include "stm32.h" +#include "port.h" +#include "utils.h" + +#define STM32_ACK 0x79 +#define STM32_NACK 0x1F +#define STM32_BUSY 0x76 + +#define STM32_CMD_INIT 0x7F +#define STM32_CMD_GET 0x00 /* get the version and command supported */ +#define STM32_CMD_GVR 0x01 /* get version and read protection status */ +#define STM32_CMD_GID 0x02 /* get ID */ +#define STM32_CMD_RM 0x11 /* read memory */ +#define STM32_CMD_GO 0x21 /* go */ +#define STM32_CMD_WM 0x31 /* write memory */ +#define STM32_CMD_WM_NS 0x32 /* no-stretch write memory */ +#define STM32_CMD_ER 0x43 /* erase */ +#define STM32_CMD_EE 0x44 /* extended erase */ +#define STM32_CMD_EE_NS 0x45 /* extended erase no-stretch */ +#define STM32_CMD_WP 0x63 /* write protect */ +#define STM32_CMD_WP_NS 0x64 /* write protect no-stretch */ +#define STM32_CMD_UW 0x73 /* write unprotect */ +#define STM32_CMD_UW_NS 0x74 /* write unprotect no-stretch */ +#define STM32_CMD_RP 0x82 /* readout protect */ +#define STM32_CMD_RP_NS 0x83 /* readout protect no-stretch */ +#define STM32_CMD_UR 0x92 /* readout unprotect */ +#define STM32_CMD_UR_NS 0x93 /* readout unprotect no-stretch */ +#define STM32_CMD_CRC 0xA1 /* compute CRC */ +#define STM32_CMD_ERR 0xFF /* not a valid command */ + +#define STM32_RESYNC_TIMEOUT 35 /* seconds */ +#define STM32_MASSERASE_TIMEOUT 35 /* seconds */ +#define STM32_SECTERASE_TIMEOUT 5 /* seconds */ +#define STM32_BLKWRITE_TIMEOUT 1 /* seconds */ +#define STM32_WUNPROT_TIMEOUT 1 /* seconds */ +#define STM32_WPROT_TIMEOUT 1 /* seconds */ +#define STM32_RPROT_TIMEOUT 1 /* seconds */ + +#define STM32_CMD_GET_LENGTH 17 /* bytes in the reply */ + +struct stm32_cmd { + uint8_t get; + uint8_t gvr; + uint8_t gid; + uint8_t rm; + uint8_t go; + uint8_t wm; + uint8_t er; /* this may be extended erase */ + uint8_t wp; + uint8_t uw; + uint8_t rp; + uint8_t ur; + uint8_t crc; +}; + +/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0) + * see ARMv7-M or ARMv6-M Architecture Reference Manual (table B3-8) + * or "The definitive guide to the ARM Cortex-M3", section 14.4. + */ +static const uint8_t stm_reset_code[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 = VECTKEY | SYSRESETREQ +}; + +static const uint32_t stm_reset_code_length = sizeof(stm_reset_code); + +extern const stm32_dev_t devices[]; + +static void stm32_warn_stretching(const char *f) +{ + fprintf(stderr, "Attention !!!\n"); + fprintf(stderr, "\tThis %s error could be caused by your I2C\n", f); + fprintf(stderr, "\tcontroller not accepting \"clock stretching\"\n"); + fprintf(stderr, "\tas required by bootloader.\n"); + fprintf(stderr, "\tCheck \"I2C.txt\" in stm32flash source code.\n"); +} + +static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, time_t timeout) +{ + struct port_interface *port = stm->port; + uint8_t byte; + port_err_t p_err; + time_t t0, t1; + + if (!(port->flags & PORT_RETRY)) + timeout = 0; + + if (timeout) + time(&t0); + + do { + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_TIMEDOUT && timeout) { + time(&t1); + if (t1 < t0 + timeout) + continue; + } + + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to read ACK byte\n"); + return STM32_ERR_UNKNOWN; + } + + if (byte == STM32_ACK) + return STM32_ERR_OK; + if (byte == STM32_NACK) + return STM32_ERR_NACK; + if (byte != STM32_BUSY) { + fprintf(stderr, "Got byte 0x%02x instead of ACK\n", + byte); + return STM32_ERR_UNKNOWN; + } + } while (1); +} + +static stm32_err_t stm32_get_ack(const stm32_t *stm) +{ + return stm32_get_ack_timeout(stm, 0); +} + +static stm32_err_t stm32_send_command_timeout(const stm32_t *stm, + const uint8_t cmd, + time_t timeout) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + uint8_t buf[2]; + + buf[0] = cmd; + buf[1] = cmd ^ 0xFF; + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send command\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, timeout); + if (s_err == STM32_ERR_OK) + return STM32_ERR_OK; + if (s_err == STM32_ERR_NACK) + fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd); + else + fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd); + return STM32_ERR_UNKNOWN; +} + +static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) +{ + return stm32_send_command_timeout(stm, cmd, 0); +} + +/* if we have lost sync, send a wrong command and expect a NACK */ +static stm32_err_t stm32_resync(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t buf[2], ack; + time_t t0, t1; + + time(&t0); + t1 = t0; + + buf[0] = STM32_CMD_ERR; + buf[1] = STM32_CMD_ERR ^ 0xFF; + while (t1 < t0 + STM32_RESYNC_TIMEOUT) { + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + usleep(500000); + time(&t1); + continue; + } + p_err = port->read(port, &ack, 1); + if (p_err != PORT_ERR_OK) { + time(&t1); + continue; + } + if (ack == STM32_NACK) + return STM32_ERR_OK; + time(&t1); + } + return STM32_ERR_UNKNOWN; +} + +/* + * some command receive reply frame with variable length, and length is + * embedded in reply frame itself. + * We can guess the length, but if we guess wrong the protocol gets out + * of sync. + * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte + * read for byte oriented interfaces (e.g. UART). + * + * to run safely, data buffer should be allocated for 256+1 bytes + * + * len is value of the first byte in the frame. + */ +static stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, uint8_t cmd, + uint8_t *data, unsigned int len) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (port->flags & PORT_BYTE) { + /* interface is UART-like */ + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + len = data[0]; + p_err = port->read(port, data + 1, len + 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; + } + + p_err = port->read(port, data, len + 2); + if (p_err == PORT_ERR_OK && len == data[0]) + return STM32_ERR_OK; + if (p_err != PORT_ERR_OK) { + /* restart with only one byte */ + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + } + + fprintf(stderr, "Re sync (len = %d)\n", data[0]); + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + len = data[0]; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, len + 2); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +/* + * Some interface, e.g. UART, requires a specific init sequence to let STM32 + * autodetect the interface speed. + * The sequence is only required one time after reset. + * stm32flash has command line flag "-c" to prevent sending the init sequence + * in case it was already sent before. + * User can easily forget adding "-c". In this case the bootloader would + * interpret the init sequence as part of a command message, then waiting for + * the rest of the message blocking the interface. + * This function sends the init sequence and, in case of timeout, recovers + * the interface. + */ +static stm32_err_t stm32_send_init_seq(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t byte, cmd = STM32_CMD_INIT; + + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_ACK) + return STM32_ERR_OK; + if (p_err == PORT_ERR_OK && byte == STM32_NACK) { + /* We could get error later, but let's continue, for now. */ + fprintf(stderr, + "Warning: the interface was not closed properly.\n"); + return STM32_ERR_OK; + } + if (p_err != PORT_ERR_TIMEDOUT) { + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; + } + + /* + * Check if previous STM32_CMD_INIT was taken as first byte + * of a command. Send a new byte, we should get back a NACK. + */ + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_NACK) + return STM32_ERR_OK; + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; +} + +/* find newer command by higher code */ +#define newer(prev, a) (((prev) == STM32_CMD_ERR) \ + ? (a) \ + : (((prev) > (a)) ? (prev) : (a))) + +stm32_t *stm32_init(struct port_interface *port, const char init) +{ + uint8_t len, val, buf[257]; + stm32_t *stm; + int i, new_cmds; + + stm = calloc(sizeof(stm32_t), 1); + stm->cmd = malloc(sizeof(stm32_cmd_t)); + memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); + stm->port = port; + + if ((port->flags & PORT_CMD_INIT) && init) + if (stm32_send_init_seq(stm) != STM32_ERR_OK) + return NULL; + + /* get the version and read protection status */ + if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* From AN, only UART bootloader returns 3 bytes */ + len = (port->flags & PORT_GVR_ETX) ? 3 : 1; + if (port->read(port, buf, len) != PORT_ERR_OK) + return NULL; + stm->version = buf[0]; + stm->option1 = (port->flags & PORT_GVR_ETX) ? buf[1] : 0; + stm->option2 = (port->flags & PORT_GVR_ETX) ? buf[2] : 0; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* get the bootloader information */ + len = STM32_CMD_GET_LENGTH; + if (port->cmd_get_reply) + for (i = 0; port->cmd_get_reply[i].length; i++) + if (stm->version == port->cmd_get_reply[i].version) { + len = port->cmd_get_reply[i].length; + break; + } + if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return NULL; + len = buf[0] + 1; + stm->bl_version = buf[1]; + new_cmds = 0; + for (i = 1; i < len; i++) { + val = buf[i + 1]; + switch (val) { + case STM32_CMD_GET: + stm->cmd->get = val; break; + case STM32_CMD_GVR: + stm->cmd->gvr = val; break; + case STM32_CMD_GID: + stm->cmd->gid = val; break; + case STM32_CMD_RM: + stm->cmd->rm = val; break; + case STM32_CMD_GO: + stm->cmd->go = val; break; + case STM32_CMD_WM: + case STM32_CMD_WM_NS: + stm->cmd->wm = newer(stm->cmd->wm, val); + break; + case STM32_CMD_ER: + case STM32_CMD_EE: + case STM32_CMD_EE_NS: + stm->cmd->er = newer(stm->cmd->er, val); + break; + case STM32_CMD_WP: + case STM32_CMD_WP_NS: + stm->cmd->wp = newer(stm->cmd->wp, val); + break; + case STM32_CMD_UW: + case STM32_CMD_UW_NS: + stm->cmd->uw = newer(stm->cmd->uw, val); + break; + case STM32_CMD_RP: + case STM32_CMD_RP_NS: + stm->cmd->rp = newer(stm->cmd->rp, val); + break; + case STM32_CMD_UR: + case STM32_CMD_UR_NS: + stm->cmd->ur = newer(stm->cmd->ur, val); + break; + case STM32_CMD_CRC: + stm->cmd->crc = newer(stm->cmd->crc, val); + break; + default: + if (new_cmds++ == 0) + fprintf(stderr, + "GET returns unknown commands (0x%2x", + val); + else + fprintf(stderr, ", 0x%2x", val); + } + } + if (new_cmds) + fprintf(stderr, ")\n"); + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + if (stm->cmd->get == STM32_CMD_ERR + || stm->cmd->gvr == STM32_CMD_ERR + || stm->cmd->gid == STM32_CMD_ERR) { + fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n"); + return NULL; + } + + /* get the device ID */ + if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + len = buf[0] + 1; + if (len < 2) { + stm32_close(stm); + fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len); + return NULL; + } + stm->pid = (buf[1] << 8) | buf[2]; + if (len > 2) { + fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len); + for (i = 2; i <= len ; i++) + fprintf(stderr, " %02x", buf[i]); + fprintf(stderr, "\n"); + } + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + stm->dev = devices; + while (stm->dev->id != 0x00 && stm->dev->id != stm->pid) + ++stm->dev; + + if (!stm->dev->id) { + fprintf(stderr, "Unknown/unsupported device (Device ID: 0x%03x)\n", stm->pid); + stm32_close(stm); + return NULL; + } + + return stm; +} + +void stm32_close(stm32_t *stm) +{ + if (stm) + free(stm->cmd); + free(stm); +} + +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->rm == STM32_CMD_ERR) { + fprintf(stderr, "Error: READ command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_send_command(stm, len - 1) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, data, len) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + return STM32_ERR_OK; +} + +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t cs, buf[256 + 2]; + unsigned int i, aligned_len; + stm32_err_t s_err; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + /* must be 32bit aligned */ + if (address & 0x3 || len & 0x3) { + fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->wm == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + /* send the address and checksum */ + if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + aligned_len = (len + 3) & ~3; + cs = aligned_len - 1; + buf[0] = aligned_len - 1; + for (i = 0; i < len; i++) { + cs ^= data[i]; + buf[i + 1] = data[i]; + } + /* padding data */ + for (i = len; i < aligned_len; i++) { + cs ^= 0xFF; + buf[i + 1] = 0xFF; + } + buf[aligned_len + 1] = cs; + if (port->write(port, buf, aligned_len + 2) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wm != STM32_CMD_WM_NS) + stm32_warn_stretching("write"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wunprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->uw == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->uw != STM32_CMD_UW_NS) + stm32_warn_stretching("WRITE UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->wp == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wp != STM32_CMD_WP_NS) + stm32_warn_stretching("WRITE PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_runprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->ur == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->ur != STM32_CMD_UR_NS) + stm32_warn_stretching("READOUT UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_readprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->rp == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->rp != STM32_CMD_RP_NS) + stm32_warn_stretching("READOUT PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + + if (!pages) + return 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 (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + fprintf(stderr, "Can't initiate chip 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) { + s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } else { + uint8_t cs = 0; + uint8_t pg_num; + uint8_t *buf; + int i = 0; + + buf = malloc(1 + pages + 1); + if (!buf) + return STM32_ERR_UNKNOWN; + + buf[i++] = pages - 1; + cs ^= (pages-1); + for (pg_num = spage; pg_num < (pages + spage); pg_num++) { + buf[i++] = pg_num; + cs ^= pg_num; + } + buf[i++] = cs; + p_err = port->write(port, buf, i); + free(buf); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Erase failed.\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } +} + +static stm32_err_t stm32_run_raw_code(const stm32_t *stm, + uint32_t target_address, + const uint8_t *code, uint32_t code_size) +{ + uint32_t stack_le = le_u32(0x20002000); + uint32_t code_address_le = le_u32(target_address + 8); + uint32_t length = code_size + 8; + uint8_t *mem, *pos; + uint32_t address, w; + + /* Must be 32-bit aligned */ + if (target_address & 0x3) { + fprintf(stderr, "Error: code address must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + mem = malloc(length); + if (!mem) + return STM32_ERR_UNKNOWN; + + memcpy(mem, &stack_le, sizeof(uint32_t)); + memcpy(mem + 4, &code_address_le, sizeof(uint32_t)); + memcpy(mem + 8, code, code_size); + + pos = mem; + address = target_address; + while (length > 0) { + w = length > 256 ? 256 : length; + if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) { + free(mem); + return STM32_ERR_UNKNOWN; + } + + address += w; + pos += w; + length -= w; + } + + free(mem); + return stm32_go(stm, target_address); +} + +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (stm->cmd->go == STM32_CMD_ERR) { + fprintf(stderr, "Error: GO command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +stm32_err_t stm32_reset_device(const stm32_t *stm) +{ + uint32_t target_address = stm->dev->ram_start; + + return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length); +} + +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc == STM32_CMD_ERR) { + fprintf(stderr, "Error: CRC command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = length >> 24; + buf[1] = (length >> 16) & 0xFF; + buf[2] = (length >> 8) & 0xFF; + buf[3] = length & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3])) + return STM32_ERR_UNKNOWN; + + *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return STM32_ERR_OK; +} + +/* + * CRC computed by STM32 is similar to the standard crc32_be() + * implemented, for example, in Linux kernel in ./lib/crc32.c + * But STM32 computes it on units of 32 bits word and swaps the + * bytes of the word before the computation. + * Due to byte swap, I cannot use any CRC available in existing + * libraries, so here is a simple not optimized implementation. + */ +#define CRCPOLY_BE 0x04c11db7 +#define CRC_MSBMASK 0x80000000 +#define CRC_INIT_VALUE 0xFFFFFFFF +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) +{ + int i; + uint32_t data; + + if (len & 0x3) { + fprintf(stderr, "Buffer length must be multiple of 4 bytes\n"); + return 0; + } + + while (len) { + data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + len -= 4; + + crc ^= data; + + for (i = 0; i < 32; i++) + if (crc & CRC_MSBMASK) + crc = (crc << 1) ^ CRCPOLY_BE; + else + crc = (crc << 1); + } + return crc; +} + +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + uint8_t buf[256]; + uint32_t start, total_len, len, current_crc; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc != STM32_CMD_ERR) + return stm32_crc_memory(stm, address, length, crc); + + start = address; + total_len = length; + current_crc = CRC_INIT_VALUE; + while (length) { + len = length > 256 ? 256 : length; + if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) { + fprintf(stderr, + "Failed to read memory at address 0x%08x, target write-protected?\n", + address); + return STM32_ERR_UNKNOWN; + } + current_crc = stm32_sw_crc(current_crc, buf, len); + length -= len; + address += len; + + fprintf(stderr, + "\rCRC address 0x%08x (%.2f%%) ", + address, + (100.0f / (float)total_len) * (float)(address - start) + ); + fflush(stderr); + } + fprintf(stderr, "Done.\n"); + *crc = current_crc; + return STM32_ERR_OK; +} diff --git a/tools/linux64/src/stm32flash_serial/src/stm32.h b/tools/linux64/src/stm32flash_serial/src/stm32.h new file mode 100644 index 0000000..1688fcb --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/stm32.h @@ -0,0 +1,84 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _STM32_H +#define _STM32_H + +#include +#include "serial.h" + +#define STM32_MAX_RX_FRAME 256 /* cmd read memory */ +#define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */ + +typedef enum { + STM32_ERR_OK = 0, + STM32_ERR_UNKNOWN, /* Generic error */ + STM32_ERR_NACK, + STM32_ERR_NO_CMD, /* Command not available in bootloader */ +} stm32_err_t; + +typedef struct stm32 stm32_t; +typedef struct stm32_cmd stm32_cmd_t; +typedef struct stm32_dev stm32_dev_t; + +struct stm32 { + const serial_t *serial; + struct port_interface *port; + uint8_t bl_version; + uint8_t version; + uint8_t option1, option2; + uint16_t pid; + stm32_cmd_t *cmd; + const stm32_dev_t *dev; +}; + +struct stm32_dev { + uint16_t id; + const char *name; + 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 opt_start, opt_end; + uint32_t mem_start, mem_end; +}; + +stm32_t *stm32_init(struct port_interface *port, const char init); +void stm32_close(stm32_t *stm); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_t *stm); +stm32_err_t stm32_wprot_memory(const stm32_t *stm); +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, + uint8_t pages); +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_t *stm); +stm32_err_t stm32_readprot_memory(const stm32_t *stm); +stm32_err_t stm32_runprot_memory(const stm32_t *stm); +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); + +#endif + diff --git a/tools/linux64/src/stm32flash_serial/src/stm32flash.1 b/tools/linux64/src/stm32flash_serial/src/stm32flash.1 new file mode 100644 index 0000000..d37292f --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/stm32flash.1 @@ -0,0 +1,407 @@ +.TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command" +.SH NAME +stm32flash \- flashing utility for STM32 and STM32W through UART or I2C +.SH SYNOPSIS +.B stm32flash +.RB [ \-cfhjkouvCR ] +.RB [ \-a +.IR bus_address ] +.RB [ \-b +.IR baud_rate ] +.RB [ \-m +.IR serial_mode ] +.RB [ \-r +.IR filename ] +.RB [ \-w +.IR filename ] +.RB [ \-e +.IR num ] +.RB [ \-n +.IR count ] +.RB [ \-g +.IR address ] +.RB [ \-s +.IR start_page ] +.RB [ \-S +.IR address [: length ]] +.RB [ \-F +.IR RX_length [: TX_length ]] +.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. + +It requires the STM32[W] to embed a bootloader compliant with ST +application note AN3155. +.B stm32flash +uses the serial port +.I tty_device +to interact with the bootloader of STM32[W]. + +.SH OPTIONS +.TP +.BI "\-a" " bus_address" +Specify address on bus for +.IR i2c_device . +This option is mandatory for I2C interface. + +.TP +.BI "\-b" " baud_rate" +Specify baud rate speed of +.IR tty_device . +Please notice that the ST bootloader can automatically detect the baud rate, +as explaned in chapter 2 of AN3155. +This option could be required together with option +.B "\-c" +or if following interaction with bootloader is expected. +Default is +.IR 57600 . + +.TP +.BI "\-m" " mode" +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 +.I 8e1 +for standard STM32 bootloader and +.I 8n1 +for standard STM32W bootloader. +Default is +.IR 8e1 . + +.TP +.BI "\-r" " filename" +Specify to read the STM32[W] 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 +.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 +.B \-f +option. + +.TP +.B \-u +Specify to disable write\-protection from STM32[W] flash. +The STM32[W] will be reset after this operation. + +.TP +.B \-j +Enable the flash read\-protection. + +.TP +.B \-k +Disable the flash read\-protection. + +.TP +.B \-o +Erase only. + +.TP +.BI "\-e" " num" +Specify to erase only +.I num +pages before writing the flash. Default is to erase the whole flash. With +.B \-e 0 +the flash would not be erased. + +.TP +.B \-v +Specify to verify flash content after write operation. + +.TP +.BI "\-n" " count" +Specify to retry failed writes up to +.I count +times. Default is 10 times. + +.TP +.BI "\-g" " address" +Specify address to start execution from (0 = flash start). + +.TP +.BI "\-s" " start_page" +Specify flash page offset (0 = flash start). + +.TP +.BI "\-S" " address" "[:" "length" "]" +Specify start address and optionally length for read/write/erase/crc operations. + +.TP +.BI "\-F" " RX_length" "[:" "TX_length" "]" +Specify the maximum frame size for the current interface. +Due to STM32 bootloader protocol, host will never handle frames bigger than +256 byte in RX or 258 byte in TX. +Due to current code, lowest limit in RX is 20 byte (to read a complete reply +of command GET). Minimum limit in TX is 5 byte, required by protocol. + +.TP +.B \-f +Force binary parser while reading file with +.BR "\-w" "." + +.TP +.B \-h +Show help. + +.TP +.B \-c +Specify to resume the existing UART connection and don't send initial +INIT sequence to detect baud rate. Baud rate must be kept the same as the +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 +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 +GPIO. (See below +.B BOOTLOADER GPIO SEQUENCE +for the format of +.I GPIO_string +and further explanation). + +.TP +.B \-C +Specify to compute CRC on memory content. +By default the CRC is computed on the whole flash content. +Use +.B "\-S" +to provide different memory address range. + +.TP +.B \-R +Specify to reset the device at exit. +This option is ignored if either +.BR "\-g" "," +.BR "\-j" "," +.B "\-k" +or +.B "\-u" +is also specified. + +.SH BOOTLOADER GPIO SEQUENCE +This feature is currently available on Linux host only. + +As explained in ST application note AN2606, after reset the STM32 will +execute either the application program in user flash or the bootloader, +depending on the level applied at specific pins of STM32 during reset. + +STM32 bootloader is automatically activated by configuring the pins +BOOT0="high" and BOOT1="low" and then by applying a reset. +Application program in user flash is activated by configuring the pin +BOOT0="low" (the level on BOOT1 is ignored) and then by applying a reset. + +When GPIO from host computer are connected to either configuration and +reset pins of STM32, +.B stm32flash +can control the host GPIO to reset STM32 and to force execution of +bootloader or execution of application program. + +The sequence of GPIO values to entry to and exit from bootloader mode is +provided with command line option +.B "\-i" +.IR "GPIO_string" . + +.PD 0 +The format of +.IR "GPIO_string" " is:" +.RS +GPIO_string = [entry sequence][:[exit sequence]] +.P +sequence = [\-]n[,sequence] +.RE +.P +In the above sequences, negative numbers correspond to GPIO at "low" level; +numbers without sign correspond to GPIO at "high" level. +The value "n" can either be the GPIO number on the host system or the +string "rts", "dtr" or "brk". The strings "rts" and "dtr" drive the +corresponding UART's modem lines RTS and DTR as GPIO. +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 + +.PD 0 +As example, let's suppose the following connection between host and STM32: +.IP \(bu 2 +host GPIO_3 connected to reset pin of STM32; +.IP \(bu 2 +host GPIO_4 connected to STM32 pin BOOT0; +.IP \(bu 2 +host GPIO_5 connected to STM32 pin BOOT1. +.PD +.P + +In this case, the sequence to enter in bootloader mode is: first put +GPIO_4="high" and GPIO_5="low"; then send reset pulse by GPIO_3="low" +followed by GPIO_3="high". +The corresponding string for +.I GPIO_string +is "4,\-5,\-3,3". + +To exit from bootloade and run the application program, the sequence is: +put GPIO_4="low"; then send reset pulse. +The corresponding string for +.I GPIO_string +is "\-4,\-3,3". + +The complete command line flag is "\-i 4,\-5,\-3,3:\-4,\-3,3". + +STM32W uses pad PA5 to select boot mode; if during reset PA5 is "low" then +STM32W will enter in bootloader mode; if PA5 is "high" it will execute the +program in flash. + +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 +.RE +provides: +.IP \(bu 2 +entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high +.IP \(bu 2 +exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high +.PD + +.SH EXAMPLES +Get device information: +.RS +.PD 0 +.P +stm32flash /dev/ttyS0 +.PD +.RE + +Write with verify and then start execution: +.RS +.PD 0 +.P +stm32flash \-w filename \-v \-g 0x0 /dev/ttyS0 +.PD +.RE + +Read flash to file: +.RS +.PD 0 +.P +stm32flash \-r filename /dev/ttyS0 +.PD +.RE + +Start execution: +.RS +.PD 0 +.P +stm32flash \-g 0x0 /dev/ttyS0 +.PD +.RE + +Specify: +.PD 0 +.IP \(bu 2 +entry sequence: RTS=low, DTR=low, DTR=high +.IP \(bu 2 +exit sequence: RTS=high, DTR=low, DTR=high +.P +.RS +stm32flash \-i \-rts,\-dtr,dtr:rts,\-dtr,dtr /dev/ttyS0 +.PD +.RE + +.SH FORMAT CONVERSION +Flash images provided by ST or created with ST tools are often in file +format Motorola S\-Record. +Conversion between raw binary, intel hex and Motorola S\-Record can be +done through software package SRecord. + +.SH AUTHORS +The original software package +.B stm32flash +is written by +.I Geoffrey McRae +and is since 2012 maintained by +.IR "Tormod Volden " . + +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 . + +.SH SEE ALSO +.BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)." + +The communication protocol used by ST bootloader is documented in +following ST application notes, depending on communication port. +The current version of +.B stm32flash +only supports +.I UART +and +.I I2C +ports. +.PD 0 +.P +.IP \(bu 2 +AN3154: CAN protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf +.RE + +.P +.IP \(bu 2 +AN3155: USART protocol used in the STM32(TM) bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf +.RE + +.P +.IP \(bu 2 +AN4221: I2C protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf +.RE + +.P +.IP \(bu 2 +AN4286: SPI protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf +.RE + +.PD + + +Boot mode selection for STM32 is documented in ST application note +AN2606, available from the ST website: +.PD 0 +.P +http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf +.PD + +.SH LICENSE +.B stm32flash +is distributed under GNU GENERAL PUBLIC LICENSE Version 2. +Copy of the license is available within the source code in the file +.IR "gpl\-2.0.txt" . diff --git a/tools/linux64/src/stm32flash_serial/src/utils.c b/tools/linux64/src/stm32flash_serial/src/utils.c new file mode 100644 index 0000000..271bb3e --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/utils.c @@ -0,0 +1,45 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "utils.h" + +/* detect CPU endian */ +char cpu_le() { + const uint32_t cpu_le_test = 0x12345678; + return ((const unsigned char*)&cpu_le_test)[0] == 0x78; +} + +uint32_t be_u32(const uint32_t v) { + if (cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} + +uint32_t le_u32(const uint32_t v) { + if (!cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} diff --git a/tools/linux64/src/stm32flash_serial/src/utils.h b/tools/linux64/src/stm32flash_serial/src/utils.h new file mode 100644 index 0000000..a8d37d2 --- /dev/null +++ b/tools/linux64/src/stm32flash_serial/src/utils.h @@ -0,0 +1,30 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_UTILS +#define _H_UTILS + +#include + +char cpu_le(); +uint32_t be_u32(const uint32_t v); +uint32_t le_u32(const uint32_t v); + +#endif diff --git a/tools/linux64/src/upload-reset/upload-reset.c b/tools/linux64/src/upload-reset/upload-reset.c new file mode 100644 index 0000000..1d03bff --- /dev/null +++ b/tools/linux64/src/upload-reset/upload-reset.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2015 Roger Clark + * + * 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. + * + * + * Utility to send the reset sequence on RTS and DTR and chars + * which resets the libmaple and causes the bootloader to be run + * + * + * + * Terminal control code by Heiko Noordhof (see copyright below) + */ + + + +/* Copyright (C) 2003 Heiko Noordhof + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function prototypes (belong in a seperate header file) */ +int openserial(char *devicename); +void closeserial(void); +int setDTR(unsigned short level); +int setRTS(unsigned short level); + + +/* Two globals for use by this module only */ +static int fd; +static struct termios oldterminfo; + + +void closeserial(void) +{ + tcsetattr(fd, TCSANOW, &oldterminfo); + close(fd); +} + + +int openserial(char *devicename) +{ + struct termios attr; + + if ((fd = open(devicename, O_RDWR)) == -1) return 0; /* Error */ + atexit(closeserial); + + if (tcgetattr(fd, &oldterminfo) == -1) return 0; /* Error */ + attr = oldterminfo; + attr.c_cflag |= CRTSCTS | CLOCAL; + attr.c_oflag = 0; + if (tcflush(fd, TCIOFLUSH) == -1) return 0; /* Error */ + if (tcsetattr(fd, TCSANOW, &attr) == -1) return 0; /* Error */ + + /* Set the lines to a known state, and */ + /* finally return non-zero is successful. */ + return setRTS(0) && setDTR(0); +} + + +/* For the two functions below: + * level=0 to set line to LOW + * level=1 to set line to HIGH + */ + +int setRTS(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setRTS(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_RTS; + else status &= ~TIOCM_RTS; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setRTS(): TIOCMSET"); + return 0; + } + return 1; +} + + +int setDTR(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setDTR(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_DTR; + else status &= ~TIOCM_DTR; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setDTR: TIOCMSET"); + return 0; + } + return 1; +} + +/* This portion of code was written by Roger Clark + * It was informed by various other pieces of code written by Leaflabs to reset their + * Maple and Maple mini boards + */ + +main(int argc, char *argv[]) +{ + + if (argc<2 || argc >3) + { + printf("Usage upload-reset \n\r"); + return; + } + + if (openserial(argv[1])) + { + // Send magic sequence of DTR and RTS followed by the magic word "1EAF" + setRTS(false); + setDTR(false); + setDTR(true); + + usleep(50000L); + + setDTR(false); + setRTS(true); + setDTR(true); + + usleep(50000L); + + setDTR(false); + + usleep(50000L); + + write(fd,"1EAF",4); + + closeserial(); + if (argc==3) + { + usleep(atol(argv[2])*1000L); + } + } + else + { + printf("Failed to open serial device.\n\r"); + } +} diff --git a/tools/linux64/stlink/st-flash b/tools/linux64/stlink/st-flash new file mode 100644 index 0000000000000000000000000000000000000000..951b11a380223872b91654ac356b4817609d8abd GIT binary patch literal 172752 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

Cci_SUHqyCIqAu|&uMFb6iRl@C2cy<7zO5er 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 B0nh*d literal 0 HcmV?d00001 diff --git a/tools/linux64/stlink/st-term b/tools/linux64/stlink/st-term new file mode 100644 index 0000000000000000000000000000000000000000..b5425db23102ecf8028fbc49083b050a9f233239 GIT binary patch literal 143235 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

4hH{?9m>BO{}IvjYi-R{nKY?#xbZ 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 literal 0 HcmV?d00001 diff --git a/tools/linux64/stlink/st-util b/tools/linux64/stlink/st-util new file mode 100644 index 0000000000000000000000000000000000000000..c6a007864524e3b0098b887556aa031d106814f8 GIT binary patch literal 231592 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

tzL{Xxi?xj8%z64hQ&ulygy$LRb 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 literal 0 HcmV?d00001 diff --git a/tools/linux64/stlink_upload b/tools/linux64/stlink_upload new file mode 100644 index 0000000..24e528d --- /dev/null +++ b/tools/linux64/stlink_upload @@ -0,0 +1,45 @@ +#!/bin/bash + +# Check for leaf device. +function leaf_status() +{ + +this_leaf_status=$(lsusb |grep "1eaf" | awk '{ print $NF}') +# Find the mode of the leaf bootloader +case $this_leaf_status in + "1eaf:0003") + echo "dfu" + ;; + "1eaf:0004") + echo "ttyACMx" + ;; + *) + #echo "$this_leaf_status" + echo "unknown" + ;; +esac +} + +# You will need the usb-reset code, see https://github.com/rogerclarkmelbourne/Arduino_STM32/wiki/Using-a-generic-stm32-board-on-linux-with-Maple-bootloader +# +USBRESET=$(which usb-reset) || USBRESET="./usb-reset" + +# Check to see if a maple compatible board is attached +LEAF_STATUS=$(leaf_status) +echo "USB Status [$LEAF_STATUS]" + +$(dirname $0)/stlink/st-flash write "$4" 0x8000000 + +sleep 4 +# Reset the usb device to bring up the tty rather than DFU +"$USBRESET" "/dev/bus/usb/$(lsusb |grep "1eaf" |awk '{print $2,$4}'|sed 's/\://g'|sed 's/ /\//g')" >/dev/null 2>&1 +# Check to see if a maple compatible board is attached +LEAF_STATUS=$(leaf_status) +echo "USB Status [$LEAF_STATUS]" +# Check to see if the tty came up +TTY_DEV=$(find /dev -cmin -2 |grep ttyAC) +echo -e "Waiting for tty device $TTY_DEV \n" +sleep 20 +echo -e "$TTY_DEV should now be available.\n" +exit 0 + diff --git a/tools/linux64/stm32flash/stm32flash b/tools/linux64/stm32flash/stm32flash new file mode 100644 index 0000000000000000000000000000000000000000..16872bba61573f09112823df05c9f4bc61d2dabd GIT binary patch literal 88981 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 B!><4U literal 0 HcmV?d00001 diff --git a/tools/linux64/upload-reset b/tools/linux64/upload-reset new file mode 100644 index 0000000000000000000000000000000000000000..26985b857674d0c8d4999d53999a80f988591b96 GIT binary patch literal 7946 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 I$I+hrKXKN%tN;K2 literal 0 HcmV?d00001 diff --git a/tools/linux64/upload_router b/tools/linux64/upload_router new file mode 100644 index 0000000..b74244d --- /dev/null +++ b/tools/linux64/upload_router @@ -0,0 +1,112 @@ +#!/bin/bash +# Translates the windows Arduino IDE upload call - something like.. +# +# upload_router ttyUSB0 1 1EAF:0003 /tmp/build9114565021046468906.tmp/STM32_Blink.cpp maple_dfu 0 +# +# to the linux dfu-util equivalent of the form... +# +# dfu-util -D ./STM32_Blink.cpp.bin -d 1eaf:0003 --intf 0 --alt 1 +# +# + +function leaf_status() +{ + +this_leaf_status=$(lsusb |grep "1eaf" | awk '{ print $NF}') +# Find the mode of the leaf bootloader +case $this_leaf_status in + "1eaf:0003") + echo "dfu" + ;; + "1eaf:0004") + echo "ttyACMx" + ;; + *) + #echo "$this_leaf_status" + echo "unknown" + ;; +esac +} + + +DEVICE="$3" +# Lowercase the 1eaf device name, since in Windows land everybody shouts. +DEVICE=${DEVICE,,} + +BINFILE="$4.bin" +INTERFACE="$6" +ALT_INTERFACE="$2" + +# You will need the usb-reset code, see https://github.com/rogerclarkmelbourne/Arduino_STM32/wiki/Using-a-generic-stm32-board-on-linux-with-Maple-bootloader +# +USBRESET=$(which usb-reset) || USBRESET="./usb-reset" + +# Check to see if a maple compatible board is attached +LEAF_STATUS=$(leaf_status) + +# Borard not found, or no boot loader on board. +if [[ $(leaf_status) = "unknown" ]] +then + echo "STM32 Maple Bootloader compatible board not found." + sleep 5 + exit 1 +fi + +# We got this far, so we need to get the board in bootloader mode. +# After the timeout period, the board goes back in to serial mode, we need it in dfu mode, which happens for the first few seconds at power on +# so we ask the user to unplug and re-plug the board. +echo -e "\n\rSTM32 Maple board is in $LEAF_STATUS mode." + +echo "Please unplug and replug the USB cable to the Maple device." +sleep 2 +# On unplugging the board will be "unknown" +while [[ $(leaf_status) != "unknown" ]] +do + echo -n "." + sleep 1 +done +# On re-plugging the board will be "dfu" +while [[ $(leaf_status) != "dfu" ]] +do + echo -n "." + sleep 1 +done + +echo -e "\n\rProgramming STM32 device with dfu-util" +until dfu-util -D "$BINFILE" -d "$DEVICE" --intf "$INTERFACE" --alt "$ALT_INTERFACE" 2>&1 +do + echo -n "." + sleep 1 +done + +echo -e "\n\rUnplug and replug the USB cable to the STM32 board again please...." +while [[ $(leaf_status) != "unknown" ]] +do + echo -n "." + sleep 1 +done + +echo -e "\n\rReconnecting" +while [[ $(leaf_status) = "unknown" ]] +do + echo -n "." + sleep 1 +done + +echo -e "\n\rWaiting for bootloader to exit." +for i in {1..6} +do + echo -n "." + sleep 1 +done + +"$USBRESET" "/dev/bus/usb/$(lsusb |grep "1eaf" |awk '{print $2,$4}'|sed 's/\://g'|sed 's/ /\//g')" >/dev/null 2>&1 + +while [[ $(leaf_status) = "unknown" ]] +do + echo -n "." + sleep 1 +done +THIS_TTY=$(find /dev/ttyACM* -cmin -2) +echo -e "\n\rSTM32 Maple board serial port re-created..." +echo -e "\n\rSerial port is $THIS_TTY Please allow 15 seconds before attempting to read from serial port." diff --git a/tools/macosx/src/build_dfu-util.sh b/tools/macosx/src/build_dfu-util.sh new file mode 100644 index 0000000..3563f57 --- /dev/null +++ b/tools/macosx/src/build_dfu-util.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo apt-get build-dep dfu-util +sudo apt-get install build-essentials +sudo apt-get install libusb-1.0-0-dev +sudo apt-get install autoconf automake autotools-dev + +cd dfu-util +./autogen.sh +./configure +make +cp src/dfu-util ../../linux/dfu-util +cp src/dfu-suffix ../../linux/dfu-util +cp src/dfu-prefix ../../linux/dfu-util + diff --git a/tools/macosx/src/dfu-util/AUTHORS b/tools/macosx/src/dfu-util/AUTHORS new file mode 100644 index 0000000..1b36c73 --- /dev/null +++ b/tools/macosx/src/dfu-util/AUTHORS @@ -0,0 +1,30 @@ +Authors ordered by first contribution. + +Harald Welte +Werner Almesberger +Michael Lauer +Jim Huang +Stefan Schmidt +Daniel Willmann +Mike Frysinger +Uwe Hermann +C. Scott Ananian +Bernard Blackham +Holger Freyther +Marc Singer +James Perkins +Tommi Keisala +Pascal Schweizer +Bradley Scott +Uwe Bonnes +Andrey Smirnov +Jussi Timperi +Hans Petter Selasky +Bo Shen +Henrique de Almeida Mendonca +Bernd Krumboeck +Dennis Meier +Veli-Pekka Peltola +Dave Hylands +Michael Grzeschik +Paul Fertser diff --git a/tools/macosx/src/dfu-util/COPYING b/tools/macosx/src/dfu-util/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/tools/macosx/src/dfu-util/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/tools/macosx/src/dfu-util/ChangeLog b/tools/macosx/src/dfu-util/ChangeLog new file mode 100644 index 0000000..37f1add --- /dev/null +++ b/tools/macosx/src/dfu-util/ChangeLog @@ -0,0 +1,93 @@ +0.8: + o New, separate dfu-prefix tool (Uwe Bonnes) + o Allow filtering on serial number (Uwe Bonnes) + o Improved VID/PID/serial filtering (Bradley Scott) + o Support reading firmware from stdin (Tormod Volden) + o Warn if missing DFU suffix (Tormod Volden) + o Improved progress bar (Hans Petter Selasky) + o Fix dfuse leave option (Uwe Bonnes) + o Major code rework (Hans Petter Selasky) + o MS Visual Studio build support (Henrique Mendonca) + o dfuse-pack.py tool for .dfu files (Antonio Galeo) + o Many other fixes from many people + +2014-09-13: Tormod Volden + +0.7: + o Support for TI Stellaris devices (Tommi Keisala) + o Fix libusb detection on MacOSX (Marc Singer) + o Fix libusb detection on FreeBSD (Tormod Volden) + o Improved DfuSe support (Tormod Volden) + o Support all special commands (leave, unprotect, mass-erase) + o Arbitrary upload lengths + o "force" option for various possible (dangerous) overrides + +2012-10-07: Tormod Volden + +0.6: + o Add detach mode (Stefan Schmidt) + o Check return value on all libusb calls (Tormod Volden) + o Fix segmentation fault with -s option (Tormod Volden) + o Add DFU suffix manipulation tool (Stefan Schmidt) + o Port to Windows: (Tormod Volden, some parts based on work from Satz + Klauer) + o Port file handling to stdio streams + o Sleep() macros + o C99 types + o Pack structs + o Detect DfuSe device correctly on big-endian architectures (Tormod + Volden) + o Add dfuse progress indication on download (Tormod Volden) + o Cleanup: gcc pedantic, gcc extension, ... (Tormod Volden) + o Rely on page size from functional descriptor. Please report if you get + an error about it. (Tormod Volden) + o Add quirk for Maple since it reports wrong DFU version (Tormod Volden) + +2012-04-22: Stefan Schmidt + +0.5: + o DfuSe extension support for ST devices (Tormod Volden) + o Add initial support for bitWillDetach flag from DFU 1.1 (Tormod + Volden) + o Internal cleanup and some manual page fixes (Tormod Volden) + +2011-11-02: Stefan Schmidt + +0.4: + o Rework to use libusb-1.0 (Stefan Schmidt) + o DFU suffix support (Tormod Volden, Stefan Schmidt) + o Sspeed up DFU downloads directly into memory (Bernard Blackham) + o More flexible -d vid:pid parsing (Tormod Volden) + o Many bug fixes and cleanups + +2011-07-20: Stefan Schmidt + +0.3: + o quirks: Add OpenOCD to the poll timeout quirk table. + +2010-12-22: Stefan Schmidt + +0.2: + o Fix some typos on the website and the README (Antonio Ospite, Uwe + Hermann) + o Remove build rule for a static binary. We can use autotools for this. + (Mike Frysinger) + o Fix infinite loop in download error path (C. Scott Ananian) + o Break out to show the 'finished' in upload (C. Scott Ananian) + o Add GPLv2+ headers (Harald Welte) + o Remove dead code (commands.[ch]) remnescent of dfu-programmer (Harald + Welte) + o Simple quirk system with Openmoko quirk for missing bwPollTimeout (Tormod Volden) + o New default (1024) and clamping of transfer size (Tormod Volden) + o Verify sending of completion packet (Tormod Volden) + o Look for DFU functional descriptor among all descriptors (Tormod + Volden) + o Print out in which direction we are transferring data + o Abort in upload if the file already exists + +2010-11-17 Stefan Schmidt + +0.1: + Initial release + +2010-05-23 Stefan Schmidt diff --git a/tools/macosx/src/dfu-util/DEVICES.txt b/tools/macosx/src/dfu-util/DEVICES.txt new file mode 100644 index 0000000..bdd9f1f --- /dev/null +++ b/tools/macosx/src/dfu-util/DEVICES.txt @@ -0,0 +1,20 @@ +List of supported software and hardware products: + +Software user (bootloader, etc) +------------------------------- +- Sam7DFU: http://www.openpcd.org/Sam7dfu +- U-boot: DFU patches +- Barebox: http://www.barebox.org/ +- Leaflabs: http://code.google.com/p/leaflabs/ +- Blackmagic DFU + +Products using DFU +------------------ +- OpenPCD (sam7dfu) +- Openmoko Neo 1973 and Freerunner (u-boot with DFU patches) +- Leaflabs Maple +- ATUSB from Qi Hardware +- STM32F105/7, STM32F2/F3/F4 in System Bootloader +- Blackmagic debug probe +- NXP LPC31xx/LPC43XX, e.g. LPC-Link and LPC-Link2, need binaries + with LPC prefix and encoding (LPC-Link) diff --git a/tools/macosx/src/dfu-util/Makefile.am b/tools/macosx/src/dfu-util/Makefile.am new file mode 100644 index 0000000..641dda5 --- /dev/null +++ b/tools/macosx/src/dfu-util/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src doc + +EXTRA_DIST = autogen.sh TODO DEVICES.txt dfuse-pack.py diff --git a/tools/macosx/src/dfu-util/README b/tools/macosx/src/dfu-util/README new file mode 100644 index 0000000..0f8f262 --- /dev/null +++ b/tools/macosx/src/dfu-util/README @@ -0,0 +1,20 @@ +Dfu-util - Device Firmware Upgrade Utilities + +Dfu-util is the host side implementation of the DFU 1.0 [1] and DFU 1.1 [2] +specification of the USB forum. + +DFU is intended to download and upload firmware to devices connected over +USB. It ranges from small devices like micro-controller boards up to mobile +phones. With dfu-util you are able to download firmware to your device or +upload firmware from it. + +dfu-util has been tested with Openmoko Neo1973 and Freerunner and many +other devices. + +[1] DFU 1.0 spec: http://www.usb.org/developers/devclass_docs/usbdfu10.pdf +[2] DFU 1.1 spec: http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf + +The official website is: + + http://dfu-util.gnumonks.org/ + diff --git a/tools/macosx/src/dfu-util/TODO b/tools/macosx/src/dfu-util/TODO new file mode 100644 index 0000000..900c30c --- /dev/null +++ b/tools/macosx/src/dfu-util/TODO @@ -0,0 +1,14 @@ +DfuSe: +- Do erase and write in two separate passes when downloading +- Skip "Set Address" command when downloading contiguous blocks +- Implement "Get Commands" command + +Devices: +- Research iPhone/iPod/iPad support + Heavily modified dfu-util fork here: + https://github.com/planetbeing/xpwn/tree/master/dfu-util +- Test against Niftylights + +Non-Code: +- Logo +- Re-License as LGPL for usage as library? diff --git a/tools/macosx/src/dfu-util/autogen.sh b/tools/macosx/src/dfu-util/autogen.sh new file mode 100644 index 0000000..e67aed3 --- /dev/null +++ b/tools/macosx/src/dfu-util/autogen.sh @@ -0,0 +1,2 @@ +#! /bin/sh +autoreconf -v -i diff --git a/tools/macosx/src/dfu-util/configure.ac b/tools/macosx/src/dfu-util/configure.ac new file mode 100644 index 0000000..8622114 --- /dev/null +++ b/tools/macosx/src/dfu-util/configure.ac @@ -0,0 +1,41 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([dfu-util],[0.8],[dfu-util@lists.gnumonks.org],,[http://dfu-util.gnumonks.org]) +AC_CONFIG_AUX_DIR(m4) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([config.h]) + +# Test for new silent rules and enable only if they are available +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +# On FreeBSD the libusb-1.0 is called libusb and resides in system location +AC_CHECK_LIB([usb], [libusb_init],, [native_libusb=no],) +AS_IF([test x$native_libusb = xno], [ + PKG_CHECK_MODULES([USB], [libusb-1.0 >= 1.0.0],, + AC_MSG_ERROR([*** Required libusb-1.0 >= 1.0.0 not installed ***])) +]) +AC_CHECK_LIB([usbpath],[usb_path2devnum],,,-lusb) + +LIBS="$LIBS $USB_LIBS" +CFLAGS="$CFLAGS $USB_CFLAGS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([usbpath.h windows.h sysexits.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([ftruncate getpagesize nanosleep err]) + +AC_CONFIG_FILES(Makefile src/Makefile doc/Makefile) +AC_OUTPUT diff --git a/tools/macosx/src/dfu-util/device-logs/README b/tools/macosx/src/dfu-util/device-logs/README new file mode 100644 index 0000000..00d3d1a --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/README @@ -0,0 +1,77 @@ +Device: +------- +qi-hardware-atusb: +- Qi Hardware ben-wpan +- DFU implementation: + http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw/usb +- Tester: Stefan Schmidt + +openpcd: +- OpenPCD RFID reader +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +simtrace: +- Sysmocom SimTrace +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +openmoko-freerunner: +- Openmoko Freerunner +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +openmoko-neo1973: +- Openmoko Neo1073 +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +tdk-bluetooth: +- TDK Corp. Bluetooth Adapter +- DFU implementation: closed soure +- Only upload has been tested +- Tester: Stefan Schmidt + +stm32f107: +- STM32 microcontrollers with built-in (ROM) DFU loader +- DFU implementation: Closed source but probably similar to the one + in their USB device libraries. Some relevant application notes: + http://www.st.com -> AN3156 and AN2606 +- Tested by Uwe Bonnes + +stm32f4discovery: +- STM32 microcontroller board with built-in (ROM) DFU loader +- DFU implementation: Closed source, probably similar to stm32f107. +- Tested by Joe Rothweiler + +dso-nano: +- DSO Nano pocket oscilloscope +- DFU implementation: Based on ST Microelectronics USB FS Library 1.0 + http://dsonano.googlecode.com/files/DS0201_OpenSource.rar +- Tester: Tormod Volden + +opc-20: +- Custom devices based on STM32F1xx +- DFU implementation: ST Microelectronics USB FS Device Library 3.1.0 + http://www.st.com -> um0424.zip +- Tester: Tormod Volden + +lpc-link, lpclink2: +- NXP LPCXpresso debug adapters +- Proprietary DFU implementation, uses special download files with + LPC prefix and encoding of the target firmware code +- Tested by Uwe Bonnes + +Adding the lsusb output and a download log of your device here helps +us to avoid regressions for hardware we cannot test while working on +the code. To extract the lsusb output use this command: +sudo lsusb -v -d $USBID > $DEVICE.lsusb +Prepare a description snippet as above, and send it to us. A log +(copy-paste of the command window) of a firmware download is also +nice, please use the double verbose option -v -v and include the +command line in the log file. + diff --git a/tools/macosx/src/dfu-util/device-logs/dsonano.lsusb b/tools/macosx/src/dfu-util/device-logs/dsonano.lsusb new file mode 100644 index 0000000..140a7bc --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/dsonano.lsusb @@ -0,0 +1,60 @@ + +Bus 002 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 1.1a + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 001 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 5 @SPI Flash : M25P64/0x00000000/64*064Kg,64*064Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/lpclink.log b/tools/macosx/src/dfu-util/device-logs/lpclink.log new file mode 100644 index 0000000..7de4dd3 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/lpclink.log @@ -0,0 +1,59 @@ +(The on-board LPC3154 has some encryption key set and LPCXpressoWIN.enc +is encrypted.) + +$ lsusb | grep NXP +Bus 003 Device 011: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link + +$ dfu-util -v -v -v -R -D /opt/lpc/lpcxpresso/bin/LPCXpressoWIN.enc + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Deducing device DFU version from functional descriptor length +Opening DFU capable USB device... +ID 0471:df55 +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 6% 2048 bytes +Download [=== ] 13% 4096 bytes +Download [==== ] 19% 6144 bytes +Download [====== ] 26% 8192 bytes +Download [======== ] 32% 10240 bytes +Download [========= ] 39% 12288 bytes +Download [=========== ] 45% 14336 bytes +Download [============= ] 52% 16384 bytes +Download [============== ] 59% 18432 bytes +Download [================ ] 65% 20480 bytes +Download [================== ] 72% 22528 bytes +Download [=================== ] 78% 24576 bytes +Download [===================== ] 85% 26624 bytes +Download [====================== ] 91% 28672 bytes +Download [======================== ] 98% 29192 bytes +Download [=========================] 100% 29192 bytes +Download done. +Sent a total of 29192 bytes +state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present +Done! +dfu-util: can't detach +Resetting USB to switch back to runtime mode + +$ lsusb | grep NXP +Bus 003 Device 012: ID 1fc9:0009 NXP Semiconductors diff --git a/tools/macosx/src/dfu-util/device-logs/lpclink.lsusb b/tools/macosx/src/dfu-util/device-logs/lpclink.lsusb new file mode 100644 index 0000000..867b2a2 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/lpclink.lsusb @@ -0,0 +1,58 @@ + +Bus 003 Device 008: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0471 Philips (or NXP) + idProduct 0xdf55 LPCXpresso LPC-Link + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 25 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 1 + Will Not Detach + Manifestation Intolerant + Upload Unsupported + Download Supported + wDetachTimeout 65535 milliseconds + wTransferSize 2048 bytes +Device Qualifier (for other device speed): + bLength 10 + bDescriptorType 6 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + bNumConfigurations 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/lpclink2.log b/tools/macosx/src/dfu-util/device-logs/lpclink2.log new file mode 100644 index 0000000..4681eff --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/lpclink2.log @@ -0,0 +1,59 @@ +$ lsusb | grep NXP +Bus 003 Device 013: ID 1fc9:000c NXP Semiconductors + +$ dfu-util -D ~/devel/dfu-util/firmware.bin.qthdr + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Possible unencryptes NXP LPC DFU prefix with the following properties +Payload length: 39 kiByte +Opening DFU capable USB device... +ID 1fc9:000c +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 4% 2048 bytes +Download [== ] 9% 4096 bytes +Download [=== ] 14% 6144 bytes +Download [==== ] 19% 8192 bytes +Download [====== ] 24% 10240 bytes +Download [======= ] 28% 12288 bytes +Download [======== ] 33% 14336 bytes +Download [========= ] 38% 16384 bytes +Download [========== ] 43% 18432 bytes +Download [============ ] 48% 20480 bytes +Download [============= ] 53% 22528 bytes +Download [============== ] 57% 24576 bytes +Download [=============== ] 62% 26624 bytes +Download [================ ] 67% 28672 bytes +Download [================== ] 72% 30720 bytes +Download [=================== ] 77% 32768 bytes +Download [==================== ] 82% 34816 bytes +Download [===================== ] 86% 36864 bytes +Download [====================== ] 91% 38912 bytes +Download [======================== ] 96% 40356 bytes +Download [=========================] 100% 40356 bytes +Download done. +Sent a total of 40356 bytes +dfu-util: unable to read DFU status + +$ lsusb | grep NXP +Bus 003 Device 014: ID 1fc9:0018 NXP Semiconductors diff --git a/tools/macosx/src/dfu-util/device-logs/lpclink2.lsusb b/tools/macosx/src/dfu-util/device-logs/lpclink2.lsusb new file mode 100644 index 0000000..b833fca --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/lpclink2.lsusb @@ -0,0 +1,203 @@ + +Bus 003 Device 007: ID 0c72:000c PEAK System PCAN-USB +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x0c72 PEAK System + idProduct 0x000c PCAN-USB + bcdDevice 1c.ff + iManufacturer 0 + iProduct 3 VER1:PEAK +VER2:02.8.01 +DAT :06.05.2004 +TIME:09:35:37 + ... + iSerial 0 + bNumConfigurations 3 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 2 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 394mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 3 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0001 + Self Powered diff --git a/tools/macosx/src/dfu-util/device-logs/opc-20.lsusb b/tools/macosx/src/dfu-util/device-logs/opc-20.lsusb new file mode 100644 index 0000000..580df90 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/opc-20.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 2.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 ÿÿÿÿÿÿÿÿÿÿÿÿ + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : M25P64/0x00000000/128*64Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1a.01 +Device Status: 0x0001 + Self Powered diff --git a/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb b/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb new file mode 100644 index 0000000..4c0abfb --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb @@ -0,0 +1,109 @@ +Bus 003 Device 017: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 81 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 7 USB Device Firmware Upgrade + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 8 RAM 0x32000000 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 9 u-boot + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 10 u-boot_env + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 11 kernel + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 4 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 12 splash + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 5 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 13 factory + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 6 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 14 rootfs + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 +Device Status: 0x0a00 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner.lsusb b/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner.lsusb new file mode 100644 index 0000000..835708d --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/openmoko-freerunner.lsusb @@ -0,0 +1,179 @@ +Bus 005 Device 033: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 USB Device Firmware Upgrade + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x9a00 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/openmoko-neo1973.lsusb b/tools/macosx/src/dfu-util/device-logs/openmoko-neo1973.lsusb new file mode 100644 index 0000000..0778950 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/openmoko-neo1973.lsusb @@ -0,0 +1,182 @@ + +Bus 006 Device 020: ID 1457:5119 First International Computer, Inc. OpenMoko Neo1973 u-boot cdc_acm serial port +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1457 First International Computer, Inc. + idProduct 0x5119 OpenMoko Neo1973 u-boot cdc_acm serial port + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x0006 + (Bus Powered) + Remote Wakeup Enabled + Test Mode diff --git a/tools/macosx/src/dfu-util/device-logs/openpcd.lsusb b/tools/macosx/src/dfu-util/device-logs/openpcd.lsusb new file mode 100644 index 0000000..f6255a9 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/openpcd.lsusb @@ -0,0 +1,60 @@ + +Bus 006 Device 016: ID 16c0:076b VOTI OpenPCD 13.56MHz RFID Reader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x076b OpenPCD 13.56MHz RFID Reader + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 OpenPCD RFID Simulator - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/qi-hardware-atusb.lsusb b/tools/macosx/src/dfu-util/device-logs/qi-hardware-atusb.lsusb new file mode 100644 index 0000000..bfc1701 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/qi-hardware-atusb.lsusb @@ -0,0 +1,59 @@ + +Bus 006 Device 013: ID 20b7:1540 Qi Hardware ben-wpan, AT86RF230-based +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x20b7 Qi Hardware + idProduct 0x1540 ben-wpan, AT86RF230-based + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 1 4630333438371508231a + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 2 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 40mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/simtrace.lsusb b/tools/macosx/src/dfu-util/device-logs/simtrace.lsusb new file mode 100644 index 0000000..578ddf0 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/simtrace.lsusb @@ -0,0 +1,70 @@ + +Bus 006 Device 017: ID 16c0:0762 VOTI +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x0762 + bcdDevice 0.00 + iManufacturer 1 sysmocom - systems for mobile communications GmbH + iProduct 2 SimTrace SIM Sniffer - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 45 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 3 SimTrace DFU Configuration + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 SimTrace DFU Interface - Application Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 SimTrace DFU Interface - Bootloader Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 SimTrace DFU Interface - RAM + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/sparkcore.lsusb b/tools/macosx/src/dfu-util/device-logs/sparkcore.lsusb new file mode 100644 index 0000000..b6029ff --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/sparkcore.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 008: ID 1d50:607f OpenMoko, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x607f + bcdDevice 2.00 + iManufacturer 1 Spark Devices + iProduct 2 CORE DFU + iSerial 3 8D80527B5055 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/20*001Ka,108*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : SST25x/0x00000000/512*04Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/macosx/src/dfu-util/device-logs/stm32f107.bin-download b/tools/macosx/src/dfu-util/device-logs/stm32f107.bin-download new file mode 100644 index 0000000..45b714f --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/stm32f107.bin-download @@ -0,0 +1,48 @@ +> src/dfu-util --intf 0 --alt 0 -v -v -v -s 0x8000000 -D test3 +dfu-util 0.4 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Opening DFU USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/128*002Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +No valid DFU suffix signature +Warning: File has no DFU suffix +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 128 x 2048 = 262144 (rew) +Uploading to address = 0x08000000, size = 16384 +Erasing page size 2048 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 +Erasing page size 2048 at address 0x08000800, page starting at 0x08000800 + Download from image offset 00000800 to memory 08000800-08000fff, size 2048 + Setting address pointer to 0x08000800 +Erasing page size 2048 at address 0x08001000, page starting at 0x08001000 + Download from image offset 00001000 to memory 08001000-080017ff, size 2048 + Setting address pointer to 0x08001000 +Erasing page size 2048 at address 0x08001800, page starting at 0x08001800 + Download from image offset 00001800 to memory 08001800-08001fff, size 2048 + Setting address pointer to 0x08001800 +Erasing page size 2048 at address 0x08002000, page starting at 0x08002000 + Download from image offset 00002000 to memory 08002000-080027ff, size 2048 + Setting address pointer to 0x08002000 +Erasing page size 2048 at address 0x08002800, page starting at 0x08002800 + Download from image offset 00002800 to memory 08002800-08002fff, size 2048 + Setting address pointer to 0x08002800 +Erasing page size 2048 at address 0x08003000, page starting at 0x08003000 + Download from image offset 00003000 to memory 08003000-080037ff, size 2048 + Setting address pointer to 0x08003000 +Erasing page size 2048 at address 0x08003800, page starting at 0x08003800 + Download from image offset 00003800 to memory 08003800-08003fff, size 2048 + Setting address pointer to 0x08003800 + diff --git a/tools/macosx/src/dfu-util/device-logs/stm32f107.lsusb b/tools/macosx/src/dfu-util/device-logs/stm32f107.lsusb new file mode 100644 index 0000000..14b45cd --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/stm32f107.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 028: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 20.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 0x418 DFU Bootloader + iSerial 3 STM32 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/128*002Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFF800/01*016 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.bin-download b/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.bin-download new file mode 100644 index 0000000..96e1722 --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.bin-download @@ -0,0 +1,36 @@ +dfu-util --device 0483:df11 --alt 0 \ + --dfuse-address 0x08000000 \ + -v -v -v \ + --download arm/iotoggle.bin +No valid DFU suffix signature +Warning: File has no DFU suffix +dfu-util 0.5 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Filter on vendor = 0x0483 product = 0xdf11 +Opening DFU capable USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuERROR, status = 10 +dfuERROR, clearing status +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 4 x 16384 = 65536 (rew) +Memory segment at 0x08010000 1 x 65536 = 65536 (rew) +Memory segment at 0x08020000 7 x 131072 = 917504 (rew) +Uploading to address = 0x08000000, size = 2308 +Erasing page size 16384 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 + Download from image offset 00000800 to memory 08000800-08000903, size 260 + Setting address pointer to 0x08000800 diff --git a/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.lsusb b/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.lsusb new file mode 100644 index 0000000..0b870de --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/stm32f4discovery.lsusb @@ -0,0 +1,80 @@ + +Bus 001 Device 010: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 21.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 BOOTLOADER + iSerial 3 315A28A0B956 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 54 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFC000/01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 @OTP Memory /0x1FFF7800/01*512 g,01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 7 @Device Feature/0xFFFF0000/01*004 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/macosx/src/dfu-util/device-logs/tdk-bluetooth.lsusb b/tools/macosx/src/dfu-util/device-logs/tdk-bluetooth.lsusb new file mode 100644 index 0000000..c0cface --- /dev/null +++ b/tools/macosx/src/dfu-util/device-logs/tdk-bluetooth.lsusb @@ -0,0 +1,269 @@ + +Bus 006 Device 014: ID 04bf:0320 TDK Corp. Bluetooth Adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 224 Wireless + bDeviceSubClass 1 Radio Frequency + bDeviceProtocol 1 Bluetooth + bMaxPacketSize0 64 + idVendor 0x04bf TDK Corp. + idProduct 0x0320 Bluetooth Adapter + bcdDevice 26.52 + iManufacturer 1 Ezurio + iProduct 2 Turbo Bluetooth Adapter + iSerial 3 008098D4FFBD + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 193 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 1 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 2 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 3 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 4 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 5 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 5000 milliseconds + wTransferSize 1023 bytes +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/macosx/src/dfu-util/dfuse-pack.py b/tools/macosx/src/dfu-util/dfuse-pack.py new file mode 100644 index 0000000..875cc5c --- /dev/null +++ b/tools/macosx/src/dfu-util/dfuse-pack.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +# Written by Antonio Galea - 2010/11/18 +# Distributed under Gnu LGPL 3.0 +# see http://www.gnu.org/licenses/lgpl-3.0.txt + +import sys,struct,zlib,os +from optparse import OptionParser + +DEFAULT_DEVICE="0x0483:0xdf11" + +def named(tuple,names): + return dict(zip(names.split(),tuple)) +def consume(fmt,data,names): + n = struct.calcsize(fmt) + return named(struct.unpack(fmt,data[:n]),names),data[n:] +def cstring(string): + return string.split('\0',1)[0] +def compute_crc(data): + return 0xFFFFFFFF & -zlib.crc32(data) -1 + +def parse(file,dump_images=False): + print 'File: "%s"' % file + data = open(file,'rb').read() + crc = compute_crc(data[:-4]) + prefix, data = consume('<5sBIB',data,'signature version size targets') + print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix + for t in range(prefix['targets']): + tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements') + tprefix['num'] = t + if tprefix['named']: + tprefix['name'] = cstring(tprefix['name']) + else: + tprefix['name'] = '' + print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix + tsize = tprefix['size'] + target, data = data[:tsize], data[tsize:] + for e in range(tprefix['elements']): + eprefix, target = consume('<2I',target,'address size') + eprefix['num'] = e + print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix + esize = eprefix['size'] + image, target = target[:esize], target[esize:] + if dump_images: + out = '%s.target%d.image%d.bin' % (file,t,e) + open(out,'wb').write(image) + print ' DUMPED IMAGE TO "%s"' % out + if len(target): + print "target %d: PARSE ERROR" % t + suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc') + print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix + if crc != suffix['crc']: + print "CRC ERROR: computed crc32 is 0x%08x" % crc + data = data[16:] + if data: + print "PARSE ERROR" + +def build(file,targets,device=DEFAULT_DEVICE): + data = '' + for t,target in enumerate(targets): + tdata = '' + for image in target: + tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data'] + tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata + data += tdata + data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data + v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1)) + data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16) + crc = compute_crc(data) + data += struct.pack(' and +Harald Welte . Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +.SH LICENCE +.B dfu-util +is covered by the GNU General Public License (GPL), version 2 or later. +.SH COPYRIGHT +This manual page was originally written by Uwe Hermann , +and is now part of the dfu-util project. diff --git a/tools/macosx/src/dfu-util/msvc/README_msvc.txt b/tools/macosx/src/dfu-util/msvc/README_msvc.txt new file mode 100644 index 0000000..6e68ec6 --- /dev/null +++ b/tools/macosx/src/dfu-util/msvc/README_msvc.txt @@ -0,0 +1,10 @@ +# (C) Roger Meier +# (C) Pascal Schweizer +# msvc folder is GPL-2.0+, LGPL-2.1+, BSD-3-Clause or MIT license(SPDX) + +Building dfu-util native on Windows with Visual Studio + +3rd party dependencies: +- libusbx ( git clone https://github.com/libusbx/libusbx.git ) + - getopt (part of libusbx: libusbx/examples/getopt) + diff --git a/tools/macosx/src/dfu-util/msvc/dfu-suffix_2010.vcxproj b/tools/macosx/src/dfu-util/msvc/dfu-suffix_2010.vcxproj new file mode 100644 index 0000000..0c316c2 --- /dev/null +++ b/tools/macosx/src/dfu-util/msvc/dfu-suffix_2010.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA} + dfusuffix + dfu-suffix + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/macosx/src/dfu-util/msvc/dfu-util_2010.sln b/tools/macosx/src/dfu-util/msvc/dfu-util_2010.sln new file mode 100644 index 0000000..ef79723 --- /dev/null +++ b/tools/macosx/src/dfu-util/msvc/dfu-util_2010.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-util", "dfu-util_2010.vcxproj", "{0E071A60-7EF2-4427-BAA8-9143CACB5BCB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4F8746D-B27E-4806-95E5-2052174E923B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-suffix", "dfu-suffix_2010.vcxproj", "{8F7600A2-3B37-4956-B39B-A1D43EF29EDA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt_2010", "..\..\libusbx\msvc\getopt_2010.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\..\libusbx\msvc\libusb_static_2010.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.Build.0 = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|x64.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.ActiveCfg = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.Build.0 = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|x64.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.Build.0 = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|x64.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.Build.0 = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|x64.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/macosx/src/dfu-util/msvc/dfu-util_2010.vcxproj b/tools/macosx/src/dfu-util/msvc/dfu-util_2010.vcxproj new file mode 100644 index 0000000..17a8bee --- /dev/null +++ b/tools/macosx/src/dfu-util/msvc/dfu-util_2010.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB} + dfuutil + dfu-util + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + copy $(SolutionDir)..\$(Platform)\$(Configuration)\dll\libusb-1.0.dll $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/macosx/src/dfu-util/src/Makefile.am b/tools/macosx/src/dfu-util/src/Makefile.am new file mode 100644 index 0000000..70179c4 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/Makefile.am @@ -0,0 +1,28 @@ +AM_CFLAGS = -Wall -Wextra + +bin_PROGRAMS = dfu-util dfu-suffix dfu-prefix +dfu_util_SOURCES = main.c \ + portable.h \ + dfu_load.c \ + dfu_load.h \ + dfu_util.c \ + dfu_util.h \ + dfuse.c \ + dfuse.h \ + dfuse_mem.c \ + dfuse_mem.h \ + dfu.c \ + dfu.h \ + usb_dfu.h \ + dfu_file.c \ + dfu_file.h \ + quirks.c \ + quirks.h + +dfu_suffix_SOURCES = suffix.c \ + dfu_file.h \ + dfu_file.c + +dfu_prefix_SOURCES = prefix.c \ + dfu_file.h \ + dfu_file.c diff --git a/tools/macosx/src/dfu-util/src/dfu.c b/tools/macosx/src/dfu-util/src/dfu.c new file mode 100644 index 0000000..14d7673 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu.c @@ -0,0 +1,357 @@ +/* + * Low-level DFU communication routines, originally taken from + * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ + * (part of dfu-programmer). + * + * Copyright 2005-2006 Weston Schmidt + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "quirks.h" + +static int dfu_timeout = 5000; /* 5 seconds - default */ + +/* + * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * timeout - the timeout in ms the USB device should wait for a pending + * USB reset before giving up and terminating the operation + * + * returns 0 or < 0 on error + */ +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DETACH, + /* wValue */ timeout, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the total number of bytes to transfer to the USB + * device - must be less than wTransferSize + * data - the data to transfer + * + * returns the number of bytes written or < 0 on error + */ +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the number of bytes received or < 0 on error + */ +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * status - the data structure to be populated with the results + * + * return the number of bytes read in or < 0 on an error + */ +int dfu_get_status( struct dfu_if *dif, struct dfu_status *status ) +{ + unsigned char buffer[6]; + int result; + + /* Initialize the status data structure */ + status->bStatus = DFU_STATUS_ERROR_UNKNOWN; + status->bwPollTimeout = 0; + status->bState = STATE_DFU_ERROR; + status->iString = 0; + + result = libusb_control_transfer( dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATUS, + /* wValue */ 0, + /* wIndex */ dif->interface, + /* Data */ buffer, + /* wLength */ 6, + dfu_timeout ); + + if( 6 == result ) { + status->bStatus = buffer[0]; + if (dif->quirks & QUIRK_POLLTIMEOUT) + status->bwPollTimeout = DEFAULT_POLLTIMEOUT; + else + status->bwPollTimeout = ((0xff & buffer[3]) << 16) | + ((0xff & buffer[2]) << 8) | + (0xff & buffer[1]); + status->bState = buffer[4]; + status->iString = buffer[5]; + } + + return result; +} + + +/* + * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * return 0 or < 0 on an error + */ +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_CLRSTATUS, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the state or < 0 on error + */ +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ) +{ + int result; + unsigned char buffer[1]; + + result = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATE, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ buffer, + /* wLength */ 1, + dfu_timeout ); + + /* Return the error if there is one. */ + if (result < 1) + return -1; + + /* Return the state. */ + return buffer[0]; +} + + +/* + * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * returns 0 or < 0 on an error + */ +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_ABORT, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +const char* dfu_state_to_string( int state ) +{ + const char *message; + + switch (state) { + case STATE_APP_IDLE: + message = "appIDLE"; + break; + case STATE_APP_DETACH: + message = "appDETACH"; + break; + case STATE_DFU_IDLE: + message = "dfuIDLE"; + break; + case STATE_DFU_DOWNLOAD_SYNC: + message = "dfuDNLOAD-SYNC"; + break; + case STATE_DFU_DOWNLOAD_BUSY: + message = "dfuDNBUSY"; + break; + case STATE_DFU_DOWNLOAD_IDLE: + message = "dfuDNLOAD-IDLE"; + break; + case STATE_DFU_MANIFEST_SYNC: + message = "dfuMANIFEST-SYNC"; + break; + case STATE_DFU_MANIFEST: + message = "dfuMANIFEST"; + break; + case STATE_DFU_MANIFEST_WAIT_RESET: + message = "dfuMANIFEST-WAIT-RESET"; + break; + case STATE_DFU_UPLOAD_IDLE: + message = "dfuUPLOAD-IDLE"; + break; + case STATE_DFU_ERROR: + message = "dfuERROR"; + break; + default: + message = NULL; + break; + } + + return message; +} + +/* Chapter 6.1.2 */ +static const char *dfu_status_names[] = { + /* DFU_STATUS_OK */ + "No error condition is present", + /* DFU_STATUS_errTARGET */ + "File is not targeted for use by this device", + /* DFU_STATUS_errFILE */ + "File is for this device but fails some vendor-specific test", + /* DFU_STATUS_errWRITE */ + "Device is unable to write memory", + /* DFU_STATUS_errERASE */ + "Memory erase function failed", + /* DFU_STATUS_errCHECK_ERASED */ + "Memory erase check failed", + /* DFU_STATUS_errPROG */ + "Program memory function failed", + /* DFU_STATUS_errVERIFY */ + "Programmed memory failed verification", + /* DFU_STATUS_errADDRESS */ + "Cannot program memory due to received address that is out of range", + /* DFU_STATUS_errNOTDONE */ + "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", + /* DFU_STATUS_errFIRMWARE */ + "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", + /* DFU_STATUS_errVENDOR */ + "iString indicates a vendor specific error", + /* DFU_STATUS_errUSBR */ + "Device detected unexpected USB reset signalling", + /* DFU_STATUS_errPOR */ + "Device detected unexpected power on reset", + /* DFU_STATUS_errUNKNOWN */ + "Something went wrong, but the device does not know what it was", + /* DFU_STATUS_errSTALLEDPKT */ + "Device stalled an unexpected request" +}; + + +const char *dfu_status_to_string(int status) +{ + if (status > DFU_STATUS_errSTALLEDPKT) + return "INVALID"; + return dfu_status_names[status]; +} + +int dfu_abort_to_idle(struct dfu_if *dif) +{ + int ret; + struct dfu_status dst; + + ret = dfu_abort(dif->dev_handle, dif->interface); + if (ret < 0) { + errx(EX_IOERR, "Error sending dfu abort request"); + exit(1); + } + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during abort get_status"); + exit(1); + } + if (dst.bState != DFU_STATE_dfuIDLE) { + errx(EX_IOERR, "Failed to enter idle state on abort"); + exit(1); + } + milli_sleep(dst.bwPollTimeout); + return ret; +} diff --git a/tools/macosx/src/dfu-util/src/dfu.h b/tools/macosx/src/dfu-util/src/dfu.h new file mode 100644 index 0000000..8e3caeb --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu.h @@ -0,0 +1,133 @@ +/* + * dfu-programmer + * + * $Id: dfu.h,v 1.2 2005/09/25 01:27:42 schmidtw Exp $ + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFU_H +#define DFU_H + +#include +#include "usb_dfu.h" + +/* DFU states */ +#define STATE_APP_IDLE 0x00 +#define STATE_APP_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + + +/* DFU status */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_ERROR_TARGET 0x01 +#define DFU_STATUS_ERROR_FILE 0x02 +#define DFU_STATUS_ERROR_WRITE 0x03 +#define DFU_STATUS_ERROR_ERASE 0x04 +#define DFU_STATUS_ERROR_CHECK_ERASED 0x05 +#define DFU_STATUS_ERROR_PROG 0x06 +#define DFU_STATUS_ERROR_VERIFY 0x07 +#define DFU_STATUS_ERROR_ADDRESS 0x08 +#define DFU_STATUS_ERROR_NOTDONE 0x09 +#define DFU_STATUS_ERROR_FIRMWARE 0x0a +#define DFU_STATUS_ERROR_VENDOR 0x0b +#define DFU_STATUS_ERROR_USBR 0x0c +#define DFU_STATUS_ERROR_POR 0x0d +#define DFU_STATUS_ERROR_UNKNOWN 0x0e +#define DFU_STATUS_ERROR_STALLEDPKT 0x0f + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +/* DFU interface */ +#define DFU_IFF_DFU 0x0001 /* DFU Mode, (not Runtime) */ + +/* This is based off of DFU_GETSTATUS + * + * 1 unsigned byte bStatus + * 3 unsigned byte bwPollTimeout + * 1 unsigned byte bState + * 1 unsigned byte iString +*/ + +struct dfu_status { + unsigned char bStatus; + unsigned int bwPollTimeout; + unsigned char bState; + unsigned char iString; +}; + +struct dfu_if { + struct usb_dfu_func_descriptor func_dfu; + uint16_t quirks; + uint16_t busnum; + uint16_t devnum; + uint16_t vendor; + uint16_t product; + uint16_t bcdDevice; + uint8_t configuration; + uint8_t interface; + uint8_t altsetting; + uint8_t flags; + uint8_t bMaxPacketSize0; + char *alt_name; + char *serial_name; + libusb_device *dev; + libusb_device_handle *dev_handle; + struct dfu_if *next; +}; + +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ); +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_get_status( struct dfu_if *dif, + struct dfu_status *status ); +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ); +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort_to_idle( struct dfu_if *dif); + +const char *dfu_state_to_string( int state ); + +const char *dfu_status_to_string( int status ); + +#endif /* DFU_H */ diff --git a/tools/macosx/src/dfu-util/src/dfu_file.c b/tools/macosx/src/dfu-util/src/dfu_file.c new file mode 100644 index 0000000..7c897d4 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_file.c @@ -0,0 +1,444 @@ +/* + * Load or store DFU files including suffix and prefix + * + * Copyright 2014 Tormod Volden + * Copyright 2012 Stefan Schmidt + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +#define DFU_SUFFIX_LENGTH 16 +#define LMDFU_PREFIX_LENGTH 8 +#define LPCDFU_PREFIX_LENGTH 16 +#define PROGRESS_BAR_WIDTH 25 +#define STDIN_CHUNK_SIZE 65536 + +static const unsigned long crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +static uint32_t crc32_byte(uint32_t accum, uint8_t delta) +{ + return crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); +} + +static int probe_prefix(struct dfu_file *file) +{ + uint8_t *prefix = file->firmware; + + if (file->size.total < LMDFU_PREFIX_LENGTH) + return 1; + if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { + file->prefix_type = LMDFU_PREFIX; + file->size.prefix = LMDFU_PREFIX_LENGTH; + file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); + } + else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f)== 0x3f)) { + file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; + file->size.prefix = LPCDFU_PREFIX_LENGTH; + } + + if (file->size.prefix + file->size.suffix > file->size.total) + return 1; + return 0; +} + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max) +{ + static char buf[PROGRESS_BAR_WIDTH + 1]; + static unsigned long long last_progress = -1; + static time_t last_time; + time_t curr_time = time(NULL); + unsigned long long progress; + unsigned long long x; + + /* check for not known maximum */ + if (max < curr) + max = curr + 1; + /* make none out of none give zero */ + if (max == 0 && curr == 0) + max = 1; + + /* compute completion */ + progress = (PROGRESS_BAR_WIDTH * curr) / max; + if (progress > PROGRESS_BAR_WIDTH) + progress = PROGRESS_BAR_WIDTH; + if (progress == last_progress && + curr_time == last_time) + return; + last_progress = progress; + last_time = curr_time; + + for (x = 0; x != PROGRESS_BAR_WIDTH; x++) { + if (x < progress) + buf[x] = '='; + else + buf[x] = ' '; + } + buf[x] = 0; + + printf("\r%s\t[%s] %3lld%% %12lld bytes", desc, buf, + (100ULL * curr) / max, curr); + + if (progress == PROGRESS_BAR_WIDTH) + printf("\n%s done.\n", desc); +} + +void *dfu_malloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL) + errx(EX_SOFTWARE, "Cannot allocate memory of size %d bytes", (int)size); + return (ptr); +} + +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size) +{ + int x; + + /* compute CRC */ + for (x = 0; x != size; x++) + crc = crc32_byte(crc, ((uint8_t *)buf)[x]); + + /* write data */ + if (write(f, buf, size) != size) + err(EX_IOERR, "Could not write %d bytes to file %d", size, f); + + return (crc); +} + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix) +{ + off_t offset; + int f; + int i; + int res; + + file->size.prefix = 0; + file->size.suffix = 0; + + /* default values, if no valid suffix is found */ + file->bcdDFU = 0; + file->idVendor = 0xffff; /* wildcard value */ + file->idProduct = 0xffff; /* wildcard value */ + file->bcdDevice = 0xffff; /* wildcard value */ + + /* default values, if no valid prefix is found */ + file->lmdfu_address = 0; + + free(file->firmware); + + if (!strcmp(file->name, "-")) { + int read_bytes; + +#ifdef WIN32 + _setmode( _fileno( stdin ), _O_BINARY ); +#endif + file->firmware = (uint8_t*) dfu_malloc(STDIN_CHUNK_SIZE); + read_bytes = fread(file->firmware, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total = read_bytes; + while (read_bytes == STDIN_CHUNK_SIZE) { + file->firmware = (uint8_t*) realloc(file->firmware, file->size.total + STDIN_CHUNK_SIZE); + if (!file->firmware) + err(EX_IOERR, "Could not allocate firmware buffer"); + read_bytes = fread(file->firmware + file->size.total, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total += read_bytes; + } + if (verbose) + printf("Read %i bytes from stdin\n", file->size.total); + /* Never require suffix when reading from stdin */ + check_suffix = MAYBE_SUFFIX; + } else { + f = open(file->name, O_RDONLY | O_BINARY); + if (f < 0) + err(EX_IOERR, "Could not open file %s for reading", file->name); + + offset = lseek(f, 0, SEEK_END); + + if ((int)offset < 0 || (int)offset != offset) + err(EX_IOERR, "File size is too big"); + + if (lseek(f, 0, SEEK_SET) != 0) + err(EX_IOERR, "Could not seek to beginning"); + + file->size.total = offset; + file->firmware = dfu_malloc(file->size.total); + + if (read(f, file->firmware, file->size.total) != file->size.total) { + err(EX_IOERR, "Could not read %d bytes from %s", + file->size.total, file->name); + } + close(f); + } + + /* Check for possible DFU file suffix by trying to parse one */ + { + uint32_t crc = 0xffffffff; + const uint8_t *dfusuffix; + int missing_suffix = 0; + const char *reason; + + if (file->size.total < DFU_SUFFIX_LENGTH) { + reason = "File too short for DFU suffix"; + missing_suffix = 1; + goto checked; + } + + dfusuffix = file->firmware + file->size.total - + DFU_SUFFIX_LENGTH; + + for (i = 0; i < file->size.total - 4; i++) + crc = crc32_byte(crc, file->firmware[i]); + + if (dfusuffix[10] != 'D' || + dfusuffix[9] != 'F' || + dfusuffix[8] != 'U') { + reason = "Invalid DFU suffix signature"; + missing_suffix = 1; + goto checked; + } + + file->dwCRC = (dfusuffix[15] << 24) + + (dfusuffix[14] << 16) + + (dfusuffix[13] << 8) + + dfusuffix[12]; + + if (file->dwCRC != crc) { + reason = "DFU suffix CRC does not match"; + missing_suffix = 1; + goto checked; + } + + /* At this point we believe we have a DFU suffix + so we require further checks to succeed */ + + file->bcdDFU = (dfusuffix[7] << 8) + dfusuffix[6]; + + if (verbose) + printf("DFU suffix version %x\n", file->bcdDFU); + + file->size.suffix = dfusuffix[11]; + + if (file->size.suffix < DFU_SUFFIX_LENGTH) { + errx(EX_IOERR, "Unsupported DFU suffix length %d", + file->size.suffix); + } + + if (file->size.suffix > file->size.total) { + errx(EX_IOERR, "Invalid DFU suffix length %d", + file->size.suffix); + } + + file->idVendor = (dfusuffix[5] << 8) + dfusuffix[4]; + file->idProduct = (dfusuffix[3] << 8) + dfusuffix[2]; + file->bcdDevice = (dfusuffix[1] << 8) + dfusuffix[0]; + +checked: + if (missing_suffix) { + if (check_suffix == NEEDS_SUFFIX) { + warnx("%s", reason); + errx(EX_IOERR, "Valid DFU suffix needed"); + } else if (check_suffix == MAYBE_SUFFIX) { + warnx("%s", reason); + warnx("A valid DFU suffix will be required in " + "a future dfu-util release!!!"); + } + } else { + if (check_suffix == NO_SUFFIX) { + errx(EX_SOFTWARE, "Please remove existing DFU suffix before adding a new one.\n"); + } + } + } + res = probe_prefix(file); + if ((res || file->size.prefix == 0) && check_prefix == NEEDS_PREFIX) + errx(EX_IOERR, "Valid DFU prefix needed"); + if (file->size.prefix && check_prefix == NO_PREFIX) + errx(EX_IOERR, "A prefix already exists, please delete it first"); + if (file->size.prefix && verbose) { + uint8_t *data = file->firmware; + if (file->prefix_type == LMDFU_PREFIX) + printf("Possible TI Stellaris DFU prefix with " + "the following properties\n" + "Address: 0x%08x\n" + "Payload length: %d\n", + file->lmdfu_address, + data[4] | (data[5] << 8) | + (data[6] << 16) | (data[7] << 14)); + else if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) + printf("Possible unencrypted NXP LPC DFU prefix with " + "the following properties\n" + "Payload length: %d kiByte\n", + data[2] >>1 | (data[3] << 7) ); + else + errx(EX_IOERR, "Unknown DFU prefix type"); + } +} + +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix) +{ + uint32_t crc = 0xffffffff; + int f; + + f = open(file->name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0666); + if (f < 0) + err(EX_IOERR, "Could not open file %s for writing", file->name); + + /* write prefix, if any */ + if (write_prefix) { + if (file->prefix_type == LMDFU_PREFIX) { + uint8_t lmdfu_prefix[LMDFU_PREFIX_LENGTH]; + uint32_t addr = file->lmdfu_address / 1024; + + /* lmdfu_dfu_prefix payload length excludes prefix and suffix */ + uint32_t len = file->size.total - + file->size.prefix - file->size.suffix; + + lmdfu_prefix[0] = 0x01; /* STELLARIS_DFU_PROG */ + lmdfu_prefix[1] = 0x00; /* Reserved */ + lmdfu_prefix[2] = (uint8_t)(addr & 0xff); + lmdfu_prefix[3] = (uint8_t)(addr >> 8); + lmdfu_prefix[4] = (uint8_t)(len & 0xff); + lmdfu_prefix[5] = (uint8_t)(len >> 8) & 0xff; + lmdfu_prefix[6] = (uint8_t)(len >> 16) & 0xff; + lmdfu_prefix[7] = (uint8_t)(len >> 24); + + crc = dfu_file_write_crc(f, crc, lmdfu_prefix, LMDFU_PREFIX_LENGTH); + } + if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { + uint8_t lpcdfu_prefix[LPCDFU_PREFIX_LENGTH] = {0}; + int i; + + /* Payload is firmware and prefix rounded to 512 bytes */ + uint32_t len = (file->size.total - file->size.suffix + 511) /512; + + lpcdfu_prefix[0] = 0x1a; /* Unencypted*/ + lpcdfu_prefix[1] = 0x3f; /* Reserved */ + lpcdfu_prefix[2] = (uint8_t)(len & 0xff); + lpcdfu_prefix[3] = (uint8_t)((len >> 8) & 0xff); + for (i = 12; i < LPCDFU_PREFIX_LENGTH; i++) + lpcdfu_prefix[i] = 0xff; + + crc = dfu_file_write_crc(f, crc, lpcdfu_prefix, LPCDFU_PREFIX_LENGTH); + } + } + /* write firmware binary */ + crc = dfu_file_write_crc(f, crc, file->firmware + file->size.prefix, + file->size.total - file->size.prefix - file->size.suffix); + + /* write suffix, if any */ + if (write_suffix) { + uint8_t dfusuffix[DFU_SUFFIX_LENGTH]; + + dfusuffix[0] = file->bcdDevice & 0xff; + dfusuffix[1] = file->bcdDevice >> 8; + dfusuffix[2] = file->idProduct & 0xff; + dfusuffix[3] = file->idProduct >> 8; + dfusuffix[4] = file->idVendor & 0xff; + dfusuffix[5] = file->idVendor >> 8; + dfusuffix[6] = file->bcdDFU & 0xff; + dfusuffix[7] = file->bcdDFU >> 8; + dfusuffix[8] = 'U'; + dfusuffix[9] = 'F'; + dfusuffix[10] = 'D'; + dfusuffix[11] = DFU_SUFFIX_LENGTH; + + crc = dfu_file_write_crc(f, crc, dfusuffix, + DFU_SUFFIX_LENGTH - 4); + + dfusuffix[12] = crc; + dfusuffix[13] = crc >> 8; + dfusuffix[14] = crc >> 16; + dfusuffix[15] = crc >> 24; + + crc = dfu_file_write_crc(f, crc, dfusuffix + 12, 4); + } + close(f); +} + +void show_suffix_and_prefix(struct dfu_file *file) +{ + if (file->size.prefix == LMDFU_PREFIX_LENGTH) { + printf("The file %s contains a TI Stellaris DFU prefix with the following properties:\n", file->name); + printf("Address:\t0x%08x\n", file->lmdfu_address); + } else if (file->size.prefix == LPCDFU_PREFIX_LENGTH) { + uint8_t * prefix = file->firmware; + printf("The file %s contains a NXP unencrypted LPC DFU prefix with the following properties:\n", file->name); + printf("Size:\t%5d kiB\n", prefix[2]>>1|prefix[3]<<7); + } else if (file->size.prefix != 0) { + printf("The file %s contains an unknown prefix\n", file->name); + } + if (file->size.suffix > 0) { + printf("The file %s contains a DFU suffix with the following properties:\n", file->name); + printf("BCD device:\t0x%04X\n", file->bcdDevice); + printf("Product ID:\t0x%04X\n",file->idProduct); + printf("Vendor ID:\t0x%04X\n", file->idVendor); + printf("BCD DFU:\t0x%04X\n", file->bcdDFU); + printf("Length:\t\t%i\n", file->size.suffix); + printf("CRC:\t\t0x%08X\n", file->dwCRC); + } +} diff --git a/tools/macosx/src/dfu-util/src/dfu_file.h b/tools/macosx/src/dfu-util/src/dfu_file.h new file mode 100644 index 0000000..abebd44 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_file.h @@ -0,0 +1,60 @@ + +#ifndef DFU_FILE_H +#define DFU_FILE_H + +#include + +struct dfu_file { + /* File name */ + const char *name; + /* Pointer to file loaded into memory */ + uint8_t *firmware; + /* Different sizes */ + struct { + int total; + int prefix; + int suffix; + } size; + /* From prefix fields */ + uint32_t lmdfu_address; + /* From prefix fields */ + uint32_t prefix_type; + + /* From DFU suffix fields */ + uint32_t dwCRC; + uint16_t bcdDFU; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; +}; + +enum suffix_req { + NO_SUFFIX, + NEEDS_SUFFIX, + MAYBE_SUFFIX +}; + +enum prefix_req { + NO_PREFIX, + NEEDS_PREFIX, + MAYBE_PREFIX +}; + +enum prefix_type { + ZERO_PREFIX, + LMDFU_PREFIX, + LPCDFU_UNENCRYPTED_PREFIX +}; + +extern int verbose; + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix); +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix); + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max); +void *dfu_malloc(size_t size); +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size); +void show_suffix_and_prefix(struct dfu_file *file); + +#endif /* DFU_FILE_H */ diff --git a/tools/macosx/src/dfu-util/src/dfu_load.c b/tools/macosx/src/dfu-util/src/dfu_load.c new file mode 100644 index 0000000..64f7009 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_load.c @@ -0,0 +1,196 @@ +/* + * DFU transfer routines + * + * This is supposed to be a general DFU implementation, as specified in the + * USB DFU 1.0 and 1.1 specification. + * + * The code was originally intended to interface with a USB device running the + * "sam7dfu" firmware (see http://www.openpcd.org/) on an AT91SAM7 processor. + * + * Copyright 2007-2008 Harald Welte + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "quirks.h" + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, + int expected_size, int fd) +{ + int total_bytes = 0; + unsigned short transaction = 0; + unsigned char *buf; + int ret; + + buf = dfu_malloc(xfer_size); + + printf("Copying data from DFU device to PC\n"); + dfu_progress_bar("Upload", 0, 1); + + while (1) { + int rc; + rc = dfu_upload(dif->dev_handle, dif->interface, + xfer_size, transaction++, buf); + if (rc < 0) { + warnx("Error during upload"); + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes (wraparound)"); + + if (rc < xfer_size) { + /* last block, return */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, expected_size); + } + ret = 0; + +out_free: + dfu_progress_bar("Upload", total_bytes, total_bytes); + if (total_bytes == 0) + printf("\nFailed.\n"); + free(buf); + if (verbose) + printf("Received a total of %i bytes\n", total_bytes); + if (expected_size != 0 && total_bytes != expected_size) + errx(EX_SOFTWARE, "Unexpected number of bytes uploaded from device"); + return ret; +} + +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) +{ + int bytes_sent; + int expected_size; + unsigned char *buf; + unsigned short transaction = 0; + struct dfu_status dst; + int ret; + + printf("Copying data from PC to DFU device\n"); + + buf = file->firmware; + expected_size = file->size.total - file->size.suffix; + bytes_sent = 0; + + dfu_progress_bar("Download", 0, 1); + while (bytes_sent < expected_size) { + int bytes_left; + int chunk_size; + + bytes_left = expected_size - bytes_sent; + if (bytes_left < xfer_size) + chunk_size = bytes_left; + else + chunk_size = xfer_size; + + ret = dfu_download(dif->dev_handle, dif->interface, + chunk_size, transaction++, chunk_size ? buf : NULL); + if (ret < 0) { + warnx("Error during download"); + goto out; + } + bytes_sent += chunk_size; + buf += chunk_size; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + goto out; + } + + if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE || + dst.bState == DFU_STATE_dfuERROR) + break; + + /* Wait while device executes flashing */ + milli_sleep(dst.bwPollTimeout); + + } while (1); + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + ret = -1; + goto out; + } + dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left); + } + + /* send one zero sized download request to signalize end */ + ret = dfu_download(dif->dev_handle, dif->interface, + 0, transaction, NULL); + if (ret < 0) { + errx(EX_IOERR, "Error sending completion packet"); + goto out; + } + + dfu_progress_bar("Download", bytes_sent, bytes_sent); + + if (verbose) + printf("Sent a total of %i bytes\n", bytes_sent); + +get_status: + /* Transition to MANIFEST_SYNC state */ + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + warnx("unable to read DFU status after completion"); + goto out; + } + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + + milli_sleep(dst.bwPollTimeout); + + /* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */ + switch (dst.bState) { + case DFU_STATE_dfuMANIFEST_SYNC: + case DFU_STATE_dfuMANIFEST: + /* some devices (e.g. TAS1020b) need some time before we + * can obtain the status */ + milli_sleep(1000); + goto get_status; + break; + case DFU_STATE_dfuIDLE: + break; + } + printf("Done!\n"); + +out: + return bytes_sent; +} diff --git a/tools/macosx/src/dfu-util/src/dfu_load.h b/tools/macosx/src/dfu-util/src/dfu_load.h new file mode 100644 index 0000000..be23e9b --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_load.h @@ -0,0 +1,7 @@ +#ifndef DFU_LOAD_H +#define DFU_LOAD_H + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, int expected_size, int fd); +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file); + +#endif /* DFU_LOAD_H */ diff --git a/tools/macosx/src/dfu-util/src/dfu_util.c b/tools/macosx/src/dfu-util/src/dfu_util.c new file mode 100644 index 0000000..b94c7cc --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_util.c @@ -0,0 +1,346 @@ +/* + * Functions for detecting DFU USB entities + * + * Written by Harald Welte + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013 Hans Petter Selasky + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +/* + * Look for a descriptor in a concatenated descriptor list. Will + * return upon the first match of the given descriptor type. Returns length of + * found descriptor, limited to res_size + */ +static int find_descriptor(const uint8_t *desc_list, int list_len, + uint8_t desc_type, void *res_buf, int res_size) +{ + int p = 0; + + if (list_len < 2) + return (-1); + + while (p + 1 < list_len) { + int desclen; + + desclen = (int) desc_list[p]; + if (desclen == 0) { + warnx("Invalid descriptor list"); + return -1; + } + if (desc_list[p + 1] == desc_type) { + if (desclen > res_size) + desclen = res_size; + if (p + desclen > list_len) + desclen = list_len - p; + memcpy(res_buf, &desc_list[p], desclen); + return desclen; + } + p += (int) desc_list[p]; + } + return -1; +} + +static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) +{ + struct usb_dfu_func_descriptor func_dfu; + libusb_device_handle *devh; + struct dfu_if *pdfu; + struct libusb_config_descriptor *cfg; + const struct libusb_interface_descriptor *intf; + const struct libusb_interface *uif; + char alt_name[MAX_DESC_STR_LEN + 1]; + char serial_name[MAX_DESC_STR_LEN + 1]; + int cfg_idx; + int intf_idx; + int alt_idx; + int ret; + int has_dfu; + + for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { + memset(&func_dfu, 0, sizeof(func_dfu)); + has_dfu = 0; + + ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); + if (ret != 0) + return; + if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { + libusb_free_config_descriptor(cfg); + continue; + } + + /* + * In some cases, noticably FreeBSD if uid != 0, + * the configuration descriptors are empty + */ + if (!cfg) + return; + + ret = find_descriptor(cfg->extra, cfg->extra_length, + USB_DT_DFU, &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; + alt_idx++) { + intf = &uif->altsetting[alt_idx]; + + ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, + &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + has_dfu = 1; + } + } + if (has_dfu) { + /* + * Finally try to retrieve it requesting the + * device directly This is not supported on + * all devices for non-standard types + */ + if (libusb_open(dev, &devh) == 0) { + ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, + (void *)&func_dfu, sizeof(func_dfu)); + libusb_close(devh); + if (ret > -1) + goto found_dfu; + } + warnx("Device has DFU interface, " + "but has no DFU functional descriptor"); + + /* fake version 1.0 */ + func_dfu.bLength = 7; + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + goto found_dfu; + } + libusb_free_config_descriptor(cfg); + continue; + +found_dfu: + if (func_dfu.bLength == 7) { + printf("Deducing device DFU version from functional descriptor " + "length\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + } else if (func_dfu.bLength < 9) { + printf("Error obtaining DFU functional descriptor\n"); + printf("Please report this as a bug!\n"); + printf("Warning: Assuming DFU version 1.0\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + printf("Warning: Transfer size can not be detected\n"); + func_dfu.wTransferSize = 0; + } + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + if (match_iface_index > -1 && match_iface_index != intf_idx) + continue; + + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; + alt_idx < uif->num_altsetting; alt_idx++) { + int dfu_mode; + + intf = &uif->altsetting[alt_idx]; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + dfu_mode = (intf->bInterfaceProtocol == 2); + /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ + if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) + dfu_mode = 1; + + if (dfu_mode && + match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) + continue; + + if (dfu_mode) { + if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || + (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { + continue; + } + } else { + if ((match_vendor >= 0 && match_vendor != desc->idVendor) || + (match_product >= 0 && match_product != desc->idProduct)) { + continue; + } + } + + if (libusb_open(dev, &devh)) { + warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); + break; + } + if (intf->iInterface != 0) + ret = libusb_get_string_descriptor_ascii(devh, + intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(alt_name, "UNKNOWN"); + if (desc->iSerialNumber != 0) + ret = libusb_get_string_descriptor_ascii(devh, + desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(serial_name, "UNKNOWN"); + libusb_close(devh); + + if (dfu_mode && + match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) + continue; + + if (dfu_mode) { + if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) + continue; + } else { + if (match_serial != NULL && strcmp(match_serial, serial_name)) + continue; + } + + pdfu = dfu_malloc(sizeof(*pdfu)); + + memset(pdfu, 0, sizeof(*pdfu)); + + pdfu->func_dfu = func_dfu; + pdfu->dev = libusb_ref_device(dev); + pdfu->quirks = get_quirks(desc->idVendor, + desc->idProduct, desc->bcdDevice); + pdfu->vendor = desc->idVendor; + pdfu->product = desc->idProduct; + pdfu->bcdDevice = desc->bcdDevice; + pdfu->configuration = cfg->bConfigurationValue; + pdfu->interface = intf->bInterfaceNumber; + pdfu->altsetting = intf->bAlternateSetting; + pdfu->devnum = libusb_get_device_address(dev); + pdfu->busnum = libusb_get_bus_number(dev); + pdfu->alt_name = strdup(alt_name); + if (pdfu->alt_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + pdfu->serial_name = strdup(serial_name); + if (pdfu->serial_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + if (dfu_mode) + pdfu->flags |= DFU_IFF_DFU; + if (pdfu->quirks & QUIRK_FORCE_DFU11) { + pdfu->func_dfu.bcdDFUVersion = + libusb_cpu_to_le16(0x0110); + } + pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; + + /* queue into list */ + pdfu->next = dfu_root; + dfu_root = pdfu; + } + } + libusb_free_config_descriptor(cfg); + } +} + +void probe_devices(libusb_context *ctx) +{ + libusb_device **list; + ssize_t num_devs; + ssize_t i; + + num_devs = libusb_get_device_list(ctx, &list); + for (i = 0; i < num_devs; ++i) { + struct libusb_device_descriptor desc; + struct libusb_device *dev = list[i]; + + if (match_bus > -1 && match_bus != libusb_get_bus_number(dev)) + continue; + if (match_device > -1 && match_device != libusb_get_device_address(dev)) + continue; + if (libusb_get_device_descriptor(dev, &desc)) + continue; + probe_configuration(dev, &desc); + } + libusb_free_device_list(list, 0); +} + +void disconnect_devices(void) +{ + struct dfu_if *pdfu; + struct dfu_if *prev = NULL; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) { + free(prev); + libusb_unref_device(pdfu->dev); + free(pdfu->alt_name); + free(pdfu->serial_name); + prev = pdfu; + } + free(prev); + dfu_root = NULL; +} + +void print_dfu_if(struct dfu_if *dfu_if) +{ + printf("Found %s: [%04x:%04x] ver=%04x, devnum=%u, cfg=%u, intf=%u, " + "alt=%u, name=\"%s\", serial=\"%s\"\n", + dfu_if->flags & DFU_IFF_DFU ? "DFU" : "Runtime", + dfu_if->vendor, dfu_if->product, + dfu_if->bcdDevice, dfu_if->devnum, + dfu_if->configuration, dfu_if->interface, + dfu_if->altsetting, dfu_if->alt_name, + dfu_if->serial_name); +} + +/* Walk the device tree and print out DFU devices */ +void list_dfu_interfaces(void) +{ + struct dfu_if *pdfu; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) + print_dfu_if(pdfu); +} diff --git a/tools/macosx/src/dfu-util/src/dfu_util.h b/tools/macosx/src/dfu-util/src/dfu_util.h new file mode 100644 index 0000000..fc0c19d --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfu_util.h @@ -0,0 +1,36 @@ +#ifndef DFU_UTIL_H +#define DFU_UTIL_H + +/* USB string descriptor should contain max 126 UTF-16 characters + * but 253 would even accomodate any UTF-8 encoding */ +#define MAX_DESC_STR_LEN 253 + +enum mode { + MODE_NONE, + MODE_VERSION, + MODE_LIST, + MODE_DETACH, + MODE_UPLOAD, + MODE_DOWNLOAD +}; + +extern struct dfu_if *dfu_root; +extern int match_bus; +extern int match_device; +extern int match_vendor; +extern int match_product; +extern int match_vendor_dfu; +extern int match_product_dfu; +extern int match_config_index; +extern int match_iface_index; +extern int match_iface_alt_index; +extern const char *match_iface_alt_name; +extern const char *match_serial; +extern const char *match_serial_dfu; + +void probe_devices(libusb_context *); +void disconnect_devices(void); +void print_dfu_if(struct dfu_if *); +void list_dfu_interfaces(void); + +#endif /* DFU_UTIL_H */ diff --git a/tools/macosx/src/dfu-util/src/dfuse.c b/tools/macosx/src/dfu-util/src/dfuse.c new file mode 100644 index 0000000..fce29fe --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfuse.c @@ -0,0 +1,652 @@ +/* + * DfuSe specific functions + * + * This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) + * The DfuSe file format is described in ST document UM0391. + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfuse.h" +#include "dfuse_mem.h" + +#define DFU_TIMEOUT 5000 + +extern int verbose; +static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ +static struct memsegment *mem_layout; +static unsigned int dfuse_address = 0; +static unsigned int dfuse_length = 0; +static int dfuse_force = 0; +static int dfuse_leave = 0; +static int dfuse_unprotect = 0; +static int dfuse_mass_erase = 0; + +unsigned int quad2uint(unsigned char *p) +{ + return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); +} + +void dfuse_parse_options(const char *options) +{ + char *end; + const char *endword; + unsigned int number; + + /* address, possibly empty, must be first */ + if (*options != ':') { + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); /* GNU strchrnul */ + + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_address = number; + } else { + errx(EX_IOERR, "Invalid dfuse address: %s", options); + } + options = endword; + } + + while (*options) { + if (*options == ':') { + options++; + continue; + } + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); + + if (!strncmp(options, "force", endword - options)) { + dfuse_force++; + options += 5; + continue; + } + if (!strncmp(options, "leave", endword - options)) { + dfuse_leave = 1; + options += 5; + continue; + } + if (!strncmp(options, "unprotect", endword - options)) { + dfuse_unprotect = 1; + options += 9; + continue; + } + if (!strncmp(options, "mass-erase", endword - options)) { + dfuse_mass_erase = 1; + options += 10; + continue; + } + + /* any valid number is interpreted as upload length */ + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_length = number; + } else { + errx(EX_IOERR, "Invalid dfuse modifier: %s", options); + } + options = endword; + } +} + +/* DFU_UPLOAD request for DfuSe 1.1a */ +int dfuse_upload(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_msg returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DFU_DNLOAD request for DfuSe 1.1a */ +int dfuse_download(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_transfer returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DfuSe only commands */ +/* Leaves the device in dfuDNLOAD-IDLE state */ +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command) +{ + const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", + "MASS_ERASE", "READ_UNPROTECT"}; + unsigned char buf[5]; + int length; + int ret; + struct dfu_status dst; + int firstpoll = 1; + + if (command == ERASE_PAGE) { + struct memsegment *segment; + int page_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { + errx(EX_IOERR, "Page at 0x%08x can not be erased", + address); + } + page_size = segment->pagesize; + if (verbose > 1) + printf("Erasing page size %i at address 0x%08x, page " + "starting at 0x%08x\n", page_size, address, + address & ~(page_size - 1)); + buf[0] = 0x41; /* Erase command */ + length = 5; + last_erased_page = address & ~(page_size - 1); + } else if (command == SET_ADDRESS) { + if (verbose > 2) + printf(" Setting address pointer to 0x%08x\n", + address); + buf[0] = 0x21; /* Set Address Pointer command */ + length = 5; + } else if (command == MASS_ERASE) { + buf[0] = 0x41; /* Mass erase command when length = 1 */ + length = 1; + } else if (command == READ_UNPROTECT) { + buf[0] = 0x92; + length = 1; + } else { + errx(EX_IOERR, "Non-supported special command %d", command); + } + buf[1] = address & 0xff; + buf[2] = (address >> 8) & 0xff; + buf[3] = (address >> 16) & 0xff; + buf[4] = (address >> 24) & 0xff; + + ret = dfuse_download(dif, length, buf, 0); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" download", + dfuse_command_name[command]); + } + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" get_status", + dfuse_command_name[command]); + } + if (firstpoll) { + firstpoll = 0; + if (dst.bState != DFU_STATE_dfuDNBUSY) { + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + errx(EX_IOERR, "Wrong state after command \"%s\" download", + dfuse_command_name[command]); + } + } + /* wait while command is executed */ + if (verbose) + printf(" Poll timeout %i ms\n", dst.bwPollTimeout); + milli_sleep(dst.bwPollTimeout); + if (command == READ_UNPROTECT) + return ret; + } while (dst.bState == DFU_STATE_dfuDNBUSY); + + if (dst.bStatus != DFU_STATUS_OK) { + errx(EX_IOERR, "%s not correctly executed", + dfuse_command_name[command]); + } + return ret; +} + +int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, + int transaction) +{ + int bytes_sent; + struct dfu_status dst; + int ret; + + ret = dfuse_download(dif, size, size ? data : NULL, transaction); + if (ret < 0) { + errx(EX_IOERR, "Error during download"); + return ret; + } + bytes_sent = ret; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + return ret; + } + milli_sleep(dst.bwPollTimeout); + } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && + dst.bState != DFU_STATE_dfuERROR && + dst.bState != DFU_STATE_dfuMANIFEST); + + if (dst.bState == DFU_STATE_dfuMANIFEST) + printf("Transitioning to dfuMANIFEST state\n"); + + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + return -1; + } + return bytes_sent; +} + +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options) +{ + int total_bytes = 0; + int upload_limit = 0; + unsigned char *buf; + int transaction; + int ret; + + buf = dfu_malloc(xfer_size); + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + if (dfuse_length) + upload_limit = dfuse_length; + if (dfuse_address) { + struct memsegment *segment; + + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) + errx(EX_IOERR, "Failed to parse memory layout"); + + segment = find_segment(mem_layout, dfuse_address); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_READABLE))) + errx(EX_IOERR, "Page at 0x%08x is not readable", + dfuse_address); + + if (!upload_limit) { + upload_limit = segment->end - dfuse_address + 1; + printf("Limiting upload to end of memory segment, " + "%i bytes\n", upload_limit); + } + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfu_abort_to_idle(dif); + } else { + /* Boot loader decides the start address, unknown to us */ + /* Use a short length to lower risk of running out of bounds */ + if (!upload_limit) + upload_limit = 0x4000; + printf("Limiting default upload to %i bytes\n", upload_limit); + } + + dfu_progress_bar("Upload", 0, 1); + + transaction = 2; + while (1) { + int rc; + + /* last chunk can be smaller than original xfer_size */ + if (upload_limit - total_bytes < xfer_size) + xfer_size = upload_limit - total_bytes; + rc = dfuse_upload(dif, xfer_size, buf, transaction++); + if (rc < 0) { + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes"); + + if (rc < xfer_size || total_bytes >= upload_limit) { + /* last block, return successfully */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, upload_limit); + } + + dfu_progress_bar("Upload", total_bytes, total_bytes); + + dfu_abort_to_idle(dif); + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + + out_free: + free(buf); + + return ret; +} + +/* Writes an element of any size to the device, taking care of page erases */ +/* returns 0 on success, otherwise -EINVAL */ +int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, + unsigned int dwElementSize, unsigned char *data, + int xfer_size) +{ + int p; + int ret; + struct memsegment *segment; + + /* Check at least that we can write to the last address */ + segment = + find_segment(mem_layout, dwElementAddress + dwElementSize - 1); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Last page at 0x%08x is not writeable", + dwElementAddress + dwElementSize - 1); + } + + dfu_progress_bar("Download", 0, 1); + + for (p = 0; p < (int)dwElementSize; p += xfer_size) { + int page_size; + unsigned int erase_address; + unsigned int address = dwElementAddress + p; + int chunk_size = xfer_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Page at 0x%08x is not writeable", + address); + } + page_size = segment->pagesize; + + /* check if this is the last chunk */ + if (p + chunk_size > (int)dwElementSize) + chunk_size = dwElementSize - p; + + /* Erase only for flash memory downloads */ + if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) { + /* erase all involved pages */ + for (erase_address = address; + erase_address < address + chunk_size; + erase_address += page_size) + if ((erase_address & ~(page_size - 1)) != + last_erased_page) + dfuse_special_command(dif, + erase_address, + ERASE_PAGE); + + if (((address + chunk_size - 1) & ~(page_size - 1)) != + last_erased_page) { + if (verbose > 2) + printf(" Chunk extends into next page," + " erase it as well\n"); + dfuse_special_command(dif, + address + chunk_size - 1, + ERASE_PAGE); + } + } + + if (verbose) { + printf(" Download from image offset " + "%08x to memory %08x-%08x, size %i\n", + p, address, address + chunk_size - 1, + chunk_size); + } else { + dfu_progress_bar("Download", p, dwElementSize); + } + + dfuse_special_command(dif, address, SET_ADDRESS); + + /* transaction = 2 for no address offset */ + ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2); + if (ret != chunk_size) { + errx(EX_IOERR, "Failed to write whole chunk: " + "%i of %i bytes", ret, chunk_size); + return -EINVAL; + } + } + if (!verbose) + dfu_progress_bar("Download", dwElementSize, dwElementSize); + return 0; +} + +static void +dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) +{ + if (size > *rem) { + errx(EX_IOERR, "Corrupt DfuSe file: " + "Cannot read %d bytes from %d bytes", size, *rem); + } + if (dst != NULL) + memcpy(dst, *src, size); + (*src) += size; + (*rem) -= size; +} + +/* Download raw binary file to DfuSe device */ +int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file, unsigned int start_address) +{ + unsigned int dwElementAddress; + unsigned int dwElementSize; + unsigned char *data; + int ret; + + dwElementAddress = start_address; + dwElementSize = file->size.total - + file->size.suffix - file->size.prefix; + + printf("Downloading to address = 0x%08x, size = %i\n", + dwElementAddress, dwElementSize); + + data = file->firmware + file->size.prefix; + + ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, + xfer_size); + if (ret != 0) + goto out_free; + + printf("File downloaded successfully\n"); + ret = dwElementSize; + + out_free: + return ret; +} + +/* Parse a DfuSe file and download contents to device */ +int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file) +{ + uint8_t dfuprefix[11]; + uint8_t targetprefix[274]; + uint8_t elementheader[8]; + int image; + int element; + int bTargets; + int bAlternateSetting; + int dwNbElements; + unsigned int dwElementAddress; + unsigned int dwElementSize; + uint8_t *data; + int ret; + int rem; + int bFirstAddressSaved = 0; + + rem = file->size.total - file->size.prefix - file->size.suffix; + data = file->firmware + file->size.prefix; + + /* Must be larger than a minimal DfuSe header and suffix */ + if (rem < (int)(sizeof(dfuprefix) + + sizeof(targetprefix) + sizeof(elementheader))) { + errx(EX_SOFTWARE, "File too small for a DfuSe file"); + } + + dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); + + if (strncmp((char *)dfuprefix, "DfuSe", 5)) { + errx(EX_IOERR, "No valid DfuSe signature"); + return -EINVAL; + } + if (dfuprefix[5] != 0x01) { + errx(EX_IOERR, "DFU format revision %i not supported", + dfuprefix[5]); + return -EINVAL; + } + bTargets = dfuprefix[10]; + printf("file contains %i DFU images\n", bTargets); + + for (image = 1; image <= bTargets; image++) { + printf("parsing DFU image %i\n", image); + dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); + if (strncmp((char *)targetprefix, "Target", 6)) { + errx(EX_IOERR, "No valid target signature"); + return -EINVAL; + } + bAlternateSetting = targetprefix[6]; + dwNbElements = quad2uint((unsigned char *)targetprefix + 270); + printf("image for alternate setting %i, ", bAlternateSetting); + printf("(%i elements, ", dwNbElements); + printf("total size = %i)\n", + quad2uint((unsigned char *)targetprefix + 266)); + if (bAlternateSetting != dif->altsetting) + printf("Warning: Image does not match current alternate" + " setting.\n" + "Please rerun with the correct -a option setting" + " to download this image!\n"); + for (element = 1; element <= dwNbElements; element++) { + printf("parsing element %i, ", element); + dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); + dwElementAddress = + quad2uint((unsigned char *)elementheader); + dwElementSize = + quad2uint((unsigned char *)elementheader + 4); + printf("address = 0x%08x, ", dwElementAddress); + printf("size = %i\n", dwElementSize); + + if (!bFirstAddressSaved) { + bFirstAddressSaved = 1; + dfuse_address = dwElementAddress; + } + /* sanity check */ + if ((int)dwElementSize > rem) + errx(EX_SOFTWARE, "File too small for element size"); + + if (bAlternateSetting == dif->altsetting) { + ret = dfuse_dnload_element(dif, dwElementAddress, + dwElementSize, data, xfer_size); + } else { + ret = 0; + } + + /* advance read pointer */ + dfuse_memcpy(NULL, &data, &rem, dwElementSize); + + if (ret != 0) + return ret; + } + } + + if (rem != 0) + warnx("%d bytes leftover", rem); + + printf("done parsing DfuSe file\n"); + + return 0; +} + +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options) +{ + int ret; + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) { + errx(EX_IOERR, "Failed to parse memory layout"); + } + if (dfuse_unprotect) { + if (!dfuse_force) { + errx(EX_IOERR, "The read unprotect command " + "will erase the flash memory" + "and can only be used with force\n"); + } + dfuse_special_command(dif, 0, READ_UNPROTECT); + printf("Device disconnects, erases flash and resets now\n"); + exit(0); + } + if (dfuse_mass_erase) { + if (!dfuse_force) { + errx(EX_IOERR, "The mass erase command " + "can only be used with force"); + } + printf("Performing mass erase, this can take a moment\n"); + dfuse_special_command(dif, 0, MASS_ERASE); + } + if (dfuse_address) { + if (file->bcdDFU == 0x11a) { + errx(EX_IOERR, "This is a DfuSe file, not " + "meant for raw download"); + } + ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); + } else { + if (file->bcdDFU != 0x11a) { + warnx("Only DfuSe file version 1.1a is supported"); + errx(EX_IOERR, "(for raw binary download, use the " + "--dfuse-address option)"); + } + ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); + } + free_segment_list(mem_layout); + + dfu_abort_to_idle(dif); + + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + return ret; +} diff --git a/tools/macosx/src/dfu-util/src/dfuse.h b/tools/macosx/src/dfu-util/src/dfuse.h new file mode 100644 index 0000000..ed1108c --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfuse.h @@ -0,0 +1,35 @@ +/* This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (Document UM0391) + * + * (C) 2010-2012 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_H +#define DFUSE_H + +#include "dfu.h" + +enum dfuse_command { SET_ADDRESS, ERASE_PAGE, MASS_ERASE, READ_UNPROTECT }; + +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command); +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options); +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options); + +#endif /* DFUSE_H */ diff --git a/tools/macosx/src/dfu-util/src/dfuse_mem.c b/tools/macosx/src/dfu-util/src/dfuse_mem.c new file mode 100644 index 0000000..a91aacf --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfuse_mem.c @@ -0,0 +1,198 @@ +/* + * Helper functions for reading the memory map of a device + * following the ST DfuSe 1.1a specification. + * + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" +#include "dfuse_mem.h" + +int add_segment(struct memsegment **segment_list, struct memsegment segment) +{ + struct memsegment *new_element; + + new_element = dfu_malloc(sizeof(struct memsegment)); + *new_element = segment; + new_element->next = NULL; + + if (*segment_list == NULL) + /* list can be empty on first call */ + *segment_list = new_element; + else { + struct memsegment *next_element; + + /* find last element in list */ + next_element = *segment_list; + while (next_element->next != NULL) + next_element = next_element->next; + next_element->next = new_element; + } + return 0; +} + +struct memsegment *find_segment(struct memsegment *segment_list, + unsigned int address) +{ + while (segment_list != NULL) { + if (segment_list->start <= address && + segment_list->end >= address) + return segment_list; + segment_list = segment_list->next; + } + return NULL; +} + +void free_segment_list(struct memsegment *segment_list) +{ + struct memsegment *next_element; + + while (segment_list->next != NULL) { + next_element = segment_list->next; + free(segment_list); + segment_list = next_element; + } + free(segment_list); +} + +/* Parse memory map from interface descriptor string + * encoded as per ST document UM0424 section 4.3.2. + */ +struct memsegment *parse_memory_layout(char *intf_desc) +{ + + char multiplier, memtype; + unsigned int address; + int sectors, size; + char *name, *typestring; + int ret; + int count = 0; + char separator; + int scanned; + struct memsegment *segment_list = NULL; + struct memsegment segment; + + name = dfu_malloc(strlen(intf_desc)); + + ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned); + if (ret < 1) { + free(name); + warnx("Could not read name, sscanf returned %d", ret); + return NULL; + } + printf("DfuSe interface name: \"%s\"\n", name); + + intf_desc += scanned; + typestring = dfu_malloc(strlen(intf_desc)); + + while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned), + ret > 0) { + + intf_desc += scanned; + while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n", + §ors, &size, &multiplier, typestring, + &scanned), ret > 2) { + intf_desc += scanned; + + count++; + memtype = 0; + if (ret == 4) { + if (strlen(typestring) == 1 + && typestring[0] != '/') + memtype = typestring[0]; + else { + warnx("Parsing type identifier '%s' " + "failed for segment %i", + typestring, count); + continue; + } + } + + /* Quirk for STM32F4 devices */ + if (strcmp(name, "Device Feature") == 0) + memtype = 'e'; + + switch (multiplier) { + case 'B': + break; + case 'K': + size *= 1024; + break; + case 'M': + size *= 1024 * 1024; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + if (!memtype) { + warnx("Non-valid multiplier '%c', " + "interpreted as type " + "identifier instead", + multiplier); + memtype = multiplier; + break; + } + /* fallthrough if memtype was already set */ + default: + warnx("Non-valid multiplier '%c', " + "assuming bytes", multiplier); + } + + if (!memtype) { + warnx("No valid type for segment %d\n", count); + continue; + } + + segment.start = address; + segment.end = address + sectors * size - 1; + segment.pagesize = size; + segment.memtype = memtype & 7; + add_segment(&segment_list, segment); + + if (verbose) + printf("Memory segment at 0x%08x %3d x %4d = " + "%5d (%s%s%s)\n", + address, sectors, size, sectors * size, + memtype & DFUSE_READABLE ? "r" : "", + memtype & DFUSE_ERASABLE ? "e" : "", + memtype & DFUSE_WRITEABLE ? "w" : ""); + + address += sectors * size; + + separator = *intf_desc; + if (separator == ',') + intf_desc += 1; + else + break; + } /* while per segment */ + + } /* while per address */ + free(name); + free(typestring); + + return segment_list; +} diff --git a/tools/macosx/src/dfu-util/src/dfuse_mem.h b/tools/macosx/src/dfu-util/src/dfuse_mem.h new file mode 100644 index 0000000..0181f0c --- /dev/null +++ b/tools/macosx/src/dfu-util/src/dfuse_mem.h @@ -0,0 +1,44 @@ +/* Helper functions for reading the memory map in a device + * following the ST DfuSe 1.1a specification. + * + * (C) 2011 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_MEM_H +#define DFUSE_MEM_H + +#define DFUSE_READABLE 1 +#define DFUSE_ERASABLE 2 +#define DFUSE_WRITEABLE 4 + +struct memsegment { + unsigned int start; + unsigned int end; + int pagesize; + int memtype; + struct memsegment *next; +}; + +int add_segment(struct memsegment **list, struct memsegment new_element); + +struct memsegment *find_segment(struct memsegment *list, unsigned int address); + +void free_segment_list(struct memsegment *list); + +struct memsegment *parse_memory_layout(char *intf_desc_str); + +#endif /* DFUSE_MEM_H */ diff --git a/tools/macosx/src/dfu-util/src/main.c b/tools/macosx/src/dfu-util/src/main.c new file mode 100644 index 0000000..acaed2f --- /dev/null +++ b/tools/macosx/src/dfu-util/src/main.c @@ -0,0 +1,699 @@ +/* + * dfu-util + * + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013-2014 Hans Petter Selasky + * + * Written by Harald Welte + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +int verbose = 0; + +struct dfu_if *dfu_root = NULL; + +int match_bus = -1; +int match_device = -1; +int match_vendor = -1; +int match_product = -1; +int match_vendor_dfu = -1; +int match_product_dfu = -1; +int match_config_index = -1; +int match_iface_index = -1; +int match_iface_alt_index = -1; +const char *match_iface_alt_name = NULL; +const char *match_serial = NULL; +const char *match_serial_dfu = NULL; + +static int parse_match_value(const char *str, int default_value) +{ + char *remainder; + int value; + + if (str == NULL) { + value = default_value; + } else if (*str == '*') { + value = -1; /* Match anything */ + } else if (*str == '-') { + value = 0x10000; /* Impossible vendor/product ID */ + } else { + value = strtoul(str, &remainder, 16); + if (remainder == str) { + value = default_value; + } + } + return value; +} + +static void parse_vendprod(const char *str) +{ + const char *comma; + const char *colon; + + /* Default to match any DFU device in runtime or DFU mode */ + match_vendor = -1; + match_product = -1; + match_vendor_dfu = -1; + match_product_dfu = -1; + + comma = strchr(str, ','); + if (comma == str) { + /* DFU mode vendor/product being specified without any runtime + * vendor/product specification, so don't match any runtime device */ + match_vendor = match_product = 0x10000; + } else { + colon = strchr(str, ':'); + if (colon != NULL) { + ++colon; + if ((comma != NULL) && (colon > comma)) { + colon = NULL; + } + } + match_vendor = parse_match_value(str, match_vendor); + match_product = parse_match_value(colon, match_product); + if (comma != NULL) { + /* Both runtime and DFU mode vendor/product specifications are + * available, so default DFU mode match components to the given + * runtime match components */ + match_vendor_dfu = match_vendor; + match_product_dfu = match_product; + } + } + if (comma != NULL) { + ++comma; + colon = strchr(comma, ':'); + if (colon != NULL) { + ++colon; + } + match_vendor_dfu = parse_match_value(comma, match_vendor_dfu); + match_product_dfu = parse_match_value(colon, match_product_dfu); + } +} + +static void parse_serial(char *str) +{ + char *comma; + + match_serial = str; + comma = strchr(str, ','); + if (comma == NULL) { + match_serial_dfu = match_serial; + } else { + *comma++ = 0; + match_serial_dfu = comma; + } + if (*match_serial == 0) match_serial = NULL; + if (*match_serial_dfu == 0) match_serial_dfu = NULL; +} + +#ifdef HAVE_USBPATH_H + +static int resolve_device_path(char *path) +{ + int res; + + res = usb_path2devnum(path); + if (res < 0) + return -EINVAL; + if (!res) + return 0; + + match_bus = atoi(path); + match_device = res; + + return 0; +} + +#else /* HAVE_USBPATH_H */ + +static int resolve_device_path(char *path) +{ + (void)path; /* Eliminate unused variable warning */ + errx(EX_SOFTWARE, "USB device paths are not supported by this dfu-util.\n"); +} + +#endif /* !HAVE_USBPATH_H */ + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-util [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -v --verbose\t\t\tPrint verbose debug statements\n" + " -l --list\t\t\tList currently attached DFU capable devices\n"); + fprintf(stderr, " -e --detach\t\t\tDetach currently attached DFU capable devices\n" + " -E --detach-delay seconds\tTime to wait before reopening a device after detach\n" + " -d --device :[,:]\n" + "\t\t\t\tSpecify Vendor/Product ID(s) of DFU device\n" + " -p --path \tSpecify path to DFU device\n" + " -c --cfg \t\tSpecify the Configuration of DFU device\n" + " -i --intf \t\tSpecify the DFU Interface number\n" + " -S --serial [,]\n" + "\t\t\t\tSpecify Serial String of DFU device\n" + " -a --alt \t\tSpecify the Altsetting of the DFU Interface\n" + "\t\t\t\tby name or by number\n"); + fprintf(stderr, " -t --transfer-size \tSpecify the number of bytes per USB Transfer\n" + " -U --upload \t\tRead firmware from device into \n" + " -Z --upload-size \tSpecify the expected upload size in bytes\n" + " -D --download \t\tWrite firmware from into device\n" + " -R --reset\t\t\tIssue USB Reset signalling once we're finished\n" + " -s --dfuse-address

\tST DfuSe mode, specify target address for\n" + "\t\t\t\traw file download or upload. Not applicable for\n" + "\t\t\t\tDfuSe file (.dfu) downloads\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf(PACKAGE_STRING "\n\n"); + printf("Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\n" + "Copyright 2010-2014 Tormod Volden and Stefan Schmidt\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to " PACKAGE_BUGREPORT "\n\n"); +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "verbose", 0, 0, 'v' }, + { "list", 0, 0, 'l' }, + { "detach", 0, 0, 'e' }, + { "detach-delay", 1, 0, 'E' }, + { "device", 1, 0, 'd' }, + { "path", 1, 0, 'p' }, + { "configuration", 1, 0, 'c' }, + { "cfg", 1, 0, 'c' }, + { "interface", 1, 0, 'i' }, + { "intf", 1, 0, 'i' }, + { "altsetting", 1, 0, 'a' }, + { "alt", 1, 0, 'a' }, + { "serial", 1, 0, 'S' }, + { "transfer-size", 1, 0, 't' }, + { "upload", 1, 0, 'U' }, + { "upload-size", 1, 0, 'Z' }, + { "download", 1, 0, 'D' }, + { "reset", 0, 0, 'R' }, + { "dfuse-address", 1, 0, 's' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int expected_size = 0; + unsigned int transfer_size = 0; + enum mode mode = MODE_NONE; + struct dfu_status status; + libusb_context *ctx; + struct dfu_file file; + char *end; + int final_reset = 0; + int ret; + int dfuse_device = 0; + int fd; + const char *dfuse_options = NULL; + int detach_delay = 5; + uint16_t runtime_vendor; + uint16_t runtime_product; + + memset(&file, 0, sizeof(file)); + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + mode = MODE_VERSION; + break; + case 'v': + verbose++; + break; + case 'l': + mode = MODE_LIST; + break; + case 'e': + mode = MODE_DETACH; + match_iface_alt_index = 0; + match_iface_index = 0; + break; + case 'E': + detach_delay = atoi(optarg); + break; + case 'd': + parse_vendprod(optarg); + break; + case 'p': + /* Parse device path */ + ret = resolve_device_path(optarg); + if (ret < 0) + errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); + if (!ret) + errx(EX_SOFTWARE, "Cannot find '%s'", optarg); + break; + case 'c': + /* Configuration */ + match_config_index = atoi(optarg); + break; + case 'i': + /* Interface */ + match_iface_index = atoi(optarg); + break; + case 'a': + /* Interface Alternate Setting */ + match_iface_alt_index = strtoul(optarg, &end, 0); + if (*end) { + match_iface_alt_name = optarg; + match_iface_alt_index = -1; + } + break; + case 'S': + parse_serial(optarg); + break; + case 't': + transfer_size = atoi(optarg); + break; + case 'U': + mode = MODE_UPLOAD; + file.name = optarg; + break; + case 'Z': + expected_size = atoi(optarg); + break; + case 'D': + mode = MODE_DOWNLOAD; + file.name = optarg; + break; + case 'R': + final_reset = 1; + break; + case 's': + dfuse_options = optarg; + break; + default: + help(); + break; + } + } + + print_version(); + if (mode == MODE_VERSION) { + exit(0); + } + + if (mode == MODE_NONE) { + fprintf(stderr, "You need to specify one of -D or -U\n"); + help(); + } + + if (match_config_index == 0) { + /* Handle "-c 0" (unconfigured device) as don't care */ + match_config_index = -1; + } + + if (mode == MODE_DOWNLOAD) { + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + /* If the user didn't specify product and/or vendor IDs to match, + * use any IDs from the file suffix for device matching */ + if (match_vendor < 0 && file.idVendor != 0xffff) { + match_vendor = file.idVendor; + printf("Match vendor ID from file: %04x\n", match_vendor); + } + if (match_product < 0 && file.idProduct != 0xffff) { + match_product = file.idProduct; + printf("Match product ID from file: %04x\n", match_product); + } + } + + ret = libusb_init(&ctx); + if (ret) + errx(EX_IOERR, "unable to initialize libusb: %i", ret); + + if (verbose > 2) { + libusb_set_debug(ctx, 255); + } + + probe_devices(ctx); + + if (mode == MODE_LIST) { + list_dfu_interfaces(); + exit(0); + } + + if (dfu_root == NULL) { + errx(EX_IOERR, "No DFU capable USB device available"); + } else if (dfu_root->next != NULL) { + /* We cannot safely support more than one DFU capable device + * with same vendor/product ID, since during DFU we need to do + * a USB bus reset, after which the target device will get a + * new address */ + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device\n"); + } + + /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ + + printf("Opening DFU capable USB device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) + errx(EX_IOERR, "Cannot open device"); + + printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); + + printf("Run-time device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + /* Transition from run-Time mode to DFU mode */ + if (!(dfu_root->flags & DFU_IFF_DFU)) { + int err; + /* In the 'first round' during runtime mode, there can only be one + * DFU Interface descriptor according to the DFU Spec. */ + + /* FIXME: check if the selected device really has only one */ + + runtime_vendor = dfu_root->vendor; + runtime_product = dfu_root->product; + + printf("Claiming USB DFU Runtime Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface %d", + dfu_root->interface); + } + + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { + errx(EX_IOERR, "Cannot set alt interface zero"); + } + + printf("Determining device status: "); + + err = dfu_get_status(dfu_root, &status); + if (err == LIBUSB_ERROR_PIPE) { + printf("Device does not implement get_status, assuming appIDLE\n"); + status.bStatus = DFU_STATUS_OK; + status.bwPollTimeout = 0; + status.bState = DFU_STATE_appIDLE; + status.iString = 0; + } else if (err < 0) { + errx(EX_IOERR, "error get_status"); + } else { + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + } + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + printf("Device really in Runtime Mode, send DFU " + "detach request...\n"); + if (dfu_detach(dfu_root->dev_handle, + dfu_root->interface, 1000) < 0) { + warnx("error detaching"); + } + if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { + printf("Device will detach and reattach...\n"); + } else { + printf("Resetting USB...\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) + errx(EX_IOERR, "error resetting " + "after detach"); + } + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, + dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + /* fall through */ + default: + warnx("WARNING: Runtime device already in DFU state ?!?"); + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + goto dfustate; + } + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + + if (mode == MODE_DETACH) { + libusb_exit(ctx); + exit(0); + } + + /* keeping handles open might prevent re-enumeration */ + disconnect_devices(); + + milli_sleep(detach_delay * 1000); + + /* Change match vendor and product to impossible values to force + * only DFU mode matches in the following probe */ + match_vendor = match_product = 0x10000; + + probe_devices(ctx); + + if (dfu_root == NULL) { + errx(EX_IOERR, "Lost device after RESET?"); + } else if (dfu_root->next != NULL) { + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device"); + } + + /* Check for DFU mode device */ + if (!(dfu_root->flags | DFU_IFF_DFU)) + errx(EX_SOFTWARE, "Device is not in DFU mode"); + + printf("Opening DFU USB Device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) { + errx(EX_IOERR, "Cannot open device"); + } + } else { + /* we're already in DFU mode, so we can skip the detach/reset + * procedure */ + /* If a match vendor/product was specified, use that as the runtime + * vendor/product, otherwise use the DFU mode vendor/product */ + runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; + runtime_product = match_product < 0 ? dfu_root->product : match_product; + } + +dfustate: +#if 0 + printf("Setting Configuration %u...\n", dfu_root->configuration); + if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { + errx(EX_IOERR, "Cannot set configuration"); + } +#endif + printf("Claiming USB DFU Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface"); + } + + printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { + errx(EX_IOERR, "Cannot set alternate interface"); + } + +status_again: + printf("Determining device status: "); + if (dfu_get_status(dfu_root, &status ) < 0) { + errx(EX_IOERR, "error get_status"); + } + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + errx(EX_IOERR, "Device still in Runtime Mode!"); + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + goto status_again; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + case DFU_STATE_dfuUPLOAD_IDLE: + printf("aborting previous incomplete transfer\n"); + if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "can't send DFU_ABORT"); + } + goto status_again; + break; + case DFU_STATE_dfuIDLE: + printf("dfuIDLE, continuing\n"); + break; + default: + break; + } + + if (DFU_STATUS_OK != status.bStatus ) { + printf("WARNING: DFU Status: '%s'\n", + dfu_status_to_string(status.bStatus)); + /* Clear our status & try again. */ + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) + errx(EX_IOERR, "USB communication error"); + if (dfu_get_status(dfu_root, &status) < 0) + errx(EX_IOERR, "USB communication error"); + if (DFU_STATUS_OK != status.bStatus) + errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); + + milli_sleep(status.bwPollTimeout); + } + + printf("DFU mode device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) + dfuse_device = 1; + + /* If not overridden by the user */ + if (!transfer_size) { + transfer_size = libusb_le16_to_cpu( + dfu_root->func_dfu.wTransferSize); + if (transfer_size) { + printf("Device returned transfer size %i\n", + transfer_size); + } else { + errx(EX_IOERR, "Transfer size must be specified"); + } + } + +#ifdef HAVE_GETPAGESIZE +/* autotools lie when cross-compiling for Windows using mingw32/64 */ +#ifndef __MINGW32__ + /* limitation of Linux usbdevio */ + if ((int)transfer_size > getpagesize()) { + transfer_size = getpagesize(); + printf("Limited transfer size to %i\n", transfer_size); + } +#endif /* __MINGW32__ */ +#endif /* HAVE_GETPAGESIZE */ + + if (transfer_size < dfu_root->bMaxPacketSize0) { + transfer_size = dfu_root->bMaxPacketSize0; + printf("Adjusted transfer size to %i\n", transfer_size); + } + + switch (mode) { + case MODE_UPLOAD: + /* open for "exclusive" writing */ + fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); + if (fd < 0) + err(EX_IOERR, "Cannot open file %s for writing", file.name); + + if (dfuse_device || dfuse_options) { + if (dfuse_do_upload(dfu_root, transfer_size, fd, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_upload(dfu_root, transfer_size, + expected_size, fd) < 0) { + exit(1); + } + } + close(fd); + break; + + case MODE_DOWNLOAD: + if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || + (file.idProduct != 0xffff && file.idProduct != runtime_product)) && + ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || + (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { + errx(EX_IOERR, "Error: File ID %04x:%04x does " + "not match device (%04x:%04x or %04x:%04x)", + file.idVendor, file.idProduct, + runtime_vendor, runtime_product, + dfu_root->vendor, dfu_root->product); + } + if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { + if (dfuse_do_dnload(dfu_root, transfer_size, &file, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) + exit(1); + } + break; + case MODE_DETACH: + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + warnx("can't detach"); + } + break; + default: + errx(EX_IOERR, "Unsupported mode: %u", mode); + break; + } + + if (final_reset) { + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + /* Even if detach failed, just carry on to leave the + device in a known state */ + warnx("can't detach"); + } + printf("Resetting USB to switch back to runtime mode\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { + errx(EX_IOERR, "error resetting after download"); + } + } + + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + libusb_exit(ctx); + + return (0); +} diff --git a/tools/macosx/src/dfu-util/src/portable.h b/tools/macosx/src/dfu-util/src/portable.h new file mode 100644 index 0000000..cf8d5df --- /dev/null +++ b/tools/macosx/src/dfu-util/src/portable.h @@ -0,0 +1,72 @@ + +#ifndef PORTABLE_H +#define PORTABLE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# define PACKAGE "dfu-util" +# define PACKAGE_VERSION "0.8-msvc" +# define PACKAGE_STRING "dfu-util 0.8-msvc" +# define PACKAGE_BUGREPORT "dfu-util@lists.gnumonks.org" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_FTRUNCATE +# include +#else +# include +#endif /* HAVE_FTRUNCATE */ + +#ifdef HAVE_NANOSLEEP +# include +# define milli_sleep(msec) do {\ + if (msec) {\ + struct timespec nanosleepDelay = { (msec) / 1000, ((msec) % 1000) * 1000000 };\ + nanosleep(&nanosleepDelay, NULL);\ + } } while (0) +#elif defined HAVE_WINDOWS_H +# define milli_sleep(msec) do {\ + if (msec) {\ + Sleep(msec);\ + } } while (0) +#else +# error "Can't get no sleep! Please report" +#endif /* HAVE_NANOSLEEP */ + +#ifdef HAVE_ERR +# include +#else +# include +# include +# define warnx(...) do {\ + fprintf(stderr, __VA_ARGS__);\ + fprintf(stderr, "\n"); } while (0) +# define errx(eval, ...) do {\ + warnx(__VA_ARGS__);\ + exit(eval); } while (0) +# define warn(...) do {\ + fprintf(stderr, "%s: ", strerror(errno));\ + warnx(__VA_ARGS__); } while (0) +# define err(eval, ...) do {\ + warn(__VA_ARGS__);\ + exit(eval); } while (0) +#endif /* HAVE_ERR */ + +#ifdef HAVE_SYSEXITS_H +# include +#else +# define EX_OK 0 /* successful termination */ +# define EX_USAGE 64 /* command line usage error */ +# define EX_SOFTWARE 70 /* internal software error */ +# define EX_IOERR 74 /* input/output error */ +#endif /* HAVE_SYSEXITS_H */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifndef off_t +# define off_t long int +#endif + +#endif /* PORTABLE_H */ diff --git a/tools/macosx/src/dfu-util/src/prefix.c b/tools/macosx/src/dfu-util/src/prefix.c new file mode 100644 index 0000000..be8e3fa --- /dev/null +++ b/tools/macosx/src/dfu-util/src/prefix.c @@ -0,0 +1,176 @@ +/* + * dfu-prefix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Uwe Bonnes + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-prefix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU prefix of \n" + " -D --delete \t\tDelete DFU prefix from \n" + " -a --add \t\tAdd DFU prefix to \n" + "In combination with -a:\n" + ); + fprintf(stderr, " -s --stellaris-address
Add TI Stellaris address prefix to \n" + "In combination with -D or -c:\n" + " -T --stellaris\t\tAct on TI Stellaris address prefix of \n" + "In combination with -a or -D or -c:\n" + " -L --lpc-prefix\t\tUse NXP LPC DFU prefix format\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-prefix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2014 Uwe Bonnes\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "stellaris-address", 1, 0, 's' }, + { "stellaris", 0, 0, 'T' }, + { "LPC", 0, 0, 'L' }, +}; +int main(int argc, char **argv) +{ + struct dfu_file file; + enum mode mode = MODE_NONE; + enum prefix_type type = ZERO_PREFIX; + uint32_t lmdfu_flash_address = 0; + char *end; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:s:TL", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + case 's': + lmdfu_flash_address = strtoul(optarg, &end, 0); + if (*end) { + errx(EX_IOERR, "Invalid lmdfu " + "address: %s", optarg); + } + /* fall-through */ + case 'T': + type = LMDFU_PREFIX; + break; + case 'L': + type = LPCDFU_UNENCRYPTED_PREFIX; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + if (type == ZERO_PREFIX) + errx(EX_IOERR, "Prefix type must be specified"); + dfu_load_file(&file, MAYBE_SUFFIX, NO_PREFIX); + file.lmdfu_address = lmdfu_flash_address; + file.prefix_type = type; + printf("Adding prefix to file\n"); + dfu_store_file(&file, file.size.suffix != 0, 1); + break; + + case MODE_CHECK: + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + break; + + case MODE_DEL: + dfu_load_file(&file, MAYBE_SUFFIX, NEEDS_PREFIX); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + printf("Removing prefix from file\n"); + /* if there was a suffix, rewrite it */ + dfu_store_file(&file, file.size.suffix != 0, 0); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/macosx/src/dfu-util/src/quirks.c b/tools/macosx/src/dfu-util/src/quirks.c new file mode 100644 index 0000000..de394a6 --- /dev/null +++ b/tools/macosx/src/dfu-util/src/quirks.c @@ -0,0 +1,56 @@ +/* + * Simple quirk system for dfu-util + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "quirks.h" + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) +{ + uint16_t quirks = 0; + + /* Device returns bogus bwPollTimeout values */ + if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) && + product >= PRODUCT_FREERUNNER_FIRST && + product <= PRODUCT_FREERUNNER_LAST) + quirks |= QUIRK_POLLTIMEOUT; + + if (vendor == VENDOR_VOTI && + product == PRODUCT_OPENPCD) + quirks |= QUIRK_POLLTIMEOUT; + + /* Reports wrong DFU version in DFU descriptor */ + if (vendor == VENDOR_LEAFLABS && + product == PRODUCT_MAPLE3 && + bcdDevice == 0x0200) + quirks |= QUIRK_FORCE_DFU11; + + /* old devices(bcdDevice == 0) return bogus bwPollTimeout values */ + if (vendor == VENDOR_SIEMENS && + (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) && + bcdDevice == 0) + quirks |= QUIRK_POLLTIMEOUT; + + /* M-Audio Transit returns bogus bwPollTimeout values */ + if (vendor == VENDOR_MIDIMAN && + product == PRODUCT_TRANSIT) + quirks |= QUIRK_POLLTIMEOUT; + + return (quirks); +} diff --git a/tools/macosx/src/dfu-util/src/quirks.h b/tools/macosx/src/dfu-util/src/quirks.h new file mode 100644 index 0000000..0e4b3ec --- /dev/null +++ b/tools/macosx/src/dfu-util/src/quirks.h @@ -0,0 +1,27 @@ +#ifndef DFU_QUIRKS_H +#define DFU_QUIRKS_H + +#define VENDOR_OPENMOKO 0x1d50 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_FIC 0x1457 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_VOTI 0x16c0 /* OpenPCD Reader */ +#define VENDOR_LEAFLABS 0x1eaf /* Maple */ +#define VENDOR_SIEMENS 0x0908 /* Siemens AG */ +#define VENDOR_MIDIMAN 0x0763 /* Midiman */ + +#define PRODUCT_FREERUNNER_FIRST 0x5117 +#define PRODUCT_FREERUNNER_LAST 0x5126 +#define PRODUCT_OPENPCD 0x076b +#define PRODUCT_MAPLE3 0x0003 /* rev 3 and 5 */ +#define PRODUCT_PXM40 0x02c4 /* Siemens AG, PXM 40 */ +#define PRODUCT_PXM50 0x02c5 /* Siemens AG, PXM 50 */ +#define PRODUCT_TRANSIT 0x2806 /* M-Audio Transit (Midiman) */ + +#define QUIRK_POLLTIMEOUT (1<<0) +#define QUIRK_FORCE_DFU11 (1<<1) + +/* Fallback value, works for OpenMoko */ +#define DEFAULT_POLLTIMEOUT 5 + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice); + +#endif /* DFU_QUIRKS_H */ diff --git a/tools/macosx/src/dfu-util/src/suffix.c b/tools/macosx/src/dfu-util/src/suffix.c new file mode 100644 index 0000000..0df248f --- /dev/null +++ b/tools/macosx/src/dfu-util/src/suffix.c @@ -0,0 +1,176 @@ +/* + * dfu-suffix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-suffix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU suffix of \n" + " -a --add \t\tAdd DFU suffix to \n" + " -D --delete \t\tDelete DFU suffix from \n" + " -p --pid \t\tAdd product ID into DFU suffix in \n" + " -v --vid \t\tAdd vendor ID into DFU suffix in \n" + " -d --did \t\tAdd device ID into DFU suffix in \n" + " -S --spec \t\tAdd DFU specification ID into DFU suffix in \n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-suffix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "pid", 1, 0, 'p' }, + { "vid", 1, 0, 'v' }, + { "did", 1, 0, 'd' }, + { "spec", 1, 0, 'S' }, +}; + +int main(int argc, char **argv) +{ + struct dfu_file file; + int pid, vid, did, spec; + enum mode mode = MODE_NONE; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + pid = vid = did = 0xffff; + spec = 0x0100; /* Default to bcdDFU version 1.0 */ + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:S:s:T", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'p': + pid = strtol(optarg, NULL, 16); + break; + case 'v': + vid = strtol(optarg, NULL, 16); + break; + case 'd': + did = strtol(optarg, NULL, 16); + break; + case 'S': + spec = strtol(optarg, NULL, 16); + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + if (spec != 0x0100 && spec != 0x011a) { + fprintf(stderr, "Only DFU specification 0x0100 and 0x011a supported\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + dfu_load_file(&file, NO_SUFFIX, MAYBE_PREFIX); + file.idVendor = vid; + file.idProduct = pid; + file.bcdDevice = did; + file.bcdDFU = spec; + /* always write suffix, rewrite prefix if there was one */ + dfu_store_file(&file, 1, file.size.prefix != 0); + printf("Suffix successfully added to file\n"); + break; + + case MODE_CHECK: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + break; + + case MODE_DEL: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + dfu_store_file(&file, 0, file.size.prefix != 0); + if (file.size.suffix) /* had a suffix */ + printf("Suffix successfully removed from file\n"); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/macosx/src/dfu-util/src/usb_dfu.h b/tools/macosx/src/dfu-util/src/usb_dfu.h new file mode 100644 index 0000000..660bedc --- /dev/null +++ b/tools/macosx/src/dfu-util/src/usb_dfu.h @@ -0,0 +1,99 @@ +#ifndef USB_DFU_H +#define USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * Protocol definitions for USB DFU + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define USB_DT_DFU 0x21 + +#ifdef _MSC_VER +# pragma pack(push) +# pragma pack(1) +#endif /* _MSC_VER */ +struct usb_dfu_func_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bmAttributes; +#define USB_DFU_CAN_DOWNLOAD (1 << 0) +#define USB_DFU_CAN_UPLOAD (1 << 1) +#define USB_DFU_MANIFEST_TOL (1 << 2) +#define USB_DFU_WILL_DETACH (1 << 3) + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +#ifdef _MSC_VER +}; +# pragma pack(pop) +#elif defined __GNUC__ +} __attribute__ ((packed)); +#else + #warning "No way to pack struct on this compiler? This will break!" +#endif /* _MSC_VER */ + +#define USB_DT_DFU_SIZE 9 + +#define USB_TYPE_DFU (LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE) + +/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +/* DFU_GETSTATUS bStatus values (Section 6.1.2, DFU Rev 1.1) */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10 +}; + +#endif /* USB_DFU_H */ diff --git a/tools/macosx/src/dfu-util/www/build.html b/tools/macosx/src/dfu-util/www/build.html new file mode 100644 index 0000000..f3036e4 --- /dev/null +++ b/tools/macosx/src/dfu-util/www/build.html @@ -0,0 +1,147 @@ + + + + + + + Building dfu-util from source + + + + + + + + + +
+

How to build dfu-util from source

+ +

Prerequisites for building from git

+

Mac OS X

+

+First install MacPorts (and if you are on 10.6 or older, the Java Developer Package) and then run: +

+
+	sudo port install libusb-devel git-core
+
+ +

FreeBSD

+
+	sudo pkg_add -r git pkgconf
+
+ +

Ubuntu and Debian and derivatives

+
+	sudo apt-get build-dep dfu-util
+	sudo apt-get install libusb-1.0-0-dev
+
+ +

Get the source code and build it

+

+The first time you will have to clone the git repository: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	cd dfu-util
+
+

+If you later want to update to latest git version, just run this: +

+
+	make maintainer-clean
+	git pull
+
+

+To build the source: +

+
+	./autogen.sh
+	./configure  # on most systems
+	make
+
+ +

+If you are building on Mac OS X, replace the ./configure command with: +

+
+	./configure --libdir=/opt/local/lib --includedir=/opt/local/include  # on MacOSX only
+
+ +

+Your dfu-util binary will be inside the src folder. Use it from there, or install it to /usr/local/bin by running "sudo make install". +

+ +

Cross-building for Windows

+ +

+Windows binaries can be built in a MinGW +environment, on a Windows computer or cross-hosted in another OS. +To build it on a Debian or Ubuntu host, first install build dependencies: +

+
+	sudo apt-get build-dep libusb-1.0-0 dfu-util
+	sudo apt-get install mingw32
+
+ +

+The below example builds dfu-util 0.8 and libusb 1.0.19 from unpacked release +tarballs. If you instead build from git, you will have to run "./autogen.sh" +before running the "./configure" steps. +

+ +
+mkdir -p build
+cd libusb-1.0.19
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+# WINVER workaround needed for 1.0.19 only
+make CFLAGS="-DWINVER=0x0501"
+make install
+cd ..
+
+cd dfu-util-0.8
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+make
+make install
+cd ..
+
+The build files will now be in build/bin. +

+ +

Building on Windows using MinGW

+This assumes using release tarballs or having run ./autogen.sh on +the git sources. +
+cd libusb-1.0.19
+./configure --prefix=$HOME
+# WINVER workaround needed for 1.0.19 only
+# MKDIR_P setting should not really be needed...
+make CFLAGS="-DWINVER=0x0501" MKDIR_P="mkdir -p"
+make install
+cd ..
+
+cd dfu-util-0.8
+./configure USB_CFLAGS="-I$HOME/include/libusb-1.0" \
+            USB_LIBS="-L $HOME/lib -lusb-1.0" PKG_CONFIG=true
+make
+make install
+cd ..
+
+To link libusb statically into dfu-util.exe use instead of "make": +
+make LDFLAGS=-static
+
+The built executables (and DLL) will now be under $HOME/bin. + +

+[Back to dfu-util main page] +

+ +
+ + diff --git a/tools/macosx/src/dfu-util/www/dfu-util.1.html b/tools/macosx/src/dfu-util/www/dfu-util.1.html new file mode 100644 index 0000000..62ca40b --- /dev/null +++ b/tools/macosx/src/dfu-util/www/dfu-util.1.html @@ -0,0 +1,411 @@ + + +Man page of DFU-UTIL + + +

DFU-UTIL(1)

+ +  +

NAME

+ +dfu-util - Device firmware update (DFU) USB programmer +  +

SYNOPSIS

+ + +
+
+dfu-util + +-l + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + + +
+dfu-util + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + +[-t + +size] + +[-Z + +size] + +[-s + +address] + +[-R] + +[-D|-U + +file] + + +
+dfu-util + +[-hV] + +
+  +

DESCRIPTION

+ +dfu-util + +is a program that implements the host (computer) side of the USB DFU +(Universal Serial Bus Device Firmware Upgrade) protocol. +

+dfu-util communicates with devices that implement the device side of the +USB DFU protocol, and is often used to upgrade the firmware of such +devices. +  +

OPTIONS

+ +
+
-l, --list + +
+List the currently attached DFU capable USB devices. +
-d, --device [Run-Time VENDOR]:[Run-Time PRODUCT][,[DFU Mode VENDOR]:[DFU Mode PRODUCT]] + +
+
+Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device +to work with. VENDOR and PRODUCT are hexadecimal numbers (no prefix +needed), "*" (match any), or "-" (match nothing). By default, any DFU capable +device in either run-time or DFU mode will be considered. +

+If you only have one standards-compliant DFU device attached to your computer, +this parameter is optional. However, as soon as you have multiple DFU devices +connected, dfu-util will detect this and abort, asking you to specify which +device to use. +

+If only run-time IDs are specified (e.g. "--device 1457:51ab"), then in +addition to the specified run-time IDs, any DFU mode devices will also be +considered. This is beneficial to allow a DFU capable device to be found +again after a switch to DFU mode, since the vendor and/or product ID of a +device usually changes in DFU mode. +

+If only DFU mode IDs are specified (e.g. "--device ,951:26"), then all +run-time devices will be ignored, making it easy to target a specific device in +DFU mode. +

+If both run-time and DFU mode IDs are specified (e.g. "--device +1457:51ab,:2bc"), then unspecified DFU mode components will use the run-time +value specified. +

+Examples: +

+
--device 1457:51ab,951:26 + +
+
+ +Work with a device in run-time mode with +vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 +and product ID 0x0026 +

+

--device 1457:51ab,:2bc + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc +

+

--device 1457:51ab + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with any vendor and product ID +

+

--device ,951:26 + +
+
+ +Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 +

+

--device *,- + +
+
+ +Work with any device in run-time mode, and ignore any device in DFU mode +

+

--device , + +
+
+ +Ignore any device in run-time mode, and Work with any device in DFU mode +
+
+ +
-p, --path BUS-PORT. ... .PORT + +
+Specify the path to the DFU device. +
-c, --cfg CONFIG-NR + +
+Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. +
-i, --intf INTF-NR + +
+Specify the DFU interface number. +
-a, --alt ALT + +
+Specify the altsetting of the DFU interface by name or by number. +
-S, --serial [Run-Time SERIAL][,[DFU Mode SERIAL]] + +
+Specify the run-time and DFU mode serial numbers used to further restrict +device matches. If multiple, identical DFU devices are simultaneously +connected to a system then vendor and product ID will be insufficient for +targeting a single device. In this situation, it may be possible to use this +parameter to specify a serial number which also must match. +

+If only a single serial number is specified, then the same serial number is +used in both run-time and DFU mode. An empty serial number will match any +serial number in the corresponding mode. +

-t, --transfer-size SIZE + +
+Specify the number of bytes per USB transfer. The optimal value is +usually determined automatically so this option is rarely useful. If +you need to use this option for a device, please report it as a bug. +
-Z, --upload-size SIZE + +
+Specify the expected upload size, in bytes. +
-U, --upload FILE + +
+Read firmware from device into +FILE. + +
-D, --download FILE + +
+Write firmware from +FILE + +into device. +
-R, --reset + +
+Issue USB reset signalling after upload or download has finished. +
-s, --dfuse-address address + +
+Specify target address for raw binary download/upload on DfuSe devices. Do +not + +use this for downloading DfuSe (.dfu) files. Modifiers can be added +to the address, separated by a colon, to perform special DfuSE commands such +as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. +
-v, --verbose + +
+Print more information about dfu-util's operation. A second +-v + +will turn on verbose logging of USB requests. Repeat this option to further +increase verbosity. +
-h, --help + +
+Show a help text and exit. +
-V, --version + +
+Show version information and exit. +
+  +

EXAMPLES

+ +  +

Using dfu-util in the OpenMoko project

+ +(with the Neo1973 hardware) +

+ +Flashing the rootfs: +
+ + $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2 + +

+ +Flashing the kernel: +
+ + $ dfu-util -a kernel -R -D /path/to/uImage + +

+ +Flashing the bootloader: +
+ + $ dfu-util -a u-boot -R -D /path/to/u-boot.bin + +

+ +Copying a kernel into RAM: +
+ + $ dfu-util -a 0 -R -D /path/to/uImage + +

+Once this has finished, the kernel will be available at the default load +address of 0x32000000 in Neo1973 RAM. +Note: + +You cannot transfer more than 2MB of data into RAM using this method. +

+  +

Using dfu-util with a DfuSe device

+ +

+ +Flashing a +.dfu + +(special DfuSe format) file to the device: +
+ + $ dfu-util -a 0 -D /path/to/dfuse-image.dfu + +

+ +Reading out 1 KB of flash starting at address 0x8000000: +
+ + $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin + +

+ +Flashing a binary file to address 0x8004000 of device memory and +ask the device to leave DFU mode: +
+ + $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin + + +  +

BUGS

+ +Please report any bugs to the dfu-util mailing list at +dfu-util@lists.gnumonks.org. + +Please use the +--verbose option (repeated as necessary) to provide more + +information in your bug report. +  +

SEE ALSO

+ +The dfu-util home page is +http://dfu-util.gnumonks.org + +  +

HISTORY

+ +dfu-util was originally written for the OpenMoko project by +Weston Schmidt <weston_schmidt@yahoo.com> and +Harald Welte <hwelte@hmw-consulting.de>. Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +  +

LICENCE

+ +dfu-util + +is covered by the GNU General Public License (GPL), version 2 or later. +  +

COPYRIGHT

+ +This manual page was originally written by Uwe Hermann <uwe@hermann-uwe.de>, +and is now part of the dfu-util project. +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
OPTIONS
+
EXAMPLES
+
+
Using dfu-util in the OpenMoko project
+
Using dfu-util with a DfuSe device
+
+
BUGS
+
SEE ALSO
+
HISTORY
+
LICENCE
+
COPYRIGHT
+
+
+This document was created by man2html, +using the doc/dfu-util.1 manual page from dfu-util 0.8.
+Time: 14:40:57 GMT, September 13, 2014 + + diff --git a/tools/macosx/src/dfu-util/www/dfuse.html b/tools/macosx/src/dfu-util/www/dfuse.html new file mode 100644 index 0000000..35e4ffa --- /dev/null +++ b/tools/macosx/src/dfu-util/www/dfuse.html @@ -0,0 +1,135 @@ + + + + + + DfuSe and dfu-util + + + + + + + + + +
+

Using dfu-util with DfuSe devices

+

DfuSe

+

+ DfuSe (DFU with ST Microsystems extensions) is a protocol based on + DFU 1.1. However, in expanding the functionality of the DFU protocol, + ST Microsystems broke all compatibility with the DFU 1.1 standard. + DfuSe devices report the DFU version as "1.1a". +

+

+ DfuSe can be used to download firmware and other data + from a host computer to a conforming device (or upload in the + opposite direction) over USB similar to standard DFU. +

+

+ The main difference from standard DFU is that the target address in + the device (flash) memory is specified by the host, so that a + download can be performed to parts of the device memory. The host + program is also responsible for erasing flash pages before they + are written to. +

+

.dfu files

+

+ A special file format is defined by ST Microsystems to carry firmware + for DfuSe devices. The file contains target information such as address + and alternate interface information in addition to the binary data. + Several blocks of binary data can be combined in one .dfu file. +

+

Alternate interfaces

+

+ Different memory locations of the device may have different + characteristics that the host program (dfu-util) has to take + into considerations, such as flash memory page size, read-only + versus read-write segments, the need to erase, and so on. + These parameters are reported by the device in the string + descriptors meant for describing the USB interfaces. + The host program decodes these strings to build a memory map of + the device. Different memory units or address spaces are listed + in separate alternate interface settings that must be selected + according to the memory unit to access. +

+

+ Note that dfu-util leaves it to the user to select alternate + interface. When parsing a .dfu file it will skip file segments + not matching the selected alternate interface. Also, some + DfuSe device firmware implementations ignore the setting of + alternate interface and deduct the memory unit from the + address, since they have no address space overlap. +

+

DfuSe special commands

+

+ DfuSe special commands are used by the host program during normal + downloads or uploads, such as SET_ADDRESS and ERASE_PAGE. Also + the normal DFU_DNLOAD and DFU_UPLOAD commands have special + implementations in DfuSe. + Many DfuSe devices also support commands to leave DFU mode, + read unprotect the flash memory or mass erase the flash memory. + dfu-util (from version 0.7) + supports adding "leave", "unprotect", or "mass-erase" + to the -s option argument to send such requests in combination + with a download request. These modifiers are separated with a colon. +

+

+ Some DfuSe devices have their DfuSe bootloader running from flash + memory. Erasing the whole flash memory would therefore destroy + the DfuSe bootloader itself and practically brick the device + for most users. Any use of modifiers such as "unprotect" + and "mass-erase" therefore needs to be combined with the "force" + modifer. This is not included in the examples, to not encourage + ignorant users to copy and paste such instructions and shoot + themselves in the foot. +

+

+ Devices based on for instance STM32F103 all run the bootloader + from flash, since there is no USB bootloader in ROM. +

+

+ For instance STM32F107, STM32F2xx and STM32F4xx devices have a + DfuSe bootloader in ROM, so the flash can be erased while + keeping the device available for USB DFU transfers as long + as the device designers use this built-in bootloader and have + not implemented another DfuSe bootloader in flash that the user is + dependent upon. +

+

+ Well-written bootloaders running from flash will report their + own memory region as read-only and not eraseable, but this does + not prevent dfu-util from sending a "unprotect" or "mass-erase" + request which overrides this, if the user insists. +

+

Example usage

+

+ Flashing a .dfu (special DfuSe format) file to the device: +

+
+         $ dfu-util -a 0 -D /path/to/dfuse-image.dfu
+	
+

+ Reading out 1 KB of flash starting at address 0x8000000: +

+
+         $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin
+	
+

+ Flashing a binary file to address 0x8004000 of device memory and ask + the device to leave DFU mode: +

+
+         $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin
+	
+

+ [Back to dfu-util main page] +

+ +
+ + diff --git a/tools/macosx/src/dfu-util/www/index.html b/tools/macosx/src/dfu-util/www/index.html new file mode 100644 index 0000000..108ddaf --- /dev/null +++ b/tools/macosx/src/dfu-util/www/index.html @@ -0,0 +1,119 @@ + + + + + + dfu-util Homepage + + + + + + + + + +
+

dfu-util - Device Firmware Upgrade Utilities

+

Description

+

+ dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. + + DFU is intended to download and upload firmware to/from devices connected + over USB. It ranges from small devices like micro-controller boards + to mobile phones. Using dfu-util you can download firmware to your + DFU-enabled device or upload firmware from it. dfu-util has been + tested with the Openmoko Neo1973 and Freerunner and many other devices. +

+

+ See the manual page for examples of use. +

+

Supported Devices

+ +

Releases

+

+ Releases of the dfu-util software can be found in the + releases folder. + The current release is 0.8. +

+

+ We offer binaries for Microsoft Windows and some other platforms. + dfu-util uses libusb 1.0 to access your device, so + on Windows you have to register the device with the WinUSB driver + (alternatively libusb-win32 or libusbK), please see the libusbx wiki + for more details. +

+

+ Mac OS X users can also get dfu-util from Homebrew with "brew install dfu-util" or from MacPorts. +

+

+ Most Linux distributions ship dfu-util in binary packages for those + who do not want to compile dfu-util from source. + On Debian, Ubuntu, Fedora and Gentoo you can install it through the + normal software package tools. For other distributions +(namely OpenSuSe, Mandriva, and CentOS) Holger Freyther was kind enough to +provide binary packages through the Open Build Service. +

+

Development

+

+ Development happens in a GIT repository. Browse it via the web +interface or clone it with: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	
+

+ See our build instructions for how to + build the source on different platforms. +

+

License

+

+ This software is licensed under the GPL version 2. +

+

Contact

+

+ If you have questions about the development or use of dfu-util please + send an e-mail to our dedicated +mailing list for dfu-util. +

+

People

+

+ dfu-util was originally written by + Harald Welte partially based on code from + dfu-programmer 0.4 and is currently maintained by Stefan Schmidt and + Tormod Volden. +

+ +
+ + diff --git a/tools/macosx/src/dfu-util/www/simple.css b/tools/macosx/src/dfu-util/www/simple.css new file mode 100644 index 0000000..98100bc --- /dev/null +++ b/tools/macosx/src/dfu-util/www/simple.css @@ -0,0 +1,56 @@ +body { + margin: 10px; + font-size: 0.82em; + background-color: #EEE; +} + +h1 { + clear: both; + padding: 0 0 12px 0; + margin: 0; + font-size: 2em; + font-weight: bold; +} + +h2 { + clear: both; + margin: 0; + font-size: 1.5em; + font-weight: normal; +} + +h3 { + clear: both; + margin: 15px 0 0 0; + font-size: 1.0em; + font-weight: bold; +} + +p { + line-height: 20px; + padding: 8px 0 8px 0; + margin: 0; + font-size: 1.1em; +} + +pre { + white-space: pre-wrap; + background-color: #CCC; + padding: 3px; +} + +a:hover { + background-color: #DDD; +} + +#middlebox { + width: 600px; + margin: 0px auto; + text-align: left; +} + +#footer { + height: 100px; + padding: 28px 3px 0 0; + margin: 20px 0 20px 0; +} diff --git a/tools/macosx/src/maple_loader/README.md b/tools/macosx/src/maple_loader/README.md new file mode 100644 index 0000000..c6c9379 --- /dev/null +++ b/tools/macosx/src/maple_loader/README.md @@ -0,0 +1,5 @@ +These files build the maple_loader.jar file used on Windows to reset the Sketch via USB Serial, so that the bootloader will run in dfu upload mode, ready for a new sketch to be uploaded + +The files were written by @bobC (github) and have been slightly modified by me (Roger Clark), so that dfu-util no longer attempts to reset the board after upload. +This change to dfu-util's reset command line argument, was required because dfu-util was showing errors on some Windows systems, because the bootloader had reset its self after upload, +before dfu-util had chance to tell it to reset. \ No newline at end of file diff --git a/tools/macosx/src/maple_loader/build.xml b/tools/macosx/src/maple_loader/build.xml new file mode 100644 index 0000000..80bdd6f --- /dev/null +++ b/tools/macosx/src/maple_loader/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project maple_loader. + + + diff --git a/tools/macosx/src/maple_loader/build/built-jar.properties b/tools/macosx/src/maple_loader/build/built-jar.properties new file mode 100644 index 0000000..10752d5 --- /dev/null +++ b/tools/macosx/src/maple_loader/build/built-jar.properties @@ -0,0 +1,4 @@ +#Mon, 20 Jul 2015 11:21:26 +1000 + + +C\:\\Users\\rclark\\Desktop\\maple-asp-master\\installer\\maple_loader= diff --git a/tools/macosx/src/maple_loader/build/classes/CliTemplate/CliMain.class b/tools/macosx/src/maple_loader/build/classes/CliTemplate/CliMain.class new file mode 100644 index 0000000000000000000000000000000000000000..37ee63000b20df7528829288b7f0fbeb28eb3b98 GIT binary patch literal 1753 zcmZ`(?^7F96g^8e*(Ke!Bot_BEkSJ?Qc5V;R-jfb6dMgcG>{5a@g;erOE=l=X2Z~N zI-?()v44qQZ9mYCj{X7uQR;cgB4LP`%)9&UJMY|k&%L{U{r%gY0A}FDk-}_0<}h#K zP8=WNu8EK0_!ysNj62~BB)v{_}O_60{U^nDSt6rA=x+pvJBnxgu zI3?lRY9BiF;IZA*FjQD@>~&f991+MIpO=N*m?v4CR>N@SQDIl?iJT)EwOldq?M7{0 z1=Gko)OB=!zaqWBcH4J_UnBKkkE}dUI6;1gw$&`>7YQ%hjU`)E zajLr0+VC7#RHd&Wbu912rH#WQ=6<>uh{}^?;k8ROkTtMLX(_0Nc+qY771^P*!g?oF zA{H)T)WDX7NBCMpqAol~Zg=9P_ogOq82HA*x7cPXO-U`CNso0H_|AernPCsT6gqfD ziB{N*&{q|KDBD5PLIqWlAG&3^RonI{{SmYoxSoP)usq+b`J%ok9YH>;)vkUrROWE2 z1fx)i@Km${ciWdu8CZ~@l4rt$J?WR-CIgLhcf3nE3pG4u+m5>OUsEmEN|O=MNK4;$ z{j^)Dw0vJy$I{IU`M8_2<0+8CF&@gCoHaGY7pQPWHk+L4obWud${p#; z27L@k4K-ZtakV#DocMerkTo@*!#%F$RY--p=emv*?bquh)s{kq%`Mr24f>z_x0oWk zLCTs8mRW_UvAgFo<0m=Ux^{yCxgX?0=#uN#1q+Prgb%YlQtp0*JB^+1Ngr)XjWN8< zkD&VXK~wJ&*EDxIT8%cEeGTnp`*@6NtnD1eJAAe}0KAL$xa-GdT%jioXby1(eJ`?+ z=|WbYUd~3RSF*9`IsFy-l92=G$>_B=h$W4rsZ9R>O+0xjarp;jEnTgr6Z)^pp1H0d zXD8G;z>`EW8R7an1~*^h+*~vjP3Z?1diV=2&c$9~Bza!{1F6kODt3Si#W#5C02f~} zu__!6^)Mo=87N_h>m}%{*ksZcy$P0*Vi}_>IX literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/CliTemplate/DFUUploader.class b/tools/macosx/src/maple_loader/build/classes/CliTemplate/DFUUploader.class new file mode 100644 index 0000000000000000000000000000000000000000..77087b052fbd03f77b9e1417f2754196beaf1844 GIT binary patch literal 7476 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 literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/CliTemplate/ExecCommand.class b/tools/macosx/src/maple_loader/build/classes/CliTemplate/ExecCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..ad95f79845d3d1b0c1d2245d06800c06c37e8c08 GIT binary patch literal 3243 zcma)8TW}NS75;u{rC%0{F$RGJfhdV%e8DI8>iD5`@f$5 zobR0TowIuL-k)v*cpTT`Xu((=I*^sf#Sz9>9Ca|&Fun>Cm{fx$aYoHgOYE&f9t8WB885cO{+|kW;2R=9EkUaYA6t zxaF3-gRbkihn%vV6KE)zS;x-R9@UxCS+nR_jxEqKP;{NFSt?ofMA|48(>ZgjJdr+9 zwr$frxFEMnAiCSKEw5J~+}d_bK6Ts5zpt3hVZm@qR|ani9V8zv@!I)W%#c?OJOT$+=a;zC}UmqFTH zV?2m#49>}jGqW>Tz-F`Y~^#NtK+Bm8Ih%QT*6BdKiBapPV4xEf|14!9gks$#A`a{ z@XG~EOL@W!{XfN|<5vp$<4XIl@r1y`E52HiR*4sN{06^O&o1luom%}~A^U^!dF!H2 zD^XI%PIT$`gmS;(++*icuIZIs+sviT?L5CEVnbtR%&aGIMaNbAQQ~zS*HBSGI4w|b zPFr3o%ewDLQ42%)KGpnwaR@x>bL>*NV3MQti;1n4$l66m0`9Y3S?L9V^{s99bBPti zYM$^W{kFu44jVbv=n~phecIK=?x>ZlXGk{bIX;gBT9@>f1N} z$}HW*mNCrshI|tYLYF9FnPqJE{mK z6v5<_kuRG=8sTcI_HMSA5xSb;dhj19djx zgIwuVNxpfKJAoeTq6D>sPebyj48*Bh2#;QeHrR2v^LDou?v5lP3GEWLbtWQRJ<(*e zf=G3W&O+awjNXI{;rNA!n4AB{_U$(j6Lz106kD zQoG$933o>m(M04D+LKx$+SMbkLcfL8v|OK*6|8A&;84NZ3K}m&srv7{uV7t!_!`!$ zA<=OI8+x=kZ0?ENLepq6QbF=MHdUc*PDbzC!ULm8t%5BK%T^^+-9G4VqguC2$XRSv z?^4RCl#+d@H{31f=Ks|bo16b95kK-*#ME?Wi}orWyo%<=MqhnX3br`OQNu>~<~ z!&bE7QSLj@&bw{cO->APH$n~^B_ocrNuMSU&XNPqbG}F>yu$2WVpiuUeHr^{VGsLq zFEg+gf5ATd4SjeEAI97GDBeLo-o?l9Zw%lr25}EVLSR_bVN^8Wgh=3&*u*y84MX%` zOk|K1Pa!8xV%+D@n^;ebO|-NJQ8KKHcJ`8!^~AT2wl%CJmk)3iA;wL#yocN}=!=rm zsAZ=w7lFkWN-j#AF{10@>MGfK5Qh*EQAXq=#2X`m>TOK%GvKaT^-~|apcui&=nq*a zBJ^oLv0UaiZxDxR?JYdV=nWIg+nC~)ZJ0LRK?z4_TOvanswS#BMyTT`PmW;CJe`P2 z9FtfjahxMVgyy@5G|gijx0-$0j#Q*>R literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/processing/app/Base.class b/tools/macosx/src/maple_loader/build/classes/processing/app/Base.class new file mode 100644 index 0000000000000000000000000000000000000000..4aa0bde02049fbb6c4e035d952edc3f002cba1e1 GIT binary patch literal 639 zcmaKp%TB^T6o&tSLZKAVg7^Ck!G)N(aDg#K6B84>Bp6NH4Q13pXq&cpc^a2)+_+KW z!Uyo7jHgH%(vSU{TL#iY#anNj3gnP^@bs|Q|HhKMoyE2H|Q1LIN$1qrOT-y|$C#{PD zcbvjD_e7B)F1=G}RqY$fA^jOcvg~v7YM(o8fhs+!)VyWT%68K=#jXsB#RI`gjbC#e zacB&x?X6h6Aymxhn8h5Cwpqozjs+|-MB1iwk7gM916!fasl&rcO_+WcD&Kj&XfPx$ zgn#7Pj&S`uhHS3%^Q!E-G+r_P8+DsnY1AJLE^ZIc>Fe~e?9mh|{hDBfK@&rxJwZ`{ zPDW>B`33BWE+Oc&M_X_PNxJLb3Ft$LqB#07KqPp)PT9fA7@=2$4doe;&^=V;0ZRCh ec&BR#29XFt##T^2{g)*ApJcUD!jNgz82kc&eSc^G literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/processing/app/Preferences.class b/tools/macosx/src/maple_loader/build/classes/processing/app/Preferences.class new file mode 100644 index 0000000000000000000000000000000000000000..89cf0100455af1f41500150e545c8bbd12583674 GIT binary patch literal 2215 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 L#S!xqDulu}Wzd50 literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class b/tools/macosx/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class new file mode 100644 index 0000000000000000000000000000000000000000..37250e7704487822639cf03f4e4a333da994be50 GIT binary patch literal 174 zcmX^0Z`VEs1_omWPId-%b_Nbc2KL<4;^M^gR7M6-4WF#UvPAuy#JqI<;F6-uymV{L zFh&Nh;QZ2}36TD<=a zee$u-T}#4J*3vf~eDmkHmbdTB3`x3-T|Q)=efH(-y}z^fnf&_KPd@_~$Jc3`K|#YE z4ezEAM=^~U<~s2b<`r2`3FpOF@DY|1aeE`vQp>rz{0}ik26Y+<5P_3@WA4aH*G_Nr)yS)eXYYsjn$=fJOWF4tYZhC37l!#w}YMI5E!%pT%fP% z+dxal=PHf9X`?)_MwAB1vTAyk>pLES*6{_t)bW){_Z9doX zp*cQw*xW5Rg|$DEB4^Hd92&M1T~J--)m&ee8#S=#_5-Loy058<-UTH+9!Y~sI?QMy zn1HJE?|PQwSw7)TWD82zQS}XvWu(;85YEQY?D>`vXiP_vZ;Q0~d|YUa5UKy|5#2V& zD&;$eAPhYZ*KvUzx$BPrxE*8^HuS-Abd74{xS?(;X{fBvh&yULy=i|kxTlNf7 z;w+!=GZ1(g31n24^DB^1jm|Zu+BsO+^IQ|O%i=s~j=OJ>nhpw19x}OH;50efeMVe!jVSW-ua2gkw zO`dcdm+%Iz;Y~`x9D}Hok8;QOw0WI5DNf}bBYzQNVV2?>S|_O6VZ79L80C_z<4CD^ zfI;%wK6*-f=pC-(g-nK|j=mppG=?XC|CSTX_X(zo5p(&F87o^tr z9?2)rD19-q?V1mm3%@)Ksv5%;9Ag$^m_dKA7Fna(JET`I5v((~5v)b52P@x^V2u+I z;12HvxQ;$d({BZrQKIK8eOE9=eg?Pczk&k!BCY2zk3}rN#66z3f(qph*T;Ae;5fm_32#8&VW5Z_DWZjif-x5zCr=eMcl9{`-9>GD2Q?oaJ}>;w=qVZ)>>5-{ha@ zC)IHMcf={5(&)lhg-58CS>Z0m^@zIs;_yKL*=f}RVTnhAt9^(L80hAo19jc~p+^+7 zg8`@6lwrath(FM+v2>NRYR{Ye%`#rtqAGQjaxxcZ8#Y@_{ZRoV0mQotV5n6@E2k~%3N36at+NEB6-iZpQsd%&@c zA5A4Tuz*j%h6Rru8x|m8Sz^Hl;6tJQ&z#FqWkoT zMK-PSux61%52o=};bEPJ4IUoy@DZt?IbKWPfebnC6t{hn9-@}T2Yu_~Sl zGS`A;(5nc_7S3xI4+ZI)kPtM>f{*(rM}FtPJ8DRlR@G}9dYyph>Z9JP2VFsL?zKCu zDyTtN_S)_8v47OBl|St_n|^2OMb&Tjf>smk3khDIvfC>O3%k1#=<|Tx>kS#<)kHgK zXjWC=*OG0dpELW%Wb8=5OE#92@hTEq^Lvj*RhcgoV`foZtZvN4!3NHxs#B^^M5P>9 z#6Ic;$!!E-EuFg8jjdt}>NO(^_{eLkgxg9tthcQM)l3P5vl+EEYKIxMb2NuOg3i=~ zV?W*`vu(6lFaE{aZ}mG>e_NJeexjyJ+@KC+DaXSt9#$Nhp^QVbbjhJtc(}~N9L@9U zjzjO$ilE#7r(*=ojSoOn4?$!Z7 z;WTsKk=&3Rg9MWTqKiRx`z2eXhyHnb=qBD$(nntT@ZiJ1n(dZyi&;I%$qF{UC!H*ZrjZjn!nbMU#8yrqi8@^Z z7ANbLa`yv3Ot{OtLrS@&0qJgTK!&?GAk)q21C%6d3}|Yc!W&3B(JFM-5bOg~yMk1$ zgJ%Pt9!joCs2OqPBd&bJm5;dciVGAn>4qeHT@mhpkduXk3~Aa0QVb>|>$in8Y$GW< z(s|e;cI4cq+w=y+aADG0O0+EJXju0z(*J;Z*ls316FrtPj(j(ijG#i)trbON%6yG8 z6E^EUU=Cs+W&~nJ&00h|hn*h>F@B!_d6EcHqPNxNG01h)Y!c)dKt8(wXIWW>;Zp|t)R=_w{k$&(nS<|sy#QGvY!8-p}wg9Yy*v>)mJ{n9+<-P@KEO7OK70uz15(}q literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class b/tools/macosx/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..27eca62622c61bc21ae557714ca4fa2d4c1eb22b GIT binary patch literal 1399 zcmah}Sy$6Q7`@XLQbO2@A}XTd)|Mr1C|W=TH?$PBD#zs|q@zUJBqpiLyT8ImUwqXU z+>ZPJe~HJR;5jZgC15~3=A6vTcjucs_gj+BKRzr2ID)Gxx-g_-7$Yil91Mp;3J#}n zB#omgnlP&1n2Hu04~MZdPNZ?N6$Olk_Jo2-1*Zg>$1U3mCIyn&+)aVhv{R7+9YxEQ zrFv~ade@DGDv?alF^%d?!?Qv=0#m`F^CTYS6zNcsy3f#h~aIPY8?ejDTV~wVGj91iJrnHr%gOLdgON zcR^s=pJY>Yt6GsBIg`?U97foXP^UC~)^LgUtdH=SyBg)_de`w7c_jW%WRiq!<-jl> zlDv^E1^oi*xu>Rd1Iw{}1*d73ow{er^H!MRu6VjeLU|f8=vHt>!xW|!oYim+=QV7_ zHi6EKDP*Pw82{Y?4Hs}xp!c^!L>%QHWS!A)371*^>01}(XG)~Aor>e%qmUrTHOw;8oku}GFUSD$z#b&AmmXn=E66?~`-!w6+5R1hf&=0k Rugelj(J99_&vgQWzW|+SRY?E< literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/dist/README.TXT b/tools/macosx/src/maple_loader/dist/README.TXT new file mode 100644 index 0000000..255b89c --- /dev/null +++ b/tools/macosx/src/maple_loader/dist/README.TXT @@ -0,0 +1,32 @@ +======================== +BUILD OUTPUT DESCRIPTION +======================== + +When you build an Java application project that has a main class, the IDE +automatically copies all of the JAR +files on the projects classpath to your projects dist/lib folder. The IDE +also adds each of the JAR files to the Class-Path element in the application +JAR files manifest file (MANIFEST.MF). + +To run the project from the command line, go to the dist folder and +type the following: + +java -jar "maple_loader.jar" + +To distribute this project, zip up the dist folder (including the lib folder) +and distribute the ZIP file. + +Notes: + +* If two JAR files on the project classpath have the same name, only the first +JAR file is copied to the lib folder. +* Only JAR files are copied to the lib folder. +If the classpath contains other types of files or folders, these files (folders) +are not copied. +* If a library on the projects classpath also has a Class-Path element +specified in the manifest,the content of the Class-Path element has to be on +the projects runtime path. +* To set a main class in a standard Java project, right-click the project node +in the Projects window and choose Properties. Then click Run and enter the +class name in the Main Class field. Alternatively, you can manually type the +class name in the manifest Main-Class element. diff --git a/tools/macosx/src/maple_loader/dist/lib/jssc.jar b/tools/macosx/src/maple_loader/dist/lib/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 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 @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/dist/maple_loader.jar b/tools/macosx/src/maple_loader/dist/maple_loader.jar new file mode 100644 index 0000000000000000000000000000000000000000..e1f9965c1ff0fe14384717ea4559c7f59e9a1ca6 GIT binary patch literal 32791 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 VEwG5^!#_X%TL6{zhrDw%{eMvMy}$qf literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/jars/jssc.jar b/tools/macosx/src/maple_loader/jars/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 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 @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/macosx/src/maple_loader/manifest.mf b/tools/macosx/src/maple_loader/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/tools/macosx/src/maple_loader/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/macosx/src/maple_loader/nbproject/build-impl.xml b/tools/macosx/src/maple_loader/nbproject/build-impl.xml new file mode 100644 index 0000000..a66f349 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/build-impl.xml @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/macosx/src/maple_loader/nbproject/genfiles.properties b/tools/macosx/src/maple_loader/nbproject/genfiles.properties new file mode 100644 index 0000000..c136721 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=2e6a03ba +build.xml.script.CRC32=4676ee6b +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=2e6a03ba +nbproject/build-impl.xml.script.CRC32=392b3f79 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/macosx/src/maple_loader/nbproject/private/config.properties b/tools/macosx/src/maple_loader/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/tools/macosx/src/maple_loader/nbproject/private/private.properties b/tools/macosx/src/maple_loader/nbproject/private/private.properties new file mode 100644 index 0000000..e5c9f10 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\rclark\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties diff --git a/tools/macosx/src/maple_loader/nbproject/private/private.xml b/tools/macosx/src/maple_loader/nbproject/private/private.xml new file mode 100644 index 0000000..a1bbd60 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/private/private.xml @@ -0,0 +1,10 @@ + + + + + + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/CliMain.java + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/DFUUploader.java + + + diff --git a/tools/macosx/src/maple_loader/nbproject/project.properties b/tools/macosx/src/maple_loader/nbproject/project.properties new file mode 100644 index 0000000..7f48d71 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/project.properties @@ -0,0 +1,79 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=maple_loader +application.vendor=bob +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/maple_loader.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jssc.jar=dist/lib/jssc.jar +file.reference.jssc.jar-1=jars/jssc.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.jssc.jar}:\ + ${file.reference.jssc.jar-1} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=CliTemplate.CliMain +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/macosx/src/maple_loader/nbproject/project.xml b/tools/macosx/src/maple_loader/nbproject/project.xml new file mode 100644 index 0000000..92218a9 --- /dev/null +++ b/tools/macosx/src/maple_loader/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + maple_loader + + + + + + + + + diff --git a/tools/macosx/src/maple_loader/src/CliTemplate/CliMain.java b/tools/macosx/src/maple_loader/src/CliTemplate/CliMain.java new file mode 100644 index 0000000..c7dc9f0 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/CliTemplate/CliMain.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import processing.app.Preferences; + +//import processing.app.I18n; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class CliMain { + + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + + String comPort = args[0]; // + String altIf = args[1]; // + String usbID = args[2]; // "1EAF:0003"; + String binFile = args[3]; // bin file + + System.out.println("maple_loader v0.1"); + + Preferences.set ("serial.port",comPort); + Preferences.set ("serial.parity","N"); + Preferences.setInteger ("serial.databits", 8); + Preferences.setInteger ("serial.debug_rate",9600); + Preferences.setInteger ("serial.stopbits",1); + + Preferences.setInteger ("programDelay",1200); + + Preferences.set ("upload.usbID", usbID); + Preferences.set ("upload.altID", altIf); + Preferences.setBoolean ("upload.auto_reset", true); + Preferences.setBoolean ("upload.verbose", false); + + // + DFUUploader dfuUploader = new DFUUploader(); + try { + //dfuUploader.uploadViaDFU(binFile); + dfuUploader.uploadViaDFU(binFile); + } catch (Exception e) + { + System.err.print (MessageFormat.format("an error occurred! {0}\n", e.getMessage())); + } + } +} diff --git a/tools/macosx/src/maple_loader/src/CliTemplate/DFUUploader.java b/tools/macosx/src/maple_loader/src/CliTemplate/DFUUploader.java new file mode 100644 index 0000000..3dee0b4 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/CliTemplate/DFUUploader.java @@ -0,0 +1,345 @@ +/* + DFUUploader - uploader implementation using DFU + + Copyright (c) 2010 + Andrew Meyer + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package CliTemplate; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import processing.app.Preferences; +import processing.app.Serial; +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; + +/** + * + * @author bob + */ +public class DFUUploader implements MessageConsumer { + + boolean firstErrorFound; + boolean secondErrorFound; + // part of the PdeMessageConsumer interface + boolean notFoundError; + boolean verbose; + RunnerException exception; + + static final String SUPER_BADNESS = + "Compiler error!"; + + public boolean uploadUsingPreferences(String binPath, boolean verbose) + throws RunnerException { + + this.verbose = verbose; + + return uploadViaDFU(binPath); + } + + // works with old and new versions of dfu-util + private boolean found_device (String dfuData, String usbID) + { + return dfuData.contains(("Found DFU: [0x"+usbID.substring(0,4)).toUpperCase()) || + dfuData.contains(("Found DFU: ["+usbID.substring(0,4)).toUpperCase()); + } + + public boolean uploadViaDFU (String binPath) + throws RunnerException { + + this.verbose = Preferences.getBoolean ("upload.verbose"); + + /* todo, check for size overruns! */ + String fileType="bin"; + + if (fileType.equals("bin")) { + String usbID = Preferences.get("upload.usbID"); + if (usbID == null) { + /* fall back on default */ + /* this isnt great because is default Avrdude or dfu-util? */ + usbID = Preferences.get("upload.usbID"); + } + + /* if auto-reset, then emit the reset pulse on dtr/rts */ + if (Preferences.get("upload.auto_reset") != null) { + if (Preferences.get("upload.auto_reset").toLowerCase().equals("true")) { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + } else { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + + String dfuList = new String(); + List commandCheck = new ArrayList(); + commandCheck.add("dfu-util"); + commandCheck.add("-l"); + long startChecking = System.currentTimeMillis(); + System.out.println("Searching for DFU device [" + usbID + "]..."); + do { + try { + Thread.sleep(100); + } catch (InterruptedException e) {} + dfuList = executeCheckCommand(commandCheck); + //System.out.println(dfuList); + } + while ( !found_device (dfuList.toUpperCase(), usbID) && (System.currentTimeMillis() - startChecking < 7000)); + + if ( !found_device (dfuList.toUpperCase(), usbID) ) + { + System.out.println(dfuList); + System.err.println("Couldn't find the DFU device: [" + usbID + "]"); + return false; + } + System.out.println("Found it!"); + + /* todo, add handle to let user choose altIf at upload time! */ + String altIf = Preferences.get("upload.altID"); + + List commandDownloader = new ArrayList(); + commandDownloader.add("dfu-util"); + commandDownloader.add("-a "+altIf); + commandDownloader.add("-R"); + commandDownloader.add("-d "+usbID); + commandDownloader.add("-D"+ binPath); //"./thisbin.bin"); + + return executeUploadCommand(commandDownloader); + } + + System.err.println("Only .bin files are supported at this time"); + return false; + } + + /* we need to ensure both RTS and DTR are low to start, + then pulse DTR on its own. This is the reset signal + maple responds to + */ + private void emitResetPulse() throws RunnerException { + + /* wait a while for the device to reboot */ + int programDelay = Preferences.getInteger("programDelay"); + + try { + Serial serialPort = new Serial(); + + // try to toggle DTR/RTS (old scheme) + serialPort.setRTS(false); + serialPort.setDTR(false); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + + // try magic number + serialPort.setRTS(true); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.write("1EAF"); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.dispose(); + + } catch(Exception e) { + System.err.println("Reset via USB Serial Failed! Did you select the right serial port?\nAssuming the board is in perpetual bootloader mode and continuing to attempt dfu programming...\n"); + } + } + + protected String executeCheckCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + String returnStr = new String(); + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(process.getInputStream())); + BufferedReader stdError = new BufferedReader(new + InputStreamReader(process.getErrorStream())); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean busy = true; + while (busy) { + try { + result = process.waitFor(); + busy = false; + } catch (InterruptedException intExc) { + } + } + + String s; + while ((s = stdInput.readLine()) != null) { + returnStr += s + "\n"; + } + + process.destroy(); + + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) return "Error!"; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + } + + return returnStr; // ? true : false; + + } + + // Need to overload this from Uploader to use the system-wide dfu-util + protected boolean executeUploadCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + new MessageSiphon(process.getInputStream(), this); + new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean compiling = true; + while (compiling) { + try { + result = process.waitFor(); + compiling = false; + } catch (InterruptedException intExc) { + } + } + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) + return false; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + //editor.error(exception); + //PdeBase.openURL(BUGS_URL); + //throw new PdeException(SUPER_BADNESS); + } + + return (result == 0); // ? true : false; + + } + + // deal with messages from dfu-util... + public void message(String s) { + + if(s.indexOf("dfu-util - (C) ") != -1) { return; } + if(s.indexOf("This program is Free Software and has ABSOLUTELY NO WARRANTY") != -1) { return; } + + if(s.indexOf("No DFU capable USB device found") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: No Maple found"); + return; + } + + if(s.indexOf("Operation not perimitted") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: Insufficient privilages"); + return; + } + + // else just print everything... + System.out.print(s); + } + +} diff --git a/tools/macosx/src/maple_loader/src/CliTemplate/ExecCommand.java b/tools/macosx/src/maple_loader/src/CliTemplate/ExecCommand.java new file mode 100644 index 0000000..3d6c106 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/CliTemplate/ExecCommand.java @@ -0,0 +1,119 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class ExecCommand implements MessageConsumer { + + private boolean verbose = true; + private boolean firstErrorFound; + private boolean secondErrorFound; + private RunnerException exception; + + /** + * Either succeeds or throws a RunnerException fit for public consumption. + * + * @param command + * @throws RunnerException + */ + public void execAsynchronously(String[] command) throws RunnerException { + + // eliminate any empty array entries + List stringList = new ArrayList<>(); + for (String string : command) { + string = string.trim(); + if (string.length() != 0) + stringList.add(string); + } + command = stringList.toArray(new String[stringList.size()]); + if (command.length == 0) + return; + int result = 0; + + if (verbose) { + for (String c : command) + System.out.print(c + " "); + System.out.println(); + } + + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + + Process process; + try { + process = ProcessUtils.exec(command); + } catch (IOException e) { + RunnerException re = new RunnerException(e.getMessage()); + re.hideStackTrace(); + throw re; + } + + MessageSiphon in = new MessageSiphon(process.getInputStream(), this); + MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + boolean compiling = true; + while (compiling) { + try { + in.join(); + err.join(); + result = process.waitFor(); + //System.out.println("result is " + result); + compiling = false; + } catch (InterruptedException ignored) { } + } + + // an error was queued up by message(), barf this back to compile(), + // which will barf it back to Editor. if you're having trouble + // discerning the imagery, consider how cows regurgitate their food + // to digest it, and the fact that they have five stomaches. + // + //System.out.println("throwing up " + exception); + if (exception != null) + throw exception; + + if (result > 1) { + // a failure in the tool (e.g. unable to locate a sub-executable) + System.err.println(MessageFormat.format("{0} returned {1}", command[0], result)); + } + + if (result != 0) { + RunnerException re = new RunnerException(MessageFormat.format("exit code: {0}", result)); + re.hideStackTrace(); + throw re; + } + } + + /** + * Part of the MessageConsumer interface, this is called + * whenever a piece (usually a line) of error message is spewed + * out from the compiler. The errors are parsed for their contents + * and line number, which is then reported back to Editor. + * @param s + */ + @Override + public void message(String s) { + int i; + + + System.err.print(s); + } + +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/Base.java b/tools/macosx/src/maple_loader/src/processing/app/Base.java new file mode 100644 index 0000000..c3a174d --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/Base.java @@ -0,0 +1,53 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-10 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + + +/** + * The base class for the main processing application. + * Primary role of this class is for platform identification and + * general interaction with the system (launching URLs, loading + * files and images, etc) that comes from that. + */ +public class Base { + + /** + * returns true if running on windows. + */ + static public boolean isWindows() { + //return PApplet.platform == PConstants.WINDOWS; + return System.getProperty("os.name").indexOf("Windows") != -1; + } + + + /** + * true if running on linux. + */ + static public boolean isLinux() { + //return PApplet.platform == PConstants.LINUX; + return System.getProperty("os.name").indexOf("Linux") != -1; + } + + + +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/Preferences.java b/tools/macosx/src/maple_loader/src/processing/app/Preferences.java new file mode 100644 index 0000000..6368e38 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/Preferences.java @@ -0,0 +1,157 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-09 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import java.io.*; +import java.util.*; + + +/** + * Storage class for user preferences and environment settings. + *

+ * This class no longer uses the Properties class, since + * properties files are iso8859-1, which is highly likely to + * be a problem when trying to save sketch folders and locations. + *

+ * The GUI portion in here is really ugly, as it uses exact layout. This was + * done in frustration one evening (and pre-Swing), but that's long since past, + * and it should all be moved to a proper swing layout like BoxLayout. + *

+ * This is very poorly put together, that the preferences panel and the actual + * preferences i/o is part of the same code. But there hasn't yet been a + * compelling reason to bother with the separation aside from concern about + * being lectured by strangers who feel that it doesn't look like what they + * learned in CS class. + *

+ * Would also be possible to change this to use the Java Preferences API. + * Some useful articles + * here and + * here. + * However, haven't implemented this yet for lack of time, but more + * importantly, because it would entail writing to the registry (on Windows), + * or an obscure file location (on Mac OS X) and make it far more difficult to + * find the preferences to tweak them by hand (no! stay out of regedit!) + * or to reset the preferences by simply deleting the preferences.txt file. + */ +public class Preferences { + + // what to call the feller + + static final String PREFS_FILE = "preferences.txt"; + + + // prompt text stuff + + static final String PROMPT_YES = "Yes"; + static final String PROMPT_NO = "No"; + static final String PROMPT_CANCEL = "Cancel"; + static final String PROMPT_OK = "OK"; + static final String PROMPT_BROWSE = "Browse"; + + /** + * Standardized width for buttons. Mac OS X 10.3 wants 70 as its default, + * Windows XP needs 66, and my Ubuntu machine needs 80+, so 80 seems proper. + */ + static public int BUTTON_WIDTH = 80; + + /** + * Standardized button height. Mac OS X 10.3 (Java 1.4) wants 29, + * presumably because it now includes the blue border, where it didn't + * in Java 1.3. Windows XP only wants 23 (not sure what default Linux + * would be). Because of the disparity, on Mac OS X, it will be set + * inside a static block. + */ + static public int BUTTON_HEIGHT = 24; + + // value for the size bars, buttons, etc + + static final int GRID_SIZE = 33; + + + // indents and spacing standards. these probably need to be modified + // per platform as well, since macosx is so huge, windows is smaller, + // and linux is all over the map + + static final int GUI_BIG = 13; + static final int GUI_BETWEEN = 10; + static final int GUI_SMALL = 6; + + + + // data model + + static Hashtable table = new Hashtable();; + static File preferencesFile; + + + static protected void init(String commandLinePrefs) { + + + } + + + public Preferences() { + + } + + // ................................................................. + + // ................................................................. + + // ................................................................. + + // ................................................................. + + + + static public String get(String attribute) { + return (String) table.get(attribute); + } + + static public void set(String attribute, String value) { + table.put(attribute, value); + } + + + static public boolean getBoolean(String attribute) { + String value = get(attribute); + return (new Boolean(value)).booleanValue(); + } + + + static public void setBoolean(String attribute, boolean value) { + set(attribute, value ? "true" : "false"); + } + + + static public int getInteger(String attribute) { + return Integer.parseInt(get(attribute)); + } + + + static public void setInteger(String key, int value) { + set(key, String.valueOf(value)); + } + +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/Serial.java b/tools/macosx/src/maple_loader/src/processing/app/Serial.java new file mode 100644 index 0000000..04566a7 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/Serial.java @@ -0,0 +1,527 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + PSerial - class for serial port goodness + Part of the Processing project - http://processing.org + + Copyright (c) 2004 Ben Fry & Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.app; +//import processing.core.*; + + +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; +import jssc.SerialPortList; +import processing.app.debug.MessageConsumer; + + +public class Serial implements SerialPortEventListener { + + //PApplet parent; + + // properties can be passed in for default values + // otherwise defaults to 9600 N81 + + // these could be made static, which might be a solution + // for the classloading problem.. because if code ran again, + // the static class would have an object that could be closed + + SerialPort port; + + int rate; + int parity; + int databits; + int stopbits; + boolean monitor = false; + + // read buffer and streams + + InputStream input; + OutputStream output; + + byte buffer[] = new byte[32768]; + int bufferIndex; + int bufferLast; + + MessageConsumer consumer; + + public Serial(boolean monitor) throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + this.monitor = monitor; + } + + public Serial() throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(int irate) throws SerialException { + this(Preferences.get("serial.port"), irate, + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate) throws SerialException { + this(iname, irate, Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname) throws SerialException { + this(iname, Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate, + char iparity, int idatabits, float istopbits) + throws SerialException { + //if (port != null) port.close(); + //this.parent = parent; + //parent.attach(this); + + this.rate = irate; + + parity = SerialPort.PARITY_NONE; + if (iparity == 'E') parity = SerialPort.PARITY_EVEN; + if (iparity == 'O') parity = SerialPort.PARITY_ODD; + + this.databits = idatabits; + + stopbits = SerialPort.STOPBITS_1; + if (istopbits == 1.5f) stopbits = SerialPort.STOPBITS_1_5; + if (istopbits == 2) stopbits = SerialPort.STOPBITS_2; + + try { + port = new SerialPort(iname); + port.openPort(); + port.setParams(rate, databits, stopbits, parity, true, true); + port.addEventListener(this); + } catch (Exception e) { + throw new SerialException(MessageFormat.format("Error opening serial port ''{0}''.", iname), e); + } + + if (port == null) { + throw new SerialException("Serial port '" + iname + "' not found. Did you select the right one from the Tools > Serial Port menu?"); + } + } + + + public void setup() { + //parent.registerCall(this, DISPOSE); + } + + public void dispose() throws IOException { + if (port != null) { + try { + if (port.isOpened()) { + port.closePort(); // close the port + } + } catch (SerialPortException e) { + throw new IOException(e); + } finally { + port = null; + } + } + } + + public void addListener(MessageConsumer consumer) { + this.consumer = consumer; + } + + public synchronized void serialEvent(SerialPortEvent serialEvent) { + if (serialEvent.isRXCHAR()) { + try { + byte[] buf = port.readBytes(serialEvent.getEventValue()); + if (buf.length > 0) { + if (bufferLast == buffer.length) { + byte temp[] = new byte[bufferLast << 1]; + System.arraycopy(buffer, 0, temp, 0, bufferLast); + buffer = temp; + } + if (monitor) { + System.out.print(new String(buf)); + } + if (this.consumer != null) { + this.consumer.message(new String(buf)); + } + } + } catch (SerialPortException e) { + errorMessage("serialEvent", e); + } + } + } + + + /** + * Returns the number of bytes that have been read from serial + * and are waiting to be dealt with by the user. + */ + public synchronized int available() { + return (bufferLast - bufferIndex); + } + + + /** + * Ignore all the bytes read so far and empty the buffer. + */ + public synchronized void clear() { + bufferLast = 0; + bufferIndex = 0; + } + + + /** + * Returns a number between 0 and 255 for the next byte that's + * waiting in the buffer. + * Returns -1 if there was no byte (although the user should + * first check available() to see if things are ready to avoid this) + */ + public synchronized int read() { + if (bufferIndex == bufferLast) return -1; + + int outgoing = buffer[bufferIndex++] & 0xff; + if (bufferIndex == bufferLast) { // rewind + bufferIndex = 0; + bufferLast = 0; + } + return outgoing; + } + + + /** + * Returns the next byte in the buffer as a char. + * Returns -1, or 0xffff, if nothing is there. + */ + public synchronized char readChar() { + if (bufferIndex == bufferLast) return (char)(-1); + return (char) read(); + } + + + /** + * Return a byte array of anything that's in the serial buffer. + * Not particularly memory/speed efficient, because it creates + * a byte array on each read, but it's easier to use than + * readBytes(byte b[]) (see below). + */ + public synchronized byte[] readBytes() { + if (bufferIndex == bufferLast) return null; + + int length = bufferLast - bufferIndex; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Grab whatever is in the serial buffer, and stuff it into a + * byte buffer passed in by the user. This is more memory/time + * efficient than readBytes() returning a byte[] array. + *

+ * Returns an int for how many bytes were read. If more bytes + * are available than can fit into the byte array, only those + * that will fit are read. + */ + public synchronized int readBytes(byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + + int length = bufferLast - bufferIndex; + if (length > outgoing.length) length = outgoing.length; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Reads from the serial port into a buffer of bytes up to and + * including a particular character. If the character isn't in + * the serial buffer, then 'null' is returned. + */ + public synchronized byte[] readBytesUntil(int interesting) { + if (bufferIndex == bufferLast) return null; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return null; + + int length = found - bufferIndex + 1; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Reads from the serial port into a buffer of bytes until a + * particular character. If the character isn't in the serial + * buffer, then 'null' is returned. + *

+ * If outgoing[] is not big enough, then -1 is returned, + * and an error message is printed on the console. + * If nothing is in the buffer, zero is returned. + * If 'interesting' byte is not in the buffer, then 0 is returned. + */ + public synchronized int readBytesUntil(int interesting, byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return 0; + + int length = found - bufferIndex + 1; + if (length > outgoing.length) { + System.err.println("readBytesUntil() byte buffer is" + + " too small for the " + length + + " bytes up to and including char " + interesting); + return -1; + } + //byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Return whatever has been read from the serial port so far + * as a String. It assumes that the incoming characters are ASCII. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readString() { + if (bufferIndex == bufferLast) return null; + return new String(readBytes()); + } + + + /** + * Combination of readBytesUntil and readString. See caveats in + * each function. Returns null if it still hasn't found what + * you're looking for. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readStringUntil(int interesting) { + byte b[] = readBytesUntil(interesting); + if (b == null) return null; + return new String(b); + } + + + /** + * This will handle both ints, bytes and chars transparently. + */ + public void write(int what) { // will also cover char + try { + port.writeInt(what & 0xff); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + public void write(byte bytes[]) { + try { + port.writeBytes(bytes); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + /** + * Write a String to the output. Note that this doesn't account + * for Unicode (two bytes per char), nor will it send UTF8 + * characters.. It assumes that you mean to send a byte buffer + * (most often the case for networking and serial i/o) and + * will only use the bottom 8 bits of each char in the string. + * (Meaning that internally it uses String.getBytes) + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public void write(String what) { + write(what.getBytes()); + } + + public void setDTR(boolean state) { + try { + port.setDTR(state); + } catch (SerialPortException e) { + errorMessage("setDTR", e); + } + } + + public void setRTS(boolean state) { + try { + port.setRTS(state); + } catch (SerialPortException e) { + errorMessage("setRTS", e); + } + } + + static public List list() { + return Arrays.asList(SerialPortList.getPortNames()); + } + + + /** + * General error reporting, all corraled here just in case + * I think of something slightly more intelligent to do. + */ + static public void errorMessage(String where, Throwable e) { + System.err.println("Error inside Serial." + where + "()"); + e.printStackTrace(); + } +} + + + /* + class SerialMenuListener implements ItemListener { + //public SerialMenuListener() { } + + public void itemStateChanged(ItemEvent e) { + int count = serialMenu.getItemCount(); + for (int i = 0; i < count; i++) { + ((CheckboxMenuItem)serialMenu.getItem(i)).setState(false); + } + CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); + item.setState(true); + String name = item.getLabel(); + //System.out.println(item.getLabel()); + PdeBase.properties.put("serial.port", name); + //System.out.println("set to " + get("serial.port")); + } + } + */ + + + /* + protected Vector buildPortList() { + // get list of names for serial ports + // have the default port checked (if present) + Vector list = new Vector(); + + //SerialMenuListener listener = new SerialMenuListener(); + boolean problem = false; + + // if this is failing, it may be because + // lib/javax.comm.properties is missing. + // java is weird about how it searches for java.comm.properties + // so it tends to be very fragile. i.e. quotes in the CLASSPATH + // environment variable will hose things. + try { + //System.out.println("building port list"); + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + //System.out.println(portId); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + //if (portId.getName().equals(port)) { + String name = portId.getName(); + //CheckboxMenuItem mi = + //new CheckboxMenuItem(name, name.equals(defaultName)); + + //mi.addItemListener(listener); + //serialMenu.add(mi); + list.addElement(name); + } + } + } catch (UnsatisfiedLinkError e) { + e.printStackTrace(); + problem = true; + + } catch (Exception e) { + System.out.println("exception building serial menu"); + e.printStackTrace(); + } + + //if (serialMenu.getItemCount() == 0) { + //System.out.println("dimming serial menu"); + //serialMenu.setEnabled(false); + //} + + // only warn them if this is the first time + if (problem && PdeBase.firstTime) { + JOptionPane.showMessageDialog(this, //frame, + "Serial port support not installed.\n" + + "Check the readme for instructions\n" + + "if you need to use the serial port. ", + "Serial Port Warning", + JOptionPane.WARNING_MESSAGE); + } + return list; + } + */ + + + diff --git a/tools/macosx/src/maple_loader/src/processing/app/SerialException.java b/tools/macosx/src/maple_loader/src/processing/app/SerialException.java new file mode 100644 index 0000000..525c240 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/SerialException.java @@ -0,0 +1,39 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) 2007 David A. Mellis + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +public class SerialException extends Exception { + public SerialException() { + super(); + } + + public SerialException(String message) { + super(message); + } + + public SerialException(String message, Throwable cause) { + super(message, cause); + } + + public SerialException(Throwable cause) { + super(cause); + } +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/debug/MessageConsumer.java b/tools/macosx/src/maple_loader/src/processing/app/debug/MessageConsumer.java new file mode 100644 index 0000000..5e20429 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/debug/MessageConsumer.java @@ -0,0 +1,42 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * Interface for dealing with parser/compiler output. + *

+ * Different instances of MessageStream need to do different things with + * messages. In particular, a stream instance used for parsing output from + * the compiler compiler has to interpret its messages differently than one + * parsing output from the runtime. + *

+ * Classes which consume messages and do something with them + * should implement this interface. + */ +public interface MessageConsumer { + + public void message(String s); + +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/debug/MessageSiphon.java b/tools/macosx/src/maple_loader/src/processing/app/debug/MessageSiphon.java new file mode 100644 index 0000000..26901c3 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/debug/MessageSiphon.java @@ -0,0 +1,104 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.SocketException; + +/** + * Slurps up messages from compiler. + */ +public class MessageSiphon implements Runnable { + + private final BufferedReader streamReader; + private final MessageConsumer consumer; + + private Thread thread; + private boolean canRun; + + public MessageSiphon(InputStream stream, MessageConsumer consumer) { + this.streamReader = new BufferedReader(new InputStreamReader(stream)); + this.consumer = consumer; + this.canRun = true; + + thread = new Thread(this); + // don't set priority too low, otherwise exceptions won't + // bubble up in time (i.e. compile errors have a weird delay) + //thread.setPriority(Thread.MIN_PRIORITY); + thread.setPriority(Thread.MAX_PRIORITY - 1); + thread.start(); + } + + + public void run() { + try { + // process data until we hit EOF; this will happily block + // (effectively sleeping the thread) until new data comes in. + // when the program is finally done, null will come through. + // + String currentLine; + while (canRun && (currentLine = streamReader.readLine()) != null) { + // \n is added again because readLine() strips it out + //EditorConsole.systemOut.println("messaging in"); + consumer.message(currentLine + "\n"); + //EditorConsole.systemOut.println("messaging out"); + } + //EditorConsole.systemOut.println("messaging thread done"); + } catch (NullPointerException npe) { + // Fairly common exception during shutdown + } catch (SocketException e) { + // socket has been close while we were wainting for data. nothing to see here, move along + } catch (Exception e) { + // On Linux and sometimes on Mac OS X, a "bad file descriptor" + // message comes up when closing an applet that's run externally. + // That message just gets supressed here.. + String mess = e.getMessage(); + if ((mess != null) && + (mess.indexOf("Bad file descriptor") != -1)) { + //if (e.getMessage().indexOf("Bad file descriptor") == -1) { + //System.err.println("MessageSiphon err " + e); + //e.printStackTrace(); + } else { + e.printStackTrace(); + } + } finally { + thread = null; + } + } + + // Wait until the MessageSiphon thread is complete. + public void join() throws java.lang.InterruptedException { + // Grab a temp copy in case another thread nulls the "thread" + // member variable + Thread t = thread; + if (t != null) t.join(); + } + + public void stop() { + this.canRun = false; + } + +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/debug/RunnerException.java b/tools/macosx/src/maple_loader/src/processing/app/debug/RunnerException.java new file mode 100644 index 0000000..0a67d1e --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/debug/RunnerException.java @@ -0,0 +1,161 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-08 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * An exception with a line number attached that occurs + * during either compile time or run time. + */ +@SuppressWarnings("serial") +public class RunnerException extends Exception { + protected String message; + protected int codeIndex; + protected int codeLine; + protected int codeColumn; + protected boolean showStackTrace; + + + public RunnerException(String message) { + this(message, true); + } + + public RunnerException(String message, boolean showStackTrace) { + this(message, -1, -1, -1, showStackTrace); + } + + public RunnerException(String message, int file, int line) { + this(message, file, line, -1, true); + } + + + public RunnerException(String message, int file, int line, int column) { + this(message, file, line, column, true); + } + + + public RunnerException(String message, int file, int line, int column, + boolean showStackTrace) { + this.message = message; + this.codeIndex = file; + this.codeLine = line; + this.codeColumn = column; + this.showStackTrace = showStackTrace; + } + + + public RunnerException(Exception e) { + super(e); + this.showStackTrace = true; + } + + /** + * Override getMessage() in Throwable, so that I can set + * the message text outside the constructor. + */ + public String getMessage() { + return message; + } + + + public void setMessage(String message) { + this.message = message; + } + + + public int getCodeIndex() { + return codeIndex; + } + + + public void setCodeIndex(int index) { + codeIndex = index; + } + + + public boolean hasCodeIndex() { + return codeIndex != -1; + } + + + public int getCodeLine() { + return codeLine; + } + + + public void setCodeLine(int line) { + this.codeLine = line; + } + + + public boolean hasCodeLine() { + return codeLine != -1; + } + + + public void setCodeColumn(int column) { + this.codeColumn = column; + } + + + public int getCodeColumn() { + return codeColumn; + } + + + public void showStackTrace() { + showStackTrace = true; + } + + + public void hideStackTrace() { + showStackTrace = false; + } + + + /** + * Nix the java.lang crap out of an exception message + * because it scares the children. + *

+ * This function must be static to be used with super() + * in each of the constructors above. + */ + /* + static public final String massage(String msg) { + if (msg.indexOf("java.lang.") == 0) { + //int dot = msg.lastIndexOf('.'); + msg = msg.substring("java.lang.".length()); + } + return msg; + //return (dot == -1) ? msg : msg.substring(dot+1); + } + */ + + + public void printStackTrace() { + if (showStackTrace) { + super.printStackTrace(); + } + } +} diff --git a/tools/macosx/src/maple_loader/src/processing/app/helpers/ProcessUtils.java b/tools/macosx/src/maple_loader/src/processing/app/helpers/ProcessUtils.java new file mode 100644 index 0000000..c023f58 --- /dev/null +++ b/tools/macosx/src/maple_loader/src/processing/app/helpers/ProcessUtils.java @@ -0,0 +1,32 @@ +package processing.app.helpers; + +//import processing.app.Base; + +import java.io.IOException; +import java.util.Map; + +import processing.app.Base; + +public class ProcessUtils { + + public static Process exec(String[] command) throws IOException { + // No problems on linux and mac + if (!Base.isWindows()) { + return Runtime.getRuntime().exec(command); + } + + // Brutal hack to workaround windows command line parsing. + // http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly + // http://msdn.microsoft.com/en-us/library/a1y7w461.aspx + // http://bugs.sun.com/view_bug.do?bug_id=6468220 + // http://bugs.sun.com/view_bug.do?bug_id=6518827 + String[] cmdLine = new String[command.length]; + for (int i = 0; i < command.length; i++) + cmdLine[i] = command[i].replace("\"", "\\\""); + + ProcessBuilder pb = new ProcessBuilder(cmdLine); + Map env = pb.environment(); + env.put("CYGWIN", "nodosfilewarning"); + return pb.start(); + } +} diff --git a/tools/macosx/src/stm32flash_serial/src/AUTHORS b/tools/macosx/src/stm32flash_serial/src/AUTHORS new file mode 100644 index 0000000..d096f22 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/AUTHORS @@ -0,0 +1,19 @@ +Authors ordered by first contribution. + +Geoffrey McRae +Bret Olmsted +Tormod Volden +Jakob Malm +Reuben Dowle +Matthias Kubisch +Paul Fertser +Daniel Strnad +Jérémie Rapin +Christian Pointner +Mats Erik Andersson +Alexey Borovik +Antonio Borneo +Armin van der Togt +Brian Silverman +Georg Hofmann +Luis Rodrigues diff --git a/tools/macosx/src/stm32flash_serial/src/Android.mk b/tools/macosx/src/stm32flash_serial/src/Android.mk new file mode 100644 index 0000000..7be3d00 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/Android.mk @@ -0,0 +1,20 @@ +TOP_LOCAL_PATH := $(call my-dir) + +include $(call all-named-subdir-makefiles, parsers) + +LOCAL_PATH := $(TOP_LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_MODULE := stm32flash +LOCAL_SRC_FILES := \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c \ + stm32.c \ + utils.c +LOCAL_STATIC_LIBRARIES := libparsers +include $(BUILD_EXECUTABLE) diff --git a/tools/macosx/src/stm32flash_serial/src/HOWTO b/tools/macosx/src/stm32flash_serial/src/HOWTO new file mode 100644 index 0000000..d8f32eb --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/HOWTO @@ -0,0 +1,35 @@ +Add new interfaces: +===================================================================== +Current version 0.4 supports the following interfaces: +- UART Windows (either "COMn" and "\\.\COMn"); +- UART posix/Linux (e.g. "/dev/ttyUSB0"); +- I2C Linux through standard driver "i2c-dev" (e.g. "/dev/i2c-n"). + +Starting from version 0.4, the back-end of stm32flash is modular and +ready to be expanded to support new interfaces. +I'm planning adding SPI on Linux through standard driver "spidev". +You are invited to contribute with more interfaces. + +To add a new interface you need to add a new file, populate the struct +port_interface (check at the end of files i2c.c, serial_posix.c and +serial_w32.c) and provide the relative functions to operate on the +interface: open/close, read/write, get_cfg_str and the optional gpio. +The include the new drive in Makefile and register the new struct +port_interface in file port.c in struct port_interface *ports[]. + +There are several USB-I2C adapter in the market, each providing its +own libraries to communicate with the I2C bus. +Could be interesting to provide as back-end a bridge between stm32flash +and such libraries (I have no plan on this item). + + +Add new STM32 devices: +===================================================================== +Add a new line in file dev_table.c, in table devices[]. +The fields of the table are listed in stm32.h, struct stm32_dev. + + +Cross compile on Linux host for Windows target with MinGW: +===================================================================== +I'm using a 64 bit Arch Linux machines, and I usually run: + make CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar diff --git a/tools/macosx/src/stm32flash_serial/src/I2C.txt b/tools/macosx/src/stm32flash_serial/src/I2C.txt new file mode 100644 index 0000000..4c05ff6 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/I2C.txt @@ -0,0 +1,94 @@ +About I2C back-end communication in stm32flash +========================================================================== + +Starting from version v0.4, beside the serial communication port, +stm32flash adds support for I2C port to talk with STM32 bootloader. + +The current I2C back-end supports only the API provided by Linux kernel +driver "i2c-dev", so only I2C controllers with Linux kernel driver can be +used. +In Linux source code, most of the drivers for I2C and SMBUS controllers +are in + ./drivers/i2c/busses/ +Only I2C is supported by STM32 bootloader, so check the section below +about SMBUS. +No I2C support for Windows is available in stm32flash v0.4. + +Thanks to the new modular back-end, stm32flash can be easily extended to +support new back-ends and API. Check HOWTO file in stm32flash source code +for details. + +In the market there are several USB-to-I2C dongles; most of them are not +supported by kernel drivers. Manufacturer provide proprietary userspace +libraries using not standardized API. +These API and dongles could be supported in feature versions. + +There are currently 3 versions of STM32 bootloader for I2C communications: +- v1.0 using I2C clock stretching synchronization between host and STM32; +- v1.1 superset of v1.0, adds non stretching commands; +- v1.2 superset of v1.1, adds CRC command and compatibility with i2cdetect. +Details in ST application note AN2606. +All the bootloaders above are tested and working with stm32flash. + + +SMBUS controllers +========================================================================== + +Almost 50% of the drivers in Linux source code folder + ./drivers/i2c/busses/ +are for controllers that "only" support SMBUS protocol. They can NOT +operate with STM32 bootloader. +To identify if your controller supports I2C, use command: + i2cdetect -F n +where "n" is the number of the I2C interface (e.g. n=3 for "/dev/i2c-3"). +Controllers that supports I2C will report + I2C yes +Controller that support both I2C and SMBUS are ok. + +If you are interested on details about SMBUS protocol, you can download +the current specs from + http://smbus.org/specs/smbus20.pdf +and you can read the files in Linux source code + ./Documentation/i2c/i2c-protocol + ./Documentation/i2c/smbus-protocol + + +About bootloader v1.0 +========================================================================== + +Version v1.0 can have issues with some I2C controllers due to use of clock +stretching during commands that require long operations, like flash erase +and programming. + +Clock stretching is a technique to synchronize host and I2C device. When +I2C device wants to force a delay in the communication, it push "low" the +I2C clock; the I2C controller detects it and waits until I2C clock returns +"high". +Most I2C controllers set a "timeout" for clock stretching, ranging from +few milli-seconds to seconds depending on specific HW or SW driver. + +It is possible that the timeout in your I2C controller is smaller than the +delay required for flash erase or programming. In this case the I2C +controller will timeout and report error to stm32flash. +There is no possibility for stm32flash to retry, so it can only signal the +error and exit. + +To by-pass the issue with bootloader v1.0 you can modify the kernel driver +of your I2C controller. Not an easy job, since every controller has its own +way to handle the timeout. + +In my case I'm using the I2C controller integrated in the VGA port of my +laptop HP EliteBook 8460p. I built the 0.25$ VGA-to-I2C adapter reported in + http://www.paintyourdragon.com/?p=43 +To change the timeout of the I2C controller I had to modify the kernel file + drivers/gpu/drm/radeon/radeon_i2c.c +line 969 +- i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ ++ i2c->bit.timeout = msecs_to_jiffies(5000); /* 5s for STM32 */ +and recompile it. +Then + $> modprobe i2c-dev + $> chmod 666 /dev/i2c-7 + #> stm32flash -a 0x39 /dev/i2c-7 + +2014-09-16 Antonio Borneo diff --git a/tools/macosx/src/stm32flash_serial/src/Makefile b/tools/macosx/src/stm32flash_serial/src/Makefile new file mode 100644 index 0000000..0328d55 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/Makefile @@ -0,0 +1,38 @@ +PREFIX = /usr/local +CFLAGS += -Wall -g + +INSTALL = install + +OBJS = dev_table.o \ + i2c.o \ + init.o \ + main.o \ + port.o \ + serial_common.o \ + serial_platform.o \ + stm32.o \ + utils.o + +LIBOBJS = parsers/parsers.a + +all: stm32flash + +serial_platform.o: serial_posix.c serial_w32.c + +parsers/parsers.a: + cd parsers && $(MAKE) parsers.a + +stm32flash: $(OBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBOBJS) + +clean: + rm -f $(OBJS) stm32flash + cd parsers && $(MAKE) $@ + +install: all + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 755 stm32flash $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -d $(DESTDIR)$(PREFIX)/share/man/man1 + $(INSTALL) -m 644 stm32flash.1 $(DESTDIR)$(PREFIX)/share/man/man1 + +.PHONY: all clean install diff --git a/tools/macosx/src/stm32flash_serial/src/TODO b/tools/macosx/src/stm32flash_serial/src/TODO new file mode 100644 index 0000000..41df614 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/TODO @@ -0,0 +1,7 @@ + +stm32: +- Add support for variable page size + +AUTHORS: +- Add contributors from Geoffrey's commits + diff --git a/tools/macosx/src/stm32flash_serial/src/dev_table.c b/tools/macosx/src/stm32flash_serial/src/dev_table.c new file mode 100644 index 0000000..399cd9d --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/dev_table.c @@ -0,0 +1,70 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2014 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. +*/ + +#include "stm32.h" + +/* + * Device table, corresponds to the "Bootloader device-dependant parameters" + * table in ST document AN2606. + * Note that the option bytes upper range is inclusive! + */ +const stm32_dev_t devices[] = { + /* F0 */ + {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x445, "STM32F042xx" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800}, + {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800}, + /* F1 */ + {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800}, + {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800}, + /* Note that F2 and F4 devices have sectors of different page sizes + and only the first sectors (of one page size) are included here */ + /* F2 */ + {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* F3 */ + {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x439, "STM32F302x4(6/8)" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + /* F4 */ + {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */ + {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + /* L0 */ + {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + /* L1 */ + {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000}, + {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + /* These are not (yet) in AN2606: */ + {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0}, + {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0}, + {0x0} +}; diff --git a/tools/macosx/src/stm32flash_serial/src/gpl-2.0.txt b/tools/macosx/src/stm32flash_serial/src/gpl-2.0.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/gpl-2.0.txt @@ -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/tools/macosx/src/stm32flash_serial/src/i2c.c b/tools/macosx/src/stm32flash_serial/src/i2c.c new file mode 100644 index 0000000..10e6bb1 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/i2c.c @@ -0,0 +1,209 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + + +#if !defined(__linux__) + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + return PORT_ERR_NODEV; +} + +struct port_interface port_i2c = { + .name = "i2c", + .open = i2c_open, +}; + +#else + +#ifdef __ANDROID__ +#define I2C_SLAVE 0x0703 /* Use this slave address */ +#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */ +/* To determine what functionality is present */ +#define I2C_FUNC_I2C 0x00000001 +#else +#include +#include +#endif + +#include + +struct i2c_priv { + int fd; + int addr; +}; + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + struct i2c_priv *h; + int fd, addr, ret; + unsigned long funcs; + + /* 1. check device name match */ + if (strncmp(ops->device, "/dev/i2c-", strlen("/dev/i2c-"))) + return PORT_ERR_NODEV; + + /* 2. check options */ + addr = ops->bus_addr; + if (addr < 0x03 || addr > 0x77) { + fprintf(stderr, "I2C address out of range [0x03-0x77]\n"); + return PORT_ERR_UNKNOWN; + } + + /* 3. open it */ + h = calloc(sizeof(*h), 1); + if (h == NULL) { + fprintf(stderr, "End of memory\n"); + return PORT_ERR_UNKNOWN; + } + fd = open(ops->device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open special file \"%s\"\n", + ops->device); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 3.5. Check capabilities */ + ret = ioctl(fd, I2C_FUNCS, &funcs); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(funcs) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + if ((funcs & I2C_FUNC_I2C) == 0) { + fprintf(stderr, "Error: controller is not I2C, only SMBUS.\n"); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 4. set options */ + ret = ioctl(fd, I2C_SLAVE, addr); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(slave) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + h->fd = fd; + h->addr = addr; + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t i2c_close(struct port_interface *port) +{ + struct i2c_priv *h; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + close(h->fd); + free(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t i2c_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = read(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = write(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n, + int level) +{ + return PORT_ERR_OK; +} + +static const char *i2c_get_cfg_str(struct port_interface *port) +{ + struct i2c_priv *h; + static char str[11]; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return "INVALID"; + snprintf(str, sizeof(str), "addr 0x%2x", h->addr); + return str; +} + +static struct varlen_cmd i2c_cmd_get_reply[] = { + {0x10, 11}, + {0x11, 17}, + {0x12, 18}, + { /* sentinel */ } +}; + +struct port_interface port_i2c = { + .name = "i2c", + .flags = PORT_STRETCH_W, + .open = i2c_open, + .close = i2c_close, + .read = i2c_read, + .write = i2c_write, + .gpio = i2c_gpio, + .cmd_get_reply = i2c_cmd_get_reply, + .get_cfg_str = i2c_get_cfg_str, +}; + +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/init.c b/tools/macosx/src/stm32flash_serial/src/init.c new file mode 100644 index 0000000..77a571b --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/init.c @@ -0,0 +1,219 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "init.h" +#include "serial.h" +#include "stm32.h" +#include "port.h" + +struct gpio_list { + struct gpio_list *next; + int gpio; +}; + + +static int write_to(const char *filename, const char *value) +{ + int fd, ret; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open file \"%s\"\n", filename); + return 0; + } + ret = write(fd, value, strlen(value)); + if (ret < 0) { + fprintf(stderr, "Error writing in file \"%s\"\n", filename); + close(fd); + return 0; + } + close(fd); + return 1; +} + +#if !defined(__linux__) +static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) +{ + fprintf(stderr, "GPIO control only available in Linux\n"); + return 0; +} +#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 */ + struct stat buf; + struct gpio_list *new; + int ret; + + sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + ret = stat(file, &buf); + if (ret) { + /* file miss, GPIO not exported yet */ + sprintf(num, "%d", n); + ret = write_to("/sys/class/gpio/export", num); + if (!ret) + return 0; + ret = stat(file, &buf); + if (ret) { + fprintf(stderr, "GPIO %d not available\n", n); + return 0; + } + new = (struct gpio_list *)malloc(sizeof(struct gpio_list)); + if (new == NULL) { + fprintf(stderr, "Out of memory\n"); + return 0; + } + new->gpio = n; + new->next = *gpio_to_release; + *gpio_to_release = new; + } + + return write_to(file, level ? "high" : "low"); +} +#endif + +static int release_gpio(int n) +{ + char num[16]; /* sized to carry MAX_INT */ + + sprintf(num, "%d", n); + return write_to("/sys/class/gpio/unexport", num); +} + +static int gpio_sequence(struct port_interface *port, const char *s, size_t l) +{ + struct gpio_list *gpio_to_release = NULL, *to_free; + int ret, level, gpio; + + ret = 1; + while (ret == 1 && *s && l > 0) { + if (*s == '-') { + level = 0; + s++; + l--; + } else + level = 1; + + if (isdigit(*s)) { + gpio = atoi(s); + while (isdigit(*s)) { + s++; + l--; + } + } else if (!strncmp(s, "rts", 3)) { + gpio = -GPIO_RTS; + s += 3; + l -= 3; + } else if (!strncmp(s, "dtr", 3)) { + gpio = -GPIO_DTR; + s += 3; + l -= 3; + } else if (!strncmp(s, "brk", 3)) { + 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)) { + if (*s == ',') { + s++; + l--; + } else { + fprintf(stderr, "Character \'%c\' is not a separator\n", *s); + ret = 0; + break; + } + } + if (gpio < 0) + ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); + else + ret = drive_gpio(gpio, level, &gpio_to_release); + usleep(100000); + } + + while (gpio_to_release) { + release_gpio(gpio_to_release->gpio); + to_free = gpio_to_release; + gpio_to_release = gpio_to_release->next; + free(to_free); + } + usleep(500000); + return ret; +} + +static int gpio_bl_entry(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL || seq[0] == ':') + return 1; + + s = strchr(seq, ':'); + if (s == NULL) + return gpio_sequence(port, seq, strlen(seq)); + + return gpio_sequence(port, seq, s - seq); +} + +static int gpio_bl_exit(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL) + return 1; + + s = strchr(seq, ':'); + if (s == NULL || s[1] == '\0') + return 1; + + return gpio_sequence(port, s + 1, strlen(s + 1)); +} + +int init_bl_entry(struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_entry(port, seq); + + return 1; +} + +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_exit(port, seq); + + if (stm32_reset_device(stm) != STM32_ERR_OK) + return 0; + return 1; +} diff --git a/tools/macosx/src/stm32flash_serial/src/init.h b/tools/macosx/src/stm32flash_serial/src/init.h new file mode 100644 index 0000000..6075b51 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/init.h @@ -0,0 +1,31 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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 _INIT_H +#define _INIT_H + +#include "stm32.h" +#include "port.h" + +int init_bl_entry(struct port_interface *port, const char *seq); +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq); + +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/main.c b/tools/macosx/src/stm32flash_serial/src/main.c new file mode 100644 index 0000000..f081d61 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/main.c @@ -0,0 +1,774 @@ +/* + 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 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "utils.h" +#include "serial.h" +#include "stm32.h" +#include "parsers/parser.h" +#include "port.h" + +#include "parsers/binary.h" +#include "parsers/hex.h" + +#define VERSION "Arduino_STM32_0.9" + +/* device globals */ +stm32_t *stm = NULL; + +void *p_st = NULL; +parser_t *parser = NULL; + +/* settings */ +struct port_options port_opts = { + .device = NULL, + .baudRate = SERIAL_BAUD_57600, + .serial_mode = "8e1", + .bus_addr = 0, + .rx_frame_max = STM32_MAX_RX_FRAME, + .tx_frame_max = STM32_MAX_TX_FRAME, +}; +int rd = 0; +int wr = 0; +int wu = 0; +int rp = 0; +int ur = 0; +int eraseOnly = 0; +int crc = 0; +int npages = 0; +int spage = 0; +int no_erase = 0; +char verify = 0; +int retry = 10; +char exec_flag = 0; +uint32_t execute = 0; +char init_flag = 1; +char force_binary = 0; +char reset_flag = 0; +char *filename; +char *gpio_seq = NULL; +uint32_t start_addr = 0; +uint32_t readwrite_len = 0; + +/* functions */ +int parse_options(int argc, char *argv[]); +void show_help(char *name); + +static int is_addr_in_ram(uint32_t addr) +{ + return addr >= stm->dev->ram_start && addr < stm->dev->ram_end; +} + +static int is_addr_in_flash(uint32_t addr) +{ + return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; +} + +static int flash_addr_to_page_floor(uint32_t addr) +{ + if (!is_addr_in_flash(addr)) + return 0; + + return (addr - stm->dev->fl_start) / stm->dev->fl_ps; +} + +static int flash_addr_to_page_ceil(uint32_t addr) +{ + if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) + return 0; + + return (addr + stm->dev->fl_ps - 1 - stm->dev->fl_start) + / stm->dev->fl_ps; +} + +static uint32_t flash_page_to_addr(int page) +{ + return stm->dev->fl_start + page * stm->dev->fl_ps; +} + +int main(int argc, char* argv[]) { + struct port_interface *port = NULL; + int ret = 1; + stm32_err_t s_err; + parser_err_t perr; + FILE *diag = stdout; + + fprintf(diag, "stm32flash " VERSION "\n\n"); + fprintf(diag, "http://github.com/rogerclarkmelbourne/arduino_stm32\n\n"); + if (parse_options(argc, argv) != 0) + goto close; + + if (rd && filename[0] == '-') { + diag = stderr; + } + + if (wr) { + /* first try hex */ + if (!force_binary) { + parser = &PARSER_HEX; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (force_binary || (perr = parser->open(p_st, filename, 0)) != PARSER_ERR_OK) { + if (force_binary || perr == PARSER_ERR_INVALID_FILE) { + if (!force_binary) { + parser->close(p_st); + p_st = NULL; + } + + /* now try binary */ + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + perr = parser->open(p_st, filename, 0); + } + + /* if still have an error, fail */ + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) perror(filename); + goto close; + } + } + + fprintf(diag, "Using Parser : %s\n", parser->name); + } else { + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (port_open(&port_opts, &port) != PORT_ERR_OK) { + fprintf(stderr, "Failed to open port: %s\n", port_opts.device); + goto close; + } + + fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); + if (init_flag && init_bl_entry(port, gpio_seq) == 0) + goto close; + stm = stm32_init(port, init_flag); + if (!stm) + goto close; + + fprintf(diag, "Version : 0x%02x\n", stm->bl_version); + if (port->flags & PORT_GVR_ETX) { + fprintf(diag, "Option 1 : 0x%02x\n", stm->option1); + fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); + } + fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name); + fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); + fprintf(diag, "- Flash : %dKiB (sector size: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps); + 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); + + uint8_t buffer[256]; + uint32_t addr, start, end; + unsigned int len; + int failed = 0; + int first_page, num_pages; + + /* + * Cleanup addresses: + * + * Starting from options + * start_addr, readwrite_len, spage, npages + * and using device memory size, compute + * start, end, first_page, num_pages + */ + if (start_addr || readwrite_len) { + start = start_addr; + + if (is_addr_in_flash(start)) + end = stm->dev->fl_end; + else { + no_erase = 1; + if (is_addr_in_ram(start)) + end = stm->dev->ram_end; + else + end = start + sizeof(uint32_t); + } + + if (readwrite_len && (end > start + readwrite_len)) + end = start + readwrite_len; + + first_page = flash_addr_to_page_floor(start); + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + else + num_pages = flash_addr_to_page_ceil(end) - first_page; + } else if (!spage && !npages) { + start = stm->dev->fl_start; + end = stm->dev->fl_end; + first_page = 0; + num_pages = 0xff; /* mass erase */ + } else { + first_page = spage; + start = flash_page_to_addr(first_page); + if (start > stm->dev->fl_end) { + fprintf(stderr, "Address range exceeds flash size.\n"); + goto close; + } + + if (npages) { + num_pages = npages; + end = flash_page_to_addr(first_page + num_pages); + if (end > stm->dev->fl_end) + end = stm->dev->fl_end; + } else { + end = stm->dev->fl_end; + num_pages = flash_addr_to_page_ceil(end) - first_page; + } + + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + } + + if (rd) { + unsigned int max_len = port_opts.rx_frame_max; + + fprintf(diag, "Memory read\n"); + + perr = parser->open(p_st, filename, 1); + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) + perror(filename); + goto close; + } + + fflush(diag); + addr = start; + while(addr < end) { + uint32_t left = end - addr; + len = max_len > left ? left : max_len; + s_err = stm32_read_memory(stm, addr, buffer, len); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr); + goto close; + } + if (parser->write(p_st, buffer, len) != PARSER_ERR_OK) + { + fprintf(stderr, "Failed to write data to file\n"); + goto close; + } + addr += len; + + fprintf(diag, + "\rRead address 0x%08x (%.2f%%) ", + addr, + (100.0f / (float)(end - start)) * (float)(addr - start) + ); + fflush(diag); + } + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (rp) { + fprintf(stdout, "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"); + /* 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) { + ret = 0; + fprintf(stdout, "Erasing flash\n"); + + if (num_pages != 0xff && + (start != flash_page_to_addr(first_page) + || end != flash_page_to_addr(first_page + num_pages))) { + fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); + ret = 1; + goto close; + } + + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + ret = 1; + goto close; + } + } else if (wu) { + fprintf(diag, "Write-unprotecting flash\n"); + /* the device automatically performs a reset after the sending the ACK */ + reset_flag = 0; + stm32_wunprot_memory(stm); + fprintf(diag, "Done.\n"); + + } else if (wr) { + fprintf(diag, "Write to memory\n"); + + off_t offset = 0; + ssize_t r; + unsigned int size; + unsigned int max_wlen, max_rlen; + + max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */ + max_wlen &= ~3; /* 32 bit aligned */ + + max_rlen = port_opts.rx_frame_max; + max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; + + /* Assume data from stdin is whole device */ + if (filename[0] == '-' && filename[1] == '\0') + 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) { + // 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 + if (!no_erase && num_pages) { + fprintf(diag, "Erasing memory\n"); + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + goto close; + } + } + + fflush(diag); + addr = start; + while(addr < end && offset < size) { + uint32_t left = end - addr; + len = max_wlen > left ? left : max_wlen; + len = len > size - offset ? size - offset : len; + + if (parser->read(p_st, buffer, &len) != PARSER_ERR_OK) + goto close; + + if (len == 0) { + if (filename[0] == '-') { + 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) { + fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr); + goto close; + } + + if (verify) { + uint8_t compare[len]; + unsigned int offset, rlen; + + offset = 0; + while (offset < len) { + rlen = len - offset; + rlen = rlen < max_rlen ? rlen : max_rlen; + s_err = stm32_read_memory(stm, addr + offset, compare + offset, rlen); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset); + goto close; + } + offset += rlen; + } + + for(r = 0; r < len; ++r) + if (buffer[r] != compare[r]) { + if (failed == retry) { + fprintf(stderr, "Failed to verify at address 0x%08x, expected 0x%02x and found 0x%02x\n", + (uint32_t)(addr + r), + buffer [r], + compare[r] + ); + goto close; + } + ++failed; + goto again; + } + + failed = 0; + } + + addr += len; + offset += len; + + fprintf(diag, + "\rWrote %saddress 0x%08x (%.2f%%) ", + verify ? "and verified " : "", + addr, + (100.0f / size) * offset + ); + fflush(diag); + + } + + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (crc) { + uint32_t crc_val = 0; + + fprintf(diag, "CRC computation\n"); + + s_err = stm32_crc_wrapper(stm, start, end - start, &crc_val); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read CRC\n"); + goto close; + } + fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end, + crc_val); + ret = 0; + goto close; + } else + ret = 0; + +close: + if (stm && exec_flag && ret == 0) { + if (execute == 0) + execute = stm->dev->fl_start; + + fprintf(diag, "\nStarting execution at address 0x%08x... ", execute); + fflush(diag); + if (stm32_go(stm, execute) == STM32_ERR_OK) { + reset_flag = 0; + fprintf(diag, "done.\n"); + } else + fprintf(diag, "failed.\n"); + } + + if (stm && reset_flag) { + fprintf(diag, "\nResetting device... "); + fflush(diag); + if (init_bl_exit(stm, port, gpio_seq)) + fprintf(diag, "done.\n"); + else fprintf(diag, "failed.\n"); + } + + if (p_st ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) + port->close(port); + + fprintf(diag, "\n"); + return ret; +} + +int parse_options(int argc, char *argv[]) +{ + int c; + char *pLen; + + while ((c = getopt(argc, argv, "a:b:m:r:w:e:vn:g:jkfcChuos:S:F:i:R")) != -1) { + switch(c) { + case 'a': + port_opts.bus_addr = strtoul(optarg, NULL, 0); + break; + + case 'b': + port_opts.baudRate = serial_get_baud(strtoul(optarg, NULL, 0)); + if (port_opts.baudRate == SERIAL_BAUD_INVALID) { + serial_baud_t baudrate; + fprintf(stderr, "Invalid baud rate, valid options are:\n"); + for (baudrate = SERIAL_BAUD_1200; baudrate != SERIAL_BAUD_INVALID; ++baudrate) + fprintf(stderr, " %d\n", serial_get_baud_int(baudrate)); + return 1; + } + break; + + case 'm': + if (strlen(optarg) != 3 + || serial_get_bits(optarg) == SERIAL_BITS_INVALID + || serial_get_parity(optarg) == SERIAL_PARITY_INVALID + || serial_get_stopbit(optarg) == SERIAL_STOPBIT_INVALID) { + fprintf(stderr, "Invalid serial mode\n"); + return 1; + } + port_opts.serial_mode = optarg; + break; + + 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"); + return 1; + } + filename = optarg; + if (filename[0] == '-') { + force_binary = 1; + } + break; + case 'e': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + npages = strtoul(optarg, NULL, 0); + if (npages > 0xFF || npages < 0) { + fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); + return 1; + } + if (!npages) + no_erase = 1; + break; + case 'u': + wu = 1; + if (rd || wr) { + fprintf(stderr, "ERROR: Invalid options, can't write unprotect and read/write at the same time\n"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + break; + + case 'v': + verify = 1; + break; + + case 'n': + retry = strtoul(optarg, NULL, 0); + break; + + case 'g': + exec_flag = 1; + execute = strtoul(optarg, NULL, 0); + if (execute % 4 != 0) { + fprintf(stderr, "ERROR: Execution address must be word-aligned\n"); + return 1; + } + break; + case 's': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + spage = strtoul(optarg, NULL, 0); + break; + case 'S': + if (spage || npages) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } else { + start_addr = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + readwrite_len = strtoul(pLen, NULL, 0); + if (readwrite_len == 0) { + fprintf(stderr, "ERROR: Invalid options, can't specify zero length\n"); + return 1; + } + } + } + break; + case 'F': + port_opts.rx_frame_max = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + port_opts.tx_frame_max = strtoul(pLen, NULL, 0); + } + if (port_opts.rx_frame_max < 0 + || port_opts.tx_frame_max < 0) { + fprintf(stderr, "ERROR: Invalid negative value for option -F\n"); + return 1; + } + if (port_opts.rx_frame_max == 0) + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + if (port_opts.tx_frame_max == 0) + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + if (port_opts.rx_frame_max < 20 + || port_opts.tx_frame_max < 5) { + fprintf(stderr, "ERROR: current code cannot work with small frames.\n"); + fprintf(stderr, "min(RX) = 20, min(TX) = 5\n"); + return 1; + } + if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) { + fprintf(stderr, "WARNING: Ignore RX length in option -F\n"); + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + } + if (port_opts.tx_frame_max > STM32_MAX_TX_FRAME) { + fprintf(stderr, "WARNING: Ignore TX length in option -F\n"); + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + } + break; + case 'f': + force_binary = 1; + break; + + case 'c': + init_flag = 0; + break; + + case 'h': + show_help(argv[0]); + exit(0); + + case 'i': + gpio_seq = optarg; + break; + + case 'R': + reset_flag = 1; + break; + + case 'C': + crc = 1; + break; + } + } + + for (c = optind; c < argc; ++c) { + if (port_opts.device) { + fprintf(stderr, "ERROR: Invalid parameter specified\n"); + show_help(argv[0]); + return 1; + } + port_opts.device = argv[c]; + } + + if (port_opts.device == NULL) { + fprintf(stderr, "ERROR: Device not specified\n"); + show_help(argv[0]); + return 1; + } + + if (!wr && verify) { + fprintf(stderr, "ERROR: Invalid usage, -v is only valid when writing\n"); + show_help(argv[0]); + return 1; + } + + return 0; +} + +void show_help(char *name) { + fprintf(stderr, + "Usage: %s [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]\n" + " -a bus_address Bus address (e.g. for I2C port)\n" + " -b rate Baud rate (default 57600)\n" + " -m mode Serial port mode (default 8e1)\n" + " -r filename Read flash to file (or - stdout)\n" + " -w filename Write flash from file (or - stdout)\n" + " -C Compute CRC of flash content\n" + " -u Disable the flash write-protection\n" + " -j Enable the flash read-protection\n" + " -k Disable the flash read-protection\n" + " -o Erase only\n" + " -e n Only erase n pages before writing the flash\n" + " -v Verify writes\n" + " -n count Retry failed writes up to count times (default 10)\n" + " -g address Start execution at specified address (0 = flash start)\n" + " -S address[:length] Specify start address and optionally length for\n" + " read/write/erase operations\n" + " -F RX_length[:TX_length] Specify the max length of RX and TX frame\n" + " -s start_page Flash at specified page (0 = flash start)\n" + " -f Force binary parser\n" + " -h Show this help\n" + " -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" + " -i GPIO_string GPIO sequence to enter/exit bootloader mode\n" + " GPIO_string=[entry_seq][:[exit_seq]]\n" + " sequence=[-]n[,sequence]\n" + " -R Reset device at exit.\n" + "\n" + "Examples:\n" + " Get device information:\n" + " %s /dev/ttyS0\n" + " or:\n" + " %s /dev/i2c-0\n" + "\n" + " Write with verify and then start execution:\n" + " %s -w filename -v -g 0x0 /dev/ttyS0\n" + "\n" + " Read flash to file:\n" + " %s -r filename /dev/ttyS0\n" + "\n" + " Read 100 bytes of flash from 0x1000 to stdout:\n" + " %s -r - -S 0x1000:100 /dev/ttyS0\n" + "\n" + " Start execution:\n" + " %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", + name, + name, + name, + name, + name, + name, + name, + name + ); +} + diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/Android.mk b/tools/macosx/src/stm32flash_serial/src/parsers/Android.mk new file mode 100644 index 0000000..afec18c --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/Android.mk @@ -0,0 +1,6 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libparsers +LOCAL_SRC_FILES := binary.c hex.c +include $(BUILD_STATIC_LIBRARY) diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/Makefile b/tools/macosx/src/stm32flash_serial/src/parsers/Makefile new file mode 100644 index 0000000..bb7df1e --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/Makefile @@ -0,0 +1,12 @@ + +CFLAGS += -Wall -g + +all: parsers.a + +parsers.a: binary.o hex.o + $(AR) rc $@ binary.o hex.o + +clean: + rm -f *.o parsers.a + +.PHONY: all clean diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/binary.c b/tools/macosx/src/stm32flash_serial/src/parsers/binary.c new file mode 100644 index 0000000..f491952 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/binary.c @@ -0,0 +1,140 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include + +#include "binary.h" + +typedef struct { + int fd; + char write; + struct stat stat; +} binary_t; + +void* binary_init() { + return calloc(sizeof(binary_t), 1); +} + +parser_err_t binary_open(void *storage, const char *filename, const char write) { + binary_t *st = storage; + if (write) { + if (filename[0] == '-') + st->fd = 1; + else + st->fd = open( + filename, +#ifndef __WIN32__ + O_WRONLY | O_CREAT | O_TRUNC, +#else + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, +#endif +#ifndef __WIN32__ + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH +#else + 0 +#endif + ); + st->stat.st_size = 0; + } else { + if (filename[0] == '-') { + st->fd = 0; + } else { + if (stat(filename, &st->stat) != 0) + return PARSER_ERR_INVALID_FILE; + st->fd = open(filename, +#ifndef __WIN32__ + O_RDONLY +#else + O_RDONLY | O_BINARY +#endif + ); + } + } + + st->write = write; + return st->fd == -1 ? PARSER_ERR_SYSTEM : PARSER_ERR_OK; +} + +parser_err_t binary_close(void *storage) { + binary_t *st = storage; + + if (st->fd) close(st->fd); + free(st); + return PARSER_ERR_OK; +} + +unsigned int binary_size(void *storage) { + binary_t *st = storage; + return st->stat.st_size; +} + +parser_err_t binary_read(void *storage, void *data, unsigned int *len) { + binary_t *st = storage; + unsigned int left = *len; + if (st->write) return PARSER_ERR_WRONLY; + + ssize_t r; + while(left > 0) { + r = read(st->fd, data, left); + /* If there is no data to read at all, return OK, but with zero read */ + if (r == 0 && left == *len) { + *len = 0; + return PARSER_ERR_OK; + } + if (r <= 0) return PARSER_ERR_SYSTEM; + left -= r; + data += r; + } + + *len = *len - left; + return PARSER_ERR_OK; +} + +parser_err_t binary_write(void *storage, void *data, unsigned int len) { + binary_t *st = storage; + if (!st->write) return PARSER_ERR_RDONLY; + + ssize_t r; + while(len > 0) { + r = write(st->fd, data, len); + if (r < 1) return PARSER_ERR_SYSTEM; + st->stat.st_size += r; + + len -= r; + data += r; + } + + return PARSER_ERR_OK; +} + +parser_t PARSER_BINARY = { + "Raw BINARY", + binary_init, + binary_open, + binary_close, + binary_size, + binary_read, + binary_write +}; + diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/binary.h b/tools/macosx/src/stm32flash_serial/src/parsers/binary.h new file mode 100644 index 0000000..d989acf --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/binary.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_BINARY_H +#define _PARSER_BINARY_H + +#include "parser.h" + +extern parser_t PARSER_BINARY; +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/hex.c b/tools/macosx/src/stm32flash_serial/src/parsers/hex.c new file mode 100644 index 0000000..3baf856 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/hex.c @@ -0,0 +1,224 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#include "hex.h" +#include "../utils.h" + +typedef struct { + size_t data_len, offset; + uint8_t *data; + uint8_t base; +} hex_t; + +void* hex_init() { + return calloc(sizeof(hex_t), 1); +} + +parser_err_t hex_open(void *storage, const char *filename, const char write) { + hex_t *st = storage; + if (write) { + return PARSER_ERR_RDONLY; + } else { + char mark; + int i, fd; + uint8_t checksum; + unsigned int c; + uint32_t base = 0; + unsigned int last_address = 0x0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return PARSER_ERR_SYSTEM; + + /* read in the file */ + + while(read(fd, &mark, 1) != 0) { + if (mark == '\n' || mark == '\r') continue; + if (mark != ':') + return PARSER_ERR_INVALID_FILE; + + char buffer[9]; + unsigned int reclen, address, type; + uint8_t *record = NULL; + + /* get the reclen, address, and type */ + buffer[8] = 0; + if (read(fd, &buffer, 8) != 8) return PARSER_ERR_INVALID_FILE; + if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* setup the checksum */ + checksum = + reclen + + ((address & 0xFF00) >> 8) + + ((address & 0x00FF) >> 0) + + type; + + switch(type) { + /* data record */ + case 0: + c = address - last_address; + st->data = realloc(st->data, st->data_len + c + reclen); + + /* if there is a gap, set it to 0xff and increment the length */ + if (c > 0) { + memset(&st->data[st->data_len], 0xff, c); + st->data_len += c; + } + + last_address = address + reclen; + record = &st->data[st->data_len]; + st->data_len += reclen; + break; + + /* extended segment address record */ + case 2: + base = 0; + break; + + /* extended linear address record */ + case 4: + base = address; + break; + } + + buffer[2] = 0; + for(i = 0; i < reclen; ++i) { + if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* add the byte to the checksum */ + checksum += c; + + switch(type) { + case 0: + if (record != NULL) { + record[i] = c; + } else { + return PARSER_ERR_INVALID_FILE; + } + break; + + case 2: + case 4: + base = (base << 8) | c; + break; + } + } + + /* read, scan, and verify the checksum */ + if ( + read(fd, &buffer, 2 ) != 2 || + sscanf(buffer, "%2x", &c) != 1 || + (uint8_t)(checksum + c) != 0x00 + ) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + switch(type) { + /* EOF */ + case 1: + close(fd); + return PARSER_ERR_OK; + + /* address record */ + 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) { + st->base = base; + break; + } + + /* we cant cope with files out of order */ + if (base < st->base) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* if there is a gap, enlarge and fill with zeros */ + 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); + st->data_len = len; + } + break; + } + } + + close(fd); + return PARSER_ERR_OK; + } +} + +parser_err_t hex_close(void *storage) { + hex_t *st = storage; + if (st) free(st->data); + free(st); + return PARSER_ERR_OK; +} + +unsigned int hex_size(void *storage) { + hex_t *st = storage; + return st->data_len; +} + +parser_err_t hex_read(void *storage, void *data, unsigned int *len) { + hex_t *st = storage; + unsigned int left = st->data_len - st->offset; + unsigned int get = left > *len ? *len : left; + + memcpy(data, &st->data[st->offset], get); + st->offset += get; + + *len = get; + return PARSER_ERR_OK; +} + +parser_err_t hex_write(void *storage, void *data, unsigned int len) { + return PARSER_ERR_RDONLY; +} + +parser_t PARSER_HEX = { + "Intel HEX", + hex_init, + hex_open, + hex_close, + hex_size, + hex_read, + hex_write +}; + diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/hex.h b/tools/macosx/src/stm32flash_serial/src/parsers/hex.h new file mode 100644 index 0000000..02413c9 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/hex.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_HEX_H +#define _PARSER_HEX_H + +#include "parser.h" + +extern parser_t PARSER_HEX; +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/parsers/parser.h b/tools/macosx/src/stm32flash_serial/src/parsers/parser.h new file mode 100644 index 0000000..c2fae3c --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/parsers/parser.h @@ -0,0 +1,56 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_PARSER +#define _H_PARSER + +enum parser_err { + PARSER_ERR_OK, + PARSER_ERR_SYSTEM, + PARSER_ERR_INVALID_FILE, + PARSER_ERR_WRONLY, + PARSER_ERR_RDONLY +}; +typedef enum parser_err parser_err_t; + +struct parser { + const char *name; + void* (*init )(); /* initialise the parser */ + parser_err_t (*open )(void *storage, const char *filename, const char write); /* open the file for read|write */ + parser_err_t (*close)(void *storage); /* close and free the parser */ + unsigned int (*size )(void *storage); /* get the total data size */ + parser_err_t (*read )(void *storage, void *data, unsigned int *len); /* read a block of data */ + parser_err_t (*write)(void *storage, void *data, unsigned int len); /* write a block of data */ +}; +typedef struct parser parser_t; + +static inline const char* parser_errstr(parser_err_t err) { + switch(err) { + case PARSER_ERR_OK : return "OK"; + case PARSER_ERR_SYSTEM : return "System Error"; + case PARSER_ERR_INVALID_FILE: return "Invalid File"; + case PARSER_ERR_WRONLY : return "Parser can only write"; + case PARSER_ERR_RDONLY : return "Parser can only read"; + default: + return "Unknown Error"; + } +} + +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/port.c b/tools/macosx/src/stm32flash_serial/src/port.c new file mode 100644 index 0000000..08e58cc --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/port.c @@ -0,0 +1,59 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + +#include +#include + +#include "serial.h" +#include "port.h" + + +extern struct port_interface port_serial; +extern struct port_interface port_i2c; + +static struct port_interface *ports[] = { + &port_serial, + &port_i2c, + NULL, +}; + + +port_err_t port_open(struct port_options *ops, struct port_interface **outport) +{ + int ret; + static struct port_interface **port; + + for (port = ports; *port; port++) { + ret = (*port)->open(*port, ops); + if (ret == PORT_ERR_NODEV) + continue; + if (ret == PORT_ERR_OK) + break; + fprintf(stderr, "Error probing interface \"%s\"\n", + (*port)->name); + } + if (*port == NULL) { + fprintf(stderr, "Cannot handle device \"%s\"\n", + ops->device); + return PORT_ERR_UNKNOWN; + } + + *outport = *port; + return PORT_ERR_OK; +} diff --git a/tools/macosx/src/stm32flash_serial/src/port.h b/tools/macosx/src/stm32flash_serial/src/port.h new file mode 100644 index 0000000..290f034 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/port.h @@ -0,0 +1,75 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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_PORT +#define _H_PORT + +typedef enum { + PORT_ERR_OK = 0, + PORT_ERR_NODEV, /* No such device */ + PORT_ERR_TIMEDOUT, /* Operation timed out */ + PORT_ERR_UNKNOWN, +} port_err_t; + +/* flags */ +#define PORT_BYTE (1 << 0) /* byte (not frame) oriented */ +#define PORT_GVR_ETX (1 << 1) /* cmd GVR returns protection status */ +#define PORT_CMD_INIT (1 << 2) /* use INIT cmd to autodetect speed */ +#define PORT_RETRY (1 << 3) /* allowed read() retry after timeout */ +#define PORT_STRETCH_W (1 << 4) /* warning for no-stretching commands */ + +/* all options and flags used to open and configure an interface */ +struct port_options { + const char *device; + serial_baud_t baudRate; + const char *serial_mode; + int bus_addr; + int rx_frame_max; + int tx_frame_max; +}; + +/* + * Specify the length of reply for command GET + * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time + * consuming try-fail-timeout-retry operation. + * On byte-oriented protocols, i.e. UART, this information would be skipped + * after read the first byte, so not needed. + */ +struct varlen_cmd { + uint8_t version; + uint8_t length; +}; + +struct port_interface { + const char *name; + unsigned flags; + port_err_t (*open)(struct port_interface *port, struct port_options *ops); + port_err_t (*close)(struct port_interface *port); + port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level); + const char *(*get_cfg_str)(struct port_interface *port); + struct varlen_cmd *cmd_get_reply; + void *private; +}; + +port_err_t port_open(struct port_options *ops, struct port_interface **outport); + +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/protocol.txt b/tools/macosx/src/stm32flash_serial/src/protocol.txt new file mode 100644 index 0000000..0391099 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/protocol.txt @@ -0,0 +1,19 @@ +The communication protocol used by ST bootloader is documented in following ST +application notes, depending on communication port. + +In current version of stm32flash are supported only UART and I2C ports. + +* AN3154: CAN protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf + +* AN3155: USART protocol used in the STM32(TM) bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf + +* AN4221: I2C protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf + +* AN4286: SPI protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf + +Boot mode selection for STM32 is documented in ST application note AN2606, available in ST website: + http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf diff --git a/tools/macosx/src/stm32flash_serial/src/serial.h b/tools/macosx/src/stm32flash_serial/src/serial.h new file mode 100644 index 0000000..227ba16 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/serial.h @@ -0,0 +1,90 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _SERIAL_H +#define _SERIAL_H + +typedef struct serial serial_t; + +typedef enum { + SERIAL_PARITY_NONE, + SERIAL_PARITY_EVEN, + SERIAL_PARITY_ODD, + + SERIAL_PARITY_INVALID +} serial_parity_t; + +typedef enum { + SERIAL_BITS_5, + SERIAL_BITS_6, + SERIAL_BITS_7, + SERIAL_BITS_8, + + SERIAL_BITS_INVALID +} serial_bits_t; + +typedef enum { + SERIAL_BAUD_1200, + SERIAL_BAUD_1800, + SERIAL_BAUD_2400, + SERIAL_BAUD_4800, + SERIAL_BAUD_9600, + SERIAL_BAUD_19200, + SERIAL_BAUD_38400, + SERIAL_BAUD_57600, + SERIAL_BAUD_115200, + SERIAL_BAUD_128000, + SERIAL_BAUD_230400, + SERIAL_BAUD_256000, + SERIAL_BAUD_460800, + SERIAL_BAUD_500000, + SERIAL_BAUD_576000, + SERIAL_BAUD_921600, + SERIAL_BAUD_1000000, + SERIAL_BAUD_1500000, + SERIAL_BAUD_2000000, + + SERIAL_BAUD_INVALID +} serial_baud_t; + +typedef enum { + SERIAL_STOPBIT_1, + SERIAL_STOPBIT_2, + + SERIAL_STOPBIT_INVALID +} serial_stopbit_t; + +typedef enum { + GPIO_RTS = 1, + GPIO_DTR, + GPIO_BRK, +} serial_gpio_t; + +/* common helper functions */ +serial_baud_t serial_get_baud(const unsigned int baud); +unsigned int serial_get_baud_int(const serial_baud_t baud); +serial_bits_t serial_get_bits(const char *mode); +unsigned int serial_get_bits_int(const serial_bits_t bits); +serial_parity_t serial_get_parity(const char *mode); +char serial_get_parity_str(const serial_parity_t parity); +serial_stopbit_t serial_get_stopbit(const char *mode); +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit); + +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/serial_common.c b/tools/macosx/src/stm32flash_serial/src/serial_common.c new file mode 100644 index 0000000..43e48e1 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/serial_common.c @@ -0,0 +1,154 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "serial.h" + +serial_baud_t serial_get_baud(const unsigned int baud) { + switch(baud) { + case 1200: return SERIAL_BAUD_1200 ; + case 1800: return SERIAL_BAUD_1800 ; + case 2400: return SERIAL_BAUD_2400 ; + case 4800: return SERIAL_BAUD_4800 ; + case 9600: return SERIAL_BAUD_9600 ; + case 19200: return SERIAL_BAUD_19200 ; + case 38400: return SERIAL_BAUD_38400 ; + case 57600: return SERIAL_BAUD_57600 ; + case 115200: return SERIAL_BAUD_115200; + case 128000: return SERIAL_BAUD_128000; + case 230400: return SERIAL_BAUD_230400; + case 256000: return SERIAL_BAUD_256000; + case 460800: return SERIAL_BAUD_460800; + case 500000: return SERIAL_BAUD_500000; + case 576000: return SERIAL_BAUD_576000; + case 921600: return SERIAL_BAUD_921600; + case 1000000: return SERIAL_BAUD_1000000; + case 1500000: return SERIAL_BAUD_1500000; + case 2000000: return SERIAL_BAUD_2000000; + + default: + return SERIAL_BAUD_INVALID; + } +} + +unsigned int serial_get_baud_int(const serial_baud_t baud) { + switch(baud) { + case SERIAL_BAUD_1200 : return 1200 ; + case SERIAL_BAUD_1800 : return 1800 ; + case SERIAL_BAUD_2400 : return 2400 ; + case SERIAL_BAUD_4800 : return 4800 ; + case SERIAL_BAUD_9600 : return 9600 ; + case SERIAL_BAUD_19200 : return 19200 ; + case SERIAL_BAUD_38400 : return 38400 ; + case SERIAL_BAUD_57600 : return 57600 ; + case SERIAL_BAUD_115200: return 115200; + case SERIAL_BAUD_128000: return 128000; + case SERIAL_BAUD_230400: return 230400; + case SERIAL_BAUD_256000: return 256000; + case SERIAL_BAUD_460800: return 460800; + case SERIAL_BAUD_500000: return 500000; + case SERIAL_BAUD_576000: return 576000; + case SERIAL_BAUD_921600: return 921600; + case SERIAL_BAUD_1000000: return 1000000; + case SERIAL_BAUD_1500000: return 1500000; + case SERIAL_BAUD_2000000: return 2000000; + + case SERIAL_BAUD_INVALID: + default: + return 0; + } +} + +serial_bits_t serial_get_bits(const char *mode) { + if (!mode) + return SERIAL_BITS_INVALID; + switch(mode[0]) { + case '5': return SERIAL_BITS_5; + case '6': return SERIAL_BITS_6; + case '7': return SERIAL_BITS_7; + case '8': return SERIAL_BITS_8; + + default: + return SERIAL_BITS_INVALID; + } +} + +unsigned int serial_get_bits_int(const serial_bits_t bits) { + switch(bits) { + case SERIAL_BITS_5: return 5; + case SERIAL_BITS_6: return 6; + case SERIAL_BITS_7: return 7; + case SERIAL_BITS_8: return 8; + + default: + return 0; + } +} + +serial_parity_t serial_get_parity(const char *mode) { + if (!mode || !mode[0]) + return SERIAL_PARITY_INVALID; + switch(mode[1]) { + case 'N': + case 'n': + return SERIAL_PARITY_NONE; + case 'E': + case 'e': + return SERIAL_PARITY_EVEN; + case 'O': + case 'o': + return SERIAL_PARITY_ODD; + + default: + return SERIAL_PARITY_INVALID; + } +} + +char serial_get_parity_str(const serial_parity_t parity) { + switch(parity) { + case SERIAL_PARITY_NONE: return 'N'; + case SERIAL_PARITY_EVEN: return 'E'; + case SERIAL_PARITY_ODD : return 'O'; + + default: + return ' '; + } +} + +serial_stopbit_t serial_get_stopbit(const char *mode) { + if (!mode || !mode[0] || !mode[1]) + return SERIAL_STOPBIT_INVALID; + switch(mode[2]) { + case '1': return SERIAL_STOPBIT_1; + case '2': return SERIAL_STOPBIT_2; + + default: + return SERIAL_STOPBIT_INVALID; + } +} + +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit) { + switch(stopbit) { + case SERIAL_STOPBIT_1: return 1; + case SERIAL_STOPBIT_2: return 2; + + default: + return 0; + } +} + diff --git a/tools/macosx/src/stm32flash_serial/src/serial_platform.c b/tools/macosx/src/stm32flash_serial/src/serial_platform.c new file mode 100644 index 0000000..98e2569 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/serial_platform.c @@ -0,0 +1,5 @@ +#if defined(__WIN32__) || defined(__CYGWIN__) +# include "serial_w32.c" +#else +# include "serial_posix.c" +#endif diff --git a/tools/macosx/src/stm32flash_serial/src/serial_posix.c b/tools/macosx/src/stm32flash_serial/src/serial_posix.c new file mode 100644 index 0000000..284b35b --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/serial_posix.c @@ -0,0 +1,395 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + int fd; + struct termios oldtio; + struct termios newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + + h->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (h->fd < 0) { + free(h); + return NULL; + } + fcntl(h->fd, F_SETFL, 0); + + tcgetattr(h->fd, &h->oldtio); + tcgetattr(h->fd, &h->newtio); + + return h; +} + +static void serial_flush(const serial_t *h) +{ + tcflush(h->fd, TCIFLUSH); +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + tcsetattr(h->fd, TCSANOW, &h->oldtio); + close(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + speed_t port_baud; + tcflag_t port_bits; + tcflag_t port_parity; + tcflag_t port_stop; + struct termios settings; + + switch (baud) { + case SERIAL_BAUD_1200: port_baud = B1200; break; + case SERIAL_BAUD_1800: port_baud = B1800; break; + case SERIAL_BAUD_2400: port_baud = B2400; break; + case SERIAL_BAUD_4800: port_baud = B4800; break; + case SERIAL_BAUD_9600: port_baud = B9600; break; + case SERIAL_BAUD_19200: port_baud = B19200; break; + case SERIAL_BAUD_38400: port_baud = B38400; break; + case SERIAL_BAUD_57600: port_baud = B57600; break; + case SERIAL_BAUD_115200: port_baud = B115200; break; + case SERIAL_BAUD_230400: port_baud = B230400; break; +#ifdef B460800 + case SERIAL_BAUD_460800: port_baud = B460800; break; +#endif /* B460800 */ +#ifdef B921600 + case SERIAL_BAUD_921600: port_baud = B921600; break; +#endif /* B921600 */ +#ifdef B500000 + case SERIAL_BAUD_500000: port_baud = B500000; break; +#endif /* B500000 */ +#ifdef B576000 + case SERIAL_BAUD_576000: port_baud = B576000; break; +#endif /* B576000 */ +#ifdef B1000000 + case SERIAL_BAUD_1000000: port_baud = B1000000; break; +#endif /* B1000000 */ +#ifdef B1500000 + case SERIAL_BAUD_1500000: port_baud = B1500000; break; +#endif /* B1500000 */ +#ifdef B2000000 + case SERIAL_BAUD_2000000: port_baud = B2000000; break; +#endif /* B2000000 */ + + case SERIAL_BAUD_INVALID: + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: port_bits = CS5; break; + case SERIAL_BITS_6: port_bits = CS6; break; + case SERIAL_BITS_7: port_bits = CS7; break; + case SERIAL_BITS_8: port_bits = CS8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: port_parity = 0; break; + case SERIAL_PARITY_EVEN: port_parity = PARENB; break; + case SERIAL_PARITY_ODD: port_parity = PARENB | PARODD; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: port_stop = 0; break; + case SERIAL_STOPBIT_2: port_stop = CSTOPB; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ +#ifndef __sun /* Used by GNU and BSD. Ignore __SVR4 in test. */ + cfmakeraw(&h->newtio); +#else /* __sun */ + h->newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR + | IGNCR | ICRNL | IXON); + if (port_parity) + h->newtio.c_iflag |= INPCK; + + h->newtio.c_oflag &= ~OPOST; + h->newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + h->newtio.c_cflag &= ~(CSIZE | PARENB); + h->newtio.c_cflag |= CS8; +#endif /* __sun */ +#ifdef __QNXNTO__ + h->newtio.c_cflag &= ~(CSIZE | IHFLOW | OHFLOW); +#else + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); +#endif + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); + h->newtio.c_iflag &= ~(IXON | IXOFF | IXANY | IGNPAR); + h->newtio.c_lflag &= ~(ECHOK | ECHOCTL | ECHOKE); + h->newtio.c_oflag &= ~(OPOST | ONLCR); + + /* setup the new settings */ + cfsetispeed(&h->newtio, port_baud); + cfsetospeed(&h->newtio, port_baud); + h->newtio.c_cflag |= + port_parity | + port_bits | + port_stop | + CLOCAL | + CREAD; + + h->newtio.c_cc[VMIN] = 0; + h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ + + /* set the settings */ + serial_flush(h); + if (tcsetattr(h->fd, TCSANOW, &h->newtio) != 0) + return PORT_ERR_UNKNOWN; + +/* this check fails on CDC-ACM devices, bits 16 and 17 of cflag differ! + * it has been disabled below for now -jcw, 2015-11-09 + if (settings.c_cflag != h->newtio.c_cflag) + fprintf(stderr, "c_cflag mismatch %lx\n", + settings.c_cflag ^ h->newtio.c_cflag); + */ + + /* confirm they were set */ + tcgetattr(h->fd, &settings); + if (settings.c_iflag != h->newtio.c_iflag || + settings.c_oflag != h->newtio.c_oflag || + //settings.c_cflag != h->newtio.c_cflag || + settings.c_lflag != h->newtio.c_lflag) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit)); + return PORT_ERR_OK; +} + +/* + * Roger clark. + * This function is no longer used. But has just been commented out in case it needs + * to be reinstated in the future + +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; + + + /* + * Roger Clark + * Commented out this check, because on OSX some devices are /dev/cu + * and some users use symbolic links to devices, hence the name may not even start + * with /dev + + if(startswith(resolved, "/dev/tty")) return 1; + + return 0; + */ + + return 1; +} + +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 */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = read(h->fd, pos, nbyte); + if (r == 0) + return PORT_ERR_TIMEDOUT; + if (r < 0) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + const uint8_t *pos = (const uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = write(h->fd, pos, nbyte); + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit, lines; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = TIOCM_RTS; + break; + + case GPIO_DTR: + bit = TIOCM_DTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (tcsendbreak(h->fd, 1)) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (ioctl(h->fd, TIOCMGET, &lines)) + return PORT_ERR_UNKNOWN; + lines = level ? lines | bit : lines & ~bit; + if (ioctl(h->fd, TIOCMSET, &lines)) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_posix_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_posix", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_posix_open, + .close = serial_posix_close, + .read = serial_posix_read, + .write = serial_posix_write, + .gpio = serial_posix_gpio, + .get_cfg_str = serial_posix_get_cfg_str, +}; diff --git a/tools/macosx/src/stm32flash_serial/src/serial_w32.c b/tools/macosx/src/stm32flash_serial/src/serial_w32.c new file mode 100644 index 0000000..56772c0 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/serial_w32.c @@ -0,0 +1,341 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2010 Gareth McMullin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + HANDLE fd; + DCB oldtio; + DCB newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + char *devName; + + /* timeout in ms */ + COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 500, 0, 0}; + + /* Fix the device name if required */ + if (strlen(device) > 4 && device[0] != '\\') { + devName = calloc(1, strlen(device) + 5); + sprintf(devName, "\\\\.\\%s", device); + } else { + devName = (char *)device; + } + + /* Create file handle for port */ + h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, + 0, /* Exclusive access */ + NULL, /* No security */ + OPEN_EXISTING, + 0, /* No overlap */ + NULL); + + if (devName != device) + free(devName); + + if (h->fd == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + fprintf(stderr, "File not found: %s\n", device); + return NULL; + } + + SetupComm(h->fd, 4096, 4096); /* Set input and output buffer size */ + + SetCommTimeouts(h->fd, &timeouts); + + SetCommMask(h->fd, EV_ERR); /* Notify us of error events */ + + GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */ + GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */ + + /* PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); */ + + return h; +} + +static void serial_flush(const serial_t *h) +{ + /* We shouldn't need to flush in non-overlapping (blocking) mode */ + /* tcflush(h->fd, TCIFLUSH); */ +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + SetCommState(h->fd, &h->oldtio); + CloseHandle(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, + const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + switch (baud) { + case SERIAL_BAUD_1200: h->newtio.BaudRate = CBR_1200; break; + /* case SERIAL_BAUD_1800: h->newtio.BaudRate = CBR_1800; break; */ + case SERIAL_BAUD_2400: h->newtio.BaudRate = CBR_2400; break; + case SERIAL_BAUD_4800: h->newtio.BaudRate = CBR_4800; break; + case SERIAL_BAUD_9600: h->newtio.BaudRate = CBR_9600; break; + case SERIAL_BAUD_19200: h->newtio.BaudRate = CBR_19200; break; + case SERIAL_BAUD_38400: h->newtio.BaudRate = CBR_38400; break; + case SERIAL_BAUD_57600: h->newtio.BaudRate = CBR_57600; break; + case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break; + case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break; + case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break; + /* These are not defined in WinBase.h and might work or not */ + case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break; + case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break; + case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break; + case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break; + case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break; + case SERIAL_BAUD_1000000: h->newtio.BaudRate = 1000000; break; + case SERIAL_BAUD_1500000: h->newtio.BaudRate = 1500000; break; + case SERIAL_BAUD_2000000: h->newtio.BaudRate = 2000000; break; + case SERIAL_BAUD_INVALID: + + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: h->newtio.ByteSize = 5; break; + case SERIAL_BITS_6: h->newtio.ByteSize = 6; break; + case SERIAL_BITS_7: h->newtio.ByteSize = 7; break; + case SERIAL_BITS_8: h->newtio.ByteSize = 8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: h->newtio.Parity = NOPARITY; break; + case SERIAL_PARITY_EVEN: h->newtio.Parity = EVENPARITY; break; + case SERIAL_PARITY_ODD: h->newtio.Parity = ODDPARITY; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: h->newtio.StopBits = ONESTOPBIT; break; + case SERIAL_STOPBIT_2: h->newtio.StopBits = TWOSTOPBITS; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ + h->newtio.fOutxCtsFlow = FALSE; + h->newtio.fOutxDsrFlow = FALSE; + h->newtio.fOutX = FALSE; + h->newtio.fInX = FALSE; + h->newtio.fNull = 0; + h->newtio.fAbortOnError = 0; + + /* set the settings */ + serial_flush(h); + if (!SetCommState(h->fd, &h->newtio)) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit) + ); + return PORT_ERR_OK; +} + +static port_err_t serial_w32_open(struct port_interface *port, + struct port_options *ops) +{ + serial_t *h; + + /* 1. check device name match */ + if (!((strlen(ops->device) == 4 || strlen(ops->device) == 5) + && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) + && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM")) + && isdigit(ops->device[strlen("\\\\.\\COM")]))) + return PORT_ERR_NODEV; + + /* 2. check options */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + 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; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + if (!WriteFile(h->fd, pos, nbyte, &r, NULL)) + return PORT_ERR_UNKNOWN; + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = level ? SETRTS : CLRRTS; + break; + + case GPIO_DTR: + bit = level ? SETDTR : CLRDTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (EscapeCommFunction(h->fd, SETBREAK) == 0) + return PORT_ERR_UNKNOWN; + usleep(500000); + if (EscapeCommFunction(h->fd, CLRBREAK) == 0) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (EscapeCommFunction(h->fd, bit) == 0) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_w32_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_w32", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_w32_open, + .close = serial_w32_close, + .read = serial_w32_read, + .write = serial_w32_write, + .gpio = serial_w32_gpio, + .get_cfg_str = serial_w32_get_cfg_str, +}; diff --git a/tools/macosx/src/stm32flash_serial/src/stm32.c b/tools/macosx/src/stm32flash_serial/src/stm32.c new file mode 100644 index 0000000..74047d2 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/stm32.c @@ -0,0 +1,1048 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright 2010 Geoffrey McRae + Copyright 2012-2014 Tormod Volden + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include "stm32.h" +#include "port.h" +#include "utils.h" + +#define STM32_ACK 0x79 +#define STM32_NACK 0x1F +#define STM32_BUSY 0x76 + +#define STM32_CMD_INIT 0x7F +#define STM32_CMD_GET 0x00 /* get the version and command supported */ +#define STM32_CMD_GVR 0x01 /* get version and read protection status */ +#define STM32_CMD_GID 0x02 /* get ID */ +#define STM32_CMD_RM 0x11 /* read memory */ +#define STM32_CMD_GO 0x21 /* go */ +#define STM32_CMD_WM 0x31 /* write memory */ +#define STM32_CMD_WM_NS 0x32 /* no-stretch write memory */ +#define STM32_CMD_ER 0x43 /* erase */ +#define STM32_CMD_EE 0x44 /* extended erase */ +#define STM32_CMD_EE_NS 0x45 /* extended erase no-stretch */ +#define STM32_CMD_WP 0x63 /* write protect */ +#define STM32_CMD_WP_NS 0x64 /* write protect no-stretch */ +#define STM32_CMD_UW 0x73 /* write unprotect */ +#define STM32_CMD_UW_NS 0x74 /* write unprotect no-stretch */ +#define STM32_CMD_RP 0x82 /* readout protect */ +#define STM32_CMD_RP_NS 0x83 /* readout protect no-stretch */ +#define STM32_CMD_UR 0x92 /* readout unprotect */ +#define STM32_CMD_UR_NS 0x93 /* readout unprotect no-stretch */ +#define STM32_CMD_CRC 0xA1 /* compute CRC */ +#define STM32_CMD_ERR 0xFF /* not a valid command */ + +#define STM32_RESYNC_TIMEOUT 35 /* seconds */ +#define STM32_MASSERASE_TIMEOUT 35 /* seconds */ +#define STM32_SECTERASE_TIMEOUT 5 /* seconds */ +#define STM32_BLKWRITE_TIMEOUT 1 /* seconds */ +#define STM32_WUNPROT_TIMEOUT 1 /* seconds */ +#define STM32_WPROT_TIMEOUT 1 /* seconds */ +#define STM32_RPROT_TIMEOUT 1 /* seconds */ + +#define STM32_CMD_GET_LENGTH 17 /* bytes in the reply */ + +struct stm32_cmd { + uint8_t get; + uint8_t gvr; + uint8_t gid; + uint8_t rm; + uint8_t go; + uint8_t wm; + uint8_t er; /* this may be extended erase */ + uint8_t wp; + uint8_t uw; + uint8_t rp; + uint8_t ur; + uint8_t crc; +}; + +/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0) + * see ARMv7-M or ARMv6-M Architecture Reference Manual (table B3-8) + * or "The definitive guide to the ARM Cortex-M3", section 14.4. + */ +static const uint8_t stm_reset_code[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 = VECTKEY | SYSRESETREQ +}; + +static const uint32_t stm_reset_code_length = sizeof(stm_reset_code); + +extern const stm32_dev_t devices[]; + +static void stm32_warn_stretching(const char *f) +{ + fprintf(stderr, "Attention !!!\n"); + fprintf(stderr, "\tThis %s error could be caused by your I2C\n", f); + fprintf(stderr, "\tcontroller not accepting \"clock stretching\"\n"); + fprintf(stderr, "\tas required by bootloader.\n"); + fprintf(stderr, "\tCheck \"I2C.txt\" in stm32flash source code.\n"); +} + +static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, time_t timeout) +{ + struct port_interface *port = stm->port; + uint8_t byte; + port_err_t p_err; + time_t t0, t1; + + if (!(port->flags & PORT_RETRY)) + timeout = 0; + + if (timeout) + time(&t0); + + do { + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_TIMEDOUT && timeout) { + time(&t1); + if (t1 < t0 + timeout) + continue; + } + + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to read ACK byte\n"); + return STM32_ERR_UNKNOWN; + } + + if (byte == STM32_ACK) + return STM32_ERR_OK; + if (byte == STM32_NACK) + return STM32_ERR_NACK; + if (byte != STM32_BUSY) { + fprintf(stderr, "Got byte 0x%02x instead of ACK\n", + byte); + return STM32_ERR_UNKNOWN; + } + } while (1); +} + +static stm32_err_t stm32_get_ack(const stm32_t *stm) +{ + return stm32_get_ack_timeout(stm, 0); +} + +static stm32_err_t stm32_send_command_timeout(const stm32_t *stm, + const uint8_t cmd, + time_t timeout) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + uint8_t buf[2]; + + buf[0] = cmd; + buf[1] = cmd ^ 0xFF; + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send command\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, timeout); + if (s_err == STM32_ERR_OK) + return STM32_ERR_OK; + if (s_err == STM32_ERR_NACK) + fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd); + else + fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd); + return STM32_ERR_UNKNOWN; +} + +static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) +{ + return stm32_send_command_timeout(stm, cmd, 0); +} + +/* if we have lost sync, send a wrong command and expect a NACK */ +static stm32_err_t stm32_resync(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t buf[2], ack; + time_t t0, t1; + + time(&t0); + t1 = t0; + + buf[0] = STM32_CMD_ERR; + buf[1] = STM32_CMD_ERR ^ 0xFF; + while (t1 < t0 + STM32_RESYNC_TIMEOUT) { + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + usleep(500000); + time(&t1); + continue; + } + p_err = port->read(port, &ack, 1); + if (p_err != PORT_ERR_OK) { + time(&t1); + continue; + } + if (ack == STM32_NACK) + return STM32_ERR_OK; + time(&t1); + } + return STM32_ERR_UNKNOWN; +} + +/* + * some command receive reply frame with variable length, and length is + * embedded in reply frame itself. + * We can guess the length, but if we guess wrong the protocol gets out + * of sync. + * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte + * read for byte oriented interfaces (e.g. UART). + * + * to run safely, data buffer should be allocated for 256+1 bytes + * + * len is value of the first byte in the frame. + */ +static stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, uint8_t cmd, + uint8_t *data, unsigned int len) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (port->flags & PORT_BYTE) { + /* interface is UART-like */ + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + len = data[0]; + p_err = port->read(port, data + 1, len + 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; + } + + p_err = port->read(port, data, len + 2); + if (p_err == PORT_ERR_OK && len == data[0]) + return STM32_ERR_OK; + if (p_err != PORT_ERR_OK) { + /* restart with only one byte */ + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + } + + fprintf(stderr, "Re sync (len = %d)\n", data[0]); + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + len = data[0]; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, len + 2); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +/* + * Some interface, e.g. UART, requires a specific init sequence to let STM32 + * autodetect the interface speed. + * The sequence is only required one time after reset. + * stm32flash has command line flag "-c" to prevent sending the init sequence + * in case it was already sent before. + * User can easily forget adding "-c". In this case the bootloader would + * interpret the init sequence as part of a command message, then waiting for + * the rest of the message blocking the interface. + * This function sends the init sequence and, in case of timeout, recovers + * the interface. + */ +static stm32_err_t stm32_send_init_seq(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t byte, cmd = STM32_CMD_INIT; + + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_ACK) + return STM32_ERR_OK; + if (p_err == PORT_ERR_OK && byte == STM32_NACK) { + /* We could get error later, but let's continue, for now. */ + fprintf(stderr, + "Warning: the interface was not closed properly.\n"); + return STM32_ERR_OK; + } + if (p_err != PORT_ERR_TIMEDOUT) { + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; + } + + /* + * Check if previous STM32_CMD_INIT was taken as first byte + * of a command. Send a new byte, we should get back a NACK. + */ + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_NACK) + return STM32_ERR_OK; + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; +} + +/* find newer command by higher code */ +#define newer(prev, a) (((prev) == STM32_CMD_ERR) \ + ? (a) \ + : (((prev) > (a)) ? (prev) : (a))) + +stm32_t *stm32_init(struct port_interface *port, const char init) +{ + uint8_t len, val, buf[257]; + stm32_t *stm; + int i, new_cmds; + + stm = calloc(sizeof(stm32_t), 1); + stm->cmd = malloc(sizeof(stm32_cmd_t)); + memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); + stm->port = port; + + if ((port->flags & PORT_CMD_INIT) && init) + if (stm32_send_init_seq(stm) != STM32_ERR_OK) + return NULL; + + /* get the version and read protection status */ + if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* From AN, only UART bootloader returns 3 bytes */ + len = (port->flags & PORT_GVR_ETX) ? 3 : 1; + if (port->read(port, buf, len) != PORT_ERR_OK) + return NULL; + stm->version = buf[0]; + stm->option1 = (port->flags & PORT_GVR_ETX) ? buf[1] : 0; + stm->option2 = (port->flags & PORT_GVR_ETX) ? buf[2] : 0; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* get the bootloader information */ + len = STM32_CMD_GET_LENGTH; + if (port->cmd_get_reply) + for (i = 0; port->cmd_get_reply[i].length; i++) + if (stm->version == port->cmd_get_reply[i].version) { + len = port->cmd_get_reply[i].length; + break; + } + if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return NULL; + len = buf[0] + 1; + stm->bl_version = buf[1]; + new_cmds = 0; + for (i = 1; i < len; i++) { + val = buf[i + 1]; + switch (val) { + case STM32_CMD_GET: + stm->cmd->get = val; break; + case STM32_CMD_GVR: + stm->cmd->gvr = val; break; + case STM32_CMD_GID: + stm->cmd->gid = val; break; + case STM32_CMD_RM: + stm->cmd->rm = val; break; + case STM32_CMD_GO: + stm->cmd->go = val; break; + case STM32_CMD_WM: + case STM32_CMD_WM_NS: + stm->cmd->wm = newer(stm->cmd->wm, val); + break; + case STM32_CMD_ER: + case STM32_CMD_EE: + case STM32_CMD_EE_NS: + stm->cmd->er = newer(stm->cmd->er, val); + break; + case STM32_CMD_WP: + case STM32_CMD_WP_NS: + stm->cmd->wp = newer(stm->cmd->wp, val); + break; + case STM32_CMD_UW: + case STM32_CMD_UW_NS: + stm->cmd->uw = newer(stm->cmd->uw, val); + break; + case STM32_CMD_RP: + case STM32_CMD_RP_NS: + stm->cmd->rp = newer(stm->cmd->rp, val); + break; + case STM32_CMD_UR: + case STM32_CMD_UR_NS: + stm->cmd->ur = newer(stm->cmd->ur, val); + break; + case STM32_CMD_CRC: + stm->cmd->crc = newer(stm->cmd->crc, val); + break; + default: + if (new_cmds++ == 0) + fprintf(stderr, + "GET returns unknown commands (0x%2x", + val); + else + fprintf(stderr, ", 0x%2x", val); + } + } + if (new_cmds) + fprintf(stderr, ")\n"); + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + if (stm->cmd->get == STM32_CMD_ERR + || stm->cmd->gvr == STM32_CMD_ERR + || stm->cmd->gid == STM32_CMD_ERR) { + fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n"); + return NULL; + } + + /* get the device ID */ + if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + len = buf[0] + 1; + if (len < 2) { + stm32_close(stm); + fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len); + return NULL; + } + stm->pid = (buf[1] << 8) | buf[2]; + if (len > 2) { + fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len); + for (i = 2; i <= len ; i++) + fprintf(stderr, " %02x", buf[i]); + fprintf(stderr, "\n"); + } + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + stm->dev = devices; + while (stm->dev->id != 0x00 && stm->dev->id != stm->pid) + ++stm->dev; + + if (!stm->dev->id) { + fprintf(stderr, "Unknown/unsupported device (Device ID: 0x%03x)\n", stm->pid); + stm32_close(stm); + return NULL; + } + + return stm; +} + +void stm32_close(stm32_t *stm) +{ + if (stm) + free(stm->cmd); + free(stm); +} + +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->rm == STM32_CMD_ERR) { + fprintf(stderr, "Error: READ command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_send_command(stm, len - 1) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, data, len) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + return STM32_ERR_OK; +} + +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t cs, buf[256 + 2]; + unsigned int i, aligned_len; + stm32_err_t s_err; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + /* must be 32bit aligned */ + if (address & 0x3 || len & 0x3) { + fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->wm == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + /* send the address and checksum */ + if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + aligned_len = (len + 3) & ~3; + cs = aligned_len - 1; + buf[0] = aligned_len - 1; + for (i = 0; i < len; i++) { + cs ^= data[i]; + buf[i + 1] = data[i]; + } + /* padding data */ + for (i = len; i < aligned_len; i++) { + cs ^= 0xFF; + buf[i + 1] = 0xFF; + } + buf[aligned_len + 1] = cs; + if (port->write(port, buf, aligned_len + 2) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wm != STM32_CMD_WM_NS) + stm32_warn_stretching("write"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wunprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->uw == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->uw != STM32_CMD_UW_NS) + stm32_warn_stretching("WRITE UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->wp == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wp != STM32_CMD_WP_NS) + stm32_warn_stretching("WRITE PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_runprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->ur == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->ur != STM32_CMD_UR_NS) + stm32_warn_stretching("READOUT UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_readprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->rp == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->rp != STM32_CMD_RP_NS) + stm32_warn_stretching("READOUT PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + + if (!pages) + return 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 (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + fprintf(stderr, "Can't initiate chip 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) { + s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } else { + uint8_t cs = 0; + uint8_t pg_num; + uint8_t *buf; + int i = 0; + + buf = malloc(1 + pages + 1); + if (!buf) + return STM32_ERR_UNKNOWN; + + buf[i++] = pages - 1; + cs ^= (pages-1); + for (pg_num = spage; pg_num < (pages + spage); pg_num++) { + buf[i++] = pg_num; + cs ^= pg_num; + } + buf[i++] = cs; + p_err = port->write(port, buf, i); + free(buf); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Erase failed.\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } +} + +static stm32_err_t stm32_run_raw_code(const stm32_t *stm, + uint32_t target_address, + const uint8_t *code, uint32_t code_size) +{ + uint32_t stack_le = le_u32(0x20002000); + uint32_t code_address_le = le_u32(target_address + 8); + uint32_t length = code_size + 8; + uint8_t *mem, *pos; + uint32_t address, w; + + /* Must be 32-bit aligned */ + if (target_address & 0x3) { + fprintf(stderr, "Error: code address must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + mem = malloc(length); + if (!mem) + return STM32_ERR_UNKNOWN; + + memcpy(mem, &stack_le, sizeof(uint32_t)); + memcpy(mem + 4, &code_address_le, sizeof(uint32_t)); + memcpy(mem + 8, code, code_size); + + pos = mem; + address = target_address; + while (length > 0) { + w = length > 256 ? 256 : length; + if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) { + free(mem); + return STM32_ERR_UNKNOWN; + } + + address += w; + pos += w; + length -= w; + } + + free(mem); + return stm32_go(stm, target_address); +} + +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (stm->cmd->go == STM32_CMD_ERR) { + fprintf(stderr, "Error: GO command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +stm32_err_t stm32_reset_device(const stm32_t *stm) +{ + uint32_t target_address = stm->dev->ram_start; + + return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length); +} + +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc == STM32_CMD_ERR) { + fprintf(stderr, "Error: CRC command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = length >> 24; + buf[1] = (length >> 16) & 0xFF; + buf[2] = (length >> 8) & 0xFF; + buf[3] = length & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3])) + return STM32_ERR_UNKNOWN; + + *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return STM32_ERR_OK; +} + +/* + * CRC computed by STM32 is similar to the standard crc32_be() + * implemented, for example, in Linux kernel in ./lib/crc32.c + * But STM32 computes it on units of 32 bits word and swaps the + * bytes of the word before the computation. + * Due to byte swap, I cannot use any CRC available in existing + * libraries, so here is a simple not optimized implementation. + */ +#define CRCPOLY_BE 0x04c11db7 +#define CRC_MSBMASK 0x80000000 +#define CRC_INIT_VALUE 0xFFFFFFFF +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) +{ + int i; + uint32_t data; + + if (len & 0x3) { + fprintf(stderr, "Buffer length must be multiple of 4 bytes\n"); + return 0; + } + + while (len) { + data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + len -= 4; + + crc ^= data; + + for (i = 0; i < 32; i++) + if (crc & CRC_MSBMASK) + crc = (crc << 1) ^ CRCPOLY_BE; + else + crc = (crc << 1); + } + return crc; +} + +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + uint8_t buf[256]; + uint32_t start, total_len, len, current_crc; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc != STM32_CMD_ERR) + return stm32_crc_memory(stm, address, length, crc); + + start = address; + total_len = length; + current_crc = CRC_INIT_VALUE; + while (length) { + len = length > 256 ? 256 : length; + if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) { + fprintf(stderr, + "Failed to read memory at address 0x%08x, target write-protected?\n", + address); + return STM32_ERR_UNKNOWN; + } + current_crc = stm32_sw_crc(current_crc, buf, len); + length -= len; + address += len; + + fprintf(stderr, + "\rCRC address 0x%08x (%.2f%%) ", + address, + (100.0f / (float)total_len) * (float)(address - start) + ); + fflush(stderr); + } + fprintf(stderr, "Done.\n"); + *crc = current_crc; + return STM32_ERR_OK; +} diff --git a/tools/macosx/src/stm32flash_serial/src/stm32.h b/tools/macosx/src/stm32flash_serial/src/stm32.h new file mode 100644 index 0000000..1688fcb --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/stm32.h @@ -0,0 +1,84 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _STM32_H +#define _STM32_H + +#include +#include "serial.h" + +#define STM32_MAX_RX_FRAME 256 /* cmd read memory */ +#define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */ + +typedef enum { + STM32_ERR_OK = 0, + STM32_ERR_UNKNOWN, /* Generic error */ + STM32_ERR_NACK, + STM32_ERR_NO_CMD, /* Command not available in bootloader */ +} stm32_err_t; + +typedef struct stm32 stm32_t; +typedef struct stm32_cmd stm32_cmd_t; +typedef struct stm32_dev stm32_dev_t; + +struct stm32 { + const serial_t *serial; + struct port_interface *port; + uint8_t bl_version; + uint8_t version; + uint8_t option1, option2; + uint16_t pid; + stm32_cmd_t *cmd; + const stm32_dev_t *dev; +}; + +struct stm32_dev { + uint16_t id; + const char *name; + 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 opt_start, opt_end; + uint32_t mem_start, mem_end; +}; + +stm32_t *stm32_init(struct port_interface *port, const char init); +void stm32_close(stm32_t *stm); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_t *stm); +stm32_err_t stm32_wprot_memory(const stm32_t *stm); +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, + uint8_t pages); +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_t *stm); +stm32_err_t stm32_readprot_memory(const stm32_t *stm); +stm32_err_t stm32_runprot_memory(const stm32_t *stm); +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); + +#endif + diff --git a/tools/macosx/src/stm32flash_serial/src/stm32flash.1 b/tools/macosx/src/stm32flash_serial/src/stm32flash.1 new file mode 100644 index 0000000..d37292f --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/stm32flash.1 @@ -0,0 +1,407 @@ +.TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command" +.SH NAME +stm32flash \- flashing utility for STM32 and STM32W through UART or I2C +.SH SYNOPSIS +.B stm32flash +.RB [ \-cfhjkouvCR ] +.RB [ \-a +.IR bus_address ] +.RB [ \-b +.IR baud_rate ] +.RB [ \-m +.IR serial_mode ] +.RB [ \-r +.IR filename ] +.RB [ \-w +.IR filename ] +.RB [ \-e +.IR num ] +.RB [ \-n +.IR count ] +.RB [ \-g +.IR address ] +.RB [ \-s +.IR start_page ] +.RB [ \-S +.IR address [: length ]] +.RB [ \-F +.IR RX_length [: TX_length ]] +.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. + +It requires the STM32[W] to embed a bootloader compliant with ST +application note AN3155. +.B stm32flash +uses the serial port +.I tty_device +to interact with the bootloader of STM32[W]. + +.SH OPTIONS +.TP +.BI "\-a" " bus_address" +Specify address on bus for +.IR i2c_device . +This option is mandatory for I2C interface. + +.TP +.BI "\-b" " baud_rate" +Specify baud rate speed of +.IR tty_device . +Please notice that the ST bootloader can automatically detect the baud rate, +as explaned in chapter 2 of AN3155. +This option could be required together with option +.B "\-c" +or if following interaction with bootloader is expected. +Default is +.IR 57600 . + +.TP +.BI "\-m" " mode" +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 +.I 8e1 +for standard STM32 bootloader and +.I 8n1 +for standard STM32W bootloader. +Default is +.IR 8e1 . + +.TP +.BI "\-r" " filename" +Specify to read the STM32[W] 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 +.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 +.B \-f +option. + +.TP +.B \-u +Specify to disable write\-protection from STM32[W] flash. +The STM32[W] will be reset after this operation. + +.TP +.B \-j +Enable the flash read\-protection. + +.TP +.B \-k +Disable the flash read\-protection. + +.TP +.B \-o +Erase only. + +.TP +.BI "\-e" " num" +Specify to erase only +.I num +pages before writing the flash. Default is to erase the whole flash. With +.B \-e 0 +the flash would not be erased. + +.TP +.B \-v +Specify to verify flash content after write operation. + +.TP +.BI "\-n" " count" +Specify to retry failed writes up to +.I count +times. Default is 10 times. + +.TP +.BI "\-g" " address" +Specify address to start execution from (0 = flash start). + +.TP +.BI "\-s" " start_page" +Specify flash page offset (0 = flash start). + +.TP +.BI "\-S" " address" "[:" "length" "]" +Specify start address and optionally length for read/write/erase/crc operations. + +.TP +.BI "\-F" " RX_length" "[:" "TX_length" "]" +Specify the maximum frame size for the current interface. +Due to STM32 bootloader protocol, host will never handle frames bigger than +256 byte in RX or 258 byte in TX. +Due to current code, lowest limit in RX is 20 byte (to read a complete reply +of command GET). Minimum limit in TX is 5 byte, required by protocol. + +.TP +.B \-f +Force binary parser while reading file with +.BR "\-w" "." + +.TP +.B \-h +Show help. + +.TP +.B \-c +Specify to resume the existing UART connection and don't send initial +INIT sequence to detect baud rate. Baud rate must be kept the same as the +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 +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 +GPIO. (See below +.B BOOTLOADER GPIO SEQUENCE +for the format of +.I GPIO_string +and further explanation). + +.TP +.B \-C +Specify to compute CRC on memory content. +By default the CRC is computed on the whole flash content. +Use +.B "\-S" +to provide different memory address range. + +.TP +.B \-R +Specify to reset the device at exit. +This option is ignored if either +.BR "\-g" "," +.BR "\-j" "," +.B "\-k" +or +.B "\-u" +is also specified. + +.SH BOOTLOADER GPIO SEQUENCE +This feature is currently available on Linux host only. + +As explained in ST application note AN2606, after reset the STM32 will +execute either the application program in user flash or the bootloader, +depending on the level applied at specific pins of STM32 during reset. + +STM32 bootloader is automatically activated by configuring the pins +BOOT0="high" and BOOT1="low" and then by applying a reset. +Application program in user flash is activated by configuring the pin +BOOT0="low" (the level on BOOT1 is ignored) and then by applying a reset. + +When GPIO from host computer are connected to either configuration and +reset pins of STM32, +.B stm32flash +can control the host GPIO to reset STM32 and to force execution of +bootloader or execution of application program. + +The sequence of GPIO values to entry to and exit from bootloader mode is +provided with command line option +.B "\-i" +.IR "GPIO_string" . + +.PD 0 +The format of +.IR "GPIO_string" " is:" +.RS +GPIO_string = [entry sequence][:[exit sequence]] +.P +sequence = [\-]n[,sequence] +.RE +.P +In the above sequences, negative numbers correspond to GPIO at "low" level; +numbers without sign correspond to GPIO at "high" level. +The value "n" can either be the GPIO number on the host system or the +string "rts", "dtr" or "brk". The strings "rts" and "dtr" drive the +corresponding UART's modem lines RTS and DTR as GPIO. +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 + +.PD 0 +As example, let's suppose the following connection between host and STM32: +.IP \(bu 2 +host GPIO_3 connected to reset pin of STM32; +.IP \(bu 2 +host GPIO_4 connected to STM32 pin BOOT0; +.IP \(bu 2 +host GPIO_5 connected to STM32 pin BOOT1. +.PD +.P + +In this case, the sequence to enter in bootloader mode is: first put +GPIO_4="high" and GPIO_5="low"; then send reset pulse by GPIO_3="low" +followed by GPIO_3="high". +The corresponding string for +.I GPIO_string +is "4,\-5,\-3,3". + +To exit from bootloade and run the application program, the sequence is: +put GPIO_4="low"; then send reset pulse. +The corresponding string for +.I GPIO_string +is "\-4,\-3,3". + +The complete command line flag is "\-i 4,\-5,\-3,3:\-4,\-3,3". + +STM32W uses pad PA5 to select boot mode; if during reset PA5 is "low" then +STM32W will enter in bootloader mode; if PA5 is "high" it will execute the +program in flash. + +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 +.RE +provides: +.IP \(bu 2 +entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high +.IP \(bu 2 +exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high +.PD + +.SH EXAMPLES +Get device information: +.RS +.PD 0 +.P +stm32flash /dev/ttyS0 +.PD +.RE + +Write with verify and then start execution: +.RS +.PD 0 +.P +stm32flash \-w filename \-v \-g 0x0 /dev/ttyS0 +.PD +.RE + +Read flash to file: +.RS +.PD 0 +.P +stm32flash \-r filename /dev/ttyS0 +.PD +.RE + +Start execution: +.RS +.PD 0 +.P +stm32flash \-g 0x0 /dev/ttyS0 +.PD +.RE + +Specify: +.PD 0 +.IP \(bu 2 +entry sequence: RTS=low, DTR=low, DTR=high +.IP \(bu 2 +exit sequence: RTS=high, DTR=low, DTR=high +.P +.RS +stm32flash \-i \-rts,\-dtr,dtr:rts,\-dtr,dtr /dev/ttyS0 +.PD +.RE + +.SH FORMAT CONVERSION +Flash images provided by ST or created with ST tools are often in file +format Motorola S\-Record. +Conversion between raw binary, intel hex and Motorola S\-Record can be +done through software package SRecord. + +.SH AUTHORS +The original software package +.B stm32flash +is written by +.I Geoffrey McRae +and is since 2012 maintained by +.IR "Tormod Volden " . + +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 . + +.SH SEE ALSO +.BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)." + +The communication protocol used by ST bootloader is documented in +following ST application notes, depending on communication port. +The current version of +.B stm32flash +only supports +.I UART +and +.I I2C +ports. +.PD 0 +.P +.IP \(bu 2 +AN3154: CAN protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf +.RE + +.P +.IP \(bu 2 +AN3155: USART protocol used in the STM32(TM) bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf +.RE + +.P +.IP \(bu 2 +AN4221: I2C protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf +.RE + +.P +.IP \(bu 2 +AN4286: SPI protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf +.RE + +.PD + + +Boot mode selection for STM32 is documented in ST application note +AN2606, available from the ST website: +.PD 0 +.P +http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf +.PD + +.SH LICENSE +.B stm32flash +is distributed under GNU GENERAL PUBLIC LICENSE Version 2. +Copy of the license is available within the source code in the file +.IR "gpl\-2.0.txt" . diff --git a/tools/macosx/src/stm32flash_serial/src/utils.c b/tools/macosx/src/stm32flash_serial/src/utils.c new file mode 100644 index 0000000..271bb3e --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/utils.c @@ -0,0 +1,45 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "utils.h" + +/* detect CPU endian */ +char cpu_le() { + const uint32_t cpu_le_test = 0x12345678; + return ((const unsigned char*)&cpu_le_test)[0] == 0x78; +} + +uint32_t be_u32(const uint32_t v) { + if (cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} + +uint32_t le_u32(const uint32_t v) { + if (!cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} diff --git a/tools/macosx/src/stm32flash_serial/src/utils.h b/tools/macosx/src/stm32flash_serial/src/utils.h new file mode 100644 index 0000000..a8d37d2 --- /dev/null +++ b/tools/macosx/src/stm32flash_serial/src/utils.h @@ -0,0 +1,30 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_UTILS +#define _H_UTILS + +#include + +char cpu_le(); +uint32_t be_u32(const uint32_t v); +uint32_t le_u32(const uint32_t v); + +#endif diff --git a/tools/macosx/src/upload-reset/upload-reset.c b/tools/macosx/src/upload-reset/upload-reset.c new file mode 100644 index 0000000..1d03bff --- /dev/null +++ b/tools/macosx/src/upload-reset/upload-reset.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2015 Roger Clark + * + * 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. + * + * + * Utility to send the reset sequence on RTS and DTR and chars + * which resets the libmaple and causes the bootloader to be run + * + * + * + * Terminal control code by Heiko Noordhof (see copyright below) + */ + + + +/* Copyright (C) 2003 Heiko Noordhof + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function prototypes (belong in a seperate header file) */ +int openserial(char *devicename); +void closeserial(void); +int setDTR(unsigned short level); +int setRTS(unsigned short level); + + +/* Two globals for use by this module only */ +static int fd; +static struct termios oldterminfo; + + +void closeserial(void) +{ + tcsetattr(fd, TCSANOW, &oldterminfo); + close(fd); +} + + +int openserial(char *devicename) +{ + struct termios attr; + + if ((fd = open(devicename, O_RDWR)) == -1) return 0; /* Error */ + atexit(closeserial); + + if (tcgetattr(fd, &oldterminfo) == -1) return 0; /* Error */ + attr = oldterminfo; + attr.c_cflag |= CRTSCTS | CLOCAL; + attr.c_oflag = 0; + if (tcflush(fd, TCIOFLUSH) == -1) return 0; /* Error */ + if (tcsetattr(fd, TCSANOW, &attr) == -1) return 0; /* Error */ + + /* Set the lines to a known state, and */ + /* finally return non-zero is successful. */ + return setRTS(0) && setDTR(0); +} + + +/* For the two functions below: + * level=0 to set line to LOW + * level=1 to set line to HIGH + */ + +int setRTS(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setRTS(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_RTS; + else status &= ~TIOCM_RTS; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setRTS(): TIOCMSET"); + return 0; + } + return 1; +} + + +int setDTR(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setDTR(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_DTR; + else status &= ~TIOCM_DTR; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setDTR: TIOCMSET"); + return 0; + } + return 1; +} + +/* This portion of code was written by Roger Clark + * It was informed by various other pieces of code written by Leaflabs to reset their + * Maple and Maple mini boards + */ + +main(int argc, char *argv[]) +{ + + if (argc<2 || argc >3) + { + printf("Usage upload-reset \n\r"); + return; + } + + if (openserial(argv[1])) + { + // Send magic sequence of DTR and RTS followed by the magic word "1EAF" + setRTS(false); + setDTR(false); + setDTR(true); + + usleep(50000L); + + setDTR(false); + setRTS(true); + setDTR(true); + + usleep(50000L); + + setDTR(false); + + usleep(50000L); + + write(fd,"1EAF",4); + + closeserial(); + if (argc==3) + { + usleep(atol(argv[2])*1000L); + } + } + else + { + printf("Failed to open serial device.\n\r"); + } +} diff --git a/tools/src/texane-stlink b/tools/src/texane-stlink deleted file mode 160000 index 1b46200..0000000 --- a/tools/src/texane-stlink +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b46200b12ff8764a869a7365eb8253622100fed diff --git a/tools/win/debugging.bat b/tools/win/debugging.bat deleted file mode 100644 index c45729e..0000000 --- a/tools/win/debugging.bat +++ /dev/null @@ -1,21 +0,0 @@ -@Echo off -set fileName=%~n2 -set BaseDir=C:\Debugging -set OutputDir=%BaseDir%\%fileName% -IF NOT EXIST %BaseDir%\*.* (echo Need to create directory %BaseDir%) -IF NOT EXIST %OutputDir%\*.* (md %OutputDir%) -set sizeFile=%fileName%.size.txt -set nmFile=%fileName%.nm.txt -set dasmFile=%fileName%.dasm.txt -set pcompFile=%fileName%.pcomp.txt -cd %1 - -arm-none-eabi-objdump.exe -d -S -l %2.elf > %OutputDir%\%dasmFile% - -arm-none-eabi-nm.exe --format="sysv" -C -l %2.elf > %OutputDir%\%nmFile% - -arm-none-eabi-size.exe -t -A %2.elf > %OutputDir%\%sizeFile% -arm-none-eabi-size.exe -t -A -x %2.elf >> %OutputDir%\%sizeFile% -start %OutputDir% - -echo Done! \ No newline at end of file diff --git a/tools/win/src/build_dfu-util.sh b/tools/win/src/build_dfu-util.sh new file mode 100644 index 0000000..3563f57 --- /dev/null +++ b/tools/win/src/build_dfu-util.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +sudo apt-get build-dep dfu-util +sudo apt-get install build-essentials +sudo apt-get install libusb-1.0-0-dev +sudo apt-get install autoconf automake autotools-dev + +cd dfu-util +./autogen.sh +./configure +make +cp src/dfu-util ../../linux/dfu-util +cp src/dfu-suffix ../../linux/dfu-util +cp src/dfu-prefix ../../linux/dfu-util + diff --git a/tools/win/src/dfu-util/AUTHORS b/tools/win/src/dfu-util/AUTHORS new file mode 100644 index 0000000..1b36c73 --- /dev/null +++ b/tools/win/src/dfu-util/AUTHORS @@ -0,0 +1,30 @@ +Authors ordered by first contribution. + +Harald Welte +Werner Almesberger +Michael Lauer +Jim Huang +Stefan Schmidt +Daniel Willmann +Mike Frysinger +Uwe Hermann +C. Scott Ananian +Bernard Blackham +Holger Freyther +Marc Singer +James Perkins +Tommi Keisala +Pascal Schweizer +Bradley Scott +Uwe Bonnes +Andrey Smirnov +Jussi Timperi +Hans Petter Selasky +Bo Shen +Henrique de Almeida Mendonca +Bernd Krumboeck +Dennis Meier +Veli-Pekka Peltola +Dave Hylands +Michael Grzeschik +Paul Fertser diff --git a/tools/win/src/dfu-util/COPYING b/tools/win/src/dfu-util/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/tools/win/src/dfu-util/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/tools/win/src/dfu-util/ChangeLog b/tools/win/src/dfu-util/ChangeLog new file mode 100644 index 0000000..37f1add --- /dev/null +++ b/tools/win/src/dfu-util/ChangeLog @@ -0,0 +1,93 @@ +0.8: + o New, separate dfu-prefix tool (Uwe Bonnes) + o Allow filtering on serial number (Uwe Bonnes) + o Improved VID/PID/serial filtering (Bradley Scott) + o Support reading firmware from stdin (Tormod Volden) + o Warn if missing DFU suffix (Tormod Volden) + o Improved progress bar (Hans Petter Selasky) + o Fix dfuse leave option (Uwe Bonnes) + o Major code rework (Hans Petter Selasky) + o MS Visual Studio build support (Henrique Mendonca) + o dfuse-pack.py tool for .dfu files (Antonio Galeo) + o Many other fixes from many people + +2014-09-13: Tormod Volden + +0.7: + o Support for TI Stellaris devices (Tommi Keisala) + o Fix libusb detection on MacOSX (Marc Singer) + o Fix libusb detection on FreeBSD (Tormod Volden) + o Improved DfuSe support (Tormod Volden) + o Support all special commands (leave, unprotect, mass-erase) + o Arbitrary upload lengths + o "force" option for various possible (dangerous) overrides + +2012-10-07: Tormod Volden + +0.6: + o Add detach mode (Stefan Schmidt) + o Check return value on all libusb calls (Tormod Volden) + o Fix segmentation fault with -s option (Tormod Volden) + o Add DFU suffix manipulation tool (Stefan Schmidt) + o Port to Windows: (Tormod Volden, some parts based on work from Satz + Klauer) + o Port file handling to stdio streams + o Sleep() macros + o C99 types + o Pack structs + o Detect DfuSe device correctly on big-endian architectures (Tormod + Volden) + o Add dfuse progress indication on download (Tormod Volden) + o Cleanup: gcc pedantic, gcc extension, ... (Tormod Volden) + o Rely on page size from functional descriptor. Please report if you get + an error about it. (Tormod Volden) + o Add quirk for Maple since it reports wrong DFU version (Tormod Volden) + +2012-04-22: Stefan Schmidt + +0.5: + o DfuSe extension support for ST devices (Tormod Volden) + o Add initial support for bitWillDetach flag from DFU 1.1 (Tormod + Volden) + o Internal cleanup and some manual page fixes (Tormod Volden) + +2011-11-02: Stefan Schmidt + +0.4: + o Rework to use libusb-1.0 (Stefan Schmidt) + o DFU suffix support (Tormod Volden, Stefan Schmidt) + o Sspeed up DFU downloads directly into memory (Bernard Blackham) + o More flexible -d vid:pid parsing (Tormod Volden) + o Many bug fixes and cleanups + +2011-07-20: Stefan Schmidt + +0.3: + o quirks: Add OpenOCD to the poll timeout quirk table. + +2010-12-22: Stefan Schmidt + +0.2: + o Fix some typos on the website and the README (Antonio Ospite, Uwe + Hermann) + o Remove build rule for a static binary. We can use autotools for this. + (Mike Frysinger) + o Fix infinite loop in download error path (C. Scott Ananian) + o Break out to show the 'finished' in upload (C. Scott Ananian) + o Add GPLv2+ headers (Harald Welte) + o Remove dead code (commands.[ch]) remnescent of dfu-programmer (Harald + Welte) + o Simple quirk system with Openmoko quirk for missing bwPollTimeout (Tormod Volden) + o New default (1024) and clamping of transfer size (Tormod Volden) + o Verify sending of completion packet (Tormod Volden) + o Look for DFU functional descriptor among all descriptors (Tormod + Volden) + o Print out in which direction we are transferring data + o Abort in upload if the file already exists + +2010-11-17 Stefan Schmidt + +0.1: + Initial release + +2010-05-23 Stefan Schmidt diff --git a/tools/win/src/dfu-util/DEVICES.txt b/tools/win/src/dfu-util/DEVICES.txt new file mode 100644 index 0000000..bdd9f1f --- /dev/null +++ b/tools/win/src/dfu-util/DEVICES.txt @@ -0,0 +1,20 @@ +List of supported software and hardware products: + +Software user (bootloader, etc) +------------------------------- +- Sam7DFU: http://www.openpcd.org/Sam7dfu +- U-boot: DFU patches +- Barebox: http://www.barebox.org/ +- Leaflabs: http://code.google.com/p/leaflabs/ +- Blackmagic DFU + +Products using DFU +------------------ +- OpenPCD (sam7dfu) +- Openmoko Neo 1973 and Freerunner (u-boot with DFU patches) +- Leaflabs Maple +- ATUSB from Qi Hardware +- STM32F105/7, STM32F2/F3/F4 in System Bootloader +- Blackmagic debug probe +- NXP LPC31xx/LPC43XX, e.g. LPC-Link and LPC-Link2, need binaries + with LPC prefix and encoding (LPC-Link) diff --git a/tools/win/src/dfu-util/Makefile.am b/tools/win/src/dfu-util/Makefile.am new file mode 100644 index 0000000..641dda5 --- /dev/null +++ b/tools/win/src/dfu-util/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src doc + +EXTRA_DIST = autogen.sh TODO DEVICES.txt dfuse-pack.py diff --git a/tools/win/src/dfu-util/README b/tools/win/src/dfu-util/README new file mode 100644 index 0000000..0f8f262 --- /dev/null +++ b/tools/win/src/dfu-util/README @@ -0,0 +1,20 @@ +Dfu-util - Device Firmware Upgrade Utilities + +Dfu-util is the host side implementation of the DFU 1.0 [1] and DFU 1.1 [2] +specification of the USB forum. + +DFU is intended to download and upload firmware to devices connected over +USB. It ranges from small devices like micro-controller boards up to mobile +phones. With dfu-util you are able to download firmware to your device or +upload firmware from it. + +dfu-util has been tested with Openmoko Neo1973 and Freerunner and many +other devices. + +[1] DFU 1.0 spec: http://www.usb.org/developers/devclass_docs/usbdfu10.pdf +[2] DFU 1.1 spec: http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf + +The official website is: + + http://dfu-util.gnumonks.org/ + diff --git a/tools/win/src/dfu-util/TODO b/tools/win/src/dfu-util/TODO new file mode 100644 index 0000000..900c30c --- /dev/null +++ b/tools/win/src/dfu-util/TODO @@ -0,0 +1,14 @@ +DfuSe: +- Do erase and write in two separate passes when downloading +- Skip "Set Address" command when downloading contiguous blocks +- Implement "Get Commands" command + +Devices: +- Research iPhone/iPod/iPad support + Heavily modified dfu-util fork here: + https://github.com/planetbeing/xpwn/tree/master/dfu-util +- Test against Niftylights + +Non-Code: +- Logo +- Re-License as LGPL for usage as library? diff --git a/tools/win/src/dfu-util/autogen.sh b/tools/win/src/dfu-util/autogen.sh new file mode 100644 index 0000000..e67aed3 --- /dev/null +++ b/tools/win/src/dfu-util/autogen.sh @@ -0,0 +1,2 @@ +#! /bin/sh +autoreconf -v -i diff --git a/tools/win/src/dfu-util/configure.ac b/tools/win/src/dfu-util/configure.ac new file mode 100644 index 0000000..8622114 --- /dev/null +++ b/tools/win/src/dfu-util/configure.ac @@ -0,0 +1,41 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([dfu-util],[0.8],[dfu-util@lists.gnumonks.org],,[http://dfu-util.gnumonks.org]) +AC_CONFIG_AUX_DIR(m4) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_HEADERS([config.h]) + +# Test for new silent rules and enable only if they are available +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. +# On FreeBSD the libusb-1.0 is called libusb and resides in system location +AC_CHECK_LIB([usb], [libusb_init],, [native_libusb=no],) +AS_IF([test x$native_libusb = xno], [ + PKG_CHECK_MODULES([USB], [libusb-1.0 >= 1.0.0],, + AC_MSG_ERROR([*** Required libusb-1.0 >= 1.0.0 not installed ***])) +]) +AC_CHECK_LIB([usbpath],[usb_path2devnum],,,-lusb) + +LIBS="$LIBS $USB_LIBS" +CFLAGS="$CFLAGS $USB_CFLAGS" + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([usbpath.h windows.h sysexits.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_CHECK_FUNCS([ftruncate getpagesize nanosleep err]) + +AC_CONFIG_FILES(Makefile src/Makefile doc/Makefile) +AC_OUTPUT diff --git a/tools/win/src/dfu-util/device-logs/README b/tools/win/src/dfu-util/device-logs/README new file mode 100644 index 0000000..00d3d1a --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/README @@ -0,0 +1,77 @@ +Device: +------- +qi-hardware-atusb: +- Qi Hardware ben-wpan +- DFU implementation: + http://projects.qi-hardware.com/index.php/p/ben-wpan/source/tree/master/atusb/fw/usb +- Tester: Stefan Schmidt + +openpcd: +- OpenPCD RFID reader +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +simtrace: +- Sysmocom SimTrace +- DFU implementation: SAM7DFU + http://www.openpcd.org/Sam7dfu, git://git.gnumonks.org/openpcd.git +- Tester: Stefan Schmidt + +openmoko-freerunner: +- Openmoko Freerunner +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +openmoko-neo1973: +- Openmoko Neo1073 +- DFU implementation: Old U-Boot + http://git.openmoko.org/?p=u-boot.git;a=shortlog;h=refs/heads/mokopatches +- Tester: Stefan Schmidt + +tdk-bluetooth: +- TDK Corp. Bluetooth Adapter +- DFU implementation: closed soure +- Only upload has been tested +- Tester: Stefan Schmidt + +stm32f107: +- STM32 microcontrollers with built-in (ROM) DFU loader +- DFU implementation: Closed source but probably similar to the one + in their USB device libraries. Some relevant application notes: + http://www.st.com -> AN3156 and AN2606 +- Tested by Uwe Bonnes + +stm32f4discovery: +- STM32 microcontroller board with built-in (ROM) DFU loader +- DFU implementation: Closed source, probably similar to stm32f107. +- Tested by Joe Rothweiler + +dso-nano: +- DSO Nano pocket oscilloscope +- DFU implementation: Based on ST Microelectronics USB FS Library 1.0 + http://dsonano.googlecode.com/files/DS0201_OpenSource.rar +- Tester: Tormod Volden + +opc-20: +- Custom devices based on STM32F1xx +- DFU implementation: ST Microelectronics USB FS Device Library 3.1.0 + http://www.st.com -> um0424.zip +- Tester: Tormod Volden + +lpc-link, lpclink2: +- NXP LPCXpresso debug adapters +- Proprietary DFU implementation, uses special download files with + LPC prefix and encoding of the target firmware code +- Tested by Uwe Bonnes + +Adding the lsusb output and a download log of your device here helps +us to avoid regressions for hardware we cannot test while working on +the code. To extract the lsusb output use this command: +sudo lsusb -v -d $USBID > $DEVICE.lsusb +Prepare a description snippet as above, and send it to us. A log +(copy-paste of the command window) of a firmware download is also +nice, please use the double verbose option -v -v and include the +command line in the log file. + diff --git a/tools/win/src/dfu-util/device-logs/dsonano.lsusb b/tools/win/src/dfu-util/device-logs/dsonano.lsusb new file mode 100644 index 0000000..140a7bc --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/dsonano.lsusb @@ -0,0 +1,60 @@ + +Bus 002 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 1.1a + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 001 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 5 @SPI Flash : M25P64/0x00000000/64*064Kg,64*064Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/lpclink.log b/tools/win/src/dfu-util/device-logs/lpclink.log new file mode 100644 index 0000000..7de4dd3 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/lpclink.log @@ -0,0 +1,59 @@ +(The on-board LPC3154 has some encryption key set and LPCXpressoWIN.enc +is encrypted.) + +$ lsusb | grep NXP +Bus 003 Device 011: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link + +$ dfu-util -v -v -v -R -D /opt/lpc/lpcxpresso/bin/LPCXpressoWIN.enc + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Deducing device DFU version from functional descriptor length +Opening DFU capable USB device... +ID 0471:df55 +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 6% 2048 bytes +Download [=== ] 13% 4096 bytes +Download [==== ] 19% 6144 bytes +Download [====== ] 26% 8192 bytes +Download [======== ] 32% 10240 bytes +Download [========= ] 39% 12288 bytes +Download [=========== ] 45% 14336 bytes +Download [============= ] 52% 16384 bytes +Download [============== ] 59% 18432 bytes +Download [================ ] 65% 20480 bytes +Download [================== ] 72% 22528 bytes +Download [=================== ] 78% 24576 bytes +Download [===================== ] 85% 26624 bytes +Download [====================== ] 91% 28672 bytes +Download [======================== ] 98% 29192 bytes +Download [=========================] 100% 29192 bytes +Download done. +Sent a total of 29192 bytes +state(8) = dfuMANIFEST-WAIT-RESET, status(0) = No error condition is present +Done! +dfu-util: can't detach +Resetting USB to switch back to runtime mode + +$ lsusb | grep NXP +Bus 003 Device 012: ID 1fc9:0009 NXP Semiconductors diff --git a/tools/win/src/dfu-util/device-logs/lpclink.lsusb b/tools/win/src/dfu-util/device-logs/lpclink.lsusb new file mode 100644 index 0000000..867b2a2 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/lpclink.lsusb @@ -0,0 +1,58 @@ + +Bus 003 Device 008: ID 0471:df55 Philips (or NXP) LPCXpresso LPC-Link +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0471 Philips (or NXP) + idProduct 0xdf55 LPCXpresso LPC-Link + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 25 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 1 + Will Not Detach + Manifestation Intolerant + Upload Unsupported + Download Supported + wDetachTimeout 65535 milliseconds + wTransferSize 2048 bytes +Device Qualifier (for other device speed): + bLength 10 + bDescriptorType 6 + bcdUSB 2.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + bNumConfigurations 1 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/lpclink2.log b/tools/win/src/dfu-util/device-logs/lpclink2.log new file mode 100644 index 0000000..4681eff --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/lpclink2.log @@ -0,0 +1,59 @@ +$ lsusb | grep NXP +Bus 003 Device 013: ID 1fc9:000c NXP Semiconductors + +$ dfu-util -D ~/devel/dfu-util/firmware.bin.qthdr + +dfu-util 0.7 + +Copyright 2005-2008 Weston Schmidt, Harald Welte and OpenMoko Inc. +Copyright 2010-2012 Tormod Volden and Stefan Schmidt +This program is Free Software and has ABSOLUTELY NO WARRANTY +Please report bugs to dfu-util@lists.gnumonks.org + +dfu-util: Invalid DFU suffix signature +dfu-util: A valid DFU suffix will be required in a future dfu-util release!!! +Possible unencryptes NXP LPC DFU prefix with the following properties +Payload length: 39 kiByte +Opening DFU capable USB device... +ID 1fc9:000c +Run-time device DFU version 0100 +Claiming USB DFU Runtime Interface... +Determining device status: +state = dfuIDLE, status = 0 +dfu-util: WARNING: Runtime device already in DFU state ?!? +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: +state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 0100 +Device returned transfer size 2048 +Copying data from PC to DFU device +Download [ ] 0% 0 bytes +Download [= ] 4% 2048 bytes +Download [== ] 9% 4096 bytes +Download [=== ] 14% 6144 bytes +Download [==== ] 19% 8192 bytes +Download [====== ] 24% 10240 bytes +Download [======= ] 28% 12288 bytes +Download [======== ] 33% 14336 bytes +Download [========= ] 38% 16384 bytes +Download [========== ] 43% 18432 bytes +Download [============ ] 48% 20480 bytes +Download [============= ] 53% 22528 bytes +Download [============== ] 57% 24576 bytes +Download [=============== ] 62% 26624 bytes +Download [================ ] 67% 28672 bytes +Download [================== ] 72% 30720 bytes +Download [=================== ] 77% 32768 bytes +Download [==================== ] 82% 34816 bytes +Download [===================== ] 86% 36864 bytes +Download [====================== ] 91% 38912 bytes +Download [======================== ] 96% 40356 bytes +Download [=========================] 100% 40356 bytes +Download done. +Sent a total of 40356 bytes +dfu-util: unable to read DFU status + +$ lsusb | grep NXP +Bus 003 Device 014: ID 1fc9:0018 NXP Semiconductors diff --git a/tools/win/src/dfu-util/device-logs/lpclink2.lsusb b/tools/win/src/dfu-util/device-logs/lpclink2.lsusb new file mode 100644 index 0000000..b833fca --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/lpclink2.lsusb @@ -0,0 +1,203 @@ + +Bus 003 Device 007: ID 0c72:000c PEAK System PCAN-USB +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x0c72 PEAK System + idProduct 0x000c PCAN-USB + bcdDevice 1c.ff + iManufacturer 0 + iProduct 3 VER1:PEAK +VER2:02.8.01 +DAT :06.05.2004 +TIME:09:35:37 + ... + iSerial 0 + bNumConfigurations 3 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 2 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 394mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 20 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 46 + bNumInterfaces 1 + bConfigurationValue 3 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 4 + bInterfaceClass 0 (Defined at Interface level) + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x01 EP 1 OUT + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 +Device Status: 0x0001 + Self Powered diff --git a/tools/win/src/dfu-util/device-logs/opc-20.lsusb b/tools/win/src/dfu-util/device-logs/opc-20.lsusb new file mode 100644 index 0000000..580df90 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/opc-20.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 004: ID 0483:df11 SGS Thomson Microelectronics +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 + bcdDevice 2.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 DFU + iSerial 3 ÿÿÿÿÿÿÿÿÿÿÿÿ + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/12*001Ka,116*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : M25P64/0x00000000/128*64Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1a.01 +Device Status: 0x0001 + Self Powered diff --git a/tools/win/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb b/tools/win/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb new file mode 100644 index 0000000..4c0abfb --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/openmoko-freerunner-dfumode.lsusb @@ -0,0 +1,109 @@ +Bus 003 Device 017: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 81 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 7 USB Device Firmware Upgrade + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 8 RAM 0x32000000 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 9 u-boot + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 10 u-boot_env + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 11 kernel + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 4 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 12 splash + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 5 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 13 factory + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 6 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 14 rootfs + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 +Device Status: 0x0a00 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/openmoko-freerunner.lsusb b/tools/win/src/dfu-util/device-logs/openmoko-freerunner.lsusb new file mode 100644 index 0000000..835708d --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/openmoko-freerunner.lsusb @@ -0,0 +1,179 @@ +Bus 005 Device 033: ID 1d50:5119 OpenMoko, Inc. GTA01/GTA02 U-Boot Bootloader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x5119 GTA01/GTA02 U-Boot Bootloader + bcdDevice 0.00 + iManufacturer 1 OpenMoko, Inc + iProduct 2 Neo1973 Bootloader U-Boot 1.3.2-moko12 + iSerial 3 0000000 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 USB Device Firmware Upgrade + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 TTY via USB + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 Control Interface + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 Bulk Data Interface + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x9a00 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/openmoko-neo1973.lsusb b/tools/win/src/dfu-util/device-logs/openmoko-neo1973.lsusb new file mode 100644 index 0000000..0778950 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/openmoko-neo1973.lsusb @@ -0,0 +1,182 @@ + +Bus 006 Device 020: ID 1457:5119 First International Computer, Inc. OpenMoko Neo1973 u-boot cdc_acm serial port +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 2 Communications + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 16 + idVendor 0x1457 First International Computer, Inc. + idProduct 0x5119 OpenMoko Neo1973 u-boot cdc_acm serial port + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 + iSerial 3 + bNumConfigurations 2 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 85 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 500mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 7 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 4096 bytes + bcdDFUVersion 1.00 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 67 + bNumInterfaces 2 + bConfigurationValue 2 + iConfiguration 4 + bmAttributes 0x80 + (Bus Powered) + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 2 Communications + bInterfaceSubClass 2 Abstract (modem) + bInterfaceProtocol 1 AT-commands (v.25ter) + iInterface 6 + CDC Header: + bcdCDC 0.6e + CDC Call Management: + bmCapabilities 0x00 + bDataInterface 1 + CDC ACM: + bmCapabilities 0x00 + CDC Union: + bMasterInterface 0 + bSlaveInterface 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 10 CDC Data + bInterfaceSubClass 0 Unused + bInterfaceProtocol 0 + iInterface 5 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 255 +Device Status: 0x0006 + (Bus Powered) + Remote Wakeup Enabled + Test Mode diff --git a/tools/win/src/dfu-util/device-logs/openpcd.lsusb b/tools/win/src/dfu-util/device-logs/openpcd.lsusb new file mode 100644 index 0000000..f6255a9 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/openpcd.lsusb @@ -0,0 +1,60 @@ + +Bus 006 Device 016: ID 16c0:076b VOTI OpenPCD 13.56MHz RFID Reader +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x076b OpenPCD 13.56MHz RFID Reader + bcdDevice 0.00 + iManufacturer 1 + iProduct 2 OpenPCD RFID Simulator - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/qi-hardware-atusb.lsusb b/tools/win/src/dfu-util/device-logs/qi-hardware-atusb.lsusb new file mode 100644 index 0000000..bfc1701 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/qi-hardware-atusb.lsusb @@ -0,0 +1,59 @@ + +Bus 006 Device 013: ID 20b7:1540 Qi Hardware ben-wpan, AT86RF230-based +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x20b7 Qi Hardware + idProduct 0x1540 ben-wpan, AT86RF230-based + bcdDevice 0.01 + iManufacturer 0 + iProduct 0 + iSerial 1 4630333438371508231a + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 34 + bNumInterfaces 2 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 40mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 1 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 0 + bInterfaceProtocol 0 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 0 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 1 + iInterface 0 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/simtrace.lsusb b/tools/win/src/dfu-util/device-logs/simtrace.lsusb new file mode 100644 index 0000000..578ddf0 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/simtrace.lsusb @@ -0,0 +1,70 @@ + +Bus 006 Device 017: ID 16c0:0762 VOTI +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 8 + idVendor 0x16c0 VOTI + idProduct 0x0762 + bcdDevice 0.00 + iManufacturer 1 sysmocom - systems for mobile communications GmbH + iProduct 2 SimTrace SIM Sniffer - DFU Mode + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 45 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 3 SimTrace DFU Configuration + bmAttributes 0x80 + (Bus Powered) + MaxPower 200mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 SimTrace DFU Interface - Application Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 SimTrace DFU Interface - Bootloader Partition + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 SimTrace DFU Interface - RAM + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 3 + Will Not Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 65280 milliseconds + wTransferSize 256 bytes + bcdDFUVersion 1.00 +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/sparkcore.lsusb b/tools/win/src/dfu-util/device-logs/sparkcore.lsusb new file mode 100644 index 0000000..b6029ff --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/sparkcore.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 008: ID 1d50:607f OpenMoko, Inc. +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x1d50 OpenMoko, Inc. + idProduct 0x607f + bcdDevice 2.00 + iManufacturer 1 Spark Devices + iProduct 2 CORE DFU + iSerial 3 8D80527B5055 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/20*001Ka,108*001Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @SPI Flash : SST25x/0x00000000/512*04Kg + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 1024 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/win/src/dfu-util/device-logs/stm32f107.bin-download b/tools/win/src/dfu-util/device-logs/stm32f107.bin-download new file mode 100644 index 0000000..45b714f --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/stm32f107.bin-download @@ -0,0 +1,48 @@ +> src/dfu-util --intf 0 --alt 0 -v -v -v -s 0x8000000 -D test3 +dfu-util 0.4 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Opening DFU USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/128*002Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +No valid DFU suffix signature +Warning: File has no DFU suffix +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 128 x 2048 = 262144 (rew) +Uploading to address = 0x08000000, size = 16384 +Erasing page size 2048 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 +Erasing page size 2048 at address 0x08000800, page starting at 0x08000800 + Download from image offset 00000800 to memory 08000800-08000fff, size 2048 + Setting address pointer to 0x08000800 +Erasing page size 2048 at address 0x08001000, page starting at 0x08001000 + Download from image offset 00001000 to memory 08001000-080017ff, size 2048 + Setting address pointer to 0x08001000 +Erasing page size 2048 at address 0x08001800, page starting at 0x08001800 + Download from image offset 00001800 to memory 08001800-08001fff, size 2048 + Setting address pointer to 0x08001800 +Erasing page size 2048 at address 0x08002000, page starting at 0x08002000 + Download from image offset 00002000 to memory 08002000-080027ff, size 2048 + Setting address pointer to 0x08002000 +Erasing page size 2048 at address 0x08002800, page starting at 0x08002800 + Download from image offset 00002800 to memory 08002800-08002fff, size 2048 + Setting address pointer to 0x08002800 +Erasing page size 2048 at address 0x08003000, page starting at 0x08003000 + Download from image offset 00003000 to memory 08003000-080037ff, size 2048 + Setting address pointer to 0x08003000 +Erasing page size 2048 at address 0x08003800, page starting at 0x08003800 + Download from image offset 00003800 to memory 08003800-08003fff, size 2048 + Setting address pointer to 0x08003800 + diff --git a/tools/win/src/dfu-util/device-logs/stm32f107.lsusb b/tools/win/src/dfu-util/device-logs/stm32f107.lsusb new file mode 100644 index 0000000..14b45cd --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/stm32f107.lsusb @@ -0,0 +1,60 @@ + +Bus 001 Device 028: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 20.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 0x418 DFU Bootloader + iSerial 3 STM32 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 36 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/128*002Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFF800/01*016 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/device-logs/stm32f4discovery.bin-download b/tools/win/src/dfu-util/device-logs/stm32f4discovery.bin-download new file mode 100644 index 0000000..96e1722 --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/stm32f4discovery.bin-download @@ -0,0 +1,36 @@ +dfu-util --device 0483:df11 --alt 0 \ + --dfuse-address 0x08000000 \ + -v -v -v \ + --download arm/iotoggle.bin +No valid DFU suffix signature +Warning: File has no DFU suffix +dfu-util 0.5 + +(C) 2005-2008 by Weston Schmidt, Harald Welte and OpenMoko Inc. +(C) 2010-2011 Tormod Volden (DfuSe support) +This program is Free Software and has ABSOLUTELY NO WARRANTY + +dfu-util does currently only support DFU version 1.0 + +Filter on vendor = 0x0483 product = 0xdf11 +Opening DFU capable USB device... ID 0483:df11 +Run-time device DFU version 011a +Found DFU: [0483:df11] devnum=0, cfg=1, intf=0, alt=0, name="@Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg" +Claiming USB DFU Interface... +Setting Alternate Setting #0 ... +Determining device status: state = dfuERROR, status = 10 +dfuERROR, clearing status +Determining device status: state = dfuIDLE, status = 0 +dfuIDLE, continuing +DFU mode device DFU version 011a +Device returned transfer size 2048 +DfuSe interface name: "Internal Flash " +Memory segment at 0x08000000 4 x 16384 = 65536 (rew) +Memory segment at 0x08010000 1 x 65536 = 65536 (rew) +Memory segment at 0x08020000 7 x 131072 = 917504 (rew) +Uploading to address = 0x08000000, size = 2308 +Erasing page size 16384 at address 0x08000000, page starting at 0x08000000 + Download from image offset 00000000 to memory 08000000-080007ff, size 2048 + Setting address pointer to 0x08000000 + Download from image offset 00000800 to memory 08000800-08000903, size 260 + Setting address pointer to 0x08000800 diff --git a/tools/win/src/dfu-util/device-logs/stm32f4discovery.lsusb b/tools/win/src/dfu-util/device-logs/stm32f4discovery.lsusb new file mode 100644 index 0000000..0b870de --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/stm32f4discovery.lsusb @@ -0,0 +1,80 @@ + +Bus 001 Device 010: ID 0483:df11 SGS Thomson Microelectronics STM Device in DFU Mode +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.00 + bDeviceClass 0 (Defined at Interface level) + bDeviceSubClass 0 + bDeviceProtocol 0 + bMaxPacketSize0 64 + idVendor 0x0483 SGS Thomson Microelectronics + idProduct 0xdf11 STM Device in DFU Mode + bcdDevice 21.00 + iManufacturer 1 STMicroelectronics + iProduct 2 STM32 BOOTLOADER + iSerial 3 315A28A0B956 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 54 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0xc0 + Self Powered + MaxPower 100mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 4 @Internal Flash /0x08000000/04*016Kg,01*064Kg,07*128Kg + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 1 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 5 @Option Bytes /0x1FFFC000/01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 2 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 6 @OTP Memory /0x1FFF7800/01*512 g,01*016 g + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 3 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 2 + iInterface 7 @Device Feature/0xFFFF0000/01*004 g + Device Firmware Upgrade Interface Descriptor: + bLength 9 + bDescriptorType 33 + bmAttributes 11 + Will Detach + Manifestation Intolerant + Upload Supported + Download Supported + wDetachTimeout 255 milliseconds + wTransferSize 2048 bytes + bcdDFUVersion 1.1a +Device Status: 0x0001 + Self Powered diff --git a/tools/win/src/dfu-util/device-logs/tdk-bluetooth.lsusb b/tools/win/src/dfu-util/device-logs/tdk-bluetooth.lsusb new file mode 100644 index 0000000..c0cface --- /dev/null +++ b/tools/win/src/dfu-util/device-logs/tdk-bluetooth.lsusb @@ -0,0 +1,269 @@ + +Bus 006 Device 014: ID 04bf:0320 TDK Corp. Bluetooth Adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 2.00 + bDeviceClass 224 Wireless + bDeviceSubClass 1 Radio Frequency + bDeviceProtocol 1 Bluetooth + bMaxPacketSize0 64 + idVendor 0x04bf TDK Corp. + idProduct 0x0320 Bluetooth Adapter + bcdDevice 26.52 + iManufacturer 1 Ezurio + iProduct 2 Turbo Bluetooth Adapter + iSerial 3 008098D4FFBD + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 193 + bNumInterfaces 3 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 64mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0010 1x 16 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0040 1x 64 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 0 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0000 1x 0 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 1 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0009 1x 9 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 2 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0011 1x 17 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 3 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0019 1x 25 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 4 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0021 1x 33 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 1 + bAlternateSetting 5 + bNumEndpoints 2 + bInterfaceClass 224 Wireless + bInterfaceSubClass 1 Radio Frequency + bInterfaceProtocol 1 Bluetooth + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x03 EP 3 OUT + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x83 EP 3 IN + bmAttributes 1 + Transfer Type Isochronous + Synch Type None + Usage Type Data + wMaxPacketSize 0x0031 1x 49 bytes + bInterval 1 + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 2 + bAlternateSetting 0 + bNumEndpoints 0 + bInterfaceClass 254 Application Specific Interface + bInterfaceSubClass 1 Device Firmware Update + bInterfaceProtocol 0 + iInterface 0 + Device Firmware Upgrade Interface Descriptor: + bLength 7 + bDescriptorType 33 + bmAttributes 7 + Will Not Detach + Manifestation Tolerant + Upload Supported + Download Supported + wDetachTimeout 5000 milliseconds + wTransferSize 1023 bytes +Device Status: 0x0000 + (Bus Powered) diff --git a/tools/win/src/dfu-util/dfuse-pack.py b/tools/win/src/dfu-util/dfuse-pack.py new file mode 100644 index 0000000..875cc5c --- /dev/null +++ b/tools/win/src/dfu-util/dfuse-pack.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +# Written by Antonio Galea - 2010/11/18 +# Distributed under Gnu LGPL 3.0 +# see http://www.gnu.org/licenses/lgpl-3.0.txt + +import sys,struct,zlib,os +from optparse import OptionParser + +DEFAULT_DEVICE="0x0483:0xdf11" + +def named(tuple,names): + return dict(zip(names.split(),tuple)) +def consume(fmt,data,names): + n = struct.calcsize(fmt) + return named(struct.unpack(fmt,data[:n]),names),data[n:] +def cstring(string): + return string.split('\0',1)[0] +def compute_crc(data): + return 0xFFFFFFFF & -zlib.crc32(data) -1 + +def parse(file,dump_images=False): + print 'File: "%s"' % file + data = open(file,'rb').read() + crc = compute_crc(data[:-4]) + prefix, data = consume('<5sBIB',data,'signature version size targets') + print '%(signature)s v%(version)d, image size: %(size)d, targets: %(targets)d' % prefix + for t in range(prefix['targets']): + tprefix, data = consume('<6sBI255s2I',data,'signature altsetting named name size elements') + tprefix['num'] = t + if tprefix['named']: + tprefix['name'] = cstring(tprefix['name']) + else: + tprefix['name'] = '' + print '%(signature)s %(num)d, alt setting: %(altsetting)s, name: "%(name)s", size: %(size)d, elements: %(elements)d' % tprefix + tsize = tprefix['size'] + target, data = data[:tsize], data[tsize:] + for e in range(tprefix['elements']): + eprefix, target = consume('<2I',target,'address size') + eprefix['num'] = e + print ' %(num)d, address: 0x%(address)08x, size: %(size)d' % eprefix + esize = eprefix['size'] + image, target = target[:esize], target[esize:] + if dump_images: + out = '%s.target%d.image%d.bin' % (file,t,e) + open(out,'wb').write(image) + print ' DUMPED IMAGE TO "%s"' % out + if len(target): + print "target %d: PARSE ERROR" % t + suffix = named(struct.unpack('<4H3sBI',data[:16]),'device product vendor dfu ufd len crc') + print 'usb: %(vendor)04x:%(product)04x, device: 0x%(device)04x, dfu: 0x%(dfu)04x, %(ufd)s, %(len)d, 0x%(crc)08x' % suffix + if crc != suffix['crc']: + print "CRC ERROR: computed crc32 is 0x%08x" % crc + data = data[16:] + if data: + print "PARSE ERROR" + +def build(file,targets,device=DEFAULT_DEVICE): + data = '' + for t,target in enumerate(targets): + tdata = '' + for image in target: + tdata += struct.pack('<2I',image['address'],len(image['data']))+image['data'] + tdata = struct.pack('<6sBI255s2I','Target',0,1,'ST...',len(tdata),len(target)) + tdata + data += tdata + data = struct.pack('<5sBIB','DfuSe',1,len(data)+11,len(targets)) + data + v,d=map(lambda x: int(x,0) & 0xFFFF, device.split(':',1)) + data += struct.pack('<4H3sB',0,d,v,0x011a,'UFD',16) + crc = compute_crc(data) + data += struct.pack(' and +Harald Welte . Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +.SH LICENCE +.B dfu-util +is covered by the GNU General Public License (GPL), version 2 or later. +.SH COPYRIGHT +This manual page was originally written by Uwe Hermann , +and is now part of the dfu-util project. diff --git a/tools/win/src/dfu-util/msvc/README_msvc.txt b/tools/win/src/dfu-util/msvc/README_msvc.txt new file mode 100644 index 0000000..6e68ec6 --- /dev/null +++ b/tools/win/src/dfu-util/msvc/README_msvc.txt @@ -0,0 +1,10 @@ +# (C) Roger Meier +# (C) Pascal Schweizer +# msvc folder is GPL-2.0+, LGPL-2.1+, BSD-3-Clause or MIT license(SPDX) + +Building dfu-util native on Windows with Visual Studio + +3rd party dependencies: +- libusbx ( git clone https://github.com/libusbx/libusbx.git ) + - getopt (part of libusbx: libusbx/examples/getopt) + diff --git a/tools/win/src/dfu-util/msvc/dfu-suffix_2010.vcxproj b/tools/win/src/dfu-util/msvc/dfu-suffix_2010.vcxproj new file mode 100644 index 0000000..0c316c2 --- /dev/null +++ b/tools/win/src/dfu-util/msvc/dfu-suffix_2010.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA} + dfusuffix + dfu-suffix + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\dll;$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/win/src/dfu-util/msvc/dfu-util_2010.sln b/tools/win/src/dfu-util/msvc/dfu-util_2010.sln new file mode 100644 index 0000000..ef79723 --- /dev/null +++ b/tools/win/src/dfu-util/msvc/dfu-util_2010.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-util", "dfu-util_2010.vcxproj", "{0E071A60-7EF2-4427-BAA8-9143CACB5BCB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C4F8746D-B27E-4806-95E5-2052174E923B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dfu-suffix", "dfu-suffix_2010.vcxproj", "{8F7600A2-3B37-4956-B39B-A1D43EF29EDA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "getopt_2010", "..\..\libusbx\msvc\getopt_2010.vcxproj", "{AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\..\libusbx\msvc\libusb_static_2010.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|Win32.Build.0 = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Debug|x64.ActiveCfg = Debug|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.ActiveCfg = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|Win32.Build.0 = Release|Win32 + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB}.Release|x64.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|Win32.Build.0 = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Debug|x64.ActiveCfg = Debug|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.ActiveCfg = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|Win32.Build.0 = Release|Win32 + {8F7600A2-3B37-4956-B39B-A1D43EF29EDA}.Release|x64.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|Win32.Build.0 = Debug|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.ActiveCfg = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Debug|x64.Build.0 = Debug|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.ActiveCfg = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|Win32.Build.0 = Release|Win32 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.ActiveCfg = Release|x64 + {AE83E1B4-CE06-47EE-B7A3-C3A1D7C2D71E}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|Win32.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|Win32.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/win/src/dfu-util/msvc/dfu-util_2010.vcxproj b/tools/win/src/dfu-util/msvc/dfu-util_2010.vcxproj new file mode 100644 index 0000000..17a8bee --- /dev/null +++ b/tools/win/src/dfu-util/msvc/dfu-util_2010.vcxproj @@ -0,0 +1,120 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0E071A60-7EF2-4427-BAA8-9143CACB5BCB} + dfuutil + dfu-util + + + + Application + true + MultiByte + + + Application + false + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(VCInstallDir)atlmfc\lib;$(VCInstallDir)lib + $(ExecutablePath) + + + $(ExecutablePath) + $(SolutionDir)..\..\libusbx\examples\getopt;$(SolutionDir)..\..\libusbx\libusb;$(IncludePath) + $(SolutionDir)..\$(Platform)\getopt\$(Configuration);$(LibraryPath) + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + Level3 + Disabled + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + true + + + + + + + + + Level3 + MaxSpeed + true + true + HAVE_WINDOWS_H;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + + + true + true + true + + + copy $(SolutionDir)..\$(Platform)\$(Configuration)\dll\libusb-1.0.dll $(SolutionDir)..\$(Platform)\$(ProjectName)\$(Configuration)\ + + + + + + + + + + + + + + + + + + + + + + + + + + {a2169bc8-cf99-40bf-83f3-b0e38f7067bd} + + + {349ee8f9-7d25-4909-aaf5-ff3fade72187} + + + + + + \ No newline at end of file diff --git a/tools/win/src/dfu-util/src/Makefile.am b/tools/win/src/dfu-util/src/Makefile.am new file mode 100644 index 0000000..70179c4 --- /dev/null +++ b/tools/win/src/dfu-util/src/Makefile.am @@ -0,0 +1,28 @@ +AM_CFLAGS = -Wall -Wextra + +bin_PROGRAMS = dfu-util dfu-suffix dfu-prefix +dfu_util_SOURCES = main.c \ + portable.h \ + dfu_load.c \ + dfu_load.h \ + dfu_util.c \ + dfu_util.h \ + dfuse.c \ + dfuse.h \ + dfuse_mem.c \ + dfuse_mem.h \ + dfu.c \ + dfu.h \ + usb_dfu.h \ + dfu_file.c \ + dfu_file.h \ + quirks.c \ + quirks.h + +dfu_suffix_SOURCES = suffix.c \ + dfu_file.h \ + dfu_file.c + +dfu_prefix_SOURCES = prefix.c \ + dfu_file.h \ + dfu_file.c diff --git a/tools/win/src/dfu-util/src/dfu.c b/tools/win/src/dfu-util/src/dfu.c new file mode 100644 index 0000000..14d7673 --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu.c @@ -0,0 +1,357 @@ +/* + * Low-level DFU communication routines, originally taken from + * $Id: dfu.c,v 1.3 2006/06/20 06:28:04 schmidtw Exp $ + * (part of dfu-programmer). + * + * Copyright 2005-2006 Weston Schmidt + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "quirks.h" + +static int dfu_timeout = 5000; /* 5 seconds - default */ + +/* + * DFU_DETACH Request (DFU Spec 1.0, Section 5.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * timeout - the timeout in ms the USB device should wait for a pending + * USB reset before giving up and terminating the operation + * + * returns 0 or < 0 on error + */ +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DETACH, + /* wValue */ timeout, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_DNLOAD Request (DFU Spec 1.0, Section 6.1.1) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the total number of bytes to transfer to the USB + * device - must be less than wTransferSize + * data - the data to transfer + * + * returns the number of bytes written or < 0 on error + */ +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_UPLOAD Request (DFU Spec 1.0, Section 6.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the number of bytes received or < 0 on error + */ +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ) +{ + int status; + + status = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ interface, + /* Data */ data, + /* wLength */ length, + dfu_timeout ); + return status; +} + + +/* + * DFU_GETSTATUS Request (DFU Spec 1.0, Section 6.1.2) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * status - the data structure to be populated with the results + * + * return the number of bytes read in or < 0 on an error + */ +int dfu_get_status( struct dfu_if *dif, struct dfu_status *status ) +{ + unsigned char buffer[6]; + int result; + + /* Initialize the status data structure */ + status->bStatus = DFU_STATUS_ERROR_UNKNOWN; + status->bwPollTimeout = 0; + status->bState = STATE_DFU_ERROR; + status->iString = 0; + + result = libusb_control_transfer( dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATUS, + /* wValue */ 0, + /* wIndex */ dif->interface, + /* Data */ buffer, + /* wLength */ 6, + dfu_timeout ); + + if( 6 == result ) { + status->bStatus = buffer[0]; + if (dif->quirks & QUIRK_POLLTIMEOUT) + status->bwPollTimeout = DEFAULT_POLLTIMEOUT; + else + status->bwPollTimeout = ((0xff & buffer[3]) << 16) | + ((0xff & buffer[2]) << 8) | + (0xff & buffer[1]); + status->bState = buffer[4]; + status->iString = buffer[5]; + } + + return result; +} + + +/* + * DFU_CLRSTATUS Request (DFU Spec 1.0, Section 6.1.3) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * return 0 or < 0 on an error + */ +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT| LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_CLRSTATUS, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +/* + * DFU_GETSTATE Request (DFU Spec 1.0, Section 6.1.5) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * length - the maximum number of bytes to receive from the USB + * device - must be less than wTransferSize + * data - the buffer to put the received data in + * + * returns the state or < 0 on error + */ +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ) +{ + int result; + unsigned char buffer[1]; + + result = libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_GETSTATE, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ buffer, + /* wLength */ 1, + dfu_timeout ); + + /* Return the error if there is one. */ + if (result < 1) + return -1; + + /* Return the state. */ + return buffer[0]; +} + + +/* + * DFU_ABORT Request (DFU Spec 1.0, Section 6.1.4) + * + * device - the usb_dev_handle to communicate with + * interface - the interface to communicate with + * + * returns 0 or < 0 on an error + */ +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ) +{ + return libusb_control_transfer( device, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_ABORT, + /* wValue */ 0, + /* wIndex */ interface, + /* Data */ NULL, + /* wLength */ 0, + dfu_timeout ); +} + + +const char* dfu_state_to_string( int state ) +{ + const char *message; + + switch (state) { + case STATE_APP_IDLE: + message = "appIDLE"; + break; + case STATE_APP_DETACH: + message = "appDETACH"; + break; + case STATE_DFU_IDLE: + message = "dfuIDLE"; + break; + case STATE_DFU_DOWNLOAD_SYNC: + message = "dfuDNLOAD-SYNC"; + break; + case STATE_DFU_DOWNLOAD_BUSY: + message = "dfuDNBUSY"; + break; + case STATE_DFU_DOWNLOAD_IDLE: + message = "dfuDNLOAD-IDLE"; + break; + case STATE_DFU_MANIFEST_SYNC: + message = "dfuMANIFEST-SYNC"; + break; + case STATE_DFU_MANIFEST: + message = "dfuMANIFEST"; + break; + case STATE_DFU_MANIFEST_WAIT_RESET: + message = "dfuMANIFEST-WAIT-RESET"; + break; + case STATE_DFU_UPLOAD_IDLE: + message = "dfuUPLOAD-IDLE"; + break; + case STATE_DFU_ERROR: + message = "dfuERROR"; + break; + default: + message = NULL; + break; + } + + return message; +} + +/* Chapter 6.1.2 */ +static const char *dfu_status_names[] = { + /* DFU_STATUS_OK */ + "No error condition is present", + /* DFU_STATUS_errTARGET */ + "File is not targeted for use by this device", + /* DFU_STATUS_errFILE */ + "File is for this device but fails some vendor-specific test", + /* DFU_STATUS_errWRITE */ + "Device is unable to write memory", + /* DFU_STATUS_errERASE */ + "Memory erase function failed", + /* DFU_STATUS_errCHECK_ERASED */ + "Memory erase check failed", + /* DFU_STATUS_errPROG */ + "Program memory function failed", + /* DFU_STATUS_errVERIFY */ + "Programmed memory failed verification", + /* DFU_STATUS_errADDRESS */ + "Cannot program memory due to received address that is out of range", + /* DFU_STATUS_errNOTDONE */ + "Received DFU_DNLOAD with wLength = 0, but device does not think that it has all data yet", + /* DFU_STATUS_errFIRMWARE */ + "Device's firmware is corrupt. It cannot return to run-time (non-DFU) operations", + /* DFU_STATUS_errVENDOR */ + "iString indicates a vendor specific error", + /* DFU_STATUS_errUSBR */ + "Device detected unexpected USB reset signalling", + /* DFU_STATUS_errPOR */ + "Device detected unexpected power on reset", + /* DFU_STATUS_errUNKNOWN */ + "Something went wrong, but the device does not know what it was", + /* DFU_STATUS_errSTALLEDPKT */ + "Device stalled an unexpected request" +}; + + +const char *dfu_status_to_string(int status) +{ + if (status > DFU_STATUS_errSTALLEDPKT) + return "INVALID"; + return dfu_status_names[status]; +} + +int dfu_abort_to_idle(struct dfu_if *dif) +{ + int ret; + struct dfu_status dst; + + ret = dfu_abort(dif->dev_handle, dif->interface); + if (ret < 0) { + errx(EX_IOERR, "Error sending dfu abort request"); + exit(1); + } + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during abort get_status"); + exit(1); + } + if (dst.bState != DFU_STATE_dfuIDLE) { + errx(EX_IOERR, "Failed to enter idle state on abort"); + exit(1); + } + milli_sleep(dst.bwPollTimeout); + return ret; +} diff --git a/tools/win/src/dfu-util/src/dfu.h b/tools/win/src/dfu-util/src/dfu.h new file mode 100644 index 0000000..8e3caeb --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu.h @@ -0,0 +1,133 @@ +/* + * dfu-programmer + * + * $Id: dfu.h,v 1.2 2005/09/25 01:27:42 schmidtw Exp $ + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFU_H +#define DFU_H + +#include +#include "usb_dfu.h" + +/* DFU states */ +#define STATE_APP_IDLE 0x00 +#define STATE_APP_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + + +/* DFU status */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_ERROR_TARGET 0x01 +#define DFU_STATUS_ERROR_FILE 0x02 +#define DFU_STATUS_ERROR_WRITE 0x03 +#define DFU_STATUS_ERROR_ERASE 0x04 +#define DFU_STATUS_ERROR_CHECK_ERASED 0x05 +#define DFU_STATUS_ERROR_PROG 0x06 +#define DFU_STATUS_ERROR_VERIFY 0x07 +#define DFU_STATUS_ERROR_ADDRESS 0x08 +#define DFU_STATUS_ERROR_NOTDONE 0x09 +#define DFU_STATUS_ERROR_FIRMWARE 0x0a +#define DFU_STATUS_ERROR_VENDOR 0x0b +#define DFU_STATUS_ERROR_USBR 0x0c +#define DFU_STATUS_ERROR_POR 0x0d +#define DFU_STATUS_ERROR_UNKNOWN 0x0e +#define DFU_STATUS_ERROR_STALLEDPKT 0x0f + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +/* DFU interface */ +#define DFU_IFF_DFU 0x0001 /* DFU Mode, (not Runtime) */ + +/* This is based off of DFU_GETSTATUS + * + * 1 unsigned byte bStatus + * 3 unsigned byte bwPollTimeout + * 1 unsigned byte bState + * 1 unsigned byte iString +*/ + +struct dfu_status { + unsigned char bStatus; + unsigned int bwPollTimeout; + unsigned char bState; + unsigned char iString; +}; + +struct dfu_if { + struct usb_dfu_func_descriptor func_dfu; + uint16_t quirks; + uint16_t busnum; + uint16_t devnum; + uint16_t vendor; + uint16_t product; + uint16_t bcdDevice; + uint8_t configuration; + uint8_t interface; + uint8_t altsetting; + uint8_t flags; + uint8_t bMaxPacketSize0; + char *alt_name; + char *serial_name; + libusb_device *dev; + libusb_device_handle *dev_handle; + struct dfu_if *next; +}; + +int dfu_detach( libusb_device_handle *device, + const unsigned short interface, + const unsigned short timeout ); +int dfu_download( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_upload( libusb_device_handle *device, + const unsigned short interface, + const unsigned short length, + const unsigned short transaction, + unsigned char* data ); +int dfu_get_status( struct dfu_if *dif, + struct dfu_status *status ); +int dfu_clear_status( libusb_device_handle *device, + const unsigned short interface ); +int dfu_get_state( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort( libusb_device_handle *device, + const unsigned short interface ); +int dfu_abort_to_idle( struct dfu_if *dif); + +const char *dfu_state_to_string( int state ); + +const char *dfu_status_to_string( int status ); + +#endif /* DFU_H */ diff --git a/tools/win/src/dfu-util/src/dfu_file.c b/tools/win/src/dfu-util/src/dfu_file.c new file mode 100644 index 0000000..7c897d4 --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_file.c @@ -0,0 +1,444 @@ +/* + * Load or store DFU files including suffix and prefix + * + * Copyright 2014 Tormod Volden + * Copyright 2012 Stefan Schmidt + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +#define DFU_SUFFIX_LENGTH 16 +#define LMDFU_PREFIX_LENGTH 8 +#define LPCDFU_PREFIX_LENGTH 16 +#define PROGRESS_BAR_WIDTH 25 +#define STDIN_CHUNK_SIZE 65536 + +static const unsigned long crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; + +static uint32_t crc32_byte(uint32_t accum, uint8_t delta) +{ + return crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8); +} + +static int probe_prefix(struct dfu_file *file) +{ + uint8_t *prefix = file->firmware; + + if (file->size.total < LMDFU_PREFIX_LENGTH) + return 1; + if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { + file->prefix_type = LMDFU_PREFIX; + file->size.prefix = LMDFU_PREFIX_LENGTH; + file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); + } + else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f)== 0x3f)) { + file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; + file->size.prefix = LPCDFU_PREFIX_LENGTH; + } + + if (file->size.prefix + file->size.suffix > file->size.total) + return 1; + return 0; +} + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max) +{ + static char buf[PROGRESS_BAR_WIDTH + 1]; + static unsigned long long last_progress = -1; + static time_t last_time; + time_t curr_time = time(NULL); + unsigned long long progress; + unsigned long long x; + + /* check for not known maximum */ + if (max < curr) + max = curr + 1; + /* make none out of none give zero */ + if (max == 0 && curr == 0) + max = 1; + + /* compute completion */ + progress = (PROGRESS_BAR_WIDTH * curr) / max; + if (progress > PROGRESS_BAR_WIDTH) + progress = PROGRESS_BAR_WIDTH; + if (progress == last_progress && + curr_time == last_time) + return; + last_progress = progress; + last_time = curr_time; + + for (x = 0; x != PROGRESS_BAR_WIDTH; x++) { + if (x < progress) + buf[x] = '='; + else + buf[x] = ' '; + } + buf[x] = 0; + + printf("\r%s\t[%s] %3lld%% %12lld bytes", desc, buf, + (100ULL * curr) / max, curr); + + if (progress == PROGRESS_BAR_WIDTH) + printf("\n%s done.\n", desc); +} + +void *dfu_malloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr == NULL) + errx(EX_SOFTWARE, "Cannot allocate memory of size %d bytes", (int)size); + return (ptr); +} + +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size) +{ + int x; + + /* compute CRC */ + for (x = 0; x != size; x++) + crc = crc32_byte(crc, ((uint8_t *)buf)[x]); + + /* write data */ + if (write(f, buf, size) != size) + err(EX_IOERR, "Could not write %d bytes to file %d", size, f); + + return (crc); +} + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix) +{ + off_t offset; + int f; + int i; + int res; + + file->size.prefix = 0; + file->size.suffix = 0; + + /* default values, if no valid suffix is found */ + file->bcdDFU = 0; + file->idVendor = 0xffff; /* wildcard value */ + file->idProduct = 0xffff; /* wildcard value */ + file->bcdDevice = 0xffff; /* wildcard value */ + + /* default values, if no valid prefix is found */ + file->lmdfu_address = 0; + + free(file->firmware); + + if (!strcmp(file->name, "-")) { + int read_bytes; + +#ifdef WIN32 + _setmode( _fileno( stdin ), _O_BINARY ); +#endif + file->firmware = (uint8_t*) dfu_malloc(STDIN_CHUNK_SIZE); + read_bytes = fread(file->firmware, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total = read_bytes; + while (read_bytes == STDIN_CHUNK_SIZE) { + file->firmware = (uint8_t*) realloc(file->firmware, file->size.total + STDIN_CHUNK_SIZE); + if (!file->firmware) + err(EX_IOERR, "Could not allocate firmware buffer"); + read_bytes = fread(file->firmware + file->size.total, 1, STDIN_CHUNK_SIZE, stdin); + file->size.total += read_bytes; + } + if (verbose) + printf("Read %i bytes from stdin\n", file->size.total); + /* Never require suffix when reading from stdin */ + check_suffix = MAYBE_SUFFIX; + } else { + f = open(file->name, O_RDONLY | O_BINARY); + if (f < 0) + err(EX_IOERR, "Could not open file %s for reading", file->name); + + offset = lseek(f, 0, SEEK_END); + + if ((int)offset < 0 || (int)offset != offset) + err(EX_IOERR, "File size is too big"); + + if (lseek(f, 0, SEEK_SET) != 0) + err(EX_IOERR, "Could not seek to beginning"); + + file->size.total = offset; + file->firmware = dfu_malloc(file->size.total); + + if (read(f, file->firmware, file->size.total) != file->size.total) { + err(EX_IOERR, "Could not read %d bytes from %s", + file->size.total, file->name); + } + close(f); + } + + /* Check for possible DFU file suffix by trying to parse one */ + { + uint32_t crc = 0xffffffff; + const uint8_t *dfusuffix; + int missing_suffix = 0; + const char *reason; + + if (file->size.total < DFU_SUFFIX_LENGTH) { + reason = "File too short for DFU suffix"; + missing_suffix = 1; + goto checked; + } + + dfusuffix = file->firmware + file->size.total - + DFU_SUFFIX_LENGTH; + + for (i = 0; i < file->size.total - 4; i++) + crc = crc32_byte(crc, file->firmware[i]); + + if (dfusuffix[10] != 'D' || + dfusuffix[9] != 'F' || + dfusuffix[8] != 'U') { + reason = "Invalid DFU suffix signature"; + missing_suffix = 1; + goto checked; + } + + file->dwCRC = (dfusuffix[15] << 24) + + (dfusuffix[14] << 16) + + (dfusuffix[13] << 8) + + dfusuffix[12]; + + if (file->dwCRC != crc) { + reason = "DFU suffix CRC does not match"; + missing_suffix = 1; + goto checked; + } + + /* At this point we believe we have a DFU suffix + so we require further checks to succeed */ + + file->bcdDFU = (dfusuffix[7] << 8) + dfusuffix[6]; + + if (verbose) + printf("DFU suffix version %x\n", file->bcdDFU); + + file->size.suffix = dfusuffix[11]; + + if (file->size.suffix < DFU_SUFFIX_LENGTH) { + errx(EX_IOERR, "Unsupported DFU suffix length %d", + file->size.suffix); + } + + if (file->size.suffix > file->size.total) { + errx(EX_IOERR, "Invalid DFU suffix length %d", + file->size.suffix); + } + + file->idVendor = (dfusuffix[5] << 8) + dfusuffix[4]; + file->idProduct = (dfusuffix[3] << 8) + dfusuffix[2]; + file->bcdDevice = (dfusuffix[1] << 8) + dfusuffix[0]; + +checked: + if (missing_suffix) { + if (check_suffix == NEEDS_SUFFIX) { + warnx("%s", reason); + errx(EX_IOERR, "Valid DFU suffix needed"); + } else if (check_suffix == MAYBE_SUFFIX) { + warnx("%s", reason); + warnx("A valid DFU suffix will be required in " + "a future dfu-util release!!!"); + } + } else { + if (check_suffix == NO_SUFFIX) { + errx(EX_SOFTWARE, "Please remove existing DFU suffix before adding a new one.\n"); + } + } + } + res = probe_prefix(file); + if ((res || file->size.prefix == 0) && check_prefix == NEEDS_PREFIX) + errx(EX_IOERR, "Valid DFU prefix needed"); + if (file->size.prefix && check_prefix == NO_PREFIX) + errx(EX_IOERR, "A prefix already exists, please delete it first"); + if (file->size.prefix && verbose) { + uint8_t *data = file->firmware; + if (file->prefix_type == LMDFU_PREFIX) + printf("Possible TI Stellaris DFU prefix with " + "the following properties\n" + "Address: 0x%08x\n" + "Payload length: %d\n", + file->lmdfu_address, + data[4] | (data[5] << 8) | + (data[6] << 16) | (data[7] << 14)); + else if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) + printf("Possible unencrypted NXP LPC DFU prefix with " + "the following properties\n" + "Payload length: %d kiByte\n", + data[2] >>1 | (data[3] << 7) ); + else + errx(EX_IOERR, "Unknown DFU prefix type"); + } +} + +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix) +{ + uint32_t crc = 0xffffffff; + int f; + + f = open(file->name, O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, 0666); + if (f < 0) + err(EX_IOERR, "Could not open file %s for writing", file->name); + + /* write prefix, if any */ + if (write_prefix) { + if (file->prefix_type == LMDFU_PREFIX) { + uint8_t lmdfu_prefix[LMDFU_PREFIX_LENGTH]; + uint32_t addr = file->lmdfu_address / 1024; + + /* lmdfu_dfu_prefix payload length excludes prefix and suffix */ + uint32_t len = file->size.total - + file->size.prefix - file->size.suffix; + + lmdfu_prefix[0] = 0x01; /* STELLARIS_DFU_PROG */ + lmdfu_prefix[1] = 0x00; /* Reserved */ + lmdfu_prefix[2] = (uint8_t)(addr & 0xff); + lmdfu_prefix[3] = (uint8_t)(addr >> 8); + lmdfu_prefix[4] = (uint8_t)(len & 0xff); + lmdfu_prefix[5] = (uint8_t)(len >> 8) & 0xff; + lmdfu_prefix[6] = (uint8_t)(len >> 16) & 0xff; + lmdfu_prefix[7] = (uint8_t)(len >> 24); + + crc = dfu_file_write_crc(f, crc, lmdfu_prefix, LMDFU_PREFIX_LENGTH); + } + if (file->prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { + uint8_t lpcdfu_prefix[LPCDFU_PREFIX_LENGTH] = {0}; + int i; + + /* Payload is firmware and prefix rounded to 512 bytes */ + uint32_t len = (file->size.total - file->size.suffix + 511) /512; + + lpcdfu_prefix[0] = 0x1a; /* Unencypted*/ + lpcdfu_prefix[1] = 0x3f; /* Reserved */ + lpcdfu_prefix[2] = (uint8_t)(len & 0xff); + lpcdfu_prefix[3] = (uint8_t)((len >> 8) & 0xff); + for (i = 12; i < LPCDFU_PREFIX_LENGTH; i++) + lpcdfu_prefix[i] = 0xff; + + crc = dfu_file_write_crc(f, crc, lpcdfu_prefix, LPCDFU_PREFIX_LENGTH); + } + } + /* write firmware binary */ + crc = dfu_file_write_crc(f, crc, file->firmware + file->size.prefix, + file->size.total - file->size.prefix - file->size.suffix); + + /* write suffix, if any */ + if (write_suffix) { + uint8_t dfusuffix[DFU_SUFFIX_LENGTH]; + + dfusuffix[0] = file->bcdDevice & 0xff; + dfusuffix[1] = file->bcdDevice >> 8; + dfusuffix[2] = file->idProduct & 0xff; + dfusuffix[3] = file->idProduct >> 8; + dfusuffix[4] = file->idVendor & 0xff; + dfusuffix[5] = file->idVendor >> 8; + dfusuffix[6] = file->bcdDFU & 0xff; + dfusuffix[7] = file->bcdDFU >> 8; + dfusuffix[8] = 'U'; + dfusuffix[9] = 'F'; + dfusuffix[10] = 'D'; + dfusuffix[11] = DFU_SUFFIX_LENGTH; + + crc = dfu_file_write_crc(f, crc, dfusuffix, + DFU_SUFFIX_LENGTH - 4); + + dfusuffix[12] = crc; + dfusuffix[13] = crc >> 8; + dfusuffix[14] = crc >> 16; + dfusuffix[15] = crc >> 24; + + crc = dfu_file_write_crc(f, crc, dfusuffix + 12, 4); + } + close(f); +} + +void show_suffix_and_prefix(struct dfu_file *file) +{ + if (file->size.prefix == LMDFU_PREFIX_LENGTH) { + printf("The file %s contains a TI Stellaris DFU prefix with the following properties:\n", file->name); + printf("Address:\t0x%08x\n", file->lmdfu_address); + } else if (file->size.prefix == LPCDFU_PREFIX_LENGTH) { + uint8_t * prefix = file->firmware; + printf("The file %s contains a NXP unencrypted LPC DFU prefix with the following properties:\n", file->name); + printf("Size:\t%5d kiB\n", prefix[2]>>1|prefix[3]<<7); + } else if (file->size.prefix != 0) { + printf("The file %s contains an unknown prefix\n", file->name); + } + if (file->size.suffix > 0) { + printf("The file %s contains a DFU suffix with the following properties:\n", file->name); + printf("BCD device:\t0x%04X\n", file->bcdDevice); + printf("Product ID:\t0x%04X\n",file->idProduct); + printf("Vendor ID:\t0x%04X\n", file->idVendor); + printf("BCD DFU:\t0x%04X\n", file->bcdDFU); + printf("Length:\t\t%i\n", file->size.suffix); + printf("CRC:\t\t0x%08X\n", file->dwCRC); + } +} diff --git a/tools/win/src/dfu-util/src/dfu_file.h b/tools/win/src/dfu-util/src/dfu_file.h new file mode 100644 index 0000000..abebd44 --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_file.h @@ -0,0 +1,60 @@ + +#ifndef DFU_FILE_H +#define DFU_FILE_H + +#include + +struct dfu_file { + /* File name */ + const char *name; + /* Pointer to file loaded into memory */ + uint8_t *firmware; + /* Different sizes */ + struct { + int total; + int prefix; + int suffix; + } size; + /* From prefix fields */ + uint32_t lmdfu_address; + /* From prefix fields */ + uint32_t prefix_type; + + /* From DFU suffix fields */ + uint32_t dwCRC; + uint16_t bcdDFU; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; +}; + +enum suffix_req { + NO_SUFFIX, + NEEDS_SUFFIX, + MAYBE_SUFFIX +}; + +enum prefix_req { + NO_PREFIX, + NEEDS_PREFIX, + MAYBE_PREFIX +}; + +enum prefix_type { + ZERO_PREFIX, + LMDFU_PREFIX, + LPCDFU_UNENCRYPTED_PREFIX +}; + +extern int verbose; + +void dfu_load_file(struct dfu_file *file, enum suffix_req check_suffix, enum prefix_req check_prefix); +void dfu_store_file(struct dfu_file *file, int write_suffix, int write_prefix); + +void dfu_progress_bar(const char *desc, unsigned long long curr, + unsigned long long max); +void *dfu_malloc(size_t size); +uint32_t dfu_file_write_crc(int f, uint32_t crc, const void *buf, int size); +void show_suffix_and_prefix(struct dfu_file *file); + +#endif /* DFU_FILE_H */ diff --git a/tools/win/src/dfu-util/src/dfu_load.c b/tools/win/src/dfu-util/src/dfu_load.c new file mode 100644 index 0000000..64f7009 --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_load.c @@ -0,0 +1,196 @@ +/* + * DFU transfer routines + * + * This is supposed to be a general DFU implementation, as specified in the + * USB DFU 1.0 and 1.1 specification. + * + * The code was originally intended to interface with a USB device running the + * "sam7dfu" firmware (see http://www.openpcd.org/) on an AT91SAM7 processor. + * + * Copyright 2007-2008 Harald Welte + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "quirks.h" + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, + int expected_size, int fd) +{ + int total_bytes = 0; + unsigned short transaction = 0; + unsigned char *buf; + int ret; + + buf = dfu_malloc(xfer_size); + + printf("Copying data from DFU device to PC\n"); + dfu_progress_bar("Upload", 0, 1); + + while (1) { + int rc; + rc = dfu_upload(dif->dev_handle, dif->interface, + xfer_size, transaction++, buf); + if (rc < 0) { + warnx("Error during upload"); + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes (wraparound)"); + + if (rc < xfer_size) { + /* last block, return */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, expected_size); + } + ret = 0; + +out_free: + dfu_progress_bar("Upload", total_bytes, total_bytes); + if (total_bytes == 0) + printf("\nFailed.\n"); + free(buf); + if (verbose) + printf("Received a total of %i bytes\n", total_bytes); + if (expected_size != 0 && total_bytes != expected_size) + errx(EX_SOFTWARE, "Unexpected number of bytes uploaded from device"); + return ret; +} + +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file) +{ + int bytes_sent; + int expected_size; + unsigned char *buf; + unsigned short transaction = 0; + struct dfu_status dst; + int ret; + + printf("Copying data from PC to DFU device\n"); + + buf = file->firmware; + expected_size = file->size.total - file->size.suffix; + bytes_sent = 0; + + dfu_progress_bar("Download", 0, 1); + while (bytes_sent < expected_size) { + int bytes_left; + int chunk_size; + + bytes_left = expected_size - bytes_sent; + if (bytes_left < xfer_size) + chunk_size = bytes_left; + else + chunk_size = xfer_size; + + ret = dfu_download(dif->dev_handle, dif->interface, + chunk_size, transaction++, chunk_size ? buf : NULL); + if (ret < 0) { + warnx("Error during download"); + goto out; + } + bytes_sent += chunk_size; + buf += chunk_size; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + goto out; + } + + if (dst.bState == DFU_STATE_dfuDNLOAD_IDLE || + dst.bState == DFU_STATE_dfuERROR) + break; + + /* Wait while device executes flashing */ + milli_sleep(dst.bwPollTimeout); + + } while (1); + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + ret = -1; + goto out; + } + dfu_progress_bar("Download", bytes_sent, bytes_sent + bytes_left); + } + + /* send one zero sized download request to signalize end */ + ret = dfu_download(dif->dev_handle, dif->interface, + 0, transaction, NULL); + if (ret < 0) { + errx(EX_IOERR, "Error sending completion packet"); + goto out; + } + + dfu_progress_bar("Download", bytes_sent, bytes_sent); + + if (verbose) + printf("Sent a total of %i bytes\n", bytes_sent); + +get_status: + /* Transition to MANIFEST_SYNC state */ + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + warnx("unable to read DFU status after completion"); + goto out; + } + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + + milli_sleep(dst.bwPollTimeout); + + /* FIXME: deal correctly with ManifestationTolerant=0 / WillDetach bits */ + switch (dst.bState) { + case DFU_STATE_dfuMANIFEST_SYNC: + case DFU_STATE_dfuMANIFEST: + /* some devices (e.g. TAS1020b) need some time before we + * can obtain the status */ + milli_sleep(1000); + goto get_status; + break; + case DFU_STATE_dfuIDLE: + break; + } + printf("Done!\n"); + +out: + return bytes_sent; +} diff --git a/tools/win/src/dfu-util/src/dfu_load.h b/tools/win/src/dfu-util/src/dfu_load.h new file mode 100644 index 0000000..be23e9b --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_load.h @@ -0,0 +1,7 @@ +#ifndef DFU_LOAD_H +#define DFU_LOAD_H + +int dfuload_do_upload(struct dfu_if *dif, int xfer_size, int expected_size, int fd); +int dfuload_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file); + +#endif /* DFU_LOAD_H */ diff --git a/tools/win/src/dfu-util/src/dfu_util.c b/tools/win/src/dfu-util/src/dfu_util.c new file mode 100644 index 0000000..b94c7cc --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_util.c @@ -0,0 +1,346 @@ +/* + * Functions for detecting DFU USB entities + * + * Written by Harald Welte + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013 Hans Petter Selasky + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +/* + * Look for a descriptor in a concatenated descriptor list. Will + * return upon the first match of the given descriptor type. Returns length of + * found descriptor, limited to res_size + */ +static int find_descriptor(const uint8_t *desc_list, int list_len, + uint8_t desc_type, void *res_buf, int res_size) +{ + int p = 0; + + if (list_len < 2) + return (-1); + + while (p + 1 < list_len) { + int desclen; + + desclen = (int) desc_list[p]; + if (desclen == 0) { + warnx("Invalid descriptor list"); + return -1; + } + if (desc_list[p + 1] == desc_type) { + if (desclen > res_size) + desclen = res_size; + if (p + desclen > list_len) + desclen = list_len - p; + memcpy(res_buf, &desc_list[p], desclen); + return desclen; + } + p += (int) desc_list[p]; + } + return -1; +} + +static void probe_configuration(libusb_device *dev, struct libusb_device_descriptor *desc) +{ + struct usb_dfu_func_descriptor func_dfu; + libusb_device_handle *devh; + struct dfu_if *pdfu; + struct libusb_config_descriptor *cfg; + const struct libusb_interface_descriptor *intf; + const struct libusb_interface *uif; + char alt_name[MAX_DESC_STR_LEN + 1]; + char serial_name[MAX_DESC_STR_LEN + 1]; + int cfg_idx; + int intf_idx; + int alt_idx; + int ret; + int has_dfu; + + for (cfg_idx = 0; cfg_idx != desc->bNumConfigurations; cfg_idx++) { + memset(&func_dfu, 0, sizeof(func_dfu)); + has_dfu = 0; + + ret = libusb_get_config_descriptor(dev, cfg_idx, &cfg); + if (ret != 0) + return; + if (match_config_index > -1 && match_config_index != cfg->bConfigurationValue) { + libusb_free_config_descriptor(cfg); + continue; + } + + /* + * In some cases, noticably FreeBSD if uid != 0, + * the configuration descriptors are empty + */ + if (!cfg) + return; + + ret = find_descriptor(cfg->extra, cfg->extra_length, + USB_DT_DFU, &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; alt_idx < cfg->interface[intf_idx].num_altsetting; + alt_idx++) { + intf = &uif->altsetting[alt_idx]; + + ret = find_descriptor(intf->extra, intf->extra_length, USB_DT_DFU, + &func_dfu, sizeof(func_dfu)); + if (ret > -1) + goto found_dfu; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + has_dfu = 1; + } + } + if (has_dfu) { + /* + * Finally try to retrieve it requesting the + * device directly This is not supported on + * all devices for non-standard types + */ + if (libusb_open(dev, &devh) == 0) { + ret = libusb_get_descriptor(devh, USB_DT_DFU, 0, + (void *)&func_dfu, sizeof(func_dfu)); + libusb_close(devh); + if (ret > -1) + goto found_dfu; + } + warnx("Device has DFU interface, " + "but has no DFU functional descriptor"); + + /* fake version 1.0 */ + func_dfu.bLength = 7; + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + goto found_dfu; + } + libusb_free_config_descriptor(cfg); + continue; + +found_dfu: + if (func_dfu.bLength == 7) { + printf("Deducing device DFU version from functional descriptor " + "length\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + } else if (func_dfu.bLength < 9) { + printf("Error obtaining DFU functional descriptor\n"); + printf("Please report this as a bug!\n"); + printf("Warning: Assuming DFU version 1.0\n"); + func_dfu.bcdDFUVersion = libusb_cpu_to_le16(0x0100); + printf("Warning: Transfer size can not be detected\n"); + func_dfu.wTransferSize = 0; + } + + for (intf_idx = 0; intf_idx < cfg->bNumInterfaces; + intf_idx++) { + if (match_iface_index > -1 && match_iface_index != intf_idx) + continue; + + uif = &cfg->interface[intf_idx]; + if (!uif) + break; + + for (alt_idx = 0; + alt_idx < uif->num_altsetting; alt_idx++) { + int dfu_mode; + + intf = &uif->altsetting[alt_idx]; + + if (intf->bInterfaceClass != 0xfe || + intf->bInterfaceSubClass != 1) + continue; + + dfu_mode = (intf->bInterfaceProtocol == 2); + /* e.g. DSO Nano has bInterfaceProtocol 0 instead of 2 */ + if (func_dfu.bcdDFUVersion == 0x011a && intf->bInterfaceProtocol == 0) + dfu_mode = 1; + + if (dfu_mode && + match_iface_alt_index > -1 && match_iface_alt_index != alt_idx) + continue; + + if (dfu_mode) { + if ((match_vendor_dfu >= 0 && match_vendor_dfu != desc->idVendor) || + (match_product_dfu >= 0 && match_product_dfu != desc->idProduct)) { + continue; + } + } else { + if ((match_vendor >= 0 && match_vendor != desc->idVendor) || + (match_product >= 0 && match_product != desc->idProduct)) { + continue; + } + } + + if (libusb_open(dev, &devh)) { + warnx("Cannot open DFU device %04x:%04x", desc->idVendor, desc->idProduct); + break; + } + if (intf->iInterface != 0) + ret = libusb_get_string_descriptor_ascii(devh, + intf->iInterface, (void *)alt_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(alt_name, "UNKNOWN"); + if (desc->iSerialNumber != 0) + ret = libusb_get_string_descriptor_ascii(devh, + desc->iSerialNumber, (void *)serial_name, MAX_DESC_STR_LEN); + else + ret = -1; + if (ret < 1) + strcpy(serial_name, "UNKNOWN"); + libusb_close(devh); + + if (dfu_mode && + match_iface_alt_name != NULL && strcmp(alt_name, match_iface_alt_name)) + continue; + + if (dfu_mode) { + if (match_serial_dfu != NULL && strcmp(match_serial_dfu, serial_name)) + continue; + } else { + if (match_serial != NULL && strcmp(match_serial, serial_name)) + continue; + } + + pdfu = dfu_malloc(sizeof(*pdfu)); + + memset(pdfu, 0, sizeof(*pdfu)); + + pdfu->func_dfu = func_dfu; + pdfu->dev = libusb_ref_device(dev); + pdfu->quirks = get_quirks(desc->idVendor, + desc->idProduct, desc->bcdDevice); + pdfu->vendor = desc->idVendor; + pdfu->product = desc->idProduct; + pdfu->bcdDevice = desc->bcdDevice; + pdfu->configuration = cfg->bConfigurationValue; + pdfu->interface = intf->bInterfaceNumber; + pdfu->altsetting = intf->bAlternateSetting; + pdfu->devnum = libusb_get_device_address(dev); + pdfu->busnum = libusb_get_bus_number(dev); + pdfu->alt_name = strdup(alt_name); + if (pdfu->alt_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + pdfu->serial_name = strdup(serial_name); + if (pdfu->serial_name == NULL) + errx(EX_SOFTWARE, "Out of memory"); + if (dfu_mode) + pdfu->flags |= DFU_IFF_DFU; + if (pdfu->quirks & QUIRK_FORCE_DFU11) { + pdfu->func_dfu.bcdDFUVersion = + libusb_cpu_to_le16(0x0110); + } + pdfu->bMaxPacketSize0 = desc->bMaxPacketSize0; + + /* queue into list */ + pdfu->next = dfu_root; + dfu_root = pdfu; + } + } + libusb_free_config_descriptor(cfg); + } +} + +void probe_devices(libusb_context *ctx) +{ + libusb_device **list; + ssize_t num_devs; + ssize_t i; + + num_devs = libusb_get_device_list(ctx, &list); + for (i = 0; i < num_devs; ++i) { + struct libusb_device_descriptor desc; + struct libusb_device *dev = list[i]; + + if (match_bus > -1 && match_bus != libusb_get_bus_number(dev)) + continue; + if (match_device > -1 && match_device != libusb_get_device_address(dev)) + continue; + if (libusb_get_device_descriptor(dev, &desc)) + continue; + probe_configuration(dev, &desc); + } + libusb_free_device_list(list, 0); +} + +void disconnect_devices(void) +{ + struct dfu_if *pdfu; + struct dfu_if *prev = NULL; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) { + free(prev); + libusb_unref_device(pdfu->dev); + free(pdfu->alt_name); + free(pdfu->serial_name); + prev = pdfu; + } + free(prev); + dfu_root = NULL; +} + +void print_dfu_if(struct dfu_if *dfu_if) +{ + printf("Found %s: [%04x:%04x] ver=%04x, devnum=%u, cfg=%u, intf=%u, " + "alt=%u, name=\"%s\", serial=\"%s\"\n", + dfu_if->flags & DFU_IFF_DFU ? "DFU" : "Runtime", + dfu_if->vendor, dfu_if->product, + dfu_if->bcdDevice, dfu_if->devnum, + dfu_if->configuration, dfu_if->interface, + dfu_if->altsetting, dfu_if->alt_name, + dfu_if->serial_name); +} + +/* Walk the device tree and print out DFU devices */ +void list_dfu_interfaces(void) +{ + struct dfu_if *pdfu; + + for (pdfu = dfu_root; pdfu != NULL; pdfu = pdfu->next) + print_dfu_if(pdfu); +} diff --git a/tools/win/src/dfu-util/src/dfu_util.h b/tools/win/src/dfu-util/src/dfu_util.h new file mode 100644 index 0000000..fc0c19d --- /dev/null +++ b/tools/win/src/dfu-util/src/dfu_util.h @@ -0,0 +1,36 @@ +#ifndef DFU_UTIL_H +#define DFU_UTIL_H + +/* USB string descriptor should contain max 126 UTF-16 characters + * but 253 would even accomodate any UTF-8 encoding */ +#define MAX_DESC_STR_LEN 253 + +enum mode { + MODE_NONE, + MODE_VERSION, + MODE_LIST, + MODE_DETACH, + MODE_UPLOAD, + MODE_DOWNLOAD +}; + +extern struct dfu_if *dfu_root; +extern int match_bus; +extern int match_device; +extern int match_vendor; +extern int match_product; +extern int match_vendor_dfu; +extern int match_product_dfu; +extern int match_config_index; +extern int match_iface_index; +extern int match_iface_alt_index; +extern const char *match_iface_alt_name; +extern const char *match_serial; +extern const char *match_serial_dfu; + +void probe_devices(libusb_context *); +void disconnect_devices(void); +void print_dfu_if(struct dfu_if *); +void list_dfu_interfaces(void); + +#endif /* DFU_UTIL_H */ diff --git a/tools/win/src/dfu-util/src/dfuse.c b/tools/win/src/dfu-util/src/dfuse.c new file mode 100644 index 0000000..fce29fe --- /dev/null +++ b/tools/win/src/dfu-util/src/dfuse.c @@ -0,0 +1,652 @@ +/* + * DfuSe specific functions + * + * This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606) + * The DfuSe file format is described in ST document UM0391. + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfuse.h" +#include "dfuse_mem.h" + +#define DFU_TIMEOUT 5000 + +extern int verbose; +static unsigned int last_erased_page = 1; /* non-aligned value, won't match */ +static struct memsegment *mem_layout; +static unsigned int dfuse_address = 0; +static unsigned int dfuse_length = 0; +static int dfuse_force = 0; +static int dfuse_leave = 0; +static int dfuse_unprotect = 0; +static int dfuse_mass_erase = 0; + +unsigned int quad2uint(unsigned char *p) +{ + return (*p + (*(p + 1) << 8) + (*(p + 2) << 16) + (*(p + 3) << 24)); +} + +void dfuse_parse_options(const char *options) +{ + char *end; + const char *endword; + unsigned int number; + + /* address, possibly empty, must be first */ + if (*options != ':') { + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); /* GNU strchrnul */ + + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_address = number; + } else { + errx(EX_IOERR, "Invalid dfuse address: %s", options); + } + options = endword; + } + + while (*options) { + if (*options == ':') { + options++; + continue; + } + endword = strchr(options, ':'); + if (!endword) + endword = options + strlen(options); + + if (!strncmp(options, "force", endword - options)) { + dfuse_force++; + options += 5; + continue; + } + if (!strncmp(options, "leave", endword - options)) { + dfuse_leave = 1; + options += 5; + continue; + } + if (!strncmp(options, "unprotect", endword - options)) { + dfuse_unprotect = 1; + options += 9; + continue; + } + if (!strncmp(options, "mass-erase", endword - options)) { + dfuse_mass_erase = 1; + options += 10; + continue; + } + + /* any valid number is interpreted as upload length */ + number = strtoul(options, &end, 0); + if (end == endword) { + dfuse_length = number; + } else { + errx(EX_IOERR, "Invalid dfuse modifier: %s", options); + } + options = endword; + } +} + +/* DFU_UPLOAD request for DfuSe 1.1a */ +int dfuse_upload(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_IN | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_UPLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_msg returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DFU_DNLOAD request for DfuSe 1.1a */ +int dfuse_download(struct dfu_if *dif, const unsigned short length, + unsigned char *data, unsigned short transaction) +{ + int status; + + status = libusb_control_transfer(dif->dev_handle, + /* bmRequestType */ LIBUSB_ENDPOINT_OUT | + LIBUSB_REQUEST_TYPE_CLASS | + LIBUSB_RECIPIENT_INTERFACE, + /* bRequest */ DFU_DNLOAD, + /* wValue */ transaction, + /* wIndex */ dif->interface, + /* Data */ data, + /* wLength */ length, + DFU_TIMEOUT); + if (status < 0) { + errx(EX_IOERR, "%s: libusb_control_transfer returned %d", + __FUNCTION__, status); + } + return status; +} + +/* DfuSe only commands */ +/* Leaves the device in dfuDNLOAD-IDLE state */ +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command) +{ + const char* dfuse_command_name[] = { "SET_ADDRESS" , "ERASE_PAGE", + "MASS_ERASE", "READ_UNPROTECT"}; + unsigned char buf[5]; + int length; + int ret; + struct dfu_status dst; + int firstpoll = 1; + + if (command == ERASE_PAGE) { + struct memsegment *segment; + int page_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_ERASABLE)) { + errx(EX_IOERR, "Page at 0x%08x can not be erased", + address); + } + page_size = segment->pagesize; + if (verbose > 1) + printf("Erasing page size %i at address 0x%08x, page " + "starting at 0x%08x\n", page_size, address, + address & ~(page_size - 1)); + buf[0] = 0x41; /* Erase command */ + length = 5; + last_erased_page = address & ~(page_size - 1); + } else if (command == SET_ADDRESS) { + if (verbose > 2) + printf(" Setting address pointer to 0x%08x\n", + address); + buf[0] = 0x21; /* Set Address Pointer command */ + length = 5; + } else if (command == MASS_ERASE) { + buf[0] = 0x41; /* Mass erase command when length = 1 */ + length = 1; + } else if (command == READ_UNPROTECT) { + buf[0] = 0x92; + length = 1; + } else { + errx(EX_IOERR, "Non-supported special command %d", command); + } + buf[1] = address & 0xff; + buf[2] = (address >> 8) & 0xff; + buf[3] = (address >> 16) & 0xff; + buf[4] = (address >> 24) & 0xff; + + ret = dfuse_download(dif, length, buf, 0); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" download", + dfuse_command_name[command]); + } + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during special command \"%s\" get_status", + dfuse_command_name[command]); + } + if (firstpoll) { + firstpoll = 0; + if (dst.bState != DFU_STATE_dfuDNBUSY) { + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + errx(EX_IOERR, "Wrong state after command \"%s\" download", + dfuse_command_name[command]); + } + } + /* wait while command is executed */ + if (verbose) + printf(" Poll timeout %i ms\n", dst.bwPollTimeout); + milli_sleep(dst.bwPollTimeout); + if (command == READ_UNPROTECT) + return ret; + } while (dst.bState == DFU_STATE_dfuDNBUSY); + + if (dst.bStatus != DFU_STATUS_OK) { + errx(EX_IOERR, "%s not correctly executed", + dfuse_command_name[command]); + } + return ret; +} + +int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size, + int transaction) +{ + int bytes_sent; + struct dfu_status dst; + int ret; + + ret = dfuse_download(dif, size, size ? data : NULL, transaction); + if (ret < 0) { + errx(EX_IOERR, "Error during download"); + return ret; + } + bytes_sent = ret; + + do { + ret = dfu_get_status(dif, &dst); + if (ret < 0) { + errx(EX_IOERR, "Error during download get_status"); + return ret; + } + milli_sleep(dst.bwPollTimeout); + } while (dst.bState != DFU_STATE_dfuDNLOAD_IDLE && + dst.bState != DFU_STATE_dfuERROR && + dst.bState != DFU_STATE_dfuMANIFEST); + + if (dst.bState == DFU_STATE_dfuMANIFEST) + printf("Transitioning to dfuMANIFEST state\n"); + + if (dst.bStatus != DFU_STATUS_OK) { + printf(" failed!\n"); + printf("state(%u) = %s, status(%u) = %s\n", dst.bState, + dfu_state_to_string(dst.bState), dst.bStatus, + dfu_status_to_string(dst.bStatus)); + return -1; + } + return bytes_sent; +} + +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options) +{ + int total_bytes = 0; + int upload_limit = 0; + unsigned char *buf; + int transaction; + int ret; + + buf = dfu_malloc(xfer_size); + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + if (dfuse_length) + upload_limit = dfuse_length; + if (dfuse_address) { + struct memsegment *segment; + + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) + errx(EX_IOERR, "Failed to parse memory layout"); + + segment = find_segment(mem_layout, dfuse_address); + if (!dfuse_force && + (!segment || !(segment->memtype & DFUSE_READABLE))) + errx(EX_IOERR, "Page at 0x%08x is not readable", + dfuse_address); + + if (!upload_limit) { + upload_limit = segment->end - dfuse_address + 1; + printf("Limiting upload to end of memory segment, " + "%i bytes\n", upload_limit); + } + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfu_abort_to_idle(dif); + } else { + /* Boot loader decides the start address, unknown to us */ + /* Use a short length to lower risk of running out of bounds */ + if (!upload_limit) + upload_limit = 0x4000; + printf("Limiting default upload to %i bytes\n", upload_limit); + } + + dfu_progress_bar("Upload", 0, 1); + + transaction = 2; + while (1) { + int rc; + + /* last chunk can be smaller than original xfer_size */ + if (upload_limit - total_bytes < xfer_size) + xfer_size = upload_limit - total_bytes; + rc = dfuse_upload(dif, xfer_size, buf, transaction++); + if (rc < 0) { + ret = rc; + goto out_free; + } + + dfu_file_write_crc(fd, 0, buf, rc); + total_bytes += rc; + + if (total_bytes < 0) + errx(EX_SOFTWARE, "Received too many bytes"); + + if (rc < xfer_size || total_bytes >= upload_limit) { + /* last block, return successfully */ + ret = total_bytes; + break; + } + dfu_progress_bar("Upload", total_bytes, upload_limit); + } + + dfu_progress_bar("Upload", total_bytes, total_bytes); + + dfu_abort_to_idle(dif); + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + + out_free: + free(buf); + + return ret; +} + +/* Writes an element of any size to the device, taking care of page erases */ +/* returns 0 on success, otherwise -EINVAL */ +int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress, + unsigned int dwElementSize, unsigned char *data, + int xfer_size) +{ + int p; + int ret; + struct memsegment *segment; + + /* Check at least that we can write to the last address */ + segment = + find_segment(mem_layout, dwElementAddress + dwElementSize - 1); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Last page at 0x%08x is not writeable", + dwElementAddress + dwElementSize - 1); + } + + dfu_progress_bar("Download", 0, 1); + + for (p = 0; p < (int)dwElementSize; p += xfer_size) { + int page_size; + unsigned int erase_address; + unsigned int address = dwElementAddress + p; + int chunk_size = xfer_size; + + segment = find_segment(mem_layout, address); + if (!segment || !(segment->memtype & DFUSE_WRITEABLE)) { + errx(EX_IOERR, "Page at 0x%08x is not writeable", + address); + } + page_size = segment->pagesize; + + /* check if this is the last chunk */ + if (p + chunk_size > (int)dwElementSize) + chunk_size = dwElementSize - p; + + /* Erase only for flash memory downloads */ + if ((segment->memtype & DFUSE_ERASABLE) && !dfuse_mass_erase) { + /* erase all involved pages */ + for (erase_address = address; + erase_address < address + chunk_size; + erase_address += page_size) + if ((erase_address & ~(page_size - 1)) != + last_erased_page) + dfuse_special_command(dif, + erase_address, + ERASE_PAGE); + + if (((address + chunk_size - 1) & ~(page_size - 1)) != + last_erased_page) { + if (verbose > 2) + printf(" Chunk extends into next page," + " erase it as well\n"); + dfuse_special_command(dif, + address + chunk_size - 1, + ERASE_PAGE); + } + } + + if (verbose) { + printf(" Download from image offset " + "%08x to memory %08x-%08x, size %i\n", + p, address, address + chunk_size - 1, + chunk_size); + } else { + dfu_progress_bar("Download", p, dwElementSize); + } + + dfuse_special_command(dif, address, SET_ADDRESS); + + /* transaction = 2 for no address offset */ + ret = dfuse_dnload_chunk(dif, data + p, chunk_size, 2); + if (ret != chunk_size) { + errx(EX_IOERR, "Failed to write whole chunk: " + "%i of %i bytes", ret, chunk_size); + return -EINVAL; + } + } + if (!verbose) + dfu_progress_bar("Download", dwElementSize, dwElementSize); + return 0; +} + +static void +dfuse_memcpy(unsigned char *dst, unsigned char **src, int *rem, int size) +{ + if (size > *rem) { + errx(EX_IOERR, "Corrupt DfuSe file: " + "Cannot read %d bytes from %d bytes", size, *rem); + } + if (dst != NULL) + memcpy(dst, *src, size); + (*src) += size; + (*rem) -= size; +} + +/* Download raw binary file to DfuSe device */ +int dfuse_do_bin_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file, unsigned int start_address) +{ + unsigned int dwElementAddress; + unsigned int dwElementSize; + unsigned char *data; + int ret; + + dwElementAddress = start_address; + dwElementSize = file->size.total - + file->size.suffix - file->size.prefix; + + printf("Downloading to address = 0x%08x, size = %i\n", + dwElementAddress, dwElementSize); + + data = file->firmware + file->size.prefix; + + ret = dfuse_dnload_element(dif, dwElementAddress, dwElementSize, data, + xfer_size); + if (ret != 0) + goto out_free; + + printf("File downloaded successfully\n"); + ret = dwElementSize; + + out_free: + return ret; +} + +/* Parse a DfuSe file and download contents to device */ +int dfuse_do_dfuse_dnload(struct dfu_if *dif, int xfer_size, + struct dfu_file *file) +{ + uint8_t dfuprefix[11]; + uint8_t targetprefix[274]; + uint8_t elementheader[8]; + int image; + int element; + int bTargets; + int bAlternateSetting; + int dwNbElements; + unsigned int dwElementAddress; + unsigned int dwElementSize; + uint8_t *data; + int ret; + int rem; + int bFirstAddressSaved = 0; + + rem = file->size.total - file->size.prefix - file->size.suffix; + data = file->firmware + file->size.prefix; + + /* Must be larger than a minimal DfuSe header and suffix */ + if (rem < (int)(sizeof(dfuprefix) + + sizeof(targetprefix) + sizeof(elementheader))) { + errx(EX_SOFTWARE, "File too small for a DfuSe file"); + } + + dfuse_memcpy(dfuprefix, &data, &rem, sizeof(dfuprefix)); + + if (strncmp((char *)dfuprefix, "DfuSe", 5)) { + errx(EX_IOERR, "No valid DfuSe signature"); + return -EINVAL; + } + if (dfuprefix[5] != 0x01) { + errx(EX_IOERR, "DFU format revision %i not supported", + dfuprefix[5]); + return -EINVAL; + } + bTargets = dfuprefix[10]; + printf("file contains %i DFU images\n", bTargets); + + for (image = 1; image <= bTargets; image++) { + printf("parsing DFU image %i\n", image); + dfuse_memcpy(targetprefix, &data, &rem, sizeof(targetprefix)); + if (strncmp((char *)targetprefix, "Target", 6)) { + errx(EX_IOERR, "No valid target signature"); + return -EINVAL; + } + bAlternateSetting = targetprefix[6]; + dwNbElements = quad2uint((unsigned char *)targetprefix + 270); + printf("image for alternate setting %i, ", bAlternateSetting); + printf("(%i elements, ", dwNbElements); + printf("total size = %i)\n", + quad2uint((unsigned char *)targetprefix + 266)); + if (bAlternateSetting != dif->altsetting) + printf("Warning: Image does not match current alternate" + " setting.\n" + "Please rerun with the correct -a option setting" + " to download this image!\n"); + for (element = 1; element <= dwNbElements; element++) { + printf("parsing element %i, ", element); + dfuse_memcpy(elementheader, &data, &rem, sizeof(elementheader)); + dwElementAddress = + quad2uint((unsigned char *)elementheader); + dwElementSize = + quad2uint((unsigned char *)elementheader + 4); + printf("address = 0x%08x, ", dwElementAddress); + printf("size = %i\n", dwElementSize); + + if (!bFirstAddressSaved) { + bFirstAddressSaved = 1; + dfuse_address = dwElementAddress; + } + /* sanity check */ + if ((int)dwElementSize > rem) + errx(EX_SOFTWARE, "File too small for element size"); + + if (bAlternateSetting == dif->altsetting) { + ret = dfuse_dnload_element(dif, dwElementAddress, + dwElementSize, data, xfer_size); + } else { + ret = 0; + } + + /* advance read pointer */ + dfuse_memcpy(NULL, &data, &rem, dwElementSize); + + if (ret != 0) + return ret; + } + } + + if (rem != 0) + warnx("%d bytes leftover", rem); + + printf("done parsing DfuSe file\n"); + + return 0; +} + +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options) +{ + int ret; + + if (dfuse_options) + dfuse_parse_options(dfuse_options); + mem_layout = parse_memory_layout((char *)dif->alt_name); + if (!mem_layout) { + errx(EX_IOERR, "Failed to parse memory layout"); + } + if (dfuse_unprotect) { + if (!dfuse_force) { + errx(EX_IOERR, "The read unprotect command " + "will erase the flash memory" + "and can only be used with force\n"); + } + dfuse_special_command(dif, 0, READ_UNPROTECT); + printf("Device disconnects, erases flash and resets now\n"); + exit(0); + } + if (dfuse_mass_erase) { + if (!dfuse_force) { + errx(EX_IOERR, "The mass erase command " + "can only be used with force"); + } + printf("Performing mass erase, this can take a moment\n"); + dfuse_special_command(dif, 0, MASS_ERASE); + } + if (dfuse_address) { + if (file->bcdDFU == 0x11a) { + errx(EX_IOERR, "This is a DfuSe file, not " + "meant for raw download"); + } + ret = dfuse_do_bin_dnload(dif, xfer_size, file, dfuse_address); + } else { + if (file->bcdDFU != 0x11a) { + warnx("Only DfuSe file version 1.1a is supported"); + errx(EX_IOERR, "(for raw binary download, use the " + "--dfuse-address option)"); + } + ret = dfuse_do_dfuse_dnload(dif, xfer_size, file); + } + free_segment_list(mem_layout); + + dfu_abort_to_idle(dif); + + if (dfuse_leave) { + dfuse_special_command(dif, dfuse_address, SET_ADDRESS); + dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */ + } + return ret; +} diff --git a/tools/win/src/dfu-util/src/dfuse.h b/tools/win/src/dfu-util/src/dfuse.h new file mode 100644 index 0000000..ed1108c --- /dev/null +++ b/tools/win/src/dfu-util/src/dfuse.h @@ -0,0 +1,35 @@ +/* This implements the ST Microsystems DFU extensions (DfuSe) + * as per the DfuSe 1.1a specification (Document UM0391) + * + * (C) 2010-2012 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_H +#define DFUSE_H + +#include "dfu.h" + +enum dfuse_command { SET_ADDRESS, ERASE_PAGE, MASS_ERASE, READ_UNPROTECT }; + +int dfuse_special_command(struct dfu_if *dif, unsigned int address, + enum dfuse_command command); +int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd, + const char *dfuse_options); +int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file, + const char *dfuse_options); + +#endif /* DFUSE_H */ diff --git a/tools/win/src/dfu-util/src/dfuse_mem.c b/tools/win/src/dfu-util/src/dfuse_mem.c new file mode 100644 index 0000000..a91aacf --- /dev/null +++ b/tools/win/src/dfu-util/src/dfuse_mem.c @@ -0,0 +1,198 @@ +/* + * Helper functions for reading the memory map of a device + * following the ST DfuSe 1.1a specification. + * + * Copyright 2011-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" +#include "dfuse_mem.h" + +int add_segment(struct memsegment **segment_list, struct memsegment segment) +{ + struct memsegment *new_element; + + new_element = dfu_malloc(sizeof(struct memsegment)); + *new_element = segment; + new_element->next = NULL; + + if (*segment_list == NULL) + /* list can be empty on first call */ + *segment_list = new_element; + else { + struct memsegment *next_element; + + /* find last element in list */ + next_element = *segment_list; + while (next_element->next != NULL) + next_element = next_element->next; + next_element->next = new_element; + } + return 0; +} + +struct memsegment *find_segment(struct memsegment *segment_list, + unsigned int address) +{ + while (segment_list != NULL) { + if (segment_list->start <= address && + segment_list->end >= address) + return segment_list; + segment_list = segment_list->next; + } + return NULL; +} + +void free_segment_list(struct memsegment *segment_list) +{ + struct memsegment *next_element; + + while (segment_list->next != NULL) { + next_element = segment_list->next; + free(segment_list); + segment_list = next_element; + } + free(segment_list); +} + +/* Parse memory map from interface descriptor string + * encoded as per ST document UM0424 section 4.3.2. + */ +struct memsegment *parse_memory_layout(char *intf_desc) +{ + + char multiplier, memtype; + unsigned int address; + int sectors, size; + char *name, *typestring; + int ret; + int count = 0; + char separator; + int scanned; + struct memsegment *segment_list = NULL; + struct memsegment segment; + + name = dfu_malloc(strlen(intf_desc)); + + ret = sscanf(intf_desc, "@%[^/]%n", name, &scanned); + if (ret < 1) { + free(name); + warnx("Could not read name, sscanf returned %d", ret); + return NULL; + } + printf("DfuSe interface name: \"%s\"\n", name); + + intf_desc += scanned; + typestring = dfu_malloc(strlen(intf_desc)); + + while (ret = sscanf(intf_desc, "/0x%x/%n", &address, &scanned), + ret > 0) { + + intf_desc += scanned; + while (ret = sscanf(intf_desc, "%d*%d%c%[^,/]%n", + §ors, &size, &multiplier, typestring, + &scanned), ret > 2) { + intf_desc += scanned; + + count++; + memtype = 0; + if (ret == 4) { + if (strlen(typestring) == 1 + && typestring[0] != '/') + memtype = typestring[0]; + else { + warnx("Parsing type identifier '%s' " + "failed for segment %i", + typestring, count); + continue; + } + } + + /* Quirk for STM32F4 devices */ + if (strcmp(name, "Device Feature") == 0) + memtype = 'e'; + + switch (multiplier) { + case 'B': + break; + case 'K': + size *= 1024; + break; + case 'M': + size *= 1024 * 1024; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + if (!memtype) { + warnx("Non-valid multiplier '%c', " + "interpreted as type " + "identifier instead", + multiplier); + memtype = multiplier; + break; + } + /* fallthrough if memtype was already set */ + default: + warnx("Non-valid multiplier '%c', " + "assuming bytes", multiplier); + } + + if (!memtype) { + warnx("No valid type for segment %d\n", count); + continue; + } + + segment.start = address; + segment.end = address + sectors * size - 1; + segment.pagesize = size; + segment.memtype = memtype & 7; + add_segment(&segment_list, segment); + + if (verbose) + printf("Memory segment at 0x%08x %3d x %4d = " + "%5d (%s%s%s)\n", + address, sectors, size, sectors * size, + memtype & DFUSE_READABLE ? "r" : "", + memtype & DFUSE_ERASABLE ? "e" : "", + memtype & DFUSE_WRITEABLE ? "w" : ""); + + address += sectors * size; + + separator = *intf_desc; + if (separator == ',') + intf_desc += 1; + else + break; + } /* while per segment */ + + } /* while per address */ + free(name); + free(typestring); + + return segment_list; +} diff --git a/tools/win/src/dfu-util/src/dfuse_mem.h b/tools/win/src/dfu-util/src/dfuse_mem.h new file mode 100644 index 0000000..0181f0c --- /dev/null +++ b/tools/win/src/dfu-util/src/dfuse_mem.h @@ -0,0 +1,44 @@ +/* Helper functions for reading the memory map in a device + * following the ST DfuSe 1.1a specification. + * + * (C) 2011 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DFUSE_MEM_H +#define DFUSE_MEM_H + +#define DFUSE_READABLE 1 +#define DFUSE_ERASABLE 2 +#define DFUSE_WRITEABLE 4 + +struct memsegment { + unsigned int start; + unsigned int end; + int pagesize; + int memtype; + struct memsegment *next; +}; + +int add_segment(struct memsegment **list, struct memsegment new_element); + +struct memsegment *find_segment(struct memsegment *list, unsigned int address); + +void free_segment_list(struct memsegment *list); + +struct memsegment *parse_memory_layout(char *intf_desc_str); + +#endif /* DFUSE_MEM_H */ diff --git a/tools/win/src/dfu-util/src/main.c b/tools/win/src/dfu-util/src/main.c new file mode 100644 index 0000000..acaed2f --- /dev/null +++ b/tools/win/src/dfu-util/src/main.c @@ -0,0 +1,699 @@ +/* + * dfu-util + * + * Copyright 2007-2008 by OpenMoko, Inc. + * Copyright 2013-2014 Hans Petter Selasky + * + * Written by Harald Welte + * + * Based on existing code of dfu-programmer-0.4 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu.h" +#include "usb_dfu.h" +#include "dfu_file.h" +#include "dfu_load.h" +#include "dfu_util.h" +#include "dfuse.h" +#include "quirks.h" + +#ifdef HAVE_USBPATH_H +#include +#endif + +int verbose = 0; + +struct dfu_if *dfu_root = NULL; + +int match_bus = -1; +int match_device = -1; +int match_vendor = -1; +int match_product = -1; +int match_vendor_dfu = -1; +int match_product_dfu = -1; +int match_config_index = -1; +int match_iface_index = -1; +int match_iface_alt_index = -1; +const char *match_iface_alt_name = NULL; +const char *match_serial = NULL; +const char *match_serial_dfu = NULL; + +static int parse_match_value(const char *str, int default_value) +{ + char *remainder; + int value; + + if (str == NULL) { + value = default_value; + } else if (*str == '*') { + value = -1; /* Match anything */ + } else if (*str == '-') { + value = 0x10000; /* Impossible vendor/product ID */ + } else { + value = strtoul(str, &remainder, 16); + if (remainder == str) { + value = default_value; + } + } + return value; +} + +static void parse_vendprod(const char *str) +{ + const char *comma; + const char *colon; + + /* Default to match any DFU device in runtime or DFU mode */ + match_vendor = -1; + match_product = -1; + match_vendor_dfu = -1; + match_product_dfu = -1; + + comma = strchr(str, ','); + if (comma == str) { + /* DFU mode vendor/product being specified without any runtime + * vendor/product specification, so don't match any runtime device */ + match_vendor = match_product = 0x10000; + } else { + colon = strchr(str, ':'); + if (colon != NULL) { + ++colon; + if ((comma != NULL) && (colon > comma)) { + colon = NULL; + } + } + match_vendor = parse_match_value(str, match_vendor); + match_product = parse_match_value(colon, match_product); + if (comma != NULL) { + /* Both runtime and DFU mode vendor/product specifications are + * available, so default DFU mode match components to the given + * runtime match components */ + match_vendor_dfu = match_vendor; + match_product_dfu = match_product; + } + } + if (comma != NULL) { + ++comma; + colon = strchr(comma, ':'); + if (colon != NULL) { + ++colon; + } + match_vendor_dfu = parse_match_value(comma, match_vendor_dfu); + match_product_dfu = parse_match_value(colon, match_product_dfu); + } +} + +static void parse_serial(char *str) +{ + char *comma; + + match_serial = str; + comma = strchr(str, ','); + if (comma == NULL) { + match_serial_dfu = match_serial; + } else { + *comma++ = 0; + match_serial_dfu = comma; + } + if (*match_serial == 0) match_serial = NULL; + if (*match_serial_dfu == 0) match_serial_dfu = NULL; +} + +#ifdef HAVE_USBPATH_H + +static int resolve_device_path(char *path) +{ + int res; + + res = usb_path2devnum(path); + if (res < 0) + return -EINVAL; + if (!res) + return 0; + + match_bus = atoi(path); + match_device = res; + + return 0; +} + +#else /* HAVE_USBPATH_H */ + +static int resolve_device_path(char *path) +{ + (void)path; /* Eliminate unused variable warning */ + errx(EX_SOFTWARE, "USB device paths are not supported by this dfu-util.\n"); +} + +#endif /* !HAVE_USBPATH_H */ + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-util [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -v --verbose\t\t\tPrint verbose debug statements\n" + " -l --list\t\t\tList currently attached DFU capable devices\n"); + fprintf(stderr, " -e --detach\t\t\tDetach currently attached DFU capable devices\n" + " -E --detach-delay seconds\tTime to wait before reopening a device after detach\n" + " -d --device :[,:]\n" + "\t\t\t\tSpecify Vendor/Product ID(s) of DFU device\n" + " -p --path \tSpecify path to DFU device\n" + " -c --cfg \t\tSpecify the Configuration of DFU device\n" + " -i --intf \t\tSpecify the DFU Interface number\n" + " -S --serial [,]\n" + "\t\t\t\tSpecify Serial String of DFU device\n" + " -a --alt \t\tSpecify the Altsetting of the DFU Interface\n" + "\t\t\t\tby name or by number\n"); + fprintf(stderr, " -t --transfer-size \tSpecify the number of bytes per USB Transfer\n" + " -U --upload \t\tRead firmware from device into \n" + " -Z --upload-size \tSpecify the expected upload size in bytes\n" + " -D --download \t\tWrite firmware from into device\n" + " -R --reset\t\t\tIssue USB Reset signalling once we're finished\n" + " -s --dfuse-address

\tST DfuSe mode, specify target address for\n" + "\t\t\t\traw file download or upload. Not applicable for\n" + "\t\t\t\tDfuSe file (.dfu) downloads\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf(PACKAGE_STRING "\n\n"); + printf("Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.\n" + "Copyright 2010-2014 Tormod Volden and Stefan Schmidt\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to " PACKAGE_BUGREPORT "\n\n"); +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "verbose", 0, 0, 'v' }, + { "list", 0, 0, 'l' }, + { "detach", 0, 0, 'e' }, + { "detach-delay", 1, 0, 'E' }, + { "device", 1, 0, 'd' }, + { "path", 1, 0, 'p' }, + { "configuration", 1, 0, 'c' }, + { "cfg", 1, 0, 'c' }, + { "interface", 1, 0, 'i' }, + { "intf", 1, 0, 'i' }, + { "altsetting", 1, 0, 'a' }, + { "alt", 1, 0, 'a' }, + { "serial", 1, 0, 'S' }, + { "transfer-size", 1, 0, 't' }, + { "upload", 1, 0, 'U' }, + { "upload-size", 1, 0, 'Z' }, + { "download", 1, 0, 'D' }, + { "reset", 0, 0, 'R' }, + { "dfuse-address", 1, 0, 's' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + int expected_size = 0; + unsigned int transfer_size = 0; + enum mode mode = MODE_NONE; + struct dfu_status status; + libusb_context *ctx; + struct dfu_file file; + char *end; + int final_reset = 0; + int ret; + int dfuse_device = 0; + int fd; + const char *dfuse_options = NULL; + int detach_delay = 5; + uint16_t runtime_vendor; + uint16_t runtime_product; + + memset(&file, 0, sizeof(file)); + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + mode = MODE_VERSION; + break; + case 'v': + verbose++; + break; + case 'l': + mode = MODE_LIST; + break; + case 'e': + mode = MODE_DETACH; + match_iface_alt_index = 0; + match_iface_index = 0; + break; + case 'E': + detach_delay = atoi(optarg); + break; + case 'd': + parse_vendprod(optarg); + break; + case 'p': + /* Parse device path */ + ret = resolve_device_path(optarg); + if (ret < 0) + errx(EX_SOFTWARE, "Unable to parse '%s'", optarg); + if (!ret) + errx(EX_SOFTWARE, "Cannot find '%s'", optarg); + break; + case 'c': + /* Configuration */ + match_config_index = atoi(optarg); + break; + case 'i': + /* Interface */ + match_iface_index = atoi(optarg); + break; + case 'a': + /* Interface Alternate Setting */ + match_iface_alt_index = strtoul(optarg, &end, 0); + if (*end) { + match_iface_alt_name = optarg; + match_iface_alt_index = -1; + } + break; + case 'S': + parse_serial(optarg); + break; + case 't': + transfer_size = atoi(optarg); + break; + case 'U': + mode = MODE_UPLOAD; + file.name = optarg; + break; + case 'Z': + expected_size = atoi(optarg); + break; + case 'D': + mode = MODE_DOWNLOAD; + file.name = optarg; + break; + case 'R': + final_reset = 1; + break; + case 's': + dfuse_options = optarg; + break; + default: + help(); + break; + } + } + + print_version(); + if (mode == MODE_VERSION) { + exit(0); + } + + if (mode == MODE_NONE) { + fprintf(stderr, "You need to specify one of -D or -U\n"); + help(); + } + + if (match_config_index == 0) { + /* Handle "-c 0" (unconfigured device) as don't care */ + match_config_index = -1; + } + + if (mode == MODE_DOWNLOAD) { + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + /* If the user didn't specify product and/or vendor IDs to match, + * use any IDs from the file suffix for device matching */ + if (match_vendor < 0 && file.idVendor != 0xffff) { + match_vendor = file.idVendor; + printf("Match vendor ID from file: %04x\n", match_vendor); + } + if (match_product < 0 && file.idProduct != 0xffff) { + match_product = file.idProduct; + printf("Match product ID from file: %04x\n", match_product); + } + } + + ret = libusb_init(&ctx); + if (ret) + errx(EX_IOERR, "unable to initialize libusb: %i", ret); + + if (verbose > 2) { + libusb_set_debug(ctx, 255); + } + + probe_devices(ctx); + + if (mode == MODE_LIST) { + list_dfu_interfaces(); + exit(0); + } + + if (dfu_root == NULL) { + errx(EX_IOERR, "No DFU capable USB device available"); + } else if (dfu_root->next != NULL) { + /* We cannot safely support more than one DFU capable device + * with same vendor/product ID, since during DFU we need to do + * a USB bus reset, after which the target device will get a + * new address */ + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device\n"); + } + + /* We have exactly one device. Its libusb_device is now in dfu_root->dev */ + + printf("Opening DFU capable USB device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) + errx(EX_IOERR, "Cannot open device"); + + printf("ID %04x:%04x\n", dfu_root->vendor, dfu_root->product); + + printf("Run-time device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + /* Transition from run-Time mode to DFU mode */ + if (!(dfu_root->flags & DFU_IFF_DFU)) { + int err; + /* In the 'first round' during runtime mode, there can only be one + * DFU Interface descriptor according to the DFU Spec. */ + + /* FIXME: check if the selected device really has only one */ + + runtime_vendor = dfu_root->vendor; + runtime_product = dfu_root->product; + + printf("Claiming USB DFU Runtime Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface %d", + dfu_root->interface); + } + + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, 0) < 0) { + errx(EX_IOERR, "Cannot set alt interface zero"); + } + + printf("Determining device status: "); + + err = dfu_get_status(dfu_root, &status); + if (err == LIBUSB_ERROR_PIPE) { + printf("Device does not implement get_status, assuming appIDLE\n"); + status.bStatus = DFU_STATUS_OK; + status.bwPollTimeout = 0; + status.bState = DFU_STATE_appIDLE; + status.iString = 0; + } else if (err < 0) { + errx(EX_IOERR, "error get_status"); + } else { + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + } + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + printf("Device really in Runtime Mode, send DFU " + "detach request...\n"); + if (dfu_detach(dfu_root->dev_handle, + dfu_root->interface, 1000) < 0) { + warnx("error detaching"); + } + if (dfu_root->func_dfu.bmAttributes & USB_DFU_WILL_DETACH) { + printf("Device will detach and reattach...\n"); + } else { + printf("Resetting USB...\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) + errx(EX_IOERR, "error resetting " + "after detach"); + } + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, + dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + /* fall through */ + default: + warnx("WARNING: Runtime device already in DFU state ?!?"); + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + goto dfustate; + } + libusb_release_interface(dfu_root->dev_handle, + dfu_root->interface); + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + + if (mode == MODE_DETACH) { + libusb_exit(ctx); + exit(0); + } + + /* keeping handles open might prevent re-enumeration */ + disconnect_devices(); + + milli_sleep(detach_delay * 1000); + + /* Change match vendor and product to impossible values to force + * only DFU mode matches in the following probe */ + match_vendor = match_product = 0x10000; + + probe_devices(ctx); + + if (dfu_root == NULL) { + errx(EX_IOERR, "Lost device after RESET?"); + } else if (dfu_root->next != NULL) { + errx(EX_IOERR, "More than one DFU capable USB device found! " + "Try `--list' and specify the serial number " + "or disconnect all but one device"); + } + + /* Check for DFU mode device */ + if (!(dfu_root->flags | DFU_IFF_DFU)) + errx(EX_SOFTWARE, "Device is not in DFU mode"); + + printf("Opening DFU USB Device...\n"); + ret = libusb_open(dfu_root->dev, &dfu_root->dev_handle); + if (ret || !dfu_root->dev_handle) { + errx(EX_IOERR, "Cannot open device"); + } + } else { + /* we're already in DFU mode, so we can skip the detach/reset + * procedure */ + /* If a match vendor/product was specified, use that as the runtime + * vendor/product, otherwise use the DFU mode vendor/product */ + runtime_vendor = match_vendor < 0 ? dfu_root->vendor : match_vendor; + runtime_product = match_product < 0 ? dfu_root->product : match_product; + } + +dfustate: +#if 0 + printf("Setting Configuration %u...\n", dfu_root->configuration); + if (libusb_set_configuration(dfu_root->dev_handle, dfu_root->configuration) < 0) { + errx(EX_IOERR, "Cannot set configuration"); + } +#endif + printf("Claiming USB DFU Interface...\n"); + if (libusb_claim_interface(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "Cannot claim interface"); + } + + printf("Setting Alternate Setting #%d ...\n", dfu_root->altsetting); + if (libusb_set_interface_alt_setting(dfu_root->dev_handle, dfu_root->interface, dfu_root->altsetting) < 0) { + errx(EX_IOERR, "Cannot set alternate interface"); + } + +status_again: + printf("Determining device status: "); + if (dfu_get_status(dfu_root, &status ) < 0) { + errx(EX_IOERR, "error get_status"); + } + printf("state = %s, status = %d\n", + dfu_state_to_string(status.bState), status.bStatus); + + milli_sleep(status.bwPollTimeout); + + switch (status.bState) { + case DFU_STATE_appIDLE: + case DFU_STATE_appDETACH: + errx(EX_IOERR, "Device still in Runtime Mode!"); + break; + case DFU_STATE_dfuERROR: + printf("dfuERROR, clearing status\n"); + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "error clear_status"); + } + goto status_again; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + case DFU_STATE_dfuUPLOAD_IDLE: + printf("aborting previous incomplete transfer\n"); + if (dfu_abort(dfu_root->dev_handle, dfu_root->interface) < 0) { + errx(EX_IOERR, "can't send DFU_ABORT"); + } + goto status_again; + break; + case DFU_STATE_dfuIDLE: + printf("dfuIDLE, continuing\n"); + break; + default: + break; + } + + if (DFU_STATUS_OK != status.bStatus ) { + printf("WARNING: DFU Status: '%s'\n", + dfu_status_to_string(status.bStatus)); + /* Clear our status & try again. */ + if (dfu_clear_status(dfu_root->dev_handle, dfu_root->interface) < 0) + errx(EX_IOERR, "USB communication error"); + if (dfu_get_status(dfu_root, &status) < 0) + errx(EX_IOERR, "USB communication error"); + if (DFU_STATUS_OK != status.bStatus) + errx(EX_SOFTWARE, "Status is not OK: %d", status.bStatus); + + milli_sleep(status.bwPollTimeout); + } + + printf("DFU mode device DFU version %04x\n", + libusb_le16_to_cpu(dfu_root->func_dfu.bcdDFUVersion)); + + if (dfu_root->func_dfu.bcdDFUVersion == libusb_cpu_to_le16(0x11a)) + dfuse_device = 1; + + /* If not overridden by the user */ + if (!transfer_size) { + transfer_size = libusb_le16_to_cpu( + dfu_root->func_dfu.wTransferSize); + if (transfer_size) { + printf("Device returned transfer size %i\n", + transfer_size); + } else { + errx(EX_IOERR, "Transfer size must be specified"); + } + } + +#ifdef HAVE_GETPAGESIZE +/* autotools lie when cross-compiling for Windows using mingw32/64 */ +#ifndef __MINGW32__ + /* limitation of Linux usbdevio */ + if ((int)transfer_size > getpagesize()) { + transfer_size = getpagesize(); + printf("Limited transfer size to %i\n", transfer_size); + } +#endif /* __MINGW32__ */ +#endif /* HAVE_GETPAGESIZE */ + + if (transfer_size < dfu_root->bMaxPacketSize0) { + transfer_size = dfu_root->bMaxPacketSize0; + printf("Adjusted transfer size to %i\n", transfer_size); + } + + switch (mode) { + case MODE_UPLOAD: + /* open for "exclusive" writing */ + fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666); + if (fd < 0) + err(EX_IOERR, "Cannot open file %s for writing", file.name); + + if (dfuse_device || dfuse_options) { + if (dfuse_do_upload(dfu_root, transfer_size, fd, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_upload(dfu_root, transfer_size, + expected_size, fd) < 0) { + exit(1); + } + } + close(fd); + break; + + case MODE_DOWNLOAD: + if (((file.idVendor != 0xffff && file.idVendor != runtime_vendor) || + (file.idProduct != 0xffff && file.idProduct != runtime_product)) && + ((file.idVendor != 0xffff && file.idVendor != dfu_root->vendor) || + (file.idProduct != 0xffff && file.idProduct != dfu_root->product))) { + errx(EX_IOERR, "Error: File ID %04x:%04x does " + "not match device (%04x:%04x or %04x:%04x)", + file.idVendor, file.idProduct, + runtime_vendor, runtime_product, + dfu_root->vendor, dfu_root->product); + } + if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) { + if (dfuse_do_dnload(dfu_root, transfer_size, &file, + dfuse_options) < 0) + exit(1); + } else { + if (dfuload_do_dnload(dfu_root, transfer_size, &file) < 0) + exit(1); + } + break; + case MODE_DETACH: + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + warnx("can't detach"); + } + break; + default: + errx(EX_IOERR, "Unsupported mode: %u", mode); + break; + } + + if (final_reset) { + if (dfu_detach(dfu_root->dev_handle, dfu_root->interface, 1000) < 0) { + /* Even if detach failed, just carry on to leave the + device in a known state */ + warnx("can't detach"); + } + printf("Resetting USB to switch back to runtime mode\n"); + ret = libusb_reset_device(dfu_root->dev_handle); + if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) { + errx(EX_IOERR, "error resetting after download"); + } + } + + libusb_close(dfu_root->dev_handle); + dfu_root->dev_handle = NULL; + libusb_exit(ctx); + + return (0); +} diff --git a/tools/win/src/dfu-util/src/portable.h b/tools/win/src/dfu-util/src/portable.h new file mode 100644 index 0000000..cf8d5df --- /dev/null +++ b/tools/win/src/dfu-util/src/portable.h @@ -0,0 +1,72 @@ + +#ifndef PORTABLE_H +#define PORTABLE_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# define PACKAGE "dfu-util" +# define PACKAGE_VERSION "0.8-msvc" +# define PACKAGE_STRING "dfu-util 0.8-msvc" +# define PACKAGE_BUGREPORT "dfu-util@lists.gnumonks.org" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_FTRUNCATE +# include +#else +# include +#endif /* HAVE_FTRUNCATE */ + +#ifdef HAVE_NANOSLEEP +# include +# define milli_sleep(msec) do {\ + if (msec) {\ + struct timespec nanosleepDelay = { (msec) / 1000, ((msec) % 1000) * 1000000 };\ + nanosleep(&nanosleepDelay, NULL);\ + } } while (0) +#elif defined HAVE_WINDOWS_H +# define milli_sleep(msec) do {\ + if (msec) {\ + Sleep(msec);\ + } } while (0) +#else +# error "Can't get no sleep! Please report" +#endif /* HAVE_NANOSLEEP */ + +#ifdef HAVE_ERR +# include +#else +# include +# include +# define warnx(...) do {\ + fprintf(stderr, __VA_ARGS__);\ + fprintf(stderr, "\n"); } while (0) +# define errx(eval, ...) do {\ + warnx(__VA_ARGS__);\ + exit(eval); } while (0) +# define warn(...) do {\ + fprintf(stderr, "%s: ", strerror(errno));\ + warnx(__VA_ARGS__); } while (0) +# define err(eval, ...) do {\ + warn(__VA_ARGS__);\ + exit(eval); } while (0) +#endif /* HAVE_ERR */ + +#ifdef HAVE_SYSEXITS_H +# include +#else +# define EX_OK 0 /* successful termination */ +# define EX_USAGE 64 /* command line usage error */ +# define EX_SOFTWARE 70 /* internal software error */ +# define EX_IOERR 74 /* input/output error */ +#endif /* HAVE_SYSEXITS_H */ + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#ifndef off_t +# define off_t long int +#endif + +#endif /* PORTABLE_H */ diff --git a/tools/win/src/dfu-util/src/prefix.c b/tools/win/src/dfu-util/src/prefix.c new file mode 100644 index 0000000..be8e3fa --- /dev/null +++ b/tools/win/src/dfu-util/src/prefix.c @@ -0,0 +1,176 @@ +/* + * dfu-prefix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Uwe Bonnes + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-prefix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU prefix of \n" + " -D --delete \t\tDelete DFU prefix from \n" + " -a --add \t\tAdd DFU prefix to \n" + "In combination with -a:\n" + ); + fprintf(stderr, " -s --stellaris-address
Add TI Stellaris address prefix to \n" + "In combination with -D or -c:\n" + " -T --stellaris\t\tAct on TI Stellaris address prefix of \n" + "In combination with -a or -D or -c:\n" + " -L --lpc-prefix\t\tUse NXP LPC DFU prefix format\n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-prefix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2014 Uwe Bonnes\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "stellaris-address", 1, 0, 's' }, + { "stellaris", 0, 0, 'T' }, + { "LPC", 0, 0, 'L' }, +}; +int main(int argc, char **argv) +{ + struct dfu_file file; + enum mode mode = MODE_NONE; + enum prefix_type type = ZERO_PREFIX; + uint32_t lmdfu_flash_address = 0; + char *end; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:s:TL", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + case 's': + lmdfu_flash_address = strtoul(optarg, &end, 0); + if (*end) { + errx(EX_IOERR, "Invalid lmdfu " + "address: %s", optarg); + } + /* fall-through */ + case 'T': + type = LMDFU_PREFIX; + break; + case 'L': + type = LPCDFU_UNENCRYPTED_PREFIX; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + if (type == ZERO_PREFIX) + errx(EX_IOERR, "Prefix type must be specified"); + dfu_load_file(&file, MAYBE_SUFFIX, NO_PREFIX); + file.lmdfu_address = lmdfu_flash_address; + file.prefix_type = type; + printf("Adding prefix to file\n"); + dfu_store_file(&file, file.size.suffix != 0, 1); + break; + + case MODE_CHECK: + dfu_load_file(&file, MAYBE_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + break; + + case MODE_DEL: + dfu_load_file(&file, MAYBE_SUFFIX, NEEDS_PREFIX); + if (type > ZERO_PREFIX && file.prefix_type != type) + errx(EX_IOERR, "No prefix of requested type"); + printf("Removing prefix from file\n"); + /* if there was a suffix, rewrite it */ + dfu_store_file(&file, file.size.suffix != 0, 0); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/win/src/dfu-util/src/quirks.c b/tools/win/src/dfu-util/src/quirks.c new file mode 100644 index 0000000..de394a6 --- /dev/null +++ b/tools/win/src/dfu-util/src/quirks.c @@ -0,0 +1,56 @@ +/* + * Simple quirk system for dfu-util + * + * Copyright 2010-2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "quirks.h" + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice) +{ + uint16_t quirks = 0; + + /* Device returns bogus bwPollTimeout values */ + if ((vendor == VENDOR_OPENMOKO || vendor == VENDOR_FIC) && + product >= PRODUCT_FREERUNNER_FIRST && + product <= PRODUCT_FREERUNNER_LAST) + quirks |= QUIRK_POLLTIMEOUT; + + if (vendor == VENDOR_VOTI && + product == PRODUCT_OPENPCD) + quirks |= QUIRK_POLLTIMEOUT; + + /* Reports wrong DFU version in DFU descriptor */ + if (vendor == VENDOR_LEAFLABS && + product == PRODUCT_MAPLE3 && + bcdDevice == 0x0200) + quirks |= QUIRK_FORCE_DFU11; + + /* old devices(bcdDevice == 0) return bogus bwPollTimeout values */ + if (vendor == VENDOR_SIEMENS && + (product == PRODUCT_PXM40 || product == PRODUCT_PXM50) && + bcdDevice == 0) + quirks |= QUIRK_POLLTIMEOUT; + + /* M-Audio Transit returns bogus bwPollTimeout values */ + if (vendor == VENDOR_MIDIMAN && + product == PRODUCT_TRANSIT) + quirks |= QUIRK_POLLTIMEOUT; + + return (quirks); +} diff --git a/tools/win/src/dfu-util/src/quirks.h b/tools/win/src/dfu-util/src/quirks.h new file mode 100644 index 0000000..0e4b3ec --- /dev/null +++ b/tools/win/src/dfu-util/src/quirks.h @@ -0,0 +1,27 @@ +#ifndef DFU_QUIRKS_H +#define DFU_QUIRKS_H + +#define VENDOR_OPENMOKO 0x1d50 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_FIC 0x1457 /* Openmoko Freerunner / GTA02 */ +#define VENDOR_VOTI 0x16c0 /* OpenPCD Reader */ +#define VENDOR_LEAFLABS 0x1eaf /* Maple */ +#define VENDOR_SIEMENS 0x0908 /* Siemens AG */ +#define VENDOR_MIDIMAN 0x0763 /* Midiman */ + +#define PRODUCT_FREERUNNER_FIRST 0x5117 +#define PRODUCT_FREERUNNER_LAST 0x5126 +#define PRODUCT_OPENPCD 0x076b +#define PRODUCT_MAPLE3 0x0003 /* rev 3 and 5 */ +#define PRODUCT_PXM40 0x02c4 /* Siemens AG, PXM 40 */ +#define PRODUCT_PXM50 0x02c5 /* Siemens AG, PXM 50 */ +#define PRODUCT_TRANSIT 0x2806 /* M-Audio Transit (Midiman) */ + +#define QUIRK_POLLTIMEOUT (1<<0) +#define QUIRK_FORCE_DFU11 (1<<1) + +/* Fallback value, works for OpenMoko */ +#define DEFAULT_POLLTIMEOUT 5 + +uint16_t get_quirks(uint16_t vendor, uint16_t product, uint16_t bcdDevice); + +#endif /* DFU_QUIRKS_H */ diff --git a/tools/win/src/dfu-util/src/suffix.c b/tools/win/src/dfu-util/src/suffix.c new file mode 100644 index 0000000..0df248f --- /dev/null +++ b/tools/win/src/dfu-util/src/suffix.c @@ -0,0 +1,176 @@ +/* + * dfu-suffix + * + * Copyright 2011-2012 Stefan Schmidt + * Copyright 2013 Hans Petter Selasky + * Copyright 2014 Tormod Volden + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "portable.h" +#include "dfu_file.h" + +enum mode { + MODE_NONE, + MODE_ADD, + MODE_DEL, + MODE_CHECK +}; + +int verbose; + +static void help(void) +{ + fprintf(stderr, "Usage: dfu-suffix [options] ...\n" + " -h --help\t\t\tPrint this help message\n" + " -V --version\t\t\tPrint the version number\n" + " -c --check \t\tCheck DFU suffix of \n" + " -a --add \t\tAdd DFU suffix to \n" + " -D --delete \t\tDelete DFU suffix from \n" + " -p --pid \t\tAdd product ID into DFU suffix in \n" + " -v --vid \t\tAdd vendor ID into DFU suffix in \n" + " -d --did \t\tAdd device ID into DFU suffix in \n" + " -S --spec \t\tAdd DFU specification ID into DFU suffix in \n" + ); + exit(EX_USAGE); +} + +static void print_version(void) +{ + printf("dfu-suffix (%s) %s\n\n", PACKAGE, PACKAGE_VERSION); + printf("Copyright 2011-2012 Stefan Schmidt, 2013-2014 Tormod Volden\n" + "This program is Free Software and has ABSOLUTELY NO WARRANTY\n" + "Please report bugs to %s\n\n", PACKAGE_BUGREPORT); + +} + +static struct option opts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "check", 1, 0, 'c' }, + { "add", 1, 0, 'a' }, + { "delete", 1, 0, 'D' }, + { "pid", 1, 0, 'p' }, + { "vid", 1, 0, 'v' }, + { "did", 1, 0, 'd' }, + { "spec", 1, 0, 'S' }, +}; + +int main(int argc, char **argv) +{ + struct dfu_file file; + int pid, vid, did, spec; + enum mode mode = MODE_NONE; + + /* make sure all prints are flushed */ + setvbuf(stdout, NULL, _IONBF, 0); + + print_version(); + + pid = vid = did = 0xffff; + spec = 0x0100; /* Default to bcdDFU version 1.0 */ + memset(&file, 0, sizeof(file)); + + while (1) { + int c, option_index = 0; + c = getopt_long(argc, argv, "hVc:a:D:p:v:d:S:s:T", opts, + &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + help(); + break; + case 'V': + exit(0); + break; + case 'D': + file.name = optarg; + mode = MODE_DEL; + break; + case 'p': + pid = strtol(optarg, NULL, 16); + break; + case 'v': + vid = strtol(optarg, NULL, 16); + break; + case 'd': + did = strtol(optarg, NULL, 16); + break; + case 'S': + spec = strtol(optarg, NULL, 16); + break; + case 'c': + file.name = optarg; + mode = MODE_CHECK; + break; + case 'a': + file.name = optarg; + mode = MODE_ADD; + break; + default: + help(); + break; + } + } + + if (!file.name) { + fprintf(stderr, "You need to specify a filename\n"); + help(); + } + + if (spec != 0x0100 && spec != 0x011a) { + fprintf(stderr, "Only DFU specification 0x0100 and 0x011a supported\n"); + help(); + } + + switch(mode) { + case MODE_ADD: + dfu_load_file(&file, NO_SUFFIX, MAYBE_PREFIX); + file.idVendor = vid; + file.idProduct = pid; + file.bcdDevice = did; + file.bcdDFU = spec; + /* always write suffix, rewrite prefix if there was one */ + dfu_store_file(&file, 1, file.size.prefix != 0); + printf("Suffix successfully added to file\n"); + break; + + case MODE_CHECK: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + show_suffix_and_prefix(&file); + break; + + case MODE_DEL: + dfu_load_file(&file, NEEDS_SUFFIX, MAYBE_PREFIX); + dfu_store_file(&file, 0, file.size.prefix != 0); + if (file.size.suffix) /* had a suffix */ + printf("Suffix successfully removed from file\n"); + break; + + default: + help(); + break; + } + return (0); +} diff --git a/tools/win/src/dfu-util/src/usb_dfu.h b/tools/win/src/dfu-util/src/usb_dfu.h new file mode 100644 index 0000000..660bedc --- /dev/null +++ b/tools/win/src/dfu-util/src/usb_dfu.h @@ -0,0 +1,99 @@ +#ifndef USB_DFU_H +#define USB_DFU_H +/* USB Device Firmware Update Implementation for OpenPCD + * (C) 2006 by Harald Welte + * + * Protocol definitions for USB DFU + * + * This ought to be compliant to the USB DFU Spec 1.0 as available from + * http://www.usb.org/developers/devclass_docs/usbdfu10.pdf + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define USB_DT_DFU 0x21 + +#ifdef _MSC_VER +# pragma pack(push) +# pragma pack(1) +#endif /* _MSC_VER */ +struct usb_dfu_func_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bmAttributes; +#define USB_DFU_CAN_DOWNLOAD (1 << 0) +#define USB_DFU_CAN_UPLOAD (1 << 1) +#define USB_DFU_MANIFEST_TOL (1 << 2) +#define USB_DFU_WILL_DETACH (1 << 3) + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + uint16_t bcdDFUVersion; +#ifdef _MSC_VER +}; +# pragma pack(pop) +#elif defined __GNUC__ +} __attribute__ ((packed)); +#else + #warning "No way to pack struct on this compiler? This will break!" +#endif /* _MSC_VER */ + +#define USB_DT_DFU_SIZE 9 + +#define USB_TYPE_DFU (LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE) + +/* DFU class-specific requests (Section 3, DFU Rev 1.1) */ +#define USB_REQ_DFU_DETACH 0x00 +#define USB_REQ_DFU_DNLOAD 0x01 +#define USB_REQ_DFU_UPLOAD 0x02 +#define USB_REQ_DFU_GETSTATUS 0x03 +#define USB_REQ_DFU_CLRSTATUS 0x04 +#define USB_REQ_DFU_GETSTATE 0x05 +#define USB_REQ_DFU_ABORT 0x06 + +/* DFU_GETSTATUS bStatus values (Section 6.1.2, DFU Rev 1.1) */ +#define DFU_STATUS_OK 0x00 +#define DFU_STATUS_errTARGET 0x01 +#define DFU_STATUS_errFILE 0x02 +#define DFU_STATUS_errWRITE 0x03 +#define DFU_STATUS_errERASE 0x04 +#define DFU_STATUS_errCHECK_ERASED 0x05 +#define DFU_STATUS_errPROG 0x06 +#define DFU_STATUS_errVERIFY 0x07 +#define DFU_STATUS_errADDRESS 0x08 +#define DFU_STATUS_errNOTDONE 0x09 +#define DFU_STATUS_errFIRMWARE 0x0a +#define DFU_STATUS_errVENDOR 0x0b +#define DFU_STATUS_errUSBR 0x0c +#define DFU_STATUS_errPOR 0x0d +#define DFU_STATUS_errUNKNOWN 0x0e +#define DFU_STATUS_errSTALLEDPKT 0x0f + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RST = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10 +}; + +#endif /* USB_DFU_H */ diff --git a/tools/win/src/dfu-util/www/build.html b/tools/win/src/dfu-util/www/build.html new file mode 100644 index 0000000..f3036e4 --- /dev/null +++ b/tools/win/src/dfu-util/www/build.html @@ -0,0 +1,147 @@ + + + + + + + Building dfu-util from source + + + + + + + + + +
+

How to build dfu-util from source

+ +

Prerequisites for building from git

+

Mac OS X

+

+First install MacPorts (and if you are on 10.6 or older, the Java Developer Package) and then run: +

+
+	sudo port install libusb-devel git-core
+
+ +

FreeBSD

+
+	sudo pkg_add -r git pkgconf
+
+ +

Ubuntu and Debian and derivatives

+
+	sudo apt-get build-dep dfu-util
+	sudo apt-get install libusb-1.0-0-dev
+
+ +

Get the source code and build it

+

+The first time you will have to clone the git repository: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	cd dfu-util
+
+

+If you later want to update to latest git version, just run this: +

+
+	make maintainer-clean
+	git pull
+
+

+To build the source: +

+
+	./autogen.sh
+	./configure  # on most systems
+	make
+
+ +

+If you are building on Mac OS X, replace the ./configure command with: +

+
+	./configure --libdir=/opt/local/lib --includedir=/opt/local/include  # on MacOSX only
+
+ +

+Your dfu-util binary will be inside the src folder. Use it from there, or install it to /usr/local/bin by running "sudo make install". +

+ +

Cross-building for Windows

+ +

+Windows binaries can be built in a MinGW +environment, on a Windows computer or cross-hosted in another OS. +To build it on a Debian or Ubuntu host, first install build dependencies: +

+
+	sudo apt-get build-dep libusb-1.0-0 dfu-util
+	sudo apt-get install mingw32
+
+ +

+The below example builds dfu-util 0.8 and libusb 1.0.19 from unpacked release +tarballs. If you instead build from git, you will have to run "./autogen.sh" +before running the "./configure" steps. +

+ +
+mkdir -p build
+cd libusb-1.0.19
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+# WINVER workaround needed for 1.0.19 only
+make CFLAGS="-DWINVER=0x0501"
+make install
+cd ..
+
+cd dfu-util-0.8
+PKG_CONFIG_PATH=$PWD/../build/lib/pkgconfig ./configure \
+    --host=i586-mingw32msvc --prefix=$PWD/../build
+make
+make install
+cd ..
+
+The build files will now be in build/bin. +

+ +

Building on Windows using MinGW

+This assumes using release tarballs or having run ./autogen.sh on +the git sources. +
+cd libusb-1.0.19
+./configure --prefix=$HOME
+# WINVER workaround needed for 1.0.19 only
+# MKDIR_P setting should not really be needed...
+make CFLAGS="-DWINVER=0x0501" MKDIR_P="mkdir -p"
+make install
+cd ..
+
+cd dfu-util-0.8
+./configure USB_CFLAGS="-I$HOME/include/libusb-1.0" \
+            USB_LIBS="-L $HOME/lib -lusb-1.0" PKG_CONFIG=true
+make
+make install
+cd ..
+
+To link libusb statically into dfu-util.exe use instead of "make": +
+make LDFLAGS=-static
+
+The built executables (and DLL) will now be under $HOME/bin. + +

+[Back to dfu-util main page] +

+ +
+ + diff --git a/tools/win/src/dfu-util/www/dfu-util.1.html b/tools/win/src/dfu-util/www/dfu-util.1.html new file mode 100644 index 0000000..62ca40b --- /dev/null +++ b/tools/win/src/dfu-util/www/dfu-util.1.html @@ -0,0 +1,411 @@ + + +Man page of DFU-UTIL + + +

DFU-UTIL(1)

+ +  +

NAME

+ +dfu-util - Device firmware update (DFU) USB programmer +  +

SYNOPSIS

+ + +
+
+dfu-util + +-l + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + + +
+dfu-util + +[-v] + +[-d + +vid:pid[,vid:pid]] + +[-p + +path] + +[-c + +configuration] + +[-i + +interface] + +[-a + +alt-intf] + +[-S + +serial[,serial]] + +[-t + +size] + +[-Z + +size] + +[-s + +address] + +[-R] + +[-D|-U + +file] + + +
+dfu-util + +[-hV] + +
+  +

DESCRIPTION

+ +dfu-util + +is a program that implements the host (computer) side of the USB DFU +(Universal Serial Bus Device Firmware Upgrade) protocol. +

+dfu-util communicates with devices that implement the device side of the +USB DFU protocol, and is often used to upgrade the firmware of such +devices. +  +

OPTIONS

+ +
+
-l, --list + +
+List the currently attached DFU capable USB devices. +
-d, --device [Run-Time VENDOR]:[Run-Time PRODUCT][,[DFU Mode VENDOR]:[DFU Mode PRODUCT]] + +
+
+Specify run-time and/or DFU mode vendor and/or product IDs of the DFU device +to work with. VENDOR and PRODUCT are hexadecimal numbers (no prefix +needed), "*" (match any), or "-" (match nothing). By default, any DFU capable +device in either run-time or DFU mode will be considered. +

+If you only have one standards-compliant DFU device attached to your computer, +this parameter is optional. However, as soon as you have multiple DFU devices +connected, dfu-util will detect this and abort, asking you to specify which +device to use. +

+If only run-time IDs are specified (e.g. "--device 1457:51ab"), then in +addition to the specified run-time IDs, any DFU mode devices will also be +considered. This is beneficial to allow a DFU capable device to be found +again after a switch to DFU mode, since the vendor and/or product ID of a +device usually changes in DFU mode. +

+If only DFU mode IDs are specified (e.g. "--device ,951:26"), then all +run-time devices will be ignored, making it easy to target a specific device in +DFU mode. +

+If both run-time and DFU mode IDs are specified (e.g. "--device +1457:51ab,:2bc"), then unspecified DFU mode components will use the run-time +value specified. +

+Examples: +

+
--device 1457:51ab,951:26 + +
+
+ +Work with a device in run-time mode with +vendor ID 0x1457 and product ID 0x51ab, or in DFU mode with vendor ID 0x0951 +and product ID 0x0026 +

+

--device 1457:51ab,:2bc + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with vendor ID 0x1457 and product ID 0x02bc +

+

--device 1457:51ab + +
+
+ +Work with a device in run-time mode with vendor ID 0x1457 and product ID +0x51ab, or in DFU mode with any vendor and product ID +

+

--device ,951:26 + +
+
+ +Work with a device in DFU mode with vendor ID 0x0951 and product ID 0x0026 +

+

--device *,- + +
+
+ +Work with any device in run-time mode, and ignore any device in DFU mode +

+

--device , + +
+
+ +Ignore any device in run-time mode, and Work with any device in DFU mode +
+
+ +
-p, --path BUS-PORT. ... .PORT + +
+Specify the path to the DFU device. +
-c, --cfg CONFIG-NR + +
+Specify the configuration of the DFU device. Note that this is only used for matching, the configuration is not set by dfu-util. +
-i, --intf INTF-NR + +
+Specify the DFU interface number. +
-a, --alt ALT + +
+Specify the altsetting of the DFU interface by name or by number. +
-S, --serial [Run-Time SERIAL][,[DFU Mode SERIAL]] + +
+Specify the run-time and DFU mode serial numbers used to further restrict +device matches. If multiple, identical DFU devices are simultaneously +connected to a system then vendor and product ID will be insufficient for +targeting a single device. In this situation, it may be possible to use this +parameter to specify a serial number which also must match. +

+If only a single serial number is specified, then the same serial number is +used in both run-time and DFU mode. An empty serial number will match any +serial number in the corresponding mode. +

-t, --transfer-size SIZE + +
+Specify the number of bytes per USB transfer. The optimal value is +usually determined automatically so this option is rarely useful. If +you need to use this option for a device, please report it as a bug. +
-Z, --upload-size SIZE + +
+Specify the expected upload size, in bytes. +
-U, --upload FILE + +
+Read firmware from device into +FILE. + +
-D, --download FILE + +
+Write firmware from +FILE + +into device. +
-R, --reset + +
+Issue USB reset signalling after upload or download has finished. +
-s, --dfuse-address address + +
+Specify target address for raw binary download/upload on DfuSe devices. Do +not + +use this for downloading DfuSe (.dfu) files. Modifiers can be added +to the address, separated by a colon, to perform special DfuSE commands such +as "leave" DFU mode, "unprotect" and "mass-erase" flash memory. +
-v, --verbose + +
+Print more information about dfu-util's operation. A second +-v + +will turn on verbose logging of USB requests. Repeat this option to further +increase verbosity. +
-h, --help + +
+Show a help text and exit. +
-V, --version + +
+Show version information and exit. +
+  +

EXAMPLES

+ +  +

Using dfu-util in the OpenMoko project

+ +(with the Neo1973 hardware) +

+ +Flashing the rootfs: +
+ + $ dfu-util -a rootfs -R -D /path/to/openmoko-devel-image.jffs2 + +

+ +Flashing the kernel: +
+ + $ dfu-util -a kernel -R -D /path/to/uImage + +

+ +Flashing the bootloader: +
+ + $ dfu-util -a u-boot -R -D /path/to/u-boot.bin + +

+ +Copying a kernel into RAM: +
+ + $ dfu-util -a 0 -R -D /path/to/uImage + +

+Once this has finished, the kernel will be available at the default load +address of 0x32000000 in Neo1973 RAM. +Note: + +You cannot transfer more than 2MB of data into RAM using this method. +

+  +

Using dfu-util with a DfuSe device

+ +

+ +Flashing a +.dfu + +(special DfuSe format) file to the device: +
+ + $ dfu-util -a 0 -D /path/to/dfuse-image.dfu + +

+ +Reading out 1 KB of flash starting at address 0x8000000: +
+ + $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin + +

+ +Flashing a binary file to address 0x8004000 of device memory and +ask the device to leave DFU mode: +
+ + $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin + + +  +

BUGS

+ +Please report any bugs to the dfu-util mailing list at +dfu-util@lists.gnumonks.org. + +Please use the +--verbose option (repeated as necessary) to provide more + +information in your bug report. +  +

SEE ALSO

+ +The dfu-util home page is +http://dfu-util.gnumonks.org + +  +

HISTORY

+ +dfu-util was originally written for the OpenMoko project by +Weston Schmidt <weston_schmidt@yahoo.com> and +Harald Welte <hwelte@hmw-consulting.de>. Over time, nearly complete +support of DFU 1.0, DFU 1.1 and DfuSe ("1.1a") has been added. +  +

LICENCE

+ +dfu-util + +is covered by the GNU General Public License (GPL), version 2 or later. +  +

COPYRIGHT

+ +This manual page was originally written by Uwe Hermann <uwe@hermann-uwe.de>, +and is now part of the dfu-util project. +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
OPTIONS
+
EXAMPLES
+
+
Using dfu-util in the OpenMoko project
+
Using dfu-util with a DfuSe device
+
+
BUGS
+
SEE ALSO
+
HISTORY
+
LICENCE
+
COPYRIGHT
+
+
+This document was created by man2html, +using the doc/dfu-util.1 manual page from dfu-util 0.8.
+Time: 14:40:57 GMT, September 13, 2014 + + diff --git a/tools/win/src/dfu-util/www/dfuse.html b/tools/win/src/dfu-util/www/dfuse.html new file mode 100644 index 0000000..35e4ffa --- /dev/null +++ b/tools/win/src/dfu-util/www/dfuse.html @@ -0,0 +1,135 @@ + + + + + + DfuSe and dfu-util + + + + + + + + + +
+

Using dfu-util with DfuSe devices

+

DfuSe

+

+ DfuSe (DFU with ST Microsystems extensions) is a protocol based on + DFU 1.1. However, in expanding the functionality of the DFU protocol, + ST Microsystems broke all compatibility with the DFU 1.1 standard. + DfuSe devices report the DFU version as "1.1a". +

+

+ DfuSe can be used to download firmware and other data + from a host computer to a conforming device (or upload in the + opposite direction) over USB similar to standard DFU. +

+

+ The main difference from standard DFU is that the target address in + the device (flash) memory is specified by the host, so that a + download can be performed to parts of the device memory. The host + program is also responsible for erasing flash pages before they + are written to. +

+

.dfu files

+

+ A special file format is defined by ST Microsystems to carry firmware + for DfuSe devices. The file contains target information such as address + and alternate interface information in addition to the binary data. + Several blocks of binary data can be combined in one .dfu file. +

+

Alternate interfaces

+

+ Different memory locations of the device may have different + characteristics that the host program (dfu-util) has to take + into considerations, such as flash memory page size, read-only + versus read-write segments, the need to erase, and so on. + These parameters are reported by the device in the string + descriptors meant for describing the USB interfaces. + The host program decodes these strings to build a memory map of + the device. Different memory units or address spaces are listed + in separate alternate interface settings that must be selected + according to the memory unit to access. +

+

+ Note that dfu-util leaves it to the user to select alternate + interface. When parsing a .dfu file it will skip file segments + not matching the selected alternate interface. Also, some + DfuSe device firmware implementations ignore the setting of + alternate interface and deduct the memory unit from the + address, since they have no address space overlap. +

+

DfuSe special commands

+

+ DfuSe special commands are used by the host program during normal + downloads or uploads, such as SET_ADDRESS and ERASE_PAGE. Also + the normal DFU_DNLOAD and DFU_UPLOAD commands have special + implementations in DfuSe. + Many DfuSe devices also support commands to leave DFU mode, + read unprotect the flash memory or mass erase the flash memory. + dfu-util (from version 0.7) + supports adding "leave", "unprotect", or "mass-erase" + to the -s option argument to send such requests in combination + with a download request. These modifiers are separated with a colon. +

+

+ Some DfuSe devices have their DfuSe bootloader running from flash + memory. Erasing the whole flash memory would therefore destroy + the DfuSe bootloader itself and practically brick the device + for most users. Any use of modifiers such as "unprotect" + and "mass-erase" therefore needs to be combined with the "force" + modifer. This is not included in the examples, to not encourage + ignorant users to copy and paste such instructions and shoot + themselves in the foot. +

+

+ Devices based on for instance STM32F103 all run the bootloader + from flash, since there is no USB bootloader in ROM. +

+

+ For instance STM32F107, STM32F2xx and STM32F4xx devices have a + DfuSe bootloader in ROM, so the flash can be erased while + keeping the device available for USB DFU transfers as long + as the device designers use this built-in bootloader and have + not implemented another DfuSe bootloader in flash that the user is + dependent upon. +

+

+ Well-written bootloaders running from flash will report their + own memory region as read-only and not eraseable, but this does + not prevent dfu-util from sending a "unprotect" or "mass-erase" + request which overrides this, if the user insists. +

+

Example usage

+

+ Flashing a .dfu (special DfuSe format) file to the device: +

+
+         $ dfu-util -a 0 -D /path/to/dfuse-image.dfu
+	
+

+ Reading out 1 KB of flash starting at address 0x8000000: +

+
+         $ dfu-util -a 0 -s 0x08000000:1024 -U newfile.bin
+	
+

+ Flashing a binary file to address 0x8004000 of device memory and ask + the device to leave DFU mode: +

+
+         $ dfu-util -a 0 -s 0x08004000:leave -D /path/to/image.bin
+	
+

+ [Back to dfu-util main page] +

+ +
+ + diff --git a/tools/win/src/dfu-util/www/index.html b/tools/win/src/dfu-util/www/index.html new file mode 100644 index 0000000..108ddaf --- /dev/null +++ b/tools/win/src/dfu-util/www/index.html @@ -0,0 +1,119 @@ + + + + + + dfu-util Homepage + + + + + + + + + +
+

dfu-util - Device Firmware Upgrade Utilities

+

Description

+

+ dfu-util is a host side implementation of the DFU 1.0 and DFU 1.1 specifications of the USB forum. + + DFU is intended to download and upload firmware to/from devices connected + over USB. It ranges from small devices like micro-controller boards + to mobile phones. Using dfu-util you can download firmware to your + DFU-enabled device or upload firmware from it. dfu-util has been + tested with the Openmoko Neo1973 and Freerunner and many other devices. +

+

+ See the manual page for examples of use. +

+

Supported Devices

+ +

Releases

+

+ Releases of the dfu-util software can be found in the + releases folder. + The current release is 0.8. +

+

+ We offer binaries for Microsoft Windows and some other platforms. + dfu-util uses libusb 1.0 to access your device, so + on Windows you have to register the device with the WinUSB driver + (alternatively libusb-win32 or libusbK), please see the libusbx wiki + for more details. +

+

+ Mac OS X users can also get dfu-util from Homebrew with "brew install dfu-util" or from MacPorts. +

+

+ Most Linux distributions ship dfu-util in binary packages for those + who do not want to compile dfu-util from source. + On Debian, Ubuntu, Fedora and Gentoo you can install it through the + normal software package tools. For other distributions +(namely OpenSuSe, Mandriva, and CentOS) Holger Freyther was kind enough to +provide binary packages through the Open Build Service. +

+

Development

+

+ Development happens in a GIT repository. Browse it via the web +interface or clone it with: +

+
+	git clone git://gitorious.org/dfu-util/dfu-util.git
+	
+

+ See our build instructions for how to + build the source on different platforms. +

+

License

+

+ This software is licensed under the GPL version 2. +

+

Contact

+

+ If you have questions about the development or use of dfu-util please + send an e-mail to our dedicated +mailing list for dfu-util. +

+

People

+

+ dfu-util was originally written by + Harald Welte partially based on code from + dfu-programmer 0.4 and is currently maintained by Stefan Schmidt and + Tormod Volden. +

+ +
+ + diff --git a/tools/win/src/dfu-util/www/simple.css b/tools/win/src/dfu-util/www/simple.css new file mode 100644 index 0000000..98100bc --- /dev/null +++ b/tools/win/src/dfu-util/www/simple.css @@ -0,0 +1,56 @@ +body { + margin: 10px; + font-size: 0.82em; + background-color: #EEE; +} + +h1 { + clear: both; + padding: 0 0 12px 0; + margin: 0; + font-size: 2em; + font-weight: bold; +} + +h2 { + clear: both; + margin: 0; + font-size: 1.5em; + font-weight: normal; +} + +h3 { + clear: both; + margin: 15px 0 0 0; + font-size: 1.0em; + font-weight: bold; +} + +p { + line-height: 20px; + padding: 8px 0 8px 0; + margin: 0; + font-size: 1.1em; +} + +pre { + white-space: pre-wrap; + background-color: #CCC; + padding: 3px; +} + +a:hover { + background-color: #DDD; +} + +#middlebox { + width: 600px; + margin: 0px auto; + text-align: left; +} + +#footer { + height: 100px; + padding: 28px 3px 0 0; + margin: 20px 0 20px 0; +} diff --git a/tools/win/src/maple_loader/README.md b/tools/win/src/maple_loader/README.md new file mode 100644 index 0000000..c6c9379 --- /dev/null +++ b/tools/win/src/maple_loader/README.md @@ -0,0 +1,5 @@ +These files build the maple_loader.jar file used on Windows to reset the Sketch via USB Serial, so that the bootloader will run in dfu upload mode, ready for a new sketch to be uploaded + +The files were written by @bobC (github) and have been slightly modified by me (Roger Clark), so that dfu-util no longer attempts to reset the board after upload. +This change to dfu-util's reset command line argument, was required because dfu-util was showing errors on some Windows systems, because the bootloader had reset its self after upload, +before dfu-util had chance to tell it to reset. \ No newline at end of file diff --git a/tools/win/src/maple_loader/build.xml b/tools/win/src/maple_loader/build.xml new file mode 100644 index 0000000..80bdd6f --- /dev/null +++ b/tools/win/src/maple_loader/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project maple_loader. + + + diff --git a/tools/win/src/maple_loader/build/built-jar.properties b/tools/win/src/maple_loader/build/built-jar.properties new file mode 100644 index 0000000..10752d5 --- /dev/null +++ b/tools/win/src/maple_loader/build/built-jar.properties @@ -0,0 +1,4 @@ +#Mon, 20 Jul 2015 11:21:26 +1000 + + +C\:\\Users\\rclark\\Desktop\\maple-asp-master\\installer\\maple_loader= diff --git a/tools/win/src/maple_loader/build/classes/CliTemplate/CliMain.class b/tools/win/src/maple_loader/build/classes/CliTemplate/CliMain.class new file mode 100644 index 0000000000000000000000000000000000000000..37ee63000b20df7528829288b7f0fbeb28eb3b98 GIT binary patch literal 1753 zcmZ`(?^7F96g^8e*(Ke!Bot_BEkSJ?Qc5V;R-jfb6dMgcG>{5a@g;erOE=l=X2Z~N zI-?()v44qQZ9mYCj{X7uQR;cgB4LP`%)9&UJMY|k&%L{U{r%gY0A}FDk-}_0<}h#K zP8=WNu8EK0_!ysNj62~BB)v{_}O_60{U^nDSt6rA=x+pvJBnxgu zI3?lRY9BiF;IZA*FjQD@>~&f991+MIpO=N*m?v4CR>N@SQDIl?iJT)EwOldq?M7{0 z1=Gko)OB=!zaqWBcH4J_UnBKkkE}dUI6;1gw$&`>7YQ%hjU`)E zajLr0+VC7#RHd&Wbu912rH#WQ=6<>uh{}^?;k8ROkTtMLX(_0Nc+qY771^P*!g?oF zA{H)T)WDX7NBCMpqAol~Zg=9P_ogOq82HA*x7cPXO-U`CNso0H_|AernPCsT6gqfD ziB{N*&{q|KDBD5PLIqWlAG&3^RonI{{SmYoxSoP)usq+b`J%ok9YH>;)vkUrROWE2 z1fx)i@Km${ciWdu8CZ~@l4rt$J?WR-CIgLhcf3nE3pG4u+m5>OUsEmEN|O=MNK4;$ z{j^)Dw0vJy$I{IU`M8_2<0+8CF&@gCoHaGY7pQPWHk+L4obWud${p#; z27L@k4K-ZtakV#DocMerkTo@*!#%F$RY--p=emv*?bquh)s{kq%`Mr24f>z_x0oWk zLCTs8mRW_UvAgFo<0m=Ux^{yCxgX?0=#uN#1q+Prgb%YlQtp0*JB^+1Ngr)XjWN8< zkD&VXK~wJ&*EDxIT8%cEeGTnp`*@6NtnD1eJAAe}0KAL$xa-GdT%jioXby1(eJ`?+ z=|WbYUd~3RSF*9`IsFy-l92=G$>_B=h$W4rsZ9R>O+0xjarp;jEnTgr6Z)^pp1H0d zXD8G;z>`EW8R7an1~*^h+*~vjP3Z?1diV=2&c$9~Bza!{1F6kODt3Si#W#5C02f~} zu__!6^)Mo=87N_h>m}%{*ksZcy$P0*Vi}_>IX literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/CliTemplate/DFUUploader.class b/tools/win/src/maple_loader/build/classes/CliTemplate/DFUUploader.class new file mode 100644 index 0000000000000000000000000000000000000000..77087b052fbd03f77b9e1417f2754196beaf1844 GIT binary patch literal 7476 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 literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/CliTemplate/ExecCommand.class b/tools/win/src/maple_loader/build/classes/CliTemplate/ExecCommand.class new file mode 100644 index 0000000000000000000000000000000000000000..ad95f79845d3d1b0c1d2245d06800c06c37e8c08 GIT binary patch literal 3243 zcma)8TW}NS75;u{rC%0{F$RGJfhdV%e8DI8>iD5`@f$5 zobR0TowIuL-k)v*cpTT`Xu((=I*^sf#Sz9>9Ca|&Fun>Cm{fx$aYoHgOYE&f9t8WB885cO{+|kW;2R=9EkUaYA6t zxaF3-gRbkihn%vV6KE)zS;x-R9@UxCS+nR_jxEqKP;{NFSt?ofMA|48(>ZgjJdr+9 zwr$frxFEMnAiCSKEw5J~+}d_bK6Ts5zpt3hVZm@qR|ani9V8zv@!I)W%#c?OJOT$+=a;zC}UmqFTH zV?2m#49>}jGqW>Tz-F`Y~^#NtK+Bm8Ih%QT*6BdKiBapPV4xEf|14!9gks$#A`a{ z@XG~EOL@W!{XfN|<5vp$<4XIl@r1y`E52HiR*4sN{06^O&o1luom%}~A^U^!dF!H2 zD^XI%PIT$`gmS;(++*icuIZIs+sviT?L5CEVnbtR%&aGIMaNbAQQ~zS*HBSGI4w|b zPFr3o%ewDLQ42%)KGpnwaR@x>bL>*NV3MQti;1n4$l66m0`9Y3S?L9V^{s99bBPti zYM$^W{kFu44jVbv=n~phecIK=?x>ZlXGk{bIX;gBT9@>f1N} z$}HW*mNCrshI|tYLYF9FnPqJE{mK z6v5<_kuRG=8sTcI_HMSA5xSb;dhj19djx zgIwuVNxpfKJAoeTq6D>sPebyj48*Bh2#;QeHrR2v^LDou?v5lP3GEWLbtWQRJ<(*e zf=G3W&O+awjNXI{;rNA!n4AB{_U$(j6Lz106kD zQoG$933o>m(M04D+LKx$+SMbkLcfL8v|OK*6|8A&;84NZ3K}m&srv7{uV7t!_!`!$ zA<=OI8+x=kZ0?ENLepq6QbF=MHdUc*PDbzC!ULm8t%5BK%T^^+-9G4VqguC2$XRSv z?^4RCl#+d@H{31f=Ks|bo16b95kK-*#ME?Wi}orWyo%<=MqhnX3br`OQNu>~<~ z!&bE7QSLj@&bw{cO->APH$n~^B_ocrNuMSU&XNPqbG}F>yu$2WVpiuUeHr^{VGsLq zFEg+gf5ATd4SjeEAI97GDBeLo-o?l9Zw%lr25}EVLSR_bVN^8Wgh=3&*u*y84MX%` zOk|K1Pa!8xV%+D@n^;ebO|-NJQ8KKHcJ`8!^~AT2wl%CJmk)3iA;wL#yocN}=!=rm zsAZ=w7lFkWN-j#AF{10@>MGfK5Qh*EQAXq=#2X`m>TOK%GvKaT^-~|apcui&=nq*a zBJ^oLv0UaiZxDxR?JYdV=nWIg+nC~)ZJ0LRK?z4_TOvanswS#BMyTT`PmW;CJe`P2 z9FtfjahxMVgyy@5G|gijx0-$0j#Q*>R literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/processing/app/Base.class b/tools/win/src/maple_loader/build/classes/processing/app/Base.class new file mode 100644 index 0000000000000000000000000000000000000000..4aa0bde02049fbb6c4e035d952edc3f002cba1e1 GIT binary patch literal 639 zcmaKp%TB^T6o&tSLZKAVg7^Ck!G)N(aDg#K6B84>Bp6NH4Q13pXq&cpc^a2)+_+KW z!Uyo7jHgH%(vSU{TL#iY#anNj3gnP^@bs|Q|HhKMoyE2H|Q1LIN$1qrOT-y|$C#{PD zcbvjD_e7B)F1=G}RqY$fA^jOcvg~v7YM(o8fhs+!)VyWT%68K=#jXsB#RI`gjbC#e zacB&x?X6h6Aymxhn8h5Cwpqozjs+|-MB1iwk7gM916!fasl&rcO_+WcD&Kj&XfPx$ zgn#7Pj&S`uhHS3%^Q!E-G+r_P8+DsnY1AJLE^ZIc>Fe~e?9mh|{hDBfK@&rxJwZ`{ zPDW>B`33BWE+Oc&M_X_PNxJLb3Ft$LqB#07KqPp)PT9fA7@=2$4doe;&^=V;0ZRCh ec&BR#29XFt##T^2{g)*ApJcUD!jNgz82kc&eSc^G literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/processing/app/Preferences.class b/tools/win/src/maple_loader/build/classes/processing/app/Preferences.class new file mode 100644 index 0000000000000000000000000000000000000000..89cf0100455af1f41500150e545c8bbd12583674 GIT binary patch literal 2215 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 L#S!xqDulu}Wzd50 literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class b/tools/win/src/maple_loader/build/classes/processing/app/debug/MessageConsumer.class new file mode 100644 index 0000000000000000000000000000000000000000..37250e7704487822639cf03f4e4a333da994be50 GIT binary patch literal 174 zcmX^0Z`VEs1_omWPId-%b_Nbc2KL<4;^M^gR7M6-4WF#UvPAuy#JqI<;F6-uymV{L zFh&Nh;QZ2}36TD<=a zee$u-T}#4J*3vf~eDmkHmbdTB3`x3-T|Q)=efH(-y}z^fnf&_KPd@_~$Jc3`K|#YE z4ezEAM=^~U<~s2b<`r2`3FpOF@DY|1aeE`vQp>rz{0}ik26Y+<5P_3@WA4aH*G_Nr)yS)eXYYsjn$=fJOWF4tYZhC37l!#w}YMI5E!%pT%fP% z+dxal=PHf9X`?)_MwAB1vTAyk>pLES*6{_t)bW){_Z9doX zp*cQw*xW5Rg|$DEB4^Hd92&M1T~J--)m&ee8#S=#_5-Loy058<-UTH+9!Y~sI?QMy zn1HJE?|PQwSw7)TWD82zQS}XvWu(;85YEQY?D>`vXiP_vZ;Q0~d|YUa5UKy|5#2V& zD&;$eAPhYZ*KvUzx$BPrxE*8^HuS-Abd74{xS?(;X{fBvh&yULy=i|kxTlNf7 z;w+!=GZ1(g31n24^DB^1jm|Zu+BsO+^IQ|O%i=s~j=OJ>nhpw19x}OH;50efeMVe!jVSW-ua2gkw zO`dcdm+%Iz;Y~`x9D}Hok8;QOw0WI5DNf}bBYzQNVV2?>S|_O6VZ79L80C_z<4CD^ zfI;%wK6*-f=pC-(g-nK|j=mppG=?XC|CSTX_X(zo5p(&F87o^tr z9?2)rD19-q?V1mm3%@)Ksv5%;9Ag$^m_dKA7Fna(JET`I5v((~5v)b52P@x^V2u+I z;12HvxQ;$d({BZrQKIK8eOE9=eg?Pczk&k!BCY2zk3}rN#66z3f(qph*T;Ae;5fm_32#8&VW5Z_DWZjif-x5zCr=eMcl9{`-9>GD2Q?oaJ}>;w=qVZ)>>5-{ha@ zC)IHMcf={5(&)lhg-58CS>Z0m^@zIs;_yKL*=f}RVTnhAt9^(L80hAo19jc~p+^+7 zg8`@6lwrath(FM+v2>NRYR{Ye%`#rtqAGQjaxxcZ8#Y@_{ZRoV0mQotV5n6@E2k~%3N36at+NEB6-iZpQsd%&@c zA5A4Tuz*j%h6Rru8x|m8Sz^Hl;6tJQ&z#FqWkoT zMK-PSux61%52o=};bEPJ4IUoy@DZt?IbKWPfebnC6t{hn9-@}T2Yu_~Sl zGS`A;(5nc_7S3xI4+ZI)kPtM>f{*(rM}FtPJ8DRlR@G}9dYyph>Z9JP2VFsL?zKCu zDyTtN_S)_8v47OBl|St_n|^2OMb&Tjf>smk3khDIvfC>O3%k1#=<|Tx>kS#<)kHgK zXjWC=*OG0dpELW%Wb8=5OE#92@hTEq^Lvj*RhcgoV`foZtZvN4!3NHxs#B^^M5P>9 z#6Ic;$!!E-EuFg8jjdt}>NO(^_{eLkgxg9tthcQM)l3P5vl+EEYKIxMb2NuOg3i=~ zV?W*`vu(6lFaE{aZ}mG>e_NJeexjyJ+@KC+DaXSt9#$Nhp^QVbbjhJtc(}~N9L@9U zjzjO$ilE#7r(*=ojSoOn4?$!Z7 z;WTsKk=&3Rg9MWTqKiRx`z2eXhyHnb=qBD$(nntT@ZiJ1n(dZyi&;I%$qF{UC!H*ZrjZjn!nbMU#8yrqi8@^Z z7ANbLa`yv3Ot{OtLrS@&0qJgTK!&?GAk)q21C%6d3}|Yc!W&3B(JFM-5bOg~yMk1$ zgJ%Pt9!joCs2OqPBd&bJm5;dciVGAn>4qeHT@mhpkduXk3~Aa0QVb>|>$in8Y$GW< z(s|e;cI4cq+w=y+aADG0O0+EJXju0z(*J;Z*ls316FrtPj(j(ijG#i)trbON%6yG8 z6E^EUU=Cs+W&~nJ&00h|hn*h>F@B!_d6EcHqPNxNG01h)Y!c)dKt8(wXIWW>;Zp|t)R=_w{k$&(nS<|sy#QGvY!8-p}wg9Yy*v>)mJ{n9+<-P@KEO7OK70uz15(}q literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class b/tools/win/src/maple_loader/build/classes/processing/app/helpers/ProcessUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..27eca62622c61bc21ae557714ca4fa2d4c1eb22b GIT binary patch literal 1399 zcmah}Sy$6Q7`@XLQbO2@A}XTd)|Mr1C|W=TH?$PBD#zs|q@zUJBqpiLyT8ImUwqXU z+>ZPJe~HJR;5jZgC15~3=A6vTcjucs_gj+BKRzr2ID)Gxx-g_-7$Yil91Mp;3J#}n zB#omgnlP&1n2Hu04~MZdPNZ?N6$Olk_Jo2-1*Zg>$1U3mCIyn&+)aVhv{R7+9YxEQ zrFv~ade@DGDv?alF^%d?!?Qv=0#m`F^CTYS6zNcsy3f#h~aIPY8?ejDTV~wVGj91iJrnHr%gOLdgON zcR^s=pJY>Yt6GsBIg`?U97foXP^UC~)^LgUtdH=SyBg)_de`w7c_jW%WRiq!<-jl> zlDv^E1^oi*xu>Rd1Iw{}1*d73ow{er^H!MRu6VjeLU|f8=vHt>!xW|!oYim+=QV7_ zHi6EKDP*Pw82{Y?4Hs}xp!c^!L>%QHWS!A)371*^>01}(XG)~Aor>e%qmUrTHOw;8oku}GFUSD$z#b&AmmXn=E66?~`-!w6+5R1hf&=0k Rugelj(J99_&vgQWzW|+SRY?E< literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/dist/README.TXT b/tools/win/src/maple_loader/dist/README.TXT new file mode 100644 index 0000000..255b89c --- /dev/null +++ b/tools/win/src/maple_loader/dist/README.TXT @@ -0,0 +1,32 @@ +======================== +BUILD OUTPUT DESCRIPTION +======================== + +When you build an Java application project that has a main class, the IDE +automatically copies all of the JAR +files on the projects classpath to your projects dist/lib folder. The IDE +also adds each of the JAR files to the Class-Path element in the application +JAR files manifest file (MANIFEST.MF). + +To run the project from the command line, go to the dist folder and +type the following: + +java -jar "maple_loader.jar" + +To distribute this project, zip up the dist folder (including the lib folder) +and distribute the ZIP file. + +Notes: + +* If two JAR files on the project classpath have the same name, only the first +JAR file is copied to the lib folder. +* Only JAR files are copied to the lib folder. +If the classpath contains other types of files or folders, these files (folders) +are not copied. +* If a library on the projects classpath also has a Class-Path element +specified in the manifest,the content of the Class-Path element has to be on +the projects runtime path. +* To set a main class in a standard Java project, right-click the project node +in the Projects window and choose Properties. Then click Run and enter the +class name in the Main Class field. Alternatively, you can manually type the +class name in the manifest Main-Class element. diff --git a/tools/win/src/maple_loader/dist/lib/jssc.jar b/tools/win/src/maple_loader/dist/lib/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 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 @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/dist/maple_loader.jar b/tools/win/src/maple_loader/dist/maple_loader.jar new file mode 100644 index 0000000000000000000000000000000000000000..e1f9965c1ff0fe14384717ea4559c7f59e9a1ca6 GIT binary patch literal 32791 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 VEwG5^!#_X%TL6{zhrDw%{eMvMy}$qf literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/jars/jssc.jar b/tools/win/src/maple_loader/jars/jssc.jar new file mode 100644 index 0000000000000000000000000000000000000000..eb74f154a01c8c16712233cca36b65120ea47986 GIT binary patch literal 152418 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 @v>QJza{i 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�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 literal 0 HcmV?d00001 diff --git a/tools/win/src/maple_loader/manifest.mf b/tools/win/src/maple_loader/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/tools/win/src/maple_loader/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/win/src/maple_loader/nbproject/build-impl.xml b/tools/win/src/maple_loader/nbproject/build-impl.xml new file mode 100644 index 0000000..a66f349 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/build-impl.xml @@ -0,0 +1,1413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/win/src/maple_loader/nbproject/genfiles.properties b/tools/win/src/maple_loader/nbproject/genfiles.properties new file mode 100644 index 0000000..c136721 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=2e6a03ba +build.xml.script.CRC32=4676ee6b +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=2e6a03ba +nbproject/build-impl.xml.script.CRC32=392b3f79 +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/win/src/maple_loader/nbproject/private/config.properties b/tools/win/src/maple_loader/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/tools/win/src/maple_loader/nbproject/private/private.properties b/tools/win/src/maple_loader/nbproject/private/private.properties new file mode 100644 index 0000000..e5c9f10 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\rclark\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties diff --git a/tools/win/src/maple_loader/nbproject/private/private.xml b/tools/win/src/maple_loader/nbproject/private/private.xml new file mode 100644 index 0000000..a1bbd60 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/private/private.xml @@ -0,0 +1,10 @@ + + + + + + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/CliMain.java + file:/C:/Users/rclark/Desktop/maple-asp-master/installer/maple_loader/src/CliTemplate/DFUUploader.java + + + diff --git a/tools/win/src/maple_loader/nbproject/project.properties b/tools/win/src/maple_loader/nbproject/project.properties new file mode 100644 index 0000000..7f48d71 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/project.properties @@ -0,0 +1,79 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=maple_loader +application.vendor=bob +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/maple_loader.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jssc.jar=dist/lib/jssc.jar +file.reference.jssc.jar-1=jars/jssc.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.jssc.jar}:\ + ${file.reference.jssc.jar-1} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=CliTemplate.CliMain +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/win/src/maple_loader/nbproject/project.xml b/tools/win/src/maple_loader/nbproject/project.xml new file mode 100644 index 0000000..92218a9 --- /dev/null +++ b/tools/win/src/maple_loader/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + maple_loader + + + + + + + + + diff --git a/tools/win/src/maple_loader/src/CliTemplate/CliMain.java b/tools/win/src/maple_loader/src/CliTemplate/CliMain.java new file mode 100644 index 0000000..c7dc9f0 --- /dev/null +++ b/tools/win/src/maple_loader/src/CliTemplate/CliMain.java @@ -0,0 +1,60 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import processing.app.Preferences; + +//import processing.app.I18n; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class CliMain { + + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + + String comPort = args[0]; // + String altIf = args[1]; // + String usbID = args[2]; // "1EAF:0003"; + String binFile = args[3]; // bin file + + System.out.println("maple_loader v0.1"); + + Preferences.set ("serial.port",comPort); + Preferences.set ("serial.parity","N"); + Preferences.setInteger ("serial.databits", 8); + Preferences.setInteger ("serial.debug_rate",9600); + Preferences.setInteger ("serial.stopbits",1); + + Preferences.setInteger ("programDelay",1200); + + Preferences.set ("upload.usbID", usbID); + Preferences.set ("upload.altID", altIf); + Preferences.setBoolean ("upload.auto_reset", true); + Preferences.setBoolean ("upload.verbose", false); + + // + DFUUploader dfuUploader = new DFUUploader(); + try { + //dfuUploader.uploadViaDFU(binFile); + dfuUploader.uploadViaDFU(binFile); + } catch (Exception e) + { + System.err.print (MessageFormat.format("an error occurred! {0}\n", e.getMessage())); + } + } +} diff --git a/tools/win/src/maple_loader/src/CliTemplate/DFUUploader.java b/tools/win/src/maple_loader/src/CliTemplate/DFUUploader.java new file mode 100644 index 0000000..3dee0b4 --- /dev/null +++ b/tools/win/src/maple_loader/src/CliTemplate/DFUUploader.java @@ -0,0 +1,345 @@ +/* + DFUUploader - uploader implementation using DFU + + Copyright (c) 2010 + Andrew Meyer + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package CliTemplate; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import processing.app.Preferences; +import processing.app.Serial; +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; + +/** + * + * @author bob + */ +public class DFUUploader implements MessageConsumer { + + boolean firstErrorFound; + boolean secondErrorFound; + // part of the PdeMessageConsumer interface + boolean notFoundError; + boolean verbose; + RunnerException exception; + + static final String SUPER_BADNESS = + "Compiler error!"; + + public boolean uploadUsingPreferences(String binPath, boolean verbose) + throws RunnerException { + + this.verbose = verbose; + + return uploadViaDFU(binPath); + } + + // works with old and new versions of dfu-util + private boolean found_device (String dfuData, String usbID) + { + return dfuData.contains(("Found DFU: [0x"+usbID.substring(0,4)).toUpperCase()) || + dfuData.contains(("Found DFU: ["+usbID.substring(0,4)).toUpperCase()); + } + + public boolean uploadViaDFU (String binPath) + throws RunnerException { + + this.verbose = Preferences.getBoolean ("upload.verbose"); + + /* todo, check for size overruns! */ + String fileType="bin"; + + if (fileType.equals("bin")) { + String usbID = Preferences.get("upload.usbID"); + if (usbID == null) { + /* fall back on default */ + /* this isnt great because is default Avrdude or dfu-util? */ + usbID = Preferences.get("upload.usbID"); + } + + /* if auto-reset, then emit the reset pulse on dtr/rts */ + if (Preferences.get("upload.auto_reset") != null) { + if (Preferences.get("upload.auto_reset").toLowerCase().equals("true")) { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + } else { + System.out.println("Resetting to bootloader via DTR pulse"); + emitResetPulse(); + } + + String dfuList = new String(); + List commandCheck = new ArrayList(); + commandCheck.add("dfu-util"); + commandCheck.add("-l"); + long startChecking = System.currentTimeMillis(); + System.out.println("Searching for DFU device [" + usbID + "]..."); + do { + try { + Thread.sleep(100); + } catch (InterruptedException e) {} + dfuList = executeCheckCommand(commandCheck); + //System.out.println(dfuList); + } + while ( !found_device (dfuList.toUpperCase(), usbID) && (System.currentTimeMillis() - startChecking < 7000)); + + if ( !found_device (dfuList.toUpperCase(), usbID) ) + { + System.out.println(dfuList); + System.err.println("Couldn't find the DFU device: [" + usbID + "]"); + return false; + } + System.out.println("Found it!"); + + /* todo, add handle to let user choose altIf at upload time! */ + String altIf = Preferences.get("upload.altID"); + + List commandDownloader = new ArrayList(); + commandDownloader.add("dfu-util"); + commandDownloader.add("-a "+altIf); + commandDownloader.add("-R"); + commandDownloader.add("-d "+usbID); + commandDownloader.add("-D"+ binPath); //"./thisbin.bin"); + + return executeUploadCommand(commandDownloader); + } + + System.err.println("Only .bin files are supported at this time"); + return false; + } + + /* we need to ensure both RTS and DTR are low to start, + then pulse DTR on its own. This is the reset signal + maple responds to + */ + private void emitResetPulse() throws RunnerException { + + /* wait a while for the device to reboot */ + int programDelay = Preferences.getInteger("programDelay"); + + try { + Serial serialPort = new Serial(); + + // try to toggle DTR/RTS (old scheme) + serialPort.setRTS(false); + serialPort.setDTR(false); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + + // try magic number + serialPort.setRTS(true); + serialPort.setDTR(true); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.setDTR(false); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.write("1EAF"); + try { + Thread.sleep(50); + } catch (InterruptedException e) {} + serialPort.dispose(); + + } catch(Exception e) { + System.err.println("Reset via USB Serial Failed! Did you select the right serial port?\nAssuming the board is in perpetual bootloader mode and continuing to attempt dfu programming...\n"); + } + } + + protected String executeCheckCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + String returnStr = new String(); + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(process.getInputStream())); + BufferedReader stdError = new BufferedReader(new + InputStreamReader(process.getErrorStream())); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean busy = true; + while (busy) { + try { + result = process.waitFor(); + busy = false; + } catch (InterruptedException intExc) { + } + } + + String s; + while ((s = stdInput.readLine()) != null) { + returnStr += s + "\n"; + } + + process.destroy(); + + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) return "Error!"; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + } + + return returnStr; // ? true : false; + + } + + // Need to overload this from Uploader to use the system-wide dfu-util + protected boolean executeUploadCommand(Collection commandDownloader) + throws RunnerException + { + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + notFoundError = false; + int result=0; // pre-initialized to quiet a bogus warning from jikes + + String userdir = System.getProperty("user.dir") + File.separator; + + try { + String[] commandArray = new String[commandDownloader.size()]; + commandDownloader.toArray(commandArray); + + String armBasePath; + + //armBasePath = new String(Base.getHardwarePath() + "/tools/arm/bin/"); + armBasePath = ""; + + commandArray[0] = armBasePath + commandArray[0]; + + if (verbose || Preferences.getBoolean("upload.verbose")) { + for(int i = 0; i < commandArray.length; i++) { + System.out.print(commandArray[i] + " "); + } + System.out.println(); + } + + Process process = Runtime.getRuntime().exec(commandArray); + new MessageSiphon(process.getInputStream(), this); + new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + // + boolean compiling = true; + while (compiling) { + try { + result = process.waitFor(); + compiling = false; + } catch (InterruptedException intExc) { + } + } + if(exception!=null) { + exception.hideStackTrace(); + throw exception; + } + if(result!=0) + return false; + } catch (Exception e) { + e.printStackTrace(); + } + //System.out.println("result2 is "+result); + // if the result isn't a known, expected value it means that something + // is fairly wrong, one possibility is that jikes has crashed. + // + if (exception != null) throw exception; + + if ((result != 0) && (result != 1 )) { + exception = new RunnerException(SUPER_BADNESS); + //editor.error(exception); + //PdeBase.openURL(BUGS_URL); + //throw new PdeException(SUPER_BADNESS); + } + + return (result == 0); // ? true : false; + + } + + // deal with messages from dfu-util... + public void message(String s) { + + if(s.indexOf("dfu-util - (C) ") != -1) { return; } + if(s.indexOf("This program is Free Software and has ABSOLUTELY NO WARRANTY") != -1) { return; } + + if(s.indexOf("No DFU capable USB device found") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: No Maple found"); + return; + } + + if(s.indexOf("Operation not perimitted") != -1) { + System.err.print(s); + exception = new RunnerException("Problem uploading via dfu-util: Insufficient privilages"); + return; + } + + // else just print everything... + System.out.print(s); + } + +} diff --git a/tools/win/src/maple_loader/src/CliTemplate/ExecCommand.java b/tools/win/src/maple_loader/src/CliTemplate/ExecCommand.java new file mode 100644 index 0000000..3d6c106 --- /dev/null +++ b/tools/win/src/maple_loader/src/CliTemplate/ExecCommand.java @@ -0,0 +1,119 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package CliTemplate; + +import java.io.IOException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import processing.app.debug.MessageConsumer; +import processing.app.debug.MessageSiphon; +import processing.app.debug.RunnerException; +import processing.app.helpers.ProcessUtils; + +/** + * + * @author cousinr + */ +public class ExecCommand implements MessageConsumer { + + private boolean verbose = true; + private boolean firstErrorFound; + private boolean secondErrorFound; + private RunnerException exception; + + /** + * Either succeeds or throws a RunnerException fit for public consumption. + * + * @param command + * @throws RunnerException + */ + public void execAsynchronously(String[] command) throws RunnerException { + + // eliminate any empty array entries + List stringList = new ArrayList<>(); + for (String string : command) { + string = string.trim(); + if (string.length() != 0) + stringList.add(string); + } + command = stringList.toArray(new String[stringList.size()]); + if (command.length == 0) + return; + int result = 0; + + if (verbose) { + for (String c : command) + System.out.print(c + " "); + System.out.println(); + } + + firstErrorFound = false; // haven't found any errors yet + secondErrorFound = false; + + Process process; + try { + process = ProcessUtils.exec(command); + } catch (IOException e) { + RunnerException re = new RunnerException(e.getMessage()); + re.hideStackTrace(); + throw re; + } + + MessageSiphon in = new MessageSiphon(process.getInputStream(), this); + MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); + + // wait for the process to finish. if interrupted + // before waitFor returns, continue waiting + boolean compiling = true; + while (compiling) { + try { + in.join(); + err.join(); + result = process.waitFor(); + //System.out.println("result is " + result); + compiling = false; + } catch (InterruptedException ignored) { } + } + + // an error was queued up by message(), barf this back to compile(), + // which will barf it back to Editor. if you're having trouble + // discerning the imagery, consider how cows regurgitate their food + // to digest it, and the fact that they have five stomaches. + // + //System.out.println("throwing up " + exception); + if (exception != null) + throw exception; + + if (result > 1) { + // a failure in the tool (e.g. unable to locate a sub-executable) + System.err.println(MessageFormat.format("{0} returned {1}", command[0], result)); + } + + if (result != 0) { + RunnerException re = new RunnerException(MessageFormat.format("exit code: {0}", result)); + re.hideStackTrace(); + throw re; + } + } + + /** + * Part of the MessageConsumer interface, this is called + * whenever a piece (usually a line) of error message is spewed + * out from the compiler. The errors are parsed for their contents + * and line number, which is then reported back to Editor. + * @param s + */ + @Override + public void message(String s) { + int i; + + + System.err.print(s); + } + +} diff --git a/tools/win/src/maple_loader/src/processing/app/Base.java b/tools/win/src/maple_loader/src/processing/app/Base.java new file mode 100644 index 0000000..c3a174d --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/Base.java @@ -0,0 +1,53 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-10 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + + +/** + * The base class for the main processing application. + * Primary role of this class is for platform identification and + * general interaction with the system (launching URLs, loading + * files and images, etc) that comes from that. + */ +public class Base { + + /** + * returns true if running on windows. + */ + static public boolean isWindows() { + //return PApplet.platform == PConstants.WINDOWS; + return System.getProperty("os.name").indexOf("Windows") != -1; + } + + + /** + * true if running on linux. + */ + static public boolean isLinux() { + //return PApplet.platform == PConstants.LINUX; + return System.getProperty("os.name").indexOf("Linux") != -1; + } + + + +} diff --git a/tools/win/src/maple_loader/src/processing/app/Preferences.java b/tools/win/src/maple_loader/src/processing/app/Preferences.java new file mode 100644 index 0000000..6368e38 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/Preferences.java @@ -0,0 +1,157 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-09 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import java.io.*; +import java.util.*; + + +/** + * Storage class for user preferences and environment settings. + *

+ * This class no longer uses the Properties class, since + * properties files are iso8859-1, which is highly likely to + * be a problem when trying to save sketch folders and locations. + *

+ * The GUI portion in here is really ugly, as it uses exact layout. This was + * done in frustration one evening (and pre-Swing), but that's long since past, + * and it should all be moved to a proper swing layout like BoxLayout. + *

+ * This is very poorly put together, that the preferences panel and the actual + * preferences i/o is part of the same code. But there hasn't yet been a + * compelling reason to bother with the separation aside from concern about + * being lectured by strangers who feel that it doesn't look like what they + * learned in CS class. + *

+ * Would also be possible to change this to use the Java Preferences API. + * Some useful articles + * here and + * here. + * However, haven't implemented this yet for lack of time, but more + * importantly, because it would entail writing to the registry (on Windows), + * or an obscure file location (on Mac OS X) and make it far more difficult to + * find the preferences to tweak them by hand (no! stay out of regedit!) + * or to reset the preferences by simply deleting the preferences.txt file. + */ +public class Preferences { + + // what to call the feller + + static final String PREFS_FILE = "preferences.txt"; + + + // prompt text stuff + + static final String PROMPT_YES = "Yes"; + static final String PROMPT_NO = "No"; + static final String PROMPT_CANCEL = "Cancel"; + static final String PROMPT_OK = "OK"; + static final String PROMPT_BROWSE = "Browse"; + + /** + * Standardized width for buttons. Mac OS X 10.3 wants 70 as its default, + * Windows XP needs 66, and my Ubuntu machine needs 80+, so 80 seems proper. + */ + static public int BUTTON_WIDTH = 80; + + /** + * Standardized button height. Mac OS X 10.3 (Java 1.4) wants 29, + * presumably because it now includes the blue border, where it didn't + * in Java 1.3. Windows XP only wants 23 (not sure what default Linux + * would be). Because of the disparity, on Mac OS X, it will be set + * inside a static block. + */ + static public int BUTTON_HEIGHT = 24; + + // value for the size bars, buttons, etc + + static final int GRID_SIZE = 33; + + + // indents and spacing standards. these probably need to be modified + // per platform as well, since macosx is so huge, windows is smaller, + // and linux is all over the map + + static final int GUI_BIG = 13; + static final int GUI_BETWEEN = 10; + static final int GUI_SMALL = 6; + + + + // data model + + static Hashtable table = new Hashtable();; + static File preferencesFile; + + + static protected void init(String commandLinePrefs) { + + + } + + + public Preferences() { + + } + + // ................................................................. + + // ................................................................. + + // ................................................................. + + // ................................................................. + + + + static public String get(String attribute) { + return (String) table.get(attribute); + } + + static public void set(String attribute, String value) { + table.put(attribute, value); + } + + + static public boolean getBoolean(String attribute) { + String value = get(attribute); + return (new Boolean(value)).booleanValue(); + } + + + static public void setBoolean(String attribute, boolean value) { + set(attribute, value ? "true" : "false"); + } + + + static public int getInteger(String attribute) { + return Integer.parseInt(get(attribute)); + } + + + static public void setInteger(String key, int value) { + set(key, String.valueOf(value)); + } + +} diff --git a/tools/win/src/maple_loader/src/processing/app/Serial.java b/tools/win/src/maple_loader/src/processing/app/Serial.java new file mode 100644 index 0000000..04566a7 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/Serial.java @@ -0,0 +1,527 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + PSerial - class for serial port goodness + Part of the Processing project - http://processing.org + + Copyright (c) 2004 Ben Fry & Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.app; +//import processing.core.*; + + +import java.io.*; +import java.text.MessageFormat; +import java.util.*; +import jssc.SerialPort; +import jssc.SerialPortEvent; +import jssc.SerialPortEventListener; +import jssc.SerialPortException; +import jssc.SerialPortList; +import processing.app.debug.MessageConsumer; + + +public class Serial implements SerialPortEventListener { + + //PApplet parent; + + // properties can be passed in for default values + // otherwise defaults to 9600 N81 + + // these could be made static, which might be a solution + // for the classloading problem.. because if code ran again, + // the static class would have an object that could be closed + + SerialPort port; + + int rate; + int parity; + int databits; + int stopbits; + boolean monitor = false; + + // read buffer and streams + + InputStream input; + OutputStream output; + + byte buffer[] = new byte[32768]; + int bufferIndex; + int bufferLast; + + MessageConsumer consumer; + + public Serial(boolean monitor) throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + this.monitor = monitor; + } + + public Serial() throws SerialException { + this(Preferences.get("serial.port"), + Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(int irate) throws SerialException { + this(Preferences.get("serial.port"), irate, + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate) throws SerialException { + this(iname, irate, Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname) throws SerialException { + this(iname, Preferences.getInteger("serial.debug_rate"), + Preferences.get("serial.parity").charAt(0), + Preferences.getInteger("serial.databits"), + new Float(Preferences.get("serial.stopbits")).floatValue()); + } + + public Serial(String iname, int irate, + char iparity, int idatabits, float istopbits) + throws SerialException { + //if (port != null) port.close(); + //this.parent = parent; + //parent.attach(this); + + this.rate = irate; + + parity = SerialPort.PARITY_NONE; + if (iparity == 'E') parity = SerialPort.PARITY_EVEN; + if (iparity == 'O') parity = SerialPort.PARITY_ODD; + + this.databits = idatabits; + + stopbits = SerialPort.STOPBITS_1; + if (istopbits == 1.5f) stopbits = SerialPort.STOPBITS_1_5; + if (istopbits == 2) stopbits = SerialPort.STOPBITS_2; + + try { + port = new SerialPort(iname); + port.openPort(); + port.setParams(rate, databits, stopbits, parity, true, true); + port.addEventListener(this); + } catch (Exception e) { + throw new SerialException(MessageFormat.format("Error opening serial port ''{0}''.", iname), e); + } + + if (port == null) { + throw new SerialException("Serial port '" + iname + "' not found. Did you select the right one from the Tools > Serial Port menu?"); + } + } + + + public void setup() { + //parent.registerCall(this, DISPOSE); + } + + public void dispose() throws IOException { + if (port != null) { + try { + if (port.isOpened()) { + port.closePort(); // close the port + } + } catch (SerialPortException e) { + throw new IOException(e); + } finally { + port = null; + } + } + } + + public void addListener(MessageConsumer consumer) { + this.consumer = consumer; + } + + public synchronized void serialEvent(SerialPortEvent serialEvent) { + if (serialEvent.isRXCHAR()) { + try { + byte[] buf = port.readBytes(serialEvent.getEventValue()); + if (buf.length > 0) { + if (bufferLast == buffer.length) { + byte temp[] = new byte[bufferLast << 1]; + System.arraycopy(buffer, 0, temp, 0, bufferLast); + buffer = temp; + } + if (monitor) { + System.out.print(new String(buf)); + } + if (this.consumer != null) { + this.consumer.message(new String(buf)); + } + } + } catch (SerialPortException e) { + errorMessage("serialEvent", e); + } + } + } + + + /** + * Returns the number of bytes that have been read from serial + * and are waiting to be dealt with by the user. + */ + public synchronized int available() { + return (bufferLast - bufferIndex); + } + + + /** + * Ignore all the bytes read so far and empty the buffer. + */ + public synchronized void clear() { + bufferLast = 0; + bufferIndex = 0; + } + + + /** + * Returns a number between 0 and 255 for the next byte that's + * waiting in the buffer. + * Returns -1 if there was no byte (although the user should + * first check available() to see if things are ready to avoid this) + */ + public synchronized int read() { + if (bufferIndex == bufferLast) return -1; + + int outgoing = buffer[bufferIndex++] & 0xff; + if (bufferIndex == bufferLast) { // rewind + bufferIndex = 0; + bufferLast = 0; + } + return outgoing; + } + + + /** + * Returns the next byte in the buffer as a char. + * Returns -1, or 0xffff, if nothing is there. + */ + public synchronized char readChar() { + if (bufferIndex == bufferLast) return (char)(-1); + return (char) read(); + } + + + /** + * Return a byte array of anything that's in the serial buffer. + * Not particularly memory/speed efficient, because it creates + * a byte array on each read, but it's easier to use than + * readBytes(byte b[]) (see below). + */ + public synchronized byte[] readBytes() { + if (bufferIndex == bufferLast) return null; + + int length = bufferLast - bufferIndex; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Grab whatever is in the serial buffer, and stuff it into a + * byte buffer passed in by the user. This is more memory/time + * efficient than readBytes() returning a byte[] array. + *

+ * Returns an int for how many bytes were read. If more bytes + * are available than can fit into the byte array, only those + * that will fit are read. + */ + public synchronized int readBytes(byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + + int length = bufferLast - bufferIndex; + if (length > outgoing.length) length = outgoing.length; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Reads from the serial port into a buffer of bytes up to and + * including a particular character. If the character isn't in + * the serial buffer, then 'null' is returned. + */ + public synchronized byte[] readBytesUntil(int interesting) { + if (bufferIndex == bufferLast) return null; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return null; + + int length = found - bufferIndex + 1; + byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex = 0; // rewind + bufferLast = 0; + return outgoing; + } + + + /** + * Reads from the serial port into a buffer of bytes until a + * particular character. If the character isn't in the serial + * buffer, then 'null' is returned. + *

+ * If outgoing[] is not big enough, then -1 is returned, + * and an error message is printed on the console. + * If nothing is in the buffer, zero is returned. + * If 'interesting' byte is not in the buffer, then 0 is returned. + */ + public synchronized int readBytesUntil(int interesting, byte outgoing[]) { + if (bufferIndex == bufferLast) return 0; + byte what = (byte)interesting; + + int found = -1; + for (int k = bufferIndex; k < bufferLast; k++) { + if (buffer[k] == what) { + found = k; + break; + } + } + if (found == -1) return 0; + + int length = found - bufferIndex + 1; + if (length > outgoing.length) { + System.err.println("readBytesUntil() byte buffer is" + + " too small for the " + length + + " bytes up to and including char " + interesting); + return -1; + } + //byte outgoing[] = new byte[length]; + System.arraycopy(buffer, bufferIndex, outgoing, 0, length); + + bufferIndex += length; + if (bufferIndex == bufferLast) { + bufferIndex = 0; // rewind + bufferLast = 0; + } + return length; + } + + + /** + * Return whatever has been read from the serial port so far + * as a String. It assumes that the incoming characters are ASCII. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readString() { + if (bufferIndex == bufferLast) return null; + return new String(readBytes()); + } + + + /** + * Combination of readBytesUntil and readString. See caveats in + * each function. Returns null if it still hasn't found what + * you're looking for. + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public synchronized String readStringUntil(int interesting) { + byte b[] = readBytesUntil(interesting); + if (b == null) return null; + return new String(b); + } + + + /** + * This will handle both ints, bytes and chars transparently. + */ + public void write(int what) { // will also cover char + try { + port.writeInt(what & 0xff); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + public void write(byte bytes[]) { + try { + port.writeBytes(bytes); + } catch (SerialPortException e) { + errorMessage("write", e); + } + } + + + /** + * Write a String to the output. Note that this doesn't account + * for Unicode (two bytes per char), nor will it send UTF8 + * characters.. It assumes that you mean to send a byte buffer + * (most often the case for networking and serial i/o) and + * will only use the bottom 8 bits of each char in the string. + * (Meaning that internally it uses String.getBytes) + *

+ * If you want to move Unicode data, you can first convert the + * String to a byte stream in the representation of your choice + * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. + */ + public void write(String what) { + write(what.getBytes()); + } + + public void setDTR(boolean state) { + try { + port.setDTR(state); + } catch (SerialPortException e) { + errorMessage("setDTR", e); + } + } + + public void setRTS(boolean state) { + try { + port.setRTS(state); + } catch (SerialPortException e) { + errorMessage("setRTS", e); + } + } + + static public List list() { + return Arrays.asList(SerialPortList.getPortNames()); + } + + + /** + * General error reporting, all corraled here just in case + * I think of something slightly more intelligent to do. + */ + static public void errorMessage(String where, Throwable e) { + System.err.println("Error inside Serial." + where + "()"); + e.printStackTrace(); + } +} + + + /* + class SerialMenuListener implements ItemListener { + //public SerialMenuListener() { } + + public void itemStateChanged(ItemEvent e) { + int count = serialMenu.getItemCount(); + for (int i = 0; i < count; i++) { + ((CheckboxMenuItem)serialMenu.getItem(i)).setState(false); + } + CheckboxMenuItem item = (CheckboxMenuItem)e.getSource(); + item.setState(true); + String name = item.getLabel(); + //System.out.println(item.getLabel()); + PdeBase.properties.put("serial.port", name); + //System.out.println("set to " + get("serial.port")); + } + } + */ + + + /* + protected Vector buildPortList() { + // get list of names for serial ports + // have the default port checked (if present) + Vector list = new Vector(); + + //SerialMenuListener listener = new SerialMenuListener(); + boolean problem = false; + + // if this is failing, it may be because + // lib/javax.comm.properties is missing. + // java is weird about how it searches for java.comm.properties + // so it tends to be very fragile. i.e. quotes in the CLASSPATH + // environment variable will hose things. + try { + //System.out.println("building port list"); + Enumeration portList = CommPortIdentifier.getPortIdentifiers(); + while (portList.hasMoreElements()) { + CommPortIdentifier portId = + (CommPortIdentifier) portList.nextElement(); + //System.out.println(portId); + + if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { + //if (portId.getName().equals(port)) { + String name = portId.getName(); + //CheckboxMenuItem mi = + //new CheckboxMenuItem(name, name.equals(defaultName)); + + //mi.addItemListener(listener); + //serialMenu.add(mi); + list.addElement(name); + } + } + } catch (UnsatisfiedLinkError e) { + e.printStackTrace(); + problem = true; + + } catch (Exception e) { + System.out.println("exception building serial menu"); + e.printStackTrace(); + } + + //if (serialMenu.getItemCount() == 0) { + //System.out.println("dimming serial menu"); + //serialMenu.setEnabled(false); + //} + + // only warn them if this is the first time + if (problem && PdeBase.firstTime) { + JOptionPane.showMessageDialog(this, //frame, + "Serial port support not installed.\n" + + "Check the readme for instructions\n" + + "if you need to use the serial port. ", + "Serial Port Warning", + JOptionPane.WARNING_MESSAGE); + } + return list; + } + */ + + + diff --git a/tools/win/src/maple_loader/src/processing/app/SerialException.java b/tools/win/src/maple_loader/src/processing/app/SerialException.java new file mode 100644 index 0000000..525c240 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/SerialException.java @@ -0,0 +1,39 @@ +/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) 2007 David A. Mellis + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +public class SerialException extends Exception { + public SerialException() { + super(); + } + + public SerialException(String message) { + super(message); + } + + public SerialException(String message, Throwable cause) { + super(message, cause); + } + + public SerialException(Throwable cause) { + super(cause); + } +} diff --git a/tools/win/src/maple_loader/src/processing/app/debug/MessageConsumer.java b/tools/win/src/maple_loader/src/processing/app/debug/MessageConsumer.java new file mode 100644 index 0000000..5e20429 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/debug/MessageConsumer.java @@ -0,0 +1,42 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * Interface for dealing with parser/compiler output. + *

+ * Different instances of MessageStream need to do different things with + * messages. In particular, a stream instance used for parsing output from + * the compiler compiler has to interpret its messages differently than one + * parsing output from the runtime. + *

+ * Classes which consume messages and do something with them + * should implement this interface. + */ +public interface MessageConsumer { + + public void message(String s); + +} diff --git a/tools/win/src/maple_loader/src/processing/app/debug/MessageSiphon.java b/tools/win/src/maple_loader/src/processing/app/debug/MessageSiphon.java new file mode 100644 index 0000000..26901c3 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/debug/MessageSiphon.java @@ -0,0 +1,104 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-06 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.SocketException; + +/** + * Slurps up messages from compiler. + */ +public class MessageSiphon implements Runnable { + + private final BufferedReader streamReader; + private final MessageConsumer consumer; + + private Thread thread; + private boolean canRun; + + public MessageSiphon(InputStream stream, MessageConsumer consumer) { + this.streamReader = new BufferedReader(new InputStreamReader(stream)); + this.consumer = consumer; + this.canRun = true; + + thread = new Thread(this); + // don't set priority too low, otherwise exceptions won't + // bubble up in time (i.e. compile errors have a weird delay) + //thread.setPriority(Thread.MIN_PRIORITY); + thread.setPriority(Thread.MAX_PRIORITY - 1); + thread.start(); + } + + + public void run() { + try { + // process data until we hit EOF; this will happily block + // (effectively sleeping the thread) until new data comes in. + // when the program is finally done, null will come through. + // + String currentLine; + while (canRun && (currentLine = streamReader.readLine()) != null) { + // \n is added again because readLine() strips it out + //EditorConsole.systemOut.println("messaging in"); + consumer.message(currentLine + "\n"); + //EditorConsole.systemOut.println("messaging out"); + } + //EditorConsole.systemOut.println("messaging thread done"); + } catch (NullPointerException npe) { + // Fairly common exception during shutdown + } catch (SocketException e) { + // socket has been close while we were wainting for data. nothing to see here, move along + } catch (Exception e) { + // On Linux and sometimes on Mac OS X, a "bad file descriptor" + // message comes up when closing an applet that's run externally. + // That message just gets supressed here.. + String mess = e.getMessage(); + if ((mess != null) && + (mess.indexOf("Bad file descriptor") != -1)) { + //if (e.getMessage().indexOf("Bad file descriptor") == -1) { + //System.err.println("MessageSiphon err " + e); + //e.printStackTrace(); + } else { + e.printStackTrace(); + } + } finally { + thread = null; + } + } + + // Wait until the MessageSiphon thread is complete. + public void join() throws java.lang.InterruptedException { + // Grab a temp copy in case another thread nulls the "thread" + // member variable + Thread t = thread; + if (t != null) t.join(); + } + + public void stop() { + this.canRun = false; + } + +} diff --git a/tools/win/src/maple_loader/src/processing/app/debug/RunnerException.java b/tools/win/src/maple_loader/src/processing/app/debug/RunnerException.java new file mode 100644 index 0000000..0a67d1e --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/debug/RunnerException.java @@ -0,0 +1,161 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-08 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app.debug; + + +/** + * An exception with a line number attached that occurs + * during either compile time or run time. + */ +@SuppressWarnings("serial") +public class RunnerException extends Exception { + protected String message; + protected int codeIndex; + protected int codeLine; + protected int codeColumn; + protected boolean showStackTrace; + + + public RunnerException(String message) { + this(message, true); + } + + public RunnerException(String message, boolean showStackTrace) { + this(message, -1, -1, -1, showStackTrace); + } + + public RunnerException(String message, int file, int line) { + this(message, file, line, -1, true); + } + + + public RunnerException(String message, int file, int line, int column) { + this(message, file, line, column, true); + } + + + public RunnerException(String message, int file, int line, int column, + boolean showStackTrace) { + this.message = message; + this.codeIndex = file; + this.codeLine = line; + this.codeColumn = column; + this.showStackTrace = showStackTrace; + } + + + public RunnerException(Exception e) { + super(e); + this.showStackTrace = true; + } + + /** + * Override getMessage() in Throwable, so that I can set + * the message text outside the constructor. + */ + public String getMessage() { + return message; + } + + + public void setMessage(String message) { + this.message = message; + } + + + public int getCodeIndex() { + return codeIndex; + } + + + public void setCodeIndex(int index) { + codeIndex = index; + } + + + public boolean hasCodeIndex() { + return codeIndex != -1; + } + + + public int getCodeLine() { + return codeLine; + } + + + public void setCodeLine(int line) { + this.codeLine = line; + } + + + public boolean hasCodeLine() { + return codeLine != -1; + } + + + public void setCodeColumn(int column) { + this.codeColumn = column; + } + + + public int getCodeColumn() { + return codeColumn; + } + + + public void showStackTrace() { + showStackTrace = true; + } + + + public void hideStackTrace() { + showStackTrace = false; + } + + + /** + * Nix the java.lang crap out of an exception message + * because it scares the children. + *

+ * This function must be static to be used with super() + * in each of the constructors above. + */ + /* + static public final String massage(String msg) { + if (msg.indexOf("java.lang.") == 0) { + //int dot = msg.lastIndexOf('.'); + msg = msg.substring("java.lang.".length()); + } + return msg; + //return (dot == -1) ? msg : msg.substring(dot+1); + } + */ + + + public void printStackTrace() { + if (showStackTrace) { + super.printStackTrace(); + } + } +} diff --git a/tools/win/src/maple_loader/src/processing/app/helpers/ProcessUtils.java b/tools/win/src/maple_loader/src/processing/app/helpers/ProcessUtils.java new file mode 100644 index 0000000..c023f58 --- /dev/null +++ b/tools/win/src/maple_loader/src/processing/app/helpers/ProcessUtils.java @@ -0,0 +1,32 @@ +package processing.app.helpers; + +//import processing.app.Base; + +import java.io.IOException; +import java.util.Map; + +import processing.app.Base; + +public class ProcessUtils { + + public static Process exec(String[] command) throws IOException { + // No problems on linux and mac + if (!Base.isWindows()) { + return Runtime.getRuntime().exec(command); + } + + // Brutal hack to workaround windows command line parsing. + // http://stackoverflow.com/questions/5969724/java-runtime-exec-fails-to-escape-characters-properly + // http://msdn.microsoft.com/en-us/library/a1y7w461.aspx + // http://bugs.sun.com/view_bug.do?bug_id=6468220 + // http://bugs.sun.com/view_bug.do?bug_id=6518827 + String[] cmdLine = new String[command.length]; + for (int i = 0; i < command.length; i++) + cmdLine[i] = command[i].replace("\"", "\\\""); + + ProcessBuilder pb = new ProcessBuilder(cmdLine); + Map env = pb.environment(); + env.put("CYGWIN", "nodosfilewarning"); + return pb.start(); + } +} diff --git a/tools/win/src/stm32flash_serial/src/AUTHORS b/tools/win/src/stm32flash_serial/src/AUTHORS new file mode 100644 index 0000000..d096f22 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/AUTHORS @@ -0,0 +1,19 @@ +Authors ordered by first contribution. + +Geoffrey McRae +Bret Olmsted +Tormod Volden +Jakob Malm +Reuben Dowle +Matthias Kubisch +Paul Fertser +Daniel Strnad +Jérémie Rapin +Christian Pointner +Mats Erik Andersson +Alexey Borovik +Antonio Borneo +Armin van der Togt +Brian Silverman +Georg Hofmann +Luis Rodrigues diff --git a/tools/win/src/stm32flash_serial/src/Android.mk b/tools/win/src/stm32flash_serial/src/Android.mk new file mode 100644 index 0000000..7be3d00 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/Android.mk @@ -0,0 +1,20 @@ +TOP_LOCAL_PATH := $(call my-dir) + +include $(call all-named-subdir-makefiles, parsers) + +LOCAL_PATH := $(TOP_LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_MODULE := stm32flash +LOCAL_SRC_FILES := \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c \ + stm32.c \ + utils.c +LOCAL_STATIC_LIBRARIES := libparsers +include $(BUILD_EXECUTABLE) diff --git a/tools/win/src/stm32flash_serial/src/HOWTO b/tools/win/src/stm32flash_serial/src/HOWTO new file mode 100644 index 0000000..d8f32eb --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/HOWTO @@ -0,0 +1,35 @@ +Add new interfaces: +===================================================================== +Current version 0.4 supports the following interfaces: +- UART Windows (either "COMn" and "\\.\COMn"); +- UART posix/Linux (e.g. "/dev/ttyUSB0"); +- I2C Linux through standard driver "i2c-dev" (e.g. "/dev/i2c-n"). + +Starting from version 0.4, the back-end of stm32flash is modular and +ready to be expanded to support new interfaces. +I'm planning adding SPI on Linux through standard driver "spidev". +You are invited to contribute with more interfaces. + +To add a new interface you need to add a new file, populate the struct +port_interface (check at the end of files i2c.c, serial_posix.c and +serial_w32.c) and provide the relative functions to operate on the +interface: open/close, read/write, get_cfg_str and the optional gpio. +The include the new drive in Makefile and register the new struct +port_interface in file port.c in struct port_interface *ports[]. + +There are several USB-I2C adapter in the market, each providing its +own libraries to communicate with the I2C bus. +Could be interesting to provide as back-end a bridge between stm32flash +and such libraries (I have no plan on this item). + + +Add new STM32 devices: +===================================================================== +Add a new line in file dev_table.c, in table devices[]. +The fields of the table are listed in stm32.h, struct stm32_dev. + + +Cross compile on Linux host for Windows target with MinGW: +===================================================================== +I'm using a 64 bit Arch Linux machines, and I usually run: + make CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar diff --git a/tools/win/src/stm32flash_serial/src/I2C.txt b/tools/win/src/stm32flash_serial/src/I2C.txt new file mode 100644 index 0000000..4c05ff6 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/I2C.txt @@ -0,0 +1,94 @@ +About I2C back-end communication in stm32flash +========================================================================== + +Starting from version v0.4, beside the serial communication port, +stm32flash adds support for I2C port to talk with STM32 bootloader. + +The current I2C back-end supports only the API provided by Linux kernel +driver "i2c-dev", so only I2C controllers with Linux kernel driver can be +used. +In Linux source code, most of the drivers for I2C and SMBUS controllers +are in + ./drivers/i2c/busses/ +Only I2C is supported by STM32 bootloader, so check the section below +about SMBUS. +No I2C support for Windows is available in stm32flash v0.4. + +Thanks to the new modular back-end, stm32flash can be easily extended to +support new back-ends and API. Check HOWTO file in stm32flash source code +for details. + +In the market there are several USB-to-I2C dongles; most of them are not +supported by kernel drivers. Manufacturer provide proprietary userspace +libraries using not standardized API. +These API and dongles could be supported in feature versions. + +There are currently 3 versions of STM32 bootloader for I2C communications: +- v1.0 using I2C clock stretching synchronization between host and STM32; +- v1.1 superset of v1.0, adds non stretching commands; +- v1.2 superset of v1.1, adds CRC command and compatibility with i2cdetect. +Details in ST application note AN2606. +All the bootloaders above are tested and working with stm32flash. + + +SMBUS controllers +========================================================================== + +Almost 50% of the drivers in Linux source code folder + ./drivers/i2c/busses/ +are for controllers that "only" support SMBUS protocol. They can NOT +operate with STM32 bootloader. +To identify if your controller supports I2C, use command: + i2cdetect -F n +where "n" is the number of the I2C interface (e.g. n=3 for "/dev/i2c-3"). +Controllers that supports I2C will report + I2C yes +Controller that support both I2C and SMBUS are ok. + +If you are interested on details about SMBUS protocol, you can download +the current specs from + http://smbus.org/specs/smbus20.pdf +and you can read the files in Linux source code + ./Documentation/i2c/i2c-protocol + ./Documentation/i2c/smbus-protocol + + +About bootloader v1.0 +========================================================================== + +Version v1.0 can have issues with some I2C controllers due to use of clock +stretching during commands that require long operations, like flash erase +and programming. + +Clock stretching is a technique to synchronize host and I2C device. When +I2C device wants to force a delay in the communication, it push "low" the +I2C clock; the I2C controller detects it and waits until I2C clock returns +"high". +Most I2C controllers set a "timeout" for clock stretching, ranging from +few milli-seconds to seconds depending on specific HW or SW driver. + +It is possible that the timeout in your I2C controller is smaller than the +delay required for flash erase or programming. In this case the I2C +controller will timeout and report error to stm32flash. +There is no possibility for stm32flash to retry, so it can only signal the +error and exit. + +To by-pass the issue with bootloader v1.0 you can modify the kernel driver +of your I2C controller. Not an easy job, since every controller has its own +way to handle the timeout. + +In my case I'm using the I2C controller integrated in the VGA port of my +laptop HP EliteBook 8460p. I built the 0.25$ VGA-to-I2C adapter reported in + http://www.paintyourdragon.com/?p=43 +To change the timeout of the I2C controller I had to modify the kernel file + drivers/gpu/drm/radeon/radeon_i2c.c +line 969 +- i2c->bit.timeout = usecs_to_jiffies(2200); /* from VESA */ ++ i2c->bit.timeout = msecs_to_jiffies(5000); /* 5s for STM32 */ +and recompile it. +Then + $> modprobe i2c-dev + $> chmod 666 /dev/i2c-7 + #> stm32flash -a 0x39 /dev/i2c-7 + +2014-09-16 Antonio Borneo diff --git a/tools/win/src/stm32flash_serial/src/Makefile b/tools/win/src/stm32flash_serial/src/Makefile new file mode 100644 index 0000000..0328d55 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/Makefile @@ -0,0 +1,38 @@ +PREFIX = /usr/local +CFLAGS += -Wall -g + +INSTALL = install + +OBJS = dev_table.o \ + i2c.o \ + init.o \ + main.o \ + port.o \ + serial_common.o \ + serial_platform.o \ + stm32.o \ + utils.o + +LIBOBJS = parsers/parsers.a + +all: stm32flash + +serial_platform.o: serial_posix.c serial_w32.c + +parsers/parsers.a: + cd parsers && $(MAKE) parsers.a + +stm32flash: $(OBJS) $(LIBOBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBOBJS) + +clean: + rm -f $(OBJS) stm32flash + cd parsers && $(MAKE) $@ + +install: all + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -m 755 stm32flash $(DESTDIR)$(PREFIX)/bin + $(INSTALL) -d $(DESTDIR)$(PREFIX)/share/man/man1 + $(INSTALL) -m 644 stm32flash.1 $(DESTDIR)$(PREFIX)/share/man/man1 + +.PHONY: all clean install diff --git a/tools/win/src/stm32flash_serial/src/TODO b/tools/win/src/stm32flash_serial/src/TODO new file mode 100644 index 0000000..41df614 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/TODO @@ -0,0 +1,7 @@ + +stm32: +- Add support for variable page size + +AUTHORS: +- Add contributors from Geoffrey's commits + diff --git a/tools/win/src/stm32flash_serial/src/dev_table.c b/tools/win/src/stm32flash_serial/src/dev_table.c new file mode 100644 index 0000000..399cd9d --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/dev_table.c @@ -0,0 +1,70 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2014 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. +*/ + +#include "stm32.h" + +/* + * Device table, corresponds to the "Bootloader device-dependant parameters" + * table in ST document AN2606. + * Note that the option bytes upper range is inclusive! + */ +const stm32_dev_t devices[] = { + /* F0 */ + {0x440, "STM32F051xx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x444, "STM32F030/F031" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 4, 1024, 0x1FFFF800, 0x1FFFF80B, 0x1FFFEC00, 0x1FFFF800}, + {0x445, "STM32F042xx" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800}, + {0x448, "STM32F072xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800}, + /* F1 */ + {0x412, "Low-density" , 0x20000200, 0x20002800, 0x08000000, 0x08008000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x410, "Medium-density" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x414, "High-density" , 0x20000200, 0x20010000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x420, "Medium-density VL" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x428, "High-density VL" , 0x20000200, 0x20008000, 0x08000000, 0x08080000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x418, "Connectivity line" , 0x20001000, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFB000, 0x1FFFF800}, + {0x430, "XL-density" , 0x20000800, 0x20018000, 0x08000000, 0x08100000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFE000, 0x1FFFF800}, + /* Note that F2 and F4 devices have sectors of different page sizes + and only the first sectors (of one page size) are included here */ + /* F2 */ + {0x411, "STM32F2xx" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* F3 */ + {0x432, "STM32F373/8" , 0x20001400, 0x20008000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x422, "F302xB/303xB/358" , 0x20001400, 0x20010000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x439, "STM32F302x4(6/8)" , 0x20001800, 0x20004000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + {0x438, "F303x4/334/328" , 0x20001800, 0x20003000, 0x08000000, 0x08040000, 2, 2048, 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800}, + /* F4 */ + {0x413, "STM32F40/1" , 0x20002000, 0x20020000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77DF}, + /* 0x419 is also used for STM32F429/39 but with other bootloader ID... */ + {0x419, "STM32F427/37" , 0x20002000, 0x20030000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x423, "STM32F401xB(C)" , 0x20003000, 0x20010000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08100000, 4, 16384, 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF77FF}, + /* L0 */ + {0x417, "L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, 128, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + /* L1 */ + {0x416, "L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x429, "L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF01000}, + {0x427, "L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, 256, 0x1FF80000, 0x1FF8000F, 0x1FF00000, 0x1FF02000}, + {0x436, "L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + {0x437, "L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08060000, 16, 256, 0x1ff80000, 0x1ff8000F, 0x1FF00000, 0x1FF02000}, + /* These are not (yet) in AN2606: */ + {0x641, "Medium_Density PL" , 0x20000200, 0x00005000, 0x08000000, 0x08020000, 4, 1024, 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800}, + {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 1, 1024, 0, 0, 0, 0}, + {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 1, 2048, 0, 0, 0, 0}, + {0x0} +}; diff --git a/tools/win/src/stm32flash_serial/src/gpl-2.0.txt b/tools/win/src/stm32flash_serial/src/gpl-2.0.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/gpl-2.0.txt @@ -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/tools/win/src/stm32flash_serial/src/i2c.c b/tools/win/src/stm32flash_serial/src/i2c.c new file mode 100644 index 0000000..10e6bb1 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/i2c.c @@ -0,0 +1,209 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + + +#if !defined(__linux__) + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + return PORT_ERR_NODEV; +} + +struct port_interface port_i2c = { + .name = "i2c", + .open = i2c_open, +}; + +#else + +#ifdef __ANDROID__ +#define I2C_SLAVE 0x0703 /* Use this slave address */ +#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */ +/* To determine what functionality is present */ +#define I2C_FUNC_I2C 0x00000001 +#else +#include +#include +#endif + +#include + +struct i2c_priv { + int fd; + int addr; +}; + +static port_err_t i2c_open(struct port_interface *port, + struct port_options *ops) +{ + struct i2c_priv *h; + int fd, addr, ret; + unsigned long funcs; + + /* 1. check device name match */ + if (strncmp(ops->device, "/dev/i2c-", strlen("/dev/i2c-"))) + return PORT_ERR_NODEV; + + /* 2. check options */ + addr = ops->bus_addr; + if (addr < 0x03 || addr > 0x77) { + fprintf(stderr, "I2C address out of range [0x03-0x77]\n"); + return PORT_ERR_UNKNOWN; + } + + /* 3. open it */ + h = calloc(sizeof(*h), 1); + if (h == NULL) { + fprintf(stderr, "End of memory\n"); + return PORT_ERR_UNKNOWN; + } + fd = open(ops->device, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open special file \"%s\"\n", + ops->device); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 3.5. Check capabilities */ + ret = ioctl(fd, I2C_FUNCS, &funcs); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(funcs) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + if ((funcs & I2C_FUNC_I2C) == 0) { + fprintf(stderr, "Error: controller is not I2C, only SMBUS.\n"); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + /* 4. set options */ + ret = ioctl(fd, I2C_SLAVE, addr); + if (ret < 0) { + fprintf(stderr, "I2C ioctl(slave) error %d\n", errno); + close(fd); + free(h); + return PORT_ERR_UNKNOWN; + } + + h->fd = fd; + h->addr = addr; + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t i2c_close(struct port_interface *port) +{ + struct i2c_priv *h; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + close(h->fd); + free(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t i2c_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = read(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + struct i2c_priv *h; + int ret; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + ret = write(h->fd, buf, nbyte); + if (ret != nbyte) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; +} + +static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n, + int level) +{ + return PORT_ERR_OK; +} + +static const char *i2c_get_cfg_str(struct port_interface *port) +{ + struct i2c_priv *h; + static char str[11]; + + h = (struct i2c_priv *)port->private; + if (h == NULL) + return "INVALID"; + snprintf(str, sizeof(str), "addr 0x%2x", h->addr); + return str; +} + +static struct varlen_cmd i2c_cmd_get_reply[] = { + {0x10, 11}, + {0x11, 17}, + {0x12, 18}, + { /* sentinel */ } +}; + +struct port_interface port_i2c = { + .name = "i2c", + .flags = PORT_STRETCH_W, + .open = i2c_open, + .close = i2c_close, + .read = i2c_read, + .write = i2c_write, + .gpio = i2c_gpio, + .cmd_get_reply = i2c_cmd_get_reply, + .get_cfg_str = i2c_get_cfg_str, +}; + +#endif diff --git a/tools/win/src/stm32flash_serial/src/init.c b/tools/win/src/stm32flash_serial/src/init.c new file mode 100644 index 0000000..77a571b --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/init.c @@ -0,0 +1,219 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "init.h" +#include "serial.h" +#include "stm32.h" +#include "port.h" + +struct gpio_list { + struct gpio_list *next; + int gpio; +}; + + +static int write_to(const char *filename, const char *value) +{ + int fd, ret; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Cannot open file \"%s\"\n", filename); + return 0; + } + ret = write(fd, value, strlen(value)); + if (ret < 0) { + fprintf(stderr, "Error writing in file \"%s\"\n", filename); + close(fd); + return 0; + } + close(fd); + return 1; +} + +#if !defined(__linux__) +static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) +{ + fprintf(stderr, "GPIO control only available in Linux\n"); + return 0; +} +#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 */ + struct stat buf; + struct gpio_list *new; + int ret; + + sprintf(file, "/sys/class/gpio/gpio%d/direction", n); + ret = stat(file, &buf); + if (ret) { + /* file miss, GPIO not exported yet */ + sprintf(num, "%d", n); + ret = write_to("/sys/class/gpio/export", num); + if (!ret) + return 0; + ret = stat(file, &buf); + if (ret) { + fprintf(stderr, "GPIO %d not available\n", n); + return 0; + } + new = (struct gpio_list *)malloc(sizeof(struct gpio_list)); + if (new == NULL) { + fprintf(stderr, "Out of memory\n"); + return 0; + } + new->gpio = n; + new->next = *gpio_to_release; + *gpio_to_release = new; + } + + return write_to(file, level ? "high" : "low"); +} +#endif + +static int release_gpio(int n) +{ + char num[16]; /* sized to carry MAX_INT */ + + sprintf(num, "%d", n); + return write_to("/sys/class/gpio/unexport", num); +} + +static int gpio_sequence(struct port_interface *port, const char *s, size_t l) +{ + struct gpio_list *gpio_to_release = NULL, *to_free; + int ret, level, gpio; + + ret = 1; + while (ret == 1 && *s && l > 0) { + if (*s == '-') { + level = 0; + s++; + l--; + } else + level = 1; + + if (isdigit(*s)) { + gpio = atoi(s); + while (isdigit(*s)) { + s++; + l--; + } + } else if (!strncmp(s, "rts", 3)) { + gpio = -GPIO_RTS; + s += 3; + l -= 3; + } else if (!strncmp(s, "dtr", 3)) { + gpio = -GPIO_DTR; + s += 3; + l -= 3; + } else if (!strncmp(s, "brk", 3)) { + 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)) { + if (*s == ',') { + s++; + l--; + } else { + fprintf(stderr, "Character \'%c\' is not a separator\n", *s); + ret = 0; + break; + } + } + if (gpio < 0) + ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); + else + ret = drive_gpio(gpio, level, &gpio_to_release); + usleep(100000); + } + + while (gpio_to_release) { + release_gpio(gpio_to_release->gpio); + to_free = gpio_to_release; + gpio_to_release = gpio_to_release->next; + free(to_free); + } + usleep(500000); + return ret; +} + +static int gpio_bl_entry(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL || seq[0] == ':') + return 1; + + s = strchr(seq, ':'); + if (s == NULL) + return gpio_sequence(port, seq, strlen(seq)); + + return gpio_sequence(port, seq, s - seq); +} + +static int gpio_bl_exit(struct port_interface *port, const char *seq) +{ + char *s; + + if (seq == NULL) + return 1; + + s = strchr(seq, ':'); + if (s == NULL || s[1] == '\0') + return 1; + + return gpio_sequence(port, s + 1, strlen(s + 1)); +} + +int init_bl_entry(struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_entry(port, seq); + + return 1; +} + +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) +{ + if (seq) + return gpio_bl_exit(port, seq); + + if (stm32_reset_device(stm) != STM32_ERR_OK) + return 0; + return 1; +} diff --git a/tools/win/src/stm32flash_serial/src/init.h b/tools/win/src/stm32flash_serial/src/init.h new file mode 100644 index 0000000..6075b51 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/init.h @@ -0,0 +1,31 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2013 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 _INIT_H +#define _INIT_H + +#include "stm32.h" +#include "port.h" + +int init_bl_entry(struct port_interface *port, const char *seq); +int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq); + +#endif diff --git a/tools/win/src/stm32flash_serial/src/main.c b/tools/win/src/stm32flash_serial/src/main.c new file mode 100644 index 0000000..f081d61 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/main.c @@ -0,0 +1,774 @@ +/* + 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 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "utils.h" +#include "serial.h" +#include "stm32.h" +#include "parsers/parser.h" +#include "port.h" + +#include "parsers/binary.h" +#include "parsers/hex.h" + +#define VERSION "Arduino_STM32_0.9" + +/* device globals */ +stm32_t *stm = NULL; + +void *p_st = NULL; +parser_t *parser = NULL; + +/* settings */ +struct port_options port_opts = { + .device = NULL, + .baudRate = SERIAL_BAUD_57600, + .serial_mode = "8e1", + .bus_addr = 0, + .rx_frame_max = STM32_MAX_RX_FRAME, + .tx_frame_max = STM32_MAX_TX_FRAME, +}; +int rd = 0; +int wr = 0; +int wu = 0; +int rp = 0; +int ur = 0; +int eraseOnly = 0; +int crc = 0; +int npages = 0; +int spage = 0; +int no_erase = 0; +char verify = 0; +int retry = 10; +char exec_flag = 0; +uint32_t execute = 0; +char init_flag = 1; +char force_binary = 0; +char reset_flag = 0; +char *filename; +char *gpio_seq = NULL; +uint32_t start_addr = 0; +uint32_t readwrite_len = 0; + +/* functions */ +int parse_options(int argc, char *argv[]); +void show_help(char *name); + +static int is_addr_in_ram(uint32_t addr) +{ + return addr >= stm->dev->ram_start && addr < stm->dev->ram_end; +} + +static int is_addr_in_flash(uint32_t addr) +{ + return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; +} + +static int flash_addr_to_page_floor(uint32_t addr) +{ + if (!is_addr_in_flash(addr)) + return 0; + + return (addr - stm->dev->fl_start) / stm->dev->fl_ps; +} + +static int flash_addr_to_page_ceil(uint32_t addr) +{ + if (!(addr >= stm->dev->fl_start && addr <= stm->dev->fl_end)) + return 0; + + return (addr + stm->dev->fl_ps - 1 - stm->dev->fl_start) + / stm->dev->fl_ps; +} + +static uint32_t flash_page_to_addr(int page) +{ + return stm->dev->fl_start + page * stm->dev->fl_ps; +} + +int main(int argc, char* argv[]) { + struct port_interface *port = NULL; + int ret = 1; + stm32_err_t s_err; + parser_err_t perr; + FILE *diag = stdout; + + fprintf(diag, "stm32flash " VERSION "\n\n"); + fprintf(diag, "http://github.com/rogerclarkmelbourne/arduino_stm32\n\n"); + if (parse_options(argc, argv) != 0) + goto close; + + if (rd && filename[0] == '-') { + diag = stderr; + } + + if (wr) { + /* first try hex */ + if (!force_binary) { + parser = &PARSER_HEX; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (force_binary || (perr = parser->open(p_st, filename, 0)) != PARSER_ERR_OK) { + if (force_binary || perr == PARSER_ERR_INVALID_FILE) { + if (!force_binary) { + parser->close(p_st); + p_st = NULL; + } + + /* now try binary */ + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + perr = parser->open(p_st, filename, 0); + } + + /* if still have an error, fail */ + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) perror(filename); + goto close; + } + } + + fprintf(diag, "Using Parser : %s\n", parser->name); + } else { + parser = &PARSER_BINARY; + p_st = parser->init(); + if (!p_st) { + fprintf(stderr, "%s Parser failed to initialize\n", parser->name); + goto close; + } + } + + if (port_open(&port_opts, &port) != PORT_ERR_OK) { + fprintf(stderr, "Failed to open port: %s\n", port_opts.device); + goto close; + } + + fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); + if (init_flag && init_bl_entry(port, gpio_seq) == 0) + goto close; + stm = stm32_init(port, init_flag); + if (!stm) + goto close; + + fprintf(diag, "Version : 0x%02x\n", stm->bl_version); + if (port->flags & PORT_GVR_ETX) { + fprintf(diag, "Option 1 : 0x%02x\n", stm->option1); + fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); + } + fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name); + fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); + fprintf(diag, "- Flash : %dKiB (sector size: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps); + 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); + + uint8_t buffer[256]; + uint32_t addr, start, end; + unsigned int len; + int failed = 0; + int first_page, num_pages; + + /* + * Cleanup addresses: + * + * Starting from options + * start_addr, readwrite_len, spage, npages + * and using device memory size, compute + * start, end, first_page, num_pages + */ + if (start_addr || readwrite_len) { + start = start_addr; + + if (is_addr_in_flash(start)) + end = stm->dev->fl_end; + else { + no_erase = 1; + if (is_addr_in_ram(start)) + end = stm->dev->ram_end; + else + end = start + sizeof(uint32_t); + } + + if (readwrite_len && (end > start + readwrite_len)) + end = start + readwrite_len; + + first_page = flash_addr_to_page_floor(start); + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + else + num_pages = flash_addr_to_page_ceil(end) - first_page; + } else if (!spage && !npages) { + start = stm->dev->fl_start; + end = stm->dev->fl_end; + first_page = 0; + num_pages = 0xff; /* mass erase */ + } else { + first_page = spage; + start = flash_page_to_addr(first_page); + if (start > stm->dev->fl_end) { + fprintf(stderr, "Address range exceeds flash size.\n"); + goto close; + } + + if (npages) { + num_pages = npages; + end = flash_page_to_addr(first_page + num_pages); + if (end > stm->dev->fl_end) + end = stm->dev->fl_end; + } else { + end = stm->dev->fl_end; + num_pages = flash_addr_to_page_ceil(end) - first_page; + } + + if (!first_page && end == stm->dev->fl_end) + num_pages = 0xff; /* mass erase */ + } + + if (rd) { + unsigned int max_len = port_opts.rx_frame_max; + + fprintf(diag, "Memory read\n"); + + perr = parser->open(p_st, filename, 1); + if (perr != PARSER_ERR_OK) { + fprintf(stderr, "%s ERROR: %s\n", parser->name, parser_errstr(perr)); + if (perr == PARSER_ERR_SYSTEM) + perror(filename); + goto close; + } + + fflush(diag); + addr = start; + while(addr < end) { + uint32_t left = end - addr; + len = max_len > left ? left : max_len; + s_err = stm32_read_memory(stm, addr, buffer, len); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x, target write-protected?\n", addr); + goto close; + } + if (parser->write(p_st, buffer, len) != PARSER_ERR_OK) + { + fprintf(stderr, "Failed to write data to file\n"); + goto close; + } + addr += len; + + fprintf(diag, + "\rRead address 0x%08x (%.2f%%) ", + addr, + (100.0f / (float)(end - start)) * (float)(addr - start) + ); + fflush(diag); + } + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (rp) { + fprintf(stdout, "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"); + /* 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) { + ret = 0; + fprintf(stdout, "Erasing flash\n"); + + if (num_pages != 0xff && + (start != flash_page_to_addr(first_page) + || end != flash_page_to_addr(first_page + num_pages))) { + fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); + ret = 1; + goto close; + } + + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + ret = 1; + goto close; + } + } else if (wu) { + fprintf(diag, "Write-unprotecting flash\n"); + /* the device automatically performs a reset after the sending the ACK */ + reset_flag = 0; + stm32_wunprot_memory(stm); + fprintf(diag, "Done.\n"); + + } else if (wr) { + fprintf(diag, "Write to memory\n"); + + off_t offset = 0; + ssize_t r; + unsigned int size; + unsigned int max_wlen, max_rlen; + + max_wlen = port_opts.tx_frame_max - 2; /* skip len and crc */ + max_wlen &= ~3; /* 32 bit aligned */ + + max_rlen = port_opts.rx_frame_max; + max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; + + /* Assume data from stdin is whole device */ + if (filename[0] == '-' && filename[1] == '\0') + 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) { + // 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 + if (!no_erase && num_pages) { + fprintf(diag, "Erasing memory\n"); + s_err = stm32_erase_memory(stm, first_page, num_pages); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to erase memory\n"); + goto close; + } + } + + fflush(diag); + addr = start; + while(addr < end && offset < size) { + uint32_t left = end - addr; + len = max_wlen > left ? left : max_wlen; + len = len > size - offset ? size - offset : len; + + if (parser->read(p_st, buffer, &len) != PARSER_ERR_OK) + goto close; + + if (len == 0) { + if (filename[0] == '-') { + 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) { + fprintf(stderr, "Failed to write memory at address 0x%08x\n", addr); + goto close; + } + + if (verify) { + uint8_t compare[len]; + unsigned int offset, rlen; + + offset = 0; + while (offset < len) { + rlen = len - offset; + rlen = rlen < max_rlen ? rlen : max_rlen; + s_err = stm32_read_memory(stm, addr + offset, compare + offset, rlen); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read memory at address 0x%08x\n", addr + offset); + goto close; + } + offset += rlen; + } + + for(r = 0; r < len; ++r) + if (buffer[r] != compare[r]) { + if (failed == retry) { + fprintf(stderr, "Failed to verify at address 0x%08x, expected 0x%02x and found 0x%02x\n", + (uint32_t)(addr + r), + buffer [r], + compare[r] + ); + goto close; + } + ++failed; + goto again; + } + + failed = 0; + } + + addr += len; + offset += len; + + fprintf(diag, + "\rWrote %saddress 0x%08x (%.2f%%) ", + verify ? "and verified " : "", + addr, + (100.0f / size) * offset + ); + fflush(diag); + + } + + fprintf(diag, "Done.\n"); + ret = 0; + goto close; + } else if (crc) { + uint32_t crc_val = 0; + + fprintf(diag, "CRC computation\n"); + + s_err = stm32_crc_wrapper(stm, start, end - start, &crc_val); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read CRC\n"); + goto close; + } + fprintf(diag, "CRC(0x%08x-0x%08x) = 0x%08x\n", start, end, + crc_val); + ret = 0; + goto close; + } else + ret = 0; + +close: + if (stm && exec_flag && ret == 0) { + if (execute == 0) + execute = stm->dev->fl_start; + + fprintf(diag, "\nStarting execution at address 0x%08x... ", execute); + fflush(diag); + if (stm32_go(stm, execute) == STM32_ERR_OK) { + reset_flag = 0; + fprintf(diag, "done.\n"); + } else + fprintf(diag, "failed.\n"); + } + + if (stm && reset_flag) { + fprintf(diag, "\nResetting device... "); + fflush(diag); + if (init_bl_exit(stm, port, gpio_seq)) + fprintf(diag, "done.\n"); + else fprintf(diag, "failed.\n"); + } + + if (p_st ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) + port->close(port); + + fprintf(diag, "\n"); + return ret; +} + +int parse_options(int argc, char *argv[]) +{ + int c; + char *pLen; + + while ((c = getopt(argc, argv, "a:b:m:r:w:e:vn:g:jkfcChuos:S:F:i:R")) != -1) { + switch(c) { + case 'a': + port_opts.bus_addr = strtoul(optarg, NULL, 0); + break; + + case 'b': + port_opts.baudRate = serial_get_baud(strtoul(optarg, NULL, 0)); + if (port_opts.baudRate == SERIAL_BAUD_INVALID) { + serial_baud_t baudrate; + fprintf(stderr, "Invalid baud rate, valid options are:\n"); + for (baudrate = SERIAL_BAUD_1200; baudrate != SERIAL_BAUD_INVALID; ++baudrate) + fprintf(stderr, " %d\n", serial_get_baud_int(baudrate)); + return 1; + } + break; + + case 'm': + if (strlen(optarg) != 3 + || serial_get_bits(optarg) == SERIAL_BITS_INVALID + || serial_get_parity(optarg) == SERIAL_PARITY_INVALID + || serial_get_stopbit(optarg) == SERIAL_STOPBIT_INVALID) { + fprintf(stderr, "Invalid serial mode\n"); + return 1; + } + port_opts.serial_mode = optarg; + break; + + 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"); + return 1; + } + filename = optarg; + if (filename[0] == '-') { + force_binary = 1; + } + break; + case 'e': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + npages = strtoul(optarg, NULL, 0); + if (npages > 0xFF || npages < 0) { + fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); + return 1; + } + if (!npages) + no_erase = 1; + break; + case 'u': + wu = 1; + if (rd || wr) { + fprintf(stderr, "ERROR: Invalid options, can't write unprotect and read/write at the same time\n"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + 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"); + return 1; + } + break; + + case 'v': + verify = 1; + break; + + case 'n': + retry = strtoul(optarg, NULL, 0); + break; + + case 'g': + exec_flag = 1; + execute = strtoul(optarg, NULL, 0); + if (execute % 4 != 0) { + fprintf(stderr, "ERROR: Execution address must be word-aligned\n"); + return 1; + } + break; + case 's': + if (readwrite_len || start_addr) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } + spage = strtoul(optarg, NULL, 0); + break; + case 'S': + if (spage || npages) { + fprintf(stderr, "ERROR: Invalid options, can't specify start page / num pages and start address/length\n"); + return 1; + } else { + start_addr = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + readwrite_len = strtoul(pLen, NULL, 0); + if (readwrite_len == 0) { + fprintf(stderr, "ERROR: Invalid options, can't specify zero length\n"); + return 1; + } + } + } + break; + case 'F': + port_opts.rx_frame_max = strtoul(optarg, &pLen, 0); + if (*pLen == ':') { + pLen++; + port_opts.tx_frame_max = strtoul(pLen, NULL, 0); + } + if (port_opts.rx_frame_max < 0 + || port_opts.tx_frame_max < 0) { + fprintf(stderr, "ERROR: Invalid negative value for option -F\n"); + return 1; + } + if (port_opts.rx_frame_max == 0) + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + if (port_opts.tx_frame_max == 0) + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + if (port_opts.rx_frame_max < 20 + || port_opts.tx_frame_max < 5) { + fprintf(stderr, "ERROR: current code cannot work with small frames.\n"); + fprintf(stderr, "min(RX) = 20, min(TX) = 5\n"); + return 1; + } + if (port_opts.rx_frame_max > STM32_MAX_RX_FRAME) { + fprintf(stderr, "WARNING: Ignore RX length in option -F\n"); + port_opts.rx_frame_max = STM32_MAX_RX_FRAME; + } + if (port_opts.tx_frame_max > STM32_MAX_TX_FRAME) { + fprintf(stderr, "WARNING: Ignore TX length in option -F\n"); + port_opts.tx_frame_max = STM32_MAX_TX_FRAME; + } + break; + case 'f': + force_binary = 1; + break; + + case 'c': + init_flag = 0; + break; + + case 'h': + show_help(argv[0]); + exit(0); + + case 'i': + gpio_seq = optarg; + break; + + case 'R': + reset_flag = 1; + break; + + case 'C': + crc = 1; + break; + } + } + + for (c = optind; c < argc; ++c) { + if (port_opts.device) { + fprintf(stderr, "ERROR: Invalid parameter specified\n"); + show_help(argv[0]); + return 1; + } + port_opts.device = argv[c]; + } + + if (port_opts.device == NULL) { + fprintf(stderr, "ERROR: Device not specified\n"); + show_help(argv[0]); + return 1; + } + + if (!wr && verify) { + fprintf(stderr, "ERROR: Invalid usage, -v is only valid when writing\n"); + show_help(argv[0]); + return 1; + } + + return 0; +} + +void show_help(char *name) { + fprintf(stderr, + "Usage: %s [-bvngfhc] [-[rw] filename] [tty_device | i2c_device]\n" + " -a bus_address Bus address (e.g. for I2C port)\n" + " -b rate Baud rate (default 57600)\n" + " -m mode Serial port mode (default 8e1)\n" + " -r filename Read flash to file (or - stdout)\n" + " -w filename Write flash from file (or - stdout)\n" + " -C Compute CRC of flash content\n" + " -u Disable the flash write-protection\n" + " -j Enable the flash read-protection\n" + " -k Disable the flash read-protection\n" + " -o Erase only\n" + " -e n Only erase n pages before writing the flash\n" + " -v Verify writes\n" + " -n count Retry failed writes up to count times (default 10)\n" + " -g address Start execution at specified address (0 = flash start)\n" + " -S address[:length] Specify start address and optionally length for\n" + " read/write/erase operations\n" + " -F RX_length[:TX_length] Specify the max length of RX and TX frame\n" + " -s start_page Flash at specified page (0 = flash start)\n" + " -f Force binary parser\n" + " -h Show this help\n" + " -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" + " -i GPIO_string GPIO sequence to enter/exit bootloader mode\n" + " GPIO_string=[entry_seq][:[exit_seq]]\n" + " sequence=[-]n[,sequence]\n" + " -R Reset device at exit.\n" + "\n" + "Examples:\n" + " Get device information:\n" + " %s /dev/ttyS0\n" + " or:\n" + " %s /dev/i2c-0\n" + "\n" + " Write with verify and then start execution:\n" + " %s -w filename -v -g 0x0 /dev/ttyS0\n" + "\n" + " Read flash to file:\n" + " %s -r filename /dev/ttyS0\n" + "\n" + " Read 100 bytes of flash from 0x1000 to stdout:\n" + " %s -r - -S 0x1000:100 /dev/ttyS0\n" + "\n" + " Start execution:\n" + " %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", + name, + name, + name, + name, + name, + name, + name, + name + ); +} + diff --git a/tools/win/src/stm32flash_serial/src/parsers/Android.mk b/tools/win/src/stm32flash_serial/src/parsers/Android.mk new file mode 100644 index 0000000..afec18c --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/Android.mk @@ -0,0 +1,6 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libparsers +LOCAL_SRC_FILES := binary.c hex.c +include $(BUILD_STATIC_LIBRARY) diff --git a/tools/win/src/stm32flash_serial/src/parsers/Makefile b/tools/win/src/stm32flash_serial/src/parsers/Makefile new file mode 100644 index 0000000..bb7df1e --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/Makefile @@ -0,0 +1,12 @@ + +CFLAGS += -Wall -g + +all: parsers.a + +parsers.a: binary.o hex.o + $(AR) rc $@ binary.o hex.o + +clean: + rm -f *.o parsers.a + +.PHONY: all clean diff --git a/tools/win/src/stm32flash_serial/src/parsers/binary.c b/tools/win/src/stm32flash_serial/src/parsers/binary.c new file mode 100644 index 0000000..f491952 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/binary.c @@ -0,0 +1,140 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include + +#include "binary.h" + +typedef struct { + int fd; + char write; + struct stat stat; +} binary_t; + +void* binary_init() { + return calloc(sizeof(binary_t), 1); +} + +parser_err_t binary_open(void *storage, const char *filename, const char write) { + binary_t *st = storage; + if (write) { + if (filename[0] == '-') + st->fd = 1; + else + st->fd = open( + filename, +#ifndef __WIN32__ + O_WRONLY | O_CREAT | O_TRUNC, +#else + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, +#endif +#ifndef __WIN32__ + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH +#else + 0 +#endif + ); + st->stat.st_size = 0; + } else { + if (filename[0] == '-') { + st->fd = 0; + } else { + if (stat(filename, &st->stat) != 0) + return PARSER_ERR_INVALID_FILE; + st->fd = open(filename, +#ifndef __WIN32__ + O_RDONLY +#else + O_RDONLY | O_BINARY +#endif + ); + } + } + + st->write = write; + return st->fd == -1 ? PARSER_ERR_SYSTEM : PARSER_ERR_OK; +} + +parser_err_t binary_close(void *storage) { + binary_t *st = storage; + + if (st->fd) close(st->fd); + free(st); + return PARSER_ERR_OK; +} + +unsigned int binary_size(void *storage) { + binary_t *st = storage; + return st->stat.st_size; +} + +parser_err_t binary_read(void *storage, void *data, unsigned int *len) { + binary_t *st = storage; + unsigned int left = *len; + if (st->write) return PARSER_ERR_WRONLY; + + ssize_t r; + while(left > 0) { + r = read(st->fd, data, left); + /* If there is no data to read at all, return OK, but with zero read */ + if (r == 0 && left == *len) { + *len = 0; + return PARSER_ERR_OK; + } + if (r <= 0) return PARSER_ERR_SYSTEM; + left -= r; + data += r; + } + + *len = *len - left; + return PARSER_ERR_OK; +} + +parser_err_t binary_write(void *storage, void *data, unsigned int len) { + binary_t *st = storage; + if (!st->write) return PARSER_ERR_RDONLY; + + ssize_t r; + while(len > 0) { + r = write(st->fd, data, len); + if (r < 1) return PARSER_ERR_SYSTEM; + st->stat.st_size += r; + + len -= r; + data += r; + } + + return PARSER_ERR_OK; +} + +parser_t PARSER_BINARY = { + "Raw BINARY", + binary_init, + binary_open, + binary_close, + binary_size, + binary_read, + binary_write +}; + diff --git a/tools/win/src/stm32flash_serial/src/parsers/binary.h b/tools/win/src/stm32flash_serial/src/parsers/binary.h new file mode 100644 index 0000000..d989acf --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/binary.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_BINARY_H +#define _PARSER_BINARY_H + +#include "parser.h" + +extern parser_t PARSER_BINARY; +#endif diff --git a/tools/win/src/stm32flash_serial/src/parsers/hex.c b/tools/win/src/stm32flash_serial/src/parsers/hex.c new file mode 100644 index 0000000..3baf856 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/hex.c @@ -0,0 +1,224 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#include "hex.h" +#include "../utils.h" + +typedef struct { + size_t data_len, offset; + uint8_t *data; + uint8_t base; +} hex_t; + +void* hex_init() { + return calloc(sizeof(hex_t), 1); +} + +parser_err_t hex_open(void *storage, const char *filename, const char write) { + hex_t *st = storage; + if (write) { + return PARSER_ERR_RDONLY; + } else { + char mark; + int i, fd; + uint8_t checksum; + unsigned int c; + uint32_t base = 0; + unsigned int last_address = 0x0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return PARSER_ERR_SYSTEM; + + /* read in the file */ + + while(read(fd, &mark, 1) != 0) { + if (mark == '\n' || mark == '\r') continue; + if (mark != ':') + return PARSER_ERR_INVALID_FILE; + + char buffer[9]; + unsigned int reclen, address, type; + uint8_t *record = NULL; + + /* get the reclen, address, and type */ + buffer[8] = 0; + if (read(fd, &buffer, 8) != 8) return PARSER_ERR_INVALID_FILE; + if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* setup the checksum */ + checksum = + reclen + + ((address & 0xFF00) >> 8) + + ((address & 0x00FF) >> 0) + + type; + + switch(type) { + /* data record */ + case 0: + c = address - last_address; + st->data = realloc(st->data, st->data_len + c + reclen); + + /* if there is a gap, set it to 0xff and increment the length */ + if (c > 0) { + memset(&st->data[st->data_len], 0xff, c); + st->data_len += c; + } + + last_address = address + reclen; + record = &st->data[st->data_len]; + st->data_len += reclen; + break; + + /* extended segment address record */ + case 2: + base = 0; + break; + + /* extended linear address record */ + case 4: + base = address; + break; + } + + buffer[2] = 0; + for(i = 0; i < reclen; ++i) { + if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* add the byte to the checksum */ + checksum += c; + + switch(type) { + case 0: + if (record != NULL) { + record[i] = c; + } else { + return PARSER_ERR_INVALID_FILE; + } + break; + + case 2: + case 4: + base = (base << 8) | c; + break; + } + } + + /* read, scan, and verify the checksum */ + if ( + read(fd, &buffer, 2 ) != 2 || + sscanf(buffer, "%2x", &c) != 1 || + (uint8_t)(checksum + c) != 0x00 + ) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + switch(type) { + /* EOF */ + case 1: + close(fd); + return PARSER_ERR_OK; + + /* address record */ + 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) { + st->base = base; + break; + } + + /* we cant cope with files out of order */ + if (base < st->base) { + close(fd); + return PARSER_ERR_INVALID_FILE; + } + + /* if there is a gap, enlarge and fill with zeros */ + 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); + st->data_len = len; + } + break; + } + } + + close(fd); + return PARSER_ERR_OK; + } +} + +parser_err_t hex_close(void *storage) { + hex_t *st = storage; + if (st) free(st->data); + free(st); + return PARSER_ERR_OK; +} + +unsigned int hex_size(void *storage) { + hex_t *st = storage; + return st->data_len; +} + +parser_err_t hex_read(void *storage, void *data, unsigned int *len) { + hex_t *st = storage; + unsigned int left = st->data_len - st->offset; + unsigned int get = left > *len ? *len : left; + + memcpy(data, &st->data[st->offset], get); + st->offset += get; + + *len = get; + return PARSER_ERR_OK; +} + +parser_err_t hex_write(void *storage, void *data, unsigned int len) { + return PARSER_ERR_RDONLY; +} + +parser_t PARSER_HEX = { + "Intel HEX", + hex_init, + hex_open, + hex_close, + hex_size, + hex_read, + hex_write +}; + diff --git a/tools/win/src/stm32flash_serial/src/parsers/hex.h b/tools/win/src/stm32flash_serial/src/parsers/hex.h new file mode 100644 index 0000000..02413c9 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/hex.h @@ -0,0 +1,27 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _PARSER_HEX_H +#define _PARSER_HEX_H + +#include "parser.h" + +extern parser_t PARSER_HEX; +#endif diff --git a/tools/win/src/stm32flash_serial/src/parsers/parser.h b/tools/win/src/stm32flash_serial/src/parsers/parser.h new file mode 100644 index 0000000..c2fae3c --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/parsers/parser.h @@ -0,0 +1,56 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_PARSER +#define _H_PARSER + +enum parser_err { + PARSER_ERR_OK, + PARSER_ERR_SYSTEM, + PARSER_ERR_INVALID_FILE, + PARSER_ERR_WRONLY, + PARSER_ERR_RDONLY +}; +typedef enum parser_err parser_err_t; + +struct parser { + const char *name; + void* (*init )(); /* initialise the parser */ + parser_err_t (*open )(void *storage, const char *filename, const char write); /* open the file for read|write */ + parser_err_t (*close)(void *storage); /* close and free the parser */ + unsigned int (*size )(void *storage); /* get the total data size */ + parser_err_t (*read )(void *storage, void *data, unsigned int *len); /* read a block of data */ + parser_err_t (*write)(void *storage, void *data, unsigned int len); /* write a block of data */ +}; +typedef struct parser parser_t; + +static inline const char* parser_errstr(parser_err_t err) { + switch(err) { + case PARSER_ERR_OK : return "OK"; + case PARSER_ERR_SYSTEM : return "System Error"; + case PARSER_ERR_INVALID_FILE: return "Invalid File"; + case PARSER_ERR_WRONLY : return "Parser can only write"; + case PARSER_ERR_RDONLY : return "Parser can only read"; + default: + return "Unknown Error"; + } +} + +#endif diff --git a/tools/win/src/stm32flash_serial/src/port.c b/tools/win/src/stm32flash_serial/src/port.c new file mode 100644 index 0000000..08e58cc --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/port.c @@ -0,0 +1,59 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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. +*/ + +#include +#include + +#include "serial.h" +#include "port.h" + + +extern struct port_interface port_serial; +extern struct port_interface port_i2c; + +static struct port_interface *ports[] = { + &port_serial, + &port_i2c, + NULL, +}; + + +port_err_t port_open(struct port_options *ops, struct port_interface **outport) +{ + int ret; + static struct port_interface **port; + + for (port = ports; *port; port++) { + ret = (*port)->open(*port, ops); + if (ret == PORT_ERR_NODEV) + continue; + if (ret == PORT_ERR_OK) + break; + fprintf(stderr, "Error probing interface \"%s\"\n", + (*port)->name); + } + if (*port == NULL) { + fprintf(stderr, "Cannot handle device \"%s\"\n", + ops->device); + return PORT_ERR_UNKNOWN; + } + + *outport = *port; + return PORT_ERR_OK; +} diff --git a/tools/win/src/stm32flash_serial/src/port.h b/tools/win/src/stm32flash_serial/src/port.h new file mode 100644 index 0000000..290f034 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/port.h @@ -0,0 +1,75 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2014 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_PORT +#define _H_PORT + +typedef enum { + PORT_ERR_OK = 0, + PORT_ERR_NODEV, /* No such device */ + PORT_ERR_TIMEDOUT, /* Operation timed out */ + PORT_ERR_UNKNOWN, +} port_err_t; + +/* flags */ +#define PORT_BYTE (1 << 0) /* byte (not frame) oriented */ +#define PORT_GVR_ETX (1 << 1) /* cmd GVR returns protection status */ +#define PORT_CMD_INIT (1 << 2) /* use INIT cmd to autodetect speed */ +#define PORT_RETRY (1 << 3) /* allowed read() retry after timeout */ +#define PORT_STRETCH_W (1 << 4) /* warning for no-stretching commands */ + +/* all options and flags used to open and configure an interface */ +struct port_options { + const char *device; + serial_baud_t baudRate; + const char *serial_mode; + int bus_addr; + int rx_frame_max; + int tx_frame_max; +}; + +/* + * Specify the length of reply for command GET + * This is helpful for frame-oriented protocols, e.g. i2c, to avoid time + * consuming try-fail-timeout-retry operation. + * On byte-oriented protocols, i.e. UART, this information would be skipped + * after read the first byte, so not needed. + */ +struct varlen_cmd { + uint8_t version; + uint8_t length; +}; + +struct port_interface { + const char *name; + unsigned flags; + port_err_t (*open)(struct port_interface *port, struct port_options *ops); + port_err_t (*close)(struct port_interface *port); + port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte); + port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level); + const char *(*get_cfg_str)(struct port_interface *port); + struct varlen_cmd *cmd_get_reply; + void *private; +}; + +port_err_t port_open(struct port_options *ops, struct port_interface **outport); + +#endif diff --git a/tools/win/src/stm32flash_serial/src/protocol.txt b/tools/win/src/stm32flash_serial/src/protocol.txt new file mode 100644 index 0000000..0391099 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/protocol.txt @@ -0,0 +1,19 @@ +The communication protocol used by ST bootloader is documented in following ST +application notes, depending on communication port. + +In current version of stm32flash are supported only UART and I2C ports. + +* AN3154: CAN protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf + +* AN3155: USART protocol used in the STM32(TM) bootloader + http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf + +* AN4221: I2C protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf + +* AN4286: SPI protocol used in the STM32 bootloader + http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf + +Boot mode selection for STM32 is documented in ST application note AN2606, available in ST website: + http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf diff --git a/tools/win/src/stm32flash_serial/src/serial.h b/tools/win/src/stm32flash_serial/src/serial.h new file mode 100644 index 0000000..227ba16 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/serial.h @@ -0,0 +1,90 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _SERIAL_H +#define _SERIAL_H + +typedef struct serial serial_t; + +typedef enum { + SERIAL_PARITY_NONE, + SERIAL_PARITY_EVEN, + SERIAL_PARITY_ODD, + + SERIAL_PARITY_INVALID +} serial_parity_t; + +typedef enum { + SERIAL_BITS_5, + SERIAL_BITS_6, + SERIAL_BITS_7, + SERIAL_BITS_8, + + SERIAL_BITS_INVALID +} serial_bits_t; + +typedef enum { + SERIAL_BAUD_1200, + SERIAL_BAUD_1800, + SERIAL_BAUD_2400, + SERIAL_BAUD_4800, + SERIAL_BAUD_9600, + SERIAL_BAUD_19200, + SERIAL_BAUD_38400, + SERIAL_BAUD_57600, + SERIAL_BAUD_115200, + SERIAL_BAUD_128000, + SERIAL_BAUD_230400, + SERIAL_BAUD_256000, + SERIAL_BAUD_460800, + SERIAL_BAUD_500000, + SERIAL_BAUD_576000, + SERIAL_BAUD_921600, + SERIAL_BAUD_1000000, + SERIAL_BAUD_1500000, + SERIAL_BAUD_2000000, + + SERIAL_BAUD_INVALID +} serial_baud_t; + +typedef enum { + SERIAL_STOPBIT_1, + SERIAL_STOPBIT_2, + + SERIAL_STOPBIT_INVALID +} serial_stopbit_t; + +typedef enum { + GPIO_RTS = 1, + GPIO_DTR, + GPIO_BRK, +} serial_gpio_t; + +/* common helper functions */ +serial_baud_t serial_get_baud(const unsigned int baud); +unsigned int serial_get_baud_int(const serial_baud_t baud); +serial_bits_t serial_get_bits(const char *mode); +unsigned int serial_get_bits_int(const serial_bits_t bits); +serial_parity_t serial_get_parity(const char *mode); +char serial_get_parity_str(const serial_parity_t parity); +serial_stopbit_t serial_get_stopbit(const char *mode); +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit); + +#endif diff --git a/tools/win/src/stm32flash_serial/src/serial_common.c b/tools/win/src/stm32flash_serial/src/serial_common.c new file mode 100644 index 0000000..43e48e1 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/serial_common.c @@ -0,0 +1,154 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "serial.h" + +serial_baud_t serial_get_baud(const unsigned int baud) { + switch(baud) { + case 1200: return SERIAL_BAUD_1200 ; + case 1800: return SERIAL_BAUD_1800 ; + case 2400: return SERIAL_BAUD_2400 ; + case 4800: return SERIAL_BAUD_4800 ; + case 9600: return SERIAL_BAUD_9600 ; + case 19200: return SERIAL_BAUD_19200 ; + case 38400: return SERIAL_BAUD_38400 ; + case 57600: return SERIAL_BAUD_57600 ; + case 115200: return SERIAL_BAUD_115200; + case 128000: return SERIAL_BAUD_128000; + case 230400: return SERIAL_BAUD_230400; + case 256000: return SERIAL_BAUD_256000; + case 460800: return SERIAL_BAUD_460800; + case 500000: return SERIAL_BAUD_500000; + case 576000: return SERIAL_BAUD_576000; + case 921600: return SERIAL_BAUD_921600; + case 1000000: return SERIAL_BAUD_1000000; + case 1500000: return SERIAL_BAUD_1500000; + case 2000000: return SERIAL_BAUD_2000000; + + default: + return SERIAL_BAUD_INVALID; + } +} + +unsigned int serial_get_baud_int(const serial_baud_t baud) { + switch(baud) { + case SERIAL_BAUD_1200 : return 1200 ; + case SERIAL_BAUD_1800 : return 1800 ; + case SERIAL_BAUD_2400 : return 2400 ; + case SERIAL_BAUD_4800 : return 4800 ; + case SERIAL_BAUD_9600 : return 9600 ; + case SERIAL_BAUD_19200 : return 19200 ; + case SERIAL_BAUD_38400 : return 38400 ; + case SERIAL_BAUD_57600 : return 57600 ; + case SERIAL_BAUD_115200: return 115200; + case SERIAL_BAUD_128000: return 128000; + case SERIAL_BAUD_230400: return 230400; + case SERIAL_BAUD_256000: return 256000; + case SERIAL_BAUD_460800: return 460800; + case SERIAL_BAUD_500000: return 500000; + case SERIAL_BAUD_576000: return 576000; + case SERIAL_BAUD_921600: return 921600; + case SERIAL_BAUD_1000000: return 1000000; + case SERIAL_BAUD_1500000: return 1500000; + case SERIAL_BAUD_2000000: return 2000000; + + case SERIAL_BAUD_INVALID: + default: + return 0; + } +} + +serial_bits_t serial_get_bits(const char *mode) { + if (!mode) + return SERIAL_BITS_INVALID; + switch(mode[0]) { + case '5': return SERIAL_BITS_5; + case '6': return SERIAL_BITS_6; + case '7': return SERIAL_BITS_7; + case '8': return SERIAL_BITS_8; + + default: + return SERIAL_BITS_INVALID; + } +} + +unsigned int serial_get_bits_int(const serial_bits_t bits) { + switch(bits) { + case SERIAL_BITS_5: return 5; + case SERIAL_BITS_6: return 6; + case SERIAL_BITS_7: return 7; + case SERIAL_BITS_8: return 8; + + default: + return 0; + } +} + +serial_parity_t serial_get_parity(const char *mode) { + if (!mode || !mode[0]) + return SERIAL_PARITY_INVALID; + switch(mode[1]) { + case 'N': + case 'n': + return SERIAL_PARITY_NONE; + case 'E': + case 'e': + return SERIAL_PARITY_EVEN; + case 'O': + case 'o': + return SERIAL_PARITY_ODD; + + default: + return SERIAL_PARITY_INVALID; + } +} + +char serial_get_parity_str(const serial_parity_t parity) { + switch(parity) { + case SERIAL_PARITY_NONE: return 'N'; + case SERIAL_PARITY_EVEN: return 'E'; + case SERIAL_PARITY_ODD : return 'O'; + + default: + return ' '; + } +} + +serial_stopbit_t serial_get_stopbit(const char *mode) { + if (!mode || !mode[0] || !mode[1]) + return SERIAL_STOPBIT_INVALID; + switch(mode[2]) { + case '1': return SERIAL_STOPBIT_1; + case '2': return SERIAL_STOPBIT_2; + + default: + return SERIAL_STOPBIT_INVALID; + } +} + +unsigned int serial_get_stopbit_int(const serial_stopbit_t stopbit) { + switch(stopbit) { + case SERIAL_STOPBIT_1: return 1; + case SERIAL_STOPBIT_2: return 2; + + default: + return 0; + } +} + diff --git a/tools/win/src/stm32flash_serial/src/serial_platform.c b/tools/win/src/stm32flash_serial/src/serial_platform.c new file mode 100644 index 0000000..98e2569 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/serial_platform.c @@ -0,0 +1,5 @@ +#if defined(__WIN32__) || defined(__CYGWIN__) +# include "serial_w32.c" +#else +# include "serial_posix.c" +#endif diff --git a/tools/win/src/stm32flash_serial/src/serial_posix.c b/tools/win/src/stm32flash_serial/src/serial_posix.c new file mode 100644 index 0000000..284b35b --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/serial_posix.c @@ -0,0 +1,395 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + int fd; + struct termios oldtio; + struct termios newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + + h->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (h->fd < 0) { + free(h); + return NULL; + } + fcntl(h->fd, F_SETFL, 0); + + tcgetattr(h->fd, &h->oldtio); + tcgetattr(h->fd, &h->newtio); + + return h; +} + +static void serial_flush(const serial_t *h) +{ + tcflush(h->fd, TCIFLUSH); +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + tcsetattr(h->fd, TCSANOW, &h->oldtio); + close(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + speed_t port_baud; + tcflag_t port_bits; + tcflag_t port_parity; + tcflag_t port_stop; + struct termios settings; + + switch (baud) { + case SERIAL_BAUD_1200: port_baud = B1200; break; + case SERIAL_BAUD_1800: port_baud = B1800; break; + case SERIAL_BAUD_2400: port_baud = B2400; break; + case SERIAL_BAUD_4800: port_baud = B4800; break; + case SERIAL_BAUD_9600: port_baud = B9600; break; + case SERIAL_BAUD_19200: port_baud = B19200; break; + case SERIAL_BAUD_38400: port_baud = B38400; break; + case SERIAL_BAUD_57600: port_baud = B57600; break; + case SERIAL_BAUD_115200: port_baud = B115200; break; + case SERIAL_BAUD_230400: port_baud = B230400; break; +#ifdef B460800 + case SERIAL_BAUD_460800: port_baud = B460800; break; +#endif /* B460800 */ +#ifdef B921600 + case SERIAL_BAUD_921600: port_baud = B921600; break; +#endif /* B921600 */ +#ifdef B500000 + case SERIAL_BAUD_500000: port_baud = B500000; break; +#endif /* B500000 */ +#ifdef B576000 + case SERIAL_BAUD_576000: port_baud = B576000; break; +#endif /* B576000 */ +#ifdef B1000000 + case SERIAL_BAUD_1000000: port_baud = B1000000; break; +#endif /* B1000000 */ +#ifdef B1500000 + case SERIAL_BAUD_1500000: port_baud = B1500000; break; +#endif /* B1500000 */ +#ifdef B2000000 + case SERIAL_BAUD_2000000: port_baud = B2000000; break; +#endif /* B2000000 */ + + case SERIAL_BAUD_INVALID: + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: port_bits = CS5; break; + case SERIAL_BITS_6: port_bits = CS6; break; + case SERIAL_BITS_7: port_bits = CS7; break; + case SERIAL_BITS_8: port_bits = CS8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: port_parity = 0; break; + case SERIAL_PARITY_EVEN: port_parity = PARENB; break; + case SERIAL_PARITY_ODD: port_parity = PARENB | PARODD; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: port_stop = 0; break; + case SERIAL_STOPBIT_2: port_stop = CSTOPB; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ +#ifndef __sun /* Used by GNU and BSD. Ignore __SVR4 in test. */ + cfmakeraw(&h->newtio); +#else /* __sun */ + h->newtio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR + | IGNCR | ICRNL | IXON); + if (port_parity) + h->newtio.c_iflag |= INPCK; + + h->newtio.c_oflag &= ~OPOST; + h->newtio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + h->newtio.c_cflag &= ~(CSIZE | PARENB); + h->newtio.c_cflag |= CS8; +#endif /* __sun */ +#ifdef __QNXNTO__ + h->newtio.c_cflag &= ~(CSIZE | IHFLOW | OHFLOW); +#else + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); +#endif + h->newtio.c_cflag &= ~(CSIZE | CRTSCTS); + h->newtio.c_iflag &= ~(IXON | IXOFF | IXANY | IGNPAR); + h->newtio.c_lflag &= ~(ECHOK | ECHOCTL | ECHOKE); + h->newtio.c_oflag &= ~(OPOST | ONLCR); + + /* setup the new settings */ + cfsetispeed(&h->newtio, port_baud); + cfsetospeed(&h->newtio, port_baud); + h->newtio.c_cflag |= + port_parity | + port_bits | + port_stop | + CLOCAL | + CREAD; + + h->newtio.c_cc[VMIN] = 0; + h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ + + /* set the settings */ + serial_flush(h); + if (tcsetattr(h->fd, TCSANOW, &h->newtio) != 0) + return PORT_ERR_UNKNOWN; + +/* this check fails on CDC-ACM devices, bits 16 and 17 of cflag differ! + * it has been disabled below for now -jcw, 2015-11-09 + if (settings.c_cflag != h->newtio.c_cflag) + fprintf(stderr, "c_cflag mismatch %lx\n", + settings.c_cflag ^ h->newtio.c_cflag); + */ + + /* confirm they were set */ + tcgetattr(h->fd, &settings); + if (settings.c_iflag != h->newtio.c_iflag || + settings.c_oflag != h->newtio.c_oflag || + //settings.c_cflag != h->newtio.c_cflag || + settings.c_lflag != h->newtio.c_lflag) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit)); + return PORT_ERR_OK; +} + +/* + * Roger clark. + * This function is no longer used. But has just been commented out in case it needs + * to be reinstated in the future + +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; + + + /* + * Roger Clark + * Commented out this check, because on OSX some devices are /dev/cu + * and some users use symbolic links to devices, hence the name may not even start + * with /dev + + if(startswith(resolved, "/dev/tty")) return 1; + + return 0; + */ + + return 1; +} + +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 */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_posix_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = read(h->fd, pos, nbyte); + if (r == 0) + return PORT_ERR_TIMEDOUT; + if (r < 0) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + ssize_t r; + const uint8_t *pos = (const uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + r = write(h->fd, pos, nbyte); + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_posix_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit, lines; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = TIOCM_RTS; + break; + + case GPIO_DTR: + bit = TIOCM_DTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (tcsendbreak(h->fd, 1)) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (ioctl(h->fd, TIOCMGET, &lines)) + return PORT_ERR_UNKNOWN; + lines = level ? lines | bit : lines & ~bit; + if (ioctl(h->fd, TIOCMSET, &lines)) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_posix_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_posix", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_posix_open, + .close = serial_posix_close, + .read = serial_posix_read, + .write = serial_posix_write, + .gpio = serial_posix_gpio, + .get_cfg_str = serial_posix_get_cfg_str, +}; diff --git a/tools/win/src/stm32flash_serial/src/serial_w32.c b/tools/win/src/stm32flash_serial/src/serial_w32.c new file mode 100644 index 0000000..56772c0 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/serial_w32.c @@ -0,0 +1,341 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + Copyright (C) 2010 Gareth McMullin + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "serial.h" +#include "port.h" + +struct serial { + HANDLE fd; + DCB oldtio; + DCB newtio; + char setup_str[11]; +}; + +static serial_t *serial_open(const char *device) +{ + serial_t *h = calloc(sizeof(serial_t), 1); + char *devName; + + /* timeout in ms */ + COMMTIMEOUTS timeouts = {MAXDWORD, MAXDWORD, 500, 0, 0}; + + /* Fix the device name if required */ + if (strlen(device) > 4 && device[0] != '\\') { + devName = calloc(1, strlen(device) + 5); + sprintf(devName, "\\\\.\\%s", device); + } else { + devName = (char *)device; + } + + /* Create file handle for port */ + h->fd = CreateFile(devName, GENERIC_READ | GENERIC_WRITE, + 0, /* Exclusive access */ + NULL, /* No security */ + OPEN_EXISTING, + 0, /* No overlap */ + NULL); + + if (devName != device) + free(devName); + + if (h->fd == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + fprintf(stderr, "File not found: %s\n", device); + return NULL; + } + + SetupComm(h->fd, 4096, 4096); /* Set input and output buffer size */ + + SetCommTimeouts(h->fd, &timeouts); + + SetCommMask(h->fd, EV_ERR); /* Notify us of error events */ + + GetCommState(h->fd, &h->oldtio); /* Retrieve port parameters */ + GetCommState(h->fd, &h->newtio); /* Retrieve port parameters */ + + /* PurgeComm(h->fd, PURGE_RXABORT | PURGE_TXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR); */ + + return h; +} + +static void serial_flush(const serial_t *h) +{ + /* We shouldn't need to flush in non-overlapping (blocking) mode */ + /* tcflush(h->fd, TCIFLUSH); */ +} + +static void serial_close(serial_t *h) +{ + serial_flush(h); + SetCommState(h->fd, &h->oldtio); + CloseHandle(h->fd); + free(h); +} + +static port_err_t serial_setup(serial_t *h, + const serial_baud_t baud, + const serial_bits_t bits, + const serial_parity_t parity, + const serial_stopbit_t stopbit) +{ + switch (baud) { + case SERIAL_BAUD_1200: h->newtio.BaudRate = CBR_1200; break; + /* case SERIAL_BAUD_1800: h->newtio.BaudRate = CBR_1800; break; */ + case SERIAL_BAUD_2400: h->newtio.BaudRate = CBR_2400; break; + case SERIAL_BAUD_4800: h->newtio.BaudRate = CBR_4800; break; + case SERIAL_BAUD_9600: h->newtio.BaudRate = CBR_9600; break; + case SERIAL_BAUD_19200: h->newtio.BaudRate = CBR_19200; break; + case SERIAL_BAUD_38400: h->newtio.BaudRate = CBR_38400; break; + case SERIAL_BAUD_57600: h->newtio.BaudRate = CBR_57600; break; + case SERIAL_BAUD_115200: h->newtio.BaudRate = CBR_115200; break; + case SERIAL_BAUD_128000: h->newtio.BaudRate = CBR_128000; break; + case SERIAL_BAUD_256000: h->newtio.BaudRate = CBR_256000; break; + /* These are not defined in WinBase.h and might work or not */ + case SERIAL_BAUD_230400: h->newtio.BaudRate = 230400; break; + case SERIAL_BAUD_460800: h->newtio.BaudRate = 460800; break; + case SERIAL_BAUD_500000: h->newtio.BaudRate = 500000; break; + case SERIAL_BAUD_576000: h->newtio.BaudRate = 576000; break; + case SERIAL_BAUD_921600: h->newtio.BaudRate = 921600; break; + case SERIAL_BAUD_1000000: h->newtio.BaudRate = 1000000; break; + case SERIAL_BAUD_1500000: h->newtio.BaudRate = 1500000; break; + case SERIAL_BAUD_2000000: h->newtio.BaudRate = 2000000; break; + case SERIAL_BAUD_INVALID: + + default: + return PORT_ERR_UNKNOWN; + } + + switch (bits) { + case SERIAL_BITS_5: h->newtio.ByteSize = 5; break; + case SERIAL_BITS_6: h->newtio.ByteSize = 6; break; + case SERIAL_BITS_7: h->newtio.ByteSize = 7; break; + case SERIAL_BITS_8: h->newtio.ByteSize = 8; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (parity) { + case SERIAL_PARITY_NONE: h->newtio.Parity = NOPARITY; break; + case SERIAL_PARITY_EVEN: h->newtio.Parity = EVENPARITY; break; + case SERIAL_PARITY_ODD: h->newtio.Parity = ODDPARITY; break; + + default: + return PORT_ERR_UNKNOWN; + } + + switch (stopbit) { + case SERIAL_STOPBIT_1: h->newtio.StopBits = ONESTOPBIT; break; + case SERIAL_STOPBIT_2: h->newtio.StopBits = TWOSTOPBITS; break; + + default: + return PORT_ERR_UNKNOWN; + } + + /* reset the settings */ + h->newtio.fOutxCtsFlow = FALSE; + h->newtio.fOutxDsrFlow = FALSE; + h->newtio.fOutX = FALSE; + h->newtio.fInX = FALSE; + h->newtio.fNull = 0; + h->newtio.fAbortOnError = 0; + + /* set the settings */ + serial_flush(h); + if (!SetCommState(h->fd, &h->newtio)) + return PORT_ERR_UNKNOWN; + + snprintf(h->setup_str, sizeof(h->setup_str), "%u %d%c%d", + serial_get_baud_int(baud), + serial_get_bits_int(bits), + serial_get_parity_str(parity), + serial_get_stopbit_int(stopbit) + ); + return PORT_ERR_OK; +} + +static port_err_t serial_w32_open(struct port_interface *port, + struct port_options *ops) +{ + serial_t *h; + + /* 1. check device name match */ + if (!((strlen(ops->device) == 4 || strlen(ops->device) == 5) + && !strncmp(ops->device, "COM", 3) && isdigit(ops->device[3])) + && !(!strncmp(ops->device, "\\\\.\\COM", strlen("\\\\.\\COM")) + && isdigit(ops->device[strlen("\\\\.\\COM")]))) + return PORT_ERR_NODEV; + + /* 2. check options */ + if (ops->baudRate == SERIAL_BAUD_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_bits(ops->serial_mode) == SERIAL_BITS_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_parity(ops->serial_mode) == SERIAL_PARITY_INVALID) + return PORT_ERR_UNKNOWN; + if (serial_get_stopbit(ops->serial_mode) == SERIAL_STOPBIT_INVALID) + return PORT_ERR_UNKNOWN; + + /* 3. open it */ + h = serial_open(ops->device); + if (h == NULL) + return PORT_ERR_UNKNOWN; + + /* 4. set options */ + if (serial_setup(h, ops->baudRate, + serial_get_bits(ops->serial_mode), + serial_get_parity(ops->serial_mode), + serial_get_stopbit(ops->serial_mode) + ) != PORT_ERR_OK) { + serial_close(h); + return PORT_ERR_UNKNOWN; + } + + port->private = h; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_close(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_close(h); + port->private = NULL; + return PORT_ERR_OK; +} + +static port_err_t serial_w32_read(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + 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; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_write(struct port_interface *port, void *buf, + size_t nbyte) +{ + serial_t *h; + DWORD r; + uint8_t *pos = (uint8_t *)buf; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + while (nbyte) { + if (!WriteFile(h->fd, pos, nbyte, &r, NULL)) + return PORT_ERR_UNKNOWN; + if (r < 1) + return PORT_ERR_UNKNOWN; + + nbyte -= r; + pos += r; + } + return PORT_ERR_OK; +} + +static port_err_t serial_w32_gpio(struct port_interface *port, + serial_gpio_t n, int level) +{ + serial_t *h; + int bit; + + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + switch (n) { + case GPIO_RTS: + bit = level ? SETRTS : CLRRTS; + break; + + case GPIO_DTR: + bit = level ? SETDTR : CLRDTR; + break; + + case GPIO_BRK: + if (level == 0) + return PORT_ERR_OK; + if (EscapeCommFunction(h->fd, SETBREAK) == 0) + return PORT_ERR_UNKNOWN; + usleep(500000); + if (EscapeCommFunction(h->fd, CLRBREAK) == 0) + return PORT_ERR_UNKNOWN; + return PORT_ERR_OK; + + default: + return PORT_ERR_UNKNOWN; + } + + /* handle RTS/DTR */ + if (EscapeCommFunction(h->fd, bit) == 0) + return PORT_ERR_UNKNOWN; + + return PORT_ERR_OK; +} + +static const char *serial_w32_get_cfg_str(struct port_interface *port) +{ + serial_t *h; + + h = (serial_t *)port->private; + return h ? h->setup_str : "INVALID"; +} + +struct port_interface port_serial = { + .name = "serial_w32", + .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, + .open = serial_w32_open, + .close = serial_w32_close, + .read = serial_w32_read, + .write = serial_w32_write, + .gpio = serial_w32_gpio, + .get_cfg_str = serial_w32_get_cfg_str, +}; diff --git a/tools/win/src/stm32flash_serial/src/stm32.c b/tools/win/src/stm32flash_serial/src/stm32.c new file mode 100644 index 0000000..74047d2 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/stm32.c @@ -0,0 +1,1048 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright 2010 Geoffrey McRae + Copyright 2012-2014 Tormod Volden + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include "stm32.h" +#include "port.h" +#include "utils.h" + +#define STM32_ACK 0x79 +#define STM32_NACK 0x1F +#define STM32_BUSY 0x76 + +#define STM32_CMD_INIT 0x7F +#define STM32_CMD_GET 0x00 /* get the version and command supported */ +#define STM32_CMD_GVR 0x01 /* get version and read protection status */ +#define STM32_CMD_GID 0x02 /* get ID */ +#define STM32_CMD_RM 0x11 /* read memory */ +#define STM32_CMD_GO 0x21 /* go */ +#define STM32_CMD_WM 0x31 /* write memory */ +#define STM32_CMD_WM_NS 0x32 /* no-stretch write memory */ +#define STM32_CMD_ER 0x43 /* erase */ +#define STM32_CMD_EE 0x44 /* extended erase */ +#define STM32_CMD_EE_NS 0x45 /* extended erase no-stretch */ +#define STM32_CMD_WP 0x63 /* write protect */ +#define STM32_CMD_WP_NS 0x64 /* write protect no-stretch */ +#define STM32_CMD_UW 0x73 /* write unprotect */ +#define STM32_CMD_UW_NS 0x74 /* write unprotect no-stretch */ +#define STM32_CMD_RP 0x82 /* readout protect */ +#define STM32_CMD_RP_NS 0x83 /* readout protect no-stretch */ +#define STM32_CMD_UR 0x92 /* readout unprotect */ +#define STM32_CMD_UR_NS 0x93 /* readout unprotect no-stretch */ +#define STM32_CMD_CRC 0xA1 /* compute CRC */ +#define STM32_CMD_ERR 0xFF /* not a valid command */ + +#define STM32_RESYNC_TIMEOUT 35 /* seconds */ +#define STM32_MASSERASE_TIMEOUT 35 /* seconds */ +#define STM32_SECTERASE_TIMEOUT 5 /* seconds */ +#define STM32_BLKWRITE_TIMEOUT 1 /* seconds */ +#define STM32_WUNPROT_TIMEOUT 1 /* seconds */ +#define STM32_WPROT_TIMEOUT 1 /* seconds */ +#define STM32_RPROT_TIMEOUT 1 /* seconds */ + +#define STM32_CMD_GET_LENGTH 17 /* bytes in the reply */ + +struct stm32_cmd { + uint8_t get; + uint8_t gvr; + uint8_t gid; + uint8_t rm; + uint8_t go; + uint8_t wm; + uint8_t er; /* this may be extended erase */ + uint8_t wp; + uint8_t uw; + uint8_t rp; + uint8_t ur; + uint8_t crc; +}; + +/* Reset code for ARMv7-M (Cortex-M3) and ARMv6-M (Cortex-M0) + * see ARMv7-M or ARMv6-M Architecture Reference Manual (table B3-8) + * or "The definitive guide to the ARM Cortex-M3", section 14.4. + */ +static const uint8_t stm_reset_code[] = { + 0x01, 0x49, // ldr r1, [pc, #4] ; () + 0x02, 0x4A, // ldr r2, [pc, #8] ; () + 0x0A, 0x60, // str r2, [r1, #0] + 0xfe, 0xe7, // endless: b endless + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 = VECTKEY | SYSRESETREQ +}; + +static const uint32_t stm_reset_code_length = sizeof(stm_reset_code); + +extern const stm32_dev_t devices[]; + +static void stm32_warn_stretching(const char *f) +{ + fprintf(stderr, "Attention !!!\n"); + fprintf(stderr, "\tThis %s error could be caused by your I2C\n", f); + fprintf(stderr, "\tcontroller not accepting \"clock stretching\"\n"); + fprintf(stderr, "\tas required by bootloader.\n"); + fprintf(stderr, "\tCheck \"I2C.txt\" in stm32flash source code.\n"); +} + +static stm32_err_t stm32_get_ack_timeout(const stm32_t *stm, time_t timeout) +{ + struct port_interface *port = stm->port; + uint8_t byte; + port_err_t p_err; + time_t t0, t1; + + if (!(port->flags & PORT_RETRY)) + timeout = 0; + + if (timeout) + time(&t0); + + do { + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_TIMEDOUT && timeout) { + time(&t1); + if (t1 < t0 + timeout) + continue; + } + + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to read ACK byte\n"); + return STM32_ERR_UNKNOWN; + } + + if (byte == STM32_ACK) + return STM32_ERR_OK; + if (byte == STM32_NACK) + return STM32_ERR_NACK; + if (byte != STM32_BUSY) { + fprintf(stderr, "Got byte 0x%02x instead of ACK\n", + byte); + return STM32_ERR_UNKNOWN; + } + } while (1); +} + +static stm32_err_t stm32_get_ack(const stm32_t *stm) +{ + return stm32_get_ack_timeout(stm, 0); +} + +static stm32_err_t stm32_send_command_timeout(const stm32_t *stm, + const uint8_t cmd, + time_t timeout) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + uint8_t buf[2]; + + buf[0] = cmd; + buf[1] = cmd ^ 0xFF; + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send command\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, timeout); + if (s_err == STM32_ERR_OK) + return STM32_ERR_OK; + if (s_err == STM32_ERR_NACK) + fprintf(stderr, "Got NACK from device on command 0x%02x\n", cmd); + else + fprintf(stderr, "Unexpected reply from device on command 0x%02x\n", cmd); + return STM32_ERR_UNKNOWN; +} + +static stm32_err_t stm32_send_command(const stm32_t *stm, const uint8_t cmd) +{ + return stm32_send_command_timeout(stm, cmd, 0); +} + +/* if we have lost sync, send a wrong command and expect a NACK */ +static stm32_err_t stm32_resync(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t buf[2], ack; + time_t t0, t1; + + time(&t0); + t1 = t0; + + buf[0] = STM32_CMD_ERR; + buf[1] = STM32_CMD_ERR ^ 0xFF; + while (t1 < t0 + STM32_RESYNC_TIMEOUT) { + p_err = port->write(port, buf, 2); + if (p_err != PORT_ERR_OK) { + usleep(500000); + time(&t1); + continue; + } + p_err = port->read(port, &ack, 1); + if (p_err != PORT_ERR_OK) { + time(&t1); + continue; + } + if (ack == STM32_NACK) + return STM32_ERR_OK; + time(&t1); + } + return STM32_ERR_UNKNOWN; +} + +/* + * some command receive reply frame with variable length, and length is + * embedded in reply frame itself. + * We can guess the length, but if we guess wrong the protocol gets out + * of sync. + * Use resync for frame oriented interfaces (e.g. I2C) and byte-by-byte + * read for byte oriented interfaces (e.g. UART). + * + * to run safely, data buffer should be allocated for 256+1 bytes + * + * len is value of the first byte in the frame. + */ +static stm32_err_t stm32_guess_len_cmd(const stm32_t *stm, uint8_t cmd, + uint8_t *data, unsigned int len) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (port->flags & PORT_BYTE) { + /* interface is UART-like */ + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + len = data[0]; + p_err = port->read(port, data + 1, len + 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; + } + + p_err = port->read(port, data, len + 2); + if (p_err == PORT_ERR_OK && len == data[0]) + return STM32_ERR_OK; + if (p_err != PORT_ERR_OK) { + /* restart with only one byte */ + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, 1); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + } + + fprintf(stderr, "Re sync (len = %d)\n", data[0]); + if (stm32_resync(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + len = data[0]; + if (stm32_send_command(stm, cmd) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + p_err = port->read(port, data, len + 2); + if (p_err != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +/* + * Some interface, e.g. UART, requires a specific init sequence to let STM32 + * autodetect the interface speed. + * The sequence is only required one time after reset. + * stm32flash has command line flag "-c" to prevent sending the init sequence + * in case it was already sent before. + * User can easily forget adding "-c". In this case the bootloader would + * interpret the init sequence as part of a command message, then waiting for + * the rest of the message blocking the interface. + * This function sends the init sequence and, in case of timeout, recovers + * the interface. + */ +static stm32_err_t stm32_send_init_seq(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + port_err_t p_err; + uint8_t byte, cmd = STM32_CMD_INIT; + + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_ACK) + return STM32_ERR_OK; + if (p_err == PORT_ERR_OK && byte == STM32_NACK) { + /* We could get error later, but let's continue, for now. */ + fprintf(stderr, + "Warning: the interface was not closed properly.\n"); + return STM32_ERR_OK; + } + if (p_err != PORT_ERR_TIMEDOUT) { + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; + } + + /* + * Check if previous STM32_CMD_INIT was taken as first byte + * of a command. Send a new byte, we should get back a NACK. + */ + p_err = port->write(port, &cmd, 1); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Failed to send init to device\n"); + return STM32_ERR_UNKNOWN; + } + p_err = port->read(port, &byte, 1); + if (p_err == PORT_ERR_OK && byte == STM32_NACK) + return STM32_ERR_OK; + fprintf(stderr, "Failed to init device.\n"); + return STM32_ERR_UNKNOWN; +} + +/* find newer command by higher code */ +#define newer(prev, a) (((prev) == STM32_CMD_ERR) \ + ? (a) \ + : (((prev) > (a)) ? (prev) : (a))) + +stm32_t *stm32_init(struct port_interface *port, const char init) +{ + uint8_t len, val, buf[257]; + stm32_t *stm; + int i, new_cmds; + + stm = calloc(sizeof(stm32_t), 1); + stm->cmd = malloc(sizeof(stm32_cmd_t)); + memset(stm->cmd, STM32_CMD_ERR, sizeof(stm32_cmd_t)); + stm->port = port; + + if ((port->flags & PORT_CMD_INIT) && init) + if (stm32_send_init_seq(stm) != STM32_ERR_OK) + return NULL; + + /* get the version and read protection status */ + if (stm32_send_command(stm, STM32_CMD_GVR) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* From AN, only UART bootloader returns 3 bytes */ + len = (port->flags & PORT_GVR_ETX) ? 3 : 1; + if (port->read(port, buf, len) != PORT_ERR_OK) + return NULL; + stm->version = buf[0]; + stm->option1 = (port->flags & PORT_GVR_ETX) ? buf[1] : 0; + stm->option2 = (port->flags & PORT_GVR_ETX) ? buf[2] : 0; + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + /* get the bootloader information */ + len = STM32_CMD_GET_LENGTH; + if (port->cmd_get_reply) + for (i = 0; port->cmd_get_reply[i].length; i++) + if (stm->version == port->cmd_get_reply[i].version) { + len = port->cmd_get_reply[i].length; + break; + } + if (stm32_guess_len_cmd(stm, STM32_CMD_GET, buf, len) != STM32_ERR_OK) + return NULL; + len = buf[0] + 1; + stm->bl_version = buf[1]; + new_cmds = 0; + for (i = 1; i < len; i++) { + val = buf[i + 1]; + switch (val) { + case STM32_CMD_GET: + stm->cmd->get = val; break; + case STM32_CMD_GVR: + stm->cmd->gvr = val; break; + case STM32_CMD_GID: + stm->cmd->gid = val; break; + case STM32_CMD_RM: + stm->cmd->rm = val; break; + case STM32_CMD_GO: + stm->cmd->go = val; break; + case STM32_CMD_WM: + case STM32_CMD_WM_NS: + stm->cmd->wm = newer(stm->cmd->wm, val); + break; + case STM32_CMD_ER: + case STM32_CMD_EE: + case STM32_CMD_EE_NS: + stm->cmd->er = newer(stm->cmd->er, val); + break; + case STM32_CMD_WP: + case STM32_CMD_WP_NS: + stm->cmd->wp = newer(stm->cmd->wp, val); + break; + case STM32_CMD_UW: + case STM32_CMD_UW_NS: + stm->cmd->uw = newer(stm->cmd->uw, val); + break; + case STM32_CMD_RP: + case STM32_CMD_RP_NS: + stm->cmd->rp = newer(stm->cmd->rp, val); + break; + case STM32_CMD_UR: + case STM32_CMD_UR_NS: + stm->cmd->ur = newer(stm->cmd->ur, val); + break; + case STM32_CMD_CRC: + stm->cmd->crc = newer(stm->cmd->crc, val); + break; + default: + if (new_cmds++ == 0) + fprintf(stderr, + "GET returns unknown commands (0x%2x", + val); + else + fprintf(stderr, ", 0x%2x", val); + } + } + if (new_cmds) + fprintf(stderr, ")\n"); + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + if (stm->cmd->get == STM32_CMD_ERR + || stm->cmd->gvr == STM32_CMD_ERR + || stm->cmd->gid == STM32_CMD_ERR) { + fprintf(stderr, "Error: bootloader did not returned correct information from GET command\n"); + return NULL; + } + + /* get the device ID */ + if (stm32_guess_len_cmd(stm, stm->cmd->gid, buf, 1) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + len = buf[0] + 1; + if (len < 2) { + stm32_close(stm); + fprintf(stderr, "Only %d bytes sent in the PID, unknown/unsupported device\n", len); + return NULL; + } + stm->pid = (buf[1] << 8) | buf[2]; + if (len > 2) { + fprintf(stderr, "This bootloader returns %d extra bytes in PID:", len); + for (i = 2; i <= len ; i++) + fprintf(stderr, " %02x", buf[i]); + fprintf(stderr, "\n"); + } + if (stm32_get_ack(stm) != STM32_ERR_OK) { + stm32_close(stm); + return NULL; + } + + stm->dev = devices; + while (stm->dev->id != 0x00 && stm->dev->id != stm->pid) + ++stm->dev; + + if (!stm->dev->id) { + fprintf(stderr, "Unknown/unsupported device (Device ID: 0x%03x)\n", stm->pid); + stm32_close(stm); + return NULL; + } + + return stm; +} + +void stm32_close(stm32_t *stm) +{ + if (stm) + free(stm->cmd); + free(stm); +} + +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->rm == STM32_CMD_ERR) { + fprintf(stderr, "Error: READ command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_send_command(stm, len - 1) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, data, len) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + return STM32_ERR_OK; +} + +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len) +{ + struct port_interface *port = stm->port; + uint8_t cs, buf[256 + 2]; + unsigned int i, aligned_len; + stm32_err_t s_err; + + if (!len) + return STM32_ERR_OK; + + if (len > 256) { + fprintf(stderr, "Error: READ length limit at 256 bytes\n"); + return STM32_ERR_UNKNOWN; + } + + /* must be 32bit aligned */ + if (address & 0x3 || len & 0x3) { + fprintf(stderr, "Error: WRITE address and length must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->wm == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + /* send the address and checksum */ + if (stm32_send_command(stm, stm->cmd->wm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + aligned_len = (len + 3) & ~3; + cs = aligned_len - 1; + buf[0] = aligned_len - 1; + for (i = 0; i < len; i++) { + cs ^= data[i]; + buf[i + 1] = data[i]; + } + /* padding data */ + for (i = len; i < aligned_len; i++) { + cs ^= 0xFF; + buf[i + 1] = 0xFF; + } + buf[aligned_len + 1] = cs; + if (port->write(port, buf, aligned_len + 2) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wm != STM32_CMD_WM_NS) + stm32_warn_stretching("write"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wunprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->uw == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->uw) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WUNPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->uw != STM32_CMD_UW_NS) + stm32_warn_stretching("WRITE UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_wprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->wp == STM32_CMD_ERR) { + fprintf(stderr, "Error: WRITE PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->wp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_WPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to WRITE PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->wp != STM32_CMD_WP_NS) + stm32_warn_stretching("WRITE PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_runprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->ur == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT UNPROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->ur) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT UNPROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->ur != STM32_CMD_UR_NS) + stm32_warn_stretching("READOUT UNPROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_readprot_memory(const stm32_t *stm) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + + if (stm->cmd->rp == STM32_CMD_ERR) { + fprintf(stderr, "Error: READOUT PROTECT command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->rp) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + s_err = stm32_get_ack_timeout(stm, STM32_RPROT_TIMEOUT); + if (s_err == STM32_NACK) { + fprintf(stderr, "Error: Failed to READOUT PROTECT\n"); + return STM32_ERR_UNKNOWN; + } + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W + && stm->cmd->rp != STM32_CMD_RP_NS) + stm32_warn_stretching("READOUT PROTECT"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; +} + +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, uint8_t pages) +{ + struct port_interface *port = stm->port; + stm32_err_t s_err; + port_err_t p_err; + + if (!pages) + return 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 (stm32_send_command(stm, stm->cmd->er) != STM32_ERR_OK) { + fprintf(stderr, "Can't initiate chip 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) { + s_err = stm32_send_command_timeout(stm, 0xFF, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } else { + uint8_t cs = 0; + uint8_t pg_num; + uint8_t *buf; + int i = 0; + + buf = malloc(1 + pages + 1); + if (!buf) + return STM32_ERR_UNKNOWN; + + buf[i++] = pages - 1; + cs ^= (pages-1); + for (pg_num = spage; pg_num < (pages + spage); pg_num++) { + buf[i++] = pg_num; + cs ^= pg_num; + } + buf[i++] = cs; + p_err = port->write(port, buf, i); + free(buf); + if (p_err != PORT_ERR_OK) { + fprintf(stderr, "Erase failed.\n"); + return STM32_ERR_UNKNOWN; + } + s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); + if (s_err != STM32_ERR_OK) { + if (port->flags & PORT_STRETCH_W) + stm32_warn_stretching("erase"); + return STM32_ERR_UNKNOWN; + } + return STM32_ERR_OK; + } +} + +static stm32_err_t stm32_run_raw_code(const stm32_t *stm, + uint32_t target_address, + const uint8_t *code, uint32_t code_size) +{ + uint32_t stack_le = le_u32(0x20002000); + uint32_t code_address_le = le_u32(target_address + 8); + uint32_t length = code_size + 8; + uint8_t *mem, *pos; + uint32_t address, w; + + /* Must be 32-bit aligned */ + if (target_address & 0x3) { + fprintf(stderr, "Error: code address must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + mem = malloc(length); + if (!mem) + return STM32_ERR_UNKNOWN; + + memcpy(mem, &stack_le, sizeof(uint32_t)); + memcpy(mem + 4, &code_address_le, sizeof(uint32_t)); + memcpy(mem + 8, code, code_size); + + pos = mem; + address = target_address; + while (length > 0) { + w = length > 256 ? 256 : length; + if (stm32_write_memory(stm, address, pos, w) != STM32_ERR_OK) { + free(mem); + return STM32_ERR_UNKNOWN; + } + + address += w; + pos += w; + length -= w; + } + + free(mem); + return stm32_go(stm, target_address); +} + +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (stm->cmd->go == STM32_CMD_ERR) { + fprintf(stderr, "Error: GO command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->go) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + return STM32_ERR_OK; +} + +stm32_err_t stm32_reset_device(const stm32_t *stm) +{ + uint32_t target_address = stm->dev->ram_start; + + return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length); +} + +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + struct port_interface *port = stm->port; + uint8_t buf[5]; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc == STM32_CMD_ERR) { + fprintf(stderr, "Error: CRC command not implemented in bootloader.\n"); + return STM32_ERR_NO_CMD; + } + + if (stm32_send_command(stm, stm->cmd->crc) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = address >> 24; + buf[1] = (address >> 16) & 0xFF; + buf[2] = (address >> 8) & 0xFF; + buf[3] = address & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + buf[0] = length >> 24; + buf[1] = (length >> 16) & 0xFF; + buf[2] = (length >> 8) & 0xFF; + buf[3] = length & 0xFF; + buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; + if (port->write(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (stm32_get_ack(stm) != STM32_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (port->read(port, buf, 5) != PORT_ERR_OK) + return STM32_ERR_UNKNOWN; + + if (buf[4] != (buf[0] ^ buf[1] ^ buf[2] ^ buf[3])) + return STM32_ERR_UNKNOWN; + + *crc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + return STM32_ERR_OK; +} + +/* + * CRC computed by STM32 is similar to the standard crc32_be() + * implemented, for example, in Linux kernel in ./lib/crc32.c + * But STM32 computes it on units of 32 bits word and swaps the + * bytes of the word before the computation. + * Due to byte swap, I cannot use any CRC available in existing + * libraries, so here is a simple not optimized implementation. + */ +#define CRCPOLY_BE 0x04c11db7 +#define CRC_MSBMASK 0x80000000 +#define CRC_INIT_VALUE 0xFFFFFFFF +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len) +{ + int i; + uint32_t data; + + if (len & 0x3) { + fprintf(stderr, "Buffer length must be multiple of 4 bytes\n"); + return 0; + } + + while (len) { + data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + len -= 4; + + crc ^= data; + + for (i = 0; i < 32; i++) + if (crc & CRC_MSBMASK) + crc = (crc << 1) ^ CRCPOLY_BE; + else + crc = (crc << 1); + } + return crc; +} + +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc) +{ + uint8_t buf[256]; + uint32_t start, total_len, len, current_crc; + + if (address & 0x3 || length & 0x3) { + fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); + return STM32_ERR_UNKNOWN; + } + + if (stm->cmd->crc != STM32_CMD_ERR) + return stm32_crc_memory(stm, address, length, crc); + + start = address; + total_len = length; + current_crc = CRC_INIT_VALUE; + while (length) { + len = length > 256 ? 256 : length; + if (stm32_read_memory(stm, address, buf, len) != STM32_ERR_OK) { + fprintf(stderr, + "Failed to read memory at address 0x%08x, target write-protected?\n", + address); + return STM32_ERR_UNKNOWN; + } + current_crc = stm32_sw_crc(current_crc, buf, len); + length -= len; + address += len; + + fprintf(stderr, + "\rCRC address 0x%08x (%.2f%%) ", + address, + (100.0f / (float)total_len) * (float)(address - start) + ); + fflush(stderr); + } + fprintf(stderr, "Done.\n"); + *crc = current_crc; + return STM32_ERR_OK; +} diff --git a/tools/win/src/stm32flash_serial/src/stm32.h b/tools/win/src/stm32flash_serial/src/stm32.h new file mode 100644 index 0000000..1688fcb --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/stm32.h @@ -0,0 +1,84 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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 _STM32_H +#define _STM32_H + +#include +#include "serial.h" + +#define STM32_MAX_RX_FRAME 256 /* cmd read memory */ +#define STM32_MAX_TX_FRAME (1 + 256 + 1) /* cmd write memory */ + +typedef enum { + STM32_ERR_OK = 0, + STM32_ERR_UNKNOWN, /* Generic error */ + STM32_ERR_NACK, + STM32_ERR_NO_CMD, /* Command not available in bootloader */ +} stm32_err_t; + +typedef struct stm32 stm32_t; +typedef struct stm32_cmd stm32_cmd_t; +typedef struct stm32_dev stm32_dev_t; + +struct stm32 { + const serial_t *serial; + struct port_interface *port; + uint8_t bl_version; + uint8_t version; + uint8_t option1, option2; + uint16_t pid; + stm32_cmd_t *cmd; + const stm32_dev_t *dev; +}; + +struct stm32_dev { + uint16_t id; + const char *name; + 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 opt_start, opt_end; + uint32_t mem_start, mem_end; +}; + +stm32_t *stm32_init(struct port_interface *port, const char init); +void stm32_close(stm32_t *stm); +stm32_err_t stm32_read_memory(const stm32_t *stm, uint32_t address, + uint8_t data[], unsigned int len); +stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, + const uint8_t data[], unsigned int len); +stm32_err_t stm32_wunprot_memory(const stm32_t *stm); +stm32_err_t stm32_wprot_memory(const stm32_t *stm); +stm32_err_t stm32_erase_memory(const stm32_t *stm, uint8_t spage, + uint8_t pages); +stm32_err_t stm32_go(const stm32_t *stm, uint32_t address); +stm32_err_t stm32_reset_device(const stm32_t *stm); +stm32_err_t stm32_readprot_memory(const stm32_t *stm); +stm32_err_t stm32_runprot_memory(const stm32_t *stm); +stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, + uint32_t length, uint32_t *crc); +uint32_t stm32_sw_crc(uint32_t crc, uint8_t *buf, unsigned int len); + +#endif + diff --git a/tools/win/src/stm32flash_serial/src/stm32flash.1 b/tools/win/src/stm32flash_serial/src/stm32flash.1 new file mode 100644 index 0000000..d37292f --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/stm32flash.1 @@ -0,0 +1,407 @@ +.TH STM32FLASH 1 "2013\-11\-03" STM32FLASH "User command" +.SH NAME +stm32flash \- flashing utility for STM32 and STM32W through UART or I2C +.SH SYNOPSIS +.B stm32flash +.RB [ \-cfhjkouvCR ] +.RB [ \-a +.IR bus_address ] +.RB [ \-b +.IR baud_rate ] +.RB [ \-m +.IR serial_mode ] +.RB [ \-r +.IR filename ] +.RB [ \-w +.IR filename ] +.RB [ \-e +.IR num ] +.RB [ \-n +.IR count ] +.RB [ \-g +.IR address ] +.RB [ \-s +.IR start_page ] +.RB [ \-S +.IR address [: length ]] +.RB [ \-F +.IR RX_length [: TX_length ]] +.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. + +It requires the STM32[W] to embed a bootloader compliant with ST +application note AN3155. +.B stm32flash +uses the serial port +.I tty_device +to interact with the bootloader of STM32[W]. + +.SH OPTIONS +.TP +.BI "\-a" " bus_address" +Specify address on bus for +.IR i2c_device . +This option is mandatory for I2C interface. + +.TP +.BI "\-b" " baud_rate" +Specify baud rate speed of +.IR tty_device . +Please notice that the ST bootloader can automatically detect the baud rate, +as explaned in chapter 2 of AN3155. +This option could be required together with option +.B "\-c" +or if following interaction with bootloader is expected. +Default is +.IR 57600 . + +.TP +.BI "\-m" " mode" +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 +.I 8e1 +for standard STM32 bootloader and +.I 8n1 +for standard STM32W bootloader. +Default is +.IR 8e1 . + +.TP +.BI "\-r" " filename" +Specify to read the STM32[W] 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 +.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 +.B \-f +option. + +.TP +.B \-u +Specify to disable write\-protection from STM32[W] flash. +The STM32[W] will be reset after this operation. + +.TP +.B \-j +Enable the flash read\-protection. + +.TP +.B \-k +Disable the flash read\-protection. + +.TP +.B \-o +Erase only. + +.TP +.BI "\-e" " num" +Specify to erase only +.I num +pages before writing the flash. Default is to erase the whole flash. With +.B \-e 0 +the flash would not be erased. + +.TP +.B \-v +Specify to verify flash content after write operation. + +.TP +.BI "\-n" " count" +Specify to retry failed writes up to +.I count +times. Default is 10 times. + +.TP +.BI "\-g" " address" +Specify address to start execution from (0 = flash start). + +.TP +.BI "\-s" " start_page" +Specify flash page offset (0 = flash start). + +.TP +.BI "\-S" " address" "[:" "length" "]" +Specify start address and optionally length for read/write/erase/crc operations. + +.TP +.BI "\-F" " RX_length" "[:" "TX_length" "]" +Specify the maximum frame size for the current interface. +Due to STM32 bootloader protocol, host will never handle frames bigger than +256 byte in RX or 258 byte in TX. +Due to current code, lowest limit in RX is 20 byte (to read a complete reply +of command GET). Minimum limit in TX is 5 byte, required by protocol. + +.TP +.B \-f +Force binary parser while reading file with +.BR "\-w" "." + +.TP +.B \-h +Show help. + +.TP +.B \-c +Specify to resume the existing UART connection and don't send initial +INIT sequence to detect baud rate. Baud rate must be kept the same as the +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 +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 +GPIO. (See below +.B BOOTLOADER GPIO SEQUENCE +for the format of +.I GPIO_string +and further explanation). + +.TP +.B \-C +Specify to compute CRC on memory content. +By default the CRC is computed on the whole flash content. +Use +.B "\-S" +to provide different memory address range. + +.TP +.B \-R +Specify to reset the device at exit. +This option is ignored if either +.BR "\-g" "," +.BR "\-j" "," +.B "\-k" +or +.B "\-u" +is also specified. + +.SH BOOTLOADER GPIO SEQUENCE +This feature is currently available on Linux host only. + +As explained in ST application note AN2606, after reset the STM32 will +execute either the application program in user flash or the bootloader, +depending on the level applied at specific pins of STM32 during reset. + +STM32 bootloader is automatically activated by configuring the pins +BOOT0="high" and BOOT1="low" and then by applying a reset. +Application program in user flash is activated by configuring the pin +BOOT0="low" (the level on BOOT1 is ignored) and then by applying a reset. + +When GPIO from host computer are connected to either configuration and +reset pins of STM32, +.B stm32flash +can control the host GPIO to reset STM32 and to force execution of +bootloader or execution of application program. + +The sequence of GPIO values to entry to and exit from bootloader mode is +provided with command line option +.B "\-i" +.IR "GPIO_string" . + +.PD 0 +The format of +.IR "GPIO_string" " is:" +.RS +GPIO_string = [entry sequence][:[exit sequence]] +.P +sequence = [\-]n[,sequence] +.RE +.P +In the above sequences, negative numbers correspond to GPIO at "low" level; +numbers without sign correspond to GPIO at "high" level. +The value "n" can either be the GPIO number on the host system or the +string "rts", "dtr" or "brk". The strings "rts" and "dtr" drive the +corresponding UART's modem lines RTS and DTR as GPIO. +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 + +.PD 0 +As example, let's suppose the following connection between host and STM32: +.IP \(bu 2 +host GPIO_3 connected to reset pin of STM32; +.IP \(bu 2 +host GPIO_4 connected to STM32 pin BOOT0; +.IP \(bu 2 +host GPIO_5 connected to STM32 pin BOOT1. +.PD +.P + +In this case, the sequence to enter in bootloader mode is: first put +GPIO_4="high" and GPIO_5="low"; then send reset pulse by GPIO_3="low" +followed by GPIO_3="high". +The corresponding string for +.I GPIO_string +is "4,\-5,\-3,3". + +To exit from bootloade and run the application program, the sequence is: +put GPIO_4="low"; then send reset pulse. +The corresponding string for +.I GPIO_string +is "\-4,\-3,3". + +The complete command line flag is "\-i 4,\-5,\-3,3:\-4,\-3,3". + +STM32W uses pad PA5 to select boot mode; if during reset PA5 is "low" then +STM32W will enter in bootloader mode; if PA5 is "high" it will execute the +program in flash. + +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 +.RE +provides: +.IP \(bu 2 +entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high +.IP \(bu 2 +exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high +.PD + +.SH EXAMPLES +Get device information: +.RS +.PD 0 +.P +stm32flash /dev/ttyS0 +.PD +.RE + +Write with verify and then start execution: +.RS +.PD 0 +.P +stm32flash \-w filename \-v \-g 0x0 /dev/ttyS0 +.PD +.RE + +Read flash to file: +.RS +.PD 0 +.P +stm32flash \-r filename /dev/ttyS0 +.PD +.RE + +Start execution: +.RS +.PD 0 +.P +stm32flash \-g 0x0 /dev/ttyS0 +.PD +.RE + +Specify: +.PD 0 +.IP \(bu 2 +entry sequence: RTS=low, DTR=low, DTR=high +.IP \(bu 2 +exit sequence: RTS=high, DTR=low, DTR=high +.P +.RS +stm32flash \-i \-rts,\-dtr,dtr:rts,\-dtr,dtr /dev/ttyS0 +.PD +.RE + +.SH FORMAT CONVERSION +Flash images provided by ST or created with ST tools are often in file +format Motorola S\-Record. +Conversion between raw binary, intel hex and Motorola S\-Record can be +done through software package SRecord. + +.SH AUTHORS +The original software package +.B stm32flash +is written by +.I Geoffrey McRae +and is since 2012 maintained by +.IR "Tormod Volden " . + +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 . + +.SH SEE ALSO +.BR "srec_cat" "(1)," " srec_intel" "(5)," " srec_motorola" "(5)." + +The communication protocol used by ST bootloader is documented in +following ST application notes, depending on communication port. +The current version of +.B stm32flash +only supports +.I UART +and +.I I2C +ports. +.PD 0 +.P +.IP \(bu 2 +AN3154: CAN protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264321.pdf +.RE + +.P +.IP \(bu 2 +AN3155: USART protocol used in the STM32(TM) bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/CD00264342.pdf +.RE + +.P +.IP \(bu 2 +AN4221: I2C protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00072315.pdf +.RE + +.P +.IP \(bu 2 +AN4286: SPI protocol used in the STM32 bootloader +.P +.RS +http://www.st.com/web/en/resource/technical/document/application_note/DM00081379.pdf +.RE + +.PD + + +Boot mode selection for STM32 is documented in ST application note +AN2606, available from the ST website: +.PD 0 +.P +http://www.st.com/web/en/resource/technical/document/application_note/CD00167594.pdf +.PD + +.SH LICENSE +.B stm32flash +is distributed under GNU GENERAL PUBLIC LICENSE Version 2. +Copy of the license is available within the source code in the file +.IR "gpl\-2.0.txt" . diff --git a/tools/win/src/stm32flash_serial/src/utils.c b/tools/win/src/stm32flash_serial/src/utils.c new file mode 100644 index 0000000..271bb3e --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/utils.c @@ -0,0 +1,45 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "utils.h" + +/* detect CPU endian */ +char cpu_le() { + const uint32_t cpu_le_test = 0x12345678; + return ((const unsigned char*)&cpu_le_test)[0] == 0x78; +} + +uint32_t be_u32(const uint32_t v) { + if (cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} + +uint32_t le_u32(const uint32_t v) { + if (!cpu_le()) + return ((v & 0xFF000000) >> 24) | + ((v & 0x00FF0000) >> 8) | + ((v & 0x0000FF00) << 8) | + ((v & 0x000000FF) << 24); + return v; +} diff --git a/tools/win/src/stm32flash_serial/src/utils.h b/tools/win/src/stm32flash_serial/src/utils.h new file mode 100644 index 0000000..a8d37d2 --- /dev/null +++ b/tools/win/src/stm32flash_serial/src/utils.h @@ -0,0 +1,30 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2010 Geoffrey McRae + + 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_UTILS +#define _H_UTILS + +#include + +char cpu_le(); +uint32_t be_u32(const uint32_t v); +uint32_t le_u32(const uint32_t v); + +#endif diff --git a/tools/win/src/upload-reset/upload-reset.c b/tools/win/src/upload-reset/upload-reset.c new file mode 100644 index 0000000..1d03bff --- /dev/null +++ b/tools/win/src/upload-reset/upload-reset.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2015 Roger Clark + * + * 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. + * + * + * Utility to send the reset sequence on RTS and DTR and chars + * which resets the libmaple and causes the bootloader to be run + * + * + * + * Terminal control code by Heiko Noordhof (see copyright below) + */ + + + +/* Copyright (C) 2003 Heiko Noordhof + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function prototypes (belong in a seperate header file) */ +int openserial(char *devicename); +void closeserial(void); +int setDTR(unsigned short level); +int setRTS(unsigned short level); + + +/* Two globals for use by this module only */ +static int fd; +static struct termios oldterminfo; + + +void closeserial(void) +{ + tcsetattr(fd, TCSANOW, &oldterminfo); + close(fd); +} + + +int openserial(char *devicename) +{ + struct termios attr; + + if ((fd = open(devicename, O_RDWR)) == -1) return 0; /* Error */ + atexit(closeserial); + + if (tcgetattr(fd, &oldterminfo) == -1) return 0; /* Error */ + attr = oldterminfo; + attr.c_cflag |= CRTSCTS | CLOCAL; + attr.c_oflag = 0; + if (tcflush(fd, TCIOFLUSH) == -1) return 0; /* Error */ + if (tcsetattr(fd, TCSANOW, &attr) == -1) return 0; /* Error */ + + /* Set the lines to a known state, and */ + /* finally return non-zero is successful. */ + return setRTS(0) && setDTR(0); +} + + +/* For the two functions below: + * level=0 to set line to LOW + * level=1 to set line to HIGH + */ + +int setRTS(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setRTS(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_RTS; + else status &= ~TIOCM_RTS; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setRTS(): TIOCMSET"); + return 0; + } + return 1; +} + + +int setDTR(unsigned short level) +{ + int status; + + if (ioctl(fd, TIOCMGET, &status) == -1) { + perror("setDTR(): TIOCMGET"); + return 0; + } + if (level) status |= TIOCM_DTR; + else status &= ~TIOCM_DTR; + if (ioctl(fd, TIOCMSET, &status) == -1) { + perror("setDTR: TIOCMSET"); + return 0; + } + return 1; +} + +/* This portion of code was written by Roger Clark + * It was informed by various other pieces of code written by Leaflabs to reset their + * Maple and Maple mini boards + */ + +main(int argc, char *argv[]) +{ + + if (argc<2 || argc >3) + { + printf("Usage upload-reset \n\r"); + return; + } + + if (openserial(argv[1])) + { + // Send magic sequence of DTR and RTS followed by the magic word "1EAF" + setRTS(false); + setDTR(false); + setDTR(true); + + usleep(50000L); + + setDTR(false); + setRTS(true); + setDTR(true); + + usleep(50000L); + + setDTR(false); + + usleep(50000L); + + write(fd,"1EAF",4); + + closeserial(); + if (argc==3) + { + usleep(atol(argv[2])*1000L); + } + } + else + { + printf("Failed to open serial device.\n\r"); + } +} diff --git a/tools/win/upload_router.bat b/tools/win/upload_router.bat deleted file mode 100644 index c242e99..0000000 --- a/tools/win/upload_router.bat +++ /dev/null @@ -1,56 +0,0 @@ -@echo off -rem: Note %~dp0 get path of this batch file -rem: Need to change drive if My Documents is on a drive other than C: -set driverLetter=%~dp0 -set driverLetter=%driverLetter:~0,2% -%driverLetter% -cd %~dp0 -rem: the two line below are needed to fix path issues with incorrect slashes before the bin file name -set str1=%4 -set str1=%str1:/=\% -set str1=%str1: =% -set str=%str1%.bin -set elf=%str1% - - -set str1=%7 -set str1=%str1:/=\% -set gcc=%str1: =% - - -if %6 == 1 (goto debug) else (if %5 == maple_serial (goto Serial) else (if %5 == maple_dfu (goto maple_loader) else (goto STLink))) -exit - -:debug -debugging.bat %gcc% %elf% -exit - - -:maple_loader - -rem:java -jar maple_loader.jar %1 %2 %3 %4 %5 %6 %7 %8 %9 -echo %1 -echo %2 -echo %3 -java -jar maple_loader.jar %1 %2 %3 %str% -exit - -:Serial -stm32flash -g 0x8000000 -b 230400 -w %str% %1 -rem: C:\Python27\python.exe stm32loader.py -e -w -p %1 -g -b 115200 %str% - -rem: ------------- use STM's own uploader -rem: ---- Need to remove the COM bit from the comm port as the STM prog just wants the number -set commport=%1 -set commportnum=%commport:COM=% -rem: --- The maple board may nee the -i setting to be -i STM32_Med-density_128K or STM32_Med-density_64K -rem: ---- 64 bit version -rem: "%ProgramFiles(x86)%\STMicroelectronics\Software\Flash Loader Demonstrator\STMFlashLoader.exe" -c --pn %commportnum% --br 230400 -i STM32_High-density_256K -e --all -d --fn %str% --a 0x8000000 -r --a 0x8000000 - -rem: -- 32 bit version -rem: "%ProgramFiles%\STMicroelectronics\Software\Flash Loader Demonstrator\STMFlashLoader.exe" -c --pn %commportnum% --br 230400 -i STM32_Med-density_64K -e --all -d --fn %str% --a 0x8000000 -r --a 0x8000000 -exit - -:STLink -stlink\ST-LINK_CLI.exe -c SWD -P %str% 0x8000000 -Rst -Run -exit \ No newline at end of file