上图为多款wifi的ko文件,比如8188eu.ko、8189es.ko。
android系统启动之后,系统启动一个服务,读取u***接口的wifi或者sdio接口的wifi的pid和vid。然后和代码里面写死的pid和vid进行比较,确认是那款wifi,比如我们这里是rtl8821cu,所以识别出来rtl8821cu然后调用insmod加载8821cu.ko的wifi驱动
大致流程如下:
开机对 wifi 模块上电,并自动进行扫描 sdio 操作。
系统启动打开 wifi 操作时,分别对系统 sys/bus/sdio(sdio wifi)
sys/bus/pic (pcie wifi )文件系统下的 uevent 进行读取。
获取到 wifi 芯片 vid pid 加载相应的 wifi ko 驱动。
识别到 wifi 类型后加载不同的 wpa_supplicant 参数启动 wifi。
实战移植USB wifi RTL8821CU
RTL8821CU简介
RTL8821CU是瑞昱
半导体推出的u***接口的wifi,下面的教程将介绍如何将RTL8821CU移植到rk3399的平台上。
移植u*** wifi需要修改的文件
Android hal层需要修改的文件
frameworks/opt/net/wifi/libwifi_hal/rk_wifi_ctrl.cpp
frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
//kernel需要修改的文件
kernel/arch/arm64/boot/dts/rockchip/rk3399-tve1030g.dtsi //除去板上的sdio的wifi
kernel/arch/arm64/configs/rockchip_defconfig //添加rtl8821CU的驱动生成模块的方式,生成rtl8821cu.ko
kernel/drivers/net/wireless/rockchip_wlan/Kconfig //将wifi驱动放置到kernel/drivers/net/wireless/rockchip_wlan这个目录下,需要修改Kconfig和Makefile
kernel/drivers/net/wireless/rockchip_wlan/Makefile
kernel/drivers/net/wireless/rockchip_wlan/rtl8821CU/ //rtl8821CU的驱动,一般这部分驱动由原厂,或者wifi模组供应商提供
rfkill-wlan.c的作用
wifi驱动分为两部分,一部分是厂家提供的wifi驱动部分,这部分是标准的基本不用修改,不管是NXP,ROCKCHIP,全志等平台都是使用这一套代码,比如kernel/drivers/net/wireless/rockchip_wlan/rtl8821CU/。另一部分是和平台SOC有差异的,这部分一般是SOC厂家提供,这部分的功能是给WIFI模组上下电使用,比如配置唤醒引脚配置,当数据来的时候用来唤醒系统,比如上下电配置,用户可以使用节点给bt进行上电,这部分的功能就集成在rfkill-wlan.c。
wifi唤醒系统中断
系统进入休眠模式,如果wifi接收到数据,这时候需要唤醒系统,然后通知系统区读取数据。在硬件设计上有一个gpio的输入引脚接连接wifi模块和soc(WIFI_WAKE_HOST,上面的原理图已经说明),在linux中需要把这个引脚设置为中断引脚,然后注册一个中断服务函数,然后在中断中将系统唤醒。
下面的代码以Realtek来展示:
static int rtw_drv_init( struct sdio_func *func,const struct sdio_device_id *id)
gpio_hostwakeup_alloc_irq(if1);
err = request_threaded_irq(oob_irq, gpio_hostwakeup_irq_thread, NULL,
status, "rtw_wifi_gpio_wakeup", padapter); //注册gpio中断函数,wifi接收到数据的时候会产生中断
static irqreturn_t gpio_hostwakeup_irq_thread(int irq, void *data)
{
PADAPTER padapter = (PADAPTER)data;
DBG_871X_LEVEL(_drv_always_, "gpio_hostwakeup_irq_threadn");
/* Disable interrupt before calling handler */
//disable_irq_nosync(oob_irq);
rtw_lock_suspend_timeout(HZ/2); //唤醒系统
#ifdef CONFIG_PLATFORM_ARM_SUN6I
return 0;
#else
return IRQ_HANDLED;
#endif
}
如何获取pid 和 vid
将u*** wifi插到rk3399的
开发板上,然后输入lsu***
通过和没插入u*** wifi之前输入的lsu***对比,插入后多了上图红色圈出来的设备,所以我们可以知道的u*** wifi rtl8821cu的pid为0bda,vid为c811,我们将这两个pid和vid记下来到到后面使用。
修改hal层
我们前面说了系统启动后会扫描u*** wifi的pid和vid然后在代码里找到是那款wifi芯片,然后找到wifi芯片的.ko文件的路径,这一部分的代码在
frameworks/opt/net/wifi/libwifi_hal/rk_wifi_ctrl.cpp
frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
我们在rk_wifi_ctrl.cpp添加rtl8821cu的pid和vid(红色方框为需要添加的)。
在wifi_hal_common.cpp添加rtl8821cu.ko文件的路径,系统启动的时候通过获取到pid和vip匹配之后,根据数组static wifi_device supported_wifi_devices找到"RTL8821CU"这个名字,然后用这个名字从数组wifi_ko_file_name module_list里面获取到RTL8821CU_DRIVER_MODULE_PATH这个宏,这个宏就是指定rtl8821的wifi驱动ko的位置。
#define RTL8821CU_DRIVER_MODULE_PATH WIFI_MODULE_PATH"8821cu.ko" //指定rtl8821的wifi驱动ko的位置
#define WIFI_MODULE_PATH "/vendor/lib/modules/"
后面就是加载不同的 wpa_supplicant 参数启动 wifi。
修改dts除去sdio wifi
dts路径:kernel/arch/arm64/boot/dts/rockchip/rk3399-tve1030g.dtsi
因为为的板子上有sdio接口的wifi,如果不除去,同时有u*** wifi和sdio wifi可能会产生冲突,所以我们先disable sdio的接口的wifi。
将rtl8816的wifi驱动放到kernel里面
我们将rtl8821CU wifi驱动复制到kernel/drivers/net/wireless/rockchip_wlan/这个目录下面,同时修改当前目录下的Kconfig文件和Makefile文件。
修改Makefile将rtl8821CU添加进去(红色方框为需要添加的)。
修改Kconfig(红色方框为需要添加的)
重新编译内核
我们修改完成全部的需要修改的文件后,需要从新编译内核和安卓系统,编译成功后确认确认rtl8821.ko文件是否打包到了vendor.img。我们可以在outtargetproductrk3399vendorlibmodules目录下查看是否有8821cu.ko这个文件,如果有这个文件就证明已经打包完成。
烧录
全部工作完成后,就可以烧录全部的img,烧录完成后等待开机,然后在Android界面设置选项wifi设置那里可以搜索到wifi信号,然后就可以像正常的使用Android
手机一样使用wifi上网了。
问题排查
1.首先确认u*** wifi已经正常。
如果通过lsu***,可以获取到pid和vip证明u***通讯是正常的,sdio的wifi也是这个原理,首先证明sdio和wifi上电是正常的先。
2.加载ko文件的时候可能因为疏忽,导致加载ko文件路径出错,如果加载正常,正常的log应该如下。
05-28 08:38:44.954 259 259 I -service: Wifi Hal is booting up...
05-28 08:39:03.808 399 399 I wificond: wificond is starting up...
06-03 05:44:35.551 466 466 I SystemServiceManager: Starting com.android.server.wifi.WifiService
06-03 05:44:35.724 466 466 I SystemServiceManager: Starting com.android.server.wifi.scanner.WifiScanningService
06-03 05:44:35.726 466 466 I SystemServiceManager: Starting com.android.server.wifi.p2p.WifiP2pService
06-03 05:44:35.746 466 466 D ConnectivityService: wifiOnly=true
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 0
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 2
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 3
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 4
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 5
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 10
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 11
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 12
06-03 05:44:35.746 466 466 D ConnectivityService: networkAttributes - ignoring mobile as this dev is wifiOnly 15
06-03 05:44:36.369 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:44:59.016 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:45:02.142 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:45:04.605 466 466 V SettingsProvider: Notifying for 0: content://settings/global/wifi_on
06-03 05:45:04.609 259 259 E -service: Kernel version is 4.4.: Success
06-03 05:45:04.609 259 259 E -service: Wifi driver is not ready.: Success
06-03 05:45:04.613 259 259 E -service: found device pid:vid :0bda:c811: Permission denied
06-03 05:45:04.614 259 259 E -service: check_wifi_chip_type_string : RTL8821CU: Permission denied
06-03 05:45:04.614 259 259 E -service: Error changing group ownership of/data/misc/wifi/wifi_chipto1010:Operation not permitted: Operation not permitted
06-03 05:45:04.614 259 259 E -service: matched ko file path /vendor/lib/modules/8821cu.ko: Operation not permitted
06-03 05:45:04.861 259 259 E -service: Kernel version is 4.4.: Operation not permitted
06-03 05:45:04.862 259 259 E -service: Wifi driver is ready for now...: Operation not permitted
06-03 05:45:04.865 259 259 I -service: Wifi HAL started
06-03 05:45:05.894 259 259 E -service: check_wifi_chip_type_string : RTL8821CU: Operation not permitted
06-03 05:45:05.898 259 259 I -service: Adding interface handle for wlan0
06-03 05:45:05.898 259 259 I -service: Adding interface handle for p2p0
06-03 05:45:05.898 259 259 E -service: Failed to register radio mode change callback
06-03 05:45:05.898 259 259 I -service: Configured chip in mode 0
06-03 05:45:05.899 259 259 E -service: Failed to set DFS flag; DFS channels may be unavailable.
06-03 05:45:05.920 399 399 I wificond: create scanner for interface with index: 6
06-03 05:45:05.920 399 399 I wificond: subscribe scan result for interface with index: 6
06-03 05:45:05.924 399 399 E wificond: No Offload Service available
06-03 05:45:05.924 399 399 I wificond: Offload HAL not supported
06-03 05:45:06.007 1576 1576 E /vendor/bin/hw/wpa_supplicant: check_wifi_chip_type_string : RTL8821CU: Try again
06-03 05:45:06.007 259 259 E -service: check_wifi_chip_type_string : RTL8821CU: Operation not permitted
06-03 05:45:06.041 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:45:06.045 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:45:06.136 1576 1576 I wpa_supplicant: Copied template conf file from /vendor/etc/wifi/wpa_supplicant.conf to /data/vendor/wifi/wpa/p2p_supplicant.conf
06-03 05:45:06.136 1576 1576 I wpa_supplicant: Use /vendor/etc/wifi/p2p_supplicant_overlay.conf
06-03 05:51:25.778 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:51:34.933 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:51:39.704 466 481 W BroadcastQueue: Permission Denial: broadcasting Intent { act=android.net.wifi.STATE_CHANGE flg=0x4000010 (has extras) } from null (pid=-1, uid=-1) requires android.permission.CHANGE_NETWORK_STATE due to registered receiver BroadcastFilter{72da8f9 u0 ReceiverList{588e3c0 861 com.android.settings/1000/u0 remote:a4ac143}}
06-03 05:51:39.705 466 481 W BroadcastQueue: Permission Denial: broadcasting Intent { act=android.net.wifi.STATE_CHANGE flg=0x4000010 (has extras) } from null (pid=-1, uid=-1) requires android.permission.CHANGE_NETWORK_STATE due to registered receiver BroadcastFilter{3f87bec u0 ReceiverList{cce2e9f 861 com.android.settings/1000/u0 remote:183003e}}
06-03 05:51:41.425 466 485 E BatteryExternalStatsWorker: no controller energy info supplied for wifi
06-03 05:57:10.567 861 861 D SettingsActivity: Switching to fragment com.android.settings.wifi.WifiSettings
06-03 05:57:10.567 861 861 D SubSettings: Launching fragment com.android.settings.wifi.WifiSettings
06-03 05:57:26.046 399 399 W wificond: Scan is not started. Ignore abort request
wifi驱动怎么知道使用那个sdio控制器
读到这里不知道你们是不是有疑问,我们的SOC上有很多的sdio控制器,有些sdio控制器接emmc flash,有些接wifi模块,wifi驱动怎么知道我接在那个sdio控制器上呢?
其实是在wifi厂商提供的驱动里面会注册一个sdio_register_driver,当上电的时候我的emmc驱动会去扫描sdio的外围设备,当确认扫描到设备的时候会去读取设备的id即wifi模块的pid和vid,如果设备id匹配上了之后会调用使用sdio_register_driver注册进去的probe函数,从而确认我们的设备是接到那个sdio设备上。代码流程如下所示。
代码路径:kerneldriversnetwirelessrockchip_wlanrtl8189fsos_deplinuxsdio_intf.c
static const struct sdio_device_id sdio_ids[] = {
#ifdef CONFIG_RTL8723B
{ SDIO_DEVICE(0x024c, 0xB723), .driver_data = RTL8723B}, //wifi设备pid 和 vid
#endif
};
static struct sdio_drv_priv sdio_drvpriv = {
.r871xs_drv.probe = rtw_drv_init,
.r871xs_drv.remove = rtw_dev_remove,
.r871xs_drv.name = (char*)DRV_NAME,
.r871xs_drv.id_table = sdio_ids,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
.r871xs_drv.drv = {
.pm = &rtw_sdio_pm_ops,
}
#endif
};
ret = sdio_register_driver(&sdio_drvpriv.r871xs_drv); //将wifi设备注册进sdio,sdio驱动扫描到wifi外围设备后会读取pid和vid,然后匹配上了会调用rtw_drv_init函数。
u*** wifi和sdio的wifi也是一样的,wifi模块插入u***口的时候,会读取u***模块的vid和pid,如果读取到的vid和pid和使用u***_register注册进去的设备的vid和pid是一样证明驱动匹配上了,然后调用probe函数。下面是Realtek u*** wifi部分注册代码。
代码路径:kerneldriversnetwirelessrockchip_wlanrtl8188fuos_deplinuxu***_intf.c
static struct u***_device_id rtw_u***_id_tbl[] ={
#ifdef CONFIG_RTL8188E
/*=== Realtek demoboard ===*/
{USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8179),.driver_info = RTL8188E}, /* 8188EUS */ //u*** wifi 的pid和vid,使用匹配驱动。
{USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179),.driver_info = RTL8188E}, /* 8188ETV */
};
struct rtw_u***_drv u***_drv = {
.u***drv.name =(char*)DRV_NAME,
.u***drv.probe = rtw_drv_init,
.u***drv.disconnect = rtw_dev_remove,
.u***drv.id_table = rtw_u***_id_tbl,
.u***drv.suspend = rtw_suspend,
.u***drv.resume = rtw_resume,
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22))
.u***drv.reset_resume = rtw_resume,
#endif
#ifdef CONFIG_AUTOSUSPEND
.u***drv.supports_autosuspend = 1,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19))
.u***drv.drvwrap.driver.shutdown = rtw_dev_shutdown,
#else
.u***drv.driver.shutdown = rtw_dev_shutdown,
#endif
};
ret = u***_register(&u***_drv.u***drv);
wifi射频测试之wl
射频测试的时候一般要使用一个指令wl,这个指令可以设置wifi模块的发射和接收等,然后通过综测仪等仪器可以用来测试wifi的性能,wl指令一般可以向wifi模组供应商索要,一般会给到源码,然后使用交叉编译工具链进行编译,编译好后push的Android系统中,然后使用指令进行测试。
wifi驱动排查步骤
1、linux sdio驱动无法识别(读取)wifi vendro ID硬件排查步骤。
1.1、第一步首先是排查电压,电压供电是否稳定,有没有电压被拉低的情况,一般来说wifi有如下几组电压。
VDD3.3 -> 3.3V的主电压。
DVDDIO_1.8V -> sdio IO的1.8v电压。
1.2、如果wifi模组没有晶振,时钟来自外部,则需要排查模组是否有32k的时钟输入,一般来说这个32k的时钟来自host,但是也可以使用外部的时钟芯片来提供,具体看原理图的实现。
1.3、排查chip_en脚有没有拉高。
1.4、如果上面的步骤都已经排查完毕,但是sdio驱动还是无法获取到wifi的vendor ID,可以排查别的脚是不是被别的电平干扰。
比如在调试amlogic W1芯片的时候,主控的32k接到了BT_WAKE_HOST引脚,本来这个引脚对主控来说是WIFI用来唤醒深度睡眠的HOST的,由于dts没有修改按照官方的默认配置,这个引脚输出了32k HZ的时钟,导致了W1进入了内部的测试模式,导致无法读取到vendor ID,或者sdio发送cmd没有响应。
的实现。
1.3、排查chip_en脚有没有拉高。
1.4、如果上面的步骤都已经排查完毕,但是sdio驱动还是无法获取到wifi的vendor ID,可以排查别的脚是不是被别的电平干扰。
比如在调试amlogic W1芯片的时候,主控的32k接到了BT_WAKE_HOST引脚,本来这个引脚对主控来说是WIFI用来唤醒深度睡眠的HOST的,由于dts没有修改按照官方的默认配置,这个引脚输出了32k HZ的时钟,导致了W1进入了内部的测试模式,导致无法读取到vendor ID,或者sdio发送cmd没有响应。
原作者:嵌入式知识大讲堂