前言
去年用这个来学习的时候,一眼看过去啥也不会,不知道怎么分析、怎么解决,现在学习了这一系列的内核模块驱动知识,再来看看会不会解决;
目前已有的信息
DuoS 的 AIC8800 驱动: https://github.com/milkv-duo/duo-buildroot-sdk-v2/tree/develop/linux_5.10/drivers/net/wireless/aicsemi
Commit 信息:https://github.com/milkv-duo/duo-buildroot-sdk-v2/commit/527e5cd68e11d46f32eefc1778096b850fb5d796
未修改的原厂内核代码。
试试?
我们其实并不要去完全读懂网卡代码是什么,我们只需要通过和已有的网卡对比差异就可以大概了解到怎么去做了。
从commit信息,我们大概可以读到差异,差异的主要内容其实就是:给power口gpio发数据,让它知道已经上线,没了。是的,就这么简单。
所以,我们该怎么开机啊 😭
从方法追踪,上电!
我们先从被修改过的厂商代码进行下手,看 aic8800_bsp/aicsdio.c 这个文件。不要被一大坨乱七八糟的东西吓怕了,我们要关注的点只有差异部分,即,这两坨东西到底是什么。
根据方法名称可以大概了解到, cvi_sdio_rescan 应该是指重新扫描 WiFi 之类的,而
cvi_get_wifi_pwr_on_gpio
应该就是拿到 power 口的 gpio了,然后手动将它设置为高电平,然后扫描 WiFi cvi_sdio_rescan
,再设置为低电平。原厂的修改我们大概了解了,接下来看看上面提到的两个方法在哪里实现。
可以看到,
cvi_get_wifi_pwr_on_gpio
在这里实现了:https://github.com/milkv-duo/duo-buildroot-sdk-v2/blob/develop/linux_5.10/drivers/soc/cvitek/wifi_pin/cvi_wifi_pin.c#L39cvi_get_wifi_pwr_on_gpio
其实就是简单地返回了一下结构体的 power_gpio 信息,而设置 power_gpio 信息的代码在 cvi_wifi_pin_probe
,可以看到代码为哦哟,还顺便设置了个 wakeup-gpio ,可能后面有用。那么,这个gpio口到底是怎么拿到的?看这个np,我也不知道具体是谁调用的这个方法传了个pdev,这让我怎么查?不如先查查这个 of_get_named_gpio 是怎么实现的?于是找到了 linux/of_gpio.h ,发现是这样的,首先这个 np 是个 device_node ,猜测应该和设备树脱不了关系。
在这个头文件没有提供,那我们继续深入查找
./linux_5.10/drivers/gpio/gpiolib-of.c
额,它还在往下传递,那就继续翻,翻到 of_parse_phandle_with_args_map 这个,找到一个 Example ,哇,这不就是我们的设备树dts吗
of_for_each_phandle
其实是一个循环,任务就是遍历 list_name
,只有 list_name
匹配时才会进入循环;当 cur_index == index
时,内核就知道它已经找到了我想要的条目,而填充 out_args
就是将其 np
指向 phandle
所指向的节点,找到匹配的条目后返回0,最后返回了一个 out_args
,而这个东西往上传递,变成了 struct gpio_desc
,最后经过 desc_to_gpio
,它最终变成了gpio号,存到了 power_gpio
的变量之中。那问题就简化成了:这个
poweron-gpio
到底是在哪里提供的呢?刚刚不是说过了嘛,它给了个example,看起来就很像设备树,所以我们就返回去找DuoS的设备树就好就是这!再回去看看这个 compatible 和哪个代码匹配
好了,至此我们已经知道上电的gpio口是怎么找的了,以及它和什么东西匹配。
从方法追踪,rescan?
这还有个
cvi_sdio_rescan
需要我们研究一下,这到底是个啥呢,还是一样,我们跟剥洋葱一样,找到它的代码,在 drivers/mmc/host/cvitek/sdhci-cv181x.c:548但我没搞懂,这不是wifi吗,和mmc有什么关系?
其实很多嵌入式设备的WiFi模块,并不是通过USB或者PCIe连接的,而是通过 SDIO 接口,它是SD接口的一个扩展,允许连接除了存储卡之外的IO设备,比如WiFi、蓝牙、GPS模块等。从驱动视角来看,SDIO WiFi 模块就像一张永不拔出的特殊SD卡。
一般,一个标准的SD卡槽会有一个物理的卡检测引脚,插入一张SD卡时,CD引脚的电平会发生变化,产生一个中断;MMC主机驱动收到这个中断时,就会发起一次rescan(有点类似于在uboot时候的 mmc rescan),识别这张新卡是什么,如何驱动。但是没有CD引脚时,比如嵌入式电路板,WiFi模块是直接焊死在主板上的,工程师可能不会连接CD引脚,那么MMC主机控制器永远等不到那个卡插入的中断信号,不会主动扫描总线,也就无法发现那个已经焊好的WiFi模块。所以,我们就手动给它触发一下。
mmc_detect_change(wifi_mmc, 0); 其实就是让MMC子系统在稍后的时间点扫描 wifi_mmc 代表的总线,后面的0表示延迟。
wifi_mmc->rescan_entered = 0; 这个标志位,在设置时用来防止重复的扫描请求,手动清零则强制使其重新扫描。
问题又来了,这个 wifi_mmc 从哪里来呢?
不用说,有dev_name,肯定跟设备树又脱不了关系,回去找设备树。
你看……诶不对啊,它这个是 &wifisd ,你读取的是 wifi-sd ,对不上啊?别急,上面还有一串 include ,我们顺着找一下
这下都串起来了。我们最后理解一下:
首先,在 base.dtsi 这里,先定义了节点标签 wifisd ,然后定义了节点名称 wifi-sd ,然后指定了单元地址 4320000 。然后,在 milkv_duos.dts ,对 wifisd 节点进行追加,设置各种属性,也就是非常基础的节点会定义在芯片级的 dtsi 文件中,而针对特定电路板的修改会放在板级的 .dts 文件中。
总结
DuoS 针对 aic8800 网卡驱动的修改,无非就两步,添加对 power button 一次开关的操作,再对网卡硬件进行重新扫描。其实就相当于我们按一下主机开关,然后检查一下硬件有没有起来。之前接触的时候发现啥也看不懂,现在重新看先不论会不会写,起码会读了。