Table of Contents
2.1 input设备的注册(/drivers/input/input.c)
1、文件系统和设备的关系
设备是特殊的文件,文件inode的操作函数通过init_special_inode赋值。
ext2_read_inode ->init_special_inode(适合ext2文件系统)
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
____inode->i_mode = mode;
____if (S_ISCHR(mode)) {
________inode->i_fop = &def_chr_fops;
________inode->i_rdev = rdev;
____} else if (S_ISBLK(mode)) {
________inode->i_fop = &def_blk_fops;
________inode->i_rdev = rdev;
____} else if (S_ISFIFO(mode))
________inode->i_fop = &def_fifo_fops;
____else if (S_ISSOCK(mode))
________inode->i_fop = &bad_sock_fops;
____else
________printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o)\n",
________ mode);
}
2. input设备的架构分析
2.1 input设备的注册(/drivers/input/input.c)
input设备只是提供了驱动的框架。
1) input_init->register_chrdev->__register_chrdev_region :注册设备区间(0-256)。首先申请一个struct char_device_struct,然后加入到全局的chrdevs中。
static struct char_device_struct {
____struct char_device_struct *next;
____unsigned int major;
____unsigned int baseminor;
____int minorct;
____char name[64];
____struct file_operations *fops;
____struct cdev *cdev;______/* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
2) input_init->register_chrdev->cdev_add->kobj_map:注册设备。实际是把复合设备号注册到系统,根据设备号,生成对应的struct probe,然后加入到cdev_map全局变量中。在字符设备驱动的open函数(chrdev_open)中会根据设备号在cdev_map中查找(kobj_lookup)对应的probe,然后执行真正的驱动所对应的open函数。
2.2 input设备的架构分析
open过程:在input对应的open中根据设备的次设备号,查找对应的input_handler,然后把设备的file->fop替换handler->fops。
2.2.1 注册input设备驱动
if (handler->fops != NULL)
____input_table[handler->minor >> 5] = handler;
input_register_handler->list_add_tail(&handler->node, &input_handler_list);
list_for_each_entry(dev, &input_dev_list, node)
____if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
________if ((id = input_match_device(handler->id_table, dev)))
____________if ((handle = handler->connect(handler, dev, id))) {
________________input_link_handle(handle);
________________if (handler->start)
____________________handler->start(handle);
____________}
2.2.2 注册input设备
input_register_device->
INIT_LIST_HEAD(&dev->h_list);
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)
____if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
________if ((id = input_match_device(handler->id_table, dev)))
____________if ((handle = handler->connect(handler, dev, id))) {
________________input_link_handle(handle);
________________if (handler->start)
____________________handler->start(handle);
____________}
3、总结:input驱动框架分两层:input核心层(input.c)、设备(device)和驱动层(driver)。
核心层提供:
3.1 register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
____.owner = THIS_MODULE,
____.open = input_open_file,
};
在input_open_file()函数中会替换handler中的fops, new_fops = fops_get(handler->fops);
file->f_op = new_fops;然后执行新的fops中的打开函数 new_fops->open(inode, file);
handler怎么来的?
是通过设备的次设备号,在input_table数组中获得,handler = input_table[iminor(inode) >> 5];
input_table又是由谁赋值的?
通过下面的input_register_handler实现的。input_table[handler->minor >> 5] = handler;
两个注册函数、两个链表。
3.2 两个链表:input_handler_list,input_dev_list
3.3 两个注册函数:
input_register_handler:注册input_handler到input_handler_list
input_register_device:注册input_dev到input_dev_list
当设备和驱动匹配后,会调用handler->start()函数。
同时input框架也提供了,input_event()函数。
input_event用在什么位置呢? 应该是处理硬件相关的代码中。在设备的中断服务程序里,确定发生的是什么事件,然后调用handle中对应的event处理函数,处理事件。
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int
value)
{
......
list_for_each_entry(handle, &dev->h_list, d_node)
____if (handle->open)
________handle->handler->event(handle, type, code, value);
.......
}
以drivers/input/evdev.c中的使用为例:应用程序在读设备的过程中会调用类似evdev_read的函数,读的过程中如果是Blocked会等待,直到evdev_event发生。
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
};
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()
{
init_waitqueue_head(&evdev->wait);
}
evdev_read()
{
retval = wait_event_interruptible(evdev->wait,
____client->head != client->tail || !evdev->exist);
}
evdev_event()
{
wake_up_interruptible(&evdev->wait);
}
4、如何写一个input框架的驱动程序
以drivers/input/keyboard/gpio_keys.c为例。
a.
struct platform_driver gpio_keys_device_driver = {
____.probe______= gpio_keys_probe,
____.remove_____= __devexit_p(gpio_keys_remove),
____.driver_____= {
________.name___= "gpio-keys",
____}
};
static int __init gpio_keys_init(void)
{
____return platform_driver_register(&gpio_keys_device_driver);
}
b.gpio_keys_probe()
{
/* 1、分配一个input_dev */
input = input_allocate_device();
/* 2、设置input_dev */
input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
/* 3、注册input_dev */
input_register_device(input);
}
/* 4、硬件相关的部分,中断处理程序中上报事件*/
request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
button->desc ? button->desc : "gpio_keys",
pdev);
gpio_keys_isr()
{
input_event(input, type, button->code, !!state);
input_sync(input);
}