参考:国嵌教育和http://blog.csdn.net/lidaqiang99/article/details/6605700
1、********************A类**********************
struct input_dev * input_dev;
input_dev=input_allocate_device();//动态申请一个结构体。
//告知输入型子系统,该设备支持那些功能。
//表示类型
set_bit(EV_SYN,input_dev->evbit);//一定记住了。表示同步。
set_bit(EV_ABS,input_dev->evbit);
//表示支持那些
set_bit(ABS_MT_TOUCH_MAJOR,input_dev->absbit);
set_bit(ABS_MT_POSITION_X,input_dev->absbit);
set_bit(ABS_MT_POSITION_Y,input_dev->absbit);
set_bit(ABS_MT_WIDTH_MAJOR,input_dev->absbit);
set_bit(EV_KEY,input_dev->evbit);
set_bit(KEY_HOME,input_dev->keybit);
set_bit(KEY_MENU,input_dev->keybit);
set_bit(KEY_BACK,input_dev->keybit);
//设置范围参数:
input_set_abs_params(input_dev,ABS_MT_POSITION_X,0,SCREEN_MAX_X,4,0),
//表示:X轴范围是0到SCREEN_MAX_X,数据误差是-4到+4,中心平滑位置是0 .
input_set_abs_params(input_dev,ABS_MT_POSITION_Y,0,SCREEN_MAX_Y,4,0),
input_set_abs_params(input_dev,BS_MT_TOUCH_MAJOR,0,PRESS_MAX,4,0),
// //相当于单点屏的ABX_PRESSURE
input_set_abs_params(input_dev,ABS_MT_WIDTH_MAJOR,0,200,0,0),
//相当于单点屏的ABX_PRESSURE
input_set_abs_params(input_dev,ABS_MT_TRACKING_ID,0,10,0,0),
input_register_device(input_dev);//注册到输入型子系统。
2、开始读数据:一按触摸屏就会产生中断,产生中断后就去读触摸屏里面的寄存器值,里面都是记录着坐标等信息。
3、读完后input系统开始上报:
struct ft5xox_ts_data *data;
input_report_abs(data->input_dev,ABS_MT_TRACKING_ID,event->touch_ID5);
input_report_abs(data->input_dev,ABS_MT_TOUCH_MAJOR,event->pressure);
input_report_abs(data->input_dev,ABS_MT_POSITION_X,event->x5);
input_report_abs(data->input_dev,ABS_MT_TRACKING_Y,event->y5);
input_report_abs(data->input_dev,ABS_MT_WIDTH_MAJOR,1);
input_mt_sync(data->input_dev);
input_sync(data->input_dev);
//由于多点触摸技术需要采集到多个点,然后再一起处理这些点,所以在软件实现中需要保证每一波点的准确性和完整性。因此,Linux内核提供了input_mt_sync(struct input_dev * input)函数。在每波的每个点上报后需要紧跟一句input_mt_sync(), 当这波所有点上报后再使用input_sync()进行同步,即使是仅上报一个点的单点事件,也需要一次input_mt_sync。
********************B类**********************
1、初始化;
struct input_dev * input_dev;
input_dev=input_allocate_device();//动态申请一个结构体。
input_register_device(input_dev);//注册到输入型子系统。
2、通知子系统该设备支持那些类型:
__set_bit(EV_SYN,input_dev->evbit);
__set_bit(EV_KEY,input_dev->evbit);
__set_bit(EV_ABS,input_dev->evbit);
__set_bit(INPUT_PROP_DIRECT,input_dev->propbit);
input_set_abs_params(input_dev,ABS_MT_PISITION_X,0,SCREEN_MAX_X,0,0);
input_set_abs_params(input_dev,ABS_MT_PISITION_Y,0,SCREEN_MAX_Y,0,0);
input_set_abs_params(input_dev,ABS_MT_TOUCH_MAJOR,0,PRESS_MAX,0,0);
input_mt_init_slots(input_dev,MAX_POINT);//报告最大支持点数;
3、上报:
释放事件:
input_mt_slot(input_dev, i); //报告有i个点
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); //释放
input_sync(input_dev);
点击事件:
input_mt_slot(input_dev, id);//上报点
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true);//true表示按下
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 200);
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
input_sync(input_dev);
一、输入子系统:input
1、首先我们要知道什么时候我们可以用到input输入子系统,以及为什么我们要用input输入子系统?
像按键、触摸屏、鼠标等输入设备我们都可以采用input接口函数来实现设备驱动。那么采用input输入子系统有什么优点呢?其实一句话,采用input输入子系统可以使驱动程序变得异常简单。
2、输入子系统包括三个部分设备驱动、输入核心、事件处理器。
一个输入事件,如鼠标移动,键盘按键按下,joystick的移动等等通过 Driver -> InputCore -> Eventhandler -> userspace 的顺序到达用户空间传给应用程序。 其中Input Core 即 Input Layer 由 driver/input/input.c及相关头文件实现。对下提供了设备驱动的接口,对上提供了Event Handler层的编程接口。
第一部分是连接在各个总线上的输入设备驱动,在我们的SOC上,这个总线可以使虚拟总线platformbus,他们的作用是将底层的硬件输入转化为统一事件型式,向输入核心(Input core)汇报.
我们看到输入子系统体系结构大概包括三部分:drivers、input core、handlers,其中input core、handlers是由内核来实现的,程序员要做的就是利用input core提供的接口函数来实现drivers。下面我们来具体说一下这三层的功能:
驱动层(drivers):将底层的硬件输入转化为统一事件形式,向输入核心层(input core)汇报。
输入核心层(input core):为驱动层提供输入设备注册与操作接口,如input_register_device;
通知事件处理层对事件进行处理;在/proc下产生相应的设备信息;
事件处理层(handlers):主要作用是和用户空间交互,我们知道linux在用户空间将所有设备当成文件来处理,在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件节点,而在输入子系统中,这些工作由事件处理层来完成。
如图:
第一部分driver 是连接在各个总线上的输入设备驱动,在我们的SOC上,这个总线可以使虚拟总线platformbus,他们的作用是将底层的硬件输入转化为统一事件型式,向输入核心(Input core)汇报.
第二部分input core 输入核心的作用如下:
调用input_register_device() used to 添加设备,调用input_unregister_device() 除去设备。input core 用来协调硬件的input事件 和 用户层应用之间的通讯。
在/PROC下产生相应的设备信息,下面这个例子即是:
/proc/bus/input/devices showing a USB mouse:
I: Bus=0003 Vendor=046d Product=c002 Version=0120
N: Name="Logitech USB-PS/2 Mouse M-BA47"
P: Phys=usb-00:01.2-2.2/input0
H: Handlers=mouse0 event2
B: EV=7
B: KEY=f0000 0 0 0 0 0 0 0 0
B: REL=103
通知事件处理器对事件进行处理
第三部分handlers 是事件处理器:
输入子系统包括了您所需要的大所属处理器,如鼠标、键盘、joystick,触摸屏,也有一个通用的处理器被叫做event handler(对于内核文件evdev.C).需要注意的是随着内核版本的发展,event handler将用来处理更多的不同硬件的输入事件。在Linux2.6.29版本中,剩下的特定设备事件处理就只有鼠标和joystick。这就意味着越来越多的输入设备将通过event handler来和用户空间打交道。事件处理层的主要作用就是和用户空间打交道,我们知道Linux在用户空间将所有设备当成文件来处理,在一般的驱动程序中都有提供fops接口,以及在/dev下生成相应的设备文件nod,而在输入子系统的驱动中,这些动作都是在事件处理器层完成的,可参考evdev.C相关代码。
在内核中,input_dev 表示一个 input设备;input_handler 来表示input设备的 interface
二、设备描述:linux内核中input设备用input_dev结构体描述,使用input子系统实现输入设备驱动时,驱动的核心工作是向系统报告按键,触摸屏,键盘,鼠标输入事件
(event,都通过input_event结构体描述),而不再需要关心文件操作接口。因为input子系统已经完成了文件操作接口。驱动报告的事件通过input core 和event handler最终到达用户空间。
1)注册输入设备:int input_register_device(&button_dev);
2)注销输入设备:void input_unregister_device(&button_dev);
三、驱动实现:
设备驱动通过set_bit()告诉input子系统它支持哪些事件,如:
set_bit(EV_KEY,button_dev.evbit);
struct input_dev
{
evbit;//事件类型
keybit;//按键类型
}
事件的支持:其中比较重要的是evbit字段用来定义该输入设备可以支持的(产生和响应)的事件的类型。包括:
EV_RST 0x00 Reset
EV_KEY 0x01 按键
EV_REL 0x02 相对坐标
EV_ABS 0x03 绝对坐标
EV_MSC 0x04 其它
EV_LED 0x11 LED
EV_SND 0x12 声音
EV_REP 0x14 Repeat
EV_FF 0x15 力反馈
一个设备可以支持一个或多个事件类型。每个事件类型下面还需要设置具体的触发事件,比如EV_KEY事件,支持哪些按键等。
如当 EV_KEY类型时,还需指明按键类型:
BTN_LEFT: 鼠标左键
BTN_RIGHT: 鼠标右键
BTN_MIDDLE: 鼠标中键
BIN_0:数字键0
BIN_1:数字键1
四、报告
当事件真正发生后,用于报告EV_KEYEV_RELEV_ABS事件的函数分别为
报告EV_KEY: void input_report_key(struct input_dev *dev,unsigned int code,int value)
报告EV_REL: void input_report_rel(struct input_dev *dev,unsigned int code,int value)
报告EV_ABS: void input_report_abs(struct input_dev *dev,unsigned int code,int value)
structinput_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
code--
事件的代码。如果事件类型是EV_KEY,该代码则为设备的键盘代码。例如:键盘上的按键代码值为0--127,鼠标按键代码为0x110---0x116,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标左键,其它代码含义参考include/linux/input.h文件。
value--
事件的值。若事件的类型是EV_KEY,当按键按下时值为1,松开值为0
input_sync()用于告诉input core:此次报告已结束。
实例:
在触摸屏设备驱动中,一次点击的整个报告过程如下:
input_report_abs(input_dev,ABS_X,x)
input_report_abs(input_dev,ABS_Y,x)
input_report_abs(input_dev,ABS_PRESSURE,1)
input_sync(input_dev)
实例分析==
在按键中断中报告事件---
static void button_interrupt(int irq,void *dummy,struct pt_regs *fp)
{
input_report_key(&button_dev,BTN_0,inb(BUTTON_PORT0));
input_report_key(&button_dev,BTN_1,inb(BUTTON_PORT1));
input_sync(&button_dev);
}
static int__init button_init(void)
{
//申请中断
if(request_irq(BUTTON_IRQ,button_interrupt,0"button",NULL))
return -EBUSY;
set_bit(EV_KEY,button_dev.evbit);支持EV_KEY事件
set_bit(BTN_0,button_dev.keybit);设备支持两个键
set_bit(BTN_1,button_dev.keybit);
input_register_device(&button_dev);注册input设备
}
按键应用程序--
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
int buttons_fd;
int key_value,i=0,count;
struct input_event ev_key;
buttons_fd = open("/dev/event0", O_RDWR);
if (buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
for (;;) {
count = read(buttons_fd,&ev_key,sizeof(struct input_event));读取事件的个数
for(i=0; i<(int)count/sizeof(struct input_event); i++)
if(EV_KEY==ev_key.type)
printf("type:%d,code:%d,value:%d\n", ev_key.type,ev_key.code,ev_key.value);
if(EV_SYN==ev_key.type)
printf("syn event\n\n");
}
close(buttons_fd);
return 0;
}
总结---------两个重要的数据结构
input_dev
input_event
以下转载:http://liu1227787871.blog.163.com/blog/static/20536319720124201150318/
linux输入子系统——深入分析(一)
2012-05-20 21:04:34| 分类: 跟着韦东山老师学 |字号 订阅
在国嵌里我们已经讲解了关于input输入子系统的基础知识,在这里我们跟着韦东山老师在来深入理解关于input输入子系统相关的知识
在分析之前我们
1、 在input输入子系统体系结构里我们说过,其核心是input core,其实input core对应的具体文件就是input.c。那么我们就来分析一下吧:
/*file_operations的设置*/
static const struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,//只有一个open函数,看来open函数肩负重任啊
};
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;
}
注释1:从上面我们可以看出,input.c里面完成的字符设备的注册,以及file_operations结构体的设置。看来input子系统设计的驱动程序也属于字符设备驱动程序,与一般的字符设备驱动程序的不同之处在于,input子系统字符设备的注册由内核自动完成,不需要程序员来完成,程序员要做的就是,将底层的硬件输入转化为统一事件形式,向输入核心层(input core)汇报。
2、我们看到在file_operations结构体里只有一个open函数,如果要想读设备或则写设备怎么办呢?这说明pen函数肩负着艰巨的使命,这诱使我们去了解这个小强
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler;
const struct file_operations *old_fops, *new_fops = NULL;
int err;
lock_kernel();
/* No load-on-demand here? */
handler = input_table[iminor(inode) >> 5];//根据打开的文件的次设备号,来得到一个input_handler结构体,关于这个结构体请见注释1
if (!handler || !(new_fops = fops_get(handler->fops)))//通过handler得到新的 file_operations结构体
{
err = -ENODEV;
goto out;
}
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {
fops_put(new_fops);
err = -ENODEV;
goto out;
}
old_fops = file->f_op;//保存文件之前的f_op
file->f_op = new_fops;//将新的f_op赋给当前文件的f_op
err = new_fops->open(inode, file);//调用open函数,当应用程序读的时候,最终会调用这个函数
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
fops_put(old_fops);
out:
unlock_kernel();
return err;
}
注释1:struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
void (*disconnect)(struct input_handle *handle);
void (*start)(struct input_handle *handle);
const struct file_operations *fops;
int minor;
const char *name;
const struct input_device_id *id_table;
const struct input_device_id *blacklist;
struct list_head h_list;
struct list_head node;
};
这个结构体可以通过 input_table来获得,那么 input_table是何许人也呢?查了一下发现input_table在input_register_handler里被构造了:
input_table[handler->minor >> 5] = handler;
而input_register_handler函数由诸如下面一些文件调用:
keyboard.c、mousedev.c、edev.c等等
我们进入edev.c来看一看,发下了重要线索:
/*这里就是 file_operations结构体了*/
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
};
.............................................................
.............................................................
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 int __init evdev_init(void)
{
return input_register_handler(&evdev_handler);
}
有点乱对吧!先来理一下:
在edev.c中构造了input_handler结构体(当然在这个结构体里包含了file_operation结构体),并向input.c注册这个结构体。在input.c的函数input_register_handler中根据该结构体构造了input_table:input_table[handler->minor >> 5] = handler,而在 input_open_file函数中,又通过input_table构造了一个input_handler结构体:handler = input_table[iminor(inode) >> 5];这样看input_table起到一个中转作用。然后通过handler构造了新的file_operation结构体:new_fops = fops_get(handler->fops。OK!
3、上面分析了input core的情况,下面我们从最底层的驱动来一步一步分析:
(1)在入口函数中注册input设备:input_register_device(button_dev);
(2)注册函数定义在input.c中:
int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
const char *path;
int error;
__set_bit(EV_SYN, dev->evbit);
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
if (!dev->getkeycode)
dev->getkeycode = input_default_getkeycode;
if (!dev->setkeycode)
dev->setkeycode = input_default_setkeycode;
snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
error = device_add(&dev->dev);
if (error)
return error;
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
kfree(path);
error = mutex_lock_interruptible(&input_mutex);
if (error) {
device_del(&dev->dev);
return error;
}
list_add_tail(&dev->node, &input_dev_list);//将input设备放入一个链表中
list_for_each_entry(handler, &input_handler_list, node)//对于一个input设备,使其与input_handler_list链表中的每一个handler都比较一下,在 //注册input_handler结构体时,会将该input_handler结构体放入链表中的,见注释
input_attach_handler(dev, handler);//根据id.table比较来判断handler是否支持设备
input_wakeup_procfs_readers();
mutex_unlock(&input_mutex);
return 0;
}
EXPORT_SYMBOL(input_register_device);
注释:注册input_handler的源代码如下:
int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
int retval;
retval = mutex_lock_interruptible(&input_mutex);
if (retval)
return retval;
INIT_LIST_HEAD(&handler->h_list);
if (handler->fops != NULL) {
if (input_table[handler->minor >> 5]) {
retval = -EBUSY;
goto out;
}
input_table[handler->minor >> 5] = handler;
}
list_add_tail(&handler->node, &input_handler_list);//将handler加入链表input_handler_list
list_for_each_entry(dev, &input_dev_list, node)//对于每一个input设备进行比较
input_attach_handler(dev, handler);//判断是否匹配
input_wakeup_procfs_readers();
out:
mutex_unlock(&input_mutex);
return retval;
}
综上:注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"
(3)那么这connect函数是怎么一回事儿呢? 让源码说话:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
int error;
for (minor = 0; minor < EVDEV_MINORS; minor++)
if (!evdev_table[minor])
break;
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);//分配一个struct evdev结构体,然后设置
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);//input设备放进去
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;//handler处理函数放进去
evdev->handle.private = evdev;
strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
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 = input_register_handle(&evdev->handle);//注册handle,我们可以走进去,见注释1
if (error)
goto err_free_evdev;
error = evdev_install_chrdev(evdev);
if (error)
goto err_unregister_handle;
error = device_add(&evdev->dev);
if (error)
goto err_cleanup_evdev;
return 0;
err_cleanup_evdev:
evdev_cleanup(evdev);
err_unregister_handle:
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
return error;
}
注释1:
int input_register_handle(struct input_handle *handle)
{
struct input_handler *handler = handle->handler;//handler处理函数拿出来
struct input_dev *dev = handle->dev;//input设备拿出来
int error;
/*
* We take dev->mutex here to prevent race with
* input_release_device().
*/
error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_tail_rcu(&handle->d_node, &dev->h_list);//将handle加入到dev->h_list链表中
mutex_unlock(&dev->mutex);
synchronize_rcu();
/*
* Since we are supposed to be called from ->connect()
* which is mutually exclusive with ->disconnect()
* we can't be racing with input_unregister_handle()
* and so separate lock is not needed here.
*/
list_add_tail(&handle->h_node, &handler->h_list);//将handle加入到handler->h_list链表中
if (handler->start)
handler->start(handle);
return 0;
}
现在我们就清楚了,如果handler支持input设备,通过connect的连接,input_device可以根据其h_list来找到handle,然后根据handle找到相应的处理函数。反之,处理函数也一样。
我们可以整理出来框架:
evdev.c文件中:
input_register_handler(&evdev_handler);//evdev_handler里面定义了event、connect和fops等一些东东
input_table[handler->minor >> 5] = handler;//将根据次设备号将handler放入数组中
list_add_tail(&handler->node, &input_handler_list);//将handler加入链表input_handler_list
list_for_each_entry(dev, &input_dev_list, node)//遍历input_handler_list中的所有项
input_attach_handler(dev, handler);//判断设备与处理函数是否匹配
id = input_match_device(handler->id_table, dev);//根据handler->id_table判断设备与处理函数是否匹配
if (!id)
return -ENODEV;
handler->connect(handler, dev, id);//如果匹配就调用handler->connect函数
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
evdev->handle.dev = dev;//将dev放进去
evdev->handle.handler = handler;//将handler放进去
input_register_handle(&evdev->handle);
struct input_handler *handler = handle->handler;
list_add_tail(&handle->d_node, &handle->dev->h_list);//将handle加入到dev->h_list链表中
list_add_tail(&handle->h_node, &handler->h_list);//将handle加入到handler->h_list链表中
input_register_device(input);
list_add_tail(&dev->node, &input_dev_list);//将dev将加入input_dev_list列表中
list_for_each_entry(handler, &input_handler_list, node)//遍历所有的handler判断是否支持设备
input_attach_handler(dev, handler);//判断设备与处理函数是否匹配
接下来的分析跟上面完全一致,我们不再分析
这样处理函数和设备就联系起来了。我们再来啰嗦一下:我们在注册设备的时候会有次设备号,而注册处理函数的时候又会根据这个次设备号来将处理函数放入数组中,然后在对设备进行操作的时候,根据次设备号来找到操作函数。这样我们就对应起来了。
input.c文件中:
register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
input_fops里面只有open函数,在open函数中会完成取出操作函数的工作。
**************************
linux输入子系统——代码编写(二)
2012-05-20 21:04:45| 分类: 跟着韦东山老师学 |字号 订阅
首先我们先来看一下代码,根据代码来分析:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
{IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L},
{IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S},
{IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER},
{IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT},
};
static struct input_dev *buttons_dev;
static struct timer_list buttons_timer;
static struct pin_desc *irq_pd;
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
irq_pd = (struct pin_desc *)dev_id;
mod_timer(&buttons_timer, jiffies+HZ/100);//修改定时器的值
return IRQ_RETVAL(IRQ_HANDLED);
}
static void buttons_timer_function(unsigned long data)
{
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);//获取引脚状态
if (pinval)
{
input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);//上报事件,请看注释1
input_sync(buttons_dev);//上报一个同步事件,表示上报结束
}
else
{
input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
input_sync(buttons_dev);
}
}
static int buttons_init(void)
{
int i;
buttons_dev = input_allocate_device();//分配一个input_dev结构体
set_bit(EV_KEY, buttons_dev->evbit);//设置能产生哪一类事件,这是表示能产生按键类事件
set_bit(EV_REP, buttons_dev->evbit);//表示可以持续上报时间,借助定时器
set_bit(KEY_L, buttons_dev->keybit);//设置可以产生按键类事件里的L按键
set_bit(KEY_S, buttons_dev->keybit);//设置可以产生按键类事件里的S按键
set_bit(KEY_ENTER, buttons_dev->keybit);//设置可以产生按键类事件里的ENTER按键
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);//设置可以产生按键类事件里的LEFTSHIFT按键
input_register_device(buttons_dev);//注册进内核
init_timer(&buttons_timer);//初始化定时器
buttons_timer.function = buttons_timer_function;//设置定时器处理函数
add_timer(&buttons_timer);//将定时器注册进内核
for (i = 0; i < 4; i++)
{
request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);//注册中断
}
return 0;
}
static void buttons_exit(void)
{
int i;
for (i = 0; i < 4; i++)
{
free_irq(pins_desc[i].irq, &pins_desc[i]);
}
del_timer(&buttons_timer);
input_unregister_device(buttons_dev);
input_free_device(buttons_dev);
}
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");
我们来总结一下编写流程:
首先要设置产生哪一类事件,然后是产生这一类事件里的那一个事件,之后就是注册,然后再适当的时候将事件上报就可以了。注册后设备和设备处理函数就对应了起来。接着就是等待事件发生,然后上报事件,那么上报之后做了什么事情呢?我们来看一看具体代码:
input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
list_for_each_entry(handle, &dev->h_list, d_node)//遍历找出本设备的处理函数
if (handle->open)
handle->handler->event(handle, type, code, value);//调用本设备的处理函数中的event,由于于本设备是按键,所以在keyboard.c文 //件里,在该文件里完成相应的处理工作
测试方法:要先关闭qt,可以在etc/init.d/rcS文件里注释掉最后一行,然后重启
方法1:cat /dev/tty1
方法2:exec 0