input子系统学习笔记五 按键驱动实例分析上

本文介绍了一个简单的按键驱动实例,展示了如何使用中断处理按键输入,并详细分析了input子系统的相关函数,如input_allocate_device()和input_register_device()等。
下面是按键驱动的简单例子,这个输入设备只有一个按键,按键被连接到一条中断线上,当按键被按下时,将产生一个中断,内核将检测到这个中断,并对其进行处理。代码含注释如下:
C++代码
  1. #include <asm/irq.h> 
  2. #include <asm/io.h> 
  3. static struct input_dev *button_dev;/*输入设备结构体*/ 
  4. static irqreturn_t button_interrupt(int,irq,void *dummy)/*中断处理函数*/ 
  5.         input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1); 
  6.         /*向输入子系统报告产生按键事件*/ 
  7.         input_sync(button_dev); 
  8.         /*通知接收者,一个报告发送完毕*/ 
  9.         return IRQ_HANDLED; 
  10. static int __init button_init(void) /*加载函数*/ 
  11.         int error; 
  12.         if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) 
  13.         /*申请中断处理函数*/ 
  14.         { 
  15.                 /*申请失败,则打印出错信息*/ 
  16.                 printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq); 
  17.                 return -EBUSY; 
  18.         } 
  19.         button_dev = input_allocate_device(); /*分配一个设备结构体*/ 
  20.         if (!button_dev) /*判断分配是否成功*/ 
  21.         { 
  22.                 printk(KERN_ERR "button.c: Not enough memory\n"); 
  23.                 error = -ENOMEM; 
  24.                 goto err_free_irq; 
  25.         } 
  26.         button_dev->evbit[0] = BIT_MASK(EV_KEY);/*设置按键信息*/ 
  27.         button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 
  28.         error = input_register_device(button_dev); /*注册一个输入设备*/ 
  29.          if (error) 
  30.         { 
  31.                 printk(KERN_ERR "button.c: Failed to register device\n"); 
  32.                 goto err_free_dev; 
  33.         } 
  34.         return 0; 
  35.         err_free_dev:  /*以下是错误处理*/ 
  36.                 input_free_device(button_dev); 
  37.         err_free_irq: 
  38.                 free_irq(BUTTON_IRQ, button_interrupt); 
  39.         return error; 
  40. static void __exit button_exit(void
  41. /*卸载函数*/ 
  42.         input_unregister_device(button_dev); 
  43.         /*注销按键设备*/ 
  44.         free_irq(BUTTON_IRQ, button_interrupt); /*释放按键占用的中断线*/ 
  45. module_init(button_init); 
  46. module_exit(button_exit); 
重要函数分析

        这个实例程序代码比较简单,在初始化函数 button_init()中注册了一个中断处理函数,然后并调用 input_register_device()调用 input_allocate_device()函数分配了一个 input_dev 结构体,函数对其进行了注册。在中断处理函数 button_interrupt()中,实例将接收到的按键信息上报给 input 子系统。从而通过 input 子系统,向用户态程序提供按键输入信息。本实例采用了中断方式,除了中断相关的代码外,实例中包含了一些 input 子系统提供的函数,现对其中一些重要的函数进行分析。

        input_allocate_device()

C++代码
  1. struct input_dev *input_allocate_device(void
  2.         struct input_dev *dev; 
  3.         dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);/*分配一个 input_dev 结构体,并初始化为 0*/ 
  4.  
  5.         if (dev) { 
  6.                 dev->dev.type = &input_dev_type;/*初始化设备的类型*/ 
  7.                 dev->dev.class = &input_class;/*设置为输入设备类*/ 
  8.                 device_initialize(&dev->dev);/*初始化 device 结构*/ 
  9.                 mutex_init(&dev->mutex);/*初始化互斥锁*/ 
  10.                 spin_lock_init(&dev->event_lock);/*初始化事件自旋锁*/ 
  11.                 INIT_LIST_HEAD(&dev->h_list);/*初始化链表*/ 
  12.                 INIT_LIST_HEAD(&dev->node);/*初始化链表*/ 
  13.                 __module_get(THIS_MODULE);/*模块引用技术加 1*/ 
  14.         } 
  15.         return dev; 

        该函数返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如设备支持的按键码、设备的名称、设备支持的事件等。

        注册函数 input_register_device()

        input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将 input_dev结构体注册到输入子系统核心中,input_dev 结构体必须由前面讲的 input_allocate_device()函数来分配。input_register_device()函数如果注册失败,必须调用 input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用 input_unregister_device()函数来注销输入设备结构体。简而言之,注册 input device 的过程就是为 input device 设置默认值,并将其挂以 input_dev_list。与挂载在 input_handler_list 中的 handler 相匹配。如果匹配成功,就会调用 handler 的 connect函数。代码如下:

C++代码
  1. int input_register_device(struct input_dev *dev) 
  2.         static atomic_t input_no = ATOMIC_INIT(0); 
  3.         struct input_handler *handler; 
  4.         const char *path; 
  5.         int error; 
  6.  
  7.         /* Every input device generates EV_SYN/SYN_REPORT events. */ 
  8.         __set_bit(EV_SYN, dev->evbit);/*调用__set_bit()函数设置 input_dev 所支持的事件类型。事件类型由 input_dev 的evbit 成员来表示,在这里将其 EV_SYN 置位,表示设备支持所有的事件。一个设备可以支持一种或者多种事件类型。*/ 
  9.  
  10.         /* KEY_RESERVED is not supposed to be transmitted to userspace. */ 
  11.         __clear_bit(KEY_RESERVED, dev->keybit); 
  12.  
  13.         /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ 
  14.         input_cleanse_bitmasks(dev); 
  15.  
  16. /*
  17. * If delay and period are pre-set by the driver, then autorepeating
  18. * is handled by the driver itself and we don't do it in input.c.
  19. */ 
  20.         init_timer(&dev->timer);/*初始化一个 timer 定时器,这个定时器是为处理重复击键而定义的。*/ 
  21.  
  22.         /*如果 dev->rep[REP_DELAY]和 dev->rep[REP_PERIOD]没有设值,则将其赋默认值,这主要是为自动处理重复按键定义的。*/ 
  23.         if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { 
  24.                 dev->timer.data = (long) dev; 
  25.                 dev->timer.function = input_repeat_key; 
  26.                 dev->rep[REP_DELAY] = 250; 
  27.                 dev->rep[REP_PERIOD] = 33; 
  28.         } 
  29.         /*检查 getkeycode()函数和 setkeycode()函数是否被定义,如果没定义,则使用默认的处理函数,这两个函数为 input_default_getkeycode()和
  30.         input_default_setkeycode()。input_default_getkeycode()函数用来得到指定位置的键值。input_default_setkeycode()函数用来设置键值。*/ 
  31.         if (!dev->getkeycode) 
  32.                 dev->getkeycode = input_default_getkeycode; 
  33.  
  34.         if (!dev->setkeycode) 
  35.                 dev->setkeycode = input_default_setkeycode; 
  36.         /*设置 input_dev 中的 device 的名字,名字以 input0、input1、input2、input3、input4等的形式出现在 sysfs 文件系统中。*/ 
  37.         dev_set_name(&dev->dev, "input%ld"
  38.                 (unsigned long) atomic_inc_return(&input_no) - 1); 
  39.  
  40.         error = device_add(&dev->dev); 
  41.         if (error) 
  42.                 return error; 
  43.  
  44.         path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); 
  45.         printk(KERN_INFO "input: %s as %s\n"
  46.                 dev->name ? dev->name : "Unspecified device", path ? path : "N/A");//打印设备的路径,输出调试信息。 
  47.         kfree(path); 
  48.  
  49.         error = mutex_lock_interruptible(&input_mutex); 
  50.         if (error) { 
  51.                 device_del(&dev->dev); 
  52.                 return error; 
  53.         } 
  54.  
  55.         list_add_tail(&dev->node, &input_dev_list); 
  56.         /*调用 list_add_tail()函数将 input_dev 加入 input_dev_list 链表中,input_dev_list 链表中包含了系统中所有的 input_dev 设备。*/ 
  57.         list_for_each_entry(handler, &input_handler_list, node) 
  58.         input_attach_handler(dev,handler);/*input_attach_handler()函数用来匹配 input_dev 和 handler,只有匹配成功,才能进行下一步的关联操作。*/ 
  59.  
  60.         input_wakeup_procfs_readers(); 
  61.  
  62.         mutex_unlock(&input_mutex); 
  63.  
  64.         return 0; 

        input_attach_handler()

        内核中代码如下:

