Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package picotool for openSUSE:Factory 
checked in at 2021-12-10 21:52:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/picotool (Old)
 and      /work/SRC/openSUSE:Factory/.picotool.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "picotool"

Fri Dec 10 21:52:59 2021 rev:8 rq:939165 version:1.1.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/picotool/picotool.changes        2021-10-20 
20:25:03.909406996 +0200
+++ /work/SRC/openSUSE:Factory/.picotool.new.2520/picotool.changes      
2021-12-10 21:53:32.590923298 +0100
@@ -1,0 +2,17 @@
+Fri Dec 10 14:30:06 UTC 2021 - Guillaume GARDET <[email protected]>
+
+- Update to 1.1.0:
+  * Add version command
+  * Fix parsing of ELF where the program header does not directly follow the 
file header
+  * Add install-ability via `cmake install`
+  * Incorporate slightly modified Win32 libusb find helpers
+  * Add ability to access device using stdio_usb
+  * Add -n option to load to avoid overwriting existing program
+  * Add -f/-F option to force reset a cooperative RP2040 board that isnt in 
BOOTSEL mode
+  * Add a sudo suggestion when can't connect on linux/macos
+- Drop upstreamed patches:
+  * picotool-ce76970.patch
+  * picotool-gh30.patch
+- Update SDK version to 1.3.0
+
+-------------------------------------------------------------------

Old:
----
  pico-sdk-1.2.0.tar.gz
  picotool-1.0.1.tar.gz
  picotool-ce76970.patch
  picotool-gh30.patch

New:
----
  pico-sdk-1.3.0.tar.gz
  picotool-1.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ picotool.spec ++++++
--- /var/tmp/diff_new_pack.ia0bCx/_old  2021-12-10 21:53:33.038923495 +0100
+++ /var/tmp/diff_new_pack.ia0bCx/_new  2021-12-10 21:53:33.042923497 +0100
@@ -16,10 +16,10 @@
 #
 
 
-%define sdk_version 1.2.0
+%define sdk_version 1.3.0
 Name:           picotool
 URL:            https://github.com/raspberrypi/picotool
-Version:        1.0.1
+Version:        1.1.0
 Release:        0
 Summary:        Tool to inspect RP2040 binaries
 License:        BSD-3-Clause
@@ -29,17 +29,12 @@
 BuildRequires:  libusb-1_0-devel
 Source0:        
https://github.com/raspberrypi/picotool/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
 Source1:        
https://github.com/raspberrypi/pico-sdk/archive/%{sdk_version}.tar.gz#/pico-sdk-%{sdk_version}.tar.gz
-# PATCH-FIX-UPSTREAM - https://github.com/raspberrypi/picotool/pull/30
-Patch1:         picotool-gh30.patch
-# PATCH-FIX-UPSTREAM - https://github.com/raspberrypi/picotool/issues/4
-Patch2:         picotool-ce76970.patch
 
 %description
 Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 
devices when they are in BOOTSEL mode.
 
 %prep
 %setup -q -a 1
-%autopatch -p1
 
 %build
 %cmake -DPICO_SDK_PATH="../pico-sdk-%{sdk_version}"

++++++ pico-sdk-1.2.0.tar.gz -> pico-sdk-1.3.0.tar.gz ++++++
++++ 15889 lines of diff (skipped)

++++++ picotool-1.0.1.tar.gz -> picotool-1.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/picotool-1.0.1/CMakeLists.txt 
new/picotool-1.1.0/CMakeLists.txt
--- old/picotool-1.0.1/CMakeLists.txt   2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/CMakeLists.txt   2021-11-01 20:37:01.000000000 +0100
@@ -2,6 +2,10 @@
 
 project(picotool)
 
+if (NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE Release)
+endif()
+
 if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
     set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
     message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
@@ -13,6 +17,11 @@
 if (NOT EXISTS ${PICO_SDK_PATH})
     message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
 endif ()
+include(${PICO_SDK_PATH}/pico_sdk_version.cmake)
+
+if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
+    message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) 
required. Your version is ${PICO_SDK_VERSION_STRING}")
+endif()
 
 set(CMAKE_CXX_STANDARD 14)
 
@@ -27,9 +36,29 @@
     add_subdirectory(${PICO_SDK_PATH}/src/common/pico_binary_info 
pico_binary_info)
     add_subdirectory(${PICO_SDK_PATH}/src/common/boot_uf2 boot_uf2_headers)
     add_subdirectory(${PICO_SDK_PATH}/src/common/boot_picoboot 
boot_picoboot_headers)
+    add_subdirectory(${PICO_SDK_PATH}/src/common/pico_usb_reset_interface 
pico_usb_reset_interface)
     add_subdirectory(${PICO_SDK_PATH}/src/host/pico_platform pico_platform)
 
     add_executable(picotool main.cpp)
+    set(PICOTOOL_VERSION 1.1.0)
+    set(SYSTEM_VERSION "${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")
+    set(COMPILER_INFO "${CMAKE_C_COMPILER_ID}-${CMAKE_C_COMPILER_VERSION}, 
${CMAKE_BUILD_TYPE}")
+    target_compile_definitions(picotool PRIVATE
+            PICOTOOL_VERSION="${PICOTOOL_VERSION}"
+            SYSTEM_VERSION="${SYSTEM_VERSION}"
+            COMPILER_INFO="${COMPILER_INFO}"
+            )
     target_include_directories(picotool PRIVATE ${LIBUSB_INCLUDE_DIR})
-    target_link_libraries(picotool pico_binary_info boot_uf2_headers 
boot_picoboot_headers pico_platform_headers picoboot_connection_cxx 
${LIBUSB_LIBRARIES})
+    # todo, this is a bit of an abstraction failure; but don't want to rev the 
SDK just for this right now
+    target_include_directories(picotool PRIVATE 
${PICO_SDK_PATH}/src/rp2_common/pico_stdio_usb/include)
+    target_link_libraries(picotool
+            pico_binary_info
+            boot_uf2_headers
+            boot_picoboot_headers
+            pico_platform_headers
+            pico_usb_reset_interface_headers
+            picoboot_connection_cxx
+            ${LIBUSB_LIBRARIES})
+    # allow `make install`
+    install(TARGETS picotool)
 endif()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/picotool-1.0.1/README.md new/picotool-1.1.0/README.md
--- old/picotool-1.0.1/README.md        2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/README.md        2021-11-01 20:37:01.000000000 +0100
@@ -1,15 +1,20 @@
 ## Building
 
-You need to set PICO_SDK_PATH in the environment, or pass it to cmake
-You need to install libusb-1.0.
+You need to set PICO_SDK_PATH in the environment, or pass it to cmake.
 
-Linux/Mac: use your favorite package tool
+You also need to install `libusb-1.0`.
+
+Linux/Mac: use your favorite package tool. For example, on Ubuntu:
+
+```console
+sudo apt install build-essential pkg-config libusb-1.0-0-dev
+```
 
 Windows: download from here https://libusb.info/
 
 If you are on Windows, set LIBUSB_ROOT environment variable to the install 
directory
 
-```bash
+```console
 mkdir build
 cd build
 cmake ..
@@ -18,7 +23,7 @@
 
 for Windows non MinGW/WSL:
 
-```bash
+```console
 mkdir build
 cd build
 cmake -G "NMake Makefiles" ..
@@ -26,9 +31,9 @@
 ```
 ## Overview
 
-Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 
devices when they are in BOOTSEL mode.
+`picotool` is a tool for inspecting RP2040 binaries, and interacting with 
RP2040 devices when they are in BOOTSEL mode. (As of version 1.1 of `picotool` 
it is also possible to interact with RP2040 devices that are not in BOOTSEL 
mode, but are using USB stdio support from the Raspberry Pi Pico SDK by using 
the `-f` argument of `picotool`).
 
-Note for full documentation see https://rptl.io/pico-get-started
+Note for additional documentation see https://rptl.io/pico-get-started
 
 ```text
 $ picotool help
@@ -36,25 +41,26 @@
     Tool for interacting with a RP2040 device in BOOTSEL mode, or with a 
RP2040 binary
 
 SYNOPSYS:
-    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>]
+    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] 
[-f] [-F]
     picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
