符号链接上的意外 ioctl 行为
Unexpected ioctl behavior on symlinks
我目前正在使用 HID 设备。它有两种设置模式:HID 和 RS232。所以我写了一个小脚本来将他切换到 RS232,只要它作为 HID 设备插入,使用这个 udev 规则:
ENV{ID_VENDOR}=="Vendor", ENV{ID_VENDOR_ID}=="001d", ENV{ID_USB_DRIVER}=="usbhid",\
SYMLINK+="hid_device", RUN+="/path/to/HID_to_serial"
脚本如下:
// 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;
}
现在,通常,当我插入设备时,Linux 会给它 /dev/hidraw0
文件。当我在 /dev/hidraw0
上使用我的脚本时,它工作得很好。脚本中使用的报告描述符是正确的,一切都按预期工作:HID 设备切换回 RS232 模式。
但是,当我尝试在由 udev 规则创建的 /dev/hid_device
符号链接上使用我的脚本时,它在 99% 的时间里都不起作用,告诉我 ioctl: Invalid argument
。更奇怪的是它有效,但只有 1% 的时间(可能更少)。
有谁知道这可能是从哪里来的,以及如何解决或解决它?提前致谢。
我发现了问题。
它在于我的 udev 规则:它不够精确。
当我插入设备时,linux 在 /dev/
中创建了两个 character special
设备文件:hidraw0
和 input/event15
(在我的例子中)。他们共享很多环境价值观,例如他们的 ID_VENDOR_ID
或他们的 ID_USB_DRIVER
。但是,它们不共享相同的 MAJOR
.
所以我所做的是在我的 udev 规则中添加 ENV{MAJOR}=="correct_major"
,现在我的符号链接链接到正确的设备文件。
这也解释了为什么它有时会起作用:我猜是因为 udev 规则中缺少详细信息,有时符号链接链接到正确的设备文件,有时却没有。
我目前正在使用 HID 设备。它有两种设置模式:HID 和 RS232。所以我写了一个小脚本来将他切换到 RS232,只要它作为 HID 设备插入,使用这个 udev 规则:
ENV{ID_VENDOR}=="Vendor", ENV{ID_VENDOR_ID}=="001d", ENV{ID_USB_DRIVER}=="usbhid",\
SYMLINK+="hid_device", RUN+="/path/to/HID_to_serial"
脚本如下:
// 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;
}
现在,通常,当我插入设备时,Linux 会给它 /dev/hidraw0
文件。当我在 /dev/hidraw0
上使用我的脚本时,它工作得很好。脚本中使用的报告描述符是正确的,一切都按预期工作:HID 设备切换回 RS232 模式。
但是,当我尝试在由 udev 规则创建的 /dev/hid_device
符号链接上使用我的脚本时,它在 99% 的时间里都不起作用,告诉我 ioctl: Invalid argument
。更奇怪的是它有效,但只有 1% 的时间(可能更少)。
有谁知道这可能是从哪里来的,以及如何解决或解决它?提前致谢。
我发现了问题。
它在于我的 udev 规则:它不够精确。
当我插入设备时,linux 在 /dev/
中创建了两个 character special
设备文件:hidraw0
和 input/event15
(在我的例子中)。他们共享很多环境价值观,例如他们的 ID_VENDOR_ID
或他们的 ID_USB_DRIVER
。但是,它们不共享相同的 MAJOR
.
所以我所做的是在我的 udev 规则中添加 ENV{MAJOR}=="correct_major"
,现在我的符号链接链接到正确的设备文件。
这也解释了为什么它有时会起作用:我猜是因为 udev 规则中缺少详细信息,有时符号链接链接到正确的设备文件,有时却没有。