Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231780
  • 博文数量: 27
  • 博客积分: 465
  • 博客等级: 下士
  • 技术积分: 1340
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-23 22:33
文章分类

全部博文(27)

文章存档

2014年(1)

2013年(2)

2012年(24)

分类: LINUX

2012-08-30 21:10:45

     一些关于输入子系统的软件流程,包括核心层、事件驱动层、设备驱动层的一些关系。上编总结下了怎么去设计一个设备驱动层的代码流程:
   1. 分配一个input_dev结构体(input_allocate_device)
   2.设置 包括设置是哪类事件等
   3.注册  input_register_device
    4.硬件相关的初始化
下   针对按键驱动的简单例子:

点击(此处)折叠或打开

  1.  static int __init button_init(void) /*加载函数*/
  2.  {
  3.         int error;
  4.       1.button_dev = input_allocate_device(); /*分配一个设备结构体*/
  5.         if (!button_dev) /*判断分配是否成功*/
  6.         {
  7.                 printk(KERN_ERR "button.c: Not enough memory\n");
  8.                 error = -ENOMEM;
  9.                 goto err_free_irq;
  10.         }
  11.       2.button_dev->evbit[0] = BIT_MASK(EV_KEY);/*设置按键信息*/
  12.         button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
  13.       3.error = input_register_device(button_dev); /*注册一个输入设备*/
  14.          if (error)
  15.         {
  16.                 printk(KERN_ERR "button.c: Failed to register device\n");
  17.                 goto err_free_dev;
  18.         }
  19.       4.if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL))
  20.         /*申请中断处理函数*/
  21.         {
  22.                 /*申请失败,则打印出错信息*/
  23.                 printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
  24.                 return -EBUSY;
  25.         }
  26. }
这     这个例子其实比较简单,分配、设置、注册和硬件的一些初始化。下面对一些重要的函数进行分析下

点击(此处)折叠或打开

  1. struct input_dev *input_allocate_device(void)
  2. {
  3.         struct input_dev *dev;
  4.         dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);/*分配一个 input_dev 结构体,并初始化为 0*/
  5.    
  6.         if (dev) {
  7.                 dev->dev.type = &input_dev_type;/*初始化设备的类型*/
  8.                 dev->dev.class = &input_class;/*设置为输入设备类*/
  9.                 device_initialize(&dev->dev);/*初始化 device 结构*/
  10.                 mutex_init(&dev->mutex);/*初始化互斥锁*/
  11.                 spin_lock_init(&dev->event_lock);/*初始化事件自旋锁*/
  12.                 INIT_LIST_HEAD(&dev->h_list);/*初始化链表*/
  13.                 INIT_LIST_HEAD(&dev->node);/*初始化链表*/
  14.                 __module_get(THIS_MODULE);/*模块引用技术加 1*/
  15.         }
  16.         return dev;
  17. }
      该函数返回一个指向input_dev类型的指针,包括输入设备的一些相关的信息,如支持的按键码,设备的名称,设备支持的事件类型等。对于按键的获取是从中断服务程序里面做的

点击(此处)折叠或打开

  1. static irqreturn_t button_interrupt(int,irq,void *dummy)/*中断处理函数*/
  2.  {
  3.         input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
  4.         /*向输入子系统报告产生按键事件*/
  5.         input_sync(button_dev);
  6.         /*通知接收者,一个报告发送完毕*/
  7.         return IRQ_HANDLED;
  8.  }

点击(此处)折叠或打开

  1. static inline void input_report_key(struct input_dev *dev,unsigned int
  2. code, int value)
  3. {
  4.         input_event(dev, EV_KEY, code, !!value);
  5. }
该     函数的第 1 个参数是产生事件的输入设备, 第2 个参数是产生的事件, 第3 个参数是事件的值。需要注意的是, 2 个参数可以取类似 BTN_0、 BTN_1、BTN_LEFT、BTN_RIGHT 等值,这些键值被定义在include/ linux/input.h 文件中。当第 2 个参数为按键时,第 3 个参数表示按键的状态,value 值为 0 表示按键释放,非 0 表示按键按下。
       在input_report_key函数中主要是调用input_event函数,用来向输入子系统报告输入设备产生的事件。

