7.输入子系统框架

1.框架小结

1、入口input_init:注册类,注册驱动

class_create(&input_class);
register_chrdev(INPUT_MAJOR, "input", &input_fops);
2、打开驱动,将真正的file_operations引入执行open,该文件同时储存input_table

struct input_handler *handle = imput_table[minor >> 5] //8bit >> 5 = 3bit(8个索引)
这里此设备号中的低5位用作特殊处理
3、input_register_handler,初始化input_table处理函数,添加实际的handler,加入全局链表input_handler_list,同时调用connect建立handlerdev连接
4、input_register_device注册具体的input_dev,加入到全局链表input_dev_list,同时调用connect建立handlerdev的连接
5、connect建立链接关系,这里有一个新的连接结构handler,包含了handlerinput_dev,这里具体的新建一个evdev结构变量,成员变量指向上面创建的devhandler,分配此设备好创建设备。调用input_register_handler,创建一个管理结构
6、app读取数据最终会调用到handlerfile中的read,阻塞方式如果进入休眠会被中断调用handlerevent来唤醒

2.有架构驱动与无架构驱动的对比

无架构驱动

1.确定主设备号
2.编写file_oprention结构体编写硬件相关操作
3.构造class结构,自动创建dev设备节点
4.注册设备驱动
5.定义入口和出口
输入子系统架构

input.c完成了主设备的注册,驱动设备注册,在这个框架下需要实现硬件设备驱动的程序即可。
input_device设备驱动
input_handler处理架构
1.向内核申请input_dev结构体
2.设置input_dev的成员
3.注册input_dev驱动设备
4.初始化定时器和中断
5.写中断服务函数
6.写定时器超时函数
7.在出口函数中,释放中断函数,删除定时器,卸载驱动,释放驱动

3.关键函数、结构体解析

input_dev :设备结构

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)]; // 支持哪些事件
/* EN_SYN:同步事件,当使用input_event()函数后就要使用这个上报同步事件
* EN_KEY:键盘事件
* EV_REL:相对坐标事件,eg:鼠标
* EV_ABS:绝对坐标事件,eg:摇杆,触摸屏
* EV_MSC:其他事件,功能
* EV_LED:LED灯事件
* EV_SND:声音事件
* EV_REP:重复键盘按键事件(内部有一个定时器,若有按键事件一直按下/松动,就重复定时,时间一到就上报事件)
* EV_FF: 受力事件
* EV_PWR:电源事件
* EV_FF_STATUB: 受力状态事件
*/

unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // 存放支持的按键值
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // 存放支持的相对坐标
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; // 存放支持的绝对坐标
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; // 存放支持的其他事件,也就是功能
unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; // 存放支持的各种状态的LED
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; // 存放支持的各种声音
unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; // 存放支持的受力设备
unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; // 存放支持的开关功能

unsigned int hint_events_per_packet;

unsigned int keycodemax;
unsigned int keycodesize;
void *keycode;

int (*setkeycode)(struct input_dev *dev,
const struct input_keymap_entry *ke,
unsigned int old_keycode);
int (
getkeycode)(struct input_dev *dev,
struct input_keymap_entry *ke);

struct ff_device *ff;

unsigned int repeat_key;
struct timer_list timer;

int rep[REP_CNT];

struct input_mt_slot *mt;
int mtsize;
int slot;
int trkid;

struct input_absinfo *absinfo;

unsigned long key[BITS_TO_LONGS(KEY_CNT)];
unsigned long led[BITS_TO_LONGS(LED_CNT)];
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT)];

int (*open)(struct input_dev dev);
void (
close)(struct input_dev dev);
int (
flush)(struct input_dev *dev, struct file file);
int (
event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

struct input_handle __rcu *grab;

spinlock_t event_lock;
struct mutex mutex;

unsigned int users;
bool going_away;

bool sync;

struct device dev;

struct list_head h_list;
struct list_head node;
};

input_allocate_device:向内核申请一个input_dev设备,然后返回这个设备

struct input_dev *input_allocate_device(void)
input_free_device:释放这个结构体,一般在驱动出口函数

void input_free_device(struct input_dev *dev)
EXPORT_SYMBOL(input_free_device);
input_unregister_device:卸载/sys/class/input目录下的input_dev这类设备,一般在驱动的出口处写

void input_unregister_device(struct input_dev *dev)
EXPORT_SYMBOL(input_unregister_device); //标签内定义的函数或者符号对全部内核代码公开
set_bit:用来设置为变量,这里用来设置支持的事件类型

static inline void set_bit(int nr, volatile unsigned long *addr)
eg: set_bit(KEY_L, buttons_dev->keybit);
input_event:事件上报

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
//dev:要上报那个input驱动设备事件
//type:要上报的事件类型:eg:按键填EV_KEY
//code:对应事件里支持那个变量,eg:如按下按键L,则填入KEY_L
//value:对应变量里的数值,eg:松开按键填入0,按下按键填入1

//注意上报完事件之后需要调用input_sync来同步事件
input_sync:同时事件,其实上报事件并未把事件上报给内核,而是存储在某个地方,由intput_sync来实现上报

static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}

4.源码解析

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 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)
{
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);
}
}

static int buttons_init(void)
{
int i;

/* 1. 分配一个input_dev结构体 */
buttons_dev = input_allocate_device();

/* 2. 设置 /
/
2.1 能产生哪类事件 */
set_bit(EV_KEY, buttons_dev->evbit);
set_bit(EV_REP, buttons_dev->evbit);

/* 2.2 能产生这类操作里的哪些事件: L,S,ENTER,LEFTSHIT */
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_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;
}

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");

posted @ 2020-07-18 15:09  人民广场的二道贩子  阅读(186)  评论(0)    收藏  举报