閒聊linux中的input裝置(轉)

weixin_34219944發表於2012-07-29

 轉自:http://blog.csdn.net/lmm670/article/details/6080998

  用過linux的哥們都知道,linux所有的裝置都是以檔案的形式實現的,要訪問一個裝置,我們只需要以open、read、write的形式對裝置的進行操作就可以了。在linux系統的/dev目錄下,羅列了當前系統支援的所有裝置。執行 ls /dev一下,著實嚇了一大跳,

[root@localhost ~]# ls /dev

adsp        full     midi      ram9        tty15  tty42  ttyS3

agpgart     fuse     mixer     ramdisk     tty16  tty43  urandom

audio       hpet     net       random      tty17  tty44  usbdev1.1_ep00

bsg         hvc0     null      root        tty18  tty45  usbdev1.1_ep81

bus         hvc1     nvram     rtc         tty19  tty46  usbmon0

cdrom       hvc2     oldmem    scd0        tty2   tty47  usbmon1

console     hvc3     parport0  sda         tty20  tty48  vcs

core        hvc4     parport1  sda1        tty21  tty49  vcs1

disk        hvc5     parport2  sda2        tty22  tty5   vcs2

dmmidi      hvc6     parport3  sda3        tty23  tty50  vcs3

dsp         hvc7     port      sequencer   tty24  tty51  vcs4

fd          initctl  ppp       sequencer2  tty25  tty52  vcs5

fd0         input    ptmx      sg0         tty26  tty53  vcs6

fd0u1040    kmsg     pts       sg1         tty27  tty54  vcs7

fd0u1120    log      ram       shm         tty28  tty55  vcs8

fd0u1440    loop0    ram0      snapshot    tty29  tty56  vcsa

fd0u1600    loop1    ram1      snd         tty3   tty57  vcsa1

fd0u1680    loop2    ram10     sr0         tty30  tty58  vcsa2

fd0u1722    loop3    ram11     stderr      tty31  tty59  vcsa3

fd0u1743    loop4    ram12     stdin       tty32  tty6   vcsa4

fd0u1760    loop5    ram13     stdout      tty33  tty60  vcsa5

fd0u1840    loop6    ram14     systty      tty34  tty61  vcsa6

fd0u1920    loop7    ram15     tty         tty35  tty62  vcsa7

fd0u360     lp0      ram2      tty0        tty36  tty63  vcsa8

fd0u720     lp1      ram3      tty1        tty37  tty7   X0R

fd0u800     lp2      ram4      tty10       tty38  tty8   XOR

fd0u820     lp3      ram5      tty11       tty39  tty9   zero

fd0u830     MAKEDEV  ram6      tty12       tty4   ttyS0

floppy      mapper   ram7      tty13       tty40  ttyS1

floppy-fd0  mem      ram8      tty14       tty41  ttyS2

 

這麼多裝置那麼管理起來是不是很麻煩,linux核心的開發者智商自然在你我之上,他們把所有的這些裝置歸為三大類,即平時我們熟悉的字元裝置、塊裝置、網路裝置。執行 ls –l /dev(這裡我只取一部分顯示資訊)

crw-rw----+ 1 root   root    14,  12 12-16 00:57 adsp

crw-------  1 root   root    10, 175 12-16 00:57 agpgart

crw-rw----+ 1 root   root    14,   4 12-16 00:57 audio

drwxr-xr-x  2 root   root         80 12-16 00:57 bsg

drwxr-xr-x  3 root   root         60 12-16 00:57 bus

lrwxrwxrwx  1 root   root          3 12-16 00:57 cdrom -> sr0

crw-------  1 lmm670 root     5,   1 12-16 00:57 console

lrwxrwxrwx  1 root   root         11 12-16 00:57 core -> /proc/kcore

drwxr-xr-x  5 root   root        100 12-16 00:57 disk

crw-rw----  1 root   root    14,   9 12-16 00:57 dmmidi

crw-rw----+ 1 root   root    14,   3 12-16 00:57 dsp

lrwxrwxrwx  1 root   root         13 12-16 00:57 fd -> /proc/self/fd

brw-r-----  1 root   floppy   2,   0 12-16 00:57 fd0

brw-r-----  1 root   floppy   2,  84 12-16 00:57 fd0u1040

brw-r-----  1 root   floppy   2,  88 12-16 00:57 fd0u1120

brw-r-----  1 root   floppy   2,  28 12-16 00:57 fd0u1440

brw-r-----  1 root   floppy   2, 124 12-16 00:57 fd0u1600

brw-r-----  1 root   floppy   2,  44 12-16 00:57 fd0u1680

brw-r-----  1 root   floppy   2,  60 12-16 00:57 fd0u1722

brw-r-----  1 root   floppy   2,  76 12-16 00:57 fd0u1743

brw-r-----  1 root   floppy   2,  96 12-16 00:57 fd0u1760

brw-r-----  1 root   floppy   2, 116 12-16 00:57 fd0u1840

brw-r-----  1 root   floppy   2, 100 12-16 00:57 fd0u1920

brw-r-----  1 root   floppy   2,  12 12-16 00:57 fd0u360

brw-r-----  1 root   floppy   2,  16 12-16 00:57 fd0u720

brw-r-----  1 root   floppy   2, 120 12-16 00:57 fd0u800

brw-r-----  1 root   floppy   2,  52 12-16 00:57 fd0u820

brw-r-----  1 root   floppy   2,  68 12-16 00:57 fd0u830

lrwxrwxrwx  1 root   root          3 12-16 00:57 floppy -> fd0

lrwxrwxrwx  1 root   root          3 12-16 00:57 floppy-fd0 -> fd0

crw-rw-rw-  1 root   root     1,   7 12-16 00:57 full

crw-rw----  1 root   fuse    10, 229 12-16 00:57 fuse

crw-rw----  1 root   root    10, 228 12-16 00:57 hpet

crw-rw----  1 root   uucp   229,   0 12-16 00:57 hvc0

crw-rw----  1 root   uucp   229,   1 12-16 00:57 hvc1

crw-rw----  1 root   uucp   229,   2 12-16 00:57 hvc2

crw-rw----  1 root   uucp   229,   3 12-16 00:57 hvc3

crw-rw----  1 root   uucp   229,   4 12-16 00:57 hvc4

crw-rw----  1 root   uucp   229,   5 12-16 00:57 hvc5

crw-rw----  1 root   uucp   229,   6 12-16 00:57 hvc6

crw-rw----  1 root   uucp   229,   7 12-16 00:57 hvc7

prw-------  1 root   root          0 12-16 00:58 initctl

drwxr-xr-x  3 root   root        200 12-16 00:57 input

crw-rw----  1 root   root     1,  11 12-16 00:57 kmsg

srw-rw-rw-  1 root   root          0 12-16 00:58 log

brw-r-----  1 root   disk     7,   0 12-16 00:57 loop0

大家可以看到,每一行的第一個字母,代表著此檔案的型別。c表示字元裝置,b表示塊裝置,s表示網路裝置,細心的哥們會問,不是說只有三類裝置嗎,怎麼還有其他型別開頭的呢?比如d、l等等。對不起,這裡講的是檔案型別,d表示是一個目錄檔案,l表示一個連結檔案。至於這三者之間的區別,我就不在這囉嗦了,Google一下一大堆。我要強調的是,無論上面三個裝置中的任何一種裝置,要想在linux實現它的裝置驅動,首先要對它進行一系列的初始化工作,然後需給它提供一個裝置操作集合(或者更簡單一點理解:介面函式),用來提供給我們的上層程式進行訪問,比如open,read,write等等。要不然,我要你這個裝置驅動幹嘛。在字元裝置驅動中,我們的操作集函式是這樣的

struct file_operations {

       struct module *owner;

       loff_t (*llseek) (struct file *, loff_t, int);

       ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

       ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

       ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       int (*readdir) (struct file *, void *, filldir_t);

       unsigned int (*poll) (struct file *, struct poll_table_struct *);

       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

       int (*mmap) (struct file *, struct vm_area_struct *);

       int (*open) (struct inode *, struct file *);

       int (*flush) (struct file *, fl_owner_t id);

       int (*release) (struct inode *, struct file *);

       int (*fsync) (struct file *, struct dentry *, int datasync);

       int (*aio_fsync) (struct kiocb *, int datasync);

       int (*fasync) (int, struct file *, int);

       int (*lock) (struct file *, int, struct file_lock *);

       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

       int (*check_flags)(int);

       int (*flock) (struct file *, int, struct file_lock *);

       ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

       ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

