上一节总体概括了输入子系统的组成及实现方法。主要是由input.c核心层,input_device“硬件”层和input_handler_dev"软件“层。硬件层和软件层都是通过向核心层来注册设备。对于每一个input_dev调用input_attach_handler,根据input_handler中的id_table来判断能否支持这个input_dev。根据注册input_dev或input_handler时,会两两比较input_dev和input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果可以则调用input_handler的connect函数建立“连接”。
- /* 参考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>
- 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-按下 , 2 - 重复 */
- 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);
- }
- }
/*
* 入口函数: buttons_init()
* 实现功能: 1.分配一个input_dev结构体
* 2.设置
* 3.注册
* 4.硬件相关的代码,比如在中断服务程序里面上报事件。
*/
- 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");
先写出入口函数、出口函数的框架,并加以修饰,然后在入口函数里面实现1,分配一个input_dev结构体。2,对这个input_dev进行相关设置:能产生哪类事件,产生这类事件中的哪些事件。3,注册设备。4,硬件相关的操作。
Input.h里面有对相关事件的定义。
unsigned long evbit[NBITS(EV_MAX)]; // 表示能产生哪类事件
#define EV_SYN 0x00 // 同步类
#define EV_KEY 0x01 // 按键类
#define EV_REL 0x02 // 相对位移类事件 relation
#define EV_ABS 0x03 // 绝对位移
#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_MAX 0x1f
unsigned long keybit[NBITS(KEY_MAX)]; // 表示能产生哪些按键
unsigned long relbit[NBITS(REL_MAX)]; // 表示能产生哪些相对位移
unsigned long absbit[NBITS(ABS_MAX)]; // 表示能产生哪些绝对位移事件
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)]; //
测试:
1.
hexdump /dev/event1 (open(/dev/event1), read(), ) hexdump是以16进制显示。
秒 微秒 类 code value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000 // code 值为26为十六进制,转为十进制为38.
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000 // 而#define KEY_L 38
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000
struct input_event {
struct timeval time; // 占4字节
__u16 type; // 占2字节
__u16 code; // 占2字节
__s32 value; // 占4字节
};
2. 如果没有启动QT: // 启动QT后,终端显示定位到LCD上,所以直接在串口终端上看不到想要的结果。
cat /dev/tty1
按:s2,s3,s4
就可以得到ls
或者:
exec 0
然后可以使用按键来输入
#define TTY_NORMAL 0
#define TTY_BREAK 1
#define TTY_FRAME 2
#define TTY_PARITY 3
#define TTY_OVERRUN 4
上面的就是将/dev/tty1改为普通模式,定义在tty.h中。
3. 如果已经启动了QT:
可以点开记事本
然后按:s2,s3,s4
只要按照输入子系统框架的4个要点来写驱动,然后加以完善就可以了,当然,完善的过程中肯定会碰到很多的问题,从分析源码入手,往往可以解决问题。这几次的笔记感觉就是照搬一些东西,但是慢慢的感觉真上来了,没有以前的那种恐怖感了。相信再多炼下,应该很快就上手。
另,现在新的内核驱动基本上都是挂platform总线上,我也问过做平板驱动的朋友,他说他们的驱动也是基本上都是挂platform上面。platform就是为了简化驱动程序的分析而创建的一个虚拟总线。当然也是现在很主流的。但是为了知道具体的工作原理和一些基本设备的驱动方式,我想等我学完后再综合起来看platform,我相信那时候看应该是很容易的,也容易理解得多。因为现在的学习过程中也会遇到很多不容易理解的,慢慢摸索了就明白了。
阅读(2747) | 评论(2) | 转发(0) |