void EvdevDevice::createMappers() { // See if this is a cursor device such as a trackball or mouse. if (mDeviceNode->hasKey(BTN_MOUSE) && mDeviceNode->hasRelativeAxis(REL_X) && mDeviceNode->hasRelativeAxis(REL_Y)) { mClasses |= INPUT_DEVICE_CLASS_CURSOR; mMappers.push_back(std::make_unique<MouseInputMapper>()); } bool isStylus = false; bool haveGamepadButtons = mDeviceNode->hasKeyInRange(BTN_MISC, BTN_MOUSE) || mDeviceNode->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI); // See if this is a touch pad or stylus. // Is this a new modern multi-touch driver? if (mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_X) && mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_Y)) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is a // touch screen. if (mDeviceNode->hasKey(BTN_TOUCH) || !haveGamepadButtons) { mClasses |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; //mMappers.push_back(std::make_unique<MultiTouchInputMapper>()); } // Is this an old style single-touch driver? } else if (mDeviceNode->hasKey(BTN_TOUCH) && mDeviceNode->hasAbsoluteAxis(ABS_X) && mDeviceNode->hasAbsoluteAxis(ABS_Y)) { mClasses |= INPUT_DEVICE_CLASS_TOUCH; //mMappers.push_back(std::make_unique<SingleTouchInputMapper>()); // Is this a BT stylus? } else if ((mDeviceNode->hasAbsoluteAxis(ABS_PRESSURE) || mDeviceNode->hasKey(BTN_TOUCH)) && !mDeviceNode->hasAbsoluteAxis(ABS_X) && !mDeviceNode->hasAbsoluteAxis(ABS_Y)) { mClasses |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; //mMappers.push_back(std::make_unique<ExternalStylusInputMapper>()); isStylus = true; mClasses &= ~INPUT_DEVICE_CLASS_KEYBOARD; } // See if this is a keyboard. Ignore everything in the button range except // for joystick and gamepad buttons which are handled like keyboards for the // most part. // Keyboard will try to claim some of the stylus buttons but we really want // to reserve those so we can fuse it with the touch screen data. Note this // means an external stylus cannot also be a keyboard device. if (!isStylus) { bool haveKeyboardKeys = mDeviceNode->hasKeyInRange(0, BTN_MISC) || mDeviceNode->hasKeyInRange(KEY_OK, KEY_CNT); if (haveKeyboardKeys || haveGamepadButtons) { mClasses |= INPUT_DEVICE_CLASS_KEYBOARD; //mMappers.push_back(std::make_unique<KeyboardInputMapper>()); } } // See if this device is a joystick. // Assumes that joysticks always have gamepad buttons in order to // distinguish them from other devices such as accelerometers that also have // absolute axes. if (haveGamepadButtons) { uint32_t assumedClasses = mClasses | INPUT_DEVICE_CLASS_JOYSTICK; for (int i = 0; i < ABS_CNT; ++i) { if (mDeviceNode->hasAbsoluteAxis(i) && getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) { mClasses = assumedClasses; //mMappers.push_back(std::make_unique<JoystickInputMapper>()); break; } } } // Check whether this device has switches. for (int i = 0; i < SW_CNT; ++i) { if (mDeviceNode->hasSwitch(i)) { mClasses |= INPUT_DEVICE_CLASS_SWITCH; mMappers.push_back(std::make_unique<SwitchInputMapper>()); break; } } // Check whether this device supports the vibrator. // TODO: decide if this is necessary. if (mDeviceNode->hasForceFeedback(FF_RUMBLE)) { mClasses |= INPUT_DEVICE_CLASS_VIBRATOR; //mMappers.push_back(std::make_unique<VibratorInputMapper>()); } ALOGD("device %s classes=0x%x %zu mappers", mDeviceNode->getPath().c_str(), mClasses, mMappers.size()); }
status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; ALOGV("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR); if(fd < 0) { LOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } InputDeviceIdentifier identifier; // Get device name. if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name.setTo(buffer); } // Check to see if the device is on our excluded list for (size_t i = 0; i < mExcludedDevices.size(); i++) { const String8& item = mExcludedDevices.itemAt(i); if (identifier.name == item) { LOGI("ignoring event id %s driver %s\n", devicePath, item.string()); close(fd); return -1; } } // Get device driver version. int driverVersion; if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { LOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; } // Get device identifier. struct input_id inputId; if(ioctl(fd, EVIOCGID, &inputId)) { LOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; } identifier.bus = inputId.bustype; identifier.product = inputId.product; identifier.vendor = inputId.vendor; identifier.version = inputId.version; // Get device physical location. if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.location.setTo(buffer); } // Get device unique id. if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.uniqueId.setTo(buffer); } // Make file descriptor non-blocking for use with poll(). if (fcntl(fd, F_SETFL, O_NONBLOCK)) { LOGE("Error %d making device file descriptor non-blocking.", errno); close(fd); return -1; } // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath), identifier); #if 0 LOGI("add device %d: %s\n", deviceId, devicePath); LOGI(" bus: %04x\n" " vendor %04x\n" " product %04x\n" " version %04x\n", identifier.bus, identifier.vendor, identifier.product, identifier.version); LOGI(" name: \"%s\"\n", identifier.name.string()); LOGI(" location: \"%s\"\n", identifier.location.string()); LOGI(" unique id: \"%s\"\n", identifier.uniqueId.string()); LOGI(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); #endif // Load the configuration file for the device. loadConfigurationLocked(device); // Figure out the kinds of events the device reports. ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1)); bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } // See if this is a cursor device such as a trackball or mouse. if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) && test_bit(REL_Y, device->relBitmask)) { device->classes |= INPUT_DEVICE_CLASS_CURSOR; } // See if this is a touch pad. // Is this a new modern multi-touch driver? if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is // a touch screen. if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; } // Is this an old style single-touch driver? } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) && test_bit(ABS_Y, device->absBitmask)) { device->classes |= INPUT_DEVICE_CLASS_TOUCH; } // See if this device is a joystick. // Assumes that joysticks always have gamepad buttons in order to distinguish them // from other devices such as accelerometers that also have absolute axes. if (haveGamepadButtons) { uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK; for (int i = 0; i <= ABS_MAX; i++) { if (test_bit(i, device->absBitmask) && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { device->classes = assumedClasses; break; } } } // Check whether this device has switches. for (int i = 0; i <= SW_MAX; i++) { if (test_bit(i, device->swBitmask)) { device->classes |= INPUT_DEVICE_CLASS_SWITCH; break; } } // Configure virtual keys. if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. // We do this now so that we can make sure to load the keymap if necessary. status_t status = loadVirtualKeyMapLocked(device); if (!status) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } } // Load the key map. // We need to do this for joysticks too because the key layout may specify axes. status_t keyMapStatus = NAME_NOT_FOUND; if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { // Load the keymap for the device. keyMapStatus = loadKeyMapLocked(device); } // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId == -1 && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; } // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycodeLocked(device, AKEYCODE_Q)) { device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } // See if this device has a DPAD. if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { device->classes |= INPUT_DEVICE_CLASS_DPAD; } // See if this device has a gamepad. for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) { if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; break; } } } // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath, device->identifier.name.string()); delete device; return -1; } // Determine whether the device is external or internal. if (isExternalDeviceLocked(device)) { device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; } // Register with epoll. struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = deviceId; if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { LOGE("Could not add device fd to epoll instance. errno=%d", errno); delete device; return -1; } LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s", deviceId, fd, devicePath, device->identifier.name.string(), device->classes, device->configurationFile.string(), device->keyMap.keyLayoutFile.string(), device->keyMap.keyCharacterMapFile.string(), toString(mBuiltInKeyboardId == deviceId)); mDevices.add(deviceId, device); device->next = mOpeningDevices; mOpeningDevices = device; return 0; }