点击(此处)折叠或打开

  1. void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
  2. {
  3.     struct input_handle *handle;

  4.     if (type > EV_MAX || !test_bit(type, dev->evbit))
  5.         return;

  6.     add_input_randomness(type, code, value);//主要是按键输入的一种随机事件

  7.     switch (type) {

  8.         case EV_SYN:
  9.             switch (code) {
  10.                 case SYN_CONFIG:
  11.                     if (dev->event)
  12.                         dev->event(dev, type, code, value);
  13.                     break;

  14.                 case SYN_REPORT:
  15.                     if (dev->sync)
  16.                         return;
  17.                     dev->sync = 1;
  18.                     break;
  19.             }
  20.             break;

  21.         case EV_KEY:
  22. //调用test_bit()函数来测试按键的状态是否有改变
  23.             if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
  24.                 return;

  25.             if (value == 2)
  26.                 break;

  27.             change_bit(code, dev->key); //调用改变键的状态

  28.             if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
  29.                 dev->repeat_key = code;
  30.                 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));//检测重复的按键
  31.             }

  32.             break;

  33.         case EV_SW:

  34.             if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
  35.                 return;

  36.             change_bit(code, dev->sw);

  37.             break;

  38.         case EV_ABS:

  39.             if (code > ABS_MAX || !test_bit(code, dev->absbit))
  40.                 return;

  41.             if (dev->absfuzz[code]) {
  42.                 if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
  43.                  (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
  44.                     return;

  45.                 if ((value > dev->abs[code] - dev->absfuzz[code]) &&
  46.                  (value < dev->abs[code] + dev->absfuzz[code]))
  47.                     value = (dev->abs[code] * 3 + value) >> 2;

  48.                 if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
  49.                  (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
  50.                     value = (dev->abs[code] + value) >> 1;
  51.             }

  52.             if (dev->abs[code] == value)
  53.                 return;

  54.             dev->abs[code] = value;
  55.             break;

  56.         case EV_REL:

  57.             if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
  58.                 return;

  59.             break;

  60.         case EV_MSC:

  61.             if (code > MSC_MAX || !test_bit(code, dev->mscbit))
  62.                 return;

  63.             if (dev->event)
  64.                 dev->event(dev, type, code, value);

  65.             break;

  66.         case EV_LED:

  67.             if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
  68.                 return;

  69.             change_bit(code, dev->led);

  70.             if (dev->event)
  71.                 dev->event(dev, type, code, value);

  72.             break;

  73.         case EV_SND:

  74.             if (code > SND_MAX || !test_bit(code, dev->sndbit))
  75.                 return;

  76.             if (!!test_bit(code, dev->snd) != !!value)
  77.                 change_bit(code, dev->snd);

  78.             if (dev->event)
  79.                 dev->event(dev, type, code, value);

  80.             break;

  81.         case EV_REP:

  82.             if (code > REP_MAX || value < 0 || dev->rep[code] == value)
  83.                 return;

  84.             dev->rep[code] = value;
  85.             if (dev->event)
  86.                 dev->event(dev, type, code, value);

  87.             break;

  88.         case EV_FF:

  89.             if (value < 0)
  90.                 return;

  91.             if (dev->event)
  92.                 dev->event(dev, type, code, value);
  93.             break;
  94.     }

  95.     if (type != EV_SYN)
  96.         dev->sync = 0;
//调用handler的event函数
  1.     if (dev->grab)
  2.         dev->grab->handler->event(dev->grab, type, code, value);
  3.     else
  4.         list_for_each_entry(handle, &dev->h_list, d_node)
  5.             if (handle->open)
  6.                 handle->handler->event(handle, type, code, value);
  7. }
        由上面可以知道,如果input device被强制指定了handler,则调用该handler的eventh函数,如果没有指定handler,就会遍历input device->h_list上的handle成员,如果handle被打开,则调用与输入设备对应的handler的event函数。
