在linux内核中 已做好各类驱动的框架,驱动程序也属于内核的一部分,我们可以在原有的驱动上修改,来匹配我们自已的硬件,也可以自已编写符合内核驱动框架的驱动程序。出于学习的目的,便于更好的理解各类驱动的框架和编程思想,先分析内核自带的驱动框架和流程,再自已编写符合内核框架的驱动程序。下面开始,从输入子系统开始学习分析,后面一步一步涉及各类驱动。

一、输入子系统

  从 drivers/input/input.c 这个文件开始分析,分析驱动程序的时候,先从其入口函数开始,因为每当加载一个驱动程序的时候就是调用这个驱动的入口函数,后面我们就假设对一个驱动的读写操作,一步一步展开深入,分析其流程,从而得到框架,当我们自已写驱动时就可以参照这个框架一步步编写程序,实现功能。

  1. 1 /* 定义一个 file_operations 结构体 并设置 */
  2. 2 static const struct file_operations input_fops = {
  3. 3 .owner = THIS_MODULE,
  4. 4 .open = input_open_file,
  5. 5 .llseek = noop_llseek,
  6. 6 };
  7. 7
  8. 8 static int __init input_init(void)
  9. 9 {
  10. 10 int err;
  11. 11 /* 注册一个 input_class 类 */
  12. 12 err = class_register(&input_class);
  13. 13 if (err) {
  14. 14 pr_err("unable to register input_dev class\n");
  15. 15 return err;
  16. 16 }
  17. 17
  18. 18 err = input_proc_init();
  19. 19 if (err)
  20. 20 goto fail1;
  21. 21 /* 注册一个字符设备 参数:INPUT_MAJOR-主设备号 "input"-名字 input_fops-file_operations 结构体 */
  22. 22 err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  23. 23 if (err) {
  24. 24 pr_err("unable to register char major %d", INPUT_MAJOR);
  25. 25 goto fail2;
  26. 26 }
  27. 27
  28. 28 return 0;
  29. 29
  30. 30 fail2: input_proc_exit();
  31. 31 fail1: class_unregister(&input_class);
  32. 32 return err;
  33. 33 }

  A:假设我们的应用程序打开这个设备,那么就会调用这个设备的open函数,上面注册设备时,传入了一个 file_operations 结构体,他里面有一个 open = input_open_file 进入这个函数来分析

  1. static int input_open_file(struct inode *inode, struct file *file)
  2. {
  3. /* 定义一个 input_handler 结构体指针 */
  4. struct input_handler *handler;
  5. /* 定义两个file_operations 结构体指针 */
  6. const struct file_operations *old_fops, *new_fops = NULL;
  7. int err;
  8. /* 互斥锁 上锁 */
  9. err = mutex_lock_interruptible(&input_mutex);
  10. if (err)
  11. return err;
  12. /* 设置 handler 指向 以打开设备的次设备号右移5位的值为下标
  13. * 取出 input_table【下标】 数组的一项 赋给 handler
  14. */
  15. /* No load-on-demand here? */
  16. handler = input_table[iminor(inode) >> 5];
  17. /* 判断这项是否存在 1-存在 0-不存在 */
  18. if (handler)
  19. /* 把 handler 里的 file_operations 结构体 赋给 new_fops */
  20. new_fops = fops_get(handler->fops);
  21. /* 互斥锁 解锁 */
  22. mutex_unlock(&input_mutex);
  23.  
  24. /*
  25. * That's _really_ odd. Usually NULL ->open means "nothing special",
  26. * not "no device". Oh, well...
  27. */
  28. if (!new_fops || !new_fops->open) {
  29. fops_put(new_fops);
  30. err = -ENODEV;
  31. goto out;
  32. }
  33. /* 保存原来的 file_operations 并把新的new_fops 赋给file->f_op */
  34. old_fops = file->f_op;
  35. file->f_op = new_fops;
  36. /* 调用新的fops里的open函数
  37. * 即执行的是 handler->fops->open 函数
  38. */
  39. err = new_fops->open(inode, file);
  40. if (err) {
  41. fops_put(file->f_op);
  42. file->f_op = fops_get(old_fops);
  43. }
  44. fops_put(old_fops);
  45. out:
  46. return err;
  47. }

