自己的總結有錯誤請評論,我們共同進步。
static struct input_handler *input_table[8];
/*入口函數*/static int __init input_init(void){
/*註冊
*#define INPUT_MAJOR 13
*主設備號爲13
*/
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
}
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
註冊的file_operation怎麼沒有讀寫等的定義呢?
當讀寫等操作時,是怎麼讀寫的呢?
下面來分析:
設備的主設備號都是13,就要靠次設備號來訪問設備了,當打開設備文件時先訪問的是input_open_file函數,
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
/*handler等於以次設備號右移5位爲下標input_table[]的值 */
handler = input_table[iminor(inode) >> 5];
if (!handler || !(new_fops = fops_get(handler->fops)))
/*new_fops等於了 handler->fops當調用讀寫時就調用handler->fops的讀寫*/
{
err = -ENODEV;
goto out;
}
old_fops = file->f_op;
file->f_op = new_fops;
/*file->f_op等於new_fops,
*而new_fops等於handler- >fops結構,
*因此file_f_op 就等於handler->fops結構,
*讀寫就調用handler->fops的讀 *寫函數
*/
}
接下來又有問題那就是input_table中的數組項又有誰來定義,用source insight搜索可得到。
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
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_table的數組項的值,要想知道input_table的數組項的值,就必須看誰調用了input_register_handler,用source insight繼續搜索可得到:
apm-power.c :return input_register_handler(&apmpower_handler);
evbug.c : return input_register_handler(&evbug_handler);
evdev.c : return input_register_handler(&evdev_handler);
joydev.c : return input_register_handler(&joydev_handler);
keyboard.c : error = input_register_handler(&kbd_handler);
mousedev.c : error = input_register_handler(&mousedev_handler);
rfkill-input.c: return input_register_handler(&&rfkill_handler);
這些設置了input_table數組項,但是它們設置了input_table哪一項就要看&*_hadler了
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_handler.fops != NULL;
#define EVDEV_MINOR_BASE64
input_table[(&evdev_handler)->minor >> 5] = input_table[2] = &evdev_handler;
static struct input_handler mousedev_handler = {
.event =mousedev_event,
.connect =mousedev_connect,
.disconnect =mousedev_disconnect,
.fops =&mousedev_fops,
.minor =MOUSEDEV_MINOR_BASE,
.name ="mousedev",
.id_table =mousedev_ids,
};
mousedev_handler !=NULL;
#define MOUSEDEV_MINOR_BASE32
input_table[(&mousedev_handler)->minor >> 5] = input_table[1] = &mousedev_handler;
static struct input_handler joydev_handler = {
.event= joydev_event,
.connect= joydev_connect,
.disconnect= joydev_disconnect,
.fops= &joydev_fops,
.minor= JOYDEV_MINOR_BASE,
.name= "joydev",
.id_table= joydev_ids,
.blacklist= joydev_blacklist,
};
joydev_handler.fops != NULL;
#define JOYDEV_MINOR_BASE0
input_table[(&joydev_handler)->minor >> 5] = input_table[0] = &joydev_handler;
static struct input_handler apmpower_handler
= {
.event =apmpower_event,
.connect =apmpower_connect,
.disconnect =apmpower_disconnect,
.name ="apm-power",
.id_table =apmpower_ids,
};
apmpower_handler.fops=NULL;因此不在input_table中定義
static struct input_handler evbug_handler = {
.event =evbug_event,
.connect =evbug_connect,
.disconnect =evbug_disconnect,
.name ="evbug",
.id_table =evbug_ids,
};
evbug_handler與apmpower_handler一樣,
static
struct input_handler kbd_handler = {
.event= kbd_event,
.connect= kbd_connect,
.disconnect= kbd_disconnect,
.start= kbd_start,
.name= "kbd",
.id_table= kbd_ids,
};
與apmpower_handler一樣;
static
struct input_handler rfkill_handler = {
.event =rfkill_event,
.connect =rfkill_connect,
.disconnect =rfkill_disconnect,
.start =rfkill_start,
.name ="rfkill",
.id_table =rfkill_ids,
};
與apmpower_handler一樣;
又有問題了,那輸入服務系統怎麼知道按鍵設備文件?怎麼就能讀了呢?
那就是input_register_device函數註冊。
int
input_register_device(struct input_dev *dev)
{
list_add_tail(&dev->node, &input_dev_list); /*放入鏈表裏去*/
/*********怎麼放入鏈表的,放入後的結果是怎樣的**************/
static LIST_HEAD(input_dev_list);
list.h中定義了
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
初始化時使得next,prev指向自身
static
inline void list_add_tail(&dev->node, &input_dev_list)
{
__list_add(&dev->node,(&input_dev_list)->prev,&input_dev_list);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
void __list_add(struct list_head *new,struct list_head *prev,struct list_head *next)
{
(&dev->node)->prev =&dev->node;
(&dev->node)->next =&input_dev_list;
(&dev->node)->prev = prev;
prev->next =&dev->node;
}
EXPORT_SYMBOL(__list_add);
/*********************************************************/
/*對於每一個input_handler都調用input_attach_handler*/
/*
根據input_handler的id_table判斷能否支
持
input_dev*/list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
}
/***********怎麼判斷是否支持,判斷過還要做什麼***********/
/*註冊input_dev或input_handler時,會兩兩比較左邊的input_dev和右邊的input_hadler
*根據input_handler的id_table判斷這個input_handler能否支持這個input_dev,
*如果能支持,就調用input_handler的connection函數與建立連接。
*/
input_attach_handler(struct input_dev
*dev,struct input_handler *handler)
{
const struct input_device_id *id;
int error;
id = input_match_device(handler->id_table, dev);
if(!id)
return -ENODEV;
error = handler->connect(handler, dev, id);
/*匹配了,就調用*input_handler結構
*中的connection進行建立聯繫
*/
}
/******************************************************/
handler->connect中做了什麼,就要看真正的input_handler中的connection。
上面有分析到只有
evdev_handler,
mousedev_handler,
joydev_handler在input_table[],
所以我們可以分析這三個中的一個。
下面以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,
};
/*
* Create new evdev device. Note that input core serializes calls
* to connect and disconnect so we don't need to lock evdev_table here.
*/
evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
/*分配一個struct evdev,此結構中有 一個input_handle結構*/
INIT_LIST_HEAD(&evdev->client_list);
/*設置*/
/*evdev->handle.dev 指向input_device*/
evdev->handle.dev = input_get_device(dev);
/*evdev->handle.handler指向input_handler*/
evdev->handle.handler = handler;
error = input_register_handle(&evdev->handle);/*註冊*/
error = evdev_install_chrdev(evdev);
}
/***註冊evdev->handle又做了什麼呢***/
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
int error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);
/*結果爲&handle->d_node的next指向了dev->h_list
*dev->h_list的prev指針指向了handle>d_node,
*以後就可以通過dev->h_list找的handle */
list_add_tail(&handle->h_node, &handler->h_list);
/*結果爲&handle->h_node的next指向了
*handler->h_list , handler->h_list的prev指針指向了
*handle>d_node,以後就可以通過dev->h_list找的handle,
*並且可以通過handle找到dev. */
}
總結過程:
1.打開設備文件;
a.調用static const struct file_operations input_fops 中的open函數 即input_open_file
b.發生轉換,file->f_op = input_table[iminor(inode) >> 5] 調用input_table[iminor(inode) >> 5]的open函數,(次設備號64-95,evdev.c,handler=&evdev_handler);
c.調用evdev_handler中的fops的open函數打開設備文件。
2.讀設備文件:
調用(次設備號64-95,evdev.c,handler=&evdev_handler)evdev_handler中的fops的read函數讀取設備文件。
當看evdev_handler中的fops的read函數時,發現問題了,沒有按鍵時進入休眠,當有按鍵按下時調用中斷處理函數進行喚醒,那輸入子服務系統睡眠了有誰來喚醒呢。
static ssize_t evdev_read(struct file *file, char
__user *buffer,size_t count, loff_t *ppos)
{
struct
evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
/*非阻塞時*/
if (client->head == client->tail && evdev->exist
&&(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*阻塞時*/
retval = wait_event_interruptible(evdev->wait,
client->head
!= client->tail ||!evdev->exist);
if (retval)
return retval;
}
source insight搜索evdev->wait
進入得到
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
wake_up_interruptible(&evdev->wait);
}
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被喚醒的,evdev_event是evdev_handler的event,那麼中斷處理函數就應該調用evdev_handler的event進行操作,接下來就看看
隨便中一個驅動程序:
我們以\linux-2.6.30.4\drivers\input\keyboard\corgikbd.c爲例。
中斷處理函數:
static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
{
struct corgikbd *corgikbd_data = dev_id;
if (!timer_pending(&corgikbd_data->timer)) {
/** wait chattering delay **/
udelay(20);
corgikbd_scankeyboard(corgikbd_data);
}
return IRQ_HANDLED;
}
只調用了 corgikbd_scankeyboard函數,我們進去看看
static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
{
unsigned int row, col, rowd;
unsigned long flags;
unsigned int num_pressed;
num_pressed = 0;
for (col = 0; col < KB_COLS; col++) {
corgikbd_discharge_all();
udelay(KB_DISCHARGE_DELAY);
corgikbd_activate_col(col);
udelay(KB_ACTIVATE_DELAY);
rowd = GET_ROWS_STATUS(col);
for (row = 0; row < KB_ROWS; row++) {
unsigned int scancode, pressed;
scancode = SCANCODE(row, col);
pressed = rowd & KB_ROWMASK(row);
input_report_key(corgikbd_data->input,
corgikbd_data->keycode[scancode], pressed);
}
corgikbd_reset_col(col);
}
corgikbd_activate_all();
input_sync(corgikbd_data->input);
}
去去判斷有input_report_key函數很顯眼
去看看
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
}
進去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_event函數很相像*/
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_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);
else
input_stop_autorepeat(dev);
}
disposition =INPUT_PASS_TO_HANDLERS;
}
break;
}
if (disposition &INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
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
/*遍歷所有的dev->h_list,如果已經調用handle->open
*調用 handle->handler->event進行處理,
*如果我們的按鍵跟evdev_handler
*匹配成功就調用evdev_handler的event,
*即我們前面所說的evdev_event*/
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,type, code, value);
rcu_read_unlock();
}
總結一下發生中斷時,怎麼處理的:
corgikbd_interrupt(int irq, void *dev_id) ->
corgikbd_scankeyboard(struct corgikbd *corgikbd_data) ->
/*上面兩個是corgikbd.c獨有的函數所以在其他驅動程序中,
*在中斷處理函數中直接調用input.h相應函數上報事件,也可直接調用
*input_event函數上報事件*/
input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode],
pressed);
-> void input_event(*dev,type,code,value) ; ->
input_handle_event(dev, type, code, value) ; ->
/*對於可以交給設備處理的事件,並且定義了dev->event,就調用dev->event進行處理,
*其他都是調用input_pass_event進行處理*/
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); ->
調用handle->handler->event(handle,type, code, value)處理;
上面就是輸入子服務系統的內容;