input输入子系统驱动分析

 

使用内核:linux-2.6.22

分析工具:Source Insight 3

 

写字符设备驱动程序的流程:

1. 确定主设备号

2. 构造file_operations结构体,

   里面有open/read/write等函数

3. register_chrdev

4. 入口函数

5. 出口函数

 

 

分析: 打开 drivers/input/input.c

/*该结构体没有read函数调用*/

static const struct file_operations input_fops = {

       .owner = THIS_MODULE,

       .open = input_open_file,  

};

 

在input_fops这个file_operations结构体里面只有open这个函数,没有读写函数。

那么怎么读按键呢?

 

 

答01:

app:   open("/dev/xxx"), 主设备号为13, 次设备号为64

------------------------------------------------

kenel: sys_open

 

打开static int input_open_file(struct inode *inode, struct file *file)这个结构体

 

 

static int input_open_file(struct inode *inode, struct file *file)

{

       struct input_handler *handler = input_table[iminor(inode) >> 5];     /*次设备号为64,右移5位,为2即input_table[2],此设备对应数组第2项,及对应 evdev_handler 所对应的设备号                            */

       const struct file_operations *old_fops, *new_fops = NULL;

       int err;

 

              old_fops = file->f_op;

       file->f_op = new_fops

err = new_fops->open(inode, file);

}

 

 

 

int input_register_handle(struct input_handle *handle)

{

       struct input_handler *handler = handle->handler;

 

       list_add_tail(&handle->d_node, &handle->dev->h_list);

       list_add_tail(&handle->h_node, &handler->h_list);

 

       if (handler->start)

              handler->start(handle);

 

       return 0;

}

 

 

 

app:    read(fd, buf, len), 主设备号为13

--------------------------------------------------------------------------------------------------------------

kernel: sys_read

            vfs_read

                ret = file->f_op->read(file, buf, count, pos); = (&evdev_fops)->read = evdev_read

                          evdev_read

                                // 无数据则休眠

                                retval = wait_event_interruptible(evdev->wait,

                                        client->head != client->tail || !evdev->exist);

                             

 

input_open_file只不过起中转作用,它会找到新的file_operations结构体。

它从input_table数组里得到一个input_handler结构体,

再从input_handler结构体里得到新的file_operations结构体。

 

 

----------------------------------------------------------------------------------------------------------------------

Evdev.c

 

static struct input_handler evdev_handler = {          /*创建input_handler evdev_handler这个结构体 */

       .event =   evdev_event,              表示设备有数据产生时,调用此函数来处理。比如按键中断服务程序里调用event函数

       .connect =      evdev_connect,        /*  表示发现能支持的设备时,调用此函数来"建立联系" */

 

       .disconnect =  evdev_disconnect,       /*表示去掉某个能支持的设备时,调用此函数来"断开联系" */

 

       .fops =           &evdev_fops,               //创建一个evdev_fops结构体

       .minor =  EVDEV_MINOR_BASE,

       .name =          "evdev",                              //名字   evdev            

       .id_table =      evdev_ids,                           //表示该input_handler能支持的设备//(input_dev)

};

 

 

static const struct file_operations evdev_fops = {

       .owner = THIS_MODULE,

       .read =           evdev_read,

       .write =   evdev_write,

       .poll =            evdev_poll,

       .open =          evdev_open,

       .release = evdev_release,

       .unlocked_ioctl = evdev_ioctl,

#ifdef CONFIG_COMPAT

       .compat_ioctl =      evdev_ioctl_compat,

#endif

       .fasync = evdev_fasync,

       .flush =   evdev_flush

};

 

 

static int __init evdev_init(void)

{

       return input_register_handler(&evdev_handler);

}

 

static void __exit evdev_exit(void)

{

       input_unregister_handler(&evdev_handler);

}

 

module_init(evdev_init);

module_exit(evdev_exit)

 

 

 

问02: input_table数组在哪里被设置?

答02:

 

 

                            input.c

 

input_register_device //        \\ input_register_handler

                                      

        gpio_keys.c                       evdev.c

                                          keyboard.c

 

 

 

 

 

 