到这里,我们就知道了,APP执行open时,最终会调用到 handler->fops->open 函数,那么,handler->fops->open 函数是谁呢?这个函数从那里来的?谁创建设置这个函数?

在上面的代码里有一条 handler = input_table[iminor(inode) >> 5]; 从 input_table 数组中得到,那这个数组又是谁构造和填充设置的呢?这个数组是一个静态数组,所以在本文件中搜索一下应该可以知道结果。

  1. static struct input_handler *input_table[8];
  1. int input_register_handler(struct input_handler *handler)
  2. {
  3. /* 定义一个 input_dev 结构体指针 */
  4. struct input_dev *dev;
  5. int retval;
  6. /* 互斥锁 上锁 */
  7. retval = mutex_lock_interruptible(&input_mutex);
  8. if (retval)
  9. return retval;
  10. /* 初始化链表头 */
  11. INIT_LIST_HEAD(&handler->h_list);
  12. /* 判断 handler->fops 是否存在 判断 input_table[handler->minor >> 5]
  13. * 数组里这项是否存在 存在就返回忙错误 跳到 out处
  14. */
  15. if (handler->fops != NULL) {
  16. if (input_table[handler->minor >> 5]) {
  17. retval = -EBUSY;
  18. goto out;
  19. }
  20. /* 把这个函数的参数 handler 赋给这一项 即设置数组这一项,open 里要用到 */
  21. input_table[handler->minor >> 5] = handler;
  22. }
  23. /* 把这个结构体加入 input_handler_list 链表 */
  24. list_add_tail(&handler->node, &input_handler_list);
  25. /* 遍历 input_dev_list 链表 对这个链表的每一个节点都执行 input_attach_handler(dev, handler); */
  26. list_for_each_entry(dev, &input_dev_list, node)
  27. /* 这个函数是比较dev和handler里的id 是否匹配 */
  28. input_attach_handler(dev, handler);
  29.  
  30. input_wakeup_procfs_readers();
  31.  
  32. out:
  33. mutex_unlock(&input_mutex);
  34. return retval;
  35. }

搜索得到在 int input_register_handler(struct input_handler *handler) 函数里构造设置了,这里传入的参数 struct input_handler *handler 我们不清楚是那来的,再搜索那里调用了 input_register_handler 把参数搞清楚

得到很多文件调用了这个函数,我们选一个简单的来分析 就以 Evdev.c 为例来分析一下,了解一个 传入参数是什么内容。进入 evdev.c 分析。

Apm-power.c (drivers\input): return input_register_handler(&apmpower_handler);
Evbug.c (drivers\input): return input_register_handler(&evbug_handler);
Evdev.c (drivers\input): return input_register_handler(&evdev_handler);
Input.c (drivers\input): * input_register_handler - register a new input handler
Input.c (drivers\input):int input_register_handler(struct input_handler *handler)
Input.c (drivers\input):EXPORT_SYMBOL(input_register_handler);
Input.c (net\rfkill): return input_register_handler(&rfkill_handler);
Input.h (include\linux):int __must_check input_register_handler(struct input_handler *);
Joydev.c (drivers\input): return input_register_handler(&joydev_handler);
Keyboard.c (drivers\tty\vt): error = input_register_handler(&kbd_handler);
Kgdboc.c (drivers\tty\serial): if (input_register_handler(&kgdboc_reset_handler) == 0)
Mac_hid.c (drivers\macintosh): err = input_register_handler(&mac_hid_emumouse_handler);
Mousedev.c (drivers\input): error = input_register_handler(&mousedev_handler);
Sysrq.c (drivers\tty): error = input_register_handler(&sysrq_handler);

  1. static struct input_handler evdev_handler = {
  2. .event = evdev_event, /* 事件处理函数 */
  3. .connect = evdev_connect, /* 连接函数 设备层和 软件处理层连接 */
  4. .disconnect = evdev_disconnect, /* 清除连接 */
  5. .fops = &evdev_fops, /* file_operations 结构体 */
  6. .minor = EVDEV_MINOR_BASE, /* 次设备号 */
  7. .name = "evdev", /* 名字 */
  8. .id_table = evdev_ids, /* ID 用于和设备层匹配 */
  9. };
  10.  
  11. static int __init evdev_init(void)
  12. {
  13. /* 注册一个 input_register_handler */
  14. return input_register_handler(&evdev_handler);
  15. }

