Linux驱动之input输入子系统

input输入子系统在实际项目中用的也比较多,按键,触摸屏,鼠标,键盘等,用来实现内核层和应用层数据之间的传递,这里得说明不只有input,还有copy_to_user等,利用input的好处是我们用自己上传数据到应用程序, 我们直接上报这个事件发生了,input自带的机制会实现上传的功能。还有很多开源的工具也是基于input输入来制作的,像tslib触摸检测程序和提取数据。


tips:不要启动QT程序,否则会出错,用cat /dev/tty1 来测试。

驱动程序:

/* 参考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 <linux/kernel.h>/*内核有关的*/
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/uaccess.h>  //copy_to_user
#include <mach/regs-gpio.h>/*寄存器设置*/
#include <mach/hardware.h>//s3c2410_gpio_getpin等的定义
#include <mach/irqs.h> //IRQ_EINT0等的定义
#include <asm/system.h>

struct pin_desc{     /* 定义一个结构体类型 */
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};

struct pin_desc pins_desc[4] = {      /* 定义这种类型的结构体数组并赋值 */
{IRQ_EINT0,  "S0", S3C2410_GPF0,   KEY_L},
{IRQ_EINT2,  "S2", S3C2410_GPF2,   KEY_S},
{IRQ_EINT3,  "S3", S3C2410_GPF3,   KEY_ENTER},
{IRQ_EINT4,  "S4", S3C2410_GPF4,   KEY_LEFTSHIFT},
};
static struct input_dev *buttons_dev;     /* 定义input_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, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 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_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,
根据input_handler的id_table判断这个input_handler能否支持这个input_dev,
如果能支持,则调用input_handler的connect函数建立"连接"


上面调用input上报事件最终都将调用到input_sync最终也将调用input_envent,该代码还有一个问题,细心的朋友应该看到了,怎么没有看到休眠和唤醒之类的代码呢?这些稳定的部分内核里面已经自带了,在input_event里面来实现唤醒的,这些内核在稳定部分已经实现了,我们只需读到数据直接上报就行了,具体应用程序怎么取读是应用层的事情。


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


#define EV_SYN 0x00      /* 同步类事件 */
#define EV_KEY 0x01/* 按键类事件 */
#define EV_REL 0x02/* 相对位移类事件 */
#define EV_ABS 0x03/* 绝对位移类事件 */


set_bit(EV_KEY, buttons_dev->evbit);                    /* 能产生按键类事件 */
set_bit(EV_REP, buttons_dev->evbit); /* 能产生重复类事件 */


set_bit(KEY_L, buttons_dev->keybit);            /*能产生(KEY_L这些事件*/
set_bit(KEY_S, buttons_dev->keybit);  /*能产生KEY_S这些事件*/
set_bit(KEY_ENTER, buttons_dev->keybit);     /*能产生KEY_ENTER这些事件*/
set_bit(KEY_LEFTSHIFT, buttons_dev->keybit);    /*能产生KEY_LEFTSHIFT这些事件*/

input_sync(buttons_dev);            /* 上报同步类事件 */

测试方法如下:

1. 
hexdump /dev/event1  (open(/dev/event1), read(), )
           秒        微秒    类  code    value
0000000 0bb2 0000 0e48 000c 0001 0026 0001 0000
0000010 0bb2 0000 0e54 000c 0000 0000 0000 0000
0000020 0bb2 0000 5815 000e 0001 0026 0000 0000
0000030 0bb2 0000 581f 000e 0000 0000 0000 0000


2. 如果没有启动QT:
cat /dev/tty1
按:s2,s3,s4
就可以得到ls


或者:
exec 0</dev/tty1                 /* 把标准输入文件改为tty1,0代表标准输入,1代表标准输出,2代表标准错误 */
然后可以使用按键来输入

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在开发输入子系统设备驱动时,通常遵循以下步骤: 1. 包含必要的头文件:在驱动程序的源文件中,你需要包含一些必要的头文件,例如`linux/input.h`、`linux/module.h`、`linux/init.h`等。 2. 定义驱动模块:使用`module_init`宏定义一个初始化函数,用来加载驱动程序。例如: ```c static int __init myinput_init(void) { // 驱动初始化逻辑 return 0; } module_init(myinput_init); ``` 3. 注册输入设备:在初始化函数中,你需要创建一个输入设备并进行注册。你可以使用`input_allocate_device`函数分配一个输入设备结构体,并设置一些属性,例如设备名称、支持的事件类型等。然后,使用`input_register_device`函数注册输入设备。例如: ```c static int __init myinput_init(void) { struct input_dev *myinput_dev; myinput_dev = input_allocate_device(); if (!myinput_dev) { pr_err("Failed to allocate input device\n"); return -ENOMEM; } // 设置设备名称、支持的事件类型等 input_register_device(myinput_dev); return 0; } ``` 4. 处理输入事件:注册完输入设备后,你需要实现一个中断处理函数或者定时器处理函数,用来处理输入事件。当触发输入事件时,驱动程序会调用该函数进行处理。你可以使用`input_report_*`系列函数上报输入事件,例如鼠标移动、按键按下等。例如: ```c static irqreturn_t myinput_interrupt(int irq, void *dev_id) { // 处理输入事件的逻辑 input_report_key(myinput_dev, KEY_A, 1); // 模拟按下 A 键 input_sync(myinput_dev); // 同步输入事件 return IRQ_HANDLED; } ``` 这只是一个简单的示例,实际的输入子系统设备驱动可能还需要处理更多的细节和特定的硬件接口。更详细的编写方法和实现细节可以参考Linux内核源中的驱动示例和相关文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值