转自http://blog.csdn.net/coldsnow33/article/details/16846105
- status_t EventHub::openDeviceLocked(const char *devicePath) {
- char buffer[80];
- ALOGV("Opening device: %s", devicePath);
- int fd = open(devicePath, O_RDWR | O_CLOEXEC);/*open()系统调用返回文件描述符,O_RDWR是指以读写方式打开,O_CLOEXEC的作用是百度来的。Linux中,文件描述符有一个属性:CLOEXEC,即当调用exec()函数成功后,文件描述符会自动关闭。在以往的内核版本(2.6.23以前)中,需要调用 fcntl(fd, F_SETFD, FD_CLOEXEC)来设置这个属性。而新版本(2.6.23开始)中,可以在调用open函数的时候,通过 flags 参数设置CLOEXEC 功能,如open(filename, O_CLOEXEC)。*/
- if(fd < 0) {
- ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
- return -1;
- }
- InputDeviceIdentifier identifier;//input设备标识符
- struct InputDeviceIdentifier {
- inline InputDeviceIdentifier() :
- bus(0), vendor(0), product(0), version(0) {
- }
- // Information provided by the kernel.
- String8 name;
- String8 location;
- String8 uniqueId;//唯一的ID
- uint16_t bus;
- uint16_t vendor;
- uint16_t product;
- uint16_t version;
- // A composite input device descriptor string that uniquely identifies the device
- // even across reboots or reconnections. The value of this field is used by
- // upper layers of the input system to associate settings with individual devices.
- // It is hashed from whatever kernel provided information is available.
- // Ideally, the way this value is computed should not change between Android releases
- // because that would invalidate persistent settings that rely on it.
- String8 descriptor;
- };
- // Get device name.
- if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {//input_dev的name,”ft5x06”
- //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
- //检查是不是要排除的device,一般都不是
- for (size_t i = 0; i < mExcludedDevices.size(); i++) {
- const String8& item = mExcludedDevices.itemAt(i);
- if (identifier.name == item) {
- ALOGI("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)) {// 得到EV_VERSION 0x010001
- ALOGE("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)) {//返回struct input_id
- ALOGE("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) {//唯一ID,字符串
- //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
- } else {
- buffer[sizeof(buffer) - 1] = '\0';
- identifier.uniqueId.setTo(buffer);
- }
- // Fill in the descriptor.
- setDescriptor(identifier);//设置identifier.descriptor
- // Make file descriptor non-blocking for use with poll().
- if (fcntl(fd, F_SETFL, O_NONBLOCK)) {//设置fd非阻塞
- ALOGE("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++;//deviceId=1,mNextDeviceId=2
- Device* device = new Device(fd, deviceId, String8(devicePath), identifier);//一个input device
- ALOGV("add device %d: %s\n", deviceId, devicePath);
- ALOGV(" bus: %04x\n"
- " vendor %04x\n"
- " product %04x\n"
- " version %04x\n",
- identifier.bus, identifier.vendor, identifier.product, identifier.version);
- ALOGV(" name: \"%s\"\n", identifier.name.string());
- ALOGV(" location: \"%s\"\n", identifier.location.string());
- ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
- ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
- ALOGV(" driver: v%d.%d.%d\n",
- driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
- // Load the configuration file for the device.
- loadConfigurationLocked(device);
- /*获取输入设备配置文件,有几个路径,依次找,找到为止
- // Figure out the kinds of events the device reports.
- /*使用EVIOCGBIT ioctl可以获取设备的能力和特性。它告知你设备是否有key或者button。EVIOCGBIT ioctl处理4个参数( ioctl(fd, EVIOCGBIT(ev_type, max_bytes), bitfield))。 ev_type是返回的 type feature( 0是个特殊 case,表示返回设备支持的所有的 type features)。 max_bytes表示返回的最大字节数。bitfield域是指向保存结果的内存指针。return value表示保存结果的实际字节数,如果调用失败,则返回负值。*/
- 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, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
- 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));
- /*
- haveKeyboardKeys:上报0~BTN_MISC(100)-1或KEY_OK(160)~KEY_MAX(0x2ff)之间的type。
- haveGamepadButtons:上报BTN_MISC~BTN_MOUSE(0x110)-1或BTN_JOYSTICK(0x120)~BTN_DIGI(0x140)的type。
- */
- 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;/*input外设是一个键盘或者按钮*/
- }
- // 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;
- }
- /*
- (1) 如果是一个touch pad(不透明的触摸板),还要看它是不是现代的多点触摸driver,所以要看一下有没有report ABS_MT_POSITION_X和ABS_MT_POSITION_Y;多点协议要求的。
- (2) 如果是多点上报协议,还要看下是不是操纵杆。比如PS3控制器也会上报坐标轴,这与多点上报 ABS_MT范围是冲突的,所以还要确认一下这个外设确实是触摸屏。怎么看呢?如果上报了BTN_TOUCH那就是touch,如果没有上报BTN_TOUCH,也不是游戏手柄按钮,那也是touch。device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT。
- (3) 如果是老式的单点上报,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.
- //如果这个外设是一个操纵杆。假设它总是有游戏手柄按钮,为了与同样上报绝对坐标的其他外设,比如感应器区分开来,还需要设置INPUT_DEVICE_CLASS_JOYSTICK。
- 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;
- }
- }
- // Check whether this device supports the vibrator.振荡器
- if (test_bit(FF_RUMBLE, device->ffBitmask)) {
- device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
- }
- // 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.
- /*如果有的话,为触摸屏load虚拟按键。我们现在这样做,所以可以确保load键映射,如果需要的话。一般虚拟按键都是利用触摸屏的边缘坐标模拟的按键。配置文件名就是/sys/board_properties/virtualkeys.{devicename},格式为:0x1:扫描码:X:Y:W:H:0x1: ……例如:
- 0x01:158:55:835:90:55:0x01:139:172:835:125:55:0x01:102:298:835:115:55:0x01:217:412:835:95:55。如果定义了这个配置文件就可以自动把RawInputEvent(原始输入事件)转换为KeyEvent(按键事件)。base/core/java/android/view/inputDevice.java负责处理虚拟按键。要实现虚拟按键还可以在driver中用input_event发送按键消息,往往是这种方式较为常用,尤其是需要校准的电阻屏。
- 注意:使用虚拟按键转换成为的是按键的扫描码,不是按键码,因此依然需要经过按键布局文件的转化才能得到按键码。我们driver中所用的也是扫描码,例如:KEY_MENU、KEY_BACK。
- */
- status_t status = loadVirtualKeyMapLocked(device);//load虚拟按键配置文件
- 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.
- //Load按键映射,我们还需要为操纵杆做这个是因为键盘布局可能是一个指定轴。
- status_t keyMapStatus = NAME_NOT_FOUND;
- if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
- // Load the keymap for the device.先找*.kl,再找*.kcm,查找顺序同
- 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的条件了
- if (!keyMapStatus//上一节load keymap失败了
- && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD(构造函数是这样初始化的)
- && isEligibleBuiltInKeyboard(device->identifier,
- device->configuration, &device->keyMap)) {
- mBuiltInKeyboardId = device->id;
- }
- /*isEligibleBuiltInKeyboard()成立的条件是:
- (1) *.kcm有,type不是SPECIAL_FUNCTION。
- (2) 如果idc文件中设置了keyboard.builtIn = true,那(1)+(2)条件成立。
- (3) 如果input device的name中含有"-keypad",那(1)+(3)条件也成立。
- */
- // '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.//D-Pad( directional pad)方向键
- 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;
- }
- }
- // Disable kernel key repeat since we handle it ourselves
- //失能 kernel key repeat,因为我们除了它
- unsigned int repeatRate[] = {0,0};
- if (ioctl(fd, EVIOCSREP, repeatRate)) {
- ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));
- }
- }
- // If the device isn't recognized as something we handle, don't monitor it.
- //如果device没有被识别为我们可以处理的东西,就不要监视它了
- 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;
- }
- (1) 如果idc配置文件中,device.internal = true,就直接是内部设备了。
- (2) 如果device.internal 没有写,要看input device的bus,如果是BUS_USB或者BUS_BLUETOOTH就是外部设备。
- // 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)) {
- ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
- delete device;
- return -1;
- }
- /*又添加了一个epoll事件,这次是要查询/dev/input/eventx是否可读。
- */
- // Enable wake-lock behavior on kernels that support it.
- // TODO: Only need this for devices that can really wake the system.
- bool usingSuspendBlockIoctl;
- char value[8];
- property_get("ro.platform.has.mbxuimode", value, "false");
- if(strcmp(value, "true") == 0) {
- usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 0);//失能
- } else {
- usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);//使能
- }
- /*int property_get(const char *key, char *value, const char *default_value);
- 失能时,到kernel调用evdev_disable_suspend_block()->
- client->use_wake_lock = false;
- wake_lock_destroy(&client->wake_lock);
- 使能时调用evdev_enable_suspend_block()->
- wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
- client->use_wake_lock = true;
- if (client->packet_head != client->tail)
- 这时候是上锁,什么时候解锁呢?循环buffer首尾相接的时候。
- if (unlikely(client->head == client->tail)) {
- if (client->use_wake_lock)
- wake_unlock(&client->wake_lock);
- }
- if (client->use_wake_lock &&
- client->packet_head == client->tail)
- wake_unlock(&client->wake_lock);
- */
- // Tell the kernel that we want to use the monotonic clock for reporting timestamps
- // associated with input events. This is important because the input system
- // uses the timestamps extensively and assumes they were recorded using the monotonic
- // clock.
- /*通知kernel我们想用monotonic(单调递增)时钟作为input events的报告时间戳,这是非常重要的,假设input system用monotonic时钟记录时间戳,时间戳的应用非常广泛。
- // In older kernel, before Linux 3.4, there was no way to tell the kernel which
- // clock to use to input event timestamps. The standard kernel behavior was to
- // record a real time timestamp, which isn't what we want. Android kernels therefore
- // contained a patch to the evdev_event() function in drivers/input/evdev.c to
- // replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic
- // clock to be used instead of the real time clock.
- /*在Linux 3.4之前的内核中,没有办法通知kernel用哪种时钟作为input系统的时间戳。标准内核行为是记录real(实时)时间的时间戳,这个时间并不是我们想要的。因此,android内核包含一个对drivers/input/evdev.c中evdev_event()函数的patch,用ktime_get_ts()取代 do_gettimeofday(),从而实现monotonic时钟代替real time时钟。
- */
- // As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.
- // Therefore, we no longer require the Android-specific kernel patch described above
- // as long as we make sure to set select the monotonic clock. We do that here.
- /*从Linux 3.4开始,出现了新的EVIOCSCLOCKID EVIOCSCLOCKID来设置期望的时钟。因此,我们不再需要android特殊的内核patch,综上所述,只有我们确定需要设置 monotonic clock,就执行下列代码。
- */
- int clockId = CLOCK_MONOTONIC;
- bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
- ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
- "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
- "usingSuspendBlockIoctl=%s, usingClockIoctl=%s",
- deviceId, fd, devicePath, device->identifier.name.string(),
- device->classes,
- device->configurationFile.string(),
- device->keyMap.keyLayoutFile.string(),
- device->keyMap.keyCharacterMapFile.string(),
- toString(mBuiltInKeyboardId == deviceId),
- toString(usingSuspendBlockIoctl), toString(usingClockIoctl));
- addDeviceLocked(device);
- return 0;
- }
- void EventHub::addDeviceLocked(Device* device) {
- mDevices.add(device->id, device);
- device->next = mOpeningDevices;
- mOpeningDevices = device;
- }
- //KeyedVector.add()添加一个键值对,最后通过device->id就能找到device。
- //通过mOpeningDevices可以找到我们第一open的设备,一直next下去,所以open的都找到了。
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC);/*open()系统调用返回文件描述符,O_RDWR是指以读写方式打开,O_CLOEXEC的作用是百度来的。Linux中,文件描述符有一个属性:CLOEXEC,即当调用exec()函数成功后,文件描述符会自动关闭。在以往的内核版本(2.6.23以前)中,需要调用 fcntl(fd, F_SETFD, FD_CLOEXEC)来设置这个属性。而新版本(2.6.23开始)中,可以在调用open函数的时候,通过 flags 参数设置CLOEXEC 功能,如open(filename, O_CLOEXEC)。*/
if(fd < 0) {
ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
return -1;
}
InputDeviceIdentifier identifier;//input设备标识符
struct InputDeviceIdentifier {
inline InputDeviceIdentifier() :
bus(0), vendor(0), product(0), version(0) {
}
// Information provided by the kernel.
String8 name;
String8 location;
String8 uniqueId;//唯一的ID
uint16_t bus;
uint16_t vendor;
uint16_t product;
uint16_t version;
// A composite input device descriptor string that uniquely identifies the device
// even across reboots or reconnections. The value of this field is used by
// upper layers of the input system to associate settings with individual devices.
// It is hashed from whatever kernel provided information is available.
// Ideally, the way this value is computed should not change between Android releases
// because that would invalidate persistent settings that rely on it.
String8 descriptor;
};
// Get device name.
if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {//input_dev的name,”ft5x06”
//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
//检查是不是要排除的device,一般都不是
for (size_t i = 0; i < mExcludedDevices.size(); i++) {
const String8& item = mExcludedDevices.itemAt(i);
if (identifier.name == item) {
ALOGI("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)) {// 得到EV_VERSION 0x010001
ALOGE("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)) {//返回struct input_id
ALOGE("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) {//唯一ID,字符串
//fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno));
} else {
buffer[sizeof(buffer) - 1] = '\0';
identifier.uniqueId.setTo(buffer);
}
// Fill in the descriptor.
setDescriptor(identifier);//设置identifier.descriptor
// Make file descriptor non-blocking for use with poll().
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {//设置fd非阻塞
ALOGE("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++;//deviceId=1,mNextDeviceId=2
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);//一个input device
ALOGV("add device %d: %s\n", deviceId, devicePath);
ALOGV(" bus: %04x\n"
" vendor %04x\n"
" product %04x\n"
" version %04x\n",
identifier.bus, identifier.vendor, identifier.product, identifier.version);
ALOGV(" name: \"%s\"\n", identifier.name.string());
ALOGV(" location: \"%s\"\n", identifier.location.string());
ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string());
ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string());
ALOGV(" driver: v%d.%d.%d\n",
driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff);
// Load the configuration file for the device.
loadConfigurationLocked(device);
/*获取输入设备配置文件,有几个路径,依次找,找到为止
// Figure out the kinds of events the device reports.
/*使用EVIOCGBIT ioctl可以获取设备的能力和特性。它告知你设备是否有key或者button。EVIOCGBIT ioctl处理4个参数( ioctl(fd, EVIOCGBIT(ev_type, max_bytes), bitfield))。 ev_type是返回的 type feature( 0是个特殊 case,表示返回设备支持的所有的 type features)。 max_bytes表示返回的最大字节数。bitfield域是指向保存结果的内存指针。return value表示保存结果的实际字节数,如果调用失败,则返回负值。*/
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, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
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));
/*
haveKeyboardKeys:上报0~BTN_MISC(100)-1或KEY_OK(160)~KEY_MAX(0x2ff)之间的type。
haveGamepadButtons:上报BTN_MISC~BTN_MOUSE(0x110)-1或BTN_JOYSTICK(0x120)~BTN_DIGI(0x140)的type。
*/
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;/*input外设是一个键盘或者按钮*/
}
// 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;
}
/*
(1) 如果是一个touch pad(不透明的触摸板),还要看它是不是现代的多点触摸driver,所以要看一下有没有report ABS_MT_POSITION_X和ABS_MT_POSITION_Y;多点协议要求的。
(2) 如果是多点上报协议,还要看下是不是操纵杆。比如PS3控制器也会上报坐标轴,这与多点上报 ABS_MT范围是冲突的,所以还要确认一下这个外设确实是触摸屏。怎么看呢?如果上报了BTN_TOUCH那就是touch,如果没有上报BTN_TOUCH,也不是游戏手柄按钮,那也是touch。device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT。
(3) 如果是老式的单点上报,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.
//如果这个外设是一个操纵杆。假设它总是有游戏手柄按钮,为了与同样上报绝对坐标的其他外设,比如感应器区分开来,还需要设置INPUT_DEVICE_CLASS_JOYSTICK。
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;
}
}
// Check whether this device supports the vibrator.振荡器
if (test_bit(FF_RUMBLE, device->ffBitmask)) {
device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;
}
// 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.
/*如果有的话,为触摸屏load虚拟按键。我们现在这样做,所以可以确保load键映射,如果需要的话。一般虚拟按键都是利用触摸屏的边缘坐标模拟的按键。配置文件名就是/sys/board_properties/virtualkeys.{devicename},格式为:0x1:扫描码:X:Y:W:H:0x1: ……例如:
0x01:158:55:835:90:55:0x01:139:172:835:125:55:0x01:102:298:835:115:55:0x01:217:412:835:95:55。如果定义了这个配置文件就可以自动把RawInputEvent(原始输入事件)转换为KeyEvent(按键事件)。base/core/java/android/view/inputDevice.java负责处理虚拟按键。要实现虚拟按键还可以在driver中用input_event发送按键消息,往往是这种方式较为常用,尤其是需要校准的电阻屏。
注意:使用虚拟按键转换成为的是按键的扫描码,不是按键码,因此依然需要经过按键布局文件的转化才能得到按键码。我们driver中所用的也是扫描码,例如:KEY_MENU、KEY_BACK。
*/
status_t status = loadVirtualKeyMapLocked(device);//load虚拟按键配置文件
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.
//Load按键映射,我们还需要为操纵杆做这个是因为键盘布局可能是一个指定轴。
status_t keyMapStatus = NAME_NOT_FOUND;
if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) {
// Load the keymap for the device.先找*.kl,再找*.kcm,查找顺序同
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的条件了
if (!keyMapStatus//上一节load keymap失败了
&& mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD(构造函数是这样初始化的)
&& isEligibleBuiltInKeyboard(device->identifier,
device->configuration, &device->keyMap)) {
mBuiltInKeyboardId = device->id;
}
/*isEligibleBuiltInKeyboard()成立的条件是:
(1) *.kcm有,type不是SPECIAL_FUNCTION。
(2) 如果idc文件中设置了keyboard.builtIn = true,那(1)+(2)条件成立。
(3) 如果input device的name中含有"-keypad",那(1)+(3)条件也成立。
*/
// '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.//D-Pad( directional pad)方向键
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;
}
}
// Disable kernel key repeat since we handle it ourselves
//失能 kernel key repeat,因为我们除了它
unsigned int repeatRate[] = {0,0};
if (ioctl(fd, EVIOCSREP, repeatRate)) {
ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno));
}
}
// If the device isn't recognized as something we handle, don't monitor it.
//如果device没有被识别为我们可以处理的东西,就不要监视它了
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;
}
(1) 如果idc配置文件中,device.internal = true,就直接是内部设备了。
(2) 如果device.internal 没有写,要看input device的bus,如果是BUS_USB或者BUS_BLUETOOTH就是外部设备。
// 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)) {
ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
delete device;
return -1;
}
/*又添加了一个epoll事件,这次是要查询/dev/input/eventx是否可读。
*/
// Enable wake-lock behavior on kernels that support it.
// TODO: Only need this for devices that can really wake the system.
bool usingSuspendBlockIoctl;
char value[8];
property_get("ro.platform.has.mbxuimode", value, "false");
if(strcmp(value, "true") == 0) {
usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 0);//失能
} else {
usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1);//使能
}
/*int property_get(const char *key, char *value, const char *default_value);
失能时,到kernel调用evdev_disable_suspend_block()->
client->use_wake_lock = false;
wake_lock_destroy(&client->wake_lock);
使能时调用evdev_enable_suspend_block()->
wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name);
client->use_wake_lock = true;
if (client->packet_head != client->tail)
这时候是上锁,什么时候解锁呢?循环buffer首尾相接的时候。
if (unlikely(client->head == client->tail)) {
if (client->use_wake_lock)
wake_unlock(&client->wake_lock);
}
if (client->use_wake_lock &&
client->packet_head == client->tail)
wake_unlock(&client->wake_lock);
*/
// Tell the kernel that we want to use the monotonic clock for reporting timestamps
// associated with input events. This is important because the input system
// uses the timestamps extensively and assumes they were recorded using the monotonic
// clock.
/*通知kernel我们想用monotonic(单调递增)时钟作为input events的报告时间戳,这是非常重要的,假设input system用monotonic时钟记录时间戳,时间戳的应用非常广泛。
// In older kernel, before Linux 3.4, there was no way to tell the kernel which
// clock to use to input event timestamps. The standard kernel behavior was to
// record a real time timestamp, which isn't what we want. Android kernels therefore
// contained a patch to the evdev_event() function in drivers/input/evdev.c to
// replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic
// clock to be used instead of the real time clock.
/*在Linux 3.4之前的内核中,没有办法通知kernel用哪种时钟作为input系统的时间戳。标准内核行为是记录real(实时)时间的时间戳,这个时间并不是我们想要的。因此,android内核包含一个对drivers/input/evdev.c中evdev_event()函数的patch,用ktime_get_ts()取代 do_gettimeofday(),从而实现monotonic时钟代替real time时钟。
*/
// As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock.
// Therefore, we no longer require the Android-specific kernel patch described above
// as long as we make sure to set select the monotonic clock. We do that here.
/*从Linux 3.4开始,出现了新的EVIOCSCLOCKID EVIOCSCLOCKID来设置期望的时钟。因此,我们不再需要android特殊的内核patch,综上所述,只有我们确定需要设置 monotonic clock,就执行下列代码。
*/
int clockId = CLOCK_MONOTONIC;
bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, "
"configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, "
"usingSuspendBlockIoctl=%s, usingClockIoctl=%s",
deviceId, fd, devicePath, device->identifier.name.string(),
device->classes,
device->configurationFile.string(),
device->keyMap.keyLayoutFile.string(),
device->keyMap.keyCharacterMapFile.string(),
toString(mBuiltInKeyboardId == deviceId),
toString(usingSuspendBlockIoctl), toString(usingClockIoctl));
addDeviceLocked(device);
return 0;
}
void EventHub::addDeviceLocked(Device* device) {
mDevices.add(device->id, device);
device->next = mOpeningDevices;
mOpeningDevices = device;
}
//KeyedVector.add()添加一个键值对,最后通过device->id就能找到device。
//通过mOpeningDevices可以找到我们第一open的设备,一直next下去,所以open的都找到了。