Linux input子系統詳解

一:前言

最近在研究android的sensor driver,主要是E-compass,其中用到了Linux input子系統.在網上也看了很多這方面的資料,感覺還是這篇分析的比較細緻透徹,因此轉載一下以便自己學習,同時和大家分享!

(這篇博客主要是以鍵盤驅動爲例的,不過講解的是Linux Input Subsystem,可以仔細的研究一下!)

鍵盤驅動將檢測到的所有按鍵都上報給了input子系統。Input子系統是所有I/O設備驅動的中間層,爲上層提供了一個統一的界面。例如,在終 端系統中,我們不需要去管有多少個鍵盤,多少個鼠標。它只要從input子系統中去取對應的事件(按鍵,鼠標移位等)就可以了。

二:使用input device的例子

下面的代碼是基於linux kernel 2.6.25.分析的代碼主要位於kernel2.6.25/drivers/input下面.

在內核自帶的文檔Documentation/input/input-programming.txt中。有一個使用input子系統的例子,並附帶相應的說明。以此爲例分析如下:

#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/io.h>

static struct input_dev *button_dev;

static irqreturn_t button_interrupt(int irq, void *dummy)
{
	input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
	input_sync(button_dev);
	return IRQ_HANDLED;
}

static int __init button_init(void)
{
	int error;

	if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {
		printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
		return -EBUSY;
	}

	button_dev = input_allocate_device();
	if (!button_dev) {
		printk(KERN_ERR "button.c: Not enough memory\n");
		error = -ENOMEM;
		goto err_free_irq;
	}

	button_dev->evbit[0] = BIT_MASK(EV_KEY);
	button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);

	error = input_register_device(button_dev);
	if (error) {
		printk(KERN_ERR "button.c: Failed to register device\n");
		goto err_free_dev;
	}

	return 0;

err_free_dev:
	input_free_device(button_dev);
err_free_irq:
	free_irq(BUTTON_IRQ, button_interrupt);
	return error;
}

static void __exit button_exit(void)
{
	input_unregister_device(button_dev);
	free_irq(BUTTON_IRQ, button_interrupt);
}

module_init(button_init);
module_exit(button_exit);

這個示例module代碼還是比較簡單,在初始化函數裏註冊了一箇中斷處理例程。然後註冊了一個input device.在中斷處理程序裏,將接收到的按鍵上報給input子系統。文檔的作者在之後的分析裏又對這個module作了優化。主要是在註冊中斷處理 的時序上。在修改過後的代碼裏,爲input device定義了open函數,在open的時候再去註冊中斷處理例程(Gick:因爲init裏首先註冊中斷,但是這個中斷裏上報的按鍵事件依賴中斷註冊後面的註冊按鍵事件,所以時序有問題)。具體的信息請自行參考這篇文檔。在資料缺乏的情況下,kernel自帶的文檔就 是剖析kernel相關知識的最好資料.文檔的作者還分析了幾個api函數。列舉如下:

1): set_bit(EV_KEY, button_dev.evbit);

      set_bit(BTN_0, button_dev.keybit);

分別用來設置設備所產生的事件以及上報的按鍵值。Struct iput_dev中有兩個成員,一個是evbit.一個是keybit.分別用表示設備所支持的事件和按鍵類型。

2): input_register_device(&button_dev);

用來註冊一個input device.

3): input_report_key()

用於給上層上報一個按鍵動作

4): input_sync()

用來告訴上層,本次的事件已經完成了.

5): NBITS(x) - returns the length of a bitfield array in longs for x bits

     LONG(x) - returns the index in the array in longs for bit x

     BIT(x)   - returns the index in a long for bit x     

這幾個宏在input子系統中經常用到。上面的英文解釋已經很清楚了。

三:input設備註冊分析

Input設備註冊的接口爲:input_register_device()。代碼如下:

int input_register_device(struct input_dev *dev)