       int (*setlease)(struct file *, long, struct file_lock **);

};

 

網名為“賣血去上網”的兄弟要說了,寫一個驅動介面函式怎麼這麼複雜,要提供這麼多介面函式。其實不然,一般的裝置驅動介面函式並不需要把上面的所有都實現,只需要實現裡面的一些:比如我們這裡:

static const struct file_operations evdev_fops = {

       .owner           = THIS_MODULE,

       .read              = evdev_read,

       .write             = evdev_write,

       .poll        = evdev_poll,

       .open             = evdev_open,

       .release    = evdev_release,

       .unlocked_ioctl      = evdev_ioctl,

#ifdef CONFIG_COMPAT

       .compat_ioctl  = evdev_ioctl_compat,

#endif

       .fasync           = evdev_fasync,

       .flush             = evdev_flush

};

 

兄弟們注意了,這個結構體就是前面那個結構體的實現,類似於c++中的類和物件。結構中若干個等式中,我們最終要實現的是右邊的那些函式。這樣做的目的大家都清楚:實現統一介面,增加程式的可移植性。上層程式碼每次open evdev這個裝置的時候最終都會通過file_operations落實到我們的evdev_open函式。魯迅先生曾說過:驅動程式碼寫起來其實並不難,當介面函式多了就變難了。說了一大推好像被忽悠了,怎麼沒提到一點關於input裝置的資訊,關於input裝置我現在只提一句,input裝置的介面函式,linux核心已經為我們寫好了。畢竟時代在進步,核心在更新,魯先生的話也可以改一下了:裝置驅動的實現本身很難,自從有了input裝置子系統,就變得不難了(當然只針對input裝置)。到底何謂input裝置呢?

究竟何謂input裝置,相信武漢跳蚤市場上賣寵物小狗的大媽都能一口答出來,你能不知道麼?對,就是我們傳說中的輸入裝置。說到輸入裝置,相信用過電腦的兄弟都不會陌生了,即按鍵、滑鼠、鍵盤、等一系列需要我們使用者“動手”產生資訊,然後丟給我們聰明絕頂的pc來處理的裝置。前面說了,linux核心input子系統中已經實現了input裝置的介面函式,這使得我們工作量大大的減輕了。我們以akm8973晶片(用於智慧手機指南針的主功能晶片,實際上就一電子羅盤)為例,來簡單看一下寫一個input裝置我們需要做的工作。

首先,在驅動模組載入函式中申請一個input裝置,並告知input子系統它支援哪些事件,如下所示:

akm->input_dev = input_allocate_device();

set_bit(EV_ABS, akm->input_dev->evbit);

input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);

input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0);

input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0);

input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);

input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0);

input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0);

input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0);

input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0);

 

以上這些都是為讓input子系統支援的某些引數而設定的,EV_ABS表示支援絕對值座標,後面都是針對這些座標的一些引數訪問範圍設定。至於為什麼這樣設定,我們繼續往下走,到後面我們就明白了。

接著,在驅動模組函式中註冊輸入裝置:

err = input_register_device(akm->input_dev);

然後,報告發生的一些事件以及對應的座標。

input_report_abs(data->input_dev, ABS_RX, rbuf[0]);

input_report_abs(data->input_dev, ABS_RY, rbuf[1]);

input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);

對應的三個方向的座標值就被驅動記錄下來了。

 

深入裡面跟蹤一下:

static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)

{

       dev->absmin[axis] = min;

       dev->absmax[axis] = max;

       dev->absfuzz[axis] = fuzz;

       dev->absflat[axis] = flat;

 

       dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);

}

 

這個函式用來幹嘛的呢?這個留到以後講,不過你得多個心眼,後面用得到的。

  

新增一個input裝置,我們要做的工作就這些了。接下來我們就可以通過input核心子系統提供的介面函式來處理這些座標值,把把他們傳到使用者空間。

看完這些,讓我想起來了食堂門前那些招行的辦信用卡活動。辦一個信用卡送一個U盾。沒辦法,現在的社會什麼事情都要收費,連上廁所也得給點小費,不給錢不讓進,哪怕你當眾尿褲子。所以更別說我們如此有技術含量的保護大家網上購物安全的U盾了。去銀行辦一個U盾,至少得花個50大洋以上。所以很多像我這樣的哥們,毫不猶豫的辦了一個信用卡。反正就填幾張表,然後就可以免費獲得一個u盾,多好啊。網名為“唐伯虎點蚊香”的兄弟馬上發話了:“這不就和我們的input裝置那個一樣的嗎,我們這些比較懶的傢伙為了避免去完善那些複雜的裝置介面函式集,所以乾脆把它註冊成一個input裝置,所以你就得先申請它,註冊它等一系列預備工作,(就如我們為了u盾而填的那些表格)做好這些之後,我們就實行魯迅先生的拿來主義,直接使用input子系統的的介面函式”。

不過不是什麼裝置都可以註冊成inpunt裝置的。就好比一兄弟隨便拿了一張紙,畫一隻小雞,然後頭上加個光圈,就號稱是唐伯虎的名畫“神鳥鳳凰圖”,然後遞給招行的工作人員說“我表格填好了,給我來一個U盾”,人家會以為這哥們肯定剛從精神病院出來的。

從前一節來看,在linux核心中新增一個input裝置變得很簡單了。我們再也不必須去動手寫那些該死的介面函式了。可是你有沒有想過,是誰讓我們的工作變得這麼簡單了呢?答案是linux核心中的input core。她總是那麼痴情,默默地不求回報地為你做許許多多的事情,在你背後默默的支援你愛著你。是的,你所想到的大多數事情,我們的input core都已經為你做好。除了感動,我們還能說什麼呢?(input core對應的實體在linux核心原始碼目錄linux-2.6.29/drivers/input/input.c檔案)

在正式接觸我們可愛的input core之前,有必要了解一下幾個重要的結構體,這幾個結構體是我們這個故事的主體。

第一個資料結構 struct input_dev。悟性高的哥們馬上就會想到,它就是我們input 裝置在linux核心中的模擬,即裡面記錄了一個input裝置的所有資訊。定義於linux-2.6.29/include/linux/input.h中

struct input_dev {

 const char *name;

       const char *phys;

       const char *uniq;

       struct input_id id;

 

       unsigned long evbit[BITS_TO_LONGS(EV_CNT)];

       unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];

       unsigned long relbit[BITS_TO_LONGS(REL_CNT)];

       unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];

       unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];

       unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];

       unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];

       unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];

       unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

 

       unsigned int keycodemax;

       unsigned int keycodesize;

       void *keycode;

       int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);

       int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

 

       struct ff_device *ff;

 

       unsigned int repeat_key;

       struct timer_list timer;

 

       int sync;

 

       int abs[ABS_MAX + 1];

       int rep[REP_MAX + 1];

 

       unsigned long key[BITS_TO_LONGS(KEY_CNT)];

       unsigned long led[BITS_TO_LONGS(LED_CNT)];

       unsigned long snd[BITS_TO_LONGS(SND_CNT)];

       unsigned long sw[BITS_TO_LONGS(SW_CNT)];

 

       int absmax[ABS_MAX + 1];

       int absmin[ABS_MAX + 1];

       int absfuzz[ABS_MAX + 1];

       int absflat[ABS_MAX + 1];

 

       int (*open)(struct input_dev *dev);

       void (*close)(struct input_dev *dev);

       int (*flush)(struct input_dev *dev, struct file *file);

       int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

 

       struct input_handle *grab;

 

       spinlock_t event_lock;

       struct mutex mutex;

 

       unsigned int users;

       int going_away;

 

       struct device dev;

 

       struct list_head  h_list;

       struct list_head  node;

};

 

很強大的一個結構體,因為她把所有的input裝置的資訊都考慮到了,真的是很無私。不過對於我們的akm驅動來說只需要關注幾個小細節,結構中的加粗部分。unsigned longevbit[BITS_TO_LONGS(EV_CNT)]表示此input裝置支援的事件,比如前面的第二節中的set_bit(EV_ABS, akm->input_dev->evbit)設定input_dev->evbit中的相應位讓它支援絕對值座標。類似的還有以下這些事件:EV_KEY -按鍵, EV_REL -相對座標EV_ABS -絕對座標,EV_LED -  LED,EV_FF- 力反饋。unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];設定相應的位以支援某一類絕對值座標。比如第二節中的input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);它的函式體如下:

static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat)

{

       dev->absmin[axis] = min;

       dev->absmax[axis] = max;

       dev->absfuzz[axis] = fuzz;

       dev->absflat[axis] = flat;

 

       dev->absbit[BIT_WORD(axis)] |= BIT_MASK(axis);

}

 