XML/HTML代码
  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) 
  2.         const struct input_device_id *id;/*输入设备的指针,该结构体表示设备的标识,标识中存储了设备的信息*/ 
  3.  
  4.         int error; 
  5.         if (handler->blacklist && input_match_device(handler->blacklist, 
  6.                 dev))/*首先判断 handle 的 blacklist 是否被赋值,如果被赋值,则匹配 blacklist 中的数据跟 dev->id 的数据是否匹配。blacklist 是一个 input_device_id*的类型,其指向 input_device_id的一个表,这个表中存放了驱动程序应该忽略的设备。即使在 id_table 中找到支持的项,也应该忽略这种设备。*/ 
  7.         return -ENODEV; 
  8.  
  9.         id = input_match_device(handler, dev); 
  10.         if (!id) 
  11.         return -ENODEV; 
  12.  
  13.         error = handler->connect(handler, dev, id);/*连接设备和处理函数*/ 
  14.  
  15.         if (error && error != -ENODEV) 
  16.         printk(KERN_ERR 
  17.                 "input: failed to attach handler %s to device %s, " 
  18.                 "error: %d\n", 
  19.         handler->name, kobject_name(&dev->dev.kobj), error); 
  20.  
  21.         return error; 

        input_device_id

C++代码
  1. struct input_device_id { 
  2.  
  3.         kernel_ulong_t flags;/*标志信息*/ 
  4.         __u16 bustype; /*总线类型*/ 
  5.         __u16 vendor;/*制造商 ID*/ 
  6.         __u16 product;/*产品 ID*/ 
  7.         __u16 version;/*版本号*/ 
  8.  
  9.         kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]; 
  10.         kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG +  1]; 
  11.         kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1]; 
  12.         kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1]; 
  13.         kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1]; 
  14.         kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1]; 
  15.         kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 
  16.         1]; 
  17.         kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1]; 
  18.         kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1]; 
  19.  
  20.         kernel_ulong_t driver_info;/*驱动额外的信息*/ 
  21.  
  22. }; 

        input_match_device ()

        input_match_device()函数匹配 handle->>id_table 和 dev->id 中的数据。

        如果不成功则返回。handle->id_table 也是一个 input_device_id 类型的指针,其表示驱动支持的设备列表。input_match_device ()函数用来与 input_dev 和 handler 进行匹配。handler 的 id_table 表中定义了其支持的 input_dev 设备。该函数的代码如下:

