S3C2440 输入子系统学习笔记 第一节
接触S3C2440已经有一段时间了,可是总是没有去坚持学习,刚毕业的我深受到自身技能的缺乏和工作中的压力,决定痛改前非,坚持每天下班都去学习,在这里我不敢说自己能把2440学完,因为技术永无止境。但是我相信我能一直坚持下去。我是一个热爱思考,并且将思考的东西通过各种方法实现,我要做我思想的造物主。考虑到每个章节知识容量不一的问题,博客从今天开始不定时更新,前期会根据韦东山老师的视频教程目录来更新,如果有写得不好的地方请大家指点指点。
好了废话不多说,进入正题。
本博客的起点是韦东山老师的第2期的学习视频。 在第一期视频中,我们学到了简单的驱动结构,比如 LED驱动, 按键驱动等,但是这些驱动仅仅适合我们平时使用,没有办法让别人去调用,可如果要写通用的驱动程序的话,就要使用现成的驱动——input 输入子系统。
首先回顾一下以前,按照韦老大的方式写一个驱动框架:
1、确定主设备好
2、构造一个file_operations结构体 结构体中包含有常用的接口, 如open、 write、 read 等等
3、接下来时注册一个字符设备驱动程序(register_chrdev)
4、入口函数、 出口函数、 加修饰。
在输入子系统中大体也是这个框架,区别在于在输入子系统中这些框架是内核驱动开发这写好的。我们要做的就是把我们的代码融合到里边去。
输入子系统的核心时input.c(目录: /linux/driver/input/input.c) 现在我们开始分析系统框架:
暂时没有框架图哈。
输入子系统的核心是input.c 我们从入口函数着手 代码如下,我们看到入口函数中的 err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 这是注册一个名为input的设备,设备号为INPUT_MAJOR, input_fops是函数调用接口。
static int __init input_init(void) { int err; err = class_register(&input_class); if (err) { printk(KERN_ERR "input: unable to register input_dev class\n"); return err; } err = input_proc_init(); if (err) goto fail1; err = register_chrdev(INPUT_MAJOR, "input", &input_fops); if (err) { printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); goto fail2; } return 0; fail2: input_proc_exit(); fail1: class_unregister(&input_class); return err; }
接下来我们来看input_fops提供了哪些调用接口呢?
static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file,//这里边仅仅提供了一个open接口,这样的接口函数仅仅存在一个接口, 猜测input_open_file 中应该有隐含的接口 };
再看看input_open_file函数。
static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler = input_table[iminor(inode) >> 5]; //根据打开文件的次设备号得到一个input_handler const struct file_operations *old_fops, *new_fops = NULL; int err; /* No load-on-demand here? */ if (!handler || !(new_fops = fops_get(handler->fops))) //新的fileoperlations 结构体 = 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; } old_fops = file->f_op; file->f_op = new_fops; //将新文件的f_ops 赋给 文件的f_op ????? 没理解。。。 err = new_fops->open(inode, file); //最后调用文件的open 函数; 最终应用程序read 函数调用的是 file->f_op->read函数. if (err) { fops_put(file->f_op); file->f_op = fops_get(old_fops); } fops_put(old_fops); return err; }
input_table是由input_handler 定义来的数组.
static struct input_handler *input_table[8]
注册input_handler 做的事情
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; //将handler 放入数组中
}
list_add_tail(&handler->node, &input_handler_list); // 放入链表中
list_for_each_entry(dev, &input_dev_list, node) //对于没个device_input 放入连表中
input_attach_handler(dev, handler);
input_wakeup_procfs_readers();
return 0;
接下来看看注册input_register_devide做的事情
int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); 。。。。 。。。。。
ist_add_tail(&dev->node, &input_dev_list); // 放入链表中
。。。。。。
list_for_each_entry(handler, &input_handler_list, node) //对handler链表里边的每一个项目都调用input_attach_handler
input_attach_handler(dev, handler); //根据input_handler的id_table判断是否支持input_dev
从input_register_device 和 input_register_handler中可以看出,不论我们先注册device 还是handler 最后都会调用到input_attach_handler。 继续向下分析input_attach_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; id = input_match_device(handler->id_table, dev); //根据handler->id_table 和输入设备比较, 看看handler是支持,是否匹配 if (!id) return -ENODEV; error = handler->connect(handler, dev, id); //如果匹配就调用handler中的connect函数 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_dev 或input_handler时,会两两比较左边的和右边的input_handler,根据input_handler的id_table 判断input_handler是否能支持input_drv, 如果支持,则调用input_handler 的connect函数进行链接。
下面了解一下时怎么建立链接的。
以evdev_connect为例 先进入evdev.c 找到evdev_handler结构体,代码如下:
static struct input_handler evdev_handler = { .event = evdev_event, .connect = evdev_connect, //这是evdev的链接函数,进入evdev_connect函数static int evdev_connect .disconnect = evdev_disconnect, .fops = &evdev_fops, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, };
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;
.....
.....
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一个input_handle
//设置
evdev->handle.dev = dev; //指向左边的input_dev
evdev->handle.name = evdev->name;
evdev->handle.handler = handler; //指向右边的input_handler
evdev->handle.private = evdev;
//注册
error = input_register_handle(&evdev->handle);
// 跟进input_register_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); //将设备和input_handle->dev建立关系
list_add_tail(&handle->h_node, &handler->h_list); //将系统的handler_list和input_handle结构体挂钩,
//这样就可以通过input_handle结构体将设备和系统联系到一起
if (handler->start)
handler->start(handle);
return 0;
}
小结: 1、分配一个inout_hanfle结构体
2、 input_handle.dev = input_dev; //指向右边的input_dev
input_handle.handler = input_handler; //指向右边的input_handler
3、注册
input_handler->h_list = &input_handle;
input_dev->h_list = &input)handle;
数据的读取:
应用程序读取程序通过read函数读取数据的时候,会调用到evdev_handler(仅仅时以evdev为例)
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 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,
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;
//休眠
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
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;
.....
.....
wake_up_interruptible(&evdev->wait); //唤醒操作
}
那么evdev_event 被谁调用呢? 猜测时硬件相关的代码,input_dev调用的, 在设备的中断服务程序中,确定是什么事件,然后再调用相应的input_handler 的event处理函数
以 gpio_key_isr为例
static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { int i; struct platform_device *pdev = dev_id; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct input_dev *input = platform_get_drvdata(pdev); for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; int gpio = button->gpio; if (irq == gpio_to_irq(gpio)) { unsigned int type = button->type ?: EV_KEY; int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; input_event(input, type, button->code, !!state); //上报事件 input_sync(input); } }
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))
......
......
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value); //event函数被调用。
哈哈,写完了,虽然时跟着视频写的博客,但是我觉的我理解得更深刻了,不像平时看视频那样效率低。貌似是个学习的好法子。继续加油!!!!!!