一,input子系统的作用和框架
1,输入设备的分类-----按产生的数据
1》按键设备------键值,按键状态等
keypad button
2》绝对坐标设备 ------x坐标,y坐标等
触摸屏,陀螺仪,gsonser等
3》相对坐标设备 ------ x坐标,y坐标等
鼠标
2,input子系统作用
1》规范了输入设备驱动的统一的管理(编写)方式
2》给应用层提供统一的数据格式
3,input子系统的框架
应用层:
app.c
-----------------------------------------------
驱动框架:------input子系统
input-handler层:-----drivers/input/evdev.c
作用:
1,与应用层进行交互
2,实现fops中的接口
--------------------------------------------
input-core层: -------- drivers/input/input.c
作用:
1,维护整个input子系统
2,给上下两层提供实现接口
3,申请设备号 ----主设备:13
--------------------------------------------
input-dev层: ---------自己编写
作用:
1,与硬件进行交互
2,实现硬件的初始化
二,input子系统驱动编程 ------key为例
1,确保input子系统上两层的代码被编译进内核镜像 -----zImage中
make menuconfig
Device Drivers --->
Input device support ---> //drivers/input/input.c
<*> Event interface //drivers/input/evdev.c
重新编译内核,并更新:
make -j2 zImage
cp arch/arm/boot/zImage /tftpboot/
2,实现input-dev层代码 -----------// 参考:Documentation/input/input-programming.txt
实现步骤:
1》分配input_dev 对象空间
2》初始化input_dev对象
3》注册input_dev对象
4》申请中断
5》实现中断处理函数
//input_dev层框架实现
参考代码: input_simple_v1
//input_dev对象的类型
struct input_dev {
//辅助信息
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //标记当前的输入设备是哪种类型(使能)
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //表示能够产生哪个按键的信息,如: KEY_UP,KEY_DOWN
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //表示能够产生相对坐标设备中哪个坐标信息,如:x坐标,y坐标
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //表示能够产生绝对坐标设备中哪个坐标信息,如:x坐标,y坐标
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
struct device dev; //父类
struct list_head h_list;
struct list_head node;
};
//编译并在开发板中加载:
[root@farsight /drv_module]# insmod input_simple_key_drv.ko
--------------^_^ input_simple_key_drv_init---------------
input: Unspecified device as /devices/virtual/input/input0
[root@farsight /drv_module]# ls -l /dev/event0
crw-rw---- 1 0 0 13, 64 Jan 1 00:00 /dev/event0 //加载input-dev层驱动模块,会自动创建设备节点
//上报数据
参考代码: input_simple_v2
//上报的数据类型
struct input_event {
struct timeval time; //时间戳
__u16 type; //输入设备类型类型
__u16 code; //数据类型
__s32 value; //具体的数值
};
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
//参数1 ----- input_dev 对象
//参数2 ----- 设备类型
//参数3 ----- 数据类型
//参数4 ----- 具体的数值
//编译并在开发板中加载:
[root@farsight /drv_module]# insmod input_simple_key_drv.ko
--------------^_^ input_simple_key_drv_init---------------
input: Unspecified device as /devices/virtual/input/input2
[root@farsight /drv_module]# ls -l /dev/event0
crw-rw---- 1 0 0 13, 64 Jan 1 00:38 /dev/event0
[root@farsight /drv_module]# ./input_app
--------------^_^ key_interrupt_handler---------------
按下--->下键
--------------^_^ key_interrupt_handler---------------
--------------^_^ key_interrupt_handler---------------
--------------^_^ key_interrupt_handler---------------
三,input子系统框架代码跟读
1,按框架的层跟读
input_handler层:drivers/input/evdev.c
module_init(evdev_init);
module_exit(evdev_exit);
|
static int __init evdev_init(void)
|
return input_register_handler(&evdev_handler);
|
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops, //fops对象
.minor = EVDEV_MINOR_BASE, //#define EVDEV_MINOR_BASE 64 ------次设备号的最小值
.name = "evdev",
.id_table = evdev_ids,
};
int input_register_handler(struct input_handler *handler)
{
//1,初始化链表
INIT_LIST_HEAD(&handler->h_list);
//2,记录handler对象到数组:input_table
input_table[handler->minor >> 5] = handler;
//3,将handler对象插入到链表:input_handler_list
list_add_tail(&handler->node, &input_handler_list);
//4,遍历input_dev_list链表,与input_handler对象进行匹配
list_for_each_entry(dev, &input_dev_list, node)
input_attach_handler(dev, handler);
|
id = input_match_device(handler, dev); //匹配input_dev与input_handler
error = handler->connect(handler, dev, id); //匹配成功,调用input_handler中的connect函数
}
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
|
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
{
//1,在数组evdev_table中找到一个空的位置
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
//2,分配了struct evdev对象的空间
struct evdev *evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
//3,初始化相关的数据
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
//4,设置设备节点名称: ls /dev/event0
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
evdev->minor = minor;
//5,将input_dev对象和input_handler对象记录到handle中
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
//6,创建设备节点
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
device_initialize(&evdev->dev);
error = device_add(&evdev->dev);
//7,注册handle对象
error = input_register_handle(&evdev->handle);
//8,将evdev对象的地址保存到数组:evdev_table中
error = evdev_install_chrdev(evdev);
|
evdev_table[evdev->minor] = evdev;
}
//创建设备节点函数跟读device_create()
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
|
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
{
//1,分配设备对象空间
struct device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
retval = device_register(dev);
|
device_initialize(dev);
return device_add(dev);
}
------------------------------------------------------------------------------
input_core层:drivers/input/input.c
subsys_initcall(input_init);
module_exit(input_exit);
|
static int __init input_init(void)
|
//1,注册类
err = class_register(&input_class);
//2,初始化/proc中进程相关数据
err = input_proc_init();
//3,申请设备号
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层:
module_init(input_simple_key_drv_init);
module_exit(input_simple_key_drv_exit);
|
//1》分配input_dev 对象空间
key_dev = input_allocate_device();
//2》初始化input_dev对象
key_dev->evbit[0] = BIT_MASK(EV_KEY);
key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
//3》注册input_dev对象
ret = input_register_device(key_dev);
|
//1,将input_dev对象插入到链表:input_dev_list
list_add_tail(&dev->node, &input_dev_list);
//2,遍历链表:input_handler_list,与input_dev对象匹配
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
|
id = input_match_device(handler, dev); //匹配input_dev与input_handler
error = handler->connect(handler, dev, id); //匹配成功,调用input_handler中的connect函数
//4》申请中断
irqno = IRQ_EINT(1);
if (request_irq(irqno, key_interrupt_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_eint1", NULL))
//保存设备数据的对象:struct evdev_client
struct evdev_client {
unsigned int head;
unsigned int tail;
unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
unsigned int bufsize;
struct input_event buffer[]; //数据包的数组--柔性数组,或者可伸缩的数组,只能用于结构体中,必须作为结构体的最后一个成员。
};
2,按应用到驱动调用过程跟读 ----open()函数
应用层:
if((fd = open("/dev/event0",O_RDWR)) < 0){
------------------------------------------------------
内核:
VFS: sys_open(), sys_read(),.....
驱动:
err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
|
static const struct file_operations input_fops = {
.open = input_open_file,
|
const struct file_operations *old_fops, *new_fops = NULL;
//1,从数组:input_table中获取input_handler对象
struct input_handler *handler = input_table[iminor(inode) >> 5];
//2,从input_handler对象获取fops对象
new_fops = fops_get(handler->fops);
//3,将fops对象地址保存到file->f_op
old_fops = file->f_op;
file->f_op = new_fops;
//4,调用input_handler中fops中的open函数
err = new_fops->open(inode, file);
示例代码
1、pdrv
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
static struct input_dev *key_dev;
int irqno;
static irqreturn_t key_interrupt_handler(int irq, void *dummy)
{
int value;
printk("--------------^_^ %s---------------\n",__FUNCTION__);
value = gpio_get_value(S5PV210_GPH0(1));
if(value){
input_report_key(key_dev, KEY_DOWN, 0);
input_sync(key_dev);
}else{
input_event(key_dev,EV_KEY,KEY_DOWN,1);
input_sync(key_dev);
}
return IRQ_HANDLED;
}
static int __init input_simple_key_drv_init(void)
{
int ret;
printk("--------------^_^ %s---------------\n",__FUNCTION__);
key_dev = input_allocate_device();
if (!key_dev) {
printk(KERN_ERR "Not enough memory\n");
return -ENOMEM;
}
key_dev->evbit[0] = BIT_MASK(EV_KEY);
key_dev->keybit[BIT_WORD(KEY_DOWN)] = BIT_MASK(KEY_DOWN);
ret = input_register_device(key_dev);
if (ret) {
printk(KERN_ERR "Failed to register device\n");
goto err_free_dev;
}
irqno = IRQ_EINT(1);
if (request_irq(irqno, key_interrupt_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_eint1", NULL)) {
printk(KERN_ERR "Can't allocate irq %d\n", irqno);
ret = -EBUSY;
goto err_unregister_dev;
}
return 0;
err_unregister_dev:
input_unregister_device(key_dev);
err_free_dev:
input_free_device(key_dev);
return ret;
}
static void __exit input_simple_key_drv_exit(void)
{
printk("----------^_^ %s----------------\n",__FUNCTION__);
free_irq(irqno, NULL);
input_unregister_device(key_dev);
input_free_device(key_dev);
}
module_init(input_simple_key_drv_init);
module_exit(input_simple_key_drv_exit);
MODULE_LICENSE("GPL");
2、app
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
int main(void)
{
int fd;
int ret;
struct input_event event;
if((fd = open("/dev/event0",O_RDWR)) < 0){
perror("open");
exit(1);
}
while(1){
if((ret = read(fd,&event,sizeof(event))) < 0){
perror("read");
exit(1);
}
if(event.type == EV_KEY){
if(event.code == KEY_DOWN){
if(event.value)
printf("按下--->下键\n");
else
printf("松开--->下键\n");
}
}
}
close(fd);
return 0;
}