我们可以看到,是在入口函数里调用了 input_register_handler 这个函数,这个参数就是 input_handler 结构体,可以看到这个结构体里很多函数,file_operations 结构体 等等,从这里我们基本就可以确定在前的所需要用到的一些值和函数了。下面简单列出一些相对重要的参数。

  1. /* input_table[handler->minor >> 5] = handler;
  2. * handler->minor >> 5 = 64 >> 5 = 2
  3. * handler = evdev_handler
  4. * input_table[2] = evdev_handler
  5. */
  6.  
  7. /* err = new_fops->open(inode, file);
  8. * new_fops->open = evdev_fops->evdev_open(inode, file);
  9. */
  10.  
  11. static const struct file_operations evdev_fops = {
  12. .owner = THIS_MODULE,
  13. .read = evdev_read,
  14. .write = evdev_write,
  15. .poll = evdev_poll,
  16. .open = evdev_open,
  17. .release = evdev_release,
  18. .unlocked_ioctl = evdev_ioctl,
  19. #ifdef CONFIG_COMPAT
  20. .compat_ioctl = evdev_ioctl_compat,
  21. #endif
  22. .fasync = evdev_fasync,
  23. .flush = evdev_flush,
  24. .llseek = no_llseek,
  25. };

由此得知,当app应用层调用操作时,最终会调用到 input_handler 结构体里的操作函数 事实上如果跟着分析下去还有很多东西,如果现在跟得太深,不太好理解,所以暂时对于handler层 先分析到这里,后面的内容和另一个层 设备层是一起的,所以下面转过头先分析设备层,等到设备层分析到和handler层相接的地方,在往下分析,由此可以便于加深理解,为什么我知道有个设备层呢?“我也是跟别人学的” 但是有一点可以看出来,上面分析的过程中,出现过一个东西在 input_register_handler 这个函里有一段处理

  1. /* 把这个结构体加入 input_handler_list 链表 */
  2. list_add_tail(&handler->node, &input_handler_list);
  3. /* 遍历 input_dev_list 链表 对这个链表的每一个节点都执行 input_attach_handler(dev, handler); */
  4. list_for_each_entry(dev, &input_dev_list, node)
  5. /* 这个函数是比较dev和handler里的id 是否匹配 */
  6. input_attach_handler(dev, handler);

这里提到一个链表 input_dev_list 这就是设备层的链表,我们搜索一下,看在那里可以找到相关的信息,搜索到一些内容,我们依次进去看一下,看在那里有这个链表的处理 最终我搜索到一条这样的处理

list_add_tail(&dev->node, &input_dev_list); 这条处理就把dev结构体加入input_dev_list这个链表,在 int input_register_device(struct input_dev *dev) 函数里调用,看名字就是注册一个输入设备,参数是input_dev 结构体

和我们前面看到的注册类同,因此我们再搜索一下谁调用了这个函数 结果是 一大堆设备调用了这个函数,由此我猜测,调用这个函数的就是设备层关于硬件相关的驱动,你问我怎么知道,因为我是天才!!!开个玩笑,我要是天才就不用学了。。。当然我也是跟别人学的。所谓的别人就是“韦东山大牛” 这不是广告,纯属个人觉得,他的东西确实好,好像跑偏了。回到正题。刚说到搜索到很多调用,我们选一个简单点的来分析一下,设备层这边的框架,现在分析的东西,就是我们自已写驱动所需要编写的内容,了解了这层的框架和逻辑思维后,按照框架就可以写出符合内核的驱动程序了,同上面一样,找一个简单的例子分析,我里我们选择 gpio_key.c 这只是一个例子,他没有具体的硬件操作。下面开始