{

         static atomic_t input_no = ATOMIC_INIT(0);

         struct input_handler *handler;

         const char *path;

         int error;

         __set_bit(EV_SYN, dev->evbit);

         /*

         * If delay and period are pre-set by the driver, then autorepeating

         * is handled by the driver itself and we don't do it in input.c.

         */

         init_timer(&dev->timer);

         if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {

                   dev->timer.data = (long) dev;

                   dev->timer.function = input_repeat_key;

                   dev->rep[REP_DELAY] = 250;

                   dev->rep[REP_PERIOD] = 33;

         }
Input_device的evbit表示該設備所支持的事件。在這裏將其EV_SYN置位,即所有設備都支持這個事件.如果 dev->rep[REP_DELAY]和dev->rep[REP_PERIOD]沒有設值,則將其賦默認值。這主要是處理重複按鍵的.(這個地方還沒有仔細研究過,有點疑問 )

         if (!dev->getkeycode)

                   dev->getkeycode = input_default_getkeycode;

         if (!dev->setkeycode)

                   dev->setkeycode = input_default_setkeycode;

         snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),

                   "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);

         error = device_add(&dev->dev);

         if (error)

                   return error;

         path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);

         printk(KERN_INFO "input: %s as %s\n",

                   dev->name ? dev->name : "Unspecified device", path ? path : "N/A");

         kfree(path);

         error = mutex_lock_interruptible(&input_mutex);

         if (error) {

                   device_del(&dev->dev);

                   return error;

         }

如果input device沒有定義getkeycode和setkeycode.則將其賦默認值。然後調用device_add()將input_dev中封裝的device註冊到sysfs。

         list_add_tail(&dev->node, &input_dev_list);

         list_for_each_entry(handler, &input_handler_list, node)

                   input_attach_handler(dev, handler);

         input_wakeup_procfs_readers();

         mutex_unlock(&input_mutex);

         return 0;

}

 

這裏就是重點了。將input device 掛到input_dev_list鏈表上.然後,對每一個掛在input_handler_list的handler調用 input_attach_handler().這裏的情況好比設備模型中的device和driver的匹配。所有的input device都掛在input_dev_list鏈上。所有的handle都掛在input_handler_list上。

 

看一下這個匹配的詳細過程。匹配是在input_attach_handler()中完成的。代碼如下:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)

{

         const struct input_device_id *id;

         int error;

         if (handler->blacklist && input_match_device(handler->blacklist, dev))

                   return -ENODEV;

         id = input_match_device(handler->id_table, dev);

         if (!id)

                   return -ENODEV;

         error = handler->connect(handler, dev, id);

         if (error && error != -ENODEV)

                   printk(KERN_ERR

                            "input: failed to attach handler %s to device %s, "

                            "error: %d\n",

                            handler->name, kobject_name(&dev->dev.kobj), error);

         return error;

}

如果handle的blacklist被賦值。要先匹配blacklist中的數據跟dev->id的數據是否匹配。匹配成功過後再來匹 配handle->id和dev->id中的數據。如果匹配成功,則調用handler->connect().

 

來看一下具體的數據匹配過程,這是在input_match_device()中完成的。代碼如下:

static const struct input_device_id *input_match_device(const struct input_device_id *id, struct input_dev *dev)

{

         int i;

         for (; id->flags || id->driver_info; id++) {

                   if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)

                            if (id->bustype != dev->id.bustype)

                                     continue;

                   if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)

                            if (id->vendor != dev->id.vendor)

                                     continue;

                   if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)

                            if (id->product != dev->id.product)

                                     continue;

                   if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)

                            if (id->version != dev->id.version)

                                     continue;

                   MATCH_BIT(evbit, EV_MAX);

                   MATCH_BIT(,, KEY_MAX);

                   MATCH_BIT(relbit, REL_MAX);

                   MATCH_BIT(absbit, ABS_MAX);

                   MATCH_BIT(mscbit, MSC_MAX);

                   MATCH_BIT(ledbit, LED_MAX);

                   MATCH_BIT(sndbit, SND_MAX);

                   MATCH_BIT(ffbit, FF_MAX);

                   MATCH_BIT(swbit, SW_MAX);

                   return id;

         }

         return NULL;

}

 

MATCH_BIT宏的定義如下:

#define MATCH_BIT(bit, max) \

                   for (i = 0; i < BITS_TO_LONGS(max); i++) \

                            if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \

                                     break; \

                   if (i != BITS_TO_LONGS(max)) \

                            continue;

