输入子系统

1、核心层:
linux-2.6.22.6\drivers\input\input.c:

  • static int __init input_init(void)

    • register_chrdev(INPUT_MAJOR, "input", &input_fops);

      • static const struct file_operations input_fops =

        {
        .owner = THIS_MODULE,
        .open = input_open_file,
        };
        • static int input_open_file(struct inode *inode, struct file *file)
          struct input_handler *handler = input_table[iminor(inode) >> 5]; 
          const struct file_operations *old_fops, *new_fops = NULL;
          ...
          new_fops = fops_get(handler->fops)
          ...
          file->f_op = new_fops;
          new_fops->open(inode, file);

①、注册input_handler:

  • int input_register_handler(struct input_handler *handler)

    • list_add_tail(&handler->node, &input_handler_list); //放入链表尾部

    • list_for_each_entry(dev, &input_dev_list, node)

      input_attach_handler(dev, handler);  //对于每个条目,调用input_attach_handler函数,根据dev的id_table判断能否支持这个input_handler
      
      
      id = input_match_device(handler->id_table, dev); 
      
      if (!id) return -ENODEV;    
      
      handler->connect(handler, dev, id); //如果支持,则建立连接

②、注册输入设备:

  • int input_register_device(struct input_dev *dev)
    • list_add_tail(&dev->node, &input_dev_list); //放入链表尾部
    • list_for_each_entry(handler, &input_handler_list, node)
      input_attach_handler(dev, handler);   //对于每个条目,调用input_attach_handler函数,根据input_handler的id_table判断能否支持这个dev 
      id = input_match_device(handler->id_table, dev); 
      if (!id) return -ENODEV;
      handler->connect(handler, dev, id); //如果支持,则建立连接

③、如何建立连接:
①②中都要建立连接,用evdev.c来示例如何建立连接:

  • static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
    • evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
    • evdev->handle.dev = dev; //指向input_dev
    • evdev->handle.handler = handler; //指向input_handler
    • input_register_handle(&evdev->handle);
      • struct input_handler *handler = handle->handler;
      • list_add_tail(&handle->d_node, &handle->dev->h_list); //将handle结构体的d_node放入input_dev结构体的h_list
      • list_add_tail(&handle->h_node, &handler->h_list); //将handle结构体的h_node放入input_handler结构体的h_list

这样input_dev和input_handler之间就通过handle建立了连接。

2、稳定程序层:
问:input_table数组由谁赋值?

  • static int __init evdev_init(void) //evdev.c、keyboard.c、mouse.c 等的初始化函数,这里以evdev.c的初始化函数为示例
    • int input_register_handler(struct input_handler *handler)
      • input_table[handler->minor >> 5] = handler;

3、设备层:
以示例程序gpio_keys.c为例:

struct platform_driver gpio_keys_device_driver = {
    .probe      = gpio_keys_probe,
    .remove     = __devexit_p(gpio_keys_remove),
    .driver     = {
        .name   = "gpio-keys",
    }
};
static int __init gpio_keys_init(void)
{
    return platform_driver_register(&gpio_keys_device_driver);
}
static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
   ...
   struct input_dev *input;
   ...
   input = input_allocate_device();
   ...
   set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
   request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
     button->desc ? button->desc : "gpio_keys", pdev);
   ...
   error = input_register_device(input);
   //*************************************************
     ******             其他硬件相关设置          *******
     *************************************************//
}

执行input_register_device函数以开始进行输入设备注册并建立连接。

4、probe是如何被调用的?
安装模块时,①、在xxx_drv.c中xxx_init被执行,进而执行platform_driver_register>>driver_register>>bus_add_driver,把driver放入bus的drv链表,从bus的dev链表中取出每一个dev,用bus的match函数判断dev能否支持drv(通常通过结构体中的名称比较,但也有例外),若可以支持,调用drv的probe函数。②、在xxx_dev.c中xxx_init被执行,进而执行platform_device_register>>platform_device_add>>device_add,把device放入bus的dev链表,从bus的drv链表中取出每一个drv,用bus的match函数判断drv能否支持dev(通常通过结构体中的名称比较,但也有例外),若可以支持,调用dev的probe函数。

5、如何读?
app : read
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
……………

  • static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
    • if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) return -EAGAIN; //无数据并且是非阻塞方式打开,则立即返回
    • wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist); //否则休眠

应用程序运行时无数据并且是非阻塞方式打开,线程就会进入休眠,谁来唤醒?由事件来唤醒:

  • static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
    • wake_up_interruptible(&evdev->wait);

那么evdev_event又被谁调用?
以GPIO按键驱动程序为例说明:在设备的中断服务程序里,确定事件是什么,然后调用相应的input_handler的event处理函数
linux-2.6.22.6\drivers\input\keyboard\gpio_keys.c:

  • static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
    • input_event(input, type, button->code, !!state); //上报事件
      • struct input_handle *handle;
      • list_for_each_entry(handle, &dev->h_list, d_node)
        if (handle->open) 
        handle->handler->event(handle, type, code, value);
    • input_sync(input);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值