CL嵌入式linux学习:第一章 SSV6256wifi驱动移植到RK3308
文章目录
前言
目前5G wifi越来越普及,给需要无线联网的设备添加5G wifi模块成为一种必要,尤其在iot物联网,5G wifi速度和连接数都相对2.4G有巨大的优势,所以这里用5G wifi模块ssc6256的驱动移植来引入嵌入式linux的学习
一、SSV6256驱动包
使用哪家的wifi模块,一般会有供应商提供驱动包,我这里有我使用的驱动包,链接如下:
链接: ssv6256驱动包
二、移植步骤
大部分操作在<南方硅谷ssv6x5x 驱动移植用户指南.doc>中也有说明,我这里对一些细节进行补充
1.将ssv驱动引入rk3308 SDK
驱动包中有个压缩包,其中LSMAC.19Q3.2126.02就是ssv6x5x原始驱动
将LSMAC.19Q3.2126.02解压出来,文件夹改名为ssv6256,然后放到rk3308的sdk下
./kernel/drivers/net/wireless/rockchip_wlan/ssv6256
就此ssv的驱动就引入到rk3308的sdk中了
2.将SSV驱动引入RK3308编译环境
为了让RK3308的编译系统能直接编译ssv驱动,需要修改相关Makefile和Kconfig
ssv6256驱动是放在"./kernel/drivers/net/wireless/rockchip_wlan/"目录下,所以需要修改rockchip_wlan目录下的Makefile和Kconfig
rockchip_wlan/Kconfig需要添加:
source “drivers/net/wireless/rockchip_wlan/ssv6256/Kconfig”
将ssv6256/Kconfig包含进系统
ssv6256/Kconfig内容如下
#menu "iComm-semi 6X5X WLAN support"
config SSV6X5X
tristate "SSV6X5X Wireless driver"
depends on MAC80211 && MMC
---help---
Enable iComm-semi SSV6X5X WLAN kernel driver.
#endmenu
这样make menuconfig中将会有SSV6X5X的选项
对应到Makefile中,就是CONFIG_SSV6X5X
rockchip_wlan/Makefile需要添加:
obj-$(CONFIG_SSV6X5X) += ssv6256/
表示如果有定义CONFIG_SSV6X5X,就添加./ssv6256目录进行编译
rockchip_wlan/ssv6256/Makefile需要添加:
#获取当前makefile的绝对路径
LAST_MAKEFILE_PATH :=$(abspath $(lastword $(MAKEFILE_LIST)))
#获取当前makefile所在文件夹的绝对路径
SSV_DRV_PATH := $(dir $(LAST_MAKEFILE_PATH))
用来给ssv的makefile定位自己的所在位置,让ssv驱动正确的编译
如果不添加这句,$(PWD)获取的就是rk3308编译脚本所在的目录会导致编译异常
最后复制驱动到sdk的时候ssv6256目录下的parser-conf.sh可能会权限异常,无法执行,需要修改权限
cd ./kernel/drivers/net/wireless/rockchip_wlan/ssv6256
chmod 777 ./parser-conf.sh
然后这个时候在make menuconfig中将SSV6X5X设置为M,或者在defconfig中添加
CONFIG_SSV6X5X=m
就可以将ssv的驱动编译成ko文件了
当然也可以直接设置成y,打包进linux系统中,但是打包进内核需要确认stacfgpath和code相关默认配置
具体可以看驱动包中的<南方硅谷ssv6x5x 驱动移植用户指南.doc>
3.ssv驱动配置以及添加网络唤醒功能
1>内核配置
内核需要支持MAC80211和cfg80211,我直接设置成y,编译进内核中
2>增加p2p interface,用于AP模式使用这个网卡
需要在linux/net/mac80211/main.c中添加:
/* add one default STA interface if supported */
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION))
{
result = ieee80211_if_add(local, "wlan%d", NULL,NL80211_IFTYPE_STATION, NULL);
if (result)
wiphy_warn(local->hw.wiphy,"Failed to add default virtual iface\n");
}
/*增加*/
if (local->hw.wiphy->interface_modes
& (BIT(NL80211_IFTYPE_P2P_GO)|BIT(NL80211_IFTYPE_P2P_CLIENT)))
{
result = ieee80211_if_add(local, "p2p%d", NULL,NL80211_IFTYPE_STATION, NULL);
if (result)
wiphy_warn(local->hw.wiphy,"Failed to add default virtual iface\n");
}
就会在驱动加载后多一个p2p0网卡,可用于打开ap热点
3>网络唤醒
网络唤醒主要需要修改./ssv6x5x-generic-wlan.c文件
主要就是在模块初始化接口中,添加唤醒中断的注册,然后休眠后,wifi模块收到网络信号,就会拉高wifi唤醒引脚,然后触发系统唤醒中断,让系统唤醒.
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/rfkill-wlan.h>
#include <linux/wakelock.h>
#include <linux/delay.h>
#include <linux/gpio.h>
extern int get_wifi_chip_type(void);
extern int rockchip_wifi_power(int on);
extern int rockchip_wifi_set_carddetect(int val);
static struct wake_lock ssv6x5x_wlock; //唤醒锁
unsigned int oob_irq; //wifi中断句柄
/*
* wifi唤醒引脚中断的回调函数
*/
static irqreturn_t gpio_hostwakeup_irq_thread(int irq, void *data)
{
printk(KERN_INFO "gpio_hostwakeup_irq_thread\n");
wake_lock_timeout(&ssv6x5x_wlock, (HZ/2)); //阻止系统休眠,0.5s后自动释放
//android_lock_suspend_auto_expire(&rtw_suspend_lock, rtw_ms_to_systime(timeout_ms));
return IRQ_HANDLED;
}
//驱动模块初始化入口
static int generic_wifi_init_module(void)
{
int err;
u32 status = 0;
wake_lock_init(&ssv6x5x_wlock, WAKE_LOCK_SUSPEND, "ssv6x5x_wakelock");
oob_irq = rockchip_wifi_get_oob_irq(); //获取wifi唤醒中断句柄,这个中断在设备树中配置
if (oob_irq == 0)
{
printk(KERN_INFO "oob_irq ZERO!\n");
return -1;
}
enable_irq_wake(oob_irq); //使能中断唤醒功能,rk3308只有gpio0的中断才支持休眠唤醒
printk(KERN_INFO "%s : oob_irq = %d\n", __func__, oob_irq);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32))
status = IRQF_NO_SUSPEND;
#endif
/*高电平有效为1,低电平有效为0
*ssv模块内部没有可以配置引脚默认电平的地方,所以这里都是应该设置为高电平有效
*/
if (1)
{
status |= IRQF_TRIGGER_RISING; //上升沿触发中断
}
else
{
status |= IRQF_TRIGGER_FALLING; //下降沿触发中断
}
err = request_threaded_irq(oob_irq, gpio_hostwakeup_irq_thread, NULL,
status, "rtw_wifi_gpio_wakeup", (void*)1);
if (err < 0)
{
printk(KERN_INFO "Oops: can't allocate gpio irq %d err:%d\n", oob_irq, err);
return -1;
}
else
{
printk(KERN_INFO "allocate gpio irq %d ok\n", oob_irq);
}
return initWlan(); //原本只有这一句,上面的都是添加的,用来wifi唤醒
}
//模块卸载出口
static void generic_wifi_exit_module(void)
{
exitWlan(); //原来自由这一句,下面都是添加的,用来释放wifi唤醒相关资源
if (oob_irq == 0)
return;
free_irq(oob_irq, (void*)1);
disable_irq_wake(oob_irq);
wake_lock_destroy(&ssv6x5x_wlock);
}
// #ifdef CONFIG_SSV6X5X //CONFIG_SSV6XXX=y //注释掉原来的
#if 0
late_initcall(generic_wifi_init_module); //因为我是设置成m,作为模块使用,所以去掉这个系统初始化接口注册
#else //CONFIG_SSV6XXX=m or =n
module_init(generic_wifi_init_module);
#endif
module_exit(generic_wifi_exit_module);
MODULE_LICENSE("Dual BSD/GPL");
wifi唤醒gpio的设置在设备树中,我这里使用的是GPIO0 PA0
提示:rk3308只有gpio0才能支持wifi唤醒
/ {
wireless_wlan: wireless-wlan {
compatible = "wlan-platdata";
rockchip,grf = <&grf>;
pinctrl-names = "default";
pinctrl-0 = <&wifi_wake_host>;
wifi_chip_type = "rtl8189es";
WIFI,host_wake_irq = <&gpio0 RK_PA0 GPIO_ACTIVE_HIGH>;
status = "okay";
};
};
&pinctrl {
wireless-wlan {
wifi_wake_host: wifi-wake-host {
rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
};
然后根据<SSV6x5x 休眠唤醒功能说明.pdf>中
在./ssv6x5x-wifi.cfg中添加
lp_wakeuppin = 1
#lp_keepalive = 120000
fw_reset = 0
powermode = 4
#lp_beaconloss_maxcnt = 10
#lp_beaconloss_guard = 40
#lp_dataretx_guard = 40
lp_userdtim = 3
并确定wif模块晶振频率
xtal_clock = 24
最后将ssv6x5x-wifi.cfg和编译好的复制打包到文件系统中
开机运行
insmod ssv6x5x.ko stacfgpath=/home/ssv6x5x-wifi.cfg
然后驱动基本移植完成,具体细节可以看<南方硅谷ssv6x5x 驱动移植用户指南.doc>
4.其他
在实际使用中发现ssv6256需要提前供电,可能是必须要在sdio初始化前就让ssv6256上电,我是修改到了uboot初始化的时候,对ssv6256的使能引脚进行了初始化
其次sta模式和ap模式必须再同一频段才能共存,否则在sta模式下用hostapd是打不开ap模式的
总结
以上就是一直ssv6256到rk3308的过程,其他wifi驱动也大同小异,重点需要注意的就是wifi唤醒和模块上电逻辑,处理不好会有一些奇怪的问题,然后wifi唤醒最好是多个路由器尝试,有些路由器可能会有唤醒不了的情况,换个路由器就正常了,具体原因没有深究,有大佬知道原因,可以互相交流下