Problems with blocking reads using libudev on Linux
- by Steve Hawkins
We are using the following routine (on Linux, with libudev) to read data from a PIC microcontroller configured as a USB HID device. The data is sent only when a button connected to the PIC microcontroller is pressed or released.
The routine is missing messages from the PIC controller, and I suspect that this is because the call to poll below is not behaving as it should.
The call to poll will reliably block for 1 second util the first message is read. As soon as the first message is read, the call to poll returns immediately instead of blocking for 1 second (1000 milliseconds) like it should.
I have worked around this problem by closing and re-opening the device after each read. This makes poll behave correctly, but I think that closing and re-opening the device may be the reason for the lost messages.
bool PicIo::Receive (unsigned char* picData, const size_t picDataSize) {
static hiddev_report_info hidReportInfo;
static hiddev_usage_ref_multi hidUsageRef;
if (-1 == PicDeviceDescriptor()) {
return false;
}
// Determine whether or not there is data available to be read
pollfd pollFd;
pollFd.fd = PicDeviceDescriptor();
pollFd.events = POLLIN;
int dataPending = poll (&pollFd, 1, 1000);
if (dataPending <= 0) {
return false;
}
// Initialize the HID Report structure for an input report
hidReportInfo.report_type = HID_REPORT_TYPE_INPUT;
hidReportInfo.report_id = 0;
hidReportInfo.num_fields = 64;
if (-1 == ioctl(PicDeviceDescriptor(), HIDIOCGREPORT, &hidReportInfo)) {
return false;
}
// Initizlize the HID Usage Reference for an Input report
hidUsageRef.uref.report_type = HID_REPORT_TYPE_INPUT;
hidUsageRef.uref.report_id = 0;
hidUsageRef.uref.field_index = 0;
hidUsageRef.uref.usage_index = 0;
hidUsageRef.num_values = 64;
if (-1 == ioctl(PicDeviceDescriptor(), HIDIOCGUSAGES, &hidUsageRef)) {
return false;
}
// Transfer bytes from the usage report into the return value.
for (size_t idx=0; (idx < 64) && (idx < picDataSize); ++idx) {
picData[idx] = hidUsageRef.values[idx];
}
return true;
}
The function PicDeviceDescriptor() does checking on the device to make sure that it is present. Here are the pertinent details of the PicDeviceDescriptor function, showing how the device is begin opened.
int PicIo::PicDeviceDescriptor(int command) {
struct stat statInfo;
static int picDeviceDescriptor = -1;
string picDevicePath = "/dev/usb/hiddev0";
if ((-1 != picDeviceDescriptor) && (CLOSE == command)) {
close (picDeviceDescriptor);
picDeviceDescriptor = -1;
} else if ((-1 != picDeviceDescriptor) && (-1 == fstat(picDeviceDescriptor, &statInfo))) {
// Handle the case where the PIC device had previously been detected, and
// is now disconnected.
close (picDeviceDescriptor);
picDeviceDescriptor = -1;
} else if ((-1 == picDeviceDescriptor) && (m_picDevice.IsConnected())) {
// Create the PIC device descriptor if the PIC device is present (i.e. its
// device node is present) and if the descriptor does not already exist
picDeviceDescriptor = open (picDevicePath.c_str(), O_RDONLY);
}
return picDeviceDescriptor;
}
I'm sure that I'm doing something wrong, but I've Googled the issue and cannot seem to find any relevant answers. Any help would be very much appreciated -- Thx.