- Input子系统的关键函数
1,input_allocate_device()
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 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的相关信息(按键码、设备名、支持的事件)
2,input_register_device()
注册一个输入设备,然后设置相关值
__set_bit(EV_SYN, dev->evbit);
__set_bit(KEY_MENU, dev->keybit);
用来标记哪些键值能够使用
3,input_attatch_handler()
匹配 input_dev 和 handler,调用input_match_device()
struct input_device_id {
kernel_ulong_t flags; /*标志信息*/
__u16 bustype; /*总线类型*/
__u16 vendor; /*制造商ID*/
__u16 product; /*产品ID*/
__u16 version; /*版本号*/
kernel_ulong_t driver_info; /*驱动额外的信息*/
}
4,input_match_device()
进行对比id的信息,如果相同,返回id,如果不相同,返回0.
这时候handler->connect(handler,dev,id)开始匹配handler和dev。
PS:只有当 input device和input handler 的 ID 成员在 evbit、keybit、… swbit 项相同才会匹配成功。而且匹配的顺序是从evbit、keybit到swbit。只要有一项不同,就会循环到ID中的下一项进行比较。
- 对于input 上报事件(键值)
1,先上报一个按键事件,然后上报一个同步事件,完成一个按键的发生
Input_report_key(dev->input_dev,KEY_HOME,1);
Input_sunc(dev->input_dev);
2,input_report_key调用input_event。在乎input_event中存在一个if
if (is_event_supported(type, dev->evbit, EV_MAX))
判断事件是否支持type类型,这个要在事先进行注册,如__set_bit(EV_SYN, dev->evbit);确定事件支持同步,__set_bit(EV_KEY, dev->evbit);确定事件支持按键
spin_lock_irqsave(&dev->event_lock, flags);
中断保护自旋锁,保护一次input event事件上报完成后,屏蔽掉本地的中断事件,直到解锁。即input_handle_event(dev, type, code, value);
3,input_handle_event(dev, type, code, value);
首先通过 disposition去判断事件的处理方式
如果是ignore,调用add_input_randomness,对随机数熵池增加事件(对事件发生没有用处)
如果是pass to device,代表事件给device去处理
如果是pass to handlers,代表事件给handler去处理(一般事件成功都是给handler处理),其中INPUT_SLOT,代表这个是多点输入设备
如果是INPUT_FLUSH代表这个事件会被下一次事件覆盖。
当事件是能被下一次按键覆盖的事件时,将会调用 input_pass_values,进而调用input_to_handler。
4,input_to_handler
先经过handler->filter过滤掉值,然后经过handler->event(s)将事件传递
上层可以通过如下查看内核的消息
cat /dev/input/event(x) |hexdump
前四列代表 触发的时间点,
第六列表示上报事件: 0001 --> EV_KEY; 0000 ---> EV_SYN
第七列表示键值: 0004 --> KEY_3
第八列表示按键事件: 0001 --> PRESS, 0000 --> RELEASE
# cat /proc/bus/input/devices 查看输入设备类型
getevent event(x)//adb 查看event事件
- IOCTL控制
int ioctl(int fd,unsigned long cmd,...);
/*
fd:文件描述符
cmd:控制命令
...:可选参数:插入*argp,具体内容依赖于cmd
*/
int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);
/*
inode与filp两个指针对应于应用程序传递的文件描述符fd,这和传递open方法的参数一样。
cmd 由用户空间直接不经修改的传递给驱动程序
arg 可选。
*/
//nr为序号,datatype为数据类型,如int
_IO(type, nr ) //没有参数的命令
_IOR(type, nr, datatype) //从驱动中读数据
_IOW(type, nr, datatype) //写数据到驱动
_IOWR(type,nr, datatype) //双向传送
Ioctl匹配设备和命令,如果不符合的话,返回-EINVAL错误值
用户使用 int ioctl(int fd,unsinged long cmd,...) 时,这个...就是要传递的参数;
再通过 int (*ioctl)(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) 中的arg传递;
如果arg是一个整数,可以直接使用;
如果是指针,我们必须确保这个用户地址是有效的,因此,使用之前需要进行正确检查。
内部有检查的,不需要检测的:
实例
xxx.C
#include <stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
fd = open("/dev/xxx",O_RDWR);
if(fd <0)
{
printf(“error \n”);return -1
}
if (ioctl(fd, cmd, &arg) < 0)
{
printf("Call cmd \n");
return -1;
}
return 0;
}