表示支援絕對值x座標,並設定它在座標系中的最大值和最小值,以及干擾值和平焊位置等。

struct list_head h_list;表示的是和該裝置相關的所有input handle的結構體連結串列(input handle為何物下文馬上會講到)。struct list_head node;所有input裝置組成的連結串列結構(後面將會知道它對應於input_dev_list)。

 

Ok 馬上進入第二個結構體struct input_handler(還是來自linux-2.6.29/include/linux/input.h)

struct input_handler {

 

       void *private;

 

       void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);

       int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);

       void (*disconnect)(struct input_handle *handle);

       void (*start)(struct input_handle *handle);

 

       const struct file_operations *fops;

       int minor;

       const char *name;

 

       const struct input_device_id *id_table;

       const struct input_device_id *blacklist;

 

       struct list_head  h_list;

       struct list_head  node;

};

 

顧名思義:用來處理input裝置的一個結構體。struct list_head h_list表示的是和該裝置相關的所有input handle的結構體連結串列和前面那個一樣;struct list_head       node所有input_handle組成的結構體連連結串列(後面將會知道它對應於input_handler_list)。每一個input裝置在註冊時,他都會遍歷input_handler_list連結串列上的每一個input_handler,去找尋他心中的那個她,同理每一個input_handler在註冊時,他也會去input_dev_list上找尋那個屬於他的她。有時候事情往往不會那麼盡如人意,當input_handler還沒出生時,你這個input_dev就一直在那等吧,等到天荒地老,等到海枯石爛。最後來一句,我等到花兒也謝了,你丫到底還來不來啊。注意這裡的input_handler和input_dev並不是一一對應的關係,有時一個input_handler對應好幾個input_dev。於是乎,作為看程式碼的我就在想,linux核心開發者的思想怎麼這麼不單純呢,這不明擺著教育我們搞一夫多妻制嗎,不過管怎樣,還是得記住公司的企業文化,本分點。如果你丫說你同時擁有兩個馬子,我將會無情的向你丟擲那句話:“出來混,遲早要還的!”。

前面多次提到那個input_handle(注意區別input_handler),她到底是何方神聖。好吧,就讓我們來一層一層揭開她那神祕的面紗,當你第一次看到她完完全全展現在你面前時,那時候你的滿足感和興奮度和她的害羞度是成正比的。

同樣來自linux-2.6.29/include/linux/input.h

struct input_handle {

 

       void *private;

 

       int open;

       const char *name;

 

       struct input_dev *dev;

       struct input_handler *handler;

 

       struct list_head  d_node;

       struct list_head  h_node;

};

 

怎麼啦?是不是很失望,原來就這麼回事啊。嗯,沒錯,兄弟,就這麼回事。人往往都這樣,得到了某樣東西,想想覺得就那麼回事,沒得到呢,那叫一個好奇,那叫一個盼望。好了既然看到她的廬山真面目了,就坦然面對她,作為一個負責的男人,我還是來好好研究一下。

Input_Handle其實也好理解,它就是input_dev和 input_handler粘合劑,通過Input_Handle這麼一攪和,input_dev就和 input_handler發生了點關係,至於什麼樣的關係我們後文將會知道的一清二楚。struct input_dev *dev對應的input裝置。struct input_handler *handler對應該裝置的handler。struct list_head d_node和struct list_head h_node則分別關聯上前面兩個結構體中的struct list_head h_list。

 

對應input core,前面我一直在誇她的好,對於一個大家都不認識的傢伙,我這樣說她,是不是顯的特虛,好了,為了證明她並不是那麼的虛,我不得不拿出前面第二節中出現過的兩行程式碼來看看:

akm->input_dev = input_allocate_device();

err = input_register_device(akm->input_dev);

沒有錯,這正是我們要把akm實現為一個input 裝置的僅有的幾行程式碼中的兩行。

第一行,申請一個input裝置:在核心中分配相應的記憶體空間,並初始化它。

第二行,把這個input裝置註冊到linux核心中,從此這個裝置在核心中生根發芽,快樂幸福的和他的handler過著屬於自己的小日子(雖然handler不一定屬於她一個人,不過她不在乎)。

作為一個男人,我還是得負責任為我們的input core說明一下,input_allocate_device()和input_register_device();都來自我們的 input core。現在知道她的偉大了吧。你看看,我們寫一個input裝置驅動本來就那麼幾行程式碼,而這僅有的幾行程式碼中還呼叫了來自核心的函式。我不得不說,input core ,你真給力。

好了,我們先來研究一下第一個函式 input_allocate_device()。(linux核心原始碼目錄linux-2.6.29/drivers/input/input.c檔案中)

struct input_dev *input_allocate_device(void)

{

       1 struct input_dev *dev;

       2 dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);

       3 if (dev) {

       4     dev->dev.type = &input_dev_type;

       5     dev->dev.class = &input_class;

       6     device_initialize(&dev->dev);

       7     mutex_init(&dev->mutex);

       8     spin_lock_init(&dev->event_lock);

       9     INIT_LIST_HEAD(&dev->h_list);

       10    INIT_LIST_HEAD(&dev->node);

 

       12    __module_get(THIS_MODULE);

       }

 

       15 return dev;

}

 

第1行,申明一個input_dev結構體變數;

第2行,kzalloc()一個陌生的傢伙,它其實等於kmalloc+memset。看到kmalloc相信大家覺得眼熟,沒錯,他的弟弟就是malloc,而kmalloc對應於核心空間的記憶體分配函式。好了,第二行程式碼的意思相信大家也明白了:在核心空間開闢一段大小為sizeof(struct input_dev)大小的記憶體區,並把它初始化為0。後面的GFP_KERNEL為分配的標誌,即為一個常規的記憶體分配,類似的還有GFP_DMA,表示分配的記憶體能供dma使用,GFP_ATOMIC分配記憶體時,不允許睡眠,一般用在中斷中,大家想想,如果在一箇中斷處理程式中,使用GFP_KERNEL標誌分配記憶體,發現記憶體不足,就一直睡在那兒等待,你受得了嗎(敲了一下鍵盤,發現過了2分鐘系統才反應過來,相信這嚴重影響到了你和漂亮mm網聊的興趣了)所以在中斷處理函式中我們不能使用GFP_KERNEL標誌分配記憶體。

分配到記憶體後,用dev指向這段記憶體。

第3行,判斷記憶體分配是否成功,若成功,則進入到4—12行的對dev的初始化工作。否則,我們們啥也別說了,說了也白說,退出,走人。

第4到6行,對input裝置的內嵌dev裝置進行初始化。

第7到8行,初始化該dev的互斥量和鎖,為防止對dev的併發訪問。

第9、10兩行,對input裝置中的兩個連結串列結構頭進行初始化。

好了,此函式分析到此,一旦順利進行,則該input裝置已經出落成來一個亭亭玉立的美少女了,注意了,哥們,她現在還是單身的。接下來要做的事,嘿嘿,想必大家會比我更清楚了。網名為“洞房不敗”的兄弟開口了:“難道是要把她賣出去”,兄弟高雅點行不,不叫賣,那叫嫁.

 

故事真正要進入高潮部分了。接下來我們來了解一下,我們前面那位美少女是在哪兒被嫁了出去的。

真正的執行者乃input_register_device()函式。

同樣來自input core中,我們來看一下她的全貌:

int input_register_device(struct input_dev *dev)

{

       1 static atomic_t input_no = ATOMIC_INIT(0);

       2 struct input_handler *handler;

       3 const char *path;

       4 int error;

 

       5 __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.

        */

 

       6 init_timer(&dev->timer);

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

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

       9     dev->timer.function = input_repeat_key;

       10    dev->rep[REP_DELAY] = 250;

       11    dev->rep[REP_PERIOD] = 33;

       12 }

 

       13 if (!dev->getkeycode)

       14    dev->getkeycode = input_default_getkeycode;

 

       15 if (!dev->setkeycode)

       16    dev->setkeycode = input_default_setkeycode;

 

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

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

 

       19 error = device_add(&dev->dev);

       20 if (error)

       21    return error;

 

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

       23 printk(KERN_INFO "input: %s as %s/n",

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

       25 kfree(path);

 

       26 error = mutex_lock_interruptible(&input_mutex);

       27 if (error) {

       28   device_del(&dev->dev);

       29    return error;

       }

 

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

 

       31 list_for_each_entry(handler, &input_handler_list, node)

       32    input_attach_handler(dev, handler);

 

       33 input_wakeup_procfs_readers();

 

       34 mutex_unlock(&input_mutex);

 

       35 return 0;

}

 

