输入子系统框架简单解释

输入子系统框架:
Linux有许多子系统,usb、video、input…input 就是今天的主角–输入子系统的相关代码位于
driver目录下的input文件夹。
鼠标、键盘、游戏手柄等等这些都属于输入设备;这些输入设备的驱动都是通过输入子系统来实现的(当然,这些设备也依赖于usb子系统)。
所有这些输入设备都各有不同,那么输入子系统也就只能实现他们的共性,差异性则由设备驱动来实现。差异性又体现在哪里?最直观的就表现在这些设备功能上的不同了。对于我们写驱动的人来说在设备驱动中就只要使用输入子系统提供的工具(也就是函数)来完成这些“差异”就行了,其他的则是输入子系统的工作。这个思想不仅存在于输入子系统,其他子系统也是一样。
输入子系统框架简单解释
一、输入子系统框架:
(1) 分为上下两层:
核心层“input.c”.它里面有“register_chdev”(input_init()中),但它简单:
err = register_chrdev(INPUT_MAJR,”input”,&input_fops);
因为这个register_chrdev()中的“input_fops”file_operations结构很简单:
static const strcut file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
只有一个“.open”函数,所以让它去读去写不行,里面还有个“中转”的过程:
input_open_file(struct inode *inode, struct file *file)
根据打开的设备节点的次设备号找到一个“handler”:
struct input_handler *handler =input_table[iminor(inode)>>5];
并且把这个文件的f_op指向新的“input_handler *handler”里面的“fops”:
old_fops = file->f_op;
File->f_op = new_fops;
然后再调用这个“handler”中的open函数:
err = new_fops->open(inode,file);
其中的“iput_table[]”数组由下面的各个“纯软件”代码构建(如:evdev.c,keyboard.c).
“纯软件(input_handler)”部分和“硬件部分(input_dev)”联系起来,
纯软件部分是由“input_register_handler”向上“input.c”核心层注册处理方式;
硬件部分是由“input_register_device”向上“input.c”核心层注册硬层。
这样注册后,会使它们两两比较,看看其中的某个“handler”是否支持其中的某个“dev”。若是“handler”能支持某个“dev”,则会接着调用“input_handler”结构下的“.connect”函数,这个函数一般会创建一个“input_handle”结构(是handle而非handler),并且这个结构“input_handle”会分别放在两边的“h_list”链表中去。这个结构体中有“.dev”和“.handler”,让它两分别具体指向右边的“纯软件处理部分”和左右的硬件部分,这样具体的某硬件就和纯软件联系起来了。可以从任何一边通过“h_list”找到这个“input_handle”结构,再通过成员找到另一端的“.dev”或“.handler”。
输入子系统框架简单解释
二、写输入子系统驱动:
Input.c 中的如上步骤:
1,主设备号:
#define INPUT_MAJOR 13
2,file_operations结构:
static const strcut file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
区别:
这个"file_operations"结构中只有一个“.open”函数。这里不像自已写字符设备驱动程序时在open函数中定义硬件引脚和注册中断等,这里的open函数是作为“中转”的作用。
在某个数组中找到所谓的“input_handler”结构,用这个结构中的“.fops”去读或写。
3,注册结构体:
err = register_chrdev(INPUT_MAJR,”input”,&input_fops);

//1、2、3 步骤内核已经做好
4,入口函数:
static int __init input_init(void)
5,出口函数:
static viod __exit input_exit(void)

入口函数:
输入子系统框架简单解释
就是说输入子系统右边的纯软件部分“处理方式”层由内核提供好了,我们只需要做左边“硬件相关的操作”这部分.

实例编写:

/* 参考drivers\input\keyboard\gpio_keys.c */

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>

#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>

/* 定义pin脚描述结构体;中断号,名字,引脚定义,按键值*/
struct pin_desc{
int irq; // 中断号为IRQ_ENINT0, IRQ_EINT2, IRQ_EINT11, IRQ_EINT19
char *name; //中断名为s2,s3,s4,s5
unsigned int pin; // 那个引脚
unsigned int key_val; //按键值
};

