mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-09-29 12:28:41 +03:00
Support for serial port "touch" functionality using libserialport (#1507)
This makes it possible to upload to boards like the Arduino Leonardo and Arduino Nano Every wwithout using an external tool to enter bootloader mode Co-authored-by: Stefan Rueger <stefan.rueger@urclocks.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
.Nm
|
||||
.Fl p Ar partno
|
||||
.Op Fl b Ar baudrate
|
||||
.Op Fl r
|
||||
.Op Fl B Ar bitclock
|
||||
.Op Fl c Ar programmer-id
|
||||
.Op Fl C Ar config-file
|
||||
@@ -365,6 +366,14 @@ for more information run avrdude -p x/h.
|
||||
.It Fl b Ar baudrate
|
||||
Override the RS-232 connection baud rate specified in the respective
|
||||
programmer's entry of the configuration file.
|
||||
.It Fl r
|
||||
Opens the serial port at 1200 baud and immediately closes it, waits 400 ms
|
||||
for each -r on the command line and then establishes communication with
|
||||
the programmer. This is commonly known as a "1200bps touch", and is used
|
||||
to trigger programming mode for certain boards like Arduino Leonardo,
|
||||
Arduino Micro/Pro Micro and the Arduino Nano Every. Longer waits, and
|
||||
therefore multiple -r options, are sometimes needed for slower, less
|
||||
powerful hosts.
|
||||
.It Fl B Ar bitclock
|
||||
Specify the bit clock period for the JTAG, PDI, TPI, UPDI, or ISP
|
||||
interface. The value is a floating-point number in microseconds.
|
||||
|
||||
@@ -469,6 +469,15 @@ ATmega328P MCU properties; for more information run @code{avrdude -p x/h}.
|
||||
Override the RS-232 connection baud rate specified in the respective
|
||||
programmer's entry of the configuration file.
|
||||
|
||||
@item -r
|
||||
Opens the serial port at 1200 baud and immediately closes it, waits 400 ms
|
||||
for each @code{-r} on the command line and then establishes communication
|
||||
with the programmer. This is commonly known as a "1200bps touch", and is
|
||||
used to trigger programming mode for certain boards like Arduino Leonardo,
|
||||
Arduino Micro/Pro Micro and the Arduino Nano Every. Longer waits, and
|
||||
therefore multiple @code{-r} options, are sometimes needed for slower, less
|
||||
powerful hosts.
|
||||
|
||||
@item -B @var{bitclock}
|
||||
Specify the bit clock period for the JTAG, PDI, TPI, UPDI, or ISP
|
||||
interface. The value is a floating-point number in microseconds.
|
||||
|
||||
@@ -668,6 +668,7 @@ struct serial_device {
|
||||
int (*open)(const char *port, union pinfo pinfo, union filedescriptor *fd);
|
||||
int (*setparams)(const union filedescriptor *fd, long baud, unsigned long cflags);
|
||||
void (*close)(union filedescriptor *fd);
|
||||
void (*rawclose)(union filedescriptor *fd); // Don't restore terminal attributes (Linux)
|
||||
|
||||
int (*send)(const union filedescriptor *fd, const unsigned char * buf, size_t buflen);
|
||||
int (*recv)(const union filedescriptor *fd, unsigned char * buf, size_t buflen);
|
||||
@@ -691,6 +692,7 @@ extern struct serial_device usbhid_serdev;
|
||||
#define serial_open (serdev->open)
|
||||
#define serial_setparams (serdev->setparams)
|
||||
#define serial_close (serdev->close)
|
||||
#define serial_rawclose (serdev->rawclose)
|
||||
#define serial_send (serdev->send)
|
||||
#define serial_recv (serdev->recv)
|
||||
#define serial_drain (serdev->drain)
|
||||
@@ -1252,6 +1254,7 @@ extern "C" {
|
||||
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 touch_serialport(char **portp, int baudrate, int nwaits);
|
||||
|
||||
int str_starts(const char *str, const char *starts);
|
||||
int str_eq(const char *str1, const char *str2);
|
||||
|
||||
37
src/main.c
37
src/main.c
@@ -237,6 +237,8 @@ static void usage(void)
|
||||
" -D Disable auto erase for flash memory; implies -A\n"
|
||||
" -i <delay> ISP Clock Delay [in microseconds]\n"
|
||||
" -P <port> Connection; -P ?s or -P ?sa lists serial ones\n"
|
||||
" -r Reconnect to -P port after \"touching\" it; wait\n"
|
||||
" 400 ms for each -r; needed for some USB boards\n"
|
||||
" -F Override invalid signature or initial checks\n"
|
||||
" -e Perform a chip erase\n"
|
||||
" -O Perform RC oscillator calibration (see AVR053)\n"
|
||||
@@ -526,6 +528,7 @@ int main(int argc, char * argv [])
|
||||
char * e; /* for strtod() error checking */
|
||||
const char *errstr; /* for str_int() error checking */
|
||||
int baudrate; /* override default programmer baud rate */
|
||||
int touch_1200bps; /* "touch" serial port prior to programming */
|
||||
double bitclock; /* Specify programmer bit clock (JTAG ICE) */
|
||||
int ispdelay; /* Specify the delay for ISP clock */
|
||||
int init_ok; /* Device initialization worked well */
|
||||
@@ -612,6 +615,7 @@ int main(int argc, char * argv [])
|
||||
explicit_e = 0;
|
||||
verbose = 0;
|
||||
baudrate = 0;
|
||||
touch_1200bps = 0;
|
||||
bitclock = 0.0;
|
||||
ispdelay = 0;
|
||||
is_open = 0;
|
||||
@@ -635,7 +639,7 @@ int main(int argc, char * argv [])
|
||||
/*
|
||||
* process command line arguments
|
||||
*/
|
||||
while ((ch = getopt(argc,argv,"?Ab:B:c:C:DeE:Fi:l:np:OP:qstT:U:uvVx:yY:")) != -1) {
|
||||
while ((ch = getopt(argc,argv,"?Ab:B:c:C:DeE:Fi:l:np:OP:qrstT:U:uvVx:yY:")) != -1) {
|
||||
|
||||
switch (ch) {
|
||||
case 'b': /* override default programmer baud rate */
|
||||
@@ -766,6 +770,10 @@ int main(int argc, char * argv [])
|
||||
quell_progress++ ;
|
||||
break;
|
||||
|
||||
case 'r' :
|
||||
touch_1200bps++;
|
||||
break;
|
||||
|
||||
case 't': /* enter terminal mode */
|
||||
ladd(updates, cmd_update("interactive terminal"));
|
||||
break;
|
||||
@@ -1175,6 +1183,13 @@ int main(int argc, char * argv [])
|
||||
}
|
||||
}
|
||||
|
||||
if(port[0] == 0 || str_eq(port, "unknown")) {
|
||||
msg_error("\n");
|
||||
pmsg_error("no port has been specified on the command line or in the config file\n");
|
||||
imsg_error("specify a port using the -P option and try again\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Divide a serialadapter port string into tokens separated by colons.
|
||||
* There are two ways such a port string can be presented:
|
||||
@@ -1229,18 +1244,12 @@ int main(int argc, char * argv [])
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
free(port_tok[i]);
|
||||
if(touch_1200bps && touch_serialport(&port, 1200, touch_1200bps) < 0)
|
||||
goto skipopen;
|
||||
}
|
||||
|
||||
/*
|
||||
* open the programmer
|
||||
*/
|
||||
if (port[0] == 0) {
|
||||
msg_error("\n");
|
||||
pmsg_error("no port has been specified on the command line or in the config file\n");
|
||||
imsg_error("specify a port using the -P option and try again\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Open the programmer
|
||||
if (verbose > 0) {
|
||||
imsg_notice("Using Port : %s\n", port);
|
||||
imsg_notice("Using Programmer : %s\n", pgmid);
|
||||
@@ -1266,9 +1275,13 @@ int main(int argc, char * argv [])
|
||||
|
||||
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)
|
||||
pmsg_error("unable to open port %s for programmer %s\n", port, pgmid);
|
||||
skipopen:
|
||||
if (print_ports && pgm->conntype == CONNTYPE_SERIAL) {
|
||||
list_available_serialports(programmers);
|
||||
if(touch_1200bps == 1)
|
||||
pmsg_info("alternatively, try -rr or -rrr for longer delays\n");
|
||||
}
|
||||
exitrc = 1;
|
||||
pgm->ppidata = 0; /* clear all bits at exit */
|
||||
goto main_exit;
|
||||
|
||||
@@ -352,6 +352,7 @@ struct serial_device avrdoper_serdev =
|
||||
{
|
||||
.open = avrdoper_open,
|
||||
.close = avrdoper_close,
|
||||
.rawclose = avrdoper_close,
|
||||
.send = avrdoper_send,
|
||||
.recv = avrdoper_recv,
|
||||
.drain = avrdoper_drain,
|
||||
|
||||
@@ -352,7 +352,6 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int ser_set_dtr_rts(const union filedescriptor *fdp, int is_on) {
|
||||
unsigned int ctl;
|
||||
int r;
|
||||
@@ -431,6 +430,11 @@ static void ser_close(union filedescriptor *fd) {
|
||||
close(fd->ifd);
|
||||
}
|
||||
|
||||
// Close but don't restore attributes
|
||||
static void ser_rawclose(union filedescriptor *fd) {
|
||||
saved_original_termios = 0;
|
||||
close(fd->ifd);
|
||||
}
|
||||
|
||||
static int ser_send(const union filedescriptor *fd, const unsigned char * buf, size_t buflen) {
|
||||
int rc;
|
||||
@@ -598,6 +602,7 @@ struct serial_device serial_serdev =
|
||||
.open = ser_open,
|
||||
.setparams = ser_setparams,
|
||||
.close = ser_close,
|
||||
.rawclose = ser_rawclose,
|
||||
.send = ser_send,
|
||||
.recv = ser_recv,
|
||||
.drain = ser_drain,
|
||||
|
||||
@@ -756,6 +756,7 @@ struct serial_device serial_serdev =
|
||||
.open = ser_open,
|
||||
.setparams = ser_setparams,
|
||||
.close = ser_close,
|
||||
.rawclose = ser_close,
|
||||
.send = ser_send,
|
||||
.recv = ser_recv,
|
||||
.drain = ser_drain,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
@@ -130,6 +131,13 @@ static SERPORT *get_libserialport_data(int *np) {
|
||||
return sp;
|
||||
}
|
||||
|
||||
// Free memory allocated from get_libserialport_data()
|
||||
static void free_libserialport_data(SERPORT *sp, int n) {
|
||||
for(int k = 0; sp && k < n; k++)
|
||||
free(sp[k].sernum), free(sp[k].port);
|
||||
free(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);
|
||||
@@ -266,10 +274,7 @@ int setport_from_serialadapter(char **portp, const SERIALADAPTER *sea, const cha
|
||||
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);
|
||||
free_libserialport_data(sp, n);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@@ -294,14 +299,62 @@ int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum) {
|
||||
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);
|
||||
free_libserialport_data(sp, n);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Potentially change port *portp after opening & closing it with baudrate
|
||||
int touch_serialport(char **portp, int baudrate, int nwaits) {
|
||||
int i, n1, n2;
|
||||
SERPORT *sp1, *sp2, **diff;
|
||||
sp1 = get_libserialport_data(&n1);
|
||||
if(!sp1 || n1 <= 0 || !portp)
|
||||
return -1;
|
||||
|
||||
pmsg_info("touching serial port %s at %d baud\n", *portp, baudrate);
|
||||
|
||||
union pinfo pinfo;
|
||||
union filedescriptor fd;
|
||||
pinfo.serialinfo.baud = baudrate;
|
||||
pinfo.serialinfo.cflags = SERIAL_8N1;
|
||||
if(serial_open(*portp, pinfo, &fd) == -1) {
|
||||
pmsg_error("%s() failed to open port %s at %d baud\n", __func__, *portp, baudrate);
|
||||
return -1;
|
||||
}
|
||||
serial_set_dtr_rts(&fd, 1);
|
||||
usleep(100);
|
||||
serial_set_dtr_rts(&fd, 0);
|
||||
serial_rawclose(&fd);
|
||||
|
||||
const int nloops = 32, nap = 50;
|
||||
#if (defined(__arm__) || defined(__aarch64__)) && !defined(__APPLE__)
|
||||
nwaits += 2;
|
||||
#endif
|
||||
pmsg_info("waiting for new port...");
|
||||
usleep(400*1000*nwaits);
|
||||
for(i = nloops; i > 0; i--) {
|
||||
usleep(nap*1000);
|
||||
if((sp2 = get_libserialport_data(&n2))) {
|
||||
diff = sa_spa_not_spb(sp2, n2, sp1, n1);
|
||||
if(*diff && diff[0]->port && !diff[1]) { // Exactly one new port sprung up
|
||||
pmsg_notice("new port %s discovered\n", (*diff)->port);
|
||||
if(*portp)
|
||||
free(*portp);
|
||||
*portp = cfg_strdup(__func__, (*diff)->port);
|
||||
msg_info(" %d ms:", (nloops-i+1)*nap + nwaits*400);
|
||||
i = -1; // Leave loop
|
||||
}
|
||||
free(diff);
|
||||
free_libserialport_data(sp2, n2);
|
||||
}
|
||||
}
|
||||
free_libserialport_data(sp1, n1);
|
||||
msg_info(" using %s port %s\n", i<0? "new": "same", *portp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// List available serial ports
|
||||
int list_available_serialports(LISTID programmers) {
|
||||
// Get serial port information from libserialport
|
||||
@@ -344,6 +397,11 @@ int list_available_serialports(LISTID programmers) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int touch_serialport(char **portp, int baudrate, int nwaits) {
|
||||
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) {
|
||||
|
||||
@@ -318,6 +318,7 @@ static int usbhid_drain(const union filedescriptor *fd, int display) {
|
||||
struct serial_device usbhid_serdev = {
|
||||
.open = usbhid_open,
|
||||
.close = usbhid_close,
|
||||
.rawclose = usbhid_close,
|
||||
.send = usbhid_send,
|
||||
.recv = usbhid_recv,
|
||||
.drain = usbhid_drain,
|
||||
|
||||
@@ -578,6 +578,7 @@ struct serial_device usb_serdev =
|
||||
{
|
||||
.open = usbdev_open,
|
||||
.close = usbdev_close,
|
||||
.rawclose = usbdev_close,
|
||||
.send = usbdev_send,
|
||||
.recv = usbdev_recv,
|
||||
.drain = usbdev_drain,
|
||||
@@ -591,6 +592,7 @@ struct serial_device usb_serdev_frame =
|
||||
{
|
||||
.open = usbdev_open,
|
||||
.close = usbdev_close,
|
||||
.rawclose = usbdev_close,
|
||||
.send = usbdev_send,
|
||||
.recv = usbdev_recv_frame,
|
||||
.drain = usbdev_drain,
|
||||
|
||||
@@ -1505,6 +1505,7 @@ static int xbeedev_set_dtr_rts(const union filedescriptor *fdp, int is_on)
|
||||
static struct serial_device xbee_serdev_frame = {
|
||||
.open = xbeedev_open,
|
||||
.close = xbeedev_close,
|
||||
.rawclose = xbeedev_close,
|
||||
.send = xbeedev_send,
|
||||
.recv = xbeedev_recv,
|
||||
.drain = xbeedev_drain,
|
||||
|
||||
Reference in New Issue
Block a user