Skip to content

Commit

Permalink
linux: Use poll() even in non-blocking mode
Browse files Browse the repository at this point in the history
It appears that calls to read() when in non-blocking mode will not set errno
to other than EAGAIN when the a device has been disconnected.  Note that
this is the same errno code set when there is simply no data to return.

Make it so that poll() is used, in non-blocking mode, to check the
status of the device. In blocking mode though, poll() is not needed.
  • Loading branch information
signal11 committed Jan 21, 2013
1 parent a991328 commit 119135b
Showing 1 changed file with 20 additions and 23 deletions.
43 changes: 20 additions & 23 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,20 +669,30 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
{
int bytes_read;

if (milliseconds != 0) {
/* milliseconds is -1 or > 0. In both cases, we want to
call poll() and wait for data to arrive. -1 means
INFINITE. */
if (milliseconds >= 0) {
/* Milliseconds is either 0 (non-blocking) or > 0 (contains
a valid timeout). In both cases we want to call poll()
and wait for data to arrive. Don't rely on non-blocking
operation (O_NONBLOCK) since some kernels don't seem to
properly report device disconnection through read() when
in non-blocking mode. */
int ret;
struct pollfd fds;

fds.fd = dev->device_handle;
fds.events = POLLIN;
fds.revents = 0;
ret = poll(&fds, 1, milliseconds);
if (ret == -1 || ret == 0)
if (ret == -1 || ret == 0) {
/* Error or timeout */
return ret;
}
else {
/* Check for errors on the file descriptor. This will
indicate a device disconnection. */
if (fds.revents & (POLLERR | POLLHUP | POLLNVAL))
return -1;
}
}

bytes_read = read(dev->device_handle, data, length);
Expand All @@ -707,25 +717,12 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)

int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
{
int flags, res;

flags = fcntl(dev->device_handle, F_GETFL, 0);
if (flags >= 0) {
if (nonblock)
res = fcntl(dev->device_handle, F_SETFL, flags | O_NONBLOCK);
else
res = fcntl(dev->device_handle, F_SETFL, flags & ~O_NONBLOCK);
}
else
return -1;
/* Do all non-blocking in userspace using poll(), since it looks
like there's a bug in the kernel in some versions where
read() will not return -1 on disconnection of the USB device */

if (res < 0) {
return -1;
}
else {
dev->blocking = !nonblock;
return 0; /* Success */
}
dev->blocking = !nonblock;
return 0; /* Success */
}


Expand Down

0 comments on commit 119135b

Please sign in to comment.