在 static int __devinit gpio_keys_probe(struct platform_device *pdev) 这个函数中调用了 error = input_register_device(input); 和上面一样,注册一个input_dev 设备,关于 gpio_keys_probe 是和平台设备相关,我们先不管他我们写驱动时也可以不管他,直接注册设备即可,在这里了解一下 注册前需要做那些事,看代码

  1. static int __devinit gpio_keys_probe(struct platform_device *pdev)
  2. {
  3. const struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
  4. struct gpio_keys_drvdata *ddata;
  5. struct device *dev = &pdev->dev;
  6. struct gpio_keys_platform_data alt_pdata;
  7. struct input_dev *input;
  8. int i, error;
  9. int wakeup = 0;
  10.  
  11. if (!pdata) {
  12. error = gpio_keys_get_devtree_pdata(dev, &alt_pdata);
  13. if (error)
  14. return error;
  15. pdata = &alt_pdata;
  16. }
  17.  
  18. ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
  19. pdata->nbuttons * sizeof(struct gpio_button_data),
  20. GFP_KERNEL);
  21. /* 分配一个 inpu_dev 结构体 前面有定义的 struct input_dev *input; */
  22. input = input_allocate_device();
  23. if (!ddata || !input) {
  24. dev_err(dev, "failed to allocate state\n");
  25. error = -ENOMEM;
  26. goto fail1;
  27. }
  28.  
  29. ddata->input = input;
  30. ddata->n_buttons = pdata->nbuttons;
  31. ddata->enable = pdata->enable;
  32. ddata->disable = pdata->disable;
  33. mutex_init(&ddata->disable_lock);
  34.  
  35. platform_set_drvdata(pdev, ddata);
  36. input_set_drvdata(input, ddata);
  37. /* 这里有一堆设置 input_dev 结构体的 我们自已编写的时候用到什么函数就实现什么函数这里是例子 */
  38. input->name = pdata->name ? : pdev->name;
  39. input->phys = "gpio-keys/input0";
  40. input->dev.parent = &pdev->dev;
  41. input->open = gpio_keys_open; /* open 函数 */
  42. input->close = gpio_keys_close;
  43.  
  44. input->id.bustype = BUS_HOST;
  45. input->id.vendor = 0x0001;
  46. input->id.product = 0x0001;
  47. input->id.version = 0x0100;
  48.  
  49. /* Enable auto repeat feature of Linux input subsystem */
  50. if (pdata->rep)
  51. __set_bit(EV_REP, input->evbit);
  52.  
  53. for (i = 0; i < pdata->nbuttons; i++) {
  54. const struct gpio_keys_button *button = &pdata->buttons[i];
  55. struct gpio_button_data *bdata = &ddata->data[i];
  56.  
  57. error = gpio_keys_setup_key(pdev, input, bdata, button);
  58. if (error)
  59. goto fail2;
  60.  
  61. if (button->wakeup)
  62. wakeup = 1;
  63. }
  64.  
  65. error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group);
  66. if (error) {
  67. dev_err(dev, "Unable to export keys/switches, error: %d\n",
  68. error);
  69. goto fail2;
  70. }
  71. /* 注册 input_dev */
  72. error = input_register_device(input);
  73. if (error) {
  74. dev_err(dev, "Unable to register input device, error: %d\n",
  75. error);
  76. goto fail3;
  77. }
  78.  
  79. /* get current state of buttons that are connected to GPIOs */
  80. for (i = 0; i < pdata->nbuttons; i++) {
  81. struct gpio_button_data *bdata = &ddata->data[i];
  82. if (gpio_is_valid(bdata->button->gpio))
  83. gpio_keys_gpio_report_event(bdata);
  84. }
  85. input_sync(input);
  86.  
  87. device_init_wakeup(&pdev->dev, wakeup);
  88.  
  89. return 0;
  90.  
  91. fail3:
  92. sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group);
  93. fail2:
  94. while (--i >= 0)
  95. gpio_remove_key(&ddata->data[i]);
  96.  
  97. platform_set_drvdata(pdev, NULL);
  98. fail1:
  99. input_free_device(input);
  100. kfree(ddata);
  101. /* If we have no platform_data, we allocated buttons dynamically. */
  102. if (!pdev->dev.platform_data)
  103. kfree(pdata->buttons);
  104.  
  105. return error;
  106. }

