linux驱动之输入子系统

一、输入子系统简介

​ 输入子系统就是Linux内核专门针对输入设备创建的框架驱动。

​ 输入子系统的主设备号都为13,我们使用input子系统处理输入设备的时候就不需要注册字符设备了。只需要定义input_device结构体即可。定义在linux/input.h中。

​ 看结构体可知大概功能,按键的事件,按键,相对位置,绝对位置,其他,led,音频,前馈状态,开关。每一项的具体功能可以进一步追踪宏条件。比如EV_CNT都有哪些事件。

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)];
	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)];
	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 *mt;

	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;

	struct device dev;

	struct list_head	h_list;
	struct list_head	node;

	unsigned int num_vals;
	unsigned int max_vals;
	struct input_value *vals;

	bool devres_managed;
};

二、输入子系统的编程

1.初始化输入子系统

​ 1.定义输入子系统的input_dev成员。

​ 2.申请输入设备input_allocate_device

​ 3.设置事件值及事件类型。

​ 4.注销驱动的时候要释放掉设备input_unregister_device

​ 5.删除输入设备input_free_device

2.上报事件

​ 上报事件的API函数input_event.设备,事件类型,按键码,事件值

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
//基于input_event的不同类型而创建的函数
static inline void input_report_key(struct input_dev *dev,unsigned int code, int value)
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value);
static inline void input_mt_sync(struct input_dev *dev);
//同步上报事件
static inline void input_sync(struct input_dev *dev);

3.驱动程序

#include<linux/module.h>
#include<linux/init.h> 
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/slab.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/io.h>
#include<linux/ioctl.h>
#include<linux/platform_device.h>
#include<linux/irq.h>
#include<linux/of_irq.h>
#include<linux/interrupt.h>
#include<linux/input.h>
#include<linux/device.h>
#include<linux/jiffies.h>
#include<asm/uaccess.h>


#define     INPUT_CNT             1
#define     INPUT_MINOR           11
#define     INPUT_NAME            "inputkey"
#define     KEYVALUE              0x01
#define     INVAIL                0xff

#define input_OPEN         _IOW(0xEF , 1 , int)
#define input_CLOSE        _IOW(0xEF , 2 , int)

/*中断IO描述结构体*/
struct irq_keydesc{
    int gpio;                               /*gpio号*/
    int irqnum;                             /*中断号*/
    char name[10];                          /*名字*/
    unsigned char value;                    /*中断按键值*/
    irqreturn_t (*handler)(int , void*);    /*中断服务函数*/
};

/*设备结构体*/
struct input_desc{
    dev_t           devid;
    int             major;
    int             minor;
    struct class    *class;
    struct device   *device;
    struct cdev     cdev;
    struct device_node *nd;

    struct timer_list timer;
    struct irq_keydesc irq[1];
    struct input_dev   *inputdev;

    int currentnum;
};

struct input_desc input;
/*
    中断处理函数
*/
static irqreturn_t key_irq_handler(int irq , void *dev_id){
    struct input_desc *dev =(struct input_desc *)dev_id;

    dev->currentnum = 0;
    dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer , jiffies + msecs_to_jiffies(10));
    return IRQ_RETVAL(IRQ_HANDLED);
}
/*
    定时器处理函数
*/
void timer_handler(unsigned long arg){
    unsigned char value;
    unsigned char num;
    struct input_desc *dev = (struct input_desc *)arg;

    num = dev->currentnum;
    value = gpio_get_value(dev->irq[0].gpio);
    if(value == 1){
        input_report_key(dev->inputdev , dev->irq[0].value , 1);//上报按键KEY_0
        input_sync(dev->inputdev);                           //同步上报
    }else{
        input_report_key(dev->inputdev , dev->irq[0].value , 0);//上报按键KEY_0
        input_sync(dev->inputdev);                           //同步上报
    }

}

