Skip to content

Commit 99a9a60

Browse files
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 <[email protected]>
1 parent 331d46f commit 99a9a60

File tree

11 files changed

+124
-21
lines changed

11 files changed

+124
-21
lines changed

src/avrdude.1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
.Nm
3030
.Fl p Ar partno
3131
.Op Fl b Ar baudrate
32+
.Op Fl r
3233
.Op Fl B Ar bitclock
3334
.Op Fl c Ar programmer-id
3435
.Op Fl C Ar config-file
@@ -365,6 +366,14 @@ for more information run avrdude -p x/h.
365366
.It Fl b Ar baudrate
366367
Override the RS-232 connection baud rate specified in the respective
367368
programmer's entry of the configuration file.
369+
.It Fl r
370+
Opens the serial port at 1200 baud and immediately closes it, waits 400 ms
371+
for each -r on the command line and then establishes communication with
372+
the programmer. This is commonly known as a "1200bps touch", and is used
373+
to trigger programming mode for certain boards like Arduino Leonardo,
374+
Arduino Micro/Pro Micro and the Arduino Nano Every. Longer waits, and
375+
therefore multiple -r options, are sometimes needed for slower, less
376+
powerful hosts.
368377
.It Fl B Ar bitclock
369378
Specify the bit clock period for the JTAG, PDI, TPI, UPDI, or ISP
370379
interface. The value is a floating-point number in microseconds.

src/doc/avrdude.texi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,15 @@ ATmega328P MCU properties; for more information run @code{avrdude -p x/h}.
469469
Override the RS-232 connection baud rate specified in the respective
470470
programmer's entry of the configuration file.
471471

472+
@item -r
473+
Opens the serial port at 1200 baud and immediately closes it, waits 400 ms
474+
for each @code{-r} on the command line and then establishes communication
475+
with the programmer. This is commonly known as a "1200bps touch", and is
476+
used to trigger programming mode for certain boards like Arduino Leonardo,
477+
Arduino Micro/Pro Micro and the Arduino Nano Every. Longer waits, and
478+
therefore multiple @code{-r} options, are sometimes needed for slower, less
479+
powerful hosts.
480+
472481
@item -B @var{bitclock}
473482
Specify the bit clock period for the JTAG, PDI, TPI, UPDI, or ISP
474483
interface. The value is a floating-point number in microseconds.