-    picotool load [-v] [-r] <filename> [-t <type>] [--bus <bus>] [--address 
<addr>]
-    picotool save [-p] [--bus <bus>] [--address <addr>] <filename> [-t <type>]
-    picotool save -a [--bus <bus>] [--address <addr>] <filename> [-t <type>]
-    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] <filename> 
[-t <type>]
-    picotool verify [--bus <bus>] [--address <addr>] <filename> [-t <type>] 
[-r <from> <to>]
-    picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>]
+    picotool load [-n] [-N] [-v] [-x] <filename> [-t <type>] [-o <offset>] 
[--bus <bus>] [--address <addr>] [-f] [-F]
+    picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> 
[-t <type>]
+    picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t 
<type>]
+    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] 
<filename> [-t <type>]
+    picotool verify [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t 
<type>] [-r <from> <to>] [-o <offset>]
+    picotool reboot [-a] [-u] [--bus <bus>] [--address <addr>] [-f] [-F]
+    picotool version [-s]
     picotool help [<cmd>]
 
 COMMANDS:
-    info     Display information from the target device(s) or file.
-             Without any arguments, this will display basic information for 
all connected RP2040 devices in
-             BOOTSEL mode
-    load     Load the program / memory range stored in a file onto the device.
-    save     Save the program / memory stored in flash on the device to a file.
-    verify   Check that the device contents match those in the file.
-    reboot   Reboot the device
-    help     Show general help or help for a specific command
+    info      Display information from the target device(s) or file.
+              Without any arguments, this will display basic information for 
all connected RP2040 devices in BOOTSEL mode
+    load      Load the program / memory range stored in a file onto the device.
+    save      Save the program / memory stored in flash on the device to a 
file.
+    verify    Check that the device contents match those in the file.
+    reboot    Reboot the device
+    version   Display picotool version
+    help      Show general help or help for a specific command
 
 Use "picotool help <cmd>" for more info
 ```
@@ -69,15 +75,14 @@
 The information can be either read from one or more connected RP2040 devices 
in BOOTSEL mode, or from 
 a file. This file can be an ELF, a UF2 or a BIN file.
 
-```asciidoc
+```text
 $ picotool help info
 INFO:
     Display information from the target device(s) or file.
-    Without any arguments, this will display basic information for all 
connected RP2040 devices in USB boot
-    mode
+    Without any arguments, this will display basic information for all 
connected RP2040 devices in BOOTSEL mode
 
 SYNOPSYS:
-    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>]
+    picotool info [-b] [-p] [-d] [-l] [-a] [--bus <bus>] [--address <addr>] 
[-f] [-F]
     picotool info [-b] [-p] [-d] [-l] [-a] <filename> [-t <type>]
 
 OPTIONS:
@@ -99,13 +104,23 @@
             Filter devices by USB bus number
         --address <addr>
             Filter devices by USB device address
+        -f, --force
+            Force a device not in BOOTSEL mode but running compatible code to 
reset so the command can be executed. After executing
+            the command (unless the command itself is a 'reboot') the device 
will be rebooted back to application mode
+        -F, --force-no-reboot
+            Force a device not in BOOTSEL mode but running compatible code to 
reset so the command can be executed. After executing
+            the command (unless the command itself is a 'reboot') the device 
will be left connected and accessible to picotool, but
+            without the RPI-RP2 drive mounted
     To target a file
         <filename>
             The file name
         -t <type>
             Specify file type (uf2 | elf | bin) explicitly, ignoring file 
extension
+
 ```
 
