高级驱动DAY2

一. input子系统的作用和框架
  1. 输入设备的分类---按照数据类性分类
    1> 按键类型的数据 --- 键值
    button  keyboard
    
    2> 绝对坐标类型的数据 --- 有具体值,并且有最大值和最小值
    触摸屏(ts/tp)  gensor  陀螺仪
    
    3> 相对坐标类型的数据 --- 相对上一个坐标的数据
    鼠标
    
  2. 作用
    a. 简化了字符设备驱动的编写流程(申请设备号,创建类,创建设备节点,都不用我们自己做了,内核已经帮我们实现了)
    b. 统一了应用程序读取输入设备的方式,不管是ts、gensor、keyboard,应用程序编程的方式是一样的
    
  3. 框架
    应用层:
       app:xxx.c
    -----------------------------------------------------
    驱动层:
          input_handler层---数据处理:/linux-3.0.8/drivers/input/evdev.c
          //知道如何把数据给用户,但是不知道如何获取数据
          //主要实现fops,和用户层进行交互
          -----------------------------------------------
          input_core层---核心层:/linux-3.0.8/drivers/input/input.c
          //维护框架,负责对上层和下层提供接口
          -----------------------------------------------
          input_dev层---输入设备层:自己写
          //知道如何采集数据,但是不知道如何把数据给到用户
          //初始化设备,获取设备数据,并且上报数据
          
二. input子系统的编程
   1. 确保input子系统的上面两层的代码被编译到内核
   Device Drivers  --->
     Input device support  ---> 
        -*- Generic input layer (needed for keyboard, mouse, ...)
           <*> Event interface
   //重新编译内核
   make zImage -j4
   //拷贝到/tftpboot目录
   cp -raf arch/arm/boot/zImage /tftpboot/
   
   2. 代码实现(//参考:/linux-3.0.8/Documentation/input/input-programming.txt)
     1> 重要的结构体
     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)];
     }
     
     struct input_event {
        struct timeval time; //时间
        __u16 type; //数据类型
        __u16 code; //键
        __s32 value; //值
    };
    
    2> 编程步骤
    // 1.创建全局的结构体对象
    button = kzalloc(sizeof(struct s5pv210_button), GFP_KERNEL);
    
    // 2.创建input_dev对象
    button->dev = input_allocate_device();
    
    // 3.初始化input_dev对象
    button->dev->evbit[0] |= BIT_MASK(EV_KEY);//(1 << 1)
    button->dev->keybit[BIT_WORD(KEY_UP)] |= BIT_MASK(KEY_UP);//(1 << (103 % 32))
    
    // 4.注册input_dev对象
    ret = input_register_device(button->dev);
    
    // 5.注册中断
    button->irq = IRQ_EINT(0);
    ret = request_irq(button->irq, input_button_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "key_up", NULL);
    
    // 6.中断处理函数中获取数据并上报
    irqreturn_t input_button_handler(int irq, void * data)
    {
        value = gpio_get_value(S5PV210_GPH0(0));
        if(value)//松开
        {
            input_report_key(button->dev, KEY_UP, 0);
            input_sync(button->dev);
        }
    }
    
三. input子系统和平台总线结合
    方法:
       先实现平台总线,再在平台驱动的probe方法中使用input子系统替代之前的初级驱动代码
    //平台自定义数据的传递方式
    //方法一:
    struct platform_device pdev = {
        .name = "s5pv210_buttons",
        .id = -1,
        .dev = {
            .release = input_button_dev_release,
            .platform_data = &btn_input_data,
        },
    };
    
    //方法二:
    void __init input_button_dev_set_platdata(struct btn_platdata *pd)
    {
        struct btn_platdata *npd;

        if (!pd) {
            printk(KERN_ERR "%s: no platform data\n", __func__);
            return;
        }
        npd = kmemdup(pd, sizeof(struct btn_platdata), GFP_KERNEL);
        if (!npd)
        {
            printk(KERN_ERR "%s: no memory for platform data\n", __func__);
            return;
        }
        pdev.dev.platform_data = npd;
    }
    //调用该函数
    static int __init input_button_dev_init(void)
    {
        input_button_dev_set_platdata(&btn_input_data);
        return platform_device_register(&pdev);
    }
    
