mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-06-02 09:46:34 +03:00
Add serial port discovery (#1498)
Co-authored-by: Stefan Rueger <stefan.rueger@urclocks.com>
This commit is contained in:
@@ -125,6 +125,7 @@ if(USE_STATIC_LIBS)
|
||||
set(PREFERRED_LIBFTDI libftdi.a ftdi)
|
||||
set(PREFERRED_LIBFTDI1 libftdi1.a ftdi1)
|
||||
set(PREFERRED_LIBREADLINE libreadline.a)
|
||||
set(PREFERRED_LIBSERIALPORT libserialport.a)
|
||||
set(PREFERRED_LIBGPIOD libgpiod.a gpiod)
|
||||
else()
|
||||
set(PREFERRED_LIBELF elf)
|
||||
@@ -134,6 +135,7 @@ else()
|
||||
set(PREFERRED_LIBFTDI ftdi)
|
||||
set(PREFERRED_LIBFTDI1 ftdi1)
|
||||
set(PREFERRED_LIBREADLINE readline)
|
||||
set(PREFERRED_LIBSERIALPORT serialport)
|
||||
set(PREFERRED_LIBGPIOD gpiod)
|
||||
endif()
|
||||
|
||||
@@ -225,6 +227,15 @@ elseif(MSVC)
|
||||
set(HAVE_LIBREADLINE 1)
|
||||
endif()
|
||||
|
||||
#-------------------------------------
|
||||
# Find libserialport
|
||||
|
||||
find_library(HAVE_LIBSERIALPORT NAMES ${PREFERRED_LIBSERIALPORT})
|
||||
if(HAVE_LIBSERIALPORT)
|
||||
set(LIB_LIBSERIALPORT ${HAVE_LIBSERIALPORT})
|
||||
set(HAVE_LIBSERIALPORT 1)
|
||||
endif()
|
||||
|
||||
# -------------------------------------
|
||||
# Find libgpiod, if needed
|
||||
if(HAVE_LINUXGPIO)
|
||||
@@ -319,6 +330,7 @@ if (DEBUG_CMAKE)
|
||||
message(STATUS "HAVE_LIBFTDI: ${HAVE_LIBFTDI}")
|
||||
message(STATUS "HAVE_LIBFTDI1: ${HAVE_LIBFTDI1}")
|
||||
message(STATUS "HAVE_LIBREADLINE: ${HAVE_LIBREADLINE}")
|
||||
message(STATUS "HAVE_LIBSERIALPORT: ${HAVE_LIBSERIALPORT}")
|
||||
message(STATUS "HAVE_LIBELF_H: ${HAVE_LIBELF_H}")
|
||||
message(STATUS "HAVE_LIBELF_LIBELF_H: ${HAVE_LIBELF_LIBELF_H}")
|
||||
message(STATUS "HAVE_USB_H: ${HAVE_USB_H}")
|
||||
@@ -377,6 +389,12 @@ else()
|
||||
message(STATUS "DON'T HAVE libreadline")
|
||||
endif()
|
||||
|
||||
if(HAVE_LIBSERIALPORT)
|
||||
message(STATUS "DO HAVE libserialport")
|
||||
else()
|
||||
message(STATUS "DON'T HAVE libserialport")
|
||||
endif()
|
||||
|
||||
if(BUILD_DOC)
|
||||
message(STATUS "ENABLED doc")
|
||||
else()
|
||||
|
||||
@@ -63,7 +63,7 @@ avrdude_CFLAGS = @ENABLE_WARNINGS@
|
||||
libavrdude_a_CFLAGS = @ENABLE_WARNINGS@
|
||||
libavrdude_la_CFLAGS = $(libavrdude_a_CFLAGS)
|
||||
|
||||
avrdude_LDADD = $(top_builddir)/$(noinst_LIBRARIES) @LIBUSB_1_0@ @LIBHIDAPI@ @LIBUSB@ @LIBFTDI1@ @LIBFTDI@ @LIBHID@ @LIBELF@ @LIBPTHREAD@ -lm
|
||||
avrdude_LDADD = $(top_builddir)/$(noinst_LIBRARIES) @LIBUSB_1_0@ @LIBHIDAPI@ @LIBUSB@ @LIBFTDI1@ @LIBFTDI@ @LIBHID@ @LIBELF@ @LIBPTHREAD@ @LIBSERIALPORT@ -lm
|
||||
|
||||
bin_PROGRAMS = avrdude
|
||||
|
||||
|
||||
@@ -601,20 +601,53 @@ Note that the result will be stored in the EEPROM cell at address 0.
|
||||
.It Fl P Ar port
|
||||
Use
|
||||
.Ar port
|
||||
to identify the device to which the programmer is attached. By
|
||||
default the
|
||||
.Pa /dev/ppi0
|
||||
port is used, but if the programmer type normally connects to the
|
||||
serial port, the
|
||||
.Pa /dev/cuaa0
|
||||
port is the default. If you need to use a different parallel or
|
||||
serial port, use this option to specify the alternate port name.
|
||||
to identify the connection through which the programmer is attached. This
|
||||
can be a parallel, serial, spi or linuxgpio connection. The programmer
|
||||
normally specifies the connection type; in absence of a -P specification,
|
||||
system-dependent default values
|
||||
.Pa default_parallel ,
|
||||
.Pa default_serial ,
|
||||
.Pa default_spi ,
|
||||
or
|
||||
.Pa default_linuxgpio
|
||||
from the configuration file are used. If you need to use a different port,
|
||||
use this option to specify the alternate port name.
|
||||
.Pp
|
||||
If
|
||||
.Nm
|
||||
has been configured with libserialport support, a serial port can be specified
|
||||
using a predefined serial adapter type in avrdude.conf or .avrduderc, e.g.,
|
||||
.Ar ch340
|
||||
or
|
||||
.Ar ft232r .
|
||||
If more than one serial adapter of the same type is connected, they can be
|
||||
distinguished by appending a serial number, e.g.,
|
||||
.Ar ft232r:12345678 .
|
||||
Note that the USB to serial chip has to have a serial number for this to work.
|
||||
.Nm Avrdude
|
||||
can check for leading and trailing serial number matches as well.
|
||||
In the above example,
|
||||
.Ar ft232r:1234
|
||||
would also result in a match, and so would
|
||||
.Ar ft232r:...5678 .
|
||||
If the USB to serial chip is not known to
|
||||
.Nm ,
|
||||
it can be specified using the hexadecimal USB vendor ID, hexadecimal
|
||||
product ID and an optional serial number, following the serial number
|
||||
matching rules described above, e.g.,
|
||||
.Ar usb:0x2341:0x0043
|
||||
or
|
||||
.Ar usb:2341:0043:12345678 .
|
||||
To see a list of currently plugged-in serial ports use -P ?s. In order to
|
||||
see a list of all possible serial adapters known to
|
||||
.Nm
|
||||
use -P ?sa.
|
||||
.Pp
|
||||
On Win32 operating systems, the parallel ports are referred to as lpt1
|
||||
through lpt3, referring to the addresses 0x378, 0x278, and 0x3BC,
|
||||
respectively. If the parallel port can be accessed through a different
|
||||
address, this address can be specified directly, using the common C
|
||||
language notation (i. e., hexadecimal values are prefixed by
|
||||
language notation (i.e., hexadecimal values are prefixed by
|
||||
.Ql 0x
|
||||
).
|
||||
.Pp
|
||||
@@ -984,7 +1017,6 @@ See below for a list of programmers accepting extended parameters
|
||||
or issue
|
||||
.Nm
|
||||
-x help ... to see the extended options of the chosen programmer.
|
||||
|
||||
.El
|
||||
.Ss Terminal mode
|
||||
In this mode,
|
||||
@@ -1873,7 +1905,7 @@ avrdude: Target prepared for ISP, signed off.
|
||||
avrdude: Please restart avrdude without power-cycling the target.
|
||||
.Ed
|
||||
.Pp
|
||||
If the target AVR has been set up for debugWire mode (i. e. the
|
||||
If the target AVR has been set up for debugWire mode (i.e., the
|
||||
.Em DWEN
|
||||
fuse is programmed), normal ISP connection attempts will fail as
|
||||
the
|
||||
@@ -1919,7 +1951,7 @@ one byte at a time.
|
||||
For that reason, updating the flash ROM from terminal mode does not
|
||||
work.
|
||||
.Pp
|
||||
Page-mode programming the EEPROM through JTAG (i.e. through an
|
||||
Page-mode programming the EEPROM through JTAG (i.e., through an
|
||||
.Fl U
|
||||
option) requires a prior chip erase.
|
||||
This is an inherent feature of the way JTAG EEPROM programming works.
|
||||
|
||||
@@ -2967,6 +2967,50 @@ serialadapter
|
||||
usbpid = 0x7523;
|
||||
;
|
||||
|
||||
#------------------------------------------------------------
|
||||
# ch9102
|
||||
#------------------------------------------------------------
|
||||
|
||||
serialadapter
|
||||
id = "ch9102";
|
||||
desc = "WCH CH9102 USB to serial adapter";
|
||||
usbvid = 0x1a86;
|
||||
usbpid = 0x55d4;
|
||||
;
|
||||
|
||||
#------------------------------------------------------------
|
||||
# cp210x
|
||||
#------------------------------------------------------------
|
||||
|
||||
serialadapter
|
||||
id = "cp210x";
|
||||
desc = "Silabs CP210x USB to serial adapter";
|
||||
usbvid = 0x10c4;
|
||||
usbpid = 0xea60, 0xea70, 0xea71;
|
||||
;
|
||||
|
||||
#------------------------------------------------------------
|
||||
# ft231x / ft234x / ft230x
|
||||
#------------------------------------------------------------
|
||||
|
||||
serialadapter
|
||||
id = "ft231x", "ft234x", "ft230x";
|
||||
desc = "FTDI FT23X series USB to serial adapter";
|
||||
usbvid = 0x0403;
|
||||
usbpid = 0x6015;
|
||||
;
|
||||
|
||||
#------------------------------------------------------------
|
||||
# pl2303
|
||||
#------------------------------------------------------------
|
||||
|
||||
serialadapter
|
||||
id = "pl2303";
|
||||
desc = "Profilic PL2303 USB to serial adapter";
|
||||
usbvid = 0x067b;
|
||||
usbpid = 0x2303;
|
||||
;
|
||||
|
||||
#
|
||||
# PART DEFINITIONS
|
||||
#
|
||||
|
||||
@@ -99,3 +99,6 @@
|
||||
|
||||
/* Define to 1 if you have the `readline' library (-lreadline). */
|
||||
#cmakedefine HAVE_LIBREADLINE 1
|
||||
|
||||
/* Define to 1 if you have the `serialport' library */
|
||||
#cmakedefine HAVE_LIBSERIALPORT 1
|
||||
|
||||
@@ -185,6 +185,19 @@ if test x$have_libhidapi = xyes; then
|
||||
fi
|
||||
AC_SUBST(LIBHIDAPI, $LIBHIDAPI)
|
||||
|
||||
AH_TEMPLATE([HAVE_LIBSERIALPORT],
|
||||
[Define if libserialport is found])
|
||||
AC_CHECK_LIB([serialport], [sp_open], [have_libserialport=yes])
|
||||
if test x$have_libserialport = xyes; then
|
||||
case $target in
|
||||
*)
|
||||
LIBSERIALPORT="-lserialport"
|
||||
;;
|
||||
esac
|
||||
AC_DEFINE([HAVE_LIBSERIALPORT])
|
||||
AC_CHECK_HEADERS([libserialport.h])
|
||||
fi
|
||||
AC_SUBST(LIBSERIALPORT, $LIBSERIALPORT)
|
||||
|
||||
AH_TEMPLATE([HAVE_LIBFTDI1],
|
||||
[Define if FTDI support is enabled via libftdi1])
|
||||
@@ -604,6 +617,12 @@ else
|
||||
echo "DON'T HAVE libreadline"
|
||||
fi
|
||||
|
||||
if test x$have_libserialport = xyes; then
|
||||
echo "DO HAVE libserialport"
|
||||
else
|
||||
echo "DON'T HAVE libserialport"
|
||||
fi
|
||||
|
||||
if test x$have_pthread = xyes; then
|
||||
echo "DO HAVE pthread"
|
||||
else
|
||||
|
||||
@@ -373,7 +373,7 @@ Windows programming software.
|
||||
For many years, the AVRDUDE source resided in public repositories on
|
||||
savannah.nongnu.org,
|
||||
where it continued to be enhanced and ported to other systems. In
|
||||
addition to FreeBSD, AVRDUDE now runs on Linux and Windows. The
|
||||
addition to FreeBSD, AVRDUDE now runs on Linux, MacOS and Windows. The
|
||||
developers behind the porting effort primarily were Ted Roth, Eric
|
||||
Weddington, and J@"org Wunsch.
|
||||
|
||||
@@ -680,12 +680,30 @@ hardware.
|
||||
Note that the result will be stored in the EEPROM cell at address 0.
|
||||
|
||||
@item -P @var{port}
|
||||
Use port to identify the device to which the programmer is attached.
|
||||
Normally, the default parallel port is used, but if the programmer type
|
||||
normally connects to the serial port, the default serial port will be
|
||||
used. See Appendix A, Platform Dependent Information, to find out the
|
||||
default port names for your platform. If you need to use a different
|
||||
parallel or serial port, use this option to specify the alternate port name.
|
||||
|
||||
Use @var{port} to identify the connection through which the programmer is
|
||||
attached. This can be a parallel, serial, spi or linuxgpio connection. The
|
||||
programmer normally specifies the connection type; in absence of a @code{-P}
|
||||
specification, system-dependent default values @code{default_parallel},
|
||||
@code{default_serial}, @code{default_spi}, or @code{default_linuxgpio} from
|
||||
the configuration file are used. If you need to use a different port, use this
|
||||
option to specify the alternate port name.
|
||||
|
||||
If avrdude has been configured with libserialport support, a serial port can
|
||||
be specified using a predefined serial adapter type in @var{avrdude.conf} or
|
||||
@var{.avrduderc}, e.g., @code{ch340} or @code{ft232r}. If more than one serial
|
||||
adapter of the same type is connected, they can be distinguished by appending
|
||||
a serial number, e.g., @code{ft232r:12345678}. Note that the USB to serial
|
||||
chip has to have a serial number for this to work. Avrdude can check for
|
||||
leading and trailing serial number matches as well. In the above example,
|
||||
@code{ft232r:1234} would also result in a match, and so would
|
||||
@code{ft232r:...5678}. If the USB to serial chip is not known to avrdude, it
|
||||
can be specified using the hexadecimal USB vendor ID, hexadecimal product ID
|
||||
and an optional serial number, following the serial number matching rules
|
||||
described above, e.g., @code{usb:0x2341:0x0043} or
|
||||
@code{usb:2341:0043:12345678}. To see a list of currently plugged-in serial
|
||||
ports use @code{-P ?s}. In order to see a list of all possible serial adapters
|
||||
known to avrdude use @code{-P ?sa}.
|
||||
|
||||
On Win32 operating systems, the parallel ports are referred to as lpt1
|
||||
through lpt3, referring to the addresses 0x378, 0x278, and 0x3BC,
|
||||
@@ -1762,7 +1780,7 @@ $ avrdude -cusbasp -pattiny13 -Ueeprom:r:-:i 2>/dev/null
|
||||
$ avrdude -pattiny13 -qq -U flash:r:-:r | strings
|
||||
|
||||
Main menu
|
||||
Distance: %dcm
|
||||
Distance: %d cm
|
||||
Exit
|
||||
|
||||
@end cartouche
|
||||
@@ -1827,6 +1845,52 @@ AVR64EA48
|
||||
@end smallexample
|
||||
|
||||
@page
|
||||
|
||||
@noindent
|
||||
@strong{List of all curently plugged-in serial devices known to the libserialport library:}
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
|
||||
$ avrdude -P ?s
|
||||
Possible candidate serial ports are:
|
||||
-P /dev/ttyUSB0 or -P ft232r:A600K203
|
||||
-P /dev/ttyUSB1 or -P ft232r:MCU8
|
||||
-P /dev/ttyUSB3, -P ch340 or -P ch340-115k
|
||||
Note that above ports might not be connected to a target board or an AVR programmer.
|
||||
Also note there may be other direct serial ports not listed above.
|
||||
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
|
||||
@noindent
|
||||
@strong{List of all serial adapters known to AVRDUDE, i.e., defined in avrdude.conf:}
|
||||
|
||||
@smallexample
|
||||
@cartouche
|
||||
|
||||
$ avrdude -P ?sa
|
||||
Valid serial adapters are:
|
||||
ch340 = [usbvid 0x1a86, usbpid 0x7523]
|
||||
ch340-115k = [usbvid 0x1a86, usbpid 0x7523]
|
||||
ch341a = [usbvid 0x1a86, usbpid 0x5512]
|
||||
ch9102 = [usbvid 0x1a86, usbpid 0x55d4]
|
||||
cp210x = [usbvid 0x10c4, usbpid 0xea60 0xea70 0xea71]
|
||||
ft2232h = [usbvid 0x0403, usbpid 0x6010]
|
||||
ft231x = [usbvid 0x0403, usbpid 0x6015]
|
||||
ft234x = [usbvid 0x0403, usbpid 0x6015]
|
||||
ft230x = [usbvid 0x0403, usbpid 0x6015]
|
||||
ft232h = [usbvid 0x0403, usbpid 0x6014]
|
||||
ft232r = [usbvid 0x0403, usbpid 0x6001]
|
||||
ft4232h = [usbvid 0x0403, usbpid 0x6011]
|
||||
pl2303 = [usbvid 0x067b, usbpid 0x2303]
|
||||
|
||||
@end cartouche
|
||||
@end smallexample
|
||||
|
||||
@page
|
||||
|
||||
@noindent
|
||||
@strong{AVRDUDE in a bash script creating terminal scripts that reset a part to factory settings:}
|
||||
@smallexample
|
||||
|
||||
@@ -1249,6 +1249,10 @@ typedef struct {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int setport_from_serialadapter(char **portp, const SERIALADAPTER *ser, const char *sernum);
|
||||
int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum);
|
||||
int list_available_serialports(LISTID programmers);
|
||||
|
||||
int str_starts(const char *str, const char *starts);
|
||||
int str_eq(const char *str1, const char *str2);
|
||||
int str_contains(const char *str, const char *substr);
|
||||
@@ -1265,6 +1269,7 @@ char *str_uc(char *s);
|
||||
char *str_lcfirst(char *s);
|
||||
char *str_ucfirst(char *s);
|
||||
char *str_utoa(unsigned n, char *buf, int base);
|
||||
char *str_endnumber(const char *str);
|
||||
const char *str_plural(int x);
|
||||
const char *str_inname(const char *fn);
|
||||
const char *str_outname(const char *fn);
|
||||
|
||||
196
src/main.c
196
src/main.c
@@ -224,19 +224,19 @@ static void usage(void)
|
||||
msg_error(
|
||||
"Usage: %s [options]\n"
|
||||
"Options:\n"
|
||||
" -p <partno> Specify AVR device\n"
|
||||
" -p <partno> Specify AVR device; -p ? lists all known parts\n"
|
||||
" -p <wildcard>/<flags> Run developer options for matched AVR devices,\n"
|
||||
" e.g., -p ATmega328P/s or /S for part definition\n"
|
||||
" -b <baudrate> Override RS-232 baud rate\n"
|
||||
" -B <bitclock> Specify bit clock period (us)\n"
|
||||
" -C <config-file> Specify location of configuration file\n"
|
||||
" -c <programmer> Specify programmer type\n"
|
||||
" -c <programmer> Specify programmer; -c ? and -c ?type list all\n"
|
||||
" -c <wildcard>/<flags> Run developer options for matched programmers,\n"
|
||||
" e.g., -c 'ur*'/s for programmer info/definition\n"
|
||||
" -A Disable trailing-0xff removal for file/AVR read\n"
|
||||
" -D Disable auto erase for flash memory; implies -A\n"
|
||||
" -i <delay> ISP Clock Delay [in microseconds]\n"
|
||||
" -P <port> Specify connection port\n"
|
||||
" -P <port> Connection; -P ?s or -P ?sa lists serial ones\n"
|
||||
" -F Override invalid signature or initial checks\n"
|
||||
" -e Perform a chip erase\n"
|
||||
" -O Perform RC oscillator calibration (see AVR053)\n"
|
||||
@@ -646,58 +646,58 @@ int main(int argc, char * argv [])
|
||||
}
|
||||
break;
|
||||
|
||||
case 'B': /* specify JTAG ICE bit clock period */
|
||||
bitclock = strtod(optarg, &e);
|
||||
if (*e != 0) {
|
||||
/* trailing unit of measure present */
|
||||
int suffixlen = strlen(e);
|
||||
switch (suffixlen) {
|
||||
case 2:
|
||||
if ((e[0] != 'h' && e[0] != 'H') || e[1] != 'z')
|
||||
bitclock = 0.0;
|
||||
else
|
||||
/* convert from Hz to microseconds */
|
||||
bitclock = 1E6 / bitclock;
|
||||
break;
|
||||
case 'B': /* specify JTAG ICE bit clock period */
|
||||
bitclock = strtod(optarg, &e);
|
||||
if (*e != 0) {
|
||||
/* trailing unit of measure present */
|
||||
int suffixlen = strlen(e);
|
||||
switch (suffixlen) {
|
||||
case 2:
|
||||
if ((e[0] != 'h' && e[0] != 'H') || e[1] != 'z')
|
||||
bitclock = 0.0;
|
||||
else
|
||||
/* convert from Hz to microseconds */
|
||||
bitclock = 1E6 / bitclock;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if ((e[1] != 'h' && e[1] != 'H') || e[2] != 'z')
|
||||
bitclock = 0.0;
|
||||
else {
|
||||
switch (e[0]) {
|
||||
case 'M':
|
||||
case 'm': /* no Millihertz here :) */
|
||||
bitclock = 1.0 / bitclock;
|
||||
break;
|
||||
case 3:
|
||||
if ((e[1] != 'h' && e[1] != 'H') || e[2] != 'z')
|
||||
bitclock = 0.0;
|
||||
else {
|
||||
switch (e[0]) {
|
||||
case 'M':
|
||||
case 'm': /* no Millihertz here :) */
|
||||
bitclock = 1.0 / bitclock;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
bitclock = 1E3 / bitclock;
|
||||
break;
|
||||
case 'k':
|
||||
bitclock = 1E3 / bitclock;
|
||||
break;
|
||||
|
||||
default:
|
||||
bitclock = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bitclock = 0.0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
bitclock = 0.0;
|
||||
break;
|
||||
}
|
||||
if (bitclock == 0.0)
|
||||
pmsg_error("invalid bit clock unit of measure '%s'\n", e);
|
||||
}
|
||||
if ((e == optarg) || bitclock == 0.0) {
|
||||
pmsg_error("invalid bit clock period specified '%s'\n", optarg);
|
||||
default:
|
||||
bitclock = 0.0;
|
||||
break;
|
||||
}
|
||||
if (bitclock == 0.0)
|
||||
pmsg_error("invalid bit clock unit of measure '%s'\n", e);
|
||||
}
|
||||
if ((e == optarg) || bitclock == 0.0) {
|
||||
pmsg_error("invalid bit clock period specified '%s'\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i': /* specify isp clock delay */
|
||||
ispdelay = str_int(optarg, STR_INT32, &errstr);
|
||||
if(errstr || ispdelay == 0) {
|
||||
pmsg_error("invalid isp clock delay %s specified", optarg);
|
||||
case 'i': /* specify isp clock delay */
|
||||
ispdelay = str_int(optarg, STR_INT32, &errstr);
|
||||
if(errstr || ispdelay == 0) {
|
||||
pmsg_error("invalid isp clock delay %s specified", optarg);
|
||||
if(errstr)
|
||||
msg_error(": %s\n", errstr);
|
||||
else
|
||||
@@ -743,23 +743,23 @@ int main(int argc, char * argv [])
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
logfile = optarg;
|
||||
break;
|
||||
logfile = optarg;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
uflags |= UF_NOWRITE;
|
||||
break;
|
||||
|
||||
case 'O': /* perform RC oscillator calibration */
|
||||
calibrate = 1;
|
||||
break;
|
||||
calibrate = 1;
|
||||
break;
|
||||
|
||||
case 'p' : /* specify AVR part */
|
||||
partdesc = optarg;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
port = optarg;
|
||||
port = cfg_strdup(__func__, optarg);
|
||||
break;
|
||||
|
||||
case 'q' : /* Quell progress output */
|
||||
@@ -1031,6 +1031,17 @@ int main(int argc, char * argv [])
|
||||
}
|
||||
}
|
||||
|
||||
if(port) {
|
||||
if(str_eq(port, "?s")) {
|
||||
list_available_serialports(programmers);
|
||||
exit(0);
|
||||
} else if(str_eq(port, "?sa")) {
|
||||
msg_error("\vValid serial adapters are:\n");
|
||||
list_serialadapters(stderr, " ", programmers);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(partdesc) {
|
||||
if(str_eq(partdesc, "?")) {
|
||||
if(pgmid && *pgmid && explicit_c) {
|
||||
@@ -1134,30 +1145,92 @@ int main(int argc, char * argv [])
|
||||
switch (pgm->conntype)
|
||||
{
|
||||
case CONNTYPE_PARALLEL:
|
||||
port = cfg_strdup("main()", default_parallel);
|
||||
port = cfg_strdup(__func__, default_parallel);
|
||||
break;
|
||||
|
||||
case CONNTYPE_SERIAL:
|
||||
port = cfg_strdup("main()", default_serial);
|
||||
port = cfg_strdup(__func__, default_serial);
|
||||
break;
|
||||
|
||||
case CONNTYPE_USB:
|
||||
port = DEFAULT_USB;
|
||||
port = cfg_strdup(__func__, DEFAULT_USB);
|
||||
break;
|
||||
|
||||
case CONNTYPE_SPI:
|
||||
port = cfg_strdup(__func__,
|
||||
#ifdef HAVE_LINUXSPI
|
||||
port = cfg_strdup("main()", *default_spi? default_spi: "unknown");
|
||||
*default_spi? default_spi:
|
||||
#endif
|
||||
"unknown");
|
||||
break;
|
||||
|
||||
case CONNTYPE_LINUXGPIO:
|
||||
port = cfg_strdup("main()", default_linuxgpio);
|
||||
port = cfg_strdup(__func__, default_linuxgpio);
|
||||
break;
|
||||
|
||||
default:
|
||||
port = cfg_strdup(__func__, "unknown");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Divide a serialadapter port string into tokens separated by colons.
|
||||
* There are two ways such a port string can be presented:
|
||||
* 1) -P <serialadapter>[:<sernum>]
|
||||
* 2) -P usb:<usbvid>:<usbpid>[:<sernum>]
|
||||
* In either case the serial number is optional. The USB vendor and
|
||||
* product ids are hexadecimal numbers.
|
||||
*/
|
||||
bool print_ports = true;
|
||||
SERIALADAPTER *ser = NULL;
|
||||
if (pgm->conntype == CONNTYPE_SERIAL) {
|
||||
char *portdup = cfg_strdup(__func__, port);
|
||||
char *port_tok[4], *tok = portdup;
|
||||
for(int t = 0, maxt = str_starts(portdup, DEFAULT_USB ":")? 4: 2; t < 4; t++) {
|
||||
char *save = tok && t < maxt? tok: "";
|
||||
if(t < maxt-1 && tok && (tok = strchr(tok, ':')))
|
||||
*tok++ = 0;
|
||||
port_tok[t] = cfg_strdup(__func__, save);
|
||||
}
|
||||
free(portdup);
|
||||
|
||||
// Use libserialport to find the actual serial port
|
||||
ser = locate_programmer(programmers, port_tok[0]);
|
||||
if (is_serialadapter(ser)) {
|
||||
int rv = setport_from_serialadapter(&port, ser, port_tok[1]);
|
||||
if (rv == -1) {
|
||||
pmsg_warning("serial adapter %s", port_tok[0]);
|
||||
if (port_tok[1][0])
|
||||
msg_warning(" with serial number %s", port_tok[1]);
|
||||
else if (ser->usbsn && ser->usbsn[0])
|
||||
msg_warning(" with serial number %s", ser->usbsn);
|
||||
msg_warning(" not connected to host\n");
|
||||
}
|
||||
else if (rv == -2)
|
||||
print_ports = false;
|
||||
if(rv)
|
||||
ser = NULL;
|
||||
} else if(str_eq(port_tok[0], DEFAULT_USB)) {
|
||||
// Port or usb:[vid]:[pid]
|
||||
int vid, pid;
|
||||
if (sscanf(port_tok[1], "%x", &vid) > 0 && sscanf(port_tok[2], "%x", &pid) > 0) {
|
||||
int rv = setport_from_vid_pid(&port, vid, pid, port_tok[3]);
|
||||
if (rv == -1) {
|
||||
if (port_tok[3][0])
|
||||
pmsg_warning("serial adapter with USB VID %s and PID %s and serial number %s not connected\n", port_tok[1], port_tok[2], port_tok[3]);
|
||||
else
|
||||
pmsg_warning("serial adapter with USB VID %s and PID %s not connected\n", port_tok[1], port_tok[2]);
|
||||
}
|
||||
else if (rv == -2)
|
||||
print_ports = false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
free(port_tok[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* open the programmer
|
||||
*/
|
||||
@@ -1177,20 +1250,25 @@ int main(int argc, char * argv [])
|
||||
imsg_notice("Overriding Baud Rate : %d\n", baudrate);
|
||||
pgm->baudrate = baudrate;
|
||||
}
|
||||
|
||||
else if (ser && ser->baudrate) {
|
||||
imsg_notice("Default Baud Rate : %d\n", ser->baudrate);
|
||||
pgm->baudrate = ser->baudrate;
|
||||
}
|
||||
if (bitclock != 0.0) {
|
||||
imsg_notice("Setting bit clk period : %.1f\n", bitclock);
|
||||
pgm->bitclock = bitclock * 1e-6;
|
||||
}
|
||||
|
||||
if (ispdelay != 0) {
|
||||
imsg_notice("Setting isp clock delay : %3i\n", ispdelay);
|
||||
imsg_notice("Setting isp clock delay : %3i\n", ispdelay);
|
||||
pgm->ispdelay = ispdelay;
|
||||
}
|
||||
|
||||
rc = pgm->open(pgm, port);
|
||||
if (rc < 0) {
|
||||
pmsg_error("unable to open programmer %s on port %s\n", pgmid, port);
|
||||
if (print_ports && pgm->conntype == CONNTYPE_SERIAL)
|
||||
list_available_serialports(programmers);
|
||||
exitrc = 1;
|
||||
pgm->ppidata = 0; /* clear all bits at exit */
|
||||
goto main_exit;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* AVRDUDE - A Downloader/Uploader for AVR device programmers
|
||||
* Copyright (C) 2023 Stefan Rueger <stefan.rueger@urclocks.com>
|
||||
* Copyright (C) 2023 Hans Eirik Bull
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +25,327 @@
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
|
||||
#ifdef HAVE_LIBSERIALPORT
|
||||
|
||||
#include <libserialport.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef struct {
|
||||
int vid, pid;
|
||||
char *sernum, *port;
|
||||
} SERPORT;
|
||||
|
||||
// Set new port string freeing any previously set one
|
||||
static int sa_setport(char **portp, const char *sp_port) {
|
||||
if(!sp_port) {
|
||||
pmsg_warning("port string to be assigned is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(portp) {
|
||||
if(*portp)
|
||||
free(*portp);
|
||||
*portp = cfg_strdup(__func__, sp_port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Is the actual serial number sn matched by the query q?
|
||||
static int sa_snmatch(const char *sn, const char *q) {
|
||||
return sn && (str_starts(sn, q) || (str_starts(q , "...") && str_ends(sn, q+3)));
|
||||
}
|
||||
|
||||
// Order two SERPORTs port strings: base first then trailing numbers, if any
|
||||
static int sa_portcmp(const void *p, const void *q) {
|
||||
int ret;
|
||||
const char *a = ((SERPORT *) p)->port, *b = ((SERPORT *) q)->port;
|
||||
const char *na = str_endnumber(a), *nb = str_endnumber(b);
|
||||
size_t la = strlen(a) - (na? strlen(na): 0), lb = strlen(b) - (nb? strlen(nb): 0);
|
||||
|
||||
// Compare string bases first
|
||||
if(la && lb && (ret = strncasecmp(a, b, la < lb? la: lb)))
|
||||
return ret;
|
||||
if((ret = la-lb))
|
||||
return ret;
|
||||
|
||||
// If string bases are the same then compare trailing numbers
|
||||
if(na && nb) {
|
||||
long long d;
|
||||
if((d = atoll(na)-atoll(nb)))
|
||||
return d < 0? -1: 1;
|
||||
} else if(na)
|
||||
return 1;
|
||||
else if(nb)
|
||||
return -1;
|
||||
|
||||
// Ports are the same (this should not happen) but still compare vid, pid and sn
|
||||
if((ret = ((SERPORT *) p)->vid - ((SERPORT *) q)->vid))
|
||||
return ret;
|
||||
if((ret = ((SERPORT *) p)->pid - ((SERPORT *) q)->pid))
|
||||
return ret;
|
||||
|
||||
return strcmp(((SERPORT *) p)->sernum, ((SERPORT *) q)->sernum);
|
||||
}
|
||||
|
||||
// Get serial port data; allocate a SERPORT array sp, store data and return it
|
||||
static SERPORT *get_libserialport_data(int *np) {
|
||||
struct sp_port **port_list = NULL;
|
||||
enum sp_return result = sp_list_ports(&port_list);
|
||||
|
||||
if(result != SP_OK) {
|
||||
pmsg_error("sp_list_ports() failed!\n");
|
||||
sp_free_port_list(port_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i, j, n;
|
||||
// Count the number of available ports and allocate space according to the needed size
|
||||
for(n = 0; port_list[n]; n++)
|
||||
continue;
|
||||
SERPORT *sp = cfg_malloc(__func__, n*sizeof*sp);
|
||||
|
||||
for(j = 0, i = 0; i < n; i++) { // j counts the number of valid ports
|
||||
struct sp_port *p = port_list[i];
|
||||
char *q;
|
||||
// Fill sp struct with port information
|
||||
if((q = sp_get_port_name(p))) {
|
||||
sp[j].port = cfg_strdup(__func__, q);
|
||||
if(sp_get_port_usb_vid_pid(p, &sp[j].vid, &sp[j].pid) != SP_OK)
|
||||
sp[j].vid = sp[j].pid = 0;
|
||||
sp[j].sernum = cfg_strdup(__func__, (q = sp_get_port_usb_serial(p))? q: "");
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
if(j > 0)
|
||||
qsort(sp, j, sizeof*sp, sa_portcmp);
|
||||
else
|
||||
free(sp), sp = NULL;
|
||||
|
||||
sp_free_port_list(port_list);
|
||||
|
||||
if(np)
|
||||
*np = j;
|
||||
return sp;
|
||||
}
|
||||
|
||||
// Returns a NULL-terminated malloc'd list of items in SERPORT list spa that are not in spb
|
||||
SERPORT **sa_spa_not_spb(SERPORT *spa, int na, SERPORT *spb, int nb) {
|
||||
SERPORT **ret = cfg_malloc(__func__, (na+1)*sizeof*ret);
|
||||
int ia = 0, ib = 0, ir = 0;
|
||||
|
||||
// Use the comm algorithm on two sorted SERPORT lists
|
||||
while(ia < na && ib < nb) {
|
||||
int d = sa_portcmp(spa+ia, spb+ib);
|
||||
if(d < 0)
|
||||
ret[ir++] = spa+ia++;
|
||||
else if(d > 0)
|
||||
ib++;
|
||||
else
|
||||
ia++, ib++;
|
||||
}
|
||||
while(ia < na)
|
||||
ret[ir++] = spa+ia++;
|
||||
|
||||
ret[ir] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Return number of SERPORTs that a serial adapter matches
|
||||
static int sa_num_matches_by_sea(const SERIALADAPTER *sea, const char *sernum, const SERPORT *sp, int n) {
|
||||
const char *sn = *sernum? sernum: sea->usbsn;
|
||||
int matches = 0;
|
||||
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sp[i].vid == sea->usbvid)
|
||||
for(LNODEID usbpid = lfirst(sea->usbpid); usbpid; usbpid = lnext(usbpid))
|
||||
if(sp[i].pid == *(int *) ldata(usbpid) && sa_snmatch(sp[i].sernum, sn)) {
|
||||
matches++;
|
||||
break;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
// Return number of SERPORTs that a (vid, pid, sernum) triple matches
|
||||
static int sa_num_matches_by_ids(int vid, int pid, const char *sernum, const SERPORT *sp, int n) {
|
||||
int matches = 0;
|
||||
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sp[i].vid == vid && sp[i].pid == pid && sa_snmatch(sp[i].sernum, sernum))
|
||||
matches++;
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
// Is the i-th SERPORT the only match with the serial adapter wrt all plugged-in ones?
|
||||
static int sa_unique_by_sea(const SERIALADAPTER *sea, const char *sn, const SERPORT *sp, int n, int i) {
|
||||
return sa_num_matches_by_sea(sea, sn, sp, n) == 1 && sa_num_matches_by_sea(sea, sn, sp+i, 1);
|
||||
}
|
||||
|
||||
// Is the i-th SERPORT the only match with (vid, pid, sn) wrt all plugged-in ones?
|
||||
static int sa_unique_by_ids(int vid, int pid, const char *sn, const SERPORT *sp, int n, int i) {
|
||||
return sa_num_matches_by_ids(vid, pid, sn, sp, n) == 1 && sa_num_matches_by_ids(vid, pid, sn, sp+i, 1);
|
||||
}
|
||||
|
||||
// Return a malloc'd list of -P specifications that uniquely address sp[i]
|
||||
static char **sa_list_specs(const SERPORT *sp, int n, int i) {
|
||||
int Pn = 4, Pi = 0;
|
||||
char **Plist = cfg_malloc(__func__, Pn*sizeof*Plist);
|
||||
const char *sn = sp[i].sernum, *via = NULL;
|
||||
|
||||
// Loop though all serial adapters in avrdude.conf
|
||||
for(LNODEID ln1 = lfirst(programmers); ln1; ln1=lnext(ln1)) {
|
||||
SERIALADAPTER *sea = ldata(ln1);
|
||||
if(!is_serialadapter(sea))
|
||||
continue;
|
||||
for(LNODEID sid = lfirst(sea->id); sid; sid = lnext(sid)) {
|
||||
char *id = ldata(sid);
|
||||
// Put id or id:sn into list if it uniquely matches sp[i]
|
||||
if(sa_unique_by_sea(sea, "", sp, n, i))
|
||||
Plist[Pi++] = cfg_strdup(__func__, id);
|
||||
else if(*sn && sa_unique_by_sea(sea, sn, sp, n, i))
|
||||
Plist[Pi++] = str_sprintf("%s:%s", id, sn);
|
||||
else if(!via && sa_num_matches_by_sea(sea, "", sp+i, 1))
|
||||
via = id;
|
||||
|
||||
if(Pi >= Pn-1) { // Ensure there is space for one more and NULL
|
||||
if(Pn >= INT_MAX/2)
|
||||
break;
|
||||
Pn *= 2;
|
||||
Plist = cfg_realloc(__func__, Plist, Pn*sizeof*Plist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Pi == 0 && sp[i].vid) { // No unique serial adapter, so maybe vid:pid[:sn] works?
|
||||
if(sa_unique_by_ids(sp[i].vid, sp[i].pid, "", sp, n, i))
|
||||
Plist[Pi++] = str_sprintf("usb:%04x:%04x", sp[i].vid, sp[i].pid);
|
||||
else if(*sn && sa_unique_by_ids(sp[i].vid, sp[i].pid, sn, sp, n, i))
|
||||
Plist[Pi++] = str_sprintf("usb:%04x:%04x:%s", sp[i].vid, sp[i].pid, sn);
|
||||
else if(via && Pi == 0)
|
||||
Plist[Pi++] = str_sprintf("(via %s serial adapter)", via);
|
||||
}
|
||||
|
||||
Plist[Pi] = NULL;
|
||||
return Plist;
|
||||
}
|
||||
|
||||
// Print possible ways SERPORT sp[i] might be specified
|
||||
static void sa_print_specs(const SERPORT *sp, int n, int i) {
|
||||
char **Pspecs = sa_list_specs(sp, n, i);
|
||||
|
||||
msg_warning(" -P %s", sp[i].port);
|
||||
for(char **Ps = Pspecs; *Ps; Ps++) {
|
||||
msg_warning("%s %s", str_starts(*Ps, "(via ")? "": Ps[1]? ", -P": " or -P", *Ps);
|
||||
free(*Ps);
|
||||
}
|
||||
msg_warning("\n");
|
||||
|
||||
free(Pspecs);
|
||||
}
|
||||
|
||||
// Set the port specs to the port iff sea matches one and only one of the connected SERPORTs
|
||||
int setport_from_serialadapter(char **portp, const SERIALADAPTER *sea, const char *sernum) {
|
||||
int rv, m, n;
|
||||
SERPORT *sp = get_libserialport_data(&n);
|
||||
if(!sp || n <= 0)
|
||||
return -1;
|
||||
|
||||
m = sa_num_matches_by_sea(sea, sernum, sp, n);
|
||||
if(m == 1) { // Unique result, set port string
|
||||
rv = -1;
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sa_num_matches_by_sea(sea, sernum, sp+i, 1))
|
||||
rv = sa_setport(portp, sp[i].port);
|
||||
} else {
|
||||
rv = -2;
|
||||
pmsg_warning("-P %s is %s; consider\n", *portp, m? "ambiguous": "not connected");
|
||||
for(int i = 0; i < n; i++)
|
||||
if(m == 0 || sa_num_matches_by_sea(sea, sernum, sp+i, 1) == 1)
|
||||
sa_print_specs(sp, n, i);
|
||||
}
|
||||
|
||||
for(int k = 0; k < n; k++)
|
||||
free(sp[k].sernum), free(sp[k].port);
|
||||
free(sp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set the port specs to the port iff the ids match one and only one of the connected SERPORTs
|
||||
int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum) {
|
||||
int rv, m, n;
|
||||
SERPORT *sp = get_libserialport_data(&n);
|
||||
if(!sp || n <= 0)
|
||||
return -1;
|
||||
|
||||
m = sa_num_matches_by_ids(vid, pid, sernum, sp, n);
|
||||
if(m == 1) { // Unique result, set port string
|
||||
rv = -1;
|
||||
for(int i = 0; i < n; i++)
|
||||
if(sa_num_matches_by_ids(vid, pid, sernum, sp+i, 1))
|
||||
rv = sa_setport(portp, sp[i].port);
|
||||
} else {
|
||||
rv = -2;
|
||||
pmsg_warning("-P %s is %s; consider\n", *portp, m? "ambiguous": "not connected");
|
||||
for(int i = 0; i < n; i++)
|
||||
if(m == 0 || sa_num_matches_by_ids(vid, pid, sernum, sp+i, 1) == 1)
|
||||
sa_print_specs(sp, n, i);
|
||||
}
|
||||
|
||||
for(int k = 0; k < n; k++)
|
||||
free(sp[k].sernum), free(sp[k].port);
|
||||
free(sp);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// List available serial ports
|
||||
int list_available_serialports(LISTID programmers) {
|
||||
// Get serial port information from libserialport
|
||||
int n;
|
||||
SERPORT *sp = get_libserialport_data(&n);
|
||||
if(!sp || n <= 0)
|
||||
return -1;
|
||||
|
||||
msg_warning("%sossible candidate serial port%s:\n",
|
||||
n>1? "P": "A p", n>1? "s are": " is");
|
||||
|
||||
for(int i = 0; i < n; i++)
|
||||
sa_print_specs(sp, n, i);
|
||||
|
||||
msg_warning("Note that above port%s might not be connected to a target board or an AVR programmer.\n",
|
||||
str_plural(n));
|
||||
msg_warning("Also note there may be other direct serial ports not listed above.\n");
|
||||
|
||||
for(int k = 0; k < n; k++)
|
||||
free(sp[k].sernum), free(sp[k].port);
|
||||
free(sp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int setport_from_serialadapter(char **portp, const SERIALADAPTER *ser, const char *sernum) {
|
||||
pmsg_error("avrdude built without libserialport support; please compile again with libserialport installed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum) {
|
||||
pmsg_error("avrdude built without libserialport support; please compile again with libserialport installed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int list_available_serialports(LISTID programmers) {
|
||||
pmsg_error("avrdude built without libserialport support; please compile again with libserialport installed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void list_serialadapters(FILE *fp, const char *prefix, LISTID programmers) {
|
||||
LNODEID ln1, ln2, ln3;
|
||||
SERIALADAPTER *sea;
|
||||
@@ -63,7 +385,6 @@ void list_serialadapters(FILE *fp, const char *prefix, LISTID programmers) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serialadapter_not_found(const char *sea_id) {
|
||||
msg_error("\v");
|
||||
if(sea_id && *sea_id)
|
||||
|
||||
@@ -363,6 +363,19 @@ char *str_utoa(unsigned n, char *buf, int base) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Returns a pointer to the start of a trailing number in the string or NULL if not there
|
||||
char *str_endnumber(const char *str) {
|
||||
const char *ret = NULL;
|
||||
|
||||
for(const char *end = str + strlen(str)-1; end >= str; end--)
|
||||
if(isdigit((unsigned char) *end))
|
||||
ret = end;
|
||||
else
|
||||
break;
|
||||
|
||||
return (char *) ret;
|
||||
}
|
||||
|
||||
|
||||
// Convenience functions for printing
|
||||
const char *str_plural(int x) {
|
||||
|
||||
Reference in New Issue
Block a user