+Note the -f arguments vary slightly for Windows vs macOS / Unix platforms.
+
 e.g.
 
 ```text
@@ -175,9 +190,9 @@
     Save the program / memory stored in flash on the device to a file.
 
 SYNOPSYS:
-    picotool save [-p] [--bus <bus>] [--address <addr>] <filename> [-t <type>]
-    picotool save -a [--bus <bus>] [--address <addr>] <filename> [-t <type>]
-    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] <filename> 
[-t <type>]
+    picotool save [-p] [--bus <bus>] [--address <addr>] [-f] [-F] <filename> 
[-t <type>]
+    picotool save -a [--bus <bus>] [--address <addr>] [-f] [-F] <filename> [-t 
<type>]
+    picotool save -r <from> <to> [--bus <bus>] [--address <addr>] [-f] [-F] 
<filename> [-t <type>]
 
 OPTIONS:
     Selection of data to save
@@ -186,7 +201,8 @@
         -a, --all
             Save all of flash memory
         -r, --range
-            Save a range of memory; note that the range is expanded to 256 
byte boundaries
+            Save a range of memory. Note that UF2s always store complete 256 
byte-aligned blocks of 256 bytes, and the range is
+            expanded accordingly
         <from>
             The lower address bound in hex
         <to>
@@ -196,6 +212,13 @@
             Filter devices by USB bus number
         --address <addr>
             Filter devices by USB device address
+        -f, --force
+            Force a device not in BOOTSEL mode but running compatible code to 
reset so the command can be executed. After executing
+            the command (unless the command itself is a 'reboot') the device 
will be rebooted back to application mode
+        -F, --force-no-reboot
+            Force a device not in BOOTSEL mode but running compatible code to 
reset so the command can be executed. After executing
+            the command (unless the command itself is a 'reboot') the device 
will be left connected and accessible to picotool, but
+            without the RPI-RP2 drive mounted
     File to save to
         <filename>
             The file name
@@ -247,11 +270,7 @@
 - program features - this is a list built from individual strings in the 
binary, that can be displayed (e.g. we will have one for UART stdio and one for 
USB stdio) in the SDK
 - build attributes - this is a similar list of strings, for things pertaining 
to the binary itself (e.g. Debug Build)
 
-Note it is my intention that things like MicroPython would include features 
for parts of the language/libraries they include.
-
-This might not be as a feature string per se, but could be another aggregating 
list (features/attributes are well known)
-but we can add another piece of binary info to name a list attribute that 
aggregates strings with a particular tag, so you
-could trivially add "MicroPython libraries:" etc to the `picotool` output 
without changing the tool itself. 
+The binary information is self-describing/extensible, so programs can include 
information picotool is not aware of (e.g. MicroPython includes a list of 
in-built libraries)
 
 ### Pins
 
@@ -389,11 +408,6 @@
 
 ## Additional binary information/picotool features
 
-### SDK version
-
-Should add this; git revision in general is hard since the user may not have 
the SDK checked out from git, so we'll have to stick
-a version number in a header
-
 ### Block devices
 
 MicroPython and CircuitPython, eventually the SDK and others may support one 
or more storage devices in flash. We already
@@ -433,7 +447,7 @@
 ### USB device descriptors
 
 Seems like tagging these might be nice (we just need to store the pointer to 
it assuming - as is often the case -
-the descriptor is just a linear chunk of memory) ... I assume there is a tool 
out there to prettyify such a thing if picotool dumps the descriptor
+the descriptor is just a linear chunk of memory) ... I assume there is a tool 
out there to prettify such a thing if picotool dumps the descriptor
 in binary.
 
 ### Issues
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/picotool-1.0.1/cli.h new/picotool-1.1.0/cli.h
--- old/picotool-1.0.1/cli.h    2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/cli.h    2021-11-01 20:37:01.000000000 +0100
@@ -18,6 +18,7 @@
 #include <iostream>
 #include <numeric>
 #include <sstream>
+#include <limits>
 
 /**
  * Note this is a hastily hacked together command line parser.
@@ -905,4 +906,4 @@
     }
 }
 
-#endif
\ No newline at end of file
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/picotool-1.0.1/cmake/FindLIBUSB.cmake 
new/picotool-1.1.0/cmake/FindLIBUSB.cmake
--- old/picotool-1.0.1/cmake/FindLIBUSB.cmake   2021-04-24 14:36:53.000000000 
+0200
+++ new/picotool-1.1.0/cmake/FindLIBUSB.cmake   2021-11-01 20:37:01.000000000 
+0100
@@ -19,10 +19,12 @@
         pkg_check_modules(PC_LIBUSB libusb-1.0)
     ENDIF(NOT WIN32)
     FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
+            HINTS $ENV{LIBUSB_ROOT}/include/libusb-1.0
             PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS})
-    FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0
+    FIND_LIBRARY(LIBUSB_LIBRARIES NAMES libusb-1.0 usb-1.0 usb
+            HINTS $ENV{LIBUSB_ROOT}/VS2019/MS32/static
             PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS})
     include(FindPackageHandleStandardArgs)
     FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES 
LIBUSB_INCLUDE_DIR)
     MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
-endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
\ No newline at end of file
+endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/picotool-1.0.1/main.cpp new/picotool-1.1.0/main.cpp
--- old/picotool-1.0.1/main.cpp 2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/main.cpp 2021-11-01 20:37:01.000000000 +0100
@@ -10,7 +10,7 @@
 
 #include "cli.h"
 #include "clipp/clipp.h"
-#include <signal.h>
+#include <csignal>
 #include <cstdio>
 #include <map>
 #include <iostream>
@@ -24,11 +24,18 @@
 #include <numeric>
 #include <memory>
 #include <functional>
+#include <ctime>
+
 #include "boot/uf2.h"
 #include "picoboot_connection_cxx.h"
 #include "pico/binary_info.h"
+#include "pico/stdio_usb/reset_interface.h"
 #include "elf.h"
 
+#if defined(__unix__) || defined(__APPLE__)
+#include <unistd.h>
+#endif
+
 // tsk namespace is polluted on windows
 #ifdef _MSC_VER
 #undef min
@@ -59,7 +66,6 @@
 typedef map<enum picoboot_device_result,vector<pair<libusb_device *, 
libusb_device_handle *>>> device_map;
 
 typedef unsigned int uint;
-static libusb_context *ctx;
 
 auto memory_names = map<enum memory_type, string>{
         {memory_type::sram, "RAM"},
@@ -80,7 +86,7 @@
 
 std::array<std::array<string, 30>, 10> pin_functions{{
     {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", 
"", "", "", "", "", "", "", "", "", "", "", ""},
-    {"SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 TX", "SPI0 RX", "SPI0 CSn", 
"SPI0 SCK", "SPI0 TX", "SPI1 RX", "SPI1 CSn", "SPI1 SCK", "SPI1 TX", "SPI1 RX", 
"SPI1 CSn", "SPI1 SCK", "PI1 TX", "SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 TX", 
"SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 TX", "SPI1 RX", "SPI1 CSn", "SPI1 
SCK", "SPI1 TX", "SPI1 RX", "SPI1 CSn"},
+    {"SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 TX", "SPI0 RX", "SPI0 CSn", 
"SPI0 SCK", "SPI0 TX", "SPI1 RX", "SPI1 CSn", "SPI1 SCK", "SPI1 TX", "SPI1 RX", 
"SPI1 CSn", "SPI1 SCK", "SPI1 TX", "SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 
TX", "SPI0 RX", "SPI0 CSn", "SPI0 SCK", "SPI0 TX", "SPI1 RX", "SPI1 CSn", "SPI1 
SCK", "SPI1 TX", "SPI1 RX", "SPI1 CSn"},
     {"UART0 TX", "UART0 RX", "UART0 CTS", "UART0 RTS", "UART1 TX", "UART1 RX", 
"UART1 CTS", "UART1 RTS", "UART1 TX", "UART1 RX", "UART1 CTS", "UART1 RTS", 
"UART0 TX", "UART0 RX", "UART0 CTS", "UART0 RTS", "UART0 TX", "UART0 RX", 
"UART0 CTS", "UART0 RTS", "UART1 TX", "UART1 RX", "UART1 CTS", "UART1 RTS", 
"UART1 TX", "UART1 RX", "UART1 CTS", "UART1 RTS", "UART0 TX", "UART0 RX"},
     {"I2C0 SDA", "I2C0 SCL", "I2C1 SDA", "I2C1 SCL", "I2C0 SDA", "I2C0 SCL", 
"I2C1 SDA", "I2C1 SCL", "I2C0 SDA", "I2C0 SCL", "I2C1 SDA", "I2C1 SCL", "I2C0 
SDA", "I2C0 SCL", "I2C1 SDA", "I2C1 SCL", "I2C0 SDA", "I2C0 SCL", "I2C1 SDA", 
"I2C1 SCL", "I2C0 SDA", "I2C0 SCL", "I2C1 SDA", "I2C1 SCL", "I2C0 SDA", "I2C0 
SCL", "I2C1 SDA", "I2C1 SCL", "I2C0 SDA", "I2C0 SCL"},
     {"PWM0 A", "PWM0 B", "PWM1 A", "PWM1 B", "PWM2 A", "PWM2 B", "PWM3 A", 
"PWM3 B", "PWM4 A", "PWM4 B", "PWM5 A", "PWM5 B", "PWM6 A", "PWM6 B", "PWM7 A", 
"PWM7 B", "PWM0 A", "PWM0 B", "PWM1 A", "PWM1 B", "PWM2 A", "PWM2 B", "PWM3 A", 
"PWM3 B", "PWM4 A", "PWM4 B", "PWM5 A", "PWM5 B", "PWM6 A", "PWM6 B"},
@@ -140,8 +146,25 @@
         to = other.clamp(to);
     }
 
+    bool intersects(const range& other) const {
+        return !(other.from >= to || other.to < from);
+    }
+
 };
 
+static void __noreturn fail(int code, string msg) {
+    throw command_failure(code, std::move(msg));
+}
+
+static void __noreturn fail(int code, const char *format, ...) {
+    va_list args;
+    va_start(args, format);
+    static char error_msg[512];
+    vsnprintf(error_msg, sizeof(error_msg), format, args);
+    va_end(args);
+    fail(code, string(error_msg));
+}
+
 // ranges should not overlap
 template <typename T> struct range_map {
     struct mapping {
@@ -150,19 +173,20 @@
         const uint32_t max_offset;
     };
 
-    void check_overlap(uint32_t p) {
-        auto f = m.lower_bound(p);
-        if (f != m.end()) {
-            assert(p >= f->first);
-            assert(p < f->second.first);
-        }
-    }
-
     void insert(const range& r, T t) {
         if (r.to != r.from) {
             assert(r.to > r.from);
-            check_overlap(r.from);
-            check_overlap(r.to);
+            // check we don't overlap any existing map entries
+
+            auto f = m.upper_bound(r.from); // first element that starts after 
r.from
+            if (f != m.begin()) f--; // back up, to catch element that starts 
on or before r.from
+            for(; f != m.end() && f->first < r.to; f++) { // loop till we 
can't possibly overlap
+                range r2(f->first, f->second.first);
+                if (r2.intersects(r)) {
+                    fail(ERROR_FORMAT, "Found overlapping memory ranges 
0x%08x->0x%08x and 0x%08x->%08x\n",
+                         r.from, r.to, r2.from, r2.to);
+                }
+            }
             m.insert(std::make_pair(r.from, std::make_pair(r.to, t)));
         }
     }
@@ -186,7 +210,7 @@
     uint32_t next(uint32_t p) {
         auto f = m.upper_bound(p);
         if (f == m.end()) {
-            std::numeric_limits<uint32_t>::max();
+            return std::numeric_limits<uint32_t>::max();
         }
         return f->first;
     }
@@ -213,11 +237,13 @@
 
 struct cmd {
     explicit cmd(string name) : _name(std::move(name)) {}
-    enum device_support { none, one, zero_or_more, one_or_more };
+    enum device_support { none, one, zero_or_more };
     virtual group get_cli() = 0;
     virtual string get_doc() const = 0;
     virtual device_support get_device_support() { return one; }
-    virtual void execute(device_map& devices) = 0;
+    virtual bool force_requires_pre_reboot() { return true; }
+    // return true if the command caused a reboot
+    virtual bool execute(device_map& devices) = 0;
     const string& name() { return _name; }
 private:
     string _name;
@@ -235,7 +261,9 @@
     bool offset_set = false;
     bool range_set = false;
     bool reboot_usb = false;
+    bool reboot_app_specified = false;
     bool force = false;
+    bool force_no_reboot = false;
 
     struct {
         bool show_basic = false;
@@ -248,11 +276,17 @@
     struct {
         bool verify = false;
         bool execute = false;
+        bool no_overwrite = false;
+        bool no_overwrite_force = false;
     } load;
 
     struct {
         bool all = false;
     } save;
+
+    struct {
+        bool semantic = false;
+    } version;
 };
 _settings settings;
 std::shared_ptr<cmd> selected_cmd;
@@ -263,6 +297,10 @@
             .if_missing([] { return "missing bus number"; })) % "Filter 
devices by USB bus number" +
         (option("--address") & 
integer("addr").min_value(1).max_value(127).set(settings.address)
             .if_missing([] { return "missing address"; })) % "Filter devices 
by USB device address"
+#if !defined(_WIN32)
+        + option('f', "--force").set(settings.force) % "Force a device not in 
BOOTSEL mode but running compatible code to reset so the command can be 
executed. After executing the command (unless the command itself is a 'reboot') 
the device will be rebooted back to application mode" +
+                option('F', "--force-no-reboot").set(settings.force_no_reboot) 
% "Force a device not in BOOTSEL mode but running compatible code to reset so 
the command can be executed. After executing the command (unless the command 
itself is a 'reboot') the device will be left connected and accessible to 
picotool, but without the RPI-RP2 drive mounted"
+#endif
     ).min(0).doc_non_optional(true);
 
 auto file_types = (option ('t', "--type") & 
value("type").set(settings.file_type))
@@ -278,7 +316,7 @@
 
 struct info_command : public cmd {
     info_command() : cmd("info") {}
-    void execute(device_map& devices) override;
+    bool execute(device_map& devices) override;
     device_support get_device_support() override {
         if (settings.filename.empty())
             return zero_or_more;
@@ -309,7 +347,7 @@
 
 struct verify_command : public cmd {
     verify_command() : cmd("verify") {}
-    void execute(device_map &devices) override;
+    bool execute(device_map &devices) override;
 
     group get_cli() override {
         return (
@@ -319,7 +357,7 @@
                 (option('r', "--range").set(settings.range_set) % "Compare a 
sub range of memory only" &
                     hex("from").set(settings.from) % "The lower address bound 
in hex" &
                     hex("to").set(settings.to) % "The upper address bound in 
hex").force_expand_help(true) +
-                (option('o', "--offset").set(settings.offset_set) % "Specify a 
load address for a BIN file" &
+                (option('o', "--offset").set(settings.offset_set) % "Specify 
the load address when comparing with a BIN file" &
                     hex("offset").set(settings.offset) % "Load offset (memory 
address; default 0x10000000)").force_expand_help(true)
            ).min(0).doc_non_optional(true) % "Address options"
         );
@@ -332,7 +370,7 @@
 
 struct save_command : public cmd {
     save_command() : cmd("save") {}
-    void execute(device_map &devices) override;
+    bool execute(device_map &devices) override;
 
     group get_cli() override {
         return (
@@ -358,17 +396,19 @@
 
 struct load_command : public cmd {
     load_command() : cmd("load") {}
-    void execute(device_map &devices) override;
+    bool execute(device_map &devices) override;
 
     group get_cli() override {
         return (
             (
+                option('n', "--no-overwrite").set(settings.load.no_overwrite) 
% "When writing flash data, do not overwrite an existing program in flash. If 
picotool cannot determine the size/presence of the program in flash, the 
command fails" +
+                option('N', 
"--no-overwrite-unsafe").set(settings.load.no_overwrite_force) % "When writing 
flash data, do not overwrite an existing program in flash. If picotool cannot 
determine the size/presence of the program in flash, the load continues anyway" 
+
                 option('v', "--verify").set(settings.load.verify) % "Verify 
the data was written correctly" +
                 option('x', "--execute").set(settings.load.execute) % "Attempt 
to execute the downloaded file as a program after the load"
             ).min(0).doc_non_optional(true) % "Post load actions" +
             file_selection % "File to load from" +
             (
-                option('o', "--offset").set(settings.offset_set) % "Specify a 
load address for a BIN file" &
+                option('o', "--offset").set(settings.offset_set) % "Specify 
the load address for a BIN file" &
                      hex("offset").set(settings.offset) % "Load offset (memory 
address; default 0x10000000)"
             ).force_expand_help(true) % "BIN file options" +
             device_selection % "Target device selection"
@@ -382,7 +422,7 @@
 
 struct help_command : public cmd {
     help_command() : cmd("help") {}
-    void execute(device_map &devices) override;
+    bool execute(device_map &devices) override;
 
     device_support get_device_support() override {
         return device_support::none;
@@ -399,19 +439,53 @@
     }
 } help_cmd;
 
+struct version_command : public cmd {
+    version_command() : cmd("version") {}
+    bool execute(device_map &devices) override {
+        if (settings.version.semantic)
+            std::cout << PICOTOOL_VERSION << "\n";
+        else
+            std::cout << "picotool v" << PICOTOOL_VERSION << " (" << 
SYSTEM_VERSION << ", " << COMPILER_INFO << ")\n";
+        return false;
+    }
+
+    device_support get_device_support() override {
+        return device_support::none;
+    }
+
+    group get_cli() override {
+        return group(
+                option('s', "--semantic").set(settings.version.semantic) % 
"Output semantic version number only"
+        );
+    }
+
+    string get_doc() const override {
+        return "Display picotool version";
+    }
+} version_cmd;
+
 struct reboot_command : public cmd {
+    bool quiet;
     reboot_command() : cmd("reboot") {}
-    void execute(device_map &devices) override;
+    bool execute(device_map &devices) override;
 
     group get_cli() override {
         return
         (
-            option('a', "--application").clear(settings.reboot_usb) % "Reboot 
back into the application (this is the default)" +
+            option('a', "--application").set(settings.reboot_app_specified) % 
"Reboot back into the application (this is the default)" +
             option('u', "--usb").set(settings.reboot_usb) % "Reboot back into 
BOOTSEL mode "
+#if defined(_WIN32)
+            + option('f', "--force").set(settings.force) % "Force a device not 
in BOOTSEL mode but running compatible code to reboot."
+#endif
         ).min(0).doc_non_optional(true) % "Reboot type" +
         device_selection % "Selecting the device to reboot";
     }
 
+    bool force_requires_pre_reboot() override {
+        // no point in rebooting twice
+        return false;
+    }
+
     string get_doc() const override {
         return "Reboot the device";
     }
@@ -423,6 +497,7 @@
         std::shared_ptr<cmd>(new save_command()),
         std::shared_ptr<cmd>(new verify_command()),
         std::shared_ptr<cmd>(new reboot_command()),
+        std::shared_ptr<cmd>(new version_command()),
         std::shared_ptr<cmd>(new help_command()),
 };
 
@@ -444,6 +519,13 @@
 
 clipp::formatting_ostream<std::ostream> fos(std::cout);
 
+static void sleep_ms(int ms) {
+#if defined(__unix__) || defined(__APPLE__)
+    usleep(ms * 1000);
+#else
+    Sleep(ms);
+#endif
+}
 using cli::option;
 using cli::integer;
 int parse(const int argc, char **argv) {
@@ -580,7 +662,8 @@
         args.erase(args.begin()); // remove the cmd itself
         cli::match(settings, selected_cmd->get_cli(), args);
     } catch (std::exception &e) {
-        std::cout << "ERROR: " << e.what() << "\n\n";
+        fos.wrap_hard();
+        fos << "ERROR: " << e.what() << "\n\n";
         usage();
         return ERROR_ARGS;
     }
@@ -666,10 +749,6 @@
     }
 };
 
-static void __noreturn fail(int code, string msg) {
-    throw command_failure(code, msg);
-}
-
 uint32_t bootrom_func_lookup(memory_access& access, uint16_t tag) {
     auto magic = access.read_int(BOOTROM_MAGIC_ADDR);
     magic &= 0xffffff; // ignore bootrom version
@@ -862,16 +941,6 @@
     }
 }
 
-static char error_msg[512];
-
-static void __noreturn fail(int code, const char *format, ...) {
-    va_list args;
-    va_start(args, format);
-    vsnprintf(error_msg, sizeof(error_msg), format, args);
-    va_end(args);
-    fail(code, string(error_msg));
-}
-
 static void __noreturn fail_read_error() {
     fail(ERROR_READ_FAILED, "Failed to read input file");
 }
@@ -934,6 +1003,12 @@
 }
 
 struct bi_visitor_base {
+    void visit(memory_access& access, const binary_info_header& hdr) {
+        for (const auto &a : hdr.bi_addr) {
+            visit(access, a);
+        }
+    }
+
     void visit(memory_access& access, uint32_t addr) {
         binary_info_core_t bi;
         access.read_raw(addr, bi);
@@ -1181,6 +1256,9 @@
     }
     if (eh.ph_num) {
         vector<elf32_ph_entry> entries(eh.ph_num);
+        if (fseek(file, eh.ph_offset, SEEK_SET)) {
+            return fail_read_error();
+        }
         if (eh.ph_num != fread(&entries[0], sizeof(struct elf32_ph_entry), 
eh.ph_num, file)) {
             fail_read_error();
         }
@@ -1332,9 +1410,7 @@
                 named_feature_groups[std::make_pair(group_tag, group_id)] = 
std::make_pair(label, flags);
             });
 
-            for (const auto &a : hdr.bi_addr) {
-                visitor.visit(access, a);
-            }
+            visitor.visit(access, hdr);
 
             visitor = bi_visitor{};
             visitor.id_and_int([&](int tag, uint32_t id, uint32_t value) {
@@ -1380,9 +1456,7 @@
                 }
             });
 
-            for (const auto &a : hdr.bi_addr) {
-                visitor.visit(access, a);
-            }
+            visitor.visit(access, hdr);
 
             if (settings.info.show_basic || settings.info.all) {
                 select_group(program_info);
@@ -1489,62 +1563,70 @@
     }
 }
 
-string missing_device_string() {
-    char buf[256];
+string missing_device_string(bool wasRetry) {
+    char b[256];
+    if (wasRetry) {
+        strcpy(b, "Despite the reboot attempt, no ");
+    } else {
+        strcpy(b, "No ");
+    }
+    char *buf = b + strlen(b);
     if (settings.address != -1) {
         if (settings.bus != -1) {
-            sprintf(buf, "No accessible RP2040 device in BOOTSEL mode was 
found at bus %d, address %d.", settings.bus, settings.address);
+            sprintf(buf, "accessible RP2040 device in BOOTSEL mode was found 
at bus %d, address %d.", settings.bus, settings.address);
         } else {
-            sprintf(buf, "No accessible RP2040 devices in BOOTSEL mode were 
found with address %d.", settings.address);
+            sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were found 
with address %d.", settings.address);
         }
     } else {
         if (settings.bus != -1) {
-            sprintf(buf, "No accessible RP2040 devices in BOOTSEL mode were 
found found on bus %d.", settings.bus);
+            sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were found 
found on bus %d.", settings.bus);
         } else {
-            sprintf(buf, "No accessible RP2040 devices in BOOTSEL mode were 
found.");
+            sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were 
found.");
         }
     }
-    return buf;
+    return b;
 }
 
-void help_command::execute(device_map &devices) {
+bool help_command::execute(device_map &devices) {
     assert(false);
+    return false;
 }
 
-void info_command::execute(device_map &devices) {
+bool info_command::execute(device_map &devices) {
+    fos.first_column(0); fos.hanging_indent(0);
     if (!settings.filename.empty()) {
         auto access = get_file_memory_access();
-        std::cout << "File " << settings.filename << ":\n\n";
+        fos << "File " << settings.filename << ":\n\n";
         info_guts(access);
-        return;
+        return false;
     }
     int size = devices[dr_vidpid_bootrom_ok].size();
     if (size) {
         if (size > 1) {
-            std::cout << "Multiple RP2040 devices in BOOTSEL mode found:\n";
+            fos << "Multiple RP2040 devices in BOOTSEL mode found:\n";
         }
         for (auto handles : devices[dr_vidpid_bootrom_ok]) {
+            fos.first_column(0); fos.hanging_indent(0);
             if (size > 1) {
                 auto s = bus_device_string(handles.first);
                 string dashes;
                 std::generate_n(std::back_inserter(dashes), s.length() + 1, [] 
{ return '-'; });
-                std::cout << "\n" << s << ":\n" << dashes << "\n";
+                fos << "\n" << s << ":\n" << dashes << "\n";
             }
             picoboot::connection connection(handles.second);
             picoboot_memory_access access(connection);
             info_guts(access);
         }
     } else {
-        fail(ERROR_NO_DEVICE, missing_device_string());
+        fail(ERROR_NO_DEVICE, missing_device_string(false));
     }
+    return false;
 }
 
-static picoboot::connection get_single_usb_boot_device(device_map& devices, 
bool exclusive = true) {
-    if (devices[dr_vidpid_bootrom_ok].size() != 1) {
-        fail(ERROR_NOT_POSSIBLE, "Command requires a single RP2040 device to 
be targeted.");
-    }
+static picoboot::connection get_single_bootsel_device_connection(device_map& 
devices, bool exclusive = true) {
+    assert(devices[dr_vidpid_bootrom_ok].size() == 1);
     libusb_device_handle *rc = devices[dr_vidpid_bootrom_ok][0].second;
-    if (!rc) fail(ERROR_USB, "Unabled to connect to device");
+    if (!rc) fail(ERROR_USB, "Unable to connect to device");
     return picoboot::connection(rc, exclusive);
 }
 
@@ -1574,8 +1656,8 @@
     int width;
 };
 
-void save_command::execute(device_map &devices) {
-    auto con = get_single_usb_boot_device(devices);
+bool save_command::execute(device_map &devices) {
+    auto con = get_single_bootsel_device_connection(devices);
     picoboot_memory_access raw_access(con);
 
     uint32_t end = 0;
@@ -1603,9 +1685,7 @@
                         return;
                     if (id == BINARY_INFO_ID_RP_BINARY_END) binary_end = value;
                 });
-                for (const auto &a : hdr.bi_addr) {
-                    visitor.visit(access, a);
-                }
+                visitor.visit(access, hdr);
             }
             if (binary_end == 0) {
                 fail(ERROR_NOT_POSSIBLE,
@@ -1690,6 +1770,7 @@
             throw;
         }
     }
+    return false;
 }
 
 vector<range> get_colaesced_ranges(file_memory_access &file_access) {
@@ -1712,13 +1793,32 @@
     return ranges;
 }
 
-void load_command::execute(device_map &devices) {
+bool load_command::execute(device_map &devices) {
     if (settings.offset_set && get_file_type() != filetype::bin) {
         fail(ERROR_ARGS, "Offset only valid for BIN files");
     }
     auto file_access = get_file_memory_access();
-    auto con = get_single_usb_boot_device(devices);
+    auto con = get_single_bootsel_device_connection(devices);
     picoboot_memory_access raw_access(con);
+    range flash_binary_range(FLASH_START, FLASH_END);
+    bool flash_binary_end_unknown = true;
+    if (settings.load.no_overwrite_force) settings.load.no_overwrite = true;
+    if (settings.load.no_overwrite) {
+        binary_info_header hdr;
+        if (find_binary_info(raw_access, hdr)) {
+            auto access = remapped_memory_access(raw_access, 
hdr.reverse_copy_mapping);
+            auto visitor = bi_visitor{};
+            visitor.id_and_int([&](int tag, uint32_t id, uint32_t value) {
+                if (tag != BINARY_INFO_TAG_RASPBERRY_PI)
+                    return;
+                if (id == BINARY_INFO_ID_RP_BINARY_END) {
+                    flash_binary_range.to = value;
+                    flash_binary_end_unknown = false;
+                }
+            });
+            visitor.visit(access, hdr);
+        }
+    }
     auto ranges = get_colaesced_ranges(file_access);
     for (auto mem_range : ranges) {
         enum memory_type t1 = get_memory_type(mem_range.from);
@@ -1727,6 +1827,16 @@
             fail(ERROR_FORMAT, "File to load contained an invalid memory range 
0x%08x-0x%08x", mem_range.from,
                  mem_range.to);
         }
+        if (settings.load.no_overwrite && 
mem_range.intersects(flash_binary_range)) {
+            if (flash_binary_end_unknown) {
+                if (!settings.load.no_overwrite_force) {
+                    fail(ERROR_NOT_POSSIBLE, "-n option specified, but the 
size/presence of an existing flash binary could not be detected; aborting. 
Consider using the -N option");
+                }
+            } else {
+                fail(ERROR_NOT_POSSIBLE, "-n option specified, and the loaded 
data range clashes with the existing flash binary range %08x->%08x",
+                     flash_binary_range.from, flash_binary_range.to);
+            }
+        }
     }
     for (auto mem_range : ranges) {
         enum memory_type type = get_memory_type(mem_range.from);
@@ -1801,15 +1911,18 @@
             fail(ERROR_FORMAT, "Cannot execute as file does not contain a 
valid RP2 executable image");
         }
         con.reboot(flash == get_memory_type(start) ? 0 : start, SRAM_END, 500);
+        std::cout << "\nThe device was rebooted to start the application.\n";
+        return true;
     }
+    return false;
 }
 
-void verify_command::execute(device_map &devices) {
+bool verify_command::execute(device_map &devices) {
     if (settings.offset_set && get_file_type() != filetype::bin) {
         fail(ERROR_ARGS, "Offset only valid for BIN files");
     }
     auto file_access = get_file_memory_access();
-    auto con = get_single_usb_boot_device(devices);
+    auto con = get_single_bootsel_device_connection(devices);
     picoboot_memory_access raw_access(con);
     auto ranges = get_colaesced_ranges(file_access);
     if (settings.range_set) {
@@ -1906,36 +2019,94 @@
             }
         }
     }
+    return false;
+}
+
+static int reboot_device(libusb_device *device, bool bootsel, uint 
disable_mask=0) {
+    // ok, the device isn't in USB boot mode, let's try to reboot via vendor 
interface
+    struct libusb_config_descriptor *config;
+    int ret = libusb_get_active_config_descriptor(device, &config);
+    if (ret) {
+        fail(ERROR_USB, "Failed to get descriptor %d\n", ret);
+    }
+    libusb_device_handle *dev_handle;
+    ret = libusb_open(device, &dev_handle);
+    if (ret) {
+#if _MSC_VER
+        fail(ERROR_USB, "Unable to access device to reboot it; Make sure there 
is a driver installed via Zadig\n", ret);
+#else
+        fail(ERROR_USB, "Unable to access device to reboot it; Use sudo or 
setup a udev rule\n", ret);
+#endif
+    }
+    for (int i = 0; i < config->bNumInterfaces; i++) {
+        if (0xff == config->interface[i].altsetting[0].bInterfaceClass &&
+            RESET_INTERFACE_SUBCLASS == 
config->interface[i].altsetting[0].bInterfaceSubClass &&
+            RESET_INTERFACE_PROTOCOL == 
config->interface[i].altsetting[0].bInterfaceProtocol) {
+            ret = libusb_claim_interface(dev_handle, i);
+            if (ret) {
+                fail(ERROR_USB, "Failed to claim interface\n");
+            }
+            if (bootsel) {
+                ret = libusb_control_transfer(dev_handle, 
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+                                              RESET_REQUEST_BOOTSEL, 
disable_mask, i, nullptr, 0, 2000);
+            } else {
+                ret = libusb_control_transfer(dev_handle, 
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+                                              RESET_REQUEST_FLASH, 0, i, 
nullptr, 0, 2000);
+            }
+//            if (ret != 0 ) {
+//                fail(ERROR_UNKNOWN, "Unable to reset the device %d\n", ret);
+//            }
+            return 0;
+//            return ret;
+        }
+    }
+    fail(ERROR_USB, "Unable to locate reset interface on the device");
 }
 
-void reboot_command::execute(device_map &devices) {
-    // not exclusive, because restoring un-exclusive could fail; also if we're 
rebooting, we don't much
-    // care what else is happening.
-    auto con = get_single_usb_boot_device(devices, false);
-    if (!settings.reboot_usb) {
-        con.reboot(0, SRAM_END, 500);
-        std::cout << "The device was rebooted.\n";
+bool reboot_command::execute(device_map &devices) {
+    if (settings.force) {
+        reboot_device(devices[dr_vidpid_stdio_usb][0].first, 
settings.reboot_usb);
+        if (!quiet) {
+            if (settings.reboot_usb) {
+                std::cout << "The device was asked to reboot into BOOTSEL 
mode.\n";
+            } else {
+                std::cout << "The device was asked to reboot into application 
mode.\n";
+            }
+        }
     } else {
-        picoboot_memory_access raw_access(con);
-        uint program_base = SRAM_START;
-        std::vector<uint32_t> program = {
-                0x20002100, // movs r0, #0;       movs r1, #0
-                0x47104a00, // ldr  r2, [pc, #0]; bx r2
-                bootrom_func_lookup(raw_access, rom_table_code('U','B'))
-        };
+        // not exclusive, because restoring un-exclusive could fail; also if 
we're rebooting, we don't much
+        // care what else is happening.
+        auto con = get_single_bootsel_device_connection(devices, false);
+        if (!settings.reboot_usb) {
+            con.reboot(0, SRAM_END, 500);
+        } else {
+            picoboot_memory_access raw_access(con);
+            uint program_base = SRAM_START;
+            std::vector<uint32_t> program = {
+                    0x20002100, // movs r0, #0;       movs r1, #0
+                    0x47104a00, // ldr  r2, [pc, #0]; bx r2
+                    bootrom_func_lookup(raw_access, rom_table_code('U', 'B'))
+            };
 
-        raw_access.write_vector(program_base, program);
-        try {
-            con.exec(program_base);
-        } catch (picoboot::connection_error &e) {
-            if (e.libusb_code == LIBUSB_ERROR_NO_DEVICE) {
-                // not unreasonable once it reboots
-                return;
+            raw_access.write_vector(program_base, program);
+            try {
+                con.exec(program_base);
+            } catch (picoboot::connection_error &e) {
+                // the reset_usb_boot above has a very short delay, so it 
frequently causes libusb to return
+                // fairly unpredictable errors... i think it is best to ignore 
them, because catching a rare
+                // case where the reboot command fails, is probably less 
important than potentially confusing
+                // the user with spurious error messages
+            }
+        }
+        if (!quiet) {
+            if (settings.reboot_usb) {
+                std::cout << "The device was rebooted into BOOTSEL mode.\n";
+            } else {
+                std::cout << "The device was rebooted into application 
mode.\n";
             }
-            throw e;
         }
-        fail(ERROR_NOT_POSSIBLE, "Reboot into USB boot not implemented yet");
     }
+    return true;
 }
 
 #if defined(_WIN32)
@@ -1965,6 +2136,8 @@
 }
 
 int main(int argc, char **argv) {
+    libusb_context *ctx = nullptr;
+
     int tw=0, th=0;
     get_terminal_size(tw, th);
     if (tw) {
@@ -1977,6 +2150,9 @@
         return 0;
     }
 
+    // save complicating the grammar
+    if (settings.force_no_reboot) settings.force = true;
+
     struct libusb_device **devs = nullptr;
     device_map devices;
     vector<libusb_device_handle *> to_close;
@@ -1984,58 +2160,148 @@
     try {
         signal(SIGINT, cancelled);
         signal(SIGTERM, cancelled);
+
+        if (settings.reboot_usb && settings.reboot_app_specified) {
+            fail(ERROR_ARGS, "Cannot specify both -u and -a reboot options");
+        }
+
         if (selected_cmd->get_device_support() != cmd::none) {
             if (libusb_init(&ctx)) {
                 fail(ERROR_USB, "Failed to initialise libUSB\n");
             }
-            rc = libusb_get_device_list(ctx, &devs);
-            if (rc < 0) {
-                fail(ERROR_USB, "Failed to enumerate USB devices\n");
-            } else {
-                rc = 0;
-            }
-            for (libusb_device **dev = devs; *dev; dev++) {
-                if (settings.bus != -1 && settings.bus != 
libusb_get_bus_number(*dev)) continue;
-                if (settings.address != -1 && settings.address != 
libusb_get_device_address(*dev)) continue;
-                libusb_device_handle *handle = nullptr;
-                auto result = picoboot_open_device(*dev, &handle);
-                if (handle) {
-                    to_close.push_back(handle);
-                }
-                if (result != dr_error) {
-                    devices[result].push_back(std::make_pair(*dev, handle));
-                }
-            }
         }
 
-        if (!rc) {
-            switch (selected_cmd->get_device_support()) {
+        // we only loop a second time if we want to reboot some devices (which 
may cause device
+        for (int tries = 0; !rc && tries < 2; tries++) {
+            if (ctx) {
+                if (libusb_get_device_list(ctx, &devs) < 0) {
+                    fail(ERROR_USB, "Failed to enumerate USB devices\n");
+                }
+                for (libusb_device **dev = devs; *dev; dev++) {
+                    if (settings.bus != -1 && settings.bus != 
libusb_get_bus_number(*dev)) continue;
+                    if (settings.address != -1 && settings.address != 
libusb_get_device_address(*dev)) continue;
+                    libusb_device_handle *handle = nullptr;
+                    auto result = picoboot_open_device(*dev, &handle);
+                    if (handle) {
+                        to_close.push_back(handle);
+                    }
+                    if (result != dr_error) {
+                        devices[result].push_back(std::make_pair(*dev, 
handle));
+                    }
+                }
+            }
+            auto supported = selected_cmd->get_device_support();
+            switch (supported) {
                 case cmd::device_support::zero_or_more:
                     if (!settings.filename.empty()) break;
                     // fall thru
                 case cmd::device_support::one:
-                case cmd::device_support::one_or_more:
-                    if (devices[dr_vidpid_bootrom_ok].empty()) {
-                        std::cout << missing_device_string() << "\n";
+                    if (devices[dr_vidpid_bootrom_ok].empty() &&
+                        (!settings.force || 
devices[dr_vidpid_stdio_usb].empty())) {
                         bool had_note = false;
-                        auto printer = [&](enum picoboot_device_result r, 
const string& description) {
+                        fos << missing_device_string(tries>0);
+                        if (tries > 0) {
+                            fos << " It is possible the device is not 
responding, and will have to be manually entered into BOOTSEL mode.\n";
+                            had_note = true; // suppress "but:" in this case
+                        }
+                        fos << "\n";
+                        fos.first_column(0);
+                        fos.hanging_indent(4);
+                        auto printer = [&](enum picoboot_device_result r, 
const string &description) {
                             if (!had_note && !devices[r].empty()) {
-                                std::cout << "\nbut:\n\n";
+                                fos << "\nbut:\n\n";
                                 had_note = true;
                             }
                             for (auto d : devices[r]) {
-                                std::cout << bus_device_string(d.first) << 
description << "\n";
+                                fos << bus_device_string(d.first) << 
description << "\n";
                             }
                         };
-                        printer(dr_vidpid_bootrom_cant_connect, " appears to 
be a RP2040 device in BOOTSEL mode, but picotool was unable to connect");
-                        printer(dr_vidpid_picoprobe, " appears to be a RP2040 
PicoProbe device not in BOOTSEL mode.");
-                        printer(dr_vidpid_micropython, " appears to be a 
RP2040 MicroPython device not in BOOTSEL mode.");
+#if defined(__linux__) || defined(__APPLE__)
+                        printer(dr_vidpid_bootrom_cant_connect,
+                                " appears to be a RP2040 device in BOOTSEL 
mode, but picotool was unable to connect. Maybe try 'sudo' or check your 
permissions.");
+#else
+                        printer(dr_vidpid_bootrom_cant_connect,
+                                " appears to be a RP2040 device in BOOTSEL 
mode, but picotool was unable to connect. You may need to install a driver. See 
\"Getting started with Raspberry Pi Pico\" for more information");
+#endif
+                        printer(dr_vidpid_picoprobe,
+                                " appears to be a RP2040 PicoProbe device not 
in BOOTSEL mode.");
+                        printer(dr_vidpid_micropython,
+                                " appears to be a RP2040 MicroPython device 
not in BOOTSEL mode.");
+                        if (selected_cmd->force_requires_pre_reboot()) {
+#if defined(_WIN32)
+                            printer(dr_vidpid_stdio_usb,
+                                    " appears to be a RP2040 device with a USB 
serial connection, not in BOOTSEL mode. You can force reboot into BOOTSEL mode 
via 'picotool reboot -f -u' first.");
+#else
+                            printer(dr_vidpid_stdio_usb,
+                                    " appears to be a RP2040 device with a USB 
serial connection, so consider -f (or -F) to force reboot in order to run the 
command.");
+#endif
+                        } else {
+                            // special case message for what is actually just 
reboot (the only command that doesn't require reboot first)
+                            printer(dr_vidpid_stdio_usb,
+                                    " appears to be a RP2040 device with a USB 
serial connection, so consider -f to force the reboot.");
+                        }
                         rc = ERROR_NO_DEVICE;
+                    } else if (supported == cmd::device_support::one) {
+                        if (devices[dr_vidpid_bootrom_ok].size() > 1 ||
+                            (devices[dr_vidpid_bootrom_ok].empty() && 
devices[dr_vidpid_stdio_usb].size() > 1)) {
+                            fail(ERROR_NOT_POSSIBLE, "Command requires a 
single RP2040 device to be targeted.");
+                        }
+                        if (!devices[dr_vidpid_bootrom_ok].empty()) {
+                            settings.force = false; // we have a device, so 
we're not forcing
+                        }
+                    } else if (supported == cmd::device_support::zero_or_more 
&& settings.force && !devices[dr_vidpid_bootrom_ok].empty()) {
+                        // we have usable devices, so lets use them without 
force
+                        settings.force = false;
                     }
+                    fos.first_column(0);
+                    fos.hanging_indent(0);
+                    break;
                 default:
                     break;
             }
-            if (!rc) selected_cmd->execute(devices);
+            if (!rc) {
+                if (settings.force && ctx) { // actually ctx should never be 
null as we are targeting device if force is set, but still
+                    if (devices[dr_vidpid_stdio_usb].size() != 1) {
+                        fail(ERROR_NOT_POSSIBLE,
+                             "Forced command requires a single rebootable 
RP2040 device to be targeted.");
+                    }
+                    if (selected_cmd->force_requires_pre_reboot()) {
+                        // we reboot into BOOTSEL mode and disable MSC 
interface (the 1 here)
+                        auto &to_reboot = 
devices[dr_vidpid_stdio_usb][0].first;
+                        reboot_device(to_reboot, true, 1);
+                        fos << "The device was asked to reboot into BOOTSEL 
mode so the command can be executed.\n\n";
+                        for (const auto &handle : to_close) {
+                            libusb_close(handle);
+                        }
+                        libusb_free_device_list(devs, 1);
+                        devs = nullptr;
+                        to_close.clear();
+                        devices.clear();
+                        sleep_ms(1200);
+
+                        // we now clear settings.force, because we expect the 
device to have rebooted and be available.
+                        // we also clear any filters, because the device may 
have moved, so the only way we can find it
+                        // again is to assume it is the only now visible 
device.
+                        settings.force = false;
+                        settings.address = -1;
+                        settings.bus = -1;
+                        continue;
+                    }
+                }
+                if (!selected_cmd->execute(devices) && tries) {
+                    if (settings.force_no_reboot) {
+                        fos << "\nThe device has been left accessible, but 
without the drive mounted; use 'picotool reboot' to reboot into regular BOOTSEL 
mode or application mode.\n";
+                    } else {
+                        // can only really do this with one device
+                        if (devices[dr_vidpid_bootrom_ok].size() == 1) {
+                            reboot_cmd.quiet = true;
+                            reboot_cmd.execute(devices);
+                            fos << "\nThe device was asked to reboot back into 
application mode.\n";
+                        }
+                    }
+                }
+                break;
+            }
         }
     } catch (command_failure &e) {
         std::cout << "ERROR: " << e.what() << "\n";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/picotool-1.0.1/picoboot_connection/picoboot_connection.c 
new/picotool-1.1.0/picoboot_connection/picoboot_connection.c
--- old/picotool-1.0.1/picoboot_connection/picoboot_connection.c        
2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/picoboot_connection/picoboot_connection.c        
2021-11-01 20:37:01.000000000 +0100
@@ -24,6 +24,7 @@
 #define PRODUCT_ID_RP2_USBBOOT 0x0003u
 #define PRODUCT_ID_PICOPROBE   0x0004u
 #define PRODUCT_ID_MICROPYTHON 0x0005u
+#define PRODUCT_ID_STDIO_USB   0x000au
 
 uint32_t crc32_for_byte(uint32_t remainder) {
     const uint32_t POLYNOMIAL = 0x4C11DB7;
@@ -72,6 +73,8 @@
                 return dr_vidpid_micropython;
             case PRODUCT_ID_PICOPROBE:
                 return dr_vidpid_picoprobe;
+            case PRODUCT_ID_STDIO_USB:
+                return dr_vidpid_stdio_usb;
             case PRODUCT_ID_RP2_USBBOOT:
                 break;
             default:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/picotool-1.0.1/picoboot_connection/picoboot_connection.h 
new/picotool-1.1.0/picoboot_connection/picoboot_connection.h
--- old/picotool-1.0.1/picoboot_connection/picoboot_connection.h        
2021-04-24 14:36:53.000000000 +0200
+++ new/picotool-1.1.0/picoboot_connection/picoboot_connection.h        
2021-11-01 20:37:01.000000000 +0100
@@ -9,6 +9,7 @@
 
 // todo we should use fully encapsulate libusb
 
+#include <assert.h>
 #include <libusb.h>
 #include "boot/picoboot.h"
 
@@ -24,6 +25,7 @@
     dr_vidpid_picoprobe,
     dr_vidpid_unknown,
     dr_error,
+    dr_vidpid_stdio_usb,
 };
 
 enum picoboot_device_result picoboot_open_device(libusb_device *device, 
libusb_device_handle **dev_handle);

Reply via email to