• 十六、输入子系统驱动模型


    一、概述

    • 输入子系统驱动模型也是字符设备驱动的一种模型,是对普通字符设备驱动的封装,专门用于输入类型的设备。
    • 嵌入式linux系统中的输入类型设备:按键、鼠标、键盘、触摸屏、游戏手柄、手写板等等。

    二、输入子系统驱动模型的优点

    • 简化普通字符设备驱动的设计步骤
    • 给应用程序提供统一的标准接口。硬件平台不同,驱动程序不同,但是驱动给应用程序的接口是相同的,应用程序是相同的。

    三、查看输入设备

    1、输入设备的设备文件

     

    输入设备的主设备号都是13,次设备号不同。

    2、设备名称

     

     3、查看输入设备驱动信息

     

     4.简单读取输入设备文件的内容

    [root@GEC6818 /]#cat /dev/input/event0

    四、应用程序如何读取驱动提供的数据

    对于输入子系统驱动模型,驱动程序提供应用程序的是一个统一格式的结构体。

    struct input_event {
    	struct timeval time;    //输入时间发生的时间戳
    	__u16 type;              //输入设备的类型    
    	__u16 code;             //   
    	__s32 value;
    };     

     

    结构体成员说明:

    (1)time:输入事件发生的时间戳。
    (2)type:输入设备的类型。
    #define EV_SYN			0x00-----同步类型的事件,按键或触摸屏每次动作都会触发一次同步类型的事件
    #define EV_KEY			0x01----按键类型的事件,键盘或鼠标的左右键
    #define EV_REL			0x02----相对位移事件,鼠标滑动
    #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
    #define EV_CNT			(EV_MAX+1)
    
    (3)code:输入事件的编码
    • 如果type==EV_ABS(绝对位移),测code表示的是坐标轴方向。
    /*
     * Absolute axes
     */
    
    #define ABS_X			0x00----x坐标轴
    #define ABS_Y			0x01----y坐标轴
    #define ABS_Z			0x02
    
    • 如果type==EV_REL(相对位移),那么code表示的是坐标轴方向
    #define REL_X			0x00----x坐标轴
    #define REL_Y			0x01----y坐标轴
    #define REL_Z			0x02----z坐标轴
    • 如果type==EV_KEY,那么code表示的是具体哪一个按键
    #define KEY_1			2
    #define KEY_2			3
    #define KEY_3			4
    #define KEY_4			5
    #define KEY_5			6
    
    #define KEY_A			30
    #define KEY_S			31
    #define KEY_D			32
    #define KEY_F			33
    
    (4)value
    如果type==EV_KEY && code == KEY_A,那么value表示的是按键A的状态:0----松开,1----按下,2---长按
    如果type==EV_ABS && code == ABS_X,那么value表示的是X轴的具体坐标值
    				&& code == ABS_Y,那么value表示的是Y轴的具体坐标值 

    五、输入子系统驱动设计---以开发板上按键为例

    1、定义一个输入设备(input_dev结构体变量)

    struct input_dev
    {
    	const char *name;----输入设备的名字  #cat /proc/bus/input/devices 
    	const char *phys;
    	const char *uniq;
    	struct input_id id;----I: Bus=0018 Vendor=12fa Product=2143 Version=0100
    	----------------------------------------------
    	struct input_id {
    		__u16 bustype;----总线类型Bus
    		__u16 vendor;-----厂商编码Vendor
    		__u16 product;----产品编号Product
    		__u16 version;----版本号Version
    	};
    		
    
    	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)];----如果type==EV_KEY,keybit标识的是那些按键
    	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_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;
    	unsigned int users_private;
    	bool going_away;
    	bool disabled;
    
    	bool sync;
    
    	struct device dev;
    
    	struct list_head	h_list;
    	struct list_head	node;
    }
    
    例如:static struct input_dev *gec6818_key;
    

    2、给输入设备结构体指针分配内存空间,并做基本的初始化。

    /**
     * input_allocate_device - allocate memory for new input device
     *
     * Returns prepared struct input_dev or NULL.
     *
     * NOTE: Use input_free_device() to free devices that have not been
     * registered; input_unregister_device() should be used for already
     * registered devices.
     */
    struct input_dev *input_allocate_device(void)

    3、向内核注册输入设备结构体

    /**
     * input_register_device - register device with input core
     * @dev: device to be registered
     *
     * This function registers device with input core. The device must be
     * allocated with input_allocate_device() and all it's capabilities
     * set up before registering.
     * If function fails the device must be freed with input_free_device().
     * Once device has been successfully registered it can be unregistered
     * with input_unregister_device(); input_free_device() should not be
     * called in this case.
     */
    
    int input_register_device(struct input_dev *dev)
    

    4、注册申请中断

    static inline int __must_check
    request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
    	    const char *name, void *dev)
    

    5、中断处理程序

    irqreturn_t xxx_handler(int, void *)
    {
    
    	//上报输入事件
    	void input_event(struct input_dev *dev,
    			unsigned int type, unsigned int code, int value)
    
    }
    

    6、驱动模块的出口函数

    (1)注销输入设备 :
    void input_unregister_device(struct input_dev *dev)
    (2)释放输入设备内存空间  
    void input_free_device(struct input_dev *dev) 

      

    六、驱动设计的注意事项

    1、输入子系统模型中,自带等待队列。如果没有输入事件,应用程序中的read会阻塞;如果有输入事件,应用程序会被唤醒

    2、输入子系统模型中,自带了默认的文件操作集合,不需要提供文件操作集合

    3、输入设备结构体中evbit keybit的位操作,要用到内核源码提供的位接口函数,以下几个位接口函数是原子操作,在位操作时不会 被打断;而”或“和”与“不是原子操作在与或过程中会被打断。

    /*
     * NMI events can occur at any time, including when interrupts have been
     * disabled by *_irqsave().  So you can get NMI events occurring while a
     * *_bit function is holding a spin lock.  If the NMI handler also wants
     * to do bit manipulation (and they do) then you can get a deadlock
     * between the original caller of *_bit() and the NMI handler.
     *
     * by Keith Owens
     */
    
    /**
     * set_bit - Atomically set a bit in memory
     * @nr: the bit to set
     * @addr: the address to start counting from
     *
     * This function is atomic and may not be reordered.  See __set_bit()
     * if you do not require the atomic guarantees.
     *
     * Note: there are no guarantees that this function will not be reordered
     * on non x86 architectures, so if you are writing portable code,
     * make sure not to rely on its reordering guarantees.
     *
     * Note that @nr may be almost arbitrarily large; this function is not
     * restricted to acting on a single-word quantity.
     */
    static inline void set_bit(int nr, volatile unsigned long *addr)
    
    
    /**
     * clear_bit - Clears a bit in memory
     * @nr: Bit to clear
     * @addr: Address to start counting from
     *
     * clear_bit() is atomic and may not be reordered.  However, it does
     * not contain a memory barrier, so if it is used for locking purposes,
     * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
     * in order to ensure changes are visible on other processors.
     */
    static inline void clear_bit(int nr, volatile unsigned long *addr)
    
    
    /**
     * change_bit - Toggle a bit in memory
     * @nr: Bit to change
     * @addr: Address to start counting from
     *
     * change_bit() is atomic and may not be reordered. It may be
     * reordered on other architectures than x86.
     * Note that @nr may be almost arbitrarily large; this function is not
     * restricted to acting on a single-word quantity.
     */
    static inline void change_bit(int nr, volatile unsigned long *addr)
    

    4、按键驱动中必须有按下和松开两种状态,如果只检测按下(下降沿触发),没有松开,程序会卡死。所以,中断注册时,上升沿和下降沿都触发中断。  

    七、按键输入设备驱动代码

    key_drv.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/ioport.h>
    #include <linux/io.h>
    #include <linux/uaccess.h>
    #include <linux/types.h>
    #include <linux/gpio.h>
    #include <cfg_type.h>
    #include <linux/interrupt.h>
    #include <linux/miscdevice.h>
    #include <linux/delay.h>
    #include <linux/input.h>
    struct key_gpio_t{
    	unsigned int irq;
    	unsigned int gpiono;
    	char irqname[20];
    	unsigned char keyvalue;
    	unsigned int code;
    };
    
    
    static struct input_dev *key_input=NULL;
    
    
    static struct key_gpio_t key_gpio[]=
    {
    	{IRQ_GPIO_A_START+28,PAD_GPIO_A+28,"KEY2_GPIOA28",2,KEY_UP},
    	{IRQ_GPIO_B_START+30,PAD_GPIO_B+30,"KEY3_GPIOB30",3,KEY_DOWN},
    	{IRQ_GPIO_B_START+31,PAD_GPIO_B+31,"KEY4_GPIOB31",4,KEY_LEFT},
    	{IRQ_GPIO_B_START+9,  PAD_GPIO_B+9,"KEY6_GPIOB9",6,KEY_LEFT},
    };
    
    static irqreturn_t key_handler(int irq, void * dev)
    {
       	int value;
    	struct key_gpio_t keytmp=*(struct key_gpio_t *)dev;
    	value=gpio_get_value(keytmp.gpiono);
    	input_report_key(key_input, keytmp.code, !value); //上报输入事件
    	input_sync(key_input);  //每次上报事件结束,都需要上报一次同步事件
    	return IRQ_HANDLED;
    }	
    
    static int __init  key_init(void)
    {
          int ret,i;
           printk(KERN_INFO"key_init\n");
    	key_input = input_allocate_device();
    	if(key_input==NULL)
    	{
    		printk(KERN_INFO"allocate input device failed.\n");
    		ret = -ENOMEM;
    		goto input_allocate_device_err;
    	}
    	key_input->name = "key_input_dev";
    	key_input->id.bustype = 0x0001;
    	key_input->id.vendor= 0x0002;
    	key_input->id.product= 0x0003;
    	key_input->id.version = 0x0004;
    	//该输入设备能够触发按键类型的事件
    	set_bit(EV_KEY,key_input->evbit);
    	//触发的是按键类型中 的KEY_UP KEY_DOWN KEY_LEFT KEY_RIGHT
    	for(i=0;i<4;i++)
    	{
    		set_bit(key_gpio[i].code,key_input->keybit);
    	}
    	
    	//注册输入设备
        ret =  input_register_device(key_input);
    	if(ret < 0)
    	{
    		printk(KERN_INFO"register input dev failed.\n");
    		goto input_register_device_err;
    	}
    	
    	for(i=0;i<4;i++)
    	{
    		//按键按下和释放都要检测到
    		 ret = request_irq(key_gpio[i].irq, key_handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,key_gpio[i].irqname,(void*)&key_gpio[i]);
    		 if(ret < 0)
    		 {
    		 	printk(KERN_INFO"request_irq fail.\n");
    			goto irq_request_err;
    		 }
    	}
    	return 0;
    
    
    irq_request_err:
    	while(i--) 
    	{
    		free_irq(key_gpio[i].irq,NULL);
    	}
    	input_unregister_device(key_input);
    input_register_device_err:
    		 input_free_device(key_input);
    input_allocate_device_err:
    		return ret;
    }
    
    static void __exit key_exit(void)
    {
    	int i;
        printk(KERN_INFO"key_exit\n"); 
         
          for(i=0;i<4;i++) 
         {
    		free_irq(key_gpio[i].irq,(void *)&key_gpio[i]);
         }
       input_unregister_device(key_input);	  
        input_free_device(key_input);	  
        	
    }
    
    module_init(key_init);
    module_exit(key_exit);
    MODULE_LICENSE("GPL"); 

    main.c

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    #include <linux/input.h>
    
    struct input_event keyinfo;
    int main()
    {
        int fd,ret;
    
        fd = open("/dev/input/event4",O_RDONLY);
        if(fd<0)
        {
            perror("open event4 error!");      
            return -1;  
        }
        while(1)
        {
            ret=read(fd,&keyinfo,sizeof(keyinfo));   //没有按键动作,阻塞。
            /*
            if(ret !=1)
            {
                perror("read error");
                continue;
            }*/
           if(keyinfo.type==EV_KEY) 
           {
                    switch(keyinfo.code)
                    {
                            case KEY_UP:
                                    if(keyinfo.value == 1)
                                    {
                                        printf("key_up press\n");
                                    }
                                    else if(keyinfo.value == 0)
                                    {
                                        printf("key_up release\n");
                                    }
                            break;
    
                            case KEY_DOWN:
                                    if(keyinfo.value == 1)
                                    {
                                        printf("KEY_DOWN press\n");
                                    }
                                    else if(keyinfo.value == 0)
                                    {
                                        printf("KEY_DOWN release\n");
                                    }
                            break;
                            case KEY_LEFT:
                                    if(keyinfo.value == 1)
                                    {
                                        printf("KEY_LEFT press\n");
                                    }
                                    else if(keyinfo.value == 0)
                                    {
                                        printf("KEY_LEFT release\n");
                                    }
                            break;
    
                            case KEY_RIGHT:
                                    if(keyinfo.value == 1)
                                    {
                                        printf("KEY_RIGHT press\n");
                                    }
                                    else if(keyinfo.value == 0)
                                    {
                                        printf("KEY_RIGHT release\n");
                                    }
                            break;
                            default:
                             printf("other\n");
                            break;     
                    }      
           }
        }
        
         close(fd);
    }
    

      

        

      

      

      

      

      

      

     

     

  • 相关阅读:
    选项菜单-OptionMenu
    Android Studio教程
    android 使用layer-list
    JavaScript OOP 学习总结
    Android应用中网络请求库Volley的使用
    Android应用中网络请求库Volley的介绍
    Android UI: LinearLayout中layout_weight 属性的使用规则
    Robot Framework 培训
    Begin :SWIFT 基本语法
    树莓派raspberrypi系统安装docker以及编译nginx和php镜像
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15757521.html
Copyright © 2020-2023  润新知