diff --git a/programmer.h b/programmer.h index b10f56894..c36b452f1 100644 --- a/programmer.h +++ b/programmer.h @@ -661,6 +661,7 @@ extern fdtype sp_fd; int serialport_shutdown(void *data); int serialport_write(unsigned char *buf, unsigned int writecnt); int serialport_read(unsigned char *buf, unsigned int readcnt); +int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read); /* Serial port/pin mapping: diff --git a/serial.c b/serial.c index 9a759aea3..d1e2cea7a 100644 --- a/serial.c +++ b/serial.c @@ -361,3 +361,73 @@ int serialport_read(unsigned char *buf, unsigned int readcnt) return 0; } + +/* Tries up to timeout ms to read readcnt characters and places them into the array starting at c. Returns + * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors. + * If really_read is not NULL, this function sets its contents to the number of bytes read successfully. */ +int serialport_read_nonblock(unsigned char *c, unsigned int readcnt, unsigned int timeout, unsigned int *really_read) +{ + int ret = 1; + /* disable blocked i/o and declare platform-specific variables */ +#ifdef _WIN32 + DWORD rv; + COMMTIMEOUTS oldTimeout; + COMMTIMEOUTS newTimeout = { + .ReadIntervalTimeout = MAXDWORD, + .ReadTotalTimeoutMultiplier = 0, + .ReadTotalTimeoutConstant = 0, + .WriteTotalTimeoutMultiplier = 0, + .WriteTotalTimeoutConstant = 0 + }; + if(!GetCommTimeouts(sp_fd, &oldTimeout)) { + msg_perr_strerror("Could not get serial port timeout settings: "); + return -1; + } + if(!SetCommTimeouts(sp_fd, &newTimeout)) { +#else + ssize_t rv; + const int flags = fcntl(sp_fd, F_GETFL); + if (fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK) != 0) { +#endif + msg_perr_strerror("Could not set serial port timeout settings %s"); + return -1; + } + + int i; + int rd_bytes = 0; + for (i = 0; i < timeout; i++) { + msg_pspew("readcnt %d rd_bytes %d\n", readcnt, rd_bytes); +#ifdef _WIN32 + ReadFile(sp_fd, c + rd_bytes, readcnt - rd_bytes, &rv, NULL); + msg_pspew("read %lu bytes\n", rv); +#else + rv = read(sp_fd, c + rd_bytes, readcnt - rd_bytes); + msg_pspew("read %zd bytes\n", rv); +#endif + if ((rv == -1) && (errno != EAGAIN)) { + msg_perr_strerror("Serial port read error: "); + ret = -1; + break; + } + if (rv > 0) + rd_bytes += rv; + if (rd_bytes == readcnt) { + ret = 0; + break; + } + internal_delay(1000); /* 1ms units */ + } + if (really_read != NULL) + *really_read = rd_bytes; + + /* restore original blocking behavior */ +#ifdef _WIN32 + if (!SetCommTimeouts(sp_fd, &oldTimeout)) { +#else + if (fcntl(sp_fd, F_SETFL, flags) != 0) { +#endif + msg_perr_strerror("Could not restore serial port timeout settings: %s\n"); + ret = -1; + } + return ret; +} diff --git a/serprog.c b/serprog.c index c36c93d88..15d1d1b5d 100644 --- a/serprog.c +++ b/serprog.c @@ -115,26 +115,6 @@ static int sp_opensocket(char *ip, unsigned int port) return sock; } -/* Returns 0 on success and places the character read into the location pointed to by c. - * Returns positive values on temporary errors and negative ones on permanent errors. - * An iteration of one loop takes up to 1ms. */ -static int sp_sync_read_timeout(unsigned int loops, unsigned char *c) -{ - int i; - for (i = 0; i < loops; i++) { - ssize_t rv; - rv = read(sp_fd, c, 1); - if (rv == 1) - return 0; - if ((rv == -1) && (errno != EAGAIN)) { - msg_perr("read: %s\n", strerror(errno)); - return -1; - } - usleep(1000); /* 1ms units */ - } - return 1; -} - /* Synchronize: a bit tricky algorithm that tries to (and in my tests has * * always succeeded in) bring the serial protocol to known waiting-for- * * command state - uses nonblocking read - rest of the driver uses * @@ -174,12 +154,12 @@ static int sp_synchronize(void) msg_pdbg("."); fflush(stdout); for (n = 0; n < 10; n++) { - int ret = sp_sync_read_timeout(50, &c); + int ret = serialport_read_nonblock(&c, 1, 50, NULL); if (ret < 0) goto err_out; if (ret > 0 || c != S_NAK) continue; - ret = sp_sync_read_timeout(20, &c); + ret = serialport_read_nonblock(&c, 1, 20, NULL); if (ret < 0) goto err_out; if (ret > 0 || c != S_ACK) @@ -189,12 +169,12 @@ static int sp_synchronize(void) msg_perr("sync write: %s\n", strerror(errno)); return 1; } - ret = sp_sync_read_timeout(500, &c); + ret = serialport_read_nonblock(&c, 1, 500, NULL); if (ret < 0) goto err_out; if (ret > 0 || c != S_NAK) break; /* fail */ - ret = sp_sync_read_timeout(100, &c); + ret = serialport_read_nonblock(&c, 1, 100, NULL); if (ret > 0 || ret < 0) goto err_out; if (c != S_ACK)