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 <[email protected]>
---
 programmer.h |    1 +
 serial.c     |   77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 serprog.c    |   19 ++++-----------
 3 files changed, 80 insertions(+), 17 deletions(-)

diff --git a/programmer.h b/programmer.h
index b4f69c4..9286e2d 100644
--- a/programmer.h
+++ b/programmer.h
@@ -645,6 +645,7 @@ extern fdtype sp_fd;
 /* 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);
 
diff --git a/serial.c b/serial.c
index c3154ab..9fa5c5d 100644
--- a/serial.c
+++ b/serial.c
@@ -390,13 +390,13 @@ int serialport_read_nonblock(unsigned char *c, unsigned 
int readcnt, unsigned in
        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
+               msg_pspew("readcnt %d rd_bytes %d\n", readcnt, rd_bytes);
                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);
+               //msg_pspew("read %zd bytes\n", rv);
 #endif
                if ((rv == -1) && (errno != EAGAIN)) {
                        msg_perr_strerror("Serial port read error: ");
@@ -406,7 +406,6 @@ int serialport_read_nonblock(unsigned char *c, unsigned int 
readcnt, unsigned in
                if (rv > 0)
                        rd_bytes += rv;
                if (rd_bytes == readcnt) {
-                       msg_perr("read successful\n");
                        ret = 0;
                        break;
                }
@@ -426,3 +425,75 @@ int serialport_read_nonblock(unsigned char *c, unsigned 
int readcnt, unsigned in
        }
        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;
+}
diff --git a/serprog.c b/serprog.c
index 4a2db28..90670cb 100644
--- a/serprog.c
+++ b/serprog.c
@@ -117,23 +117,19 @@ static int sp_opensocket(char *ip, unsigned int port)
 
 /* 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 @@ static int sp_synchronize(void)
        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 @@ static int sp_synchronize(void)
                        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 @@ static int sp_synchronize(void)
                                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;
                }
-- 
Kind regards, Stefan Tauner


_______________________________________________
flashrom mailing list
[email protected]
http://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to