Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package picotool for openSUSE:Factory checked in at 2023-02-20 17:46:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/picotool (Old) and /work/SRC/openSUSE:Factory/.picotool.new.22824 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "picotool" Mon Feb 20 17:46:09 2023 rev:9 rq:1066719 version:1.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/picotool/picotool.changes 2021-12-10 21:53:32.590923298 +0100 +++ /work/SRC/openSUSE:Factory/.picotool.new.22824/picotool.changes 2023-02-20 17:46:39.623705034 +0100 @@ -1,0 +2,11 @@ +Wed Feb 15 13:02:35 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 1.1.1: + * Add --update option to `load` command to skip unchanged flash + sectors (speed improvement). + * A number of minor bug fixes and documentation improvements. + * Support non contiguous flash ELF/UF2 which were written + incorrectly before (and yet still passed verify due to + another bug). + +------------------------------------------------------------------- Old: ---- picotool-1.1.0.tar.gz New: ---- picotool-1.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ picotool.spec ++++++ --- /var/tmp/diff_new_pack.4YYd3L/_old 2023-02-20 17:46:42.567721665 +0100 +++ /var/tmp/diff_new_pack.4YYd3L/_new 2023-02-20 17:46:42.571721688 +0100 @@ -1,7 +1,7 @@ # # spec file for package picotool # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2023 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %define sdk_version 1.3.0 Name: picotool URL: https://github.com/raspberrypi/picotool -Version: 1.1.0 +Version: 1.1.1 Release: 0 Summary: Tool to inspect RP2040 binaries License: BSD-3-Clause ++++++ pico-sdk-1.3.0.tar.gz ++++++ ++++++ picotool-1.1.0.tar.gz -> picotool-1.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/CMakeLists.txt new/picotool-1.1.1/CMakeLists.txt --- old/picotool-1.1.0/CMakeLists.txt 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/CMakeLists.txt 2023-02-10 15:49:19.000000000 +0100 @@ -40,7 +40,7 @@ add_subdirectory(${PICO_SDK_PATH}/src/host/pico_platform pico_platform) add_executable(picotool main.cpp) - set(PICOTOOL_VERSION 1.1.0) + set(PICOTOOL_VERSION 1.1.1) 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 @@ -60,5 +60,5 @@ picoboot_connection_cxx ${LIBUSB_LIBRARIES}) # allow `make install` - install(TARGETS picotool) + install(TARGETS picotool RUNTIME DESTINATION bin) endif() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/README.md new/picotool-1.1.1/README.md --- old/picotool-1.1.0/README.md 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/README.md 2023-02-10 15:49:19.000000000 +0100 @@ -4,15 +4,39 @@ You also need to install `libusb-1.0`. -Linux/Mac: use your favorite package tool. For example, on Ubuntu: +### Linux / macOS + +Use your favorite package tool to install dependencies. For example, on Ubuntu: + +```console +sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake +``` + +On Linux you can add udev rules in order to run picotool without sudo: ```console -sudo apt install build-essential pkg-config libusb-1.0-0-dev +sudo cp udev/99-picotool.rules /etc/udev/rules.d/ ``` -Windows: download from here https://libusb.info/ +### Windows + +##### For Windows without MinGW -If you are on Windows, set LIBUSB_ROOT environment variable to the install directory +Download libUSB from here https://libusb.info/ + +set LIBUSB_ROOT environment variable to the install directory. +```console +mkdir build +cd build +cmake -G "NMake Makefiles" .. +nmake +``` + +##### For Windows with MinGW in WSL + +Download libUSB from here https://libusb.info/ + +set LIBUSB_ROOT environment variable to the install directory. ```console mkdir build @@ -21,14 +45,19 @@ make ``` -for Windows non MinGW/WSL: +##### For Windows with MinGW in MSYS2: + +No need to download libusb separately or set `LIBUSB_ROOT`. ```console +pacman -S $MINGW_PACKAGE_PREFIX-{toolchain,cmake,libusb} mkdir build cd build -cmake -G "NMake Makefiles" .. -nmake +MSYS2_ARG_CONV_EXCL=- cmake .. -G"MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX +make +make install DESTDIR=/ # optional ``` + ## Overview `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`). @@ -43,7 +72,7 @@ SYNOPSYS: 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 [-n] [-N] [-v] [-x] <filename> [-t <type>] [-o <offset>] [--bus <bus>] [--address <addr>] [-f] [-F] + picotool load [-n] [-N] [-u] [-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>] @@ -399,7 +428,7 @@ ```cmake pico_set_program_name(foo "not foo") # as "foo" would be the default pico_set_program_description(foo "this is a foo") -pico_set_program_version_string(foo "0.00001a") +pico_set_program_version(foo "0.00001a") pico_set_program_url(foo "www.plinth.com/foo") ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/cli.h new/picotool-1.1.1/cli.h --- old/picotool-1.1.0/cli.h 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/cli.h 2023-02-10 15:49:19.000000000 +0100 @@ -88,7 +88,7 @@ void add(const string& major_group, const string& minor_group, const string& option, const string& description) { auto &v = contents[major_group][minor_group]; - // we don't want to repeated the same option + // we don't want to repeat the same option if (std::find_if(v.begin(), v.end(), [&](const auto &x) { return x.first == option; }) == v.end()) { v.emplace_back(option, description); } @@ -301,6 +301,7 @@ vector<string> synopsys() const override { string s = string("<") + this->_name + ">"; + if (this->_max > 1) s += ".."; return {s}; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/cmake/FindLIBUSB.cmake new/picotool-1.1.1/cmake/FindLIBUSB.cmake --- old/picotool-1.1.0/cmake/FindLIBUSB.cmake 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/cmake/FindLIBUSB.cmake 2023-02-10 15:49:19.000000000 +0100 @@ -12,12 +12,12 @@ # in cache already set(LIBUSB_FOUND TRUE) else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) - IF (NOT WIN32) + IF (NOT MSVC) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) pkg_check_modules(PC_LIBUSB libusb-1.0) - ENDIF(NOT WIN32) + ENDIF () FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h HINTS $ENV{LIBUSB_ROOT}/include/libusb-1.0 PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/main.cpp new/picotool-1.1.1/main.cpp --- old/picotool-1.1.0/main.cpp 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/main.cpp 2023-02-10 15:49:19.000000000 +0100 @@ -4,7 +4,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#ifdef _MSC_VER +#ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS #endif @@ -37,7 +37,7 @@ #endif // tsk namespace is polluted on windows -#ifdef _MSC_VER +#ifdef _WIN32 #undef min #undef max @@ -201,7 +201,7 @@ } f--; assert(p >= f->first); - if (p > f->second.first) { + if (p >= f->second.first) { throw not_mapped_exception(); } return std::make_pair(mapping(p - f->first, f->second.first - f->first), f->second.second); @@ -278,6 +278,7 @@ bool execute = false; bool no_overwrite = false; bool no_overwrite_force = false; + bool update = false; } load; struct { @@ -343,7 +344,7 @@ string get_doc() const override { return "Display information from the target device(s) or file.\nWithout any arguments, this will display basic information for all connected RP2040 devices in BOOTSEL mode"; } -} info_cmd; +}; struct verify_command : public cmd { verify_command() : cmd("verify") {} @@ -366,7 +367,7 @@ string get_doc() const override { return "Check that the device contents match those in the file."; } -} verify_cmd; +}; struct save_command : public cmd { save_command() : cmd("save") {} @@ -392,7 +393,7 @@ string get_doc() const override { return "Save the program / memory stored in flash on the device to a file."; } -} save_cmd; +}; struct load_command : public cmd { load_command() : cmd("load") {} @@ -403,6 +404,7 @@ ( 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('u', "--update").set(settings.load.update) % "Skip writing flash sectors that already contain identical data" + 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" + @@ -418,7 +420,7 @@ string get_doc() const override { return "Load the program / memory range stored in a file onto the device."; } -} load_cmd; +}; struct help_command : public cmd { help_command() : cmd("help") {} @@ -437,7 +439,7 @@ string get_doc() const override { return "Show general help or help for a specific command"; } -} help_cmd; +}; struct version_command : public cmd { version_command() : cmd("version") {} @@ -462,7 +464,7 @@ string get_doc() const override { return "Display picotool version"; } -} version_cmd; +}; struct reboot_command : public cmd { bool quiet; @@ -699,7 +701,11 @@ } struct memory_access { - virtual void read(uint32_t, uint8_t *buffer, uint size) = 0; + virtual void read(uint32_t p, uint8_t *buffer, uint size) { + read(p, buffer, size, false); + } + + virtual void read(uint32_t, uint8_t *buffer, uint size, bool zero_fill) = 0; virtual bool is_device() { return false; } @@ -726,10 +732,10 @@ } // read a vector of types that have a raw_type_mapping - template <typename T> vector<T> read_vector(uint32_t addr, uint count) { + template <typename T> vector<T> read_vector(uint32_t addr, uint count, bool zero_fill = false) { assert(count); vector<typename raw_type_mapping<T>::access_type> buffer(count); - read(addr, (uint8_t *)buffer.data(), count * sizeof(typename raw_type_mapping<T>::access_type)); + read(addr, (uint8_t *)buffer.data(), count * sizeof(typename raw_type_mapping<T>::access_type), zero_fill); vector<T> v; v.reserve(count); for(const auto &e : buffer) { @@ -738,9 +744,9 @@ return v; } - template <typename T> void read_into_vector(uint32_t addr, uint count, vector<T> &v) { + template <typename T> void read_into_vector(uint32_t addr, uint count, vector<T> &v, bool zero_fill = false) { vector<typename raw_type_mapping<T>::access_type> buffer(count); - if (count) read(addr, (uint8_t *)buffer.data(), count * sizeof(typename raw_type_mapping<T>::access_type)); + if (count) read(addr, (uint8_t *)buffer.data(), count * sizeof(typename raw_type_mapping<T>::access_type), zero_fill); v.clear(); v.reserve(count); for(const auto &e : buffer) { @@ -784,7 +790,7 @@ return FLASH_START; } - void read(uint32_t address, uint8_t *buffer, uint size) override { + void read(uint32_t address, uint8_t *buffer, uint size, __unused bool zero_fill) override { if (flash == get_memory_type(address)) { connection.exit_xip(); } @@ -852,13 +858,25 @@ return binary_start; } - void read(uint32_t address, uint8_t *buffer, uint32_t size) override { + void read(uint32_t address, uint8_t *buffer, uint32_t size, bool zero_fill) override { while (size) { - auto result = rmap.get(address); - uint this_size = std::min(size, result.first.max_offset - result.first.offset); - assert( this_size); - fseek(file, result.second + result.first.offset, SEEK_SET); - fread(buffer, this_size, 1, file); + uint this_size; + try { + auto result = rmap.get(address); + this_size = std::min(size, result.first.max_offset - result.first.offset); + assert(this_size); + fseek(file, result.second + result.first.offset, SEEK_SET); + fread(buffer, this_size, 1, file); + } catch (not_mapped_exception &e) { + if (zero_fill) { + // address is not in a range, so fill up to next range with zeros + this_size = rmap.next(address) - address; + this_size = std::min(this_size, size); + memset(buffer, 0, this_size); + } else { + throw e; + } + } buffer += this_size; address += this_size; size -= this_size; @@ -881,12 +899,12 @@ struct remapped_memory_access : public memory_access { remapped_memory_access(memory_access &wrap, range_map<uint32_t> rmap) : wrap(wrap), rmap(rmap) {} - void read(uint32_t address, uint8_t *buffer, uint size) override { + void read(uint32_t address, uint8_t *buffer, uint size, bool zero_fill) override { while (size) { auto result = get_remapped(address); uint this_size = std::min(size, result.first.max_offset - result.first.offset); assert( this_size); - wrap.read(result.second + result.first.offset, buffer, this_size); + wrap.read(result.second + result.first.offset, buffer, this_size, zero_fill); buffer += this_size; address += this_size; size -= this_size; @@ -991,16 +1009,15 @@ } string read_string(memory_access &access, uint32_t addr) { - const uint max_length = 256; // todo better incremental length handling - auto v = access.read_vector<char>(addr, 256); + const uint max_length = 512; + auto v = access.read_vector<char>(addr, max_length, true); // zero fill uint length; for (length = 0; length < max_length; length++) { if (!v[length]) { break; } } - return string(v.data(), length); -} + return string(v.data(), length);} struct bi_visitor_base { void visit(memory_access& access, const binary_info_header& hdr) { @@ -1571,17 +1588,18 @@ strcpy(b, "No "); } char *buf = b + strlen(b); + int buf_len = b + sizeof(b) - buf; if (settings.address != -1) { if (settings.bus != -1) { - sprintf(buf, "accessible RP2040 device in BOOTSEL mode was found at bus %d, address %d.", settings.bus, settings.address); + snprintf(buf, buf_len, "accessible RP2040 device in BOOTSEL mode was found at bus %d, address %d.", settings.bus, settings.address); } else { - sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were found with address %d.", settings.address); + snprintf(buf, buf_len, "accessible RP2040 devices in BOOTSEL mode were found with address %d.", settings.address); } } else { if (settings.bus != -1) { - sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were found found on bus %d.", settings.bus); + snprintf(buf, buf_len, "accessible RP2040 devices in BOOTSEL mode were found found on bus %d.", settings.bus); } else { - sprintf(buf, "accessible RP2040 devices in BOOTSEL mode were found."); + snprintf(buf, buf_len,"accessible RP2040 devices in BOOTSEL mode were found."); } } return b; @@ -1773,7 +1791,7 @@ return false; } -vector<range> get_colaesced_ranges(file_memory_access &file_access) { +vector<range> get_coalesced_ranges(file_memory_access &file_access) { auto rmap = file_access.get_rmap(); auto ranges = rmap.ranges(); std::sort(ranges.begin(), ranges.end(), [](const range& a, const range &b) { @@ -1782,7 +1800,14 @@ // coalesce all the contiguous ranges for(auto i = ranges.begin(); i < ranges.end(); ) { if (i != ranges.end() - 1) { - if (i->to == (i+1)->from) { + uint32_t erase_size; + // we want to coalesce flash sectors together (this ends up creating ranges that may have holes) + if( get_memory_type(i->from) == flash ) { + erase_size = FLASH_SECTOR_ERASE_SIZE; + } else { + erase_size = 1; + } + if (i->to / erase_size == (i+1)->from / erase_size) { i->to = (i+1)->to; i = ranges.erase(i+1) - 1; continue; @@ -1819,7 +1844,7 @@ visitor.visit(access, hdr); } } - auto ranges = get_colaesced_ranges(file_access); + auto ranges = get_coalesced_ranges(file_access); for (auto mem_range : ranges) { enum memory_type t1 = get_memory_type(mem_range.from); enum memory_type t2 = get_memory_type(mem_range.to); @@ -1847,21 +1872,31 @@ bool ok = true; vector<uint8_t> file_buf; vector<uint8_t> device_buf; - for (uint32_t base = mem_range.from; base < mem_range.to && ok; ) { + for (uint32_t base = mem_range.from; base < mem_range.to && ok;) { uint32_t this_batch = std::min(mem_range.to - base, batch_size); if (type == flash) { // we have to erase an entire page, so then fill with zeros - range aligned_range(base & ~(FLASH_SECTOR_ERASE_SIZE - 1), (base & ~(FLASH_SECTOR_ERASE_SIZE - 1)) + FLASH_SECTOR_ERASE_SIZE); + range aligned_range(base & ~(FLASH_SECTOR_ERASE_SIZE - 1), + (base & ~(FLASH_SECTOR_ERASE_SIZE - 1)) + FLASH_SECTOR_ERASE_SIZE); range read_range(base, base + this_batch); read_range.intersect(aligned_range); - file_access.read_into_vector(read_range.from, read_range.to - read_range.from, file_buf); + file_access.read_into_vector(read_range.from, read_range.to - read_range.from, file_buf, true); // zero fill to cope with holes // zero padding up to FLASH_SECTOR_ERASE_SIZE file_buf.insert(file_buf.begin(), read_range.from - aligned_range.from, 0); file_buf.insert(file_buf.end(), aligned_range.to - read_range.to, 0); assert(file_buf.size() == FLASH_SECTOR_ERASE_SIZE); - con.exit_xip(); - con.flash_erase(aligned_range.from, FLASH_SECTOR_ERASE_SIZE); - raw_access.write_vector(aligned_range.from, file_buf); + + bool skip = false; + if (settings.load.update) { + vector<uint8_t> read_device_buf; + raw_access.read_into_vector(aligned_range.from, batch_size, read_device_buf); + skip = file_buf == read_device_buf; + } + if (!skip) { + con.exit_xip(); + con.flash_erase(aligned_range.from, FLASH_SECTOR_ERASE_SIZE); + raw_access.write_vector(aligned_range.from, file_buf); + } base = read_range.to; // about to add batch_size } else { file_access.read_into_vector(base, this_batch, file_buf); @@ -1871,6 +1906,9 @@ bar.progress(base - mem_range.from, mem_range.to - mem_range.from); } } + } + for (auto mem_range : ranges) { + enum memory_type type = get_memory_type(mem_range.from); if (settings.load.verify) { bool ok = true; { @@ -1881,7 +1919,10 @@ uint32_t pos = mem_range.from; for (uint32_t base = mem_range.from; base < mem_range.to && ok; base += batch_size) { uint32_t this_batch = std::min(mem_range.to - base, batch_size); - file_access.read_into_vector(base, this_batch, file_buf); + // note we pass zero_fill = true in case the file has holes, but this does + // mean that the verification will fail if those holes are not filed with zeros + // on the device + file_access.read_into_vector(base, this_batch, file_buf, true); raw_access.read_into_vector(base, this_batch, device_buf); assert(file_buf.size() == device_buf.size()); for (uint i = 0; i < this_batch; i++) { @@ -1924,7 +1965,7 @@ auto file_access = get_file_memory_access(); auto con = get_single_bootsel_device_connection(devices); picoboot_memory_access raw_access(con); - auto ranges = get_colaesced_ranges(file_access); + auto ranges = get_coalesced_ranges(file_access); if (settings.range_set) { range filter(settings.from, settings.to); for(auto& range : ranges) { @@ -1950,7 +1991,10 @@ uint32_t batch_size = 1024; for(uint32_t base = mem_range.from; base < mem_range.to && ok; base += batch_size) { uint32_t this_batch = std::min(mem_range.to - base, batch_size); - file_access.read_into_vector(base, this_batch, file_buf); + // note we pass zero_fill = true in case the file has holes, but this does + // mean that the verification will fail if those holes are not filed with zeros + // on the device + file_access.read_into_vector(base, this_batch, file_buf, true); raw_access.read_into_vector(base, this_batch, device_buf); assert(file_buf.size() == device_buf.size()); for(uint i=0;i<this_batch;i++) { @@ -2032,7 +2076,7 @@ libusb_device_handle *dev_handle; ret = libusb_open(device, &dev_handle); if (ret) { -#if _MSC_VER +#if _WIN32 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); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/picoboot_connection/picoboot_connection_cxx.h new/picotool-1.1.1/picoboot_connection/picoboot_connection_cxx.h --- old/picotool-1.1.0/picoboot_connection/picoboot_connection_cxx.h 2021-11-01 20:37:01.000000000 +0100 +++ new/picotool-1.1.1/picoboot_connection/picoboot_connection_cxx.h 2023-02-10 15:49:19.000000000 +0100 @@ -28,11 +28,16 @@ struct connection { explicit connection(libusb_device_handle *device, bool exclusive = true) : device(device), exclusive(exclusive) { + // do a device reset in case it was left in a bad state + reset(); if (exclusive) exclusive_access(EXCLUSIVE); } ~connection() { if (exclusive) { - picoboot_exclusive_access(device, NOT_EXCLUSIVE); + if (picoboot_exclusive_access(device, NOT_EXCLUSIVE)) { + // failed to restore exclusive access, so just reset + reset(); + } } } void reset(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/picotool-1.1.0/udev/99-picotool.rules new/picotool-1.1.1/udev/99-picotool.rules --- old/picotool-1.1.0/udev/99-picotool.rules 1970-01-01 01:00:00.000000000 +0100 +++ new/picotool-1.1.1/udev/99-picotool.rules 2023-02-10 15:49:19.000000000 +0100 @@ -0,0 +1,10 @@ +SUBSYSTEM=="usb", \ + ATTRS{idVendor}=="2e8a", \ + ATTRS{idProduct}=="0003", \ + MODE="660", \ + GROUP="plugdev" +SUBSYSTEM=="usb", \ + ATTRS{idVendor}=="2e8a", \ + ATTRS{idProduct}=="000a", \ + MODE="660", \ + GROUP="plugdev"