/定义4个按键/
struct pin_desc pins_desc[4] = { //每个按键分别对:中断号,名字,引脚定义和按键值
{IRQ_EINT0, “S2”, S3C2410_GPF0, KEY_L}, //当按下s2时就对应这“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 pin_desc *irq_pd; //定义“定时器”
static struct timer_list buttons_timer; //定义一个定时器,定时器要初始化

static irqreturn_t buttons_irq (int irq, void dev_id) //中断函数
{
/
10ms后启动定时器 */
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) //定时器初始化
{
//以前是 :“确定按键值”—> “唤醒应用程序”或“发送信号”
//这里只需上报“按键值”:上报事件----input _ rvent上报事件
struct pin_desc * pindesc = irq_pd;
unsigned int pinval;
if (!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 : 最后一个参数: 0-松开, 1-按下 /
input_event(buttons_dev, EV_KEY, pindesc->key_val, 0);
input_sync(buttons_dev); //上报同步事件
}
else
{
/
按下 */
input_event(buttons_dev, EV_KEY, pindesc->key_val, 1);
input_sync(buttons_dev); //上报同步事件

}

}

//1.先写入口函数
static int buttons_init(void)
{
int i;
/* 1. 分配一个input_dev结构体 /
/

a,定义input_dev 结构变量:
输入子系统框架简单解释
b,用函数来分配这个input_dev *buttons_dev 变量:
输入子系统框架简单解释
c,从上面的“gpio_keys_init()”分析
,分配“input_dev”结构体是“input_allocate_device()”,则这里自已民写分配一个“input_dev”结构是用此函数。
输入子系统框架简单解释
正常情况下要判断此“Input_dev”结构是否分配成功,但这里为简化代码不予判断。但一般都会成功的。
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移事件, x,y,滚轮
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件, x,y
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];
}
/
buttons_dev = input_allocate_device();
/
2. 设置 */
输入子系统框架简单解释
下面还很长。
“unsigned long evbit[NBITS(EV_MAX)];”是:表示能产生哪类事件。这里是类,所以会有很多宏 表示的事件:
#define EV_SYN 0x00 //同步类
#define EV_KEY 0x01 //按键类,如键盘上的a,b等按键事件。
#define EV_REL 0x02 //relation相对位移事件(如鼠标的位移是基于上一个位置的)。
#define EV_ABS 0x03 //ABS是绝对位移(如触摸屏是XY坐标绝对位置)。
#define EV_MSC 0x04
#define EV_SW 0x05
#define EV_LED 0x11
#define EV_SND 0x12
#define EV_REP 0x14
#define EV_FF 0x15
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX0x1f
用“set_bit()”设置产生哪类事件
/
/
2.1 能产生哪类事件 /
set_bit(EV_KEY, buttons_dev->evbit);
set_bit(EV_REP, buttons_dev->evbit);
/
2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
开始设置,首先设置它能产生哪类事件:
如这里产生按键类事件。
输入子系统框架简单解释
设置这个“keybit”数组中某一位表示它能产生哪种按键事件。
set_bit(KEY_L, buttons_dev->keybit);
set_bit(KEY_S, buttons_dev->keybit);
set_bit(KEY_ENTER, buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);

/* 3. 注册 /
/

注册了“input_dev”结构体之后,就会把这里具体的“buttons_dev”设备结构体挂到内核的“input_dev”输入设备的链表中去。接着就从右边的“input_handler”链表中一个个取出来具体的id_table[]与“buttons_dev”进行比较。若是能匹配说明“input_handler”结构中的“id_table”能支持这个“input_dev”设备“buttons_dev”,这时支持后,就会接着调用“input_handler”结构中的“.connect”函数。
到了“.connect”建立连接这个过程时,就会创建一个新的结构“input_handle”,这个结构中有两个成员“.handler”(具体处理方式)和“.dev”(具体设备)。“.dev”指向左边的设备层,“.handle”指向右边的“处理方式”层。这个“input_handle”结构会分别放到左边设备层的“input_dev”这个结构成员“h_list”链表中,也挂到处理方式层的“input_handler”结构的成员“h_list”链表中去。
*/
input_register_device(buttons_dev);

/* 4. 硬件相关的操作 */
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;
}

//2.写出口函数
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_dev结构
input_free_device(buttons_dev); //释放“input_dev”结构分配的空间
}

//3.入口函数、出口函数的修饰
module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE(“GPL”);