四. input子系统内核代码实现
    应用层:
       app:xxx.c
    -----------------------------------------------------
    驱动层:
          input_handler层---数据处理:/linux-3.0.8/drivers/input/evdev.c
          //知道如何把数据给用户,但是不知道如何获取数据
          //主要实现fops,和用户层进行交互
          module_init(evdev_init);
            |
            static int __init evdev_init(void)
             |
             input_register_handler(&evdev_handler);
               |
               //初始化evdev_handler中的h_list链表
               INIT_LIST_HEAD(&handler->h_list);
               //判断input_table数组下标为2的元素是不是为空,为空的则将evdev_handler放入下标为2的地方
               if (handler->fops != NULL) {
                    if (input_table[handler->minor >> 5]) {
                        retval = -EBUSY;
                        goto out;
                    }
                    input_table[handler->minor >> 5] = handler;
                }
                //把evdev_handler注册到input_handler_list链表中
                  list_add_tail(&handler->node, &input_handler_list);
                //然后再去遍历input_dev_list
                list_for_each_entry(dev, &input_dev_list, node)
                  input_attach_handler(dev, handler);
                     |
                     id = input_match_device(handler, dev);
                     error = handler->connect(handler, dev, id);
          -----------------------------------------------
          input_core层---核心层:/linux-3.0.8/drivers/input/input.c
          //维护框架,负责对上层和下层提供接口
          //初始化两个链表
          static LIST_HEAD(input_dev_list);
          static LIST_HEAD(input_handler_list);
          subsys_initcall(input_init);
            |
            static int __init input_init(void)
              |
              //创建类
              err = class_register(&input_class);
              //注册设备号
              err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
              static const struct file_operations input_fops = {
                .owner = THIS_MODULE,
                .open = input_open_file,
                .llseek = noop_llseek,
              };
          -----------------------------------------------
          input_dev层---输入设备层:自己写
          //知道如何采集数据,但是不知道如何把数据给到用户
          //初始化设备,获取设备数据,并且上报数据
          //创建input_dev对象
          button->dev = input_allocate_device();
          //初始化input_dev对象
          button->dev->name = "s5pv210_key_up";
          __set_bit(EV_KEY, button->dev->evbit);
          set_bit(key_info->event.code, button->dev->keybit);
          //注册input_dev对象
          ret = input_register_device(button->dev);
               |
             //设置了EV_SYN
             __set_bit(EV_SYN, dev->evbit);
             //把input_dev添加到input_dev_list
             list_add_tail(&dev->node, &input_dev_list);
             //然后去遍历input_handler_list
             list_for_each_entry(handler, &input_handler_list, node)
                input_attach_handler(dev, handler);
                   |
                   id = input_match_device(handler, dev);
                   error = handler->connect(handler, dev, id);

   2.从调用过程看代码
    open()
    --------------------------------
    sys_open()
      |
      static int input_open_file(struct inode *inode, struct file *file)    
       |
       //获取input_table中的input_handler对象
       handler = input_table[iminor(inode) >> 5];
       if (handler)
        //把input_handler对象中的fops给new_fops
        new_fops = fops_get(handler->fops);    
        old_fops = file->f_op;
        //让file->fops指向input_handler对象中的fops
        file->f_op = new_fops;        
        err = new_fops->open(inode, file);
                |
                static int evdev_open(struct inode *inode, struct file *file)
                  |
                  //根据次设备号找到evdev_table数组的下标,进而获取里面的evdev对象
                  int i = iminor(inode) - EVDEV_MINOR_BASE;
                  evdev = evdev_table[i];
                  //计算要给当前的input_dev对应的设备分配多大的input_event数组
                  bufsize = evdev_compute_buffer_size(evdev->handle.dev);
                  //给input_event数组分配空间
                  client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event),GFP_KERNEL);
                  //建立evdev_client和evdev之间的双向关系
                  client->evdev = evdev;
                  evdev_attach_client(evdev, client);
                      |
                      list_add_tail_rcu(&client->node, &evdev->client_list);
                  //为了把evdev_client对象传递给read函数
                  file->private_data = client;
    
    read()
    --------------------------------
    sys_read()
       |
       static ssize_t evdev_read(struct file *file, char __user *buffer,size_t count, loff_t *ppos)
        |
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
        retval = wait_event_interruptible(evdev->wait, client->packet_head != client->tail || !evdev->exist);
        while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event)) 
                                                           |
                                                       //从client->buffer队列里面取数据
                                                       *event = client->buffer[client->tail++];
        {
            if (input_event_to_user(buffer + retval, &event))//给到用户空间
                return -EFAULT;
            retval += input_event_size();
        }
        
    //数据上报
    input_event(button->dev, EV_KEY, event.code, event.value);
       |
       input_handle_event(dev, type, code, value);
         |
         input_pass_event(dev, type, code, value);
           |
           //通过input_dev找到input_handle
           handle = rcu_dereference(dev->grab);
           //通过input_handle找到input_handler,进而调用input_handler里面的event方法
           if (handle)
             handle->handler->event(handle, type, code, value);
            |
            static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
               |
               //通过input_handle对象找到evdev对象
               struct evdev *evdev = handle->private;
               //创建一个struct input_event对象,并赋值
               struct input_event event;
               event.type = type;
               event.code = code;
               event.value = value;
               //通过evdev对象找到evdev_client对象
               client = rcu_dereference(evdev->grab);
               evdev_pass_event(client, &event);
                   |
                   client->buffer[client->head++] = *event;
        
    //唤醒等待队列头,解除阻塞状态        
    input_sync(button->dev);
       |
       input_handle_event(dev, type, code, value);
         |
         input_pass_event(dev, type, code, value);
           |
           //通过input_dev找到input_handle
           handle = rcu_dereference(dev->grab);
           //通过input_handle找到input_handler,进而调用input_handler里面的event方法
           if (handle)
             handle->handler->event(handle, type, code, value);
            |
            static void evdev_event(struct input_handle *handle,unsigned int type, unsigned int code, int value)
               |
               if (type == EV_SYN && code == SYN_REPORT)
                wake_up_interruptible(&evdev->wait);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Lewis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值