由此看到。在id->flags中定義了要匹配的項。定義INPUT_DEVICE_ID_MATCH_BUS。則是要比較input device和input handler的總線類型。 INPUT_DEVICE_ID_MATCH_VENDOR,INPUT_DEVICE_ID_MATCH_PRODUCT,INPUT_DEVICE_ID_MATCH_VERSION 分別要求設備廠商。設備號和設備版本.

 

如果id->flags定義的類型匹配成功。或者是id->flags沒有定義,就會進入到MATCH_BIT的匹配項了.從 MATCH_BIT宏的定義可以看出。只有當iput device和input handler的id成員在evbit, keybit,… swbit項相同纔會匹配成功。而且匹配的順序是從evbit, keybit到swbit.只要有一項不同,就會循環到id中的下一項進行比較.

 

簡而言之,註冊input device的過程就是爲input device設置默認值(初始化),並將其掛到input_dev_list鏈表中.然後與掛載在input_handler_list中的 handler相匹配。如果匹配成功,就會調用handler的connect函數.

 

四:handler註冊分析

Handler註冊的接口如下所示:

int input_register_handler(struct input_handler *handler)

{

         struct input_dev *dev;

         int retval;

         retval = mutex_lock_interruptible(&input_mutex);

         if (retval)

                   return retval;

         INIT_LIST_HEAD(&handler->h_list);

         if (handler->fops != NULL) {

                   if (input_table[handler->minor >> 5]) {

                            retval = -EBUSY;

                            goto out;

                   }

                   input_table[handler->minor >> 5] = handler;

         }

         list_add_tail(&handler->node, &input_handler_list);

         list_for_each_entry(dev, &input_dev_list, node)

                   input_attach_handler(dev, handler);

         input_wakeup_procfs_readers();

out:

         mutex_unlock(&input_mutex);

         return retval;

}

handler->minor表示對應input設備節點的次設備號.以handler->minor右移五位做爲索引值插入到input_table[ ]中..之後再來分析input_talbe[ ]的作用.

 

然後將handler掛到input_handler_list中.然後將其與掛在input_dev_list中的input device匹配.這個過程和input device的註冊有相似的地方.都是註冊到各自的鏈表,.然後與另外一條鏈表的對象相匹配.

五:handle的註冊

int input_register_handle(struct input_handle *handle)

{

         struct input_handler *handler = handle->handler;

         struct input_dev *dev = handle->dev;

         int error;

         /*

         * We take dev->mutex here to prevent race with

         * input_release_device().

         */

         error = mutex_lock_interruptible(&dev->mutex);

         if (error)

                   return error;

         list_add_tail_rcu(&handle->d_node, &dev->h_list);

         mutex_unlock(&dev->mutex);

         synchronize_rcu();

         /*

         * Since we are supposed to be called from ->connect()

         * which is mutually exclusive with ->disconnect()

         * we can't be racing with input_unregister_handle()

         * and so separate lock is not needed here.

         */

         list_add_tail(&handle->h_node, &handler->h_list);

         if (handler->start)

                   handler->start(handle);

         return 0;

}

在這個函數裏所做的處理其實很簡單.將handle掛到所對應input device的h_list鏈表上. 再將handle掛到對應的handler的h_list鏈表上.如果handler定義了start函數,將調用之.

 

到這裏,我們已經看到了input device, handler和handle是怎麼關聯起來的了.以圖的方式總結如下:

但這個圖上顯示: 在Handler->event中調用input_register_handle(),但是我在code裏面看到

/**
* input_register_handle - register a new input handle
* @handle: handle to register
*
* This function puts a new input handle onto device's
* and handler's lists so that events can flow through
* it once it is opened using input_open_device().
*
* This function is supposed to be called from handler's
* connect() method.
*/

建議在connect中調用input_register_handle(),這裏也是有點疑問的。

六:event事件的處理

在開始的時候曾以linux kernel文檔中自帶的代碼作分析.提出了幾個事件上報的API. 這些API其實都是input_event()的封裝.代碼如下:

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

