Reputation: 1112
I'm currently working with a HID device. It has two setup modes : HID, and RS232. So I wrote a little script to switch him to RS232, whenever it's plugged as a HID device, using this udev rule :
ENV{ID_VENDOR}=="Vendor", ENV{ID_VENDOR_ID}=="001d", ENV{ID_USB_DRIVER}=="usbhid",\
SYMLINK+="hid_device", RUN+="/path/to/HID_to_serial"
The script is as follows :
// HID_to_serial.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
//#define DEFAULT_DEVICE_PATH "/dev/hidraw0"
#define DEFAULT_DEVICE_PATH "/dev/hid_device"
int main(int argc, char **argv)
{
int fd = open(DEFAULT_DEVICE_PATH, O_RDWR);
if (fd < 0)
{
perror("Unable to open device");
return 1;
}
// Very specific report descriptor
const char buf[64] = { 0x02, 0x0b, 0x02, 0x04, 0x42, 0x40, 0x10, 0x42,
0x62, 0x10, 0x42, 0x42, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int res = ioctl(fd, _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, 64), buf);
if (res < 0)
perror("ioctl");
else
printf("Device was succesfully switched back to serial mode!\n");
return 0;
}
Now, usually, when I plug the device, Linux gives it the /dev/hidraw0
file. And when I use my script on /dev/hidraw0
, it works perfectly. The report descriptor used in the script is correct, and everything works as intended : the HID device switches back to RS232 mode.
However, when I try to use my script on the /dev/hid_device
symlink created by the udev rule, it doesn't work 99% of the time, telling me ioctl: Invalid argument
. The even stranger thing is that it works, but 1% of the time (maybe even less often).
Does anyone have any idea where this might come from, and how to fix it, or work around it? Thanks in advance.
Upvotes: 1
Views: 540
Reputation: 1112
I found the problem.
It lies in my udev rule : it's not precise enough.
When I plug the device, linux creates two character special
device files in /dev/
: hidraw0
, and input/event15
(in my case). They share a lot of environment values, like for example their ID_VENDOR_ID
, or their ID_USB_DRIVER
. However, they do not share the same MAJOR
.
So what I did was add ENV{MAJOR}=="correct_major"
in my udev rule, and now my symlink is linked to the correct device file.
It also explains why it worked sometimes : I guess that because of the lack of details in the udev rule, sometimes the symlink was linked to the correct device file, sometimes not.
Upvotes: 1