EventHub.cpp檔案中open_device函數淺析
使用者態中,與核心態聯絡緊密的一個函數是open_device,它位於EventHub.cpp檔案中,該函數有大量的底層操作,以完成上層對硬體的操作。該函數的首部為:
int EventHub::open_device(const char *deviceName)
其中參數有scan_dir()函數獲得。在這裡,deviceName=”/dev/input/event0”
for (attempt = 0; attempt < 10; attempt++) {
fd = open(deviceName, O_RDWR);
if (fd >= 0) break;
usleep(100);
}
這個迴圈給了系統10次開啟裝置的機會,若失敗,則等待100usec後繼續嘗試。
---
if(ioctl(fd, EVIOCGVERSION, &version))
if(ioctl(fd, EVIOCGID, &id))
if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1)
通過ioctl介面擷取驅動版本號碼、id號及裝置名稱。
List<String8>::iterator iter = mExcludedDevices.begin();
List<String8>::iterator end = mExcludedDevices.end();
for ( ; iter != end; iter++) {
const char* test = *iter;
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s/n", deviceName, test);
close(fd);
fd = -1;
return -1;
}
}
檢查裝置執行列變,若列表中有與上一步獲的裝置名稱相同的情況(裝置已經開啟),則關閉裝置。
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1)
if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1)
int devid = 0;
if (devid >= mNumDevicesById) { // mNumDevicesById在建構函式中被初始化為0;
//所以會執行下面的內容;
device_ent* new_devids = (device_ent*)realloc(mDevicesById,
sizeof(mDevicesById[0]) * (devid + 1));
if (new_devids == NULL) {
LOGE("out of memory");
return -1;
}
mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK;
device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
當new一個device_t時,會把後面的參數傳送到:
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
, keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
}
其中layoutMap初始化成了:new KeyLayoutMap(),因此,進入會KeyLayoutMap();
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0)
通過ioctl介面擷取鍵盤位元遮罩。
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
bool defaultKeymap = false;
if (access(keylayoutFilename, R_OK)) {
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s", root, "qwerty.kl");
defaultKeymap = true;
}
device->layoutMap->load(keylayoutFilename);
這段代碼的作用是載入鍵盤配置檔案。由
snprintf(keylayoutFilename, sizeof(keylayoutFilename)......
可知:keylayoutFilename與tmptn有關。由strcpy(tmpfn, name);可知tmptn=name。在前面的一個ioctl介面中,已經擷取到了裝置名稱。由類比機開機記錄可以知道:name=qwerty2。若access(keylayoutFilename, R_OK)為假,則使用預設的布局檔案qwerty.kl
device->layoutMap->load(keylayoutFilename);
接下來,載入這個鍵盤配置檔案。
進入load函數,我們就可以能清楚地看到檔案的載入過程。
int fd = open(filename, O_RDONLY); //開啟檔案
off_t len = lseek(fd, 0, SEEK_END); //擷取檔案長度
char* buf = (char*)malloc(len+1); //開闢讀取檔案的緩衝區
if (read(fd, buf, len) != len) //將檔案內容讀到緩衝區
buf[len] = '/0' //標誌檔案結束
int line = 1;
char const* p = buf;
enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN;
while (true) {
String8 token = next_token(&p, &line);
進入這個死迴圈之間的第一條語句是:String8 token = next_token(&p, &line);而該函數的返回為:return String8(begin, end-begin);從String8類的建構函式我們知道next_token到底返回了個什麼東西:
String8::String8(const char16_t* o, size_t len)
: mString(allocFromUTF16(o, len)) { }
我們可以看到,String8的這個建構函式初始化了一個字串常量(const char* mString;)
這個字串常量在後面會用到:
scancode = strtol(token.string(), &end, 0);
因為token.string這個函數原型為;
inline const char* String8::string() const
{
return mString;
}
以上所有對底層地操作,如:
fd = open(deviceName, O_RDWR);
if(ioctl(fd, EVIOCGVERSION, &version))
等都不是直接對鍵盤驅動進行操作,而是對事件處理驅動進行的操作。也就是說,事件處理驅動對鍵盤驅動進行了封裝。
接下來,是對底層驅動的分析:
鍵盤驅動函數Goldfish_events.c
輸入核心驅動函數input.c
事件處理驅動函數evdev.c