input.c

 

static struct input_handler *input_table[8];

 

 

 

 

 

----------------------------------------------------------------------------------------------------------------------

int input_register_device(struct input_dev *dev)

{

       static atomic_t input_no = ATOMIC_INIT(0);

       struct input_handler *handler;

       const char *path;

       int error;

 

 

 

       init_timer(&dev->timer);

 

 

       if (!dev->cdev.dev)

              dev->cdev.dev = dev->dev.parent;

 

       error = class_device_add(&dev->cdev);

 

       path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);

       printk(KERN_INFO "input: %s as %s\n",

              dev->name ? dev->name : "Unspecified device", path ? path : "N/A");  /*如果dev->name 则会打印dev->name 指向的字符,否则"Unspecified device"。这行字符在运行时会被打印出来*/

       kfree(path);

 

       list_for_each_entry(handler, &input_handler_list, node)     /*该函数会查找input_handler_list链表中的每一个handler */

              input_attach_handler(dev, handler);       //

 

       input_wakeup_procfs_readers();

 

       return 0;

}

 

 

void input_unregister_device(struct input_dev *dev)

{

       struct input_handle *handle, *next;

       int code;

 

 

       list_for_each_entry_safe(handle, next, &dev->h_list, d_node)

              handle->handler->disconnect(handle);

 

       class_device_unregister(&dev->cdev);

}

 

---------------------------------------------------------------------------------------

gpio_keys.c

input_register_device(input);

 

 

 

 

input.c

 

 

int input_register_handler(struct input_handler *handler)

{

   struct input_dev *dev;

 

   INIT_LIST_HEAD(&handler->h_list);

 

                

 

 

 

          input_table[handler->minor >> 5] = handler;

 

   list_add_tail(&handler->node, &input_handler_list);    /*将handler放入input_handler_list链表的inode 节点中 */

 

   list_for_each_entry(dev, &input_dev_list, node)       /*查找input_dev_list结构体中的dev */

          input_attach_handler(dev, handler);

 

   input_wakeup_procfs_readers();

   return 0;

}

 

 

void input_unregister_handler(struct input_handler *handler)

{

   struct input_handle *handle, *next;

 

   list_for_each_entry_safe(handle, next, &handler->h_list, h_node)

          handler->disconnect(handle);

}

 

-------------------------------------------------------------------------------------------------

evdev.c:

     evdev_init

          input_register_handler(&evdev_handler)

               input_table[2] = &evdev_handler;

 

------------------------------------------------------------------------------------------------------

 

问03: 在evdev_read里休眠,谁来唤醒?

答03:

input_dev的中断处理函数确定按键值

    input_evnet

        list_for_each_entry(handle, &dev->h_list, d_node)

                if (handle->open)

                        handle->handler->event(handle, type, code, value);

                                             evdev_event > wake_up_interruptible(&evdev->wait);

 

 

 

static struct input_handler evdev_handler = {

        .event =        evdev_event,

 

假设:按键中断服务程序里,会调用input_handler里面的event。

 

猜测evdev.c的input_handler evdev_handler里面的成员的含义:

1. id_table:  表示该input_handler能支持的设备(input_dev)

2. connect:   表示发现能支持的设备时,调用此函数来"建立联系"

3. disconnect:表示去掉某个能支持的设备时,调用此函数来"断开联系"

4. event:     表示设备有数据产生时,调用此函数来处理。比如按键中断服务程序里调用event函数

 

 

问04:右边的input_handler怎么找到左边的能支持的input_dev?

答04:input_register_handler

            // 把input_handler放入数组

            input_table[handler->minor >> 5] = handler;

 

            // 把input_handler放入链表

            list_add_tail(&handler->node, &input_handler_list);

 

            // 从input_dev_list取出每个input_dev,确定能否支持

            list_for_each_entry(dev, &input_dev_list, node)

                input_attach_handler(dev, handler);

 

 

问05:左边的input_dev怎么找到右边的能支持它的input_handler?

答05:input_register_device

            // 把input_dev放入链表