是     上面是整个按键的注册和事件的上报处理过程,下面来分析下evdev,evdev对应的设备节点一般对应于/dev/input/event0---dev/input/event4,理论上可以对应32个设备节点,由input_table[handler->minor >> 5] 决定,一般可以用cat /dev/input/event(),然后移动鼠标或者键盘就有数据输出。关于核心层得input_register_handler和核心层的一些注册和操作在第一章已经梳理了,那么不做梳理了。由一可以知道匹配的成功在于handler中的blacklist和id_table,evdev的id_table定义如下:

点击(此处)折叠或打开

  1. static const struct input_device_id evdev_ids[] = {
  2.     { .driver_info = 1 },    /* Matches all devices */
  3.     { },            /* Terminating zero entry */
  4. };
        它没有定义flags,也没有定义匹配的属性项,这个handler匹配所有的input device。
       对于主设备为INPUT_MAJOR的设备节点,由input_register_handler会将转化成handler的evdev_fops,对应的open函数为:

点击(此处)折叠或打开

  1. static int evdev_open(struct inode *inode, struct file *file)
  2. {
  3.     struct evdev_client *client;
  4.     struct evdev *evdev;
  5.     int i = iminor(inode) - EVDEV_MINOR_BASE; //得到evdev_table[]序列
  6.     int error;

  7.     if (i >= EVDEV_MINORS)
  8.         return -ENODEV;

  9.     evdev = evdev_table[i];//取出对应的evdev取出

  10.     if (!evdev || !evdev->exist)
  11.         return -ENODEV;

  12.     client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);  //分配一个client
  13.     if (!client)
  14.         return -ENOMEM;

  15.     client->evdev = evdev;
  16.     list_add_tail(&client->node, &evdev->client_list);//初始化并将它和evdev关联起来,将client->evdev指向它所表示的evdev,将client挂到evdev->client_list上

  17.     if (!evdev->open++ && evdev->exist) {
  18.         error = input_open_device(&evdev->handle);
  19.         if (error) {
  20.             list_del(&client->node);
  21.             kfree(client);
  22.             return error;
  23.         }
  24.     }

  25.     file->private_data = client;//将client赋值为私有
  26.     return 0;
  27. }
       如果evdev是第一次打开,对应的会打开input_open_device,完成handle的打开。

点击(此处)折叠或打开

  1. int input_open_device(struct input_handle *handle)
  2. {
  3.     struct input_dev *dev = handle->dev;
  4.     int err;

  5.     err = mutex_lock_interruptible(&dev->mutex);
  6.     if (err)
  7.         return err;

  8.     handle->open++;

  9.     if (!dev->users++ && dev->open)
  10.         err = dev->open(dev);

  11.     if (err)
  12.         handle->open--;

  13.     mutex_unlock(&dev->mutex);

  14.     return err;
  15. }
      对于新赋值的fops,大致内容基本与字符设备相同,可以自己参考源码分析

点击(此处)折叠或打开

  1. static const struct file_operations evdev_fops = {
  2.     .owner =    THIS_MODULE,
  3.     .read =        evdev_read,
  4.     .write =    evdev_write,
  5.     .poll =        evdev_poll,
  6.     .open =        evdev_open,
  7.     .release =    evdev_release,
  8.     .unlocked_ioctl = evdev_ioctl,
  9. #ifdef CONFIG_COMPAT
  10.     .compat_ioctl =    evdev_ioctl_compat,
  11. #endif
  12.     .fasync =    evdev_fasync,
  13.     .flush =    evdev_flush
  14. };

       


 


 
阅读(2201) | 评论(0) | 转发(4) |
0

上一篇:输入子系统一

下一篇:linux设备驱动之kset

给主人留下些什么吧!~~