一些关于输入子系统的软件流程,包括核心层、事件驱动层、设备驱动层的一些关系。上编总结下了怎么去设计一个设备驱动层的代码流程:
1. 分配一个input_dev结构体(input_allocate_device)
2.设置 包括设置是哪类事件等
3.注册 input_register_device
4.硬件相关的初始化
下 针对按键驱动的简单例子:
- static int __init button_init(void) /*加载函数*/
- {
- int error;
- 1.button_dev = input_allocate_device(); /*分配一个设备结构体*/
- if (!button_dev) /*判断分配是否成功*/
- {
- printk(KERN_ERR "button.c: Not enough memory\n");
- error = -ENOMEM;
- goto err_free_irq;
- }
- 2.button_dev->evbit[0] = BIT_MASK(EV_KEY);/*设置按键信息*/
- button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
- 3.error = input_register_device(button_dev); /*注册一个输入设备*/
- if (error)
- {
- printk(KERN_ERR "button.c: Failed to register device\n");
- goto err_free_dev;
- }
- 4.if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL))
- /*申请中断处理函数*/
- {
- /*申请失败,则打印出错信息*/
- printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
- return -EBUSY;
- }
- }
这 这个例子其实比较简单,分配、设置、注册和硬件的一些初始化。下面对一些重要的函数进行分析下
- struct input_dev *input_allocate_device(void)
- {
- struct input_dev *dev;
- dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);/*分配一个 input_dev 结构体,并初始化为 0*/
-
- if (dev) {
- dev->dev.type = &input_dev_type;/*初始化设备的类型*/
- dev->dev.class = &input_class;/*设置为输入设备类*/
- device_initialize(&dev->dev);/*初始化 device 结构*/
- mutex_init(&dev->mutex);/*初始化互斥锁*/
- spin_lock_init(&dev->event_lock);/*初始化事件自旋锁*/
- INIT_LIST_HEAD(&dev->h_list);/*初始化链表*/
- INIT_LIST_HEAD(&dev->node);/*初始化链表*/
- __module_get(THIS_MODULE);/*模块引用技术加 1*/
- }
- return dev;
- }
该函数返回一个指向input_dev类型的指针,包括输入设备的一些相关的信息,如支持的按键码,设备的名称,设备支持的事件类型等。对于按键的获取是从中断服务程序里面做的
- static irqreturn_t button_interrupt(int,irq,void *dummy)/*中断处理函数*/
- {
- input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
- /*向输入子系统报告产生按键事件*/
- input_sync(button_dev);
- /*通知接收者,一个报告发送完毕*/
- return IRQ_HANDLED;
- }
- static inline void input_report_key(struct input_dev *dev,unsigned int
- code, int value)
- {
- input_event(dev, EV_KEY, code, !!value);
- }
该
函数的第 1 个参数是产生事件的输入设备, 第2 个参数是产生的事件, 第3 个参数是事件的值。需要注意的是, 2 个参数可以取类似 BTN_0、 BTN_1、BTN_LEFT、BTN_RIGHT 等值,这些键值被定义在include/ linux/input.h 文件中。当第 2 个参数为按键时,第 3 个参数表示按键的状态,value 值为 0 表示按键释放,非 0 表示按键按下。
在input_report_key函数中主要是调用input_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);//主要是按键输入的一种随机事件
- 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:
- //调用test_bit()函数来测试按键的状态是否有改变
- if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
- return;
- if (value == 2)
- break;
- 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;
- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));//检测重复的按键
- }
- 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;
//调用handler的event函数
- if (dev->grab)
- dev->grab->handler->event(dev->grab, type, code, value);
- else
- list_for_each_entry(handle, &dev->h_list, d_node)
- if (handle->open)
- handle->handler->event(handle, type, code, value);
- }
由上面可以知道,如果input device被强制指定了handler,则调用该handler的eventh函数,如果没有指定handler,就会遍历input device->h_list上的handle成员,如果handle被打开,则调用与输入设备对应的handler的event函数。
是 上面是整个按键的注册和事件的上报处理过程,下面来分析下evdev,evdev对应的设备节点一般对应于/dev/input/event0---dev/input/event4,理论上可以对应32个设备节点,由input_table[handler->minor >> 5] 决定,一般可以用cat /dev/input/event(),然后移动鼠标或者键盘就有数据输出。关于核心层得input_register_handler和核心层的一些注册和操作在第一章已经梳理了,那么不做梳理了。由一可以知道匹配的成功在于handler中的blacklist和id_table,evdev的id_table定义如下:
- static const struct input_device_id evdev_ids[] = {
- { .driver_info = 1 }, /* Matches all devices */
- { }, /* Terminating zero entry */
- };
它没有定义flags,也没有定义匹配的属性项,这个handler匹配所有的input device。
对于主设备为INPUT_MAJOR的设备节点,由input_register_handler会将转化成handler的evdev_fops,对应的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; //得到evdev_table[]序列
- int error;
- if (i >= EVDEV_MINORS)
- return -ENODEV;
- evdev = evdev_table[i];//取出对应的evdev取出
- if (!evdev || !evdev->exist)
- return -ENODEV;
- client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); //分配一个client
- if (!client)
- return -ENOMEM;
- client->evdev = evdev;
- list_add_tail(&client->node, &evdev->client_list);//初始化并将它和evdev关联起来,将client->evdev指向它所表示的evdev,将client挂到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;//将client赋值为私有
- return 0;
- }
如果evdev是第一次打开,对应的会打开input_open_device,完成handle的打开。
- int input_open_device(struct input_handle *handle)
- {
- struct input_dev *dev = handle->dev;
- int err;
- err = mutex_lock_interruptible(&dev->mutex);
- if (err)
- return err;
- handle->open++;
- if (!dev->users++ && dev->open)
- err = dev->open(dev);
- if (err)
- handle->open--;
- mutex_unlock(&dev->mutex);
- return err;
- }
对于新赋值的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
- };
阅读(892) | 评论(0) | 转发(0) |