目录
2.3.3 新增wireless-bluetooth设备节点
0 前言
之前,我成功地为RK3399移植了U-Boot 2023.07、Linux 5.2.8内核以及Ubuntu 20.04.4的根文件系统。为了开发板的网络连接能力,本文章移植WIFI模块AP6356的驱动程序。在此,我将记录下整个学习和移植过程,以便于日后回顾和复习。同时,我也希望这些记录能够为其他正在进行类似工作的学习者提供一定的参考和帮助。
1 相关驱动介绍
SDIO总线和USB总线类似,SDIO也有两端,其中一端为主机(Host)端,另一端是设备端(Device),采用Host-Device这样的设计是为了简化Device的设计,所有的通信都由Host端发出命令开始、在Device端只要能解析Host发出的命令,就可以同Host进行通信了,SDIO的Host可以连接多个Device。
SDIO的驱动可以分为:
- SDIO主机控制器驱动:针对不同主机端的SDIO控制器的驱动;
- 主机端SDIO设备驱动:针对不同客户端的设备驱动程序。如SD卡、T-Flash卡、SDIO接口的GPS和WiFi等设备驱动;
而WiFi设备驱动实际上又涉及到多个驱动模块,比如rkfill驱动,802.11模块驱动、以及WiFI设备自身驱动。
1.1 rfkill驱动
rfkill驱动是和平台SoC有关联的的,一般是SoC厂家提供,这部分的功能是给RF设备(无线通信设备、比如蓝牙、WiFi)上下电使用,比如唤醒引脚配置,当RF设备接收到数据的时候用来唤醒系统,比如上下电引脚配置,用于开启/禁用RF设备,这部分的功能就集成在rfkill-wlan.c、rfkill-bt.c。
1.2 WiFI设备自身驱动
WiFI设备自身驱动一般都是由WiFi芯片厂家提供,比如WiFi芯片厂商瑞昱、博通;这部分是标准的基本不用修改,不管是NXP、Rockchip、全志等平台都是使用这一套代码。
2 设备树以及源码调整
由于linux 5.2.8 版本并没有直接支持AP6356,因此为了支持AP6356这里需要修改的内容比较多,因为这涉及到到了源码以及设备树的调整。
2.1 MMC驱动配置
2.1.1 新增sdio_pwrseq设备节点
修改arch/arm64/boot/dts/rockchip/rk3399-firefly.dts,在根节点下新增sdio_pwrseq设备节点。sdio_pwrseq设备节点节点用于控制WiFi功能的开启和关闭。
sdio_pwrseq: sdio-pwrseq {
compatible = "mmc-pwrseq-simple";
clocks = <&rk808 1>;
clock-names = "ext_clock";
pinctrl-names = "default";
pinctrl-0 = <&wifi_enable_h>;
/*
* On the module itself this is one of these (depending
* on the actual card populated):
* - SDIO_RESET_L_WL_REG_ON
* - PDN (power down when low)
*/
reset-gpios = <&gpio0 RK_PB2 GPIO_ACTIVE_LOW>;
};
rk3399-firefly.dts已经有sdio_pwrseq设备节点,无需再修改。
其中属性:
- clocks:指定了设备使用的时钟源,即使用rk808时钟控制器ID为1的时钟,rk808是一个时钟提供者clock provider;
- clock-names:指定了所使用的时钟的名称,即 "ext_clock";
- pinctrl-names:设置了引脚的默认状态,引脚配置设置为wifi_enable_h;
- pinctrl-0:指定了default状态的对应的引脚配置,即wifi_enable_h;
- reset-gpios:指定wlan芯片所使用的的引脚为GPIO0_B2(这个引脚是gpio0控制器的第 10 个引脚,GPIO0_B2连接的是AP6356 的WL_REG_ON引脚),低电平有效,即低电平时会关闭WiFi功能;
sdio_pwrseq设备节点对应的驱动文件为drivers/mmc/core/pwrseq_simple.c。
2.1.2 新增wifi_enable_h引脚配置节点
在引脚配置节点之前,我们需要了解RK3399 GPIO的一些基础知识,RK3399共有5组GPIO口,依次为GPIO0~GPIO4;每一组又以A0~A7、B0~B7、C0~C7、D0~D7作为编号区分。
由于sdio_pwrseq节点配置了default状态对应的引脚配置节点为wifi_enable_h,因此需要在pinctrl节点下增加引脚配置节点wifi_enable_h:
sdio-pwrseq {
wifi_enable_h: wifi-enable-h {
rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
rk3399-firefly.dts已配置,无需再修改。
其中属性rockchip,pins = <PIN_BANK PIN_BANK_IDX MUX &phandle>的意义如下:
- PIN_BANK:引脚所在的 bank;GPIO0~GPIO3依次对应0~3;
- PIN_BANK_IDX:引脚所在bank的引脚号;0~31,;
- MUX:功能复用配置,0 表示普通 GPIO,1-N 表示特殊的功能复用;
- phandle:引脚的电气特性,例如内部上拉、电流强度等;
因此此处配置GPIO0_B2引脚功能为GPIO,电气特性为pcfg_pull_none,表示普通配置。
2.1.3 新增sdio0设备节点属性
sdio0设备节点已经在arch/arm64/boot/dts/rockchip/rk3399.dtsi文件中定义,在根节点下我们可以找到:
sdio0: dwmmc@fe310000 {
compatible = "rockchip,rk3399-dw-mshc",
"rockchip,rk3288-dw-mshc";
reg = <0x0 0xfe310000 0x0 0x4000>;
interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>;
max-frequency = <150000000>;
clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
<&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
fifo-depth = <0x100>;
power-domains = <&power RK3399_PD_SDIOAUDIO>;
resets = <&cru SRST_SDIO0>;
reset-names = "reset";
status = "disabled";
};
同时可以在pinctrl节点下可以找到sdio0引脚配置节点:
sdio0 {
sdio0_bus1: sdio0-bus1 {
rockchip,pins =
<2 RK_PC4 1 &pcfg_pull_up>;
};
sdio0_bus4: sdio0-bus4 {
rockchip,pins =
<2 RK_PC4 1 &pcfg_pull_up>,
<2 RK_PC5 1 &pcfg_pull_up>,
<2 RK_PC6 1 &pcfg_pull_up>,
<2 RK_PC7 1 &pcfg_pull_up>;
};
sdio0_cmd: sdio0-cmd {
rockchip,pins =
<2 RK_PD0 1 &pcfg_pull_up>;
};
sdio0_clk: sdio0-clk {
rockchip,pins =
<2 RK_PD1 1 &pcfg_pull_none>;
};
sdio0_cd: sdio0-cd {
rockchip,pins =
<2 RK_PD2 1 &pcfg_pull_up>;
};
sdio0_pwr: sdio0-pwr {
rockchip,pins =
<2 RK_PD3 1 &pcfg_pull_up>;
};
sdio0_bkpwr: sdio0-bkpwr {
rockchip,pins =
<2 RK_PD4 1 &pcfg_pull_up>;
};
sdio0_wp: sdio0-wp {
rockchip,pins =
<0 RK_PA3 1 &pcfg_pull_up>;
};
sdio0_int: sdio0-int {
rockchip,pins =
<0 RK_PA4 1 &pcfg_pull_up>;
};
};
这里我们需要在arch/arm64/boot/dts/rockchip/rk3399-firefly.dts文件为sdio0设备节点新增属性:
&sdio0 {
/* WiFi & BT combo module Ampak AP6356S */
clock-frequency = <150000000>;
clock-freq-min-max = <200000 150000000>;
supports-sdio;
bus-width = <4>;
disable-wp;
cap-sdio-irq;
cap-sd-highspeed;
keep-power-in-suspend;
mmc-pwrseq = <&sdio_pwrseq>;
non-removable;
num-slots = <1>;
pinctrl-names = "default";
pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>;
sd-uhs-sdr104;
/* Power supply */
vqmmc-supply = &vcc1v8_s3; /* IO line */
vmmc-supply = &vcc_sdio; /* card's power */
status = "okay";
};
rk3399-firefly.dts已配置,无需再修改。
2.2 WiFi驱动配置
Rockchip官方平台linux版本(4.4、4.19、5.10)实现了一套兼容多款WiFi芯片的自适应框架,位于drivers/net/wireless/rockchip_wlan路径下,简单来说就是将多款WiFi芯片的驱动编译成xxx.ok文件,比如常用的WiFi芯片厂商瑞昱、博通的WiFi芯片的驱动统一编译成不同的xxx.ko文件。
但是我们使用的Linux 5.2.8 版本并没有drivers/net/wireless/rockchip_wlan这个文件夹,因此我们将这个文件夹从Rockchip官方平台linux版本拷贝过来。
AP6356使用的是博通BCM4356方案,所以在linux中,使能brcmfmac模块驱动即可。
2.2.1 配置brcmfmac驱动
需要配置内核支持brcmfmac驱动:
Device Drivers --->
[*] Network device support --->
[*] Wireless LAN --->
[*] Broadcom devices
< > Broadcom 43xx wireless support (mac80211 stack) # CONFIG_B43
< > Broadcom 43xx-legacy wireless support (mac80211 stack) # CONFIG_B43LEGACY
< > Broadcom IEEE802.11n PCIe SoftMAC WLAN driver # CONFIG_BRCMSMAC 支持较老的芯片
[M] Broadcom FullMAC WLAN driver # CONFIG_BRCMFMAC 支持较新的芯片
[*] SDIO bus interface support for FullMAC driver # CONFIG_BRCMFMAC_SDIO
[ ] USB bus interface support for FullMAC driver # CONFIG_BRCMFMAC_USB
[*] Broadcom device tracing # CONFIG_BRCM_TRACING
[*] Broadcom driver debug functions # CONFIG_BRCMDBG
由于brcmfmac驱动会加载固件/lib/firmware/brcm/brcmfmac4356-sdio.bin,因此这里只能将Broadcom FullMAC WLAN driver编译成模块。
如果编译进内核,由于加载固件在根文件系统挂载之前,导致出现无法找到固件文件的错误。
2.2.2 配置IEEE 802.11驱动
需要配置内核支持IEEE 802.11驱动:
[*] Networking support --->
-*- Wireless --->
<M> cfg80211 - wireless configuration API
cfg80211.ko驱动模块同样需要加载固件/lib/firmware/regulatory.db,因此这里也只能将cfg80211 - wireless configuration API编译成模块。
2.2.3 固件下载
固件使用armbian提供的:https://github.com/armbian/firmware/tree/master/brcm;
我们把文件全部下载下来,这里面也包含了固件firmware/regulatory.db,下载完成后在内核启动后我们将这些文件拷贝到开发板根文件系统的/lib/目录中。
git clone https://github.com/armbian/firmware.git --depth 1
2.3 rfkill驱动配置
2.3.1 源码拷贝
将Rockchip官方linux 4.19版本:net/rfkill/rfkill-bt.c文件复制到linux 5.2.8 net/rfkill路径下;
将Rockchip官方linux 5.10版本:net/rfkill/rfkill-wlan.c、rfkill-bt.h、rfkill-wlan.h等文件复制到linux 5.2.8 net/rfkill路径下。
cp /RK3399/linux-4.19/net/rfkill/rfkill-bt.c /RK3399/linux-5.2.8/net/rfkill/
cp /RK3399/linux-5.10/net/rfkill/rfkill-wlan.c /RK3399/linux-5.2.8/net/rfkill/
cp /RK3399/linux-5.10/include/linux/rfkill-bt.h /RK3399/linux-5.2.8/include/linux/
cp /RK3399/linux-5.10/include/linux/rfkill-wlan.h /RK3399/linux-5.2.8/include/linux/
cp /RK3399/linux-5.10/include/linux/wakelock.h /RK3399/linux-5.2.8/include/linux/
cp -R /RK3399/linux-5.10/include/linux/rockchip /RK3399/linux-5.2.8/include/linux/
cp -R /RK3399/linux-5.10/include/linux/soc/rockchip /RK3399/linux-5.2.8/include/linux/soc
修改net/rfkill/Makefile添加如下代码:
rfkill-rk-y += rfkill-wlan.o rfkill-bt.o
obj-$(CONFIG_RFKILL_RK) += rfkill-rk.o
修改net/rfkill/Kconfig添加如下代码:
config RFKILL_RK
tristate "Rockchip RFKILL driver"
depends on RFKILL
depends on MMC
depends on ARCH_ROCKCHIP
default n
help
Rockchip rfkill driver for rk29/rk3X
2.3.2 新增wireless-wlan设备节点
为了支持rfkill WiFi驱动,在arch/arm64/boot/dts/rockchip/rk3399-firefly.dts根节点下新增:
wireless-wlan {
compatible = "wlan-platdata";
rockchip,grf = <&grf>;
wifi_chip_type = "ap6356";
sdio_vref = <1800>;
WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO0_a3 */
status = "okay";
};
这里比较重要的属性:
- wifi_chip_type:指定WiFi芯片型号;
- WIFI,host_wake_irq:指定WiFi芯片唤醒主机的中断引脚,这里配置的位RK399 GPIO0_A3引脚;
2.3.3 新增wireless-bluetooth设备节点
为了支持rfkill蓝牙驱动,我们需要在arch/arm64/boot/dts/rockchip/rk3399-firefly.dts根节点下新增:
wireless-bluetooth {
compatible = "bluetooth-platdata";
clocks = <&rk808 1>;
clock-names = "ext_clock";
//wifi-bt-power-toggle;
uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */
pinctrl-names = "default", "rts_gpio";
pinctrl-0 = <&uart0_rts>;
pinctrl-1 = <&uart0_gpios>;
//BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */
BT,reset_gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; /* GPIO0_B1 */
BT,wake_gpio = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */
BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */
status = "okay";
};
其中:
- clocks:指定了设备使用的时钟源,即使用rk808时钟控制器ID为1的时钟,rk808是一个时钟提供者clock provider;
- clock-names:指定了所使用的时钟的名称,即 "ext_clock";
- uart_rts_gpios:表示控制蓝牙串口请求发送引脚UART_CTS_N为GPIO2_C3,低电平有效;
- pinctrl-names:表示该设备节点支持两种pinctrl模式,分别为default和rts_gpio;
- pinctrl-0:设置default状态对应的引脚配置为uart0_rts;uart0_rts定义GPIO2_C3功能复用为UART RST;
- pinctrl-1:表设置rts_gpio状态对应的引脚配置为uart0_gpios;uart0_gpios定义GPIO2_C3功能复用为GPIO;
- BT,reset_gpio:配置蓝牙复位引脚BT_REG_ON为GPIO0_B1,高电平有效;
- BT,wake_gpio:配置主机唤醒蓝牙设备引脚BT_WAKE为GPIO2_D2,高电平有效;
- BT,wake_host_irq:配置蓝牙设备唤醒主机引脚BT_HOST_WAKE为GPIO0_A4,高电平有效;
- status:表示该设备状态为正常运行;
// 这个也是要新增的,默认没有这个配置 在&pinctrl下新增该子节点
wireless-bluetooth {
uart0_gpios: uart0-gpios {
rockchip,pins = <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
2.3.4 配置内核
需要配置内核:
[*] Networking support --->
<*> RF switch subsystem support --->
<*> Rockchip RFKILL driver
2.4 rk808驱动配置
在arch/arm64/boot/dts/rockchip/rk3399-firefly.dts中添加如下节点:
&i2c0 {
clock-frequency = <400000>;
i2c-scl-rising-time-ns = <160>;
i2c-scl-falling-time-ns = <30>;
status = "okay";
vdd_cpu_b: regulator@40 {
compatible = "silergy,syr827";
reg = <0x40>;
fcs,suspend-voltage-selector = <1>;
pinctrl-names = "default";
pinctrl-0 = <&cpu_b_sleep>;
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1500000>;
regulator-name = "vdd_cpu_b";
regulator-ramp-delay = <1000>;
vin-supply = <&vcc3v3_sys>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vdd_gpu: regulator@41 {
compatible = "silergy,syr828";
reg = <0x41>;
fcs,suspend-voltage-selector = <1>;
pinctrl-names = "default";
pinctrl-0 = <&gpu_sleep>;
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <712500>;
regulator-max-microvolt = <1500000>;
regulator-name = "vdd_gpu";
regulator-ramp-delay = <1000>;
vin-supply = <&vcc3v3_sys>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
rk808: pmic@1b {
compatible = "rockchip,rk808";
reg = <0x1b>;
clock-output-names = "xin32k", "rtc_clko_wifi";
#clock-cells = <1>;
interrupt-parent = <&gpio1>;
interrupts = <21 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pmic_int_l>;
rockchip,system-power-controller;
wakeup-source;
vcc1-supply = <&vcc3v3_sys>;
vcc2-supply = <&vcc3v3_sys>;
vcc3-supply = <&vcc3v3_sys>;
vcc4-supply = <&vcc3v3_sys>;
vcc6-supply = <&vcc3v3_sys>;
vcc7-supply = <&vcc3v3_sys>;
vcc8-supply = <&vcc3v3_sys>;
vcc9-supply = <&vcc3v3_sys>;
vcc10-supply = <&vcc3v3_sys>;
vcc11-supply = <&vcc3v3_sys>;
vcc12-supply = <&vcc3v3_sys>;
vddio-supply = <&vcc_3v0>;
regulators {
vdd_center: DCDC_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-name = "vdd_center";
regulator-ramp-delay = <6001>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vdd_cpu_l: DCDC_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <750000>;
regulator-max-microvolt = <1350000>;
regulator-name = "vdd_cpu_l";
regulator-ramp-delay = <6001>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_ddr: DCDC_REG3 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc_ddr";
regulator-state-mem {
regulator-on-in-suspend;
};
};
vcc_1v8: DCDC_REG4 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcc_1v8";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1800000>;
};
};
vcc1v8_cam: LDO_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcc1v8_cam";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc3v0_touch: LDO_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcc3v0_touch";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc1v8_pmupll: LDO_REG3 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcc1v8_pmupll";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1800000>;
};
};
vcc_sdio: LDO_REG4 {
regulator-always-on;
regulator-boot-on;
regulator-init-microvolt = <3000000>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-name = "vcc_sdio";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
};
};
vcca3v0_codec: LDO_REG5 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcca3v0_codec";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_1v5: LDO_REG6 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1500000>;
regulator-max-microvolt = <1500000>;
regulator-name = "vcc_1v5";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <1500000>;
};
};
vcca1v8_codec: LDO_REG7 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-name = "vcca1v8_codec";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc_3v0: LDO_REG8 {
regulator-always-on;
regulator-boot-on;
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-name = "vcc_3v0";
regulator-state-mem {
regulator-on-in-suspend;
regulator-suspend-microvolt = <3000000>;
};
};
vcc3v3_s3: SWITCH_REG1 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc3v3_s3";
regulator-state-mem {
regulator-off-in-suspend;
};
};
vcc3v3_s0: SWITCH_REG2 {
regulator-always-on;
regulator-boot-on;
regulator-name = "vcc3v3_s0";
regulator-state-mem {
regulator-off-in-suspend;
};
};
};
};
};
rk3399-firefly.dts已配置,无需再修改。
修改&pinctrl下pmic引脚配置节点:
pmic {
cpu_b_sleep: cpu-b-sleep {
rockchip,pins = <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
};
gpu_sleep: gpu-sleep {
rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
};
};
2.5 配置内核
配置完内核之后保存配置为firefly-rk3399_defconfig
存档:
mv firefly-rk3399_defconfig ./arch/arm64/configs/
重新配置内核:
make firefly-rk3399_defconfig
2.6 编译内核
在linux内核根目录下执行如下命令进行编译内核:
make -j8
出现错误!!!
3 解决错误
执行make出现以下错误:
In file included from net/rfkill/rfkill-wlan.c:27:
./include/linux/wakelock.h: 在函数‘wake_lock_destroy’中:
./include/linux/wakelock.h:44:9: 错误: implicit declaration of function ‘wakeup_source_trash’; did you mean ‘wakeup_source_add’? [-Werror=implicit-function-declaration]
44 | wakeup_source_trash(&lock->ws);
| ^~~~~~~~~~~~~~~~~~~
| wakeup_source_add
In file included from net/rfkill/rfkill-bt.c:27:
./include/linux/wakelock.h: 在函数‘wake_lock_destroy’中:
./include/linux/wakelock.h:44:9: 错误: implicit declaration of function ‘wakeup_source_trash’; did you mean ‘wakeup_source_add’? [-Werror=implicit-function-declaration]
44 | wakeup_source_trash(&lock->ws);
| ^~~~~~~~~~~~~~~~~~~
| wakeup_source_add
不存在wakeup_source_trash函数,也就是linux 5.2.8 内核中没有这个函数,但是我们从Rockchip官方linux 5.10版本拷贝的rfkill-wlan.h文件使用到这个函数,没有我们就添加。在查看官方linux 5.10 源码后发现,wakeup_source_trash函数定义在include/inux/pm_wakeup.h,函数的原型为:
static inline void wakeup_source_trash(struct wakeup_source *ws)
{
wakeup_source_remove(ws);
wakeup_source_drop(ws);
}
在linux 5.2.8 的源码也在相应文件上添加这个函数。
继续make,继续报错!!!我们添加的wakeup_source_trash函数中使用到wakeup_source_drop函数,linux 5.2.8 的源码没有wakeup_source_drop函数,没有我们就继续添加,在文件include/inux/pm_wakeup.h中添加声明:
static inline void wakeup_source_drop(struct wakeup_source *ws) {}
extern void wakeup_source_drop(struct wakeup_source *ws);
在文件drivers/base/power/wakeip.c中添加定义:
/**
* wakeup_source_drop - Prepare a struct wakeup_source object for destruction.
* @ws: Wakeup source to prepare for destruction.
*
* Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never
* be run in parallel with this function for the same wakeup source object.
*/
void wakeup_source_drop(struct wakeup_source *ws)
{
if (!ws)
return;
__pm_relax(ws);
}
EXPORT_SYMBOL_GPL(wakeup_source_drop);
继续make,继续报错!!!出现以下错误:
aarch64-none-linux-gnu-ld: warning: .tmp_vmlinux1 has a LOAD segment with RWX permissions
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_set_carddetect':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): undefined reference to `mmc_host_rescan'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `mmc_host_rescan'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_power':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): undefined reference to `primary_sdio_host'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against undefined symbol `primary_sdio_host'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0x100): undefined reference to `primary_sdio_host'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `get_wifi_addr_vendor':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:484:(.text.unlikely+0xbac): undefined reference to `is_rk_vendor_ready'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:484:(.text.unlikely+0xbac): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `is_rk_vendor_ready'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:489:(.text.unlikely+0xbd4): undefined reference to `rk_vendor_read'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:489:(.text.unlikely+0xbd4): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `rk_vendor_read'
make: *** [Makefile:1055:vmlinux] 错误 1
没有rk_vendor_read、is_rk_vendor_ready、get_wifi_addr_vendor等函数,发现这些函数定义在rk_vendor_storage.c文件中,而在linux 5.2.8中并没有这个文件,所以:
sudo cp ../kernel-5.10/drivers/soc/rockchip/rk_vendor_storage.c ./drivers/soc/rockchip/
并修改/drivers/soc/rockchip/目录下的Makefile添加:
obj-y += rk_vendor_storage.o
继续make,继续报错!!!出现以下错误:
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_set_carddetect':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): undefined reference to `mmc_host_rescan'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `mmc_host_rescan'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_power':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): undefined reference to `primary_sdio_host'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against undefined symbol `primary_sdio_host'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0x100): undefined reference to `primary_sdio_host'
make: *** [Makefile:1055:vmlinux] 错误 1
不存在mmc_host_rescan函数,linux 5.2.8内核中没有这个函数,该函数定义在drivers/mmc/core/host.c文件,原型为:
/**
* mmc_host_rescan - triger software rescan flow
* @host: mmc host
*
* rescan slot attach in the assigned host.
* If @host is NULL, default rescan primary_sdio_host
* saved by mmc_add_host().
* OR, rescan host from argument.
*
*/
int mmc_host_rescan(struct mmc_host *host, int val, int is_cap_sdio_irq)
{
if (NULL != primary_sdio_host) {
if (!host)
host = primary_sdio_host;
else
pr_info("%s: mmc_host_rescan pass in host from argument!\n",
mmc_hostname(host));
} else {
pr_err("sdio: host isn't initialization successfully.\n");
return -ENOMEDIUM;
}
pr_info("%s:mmc host rescan start!\n", mmc_hostname(host));
/* 0: oob 1:cap-sdio-irq */
if (is_cap_sdio_irq == 1) {
host->caps |= MMC_CAP_SDIO_IRQ;
} else if (is_cap_sdio_irq == 0) {
host->caps &= ~MMC_CAP_SDIO_IRQ;
} else {
dev_err(&host->class_dev, "sdio: host doesn't identify oob or sdio_irq mode!\n");
return -ENOMEDIUM;
}
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->set_sdio_status)
host->ops->set_sdio_status(host, val);
return 0;
}
EXPORT_SYMBOL(mmc_host_rescan);
因为linux 5.2.8 没有 host->ops->set_sdio_status,为了不修改过多造成更多的错误,我就将它删除if,等移植后看看有没有问题,没有问题就不管,主打一个能跑就行。
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->set_sdio_status)
host->ops->set_sdio_status(host, val);
在linux-5.2.8目录下drivers/mmc/core/host.c文件添加以下内容:
/**
* mmc_host_rescan - triger software rescan flow
* @host: mmc host
*
* rescan slot attach in the assigned host.
* If @host is NULL, default rescan primary_sdio_host
* saved by mmc_add_host().
* OR, rescan host from argument.
*
*/
int mmc_host_rescan(struct mmc_host *host, int val, int is_cap_sdio_irq)
{
if (NULL != primary_sdio_host) {
if (!host)
host = primary_sdio_host;
else
pr_info("%s: mmc_host_rescan pass in host from argument!\n",
mmc_hostname(host));
} else {
pr_err("sdio: host isn't initialization successfully.\n");
return -ENOMEDIUM;
}
pr_info("%s:mmc host rescan start!\n", mmc_hostname(host));
/* 0: oob 1:cap-sdio-irq */
if (is_cap_sdio_irq == 1) {
host->caps |= MMC_CAP_SDIO_IRQ;
} else if (is_cap_sdio_irq == 0) {
host->caps &= ~MMC_CAP_SDIO_IRQ;
} else {
dev_err(&host->class_dev, "sdio: host doesn't identify oob or sdio_irq mode!\n");
return -ENOMEDIUM;
}
return 0;
}
EXPORT_SYMBOL(mmc_host_rescan);
mmc_host_rescan函数中有使用到primary_sdio_host,linux 5.2.8 内核并没有定义,添加定义:
struct mmc_host *primary_sdio_host;
对比了Rockchip官方linux 5.10版本,primary_sdio_host在mmc_add_host函数下有使用到:
if (host->restrict_caps & RESTRICT_CARD_TYPE_SDIO)
primary_sdio_host = host;
因为linux 5.2.8 没有定义host->restrict_caps和RESTRICT_CARD_TYPE_SDIO,为了不改动过多,我就将它删除if,直接添加到mmc_add_host函数里:
primary_sdio_host = host;
继续make,没有报错,至此编译通过,成功生成Image。
4 烧录测试
4.1 下载内核和WiFi固件
通过上面的流程我们得到:
boot.img
linux-5.2.8/firmware
linux-5.2.8/modules.builtin
linux-5.2.8/modules.order
linux-5.2.8/net/wireless/cfg80211.ko
linux-5.2.8/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko
linux-5.2.8/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko
将内核镜像boot.img烧录到eMMC第0x8000个扇区处,启动板子,启动内核。
我们将内核编译出的模块打包放到根文件系统/lib/modules/{uname -r命令查看}下面,内核启动时会自动加载,加载顺序如下;
- IEEE 802.11模块:net/wireless/cfg80211.ko;
- brcm驱动使用的工具:drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko;
- brcm驱动:drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko;
将以上文件拷贝到:
mkdir -p /lib/modules/5.2.8
可以使用多种方式拷贝
firmware --> 开发板/lib
modules.builtin --> 开发板/lib/modules/5.2.8
modules.order --> 开发板/lib/modules/5.2.8
cfg80211.ko --> 开发板/lib/modules/5.2.8
brcmutil.ko --> 开发板/lib/modules/5.2.8
brcmfmac.ko --> 开发板/lib/modules/5.2.8
内核启动的时候,使用/sbin/modprobe加载/lib/modules/{uname -r命令查看}下面的模块,modules.order文件指定了模块的加载顺序。需要在加载模块之前建立该模块的依赖关系,也即必须用depmod来更新一下/lib/modules/$(uname -r)/modules.dep 文件:
在开发板/lib/modules/5.2.8下执行:
depmod -a
4.2 测试
4.2.1 查看网卡
先运行ifconfig查看网络设备信息:
eth0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 8e:3c:ec:3a:60:82 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 27
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 173 bytes 13727 (13.7 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 173 bytes 13727 (13.7 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlan0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether d4:12:43:82:2c:2c txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
可以看到有一个无线网卡,网卡名为wlan0。
4.2.2 扫描wifi
我们使用nmcli device wifi list命令扫描无线网:
IN-USE BSSID SSID MODE CHAN RATE SIGNAL BARS SE
CURITY
C6:44:44:0E:70:51 K40 Infra 11
270 Mbit/s 39 ▂▄__ WPA2
7E:36:BD:87:55:2B NK Infra 11
130 Mbit/s 37 ▂▄__ WPA2
F8:32:E4:D6:07:B0 nb Infra 1
270 Mbit/s 25 ▂___ WPA2
FA:32:E4:D6:07:B0 nb2 Infra 14
9 270 Mbit/s 24 ▂___ WPA2
这里扫描到一个SSID为K40的无线网。我们尝试连接它:
nmcli device wifi connect "K40" password "1234567890"
我们再次查看网络设备信息:
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::24e4:6ddb:bd69:98f1 prefixlen 64 scopeid 0x20<link>
inet6 2408:8459:6a10:23e0:9aa8:69ac:9049:756a prefixlen 64 scopeid 0x0<global>
inet6 2408:8459:6a10:23e0:6cad:9b9:3d7d:ee87 prefixlen 64 scopeid 0x0<global>
ether d4:12:43:82:2c:2c txqueuelen 1000 (Ethernet)
RX packets 52 bytes 6974 (6.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 115 bytes 17264 (17.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4.2.3 网络测试
然后测试连接互联网:
ping www.baidu.com
可以看到网络是通的:
PING www.baidu.com(2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b)) 56 data bytes
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=1 ttl=51 time=98.9 ms
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=2 ttl=51 time=140 ms
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=3 ttl=51 time=398 ms
^C
--- www.baidu.com ping statistics ---
4 packets transmitted, 3 received, 25% packet loss, time 3005ms
rtt min/avg/max/mdev = 98.857/212.139/397.593/132.205 ms
参考文章
Rockchip RK3399 - WiFi AP6356驱动 - 大奥特曼打小怪兽 - 博客园 (cnblogs.com)
Rockchip基于RK3566/RK3568 WiFi AP6256调试笔记
RK3399驱动开发 | 13 - AP6356 SDIO WiFi 调试(基于linux4.5.104内核)
[RK3399] SDIO 接口 Wifi 驱动流程分析 (AP6354)
[物联网篇 ] 15 -博通AP6255模块中WL_HOST_WAKE功能