這裡我在給程式碼表明程式碼行數時,為了方便,只標出了那些有程式碼的行,對於空行,就略了。

1-4行,初始化一些基本變數,以備後文使用。這裡我們重點了解一下static atomic_t input_no = ATOMIC_INIT(0); 這裡atomic_t表明一個原子變數。記住對於原子變數,不能被併發的訪問,比如有兩個執行緒,都是想讓nput_no加1操作,如果input_no初值為0,當兩執行緒序列訪問,當然可以得到我們想要的值2,可以如果是併發呢,兩個執行緒同時訪問它,得到的初值都為0並同時為她加1,我們最後看到的值就只增加了一次,即為1。原子變數很好的解決了此類問題。每次只能讓一個執行緒獲得它,進行操作,釋放,然後第二個執行緒訪問,然後釋放,是不是讓你想到了自旋鎖機制,聰明,他就是一個簡化版的自旋鎖,不過操作的物件是一個變數,而非一段臨界區程式碼。

5行,設定EV_SYN,讓所有的裝置都支援它。

6-18行是跟按鍵有關的,與我們這裡無關。暫且不論。

19行 error = device_add(&dev->dev);和4小結中的device_initialize(&dev->dev);夫唱婦和,共同讓我們的input裝置中的內嵌dev結構在系統中註冊,並在sysfs檔案系統中形成相應的檔案層次結構。

20-25行,列印此裝置在檔案系統中的路徑。

26、34兩行程式碼就是為了防止其中間的臨界區程式碼被併發訪問。還記得他在哪兒初始化的嗎?正是在我們第4節的input_allocate_device函式中,原句如下:mutex_init(&dev->mutex);為什麼要使用互斥體呢,這個問題留到後面分析中間的臨界區程式碼再來討論。這裡mutex_lock_interruptible()用mutex_lock取代,前者表示該可被訊號打斷,後者則顯然不行。

27-29行程式碼,如果該程式沒有獲取到互斥量,說明此時已有另外一個程式佔有了她。哥們你天生沒那命,你前面的所有工作,什麼買花,什麼請客看電影,都白忙活了,死心吧,放棄一切(device_del(&dev->dev);),回老家,然後慢慢的躺在家裡邊睡邊等,等待前面那位哥們,等待他放手的那一天……

30-33行,很幸運,她還是獨身,也等著你的出現,所以情同意和,兩小無猜,繼續牽手往下走…30行,把該裝置新增到input_dev_list中,31行 遍歷系統中所有已註冊的handler連結串列即input_handler_list,從中找到和自己的Mr.Right..ok,一切順利函式返回,成功把該input裝置嫁出去,任務完成,打道回府。網名叫“西門吹牛”的兄弟要說了,好像漏了點什麼?哦,對!前面說到,為什麼要使用互斥量了,來防止併發訪問這段程式碼呢?如果此時你還來問我這個問題,我就要罵你,你丫到底有沒有在看我文件?

 

看到這裡,有的哥們要生氣了,怎麼我們美麗的input裝置被嫁出去,居然一下就忽悠過去了,都不詳細描述一下她的具體被嫁過程,她到底嫁給哪個handler了?又是怎麼相中的?相中後他們兩又一起做了些什麼?好了,為了滿足這位兄弟的慾望,我們來詳細閱讀一下前面那個input_attach_handler(dev, handler)函式。就是在這個函式中,發生了所有該發生的事情。

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