{

         unsigned long flags;

         //判斷設備是否支持這類事件

         if (is_event_supported(type, dev->evbit, EV_MAX)) {

                   spin_lock_irqsave(&dev->event_lock, flags);

                   //利用鍵盤輸入來調整隨機數產生器

                   add_input_randomness(type, code, value);

                   input_handle_event(dev, type, code, value);

                   spin_unlock_irqrestore(&dev->event_lock, flags);

         }

}

首先,先判斷設備產生的這個事件是否合法.如果合法,流程轉入到input_handle_event()中.

代碼如下:

static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

{

         int disposition = INPUT_IGNORE_EVENT;

         switch (type) {

         case EV_SYN:

                   switch (code) {

                   case SYN_CONFIG:

                            disposition = INPUT_PASS_TO_ALL;

                            break;

                   case SYN_REPORT:

                            if (!dev->sync) {

                                     dev->sync = 1;

                                     disposition = INPUT_PASS_TO_HANDLERS;

                            }

                            break;

                   }

                   break;

         case EV_KEY:

                   //判斷按鍵值是否被支持

                   if (is_event_supported(code, dev->keybit, KEY_MAX) &&

                       !!test_bit(code, dev->key) != value) {

                            if (value != 2) {

                                     __change_bit(code, dev->key);

                                     if (value)

                                               input_start_autorepeat(dev, code);

                            }

                            disposition = INPUT_PASS_TO_HANDLERS;

                   }

                   break;

         case EV_SW:

                   if (is_event_supported(code, dev->swbit, SW_MAX) &&

                       !!test_bit(code, dev->sw) != value) {

                            __change_bit(code, dev->sw);

                            disposition = INPUT_PASS_TO_HANDLERS;

                   }

                   break;

         case EV_ABS:

                   if (is_event_supported(code, dev->absbit, ABS_MAX)) {

                            value = input_defuzz_abs_event(value,

                                               dev->abs[code], dev->absfuzz[code]);

                            if (dev->abs[code] != value) {

                                     dev->abs[code] = value;

                                     disposition = INPUT_PASS_TO_HANDLERS;

                            }

                   }

                   break;

         case EV_REL:

                   if (is_event_supported(code, dev->relbit, REL_MAX) && value)

                            disposition = INPUT_PASS_TO_HANDLERS;

                   break;

         case EV_MSC:

                   if (is_event_supported(code, dev->mscbit, MSC_MAX))

                            disposition = INPUT_PASS_TO_ALL;

                   break;

         case EV_LED:

                   if (is_event_supported(code, dev->ledbit, LED_MAX) &&

                       !!test_bit(code, dev->led) != value) {

                            __change_bit(code, dev->led);

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_SND:

                   if (is_event_supported(code, dev->sndbit, SND_MAX)) {

                            if (!!test_bit(code, dev->snd) != !!value)

                                     __change_bit(code, dev->snd);

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_REP:

                   if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {

                            dev->rep[code] = value;

                            disposition = INPUT_PASS_TO_ALL;

                   }

                   break;

         case EV_FF:

                   if (value >= 0)

                            disposition = INPUT_PASS_TO_ALL;

                   break;

         case EV_PWR:

                   disposition = INPUT_PASS_TO_ALL;

                   break;

         }

         if (type != EV_SYN)

                   dev->sync = 0;

         if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)

                   dev->event(dev, type, code, value);

         if (disposition & INPUT_PASS_TO_HANDLERS)

                   input_pass_event (dev, type, code, value);

}

在這裏, 忽略掉具體事件的處理. 到最後,如果該事件需要input device來完成的,就會將disposition設置成INPUT_PASS_TO_DEVICE.如果需要handler來完成的,就將 dispostion設爲INPUT_PASS_TO_HANDLERS.如果需要兩者都參與,將disposition設置爲 INPUT_PASS_TO_ALL.

需要輸入設備參與的,回調設備的event函數.如果需要handler參與的.調用input_pass_event().代碼如下:

static void input_pass_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

{

         struct input_handle *handle;

         rcu_read_lock();

         handle = rcu_dereference(dev->grab);

         if (handle)

                   handle->handler->event(handle, type, code, value);

         else

                   list_for_each_entry_rcu(handle, &dev->h_list, d_node)

                            if (handle->open)

                                     handle->handler->event(handle,

                                                                 type, code, value);

         rcu_read_unlock();

}

如果input device被強制指定了handler,則調用該handler的event函數.

結合handle註冊的分析.我們知道.會將handle掛到input device的h_list鏈表上.

如果沒有爲input device強制指定handler.就會遍歷input device->h_list上的handle成員.如果該handle被打開,則調用與輸入設備對應的handler的event()函數.注意,只有在handle被打開的情況下才會接收到事件.

 

另外,輸入設備的handler強制設置一般是用帶EVIOCGRAB標誌的ioctl來完成的.如下是發圖的方示總結evnet的處理過程:

到此,已經分析了input device,handler和handle的註冊過程以及事件的上報和處理.下面以evdev爲實例做分析.來貫穿理解一下整個過程.

七:evdev概述

Evdev對應的設備節點一般位於/dev/input/event0 ~ /dev/input/event4.理論上可以對應32個設備節點.分別代表被handler匹配的32個input device.

 

可以用cat /dev/input/event0.然後移動鼠標或者鍵盤按鍵就會有數據輸出(兩者之間只能選一.因爲一個設備文件只能關能一個輸入設備).還可以往這個文件裏寫數據,使其產生特定的事件.這個過程之後再詳細分析.

爲了分析這一過程,必須從input子系統的初始化說起.

八:input子系統的初始化

Input子系統的初始化函數爲input_init().代碼如下:

static int __init input_init(void)

{

         int err;

         err = class_register(&input_class);

         if (err) {

                   printk(KERN_ERR "input: unable to register input_dev class\n");

                   return err;

         }

         err = input_proc_init();

         if (err)

                   goto fail1;

         err = register_chrdev(INPUT_MAJOR, "input", &input_fops);

         if (err) {

                   printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);

                   goto fail2;

         }

         return 0;

fail2:        input_proc_exit();

fail1:        class_unregister(&input_class);

         return err;

}

