zoukankan      html  css  js  c++  java
  • 7.输入子系统框架

    1.框架小结

    1、入口input_init:注册类,注册驱动

      class_create(&input_class);
      register_chrdev(INPUT_MAJOR, "input", &input_fops);
    

    2、打开驱动,将真正的file_operations引入执行open,该文件同时储存input_table

      struct input_handler *handle = imput_table[minor >> 5]            //8bit >> 5 = 3bit(8个索引)
                                                                          这里此设备号中的低5位用作特殊处理
    

    3、input_register_handler,初始化input_table处理函数,添加实际的handler,加入全局链表input_handler_list,同时调用connect建立handlerdev连接
    4、input_register_device注册具体的input_dev,加入到全局链表input_dev_list,同时调用connect建立handlerdev的连接
    5、connect建立链接关系,这里有一个新的连接结构handler,包含了handlerinput_dev,这里具体的新建一个evdev结构变量,成员变量指向上面创建的devhandler,分配此设备好创建设备。调用input_register_handler,创建一个管理结构
    6、app读取数据最终会调用到handlerfile中的read,阻塞方式如果进入休眠会被中断调用handlerevent来唤醒

    2.有架构驱动与无架构驱动的对比

    无架构驱动

      1.确定主设备号
      2.编写file_oprention结构体编写硬件相关操作
      3.构造class结构,自动创建dev设备节点
      4.注册设备驱动
      5.定义入口和出口
    

    输入子系统架构

      input.c完成了主设备的注册,驱动设备注册,在这个框架下需要实现硬件设备驱动的程序即可。
      input_device设备驱动
      input_handler处理架构
      1.向内核申请input_dev结构体
      2.设置input_dev的成员
      3.注册input_dev驱动设备
      4.初始化定时器和中断
      5.写中断服务函数
      6.写定时器超时函数
      7.在出口函数中,释放中断函数,删除定时器,卸载驱动,释放驱动
    

    3.关键函数、结构体解析

    input_dev :设备结构

    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)];              // 支持哪些事件
            /* EN_SYN:同步事件,当使用input_event()函数后就要使用这个上报同步事件
             * EN_KEY:键盘事件
             * EV_REL:相对坐标事件,eg:鼠标
             * EV_ABS:绝对坐标事件,eg:摇杆,触摸屏
             * EV_MSC:其他事件,功能
             * EV_LED:LED灯事件
             * EV_SND:声音事件
             * EV_REP:重复键盘按键事件(内部有一个定时器,若有按键事件一直按下/松动,就重复定时,时间一到就上报事件)
             * EV_FF: 受力事件
             * EV_PWR:电源事件
             * EV_FF_STATUB: 受力状态事件
             */
    
    	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)];            // 存放支持的各种状态的LED
    	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;
    	bool going_away;
    
    	bool sync;
    
    	struct device dev;
    
    	struct list_head	h_list;
    	struct list_head	node;
    };
    

    input_allocate_device:向内核申请一个input_dev设备,然后返回这个设备

      struct input_dev *input_allocate_device(void)
    

    input_free_device:释放这个结构体,一般在驱动出口函数

    void input_free_device(struct input_dev *dev)
    EXPORT_SYMBOL(input_free_device);
    

    input_unregister_device:卸载/sys/class/input目录下的input_dev这类设备,一般在驱动的出口处写

    void input_unregister_device(struct input_dev *dev)
    EXPORT_SYMBOL(input_unregister_device);            //标签内定义的函数或者符号对全部内核代码公开
    

    set_bit:用来设置为变量,这里用来设置支持的事件类型

      static inline void set_bit(int nr, volatile unsigned long *addr)
      eg:      set_bit(KEY_L, buttons_dev->keybit);
    

    input_event:事件上报

      void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
      //dev:要上报那个input驱动设备事件
      //type:要上报的事件类型:eg:按键填EV_KEY
      //code:对应事件里支持那个变量,eg:如按下按键L,则填入KEY_L
      //value:对应变量里的数值,eg:松开按键填入0,按下按键填入1
      
      //注意上报完事件之后需要调用input_sync来同步事件
    

    input_sync:同时事件,其实上报事件并未把事件上报给内核,而是存储在某个地方,由intput_sync来实现上报

      static inline void input_sync(struct input_dev *dev)
      {
          input_event(dev, EV_SYN, SYN_REPORT, 0);
      }
    

    4.源码解析

    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-按下 */
    		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, 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");
  • 相关阅读:
    mint17上建立lamp环境
    iptables开始ftp
    查看mysql集群状态是否正常
    限制SSH访问源,禁止4A之外的地址跳转访问
    查看cpu、内存和硬盘
    降kipmi0的CPU
    更改密钥对
    eNSP
    划分分区GPT11
    修改虚机IP
  • 原文地址:https://www.cnblogs.com/huangdengtao/p/13296741.html
Copyright © 2011-2022 走看看