Java代码
  1. static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev) 
  2.         const struct input_device_id *id; 
  3.         int i;//声明一个局部变量 i,用于循环。 
  4.  
  5.         /*是一个 for 循环,用来匹配 id 和 dev->id 中的信息,只要有一项相同则返回。*/ 
  6.         for (id = handler->id_table; id->flags || id->driver_info; id++) { 
  7.                 /*用 来 匹 配 总 线 类 型 。 id->flags 中 定 义 了 要 匹 配 的 项 , 其 中INPUT_DEVICE_ID_MATCH_BUS 如果没有设置,则比较 input device 和 input handler 的总线类型。*/ 
  8.                 if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) 
  9.                         if (id->bustype != dev->id.bustype) 
  10.                                 continue
  11.  
  12.                         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)//匹配设备厂商的信息。 
  13.  
  14.                                 if (id->vendor != dev->id.vendor) 
  15.                                        continue
  16.  
  17.                         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)//分别匹配设备号的信息。 
  18.  
  19.                                 if (id->product != dev->id.product) 
  20.                                         continue
  21.  
  22.                         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) 
  23.                                 if (id->version != dev->id.version) 
  24.                                         continue
  25.                 /*使用 MATCH_BIT 匹配项。如果 id->flags 定义的类型匹配成功,或者 id->flags没有定义,才会进入到 MATCH_BIT 的匹配项。*/ 
  26.                 MATCH_BIT(evbit, EV_MAX); 
  27.                 MATCH_BIT(keybit, KEY_MAX); 
  28.                 MATCH_BIT(relbit, REL_MAX); 
  29.                 MATCH_BIT(absbit, ABS_MAX); 
  30.                 MATCH_BIT(mscbit, MSC_MAX); 
  31.                 MATCH_BIT(ledbit, LED_MAX); 
  32.                 MATCH_BIT(sndbit, SND_MAX); 
  33.                 MATCH_BIT(ffbit, FF_MAX); 
  34.                 MATCH_BIT(swbit, SW_MAX); 
  35.  
  36.                 if (!handler->match || handler->match(handler, dev)) 
  37.                         return id; 
  38.         } 
  39.  
  40.         return NULL; 

        从 MATCH_BIT 宏的定义可以看出。只有当 iput device 和 input handler 的 ID 成员在 evbit、keybit、... swbit 项相同才会匹配成功。而且匹配的顺序是从 evbit、keybit 到 swbit。只要有一项不同,就会循环到 ID 中的下一项进行比较。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值