Merge branch 'main' into main
This commit is contained in:
commit
4b76ccc951
|
@ -16,7 +16,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
name: CMake Build
|
name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -27,7 +27,7 @@ env:
|
||||||
BUILD_TYPE: RelWithDebInfo
|
BUILD_TYPE: RelWithDebInfo
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
linux-x86_64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
@ -46,16 +46,22 @@ jobs:
|
||||||
libftdi1-dev
|
libftdi1-dev
|
||||||
libhidapi-dev
|
libhidapi-dev
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -DDEBUG_CMAKE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -B ${{github.workspace}}/build
|
run: >-
|
||||||
|
cmake
|
||||||
|
-D DEBUG_CMAKE=1
|
||||||
|
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
-B ${{github.workspace}}/build
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build ../build
|
run: cmake --build ${{github.workspace}}/build
|
||||||
- name: Archive build artifacts
|
- name: Archive build artifacts
|
||||||
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: build-ubuntu
|
name: linux-x86_64
|
||||||
path: |
|
path: |
|
||||||
${{github.workspace}}/build/*
|
${{github.workspace}}/build/*
|
||||||
build-macos:
|
|
||||||
|
macos-x86_64:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
@ -73,16 +79,23 @@ jobs:
|
||||||
libftdi
|
libftdi
|
||||||
hidapi
|
hidapi
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -DCMAKE_C_FLAGS=-I/usr/local/include -DCMAKE_EXE_LINKER_FLAGS=-L/usr/local/Cellar -DDEBUG_CMAKE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -B ${{github.workspace}}/build
|
run: >-
|
||||||
|
cmake
|
||||||
|
-D CMAKE_C_FLAGS=-I/usr/local/include
|
||||||
|
-D CMAKE_EXE_LINKER_FLAGS=-L/usr/local/Cellar
|
||||||
|
-D DEBUG_CMAKE=1
|
||||||
|
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
-B ${{github.workspace}}/build
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build ../build
|
run: cmake --build ${{github.workspace}}/build
|
||||||
- name: Archive build artifacts
|
- name: Archive build artifacts
|
||||||
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: build-macos
|
name: macos-x86_64
|
||||||
path: |
|
path: |
|
||||||
${{github.workspace}}/build/*
|
${{github.workspace}}/build/*
|
||||||
build-mingw:
|
mingw:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
@ -109,12 +122,18 @@ jobs:
|
||||||
mingw-w64-${{matrix.env}}-hidapi
|
mingw-w64-${{matrix.env}}-hidapi
|
||||||
mingw-w64-${{matrix.env}}-libftdi
|
mingw-w64-${{matrix.env}}-libftdi
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -G"MSYS Makefiles" -DDEBUG_CMAKE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -B ../build_${{matrix.sys}}
|
run: >-
|
||||||
|
cmake
|
||||||
|
-G"MSYS Makefiles"
|
||||||
|
-D DEBUG_CMAKE=1
|
||||||
|
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
-B ../build
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build ../build_${{matrix.sys}}
|
run: cmake --build ../build
|
||||||
- name: Archive build artifacts
|
- name: Archive build artifacts
|
||||||
|
if: always()
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: build-mingw-${{matrix.sys}}
|
name: mingw-${{matrix.env}}
|
||||||
path: |
|
path: |
|
||||||
${{github.workspace}}/build_${{matrix.sys}}/*
|
${{github.workspace}}/build/*
|
||||||
|
|
33
NEWS
33
NEWS
|
@ -15,15 +15,44 @@ Changes since version 6.4:
|
||||||
|
|
||||||
* New programmers supported:
|
* New programmers supported:
|
||||||
|
|
||||||
|
- SerialUPDI (UPDI devices connected to serial port with few
|
||||||
|
passive parts)
|
||||||
|
- PicKit4 / SNAP (now also in ISP and PDI mode)
|
||||||
|
|
||||||
* Bugfixes:
|
* Issues fixed:
|
||||||
|
|
||||||
|
- Curiosity Nano and terminal mode #790 (only the actual bugs
|
||||||
|
reported)
|
||||||
|
- CMake doesn't correctly handle conditionals in avrdude.conf.in
|
||||||
|
#776
|
||||||
|
- CMake doesn't detect FreeBSD's libusb-1.0 (compatibility) #775
|
||||||
|
- CMake doesn't correctly handle conditionals in avrdude.conf.in
|
||||||
|
#776
|
||||||
|
- CMake: Recognize more than just bison #785
|
||||||
|
|
||||||
* Patches:
|
* Pull requests:
|
||||||
|
|
||||||
|
- GitHub Migration #765
|
||||||
|
- Update toplevel files. #767
|
||||||
|
- GitHub Migration part 2 #768
|
||||||
|
- Remove 'windows' folder with giveio.sys driver #769
|
||||||
|
- SerialUPDI implementation - release candidate 1 #772
|
||||||
|
- Fix typos #777
|
||||||
|
- Fix memory leaks #779
|
||||||
|
- As promised, documentation for SerialUPDI programmer #782
|
||||||
|
- Improve CMake project #783
|
||||||
|
- Fix avr_read() for page reads #784
|
||||||
|
- Serialupdi manpage #787
|
||||||
|
- Add PicKit4 and SNAP programmers #791
|
||||||
|
- Use yacc/byacc as an alternative to bison, closes #785 #793
|
||||||
|
- Derive program version string from last commit #794
|
||||||
|
- Find 'avrdude.conf' based on absolute path to executable #780
|
||||||
|
- buspirate: fix -Wtautological-constant-out-of-range-compare #796
|
||||||
|
|
||||||
* Internals:
|
* Internals:
|
||||||
|
|
||||||
|
- Development moved to Github
|
||||||
|
|
||||||
|
|
||||||
Version 6.4:
|
Version 6.4:
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,3 @@ MacOS Brew requires
|
||||||
./configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/Cellar
|
./configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/Cellar
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
AVRDUDE is licensed under the [GNU GPLv2](./COPYING).
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
# cmake --build build
|
# cmake --build build
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.12)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(avrdude VERSION 6.99.20211218)
|
project(avrdude VERSION 6.99)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||||
|
@ -37,12 +37,58 @@ option(DEBUG_CMAKE "Enable debugging output for this CMake project" OFF)
|
||||||
include(CheckIncludeFile)
|
include(CheckIncludeFile)
|
||||||
include(CheckFunctionExists)
|
include(CheckFunctionExists)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
include(FindPackageMessage)
|
||||||
|
|
||||||
set(CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
set(CONFIG_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
|
||||||
|
set(AVRDUDE_FULL_VERSION ${CMAKE_PROJECT_VERSION})
|
||||||
set(AVRDUDE_EXTERNAL_PATH "${PROJECT_SOURCE_DIR}/external")
|
set(AVRDUDE_EXTERNAL_PATH "${PROJECT_SOURCE_DIR}/external")
|
||||||
|
|
||||||
# =====================================
|
# =====================================
|
||||||
# Detect Flex and Bison
|
# Get Git commit info
|
||||||
|
# =====================================
|
||||||
|
|
||||||
|
# GIT_COMMIT_HASH -> hash of latest commit, e.g. b8b859f5
|
||||||
|
# GIT_COMMIT_DATE -> date of latest commit, e.g. 20201231
|
||||||
|
# GIT_COMMIT_YEAR -> year of latest commit, e.g. 2020
|
||||||
|
|
||||||
|
find_package(Git)
|
||||||
|
if(Git_FOUND)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" log -1 --format=%h
|
||||||
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" log -1 --format=%ad --date=format:%Y%m%d
|
||||||
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_DATE
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" log -1 --format=%ad --date=format:%Y
|
||||||
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_YEAR
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" log -1 --tags --format=%h
|
||||||
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE GIT_TAG_HASH
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
# If the commit is not tagged, include the date and commit hash in the full version string.
|
||||||
|
if(NOT GIT_COMMIT_HASH STREQUAL GIT_TAG_HASH)
|
||||||
|
set(AVRDUDE_FULL_VERSION "${CMAKE_PROJECT_VERSION}-${GIT_COMMIT_DATE} (${GIT_COMMIT_HASH})")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# =====================================
|
||||||
|
# Detect flex and yacc/bison
|
||||||
# =====================================
|
# =====================================
|
||||||
|
|
||||||
find_package(FLEX)
|
find_package(FLEX)
|
||||||
|
@ -56,11 +102,26 @@ else()
|
||||||
message(SEND_ERROR "This CMake project requires 'flex', which is not installed on your system." )
|
message(SEND_ERROR "This CMake project requires 'flex', which is not installed on your system." )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(BISON)
|
find_package(BISON QUIET)
|
||||||
if(BISON_FOUND)
|
if(BISON_FOUND)
|
||||||
|
find_package_message(BISON "Found BISON: ${BISON_EXECUTABLE} (found version \"${BISON_VERSION}\")" "[${BISON_EXECUTABLE}][${BISON_VERSION}]")
|
||||||
BISON_TARGET(Parser config_gram.y "${PROJECT_BINARY_DIR}/config_gram.c" DEFINES_FILE "${PROJECT_BINARY_DIR}/config_gram.h")
|
BISON_TARGET(Parser config_gram.y "${PROJECT_BINARY_DIR}/config_gram.c" DEFINES_FILE "${PROJECT_BINARY_DIR}/config_gram.h")
|
||||||
else()
|
else()
|
||||||
message(SEND_ERROR "This CMake project requires 'bison', which is not installed on your system." )
|
find_program(YACC_EXECUTABLE NAMES yacc byacc DOC "path to the yacc executable")
|
||||||
|
mark_as_advanced(YACC_EXECUTABLE)
|
||||||
|
if(YACC_EXECUTABLE)
|
||||||
|
find_package_message(YACC "Found YACC: ${YACC_EXECUTABLE}" "[${YACC_EXECUTABLE}]")
|
||||||
|
set(YACC_TARGET_outputs "${PROJECT_BINARY_DIR}/config_gram.c")
|
||||||
|
add_custom_command(OUTPUT ${YACC_TARGET_outputs}
|
||||||
|
COMMAND ${YACC_EXECUTABLE} -d -o ${YACC_TARGET_outputs} config_gram.y
|
||||||
|
VERBATIM
|
||||||
|
COMMENT "[YACC][Parser] Building parser with yacc"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
set(BISON_Parser_OUTPUTS ${YACC_TARGET_outputs})
|
||||||
|
else()
|
||||||
|
message(SEND_ERROR "This CMake project requires 'bison', 'yacc', or 'byacc', which is not installed on your system." )
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# =====================================
|
# =====================================
|
||||||
|
@ -237,6 +298,7 @@ if (DEBUG_CMAKE)
|
||||||
message(STATUS "CMAKE_HOST_SYSTEM: ${CMAKE_HOST_SYSTEM}")
|
message(STATUS "CMAKE_HOST_SYSTEM: ${CMAKE_HOST_SYSTEM}")
|
||||||
message(STATUS "CMAKE_SYSTEM: ${CMAKE_SYSTEM}")
|
message(STATUS "CMAKE_SYSTEM: ${CMAKE_SYSTEM}")
|
||||||
message(STATUS "CONFIG_DIR: ${CONFIG_DIR}")
|
message(STATUS "CONFIG_DIR: ${CONFIG_DIR}")
|
||||||
|
message(STATUS "AVRDUDE_FULL_VERSION: ${AVRDUDE_FULL_VERSION}")
|
||||||
message(STATUS "USE_EXTERNAL: ${USE_EXTERNAL}")
|
message(STATUS "USE_EXTERNAL: ${USE_EXTERNAL}")
|
||||||
message(STATUS "USE_LIBUSBWIN32: ${USE_LIBUSBWIN32}")
|
message(STATUS "USE_LIBUSBWIN32: ${USE_LIBUSBWIN32}")
|
||||||
message(STATUS "HAVE_LIBELF: ${HAVE_LIBELF}")
|
message(STATUS "HAVE_LIBELF: ${HAVE_LIBELF}")
|
||||||
|
@ -405,6 +467,8 @@ add_library(libavrdude STATIC
|
||||||
linuxspi.h
|
linuxspi.h
|
||||||
linux_ppdev.h
|
linux_ppdev.h
|
||||||
lists.c
|
lists.c
|
||||||
|
micronucleus.c
|
||||||
|
micronucleus.h
|
||||||
my_ddk_hidsdi.h
|
my_ddk_hidsdi.h
|
||||||
par.c
|
par.c
|
||||||
par.h
|
par.h
|
||||||
|
@ -485,6 +549,8 @@ add_executable(avrdude
|
||||||
main.c
|
main.c
|
||||||
term.c
|
term.c
|
||||||
term.h
|
term.h
|
||||||
|
whereami.c
|
||||||
|
whereami.h
|
||||||
"${EXTRA_WINDOWS_SOURCES}"
|
"${EXTRA_WINDOWS_SOURCES}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,8 @@ libavrdude_a_SOURCES = \
|
||||||
linuxspi.h \
|
linuxspi.h \
|
||||||
linux_ppdev.h \
|
linux_ppdev.h \
|
||||||
lists.c \
|
lists.c \
|
||||||
|
micronucleus.c \
|
||||||
|
micronucleus.h \
|
||||||
my_ddk_hidsdi.h \
|
my_ddk_hidsdi.h \
|
||||||
par.c \
|
par.c \
|
||||||
par.h \
|
par.h \
|
||||||
|
@ -194,6 +196,8 @@ include_HEADERS = libavrdude.h
|
||||||
|
|
||||||
avrdude_SOURCES = \
|
avrdude_SOURCES = \
|
||||||
main.c \
|
main.c \
|
||||||
|
whereami.c \
|
||||||
|
whereami.h \
|
||||||
term.c \
|
term.c \
|
||||||
term.h
|
term.h
|
||||||
|
|
||||||
|
|
|
@ -216,8 +216,8 @@ The Atmel DFU bootloader is supported in both, FLIP protocol version 1
|
||||||
(AT90USB* and ATmega*U* devices), as well as version 2 (Xmega devices).
|
(AT90USB* and ATmega*U* devices), as well as version 2 (Xmega devices).
|
||||||
See below for some hints about FLIP version 1 protocol behaviour.
|
See below for some hints about FLIP version 1 protocol behaviour.
|
||||||
.Pp
|
.Pp
|
||||||
The MPLAB(R) PICkit 4, MPLAB(R) SNAP, and Curiosity Nano boards are
|
The MPLAB(R) PICkit 4 and MPLAB(R) SNAP, are supported in ISP, PDI and UPDI mode.
|
||||||
supported in UPDI mode. The Curiosity Nano board is dubbed
|
The Curiosity Nano board is supported in UPDI mode. It is dubbed
|
||||||
.Dq PICkit on Board ,
|
.Dq PICkit on Board ,
|
||||||
thus the name
|
thus the name
|
||||||
.Pa pkobn_updi .
|
.Pa pkobn_updi .
|
||||||
|
@ -240,6 +240,15 @@ which enables it to interface with avrdude using the jtagice mkii protocol
|
||||||
via a serial link.
|
via a serial link.
|
||||||
.Li https://github.com/ElTangas/jtag2updi
|
.Li https://github.com/ElTangas/jtag2updi
|
||||||
.Pp
|
.Pp
|
||||||
|
The Micronucleus bootloader is supported for both protocol version V1
|
||||||
|
and V2. As the bootloader does not support reading from flash memory,
|
||||||
|
use the
|
||||||
|
.Fl V
|
||||||
|
option to prevent AVRDUDE from verifing the flash memory.
|
||||||
|
See the section on
|
||||||
|
.Em extended parameters
|
||||||
|
for Micronucleus specific options.
|
||||||
|
.Pp
|
||||||
Input files can be provided, and output files can be written in
|
Input files can be provided, and output files can be written in
|
||||||
different file formats, such as raw binary files containing the data
|
different file formats, such as raw binary files containing the data
|
||||||
to download to the chip, Intel hex format, or Motorola S-record
|
to download to the chip, Intel hex format, or Motorola S-record
|
||||||
|
@ -1091,6 +1100,16 @@ Especially in ascii mode this happens very often, so setting a smaller value
|
||||||
can speed up programming a lot.
|
can speed up programming a lot.
|
||||||
The default value is 100ms. Using 10ms might work in most cases.
|
The default value is 100ms. Using 10ms might work in most cases.
|
||||||
.El
|
.El
|
||||||
|
.It Ar Micronucleus bootloader
|
||||||
|
.Bl -tag -offset indent -width indent
|
||||||
|
.It Ar wait[=<timeout>]
|
||||||
|
If the device is not connected, wait for the device to be plugged in.
|
||||||
|
The optional
|
||||||
|
.Ar timeout
|
||||||
|
specifies the connection time-out in seconds.
|
||||||
|
If no time-out is specified, AVRDUDE will wait indefinitely until the
|
||||||
|
device is plugged in.
|
||||||
|
.El
|
||||||
.It Ar Wiring
|
.It Ar Wiring
|
||||||
When using the Wiring programmer type, the
|
When using the Wiring programmer type, the
|
||||||
following optional extended parameter is accepted:
|
following optional extended parameter is accepted:
|
||||||
|
@ -1154,8 +1173,16 @@ line.
|
||||||
.It Pa /dev/ppi0
|
.It Pa /dev/ppi0
|
||||||
default device to be used for communication with the programming
|
default device to be used for communication with the programming
|
||||||
hardware
|
hardware
|
||||||
.It Pa ${PREFIX}/etc/avrdude.conf
|
.It Pa avrdude.conf
|
||||||
programmer and parts configuration file
|
programmer and parts configuration file
|
||||||
|
.Pp
|
||||||
|
On Windows systems, this file is looked up in the same directory as the
|
||||||
|
executable file.
|
||||||
|
On all other systems, the file is first looked up in
|
||||||
|
.Pa ../etc/ ,
|
||||||
|
relative to the path of the executable, then in the same directory as
|
||||||
|
the executable itself, and finally in the system default location
|
||||||
|
.Pa ${PREFIX}/etc/avrdude.conf .
|
||||||
.It Pa ${HOME}/.avrduderc
|
.It Pa ${HOME}/.avrduderc
|
||||||
programmer and parts configuration file (per-user overrides)
|
programmer and parts configuration file (per-user overrides)
|
||||||
.It Pa ~/.inputrc
|
.It Pa ~/.inputrc
|
||||||
|
|
|
@ -916,6 +916,15 @@ programmer
|
||||||
usbpid = 0x0BA5;
|
usbpid = 0x0BA5;
|
||||||
;
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "micronucleus";
|
||||||
|
desc = "Micronucleus Bootloader";
|
||||||
|
type = "micronucleus";
|
||||||
|
connection_type = usb;
|
||||||
|
usbvid = 0x16D0;
|
||||||
|
usbpid = 0x0753;
|
||||||
|
;
|
||||||
|
|
||||||
# commercial version of USBtiny, using a separate VID/PID
|
# commercial version of USBtiny, using a separate VID/PID
|
||||||
programmer
|
programmer
|
||||||
id = "iseavrprog";
|
id = "iseavrprog";
|
||||||
|
@ -1274,6 +1283,22 @@ programmer
|
||||||
usbpid = 0x2177, 0x2178, 0x2179;
|
usbpid = 0x2177, 0x2178, 0x2179;
|
||||||
;
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "pickit4_pdi";
|
||||||
|
desc = "MPLAB(R) PICkit 4 in PDI mode";
|
||||||
|
type = "jtagice3_pdi";
|
||||||
|
connection_type = usb;
|
||||||
|
usbpid = 0x2177, 0x2178, 0x2179;
|
||||||
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "pickit4_isp";
|
||||||
|
desc = "MPLAB(R) PICkit 4 in ISP mode";
|
||||||
|
type = "jtagice3_isp";
|
||||||
|
connection_type = usb;
|
||||||
|
usbpid = 0x2177, 0x2178, 0x2179;
|
||||||
|
;
|
||||||
|
|
||||||
programmer
|
programmer
|
||||||
id = "snap_updi";
|
id = "snap_updi";
|
||||||
desc = "MPLAB(R) SNAP in UPDI mode";
|
desc = "MPLAB(R) SNAP in UPDI mode";
|
||||||
|
@ -1282,6 +1307,22 @@ programmer
|
||||||
usbpid = 0x217F, 0x2180, 0x2181;
|
usbpid = 0x217F, 0x2180, 0x2181;
|
||||||
;
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "snap_pdi";
|
||||||
|
desc = "MPLAB(R) SNAP in PDI mode";
|
||||||
|
type = "jtagice3_pdi";
|
||||||
|
connection_type = usb;
|
||||||
|
usbpid = 0x217F, 0x2180, 0x2181;
|
||||||
|
;
|
||||||
|
|
||||||
|
programmer
|
||||||
|
id = "snap_isp";
|
||||||
|
desc = "MPLAB(R) SNAP in ISP mode";
|
||||||
|
type = "jtagice3_isp";
|
||||||
|
connection_type = usb;
|
||||||
|
usbpid = 0x217F, 0x2180, 0x2181;
|
||||||
|
;
|
||||||
|
|
||||||
programmer
|
programmer
|
||||||
id = "pkobn_updi";
|
id = "pkobn_updi";
|
||||||
desc = "Curiosity nano (nEDBG) in UPDI mode";
|
desc = "Curiosity nano (nEDBG) in UPDI mode";
|
||||||
|
|
|
@ -178,6 +178,7 @@ static int buspirate_getc(struct programmer_t *pgm)
|
||||||
static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size_t len)
|
static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size_t len)
|
||||||
{
|
{
|
||||||
char *buf_p;
|
char *buf_p;
|
||||||
|
int c;
|
||||||
long orig_serial_recv_timeout = serial_recv_timeout;
|
long orig_serial_recv_timeout = serial_recv_timeout;
|
||||||
|
|
||||||
/* Static local buffer - this may come handy at times */
|
/* Static local buffer - this may come handy at times */
|
||||||
|
@ -190,12 +191,12 @@ static char *buspirate_readline_noexit(struct programmer_t *pgm, char *buf, size
|
||||||
buf_p = buf;
|
buf_p = buf;
|
||||||
memset(buf, 0, len);
|
memset(buf, 0, len);
|
||||||
while (buf_p < (buf + len - 1)) { /* keep the very last byte == 0 */
|
while (buf_p < (buf + len - 1)) { /* keep the very last byte == 0 */
|
||||||
*buf_p = buspirate_getc(pgm);
|
*buf_p = c = buspirate_getc(pgm);
|
||||||
if (*buf_p == '\r')
|
if (c == '\r')
|
||||||
continue;
|
continue;
|
||||||
if (*buf_p == '\n')
|
if (c == '\n')
|
||||||
break;
|
break;
|
||||||
if (*buf_p == EOF) {
|
if (c == EOF) {
|
||||||
*buf_p = '\0';
|
*buf_p = '\0';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "msvc/msvc_compat.h"
|
#include "msvc/msvc_compat.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VERSION "@PROJECT_VERSION@"
|
#define VERSION "@AVRDUDE_FULL_VERSION@"
|
||||||
|
|
||||||
/* Options */
|
/* Options */
|
||||||
|
|
||||||
|
|
|
@ -303,8 +303,8 @@ The Atmel DFU bootloader is supported in both, FLIP protocol version 1
|
||||||
(AT90USB* and ATmega*U* devices), as well as version 2 (Xmega devices).
|
(AT90USB* and ATmega*U* devices), as well as version 2 (Xmega devices).
|
||||||
See below for some hints about FLIP version 1 protocol behaviour.
|
See below for some hints about FLIP version 1 protocol behaviour.
|
||||||
|
|
||||||
The MPLAB(R) PICkit 4, MPLAB(R) SNAP, and Curiosity Nano boards are
|
The MPLAB(R) PICkit 4 and MPLAB(R) SNAP are supported in ISP, PDI and UPDI mode.
|
||||||
supported in UPDI mode. The Curiosity Nano board is dubbed ``PICkit on
|
The Curiosity Nano board is supported in UPDI mode. It is dubbed ``PICkit on
|
||||||
Board'', thus the name @code{pkobn_updi}.
|
Board'', thus the name @code{pkobn_updi}.
|
||||||
|
|
||||||
SerialUPDI programmer implementation is based on Microchip's
|
SerialUPDI programmer implementation is based on Microchip's
|
||||||
|
@ -321,6 +321,12 @@ Jtag2updi is just a firmware that can be uploaded to an AVR,
|
||||||
which enables it to interface with avrdude using the jtagice mkii protocol
|
which enables it to interface with avrdude using the jtagice mkii protocol
|
||||||
via a serial link (@url{https://github.com/ElTangas/jtag2updi}).
|
via a serial link (@url{https://github.com/ElTangas/jtag2updi}).
|
||||||
|
|
||||||
|
The Micronucleus bootloader is supported for both protocol version V1
|
||||||
|
and V2. As the bootloader does not support reading from flash memory,
|
||||||
|
use the @code{-V} option to prevent AVRDUDE from verifing the flash memory.
|
||||||
|
See the section on @emph{extended parameters}
|
||||||
|
below for Micronucleus specific options.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* History::
|
* History::
|
||||||
@end menu
|
@end menu
|
||||||
|
@ -454,10 +460,22 @@ Currently, the following programmer ids are understood and supported:
|
||||||
@item -C @var{config-file}
|
@item -C @var{config-file}
|
||||||
Use the specified config file for configuration data. This file
|
Use the specified config file for configuration data. This file
|
||||||
contains all programmer and part definitions that AVRDUDE knows about.
|
contains all programmer and part definitions that AVRDUDE knows about.
|
||||||
If not
|
If not specified, AVRDUDE looks for the configuration file in the following
|
||||||
specified, AVRDUDE reads the configuration file from
|
two locations:
|
||||||
/usr/local/etc/avrdude.conf (FreeBSD and Linux). See Appendix A for
|
|
||||||
the method of searching for the configuration file for Windows.
|
@enumerate
|
||||||
|
|
||||||
|
@item
|
||||||
|
@code{<directory from which application loaded>/../etc/avrdude.conf}
|
||||||
|
|
||||||
|
@item
|
||||||
|
@code{<directory from which application loaded>/avrdude.conf}
|
||||||
|
|
||||||
|
@end enumerate
|
||||||
|
|
||||||
|
If not found there, the lookup procedure becomes platform dependent. On FreeBSD
|
||||||
|
and Linux, AVRDUDE looks at @code{/usr/local/etc/avrdude.conf}. See Appendix A
|
||||||
|
for the method of searching on Windows.
|
||||||
|
|
||||||
If @var{config-file} is written as @var{+filename}
|
If @var{config-file} is written as @var{+filename}
|
||||||
then this file is read after the system wide and user configuration
|
then this file is read after the system wide and user configuration
|
||||||
|
@ -962,6 +980,18 @@ The default value is 100ms. Using 10ms might work in most cases.
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@item Micronucleus bootloader
|
||||||
|
|
||||||
|
When using the Micronucleus programmer type, the
|
||||||
|
following optional extended parameter is accepted:
|
||||||
|
@table @code
|
||||||
|
@item @samp{wait=@var{timeout}}
|
||||||
|
If the device is not connected, wait for the device to be plugged in.
|
||||||
|
The optional @var{timeout} specifies the connection time-out in seconds.
|
||||||
|
If no time-out is specified, AVRDUDE will wait indefinitely until the
|
||||||
|
device is plugged in.
|
||||||
|
@end table
|
||||||
|
|
||||||
@item Wiring
|
@item Wiring
|
||||||
|
|
||||||
When using the Wiring programmer type, the
|
When using the Wiring programmer type, the
|
||||||
|
@ -2302,6 +2332,10 @@ configuration files:
|
||||||
|
|
||||||
@enumerate
|
@enumerate
|
||||||
|
|
||||||
|
@item
|
||||||
|
Only for the system configuration file:
|
||||||
|
@code{<directory from which application loaded>/../etc/avrdude.conf}
|
||||||
|
|
||||||
@item
|
@item
|
||||||
The directory from which the application loaded.
|
The directory from which the application loaded.
|
||||||
|
|
||||||
|
|
32
src/jtag3.c
32
src/jtag3.c
|
@ -2305,6 +2305,31 @@ int jtag3_read_sib(PROGRAMMER * pgm, AVRPART * p, char * sib)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int jtag3_set_vtarget(PROGRAMMER * pgm, double v)
|
||||||
|
{
|
||||||
|
unsigned uaref, utarg;
|
||||||
|
unsigned char buf[2];
|
||||||
|
|
||||||
|
utarg = (unsigned)(v * 1000);
|
||||||
|
|
||||||
|
if (jtag3_getparm(pgm, SCOPE_GENERAL, 1, PARM3_VTARGET, buf, 2) < 0) {
|
||||||
|
avrdude_message(MSG_INFO, "%s: jtag3_set_vtarget(): cannot obtain V[aref]\n",
|
||||||
|
progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uaref = b2_to_u16(buf);
|
||||||
|
u16_to_b2(buf, utarg);
|
||||||
|
|
||||||
|
avrdude_message(MSG_INFO, "%s: jtag3_set_vtarget(): changing V[target] from %.1f to %.1f\n",
|
||||||
|
progname, uaref / 1000.0, v);
|
||||||
|
|
||||||
|
if (jtag3_setparm(pgm, SCOPE_GENERAL, 1, PARM3_VADJUST, buf, sizeof(buf)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void jtag3_display(PROGRAMMER * pgm, const char * p)
|
static void jtag3_display(PROGRAMMER * pgm, const char * p)
|
||||||
{
|
{
|
||||||
unsigned char parms[5];
|
unsigned char parms[5];
|
||||||
|
@ -2564,5 +2589,12 @@ void jtag3_updi_initpgm(PROGRAMMER * pgm)
|
||||||
pgm->flag = PGM_FL_IS_UPDI;
|
pgm->flag = PGM_FL_IS_UPDI;
|
||||||
pgm->unlock = jtag3_unlock_erase_key;
|
pgm->unlock = jtag3_unlock_erase_key;
|
||||||
pgm->read_sib = jtag3_read_sib;
|
pgm->read_sib = jtag3_read_sib;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* enable target voltage adjustment for PKOB/nEDBG boards
|
||||||
|
*/
|
||||||
|
if (matches(ldata(lfirst(pgm->id)), "pkobn_updi")) {
|
||||||
|
pgm->set_vtarget = jtag3_set_vtarget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,8 +189,11 @@
|
||||||
#define PARM3_FW_RELEASE 0x03 /* section 0, generic scope, 1 byte;
|
#define PARM3_FW_RELEASE 0x03 /* section 0, generic scope, 1 byte;
|
||||||
* always asked for by Atmel Studio,
|
* always asked for by Atmel Studio,
|
||||||
* but never displayed there */
|
* but never displayed there */
|
||||||
#define PARM3_VTARGET 0x00 /* section 1, generic scope, 2 bytes,
|
#define PARM3_VTARGET 0x00 /* section 1, generic scope, 2 bytes, in millivolts */
|
||||||
* in millivolts */
|
#define PARM3_VBUF 0x01 /* section 1, generic scope, 2 bytes, bufferred target voltage reference */
|
||||||
|
#define PARM3_VUSB 0x02 /* section 1, generic scope, 2 bytes, USB voltage */
|
||||||
|
#define PARM3_VADJUST 0x20 /* section 1, generic scope, 2 bytes, set voltage */
|
||||||
|
|
||||||
#define PARM3_DEVICEDESC 0x00 /* section 2, memory etc. configuration,
|
#define PARM3_DEVICEDESC 0x00 /* section 2, memory etc. configuration,
|
||||||
* 31 bytes for tiny/mega AVR, 47 bytes
|
* 31 bytes for tiny/mega AVR, 47 bytes
|
||||||
* for Xmega; is also used in command
|
* for Xmega; is also used in command
|
||||||
|
|
123
src/main.c
123
src/main.c
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <whereami.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -131,7 +132,7 @@ static void usage(void)
|
||||||
" -q Quell progress output. -q -q for less.\n"
|
" -q Quell progress output. -q -q for less.\n"
|
||||||
" -l logfile Use logfile rather than stderr for diagnostics.\n"
|
" -l logfile Use logfile rather than stderr for diagnostics.\n"
|
||||||
" -? Display this usage.\n"
|
" -? Display this usage.\n"
|
||||||
"\navrdude version %s, URL: <http://savannah.nongnu.org/projects/avrdude/>\n"
|
"\navrdude version %s, URL: <https://github.com/avrdudes/avrdude>\n"
|
||||||
,progname, version);
|
,progname, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +330,10 @@ int main(int argc, char * argv [])
|
||||||
char * partdesc; /* part id */
|
char * partdesc; /* part id */
|
||||||
char sys_config[PATH_MAX]; /* system wide config file */
|
char sys_config[PATH_MAX]; /* system wide config file */
|
||||||
char usr_config[PATH_MAX]; /* per-user config file */
|
char usr_config[PATH_MAX]; /* per-user config file */
|
||||||
|
char executable_abspath[PATH_MAX]; /* absolute path to avrdude executable */
|
||||||
|
char executable_dirpath[PATH_MAX]; /* absolute path to folder with executable */
|
||||||
|
bool executable_abspath_found = false; /* absolute path to executable found */
|
||||||
|
bool sys_config_found = false; /* 'avrdude.conf' file found */
|
||||||
char * e; /* for strtol() error checking */
|
char * e; /* for strtol() error checking */
|
||||||
int baudrate; /* override default programmer baud rate */
|
int baudrate; /* override default programmer baud rate */
|
||||||
double bitclock; /* Specify programmer bit clock (JTAG ICE) */
|
double bitclock; /* Specify programmer bit clock (JTAG ICE) */
|
||||||
|
@ -417,31 +422,131 @@ int main(int argc, char * argv [])
|
||||||
is_open = 0;
|
is_open = 0;
|
||||||
logfile = NULL;
|
logfile = NULL;
|
||||||
|
|
||||||
#if defined(WIN32NATIVE)
|
/*
|
||||||
|
* EXECUTABLE ABSPATH
|
||||||
|
* ------------------
|
||||||
|
* Determine the absolute path to avrdude executable. This will be used to
|
||||||
|
* locate the 'avrdude.conf' file later.
|
||||||
|
*/
|
||||||
|
int executable_dirpath_len;
|
||||||
|
int executable_abspath_len = wai_getExecutablePath(
|
||||||
|
executable_abspath,
|
||||||
|
PATH_MAX,
|
||||||
|
&executable_dirpath_len
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
(executable_abspath_len != -1) &&
|
||||||
|
(executable_abspath_len != 0) &&
|
||||||
|
(executable_dirpath_len != -1) &&
|
||||||
|
(executable_dirpath_len != 0)
|
||||||
|
) {
|
||||||
|
// All requirements satisfied, executable path was found
|
||||||
|
executable_abspath_found = true;
|
||||||
|
|
||||||
win_sys_config_set(sys_config);
|
// Make sure the string is null terminated
|
||||||
win_usr_config_set(usr_config);
|
executable_abspath[executable_abspath_len] = '\0';
|
||||||
|
|
||||||
#else
|
// Replace all backslashes with forward slashes
|
||||||
|
i = 0;
|
||||||
|
while (true) {
|
||||||
|
if (executable_abspath[i] == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (executable_abspath[i] == '\\') {
|
||||||
|
executable_abspath[i] = '/';
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(sys_config, CONFIG_DIR);
|
// Define 'executable_dirpath' to be the path to the parent folder of the
|
||||||
|
// executable.
|
||||||
|
strcpy(executable_dirpath, executable_abspath);
|
||||||
|
executable_dirpath[executable_dirpath_len] = '\0';
|
||||||
|
|
||||||
|
// Debug output
|
||||||
|
// avrdude_message(MSG_INFO, "executable_abspath = %s\n", executable_abspath);
|
||||||
|
// avrdude_message(MSG_INFO, "executable_abspath_len = %i\n", executable_abspath_len);
|
||||||
|
// avrdude_message(MSG_INFO, "executable_dirpath = %s\n", executable_dirpath);
|
||||||
|
// avrdude_message(MSG_INFO, "executable_dirpath_len = %i\n", executable_dirpath_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SYSTEM CONFIG
|
||||||
|
* -------------
|
||||||
|
* Determine the location of 'avrdude.conf'. Check in this order:
|
||||||
|
* 1. <dirpath of executable>/../etc/avrdude.conf
|
||||||
|
* 2. <dirpath of executable>/avrdude.conf
|
||||||
|
* 3. CONFIG_DIR/avrdude.conf
|
||||||
|
*
|
||||||
|
* When found, write the result into the 'sys_config' variable.
|
||||||
|
*/
|
||||||
|
if (executable_abspath_found) {
|
||||||
|
// 1. Check <dirpath of executable>/../etc/avrdude.conf
|
||||||
|
strcpy(sys_config, executable_dirpath);
|
||||||
|
sys_config[PATH_MAX - 1] = '\0';
|
||||||
i = strlen(sys_config);
|
i = strlen(sys_config);
|
||||||
if (i && (sys_config[i-1] != '/'))
|
if (i && (sys_config[i - 1] != '/'))
|
||||||
|
strcat(sys_config, "/");
|
||||||
|
strcat(sys_config, "../etc/avrdude.conf");
|
||||||
|
sys_config[PATH_MAX - 1] = '\0';
|
||||||
|
if (access(sys_config, F_OK) == 0) {
|
||||||
|
sys_config_found = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 2. Check <dirpath of executable>/avrdude.conf
|
||||||
|
strcpy(sys_config, executable_dirpath);
|
||||||
|
sys_config[PATH_MAX - 1] = '\0';
|
||||||
|
i = strlen(sys_config);
|
||||||
|
if (i && (sys_config[i - 1] != '/'))
|
||||||
strcat(sys_config, "/");
|
strcat(sys_config, "/");
|
||||||
strcat(sys_config, "avrdude.conf");
|
strcat(sys_config, "avrdude.conf");
|
||||||
|
sys_config[PATH_MAX - 1] = '\0';
|
||||||
|
if (access(sys_config, F_OK) == 0) {
|
||||||
|
sys_config_found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sys_config_found) {
|
||||||
|
// 3. Check CONFIG_DIR/avrdude.conf
|
||||||
|
#if defined(WIN32NATIVE)
|
||||||
|
win_sys_config_set(sys_config);
|
||||||
|
#else
|
||||||
|
strcpy(sys_config, CONFIG_DIR);
|
||||||
|
i = strlen(sys_config);
|
||||||
|
if (i && (sys_config[i - 1] != '/'))
|
||||||
|
strcat(sys_config, "/");
|
||||||
|
strcat(sys_config, "avrdude.conf");
|
||||||
|
#endif
|
||||||
|
if (access(sys_config, F_OK) == 0) {
|
||||||
|
sys_config_found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Debug output
|
||||||
|
// avrdude_message(MSG_INFO, "sys_config = %s\n", sys_config);
|
||||||
|
// avrdude_message(MSG_INFO, "sys_config_found = %s\n", sys_config_found ? "true" : "false");
|
||||||
|
// avrdude_message(MSG_INFO, "\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* USER CONFIG
|
||||||
|
* -----------
|
||||||
|
* Determine the location of '.avrduderc'. Nothing changed here.
|
||||||
|
*/
|
||||||
|
#if defined(WIN32NATIVE)
|
||||||
|
win_usr_config_set(usr_config);
|
||||||
|
#else
|
||||||
usr_config[0] = 0;
|
usr_config[0] = 0;
|
||||||
homedir = getenv("HOME");
|
homedir = getenv("HOME");
|
||||||
if (homedir != NULL) {
|
if (homedir != NULL) {
|
||||||
strcpy(usr_config, homedir);
|
strcpy(usr_config, homedir);
|
||||||
i = strlen(usr_config);
|
i = strlen(usr_config);
|
||||||
if (i && (usr_config[i-1] != '/'))
|
if (i && (usr_config[i - 1] != '/'))
|
||||||
strcat(usr_config, "/");
|
strcat(usr_config, "/");
|
||||||
strcat(usr_config, ".avrduderc");
|
strcat(usr_config, ".avrduderc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
len = strlen(progname) + 2;
|
len = strlen(progname) + 2;
|
||||||
for (i=0; i<len; i++)
|
for (i=0; i<len; i++)
|
||||||
progbuf[i] = ' ';
|
progbuf[i] = ' ';
|
||||||
|
|
|
@ -0,0 +1,951 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2019 Marius Greuel
|
||||||
|
* Portions Copyright (C) 2014 T. Bo"scke
|
||||||
|
* Portions Copyright (C) 2012 ihsan Kehribar
|
||||||
|
*
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Notes:
|
||||||
|
// This file adds support for the Micronucleus bootloader V1 and V2,
|
||||||
|
// so you do no longer need the Micronucleus command-line utility.
|
||||||
|
//
|
||||||
|
// This bootloader is typically used on small ATtiny boards,
|
||||||
|
// such as Digispark (ATtiny85), Digispark Pro (ATtiny167),
|
||||||
|
// and the respective clones.
|
||||||
|
// By default, it bootloader uses the VID/PID 16d0:0753 (MCS Digistump).
|
||||||
|
//
|
||||||
|
// As the micronucleus bootloader is optimized for size, it implements
|
||||||
|
// writing to flash memory only. Since it does not support reading,
|
||||||
|
// use the -V option to prevent avrdude from verifing the flash memory.
|
||||||
|
// To have avrdude wait for the device to be connected, use the
|
||||||
|
// extended option '-x wait'.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// avrdude -c micronucleus -p t85 -x wait -V -U flash:w:main.hex
|
||||||
|
|
||||||
|
#include "ac_cfg.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "avrdude.h"
|
||||||
|
#include "micronucleus.h"
|
||||||
|
#include "usbdevs.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBUSB)
|
||||||
|
|
||||||
|
#if defined(HAVE_USB_H)
|
||||||
|
#include <usb.h>
|
||||||
|
#elif defined(HAVE_LUSB0_USB_H)
|
||||||
|
#include <lusb0_usb.h>
|
||||||
|
#else
|
||||||
|
#error "libusb needs either <usb.h> or <lusb0_usb.h>"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define MICRONUCLEUS_VID 0x16D0
|
||||||
|
#define MICRONUCLEUS_PID 0x0753
|
||||||
|
|
||||||
|
#define MICRONUCLEUS_CONNECT_WAIT 100
|
||||||
|
|
||||||
|
#define MICRONUCLEUS_CMD_INFO 0
|
||||||
|
#define MICRONUCLEUS_CMD_TRANSFER 1
|
||||||
|
#define MICRONUCLEUS_CMD_ERASE 2
|
||||||
|
#define MICRONUCLEUS_CMD_PROGRAM 3
|
||||||
|
#define MICRONUCLEUS_CMD_START 4
|
||||||
|
|
||||||
|
#define MICRONUCLEUS_DEFAULT_TIMEOUT 500
|
||||||
|
#define MICRONUCLEUS_MAX_MAJOR_VERSION 2
|
||||||
|
|
||||||
|
#define PDATA(pgm) ((pdata_t*)(pgm->cookie))
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
typedef struct pdata
|
||||||
|
{
|
||||||
|
usb_dev_handle* usb_handle;
|
||||||
|
// Extended parameters
|
||||||
|
bool wait_until_device_present;
|
||||||
|
int wait_timout; // in seconds
|
||||||
|
// Bootloader version
|
||||||
|
uint8_t major_version;
|
||||||
|
uint8_t minor_version;
|
||||||
|
// Bootloader info (via USB request)
|
||||||
|
uint16_t flash_size; // programmable size (in bytes) of flash
|
||||||
|
uint8_t page_size; // size (in bytes) of page
|
||||||
|
uint8_t write_sleep; // milliseconds
|
||||||
|
uint8_t signature1; // only used in protocol v2
|
||||||
|
uint8_t signature2; // only used in protocol v2
|
||||||
|
// Calculated bootloader info
|
||||||
|
uint16_t pages; // total number of pages to program
|
||||||
|
uint16_t bootloader_start; // start of the bootloader (at page boundary)
|
||||||
|
uint16_t erase_sleep; // milliseconds
|
||||||
|
// State
|
||||||
|
uint16_t user_reset_vector; // reset vector of user program
|
||||||
|
bool write_last_page; // last page already programmed
|
||||||
|
bool start_program; // require start after flash
|
||||||
|
} pdata_t;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void delay_ms(uint32_t duration)
|
||||||
|
{
|
||||||
|
usleep(duration * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_check_connection(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
if (pdata->major_version >= 2)
|
||||||
|
{
|
||||||
|
uint8_t buffer[6] = { 0 };
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
return result == sizeof(buffer) ? 0 : -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint8_t buffer[4] = { 0 };
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
return result == sizeof(buffer) ? 0 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_reconnect(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
struct usb_device* device = usb_device(pdata->usb_handle);
|
||||||
|
|
||||||
|
usb_close(pdata->usb_handle);
|
||||||
|
pdata->usb_handle = NULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < 25; i++)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Trying to reconnect...\n", progname);
|
||||||
|
|
||||||
|
pdata->usb_handle = usb_open(device);
|
||||||
|
if (pdata->usb_handle != NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
delay_ms(MICRONUCLEUS_CONNECT_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_get_bootloader_info_v1(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
uint8_t buffer[4] = { 0 };
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Failed to get bootloader info block: %s\n",
|
||||||
|
progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if (result < sizeof(buffer))
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Received invalid bootloader info block size: %d\n",
|
||||||
|
progname, result);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->flash_size = (buffer[0] << 8) | buffer[1];
|
||||||
|
pdata->page_size = buffer[2];
|
||||||
|
pdata->write_sleep = buffer[3] & 127;
|
||||||
|
|
||||||
|
// Take a wild guess on the part ID, so that we can supply it for device verification
|
||||||
|
if (pdata->page_size == 128)
|
||||||
|
{
|
||||||
|
// ATtiny167
|
||||||
|
pdata->signature1 = 0x94;
|
||||||
|
pdata->signature2 = 0x87;
|
||||||
|
}
|
||||||
|
else if (pdata->page_size == 64)
|
||||||
|
{
|
||||||
|
if (pdata->flash_size > 4096)
|
||||||
|
{
|
||||||
|
// ATtiny85
|
||||||
|
pdata->signature1 = 0x93;
|
||||||
|
pdata->signature2 = 0x0B;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// ATtiny45
|
||||||
|
pdata->signature1 = 0x92;
|
||||||
|
pdata->signature2 = 0x06;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pdata->page_size == 16)
|
||||||
|
{
|
||||||
|
// ATtiny841
|
||||||
|
pdata->signature1 = 0x93;
|
||||||
|
pdata->signature2 = 0x15;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Unknown device
|
||||||
|
pdata->signature1 = 0;
|
||||||
|
pdata->signature2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->pages = (pdata->flash_size + pdata->page_size - 1) / pdata->page_size;
|
||||||
|
pdata->bootloader_start = pdata->pages * pdata->page_size;
|
||||||
|
pdata->erase_sleep = pdata->write_sleep * pdata->pages;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_get_bootloader_info_v2(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
uint8_t buffer[6] = { 0 };
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_INFO, 0, 0, (char*)buffer, sizeof(buffer), MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Failed to get bootloader info block: %s\n",
|
||||||
|
progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else if (result < sizeof(buffer))
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Received invalid bootloader info block size: %d\n",
|
||||||
|
progname, result);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->flash_size = (buffer[0] << 8) + buffer[1];
|
||||||
|
pdata->page_size = buffer[2];
|
||||||
|
pdata->write_sleep = (buffer[3] & 127) + 2;
|
||||||
|
pdata->signature1 = buffer[4];
|
||||||
|
pdata->signature2 = buffer[5];
|
||||||
|
|
||||||
|
pdata->pages = (pdata->flash_size + pdata->page_size - 1) / pdata->page_size;
|
||||||
|
pdata->bootloader_start = pdata->pages * pdata->page_size;
|
||||||
|
pdata->erase_sleep = pdata->write_sleep * pdata->pages;
|
||||||
|
|
||||||
|
// if bit 7 of write sleep time is set, divide the erase time by four to
|
||||||
|
// accomodate to the 4*page erase of the ATtiny841/441
|
||||||
|
if ((buffer[3] & 128) != 0)
|
||||||
|
{
|
||||||
|
pdata->erase_sleep /= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_get_bootloader_info(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
if (pdata->major_version >= 2)
|
||||||
|
{
|
||||||
|
return micronucleus_get_bootloader_info_v2(pdata);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return micronucleus_get_bootloader_info_v1(pdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_dump_device_info(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Bootloader version: %d.%d\n", progname, pdata->major_version, pdata->minor_version);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Available flash size: %u\n", progname, pdata->flash_size);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Page size: %u\n", progname, pdata->page_size);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Bootloader start: 0x%04X\n", progname, pdata->bootloader_start);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Write sleep: %ums\n", progname, pdata->write_sleep);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Erase sleep: %ums\n", progname, pdata->erase_sleep);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Signature1: 0x%02X\n", progname, pdata->signature1);
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Signature2: 0x%02X\n", progname, pdata->signature2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_erase_device(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_erase_device()\n", progname);
|
||||||
|
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_ERASE, 0, 0, NULL, 0, MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case -EIO:
|
||||||
|
case -EPIPE:
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Ignoring last error of erase command: %s\n", progname, usb_strerror());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Failed is issue erase command, code %d: %s\n", progname, result, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_ms(pdata->erase_sleep);
|
||||||
|
|
||||||
|
result = micronucleus_check_connection(pdata);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Connection dropped, trying to reconnect...\n", progname);
|
||||||
|
|
||||||
|
result = micronucleus_reconnect(pdata);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Failed to reconnect USB device: %s\n", progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_patch_reset_vector(pdata_t* pdata, uint8_t* buffer)
|
||||||
|
{
|
||||||
|
// Save user reset vector.
|
||||||
|
uint16_t word0 = (buffer[1] << 8) | buffer[0];
|
||||||
|
uint16_t word1 = (buffer[3] << 8) | buffer[2];
|
||||||
|
|
||||||
|
if (word0 == 0x940C)
|
||||||
|
{
|
||||||
|
// long jump
|
||||||
|
pdata->user_reset_vector = word1;
|
||||||
|
}
|
||||||
|
else if ((word0 & 0xF000) == 0xC000)
|
||||||
|
{
|
||||||
|
// rjmp
|
||||||
|
pdata->user_reset_vector = (word0 & 0x0FFF) + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: The reset vector of the user program does not contain a branch instruction.\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch in jmp to bootloader.
|
||||||
|
if (pdata->bootloader_start > 0x2000)
|
||||||
|
{
|
||||||
|
// jmp
|
||||||
|
uint16_t data = 0x940C;
|
||||||
|
buffer[0] = (uint8_t)(data >> 0);
|
||||||
|
buffer[1] = (uint8_t)(data >> 8);
|
||||||
|
buffer[2] = (uint8_t)(pdata->bootloader_start >> 0);
|
||||||
|
buffer[3] = (uint8_t)(pdata->bootloader_start >> 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// rjmp
|
||||||
|
uint16_t data = 0xC000 | ((pdata->bootloader_start / 2 - 1) & 0x0FFF);
|
||||||
|
buffer[0] = (uint8_t)(data >> 0);
|
||||||
|
buffer[1] = (uint8_t)(data >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_patch_user_vector(pdata_t* pdata, uint8_t* buffer)
|
||||||
|
{
|
||||||
|
uint16_t user_reset_addr = pdata->bootloader_start - 4;
|
||||||
|
uint16_t address = pdata->bootloader_start - pdata->page_size;
|
||||||
|
if (user_reset_addr > 0x2000)
|
||||||
|
{
|
||||||
|
// jmp
|
||||||
|
uint16_t data = 0x940C;
|
||||||
|
buffer[user_reset_addr - address + 0] = (uint8_t)(data >> 0);
|
||||||
|
buffer[user_reset_addr - address + 1] = (uint8_t)(data >> 8);
|
||||||
|
buffer[user_reset_addr - address + 2] = (uint8_t)(pdata->user_reset_vector >> 0);
|
||||||
|
buffer[user_reset_addr - address + 3] = (uint8_t)(pdata->user_reset_vector >> 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// rjmp
|
||||||
|
uint16_t data = 0xC000 | ((pdata->user_reset_vector - user_reset_addr / 2 - 1) & 0x0FFF);
|
||||||
|
buffer[user_reset_addr - address + 0] = (uint8_t)(data >> 0);
|
||||||
|
buffer[user_reset_addr - address + 1] = (uint8_t)(data >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_write_page_v1(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size)
|
||||||
|
{
|
||||||
|
int result = usb_control_msg(pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_TRANSFER,
|
||||||
|
size, address,
|
||||||
|
buffer, size,
|
||||||
|
MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_write_page_v2(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size)
|
||||||
|
{
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_TRANSFER,
|
||||||
|
size, address,
|
||||||
|
NULL, 0,
|
||||||
|
MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i += 4)
|
||||||
|
{
|
||||||
|
int w1 = (buffer[i + 1] << 8) | (buffer[i + 0] << 0);
|
||||||
|
int w2 = (buffer[i + 3] << 8) | (buffer[i + 2] << 0);
|
||||||
|
result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_PROGRAM,
|
||||||
|
w1, w2,
|
||||||
|
NULL, 0,
|
||||||
|
MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to transfer page: %s\n", progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_write_page(pdata_t* pdata, uint32_t address, uint8_t* buffer, uint32_t size)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_write_page(address=0x%04X, size=%d)\n", progname, address, size);
|
||||||
|
|
||||||
|
if (address == 0)
|
||||||
|
{
|
||||||
|
if (pdata->major_version >= 2)
|
||||||
|
{
|
||||||
|
int result = micronucleus_patch_reset_vector(pdata, buffer);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Require last page (with application reset vector) to be written.
|
||||||
|
pdata->write_last_page = true;
|
||||||
|
|
||||||
|
// Require software start.
|
||||||
|
pdata->start_program = true;
|
||||||
|
}
|
||||||
|
else if (address >= pdata->bootloader_start - pdata->page_size)
|
||||||
|
{
|
||||||
|
if (pdata->major_version >= 2)
|
||||||
|
{
|
||||||
|
micronucleus_patch_user_vector(pdata, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark last page as written.
|
||||||
|
pdata->write_last_page = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if (pdata->major_version >= 2)
|
||||||
|
{
|
||||||
|
result = micronucleus_write_page_v2(pdata, address, buffer, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = micronucleus_write_page_v1(pdata, address, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_ms(pdata->write_sleep);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_start(pdata_t* pdata)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_start()\n", progname);
|
||||||
|
|
||||||
|
int result = usb_control_msg(
|
||||||
|
pdata->usb_handle,
|
||||||
|
USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||||
|
MICRONUCLEUS_CMD_START, 0, 0, NULL, 0, MICRONUCLEUS_DEFAULT_TIMEOUT);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: Failed is issue start command: %s\n", progname, usb_strerror());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void micronucleus_setup(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_setup()\n", progname);
|
||||||
|
|
||||||
|
if ((pgm->cookie = malloc(sizeof(pdata_t))) == 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: micronucleus_setup(): Out of memory allocating private data\n", progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pgm->cookie, 0, sizeof(pdata_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_teardown(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_teardown()\n", progname);
|
||||||
|
free(pgm->cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_initialize(PROGRAMMER* pgm, AVRPART* p)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_initialize()\n", progname);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
|
||||||
|
int result = micronucleus_get_bootloader_info(pdata);
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
micronucleus_dump_device_info(pdata);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_display(PROGRAMMER* pgm, const char* prefix)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_display()\n", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_powerup(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_powerup()\n", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_powerdown(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_powerdown()\n", progname);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
if (pdata->write_last_page)
|
||||||
|
{
|
||||||
|
pdata->write_last_page = false;
|
||||||
|
|
||||||
|
uint8_t* buffer = (unsigned char*)malloc(pdata->page_size);
|
||||||
|
if (buffer != NULL)
|
||||||
|
{
|
||||||
|
memset(buffer, 0xFF, pdata->page_size);
|
||||||
|
micronucleus_write_page(pdata, pdata->bootloader_start - pdata->page_size, buffer, pdata->page_size);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->start_program)
|
||||||
|
{
|
||||||
|
pdata->start_program = false;
|
||||||
|
|
||||||
|
micronucleus_start(pdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_enable(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_enable()\n", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_disable(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_disable()\n", progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_program_enable(PROGRAMMER* pgm, AVRPART* p)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_program_enable()\n", progname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_read_sig_bytes(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_read_sig_bytes()\n", progname);
|
||||||
|
|
||||||
|
if (mem->size < 3)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: memory size too small for read_sig_bytes", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
mem->buf[0] = 0x1E;
|
||||||
|
mem->buf[1] = pdata->signature1;
|
||||||
|
mem->buf[2] = pdata->signature2;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_chip_erase(PROGRAMMER* pgm, AVRPART* p)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_chip_erase()\n", progname);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
return micronucleus_erase_device(pdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_open(PROGRAMMER* pgm, char* port)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_open(\"%s\")\n", progname, port);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
char* bus_name = NULL;
|
||||||
|
char* dev_name = NULL;
|
||||||
|
|
||||||
|
// if no -P was given or '-P usb' was given
|
||||||
|
if (strcmp(port, "usb") == 0)
|
||||||
|
{
|
||||||
|
port = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// calculate bus and device names from -P option
|
||||||
|
if (strncmp(port, "usb", 3) == 0 && ':' == port[3])
|
||||||
|
{
|
||||||
|
bus_name = port + 4;
|
||||||
|
dev_name = strchr(bus_name, ':');
|
||||||
|
if (dev_name != NULL)
|
||||||
|
{
|
||||||
|
*dev_name = '\0';
|
||||||
|
dev_name++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port != NULL && dev_name == NULL)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: ERROR: Invalid -P value: '%s'\n", progname, port);
|
||||||
|
avrdude_message(MSG_INFO, "%sUse -P usb:bus:device\n", progbuf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine VID/PID
|
||||||
|
int vid = pgm->usbvid ? pgm->usbvid : MICRONUCLEUS_VID;
|
||||||
|
int pid = MICRONUCLEUS_PID;
|
||||||
|
|
||||||
|
LNODEID usbpid = lfirst(pgm->usbpid);
|
||||||
|
if (usbpid != NULL)
|
||||||
|
{
|
||||||
|
pid = *(int*)(ldata(usbpid));
|
||||||
|
if (lnext(usbpid))
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: using PID 0x%04x, ignoring remaining PIDs in list\n",
|
||||||
|
progname, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_init();
|
||||||
|
|
||||||
|
bool show_retry_message = true;
|
||||||
|
|
||||||
|
time_t start_time = time(NULL);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
usb_find_busses();
|
||||||
|
usb_find_devices();
|
||||||
|
|
||||||
|
pdata->usb_handle = NULL;
|
||||||
|
|
||||||
|
// Search for device
|
||||||
|
struct usb_bus* bus = NULL;
|
||||||
|
for (bus = usb_busses; bus != NULL && pdata->usb_handle == NULL; bus = bus->next)
|
||||||
|
{
|
||||||
|
struct usb_device* device = NULL;
|
||||||
|
for (device = bus->devices; device != NULL && pdata->usb_handle == NULL; device = device->next)
|
||||||
|
{
|
||||||
|
if (device->descriptor.idVendor == vid && device->descriptor.idProduct == pid)
|
||||||
|
{
|
||||||
|
pdata->major_version = (uint8_t)(device->descriptor.bcdDevice >> 8);
|
||||||
|
pdata->minor_version = (uint8_t)(device->descriptor.bcdDevice >> 0);
|
||||||
|
|
||||||
|
avrdude_message(MSG_NOTICE, "%s: Found device with Micronucleus V%d.%d, bus:device: %s:%s\n",
|
||||||
|
progname,
|
||||||
|
pdata->major_version, pdata->minor_version,
|
||||||
|
bus->dirname, device->filename);
|
||||||
|
|
||||||
|
// if -P was given, match device by device name and bus name
|
||||||
|
if (port != NULL)
|
||||||
|
{
|
||||||
|
if (dev_name == NULL || strcmp(bus->dirname, bus_name) || strcmp(device->filename, dev_name))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->major_version > MICRONUCLEUS_MAX_MAJOR_VERSION)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: WARNING: device with unsupported version (V%d.%d) of Micronucleus detected.\n",
|
||||||
|
progname,
|
||||||
|
pdata->major_version, pdata->minor_version);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdata->usb_handle = usb_open(device);
|
||||||
|
if (pdata->usb_handle == NULL)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: ERROR: Failed to open USB device: %s\n", progname, usb_strerror());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->usb_handle == NULL && pdata->wait_until_device_present)
|
||||||
|
{
|
||||||
|
if (show_retry_message)
|
||||||
|
{
|
||||||
|
if (pdata->wait_timout < 0)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: No device found, waiting for device to be plugged in...\n", progname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: No device found, waiting %d seconds for device to be plugged in...\n",
|
||||||
|
progname,
|
||||||
|
pdata->wait_timout);
|
||||||
|
}
|
||||||
|
|
||||||
|
avrdude_message(MSG_INFO, "%s: Press CTRL-C to terminate.\n", progname);
|
||||||
|
show_retry_message = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata->wait_timout < 0 || (time(NULL) - start_time) < pdata->wait_timout)
|
||||||
|
{
|
||||||
|
delay_ms(MICRONUCLEUS_CONNECT_WAIT);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pdata->usb_handle)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: ERROR: Could not find device with Micronucleus bootloader (%04X:%04X)\n",
|
||||||
|
progname, vid, pid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void micronucleus_close(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_close()\n", progname);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
if (pdata->usb_handle != NULL)
|
||||||
|
{
|
||||||
|
usb_close(pdata->usb_handle);
|
||||||
|
pdata->usb_handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_read_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
|
||||||
|
unsigned long addr, unsigned char* value)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_read_byte(desc=%s, addr=0x%0X)\n",
|
||||||
|
progname, mem->desc, addr);
|
||||||
|
|
||||||
|
if (strcmp(mem->desc, "lfuse") == 0 ||
|
||||||
|
strcmp(mem->desc, "hfuse") == 0 ||
|
||||||
|
strcmp(mem->desc, "efuse") == 0 ||
|
||||||
|
strcmp(mem->desc, "lock") == 0)
|
||||||
|
{
|
||||||
|
*value = 0xFF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_write_byte(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
|
||||||
|
unsigned long addr, unsigned char value)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_write_byte(desc=%s, addr=0x%0X)\n",
|
||||||
|
progname, mem->desc, addr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_paged_load(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
|
||||||
|
unsigned int page_size,
|
||||||
|
unsigned int addr, unsigned int n_bytes)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_paged_load(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n",
|
||||||
|
progname, page_size, addr, n_bytes);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_paged_write(PROGRAMMER* pgm, AVRPART* p, AVRMEM* mem,
|
||||||
|
unsigned int page_size,
|
||||||
|
unsigned int addr, unsigned int n_bytes)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_paged_write(page_size=0x%X, addr=0x%X, n_bytes=0x%X)\n",
|
||||||
|
progname, page_size, addr, n_bytes);
|
||||||
|
|
||||||
|
if (strcmp(mem->desc, "flash") == 0)
|
||||||
|
{
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
|
||||||
|
if (n_bytes > page_size)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Buffer size (%u) exceeds page size (%u)\n", progname, n_bytes, page_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr + n_bytes > pdata->flash_size)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Program size (%u) exceeds flash size (%u)\n", progname, addr + n_bytes, pdata->flash_size);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* page_buffer = (uint8_t*)malloc(pdata->page_size);
|
||||||
|
if (page_buffer == NULL)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Failed to allocate memory\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Page size reported by the bootloader may be smaller than device page size as configured in avrdude.conf.
|
||||||
|
int result = 0;
|
||||||
|
while (n_bytes > 0)
|
||||||
|
{
|
||||||
|
size_t chunk_size = n_bytes < pdata->page_size ? n_bytes : pdata->page_size;
|
||||||
|
|
||||||
|
memcpy(page_buffer, mem->buf + addr, chunk_size);
|
||||||
|
memset(page_buffer + chunk_size, 0xFF, pdata->page_size - chunk_size);
|
||||||
|
|
||||||
|
result = micronucleus_write_page(pdata, addr, page_buffer, pdata->page_size);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr += chunk_size;
|
||||||
|
n_bytes -= chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(page_buffer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Unsupported memory type: %s\n", progname, mem->desc);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int micronucleus_parseextparams(PROGRAMMER* pgm, LISTID xparams)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_DEBUG, "%s: micronucleus_parseextparams()\n", progname);
|
||||||
|
|
||||||
|
pdata_t* pdata = PDATA(pgm);
|
||||||
|
for (LNODEID node = lfirst(xparams); node != NULL; node = lnext(node))
|
||||||
|
{
|
||||||
|
const char* param = ldata(node);
|
||||||
|
|
||||||
|
if (strcmp(param, "wait") == 0)
|
||||||
|
{
|
||||||
|
pdata->wait_until_device_present = true;
|
||||||
|
pdata->wait_timout = -1;
|
||||||
|
}
|
||||||
|
else if (strncmp(param, "wait=", 5) == 0)
|
||||||
|
{
|
||||||
|
pdata->wait_until_device_present = true;
|
||||||
|
pdata->wait_timout = atoi(param + 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: Invalid extended parameter '%s'\n", progname, param);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void micronucleus_initpgm(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
strcpy(pgm->type, "Micronucleus V2.0");
|
||||||
|
|
||||||
|
pgm->setup = micronucleus_setup;
|
||||||
|
pgm->teardown = micronucleus_teardown;
|
||||||
|
pgm->initialize = micronucleus_initialize;
|
||||||
|
pgm->display = micronucleus_display;
|
||||||
|
pgm->powerup = micronucleus_powerup;
|
||||||
|
pgm->powerdown = micronucleus_powerdown;
|
||||||
|
pgm->enable = micronucleus_enable;
|
||||||
|
pgm->disable = micronucleus_disable;
|
||||||
|
pgm->program_enable = micronucleus_program_enable;
|
||||||
|
pgm->read_sig_bytes = micronucleus_read_sig_bytes;
|
||||||
|
pgm->chip_erase = micronucleus_chip_erase;
|
||||||
|
pgm->cmd = NULL;
|
||||||
|
pgm->open = micronucleus_open;
|
||||||
|
pgm->close = micronucleus_close;
|
||||||
|
pgm->read_byte = micronucleus_read_byte;
|
||||||
|
pgm->write_byte = micronucleus_write_byte;
|
||||||
|
pgm->paged_load = micronucleus_paged_load;
|
||||||
|
pgm->paged_write = micronucleus_paged_write;
|
||||||
|
pgm->parseextparams = micronucleus_parseextparams;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !HAVE_LIBUSB */
|
||||||
|
|
||||||
|
// Give a proper error if we were not compiled with libusb
|
||||||
|
static int micronucleus_nousb_open(struct programmer_t* pgm, char* name)
|
||||||
|
{
|
||||||
|
avrdude_message(MSG_INFO, "%s: error: No usb support. Please compile again with libusb installed.\n", progname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void micronucleus_initpgm(PROGRAMMER* pgm)
|
||||||
|
{
|
||||||
|
strcpy(pgm->type, "micronucleus");
|
||||||
|
pgm->open = micronucleus_nousb_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_LIBUSB */
|
||||||
|
|
||||||
|
const char micronucleus_desc[] = "Micronucleus Bootloader";
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
||||||
|
* Copyright (C) 2019 Marius Greuel
|
||||||
|
*
|
||||||
|
* 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef micronucleus_h
|
||||||
|
#define micronucleus_h
|
||||||
|
|
||||||
|
#include "libavrdude.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const char micronucleus_desc[];
|
||||||
|
void micronucleus_initpgm(PROGRAMMER* pgm);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* micronucleus_h */
|
|
@ -41,6 +41,7 @@
|
||||||
#include "jtag3.h"
|
#include "jtag3.h"
|
||||||
#include "linuxgpio.h"
|
#include "linuxgpio.h"
|
||||||
#include "linuxspi.h"
|
#include "linuxspi.h"
|
||||||
|
#include "micronucleus.h"
|
||||||
#include "par.h"
|
#include "par.h"
|
||||||
#include "pickit2.h"
|
#include "pickit2.h"
|
||||||
#include "ppi.h"
|
#include "ppi.h"
|
||||||
|
@ -85,6 +86,7 @@ const PROGRAMMER_TYPE programmers_types[] = {
|
||||||
{"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc},
|
{"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc},
|
||||||
{"linuxgpio", linuxgpio_initpgm, linuxgpio_desc},
|
{"linuxgpio", linuxgpio_initpgm, linuxgpio_desc},
|
||||||
{"linuxspi", linuxspi_initpgm, linuxspi_desc},
|
{"linuxspi", linuxspi_initpgm, linuxspi_desc},
|
||||||
|
{"micronucleus", micronucleus_initpgm, micronucleus_desc},
|
||||||
{"par", par_initpgm, par_desc},
|
{"par", par_initpgm, par_desc},
|
||||||
{"pickit2", pickit2_initpgm, pickit2_desc},
|
{"pickit2", pickit2_initpgm, pickit2_desc},
|
||||||
{"serbb", serbb_initpgm, serbb_desc},
|
{"serbb", serbb_initpgm, serbb_desc},
|
||||||
|
|
|
@ -733,18 +733,26 @@ static int cmd_help(PROGRAMMER * pgm, struct avrpart * p,
|
||||||
static int cmd_spi(PROGRAMMER * pgm, struct avrpart * p,
|
static int cmd_spi(PROGRAMMER * pgm, struct avrpart * p,
|
||||||
int argc, char * argv[])
|
int argc, char * argv[])
|
||||||
{
|
{
|
||||||
|
if (pgm->setpin != NULL) {
|
||||||
pgm->setpin(pgm, PIN_AVR_RESET, 1);
|
pgm->setpin(pgm, PIN_AVR_RESET, 1);
|
||||||
spi_mode = 1;
|
spi_mode = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_INFO, "`spi' command unavailable for this programmer type\n");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_pgm(PROGRAMMER * pgm, struct avrpart * p,
|
static int cmd_pgm(PROGRAMMER * pgm, struct avrpart * p,
|
||||||
int argc, char * argv[])
|
int argc, char * argv[])
|
||||||
{
|
{
|
||||||
|
if (pgm->setpin != NULL) {
|
||||||
pgm->setpin(pgm, PIN_AVR_RESET, 0);
|
pgm->setpin(pgm, PIN_AVR_RESET, 0);
|
||||||
spi_mode = 0;
|
spi_mode = 0;
|
||||||
pgm->initialize(pgm, p);
|
pgm->initialize(pgm, p);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
avrdude_message(MSG_INFO, "`pgm' command unavailable for this programmer type\n");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cmd_verbose(PROGRAMMER * pgm, struct avrpart * p,
|
static int cmd_verbose(PROGRAMMER * pgm, struct avrpart * p,
|
||||||
|
|
|
@ -0,0 +1,796 @@
|
||||||
|
// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
|
||||||
|
// without any warranty.
|
||||||
|
// by Gregory Pakosz (@gpakosz)
|
||||||
|
// https://github.com/gpakosz/whereami
|
||||||
|
|
||||||
|
// in case you want to #include "whereami.c" in a larger compilation unit
|
||||||
|
#if !defined(WHEREAMI_H)
|
||||||
|
#include <whereami.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(WAI_MALLOC)
|
||||||
|
#define WAI_MALLOC(size) malloc(size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(WAI_FREE)
|
||||||
|
#define WAI_FREE(p) free(p)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(WAI_REALLOC)
|
||||||
|
#define WAI_REALLOC(p, size) realloc(p, size)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WAI_NOINLINE
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define WAI_NOINLINE __declspec(noinline)
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define WAI_NOINLINE __attribute__((noinline))
|
||||||
|
#else
|
||||||
|
#error unsupported compiler
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define WAI_RETURN_ADDRESS() _ReturnAddress()
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
|
||||||
|
#else
|
||||||
|
#error unsupported compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push, 3)
|
||||||
|
#endif
|
||||||
|
#include <windows.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
wchar_t buffer1[MAX_PATH];
|
||||||
|
wchar_t buffer2[MAX_PATH];
|
||||||
|
wchar_t* path = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
DWORD size;
|
||||||
|
int length_, length__;
|
||||||
|
|
||||||
|
size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
break;
|
||||||
|
else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
|
||||||
|
{
|
||||||
|
DWORD size_ = size;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
wchar_t* path_;
|
||||||
|
|
||||||
|
path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
|
||||||
|
if (!path_)
|
||||||
|
break;
|
||||||
|
size_ *= 2;
|
||||||
|
path = path_;
|
||||||
|
size = GetModuleFileNameW(module, path, size_);
|
||||||
|
}
|
||||||
|
while (size == size_);
|
||||||
|
|
||||||
|
if (size == size_)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
path = buffer1;
|
||||||
|
|
||||||
|
if (!_wfullpath(buffer2, path, MAX_PATH))
|
||||||
|
break;
|
||||||
|
length_ = (int)wcslen(buffer2);
|
||||||
|
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
|
||||||
|
|
||||||
|
if (length__ == 0)
|
||||||
|
length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
|
||||||
|
if (length__ == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (length__ <= capacity && dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length__ - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '\\')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length = length__;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path != buffer1)
|
||||||
|
WAI_FREE(path);
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAI_NOINLINE WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
WAI_NOINLINE WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
HMODULE module;
|
||||||
|
int length = -1;
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4054)
|
||||||
|
#endif
|
||||||
|
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <linux/limits.h>
|
||||||
|
#else
|
||||||
|
#include <limits.h>
|
||||||
|
#endif
|
||||||
|
#ifndef __STDC_FORMAT_MACROS
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#endif
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if !defined(WAI_PROC_SELF_EXE)
|
||||||
|
#if defined(__sun)
|
||||||
|
#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
|
||||||
|
#else
|
||||||
|
#define WAI_PROC_SELF_EXE "/proc/self/exe"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
resolved = realpath(WAI_PROC_SELF_EXE, buffer);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(WAI_PROC_SELF_MAPS_RETRY)
|
||||||
|
#define WAI_PROC_SELF_MAPS_RETRY 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(WAI_PROC_SELF_MAPS)
|
||||||
|
#if defined(__sun)
|
||||||
|
#define WAI_PROC_SELF_MAPS "/proc/self/map"
|
||||||
|
#else
|
||||||
|
#define WAI_PROC_SELF_MAPS "/proc/self/maps"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ANDROID__) || defined(ANDROID)
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
WAI_NOINLINE WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
int length = -1;
|
||||||
|
FILE* maps = NULL;
|
||||||
|
|
||||||
|
for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
|
||||||
|
{
|
||||||
|
maps = fopen(WAI_PROC_SELF_MAPS, "r");
|
||||||
|
if (!maps)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
|
||||||
|
uint64_t low, high;
|
||||||
|
char perms[5];
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t major, minor;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
uint32_t inode;
|
||||||
|
|
||||||
|
if (!fgets(buffer, sizeof(buffer), maps))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
|
||||||
|
{
|
||||||
|
uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
|
||||||
|
if (low <= addr && addr <= high)
|
||||||
|
{
|
||||||
|
char* resolved;
|
||||||
|
|
||||||
|
resolved = realpath(path, buffer);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
#if defined(__ANDROID__) || defined(ANDROID)
|
||||||
|
if (length > 4
|
||||||
|
&&buffer[length - 1] == 'k'
|
||||||
|
&&buffer[length - 2] == 'p'
|
||||||
|
&&buffer[length - 3] == 'a'
|
||||||
|
&&buffer[length - 4] == '.')
|
||||||
|
{
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
length = -1; // retry
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (begin == MAP_FAILED)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
length = -1; // retry
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* p = begin + offset - 30; // minimum size of local file header
|
||||||
|
while (p >= begin) // scan backwards
|
||||||
|
{
|
||||||
|
if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found
|
||||||
|
{
|
||||||
|
uint16_t length_ = *((uint16_t*)(p + 26));
|
||||||
|
|
||||||
|
if (length + 2 + length_ < (int)sizeof(buffer))
|
||||||
|
{
|
||||||
|
memcpy(&buffer[length], "!/", 2);
|
||||||
|
memcpy(&buffer[length + 2], p + 30, length_);
|
||||||
|
length += 2 + length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
--p;
|
||||||
|
}
|
||||||
|
|
||||||
|
munmap(begin, offset);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(maps);
|
||||||
|
maps = NULL;
|
||||||
|
|
||||||
|
if (length != -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
#define _DARWIN_BETTER_REALPATH
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer1[PATH_MAX];
|
||||||
|
char buffer2[PATH_MAX];
|
||||||
|
char* path = buffer1;
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
uint32_t size = (uint32_t)sizeof(buffer1);
|
||||||
|
if (_NSGetExecutablePath(path, &size) == -1)
|
||||||
|
{
|
||||||
|
path = (char*)WAI_MALLOC(size);
|
||||||
|
if (!_NSGetExecutablePath(path, &size))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved = realpath(path, buffer2);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path != buffer1)
|
||||||
|
WAI_FREE(path);
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAI_NOINLINE WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
Dl_info info;
|
||||||
|
|
||||||
|
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||||
|
{
|
||||||
|
resolved = realpath(info.dli_fname, buffer);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__QNXNTO__)
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if !defined(WAI_PROC_SELF_EXE)
|
||||||
|
#define WAI_PROC_SELF_EXE "/proc/self/exefile"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer1[PATH_MAX];
|
||||||
|
char buffer2[PATH_MAX];
|
||||||
|
char* resolved = NULL;
|
||||||
|
FILE* self_exe = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
self_exe = fopen(WAI_PROC_SELF_EXE, "r");
|
||||||
|
if (!self_exe)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!fgets(buffer1, sizeof(buffer1), self_exe))
|
||||||
|
break;
|
||||||
|
|
||||||
|
resolved = realpath(buffer1, buffer2);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(self_exe);
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
Dl_info info;
|
||||||
|
|
||||||
|
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||||
|
{
|
||||||
|
resolved = realpath(info.dli_fname, buffer);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
|
||||||
|
defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer1[4096];
|
||||||
|
char buffer2[PATH_MAX];
|
||||||
|
char buffer3[PATH_MAX];
|
||||||
|
char** argv = (char**)buffer1;
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (size > sizeof(buffer1))
|
||||||
|
{
|
||||||
|
argv = (char**)WAI_MALLOC(size);
|
||||||
|
if (!argv)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sysctl(mib, 4, argv, &size, NULL, 0) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strchr(argv[0], '/'))
|
||||||
|
{
|
||||||
|
resolved = realpath(argv[0], buffer2);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const char* PATH = getenv("PATH");
|
||||||
|
if (!PATH)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size_t argv0_length = strlen(argv[0]);
|
||||||
|
|
||||||
|
const char* begin = PATH;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
const char* separator = strchr(begin, ':');
|
||||||
|
const char* end = separator ? separator : begin + strlen(begin);
|
||||||
|
|
||||||
|
if (end - begin > 0)
|
||||||
|
{
|
||||||
|
if (*(end -1) == '/')
|
||||||
|
--end;
|
||||||
|
|
||||||
|
if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2))
|
||||||
|
{
|
||||||
|
memcpy(buffer2, begin, end - begin);
|
||||||
|
buffer2[end - begin] = '/';
|
||||||
|
memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1);
|
||||||
|
|
||||||
|
resolved = realpath(buffer2, buffer3);
|
||||||
|
if (resolved)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!separator)
|
||||||
|
break;
|
||||||
|
|
||||||
|
begin = ++separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv != (char**)buffer1)
|
||||||
|
WAI_FREE(argv);
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer1[PATH_MAX];
|
||||||
|
char buffer2[PATH_MAX];
|
||||||
|
char* path = buffer1;
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (ok = false; !ok; ok = true)
|
||||||
|
{
|
||||||
|
#if defined(__NetBSD__)
|
||||||
|
int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
|
||||||
|
#else
|
||||||
|
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||||
|
#endif
|
||||||
|
size_t size = sizeof(buffer1);
|
||||||
|
|
||||||
|
if (sysctl(mib, 4, path, &size, NULL, 0) != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
resolved = realpath(path, buffer2);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok ? length : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WAI_NOINLINE WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
|
||||||
|
{
|
||||||
|
char buffer[PATH_MAX];
|
||||||
|
char* resolved = NULL;
|
||||||
|
int length = -1;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
Dl_info info;
|
||||||
|
|
||||||
|
if (dladdr(WAI_RETURN_ADDRESS(), &info))
|
||||||
|
{
|
||||||
|
resolved = realpath(info.dli_fname, buffer);
|
||||||
|
if (!resolved)
|
||||||
|
break;
|
||||||
|
|
||||||
|
length = (int)strlen(resolved);
|
||||||
|
if (length <= capacity)
|
||||||
|
{
|
||||||
|
memcpy(out, resolved, length);
|
||||||
|
|
||||||
|
if (dirname_length)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = length - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
if (out[i] == '/')
|
||||||
|
{
|
||||||
|
*dirname_length = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error unsupported platform
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,67 @@
|
||||||
|
// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
|
||||||
|
// without any warranty.
|
||||||
|
// by Gregory Pakosz (@gpakosz)
|
||||||
|
// https://github.com/gpakosz/whereami
|
||||||
|
|
||||||
|
#ifndef WHEREAMI_H
|
||||||
|
#define WHEREAMI_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WAI_FUNCSPEC
|
||||||
|
#define WAI_FUNCSPEC
|
||||||
|
#endif
|
||||||
|
#ifndef WAI_PREFIX
|
||||||
|
#define WAI_PREFIX(function) wai_##function
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the current executable.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to
|
||||||
|
* retrieve the length of the path
|
||||||
|
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||||
|
* - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the
|
||||||
|
* path
|
||||||
|
* - add a terminal NUL character with `path[length] = '\0';`
|
||||||
|
*
|
||||||
|
* @param out destination buffer, optional
|
||||||
|
* @param capacity destination buffer capacity
|
||||||
|
* @param dirname_length optional recipient for the length of the dirname part
|
||||||
|
* of the path.
|
||||||
|
*
|
||||||
|
* @return the length of the executable path on success (without a terminal NUL
|
||||||
|
* character), otherwise `-1`
|
||||||
|
*/
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path to the current module
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve
|
||||||
|
* the length of the path
|
||||||
|
* - allocate the destination buffer with `path = (char*)malloc(length + 1);`
|
||||||
|
* - call `wai_getModulePath(path, length, NULL)` again to retrieve the path
|
||||||
|
* - add a terminal NUL character with `path[length] = '\0';`
|
||||||
|
*
|
||||||
|
* @param out destination buffer, optional
|
||||||
|
* @param capacity destination buffer capacity
|
||||||
|
* @param dirname_length optional recipient for the length of the dirname part
|
||||||
|
* of the path.
|
||||||
|
*
|
||||||
|
* @return the length of the module path on success (without a terminal NUL
|
||||||
|
* character), otherwise `-1`
|
||||||
|
*/
|
||||||
|
WAI_FUNCSPEC
|
||||||
|
int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // #ifndef WHEREAMI_H
|
|
@ -27,10 +27,40 @@ BEGIN
|
||||||
100 "AVRDUDE"
|
100 "AVRDUDE"
|
||||||
END
|
END
|
||||||
|
|
||||||
#define VER_MAJOR @PROJECT_VERSION_MAJOR@
|
#cmakedefine PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||||
#define VER_MINOR @PROJECT_VERSION_MINOR@
|
#ifdef PROJECT_VERSION_MAJOR
|
||||||
#define VER_BUILD @PROJECT_VERSION_PATCH@
|
#define VER_MAJOR PROJECT_VERSION_MAJOR
|
||||||
#define VER_REVISION @PROJECT_VERSION_TWEAK@
|
#else
|
||||||
|
#define VER_MAJOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#cmakedefine PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||||
|
#ifdef PROJECT_VERSION_MINOR
|
||||||
|
#define VER_MINOR PROJECT_VERSION_MINOR
|
||||||
|
#else
|
||||||
|
#define VER_MINOR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#cmakedefine PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||||
|
#ifdef PROJECT_VERSION_PATCH
|
||||||
|
#define VER_BUILD PROJECT_VERSION_PATCH
|
||||||
|
#else
|
||||||
|
#define VER_BUILD 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#cmakedefine PROJECT_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
|
||||||
|
#ifdef PROJECT_VERSION_TWEAK
|
||||||
|
#define VER_REVISION PROJECT_VERSION_TWEAK
|
||||||
|
#else
|
||||||
|
#define VER_REVISION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#cmakedefine GIT_COMMIT_YEAR "@GIT_COMMIT_YEAR@"
|
||||||
|
#ifdef GIT_COMMIT_YEAR
|
||||||
|
#define VER_COMMIT_YEAR GIT_COMMIT_YEAR
|
||||||
|
#else
|
||||||
|
#define VER_COMMIT_YEAR ""
|
||||||
|
#endif
|
||||||
|
|
||||||
#define _STR(s) #s
|
#define _STR(s) #s
|
||||||
#define _VER_STR(a, b, c, d) _STR(a) "." _STR(b) "." _STR(c) "." _STR(d)
|
#define _VER_STR(a, b, c, d) _STR(a) "." _STR(b) "." _STR(c) "." _STR(d)
|
||||||
|
@ -43,7 +73,7 @@ END
|
||||||
#define VER_FILEDESCRIPTION_STR "AVRDUDE"
|
#define VER_FILEDESCRIPTION_STR "AVRDUDE"
|
||||||
#define VER_PRODUCTNAME_STR "AVRDUDE"
|
#define VER_PRODUCTNAME_STR "AVRDUDE"
|
||||||
#define VER_INTERNALNAME_STR "avrdude.exe"
|
#define VER_INTERNALNAME_STR "avrdude.exe"
|
||||||
#define VER_LEGALCOPYRIGHT_STR "\251 2021 The AVRDUDE authors"
|
#define VER_LEGALCOPYRIGHT_STR "\251 " VER_COMMIT_YEAR " The AVRDUDE authors"
|
||||||
#define VER_ORIGINALFILENAME_STR VER_INTERNALNAME_STR
|
#define VER_ORIGINALFILENAME_STR VER_INTERNALNAME_STR
|
||||||
#define VER_COMMENTS_STR "https://github.com/avrdudes/avrdude"
|
#define VER_COMMENTS_STR "https://github.com/avrdudes/avrdude"
|
||||||
#define VER_FILETYPE VFT_APP
|
#define VER_FILETYPE VFT_APP
|
||||||
|
|
Loading…
Reference in New Issue