Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for serial port "touch" functionality using libserialport #1507

Merged
merged 19 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/avrdude.1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions src/doc/avrdude.texi
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions src/libavrdude.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
37 changes: 25 additions & 12 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/ser_avrdoper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 6 additions & 1 deletion src/ser_posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,6 @@ static int net_open(const char *port, union filedescriptor *fdp) {
return ret;
}


static int ser_set_dtr_rts(const union filedescriptor *fdp, int is_on) {
unsigned int ctl;
int r;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/ser_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
74 changes: 66 additions & 8 deletions src/serialadapter.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "avrdude.h"
#include "libavrdude.h"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions src/usb_hidapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/usb_libusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/xbee.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down