我们在上一遍看了输入子系统的启动流程,最后,我们看到运行了两个线程:
mDispatcherThread->run mReaderThread->run.
单从字面上来理解,一个是分发线程,另一个是读取线程.没有读取哪来分发?接下来我们先看一下读取线程是干了什么.
InputReader.cpp
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
这个函数很简单,直接调用了mReader->loopOnce().
接下来,我们逐句分析loopOnce的实现.当然,本章只讲设备注册部分.
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
这里可以看到InputDeviceInfo类型的矢量 inputDevices 这个是输入子系统的重点,即然是输入子系统,就一定要有输入设备嘛,那就是它了.
{ // acquire lock
AutoMutex _l(mLock);
oldGeneration = mGeneration;
timeoutMillis = -1;
uint32_t changes = mConfigurationChangesToRefresh;
if (changes) {
mConfigurationChangesToRefresh = 0;
timeoutMillis = 0;
refreshConfigurationLocked(changes);
} else if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
}
} // release lock
这一部分是设备参数的刷新,我们以后来讲,在此带过.
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
接下来,我们看到了重点的一行.getEvents,就是获取事件嘛.返回事件的个数,待后续逐个处理.timoutMillis就是超时返回撒,mEventBuffer 是事件数组的指针,用于收集事件,EVENT_BUFFER_SIZE一次可收集事件的最大个数,现在系统中定义为256个.
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
AutoMutex _l(mLock);
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer;
size_t capacity = bufferSize;
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
// Reopen input devices if needed.
if (mNeedToReopenDevices) {
mNeedToReopenDevices = false;
ALOGI("Reopening all input devices due to a configuration change.");
closeAllDevicesLocked();
mNeedToScanDevices = true;
break; // return to the caller before we actually rescan
}
// Report any devices that had last been added/removed.
while (mClosingDevices) {
Device* device = mClosingDevices;
ALOGV("Reporting device closed: id=%d, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
event->when = now;
event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
event->type = DEVICE_REMOVED;
event += 1;
delete device;
mNeedToSendFinishedDeviceScan = true;
if (--capacity == 0) {
break;
}
}
就一部分有两个关键变量mNeedToReopenDevices 与 mClosingDevices 这两个变量在子系统刚起来时为0,这两小段代码我们可以先不去看它. 而需要关注是另一个变量mNeedToScanDevices,字面理解,是否需要扫描设备.
EventHub::EventHub(void) :
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false), mNeedToScanDevices(true),
mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
可以看到,在eventhub构造时mNeedToScanDevices即为true.其实子系统起来之后的第一件事情就是注册加载设备撒.接下来我们看一下是如何做的:
if (mNeedToScanDevices) {
mNeedToScanDevices = false;
scanDevicesLocked();
mNeedToSendFinishedDeviceScan = true;
}
scanDevicesLocked() 来扫描设备.并将变量置为false,扫描一回就行了嘛,老扫设备干嘛,当然,也有要重扫的可能,比如我插入一个USB鼠标,这个我们后面再说.
void EventHub::scanDevicesLocked() {
status_t res = scanDirLocked(DEVICE_PATH);
if(res < 0) {
ALOGE("scan dir failed for %s\n", DEVICE_PATH);
}
static const char *DEVICE_PATH = "/dev/input";
一看到/dev/input 是不是有一种快要摸到 driver的感觉?呵呵.
闲话不多说,我们来看一下scanDirLocked在干什么
status_t EventHub::scanDirLocked(const char *dirname)
{
char devname[PATH_MAX];
char *filename;
DIR *dir;
struct dirent *de;
dir = opendir(dirname);
if(dir == NULL)
return -1;
strcpy(devname, dirname);
filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' &&
(de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0')))
continue;
strcpy(filename, de->d_name);
openDeviceLocked(devname);
}
closedir(dir);
return 0;
}
这里很简单,遍历整个/dev/input文件夹,然后openDeviceLocked,这个东东有点长,我们一段一段的来看哈.
status_t EventHub::openDeviceLocked(const char *devicePath) {
char buffer[80];
ALOGV("Opening device: %s", devicePath);
int fd = open(devicePath, O_RDWR | O_CLOEXEC);
if(fd < 0) {
ALOGE("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);
}
哈哈,看到ioctl了哈,驱动在干嘛下一遍再看,这里就是读到设备的名字啊.
// 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) {
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)) {
ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno));
close(fd);
return -1;
}
读一下驱动的版本,仅此而已.
struct input_id inputId;
if(ioctl(fd, EVIOCGID, &inputId)) {
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;
读取设备参数信息,忘了说这个identifier了,这个就是设备的身份证.
// 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);
}
接下来这一段的是读取物理位置的,有什么用呢?比如你插入两个一样的鼠标,那么它们的inputId是一样的,如何区别呢?通过这个就可以,比如一个插在usb1,另一个插在usb2.
// 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);
}
这一段是获取特征ID,这个东东有可能两个设备的特征一下,比如两个一样的鼠标.
// Fill in the descriptor.
setDescriptor(identifier);
// Make file descriptor non-blocking for use with poll().
if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
ALOGE("Error %d making device file descriptor non-blocking.", errno);
close(fd);
return -1;
}
首先根据以上获取的身份信息生成dsccriptor.并填入dentifier
设置设备文件为非阻塞.
int32_t deviceId = mNextDeviceId++;
Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
生成设备ID并正式构造device对象,
// Load the configuration file for the device.
loadConfigurationLocked(device);
读取设备的configuration 文件,这个文件在哪里读,有什么用,暂时先放一边.后续我再补.
// 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, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
接下来的这一部分,获取设备的类型.这个类型的意思是这个设备是一个键盘啊,还是鼠标啊,还是触屏啊,最终通过这个bitmask来判断,这个也是在驱动中有明确标明的.