在這個初始化函數裏,先註冊了一個名爲”input”的類.所有input device都屬於這個類.在sysfs中表現就是.所有input device所代表的目錄都位於/dev/class/input下面.

然後調用input_proc_init()在/proc下面建立相關的交互文件.

最後調用register_chrdev()註冊了主設備號爲INPUT_MAJOR(13).次設備號爲0~255的字符設備.它的操作指針爲input_fops.

這裏可以看到.所有主設備號13的字符設備的操作最終都會轉入到input_fops中.在前面分析的/dev/input/event0~/dev/input/event4的主設備號爲13.操作也不例外的落在了input_fops中.

Input_fops定義如下:

static const struct file_operations input_fops = {

         .owner = THIS_MODULE,

         .open = input_open_file,

};

打開文件所對應的操作函數爲input_open_file.代碼如下示:

static int input_open_file(struct inode *inode, struct file *file)

{

         struct input_handler *handler = input_table[iminor(inode) >> 5];

         const struct file_operations *old_fops, *new_fops = NULL;

         int err;

         /* No load-on-demand here? */

         if (!handler || !(new_fops = fops_get(handler->fops)))

                   return -ENODEV;

iminor(inode)爲打開文件所對應的次設備號.input_table是一個struct input_handler全局數組.先將設備結點的次設備號右移5位做爲索引值到input_table中取對應項.這裏可以看到.一 個handler代表1<<5個設備節點(因爲在input_table中取值是以次備號右移5位爲索引的.即低5位相同的次備號對應的是同一 個索引).終於看到了input_talbe大顯身手的地方了.input_talbe[ ]中取值和input_talbe[ ]的賦值,這兩個過程是相對應的.

在input_table中找到對應的handler之後,就會檢驗這個handler是否存,是否帶有fops文件操作集.如果沒有.則返回一個設備不存在的錯誤.

         /*

         * That's _really_ odd. Usually NULL ->open means "nothing special",

         * not "no device". Oh, well...

         */

         if (!new_fops->open) {

                   fops_put(new_fops);

                   return -ENODEV;

         }

         old_fops = file->f_op;

         file->f_op = new_fops;

         err = new_fops->open(inode, file);

         if (err) {

                   fops_put(file->f_op);

                   file->f_op = fops_get(old_fops);

         }

         fops_put(old_fops);

         return err;

}

