+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("ClearCommError() 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("ClearCommError() 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
+}
+
+enum sp_return sp_new_event_set(struct sp_event_set **result_ptr)
+{
+ struct sp_event_set *result;
+
+ TRACE("%p", result_ptr);
+
+ if (!result_ptr)
+ RETURN_ERROR(SP_ERR_ARG, "Null result");
+
+ *result_ptr = NULL;
+
+ if (!(result = malloc(sizeof(struct sp_event_set))))
+ RETURN_ERROR(SP_ERR_MEM, "sp_event_set malloc() failed");
+
+ memset(result, 0, sizeof(struct sp_event_set));
+
+ *result_ptr = result;
+
+ RETURN_OK();
+}
+
+static enum sp_return add_handle(struct sp_event_set *event_set,
+ event_handle handle, enum sp_event mask)
+{
+ void *new_handles;
+ enum sp_event *new_masks;
+
+ TRACE("%p, %d, %d", event_set, handle, mask);
+
+ if (!(new_handles = realloc(event_set->handles,
+ sizeof(event_handle) * (event_set->count + 1))))
+ RETURN_ERROR(SP_ERR_MEM, "handle array realloc() failed");
+
+ if (!(new_masks = realloc(event_set->masks,
+ sizeof(enum sp_event) * (event_set->count + 1))))
+ RETURN_ERROR(SP_ERR_MEM, "mask array realloc() failed");
+
+ event_set->handles = new_handles;
+ event_set->masks = new_masks;
+
+ ((event_handle *) event_set->handles)[event_set->count] = handle;
+ event_set->masks[event_set->count] = mask;
+
+ event_set->count++;
+
+ RETURN_OK();
+}
+
+enum sp_return sp_add_port_events(struct sp_event_set *event_set,
+ const struct sp_port *port, enum sp_event mask)
+{
+ TRACE("%p, %p, %d", event_set, port, mask);
+
+ if (!event_set)
+ RETURN_ERROR(SP_ERR_ARG, "Null event set");
+
+ if (!port)
+ RETURN_ERROR(SP_ERR_ARG, "Null port");
+
+ if (mask > (SP_EVENT_RX_READY | SP_EVENT_TX_READY | SP_EVENT_ERROR))
+ RETURN_ERROR(SP_ERR_ARG, "Invalid event mask");
+
+ if (!mask)
+ RETURN_OK();
+
+#ifdef _WIN32
+ enum sp_event handle_mask;
+ if ((handle_mask = mask & SP_EVENT_TX_READY))
+ TRY(add_handle(event_set, port->write_ovl.hEvent, handle_mask));
+ if ((handle_mask = mask & (SP_EVENT_RX_READY | SP_EVENT_ERROR)))
+ TRY(add_handle(event_set, port->wait_ovl.hEvent, handle_mask));
+#else
+ TRY(add_handle(event_set, port->fd, mask));
+#endif
+
+ RETURN_OK();
+}
+
+void sp_free_event_set(struct sp_event_set *event_set)
+{
+ TRACE("%p", event_set);
+
+ if (!event_set) {
+ DEBUG("Null event set");
+ RETURN();
+ }
+
+ DEBUG("Freeing event set");
+
+ if (event_set->handles)
+ free(event_set->handles);
+ if (event_set->masks)
+ free(event_set->masks);
+
+ free(event_set);
+
+ RETURN();
+}
+
+enum sp_return sp_wait(struct sp_event_set *event_set, unsigned int timeout)
+{
+ TRACE("%p, %d", event_set, timeout);
+
+ if (!event_set)
+ RETURN_ERROR(SP_ERR_ARG, "Null event set");
+
+#ifdef _WIN32
+ if (WaitForMultipleObjects(event_set->count, event_set->handles, FALSE,
+ timeout ? timeout : INFINITE) == WAIT_FAILED)
+ RETURN_FAIL("WaitForMultipleObjects() failed");
+
+ RETURN_OK();
+#else
+ struct timeval start, delta, now, end = {0, 0};
+ int result, timeout_remaining;
+ struct pollfd *pollfds;
+ unsigned int i;
+
+ if (!(pollfds = malloc(sizeof(struct pollfd) * event_set->count)))
+ RETURN_ERROR(SP_ERR_MEM, "pollfds malloc() failed");
+
+ for (i = 0; i < event_set->count; i++) {
+ pollfds[i].fd = ((int *) event_set->handles)[i];
+ pollfds[i].events = 0;
+ pollfds[i].revents = 0;
+ if (event_set->masks[i] & SP_EVENT_RX_READY)
+ pollfds[i].events |= POLLIN;
+ if (event_set->masks[i] & SP_EVENT_TX_READY)
+ pollfds[i].events |= POLLOUT;
+ if (event_set->masks[i] & SP_EVENT_ERROR)
+ pollfds[i].events |= POLLERR;
+ }
+
+ 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 an event occurs. */
+ while (1)
+ {
+ if (timeout) {
+ gettimeofday(&now, NULL);
+ if (timercmp(&now, &end, >)) {
+ DEBUG("wait timed out");
+ break;
+ }
+ timersub(&end, &now, &delta);
+ timeout_remaining = delta.tv_sec * 1000 + delta.tv_usec / 1000;
+ }
+
+ result = poll(pollfds, event_set->count, timeout ? timeout_remaining : -1);
+
+ if (result < 0) {
+ if (errno == EINTR) {
+ DEBUG("poll() call was interrupted, repeating");
+ continue;
+ } else {
+ free(pollfds);
+ RETURN_FAIL("poll() failed");
+ }
+ } else if (result == 0) {
+ DEBUG("poll() timed out");
+ break;
+ } else {
+ DEBUG("poll() completed");
+ break;
+ }
+ }
+
+ free(pollfds);
+ RETURN_OK();
+#endif
+}
+
+#ifdef USE_TERMIOS_SPEED