• 实现的框架:
      • image.png
    • 新的驱动程序有3个ko(leddrv、chip_demo_gpio、board_A_led)
      • makefile写法:image.png
      • image.png
      • 之前的驱动程序将三者编译成一个ko
    • 加载模块
      • image.png
      • chip_demo_gpio模块依赖于leddrv,所以需先加载leddrv模块
    • 要修改引脚(增删改)只需修改board_A_led中的资源,然后重新编译、加载board_A_led模块即可;就不同编译加载所有的模块了
      • image.png
      • image.png
    • 最终实现如下:
      • image.png
    • board_A_led.c(板级相关的代码) ```c static void led_dev_release(struct device dev) { / release函数必须提供,否则在调用 platform_device_unregister 时会出现警告 / } / 资源结构体数组 */ static struct resource resources[] = {
      1. {
      2. .start = GROUP_PIN(3,1),
      3. .flags = IORESOURCE_IRQ,
      4. .name = "100ask_led_pin",
      5. },
      6. {
      7. .start = GROUP_PIN(5,8), /* 表示GPIO组和引脚 */
      8. .flags = IORESOURCE_IRQ, /* 没有GPIO类型,用IRQ类型当作GPIO */
      9. .name = "100ask_led_pin", /* 名字 */
      10. },
      }; / platform_device结构体 / static struct platform_device board_A_led_dev = {
      1. .name = "100ask_led",
      2. .num_resources = ARRAY_SIZE(resources), /* 计算有多少个资源 */
      3. .resource = resources, /* resource等于资源数组 */
      4. .dev = {
      5. .release = led_dev_release, /* 提供release函数,在unregister时被调用 */
      6. },
      }; / 该模块的入口函数, 因为单独编译成ko文件,所以需要提供入口和出口函数 / static int __init led_dev_init(void) { int err;
      err = platform_device_register(&board_A_led_dev); / 注册platform_device结构体 / return 0; }

    static void __exit led_dev_exit(void) { platform_device_unregister(&board_A_led_dev); / 注销platform_device结构体 / }

    module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE(“GPL”);

    1. - chip_demo_gpio.c
    2. ```c
    3. static int g_ledpins[100];
    4. static int g_ledcnt = 0;
    5. static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
    6. {
    7. printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    8. switch(GROUP(g_ledpins[which]))
    9. {
    10. case 0:
    11. {
    12. printk("init pin of group 0 ...\n");
    13. break;
    14. }
    15. case 1:
    16. {
    17. printk("init pin of group 1 ...\n");
    18. break;
    19. }
    20. case 2:
    21. {
    22. printk("init pin of group 2 ...\n");
    23. break;
    24. }
    25. case 3:
    26. {
    27. printk("init pin of group 3 ...\n");
    28. break;
    29. }
    30. }
    31. return 0;
    32. }
    33. static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
    34. {
    35. printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
    36. switch(GROUP(g_ledpins[which]))
    37. {
    38. case 0:
    39. {
    40. printk("set pin of group 0 ...\n");
    41. break;
    42. }
    43. case 1:
    44. {
    45. printk("set pin of group 1 ...\n");
    46. break;
    47. }
    48. case 2:
    49. {
    50. printk("set pin of group 2 ...\n");
    51. break;
    52. }
    53. case 3:
    54. {
    55. printk("set pin of group 3 ...\n");
    56. break;
    57. }
    58. }
    59. return 0;
    60. }
    61. /* 根据platform_device进行初始化 */
    62. static int chip_demo_gpio_probe(struct platform_device *pdev)
    63. {
    64. struct resource *res;
    65. int i = 0;
    66. while (1)
    67. {
    68. res = platform_get_resource(pdev, IORESOURCE_IRQ, i++); /* 获取pdev设备中IORESOURCE_IRQ类型的第i个资源 */
    69. if (!res)
    70. break;
    71. g_ledpins[g_ledcnt] = res->start; /* 将资源的起始地址赋给引脚数组 */
    72. led_class_create_device(g_ledcnt); /* 根据次设备号调用leddrv提供的led_class_create_device */
    73. g_ledcnt++;
    74. }
    75. return 0;
    76. }
    77. /* 移除platform_device */
    78. static int chip_demo_gpio_remove(struct platform_device *pdev)
    79. {
    80. struct resource *res;
    81. int i = 0;
    82. while (1)
    83. {
    84. res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
    85. if (!res)
    86. break;
    87. led_class_destroy_device(i); /* 根据次设备号调用leddrv提供的led_class_destroy_device进行销毁 */
    88. i++;
    89. g_ledcnt--;
    90. }
    91. return 0;
    92. }
    93. /* 实现led_operations */
    94. static struct led_operations board_demo_led_opr = {
    95. .init = board_demo_led_init,
    96. .ctl = board_demo_led_ctl,
    97. };
    98. /* 实现platform_driver */
    99. static struct platform_driver chip_demo_gpio_driver = {
    100. .probe = chip_demo_gpio_probe, /* 在注册时调用 */
    101. .remove = chip_demo_gpio_remove, /* 在注销时调用 */
    102. .driver = {
    103. .name = "100ask_led",
    104. },
    105. };
    106. /* 入口函数 */
    107. static int __init chip_demo_gpio_drv_init(void)
    108. {
    109. int err;
    110. err = platform_driver_register(&chip_demo_gpio_driver); /* 注册platform_driver */
    111. register_led_operations(&board_demo_led_opr); /* 注册led_operations, register_led_operations由上层leddrv模块提供 */
    112. return 0;
    113. }
    114. /* 出口函数 */
    115. static void __exit chip_demo_gpio_drv_exit(void)
    116. {
    117. platform_driver_unregister(&chip_demo_gpio_driver); /* 注销platform_driver */
    118. }
    119. module_init(chip_demo_gpio_drv_init);
    120. module_exit(chip_demo_gpio_drv_exit);
    121. MODULE_LICENSE("GPL");
    • leddrv.c ```c

    / 1. 确定主设备号 / static int major = 0; static struct class led_class; struct led_operations p_led_opr;

    void led_class_create_device(int minor) { device_create(led_class, NULL, MKDEV(major, minor), NULL, “100ask_led%d”, minor); / /dev/100ask_led0,1,… / } void led_class_destroy_device(int minor) { device_destroy(led_class, MKDEV(major, minor)); } / chip_demo_gpio中调用时,将其实现的led_operations赋值给p_led_opr(使得p_led_opr指向实现的结构体) / void register_led_operations(struct led_operations *opr) { p_led_opr = opr; }

    / 进行函数的外部声明,使得这些函数可以在底层被chip_demo_gpio模块调用(这表示chip_demo_gpio模块依赖于该模块) / EXPORT_SYMBOL(led_class_create_device); EXPORT_SYMBOL(led_class_destroy_device); EXPORT_SYMBOL(register_led_operations); / 在驱动程序中,该模块作为ko加载,别的驱动程序想要引用这些函数的话,必须将它们导出来 /

    / 3. 实现对应的open/read/write等函数,填入file_operations结构体 / static ssizet leddrvread (struct file file, char __user buf, sizet size, lofft *offset) { printk(“%s %s line %d\n”, _FILE, __FUNCTION, __LINE); return 0; }

    / write(fd, &val, 1); / static ssize_t led_drv_write (struct file file, const char __user buf, size_t size, loff_t offset) { int err; char status; struct inode inode = file_inode(file); int minor = iminor(inode);

    1. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    2. err = copy_from_user(&status, buf, 1);
    3. /* 根据次设备号和status控制LED */
    4. p_led_opr->ctl(minor, status);
    5. return 1;

    }

    static int led_drv_open (struct inode node, struct file file) { int minor = iminor(node);

    1. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    2. /* 根据次设备号初始化LED */
    3. p_led_opr->init(minor);
    4. return 0;

    }

    static int leddrvclose (struct inode node, struct file file) { printk(“%s %s line %d\n”, FILE, FUNCTION, __LINE); return 0; }

    / 2. 定义自己的file_operations结构体 / static struct file_operations led_drv = { .owner = THIS_MODULE, .open = led_drv_open, .read = led_drv_read, .write = led_drv_write, .release = led_drv_close, };

    / 4. 把file_operations结构体告诉内核:注册驱动程序 / / 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 / static int __init led_init(void) { int err; major = register_chrdev(0, “100ask_led”, &led_drv); / 注册file_operations结构体 /

    1. led_class = class_create(THIS_MODULE, "100ask_led_class");
    2. err = PTR_ERR(led_class);
    3. if (IS_ERR(led_class)) {
    4. printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    5. unregister_chrdev(major, "led");
    6. return -1;
    7. }
    8. return 0;

    }

    / 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 / static void exit led_exit(void) { printk(“%s %s line %d\n”, FILE, FUNCTION, LINE__); class_destroy(led_class); / 销毁class,至于device在platform_device移除时会调用platform_device的remove函数进行销毁 / unregister_chrdev(major, “100ask_led”); / 注销file_operations / }

    / 7. 其他完善:提供设备信息,自动创建设备节点 / module_init(led_init); module_exit(led_exit); MODULE_LICENSE(“GPL”); ```

    • leddrv上层代码只是一个空架子(框架),都没有跟硬件挂钩
    • a.c 编译为 a.ko,里面定义了 func_a;如果它想让 b.ko 使用该函数,那么 a.c 里需要导出此函数(如果 a.c, b.c 都编进内核,则无需导出): EXPORT_SYMBOL(led_device_create);
      • 使用时要先加载 a.ko