SDIO Wi‑Fi 设备的驱动向insmod + 反向rmmod

SDIO WiFi

SDIO WiFi 是 WiFi 芯片采用 SDIO 总线和主控通信,复用 SD 卡接口,是嵌入式里很常见的 WiFi 方案,主控通过 SDIO 向 WiFi 芯片下发控制指令、收发网络报文。

Hi3516CV610+RTL8189FS 从驱动加载、连接热点、获取 IP 到传输视频的完整流程

#mermaid-svg-6qX4r4I9jykLTvHo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6qX4r4I9jykLTvHo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6qX4r4I9jykLTvHo .error-icon{fill:#552222;}#mermaid-svg-6qX4r4I9jykLTvHo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6qX4r4I9jykLTvHo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6qX4r4I9jykLTvHo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6qX4r4I9jykLTvHo .marker.cross{stroke:#333333;}#mermaid-svg-6qX4r4I9jykLTvHo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6qX4r4I9jykLTvHo p{margin:0;}#mermaid-svg-6qX4r4I9jykLTvHo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6qX4r4I9jykLTvHo .cluster-label text{fill:#333;}#mermaid-svg-6qX4r4I9jykLTvHo .cluster-label span{color:#333;}#mermaid-svg-6qX4r4I9jykLTvHo .cluster-label span p{background-color:transparent;}#mermaid-svg-6qX4r4I9jykLTvHo .label text,#mermaid-svg-6qX4r4I9jykLTvHo span{fill:#333;color:#333;}#mermaid-svg-6qX4r4I9jykLTvHo .node rect,#mermaid-svg-6qX4r4I9jykLTvHo .node circle,#mermaid-svg-6qX4r4I9jykLTvHo .node ellipse,#mermaid-svg-6qX4r4I9jykLTvHo .node polygon,#mermaid-svg-6qX4r4I9jykLTvHo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6qX4r4I9jykLTvHo .rough-node .label text,#mermaid-svg-6qX4r4I9jykLTvHo .node .label text,#mermaid-svg-6qX4r4I9jykLTvHo .image-shape .label,#mermaid-svg-6qX4r4I9jykLTvHo .icon-shape .label{text-anchor:middle;}#mermaid-svg-6qX4r4I9jykLTvHo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6qX4r4I9jykLTvHo .rough-node .label,#mermaid-svg-6qX4r4I9jykLTvHo .node .label,#mermaid-svg-6qX4r4I9jykLTvHo .image-shape .label,#mermaid-svg-6qX4r4I9jykLTvHo .icon-shape .label{text-align:center;}#mermaid-svg-6qX4r4I9jykLTvHo .node.clickable{cursor:pointer;}#mermaid-svg-6qX4r4I9jykLTvHo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6qX4r4I9jykLTvHo .arrowheadPath{fill:#333333;}#mermaid-svg-6qX4r4I9jykLTvHo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6qX4r4I9jykLTvHo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6qX4r4I9jykLTvHo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6qX4r4I9jykLTvHo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6qX4r4I9jykLTvHo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6qX4r4I9jykLTvHo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6qX4r4I9jykLTvHo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6qX4r4I9jykLTvHo .cluster text{fill:#333;}#mermaid-svg-6qX4r4I9jykLTvHo .cluster span{color:#333;}#mermaid-svg-6qX4r4I9jykLTvHo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6qX4r4I9jykLTvHo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6qX4r4I9jykLTvHo rect.text{fill:none;stroke-width:0;}#mermaid-svg-6qX4r4I9jykLTvHo .icon-shape,#mermaid-svg-6qX4r4I9jykLTvHo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6qX4r4I9jykLTvHo .icon-shape p,#mermaid-svg-6qX4r4I9jykLTvHo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6qX4r4I9jykLTvHo .icon-shape .label rect,#mermaid-svg-6qX4r4I9jykLTvHo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6qX4r4I9jykLTvHo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6qX4r4I9jykLTvHo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6qX4r4I9jykLTvHo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 一个视频数据包的发送路径
RTSP 视频传输
获取 IP 地址
连接热点
外设层:RTL8189FS 驱动
核心层:Linux 内核框架
主控层:Hi3516 SDIO 控制器
系统启动
解析设备树

SDIO、时钟、GPIO、电源
加载 rtl8189fs.ko
Hi3516 SDIO 主控驱动
配置时钟、总线宽度、DMA、中断
MMC / SDIO Core
Linux 网络核心

net_device
无线核心

cfg80211 / nl80211
SDIO probe

识别 RTL8189FTV
初始化 Wi-Fi 芯片

加载固件、配置寄存器
注册 wiphy 和 net_device
生成网络接口 wlan0
ifconfig wlan0 up
启动 wpa_supplicant
通过 nl80211 请求扫描
cfg80211 调用驱动 scan
RTL8189FS 扫描无线信道
发现热点 liguangdou666
选择目标 AP
802.11 Authentication
802.11 Association
WPA 四次握手
密钥下发给 RTL8189FS
wpa_state = COMPLETED
启动 udhcpc -i wlan0
DHCP Discover
Windows 热点返回 Offer
DHCP Request
DHCP ACK
板子获得 192.168.137.62
设置掩码和默认网关

192.168.137.1
启动 sample_venc 0 1
摄像头采集
VI / VPSS 图像处理
VENC 编码为 H.265
RTSP 服务监听 TCP 554
VLC 连接

192.168.137.62:554
TCP 建立连接
RTSP 控制+H.265 RTP 数据
sample_venc 调用 socket send
Linux TCP/IP 协议栈
根据路由选择 wlan0
生成 sk_buff
调用 RTL8189FS ndo_start_xmit
RTL8189FS 封装并排队
sdio_memcpy_toio
MMC / SDIO Core
Hi3516 SDIO 主控

DMA传输
RTL8189FTV 发射无线帧
Windows 热点接收
VLC 接收并解码 H.265

三层在这条链路中的职责可以压缩为:

text 复制代码
核心层
├─ MMC/SDIO Core:统一管理SDIO请求
├─ cfg80211/nl80211:统一管理Wi-Fi扫描和连接
└─ net_device/TCP-IP:统一管理网络数据

主控层
└─ Hi3516 SDIO主控:操作时钟、DMA、中断和控制器寄存器

外设层
└─ RTL8189FS驱动:控制Wi-Fi芯片扫描、关联、密钥和数据收发

最关键的两条源码调用链是:

text 复制代码
连接控制链:

wpa_supplicant
→ nl80211
→ cfg80211
→ ioctl_cfg80211.c
→ rtw_mlme.c
→ RTL8189FTV
text 复制代码
视频数据链:

sample_venc
→ socket/TCP
→ net_device
→ rtw_xmit.c
→ SDIO Core
→ Hi3516 SDIO主控
→ RTL8189FTV
→ Windows热点
→ VLC

其中 udhcpc、TCP、RTSP 都不属于 Wi‑Fi 驱动。Wi‑Fi 驱动只负责建立无线链路并收发帧;IP地址、路由和视频协议由更上层的软件处理。

SDIO核心注册驱动

精简流程

insmod → module_init → rtw_drv_entry

  1. 打印版本,WiFi芯片上电 platform_wifi_power_on()
  2. 初始化锁、proc、私有netlink、网络通知链
  3. sdio_register_driver(&r871xs_drv) 向SDIO核心注册驱动
  4. 成功:入口结束;失败:逆序释放资源、WiFi下电,返回错误

一句话

入口函数只做上电+资源初始化,向内核登记sdio_driver;芯片识别、创建wlan0是后续probe回调的事。

要点

  • 登记的是 struct sdio_driver
  • probe不由这里直接调用,由SDIO核心匹配设备后回调

rtw_drv_entry

  1. 找到 sdio_drvpriv.r871xs_drv 这个 struct sdio_driver
    重点看成员:

    • .id_table:设备匹配表(内核靠这个认出 RTL8189FTV)
    • .probe:匹配成功后执行的回调函数(核心!)
    • .remove:rmmod 时清理
  2. 进入 .probe 指向的函数(一般名字类似 rtw_sdio_probe / r871xs_sdio_probe)

  3. 这是干嘛的:静态初始化 SDIO 驱动登记表,告诉内核 SDIO 子系统,设备匹配上、卸载、休眠时分别调用哪个函数。

  4. 上一步为什么要用它:rtw_drv_entry里sdio_register_driver需要传入这份sdio_driver描述,完成驱动登记。

  5. 重点是什么:id_table是匹配钥匙;.probe=rtw_drv_init是匹配成功后进入的主函数。

  6. 下一步重点看哪里:rtw_drv_init(probe 回调)。

类比(平台驱动 platform_driver ↔ SDIO驱动 sdio_driver)

先一句话总:套路一模一样!都是【匹配表 + probe + remove】,只是总线不一样

  • platform_driver:平台总线(platform_bus),靠 dts compatible 匹配
  • sdio_driver:SDIO总线(sdio_bus),靠 sdio_ids 硬件ID匹配

对照表格

平台驱动(你写的platform示例) SDIO驱动(RTL8189FS这一段) 作用
of_device_id of_plat_match[] sdio_ids ✅匹配表:内核拿它去总线上找设备
.of_match_table = of_plat_match .id_table = sdio_ids 告诉驱动:用这张表做匹配
.probe = plat_probe .probe = rtw_drv_init 匹配成功,内核回调这个函数
.remove = plat_remove .remove = rtw_dev_remove 设备移除时回调
module_platform_driver(plat_driver) sdio_register_driver(&r871xs_drv) 向对应总线内核框架注册驱动
MODULE_DEVICE_TABLE(of, of_plat_match) SDIO这边也有MODULE_DEVICE_TABLE(sdio, sdio_ids) 导出匹配表,支持自动加载

通俗类比

  1. platform:
    设备树写 compatible="yhai-plat" → 内核在platform总线扫设备,找到匹配 → 跑 plat_probe,拿内存资源
  2. SDIO(RTL8189):
    SDIO总线扫描硬件,读出芯片的厂商ID+设备ID,和 sdio_ids 比对 → 匹配上 → 跑 rtw_drv_init,初始化WiFi芯片

唯一差别

  • platform:匹配依据是 dts里的compatible字符串(软件字符串匹配),一般用于片上外设(寄存器、GPIO)
  • SDIO:匹配依据是 硬件读到的SDIO vendor ID/device ID(硬件寄存器读出来的数值),SDIO外设(WiFi、蓝牙)

虚拟总线、硬件总线

platform 总线:软件虚拟总线,靠设备树 compatible 字符串匹配;
SDIO 总线:真实硬件总线,靠芯片硬件ID(vendor/device id)匹配。

两套驱动框架套路一致(注册驱动 + 匹配 + 回调probe),只是总线本身、匹配方式、使用场景不一样。

逐项对比

1. 总线性质

  • platform(平台总线):虚拟总线,没有真实硬件信号线
    内核虚拟出来的,专门管理SOC内部自带外设(寄存器、GPIO、时钟、片上控制器,比如Hi3516内部SDIO控制器、I2C控制器)。
    设备靠设备树DTS描述出来,不是总线自动扫描发现。
  • SDIO总线:真实物理总线
    有CLK、CMD、D0~D3实实在在的引脚;主控上电后,SDIO控制器会在总线上扫描,读取SDIO设备的硬件ID。
    RTL8189FTV 这个WiFi芯片,就是挂在这条物理SDIO线上。

2. 匹配规则(最容易考!)

  • platform:字符串匹配
    DTS里写 compatible = "yhai-plat"
    驱动 of_device_id 表里写一样的字符串,对上就匹配成功。 字符串是我们写代码、写dts自己定义的,随便起名,只要两边一致。
  • SDIO:硬件数值ID匹配
    SDIO Core去读芯片内部寄存器,读出厂商ID、设备ID,和驱动里 sdio_ids 的数值对比。 ID是芯片出厂固定写死在硬件里的,不能随便改。

3. 设备怎么来的

  • platform设备:人在DTS里静态写出来
    你不在dts写这个platform设备,内核就看不到这个设备,不会去匹配。总线不会自己去扫描硬件。
  • SDIO设备:SDIO控制器自动扫描总线发现
    只要板子上电、SDIO电源正常、芯片焊上去,SDIO控制器就会自动识别总线上挂着的SDIO设备,生成设备。

4. 典型使用场景

  • platform驱动:SOC内置控制器
    Hi3516的SDIO控制器、I2C、UART、GPIO、时钟、电源管理模块。
  • SDIO驱动:外接SDIO外设
    SDIO WiFi(RTL8189FTV)、SDIO蓝牙、SDIO摄像头。

5. 注册方式

  • platform:module_platform_driver(),内核宏封装,内部调用 platform_driver_register
  • SDIO:手动调用 sdio_register_driver()

相同点(别忘)

都是Linux设备驱动模型:

  1. 向总线注册驱动(登记表)
  2. 总线做匹配
  3. 匹配成功 → 内核回调probe
  4. 设备移除 → 回调remove

极简背诵版

  1. platform:虚拟总线,DTS字符串匹配,用来驱动SOC内部控制器;设备需要DTS手动描述。
  2. SDIO:真实物理总线,硬件ID数值匹配,用来驱动挂在SDIO线上的外设(WiFi);总线自动扫描发现设备。

platform 虚拟总线完整过程

先记住核心:platform总线是内核造出来的,没有真实扫描硬件的电路;设备不是扫出来的,是DTS"告诉"内核的

完整流程

  1. 你写设备树DTS,在soc节点下加一段platform设备:
dts 复制代码
yhai_plat@10000000 {
    compatible = "yhai-plat";
    reg = <0x10000000 0x1000>;
};
  • compatible:匹配字符串
  • reg:内存地址资源
  1. 内核启动解析DTS

    内核把这段节点转成一个platform_device设备对象,放到platform总线上。

  2. 加载驱动(insmod 或者自动加载)

    驱动里注册 platform_driver,附带 of_match_table(字符串匹配表)。

  3. platform总线的匹配逻辑(内核总线框架做)

    总线拿【platform_device里的compatible字符串】和【驱动of_match_table里的字符串】对比

  • 对上 → 回调驱动的 .probe
  • 对不上 → 不调用probe
  1. probe里面:用 platform_get_resource 拿DTS里的reg、中断等资源,初始化硬件寄存器。

和SDIO物理总线最关键差别

  • SDIO:上电 → SDIO控制器硬件自动扫描总线 → 读芯片硬件ID → 内核生成sdio设备 → 和sdio_ids数值匹配
  • platform:上电 → 解析DTS脚本 → 内核直接凭空创建platform_device → 字符串匹配

满足进入probe的前提条件

SDIO Core拿总线上扫出来的SDIO设备ID,和sdio_ids比对,匹配成功 → SDIO Core调用这个sdio_driver的 .probe,也就是 rtw_drv_init

给你按你习惯的四段总结:

  1. 这是干嘛的:给sdio_driver绑定SDIO ID匹配表,告诉内核:哪些SDIO芯片归我这个驱动管
  2. 上一步为什么要用它:sdio_register_driver注册驱动后,SDIO总线框架需要一张匹配表用来识别芯片
  3. 重点是什么:
    ✅ id_table是匹配钥匙;
    ✅ 匹配成功只是满足进入probe的前提条件;
  4. 下一步重点看哪里:rtw_drv_init(probe回调里面的初始化逻辑)

补充一句和platform对照:

  • platform:匹配 of_device_id(compatible字符串)→ 进probe
  • SDIO:匹配 sdio_ids(硬件Vendor/Device数值)→ 进probe

SDIO的probe回调函数 rtw_drv_init

  1. 这是干嘛的:SDIO的probe回调函数 rtw_drv_init,id_table匹配成功后内核自动调用;负责创建驱动内部管理对象、初始化适配器、注册wlan网卡、申请中断。
  2. 上一步为什么要用它:sdio_driver登记好,SDIO Core匹配芯片ID之后,就要跑这个函数,真正把WiFi设备初始化起来。
  3. 重点是什么:
  • sdio_dvobj_init:创建驱动顶层设备管理结构体(dvobj)
  • rtw_sdio_primary_adapter_init:WiFi适配器(adapter)初始化,RTL驱动核心管理单元
  • rtw_os_ndevs_init:注册net_device,生成wlan0
  • 失败是goto逆序释放资源,典型内核驱动写法
  1. 下一步重点看哪里:rtw_os_ndevs_init,里面做net_device注册、wiphy/cfg80211注册。

初始化 SDIO 底层设备对象sdio_dvobj_init

  1. 这是干嘛的:初始化 SDIO 底层设备对象 dvobj,绑定 SDIO 操作集、sdio_func,识别芯片型号,是 RTL 驱动最底层硬件接口初始化。
  2. 上一步为什么要用它:rtw_drv_init(probe)最先调用它,先把 SDIO 通道、驱动私有容器建好,后面才能创建 adapter、注册网卡。
  3. 重点是什么:
  • devobj_init():分配 dvobj_priv 结构体
  • dvobj->intf_ops = &sdio_ops:绑定 SDIO 收发、读写寄存器的底层函数集
  • sdio_set_drvdata(func, dvobj):内核 sdio_func 和驱动私有 dvobj 互相挂钩子(非常经典)
  • rtw_decide_chip_type_by_device_id:根据匹配到的 sdio_id 确定是哪款芯片(8189FTV)
  1. 下一步重点看哪里:sdio_init(dvobj),里面做 SDIO 总线配置、读芯片寄存器、固件相关初始化。

初始化SDIO主适配器函数:rtw_sdio_primary_adapter_init

一句话总述:adapter 是RTL驱动里WiFi的核心管理大对象,这个函数就是创建主WiFi适配器,配置HAL硬件层、读芯片信息、加载软件参数,硬件初始化的核心环节。

下面逐条解释刚才5项:

  1. 分配/取得核心结构体:padapter(_adapter)

    dvobj 是SDIO总线层面的设备容器(一个芯片一个dvobj);

    padapter 是WiFi业务适配器,管WiFi本身:扫描、连接、发包、加密、MLME状态。

    一个WiFi芯片至少有一个主adapter(primary),多AP/并发模式会再加虚拟adapter。

    rtw_zvmalloc:内核分配清零内存,拿到padapter。

  2. 调用的初始化函数含义(只简单解释作用,不钻子函数)

  • loadparam(padapter):加载驱动配置参数(速率、功率、国家码等)
  • rtw_set_hal_ops:绑定HAL层硬件操作函数集(读写寄存器、发送命令,HAL是芯片硬件抽象层)
  • rtw_init_io_priv:绑定SDIO收发接口,告诉adapter走SDIO总线和芯片通信
  • rtw_hal_read_chip_version:读芯片版本(8189FTV),区分不同版本固件
  • rtw_hal_chip_configure:芯片硬件基础配置(寄存器默认值)
  • rtw_hal_read_chip_info:读efuse,里面存MAC地址、校准信息
  • rtw_init_drv_sw:初始化驱动软件状态、链表、锁、定时器
  • rtw_hal_disable_interrupt:初始化阶段先关掉硬件中断,防止乱中断干扰初始化
  1. 初始化成功后调用谁

    返回padapter给上层 rtw_drv_init;

    rtw_drv_init拿到合法padapter之后,下一步执行:rtw_os_ndevs_init,这个函数就是注册Linux标准net_device、wiphy(cfg80211),生成wlan0网卡。

  2. 失败清理标签 free_hal_data / free_adapter

    内核驱动经典逆序清理:

  • free_hal_data:HAL硬件相关资源释放
  • free_adapter:把刚才rtw_zvmalloc分配的padapter内存释放掉,防止内存泄漏
    只要中间任意一步初始化失败,就顺着goto走到这里清理,不会留下半初始化对象。
  1. 返回值
    ✅成功:返回padapter指针,上层拿到这个适配器继续往下走网卡注册
    ❌失败:返回NULL,上层rtw_drv_init识别到NULL,跳到free_dvobj,销毁dvobj

函数:rtw_os_ndevs_init

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • 内部子函数里分配net_device、wiphy等网卡相关结构体(本层只调用,不直接分配)
  1. 调用的初始化函数
  • rtw_os_ndevs_alloc(dvobj):分配net_device、wiphy等网卡对象
  • rtw_os_ndevs_register(dvobj):向内核网络子系统、cfg80211注册网卡,生成wlan0
  1. 初始化成功后调用谁
    返回 _SUCCESS,回到上层 rtw_drv_init,继续后面的中断、GPIO等初始化
  2. 失败清理标签
    os_ndevs_free
    清理动作:rtw_os_ndevs_free,释放网卡相关结构体
  3. 返回值
    ✅成功:_SUCCESS
    ❌失败:_FAIL

简单解释

这是对接Linux内核网络框架的一层封装:

  • alloc:把 net_device(内核标准网卡结构)、wiphy(cfg80211无线框架结构)内存申请出来,绑定到adapter
  • register:注册到内核,系统里才会出现wlan0,上层wpa_supplicant、ifconfig才能识别这个无线网卡

前面dvobj、padapter都是驱动内部自家管理结构;走到这里,才把WiFi设备交给Linux标准网络子系统。

主线关系帮你串一下

  • dvobj:SDIO总线设备(管SDIO通道、芯片本身)
  • padapter:WiFi业务适配器(管无线功能、连接、收发)

顺序:先有dvobj → 再创建padapter → 再注册wlan0网卡给Linux网络子系统

主线回顾:

rtw_drv_init → sdio_dvobj_init → rtw_sdio_primary_adapter_init → rtw_os_ndevs_init(刚看完)

网卡注册

函数:rtw_os_ndevs_register

函数作用:把adapter对应的网卡注册到内核cfg80211无线框架与网络子系统,生成wlan0这类无线网卡设备;支持多网卡,注册失败时倒序注销已注册网卡,避免残留。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • regsty = dvobj_to_regsty(dvobj):拿到registry_priv(网卡名称、配置)
  • adapter:循环取出dvobj管理的各个adapter
  1. 调用的初始化函数
  • rtw_cfg80211_dev_res_register(dvobj):cfg80211无线框架资源注册
  • rtw_os_ndev_register(adapter, name):注册单个net_device,绑定网卡名称
  • 失败分支:rtw_os_ndev_unregister(adapter)、rtw_cfg80211_dev_res_unregister(dvobj)
  1. 初始化成功后调用谁
    返回 _SUCCESS,回到上层 rtw_os_ndevs_init
  2. 失败清理标签
    无goto标签,反向循环注销已经注册的网卡,同步释放cfg80211资源
  3. 返回值
    ✅成功:_SUCCESS
    ❌失败:_FAIL

清理函数

函数:rtw_os_ndevs_deinit

函数作用:网卡资源反向清理入口,先注销内核里的网卡设备,再释放网卡相关内存,和 rtw_os_ndevs_init 成对。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  1. 调用的初始化函数
  • rtw_os_ndevs_unregister(dvobj)
  • rtw_os_ndevs_free(dvobj)
  1. 初始化成功后调用谁
    执行完毕返回,回到上层清理分支
  2. 失败清理标签
    无goto标签,内部两个子函数依次做注销、释放
  3. 返回值
    void 无返回

函数:rtw_os_ndevs_unregister

函数作用:遍历所有adapter,逐个把net_device从内核网络子系统注销,同时注销cfg80211无线资源,系统不再识别wlan0。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • 循环取出 adapter
  1. 调用的初始化函数
  • rtw_os_ndev_unregister(adapter)
  • rtw_cfg80211_dev_res_unregister(dvobj)
  1. 初始化成功后调用谁
    返回,回到 rtw_os_ndevs_deinit,继续执行 rtw_os_ndevs_free
  2. 失败清理标签
    无
  3. 返回值
    void 无返回

函数:rtw_os_ndevs_free

函数作用:注销完成后,释放net_device、wiphy等网卡对象内存,释放cfg80211资源内存。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • 循环取出 adapter
  1. 调用的初始化函数
  • rtw_os_ndev_free(adapter)
  • rtw_cfg80211_dev_res_free(dvobj)
  1. 初始化成功后调用谁
    返回,回到 rtw_os_ndevs_deinit
  2. 失败清理标签
    无
  3. 返回值
    void 无返回

配对关系:

rtw_os_ndevs_init(alloc + register) ↔ rtw_os_ndevs_deinit(unregister + free)

顺序严格:先注册 → 后分配;释放时反过来:先注销 → 再释放

预先分配cfg80211资源

函数:rtw_os_ndevs_alloc

函数作用:预先分配cfg80211资源、逐个给adapter分配net_device(pnetdev)内存;分配失败时反向释放已经分配好的资源,防止内存泄漏。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • 循环取出 adapter
  1. 调用的初始化函数
  • rtw_cfg80211_dev_res_alloc(dvobj):分配cfg80211框架相关资源
  • rtw_os_ndev_alloc(adapter):为单个adapter分配net_device
  • 失败分支:rtw_os_ndev_free(adapter)、rtw_cfg80211_dev_res_free(dvobj)
  1. 初始化成功后调用谁
    返回 _SUCCESS,回到上层 rtw_os_ndevs_init,接着执行 rtw_os_ndevs_register
  2. 失败清理标签
    无goto标签,倒序释放已经分配好的net_device,同步释放cfg80211资源

串一下这一对:

rtw_os_ndevs_alloc:分配内存(net_device、wiphy)

rtw_os_ndevs_register:向内核注册设备,出现wlan0

顺序:alloc → register

清理顺序:unregister → free

主线回顾:

insmod → 注册sdio_driver → ID匹配 → probe(rtw_drv_init)

→ sdio_dvobj_init

→ rtw_sdio_primary_adapter_init

→ rtw_os_ndevs_init

  → rtw_os_ndevs_alloc(刚看完)

  → rtw_os_ndevs_register

函数:rtw_os_ndevs_register

函数作用:注册cfg80211无线框架资源,遍历adapter逐个注册net_device并绑定网卡名称;任意网卡注册失败时,倒序注销已经注册成功的网卡与cfg80211资源。

  1. 分配/取得核心结构体
  • 入参:struct dvobj_priv *dvobj
  • regsty = dvobj_to_regsty(dvobj):拿到registry_priv,存放网卡名称配置
  • adapter:循环取出dvobj下的各个WiFi适配器
  1. 调用的初始化函数
  • rtw_cfg80211_dev_res_register(dvobj):cfg80211资源注册
  • rtw_os_ndev_register(adapter, name):注册单个net_device,指定网卡名字(wlan0)
  • 失败分支:rtw_os_ndev_unregister(adapter)、rtw_cfg80211_dev_res_unregister(dvobj)
  1. 初始化成功后调用谁
    返回_SUCCESS,回到上层rtw_os_ndevs_init
  2. 失败清理标签
    无goto标签,循环反向注销网卡、释放cfg80211注册资源
  3. 返回值
    ✅成功:_SUCCESS
    ❌失败:_FAIL

主线位置:

rtw_os_ndevs_init → rtw_os_ndevs_alloc → rtw_os_ndevs_register

总结

RTL8189FS SDIO WiFi驱动完整流程总结(面试背诵版,分正向insmod + 反向rmmod)