/*
    驱动加载模块
*/
static int __init input_init(void){
    int ret = 0;

    printk("----------input_init----------\r\n");
    /*  1.找结点    */
    input.nd = of_find_node_by_path("/key");
    if(input.nd == NULL){
        goto fail_findnd;
    }

    /*  2.申请IO    */
    input.irq[0].gpio = of_get_named_gpio(input.nd , "key-gpios" , 0);
    if(input.irq[0].gpio < 0){
        goto fail_gpio;
    }
    gpio_request(input.irq[0].gpio , "KEY");
    gpio_direction_input(input.irq[0].gpio);
    input.irq[0].irqnum = irq_of_parse_and_map(input.nd , 0);

    /*  4.申请中断  */
    input.irq[0].handler = key_irq_handler;
    input.irq[0].value = KEY_0;
    input.irq[0].name[0] = "KEY_0";
    ret = request_irq(input.irq[0].irqnum , input.irq[0].handler , 
                    IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                    input.irq[0].name , &input);
    if(ret < 0){
        goto fail_request;
    }

    /*  5.初始化定时器  */
    init_timer(&input.timer);
    input.timer.function = timer_handler;
 
    /*  6.申请输入子系统    */
    input.inputdev = input_allocate_device();
    input.inputdev->name = INPUT_NAME;

    __set_bit(EV_KEY , input.inputdev->evbit);
    __set_bit(EV_REP , input.inputdev->evbit);
    __set_bit(KEY_0 , input.inputdev->keybit);

    /*  7.注册输入设备   */
    ret = input_register_device(input.inputdev);
    if(ret < 0){
        goto fail_register;
    }

    return 0;
fail_register:
    printk("fail_register\r\n");
    del_timer_sync(&input.timer);
    free_irq(input.irq[0].irqnum ,&input);
fail_request:
    printk("fail_request\r\n");
fail_gpio:
    printk("fail_gpio\r\n");
fail_findnd:
    printk("fail_findnd\r\n");
    return -EINVAL;
}
/*
    驱动注销模块    
*/
static void __exit input_exit(void){
    printk("----------input_exit----------\r\n");
    del_timer_sync(&input.timer);
    free_irq(input.irq[0].irqnum ,&input);
    /*释放输入设备*/
    input_unregister_device(input.inputdev);
    input_free_device(input.inputdev);
}

module_init(input_init);
module_exit(input_exit);
MODULE_LICENSE("GPL");

4.应用程序

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/input.h>

static struct input_event inputevent;

/*
    要求命令    ./led_app <read/write>
*/
int main(int argc, char *argv[])
{
    int key_fd = 0 , ret = 0 ;
    unsigned int cmd;
    unsigned char arg;
    unsigned char str[100];
    
    key_fd = open("/dev/input/event2" , O_RDWR);
    if(key_fd < 0){
        printf("打开KEY设备结点失败\n");
        return -1;
    }

    while(1){
        ret = read(key_fd , &inputevent , sizeof(inputevent));
        if(ret > 0){
            switch(inputevent.type){
                case EV_KEY:
                    if(inputevent.code < BTN_MISC){
                        printf("key %d %s \r\n",inputevent.code ,
                                inputevent.value ? "press" : "release");
                    }else{
                        printf("button %d %s \r\n",inputevent.code ,
                                inputevent.value ? "press" : "release");
                    }
                break;
            }
        }
    }
    close(key_fd);
    return 0;
}

三、测试

​ 在注册input子系统以后,在debian中发现/dev/input中产生一个event2的设备文件。

​ 定义的副设备号为11,打印按键值为11

key 11 press
key 11 release

​ 也可以使用hexdump指令进行测试,不用app程序。

每当按下一次按钮就会产生数据

/*  编号 */  /* tv_sec */ /* tv_usec */ /* type */  /* code */ /* value */
0000000		 0c41 0000 	 d7cd 000c 		0001 		000b 	0001 0000
0000010 	 0c41 0000 	 d7cd 000c 		0000 		0000	0000 0000
0000020 	 0c42 0000   54bb 0000 		0001 		000b 	0000 0000
0000030 	 0c42 0000 	 54bb 0000 		0000 		0000 	0000 0000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值