我们看到,在注册前,分配了一个input_dev 结构体 并设置他。然后调用注册函数,我们到注册函数里看一下做了些什么鬼事情?

  1. int input_register_device(struct input_dev *dev)
  2. {
  3. /* 这是什么鬼,不认识 不管他 */
  4. static atomic_t input_no = ATOMIC_INIT(0);
  5. /* 定义一个 input_handler 结构体 */
  6. struct input_handler *handler;
  7. const char *path;
  8. int error;
  9.  
  10. /* Every input device generates EV_SYN/SYN_REPORT events. */
  11. __set_bit(EV_SYN, dev->evbit);
  12.  
  13. /* KEY_RESERVED is not supposed to be transmitted to userspace. */
  14. __clear_bit(KEY_RESERVED, dev->keybit);
  15.  
  16. /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
  17. input_cleanse_bitmasks(dev);
  18.  
  19. if (!dev->hint_events_per_packet)
  20. dev->hint_events_per_packet =
  21. input_estimate_events_per_packet(dev);
  22.  
  23. /*
  24. * If delay and period are pre-set by the driver, then autorepeating
  25. * is handled by the driver itself and we don't do it in input.c.
  26. */
  27. init_timer(&dev->timer); /* 应该是初始化定时器 */
  28. if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
  29. dev->timer.data = (long) dev;
  30. dev->timer.function = input_repeat_key;
  31. dev->rep[REP_DELAY] = 250;
  32. dev->rep[REP_PERIOD] = 33;
  33. }
  34.  
  35. if (!dev->getkeycode)
  36. dev->getkeycode = input_default_getkeycode;
  37.  
  38. if (!dev->setkeycode)
  39. dev->setkeycode = input_default_setkeycode;
  40.  
  41. dev_set_name(&dev->dev, "input%ld",
  42. (unsigned long) atomic_inc_return(&input_no) - 1);
  43.  
  44. error = device_add(&dev->dev);
  45. if (error)
  46. return error;
  47.  
  48. path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
  49. pr_info("%s as %s\n",
  50. dev->name ? dev->name : "Unspecified device",
  51. path ? path : "N/A");
  52. kfree(path);
  53. /* 互斥锁 上锁 老是有这东东,怎么互斥怎么锁,没空理它以后再说 */
  54. error = mutex_lock_interruptible(&input_mutex);
  55. if (error) {
  56. device_del(&dev->dev);
  57. return error;
  58. }
  59. /* 把 input_dev 加入 input_dev_list 链表 */
  60. list_add_tail(&dev->node, &input_dev_list);
  61. /* 遍历 input_handler_list 链表 对每一项都执行input_attach_handler 函数 */
  62. list_for_each_entry(handler, &input_handler_list, node)
  63. input_attach_handler(dev, handler);
  64.  
  65. input_wakeup_procfs_readers();
  66.  
  67. mutex_unlock(&input_mutex);
  68.  
  69. return 0;
  70. }

我们看到了在后面同样是加入了链表,同样也遍历链表,执行相同的函数,在dve层是遍历 input_headler_list链表 在handler层是遍历 input_dev_list链表 嗯,我主场我做主,我找你,你主场你做主,你找我,谁是你,谁是我,你高兴就好,爱谁谁。两个层都调用了相同的函数 input_attach_handler 所以我们很有必要去看一下他想干啥,

  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
  2. {
  3. const struct input_device_id *id;
  4. int error;
  5. /* 找好基友函数 对比ID 是否相同,相同的话返回值 id=1 */
  6. id = input_match_device(handler, dev);
  7. if (!id)
  8. return -ENODEV;
  9. /* 上面必须返回1才会执行这条 即调用handr层的connect函数 */
  10. error = handler->connect(handler, dev, id);
  11. if (error && error != -ENODEV)
  12. pr_err("failed to attach handler %s to device %s, error: %d\n",
  13. handler->name, kobject_name(&dev->dev.kobj), error);
  14.  
  15. return error;
  16. }

