3

我有一个 64 位 Enterprice SuSE 11 我有一个应用程序,它打开一个 HIDRAW 设备并在其上运行 ioctl 函数以从该设备获取原始信息,如下所示:

struct hidraw_devinfo devinfo;
int fd = open("/dev/hidraw0", 0);
int ret = ioctl(fd, HIDIOCGRAWINFO, &devinfo);
...

如果我在 64 位模式下编译这个程序,没有错误也没有问题,当我执行应用程序时,ioctl 函数可以正常工作。

g++ main.cpp

如果我在 32 位模式下编译这个程序也没有错误也没有问题。但是当我执行应用程序时,ioctl 函数返回 EINVAL 错误(errno = 22,无效参数)

g++ -m32 main.cpp

有什么问题?

笔记:

struct hidraw_devinfo 
{
     __u32 bustype;
     __s16 vendor;
     __s16 product;
}
4

2 回答 2

4

Linuxioctl定义和兼容性层是我刚刚猛烈抨击的一个引人入胜的话题。

通常ioctl,定义使用一系列宏 _IOW/_IOR 等,它们将您的参数类型名称作为参考,以及一个幻数和序数值,这些数值和序数值会为您提供您的 ioctl 参数值(例如HIDIOCGRAWINFO)。类型名称用于编码sizeof(arg_type)到定义中。这意味着用户空间中使用的类型决定了宏生成的ioctl——即 HIDIOCGRAWINFO 可能会根据包含条件而有所不同。

这是 32 位和 64 位不同的第一点,sizeof可能会有所不同,具体取决于打包、使用模糊数据大小(例如 long),但尤其是(并且不可避免地)如果您使用指针参数。因此,在这种情况下,想要支持 32 位客户端的 64 位内核模块需要定义一个兼容性参数类型,以匹配该参数类型的 32 位等效布局,从而匹配一个 32 位兼容 ioctl。这些 32 位等效定义使用名为compat.

在您的情况下,这sizeof()是相同的,因此这不是您要走的路-但了解可能发生的全部情况很重要。

此外,内核配置可以定义CONFIG_COMPAT哪些更改系统调用包装器(尤其是围绕用户/内核接口 wrt 的代码ioctl)以减轻支持 32 位和 64 位的负担。其中一部分包括一个ioctl名为ioctl_compat.

我所看到的是CONFIG_COMPAT定义 32 位程序将生成将ioctls 传递给ioctl_compat回调的代码,即使它可以生成与ioctl64 位相同的值(例如在您的情况下)。因此驱动程序编写者需要确保ioctl_compat处理特殊(不同)32 位兼容ioctl类型和正常的“64 位 - 或未更改的 32 位”类型。

因此,在仅 32 位和仅 64 位系统(没有 CONFIG_COMPAT)上设计和测试的内核模块可能适用于 32 位和 64 位程序,但不适用于同时支持两者的程序。

所以查看 HID 我看到这是在 2.6.38 中添加的:

http://lxr.linux.no/#linux+v2.6.38/drivers/hid/hidraw.c#L347

于 2012-03-27T04:35:44.557 回答
3

问题可能是devinfo程序传递给ioctl函数的结构不匹配。

我猜你在 64 位系统上的工作。因此,您的内核以 64 位运行,而您正在与之通信的内核模块(带有ioctl)也是 64 位的。

当您以 64 位编译用户程序时,devinfo内核模块和用户程序中的定义是相同的。

当您以 32 位编译用户程序时,devinfo内核模块中的定义与用户程序中的定义不同。实际上,在 32 位中,某些类型的大小会发生变化:主要是long指针。因此,您的程序创建了一个特定大小的结构,内核模块以不同的方式解释它接收到的数据。内核模块可能不理解您赋予它的值,因为它不会在您放置它的位置查找它。

解决方法是注意devinfo结构的定义,使其在编译32位和64位时具有相同的二进制表示。

于 2012-02-13T11:58:53.707 回答