前言
基於韋東山視頻的簡單總結
內核基於 Linux2.6.22
框架
左邊是基於各種接口的輸入設備,比如 USB 鼠標,藍牙鼠標,2.4G 鼠標等
右邊呢則是封裝了統一設備層,這些亂七八槽的接口鍵盤,統一都由鼠標類處理。
左邊註冊流程:
1. input_allocate_device(): 分配 input_dev
2. 設置 input_dev
3. input_register_device(): 註冊 input_dev
右邊註冊流程:
1. 創建一個 input_handler
2. 註冊 input_handler
兩邊連接: connect() 創建如下結構體管理
左右就通過如下結構管理的:
handle{
input device: 左邊
input handler: 右邊
}
左邊:通過一個全局鏈表 input_dev_list 管理
右邊:通過一個全局鏈表 input_handler_list 管理
流程彙總
右邊通用驅動註冊流程
參考代碼: Evdev.c (linux-2.6.22.6\drivers\input)
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,
};
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
module_init(evdev_init);
左邊具體驅動註冊流程
1. input_allocate_device(): 分配 input_dev
2. 設置 input_dev
3. input_register_device(): 註冊 input_dev
參考代碼: Gpio_keys.c (drivers\input\keyboard)
///////////////////////////////////////////////////////////////////////////////
// 以一個普通的按鍵驅動爲例
module_init(gpio_keys_init);
static int __init gpio_keys_init//(void)
{
return platform_driver_register(&gpio_keys_device_driver);
struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
.driver = {
.name = "gpio-keys",
}
};
}
// 平臺設備匹配後調用 probe 函數
static int __devinit gpio_keys_probe//(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
int i, error;
////////////////////////////////////////////////////////
// 1. 分配 input_dev
// 爲新的輸入設備分配內存
input = input_allocate_device();
struct input_dev *input_allocate_device//(void)
{
struct input_dev *dev;
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
dev->cdev.class = &input_class;
dev->cdev.groups = input_dev_attr_groups;
class_device_initialize(&dev->cdev);
mutex_init(&dev->mutex);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
__module_get(THIS_MODULE);
}
return dev;
}
if (!input)
return -ENOMEM;
platform_set_drvdata(pdev, input);
////////////////////////////////////////////////////////
// 2. 設置 input_dev
// 能產生哪類事件
input->evbit[0] = BIT(EV_KEY);
input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
// 匹配用的 id
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
for (i = 0; i < pdata->nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
int irq = gpio_to_irq(button->gpio);
unsigned int type = button->type ?: EV_KEY;
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
// 註冊按鍵中斷,當有按鍵發生時調用
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM, button->desc ? button->desc : "gpio_keys", pdev);
if (error) {
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
irq, error);
goto fail;
}
input_set_capability(input, type, button->code);
}
////////////////////////////////////////////////////////
// 3. 註冊 input_dev
error = input_register_device(input);
int input_register_device//(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);// static 這樣就會自增了,在 bss 段應該
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;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
// 將 input_dev 放入全局鏈表 input_dev_list 中管理
list_add_tail(&dev->node, &input_dev_list);
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
if (!dev->cdev.dev)
dev->cdev.dev = dev->dev.parent;
error = class_device_add(&dev->cdev);
if (error)
return error;
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
// 遍歷系統中的 handler,查找是否有對應的 handle,即設備驅動可以處理這個設備
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, 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;
/* 註冊 input_dev 或 input_handler 時,會兩兩比較左邊的 input_dev 和右邊的 input_handler,
根據 input_handler 的【id_table】判斷這個 input_handler 能否支持這個 input_dev,
如果能支持,則調用 input_handler 的 connect 函數建立"連接"*/
// 比較 handler->id_table 與 dev,
id = input_match_device(handler->id_table, dev);
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;
}
if (!id)
return -ENODEV;
// 調用 handler 的 connect() 函數,將其掛入 handle.input_dev 鏈表中
error = handler->connect(handler, dev, id);
/*
connect 怎麼建立連接?
1. 分配一個 input_handle 結構體
2.
input_handle.dev = input_dev; // 指向左邊的 input_dev
input_handle.handler = input_handler; // 指向右邊的 input_handler
3. 註冊:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
*/
// 以 event 驅動爲例:
static int evdev_connect//(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
// 設置,在驅動層保存驅動程序與設備的對應關係 handle: handler / input dev
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);
if (IS_ERR(cdev)) {
error = PTR_ERR(cdev);
goto err_free_evdev;
}
/* temporary symlink to keep userspace happy */
error = sysfs_create_link(&input_class.subsys.kobj,
&cdev->kobj, evdev->name);
if (error)
goto err_cdev_destroy;
// 只是將其添加到 handle 雙鏈表中,如果有 handler->star ,則會調用。
error = input_register_handle(&evdev->handle);
int input_register_handle//(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
if (error)
goto err_remove_link;
return 0;
err_remove_link:
sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
err_cdev_destroy:
class_device_destroy(&input_class, devt);
err_free_evdev:
kfree(evdev);
evdev_table[minor] = NULL;
return error;
}
input_wakeup_procfs_readers();
return 0;
}
if (error) {
printk(KERN_ERR "Unable to register gpio-keys input device\n");
goto fail;
}
return 0;
fail:
for (i = i - 1; i >= 0; i--)
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
input_free_device(input);
return error;
}
用戶空間系統調用路由
仔細看 input.c 核心,裏面註冊了一個字符設備,然後只聲明瞭一個 open() 函數
static int __init input_init(void)
/////////////////////////////////////////////////////////////////////////////////////////
// static const struct file_operations input_fops = {
// .owner = THIS_MODULE,
// .open = input_open_file,
// };
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
在 open() 函數裏面,將路徑到各通用程序中註冊的 file_operations 結構體
// 打來按鍵,調用的是這裏的打開函數
static int input_open_file(struct inode *inode, struct file *file)
{
// 通過子設備號獲得相應的設備操作函數
struct input_handler *handler = input_table[iminor(inode) >> 5];
/**
* struct input_handler - implements one of interfaces for input devices
* @private: driver-specific data
* @event: event handler
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
* input core right after connect() method and also when a process
* that "grabbed" a device releases it
* @fops: file operations this driver implements
* @minor: beginning of range of 32 minors for devices this driver
* can provide
* @name: name of the handler, to be shown in /proc/bus/input/handlers
* @id_table: pointer to a table of input_device_ids this driver can
* handle
* @blacklist: prointer to a table of input_device_ids this driver should
* ignore even if they match @id_table
* @h_list: list of input handles associated with the handler
* @node: for placing the driver onto input_handler_list
*/
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;
};
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;
/*
* 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;
}
// 替換 file 結構體的 f_op,並保存,這樣在以後操作就是直接操作驅動的 file_operations 結構了
old_fops = file->f_op;
file->f_op = new_fops;
// 調用驅動程序的 open() 函數
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;
}
對應按鍵驅動,相關操作是在 input_register_handler() 中註冊的
// 而這個 open() 是在哪裏設置的呢,根據匹配 id, 知道按鍵設備最終匹配的是 evdev.c 裏的驅動
static int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
// static struct input_handler evdev_handler = {
// .event = evdev_event,
// .connect = evdev_connect,
// .disconnect = evdev_disconnect,
// .fops = &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
// };
// .minor = EVDEV_MINOR_BASE,
// .name = "evdev",
// .id_table = evdev_ids,
// static const struct input_device_id evdev_ids[] = {
// { .driver_info = 1 }, /* Matches all devices */
// { }, /* Terminating zero entry */
// };
// };
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])
return -EBUSY;
input_table[handler->minor >> 5] = handler;
}
// 添加到全局數組中方便管理
list_add_tail(&handler->node, &input_handler_list);
// 遍歷 input_dev 輸入設備鏈表,並匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, 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;
/* 註冊 input_dev 或 input_handler 時,會兩兩比較左邊的 input_dev 和右邊的 input_handler,
根據 input_handler 的【id_table】判斷這個 input_handler 能否支持這個 input_dev,
如果能支持,則調用 input_handler 的 connect 函數建立"連接"*/
// 比較 handler->id_table 與 dev,
id = input_match_device(handler->id_table, dev);
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;
}
if (!id)
return -ENODEV;
// 調用 handler 的 connect() 函數, 將 handler 與對應的 input_dev 連接起來,這裏是鏈接在 event_handler 上面
error = handler->connect(handler, dev, id);
/*
connect 怎麼建立連接?
1. 分配一個 input_handle 結構體
2.
input_handle.dev = input_dev; // 指向左邊的input_dev
input_handle.handler = input_handler; // 指向右邊的input_handler
3. 註冊:
input_handler->h_list = &input_handle;
inpu_dev->h_list = &input_handle;
*/
// 對應按鍵驅動,應該鏈接在 event_handler 下面的鏈表中
static int evdev_connect//(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
// 設置,在驅動層保存驅動程序與設備的對應關係 handle: handler / input dev
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);
if (IS_ERR(cdev)) {
error = PTR_ERR(cdev);
goto err_free_evdev;
}
/* temporary symlink to keep userspace happy */
error = sysfs_create_link(&input_class.subsys.kobj,
&cdev->kobj, evdev->name);
if (error)
goto err_cdev_destroy;
// 只是將其添加到 handle 雙鏈表中,如果有 handler->star ,則會調用。
error = input_register_handle(&evdev->handle);
int input_register_handle//(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;
list_add_tail(&handle->d_node, &handle->dev->h_list);
list_add_tail(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
if (error)
goto err_remove_link;
return 0;
err_remove_link:
sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
err_cdev_destroy:
class_device_destroy(&input_class, devt);
err_free_evdev:
kfree(evdev);
evdev_table[minor] = NULL;
return error;
}
if (error && error != -ENODEV)
printk(KERN_ERR
"input: failed to attach handler %s to device %s, "
"error: %d\n",
handler->name, kobject_name(&dev->cdev.kobj), error);
return error;
}
input_wakeup_procfs_readers();
return 0;
}
}
所以對於按鍵,最終調用的 open() 函數爲:
// 最終調用的 open() 函數爲
static int evdev_open//(struct inode *inode, struct file *file)
{
struct evdev_client *client;
struct evdev *evdev;
int i = iminor(inode) - EVDEV_MINOR_BASE;
int error;
if (i >= EVDEV_MINORS)
return -ENODEV;
evdev = evdev_table[i];
if (!evdev || !evdev->exist)
return -ENODEV;
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client)
return -ENOMEM;
client->evdev = evdev;
// evdev.c 用來管理下轄設備驅動的鏈表
list_add_tail(&client->node, &evdev->client_list);
if (!evdev->open++ && evdev->exist) {
error = input_open_device(&evdev->handle);
if (error) {
list_del(&client->node);
kfree(client);
return error;
}
}
file->private_data = client;
return 0;
}
經過上面的折騰,讀按鍵時則會調用, 真正的讀按鍵了,open() 之後就調用 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;
int retval;
if (count < evdev_event_size())
return -EINVAL;
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
// 等待 event 中斷,如果沒有數據就掛起等待
retval = wait_event_interruptible(evdev->wait, client->head != client->tail || !evdev->exist);
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
// 這裏將獲得的 event 複製到用戶空間去
while (client->head != client->tail && retval + evdev_event_size() <= count) {
struct input_event *event = (struct input_event *) client->buffer + client->tail;
if (evdev_event_to_user(buffer + retval, event))
return -EFAULT;
client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
retval += evdev_event_size();
}
return retval;
}
異步喚醒上報鍵值
用戶例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>
#include<poll.h>
#include<signal.h>
int Oflags;
int buttons_fd;
int key_value,i=0,count;
struct input_event ev_key;
void my_signal_fun(int signum)
{
printf("my_signal_fun: start!\n");
for (;;) {
printf("for()\n");
count = read(buttons_fd,&ev_key,sizeof(struct input_event));
for(i=0; i<(int)count/sizeof(struct input_event); i++)
if(EV_KEY==ev_key.type)
printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);
if(EV_SYN==ev_key.type)
{
printf("syn event\n\n");
break;
}
}
}
int main(void)
{
signal(SIGIO,my_signal_fun);
buttons_fd = open("/dev/input/event0", O_RDWR);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
fcntl(buttons_fd, F_SETOWN, getpid());
Oflags = fcntl(buttons_fd, F_GETFL);
fcntl(buttons_fd, F_SETFL, Oflags | FASYNC);
while(1)
{
sleep(1000);
}
close(buttons_fd);
return 0;
}
驅動上報流程
/* 驅動程序上報按鍵事件
// 在設備的中斷服務程序裏,確定事件是什麼,然後調用相應的 input_handler 的 event 處理函數
gpio_keys_isr//(int irq, void * dev_id)
// 上報事件
input_event(input, type, button->code, !!state);
input_sync(input);
*/
// 上報按鍵事件的函數
// 爲了好看,打亂的順序
//////////////////////////////////////////////////////////
// 第一步
input_event//(input, type, button->code, !!state);
///////////////////////////////////////////////////////////////////////////////////////
// 第二步
static inline void input_sync//(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
/**
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event//(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
if (type > EV_MAX || !test_bit(type, dev->evbit))
return;
add_input_randomness(type, code, value);
// 根據 event 事件類似,做相關處理
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event)
dev->event(dev, type, code, value);
break;
case SYN_REPORT:
if (dev->sync)
return;
dev->sync = 1;
break;
}
break;
case EV_KEY:
if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;
if (value == 2)
break;
// 置位 input_dev.key[] 表示有按鍵發生?
change_bit(code, dev->key);
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
// 這裏的時鐘是 input_register_device() 中設置,對應處理函數爲 input_repeat_key()
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
static void input_repeat_key//(unsigned long data)
{
struct input_dev *dev = (void *) data;
if (!test_bit(dev->repeat_key, dev->key))
return;
input_event(dev, EV_KEY, dev->repeat_key, 2);
input_sync(dev);
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
}
}
break;
case EV_SW:
if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
return;
change_bit(code, dev->sw);
break;
case EV_ABS:
if (code > ABS_MAX || !test_bit(code, dev->absbit))
return;
if (dev->absfuzz[code]) {
if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
return;
if ((value > dev->abs[code] - dev->absfuzz[code]) &&
(value < dev->abs[code] + dev->absfuzz[code]))
value = (dev->abs[code] * 3 + value) >> 2;
if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
(value < dev->abs[code] + (dev->absfuzz[code] << 1)))
value = (dev->abs[code] + value) >> 1;
}
if (dev->abs[code] == value)
return;
dev->abs[code] = value;
break;
case EV_REL:
if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
return;
break;
case EV_MSC:
if (code > MSC_MAX || !test_bit(code, dev->mscbit))
return;
if (dev->event)
dev->event(dev, type, code, value);
break;
case EV_LED:
if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return;
change_bit(code, dev->led);
if (dev->event)
dev->event(dev, type, code, value);
break;
case EV_SND:
if (code > SND_MAX || !test_bit(code, dev->sndbit))
return;
if (!!test_bit(code, dev->snd) != !!value)
change_bit(code, dev->snd);
if (dev->event)
dev->event(dev, type, code, value);
break;
case EV_REP:
if (code > REP_MAX || value < 0 || dev->rep[code] == value)
return;
dev->rep[code] = value;
if (dev->event)
dev->event(dev, type, code, value);
break;
case EV_FF:
if (value < 0)
return;
if (dev->event)
dev->event(dev, type, code, value);
break;
}
if (type != EV_SYN)
dev->sync = 0;
if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
// 遍歷所有的 handle,如果是打開的,則調用其 event 函數,這個 handle 是在 connect 中註冊的。
// 所有的 event0,event1...eventn 他用使用的都是相同的 【evdev_handler】
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
// 有數據時的喚醒位置, 這個函數在 input_event() 那層被調用,即 input.c 函數中
static void evdev_event//(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
if (evdev->grab) {
client = evdev->grab;
//////////////////////////////////////////////////////////////////////
// 在這裏構造要上報給應用層的數據
/* struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
*/
do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
// 異步通知用戶程序,如果有等待的話,其實就是發送信號給那個進程處理,關於信號處理,看我其他博文了
kill_fasync(&client->fasync, SIGIO, POLL_IN);
} else
list_for_each_entry(client, &evdev->client_list, node) {
do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
// 這裏進行喚醒在讀/寫上睡眠的進程
wake_up_interruptible(&evdev->wait);
}
}
}