{

       1 const struct input_device_id *id;

       2 int error;

 

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

       4     return -ENODEV;

 

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

       6 if (!id)

       7     return -ENODEV;

 

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

       9 if (error && error != -ENODEV)

       10    printk(KERN_ERR

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

       12           "error: %d/n",

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

 

       14 return error;

}

 

1行,定義一個結構體struct input_device_id的變數id。該結構體是屬於handler的,它為我們的handler提供了一套擇偶標準。不在標準以內了,一律拒之門外,不管你是何方妖女。

2行,定義一個整形變數 error,後面要用得到。

3行,我們的handler應該來頭不小,不是富一代,也至少是個富二代。不僅有前面那一堆擇偶標準,還來一個黑名單列表,對於那些在黑名單以內的裝置,比如說好不容易打聽到一女的,電話一通,那頭突然冒出鳳姐的聲音。別說handler兄,對於平凡的我估計也接受不了,所以直接槍斃。如果不是鳳姐,證明你不是在黑名單以內,哪怕你身高只有1米48,哪怕你有一張大嘴,哪怕你還外加一口齙牙,我們好歹給個面子先見個面聊聊,然後找理由說你不符合我的擇偶標準。

4行,好了,我們的input裝置美少女開始和handler兄首次見面,然後互相匹配雙方資訊,深入input_match_device中你會發現,最終的主動權還是在我們的handler兄這裡。

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(keybit, 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;

}

 

看到沒,只要我們的handler兄有某一個flag設定了,你的input裝置對應的條件必須具備,否則,我們倆玩完了。深入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;

 

還是這樣,只要我們的handler兄的的id中evbit、keybit等等中的某一位設定了,input裝置美眉也得具備這個條件(還記不記得我們在第二節中用set_bit(EV_ABS, akm->input_dev->evbit);為我們這個input 裝置美眉製造了一個條件),否則,還是槍斃。於是乎,我開始懷疑:寫linux程式碼的這些兄弟們原來比春哥還純爺們,所有的想來和他相親的女的必須要服從我這些苛刻的條件,哪怕有一個不符合,你別想和他好。不過人家確實有那個資本,能夠在linux核心的世界裡馳騁的人一般不簡單,像國內這方面的人估計還找不上幾個。還好的是,我們的input裝置美眉可以遍歷input_handler_list上所有的handler兄,就不相信沒有一個條件稍微要求低點的。那麼請問條件要求很低的這樣的handler現在哪裡有呢?哎!這位美眉你運氣真好,這裡正好有一個。

看看這位仁兄的擇偶標準:

 

static const struct input_device_id evdev_ids[] = {

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

       { },                /* Terminating zero entry */

};

 

他就一個條件,而且還是一個可以說不是條件的條件,為什麼會這麼說呢,請繼續回到我們的input_match_device函式中,看到了嗎?某些兄弟可能會驚奇的大呼一聲,我的媽呀!他確實是沒有要求,一沒設定flag,而沒設定evbit、keybit等等。所以…所以…這哥們其實最有福了,就是那些所有找不到男朋友的input裝置美眉,都會和他好上,(當然鳳姐是看不上他的,因為他不是北大清華的經濟學碩士,他長得沒有金城武帥,他身高也沒有1米8……)所以就出現了傳說中的一夫多妻制。

這裡我們這個input裝置美眉因為某些條件不夠,(這裡申明一下,不是我們的這個input裝置條件不好,是那些其他的handler針對性實在太強了,比如說,他只要嘴角下方有顆美人痣,笑起來像張曼玉,並且還得有著像張馨予一樣身材的,或者是年紀20歲左右、巨蟹座的、沒談過戀愛、還長著一張張柏芝的臉的)所以也和我們這個evdev_handler結合了。evdev_handler兄偷偷地笑了,罵其他的哥們真傻,又輕鬆搞定一妞。

6、7行,沒找到,則返回。

8行,既然找到了,就發生上關係了,馬上呼叫我們evdev handler中的connect函式進行聯姻。在此函式中,又發生什麼了呢,這個我們留到下節中講。

9-13行,如果匹配不成功,顯示列印出來。

14行,打道回府。

 

Handler兄果然是handler兄,很給力,這不,剛和人家好上,就有了愛情的種子。有位仁兄要問了:“他是怎麼做到的呢?說出來讓我學習學習一下,哥這麼多年了,還是一直和自己的左手生活著”,好吧,我們就來看看事情是怎樣發生的:

  沒有錯,就在第六節的error = handler->connect(handler, dev, id);這行程式碼中,發生了那麼點事兒,也就是那麼點事兒,讓他們最終走到了一起了,有時候你不得不佩服那句話的力量“生米煮成熟飯”,不過在當今社會,貌似這話也不再那麼給力了……可以看到,此行程式碼呼叫了我們這位evdev handler兄的connect函式。好了,見證奇蹟的時刻到了:

static int evdev_connect(struct input_handler *handler, struct input_dev *dev,

                      const struct input_device_id *id)

{

       1 struct evdev *evdev;

       2 int minor;

       3 int error;

 

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

       5     if (!evdev_table[minor])

       6            break;

 

       7 if (minor == EVDEV_MINORS) {

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

       9     return -ENFILE;

       10 }

 

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

       12 if (!evdev)

       13    return -ENOMEM;

 

       14 INIT_LIST_HEAD(&evdev->client_list);

       15 spin_lock_init(&evdev->client_lock);

       16 mutex_init(&evdev->mutex);

       17 init_waitqueue_head(&evdev->wait);

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

       19 evdev->exist = 1;

       20 evdev->minor = minor;

 

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

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

       23 evdev->handle.handler = handler;

       24 evdev->handle.private = evdev;

 

       25 dev_set_name(&evdev->dev, evdev->name);

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

       27 evdev->dev.class = &input_class;

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

       29 evdev->dev.release = evdev_free;

       30 device_initialize(&evdev->dev);

 

       31 error = input_register_handle(&evdev->handle);

       32 if (error)

       33    goto err_free_evdev;

 

       34 error = evdev_install_chrdev(evdev);

       35 if (error)

       36    goto err_unregister_handle;

 

       37 error = device_add(&evdev->dev);

       38 if (error)

       39 goto err_cleanup_evdev;

 

       40 return 0;

 

 41 err_cleanup_evdev:

       42 evdev_cleanup(evdev);

    43 err_unregister_handle:

       44 input_unregister_handle(&evdev->handle);

    45 err_free_evdev:

       46 put_device(&evdev->dev);

       47 return error;

48 }

 

估計兄弟你看到這麼長的一個函式(其實不算很長,48行程式碼的函式在核心中算很短的了,當然中間省略了好多的空行),馬上會拖動滑鼠的滑輪一直往下走,如果你真的這麼幹了,說明兄弟你很有遠見,因為我接下來會一行行的和你來談論這個事件的經過。如果你沒有這樣,說明你是一個自由探索精神很強的哥們,好好保持,linux核心開發就需要你這樣的人才。閒話不多說,我們來慢慢欣賞這小兩口的那點事兒。

1-3行,定義一些基本變數,以後我們會用到的,注意這裡來了一新的面孔,struct evdev *evdev,他是誰呢?這兒等會我們再討論,先來看看這個傢伙長什麼樣:

 

struct evdev {

       int exist;

       int open;

       int minor;

       char name[16];

       struct input_handle handle;

       wait_queue_head_t wait;

       struct evdev_client *grab;

       struct list_head client_list;

       spinlock_t client_lock; /* protects client_list */

       struct mutex mutex;

       struct device dev;

};

 

現在我們不需要搞清楚裡面每一個成員的含義,到了後面你自然就明白了。就是這樣,有些東西你在某一個時侯怎麼想也想不明白,只要一旦到了某個階段,再一想想,其實就那麼回事。怪自己當時想的多了,考慮的太複雜了,完全沒必要。所以人啊,還是要活在當下,好好做好的當前的每一件事情,未來不可遇見,也不知會有什麼即將發生在你身上,比如說2012。

4-6行 注意第5行中的evdev_table[minor]陣列,她是一個專門用來裝evdev的陣列,可以把它想象成一個搖籃,只不過這個搖籃很大,一次可以裝很多個孩子。注意到34行加粗部分evdev_install_chrdev(evdev)跟蹤進去:

static int evdev_install_chrdev(struct evdev *evdev)

{

       /*

        * No need to do any locking here as calls to connect and

        * disconnect are serialized by the input core

        */

       evdev_table[evdev->minor] = evdev;

       return 0;

}

 

這個函式很簡單,把一個孩子evdev放到我們的搖籃中去。現在可以來討論evdev這個結構是個啥東東了,沒有錯,他正是evdev handler 和我們那個input 裝置美眉兩個人在那些激情燃燒的歲月裡創造出來的愛情結晶。而evdev_table[minor]這個搖籃是給他們裝孩子用的。注意了,前面提過,evdev handler兄比較有福氣,女人多,所以linux核心給他準備了一個很大的搖籃,不過搖籃的不同位置有標號的。記住,這位仁兄和一個女人只有一個孩子(計劃生意在核心裡也有體現的),不過男人畢竟在社會上還是主力軍,還得為社會貢獻自己的價值,所以老婆不能太多,最多也只可以有EVDEV_MINORS個女人,所以給他分配的那個搖籃最多隻能裝EVDEV_MINORS個孩子。好了現在回過頭來看第4-6行程式碼。遍歷整個搖籃的所有位置。找到一個沒有孩子的空位,等下好放孩子。

7-10行,對不起,姐姐,你來晚了,搖籃都滿了,說明什麼,對,說明handler兄已經有EVDEV_MINORS個老婆了,那麼對不起,雖然你們兩情同意和,但我們還得遵守婚姻法,走吧,姐姐,當二奶一般轉正的可能性不大,何況這位handler兄女人如雲。

11-13行,很幸運,姐姐你來的夠早,這兒還有位子,所以你們兩個可以結婚,還可以生孩子。所以這一行程式碼比較關鍵,孩子的生命體在這裡誕生了。至於怎麼誕生的?這還問我?相信每一個智商正常的哥們都明白如何讓他誕生,不明白的話,到兩性網上去好好學習一下相關知識。12-13行,很不幸,孩子這個生命體是誕生了,不夠還沒出娘肚,就夭折了。這是很不幸的。

14-20行,初始化evdev的一些內部成員。比如鎖,互斥量,等待佇列等等。這裡要注意第20行:evdev->minor = minor;把前面核心給孩子分配的搖籃位置號給孩子掛上,等下好對號入座。

21-24行,前面的三大資料結構,好像一直還有一個沒出現,對!就是input_handle。這裡終於出現了。這三行就是來填充這個資料結構的。input_handle怎麼理解呢?對,他就是把孩子和他們的爸爸媽媽綁在一起的一個戶口資訊本。孩子的爸爸是誰,孩子的媽媽是誰,孩子叫什麼名字,從此整個孩子的資訊就到我們的handle裡面去了。

25-29行,每一個dev裝置,最終都要在linux系統中的/dev或者它的遞迴子目錄生成一個檔案,以後我們就可以對它進行open,read….操作了,這裡幾行再加上37行就是專幹這事的。

31-33行 提交戶口資訊本給相關政府單位,讓我們可愛的政府知道你們兩有孩子了。

後面都是一些錯誤處理函式,這裡就不一一分析,有興趣的哥們可以去研究一下。

好了,該講的都講了,不該講的好像也講了。整個故事貌似結束了。如意郎君找到了,婚姻也結了,孩子也生了,戶口也註冊了。可是當你當回到這個故事的開頭,你發現好像什麼都還沒開始。我們現在確實創造了一個裝置檔案evdevX(X代表搖籃位置編號)。可是用它來幹什麼呢?曾哥要說話了:七月份的尾巴,我是獅子座……這還不簡單!這裡的evdev就是拿來給我們應用開發者操作的,她是她媽akm這個input 裝置對應的裝置檔案實體。怎麼用她?那麼我們得先搞定她老爸,evdev handler叔(畢竟有女兒的人了,不能再稱兄了)。

 

說到evdev handler這個名字,相信大家一定再熟悉不過了,就是那個妻妾成群孩子成堆的傢伙,一個讓世界上很多男人都望塵莫及的傢伙。嫉妒吧,羨慕吧,這些或許都已不重要,為了解解恨,我們還得想辦法搞定她跟akm input裝置大媽生下的那個女兒了。不過想搞定他女兒,我們得先搞定她爸。只要她爸開口了,後面的事情可能就好說了一些。不管怎樣,還是來熟悉一下這位evdev handle大叔。

static struct input_handler evdev_handler = {

       .event             = evdev_event,

       .connect  = evdev_connect,

       .disconnect     = evdev_disconnect,

       .fops              = &evdev_fops,

       .minor            = EVDEV_MINOR_BASE,

       .name             = "evdev",

       .id_table  = evdev_ids,

};

 

其實我們對他裡面的幾個函式已經有所瞭解。比如evdev_connect()(上一節剛分析過,不要告訴我你不知道?那樣我會很傷心地,)那麼evdev_disconnect()顧名思義就是完成相反的動作(拋妻棄女)。.id_table     = evdev_ids(擇偶標準),.name              = "evdev"孩子的姓,再加一個搖籃編號就是姓名了,顯示在/dev相關目錄下。.minor= EVDEV_MINOR_BASE,,還記得第7節中的那一行程式碼嗎?evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);可以看到通過這條程式碼生成了裝置的裝置號。好了還剩下兩個咚咚我們還不瞭解的。

