zoukankan      html  css  js  c++  java
  • 驱动开发之输入子系统

    驱动开发之输入子系统:

    输入子系统
      事件处理层:drivers/input/evdev.c 给应用层提供统一的交互接口
      核心层:drivers/input/input.c 承上启下(提供的接口会被设备驱动调用,调用后会访问事件处理层)
      设备驱动层:自己实现,操作硬件

    xxx子系统意义:

      内核实现的,为了提高代码的通用性

    vi drivers/input/input.c

    2401 static int __init input_init(void)
    2402 {
    2405 err = class_register(&input_class);//创建设备类
    2411 err = input_proc_init();//在/proc目录下创建input文件夹
    2415 err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), //INPUT_MAJOR 为13 说明所有的输入子系统,使用的主设备号都是13
    2416 INPUT_MAX_CHAR_DEVICES, "input");
    静态注册设备号
    }


    vi Documetation/devices.txt

    488 13 char Input core 
    489 0 = /dev/input/js0 First joystick 游戏手柄
    490 1 = /dev/input/js1 Second joystick
    491 ...
    492 32 = /dev/input/mouse0 First mouse 鼠标
    493 33 = /dev/input/mouse1 Second mouse
    494 ...
    495 63 = /dev/input/mice Unified mouse 通用鼠标
    496 64 = /dev/input/event0 First event queue 除了手柄和鼠标之外的其他输入设备
    497 65 = /dev/input/event1 Second event queue
    498 ...

    通过输入子系统来实现驱动,基本操作过程:
    1、给指定的结构体申请空间

    1 struct input_dev *input_allocate_device(void)

    给struct input_dev申请空间
    2、基本配置
    3、将申请好的结构体注册到内核中

    1 int input_register_device(struct input_dev *dev)

    4、操作硬件

    2067 int input_register_device(struct input_dev *dev)
    ------>
    
    2138 input_attach_handler(dev, handler);
    ------->
    991 error = handler->connect(handler, dev, id);
    -------->
            进入事件处理层
    1202 static struct input_handler evdev_handler = {
    1203 .event = evdev_event,
    1204 .events = evdev_events,
    1205 .connect = evdev_connect, 
    1206 .disconnect = evdev_disconnect,
    1207 .legacy_minors = true,
    1208 .minor = EVDEV_MINOR_BASE,
    1209 .name = "evdev",
    1210 .id_table = evdev_ids,
    1211 };
    ---------->
    
    1065 static const struct file_operations evdev_fops = {
    1066 .owner = THIS_MODULE, 
    1067 .read = evdev_read,
    1068 .write = evdev_write,
    1069 .poll = evdev_poll,
    1070 .open = evdev_open,
    1071 .release = evdev_release,
    1072 .unlocked_ioctl = evdev_ioctl,
    1073 #ifdef CONFIG_COMPAT
    1074 .compat_ioctl = evdev_ioctl_compat,
    1075 #endif
    1076 .fasync = evdev_fasync,
    1077 .flush = evdev_flush,
    1078 .llseek = no_llseek,
    1079 };
    
    1113 static int evdev_connect(struct input_handler *handler, struct input_dev *d
    1114 const struct input_device_id *id)
    1115 {
    1116 struct evdev *evdev;
    1117 int minor; 
    1118 int dev_no;
    1119 int error;
    1120 
    //获取次设备号
    1121 minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
    1128 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    1140 dev_no = minor; 
    1142 if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
    1143 dev_no -= EVDEV_MINOR_BASE;
    1144 dev_set_name(&evdev->dev, "event%d", dev_no);//创建设备文件 
    1161 cdev_init(&evdev->cdev, &evdev_fops);
    1162 evdev->cdev.kobj.parent = &evdev->dev.kobj;
    1163 error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
    }
    
      

    字符设备框架的基本搭建过程:

    定义出各种接口函数; //已实现
    struct file_operations fops = { //已实现
    .owner = ,
    .open = ,
    .read = ,
    .....
    };
    加载函数
    {
      设备号 = MKDEV(主设备号,次设备号);
      register_chrdev_region(起始设备号,,);//已实现
      kzalloc();//申请空间,已实现
      cdev_init();//已实现
      cdev_add();//已实现
    
      class_create();//创建设备类 已实现
      device_create();//创建设备文件 已实现
    }

    总结:只要调用了input_register_chrdev()那么字符设备框架以及函数接口都可以被创建。

    vi -t input_event

    421 void input_event(struct input_dev *dev,
    422 unsigned int type, unsigned int code, int value) 
    423 {
    424   unsigned long flags;
    425 
    426   if (is_event_supported(type, dev->evbit, EV_MAX)) {//保证为真
    427 
    428       spin_lock_irqsave(&dev->event_lock, flags);
    429       input_handle_event(dev, type, code, value);
    430       spin_unlock_irqrestore(&dev->event_lock, flags);
    431   }
    432 }
    1 void input_event(struct input_dev *dev,unsigned int type, unsigned int code, int value) //
    调用唤醒接口

    参数1:
    参数2:事件的类型

    143 #define EV_SYN 0x00
    144 #define EV_KEY 0x01 
    145 #define EV_REL 0x02
    146 #define EV_ABS 0x03
    147 #define EV_MSC 0x04
    148 #define EV_SW 0x05
    149 #define EV_LED 0x11
    150 #define EV_SND 0x12
    151 #define EV_REP 0x14
    152 #define EV_FF 0x15
    153 #define EV_PWR 0x16
    154 #define EV_FF_STATUS 0x17
    155 #define EV_MAX 0x1f

    参数3:某种事件下,使用的具体值
    参数4:状态值,如果使用了按键,0代表按键按下状态,1代表按键抬起状态,

      2代表的只关心按下状态内核源码如下:

    ------->
    368 disposition = input_get_disposition(dev, type, code, value); 
    ------>
    281 case EV_KEY:
    282 if (is_event_supported(code, dev->keybit, KEY_MAX)) {
    283 
    284 /* auto-repeat bypasses state updates */ 
    285 if (value == 2) {
    286 disposition = INPUT_PASS_TO_HANDLERS;
    287 break;
    288 }
    289 
    290 if (!!test_bit(code, dev->key) != !!value) {
    291 
    292 __change_bit(code, dev->key);
    293 disposition = INPUT_PASS_TO_HANDLERS ;//结果为1
    294 }
    295 }
    296 break;

      参数4可以有逻辑真、逻辑假、可以是2。内核源码如下:

    376 if (disposition & INPUT_PASS_TO_HANDLERS) {
    377 struct input_value *v;
    378 
    379 if (disposition & INPUT_SLOT) {    //条件为假
    380 v = &dev->vals[dev->num_vals++];
    381 v->type = EV_ABS;
    382 v->code = ABS_MT_SLOT; 
    383 v->value = dev->mt->slot;
    384 }
    385 
    386 v = &dev->vals[dev->num_vals++];
    387 v->type = type;       //对结构体成员初始化,给别人用的
    388 v->code = code;
    389 v->value = value;
    390 }
    
    396 } else if (dev->num_vals >= dev->max_vals - 2) {
    397 dev->vals[dev->num_vals++] = input_value_sync;
    398 input_pass_values(dev, dev->vals, dev->num_vals);
    399 dev->num_vals = 0;
    400 }
    
    static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1};
    type = EV_SYN code = SYN_REPORT value = 1;
    
    ------->
    
    143 count = input_to_handler(handle, vals, count);
    
    ------>
    
    116 if (handler->events) 
    117 handler->events(handle, vals, count);
        如果条件成立则会调用evdev.c中的evdev_events函数
    ----------->
    1202 static struct input_handler evdev_handler = {
    1203 .event = evdev_event,
    1204 .events = evdev_events,
    1205 .connect = evdev_connect, 
    1206 .disconnect = evdev_disconnect,
    1207 .legacy_minors = true,
    1208 .minor = EVDEV_MINOR_BASE,
    1209 .name = "evdev",
    1210 .id_table = evdev_ids,
    1211 };
            进入事件处理层:
    197 static void evdev_events(struct input_handle *handle,
    198 const struct input_value *vals, unsigned int count)
    199 {
    211 if (client)
    212 evdev_pass_values(client, vals, count, time_mono, time_real);    
    } 
    ------->
    
    179 for (v = vals; v != vals + count; v++) {
    180 event.type = v->type; //将type code value的值存放给struct input_event结构体
    181 event.code = v->code;
    182 event.value = v->value;
    183 __pass_event(client, &event);
    184 if (v->type == EV_SYN && v->code == SYN_REPORT) //最初type=EV_KEY code=KEY_2
    185 wakeup = true;
    186 }
    187 
    188 spin_unlock(&client->buffer_lock);
    189 
    190 if (wakeup)
    191 wake_up_interruptible(&evdev->wait);//正常的需求下要调用唤醒接口。    
    //为了保证调用唤醒接口,必须要保证type=EV_SYN code=SYN_REPORT    

    如何保证?调用input_sync();

    1 input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口

    应用层中调用read(fd,&ev,sizeof(ev));实际上在驱动中会调用事件处理层定义的read函数

    vi drivers/input/evdev.c:

    vi drivers/input/evdev.c:
    484 static ssize_t evdev_read(struct file *file, char __user *buffer,
    485 size_t count, loff_t *ppos)
    {
    514     if (input_event_to_user(buffer + read, &event))
    47         compat_event.time.tv_sec = event->time.tv_sec;
    48     compat_event.time.tv_usec = event->time.tv_usec;
    49     compat_event.type = event->type;
    50     compat_event.code = event->code;
    51     compat_event.value = event->value;
    52 
    53     if (copy_to_user(buffer, &compat_event,sizeof(struct input_event_compat)))
    524     error = wait_event_interruptible(evdev->wait,client->packet_head != client->tail || !evdev->exist || client->revoked);
    }

    为什么需要调用set_bit函数?
    vi -t input_event

    426 if (is_event_supported(type, dev->evbit, EV_MAX)) {
    //调用时input_event(EV_KEY,KEY_2,2);传参后type=EV_KEY EV_MAX = 31
    
    -------->
    53 static inline int is_event_supported(unsigned int code,unsigned long *bm, unsigned int max)
    55 {
    56     return code <= max && test_bit(code, bm);
    57 } 
    //code=EV_KEY bm = dev->evbit 数组名 max = 31
    
    103 static inline int test_bit(int nr, const volatile unsigned long *addr) 
    104 {
    105     return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
    106 }
    //nr = EV_KEY addr = dev->evbit  1UL 代表的是unsigned long
    
    #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) //<==> 1 / 32 = 0
    addr[BIT_WORD(nr)] <==> addr[0] <==> dev->evbit[0];
    
    nr & (BITS_PER_LONG-1) <==> 1 & 31 结果为1
    
    (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))) //理解为dev->evbit[0] >> 1
    
    //1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))) 
    //理解为:1 & (dev->evbit[0] >> 1) 保证这个结果为真。暂时还保证不了,此时不知道dev->evbit[0]的值为多少。
    //解决方法:可以将一个数值放到dev->evbit[0]中并且保证整个结果为真.具体操作就需要调用set_bit()函数

    vi -t set_bit

    1 set_bit(EV_KEY,fs4412_idev->evbit)//基本配置
    //调用时传递的是:set_bit(EV_KEY,fs4412_idev->evbit)
    65 static inline void set_bit(int nr, volatile unsigned long *addr) 
    66 {    nr=EV_KEY=1 addr=fs4412_idev->evbit
    67 unsigned long mask = BIT_MASK(nr);
    68 unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
    69 unsigned long flags;
    70 
    71 _atomic_spin_lock_irqsave(p, flags);
    72 *p |= mask;
    73 _atomic_spin_unlock_irqrestore(p, flags);
    74 }
    
    
    #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) 
    //(1UL << (1 % 32)) <==> (1 << 1结果为2)
    
    #define BIT_WORD(nr) ((nr) / //BITS_PER_LONG) 《==》1 / 32 = 0
    unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); 
    = ((unsigned long *)addr) + 0;
    = ((unsignd long *)fs4412_idev->evbit);
    
    //*p |= mask; <==> *p = *p | mask;
    //*p = fs4412_idev->evbit[0] | mask;
    //*p = fs4412_idev->evbit[0] | 2;
    //fs4412_idev->evbit[0] = fs4412_idev->evbit[0] | 2;
    //不关心原有值是多少
    
    //最终:1 & (dev->evbit[0] >> 1) <==> 1 & (2 >> 1) 为真
    //所以if (is_event_supported(type, dev->evbit, EV_MAX))才为真。

    参考代码:

      1 #include <linux/module.h>
      2 #include <linux/platform_device.h>
      3 #include <linux/input.h>
      4 #include <linux/interrupt.h>
      5 #include <linux/irqreturn.h>
      6 #include <linux/sched.h>
      7 #include <linux/slab.h>
      8 
      9 struct input_dev *fs4412_idev;
     10 struct resource *res_key2;
     11 struct resource *res_key3;
     12 int flag2 = 0,flag3 = 0;//模拟按键的高低电平
     13 
     14 irqreturn_t fs4412_inputkey_handler(int irqno,void *id)
     15 {
     16 #if 0
     17     if(irqno == res_key2->start)
     18     {
     19         input_event(fs4412_idev,EV_KEY,KEY_2,2);//设置了type=,code=,value=,调用唤醒接口
     20         input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     21     }
     22     if(irqno == res_key3->start)
     23     {
     24         input_event(fs4412_idev,EV_KEY,KEY_3,2);
     25         input_sync(fs4412_idev);
     26     }
     27 #endif
     28     if(irqno == res_key2->start)
     29     {
     30         if(flag2 == 0)
     31         {
     32             input_event(fs4412_idev,EV_KEY,KEY_2,1);//设置了type=,code=,value=,调用唤醒接口
     33             input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     34             flag2 = 1;
     35         }
     36         if(flag2 == 1)
     37         {
     38             input_event(fs4412_idev,EV_KEY,KEY_2,0);//设置了type=,code=,value=,调用唤醒接口
     39             input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     40             flag2 = 0;
     41         }
     42     }
     43     if(irqno == res_key3->start)
     44     {
     45         if(flag3 == 0)
     46         {
     47             input_event(fs4412_idev,EV_KEY,KEY_3,1);
     48             input_sync(fs4412_idev);
     49             flag3 = 1;
     50         }
     51         if(flag3 == 1)
     52         {
     53             input_event(fs4412_idev,EV_KEY,KEY_3,0);
     54             input_sync(fs4412_idev);
     55             flag3 = 0;
     56         }
     57     }
     58 
     59     return IRQ_HANDLED;
     60 }
     61 
     62 struct of_device_id inputkey_match_tbl[] = {
     63     {
     64         .compatible = "fs4412,key",
     65     },
     66     {},
     67 };
     68 
     69 int fs4412_inputkey_probe(struct platform_device *pdev)
     70 {
     71     int ret;
     72     printk("match ok
    ");
     73     fs4412_idev = input_allocate_device();//申请空间
     74 
     75     set_bit(EV_KEY,fs4412_idev->evbit);
     76     set_bit(KEY_2,fs4412_idev->keybit);
     77     set_bit(KEY_3,fs4412_idev->keybit);
     78 
     79     ret = input_register_device(fs4412_idev);
     80 
     81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);
     82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
     83     ret = request_irq(res_key2->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
     84     ret = request_irq(res_key3->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
     85     return 0;
     86 }
     87 
     88 int fs4412_inputkey_remove(struct platform_device *pdev)
     89 {
     90     free_irq(res_key3->start,NULL);
     91     free_irq(res_key2->start,NULL);
     92     
     93     input_unregister_device(fs4412_idev);
     94     kfree(fs4412_idev);
     95     return 0;
     96 }
     97 
     98 struct platform_driver pdrv = {
     99     .driver = {
    100         .name = "inputkey",
    101         .of_match_table = inputkey_match_tbl,
    102     },
    103 
    104     .probe = fs4412_inputkey_probe,
    105     .remove = fs4412_inputkey_remove,
    106 };
    107 
    108 module_platform_driver(pdrv);
    109 MODULE_LICENSE("GPL");
    key.c
      1 #include <linux/module.h>
      2 #include <linux/platform_device.h>
      3 #include <linux/input.h>
      4 #include <linux/interrupt.h>
      5 #include <linux/irqreturn.h>
      6 #include <linux/sched.h>
      7 #include <linux/slab.h>
      8 
      9 struct input_dev *fs4412_idev;
     10 struct resource *res_key2;
     11 struct resource *res_key3;
     12 int flag2 = 0,flag3 = 0;//模拟按键的高低电平
     13 
     14 irqreturn_t fs4412_inputkey_handler(int irqno,void *id)
     15 {
     16 #if 0
     17     if(irqno == res_key2->start)
     18     {
     19         input_event(fs4412_idev,EV_KEY,KEY_2,2);//设置了type=,code=,value=,调用唤醒接口
     20         input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     21     }
     22     if(irqno == res_key3->start)
     23     {
     24         input_event(fs4412_idev,EV_KEY,KEY_3,2);
     25         input_sync(fs4412_idev);
     26     }
     27 #endif
     28     if(irqno == res_key2->start)
     29     {
     30         if(flag2 == 0)
     31         {
     32             input_event(fs4412_idev,EV_KEY,KEY_2,1);//设置了type=,code=,value=,调用唤醒接口
     33             input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     34             flag2 = 1;
     35         }
     36         if(flag2 == 1)
     37         {
     38             input_event(fs4412_idev,EV_KEY,KEY_2,0);//设置了type=,code=,value=,调用唤醒接口
     39             input_sync(fs4412_idev);//保证type=EV_SYN,code=SYN_REPORT,为了调用唤醒接口
     40             flag2 = 0;
     41         }
     42     }
     43     if(irqno == res_key3->start)
     44     {
     45         if(flag3 == 0)
     46         {
     47             input_event(fs4412_idev,EV_KEY,KEY_3,1);
     48             input_sync(fs4412_idev);
     49             flag3 = 1;
     50         }
     51         if(flag3 == 1)
     52         {
     53             input_event(fs4412_idev,EV_KEY,KEY_3,0);
     54             input_sync(fs4412_idev);
     55             flag3 = 0;
     56         }
     57     }
     58 
     59     return IRQ_HANDLED;
     60 }
     61 
     62 struct of_device_id inputkey_match_tbl[] = {
     63     {
     64         .compatible = "fs4412,key",
     65     },
     66     {},
     67 };
     68 
     69 int fs4412_inputkey_probe(struct platform_device *pdev)
     70 {
     71     int ret;
     72     printk("match ok
    ");
     73     fs4412_idev = input_allocate_device();//申请空间
     74 
     75     set_bit(EV_KEY,fs4412_idev->evbit);
     76     set_bit(KEY_2,fs4412_idev->keybit);
     77     set_bit(KEY_3,fs4412_idev->keybit);
     78 
     79     ret = input_register_device(fs4412_idev);
     80 
     81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);
     82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
     83     ret = request_irq(res_key2->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
     84     ret = request_irq(res_key3->start,fs4412_inputkey_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
     85     return 0;
     86 }
     87 
     88 int fs4412_inputkey_remove(struct platform_device *pdev)
     89 {
     90     free_irq(res_key3->start,NULL);
     91     free_irq(res_key2->start,NULL);
     92     
     93     input_unregister_device(fs4412_idev);
     94     kfree(fs4412_idev);
     95     return 0;
     96 }
     97 
     98 struct platform_driver pdrv = {
     99     .driver = {
    100         .name = "inputkey",
    101         .of_match_table = inputkey_match_tbl,
    102     },
    103 
    104     .probe = fs4412_inputkey_probe,
    105     .remove = fs4412_inputkey_remove,
    106 };
    107 
    108 module_platform_driver(pdrv);
    109 MODULE_LICENSE("GPL");
    app.c
  • 相关阅读:
    C++ Tr1中的正則表達式
    html中#include file的使用方法
    InstallShield12豪华版破解版下载|InstallShield下载|软件打包工具
    linux-多线程
    使用 ArcGIS Online和APP进行监控操作和数据采集
    西藏印象:时光篇
    西藏印象:夜色篇
    西藏印象:蓝白篇
    hex2bin
    使用openssl的aes各种加密算法
  • 原文地址:https://www.cnblogs.com/hslixiqian/p/9702612.html
Copyright © 2011-2022 走看看