Linux:尝试通过ioctl获取游戏杆供应商和产品ID,取而代之的是EINVAL

我试图读取Ubuntu上的USB游戏杆的名称,供应商ID和产品ID(特别是我正在使用Ubuntu 13.10 x64上的有线Xbox 360键盘)。 我可以读取名称,但是当试图读取供应商和产品ID时,我得到一个EINVAL错误。 代码如下:

 if (plugged[index]) { char name[32]; std::snprintf(name, sizeof(name), "/dev/input/js%u", index); // Open the joystick's file descriptor (read-only and non-blocking) m_file = ::open(name, O_RDONLY | O_NONBLOCK); if (m_file >= 0) { // Retrieve the axes mapping ioctl(m_file, JSIOCGAXMAP, m_mapping); // Get the name char joyname[128]; if (ioctl(m_file, JSIOCGNAME(128), joyname) < 0) { m_name = "Unknown Joystick"; } else { m_name = joyname; } // Get vendor and product IDs input_id inpid; if (ioctl(m_file, EVIOCGID, &inpid) < 0) { if (errno == EBADF) printf("EBADF\n"); if (errno == EFAULT) printf("EFAULT\n"); if (errno == ENOTTY) printf("ENOTTY\n"); if (errno == EINVAL) printf("EINVAL\n"); m_manufacturerID = 0; m_productID = 0; } else { m_manufacturerID = inpid.vendor; m_productID = inpid.product; } // Reset the joystick state m_state = JoystickState(); return true; } else { return false; } } else { return false; } 

读取供应商和产品ID的代码片段是:

 ioctl(m_file, EVIOCGID, &inpid) 

根据ioctl的手册页 , EINVAL 请求EVIOCGID )或者inpidinpid )是无效的。

我怎样才能确定哪一个是无效的?

ioctl(m_file, EVIOCGID, &inpid)失败的原因是,我打开的设备是一个游戏杆( /dev/input/js ), EVIOCGID ioctl用于事件设备( /dev/input/event ),因此失败。 不幸的是没有JSIOCGID ioctl,所以我不得不改变战术。 相反,我使用udev来访问游戏杆的供应商和产品ID。 以下是我正在使用的代码:

 // Use udev to look up the product and manufacturer IDs struct udev *udev = udev_new(); if (udev) { char sysname[32]; std::snprintf(sysname, sizeof(sysname), "js%u", index); struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", sysname); dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); if (!dev) { err() << "Unable to find parent USB device" << std::endl; } std::stringstream ss; ss << std::hex << udev_device_get_sysattr_value(dev, "idVendor"); ss >> m_manufacturerID; ss.clear(); ss.str(""); ss << std::hex << udev_device_get_sysattr_value(dev, "idProduct"); ss >> m_productID; udev_device_unref(dev); udev_unref(udev); } else { err() << "Cannot create udev" << std::endl; } 

随着udev我已经能够在Ubuntu 13.10 x64一致检索USB游戏杆的供应商和产品ID。

UPDATE

我进一步测试了这个:

  • Linux Mint 16 x64
  • 曼杰罗x64
  • Fedora 20 x64

在所有情况下,这个代码运行良好。

稍微修正一些小错误,

 #include <iostream> #include <libudev.h> #include <sstream> bool getJoystickInfo(int index, std::string& manufacturerID, std::string& productID, std::string& message) { // Use udev to look up the product and manufacturer IDs struct udev *udev = udev_new(); if (udev) { char sysname[32]; std::snprintf(sysname, sizeof(sysname), "js%u", index); struct udev_device *dev = udev_device_new_from_subsystem_sysname(udev, "input", sysname); dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"); if (!dev) { message = "Unable to find parent USB device"; return false; } std::stringstream ss; ss << std::hex << udev_device_get_sysattr_value(dev, "idVendor"); ss >> manufacturerID; ss.clear(); ss.str(""); ss << std::hex << udev_device_get_sysattr_value(dev, "idProduct"); ss >> productID; udev_device_unref(dev); } else { message = "Cannot create udev"; return false; } udev_unref(udev); return true; } int main() { std::string manufacturerID, productID, message; if (getJoystickInfo(1, manufacturerID, productID, message)) std::cout << manufacturerID << "\t" << productID << std::endl; else std::cerr << message << std::endl; return 0; } 

要编译它,可以将-ludev选项传递给gcc或在CMakeLists.txt中使用以下代码:

 target_link_libraries(MyExecutable udev)