int hs_poll(hs_poll_source *sources, unsigned int count, int timeout) { assert(sources); assert(count); assert(count <= HS_POLL_MAX_SOURCES); fd_set fds; uint64_t start; int maxfd, r; FD_ZERO(&fds); maxfd = 0; for (unsigned int i = 0; i < count; i++) { if (sources[i].desc >= FD_SETSIZE) { for (unsigned int j = i; j < count; j++) sources[j].ready = 0; return hs_error(HS_ERROR_SYSTEM, "Cannot select() on descriptor %d (too big)", sources[i].desc); } FD_SET(sources[i].desc, &fds); sources[i].ready = 0; if (sources[i].desc > maxfd) maxfd = sources[i].desc; } start = hs_millis(); restart: if (timeout >= 0) { int adjusted_timeout; struct timeval tv; adjusted_timeout = hs_adjust_timeout(timeout, start); tv.tv_sec = adjusted_timeout / 1000; tv.tv_usec = (adjusted_timeout % 1000) * 1000; r = select(maxfd + 1, &fds, NULL, NULL, &tv); } else { r = select(maxfd + 1, &fds, NULL, NULL, NULL); } if (r < 0) { if (errno == EINTR) goto restart; return hs_error(HS_ERROR_SYSTEM, "poll() failed: %s", strerror(errno)); } if (!r) return 0; for (unsigned int i = 0; i < count; i++) sources[i].ready = !!FD_ISSET(sources[i].desc, &fds); return r; }
ssize_t hs_hid_read(hs_handle *h, uint8_t *buf, size_t size, int timeout) { assert(h); assert(h->dev->type == HS_DEVICE_TYPE_HID); assert(buf); assert(size); ssize_t r; if (timeout) { struct pollfd pfd; uint64_t start; pfd.events = POLLIN; pfd.fd = h->fd; start = hs_millis(); restart: r = poll(&pfd, 1, hs_adjust_timeout(timeout, start)); if (r < 0) { if (errno == EINTR) goto restart; return hs_error(HS_ERROR_SYSTEM, "poll('%s') failed: %s", h->dev->path, strerror(errno)); } if (!r) return 0; } if (h->numbered_reports) { /* Work around a hidraw bug introduced in Linux 2.6.28 and fixed in Linux 2.6.34, see https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5a38f2c7c4dd53d5be097930902c108e362584a3 */ if (detect_kernel26_byte_bug()) { if (size + 1 > h->buf_size) { free(h->buf); h->buf_size = 0; h->buf = malloc(size + 1); if (!h->buf) return hs_error(HS_ERROR_MEMORY, NULL); h->buf_size = size + 1; } r = read(h->fd, h->buf, size + 1); if (r > 0) memcpy(buf, h->buf + 1, (size_t)--r); } else { r = read(h->fd, buf, size); } } else { r = read(h->fd, buf + 1, size - 1); if (r > 0) { buf[0] = 0; r++; } } if (r < 0) { switch (errno) { case EAGAIN: #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif return 0; case EIO: case ENXIO: return hs_error(HS_ERROR_IO, "I/O error while reading from '%s'", h->dev->path); } return hs_error(HS_ERROR_SYSTEM, "read('%s') failed: %s", h->dev->path, strerror(errno)); } return r; }