            list_add_tail(&dev->node, &input_dev_list);   

 

            // 从&input_handler_list取出每个input_handler,确定能否被它支持

            list_for_each_entry(handler, &input_handler_list, node)

                    input_attach_handler(dev, handler);

 

 

 

 

 

 

问06:input_attach_handler(struct input_dev *dev, struct input_handler *handler)做什么?

答06:根据input_handler的id_table判断能否input_dev,

      如果可以,调用input_handler的connect函数

 

input_attach_handler(struct input_dev *dev, struct input_handler *handler) /*注意input_dev 和input_handler */

      id = input_match_device(handler->id_table, dev);  /*表示dev能够匹配handler->id_table */

      if (!id)

          return -ENODEV;

      error = handler->connect(handler, dev, id);

 

 

左边的input_dev驱动怎么写:

1. 分配input_dev结构体:input_allocate_device

2. 设置:能产生哪类事件(EV_KEY/EV_REL/EV_ABS)(rel: relation, abs: absolute)

         能产生这类里的哪些事件

3. 注册: input_register_device

4. 硬件相关的操作

   request_irq....

   在中断里确定按键值,调用input_event函数进一步处理

   xxx_irq

       input_event(struct input_dev *dev, ....

        .......

             evdev_hanlder.event

 

问07:input_evnet函数怎么根据input_dev找到右边的能支持它的input_handler,从而调用它的event函数?

答07:input_attach_handler函数确定右边的input_handler能支持左边的input_dev的时候,

      会调用handler->connect(handler, dev, id)来建立联系。

                               handler->connect(handler, dev, id)/*很关键的一个函数*/

         

      它一般会分配一个input_handle,

      input_handle->dev     = 左边的input_dev

      input_handle->handler = 右边的input_handler

      然后把这个input_handle放入input_dev的h_list,

          把这个input_handle放入input_handler的h_list

 

input_event         

        list_for_each_entry(handle, &dev->h_list, d_node)

                if (handle->open)

                        handle->handler->event(handle, type, code, value);

                        // 回去看问题03

 

 

 

 

 

环境参数的设置在/linux-2.6.22.6/Doucments/nfs.txt里有详细的说明。

 

 

root=/dev/nf

 

nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>

 

 

ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf

 

 

 

set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.7.8:/opt/rootfs ip=192.168.7.123:192.168.7.8:192.168.7.1:255.255.255.0::eth0:off

tftp 30000000 uImage;bootm 30000000

 

测试方法:

1. make menuconfig,把evdev选上:

-> Device Drivers

  -> Input device support

<*>   Event interface

 

编译,使用新内核启动

 

2. insmod buttons.ko

 

3. cat /dev/tty1

 

4. hexdump /dev/event0

 

struct input_event

            秒      微秒    type code   value

0000000 002e 0000 604a 0001 0001 0026 0001 0000

0000010 002e 0000 6067 0001 0000 0000 0000 0000

 

 

0000020 0030 0000 ceee 0005 0001 0026 0000 0000

0000030 0030 0000 cf08 0005 0000 0000 0000 0000

 

 

0000040 0031 0000 0fd5 0006 0001 0026 0001 0000

0000050 0031 0000 0ff4 0006 0000 0000 0000 0000

 

 

0000060 0032 0000 f598 0005 0001 0026 0000 0000

0000070 0032 0000 f5b1 0005 0000 0000 0000 0000

 

0000080 0033 0000 6fe5 000a 0001 0026 0001 0000

0000090 0033 0000 7003 000a 0000 0000 0000 0000

 

 

00000a0 0034 0000 5016 000b 0001 0026 0000 0000

00000b0 0034 0000 502f 000b 0000 0000 0000 0000

 

 

 

 

注意:由于我们可能存在多个handler设备

       那么我们应该如何让我们的dev去匹配我们的handler呢?

 

         handle->handler->event(handle, type, code, value);

 

这个函数指向的这个event就是我们处理

数据的函数,这个函数是唯一的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Keep Coding...

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

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

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

打赏作者

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

抵扣说明:

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

余额充值