https://www.bilibili.com/video/BV14f4y1Q7ti

2 hello 驱动

  • 可以参考 drivers\char\misc.c 这个是非常经典的字符设备驱动程序。
  • printk 打印信息,可以通过 dmesg 来查看。

9 总线设备驱动模型

  • platform_device 用于存放设备的资源相关. struct resourceinclude\linux\ioport.h 中, struct resource 给出明确的定义,需要按照这种形式去写。
    struct resource {
    resource_size_t start;
    resource_size_t end;
    const char *name;
    unsigned long flags;
    unsigned long desc;
    struct resource *parent, *sibling, *child;
    };
  • 资源也可以放在 platform_device -> struct device dev -> void *platform_data 定义在 include\linux\device.h 中,这个 platform_data 那就是根据需要完全自己定义就行。
  • platform_driver 用于存放操作相关的函数。
  • bus 维持两个链表,一个是 device 链表,一个是 driver 链表,两个链表中有任何一个新增,就会扫描对方链表,查看是否有匹配,probe.
  • drivers\base\platform.c 中的 struct bus_type platform_bus_type 定义了匹配用到 platform_match
    struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
    };
  • 具体的匹配,可以看 drivers\base\platform.c 中的 static int platform_match(struct device *dev, struct device_driver *drv) 可以看到匹配的优先级。
    /* When driver_override is set, only bind to the matching driver */
    if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);
    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
        return 1;
    /* Then try ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;
    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;
    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);

11 设备树处理与使用

  • 根节点 of_root 可以通过 device_node 中的 parent child 来遍历所有节点。
  • 子节点能否转换为 platform_device, 取决于父节点的 comptible 是否是 "simple-bus","simple-mfd","isa","arm,amba-bus", 而且子节点也要有 comptible
  • 当前设备树 /sys/firmware/devicetree/base, 搜索 ls -ld *100ask*
  • cd 打开设备节点文件夹,里面有 comptible, name, pin,可以用 cat, hexdump 来读取数据
  • 打开platform_device对应的文件夹 /sys/devices/platform 或者 /sys/devices/soc0 , 然后 ls -ld *100ask* 也可以找到对应的设备节点。文件夹下面的 of_node 指向设备树里面的节点。
  • 加载驱动之后,自动 probe了,在platform_device对应的文件夹下,会多出来一个 driver 文件夹,指向了 /sys/devices/soc0/100ask_led@0/driver 这个对应的驱动文件夹。并且 uevent 这个文件里面,也多了 driver 这一行。
  • 打开 驱动文件夹 /sys/devices/soc0/100ask_led@0/driver,可以看到下面 100ask_led@0, 100ask_led@1, 分别指向了设备文件夹下面的设备节点。module 指向了驱动模块文件夹 /sys/devices/soc0/100ask_led@0/driver/module

16 2gpio子系统

  • #gpio-cells = <2>; 值得是,除了 gpioX 这个以外,还要两个数值。
  • gpios 或者 xxx-gpios 可以指定多个引脚。
    led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */ 
      <&gpio 16 GPIO_ACTIVE_HIGH>, /* green */ 
      <&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
  • 只有一个引脚的时候,使用 gpiod_get。多个引脚取一个的时候,使用 gpiod_get_index

16_4 上机

  • 设备树中新增 pinctrl 相关
    &iomuxc_snvs { 
    …… 
        myled_for_gpio_subsys: myled_for_gpio_subsys{  
            fsl,pins = < 
                MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0 
            >; 
        }; 
  • 设备树中新增节点
        myled { 
            compatible = "100ask,leddrv"; 
            pinctrl-names = "default"; 
            pinctrl-0 = <&myled_for_gpio_subsys>; 
            led-gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; 
        }; 
  • 检查这个引脚有没有其他地方使用,可以通过 MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 来查找,找到使用的设备节点后,在里面增加 status = "disabled"; 禁用。

18_2 中断处理的演进

中断下半部开始执行的时候,开了中断. 如果在下半部被中断了,那么第二次的中断上半部执行完后,后发现 preempt_count 不为0,就不执行下半部了。 这样就会回到第一次被中断的下半部,继续执行完成下半部的处理。

这就是上下多对一。 并且下半部会处理所有的软件中断。

18_3 中断系统数据结构

  • GIC, GPIO 这些都是芯片内部的,BSP 可以直接处理,比如说,发生了一个中断,那就先去看看 GIC 里面,是哪个模块发生了中断? 如果是 GPIO 模块中断了,再去 GPIO 这边处理下。
  • 实际的操作流程是,中断发生之后, GIC 的 irq_desc.handle_irq 内部分辨是哪个模块,然后自动调用 GPIO 的 irq_desc.handle_irq
  • gpio 会挨个调用中断引脚的处理函数。 也就是通过 irq_desc.action 链表,挨个调用链表中的 irq_action.handler
  • irq_action.handler 上半部结束后会调用 irq_action.thread_fn下半部
  • irq_action.thread_fn 涉及到的线程是 irq_action.thread
  • irq_actionrequest_irq() 注册的。
  • request_irq() 用到的中断号并不是从设备树中直接拿来用的。需要 irq_domain.ops.xlate 先从设备树进行解析,然后 irq_domain.ops.map 对设备树中的硬件中断和 request_irq() 用到的中断号进行映射。 映射关系保存在 irq_domain.linear_revmap[] 中。
  • irq_chip 里面的 irq_enable() irq_disable() 会被自动调用。

实际上,GIC 中断之后,是需要先通过 irq_desc.irq_data.domain.linear_revmap[] 来获取到中断号对应的硬件中断号,然后才能去调用 GPIO 的 irq_desc.handle_irq

18_4 代码中获得中断

  • 设备树中,一定要看清楚 interrupt_parent 是谁,并且对应的 #interrupt-cells 是多少,不同层级可能这个数目是不一样多的。
  • 如果本层没有指明 interrupt_parent ,那可能是继承了上一层的,去上一层查看。

19_9 mmap 基础

cat /proc/pid/maps 可以查看进程使用的内存情况。

一般不适用 cache

  • register
  • framebuffer
  • dma

一般不适用于 写缓冲,可能会有多个字节合并为 word 的可能。

  • register

19_10 mmap 编程

mmap 函 数 MAP_SHAREDMAP_PRIVATE 参 数 。 MAP_PRIVATE 会导致,当对映射的 buf 进行写入,那么内核会自动复制一块内存,并让 buf 指向它,只有这个 buf 才有新的数据。就算是驱动程序也只是旧内存。非常适合需要临时修改文件内容或创建文件副本的场景。

发表评论