Estoy tratando de leer el nombre, la identificación del proveedor y la identificación del producto para un joystick USB en Ubuntu (específicamente, estoy trabajando con una almohadilla Xbox 360 con cable en Ubuntu 13.10 x64). Puedo leer el nombre, pero cuando intento leer las identificaciones del proveedor y del producto, aparece un error EINVAL . El código es el siguiente:
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; }El fragmento que lee los ID de proveedores y productos es:
ioctl(m_file, EVIOCGID, &inpid) De acuerdo con la página del manual para ioctl , EINVAL , la solicitud ( EVIOCGID ) o argp ( inpid ) no es válida.
¿Cómo puedo determinar cuál no es válido?
Después de investigar un poco más, descubrí que la razón por la que ioctl(m_file, EVIOCGID, &inpid) es que el dispositivo que abro es un joystick ( /dev/input/js ) y EVIOCGID ioctl es para dispositivos de eventos ( /dev/input/event ) por lo tanto, la falla. Desafortunadamente, no hay JSIOCGID ioctl, así que tuve que cambiar de táctica. En su lugar, estoy usando udev para acceder a los identificadores de productos y proveedores del joystick. Aquí está el código que estoy usando:
// 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; }Con udev, he podido recuperar de manera consistente las ID de proveedores y productos para joysticks USB en Ubuntu 13.10 x64.
ACTUALIZAR
He probado esto más con:
En todos los casos, este código funcionó muy bien.
Solo un ejemplo un poco más limpio con corrección de errores menores:
#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; } Para compilarlo, pase la opción -ludev a gcc o use lo siguiente en CMakeLists.txt:
target_link_libraries(MyExecutable udev)