里面还有一个函数我们再进去看看

  1. static const struct input_device_id *input_match_device(struct input_handler *handler,
  2. struct input_dev *dev)
  3. {
  4. const struct input_device_id *id;
  5. int i;
  6.  
  7. for (id = handler->id_table; id->flags || id->driver_info; id++) {
  8.  
  9. if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
  10. if (id->bustype != dev->id.bustype)
  11. continue;
  12.  
  13. if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
  14. if (id->vendor != dev->id.vendor)
  15. continue;
  16.  
  17. if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
  18. if (id->product != dev->id.product)
  19. continue;
  20.  
  21. if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
  22. if (id->version != dev->id.version)
  23. continue;
  24.  
  25. MATCH_BIT(evbit, EV_MAX);
  26. MATCH_BIT(keybit, KEY_MAX);
  27. MATCH_BIT(relbit, REL_MAX);
  28. MATCH_BIT(absbit, ABS_MAX);
  29. MATCH_BIT(mscbit, MSC_MAX);
  30. MATCH_BIT(ledbit, LED_MAX);
  31. MATCH_BIT(sndbit, SND_MAX);
  32. MATCH_BIT(ffbit, FF_MAX);
  33. MATCH_BIT(swbit, SW_MAX);
  34.  
  35. if (!handler->match || handler->match(handler, dev))
  36. return id;
  37. }
  38.  
  39. return NULL;
  40. }

就是看一下 handler->id_tabe 支不支持 dev,支持就返回1 我们看到 handler->id_tabe  里说了,匹配所有设备,然后调用handler->connect,进去看看这个函数干啥?

  1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
  2. const struct input_device_id *id)
  3. {
  4. struct evdev *evdev;
  5. int minor;
  6. int error;
  7.  
  8. for (minor = 0; minor < EVDEV_MINORS; minor++)
  9. if (!evdev_table[minor])
  10. break;
  11.  
  12. if (minor == EVDEV_MINORS) {
  13. pr_err("no more free evdev devices\n");
  14. return -ENFILE;
  15. }
  16.  
  17. evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  18. if (!evdev)
  19. return -ENOMEM;
  20.  
  21. INIT_LIST_HEAD(&evdev->client_list);
  22. spin_lock_init(&evdev->client_lock);
  23. mutex_init(&evdev->mutex);
  24. init_waitqueue_head(&evdev->wait);
  25.  
  26. dev_set_name(&evdev->dev, "event%d", minor);
  27. evdev->exist = true;
  28. evdev->minor = minor;
  29.  
  30. evdev->handle.dev = input_get_device(dev);
  31. evdev->handle.name = dev_name(&evdev->dev);
  32. evdev->handle.handler = handler;
  33. evdev->handle.private = evdev;
  34.  
  35. evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
  36. evdev->dev.class = &input_class;
  37. evdev->dev.parent = &dev->dev;
  38. evdev->dev.release = evdev_free;
  39. device_initialize(&evdev->dev);
  40.  
  41. error = input_register_handle(&evdev->handle);
  42. if (error)
  43. goto err_free_evdev;
  44.  
  45. error = evdev_install_chrdev(evdev);
  46. if (error)
  47. goto err_unregister_handle;
  48.  
  49. error = device_add(&evdev->dev);
  50. if (error)
  51. goto err_cleanup_evdev;
  52.  
  53. return 0;
  54.  
  55. err_cleanup_evdev:
  56. evdev_cleanup(evdev);
  57. err_unregister_handle:
  58. input_unregister_handle(&evdev->handle);
  59. err_free_evdev:
  60. put_device(&evdev->dev);
  61. return error;
  62. }

这里面又分配设置了一个input_headle 结构体 注意看名称,少了个 r

evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;

error = input_register_handle(&evdev->handle); 进去看个函数

  1. int input_register_handle(struct input_handle *handle)
  2. {
  3. struct input_handler *handler = handle->handler;
  4. struct input_dev *dev = handle->dev;
  5. int error;
  6.  
  7. /*
  8. * We take dev->mutex here to prevent race with
  9. * input_release_device().
  10. */
  11. error = mutex_lock_interruptible(&dev->mutex);
  12. if (error)
  13. return error;
  14.  
  15. /*
  16. * Filters go to the head of the list, normal handlers
  17. * to the tail.
  18. */
  19. if (handler->filter)
  20. list_add_rcu(&handle->d_node, &dev->h_list);
  21. else
  22. list_add_tail_rcu(&handle->d_node, &dev->h_list);
  23.  
  24. mutex_unlock(&dev->mutex);
  25.  
  26. /*
  27. * Since we are supposed to be called from ->connect()
  28. * which is mutually exclusive with ->disconnect()
  29. * we can't be racing with input_unregister_handle()
  30. * and so separate lock is not needed here.
  31. */
  32. list_add_tail_rcu(&handle->h_node, &handler->h_list);
  33.  
  34. if (handler->start)
  35. handler->start(handle);
  36.  
  37. return 0;
  38. }