即evdev_event,和evdev_fops。evdev_event是麼子東西,這裡不講,留個懸念先後面我會詳細道來…..我們來看evdev_fops。跟蹤進去:你會驚奇的發現:

static const struct file_operations evdev_fops = {

       .owner           = THIS_MODULE,

       .read              = evdev_read,

       .write             = evdev_write,

       .poll        = evdev_poll,

       .open             = evdev_open,

       .release    = evdev_release,

       .unlocked_ioctl      = evdev_ioctl,

#ifdef CONFIG_COMPAT

       .compat_ioctl  = evdev_ioctl_compat,

#endif

       .fasync           = evdev_fasync,

       .flush             = evdev_flush

};

 

記憶力好的哥們馬上會想到我第一節所說的話了。沒錯,這就是input子系統為我們提供的那些檔案操作集函式函式,還記得魯迅先生說過什麼了嗎?不記得的話,建議回頭再看看第一節的內容,讓他老人家去的安心。沒錯,我們的應用層開發者最終都會用到它。想泡美眉的哥們絕對要記住這個結構體了,因為只有用到它,才能讓這位handler叔的女兒拜倒在你的石榴裙下。

 

 

好好把握它,你就可以想開啟她的心就開啟她的心(open),嚮往她的內心寫東西就可以隨便寫(write),想從她內心讀出很多故事,她就讓你讀(read),最關鍵的是,你可以隨意操作她(evdev_ioctl)。是不是很high啊。還記得我們第二節中描述的如何關於在linux中把akm這個裝置註冊成一個input裝置嗎?不記得的話,可以回頭看看。關於那短短的幾行程式碼我們基本上分析完了,不過還有一個沒有分析:

input_report_abs(data->input_dev, ABS_RX, rbuf[0]);

input_report_abs(data->input_dev, ABS_RY, rbuf[1]);

input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);

 

等幾行程式碼。前面提過,他就是向我們的input子系統報告發生的一些事情,然後我們的input 子系統就會把它進行處理,最終提交到我們的應用層,然後就可以用上面的evdev_fops進行讀寫操作了。整個過程是不是有一些些明白了。

好了,關於這幾個函式是如何提交我們的裝置資訊的,那就要跟前面evdev_handler中那個evdev_event扯上關係了。該來的它終究還是回來的,逃也逃不掉。這就是我們下一節要討論的東東。

 

這裡我們只以其中的一行為例來分析一下如何給我們的input子系統上報事件。

input_report_abs(data->input_dev, ABS_RX, rbuf[0]);繼續跟蹤:

 

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)

{

       input_event(dev, EV_ABS, code, value);

}

 

可以看到,該函式也是隻呼叫了一個input_event函式。網名為“拉登他爹”要罵人了:“為什麼一行程式碼也來實現一個函式,這不是忽悠老子嗎?”兄弟,你要體會我們那些寫linux核心程式碼的哥們的用心良苦(你說你想要逃,偏偏註定要落腳 ,情滅了愛熄了 ,剩下空心要不要 ,春已走花又落 ,用心良苦卻成空 ,我的痛怎麼形容 ,一生愛錯放你的手 。張宇大哥很經典的一首歌)他們這麼做完全是希望同一個函式能同時被多個其他函式呼叫,達到程式碼複用的作用,比如這裡input_event(dev, EV_ABS, code, value);根據第二個引數的不同來進行不同的處理來。下次兄弟你又要處理一個按鍵事件,我們就直接可以這樣:
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
        input_event(dev, EV_KEY, code, value);
}

 

好了,廢話不多說,繼續跟蹤進來:
void input_event(struct input_dev *dev,  unsigned int type, unsigned int code, int value)

{

       1 unsigned long flags;

 

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

 

       3     spin_lock_irqsave(&dev->event_lock, flags);

       4     add_input_randomness(type, code, value);

       5     input_handle_event(dev, type, code, value);

       6     spin_unlock_irqrestore(&dev->event_lock, flags);

       }

}

 

終於看到一個還像個函式的函式了,至少有那麼6行程式碼。兄弟,其實你我都應該感到很幸福。Linux核心中達到幾百行程式碼程式碼的函式比比皆是。

1行,定義一個識別符號,等下會用到。

2行,判斷我們的input 裝置中的evbit中是否設定了EV_ABS這個標識,以表示支援絕對值座標。回想第二節中的set_bit(EV_ABS, akm->input_dev->evbit);沒有錯,我們是有設定的,所以可以往下走。

3、6兩行,為我們的臨界區程式碼加個自旋鎖,防止併發訪問。細心的哥們可能會發現,這和我們平時的自旋鎖有些不同,一般的自旋鎖都是spin_lock()和spin_unlock()成對出現的。這裡好像多長了個尾巴。沒錯,男人和女人的某些區別好像也在這裡,不好意思,思想又遊離了…繼續回來,我們這個自旋鎖條件更苛刻。一般的自旋鎖保護臨界區程式碼不受其他cpu和本cpu的搶佔程式打擾。這兩個長尾巴的哥們更狠,他連我們的中斷也一併拒之門外。總之,通告天下,被我佔有的東西,你天王老子都別想再碰,除非我釋放她了。夠狠吧!是的,男人就應該對自己狠一點。補充一句:前面定義的那個flag就是用來儲存被我們拒之門外的那個中斷的狀態資訊的。

4行,為核心產生隨機因子。

5行,真正有悟性的哥們應該掃一眼這幾行程式碼就會發現,這一行才是真正的國家寶藏所在,難道這就是所謂的天賦,你不得不佩服尼古拉斯凱奇的犀利,憑著那麼一點點資訊就可以發現那麼大的寶藏。不過現實生活中這樣事情發生的概率就好比張曼玉突然為了你和她的外籍男友分手,然後來到長安,為的就是能默默守侯在你身邊。繼續跟進input_handle_event(dev, type, code, value):

又是一來自 input core的函式。

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:

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

      2     {

 

      3            value = input_defuzz_abs_event(value,

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

                   

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

      6                   {

                                  dev->abs[code] = value;

      7                          disposition = INPUT_PASS_TO_HANDLERS;

      8                   }

      9            }

      10   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 (disposition != INPUT_IGNORE_EVENT && 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);

}

 

有兄弟要發話了:“好長的一堆程式碼“,是的,我承認,這是我這篇文件到目前為止最長的一個函式。不夠在大學裡面稍微學了一點點C語言的哥們應該都不覺得它複雜,否則你就將無言見江東父老,更對不起譚浩強大哥。整個函式先一個switch。然後根據不同的case進行不同的處理,得到不同的disposition變數值。然後,然後就到整個程式碼的最後六行程式碼了,是不是簡單的一B。這裡我們傳進來的引數正好是EV_ABS。所以我們只需把注意力放到程式碼中的加粗部分。

 

1行,判斷我們的input 裝置中的absbit中是否設定了ABS_RX,顯然前面我們已經設定了不信你回過頭來看看第二節中那個曾經沒有講過的函式。相信哥們你的c語言程式碼閱讀能力絕對在我之上,區區幾行程式碼怎麼能唬得了你。

3-4行,進行誤差校準。這裡我初始化時設定為0。故其實啥也沒幹,星星還是那顆星星喲,月亮還是那個月亮, value也還是那個value。

5-7行,把value的值付給dev->abs[code],dev->abs[code]記錄的是上一次該座標值。如果是第一次,顯然為0.所以最終我們得到了disposition = INPUT_PASS_TO_HANDLERS。默默地做了那麼多前期工作為的就是得到她。現在終於如願以償,帶著她從此開始浪跡天涯…..

浪到哪兒去了呢?聰明的你馬上發現了浪到了最後兩行程式碼。太陽依然發光,地球依然轉動,你依然愛著心中的那個她,故事依然還在繼續,input_pass_event(dev, type, code, value)也依然還在傳值,他到底要把那個我們收集到的type、code、value傳到哪兒去呢?

 

廢話不多講,開門見山,我們繼續上節那個沒有完的故事,我們收集到的那些值到底會傳到哪裡去呢?深入input_pass_event(dev, type, code, value)內部,看她花落誰家?

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

{

       1  struct input_handle *handle;

 

       2  rcu_read_lock();

 

       3  handle = rcu_dereference(dev->grab);

       4  if (handle)

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

       6   else

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

       8            if (handle->open)

       9                   handle->handler->event(handle,

       10                                       type, code, value);

       11    rcu_read_unlock();

}

 

1行,定義一個input_handle結構體變數,不會這麼快就忘了input_handle這位兄弟吧?

