X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=serialport.c;h=7ab8258511aed5fdb801ac8539f0b451cd73ec58;hb=bccc7c9fa0f00b762392438bea16d4672a984526;hp=bb5e90a5850ce5bb2399abcfa5838e357ca5279f;hpb=aac4d7f292e011e73a1bc04d089a43af5a40a1cf;p=libserialport.git diff --git a/serialport.c b/serialport.c index bb5e90a..7ab8258 100644 --- a/serialport.c +++ b/serialport.c @@ -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 @@ -570,6 +573,10 @@ void sp_free_port_list(struct sp_port **list) enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) { + struct port_data data; + struct sp_port_config config; + enum sp_return ret; + TRACE("%p, %x", port, flags); CHECK_PORT(); @@ -583,6 +590,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,11 +614,32 @@ 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; - struct sp_port_config config; - int ret; /* Map 'flags' to the OS-specific settings. */ if (flags & (SP_MODE_READ | SP_MODE_WRITE)) @@ -624,6 +653,7 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) if ((port->fd = open(port->name, flags_local)) < 0) RETURN_FAIL("open() failed"); +#endif ret = get_config(port, &data, &config); @@ -632,17 +662,27 @@ 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__) + /* Set sane port settings. */ +#ifdef _WIN32 + data.dcb.fBinary = TRUE; + data.dcb.fDsrSensitivity = FALSE; + data.dcb.fErrorChar = FALSE; + data.dcb.fNull = FALSE; + data.dcb.fAbortOnError = TRUE; +#else + /* 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); +#endif ret = set_config(port, &data, &config); @@ -650,7 +690,6 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags) sp_close(port); RETURN_CODEVAL(ret); } -#endif RETURN_OK(); } @@ -668,6 +707,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 +793,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 +1034,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 +1166,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 +1249,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 +1417,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 +1428,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"); } @@ -1447,7 +1559,7 @@ static enum sp_return set_config(struct sp_port *port, struct port_data *data, } } - if (tcsetattr(port->fd, TCSADRAIN, &data->term) < 0) + if (tcsetattr(port->fd, TCSANOW, &data->term) < 0) RETURN_FAIL("tcsetattr() failed"); #ifdef __APPLE__