一个指向 handler 一个指向dev 所以无论从那边进入都可以找到对方,到此双方就建立“基友”的连接关系了。

  B:假设app读操作,通过上面的分析我们知道最终会调用到对应的handler里的fops里的read函数,我们来看一下他怎么读的

  1. static ssize_t evdev_read(struct file *file, char __user *buffer,
  2. size_t count, loff_t *ppos)
  3. {
  4. struct evdev_client *client = file->private_data;
  5. struct evdev *evdev = client->evdev;
  6. struct input_event event;
  7. int retval = 0;
  8.  
  9. if (count < input_event_size())
  10. return -EINVAL;
  11.  
  12. if (!(file->f_flags & O_NONBLOCK)) {
  13. retval = wait_event_interruptible(evdev->wait,/* 休眠 谁来唤醒 搜索evdev->wait */
  14. client->packet_head != client->tail ||
  15. !evdev->exist);
  16. if (retval)
  17. return retval;
  18. }
  19.  
  20. if (!evdev->exist)
  21. return -ENODEV;
  22.  
  23. while (retval + input_event_size() <= count &&
  24. evdev_fetch_next_event(client, &event)) {
  25.  
  26. if (input_event_to_user(buffer + retval, &event))
  27. return -EFAULT;
  28.  
  29. retval += input_event_size();
  30. }
  31.  
  32. if (retval == 0 && (file->f_flags & O_NONBLOCK))
  33. return -EAGAIN;
  34.  
  35. return retval;
  36. }

在读函数里休眠,那谁来唤醒他呢,他在那个队列里休眠,我们就搜索这个队列,看在那里有唤醒操作 在static void evdev_event(struct input_handle *handle)函数里调用 wake_up_interruptible(&evdev->wait); 来唤醒,谁又调用了evdev_event 函数呢 别人猜得很准,说是从设备层那边会调用到这个函数,用例子来说明,当有按键按下时,进入设备层中断处理函数 在里面会调用inpu_event,分析这个函数可以知道如何操作

  1. static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
  2. {
  3. struct gpio_button_data *bdata = dev_id;
  4. const struct gpio_keys_button *button = bdata->button;
  5. struct input_dev *input = bdata->input;
  6. unsigned long flags;
  7.  
  8. BUG_ON(irq != bdata->irq);
  9.  
  10. spin_lock_irqsave(&bdata->lock, flags);
  11.  
  12. if (!bdata->key_pressed) {
  13. input_event(input, EV_KEY, button->code, 1);
  14. input_sync(input);
  15.  
  16. if (!bdata->timer_debounce) {
  17. input_event(input, EV_KEY, button->code, 0);
  18. input_sync(input);
  19. goto out;
  20. }
  21.  
  22. bdata->key_pressed = true;
  23. }
  24.  
  25. if (bdata->timer_debounce)
  26. mod_timer(&bdata->timer,
  27. jiffies + msecs_to_jiffies(bdata->timer_debounce));
  28. out:
  29. spin_unlock_irqrestore(&bdata->lock, flags);
  30. return IRQ_HANDLED;
  31. }

这里就简单列出调用关系,就不做进一步细化分析了

inpu_event

  input_handle_event

    input_pass_event

      handler->event(handle, type, code, value);  //这里最终就是调用 evdev_event 唤醒 evdev_read函数唤醒后调用 input_event_to_user 把数据给app

到这里框架就分析完毕了,那么 我们自已如何编写一个符合内核这套input 输入子系统的驱动呢,我们主要关心设备层

1:分配一个input_dev 结构体

2:设置

3:注册

4:硬件机关的操作 (例:当按键按下中,产生中断,在中断服务函数里上报事件即可)

