上一节我们知道,写符合输入子系统框架的驱动程序的步骤为:
1. 分配一个input_dev结构体
2. 设置
3. 注册
4. 硬件相关的代码,比如在中断服务程序里上报事件
参考我们/*参考gpio_keys.c代码和他的probe函数*/
input子系统的主设备号为13
1.写出输入子系统框架代码:
/*参考gpio_keys.c代码和他的probe函数*/
#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 <linux/gpio_keys.h>
#include <asm/gpio.h>
struct input_dev *input_mybuttons_dev;
static int input_mybuttons_init(void)
{
/*1.分配一个input_dev结构体*/
input_mybuttons_dev = input_allocate_device();
/*2.设置结构体*/
//2.1设置结构体的evbit,表示能产生按键类事件
set_bit(EV_KEY,input_mybuttons_dev->evbit);
set_bit(EV_REP,input_mybuttons_dev->evbit);//产生重复类事件,会调用定时器,如一直按下按键一直输入键值
//2.2设置结构体的evbit,表示能产生按键类事件
set_bit(KEY_L,input_mybuttons_dev->keybit);
set_bit(KEY_S,input_mybuttons_dev->keybit);
set_bit(KEY_ENTER,input_mybuttons_dev->keybit);
set_bit(KEY_LEFTSHIFT,input_mybuttons_dev->keybit);
/*3.注册*/
input_register_device(input_mybuttons_dev);
/*4.硬件相关操作*/
return 0;
}
static void input_mybuttons_exit(void)
{
}
module_init(input_mybuttons_init);
module_exit(input_mybuttons_exit);
MODULE_LICENSE("GPL");
2.硬件相关操作:
复制以前的硬件代码:
修改定义,在init里注册四个中断
主要是mybuttons_irq,mybuttons_irq之前做的是定时器mod_timer。现在也这样
并在init函数里初始化定时器
修改function,以前写的function使用了唤醒,已经kill_fasync发信号。现在这些都不用做,只需要使用input_event上报事件(input_event最终会调用input_handler里的event函数),内核自动就会帮我们实现这些功能。
写出口函数,释放
3.总结:
以前我们要自己写file_operations里要写open,read,write,还要写class_create和class_device_create(创建类设备,和类设备节点)现在都不用写,输入子系统帮我们写好,在evdev里的file_operations。只不过分成了两层:input_handler和input_device以及使用input_allocate_device和input_register_device注册,并在内核代码里实现connect里创建类设备和设备节点。设备名为/dev/eventX:
evdev.c里的evdev_handler里的connect函数
我们只需要做硬件相关的部分就行,即input_device
4.代码:
/*参考gpio_keys.c代码和他的probe函数*/
#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 <linux/gpio_keys.h>
#include <asm/gpio.h>
struct timer_list mybuttons_timer;
struct pin_desc *irq_dev;
struct input_dev *input_mybuttons_dev;
struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};
struct pin_desc pins_desc[4] = {
{IRQ_EINT0, "S1",S3C2410_GPF0, KEY_L},
{IRQ_EINT2, "S2",S3C2410_GPF2, KEY_S},
{IRQ_EINT11,"S3",S3C2410_GPG3, KEY_ENTER},
{IRQ_EINT19,"S4",S3C2410_GPG11,KEY_LEFTSHIFT},
};
static irqreturn_t mybuttons_irq(int irq, void *dev_id)
{
/*让他10ms后启动mybuttons_timerfun*/
irq_dev=(struct pin_desc *)dev_id;
mod_timer(&mybuttons_timer,jiffies+HZ/100);//jiffies是当前的tick,HZ代表1s,HZ/100=10ms.jiffies会++到jiffies+10ms后产生定时器中断
return IRQ_RETVAL(IRQ_HANDLED);
}
static void mybuttons_timerfun(unsigned long p)
{
unsigned int pinval;
struct pin_desc *pindesc =irq_dev;
if(!pindesc)
return;
pinval = s3c2410_gpio_getpin(pindesc->pin); //内核提供的读取引脚值的函数
if(pinval)
{
// 松开: 最后一个参数,1为按下,0为松开
input_event(input_mybuttons_dev,EV_KEY,pindesc->key_val,0);
input_sync(input_mybuttons_dev);//表示已经上报完
}
else
{
input_event(input_mybuttons_dev,EV_KEY,pindesc->key_val,1);
input_sync(input_mybuttons_dev);
}
}
static int input_mybuttons_init(void)
{
int i;
/*1.分配一个input_dev结构体*/
input_mybuttons_dev = input_allocate_device();
/*2.设置结构体*/
//2.1设置结构体的evbit,表示能产生哪一类事件-按键类事件
set_bit(EV_KEY,input_mybuttons_dev->evbit);
set_bit(EV_REP,input_mybuttons_dev->evbit);//产生重复类事件,会调用定时器,如一直按下按键一直输入键值
//2.2设置结构体的evbit,表示能产生按键类事件
set_bit(KEY_L,input_mybuttons_dev->keybit);
set_bit(KEY_S,input_mybuttons_dev->keybit);
set_bit(KEY_ENTER,input_mybuttons_dev->keybit);
set_bit(KEY_LEFTSHIFT,input_mybuttons_dev->keybit);
/*3.注册*/
input_register_device(input_mybuttons_dev);
/*4.硬件相关操作*/
/*初始化定时器,为了防抖动*/
init_timer(&mybuttons_timer); //初始化定时器
//mybuttons_timer.data = (unsigned long) SCpnt; //function的传参,不用可省略
//mybuttons_timer.expires = jiffies + 100*HZ; /* 超时时间,放在中断处理函数使用,没有设置默认=0,即fuction会立即执行 */
mybuttons_timer.function = mybuttons_timerfun;
add_timer(&mybuttons_timer);
/*注册四个中断*/
for(i=0;i<4;i++)
{
request_irq(pins_desc[i].irq,mybuttons_irq, IRQT_BOTHEDGE,pins_desc[i].name,&pins_desc[i]);
}
return 0;
}
static void input_mybuttons_exit(void)
{
int i;
for(i=0;i<4;i++)
{
free_irq(pins_desc[i].irq,&pins_desc[i]);
}
del_timer(&mybuttons_timer);
input_unregister_device(input_mybuttons_dev);
input_free_device(input_mybuttons_dev);
}
module_init(input_mybuttons_init);
module_exit(input_mybuttons_exit);
MODULE_LICENSE("GPL");
5.测试:
直接insmod input_mybuttons.ko
ls -l /dev/event*
就可以看到已经装载了驱动/dev/event1
方法一:
# hexdump /dev/event1
hexdump /dev/event1 会去做:
1.open( /dev/event1);
2.read(); //调用evdev_read函数,最终把结构体input_event copy_to_user(发到用户空间),这样应用空间就可以读到
执行 hexdump /dev/event1,按下按键1,打印出:
# hexdump /dev/event1
秒 微妙 类 code 值
按下:0000000 0046 0000 fe20 0000 0001 0026 0001 0000
0000010 0046 0000 fe2a 0000 0000 0000 0000 0000
松开:0000020 0046 0000 8092 0004 0001 0026 0000 0000
0000030 0046 0000 809a 0004 0000 0000 0000 0000
因为1表示按下,0表示松开,code显示如上,所以上面的输出是对的
方法二:
# cat /dev/tty1(没有启动QT的话)
执行后按下按键123,会打印
ls
什么是tty1?
主设备号为4,此设备号为1,是通过tty1_io.c去访问到keyboard.c
方法三:
执行exec 0</dev/tty1 (把/dev/tty1的输入重定位到标准输入)
环形缓冲区: