首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

drivers/input/ 源码目录与驱动索引

drivers/input/ 源码目录与驱动索引

1. Core 和 handlers

文件 作用
input.c Core、注册匹配、事件状态与分发
input-compat.c 32-bit compat ioctl数据转换
input-mt.c MT slot和tracking辅助
input-poller.c 通用轮询
ff-core.c / ff-memless.c 力反馈
touchscreen.c DT touchscreen属性转换
evdev.c /dev/input/eventN
mousedev.c mouseX/mice兼容接口
joydev.c jsX兼容接口
input-leds.c LED class桥接
sparse-keymap.c 稀疏扫描码映射
matrix-keymap.c 行列矩阵 keymap

2. 子目录

目录 内容
keyboard/ GPIO、ADC、matrix、PMIC等键盘
mouse/ PS/2、I2C touchpad、trackpoint
touchscreen/ I2C/SPI/USB/ADC触摸,含大量厂商树代码
joystick/ gamepad/steering/FF
tablet/ 非 HID tablet
misc/ pwrkey、rotary、uinput、switch等
serio/ PS/2/serial input transport
gameport/ legacy analog gameport
rmi4/ Synaptics RMI4
remotectl/ Rockchip红外遥控
sensors/ Rockchip厂商 sensor stack

3. RK3588 主配置

明确启用:

1
2
3
4
5
6
7
8
9
10
11
INPUT_EVDEV
KEYBOARD_ADC
KEYBOARD_GPIO
KEYBOARD_GPIO_POLLED
INPUT_TOUCHSCREEN
TOUCHSCREEN_GT1X/HYN/ELAN/...
ROCKCHIP_REMOTECTL
ROCKCHIP_REMOTECTL_PWM
INPUT_MISC
INPUT_UINPUT
INPUT_RK805_PWRKEY

明确禁用:

1
2
3
KEYBOARD_ATKBD
MOUSE_PS2
SERIO

这符合无传统 PS/2、以 DT/I2C/USB为主的 ARM板。

4. 厂商触摸代码

本目录包含 Goodix GT1X/GT9xx、FocalTech、Hynitron、Himax、Parade、Chipone等大型私有驱动树。源码存在不代表编译;必须核对其子目录 Makefile/Kconfig和最终配置。

5. UAPI

  • include/uapi/linux/input.h
  • include/uapi/linux/input-event-codes.h
  • include/uapi/linux/uinput.h
  • include/uapi/linux/joystick.h

内核 driver API主要在 include/linux/input.hinput/mt.hinput/touchscreen.h

6. 关键文档

1
2
3
4
5
6
Documentation/input/input.rst
Documentation/input/event-codes.rst
Documentation/input/multi-touch-protocol.rst
Documentation/input/event-interface.rst
Documentation/input/uinput.rst
Documentation/devicetree/bindings/input/

7. 注意

目录约 815 个文件,其中许多面向其它架构或厂商产品。RK3588相关性必须由 compatible、MFD cell、Kconfig和实际硬件共同证明。

rk3588/kernel-6.1 IPC 架构与实现原理分析

rk3588/kernel-6.1 IPC 架构与实现原理分析

1. IPC 子系统用途概览

ipc/ 目录实现 Linux 内核中的进程间通信核心能力,主要包括:

  • System V IPC
    • 消息队列(msg.c
    • 信号量(sem.c
    • 共享内存(shm.c
  • POSIX Message Queue
    • 消息队列文件系统与系统调用(mqueue.c
  • IPC 命名空间
    • 隔离不同容器/命名空间的 IPC 对象(namespace.c
  • 通用框架
    • IPC ID 分配、权限、生命周期与并发模型(util.c/.h

简化理解:
ipc/ 是“对象管理层 + 同步/消息语义层 + namespace 隔离层”的组合。


2. 编译与配置入口

2.1 Kconfig

位于 init/Kconfig 的关键项:

  • SYSVIPC:启用 SysV IPC
  • POSIX_MQUEUE:启用 POSIX 消息队列(依赖 NET
  • IPC_NS:启用 IPC namespace(依赖 SYSVIPC || POSIX_MQUEUE
  • SYSVIPC_SYSCTLPOSIX_MQUEUE_SYSCTL:对应 sysctl 配置接口

2.2 Makefile

ipc/Makefile 体现模块分层:

  • obj-$(CONFIG_SYSVIPC)util.o msgutil.o msg.o sem.o shm.o syscall.o
  • obj-$(CONFIG_POSIX_MQUEUE)mqueue.o msgutil.o
  • obj-$(CONFIG_IPC_NS)namespace.o
  • obj-$(CONFIG_*_SYSCTL):sysctl 参数支持

3. 总体架构分层

可分为四层:

  1. 系统调用入口层

    • 现代路径:msgget/msgsnd/msgrcvsemget/semtimedop/semctlshmget/shmat/shmdt/shmctlmq_*
    • 兼容路径:ipc/syscall.csys_ipc 复用旧多路复用接口
  2. 通用对象管理层(SysV 共用)

    • ipc_ids + idr + rhashtable 管理对象 ID 与 key 查找
    • ipc_addid/ipc_rmid/ipcget/ipcperms 提供统一创建/查找/鉴权逻辑
    • ipc_ops{getnew, associate, more_checks} 抽象 msg/sem/shm 的差异
  3. 机制实现层

    • msg.c:消息队列收发/阻塞唤醒
    • sem.c:信号量集合、原子 semop、undo 机制
    • shm.c:共享内存段创建、映射、拆映射
    • mqueue.c:POSIX mqueue 的 VFS 化实现
  4. 隔离与可观测层

    • namespace.c:IPC namespace 创建、切换、销毁
    • ipc_sysctl.c/mq_sysctl.c:运行时参数调优
    • /proc/sysvipc/*:对象状态观测

4. 通用实现原理(SysV IPC 核心骨架)

4.1 对象标识设计

util.h 中 ID 由 index + sequence 组成:

  • ipcid_to_idx() 取索引
  • ipcid_to_seqx() 校验序号
  • 防止“ID 被回收后复用”导致陈旧句柄误命中

4.2 ipcget() 统一建/取流程

msgget/semget/shmget 都走类似流程:

  1. 构造 ipc_opsgetnew/associate/more_checks
  2. 通过 key 在 ipc_ids 查找对象
  3. 找不到且允许创建时,调用 getnew() 分配对象
  4. 执行 LSM 安全检查与权限校验

这让三类 SysV IPC 在入口逻辑一致,语义差异下沉到各自文件。

4.3 并发与锁模型

util.c 顶部注释给出关键模型:

  • ids->rwsem:保护 IPC 集合(创建/删除/遍历)
  • kern_ipc_perm.lock:保护单个 IPC 对象状态
  • RCU:支持 lockless 查找和并发删除期间安全访问

总体思想:集合级 rwsem + 对象级 spinlock + RCU 生命周期


5. 三大 SysV IPC 机制

5.1 消息队列(msg.c

用途

  • 进程间按消息粒度交换数据,支持类型匹配(msgtyp)。

核心调用链

  • 创建:msgget -> ksys_msgget -> ipcget(..., newque, ...)
  • 发送:msgsnd -> ksys_msgsnd -> do_msgsnd
  • 接收:msgrcv -> ksys_msgrcv -> do_msgrcv
  • 控制:msgctlIPC_SET/IPC_RMID/IPC_STAT 等)

实现要点

  • 队列满时,发送方进入等待队列;IPC_NOWAIT 下直接 -EAGAIN
  • 接收方按 msgtyp 策略筛选(SEARCH_ANY/EQUAL/LESSEQUAL/...
  • 支持“管线化发送/接收”(pipelined_send),减少中间排队开销
  • 删除队列时 freeque() 负责唤醒阻塞 sender/receiver 并回收消息

5.2 信号量(sem.c

用途

  • 对共享资源做计数同步与互斥控制。

核心调用链

  • 创建:semget -> ksys_semget -> ipcget(..., newary, ...)
  • 操作:semop/semtimedop -> do_semtimedop -> __do_semtimedop
  • 控制:semctl -> semctl_main

实现要点

  • newary() 创建的是 信号量集合nsems 个)
  • perform_atomic_semop() 保证一个 sembuf[] 批操作的原子语义
  • SEM_UNDO 记录进程级 undo,进程异常退出可回滚影响
  • 不可满足操作条件时可阻塞等待,超时/信号打断返回对应错误

5.3 共享内存(shm.c

用途

  • 多进程共享一段虚拟内存,适合大数据低拷贝通信。

核心调用链

  • 创建:shmget -> ksys_shmget -> ipcget(..., newseg, ...)
  • 挂接:shmat -> do_shmat
  • 脱离:shmdt -> ksys_shmdt
  • 控制:shmctl -> shmctl_down

实现要点

  • newseg() 基于 shmemhugetlb 创建后端文件对象
  • do_shmat() 完成权限检查、地址对齐策略、do_mmap 映射
  • shmdt 会处理被 mremap/munmap 改写后的复杂 VMA 拆卸场景
  • 通过 shm_nattchshm_may_destroy 管理段生命周期

6. POSIX 消息队列(mqueue.c

6.1 用途与模型

  • 提供 mq_open/mq_timedsend/mq_timedreceive/mq_notify 等 POSIX API。
  • 与 SysV msg 的差异:VFS 对象化 + 优先级消息 + 命名队列路径语义

6.2 核心调用链

  • mq_open -> do_mq_open(在当前 IPC namespace 的 mq_mnt 下查找/创建)
  • mq_timedsend -> do_mq_timedsend
  • mq_timedreceive -> do_mq_timedreceive

6.3 实现要点

  • 队列底层是 mqueue 文件系统 inode(mqueue_get_inode
  • 发送/接收使用自旋锁 + 等待队列管理并发
  • 支持 sender/receiver“直接管线化交付”,避免总是入队再出队
  • 定时版本通过绝对超时参数实现可中断等待

7. IPC Namespace 实现原理(namespace.c

7.1 创建

  • copy_ipcs(CLONE_NEWIPC) -> create_ipc_ns()
  • 初始化 mq/msg/sem/shm 各子系统与 sysctl
  • 将对象隔离到新的 ipc_namespace

7.2 销毁

  • put_ipc_ns() 引用计数归零后异步释放
  • free_ipc_ns() 顺序清理 sem/msg/shm/mqueue 对象与 sysctl

7.3 意义

  • 不同容器拥有独立 IPC 视图(ID 空间、对象集合、限制参数互不干扰)。

8. 安全与权限控制

IPC 路径中有三层权限约束:

  • 经典权限位检查ipcperms()
  • 能力检查:如 CAP_SYS_RESOURCECAP_SYS_ADMIN
  • LSM Hooksecurity_msg_* / security_sem_* / security_shm_*

因此 IPC 访问不仅受 uid/gid 控制,也受安全模块策略控制。


9. 调优与排障建议

  1. 对象创建失败(ENOSPC/ENOMEM)

    • 检查 msgmni/msgmnb/msgmaxsemmni/semmnsshm_ctl* 限制
    • 检查 namespace 下是否资源已耗尽
  2. 阻塞超时或吞吐低

    • 消息队列是否频繁满队列(sender 阻塞)
    • semop 是否存在热点 semaphore 争用
    • shm 方案是否更适合高吞吐场景
  3. 容器中 IPC 行为异常

    • 确认是否在预期的 IPC namespace
    • 检查容器 runtime 的 --ipc 模式与挂载的 mqueuefs
  4. 兼容接口问题

    • 关注 sys_ipc 老接口与 compat 路径参数差异
    • 注意 IPC_OLD/IPC_64 结构体转换逻辑

10. 一句话总结

ipc/ 的本质是:
util.c 提供统一对象管理与并发骨架,分别实现 msg/sem/shm 三类 SysV IPC 与 mqueue POSIX IPC,并通过 IPC namespace 提供容器级隔离。

rk3588/kernel-6.1 中断流程与上下半部机制分析

rk3588/kernel-6.1 中断流程与上下半部机制分析

1. 中断处理整体框架

Linux 内核中断处理通常分为:

1
2
3
4
5
6
7
8
9
10
11
硬件中断
-> CPU 异常入口
-> 架构层 IRQ handler
-> 中断控制器 GIC handler
-> generic IRQ core
-> ISR / top half
-> bottom half
-> softirq
-> tasklet
-> workqueue
-> threaded IRQ

用户提到的经典模型:

1
硬件中断 -> ISR -> bottom half

在 Linux 6.1 中可以展开为:

1
2
3
4
5
6
7
8
9
10
硬件 IRQ
-> arm64 exception vector
-> el1h_64_irq_handler()
-> handle_arch_irq()
-> gic_handle_irq()
-> generic_handle_domain_irq()
-> irq_desc->handle_irq()
-> handle_irq_event()
-> irqaction->handler()
-> softirq/tasklet/workqueue/thread_fn

核心目录:

  • arch/arm64/kernel/entry-common.c
  • arch/arm64/kernel/irq.c
  • drivers/irqchip/irq-gic-v3.c
  • kernel/irq/
  • kernel/softirq.c
  • kernel/workqueue.c
  • include/linux/interrupt.h
  • include/linux/irqdesc.h
  • include/linux/irq.h

2. 中断核心文件作用

kernel/irq/Makefile 中核心模块:

1
2
3
4
5
6
7
8
9
10
11
irqdesc.o      IRQ 描述符分配和管理
handle.o 通用 IRQ 分发,调用驱动 ISR
manage.o request_irq/request_threaded_irq/free_irq
chip.o 电平/边沿/fasteoi 等 flow handler
irqdomain.o hwirq 到 Linux virq 映射
msi.o MSI/MSI-X 中断
ipi.o SMP IPI
affinity.o IRQ affinity
proc.o /proc/interrupts 和 /proc/irq
pm.o suspend/resume 中断管理
spurious.o 异常/伪中断检测

重要结构:

  • struct irq_desc:一个 Linux IRQ 的核心描述符;
  • struct irq_data:IRQ 与 irqchip/domain 的底层数据;
  • struct irq_chip:中断控制器操作;
  • struct irqaction:驱动注册的中断处理函数;
  • struct irq_domain:硬件中断号 hwirq 到 Linux irq 的映射域。

3. ARM64 硬件中断入口

RK3588 是 ARM64 SoC,普通外设中断会从 ARM64 异常向量进入。

相关文件:

  • arch/arm64/kernel/entry-common.c
  • arch/arm64/kernel/irq.c

关键入口:

1
2
3
4
5
6
el1h_64_irq_handler()
-> el1_interrupt()
-> __el1_irq()
-> irq_enter_rcu()
-> do_interrupt_handler(regs, handle_arch_irq)
-> irq_exit_rcu()

其中:

  • irq_enter_rcu():进入 hardirq 上下文,更新 preempt count、RCU、统计;
  • do_interrupt_handler():必要时切换到 IRQ stack 后调用 handler;
  • irq_exit_rcu():退出 hardirq,并在合适时机处理 pending softirq;
  • handle_arch_irq:架构 root IRQ handler,由具体 irqchip 初始化时设置。

arch/arm64/kernel/irq.c 中:

1
2
3
4
init_IRQ()
-> init_irq_stacks()
-> init_irq_scs()
-> irqchip_init()

irqchip_init() 会根据设备树初始化 GICv3 等中断控制器。


4. GICv3 中断控制器入口

RK3588 ARM64 平台通常使用 GICv3。

相关文件:

  • drivers/irqchip/irq-gic-v3.c

初始化中:

1
2
3
4
5
gic_init_bases()
-> set_handle_irq(gic_handle_irq)
-> gic_dist_init()
-> gic_cpu_init()
-> gic_smp_init()

中断到来后:

1
2
3
4
5
gic_handle_irq()
-> gic_read_iar()
-> __gic_handle_irq()
-> gic_complete_ack()
-> generic_handle_domain_irq(gic_data.domain, irqnr)

其中:

  • GIC IAR 寄存器给出硬件中断号 irqnr
  • generic_handle_domain_irq() 通过 irq_domain 把 hwirq 转成 Linux irq;
  • 转换后进入 generic IRQ core。

这一步完成了:

1
GIC hardware interrupt ID -> Linux virtual IRQ number

5. irq_domain:硬件中断号到 Linux IRQ

相关文件:

  • kernel/irq/irqdomain.c
  • include/linux/irqdomain.h

为什么需要 irq_domain?

不同中断控制器的硬件中断号可能重复,层级也不同。Linux 需要统一的逻辑 IRQ 编号给驱动使用。

典型映射:

1
2
3
4
5
设备树 interrupt specifier
-> irq_domain translate
-> hwirq
-> virq / Linux irq
-> irq_desc

驱动中常见:

1
2
irq = platform_get_irq(pdev, 0);
devm_request_irq(dev, irq, handler, flags, name, data);

platform_get_irq() 背后会通过 OF/ACPI 和 irq_domain 得到 Linux irq。


6. irq_desc:每个中断的核心状态

include/linux/irqdesc.hstruct irq_desc 是 generic IRQ layer 的核心。

重要字段:

1
2
3
4
5
6
7
8
9
10
irq_data             irqchip/domain 底层数据
kstat_irqs 每 CPU 中断统计
handle_irq 高层 flow handler
action 驱动注册的 irqaction 链表
status_use_accessors IRQ 状态
depth disable_irq 嵌套深度
lock 保护 desc 状态
threads_oneshot threaded IRQ oneshot 状态
threads_active 正在运行的 IRQ thread 数
wait_for_threads synchronize_irq 等待队列

中断核心分发时会找到对应 irq_desc

1
2
3
4
generic_handle_irq()
-> irq_to_desc()
-> generic_handle_irq_desc(desc)
-> desc->handle_irq(desc)

desc->handle_irq 不是驱动 ISR,而是 flow handler,例如:

  • handle_level_irq
  • handle_edge_irq
  • handle_fasteoi_irq
  • handle_simple_irq
  • handle_percpu_irq

flow handler 根据中断类型负责 mask/ack/eoi/replay,然后再调用驱动 ISR。


7. irqaction:驱动注册的 ISR

include/linux/interrupt.h 中:

1
2
3
4
5
6
7
8
9
10
struct irqaction {
irq_handler_t handler;
void *dev_id;
struct irqaction *next;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned int irq;
unsigned int flags;
const char *name;
};

驱动通过以下接口注册:

1
2
3
4
request_irq(irq, handler, flags, name, dev_id);
devm_request_irq(dev, irq, handler, flags, name, dev_id);
request_threaded_irq(irq, handler, thread_fn, flags, name, dev_id);
devm_request_threaded_irq(dev, irq, handler, thread_fn, flags, name, dev_id);

request_irq() 实际只是:

1
request_threaded_irq(irq, handler, NULL, flags, name, dev)

因此普通 IRQ 和 threaded IRQ 共用注册框架。


8. request_irq 注册流程

相关文件:

  • kernel/irq/manage.c

注册流程:

1
2
3
4
5
6
7
8
request_irq()
-> request_threaded_irq()
-> irq_to_desc()
-> kzalloc(struct irqaction)
-> action->handler = handler
-> action->thread_fn = thread_fn
-> action->flags = irqflags
-> __setup_irq()

request_threaded_irq() 关键点:

  • IRQF_SHARED 必须提供非空 dev_id
  • 如果 handler == NULLthread_fn != NULL,内核使用默认 primary handler;
  • 默认 primary handler 返回 IRQ_WAKE_THREAD
  • __setup_irq() 会把 irqaction 挂到 irq_desc->action
  • 如果有 thread_fn,会创建 irq/%d-%s 内核线程。

驱动必须保证:

  • 注册中断前硬件状态已经初始化好;
  • ISR 能识别是否是自己的中断;
  • level 中断必须清除设备中断源;
  • shared IRQ 必须使用唯一 dev_id

9. 硬中断分发流程

以 level IRQ 为例,kernel/irq/chip.c

1
2
3
4
5
6
7
8
handle_level_irq(desc)
-> raw_spin_lock(desc->lock)
-> mask_ack_irq(desc)
-> 检查是否可运行
-> kstat_incr_irqs_this_cpu(desc)
-> handle_irq_event(desc)
-> cond_unmask_irq(desc)
-> raw_spin_unlock(desc->lock)

以 fasteoi IRQ 为例:

1
2
3
4
5
handle_fasteoi_irq(desc)
-> 检查 action/disabled
-> kstat_incr_irqs_this_cpu(desc)
-> handle_irq_event(desc)
-> irq_eoi()

flow handler 的职责是:

  • 根据中断类型执行 mask/ack/eoi;
  • 防止重入和禁用状态下误处理;
  • 更新统计;
  • 调用 handle_irq_event()
  • 结束时恢复中断线状态。

10. ISR / top half

真正调用驱动 ISR 的路径在 kernel/irq/handle.c

1
2
3
4
5
handle_irq_event()
-> handle_irq_event_percpu()
-> __handle_irq_event_percpu()
-> for_each_action_of_desc(desc, action)
-> action->handler(irq, action->dev_id)

驱动 ISR 返回值:

1
2
3
IRQ_NONE        不是我的中断,常见于 shared IRQ
IRQ_HANDLED 已处理完成
IRQ_WAKE_THREAD 唤醒 threaded IRQ 下半部

top half 特点:

  • 运行在 hardirq context;
  • 当前 CPU 本地中断通常关闭;
  • 不能睡眠;
  • 不能做慢速 I2C/SPI/MDIO 等可能睡眠操作;
  • 不能持有锁后调用可能睡眠函数;
  • 应尽快确认中断源、清中断、保存必要状态;
  • 把耗时工作推给 bottom half。

典型 ISR:

1
2
3
4
5
6
7
8
9
10
11
12
13
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
struct my_dev *d = dev_id;
u32 status = readl(d->regs + IRQ_STATUS);

if (!status)
return IRQ_NONE;

writel(status, d->regs + IRQ_CLEAR);

tasklet_schedule(&d->tasklet);
return IRQ_HANDLED;
}

11. threaded IRQ

threaded IRQ 是 Linux 推荐的下半部形式之一,尤其适合驱动里需要睡眠的中断处理。

注册:

1
2
3
4
5
ret = devm_request_threaded_irq(dev, irq,
top_handler,
thread_handler,
IRQF_ONESHOT,
"mydev", data);

流程:

1
2
3
4
5
6
7
硬件中断
-> top_handler()
-> return IRQ_WAKE_THREAD
-> __irq_wake_thread()
-> wake_up_process(action->thread)
-> irq_thread()
-> thread_handler()

kernel/irq/manage.c 中:

  • setup_irq_thread() 创建 irq/%d-%s 线程;
  • irq_thread() 是 IRQ thread 主循环;
  • irq_wait_for_interrupt() 等待 IRQTF_RUNTHREAD
  • irq_thread_fn() 调用 action->thread_fn()
  • irq_finalize_oneshot() 处理 IRQF_ONESHOT 的重新 unmask。

IRQF_ONESHOT 含义:

  • top half 返回 IRQ_WAKE_THREAD 后,中断线保持 masked;
  • thread_fn 执行完成后再 unmask;
  • 避免 level 中断在下半部未处理完时反复触发。

threaded IRQ 可以睡眠,因此适合:

  • 需要访问 I2C/SPI 慢速外设;
  • 需要 mutex;
  • 需要较长处理流程;
  • 需要与进程上下文 API 交互。

12. bottom half 机制概览

下半部用于把 ISR 中不适合执行的工作延后。

Linux 6.1 中常见 bottom half:

机制 上下文 能否睡眠 适合场景
softirq softirq context 不能睡眠 网络收发、定时器、RCU
tasklet softirq context 不能睡眠 老式驱动延后处理
workqueue process context 可以睡眠 需要慢速操作、复杂处理
threaded IRQ kernel thread 可以睡眠 中断专属下半部

选择建议:

  • 只需短小、原子、低延迟:softirq/tasklet;
  • 需要睡眠或访问慢速总线:workqueue/threaded IRQ;
  • 新驱动优先考虑 threaded IRQ 或 workqueue;
  • tasklet 仍存在,但新代码应谨慎使用。

13. softirq 机制

相关文件:

  • kernel/softirq.c
  • include/linux/interrupt.h

softirq 是静态注册的高性能下半部机制。

注册:

1
open_softirq(nr, action);

触发:

1
2
raise_softirq(nr);
raise_softirq_irqoff(nr);

执行路径:

1
2
3
4
5
6
7
8
9
10
raise_softirq()
-> __raise_softirq_irqoff()
-> 标记 pending bit

irq_exit_rcu()
-> 如果不在 interrupt 且有 pending softirq
-> invoke_softirq()
-> __do_softirq()
-> handle_softirqs()
-> softirq_vec[nr].action()

如果 softirq 过多或当前不适合立即执行,会唤醒:

1
ksoftirqd/N

softirq.c 中:

1
2
3
DEFINE_PER_CPU(struct task_struct *, ksoftirqd)
spawn_ksoftirqd()
run_ksoftirqd()

常见 softirq 类型:

  • HI_SOFTIRQ
  • TIMER_SOFTIRQ
  • NET_TX_SOFTIRQ
  • NET_RX_SOFTIRQ
  • BLOCK_SOFTIRQ
  • IRQ_POLL_SOFTIRQ
  • TASKLET_SOFTIRQ
  • SCHED_SOFTIRQ
  • HRTIMER_SOFTIRQ
  • RCU_SOFTIRQ

14. tasklet 机制

tasklet 基于 softirq 实现。

相关文件:

  • kernel/softirq.c
  • include/linux/interrupt.h

初始化:

1
2
tasklet_init(&t, func, data);
tasklet_setup(&t, callback);

调度:

1
2
tasklet_schedule(&t);
tasklet_hi_schedule(&t);

内部流程:

1
2
3
4
5
6
7
8
9
10
tasklet_schedule()
-> test_and_set_bit(TASKLET_STATE_SCHED)
-> __tasklet_schedule()
-> 加入当前 CPU tasklet_vec
-> raise_softirq_irqoff(TASKLET_SOFTIRQ)

__do_softirq()
-> tasklet_action()
-> tasklet_action_common()
-> t->func(t->data) 或 t->callback(t)

tasklet 特点:

  • 运行在 softirq context;
  • 不能睡眠;
  • 同一个 tasklet 同一时间不会在多个 CPU 并发运行;
  • 不适合长时间处理;
  • 驱动卸载时需要 tasklet_kill()
  • 新驱动不建议大量使用 tasklet,优先考虑 threaded IRQ/workqueue。

典型使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void my_tasklet_func(unsigned long data)
{
struct my_dev *d = (struct my_dev *)data;
/* 处理 ISR 保存的状态,不能睡眠 */
}

tasklet_init(&d->tasklet, my_tasklet_func, (unsigned long)d);

static irqreturn_t my_irq(int irq, void *dev_id)
{
struct my_dev *d = dev_id;
tasklet_schedule(&d->tasklet);
return IRQ_HANDLED;
}

15. workqueue 机制

workqueue 是进程上下文下半部,适合需要睡眠的工作。

相关文件:

  • kernel/workqueue.c
  • include/linux/workqueue.h

初始化:

1
2
INIT_WORK(&d->work, my_work_func);
INIT_DELAYED_WORK(&d->dwork, my_delayed_work_func);

调度:

1
2
3
schedule_work(&d->work);
queue_work(system_wq, &d->work);
queue_delayed_work(system_wq, &d->dwork, delay);

执行路径:

1
2
3
4
5
6
7
8
queue_work()
-> queue_work_on()
-> __queue_work()
-> insert_work()
-> wake_up_worker()
-> worker_thread()
-> process_one_work()
-> work->func(work)

系统 workqueue:

1
2
3
4
5
6
7
system_wq                         events
system_highpri_wq events_highpri
system_long_wq events_long
system_unbound_wq events_unbound
system_freezable_wq events_freezable
system_power_efficient_wq events_power_efficient
system_freezable_power_efficient_wq

workqueue 特点:

  • 运行在 kworker 进程上下文;
  • 可以睡眠;
  • 可以拿 mutex;
  • 可以访问 I2C/SPI 等可能睡眠接口;
  • 延迟较 tasklet/softirq 高;
  • 需要在 remove/shutdown 中 cancel_work_sync()flush_work()

典型使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void my_work_func(struct work_struct *work)
{
struct my_dev *d = container_of(work, struct my_dev, work);
/* 可以睡眠,可以做耗时处理 */
}

INIT_WORK(&d->work, my_work_func);

static irqreturn_t my_irq(int irq, void *dev_id)
{
struct my_dev *d = dev_id;
queue_work(system_wq, &d->work);
return IRQ_HANDLED;
}

16. top half 与 bottom half 对比

16.1 top half

top half 即 ISR:

1
irqaction->handler()

适合做:

  • 读取中断状态寄存器;
  • 判断是否为本设备中断;
  • ack/clear 设备中断;
  • 保存少量状态;
  • 唤醒 thread/tasklet/workqueue;
  • 返回 IRQ_HANDLEDIRQ_WAKE_THREAD

不适合做:

  • 睡眠;
  • 大量数据拷贝;
  • 长时间循环;
  • 复杂协议处理;
  • I2C/SPI 同步访问;
  • 等待锁或等待硬件完成。

16.2 bottom half

bottom half 适合做:

  • 数据包处理;
  • 协议解析;
  • 唤醒等待队列;
  • 调度下一次 DMA;
  • 访问慢速总线;
  • 上报 input/net/block 子系统;
  • 较复杂状态机处理。

不同下半部选择:

1
2
3
不能睡眠 + 低延迟:tasklet/softirq
可以睡眠 + 驱动私有处理:workqueue
可以睡眠 + 中断专属处理:threaded IRQ

17. 典型驱动中断设计

17.1 tasklet 模式

1
2
3
4
5
6
7
8
9
ISR:
读状态
清中断
保存状态
tasklet_schedule()
return IRQ_HANDLED

tasklet:
原子上下文处理后续数据

适合:

  • 处理很短;
  • 不需要睡眠;
  • 对延迟敏感。

17.2 workqueue 模式

1
2
3
4
5
6
7
8
9
10
ISR:
读状态
mask/clear 中断
queue_work()
return IRQ_HANDLED

work:
慢速处理
访问 I2C/SPI
恢复/打开中断

适合:

  • 需要睡眠;
  • 需要复杂状态机;
  • 延迟要求不是极端严格。

17.3 threaded IRQ 模式

1
2
3
4
5
6
7
8
9
top handler:
判断中断源
清/屏蔽中断
return IRQ_WAKE_THREAD

thread_fn:
完成耗时处理
必要时重新使能设备中断
return IRQ_HANDLED

适合:

  • 中断处理天然分成“快速确认 + 慢速处理”;
  • 需要睡眠;
  • 希望一个 IRQ 对应一个内核线程;
  • level 中断可配合 IRQF_ONESHOT

18. 中断上下文限制

hardirq 和 softirq/tasklet 中:

  • 不能调用会睡眠的函数;
  • 不能使用普通 mutex;
  • 不能执行可能阻塞的内存分配;
  • 内存分配应使用 GFP_ATOMIC
  • 持锁时间必须短;
  • 不能直接访问用户空间;
  • 不能做长时间 busy loop。

workqueue/threaded IRQ 中:

  • 可以睡眠;
  • 可以使用 mutex;
  • 可以使用 GFP_KERNEL
  • 仍需考虑和 remove/suspend 并发;
  • 仍需避免长时间占用 worker。

19. 调试与观察

常用文件:

1
2
3
4
5
/proc/interrupts
/proc/irq/<irq>/
/sys/kernel/debug/irq/
/sys/kernel/debug/tracing/events/irq/
/sys/kernel/debug/tracing/events/workqueue/

常用命令:

1
2
3
4
5
cat /proc/interrupts
cat /proc/softirqs
ps -e | grep irq
ps -e | grep ksoftirqd
ps -e | grep kworker

常用 ftrace 事件:

1
2
3
4
5
6
7
8
irq:irq_handler_entry
irq:irq_handler_exit
irq:softirq_raise
irq:softirq_entry
irq:softirq_exit
workqueue:workqueue_queue_work
workqueue:workqueue_execute_start
workqueue:workqueue_execute_end

常见问题:

  1. 中断风暴

    • 没有正确清设备中断源;
    • level IRQ 没有让硬件线回到 inactive;
    • shared IRQ handler 错误返回 IRQ_HANDLED
  2. ISR 中睡眠

    • hardirq 中调用 mutex/I2C/SPI 同步接口;
    • 应改为 threaded IRQ 或 workqueue。
  3. remove 后崩溃

    • free_irq()
    • tasklet_kill()
    • cancel_work_sync()
    • threaded IRQ/workqueue 仍访问已释放私有数据。
  4. 延迟过高

    • ISR 过长;
    • softirq 被 ksoftirqd 延后;
    • workqueue 被长任务占用;
    • IRQ affinity 不合理。

20. 总结

RK3588 Linux 6.1 的中断路径可以总结为:

1
2
3
4
5
6
7
硬件中断
-> ARM64 exception entry
-> GICv3 gic_handle_irq()
-> irq_domain hwirq 到 virq 映射
-> irq_desc->handle_irq()
-> handle_irq_event()
-> irqaction->handler()

上半部:

1
top half = irqaction->handler()

职责是快速确认、清中断、保存状态、唤醒下半部。

下半部:

1
2
3
4
5
bottom half =
softirq
tasklet
workqueue
threaded IRQ

其中:

  • tasklet 基于 TASKLET_SOFTIRQ
  • workqueue 基于 kworker 线程;
  • threaded IRQ 基于 irq/%d-%s 内核线程;
  • irq_exit_rcu() 是 hardirq 返回时触发 softirq 的关键点。

一句话概括:
Linux 中断上半部只做必须的快速硬件响应,下半部负责把耗时或可睡眠的工作延后到 softirq/tasklet/workqueue/threaded IRQ 中执行。

RK3588 kernel-6.1 net 网络架构与协议栈分析

RK3588 kernel-6.1 net 网络架构与协议栈分析

1. 分析范围

本文基于:

  • rk3588/kernel-6.1/net

目标:

  • 梳理 net 子系统总体分层架构
  • 解释 Linux 网络协议栈主数据路径(收包、发包、转发)
  • 给出 TCP/UDP 在 IPv4 栈中的关键调用链
  • 说明 Netfilter、路由子系统在主路径中的切入点

2. net 目录的总体分层

net/Makefile 可见网络栈是模块化分层组织:

  1. 套接字与核心层
    • socket.ccore/
    • 提供 socket 系统调用入口、sk_buff 收发调度、NAPI/软中断处理。
  2. 协议层
    • ipv4/ipv6/tcp/udp 等协议实现
    • 负责 L3/L4 协议处理、连接管理、分片重组、拥塞控制等。
  3. 策略与过滤层
    • netfilter/xfrm/
    • 提供防火墙/NAT/连接跟踪/IPsec 等路径钩子和策略。
  4. 链路与扩展层
    • packet/bridge/8021q/wireless/
    • 覆盖二层、虚拟网络、隧道、交换与无线子系统。

可概括为:socket API -> L4/L3 协议栈 -> netdevice 数据平面 -> 驱动硬件


3. 用户态到内核网络入口(net/socket.c

socket() 系统调用会进入:

  • SYSCALL_DEFINE3(socket) -> __sys_socket() -> sock_create()
  • sock_create() 最终调用 __sock_create()

__sock_create() 的关键动作:

  1. 根据 family/type/protocol 选择协议族(如 PF_INET
  2. 通过 net_families[family] 找到 net_proto_family
  3. 调用该 family 的 create()(IPv4 对应 inet_create()

因此 socket 层只是统一门面,真正协议实例化由各协议族完成。


4. IPv4 协议族装配(net/ipv4/af_inet.c

af_inet.c 中定义了 IPv4 的协议族与协议分发表:

  • inet_family_ops.create = inet_create
  • inetsw_array[] 注册 SOCK_STREAM/SOCK_DGRAM 等与 tcp_prot/udp_prot 的映射
  • inet_add_protocol()tcp_v4_rcvudp_rcv 挂到 IPv4 协议号分发表

inet_init() 初始化时会完成:

  1. proto_register(&tcp_prot) / proto_register(&udp_prot)
  2. sock_register(&inet_family_ops)
  3. 协议处理函数与传输协议绑定

这一步决定了“协议号 -> 处理函数”的分发关系。


5. 收包主路径(RX Path)

5.1 从驱动到协议栈

驱动/NAPI 把 skb 上送后,核心路径进入 net/core/dev.c

  • netif_receive_skb() -> netif_receive_skb_internal() -> __netif_receive_skb()
  • 核心分发逻辑在 __netif_receive_skb_core()

这里会做:

  • L2 类型分发(packet_type)
  • tc/ingress 等处理
  • ETH_P_IP 包送入 IPv4 入口(ip_rcv

5.2 IPv4 接收处理

net/ipv4/ip_input.c 中:

  • ip_rcv()NF_INET_PRE_ROUTING 钩子后进入 ip_rcv_finish
  • ip_rcv_finish_core() 做路由查找(ip_route_input_noref)、选项处理、early demux

然后按路由类型分流:

  1. 目的本机:ip_local_deliver()
  2. 需要转发:ip_forward()

ip_local_deliver() 会先处理分片重组(ip_defrag),再经 NF_INET_LOCAL_IN,最后进入 ip_local_deliver_finish(),按协议号分发到 tcp_v4_rcv/udp_rcv 等。


6. 发包主路径(TX Path)

6.1 L4 到 IPv4 输出

TCP 发送常见路径:

  • tcp_sendmsg() -> ip_queue_xmit() -> __ip_queue_xmit()

UDP 发送常见路径:

  • udp_sendmsg() -> 路由选择(ip_route_output_flow)-> ip_local_out()

6.2 IPv4 输出与 Netfilter

net/ipv4/ip_output.c 中:

  • ip_local_out() / __ip_local_out() 在发送前构建 IP 头与校验
  • 通过 NF_INET_LOCAL_OUT 钩子
  • ip_output() / ip_finish_output() 经过 NF_INET_POST_ROUTING
  • 最终下沉到邻居子系统与设备发送队列

6.3 进入设备发送队列

net/core/dev.c

  • __dev_queue_xmit() 选择 tx queue/qdisc
  • 调用 qdisc enqueue 或 dev_hard_start_xmit() 进入网卡驱动

这就是从协议栈到 NIC 的统一发送出口。


7. 转发路径(Forwarding)

ip_forward()net/ipv4/ip_forward.c)是三层转发核心:

  1. TTL 检查与递减
  2. MTU/分片约束检查
  3. 路由与策略校验
  4. 通过 NF_INET_FORWARD 钩子
  5. ip_forward_finish() -> dst_output() 发出

因此路由器模式下,转发路径与本机收发路径共享大量基础设施,但在 FORWARD 钩子上分叉。


8. 路由子系统在协议栈中的位置

IPv4 路由核心在 net/ipv4/route.c,关键接口:

  • 入方向:ip_route_input_noref()
  • 出方向:ip_route_output_flow() / ip_route_output_key()
  • FIB 查找:fib_lookup()

路由查找结果(dst/rtable)决定了:

  • 包是本地交付、转发还是丢弃
  • 发包下一跳和输出设备
  • 后续 Netfilter 与邻居子系统行为

9. TCP/UDP 协议栈关键路径

9.1 TCP

  • 收包:tcp_v4_rcv() -> tcp_rcv_established()(快路径)
  • 发包:tcp_sendmsg() -> ip_queue_xmit()
  • 用户态读:tcp_recvmsg()

TCP 复杂度主要在连接状态机、重传、拥塞控制和确认机制;这些都建立在 IPv4 路由与 dev 队列机制之上。

9.2 UDP

  • 收包:udp_rcv() -> __udp4_lib_rcv()
  • 发包:udp_sendmsg()
  • 用户态读:udp_recvmsg()

UDP 路径更短,无连接状态机,但依旧走同一套 IP 路由与 netdevice 发包框架。


10. Netfilter 在主路径的切入点

IPv4 主路径中可见典型钩子:

  • NF_INET_PRE_ROUTINGip_rcv
  • NF_INET_LOCAL_INip_local_deliver
  • NF_INET_FORWARDip_forward
  • NF_INET_LOCAL_OUTip_local_out
  • NF_INET_POST_ROUTINGip_output

这五个点构成了防火墙/NAT/conntrack 的核心挂载面。


11. 架构总结

RK3588 kernel-6.1net 架构可总结为:

  • 统一入口层socket.c 把系统调用映射到具体协议族。
  • 协议分发层af_inet.c 组织 IPv4 协议创建与 L4 分发。
  • 数据平面核心层net/core/dev.c 承担 RX/TX 主循环和与网卡驱动的衔接。
  • 协议处理层ipv4/ 中的 ip_input/ip_output/route/tcp/udp 形成完整 L3/L4 协议栈。
  • 策略控制层:Netfilter/FIB/XFRM 在关键节点插入策略与转发决策。

一句话:net 子系统是“socket 抽象 + 协议栈处理 + 设备数据平面 + 策略钩子”协同工作的分层体系。

01 总体架构与分层设计

01 总体架构与分层设计

源码:drivers/net/ethernet/stmicro/stmmac/
关联:stmmac.hcommon.hhwif.hhwif.c

1. 设计目标

STMMAC 驱动需同时支持:

  1. 多代 Synopsys IP(GMAC3 → GMAC4 → DWMAC5 → XGMAC/XLGMAC)
  2. 数十种 SoC Platform Glue(Rockchip、NXP、Qualcomm、ST 等)
  3. 完整 netdev 特性(多队列、offload、PTP、TC、XDP)

核心策略:核心逻辑与硬件/平台解耦,通过 ops 表和平台数据注入实现扩展。

2. 三层架构

flowchart TB
    subgraph App["Linux 网络栈"]
        NET["net/core/dev.c"]
        TC["net/sched"]
    end

    subgraph L2["L2 核心驱动 stmmac_main.c"]
        NDO["net_device_ops"]
        DMA["DMA Ring 管理"]
        NAPI["NAPI / IRQ"]
    end

    subgraph L3["L3 HWIF"]
        OPS["mac/dma/desc/ptp/tc/mmc ops"]
    end

    subgraph L1["L1 Platform Glue"]
        PLAT["plat_stmmacenet_data"]
        BSP["bsp_priv 回调"]
    end

    NET --> NDO
    TC --> NDO
    NDO --> DMA
    DMA --> OPS
    L1 --> L2
    OPS --> REG["MMIO 寄存器"]

2.1 L1 — Platform Glue

输入:Device Tree / ACPI / 平台数据
输出plat_stmmacenet_data + stmmac_resources

回调字段 典型用途
fix_mac_speed 切换 RGMII/RMII 时钟分频
get_eth_addr 从 OTP/eFuse 读 MAC
integrated_phy_power 内置 PHY 上下电
clks_config 运行时门控 MAC 时钟
bsp_priv SoC 私有数据(如 rk_priv_data

统一入口:所有 Glue 最终调用 stmmac_dvr_probe(device, plat_dat, res)

2.2 L2 — Core Driver

stmmac_main.c 是驱动中枢(约 8360 行),职责:

  • 实现 net_device_ops 全套回调
  • 管理 TX/RX descriptor ring 与 buffer 生命周期
  • 调度 NAPI、处理中断、错误恢复
  • 协调 phylink、PTP、TC、XDP 等子模块

不直接操作版本相关寄存器,而是通过 HWIF 宏调用 L3。

2.3 L3 — HWIF(Hardware Interface)

hwif.c 在 probe 时:

  1. 读取 GMAC_VERSIONGMAC4_VERSION 寄存器
  2. stmmac_hw[] 表中匹配 Synopsys ID
  3. 将对应 ops 指针填入 mac_device_info
  4. 调用版本专用 setup()(如 dwmac4_setup()

调用示例:

1
2
stmmac_start_tx(priv, priv->ioaddr, chan);
// 展开为:priv->hw->dma->start_tx(ioaddr, chan)

3. 核心数据结构关系

classDiagram
    class net_device {
        +netdev_ops
        +features
    }
    class stmmac_priv {
        +ioaddr
        +hw
        +dma_conf
        +channel[]
        +phylink
        +plat
    }
    class mac_device_info {
        +mac ops
        +dma ops
        +desc ops
        +ptp ops
        +tc ops
        +mmc ops
    }
    class plat_stmmacenet_data {
        +tx_queues_to_use
        +rx_queues_to_use
        +fix_mac_speed()
        +bsp_priv
    }
    class stmmac_dma_conf {
        +tx_queue[]
        +rx_queue[]
    }

    net_device --> stmmac_priv : netdev_priv
    stmmac_priv --> mac_device_info : hw
    stmmac_priv --> plat_stmmacenet_data : plat
    stmmac_priv --> stmmac_dma_conf : dma_conf

4. 队列与 NAPI 模型

每个 channel 对应一对(或合并)NAPI:

结构体 关键字段 作用
stmmac_tx_queue cur_tx, dirty_tx, dma_tx TX ring 与 skb 映射
stmmac_rx_queue cur_rx, page_pool, buf_pool RX ring 与页缓冲
stmmac_channel rx_napi, tx_napi, rxtx_napi 软中断上下文

AF_XDP zero-copy 启用时,同一 channel 使用 rxtx_napi 合并 poll。

5. 设计模式

模式 实现
Strategy mac_device_info 下各 ops 表
Template Method probe 固定流程,HW 步骤由 ops 填充
Dependency Injection plat_stmmacenet_data 回调
Per-CPU / cacheline 热路径结构体 ____cacheline_aligned

6. 编译与配置关系

1
2
3
4
5
CONFIG_STMMAC_ETH          → stmmac.ko(必选)
CONFIG_STMMAC_PLATFORM → stmmac-platform.ko
CONFIG_DWMAC_ROCKCHIP → dwmac-rockchip.ko
CONFIG_STMMAC_FULL → 全版本 IP 支持(非仅 4.10)
CONFIG_STMMAC_ETHTOOL → ethtool 接口

RK3588 典型 defconfig 会同时启用 ETH + PLATFORM + ROCKCHIP + FULL + ETHTOOL。

7. 与内核网络栈边界

方向 接口
下行(栈→驱动) ndo_start_xmitndo_openndo_setup_tcndo_bpf
上行(驱动→栈) napi_gro_receivenetif_receive_skb
链路 phylinkphylib / PCS
管理 ethtool_opsrtnetlink

8. 扩展新平台步骤

  1. 新增 dwmac-xxx.c,实现 probe/remove
  2. 填充 plat_stmmacenet_data(队列、接口类型、回调)
  3. 调用 stmmac_dvr_probe()
  4. Makefile / Kconfig 注册
  5. 提供 DTS binding(snps,dwmac 或厂商 compatible)

无需修改 stmmac_main.c,除非有新硬件特性需核心支持。

9. 相关文档

02 stmmac_main 核心驱动

02 stmmac_main 核心驱动

源码:stmmac_main.c(约 8360 行)
头文件:stmmac.hcommon.h

1. 模块职责

stmmac_main.c 是 STMMAC 驱动的 控制面与数据面中枢,负责:

  • 设备 probe/remove/suspend/resume
  • net_device_ops 完整实现
  • DMA descriptor ring 分配与回收
  • TX/RX 数据路径
  • 中断分发与 NAPI
  • phylink 集成、EEE、WoL、VLAN、RSS
  • 与 PTP/TC/XDP 子模块协作

2. 对外入口函数

函数 调用者 说明
stmmac_dvr_probe() Platform/PCI glue 核心 probe
stmmac_dvr_remove() Platform/PCI glue 核心 remove
stmmac_resume() / stmmac_suspend() PM 框架 电源管理
stmmac_bus_clks_config() glue / PM 总线时钟门控

3. net_device_ops 映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static const struct net_device_ops stmmac_netdev_ops = {
.ndo_open = stmmac_open,
.ndo_stop = stmmac_release,
.ndo_start_xmit = stmmac_xmit,
.ndo_change_mtu = stmmac_change_mtu,
.ndo_fix_features = stmmac_fix_features,
.ndo_set_features = stmmac_set_features,
.ndo_set_rx_mode = stmmac_set_rx_mode,
.ndo_tx_timeout = stmmac_tx_timeout,
.ndo_eth_ioctl = stmmac_ioctl,
.ndo_setup_tc = stmmac_setup_tc,
.ndo_select_queue = stmmac_select_queue,
.ndo_bpf = stmmac_bpf,
.ndo_xdp_xmit = stmmac_xdp_xmit,
.ndo_xsk_wakeup = stmmac_xsk_wakeup,
.ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid,
// ...
};

4. Probe 流程详解

stmmac_dvr_probe() 主要步骤:

register_netdevstmmac_phy_setupstmmac_hw_initstmmac_dvr_proberegister_netdevstmmac_phy_setupstmmac_hw_initstmmac_dvr_probedevm_alloc_etherdev_mqs()绑定 ioaddr/irq/platcreate workqueue (service_task)stmmac_hwif_init + get_hw_features设置 hw_features (TSO/GRO/VLAN/TC)dma_set_mask_and_coherent()phylink + MDIO 注册register_netdev()

4.1 硬件能力探测

stmmac_hw_init() 调用 stmmac_get_hw_features() 填充 priv->dma_cap

  • TSO、RX/TX checksum、RSS、VLAN、SPH
  • 队列数量、hash 表大小
  • 64 位 DMA 地址宽度

平台参数可被 HW capability 覆盖(如 enh_descrx_coe)。

5. Open / Release

5.1 stmmac_open()

1
2
3
4
5
6
7
8
9
10
11
stmmac_open(dev)
→ stmmac_setup_dma_desc(priv, mtu) # 按 MTU 计算 buffer/ring
→ __stmmac_open(dev, dma_conf)
→ alloc_dma_desc_resources()
→ stmmac_init_rx_buffers() # page_pool 预填 RX
→ stmmac_hw_setup() # MAC/DMA/MTL 寄存器
→ stmmac_request_irq()
→ stmmac_napi_add() + napi_enable()
→ phylink_start()
→ netif_tx_start_all_queues()
→ stmmac_start_all_dma()

5.2 stmmac_release()

逆序关闭:phylink_stop → disable queues → free_irq → stop DMA → free ring → MAC disable。

6. TX 路径

6.1 stmmac_xmit()

入口:ndo_start_xmit,由 dev_queue_xmit() 触发。

关键步骤:

  1. skb_get_queue_mapping() 选择 tx_queue
  2. EEE:若在 LPI 模式则先 stmmac_disable_eee_mode()
  3. GSO 分流 → stmmac_tso_xmit()(TCP/UDP TSO)
  4. 检查 ring 空间 stmmac_tx_avail()
  5. 可选 VLAN 硬件插入 stmmac_vlan_insert()
  6. DMA map skb 各段(linear + frags)
  7. stmmac_prepare_tx_desc() 填描述符
  8. stmmac_set_tx_owner() 交 ownership 给 DMA
  9. stmmac_set_tx_tail_ptr() 通知硬件

6.2 stmmac_tx_clean()

NAPI TX poll 中调用:

  • 扫描已完成 descriptor(owner bit 归还 CPU)
  • stmmac_tx_status() 检查错误
  • unmap DMA、释放 skb
  • 更新 dirty_tx,必要时 netif_wake_queue()

6.3 中断合并

  • tx_coal_frames / tx_coal_timer:每 N 帧或超时设 IC bit
  • hrtimer txtimer:TX 完成延迟唤醒

7. RX 路径

7.1 stmmac_rx()

NAPI RX poll 主循环:

  1. 读 descriptor status(stmmac_rx_status()
  2. owner 仍属 DMA 则 break
  3. 错误帧 discard 或统计
  4. 多段帧组装 skb(state_saved 跨 poll 保存中间状态)
  5. XDP:bpf_prog_run() → PASS/DROP/REDIRECT/TX
  6. 正常路径:napi_gro_receive() 上交协议栈
  7. stmmac_alloc_rx_buffers() 补充 RX 描述符

7.2 page_pool

RX 使用 page_pool 分配页:

  • 减少 alloc/free 开销
  • 与 XDP 共享页框架
  • rx_copybreak:小帧可 copy 到 skb 头(非 zero-copy 路径)

7.3 stmmac_rx_zc()

AF_XDP zero-copy 专用 RX,DMA 直接写入 XSK umem。

8. 中断与 NAPI

hardirq: stmmac_interruptstmmac_dma_interrupt()stmmac_napi_check() per channeldisable_dma_irq__napi_schedulesoftirq: napi_pollstmmac_napi_poll_rx → stmmac_rxstmmac_napi_poll_tx → stmmac_tx_cleannapi_complete → enable_dma_irq

支持 单 IRQper-queue MSImulti_msi_en)。

回调 作用
stmmac_mac_config 接口模式变化(RGMII/SGMII 等)
stmmac_mac_link_up 链路 up:速率、双工、流控、启动 DMA
stmmac_mac_link_down 链路 down:停 DMA、carrier off

stmmac_phy_setup() 创建 phylink 实例并连接 PHY/PCS。

10. 错误恢复

机制 触发
stmmac_tx_timeout TX watchdog 超时
stmmac_global_err 调度 service_task 异步复位
stmmac_tx_err DMA TX hard error

service_task workqueue 避免在 IRQ 上下文做完整 reset。

11. 其它内嵌功能

功能 主要函数
EEE LPI stmmac_enable_eee_mode, stmmac_eee_ctrl_timer
VLAN stmmac_vlan_rx_add_vid, HW filter
RSS stmmac_set_rxfh, stmmac_rss_configure
FPE stmmac_fpe_handshake, fpe_wq
debugfs stmmac_init_fs

12. 关键文件内函数索引

行号区间(约) 内容
4000–4100 open/release
4470–4700 xmit
5300–5700 rx / rx_zc
2530–2720 tx_clean
2770–2850 dma_interrupt / napi_check
5750–5840 napi_poll_rx/tx/rxtx
7100–7260 netdev_ops
7290–7390 hw_init
7560–7900 dvr_probe

13. 相关文档

03 HWIF 硬件抽象层

03 HWIF 硬件抽象层

源码:hwif.chwif.h
关联:common.hmac_device_info

1. 模块职责

HWIF(Hardware Interface)层解决 Synopsys DWMAC 多版本共存 问题:

  • 统一 MAC/DMA/描述符/PTP/TC/MMC 操作接口
  • 根据芯片 ID 自动绑定对应 ops 实现
  • 提供宏封装,核心代码无需 #ifdef 版本分支

2. ops 结构体体系

定义于 hwif.h

结构体 主要方法示例
stmmac_desc_ops init_rx_desc, prepare_tx_desc, rx_status, set_mss
stmmac_dma_ops reset, start_tx/rx, set_tx_tail_ptr, enable_dma_irq
stmmac_ops core_init, set_filter, flow_ctrl, rss_configure
stmmac_hwtimestamp init_systime, adjust_systime, config_hw_tstamping
stmmac_mode_ops init, refill_desc3, clean_desc3(ring/chain)
stmmac_tc_ops setup_cls_u32, setup_cbs, setup_taprio
stmmac_mmc_ops ctrl, read

3. 宏调用机制

1
2
3
4
5
6
7
#define stmmac_do_callback(__priv, __module, __cname, __arg0, __args...) \
({ \
int __result = -EINVAL; \
if ((__priv)->hw->__module && (__priv)->hw->__module->__cname) \
__result = (__priv)->hw->__module->__cname((__arg0), ##__args); \
__result; \
})

示例:

1
2
stmmac_start_tx(priv, priv->ioaddr, chan);
// → priv->hw->dma->start_tx(priv->ioaddr, chan)

若 ops 或函数指针为 NULL,返回 -EINVAL,避免空指针崩溃。

4. 版本匹配表 stmmac_hw[]

hwif.cstmmac_hwif_init() 遍历 stmmac_hw[]从新到旧匹配):

条目 min_id gmac4 xgmac MAC ops DMA ops
DWXLGMAC2 0x20 dwxlgmac2_ops dwxgmac210_dma_ops
DWXGMAC2.10 0x21 dwxgmac210_ops dwxgmac210_dma_ops
DWMAC5.10 0x51 dwmac510_ops dwmac410_dma_ops
DWMAC4.10+ 0x41 dwmac410_ops dwmac410_dma_ops
DWMAC4.00 0x40 dwmac4_ops dwmac4_dma_ops
DWMAC1000 0x00 dwmac1000_ops dwmac1000_dma_ops
DWMAC100 0x00 dwmac100_ops dwmac100_dma_ops

4.1 匹配条件

1
2
3
4
5
needs_gmac   ^ entry->gmac    → skip
needs_gmac4 ^ entry->gmac4 → skip
needs_xgmac ^ entry->xgmac → skip
priv->synopsys_id < entry->min_id → skip
needs_xgmac && (dev_id ^ entry->dev_id) → skip

平台通过 plat->has_gmac4 / has_xgmac 声明 IP 类型;RK3588 为 has_gmac4=true

4.2 寄存器偏移

每条目含 stmmac_regs_off

  • ptp_off:PTP 子模块相对基址偏移
  • mmc_off:MMC 计数器偏移

GMAC4 使用 PTP_GMAC4_OFFSET / MMC_GMAC4_OFFSET

5. stmmac_hwif_init() 流程

读 GMAC4_VERSION保存 synopsys_id"plat->setup?"平台自定义 mac_device_infodevm_kzalloc mac_device_info遍历 stmmac_hw[]绑定 desc/dma/mac/ptp/tc/mmcentry->setup(priv)保存 hwif_quirks

5.1 setup 函数

setup 文件 作用
dwmac4_setup dwmac4_lib.c 填充 GMAC4 寄存器地址、过滤容量
dwmac1000_setup dwmac1000_core.c GMAC3 配置
dwxgmac2_setup dwxgmac2_core.c XGMAC 配置

5.2 quirks

quirks 作用
stmmac_dwmac1_quirks 选择 norm/enh desc、ring/chain
stmmac_dwmac4_quirks ring/chain 模式
stmmac_dwxlgmac_quirks 设置 xlgmac 标志

6. mac_device_info 聚合

1
2
3
4
5
6
7
8
9
10
struct mac_device_info {
const struct stmmac_ops *mac;
const struct stmmac_desc_ops *desc;
const struct stmmac_dma_ops *dma;
const struct stmmac_mode_ops *mode;
const struct stmmac_hwtimestamp *ptp;
const struct stmmac_tc_ops *tc;
const struct stmmac_mmc_ops *mmc;
// 运行时字段:mii、link、vlan_filter、xpcs...
};

priv->hw 指向该结构,全驱动共享。

7. 与 stmmac_hw_init 协作

stmmac_hw_init()(stmmac_main.c)在 stmmac_hwif_init() 之后:

  1. stmmac_get_hw_features() 读 DMA capability 寄存器
  2. 根据 capability 修正 plat->tx_coerx_coeenh_desc
  3. 执行 hwif_quirks()(若存在)
  4. 决定是否启用 RIWT(RX 中断 watchdog)

8. 扩展新版本 IP

  1. 实现 *_ops 结构体(mac/dma/desc)
  2. 实现 *_setup() 初始化 mac_device_info
  3. stmmac_hw[] 末尾追加条目(注释要求新版本放表尾)
  4. MakefileCONFIG_STMMAC_FULL 块加入 .o 文件

9. 调试

probe 日志示例:

1
2
User ID: 0x0, Synopsys ID: 0x51
DMA HW capability register supported

Synopsys ID 0x41 = Core 4.10,0x51 = Core 5.10。

10. 相关文档

04 DWMAC4 与描述符 / DMA

04 DWMAC4 与描述符 / DMA

源码:dwmac4_core.cdwmac4_dma.cdwmac4_descs.cdwmac4_lib.cdwmac4.h
RK3588 使用 GMAC4 Core 4.10+dwmac410_ops / dwmac410_dma_ops

1. 模块划分

文件 职责
dwmac4.h 寄存器偏移、MTL/DMA/MAC 位域
dwmac4_core.c MAC + MTL:地址过滤、队列、VLAN、RSS、流控
dwmac4_dma.c DMA 通道:reset、启停、IRQ、tail pointer
dwmac4_descs.c Normal/Extended 描述符读写
dwmac4_lib.c dwmac4_setup()、公共初始化
ring_mode.c GMAC4 ring 模式 desc3 管理

2. GMAC4 描述符格式

2.1 Normal Descriptor

每个 TX/RX descriptor 为 16 字节(4×u32):

TX (TDES)

  • TDES0:status(own bit 在 MSB)、timestamp status
  • TDES1:buffer 长度、checksum insert、LS/FS
  • TDES2:buffer 物理地址
  • TDES3:buffer 地址高位 / 控制位

RX (RDES)

  • RDES0:帧长度、error summary、LS/FS
  • RDES1:buffer 长度
  • RDES2/3:buffer 地址

2.2 Extended / Context Descriptor

  • Extended desc:更大 status 域,支持 PTP、RSS hash
  • Context desc:VLAN 插入、TSO MSS、TBS 时间戳

priv->extend_desc 由 HW capability 或平台 enh_desc 决定。

3. desc_ops 关键方法

dwmac4_desc_ops(dwmac4_descs.c):

方法 TX/RX 说明
init_rx_desc RX 初始化空描述符,设 buffer size
init_tx_desc TX 初始化 TX 环
prepare_tx_desc TX 填长度、checksum、FS/LS
prepare_tso_tx_desc TX TSO 分段描述符
set_tx_owner / set_rx_owner 两者 切换 DMA/CPU ownership
tx_status / rx_status 两者 解析完成状态
get_rx_hash RX RSS hash 与类型
set_vlan_tag TX VLAN context
set_mss TX TSO MSS context

4. DMA 引擎

4.1 通道模型

GMAC4 支持多 DMA channel,每 channel 映射 MTL queue:

1
2
MTL TX Queue N  ←→  DMA TX Channel N
MTL RX Queue M ←→ DMA RX Channel M

stmmac_map_mtl_to_dma() 配置映射关系。

4.2 dma_ops 关键方法

方法 说明
reset DMA 软复位
init / init_chan 全局/单通道初始化
init_rx_chan / init_tx_chan 设 ring 基址、长度
start_tx / stop_tx 启停 TX DMA
start_rx / stop_rx 启停 RX DMA
set_tx_tail_ptr / set_rx_tail_ptr 通知硬件新 descriptor
enable_dma_irq / disable_dma_irq 通道 IRQ 掩码
dma_interrupt 读 DMA 状态,返回 handle_rx/handle_tx
get_hw_feature 读 HW Feature 寄存器
enable_tso 通道 TSO 使能
rx_watchdog RIWT 接收中断合并

4.3 Tail Pointer 机制

驱动填好 descriptor 后写 Tail Pointer 寄存器,DMA 从 cur 追到 tail 发送/接收。这是典型 环形队列 + 生产者/消费者 模型。

5. Ring vs Chain 模式

模式 文件 desc3 用途
Ring ring_mode.c 下一描述符物理地址(环回)
Chain chain_mode.c 链式指向下一块

GMAC4 默认 ring modedwmac4_ring_mode_ops)。
GMAC3 可通过模块参数 chain_mode 切换。

6. MTL 调度

dwmac4_core.c 中 MTL 相关 ops:

  • rx_queue_enable / tx_queue_prio:队列优先级
  • prog_mtl_rx_algorithms / prog_mtl_tx_algorithms:DWRR/WRR/SP
  • config_cbs:Credit-Based Shaper(AVB)
  • set_mtl_tx_queue_weight:队列权重

TC offload(CBS/TAPRIO)最终配置 MTL 寄存器。

7. MAC 层功能

功能 ops 方法
单播/组播过滤 set_filter, set_umac_addr
流控 flow_ctrl
VLAN enable_vlan, add_hw_vlan_rx_fltr
RSS rss_configure
L3/L4 过滤 config_l3_filter, config_l4_filter
PCS(SGMII) pcs_ctrl_ane, pcs_get_adv_lp
EST/TAS est_configure(DWMAC5+)
FPE fpe_configure(帧抢占)

8. HW Feature 寄存器

get_hw_feature() 填充 struct dma_features

  • 队列数量、TSO、checksum 类型
  • VLAN、AV、TBS、SPH
  • hash 表大小、64 位地址

stmmac_hw_init() 据此设置 ndev->hw_features

9. RK3588 注意点

  • Core ID 通常 ≥ 0x41(4.10),匹配 dwmac410_ops
  • CONFIG_STMMAC_FULL=y 时链接完整 dwmac4 + dwmac5 代码
  • dche(descriptor cache):Core ≥ 5.20 才支持 HW prefetch
  • 平台 sph_disable=true 时不暴露 GRO/SPH

10. 数据结构与内存布局

1
2
3
4
5
6
7
8
9
10
11
stmmac_tx_queue:
dma_tx[] ── DMA 一致性内存(descriptor ring)
tx_skbuff[] ── 与每个 desc 关联的 skb 指针
tx_skbuff_dma[] ── DMA 映射信息
cur_tx / dirty_tx ── 生产者/消费者索引

stmmac_rx_queue:
dma_rx[] ── RX descriptor ring
buf_pool[] ── 每 desc 对应 page + dma_addr
page_pool ── 页分配器
cur_rx ── 当前处理位置

Ring 大小须为 2 的幂DMA_MIN_* 64 ~ DMA_MAX_* 1024)。

11. 相关文档

05 Platform 平台 Glue 层

05 Platform 平台 Glue 层

源码:stmmac_platform.cstmmac_platform.hdwmac-generic.c
各 SoC 专用:dwmac-rk.cdwmac-imx.c

1. 模块职责

Platform Glue 是 STMMAC 与 具体 SoC/板级 之间的适配层:

  • 解析 Device Tree,构建 plat_stmmacenet_data
  • 获取 MMIO、IRQ、MAC 地址等资源
  • 提供时钟/复位/引脚复用回调
  • 调用统一核心入口 stmmac_dvr_probe()

核心驱动 不依赖 任何特定 SoC 头文件。

2. 通用平台框架

2.1 stmmac_platform.c

函数 说明
stmmac_get_platform_resources() 映射 reg、解析 interrupt
stmmac_probe_config_dt() 解析 DT → plat_stmmacenet_data
stmmac_remove_config_dt() 释放 DT 相关内存
stmmac_pltfr_probe() 通用 platform probe(部分平台)
stmmac_pltfr_remove() 通用 remove

2.2 DT 解析要点

snps,dwmac 或厂商 compatible 节点读取:

属性 映射字段
snps,tx-sched-sp TX 调度算法
snps,rx-queues-config RX 队列 DCB/AVB
snps,tx-queues-config TX 队列配置
snps,axi-config AXI 总线 tuning
snps,mixed-burst DMA burst
max-speed max_speed
tx-fifo-depth / rx-fifo-depth FIFO 大小
dma-rx-size / dma-tx-size ring 深度

phy-modemdiophy-handle 供 phylink 使用。

2.3 plat_stmmacenet_data 关键字段

定义于 include/linux/stmmac.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct plat_stmmacenet_data {
int phy_addr;
phy_interface_t phy_interface;
struct stmmac_dma_cfg *dma_cfg;
u32 rx_queues_to_use;
u32 tx_queues_to_use;
void (*fix_mac_speed)(void *priv, unsigned int speed);
void (*get_eth_addr)(void *priv, unsigned char *addr);
void *bsp_priv;
struct reset_control *stmmac_rst;
int has_gmac4;
bool tso_en;
// ...
};

3. Probe 统一模式

所有 Platform 驱动遵循相同模板:

1
2
3
4
5
6
7
8
9
10
11
12
static int xxx_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources res;

ret = stmmac_get_platform_resources(pdev, &res);
plat_dat = stmmac_probe_config_dt(pdev, res.mac);
plat_dat->bsp_priv = xxx_setup(pdev, plat_dat);
/* 填充平台回调 */
plat_dat->fix_mac_speed = xxx_fix_speed;
return stmmac_dvr_probe(&pdev->dev, plat_dat, &res);
}

4. 各 Platform 驱动一览

模块文件 Kconfig 平台
dwmac-rk.c DWMAC_ROCKCHIP Rockchip 全系
dwmac-imx.c DWMAC_IMX8 NXP i.MX
dwmac-qcom-ethqos.c DWMAC_QCOM_ETHQOS Qualcomm
dwmac-stm32.c DWMAC_STM32 STM32
dwmac-generic.c DWMAC_GENERIC 无特殊逻辑
dwmac-dwc-qos-eth.c DWMAC_DWC_QOS_ETH DWC QoS binding
stmmac_pci.c STMMAC_PCI PCI 设备

Makefile 注释:Generic driver must be last(避免覆盖专用匹配)。

5. stmmac_resources

1
2
3
4
5
6
7
8
struct stmmac_resources {
void __iomem *addr;
u8 mac[ETH_ALEN];
int irq; // 主中断
int wol_irq, lpi_irq;
int rx_irq[MTL_MAX_RX_QUEUES];
int tx_irq[MTL_MAX_TX_QUEUES];
};

multi_msi_en 时 per-queue IRQ 从 DT interrupt-names 解析。

6. PCI 路径

stmmac_pci.c

  • PCI probe → 映射 BAR
  • 构建 plat_stmmacenet_data(较固定)
  • 同样调用 stmmac_dvr_probe()

Intel 专用:dwmac-intel.c + dwmac-intel-plat.c

7. 电源管理

Platform glue 通常注册:

1
2
3
4
#ifdef CONFIG_PM
.suspend = stmmac_suspend,
.resume = stmmac_resume,
#endif

或通过 dev_pm_ops 委托给 stmmac_suspend/resume(),内部处理 DMA stop、PHY、时钟。

8. UIO 变体

stmmac_uio.cCONFIG_STMMAC_UIO):

  • 将 MAC 暴露给 DPDK 等用户态
  • 绕过内核 netdev 数据路径
  • 默认更大 ring(1024)

9. RK3588 详见

Rockchip 专用逻辑见 11-dwmac-rockchip-RK3588.md

10. 新增 Platform 检查清单

  • DTS binding 文档
  • 时钟/复位/电源域
  • phy-mode 与 GRF 引脚配置
  • MAC 地址来源(DT / OTP / 随机)
  • has_gmac4has_xgmac 正确设置
  • Kconfig + Makefile 注册
  • 调用 stmmac_dvr_probe / stmmac_dvr_remove

11. 相关文档

06 MDIO 与 phylink

06 MDIO 与 phylink

源码:stmmac_mdio.cstmmac_main.c(phylink 回调)
依赖:Linux phylinkphylibpcs-xpcs

1. 模块职责

组件 文件 职责
MDIO 总线 stmmac_mdio.c 通过 MAC 内置 MII 访问 PHY 寄存器
phylink stmmac_main.c 链路状态机、接口模式、MAC 侧配置
PCS/XPCS stmmac_mdio.c + mac ops SGMII/QSGMII 等 SerDes 模式

2. MDIO 实现

2.1 注册流程

1
2
3
4
stmmac_dvr_probe()
→ stmmac_phy_setup()
→ stmmac_mdio_register()
→ mdiobus_alloc + of_mdiobus_register

priv->miistruct mii_bus *,挂接在 netdev 上供 phylink/PHY 驱动使用。

2.2 读写路径

GMAC4 MDIO 寄存器格式(stmmac_mdio.c):

  • MII_GMAC4_GOC_SHIFT:读/写命令
  • MII_GMAC4_REG_ADDR_SHIFT:PHY 地址与寄存器号
  • MII_GMAC4_C45E:Clause 45 扩展

XGMAC 使用独立 MII_XGMAC_* 位域,支持 C22/C45 端口映射。

2.3 主要 API

函数 说明
stmmac_mdio_register() 注册 MDIO 总线
stmmac_mdio_unregister() 注销
stmmac_mdio_idle() 等待 MII busy 清除
stmmac_mdio_reset() 总线复位
stmmac_xpcs_setup() 配置 Synopsys XPCS

2.4 时钟分频

stmmac_clk_csr_set() 根据 MAC 时钟设置 MDIO CSR 分频(priv->clk_csr),保证 MDC 频率符合 IEEE 802.3(≤ 2.5 MHz)。

3.1 初始化

stmmac_phy_setup()

  1. 填充 phylink_configmac_capabilities 等)
  2. phylink_create() / phylink_of_phy_connect() 或 PCS 模式
  3. 注册 mac ops:
1
2
3
4
5
static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
.mac_config = stmmac_mac_config,
.mac_link_down = stmmac_mac_link_down,
.mac_link_up = stmmac_mac_link_up,
};

3.2 链路 Up 流程

stmmac_mac_link_up()

  1. 记录 priv->speedduplex
  2. 调用 plat->fix_mac_speed()RK3588 配置 RGMII 时钟
  3. stmmac_mac_flow_ctrl() 配置 PAUSE
  4. stmmac_mac_set(enable) 使能 MAC
  5. netif_carrier_on()

3.3 链路 Down 流程

stmmac_mac_link_down()

  • 停 DMA / 关 MAC
  • netif_carrier_off()
  • EEE、FPE 状态清理

3.4 接口模式

stmmac_mac_config() 处理:

  • MIIRMIIRGMIISGMII1000BASE-X
  • PCS 模式:stmmac_check_pcs_mode()pcs_ctrl_ane()

4. PHY 与内置 PHY

场景 处理
外接 PHY DT phy-handle → phylink 连接
固定链路 fixed-link 节点
内置 PHY integrated_phy_power() 回调上下电
SerDes XPCS + PCS ops,RK3588 dwmac-rk.c

5. 与 Platform Glue 协作

Rockchip rk_fix_speed() 在 link up 时:

  • 调用 rk3588_ops->set_rgmii_speed() 或 RMII 等
  • 写 GRF 寄存器切换 TX/RX clock

详见 11-dwmac-rockchip-RK3588.md

6. 调试

1
2
3
4
5
# 查看 PHY 状态
ethtool eth0

# MDIO 调试(需 CONFIG_PHYLIB)
bus=platform-xxx mdio dev

7. 相关文档