input_subsys 输入子系统框架分析的更多相关文章

  1. Linux输入子系统框架分析(1)

    在Linux下的输入设备键盘.触摸屏.鼠标等都能够用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层.事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备 ...

  2. linux input输入子系统应用分析

    输入设备(如按键.键盘.触摸屏.鼠标等)是典型的字符设备,其一般的工作机理是底层在按键.触摸等动作发送时产生一个中断(或驱动通过timer定时查询),然后CPU通过SPI.I2 C或外部存储器总线读取 ...

  3. 【驱动】input子系统全面分析

    初识linux输入子系统 linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(InputC ...

  4. INPUT输入子系统——按键

    一.什么是input输入子系统? 1.1. Linux系统支持的输入设备繁多,例如键盘.鼠标.触摸屏.手柄或者是一些输入设备像体感输入等等,Linux系统是如何管理如此之多的不同类型.不同原理.不同的 ...

  5. 7.Linux 输入子系统分析

    为什么要引入输入子系统? 在前面我们写了一些简单的字符设备的驱动程序,我们是怎么样打开一个设备并操作的呢? 一般都是在执行应用程序时,open一个特定的设备文件,如:/dev/buttons .... ...

  6. Linux输入子系统详解

    input输入子系统框架  linux输入子系统(linux input subsystem)从上到下由三层实现,分别为:输入子系统事件处理层(EventHandler).输入子系统核心层(Input ...

  7. linux输入子系统之按键驱动

    上一节中,我们讲解了Linux  input子系统的框架,到内核源码里详细分析了输入子系统的分离分层的框架等. 上一节文章链接:http://blog.csdn.net/lwj103862095/ar ...

  8. linux输入子系统概念介绍

    在此文章之前,我们讲解的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥.非阻塞.定时器去抖动. 上一节文章链接:http://blo ...

  9. 8、linux下输入子系统

    input_sync(button_dev);    /*通知接收者,一个报告发送完毕*/ 参考:http://www.51hei.com/bbs/dpj-27652-1.html  很详细说明 in ...

  10. 12.Linux之输入子系统分析(详解)

    版权声明:本文为博主原创文章,转载请标注出处:   在此节之前,我们学的都是简单的字符驱动,涉及的内容有字符驱动的框架.自动创建设备节点.linux中断.poll机制.异步通知.同步互斥/非阻塞.定时 ...

随机推荐

  1. Python学习笔记调式之抛出异常

    随笔记录方便自己和同路人查阅. #------------------------------------------------我是可耻的分割线--------------------------- ...

  2. 计数 dp 部分例题(六~十部分)

    六.转化求和顺序(線形和への分解) 例题1 题意 有一个长为 \(n\) 的数组 \(a\).求在 \(a\) 中选择 \(k\) 个数的所有方案中,每次选择的所有数的中位数的和.\(n\le 10^ ...

  3. vue 打开页面触发事件

    vue中created(),mounted()与activated()区别及应用 created():在创建vue对象时,当html渲染之前就触发:但是注意,全局vue.js不强制刷新或者重启时只创建 ...

  4. js 获取标签属性值

    有时候需要获取标签里属性里的值,可以采用以下方法: demo: <a href="/admin/article/${id}" onclick="return del ...

  5. WindowsServer2012搭建FTP服务器站点

    公司需要搭建一个FTP服务器给银行推送账单,这个文章整理的比较详细,可以参考 数据来源: https://blog.csdn.net/u010483330/article/details/125931 ...

  6. hadoop单机测试环境安装(简)

    1.下载hadoop官网就可以下载.可以直接搜hadoop  ,  其实可以直接查看官网的版主文档搭建https://hadoop.apache.org/docs/stable/hadoop-proj ...

  7. RN 使用react-native-video 播放视频(包含进度条、全屏)

    21年12月3日,阐述上有问题:应该将问题拆分,不该将代码整一大堆,看着很不舒适 目标需求:1. 实现视频播放 2. 进度条 3. 进入全屏 目标图是这样的: 需要三个组件 1. 播放视频组件, re ...

  8. noi 1.5 28 分离整数的各个数位

    描述 给定一个整数,要求从个位开始分离出它的每一位数字. 输入 输入一个整数,整数在1到100000000之间. 输出 从个位开始按照从低位到高位的顺序依次输出每一位数字.数字之间以一个空格分开. 样 ...

  9. 2D 消隐效果

    在触发消隐的物体(比如玩家)挂下面的代码,以及box collider,rigidbody 2d using UnityEngine; public class ColliderFader : Mon ...

  10. Jenkins项目中的Performance Trend图表不显示

    权限问题:chmod 777 /.../*.jtl 其中上述目录为jmeter生成jtl格式的结果报告的路径,也就是ant对应build.xml里配置好的路径.