一. input子系统的作用和框架
1. 输入设备的分类---按照数据类性分类
1> 按键类型的数据 --- 键值
button keyboard
2> 绝对坐标类型的数据 --- 有具体值,并且有最大值和最小值
触摸屏(ts/tp) gensor 陀螺仪
3> 相对坐标类型的数据 --- 相对上一个坐标的数据
鼠标
2. 作用
a. 简化了字符设备驱动的编写流程(申请设备号,创建类,创建设备节点,都不用我们自己做了,内核已经帮我们实现了)
b. 统一了应用程序读取输入设备的方式,不管是ts、gensor、keyboard,应用程序编程的方式是一样的
3. 框架
应用层:
app:xxx.c
-----------------------------------------------------
驱动层:
input_handler层---数据处理:/linux-3.0.8/drivers/input/evdev.c
//知道如何把数据给用户,但是不知道如何获取数据
//主要实现fops,和用户层进行交互
-----------------------------------------------
input_core层---核心层:/linux-3.0.8/drivers/input/input.c
//维护框架,负责对上层和下层提供接口
-----------------------------------------------
input_dev层---输入设备层:自己写
//知道如何采集数据,但是不知道如何把数据给到用户
//初始化设备,获取设备数据,并且上报数据
二. input子系统的编程
1. 确保input子系统的上面两层的代码被编译到内核
Device Drivers --->
Input device support --->
-*- Generic input layer (needed for keyboard, mouse, ...)
<*> Event interface
//重新编译内核
make zImage -j4
//拷贝到/tftpboot目录
cp -raf arch/arm/boot/zImage /tftpboot/
2. 代码实现(//参考:/linux-3.0.8/Documentation/input/input-programming.txt)
1> 重要的结构体
struct input_dev {
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//能够产生那种类型的数据
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//能够产生这种类型下面的哪些数据
unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
}
struct input_event {
struct timeval time; //时间
__u16 type; //数据类型
__u16 code; //键
__s32 value; //值
};
2> 编程步骤
// 1.创建全局的结构体对象
button = kzalloc(sizeof(struct s5pv210_button), GFP_KERNEL);
// 2.创建input_dev对象
button->dev = input_allocate_device();
// 3.初始化input_dev对象
button->dev->evbit[0] |= BIT_MASK(EV_KEY);//(1 << 1)
button->dev->keybit[BIT_WORD(KEY_UP)] |= BIT_MASK(KEY_UP);//(1 << (103 % 32))
// 4.注册input_dev对象
ret = input_register_device(button->dev);
// 5.注册中断
button->irq = IRQ_EINT(0);
ret = request_irq(button->irq, input_button_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_up", NULL);
// 6.中断处理函数中获取数据并上报
irqreturn_t input_button_handler(int irq, void * data)
{
value = gpio_get_value(S5PV210_GPH0(0));
if(value)//松开
{
input_report_key(button->dev, KEY_UP, 0);
input_sync(button->dev);
}
}
三. input子系统和平台总线结合
方法:
先实现平台总线,再在平台驱动的probe方法中使用input子系统替代之前的初级驱动代码
//平台自定义数据的传递方式
//方法一:
struct platform_device pdev = {
.name = "s5pv210_buttons",
.id = -1,
.dev = {
.release = input_button_dev_release,
.platform_data = &btn_input_data,
},
};
//方法二:
void __init input_button_dev_set_platdata(struct btn_platdata *pd)
{
struct btn_platdata *npd;
if (!pd) {
printk(KERN_ERR "%s: no platform data\n", __func__);
return;
}
npd = kmemdup(pd, sizeof(struct btn_platdata), GFP_KERNEL);
if (!npd)
{
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
return;
}
pdev.dev.platform_data = npd;
}
//调用该函数
static int __init input_button_dev_init(void)
{
input_button_dev_set_platdata(&btn_input_data);
return platform_device_register(&pdev);
}
四. input子系统内核代码实现
应用层:
app:xxx.c
-----------------------------------------------------
驱动层:
input_handler层---数据处理:/linux-3.0.8/drivers/input/evdev.c
//知道如何把数据给用户,但是不知道如何获取数据
//主要实现fops,和用户层进行交互
module_init(evdev_init);
|
static int __init evdev_init(void)
|
input_register_handler(&evdev_handler);
|
//初始化evdev_handler中的h_list链表
INIT_LIST_HEAD(&handler->h_list);
//判断input_table数组下标为2的元素是不是为空,为空的则将evdev_handler放入下标为2的地方
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
//把evdev_handler注册到input_handler_list链表中
list_add_tail(&handler->node, &input_handler_list);
//然后再去遍历input_dev_list
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
|
id = input_match_device(handler, dev);
error = handler->connect(handler, dev, id);
-----------------------------------------------
input_core层---核心层:/linux-3.0.8/drivers/input/input.c
//维护框架,负责对上层和下层提供接口
//初始化两个链表
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
subsys_initcall(input_init);
|
static int __init input_init(void)
|
//创建类
err = class_register(&input_class);
//注册设备号
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
.llseek = noop_llseek,
};
-----------------------------------------------
input_dev层---输入设备层:自己写
//知道如何采集数据,但是不知道如何把数据给到用户
//初始化设备,获取设备数据,并且上报数据
//创建input_dev对象
button->dev = input_allocate_device();
//初始化input_dev对象
button->dev->name = "s5pv210_key_up";
__set_bit(EV_KEY, button->dev->evbit);
set_bit(key_info->event.code, button->dev->keybit);
//注册input_dev对象
ret = input_register_device(button->dev);
|
//设置了EV_SYN
__set_bit(EV_SYN, dev->evbit);
//把input_dev添加到input_dev_list
list_add_tail(&dev->node, &input_dev_list);
//然后去遍历input_handler_list
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
|
id = input_match_device(handler, dev);
error = handler->connect(handler, dev, id);
2.从调用过程看代码
open()
--------------------------------
sys_open()
|
static int input_open_file(struct inode *inode, struct file *file)
|
//获取input_table中的input_handler对象
handler = input_table[iminor(inode) >> 5];
if (handler)
//把input_handler对象中的fops给new_fops
new_fops = fops_get(handler->fops);
old_fops = file->f_op;
//让file->fops指向input_handler对象中的fops
file->f_op = new_fops;
err = new_fops->open(inode, file);
|
static int evdev_open(struct inode *inode, struct file *file)
|
//根据次设备号找到evdev_table数组的下标,进而获取里面的evdev对象
int i = iminor(inode) - EVDEV_MINOR_BASE;
evdev = evdev_table[i];
//计算要给当前的input_dev对应的设备分配多大的input_event数组
bufsize = evdev_compute_buffer_size(evdev->handle.dev);
//给input_event数组分配空间
client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event),GFP_KERNEL);
//建立evdev_client和evdev之间的双向关系
client->evdev = evdev;
evdev_attach_client(evdev, client);
|
list_add_tail_rcu(&client->node, &evdev->client_list);
//为了把evdev_client对象传递给read函数
file->private_data = client;
read()
--------------------------------
sys_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;
retval = wait_event_interruptible(evdev->wait, client->packet_head != client->tail || !evdev->exist);
while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event))
|
//从client->buffer队列里面取数据
*event = client->buffer[client->tail++];
{
if (input_event_to_user(buffer + retval, &event))//给到用户空间
return -EFAULT;
retval += input_event_size();
}
//数据上报
input_event(button->dev, EV_KEY, event.code, event.value);
|
input_handle_event(dev, type, code, value);
|
input_pass_event(dev, type, code, value);
|
//通过input_dev找到input_handle
handle = rcu_dereference(dev->grab);
//通过input_handle找到input_handler,进而调用input_handler里面的event方法
if (handle)
handle->handler->event(handle, type, code, value);
|
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
|
//通过input_handle对象找到evdev对象
struct evdev *evdev = handle->private;
//创建一个struct input_event对象,并赋值
struct input_event event;
event.type = type;
event.code = code;
event.value = value;
//通过evdev对象找到evdev_client对象
client = rcu_dereference(evdev->grab);
evdev_pass_event(client, &event);
|
client->buffer[client->head++] = *event;
//唤醒等待队列头,解除阻塞状态
input_sync(button->dev);
|
input_handle_event(dev, type, code, value);
|
input_pass_event(dev, type, code, value);
|
//通过input_dev找到input_handle
handle = rcu_dereference(dev->grab);
//通过input_handle找到input_handler,进而调用input_handler里面的event方法
if (handle)
handle->handler->event(handle, type, code, value);
|
static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
|
if (type == EV_SYN && code == SYN_REPORT)
wake_up_interruptible(&evdev->wait);