2、11兩行是一種 RCU形式的鎖機制。它允許多個執行單元對它同時對它進行讀,但是多個執行單元對它進行寫操作時,並不會讓你隨隨便便就碰她,它會要求你先複製一個副本,要改要刪隨你便,反正你改的不是她本身。當所有的寫單元都一一改完後,再統一寫回,是不是比一般的自旋鎖高階多了。不過不要輕易用它,系統開銷較大。

3-5行,看看dev->grab有沒有設定,回顧前面dev的初始化。顯然沒動它。所以這幾行程式碼就這樣廢了。

7-10行,遍歷dev->h_list連結串列上的handle,看看有沒有handle->open不為0的,這裡在我們在使用者空間通過open函式開啟某個evdev的時候,相應的handle->open會++,這裡顯然我們要開啟evdev裝置來對input裝置進行操作,才會發生前面那些那兒,要不然我說了這麼多不是等於一個0.?

第9行程式碼,我們通過這個handle戶口資訊表找到孩子的爸爸然後呼叫孩子爸爸裡面的函式evdev_event。

我沒有食言!記性好的哥們還記不記得,我們在第8節中瞭解evdev_handler的時候,有一個函式沒講,正是此函式。我們再來看一下evdev_handler叔的全貌:

static struct input_handler evdev_handler = {

       .event             = evdev_event,

       .connect  = evdev_connect,

       .disconnect     = evdev_disconnect,

       .fops              = &evdev_fops,

       .minor            = EVDEV_MINOR_BASE,

       .name             = "evdev",

       .id_table  = evdev_ids,

};

 

 

現在是時候來分析這個evdev_event,的時候了,為了成功泡上他的美麗女兒,我們還是有必要來攻下這最後一座城池:

static void evdev_event(struct input_handle *handle,

                     unsigned int type, unsigned int code, int value)

{

       1 struct evdev *evdev = handle->private;

       2 struct evdev_client *client;

       3 struct input_event event;

       4 struct timespec ts;

 

       5 ktime_get_ts(&ts);

       6 event.time.tv_sec = ts.tv_sec;

       7 event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;

       8 event.type = type;

       9 event.code = code;

       10 event.value = value;

 

       11 rcu_read_lock();

 

       12 client = rcu_dereference(evdev->grab);

       13 if (client)

       14    evdev_pass_event(client, &event);

       15 else

       16    list_for_each_entry_rcu(client, &evdev->client_list, node)

       17           evdev_pass_event(client, &event);

 

       18 rcu_read_unlock();

 

       19 wake_up_interruptible(&evdev->wait);

}

 

1行,大家還記不記得,那個時候,evdev handler兄和我們的akm input裝置美眉喜得貴女後,帶著孩子去政府部門註冊戶口資訊時,就是把他們的女兒交給了handle->private。這裡把她再提取出來,賦給我們的struct evdev *evdev,沒有疑問吧!

2行,又來一個新的資料結構struct evdev_client *client;,先看看她的內部結構:

 

struct evdev_client {

       struct input_event buffer[EVDEV_BUFFER_SIZE];

       int head;

       int tail;

       spinlock_t buffer_lock; /* protects access to buffer, head and tail */

       struct fasync_struct *fasync;

       struct evdev *evdev;

       struct list_head node;

       struct wake_lock wake_lock;

};

 

眼神犀利的哥們馬上會看到很關鍵的一行程式碼:struct evdev *evdev;沒有錯,她內嵌了一個evdev結構,怎麼理解呢?這就好比我們把mtk、高通、或者Marvel廠商那兒提供給我們的整個手機設計方案搬過來,改改程式碼,新增點應用程式,加上一個漂亮的外殼,再來一個時尚的品牌商標,一款時尚的手機就出爐了,還賣的很好哦。對,這裡的struct evdev_client就是我們那個evdev的pvt版本,她是和我們的evdev緊緊地繫結在一起的,在哪兒繫結的呢?作為一個負責任的男人,我得告訴你,還是發生在應用層呼叫open時發生的。關於這個open整個過程我們下一節會詳細講解。不過她確實多了幾個很重要的東東:struct input_event buffer[EVDEV_BUFFER_SIZE];定義一個核心區buffer。它用來幹嘛?input_event結構是什麼樣?請看接下來的第三行程式碼。

3行,世界上誰跑的最快?“當然是input_event了”鳳姐很得意的回答道。鳳姐智商果然超群。說input_event,input_event到。我們來看看這個傢伙長什麼樣:

struct input_event {

       struct timeval time;

       __u16 type;

       __u16 code;

       __s32 value;

};

 

她的作用很明顯,就是用來儲存我們要傳遞的那些值type、code、value。不信,不信請往下看。

4-7行,跟時間片有關的一些東東,和我們的這個akm 裝置無關。不過handler兄老婆多,總有一個用得上的。

8-10行,把通過引數傳進來的ype、code、value放到我們的event中。是不是驗證了前面的說法。

11、18兩行,又是加鎖操作。前面有聊過,飄過。

12-14行,看看這個client有沒有被強制繫結,這裡我們沒繫結,故略過。

16-17行,遍歷client連結串列,找到與我們對應的那個client,然後把剛剛那個event放到裡面去。放到哪裡面去呢?放到struct input_event buffer[EVDEV_BUFFER_SIZE]裡面去,注意最多可放EVDEV_BUFFER_SIZE個event。

進入evdev_pass_event(client, &event);跟蹤:

 

static void evdev_pass_event(struct evdev_client *client,

                          struct input_event *event)

{

       /*

        * Interrupts are disabled, just acquire the lock

        */

       spin_lock(&client->buffer_lock);

       wake_lock_timeout(&client->wake_lock, 5 * HZ);

       client->buffer[client->head++] = *event;

       client->head &= EVDEV_BUFFER_SIZE - 1;

       spin_unlock(&client->buffer_lock);

 

       kill_fasync(&client->fasync, SIGIO, POLL_IN);

}

 

等下我們的使用者程式就可以通過一定的方式(read()系統呼叫)來從這個buffer中拷貝資料了。這樣一次從最下層驅動通過input子系統傳到核心區buffer,然後再到使用者空間的資料傳輸就這樣完成了。

好了,貌似故事就這樣結束了,資料終於傳完了。整個handler叔全弄明白了,既然這樣,那還等什麼,趕緊操著傢伙evdev_fops,去把玩他的美麗女兒吧。不過兄弟先別急,再去之前,有兩個小小的注意事項需要交代一下,以防不測:

1,    上面的evdev_event()函式的最後一行程式碼wake_up_interruptible(&evdev->wait);是用來喚醒一個等待佇列,他的另一半是wait_event_interruptible(&evdev->wait),她一般位於另外一個地方。比如:

int fun1()

{

……

……

wake_up_interruptible(&evdev->wait);

 

 

}

int fun2()

{

……

……

wait_event_interruptible(&evdev->wait);

……

……

}

函式fun2執行到wait_event_interruptible(&evdev->wait)這一句時,她就不走了,幹嘛?睡個覺先,等待她的另一半的到來,然後把她喚醒。這就需要我們的fun1函式執行到wake_up_interruptible(&evdev->wait);明白了吧!有哥們要發話了,這個機制有什麼作用啊?

好吧,舉個例子:兩個函式對一buffer進行操作,一個讀她,一個寫她。要讀的那個哥們是不是要先等那個要寫的哥們把資料寫到裡面去了再讀呢?否則,你去讀空氣啊?

好了,這裡在我們這個evdev_event()函式來這麼一個wake_up_interruptible(&evdev->wait);幹嘛,表示你的資料已經好了,那位想讀的哥們趕緊過來讀吧,再不讀就被別人讀走了哦!

 

2我們通過input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);最開始傳輸資料的,可是rbuf[2])哪來的呢?具體過程是這樣的:akm8973感測器是通過I2C匯流排掛到系統中的,它不斷地從外界收集資料(比如各座標資訊)然後把他們送到自己的暫存器中,然後我們就通過I2C匯流排從它的暫存器中獲取資料,最終放到我們上面的rbuf[2]中。接下來就發生了上面的那一切,致使這些資料最終被傳到我們的使用者空間被我的應用程式處理和顯示,喲!從手機的GPS導航可以發現,兄弟你現在位於廣東省東莞市長安鎮烏沙區大潤發旁不遠處的一個神祕地方,喲!顯示了,是一個叫海豚灣的地方!

 

沒錯,到目前為止,akm input裝置也註冊了,evdev handler大叔的全貌也搞明白了,他們的女兒也亭亭玉立了……是不是該幹正事的時候了?