src/libavrdude.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ struct serial_device {
668668
int (*open)(const char *port, union pinfo pinfo, union filedescriptor *fd);
669669
int (*setparams)(const union filedescriptor *fd, long baud, unsigned long cflags);
670670
void (*close)(union filedescriptor *fd);
671+
void (*rawclose)(union filedescriptor *fd); // Don't restore terminal attributes (Linux)
671672

672673
int (*send)(const union filedescriptor *fd, const unsigned char * buf, size_t buflen);
673674
int (*recv)(const union filedescriptor *fd, unsigned char * buf, size_t buflen);
@@ -691,6 +692,7 @@ extern struct serial_device usbhid_serdev;
691692
#define serial_open (serdev->open)
692693
#define serial_setparams (serdev->setparams)
693694
#define serial_close (serdev->close)
695+
#define serial_rawclose (serdev->rawclose)
694696
#define serial_send (serdev->send)
695697
#define serial_recv (serdev->recv)
696698
#define serial_drain (serdev->drain)
@@ -1252,6 +1254,7 @@ extern "C" {
12521254
int setport_from_serialadapter(char **portp, const SERIALADAPTER *ser, const char *sernum);
12531255
int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum);
12541256
int list_available_serialports(LISTID programmers);
1257+
int touch_serialport(char **portp, int baudrate, int nwaits);
12551258

12561259
int str_starts(const char *str, const char *starts);
12571260
int str_eq(const char *str1, const char *str2);

src/main.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ static void usage(void)
237237
" -D Disable auto erase for flash memory; implies -A\n"
238238
" -i <delay> ISP Clock Delay [in microseconds]\n"
239239
" -P <port> Connection; -P ?s or -P ?sa lists serial ones\n"
240+
" -r Reconnect to -P port after \"touching\" it; wait\n"
241+
" 400 ms for each -r; needed for some USB boards\n"
240242
" -F Override invalid signature or initial checks\n"
241243
" -e Perform a chip erase\n"
242244
" -O Perform RC oscillator calibration (see AVR053)\n"
@@ -526,6 +528,7 @@ int main(int argc, char * argv [])
526528
char * e; /* for strtod() error checking */
527529
const char *errstr; /* for str_int() error checking */
528530
int baudrate; /* override default programmer baud rate */
531+
int touch_1200bps; /* "touch" serial port prior to programming */
529532
double bitclock; /* Specify programmer bit clock (JTAG ICE) */
530533
int ispdelay; /* Specify the delay for ISP clock */
531534
int init_ok; /* Device initialization worked well */
@@ -612,6 +615,7 @@ int main(int argc, char * argv [])
612615
explicit_e = 0;
613616
verbose = 0;
614617
baudrate = 0;
618+
touch_1200bps = 0;
615619
bitclock = 0.0;
616620
ispdelay = 0;
617621
is_open = 0;
@@ -635,7 +639,7 @@ int main(int argc, char * argv [])
635639
/*
636640
* process command line arguments
637641
*/
638-
while ((ch = getopt(argc,argv,"?Ab:B:c:C:DeE:Fi:l:np:OP:qstT:U:uvVx:yY:")) != -1) {
642+
while ((ch = getopt(argc,argv,"?Ab:B:c:C:DeE:Fi:l:np:OP:qrstT:U:uvVx:yY:")) != -1) {
639643

640644
switch (ch) {
641645
case 'b': /* override default programmer baud rate */
@@ -766,6 +770,10 @@ int main(int argc, char * argv [])
766770
quell_progress++ ;
767771
break;
768772

773+
case 'r' :
774+
touch_1200bps++;
775+
break;
776+
769777
case 't': /* enter terminal mode */
770778
ladd(updates, cmd_update("interactive terminal"));
771779
break;
@@ -1175,6 +1183,13 @@ int main(int argc, char * argv [])
11751183
}
11761184
}
11771185

1186+
if(port[0] == 0 || str_eq(port, "unknown")) {
1187+
msg_error("\n");
1188+
pmsg_error("no port has been specified on the command line or in the config file\n");
1189+
imsg_error("specify a port using the -P option and try again\n\n");
1190+
exit(1);
1191+
}
1192+
11781193
/*
11791194
* Divide a serialadapter port string into tokens separated by colons.
11801195
* There are two ways such a port string can be presented:
@@ -1229,18 +1244,12 @@ int main(int argc, char * argv [])
12291244
}
12301245
for (int i = 0; i < 4; i++)
12311246
free(port_tok[i]);
1247+
if(touch_1200bps && touch_serialport(&port, 1200, touch_1200bps) < 0)
1248+
goto skipopen;
12321249
}
12331250

1234-
/*
1235-
* open the programmer
1236-
*/
1237-
if (port[0] == 0) {
1238-
msg_error("\n");
1239-
pmsg_error("no port has been specified on the command line or in the config file\n");
1240-
imsg_error("specify a port using the -P option and try again\n\n");
1241-
exit(1);
1242-
}
12431251

1252+
// Open the programmer
12441253
if (verbose > 0) {
12451254
imsg_notice("Using Port : %s\n", port);
12461255
imsg_notice("Using Programmer : %s\n", pgmid);
@@ -1266,9 +1275,13 @@ int main(int argc, char * argv [])
12661275

12671276
rc = pgm->open(pgm, port);
12681277
if (rc < 0) {
1269-
pmsg_error("unable to open programmer %s on port %s\n", pgmid, port);
1270-
if (print_ports && pgm->conntype == CONNTYPE_SERIAL)
1278+
pmsg_error("unable to open port %s for programmer %s\n", port, pgmid);
1279+
skipopen:
1280+
if (print_ports && pgm->conntype == CONNTYPE_SERIAL) {
12711281
list_available_serialports(programmers);
1282+
if(touch_1200bps == 1)
1283+
pmsg_info("alternatively, try -rr or -rrr for longer delays\n");
1284+
}
12721285
exitrc = 1;
12731286
pgm->ppidata = 0; /* clear all bits at exit */
12741287
goto main_exit;

src/ser_avrdoper.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ struct serial_device avrdoper_serdev =
352352
{
353353
.open = avrdoper_open,
354354
.close = avrdoper_close,
355+
.rawclose = avrdoper_close,
355356
.send = avrdoper_send,
356357
.recv = avrdoper_recv,
357358
.drain = avrdoper_drain,

src/ser_posix.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,6 @@ static int net_open(const char *port, union filedescriptor *fdp) {
352352
return ret;
353353
}
354354

355-
356355
static int ser_set_dtr_rts(const union filedescriptor *fdp, int is_on) {
357356
unsigned int ctl;
358357
int r;
@@ -431,6 +430,11 @@ static void ser_close(union filedescriptor *fd) {
431430
close(fd->ifd);
432431
}
433432

433+
// Close but don't restore attributes
434+
static void ser_rawclose(union filedescriptor *fd) {
435+
saved_original_termios = 0;
436+
close(fd->ifd);
437+
}
434438

435439
static int ser_send(const union filedescriptor *fd, const unsigned char * buf, size_t buflen) {
436440
int rc;
@@ -598,6 +602,7 @@ struct serial_device serial_serdev =
598602
.open = ser_open,
599603
.setparams = ser_setparams,
600604
.close = ser_close,
605+
.rawclose = ser_rawclose,
601606
.send = ser_send,
602607
.recv = ser_recv,
603608
.drain = ser_drain,

src/ser_win32.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ struct serial_device serial_serdev =
756756
.open = ser_open,
757757
.setparams = ser_setparams,
758758
.close = ser_close,
759+
.rawclose = ser_close,
759760
.send = ser_send,
760761
.recv = ser_recv,
761762
.drain = ser_drain,

src/serialadapter.c

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <string.h>
2222
#include <stdio.h>
2323
#include <stdlib.h>
24+
#include <unistd.h>
2425

2526
#include "avrdude.h"
2627
#include "libavrdude.h"
@@ -130,6 +131,13 @@ static SERPORT *get_libserialport_data(int *np) {
130131
return sp;
131132
}
132133

134+
// Free memory allocated from get_libserialport_data()
135+
static void free_libserialport_data(SERPORT *sp, int n) {
136+
for(int k = 0; sp && k < n; k++)
137+
free(sp[k].sernum), free(sp[k].port);
138+
free(sp);
139+
}
140+
133141
// Returns a NULL-terminated malloc'd list of items in SERPORT list spa that are not in spb
134142
SERPORT **sa_spa_not_spb(SERPORT *spa, int na, SERPORT *spb, int nb) {
135143
SERPORT **ret = cfg_malloc(__func__, (na+1)*sizeof*ret);
@@ -266,10 +274,7 @@ int setport_from_serialadapter(char **portp, const SERIALADAPTER *sea, const cha
266274
if(m == 0 || sa_num_matches_by_sea(sea, sernum, sp+i, 1) == 1)
267275
sa_print_specs(sp, n, i);
268276
}
269-
270-
for(int k = 0; k < n; k++)
271-
free(sp[k].sernum), free(sp[k].port);
272-
free(sp);
277+
free_libserialport_data(sp, n);
273278

274279
return rv;
275280
}
@@ -294,14 +299,62 @@ int setport_from_vid_pid(char **portp, int vid, int pid, const char *sernum) {
294299
if(m == 0 || sa_num_matches_by_ids(vid, pid, sernum, sp+i, 1) == 1)
295300
sa_print_specs(sp, n, i);
296301
}
297-
298-
for(int k = 0; k < n; k++)
299-
free(sp[k].sernum), free(sp[k].port);
300-
free(sp);
302+
free_libserialport_data(sp, n);
301303

302304
return rv;
303305
}
304306

307+
// Potentially change port *portp after opening & closing it with baudrate
308+
int touch_serialport(char **portp, int baudrate, int nwaits) {
309+
int i, n1, n2;
310+
SERPORT *sp1, *sp2, **diff;
311+
sp1 = get_libserialport_data(&n1);
312+
if(!sp1 || n1 <= 0 || !portp)
313+
return -1;
314+
315+
pmsg_info("touching serial port %s at %d baud\n", *portp, baudrate);
316+
317+
union pinfo pinfo;
318+
union filedescriptor fd;
319+
pinfo.serialinfo.baud = baudrate;
320+
pinfo.serialinfo.cflags = SERIAL_8N1;
321+
if(serial_open(*portp, pinfo, &fd) == -1) {
322+
pmsg_error("%s() failed to open port %s at %d baud\n", __func__, *portp, baudrate);
323+
return -1;
324+
}
325+
serial_set_dtr_rts(&fd, 1);
326+
usleep(100);
327+
serial_set_dtr_rts(&fd, 0);
328+
serial_rawclose(&fd);
329+
330+
const int nloops = 32, nap = 50;
331+
#if (defined(__arm__) || defined(__aarch64__)) && !defined(__APPLE__)
332+
nwaits += 2;
333+
#endif
334+
pmsg_info("waiting for new port...");
335+
usleep(400*1000*nwaits);
336+
for(i = nloops; i > 0; i--) {
337+
usleep(nap*1000);
338+
if((sp2 = get_libserialport_data(&n2))) {
339+
diff = sa_spa_not_spb(sp2, n2, sp1, n1);
340+
if(*diff && diff[0]->port && !diff[1]) { // Exactly one new port sprung up
341+
pmsg_notice("new port %s discovered\n", (*diff)->port);
342+
if(*portp)
343+
free(*portp);
344+
*portp = cfg_strdup(__func__, (*diff)->port);
345+
msg_info(" %d ms:", (nloops-i+1)*nap + nwaits*400);
346+
i = -1; // Leave loop
347+
}
348+
free(diff);
349+
free_libserialport_data(sp2, n2);
350+
}
351+
}
352+
free_libserialport_data(sp1, n1);
353+
msg_info(" using %s port %s\n", i<0? "new": "same", *portp);
354+
355+
return 0;
356+
}
357+
305358
// List available serial ports
306359
int list_available_serialports(LISTID programmers) {
307360
// Get serial port information from libserialport
@@ -344,6 +397,11 @@ int list_available_serialports(LISTID programmers) {
344397
return -1;
345398
}
346399

400+
int touch_serialport(char **portp, int baudrate, int nwaits) {
401+
pmsg_error("avrdude built without libserialport support; please compile again with libserialport installed\n");
402+
return -1;
403+
}
404+
347405
#endif
348406

349407
void list_serialadapters(FILE *fp, const char *prefix, LISTID programmers) {

src/usb_hidapi.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ static int usbhid_drain(const union filedescriptor *fd, int display) {
318318
struct serial_device usbhid_serdev = {
319319
.open = usbhid_open,
320320
.close = usbhid_close,
321+
.rawclose = usbhid_close,
321322
.send = usbhid_send,
322323
.recv = usbhid_recv,
323324
.drain = usbhid_drain,

src/usb_libusb.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ struct serial_device usb_serdev =
578578
{
579579
.open = usbdev_open,
580580
.close = usbdev_close,
581+
.rawclose = usbdev_close,
581582
.send = usbdev_send,
582583
.recv = usbdev_recv,
583584
.drain = usbdev_drain,
@@ -591,6 +592,7 @@ struct serial_device usb_serdev_frame =
591592
{
592593
.open = usbdev_open,
593594
.close = usbdev_close,
595+
.rawclose = usbdev_close,
594596
.send = usbdev_send,
595597
.recv = usbdev_recv_frame,
596598
.drain = usbdev_drain,

0 commit comments

Comments
 (0)