Author: stefanct Date: Mon Apr 1 02:45:45 2013 New Revision: 1662 URL: http://flashrom.org/trac/flashrom/changeset/1662
Log: Introduce serialport_write_nonblock().
It seems useful to have a generic and platform-independent method to read and write to a serial port without blocking. This is the write part.
This allows to get rid of the explicit temporary disabling of blocking I/O in serprog's sp_synchronize().
Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at Acked-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at
Modified: trunk/programmer.h trunk/serial.c trunk/serprog.c
Modified: trunk/programmer.h ============================================================================== --- trunk/programmer.h Mon Apr 1 02:45:38 2013 (r1661) +++ trunk/programmer.h Mon Apr 1 02:45:45 2013 (r1662) @@ -660,6 +660,7 @@ /* expose serialport_shutdown as it's currently used by buspirate */ int serialport_shutdown(void *data); int serialport_write(unsigned char *buf, unsigned int writecnt); +int serialport_write_nonblock(unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote); 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);
Modified: trunk/serial.c ============================================================================== --- trunk/serial.c Mon Apr 1 02:45:38 2013 (r1661) +++ trunk/serial.c Mon Apr 1 02:45:45 2013 (r1662) @@ -431,3 +431,75 @@ } return ret; } + +/* Tries up to timeout ms to write writecnt characters from the array starting at buf. Returns + * 0 on success, positive values on temporary errors (e.g. timeouts) and negative ones on permanent errors. + * If really_wrote is not NULL, this function sets its contents to the number of bytes written successfully. */ +int serialport_write_nonblock(unsigned char *buf, unsigned int writecnt, unsigned int timeout, unsigned int *really_wrote) +{ + 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)) { + msg_perr_strerror("Could not set serial port timeout settings: "); + return -1; + } +#else + ssize_t rv; + const int flags = fcntl(sp_fd, F_GETFL); + fcntl(sp_fd, F_SETFL, flags | O_NONBLOCK); +#endif + + int i; + int wr_bytes = 0; + for (i = 0; i < timeout; i++) { + msg_pspew("writecnt %d wr_bytes %d\n", writecnt, wr_bytes); +#ifdef _WIN32 + WriteFile(sp_fd, buf + wr_bytes, writecnt - wr_bytes, &rv, NULL); + msg_pspew("wrote %lu bytes\n", rv); +#else + rv = write(sp_fd, buf + wr_bytes, writecnt - wr_bytes); + msg_pspew("wrote %zd bytes\n", rv); +#endif + if ((rv == -1) && (errno != EAGAIN)) { + msg_perr_strerror("Serial port write error: "); + ret = -1; + break; + } + if (rv > 0) { + wr_bytes += rv; + if (wr_bytes == writecnt) { + msg_pspew("write successful\n"); + ret = 0; + break; + } + } + internal_delay(1000); /* 1ms units */ + } + if (really_wrote != NULL) + *really_wrote = wr_bytes; + + /* restore original blocking behavior */ +#ifdef _WIN32 + if (!SetCommTimeouts(sp_fd, &oldTimeout)) { + msg_perr_strerror("Could not restore serial port timeout settings: "); +#else + if (fcntl(sp_fd, F_SETFL, flags) != 0) { +#endif + return -1; + } + return ret; +}
Modified: trunk/serprog.c ============================================================================== --- trunk/serprog.c Mon Apr 1 02:45:38 2013 (r1661) +++ trunk/serprog.c Mon Apr 1 02:45:45 2013 (r1662) @@ -117,23 +117,19 @@
/* 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 * + * command state - uses nonblocking I/O - rest of the driver uses * * blocking read - TODO: add an alarm() timer for the rest of the app on * * serial operations, though not such a big issue as the first thing to * * do is synchronize (eg. check that device is alive). */ static int sp_synchronize(void) { int i; - int flags = fcntl(sp_fd, F_GETFL); unsigned char buf[8]; - flags |= O_NONBLOCK; - fcntl(sp_fd, F_SETFL, flags); /* First sends 8 NOPs, then flushes the return data - should cause * * the device serial parser to get to a sane state, unless if it * * is waiting for a real long write-n. */ memset(buf, S_CMD_NOP, 8); - if (write(sp_fd, buf, 8) != 8) { - msg_perr("flush write: %s\n", strerror(errno)); + if (serialport_write_nonblock(buf, 8, 1, NULL) != 0) { goto err_out; } /* A second should be enough to get all the answers to the buffer */ @@ -147,8 +143,7 @@ for (i = 0; i < 8; i++) { int n; unsigned char c = S_CMD_SYNCNOP; - if (write(sp_fd, &c, 1) != 1) { - msg_perr("sync write: %s\n", strerror(errno)); + if (serialport_write_nonblock(&c, 1, 1, NULL) != 0) { goto err_out; } msg_pdbg("."); @@ -165,9 +160,8 @@ if (ret > 0 || c != S_ACK) continue; c = S_CMD_SYNCNOP; - if (write(sp_fd, &c, 1) != 1) { - msg_perr("sync write: %s\n", strerror(errno)); - return 1; + if (serialport_write_nonblock(&c, 1, 1, NULL) != 0) { + goto err_out; } ret = serialport_read_nonblock(&c, 1, 500, NULL); if (ret < 0) @@ -179,9 +173,6 @@ goto err_out; if (c != S_ACK) break; /* fail */ - /* Ok, synchronized; back to blocking reads and return. */ - flags &= ~O_NONBLOCK; - fcntl(sp_fd, F_SETFL, flags); msg_pdbg("\n"); return 0; }