然後將handler中的fops替換掉當前file的fops.如果handler的fops中有open()函數,則調用它.(如果以evdev爲例的話,這裏的open()函數應該就是evdev_open()函數)

九:evdev的初始化

Evdev的模塊初始化函數爲evdev_init().代碼如下:

static int __init evdev_init(void)

{

         return input_register_handler(&evdev_handler);

}

它調用了input_register_handler註冊了一個handler.

注意到,在這裏evdev_handler中定義的minor爲EVDEV_MINOR_BASE(64).也就是說evdev_handler所表示的設備文件範圍爲(13,64)~(13,64+32).

 

從之前的分析知道.匹配成功的關鍵在於handler中的blacklist和id_talbe. Evdev_handler的id_table定義如下:

static const struct input_device_id evdev_ids[] = {

         { .driver_info = 1 },     /* Matches all devices */

         { },                       /* Terminating zero entry */

};

它沒有定義flags.也沒有定義匹配屬性值.這個handler是匹配所有input device的.匹配成功之後會調用handler->connect函數.

在Evdev_handler中,該成員函數如下所示:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)

{

         struct evdev *evdev;

         int minor;

         int error;

         for (minor = 0; minor < EVDEV_MINORS; minor++)

                   if (!evdev_table[minor])

                            break;

         if (minor == EVDEV_MINORS) {

                   printk(KERN_ERR "evdev: no more free evdev devices\n");

                   return -ENFILE;

         }

EVDEV_MINORS定義爲32.表示evdev_handler所表示的32個設備文件.evdev_talbe是一個struct evdev類型的數組.struct evdev是模塊使用的封裝結構.在接下來的代碼中可以看到這個結構的使用.

這一段代碼的在evdev_talbe找到爲空的那一項.minor就是數組中第一項爲空的序號.

         evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);

         if (!evdev)

                   return -ENOMEM;

         INIT_LIST_HEAD(&evdev->client_list);

         spin_lock_init(&evdev->client_lock);

         mutex_init(&evdev->mutex);

         init_waitqueue_head(&evdev->wait);

         snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);

         evdev->exist = 1;

         evdev->minor = minor;

         evdev->handle.dev = input_get_device(dev);

         evdev->handle.name = evdev->name;

         evdev->handle.handler = handler;

         evdev->handle.private = evdev;

接下來,分配了一個evdev結構,並對這個結構進行初始化.這個結構封裝了一個handle結構,這結構與之前所討論的handler是不相同的.注意有一個字母的差別哦.可以把handle看成是handler和input device的信息集合體.在這個結構裏集合了匹配成功的handler和input device

         strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));

         evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);

         evdev->dev.class = &input_class;

         evdev->dev.parent = &dev->dev;

         evdev->dev.release = evdev_free;

         device_initialize(&evdev->dev);

在這段代碼裏主要完成evdev封裝的device的初始化.注意在這裏,使它所屬的類指向input_class.這樣在/sysfs中創建的設備目錄就會在/sys/class/input/下面顯示.

         error = input_register_handle(&evdev->handle);

         if (error)

                   goto err_free_evdev;

         error = evdev_install_chrdev(evdev);

         if (error)

                   goto err_unregister_handle;

         error = device_add(&evdev->dev);

         if (error)

                   goto err_cleanup_evdev;

         return 0;

err_cleanup_evdev:

         evdev_cleanup(evdev);

err_unregister_handle:

         input_unregister_handle(&evdev->handle);

err_free_evdev:

         put_device(&evdev->dev);

         return error;

}

註冊handle,如果成功,那麼調用evdev_install_chrdev將evdev_table的minor項指向evdev. 然後將evdev->device註冊到sysfs.如果失敗,則進行相關的錯誤處理.

萬事俱備了,但是要接收事件,還得要等”東風”.這個”東風”就是要打開相應的handle.這個打開過程是在文件的open()中完成的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章