一、input輸入子系統框架
下 圖是input輸入子系統框架,輸入子系統由輸入子系統核心層(input core),驅動層和事件處理層(Event Handler)三部分組成。一個輸入事件,比如滑動觸摸屏都是通過input driver -> input core -> event handler -> user space 到達用戶空間傳給應用程序。
event hander事件處理層主要和用戶空間交互,接收用戶空間下發的file operation操作命令,生成/dev/input/xx設備節點供用戶空間進行file operations操作;
input core層負責管理系統中的input dev設備 和input hander事件處理,並起到承上啟下作用,負責輸入設備和input handler之間信息傳輸;
input driver為具體用戶設備驅動,輸入設備由struct input-dev 結構表示,並由input_register_device和input_unregister_device來注冊和卸載;
輸入子系統結構方框圖如下圖:

二、輸入事件驅動--->evdev_handler的實現大致分析
Linux 輸入子系統已經建立好了幾個handler,用來處理幾類常見的事件,如鼠標、鍵盤、搖桿等。其中最為基礎的是evdev_handler,它是在 driver/input/evdev.c中實現的。它能夠接收任意類型的事件,任意id的設備都可以和它匹配連接,它對應的設備節點為/dev/eventX,次設備號的范圍為64~95。
evdev輸入事件驅動,為輸入子系統提供了一個默認的事件處理方法。其接收來自底層驅動的大多數事件,並使用相應的邏輯對其進行處理。
module_exit(evdev_exit);//初始化evdev_handlerstatic int __init evdev_init(void){
return input_register_handler(&evdev_handler);}
/**一般事件驅動定義了evdev_handler結構表示自己,
*並嘗試去找到與handler中id_table相匹配的輸入設備。*/static struct input_handler evdev_handler = {.event = evdev_event,
其中有:#define EVDEV_MINOR_BASE 64
.id_table = evdev_ids,};
MODULE_DEVICE_TABLE(input, evdev_ids);
static const struct input_device_id evdev_ids[] = {{ .driver_info = 1 },/* Matches all devices */{ },/* Terminating zero entry */};/**不管是事件驅動還是輸入設備驅動在初始化時注冊自身到全局鏈表中,並嘗試與對應的輸入設備驅動或事件驅動連接,*最終的操作都是調用事件驅動的XX_connect(...)完成兩者的連接的。*/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;
其中有定義:int open;
char name[16];
wait_queue_head_t wait;
spinlock_t client_lock; /* protects client_list */
struct device dev;
表示evdev_handler所表示的32個設備,這個循環為了找到空的一項
if (minor == EVDEV_MINORS) { //return -ENFILE;}
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);
dev_set_name(&evdev->dev, "event%d", minor); //event對應節點號,event0/evdev->minor = minor;
evdev->handle.dev = input_get_device(dev); //掛載到對evdev中handle的初始化,這些初始化的目的是使input_dev和input_handler聯系起來。evdev->handle.name = dev_name(&evdev->dev);evdev->handle.handler = handler;evdev->handle.private = evdev;
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); //生成設備號evdev->dev.class = &input_class; //input的class類下evdev->dev.parent = &dev->dev;evdev->dev.release = evdev_free;device_initialize(&evdev->dev); //設備初始化
/*input_register_handle()最終完成了兩者的連接,具體實現可研究其實現源碼。input_register_handle -登記一個新的 input handle,此功能將一個新的而一旦打開使用input_open_device(),events事件可以隨時跟隨。*/error = input_register_handle(&evdev->handle); //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;}
對主設備號為INPUT_MAJOR的設備結點進行操作,會將操作集轉換成handler的操作集。在evdev_handler中定義了一個fops集合,被賦值為evdev_fops的指針,static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE; //得到了在evdev_table[]中的序號
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
error = mutex_lock_interruptible(&evdev_table_mutex);
if (error)
return error;
if (evdev)
mutex_unlock(&evdev_table_mutex);
其中:
int head;
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct list_head node;
evdev_open_device(evdev); //打開輸入設備
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;
input_open_device打開evdev對應的handle
if (retval)
}
return retval;
if (error)
goto err_free_client;
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
err_put_evdev:
put_device(&evdev->dev);
return error;
}
return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);}
static long evdev_ioctl_handler(struct file *file, unsigned int cmdvoid __user *p, int compat_mode){struct evdev_client *client = file->private_data;struct evdev *evdev = client->evdev;int retval;retval = mutex_lock_interruptible(&evdev->mutex);if (retval)return retval;if (!evdev->exist) {retval = -ENODEV;goto out;}retval = evdev_do_ioctl(file, cmd, p, compat_mode); out:mutex_unlock(&evdev->mutex);return retval;}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
struct ff_effect effect;
int __user *ip = (int __user *)p;
unsigned int i, t, u, v;
unsigned int size;
int error;
/* First we check for fixed-length commands */
switch (cmd) {
case EVIOCGVERSION:
/* 設置重復類事件 */
%
