• 写驱动程序要去操作硬件,但是操作硬件并不是主要的,主要的是要理解驱动程序的框架
  • Linux驱动 = 驱动框架+硬件操作 = 驱动框架+单片机
  • 实际工作中,更多的是去理解修改别人的驱动程序
  • 面向对象
    • 字符设备驱动程序抽象出一个 file_operations 结构体
    • 我们写的程序针对硬件部分抽象出 led_operations 结构体
  • 分层
    • 前面的 LED 驱动程序就分为 2 层:

① 上层实现硬件无关的操作,比如注册字符设备驱动:leddrv.c
② 下层实现硬件相关的操作,比如 board_A.c 实现单板 A 的 LED 操作
image.png

  • 分离
    • 之前的board_x.c的代码跟硬件绑定的得太死了,操作某个灯就操作某个寄存器,如果想换个灯就需要换个寄存器
    • 对于同一款芯片,它的GPIO引脚操作是类似的
      • image.png
    • 既然引脚操作那么有规律,并且这是跟主芯片相关的,那可以针对该芯片写出比较通用的硬件操作代码
    • 对于某一款芯片,可以写出一个GPIO的驱动程序,让它支持所有的GPIO操作
    • 比如 board_A.c 使用芯片 chipY,那就可以写出:chipY_gpio.c,它实现芯片 Y 的 GPIO 操作,适用于芯片 Y 的所有 GPIO 引脚
    • image.png
    • 左边是led资源(实现led_resource),右边是某款芯片通用的硬件驱动
    • 以面向对象的思想,在 board_A_led.c 中实现 led_resouce 结构体,它定义“资源”──要用哪一个引脚
  • 修改内核打印级别
    • image.png
  • 程序仍分为上下结构:上层 leddrv.c 向内核注册 file_operations 结构体;下层 chip_demo_gpio.c 提供 led_operations 结构体来操作硬件。
  • 下层的代码分为 2 个:chip_demo_gpio.c 实现通用的 GPIO 操作,board_A_led.c 指定使用哪个 GPIO,即“资源”
  • 这样当不同的板子使用不同gpio时,只需修改board_x_led.c指定哪个gpio即可(修改资源)
  • led_resource.h ```c / GPIO3_0 / / bit[31:16] = group / / bit[15:0] = which pin /

    define GROUP(x) (x>>16)

    define PIN(x) (x&0xFFFF)

    define GROUP_PIN(g,p) ((g<<16) | (p))

struct led_resource { int pin; };

struct led_resource *get_led_resouce(void);

  1. - board_A_led.c中实现资源的确定
  2. ```c
  3. static struct led_resource board_A_led = {
  4. .pin = GROUP_PIN(3,1),
  5. };
  6. struct led_resource *get_led_resouce(void)
  7. {
  8. return &board_A_led;
  9. }
  • chip_demo_gpio.c 中实现了通用的 GPIO 操作,只需包含资源即可

    1. static struct led_resource *led_rsc;
    2. static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
    3. {
    4. //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which);
    5. if (!led_rsc)
    6. {
    7. led_rsc = get_led_resouce(); /* 获取资源 */
    8. }
    9. printk("init gpio: group %d, pin %d\n", GROUP(led_rsc->pin), PIN(led_rsc->pin));
    10. /* 根据资源进行初始化 */
    11. switch(GROUP(led_rsc->pin))
    12. {
    13. case 0:
    14. {
    15. printk("init pin of group 0 ...\n");
    16. break;
    17. }
    18. case 1:
    19. {
    20. printk("init pin of group 1 ...\n");
    21. break;
    22. }
    23. case 2:
    24. {
    25. printk("init pin of group 2 ...\n");
    26. break;
    27. }
    28. case 3:
    29. {
    30. printk("init pin of group 3 ...\n");
    31. break;
    32. }
    33. }
    34. return 0;
    35. }
  • makefile修改成 ```makefile

    参考内核源码drivers/char/ipmi/Makefile

    要想把a.c, b.c编译成ab.ko, 可以这样指定:

    ab-y := a.o b.o

    obj-m += ab.o

leddrv.c chip_demo_gpio.c board_A_led.c 编译成 100ask.ko

100ask_led-y := leddrv.o chip_demo_gpio.o board_A_led.o obj-m += 100ask_led.o ```

  • 课后练习

    使用“分离”的思想,去改造前面写的 LED 驱动程序:实现 led_resouce,在里面可以指定要使用哪一个 LED;改造led_operations,让它能支持更多 GPIO。

注意:作为练习,led_operations 结构体不需要写得很完善,不需要支持所有 GPIO,你可以只支持若干个 GPIO 即可。