正向流程:insmod rtl8189fs.ko

  1. 模块入口 :rtw_drv_entry,调用 sdio_register_driver,注册 sdio_driver
    • 里面绑定:id_table(sdio_ids)、.probe = rtw_drv_init、.remove = rtw_dev_remove
  2. SDIO总线匹配
    SDIO控制器硬件扫描SDIO设备,读取芯片Vendor/Device ID,和驱动里sdio_ids对比;匹配成功,内核回调probe:rtw_drv_init
  3. probe:rtw_drv_init(主线核心)
    1. sdio_dvobj_init:创建dvobj_priv(SDIO通道层容器,绑定sdio_func、识别芯片型号)
    2. rtw_sdio_primary_adapter_init:创建_adapter(WiFi业务适配器)
      • 分配adapter内存、加载驱动参数、绑定HAL硬件操作集
      • 读取芯片版本、efuse(MAC、校准参数)、初始化驱动软件资源
    3. rtw_os_ndevs_init(网卡注册总入口)
      • rtw_os_ndevs_alloc:分配cfg80211资源、分配每个adapter对应的net_device
      • rtw_os_ndevs_register:注册cfg80211无线框架 + net_device,内核生成wlan0
    4. 可选宏分支(你的配置下被关闭,灰色不编译):
      • sdio_alloc_irq:申请SDIO中断,芯片异步上报数据包/事件
      • GPIO唤醒、BT共存初始化
  4. probe全部成功,返回0;上层应用(wpa_supplicant、iw、ifconfig)可以操作wlan0,扫描、连接WiFi

✅ 关键结构体区分

  • dvobj_priv:SDIO芯片、总线通道层面,一个芯片一个dvobj
  • _adapter:WiFi业务管理对象,负责扫描、连接、收发报文
  • net_device:Linux内核标准网卡结构,内核网络子系统识别它
  • wiphy:cfg80211无线子系统对象,负责无线配置

反向流程:rmmod rtl8189fs.ko / probe中途失败goto清理

  1. 触发 .remove 回调 rtw_dev_remove;或者probe中间任意一步失败,走goto清理链
  2. rtw_os_ndevs_deinit(网卡清理入口,和rtw_os_ndevs_init成对)
    • rtw_os_ndevs_unregister:先把net_device、cfg80211从内核注销,系统看不到wlan0
    • rtw_os_ndevs_free:释放net_device、wiphy、cfg80211内存
  3. 释放adapter:rtw_sdio_primary_adapter_deinit
  4. 释放dvobj:sdio_dvobj_deinit
  5. 可选:释放中断sdio_free_irq、释放GPIO资源
  6. 最后 sdio_unregister_driver

顺序口诀(必背)

正向:注册驱动 → ID匹配 → probe

dvobj → adapter → alloc网卡 → register网卡(wlan0出现)→ 可选中断

反向:注销网卡 → 释放网卡 → 释放adapter → 释放dvobj

相关推荐
像风一样的男人@1 小时前
python --基于scp的文件传输助手
开发语言·python
m0_639906941 小时前
Java 重写 Override 与重载 Overload 区别、访问修饰符、static 关键字
java·开发语言
Evand J1 小时前
【MATLAB例程】平面下,5基站对待定位点的定位。基于距离辅助下的AOA。完整代码下载链接
开发语言·matlab·平面·定位·导航·测距·aoa
GIS数据转换器1 小时前
基于CIM的城市智慧供热监管服务平台
java·大数据·开发语言·人工智能·后端·物联网·struts
weixin_417396832 小时前
星级酒店境外电视频道怎么合法接入?合规要点与核对清单
开发语言·前端·php·1024程序员节·综合布线
熊猫钓鱼>_>2 小时前
鸿蒙PC构建实战:hvigor modelVersion 降级 + schema 清理 + ArkTS 编译三连坑完整复盘
开发语言·华为·ai编程·harmonyos·openharmony·数据·ai生成
FRing032 小时前
JavaScript入门到精通三十一(原型继承、原型链)
开发语言·前端·javascript
4SAPI2 小时前
会议智能处理大模型 API 生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
人工智能·架构·自动化·gateway·php
1024奇点2 小时前
【仓颉语言入门 · 第二篇 第 12 课】
开发语言·数据库·ide·mysql·开源