]> sigrok.org Git - libserialport.git/blobdiff - serialport.c
Support mark/space parity settings.
[libserialport.git] / serialport.c
index bb5e90a5850ce5bb2399abcfa5838e357ca5279f..05fb85cd06d28ebf494fe957eb8229ee94863761 100644 (file)
@@ -63,6 +63,9 @@ struct sp_port {
        int nonblocking;
 #ifdef _WIN32
        HANDLE hdl;
+       OVERLAPPED write_ovl;
+       BYTE pending_byte;
+       BOOL writing;
 #else
        int fd;
 #endif
@@ -583,6 +586,7 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
 
 #ifdef _WIN32
        DWORD desired_access = 0, flags_and_attributes = 0;
+       COMMTIMEOUTS timeouts;
        char *escaped_port_name;
 
        /* Prefix port name with '\\.\' to work with ports above COM9. */
@@ -606,6 +610,30 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
 
        if (port->hdl == INVALID_HANDLE_VALUE)
                RETURN_FAIL("CreateFile() failed");
+
+       /* All timeouts disabled. */
+       timeouts.ReadIntervalTimeout = 0;
+       timeouts.ReadTotalTimeoutMultiplier = 0;
+       timeouts.ReadTotalTimeoutConstant = 0;
+       timeouts.WriteTotalTimeoutMultiplier = 0;
+       timeouts.WriteTotalTimeoutConstant = 0;
+
+       if (port->nonblocking) {
+               /* Set read timeout such that all reads return immediately. */
+               timeouts.ReadIntervalTimeout = MAXDWORD;
+               /* Prepare OVERLAPPED structure for non-blocking writes. */
+               memset(&port->write_ovl, 0, sizeof(port->write_ovl));
+               if (!(port->write_ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
+                       sp_close(port);
+                       RETURN_FAIL("CreateEvent() failed");
+               }
+               port->writing = FALSE;
+       }
+
+       if (SetCommTimeouts(port->hdl, &timeouts) == 0) {
+               sp_close(port);
+               RETURN_FAIL("SetCommTimeouts() failed");
+       }
 #else
        int flags_local = 0;
        struct port_data data;
@@ -632,17 +660,18 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
                RETURN_CODEVAL(ret);
        }
 
-       /* Turn off all serial port cooking. */
-       data.term.c_iflag &= ~(ISTRIP | INLCR | ICRNL);
-       data.term.c_oflag &= ~(ONLCR | OCRNL | ONOCR);
-#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+       /* Turn off all fancy termios tricks, give us a raw channel. */
+       data.term.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IMAXBEL);
+       data.term.c_oflag &= ~(OPOST|OLCUC|ONLCR|OCRNL|ONOCR|ONLRET|NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
+#ifdef OFILL
        data.term.c_oflag &= ~OFILL;
 #endif
-       /* Disable canonical mode, and don't echo input characters. */
-       data.term.c_lflag &= ~(ICANON | ECHO);
+       data.term.c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
+       data.term.c_cc[VMIN] = 0;
+       data.term.c_cc[VTIME] = 0;
 
-       /* Ignore modem status lines; enable receiver */
-       data.term.c_cflag |= (CLOCAL | CREAD);
+       /* Ignore modem status lines; enable receiver; leave control lines alone on close. */
+       data.term.c_cflag |= (CLOCAL | CREAD | HUPCL);
 
        ret = set_config(port, &data, &config);
 
@@ -668,6 +697,11 @@ enum sp_return sp_close(struct sp_port *port)
        if (CloseHandle(port->hdl) == 0)
                RETURN_FAIL("CloseHandle() failed");
        port->hdl = INVALID_HANDLE_VALUE;
+       if (port->nonblocking) {
+               /* Close event handle created for overlapped writes. */
+               if (CloseHandle(port->write_ovl.hEvent) == 0)
+                       RETURN_FAIL("CloseHandle() failed");
+       }
 #else
        /* Returns 0 upon success, -1 upon failure. */
        if (close(port->fd) == -1)
@@ -749,12 +783,60 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count)
 
        DEBUG("Writing up to %d bytes to port %s", count, port->name);
 
+       if (count == 0)
+               RETURN_VALUE("0", 0);
+
 #ifdef _WIN32
        DWORD written = 0;
