+ /* 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) {
+ if (HasOverlappedIoCompleted(&port->write_ovl)) {
+ DEBUG("Asynchronous write completed immediately");
+ port->writing = 0;
+ written++;
+ continue;
+ } else {
+ DEBUG("Asynchronous write running");
+ 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.");
+
+ RETURN_VALUE("%d", written);
+#else
+ /* Returns the number of bytes written, or -1 upon failure. */
+ ssize_t written = write(port->fd, buf, count);
+
+ if (written < 0)
+ RETURN_FAIL("write() failed");
+ else
+ RETURN_VALUE("%d", written);
+#endif
+}
+
+enum sp_return sp_blocking_read(struct sp_port *port, void *buf, size_t count, unsigned int timeout)
+{
+ TRACE("%p, %p, %d, %d", port, buf, count, timeout);
+
+ CHECK_OPEN_PORT();
+
+ if (!buf)
+ RETURN_ERROR(SP_ERR_ARG, "Null buffer");
+
+ if (timeout)
+ DEBUG("Reading %d bytes from port %s, timeout %d ms", count, port->name, timeout);
+ else
+ DEBUG("Reading %d bytes from port %s, no timeout", count, port->name);
+
+ if (count == 0)
+ RETURN_VALUE("0", 0);
+
+#ifdef _WIN32
+ DWORD bytes_read = 0;
+
+ /* Set timeout. */
+ port->timeouts.ReadIntervalTimeout = 0;
+ port->timeouts.ReadTotalTimeoutConstant = timeout;
+ if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
+ RETURN_FAIL("SetCommTimeouts() failed");
+
+ /* Start read. */
+ if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl) == 0) {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ DEBUG("Waiting for read to complete");
+ GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE);
+ DEBUG("Read completed, %d/%d bytes read", bytes_read, count);
+ RETURN_VALUE("%d", bytes_read);
+ } else {
+ RETURN_FAIL("ReadFile() failed");
+ }
+ } else {
+ DEBUG("Read completed immediately");
+ RETURN_VALUE("%d", count);
+ }
+#else
+ size_t bytes_read = 0;
+ unsigned char *ptr = (unsigned char *) buf;
+ struct timeval start, delta, now, end = {0, 0};
+ fd_set fds;
+ int result;
+
+ if (timeout) {
+ /* Get time at start of operation. */
+ gettimeofday(&start, NULL);
+ /* Define duration of timeout. */
+ delta.tv_sec = timeout / 1000;
+ delta.tv_usec = (timeout % 1000) * 1000;
+ /* Calculate time at which we should give up. */
+ timeradd(&start, &delta, &end);
+ }
+
+ /* Loop until we have the requested number of bytes. */
+ while (bytes_read < count)
+ {
+ /* Wait until data is available. */
+ FD_ZERO(&fds);
+ FD_SET(port->fd, &fds);
+ if (timeout) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &end, >))
+ /* Timeout has expired. */
+ RETURN_VALUE("%d", bytes_read);
+ timersub(&end, &now, &delta);
+ }
+ result = select(port->fd + 1, &fds, NULL, NULL, timeout ? &delta : NULL);
+ if (result < 0) {
+ if (errno == EINTR) {
+ DEBUG("select() call was interrupted, repeating");
+ continue;
+ } else {
+ RETURN_FAIL("select() failed");
+ }
+ } else if (result == 0) {
+ DEBUG("read timed out");
+ RETURN_VALUE("%d", bytes_read);
+ }
+
+ /* Do read. */
+ result = read(port->fd, ptr, count - bytes_read);
+
+ if (result < 0) {
+ if (errno == EAGAIN)
+ /* This shouldn't happen because we did a select() first, but handle anyway. */
+ continue;
+ else
+ /* This is an actual failure. */
+ RETURN_FAIL("read() failed");
+ }
+
+ bytes_read += result;
+ ptr += result;
+ }
+
+ RETURN_VALUE("%d", bytes_read);
+#endif
+}
+
+enum sp_return sp_nonblocking_read(struct sp_port *port, void *buf, size_t count)
+{
+ TRACE("%p, %p, %d", port, buf, count);
+
+ CHECK_OPEN_PORT();
+
+ if (!buf)
+ RETURN_ERROR(SP_ERR_ARG, "Null buffer");
+
+ DEBUG("Reading up to %d bytes from port %s", count, port->name);
+
+#ifdef _WIN32
+ DWORD bytes_read;
+
+ /* Set timeout. */
+ port->timeouts.ReadIntervalTimeout = MAXDWORD;
+ port->timeouts.ReadTotalTimeoutConstant = 0;
+ if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
+ RETURN_FAIL("SetCommTimeouts() failed");
+
+ /* Do read. */
+ if (ReadFile(port->hdl, buf, count, NULL, &port->read_ovl) == 0)
+ RETURN_FAIL("ReadFile() failed");
+
+ /* Get number of bytes read. */
+ if (GetOverlappedResult(port->hdl, &port->read_ovl, &bytes_read, TRUE) == 0)
+ RETURN_FAIL("GetOverlappedResult() failed");
+
+ RETURN_VALUE("%d", bytes_read);
+#else
+ ssize_t bytes_read;
+
+ /* Returns the number of bytes read, or -1 upon failure. */
+ if ((bytes_read = read(port->fd, buf, count)) < 0) {
+ if (errno == EAGAIN)
+ /* No bytes available. */
+ bytes_read = 0;
+ else
+ /* This is an actual failure. */
+ RETURN_FAIL("read() failed");
+ }
+ RETURN_VALUE("%d", bytes_read);
+#endif
+}
+
+enum sp_return sp_input_waiting(struct sp_port *port)
+{
+ TRACE("%p", port);
+
+ CHECK_OPEN_PORT();
+
+ DEBUG("Checking input bytes waiting on port %s", port->name);
+
+#ifdef _WIN32
+ DWORD errors;
+ COMSTAT comstat;
+
+ if (ClearCommError(port->hdl, &errors, &comstat) == 0)
+ RETURN_FAIL("ClearComError() failed");
+ RETURN_VALUE("%d", comstat.cbInQue);
+#else
+ int bytes_waiting;
+ if (ioctl(port->fd, TIOCINQ, &bytes_waiting) < 0)
+ RETURN_FAIL("TIOCINQ ioctl failed");
+ RETURN_VALUE("%d", bytes_waiting);
+#endif
+}
+
+enum sp_return sp_output_waiting(struct sp_port *port)
+{
+ TRACE("%p", port);
+
+ CHECK_OPEN_PORT();
+
+ DEBUG("Checking output bytes waiting on port %s", port->name);
+
+#ifdef _WIN32
+ DWORD errors;
+ COMSTAT comstat;
+
+ if (ClearCommError(port->hdl, &errors, &comstat) == 0)
+ RETURN_FAIL("ClearComError() failed");
+ RETURN_VALUE("%d", comstat.cbOutQue);
+#else
+ int bytes_waiting;
+ if (ioctl(port->fd, TIOCOUTQ, &bytes_waiting) < 0)
+ RETURN_FAIL("TIOCOUTQ ioctl failed");
+ RETURN_VALUE("%d", bytes_waiting);
+#endif
+}
+
+#ifdef __linux__
+static enum sp_return get_baudrate(int fd, int *baudrate)
+{
+ void *data;
+
+ TRACE("%d, %p", fd, baudrate);
+
+ DEBUG("Getting baud rate");
+
+ if (!(data = malloc(get_termios_size())))
+ RETURN_ERROR(SP_ERR_MEM, "termios malloc failed");
+
+ if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
+ free(data);
+ RETURN_FAIL("getting termios failed");
+ }
+
+ *baudrate = get_termios_speed(data);
+
+ free(data);
+
+ RETURN_OK();
+}
+
+static enum sp_return set_baudrate(int fd, int baudrate)
+{
+ void *data;
+
+ TRACE("%d, %d", fd, baudrate);
+
+ DEBUG("Getting baud rate");
+
+ if (!(data = malloc(get_termios_size())))
+ RETURN_ERROR(SP_ERR_MEM, "termios malloc failed");
+
+ if (ioctl(fd, get_termios_get_ioctl(), data) < 0) {
+ free(data);
+ RETURN_FAIL("getting termios failed");
+ }
+
+ DEBUG("Setting baud rate");
+
+ set_termios_speed(data, baudrate);
+
+ if (ioctl(fd, get_termios_set_ioctl(), data) < 0) {
+ free(data);
+ RETURN_FAIL("setting termios failed");
+ }
+
+ free(data);
+
+ RETURN_OK();
+}
+
+#ifdef USE_TERMIOX
+static enum sp_return get_flow(int fd, int *flow)
+{
+ void *data;
+
+ TRACE("%d, %p", fd, flow);
+
+ DEBUG("Getting advanced flow control");
+
+ if (!(data = malloc(get_termiox_size())))
+ RETURN_ERROR(SP_ERR_MEM, "termiox malloc failed");
+
+ if (ioctl(fd, TCGETX, data) < 0) {
+ free(data);
+ RETURN_FAIL("getting termiox failed");
+ }
+
+ *flow = get_termiox_flow(data);
+
+ free(data);
+
+ RETURN_OK();
+}
+
+static enum sp_return set_flow(int fd, int flow)
+{
+ void *data;
+
+ TRACE("%d, %d", fd, flow);
+
+ DEBUG("Getting advanced flow control");
+
+ if (!(data = malloc(get_termiox_size())))
+ RETURN_ERROR(SP_ERR_MEM, "termiox malloc failed");
+
+ if (ioctl(fd, TCGETX, data) < 0) {
+ free(data);
+ RETURN_FAIL("getting termiox failed");
+ }
+
+ DEBUG("Setting advanced flow control");
+
+ set_termiox_flow(data, flow);
+
+ if (ioctl(fd, TCSETX, data) < 0) {
+ free(data);
+ RETURN_FAIL("setting termiox failed");
+ }
+
+ free(data);
+
+ RETURN_OK();
+}
+#endif /* USE_TERMIOX */
+#endif /* __linux__ */
+
+static enum sp_return get_config(struct sp_port *port, struct port_data *data,
+ struct sp_port_config *config)
+{
+ unsigned int i;
+
+ TRACE("%p, %p, %p", port, data, config);
+
+ DEBUG("Getting configuration for port %s", port->name);
+
+#ifdef _WIN32
+ if (!GetCommState(port->hdl, &data->dcb))
+ RETURN_FAIL("GetCommState() failed");
+
+ for (i = 0; i < NUM_STD_BAUDRATES; i++) {
+ if (data->dcb.BaudRate == std_baudrates[i].index) {
+ config->baudrate = std_baudrates[i].value;
+ break;
+ }
+ }
+
+ if (i == NUM_STD_BAUDRATES)
+ /* BaudRate field can be either an index or a custom baud rate. */
+ config->baudrate = data->dcb.BaudRate;
+
+ config->bits = data->dcb.ByteSize;
+
+ if (data->dcb.fParity)
+ switch (data->dcb.Parity) {
+ 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 MARKPARITY:
+ config->parity = SP_PARITY_MARK;
+ break;
+ case SPACEPARITY:
+ config->parity = SP_PARITY_SPACE;
+ break;
+ default:
+ config->parity = -1;
+ }
+ else
+ config->parity = SP_PARITY_NONE;
+
+ switch (data->dcb.StopBits) {
+ case ONESTOPBIT:
+ config->stopbits = 1;