好吧,我們就來看看如何來幹這份正事:

還是列出那份操作集函式(handler大叔為我們這些笑的最後的弟兄精心準備的泡妹妹大法,不過現實生活中可沒有這樣的爸爸):

static const struct file_operations evdev_fops = {

       .owner           = THIS_MODULE,

       .read              = evdev_read,

       .write             = evdev_write,

       .poll        = evdev_poll,

       .open             = evdev_open,

       .release    = evdev_release,

       .unlocked_ioctl      = evdev_ioctl,

#ifdef CONFIG_COMPAT

       .compat_ioctl  = evdev_ioctl_compat,

#endif

       .fasync           = evdev_fasync,

       .flush             = evdev_flush

};

 

還記得嗎?這就是我們精心填表後得到的那個u盾。

要了解evdev,我們先得開啟她的心扉,來了解了解她。記得一位情聖曾經說過的一句話:要獲得一位美女的好感,要做的第一件事就是能開啟她的話匣子,要聊那些她感興趣的事兒,徹底開啟她的心扉,這樣她就會對你產生信任感….接著呢?接著就不說了,有興趣的兄弟可以找我聊聊(研發樓二樓進門就可以看到一個長的不是很搓的瘦瘦哥們)。

好了,在應用層,我們可以通過呼叫open函式然後轉到我們這個操作集函式中的.open  = evdev_open,。是不是很簡單。嗯,確實簡單的一B。繼續,看看具體開啟過程:

 

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

{

       1 struct evdev *evdev;

       2 struct evdev_client *client;

       3 int i = iminor(inode) - EVDEV_MINOR_BASE;

       4 int error;

 

       5 if (i >= EVDEV_MINORS)

       6     return -ENODEV;

 

       7 error = mutex_lock_interruptible(&evdev_table_mutex);

       8 if (error)

       9 return error;

       10 evdev = evdev_table[i];

       11 if (evdev)

       12    get_device(&evdev->dev);

       13 mutex_unlock(&evdev_table_mutex);

 

       14 if (!evdev)

       15    return -ENODEV;

 

       16 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);

       17 if (!client) {

       18    error = -ENOMEM;

       19    goto err_put_evdev;

       20 }

 

       21 spin_lock_init(&client->buffer_lock);

       22 wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, "evdev");

       23 client->evdev = evdev;

       24 evdev_attach_client(evdev, client);

 

       25 error = evdev_open_device(evdev);

       26 if (error)

       27   goto err_free_client;

 

       28 file->private_data = client;

       29 return 0;

 

30 err_free_client:

       31   evdev_detach_client(evdev, client);

       32  kfree(client);

    33  err_put_evdev:

       34   put_device(&evdev->dev);

       35 return error;

}

 

俗話說:話說三遍淡如水,再說三遍打驢嘴。相信看過我前面章節的哥們要來讀懂這個函式,簡直是 a piece of cake。不過還是有幾行程式碼需要提一提:

3、10兩行,第3行:通過裝置節點號找到搖籃位置編號;第10行:通過這個搖籃編號找到那個孩子,對了,已經不是孩子了,是個美少女了。

16行,建立struct evdev_client,還記得嗎,她是在struct evdev的基礎上改造出來的品牌機。再三強調:不是山寨。

24行,進行繫結。

25行,關鍵的25行,怎麼關鍵了,跟蹤進去:

static int evdev_open_device(struct evdev *evdev)

{

       int retval;

 

       retval = mutex_lock_interruptible(&evdev->mutex);

       if (retval)

              return retval;

 

       if (!evdev->exist)

              retval = -ENODEV;

       else if (!evdev->open++) {

              retval = input_open_device(&evdev->handle);

              if (retval)

                     evdev->open--;

       }

 

       mutex_unlock(&evdev->mutex);

       return retval;

}

 

眼神不是很差的哥們馬上會看到,有一行程式碼加粗了,他就是我們這個函式的核心。就好比小時候老師經常讓我們寫文章段落大意,找中心句子,如果找不出來,就去操場罰跑步。天生缺乏語言細胞,那時的我步可沒少跑,連操場上撿垃圾的大媽都認識我了,偶爾一天僥倖過關沒去跑,第二天大媽會說“小娃子,昨天是不是沒來上課啊”。估計長不胖的一個重大原因就是小時候語文課上中心句子找少了。搞得現在一看到一堆程式碼,就想找出那個中心來。

好了繼續跟蹤進來,看看input_open_device(&evdev->handle)這個中心幹了什麼事情:

int input_open_device(struct input_handle *handle)

{

       struct input_dev *dev = handle->dev;

       int retval;

 

       retval = mutex_lock_interruptible(&dev->mutex);

       if (retval)

              return retval;

 

       if (dev->going_away) {

              retval = -ENODEV;

              goto out;

       }

 

       handle->open++;

 

       if (!dev->users++ && dev->open)

              retval = dev->open(dev);

 

       if (retval) {

              dev->users--;

              if (!--handle->open) {

                     /*

                      * Make sure we are not delivering any more events

                      * through this handle

                      */

                     synchronize_rcu();

              }

       }

 

 out:

       mutex_unlock(&dev->mutex);

       return retval;
}

 

我只能說:兄弟你太有才了,馬上又一眼看穿中心句了;對就這裡這個加粗部分:

handle->open++;。哥們對它有沒有印象,不記得的話,開啟第10節,看裡面的第一段函式中的加粗部分,就是因為它,我們的資料才得以傳遞下去……

28行 file->private_data = client;把這個client付給檔案的私有資料.

 

前面已經開啟她的心扉了。某位腦殘的兄弟問:“開啟之後幹嘛呢”,還能幹嘛呢?當然是讀了,好,下來我們分析操作集中的第二個函式:evdev_read()深入跟蹤下去:

static ssize_t evdev_read(struct file *file, char __user *buffer,

                       size_t count, loff_t *ppos)

{

       1 struct evdev_client *client = file->private_data;

       2 struct evdev *evdev = client->evdev;

       3 struct input_event event;

       4 int retval;

 

       5 if (count < input_event_size())

       6     return -EINVAL;

 

       7 if (client->head == client->tail && evdev->exist &&

       8   (file->f_flags & O_NONBLOCK))

       9     return -EAGAIN;

 

       10 retval = wait_event_interruptible(evdev->wait,

       11    client->head != client->tail || !evdev->exist);

       12 if (retval)

       13    return retval;

 

       14 if (!evdev->exist)

       15   return -ENODEV;

 

       16 while (retval + input_event_size() <= count &&

       17       evdev_fetch_next_event(client, &event)) {

 

       18    if (input_event_to_user(buffer + retval, &event))

                     return -EFAULT;

 

       19    retval += input_event_size();

       }

 

       20 return retval;

}

 

相信在座的各位,現在分析這個函式也不會有什麼困難。

1行,把在evdev_open裡那個私有資料放到struct evdev_client *client中。

5-6行,如果要copy的位元組數少於一個event的大小,對不起,結束吧。不完整的event對我們來說沒什麼用,別浪費表情。

10-11兩行,wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);

還記得她嗎?是的,她已經在這裡睡了好久,等待她的的如意郎君把她喚醒。後面的client->head != client->tail || !evdev->exist為條件,條件必須滿足,她才會醒來。

16-19行,每次從client的buffer中取出一個input event資料放到我們這裡的event中,然後把它傳到應用層的buffer中,retval記錄總共返回的位元組數。深入input_event_to_user(buffer + retval, &event):

int input_event_to_user(char __user *buffer,

                     const struct input_event *event)

{

       if (copy_to_user(buffer, event, sizeof(struct input_event)))

              return -EFAULT;

 

       return 0;

}

 

函式copy_to_user就是把一個核心空間buffer放到我們的使用者空間buffer,他的兄弟copy_from_user完成相反的動作。

好了,資料都已經到我們的使用者空間了,現在我們想幹嘛就可以幹嘛了……

至於函式集中的evdev_write、evdev_ioctl等函式我就不一一分析了,怎麼去往她心裡面寫東西,怎麼去操作她,這些東西聽起來是不是有點yellow,我就不好意思再和你一起探討了。這些東西哥們你要自己慢慢去摸索,俗話說的好:“自己動手,豐衣足食”。

到此,整個系列都已分析完了,發現自己理解和分析程式碼是一回事,寫出來是另外一回事。還是那句話:兄弟們,我們要多實踐啊,無論是工作還是把妹,你不去實踐,光yy的話,效果還是是相差的很遠的。

 轉自:http://blog.csdn.net/lmm670/article/details/6080998

相關文章