+       BYTE *ptr = (BYTE *) buf;
+
+       if (port->nonblocking) {
+               /* Non-blocking write. */
+
+               /* Check whether previous write is complete. */
+               if (port->writing) {
+                       if (HasOverlappedIoCompleted(&port->write_ovl)) {
+                               DEBUG("Previous write completed");
+                               port->writing = 0;
+                       } else {
+                               DEBUG("Previous write not complete");
+                               /* Can't take a new write until the previous one finishes. */
+                               RETURN_VALUE("0", 0);
+                       }
+               }
+
+               /* Keep writing data until the OS has to actually start an async IO for it.
+                * At that point we know the buffer is full. */
+               while (written < count)
+               {
+                       /* Copy first byte of user buffer. */
+                       port->pending_byte = *ptr++;
+
+                       /* Start asynchronous write. */
+                       if (WriteFile(port->hdl, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) {
+                               if (GetLastError() == ERROR_IO_PENDING) {
+                                       DEBUG("Asynchronous write started");
+                                       port->writing = 1;
+                                       RETURN_VALUE("%d", ++written);
+                               } else {
+                                       /* Actual failure of some kind. */
+                                       RETURN_FAIL("WriteFile() failed");
+                               }
+                       } else {
+                               DEBUG("Single byte written immediately.");
+                               written++;
+                       }
+               }
+
+               DEBUG("All bytes written immediately.");
+
+       } else {
+               /* Blocking write. */
+               if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) {
+                       RETURN_FAIL("WriteFile() failed");
+               }
+       }
 
-       /* Returns non-zero upon success, 0 upon failure. */
-       if (WriteFile(port->hdl, buf, count, &written, NULL) == 0)
-               RETURN_FAIL("WriteFile() failed");
        RETURN_VALUE("%d", written);
 #else
        /* Returns the number of bytes written, or -1 upon failure. */
@@ -942,11 +1024,17 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data,
                case NOPARITY:
                        config->parity = SP_PARITY_NONE;
                        break;
+               case ODDPARITY:
+                       config->parity = SP_PARITY_ODD;
+                       break;
                case EVENPARITY:
                        config->parity = SP_PARITY_EVEN;
                        break;
-               case ODDPARITY:
-                       config->parity = SP_PARITY_ODD;
+               case MARKPARITY:
+                       config->parity = SP_PARITY_MARK;
+                       break;
+               case SPACEPARITY:
+                       config->parity = SP_PARITY_SPACE;
                        break;
                default:
                        config->parity = -1;
@@ -1068,6 +1156,8 @@ static enum sp_return get_config(struct sp_port *port, struct port_data *data,
                config->parity = SP_PARITY_NONE;
        else if (!(data->term.c_cflag & PARENB) || (data->term.c_iflag & IGNPAR))
                config->parity = -1;
+       else if (data->term.c_cflag & CMSPAR)
+               config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_MARK : SP_PARITY_SPACE;
        else
                config->parity = (data->term.c_cflag & PARODD) ? SP_PARITY_ODD : SP_PARITY_EVEN;
 
@@ -1149,11 +1239,17 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data,
                case SP_PARITY_NONE:
                        data->dcb.Parity = NOPARITY;
                        break;
+               case SP_PARITY_ODD:
+                       data->dcb.Parity = ODDPARITY;
+                       break;
                case SP_PARITY_EVEN:
                        data->dcb.Parity = EVENPARITY;
                        break;
-               case SP_PARITY_ODD:
-                       data->dcb.Parity = ODDPARITY;
+               case SP_PARITY_MARK:
+                       data->dcb.Parity = MARKPARITY;
+                       break;
+               case SP_PARITY_SPACE:
+                       data->dcb.Parity = SPACEPARITY;
                        break;
                default:
                        RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting");
@@ -1311,7 +1407,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data,
 
        if (config->parity >= 0) {
                data->term.c_iflag &= ~IGNPAR;
-               data->term.c_cflag &= ~(PARENB | PARODD);
+               data->term.c_cflag &= ~(PARENB | PARODD | CMSPAR);
                switch (config->parity) {
                case SP_PARITY_NONE:
                        data->term.c_iflag |= IGNPAR;
@@ -1322,6 +1418,12 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data,
                case SP_PARITY_ODD:
                        data->term.c_cflag |= PARENB | PARODD;
                        break;
+               case SP_PARITY_MARK:
+                       data->term.c_cflag |= PARENB | PARODD | CMSPAR;
+                       break;
+               case SP_PARITY_SPACE:
+                       data->term.c_cflag |= PARENB | CMSPAR;
+                       break;
                default:
                        RETURN_ERROR(SP_ERR_ARG, "Invalid parity setting");
                }