OpenHarmony实战:瑞芯微RK3566移植案例(下)

OpenHarmony实战:瑞芯微RK3566移植案例(下)

OpenHarmony实战:瑞芯微RK3566移植案例(中)


WIFI

整改思路及实现流程
整改思路

接下来熟悉HCS文件的格式以及"HDF WIFI”核心驱动框架的代码启动初始化过程,参考hi3881的代码进行改造。

HDF WiFi框架总体框架图

WLAN驱动架构组成:

img

ap6256驱动代码流程分析
驱动模块初始化流程分析

img

Ap6256 是一款SDIO设备WiFi模组驱动,使用标准Linux的SDIO设备驱动。内核模块初始化入口module_init()调用dhd_wifi_platform_load_sdio()函数进行初始化工作,这里调用wifi_platform_set_power()进行GPIO上电,调用dhd_wlan_set_carddetect()进行探测SDIO设备卡,最后调用sdio_register_driver(&bcmsdh_sdmmc_driver);进行SDIO设备驱动的注册,SDIO总线已经检测到WiFi模块设备,根据设备号和厂商号与该设备驱动匹配, 所以立即回调该驱动的bcmsdh_sdmmc_probe()函数,这里进行WiFi模组芯片的初始化工作,最后创建net_device网络接口wlan0,然后注册到Linux内核协议栈中。

下面对其中比较重要的函数进行举例分析:

(1) dhd_bus_register函数,主要实现sdio设备的注册,通过回调dhd_sdio中的相关函数,对wifi模块进行驱动注册等相关操作。

img

其中函数bcmsdh_register将静态结构体变量dhd_sdio赋值给静态结构体drvinfo,然后通过函数bcmsdh_register_client_driver调用函数sdio_register_driver向系统注册sdio接口驱动。

img

img

当sdio设备与sdio总线进行匹配后,会回调函数bcmsdh_sdmmc_probe,函数bcmsdh_sdmmc_probe会进一步回调dhd_sdio结构体中的成员函数dhdsdio_probe。

(2) dhdsdio_probe函数,主要实现net_device对象(wlan0)的创建,以及wireless_dev对象创建,并与net_device对象的成员ieee80211_ptr进行关联,给net_device对象的操作方法成员netdev_ops赋值,最后将net_device对象注册到协议栈中。

  • 创建net_device网络接口wlan0对象

dhd_allocate_if()会调用alloc_etherdev()创建net_device对象,即wlan0网络接口。wl_cfg80211_attach()会创建wireless_dev对象,并将wireless_dev对象赋值给net_device对象的成员ieee80211_ptr。

  • 将wlan0注册到内核协议栈

调用dhd_register_if()函数,这里将net_device_ops操作方法的实例dhd_ops_pri赋值给net_device对象的成员netdev_ops,然后调用register_netdev(net);将net_device对象wlan0网络接口注册到协议栈。

整改代码适配HDF WiFi框架

对于系统WiFi功能的使用,需要实现AP模式、STA模式、P2P三种主流模式,这里使用wpa_supplicant应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现STA模式和P2P模式的功能,使用hostapd应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现AP模式和P2P模式的功能。

Ap6256 WiFi6内核驱动依赖platform能力,主要包括SDIO总线的通讯能力;与用户态通信依赖HDF WiFi框架的能力,在确保上述能力功能正常后,即可开始本次WiFi驱动的HDF适配移植工作。本文档基于已经开源的rk3568开源版代码为基础版本,来进行此次移植。

适配移植ap6256 WiFi驱动涉及到的文件和目录如下:

3.1 WIFI相关的HDF框架编译控制宏

ap6256采用的是sdio总线,涉及到的通用编译控制宏如下:

CONFIG_DRIVERS_HDF_PLATFORM_SDIO=y

CONFIG_DRIVERS_HDF_PLATFORM_MMC=y

CONFIG_DRIVERS_HDF_WIFI=y

CONFIG_DRIVERS_HDF_STORAGE=y

3.2 具体WiFi设备驱动编译控制宏

涉及到wifi设备驱动的编译控制宏位于drivers/adapter/khdf/linux/model/network/wifi/Kconfig中,其中主要涉及到编译控制宏如下:

CONFIG_DRIVERS_HDF_NETDEV_EXT=y

CONFIG_AP6XXX_WIFI6_HDF=y

编译控制选项CONFIG_AP6XXX_WIFI6_HDF,内容如下:

config AP6XXX_WIFI6_HDF

tristate "support ap6xxx wifi6(80211ax) HDF"

depends on DRIVERS_HDF_WIFI

​ select CFG80211

​ select MAC80211

​ select DRIVERS_HDF_NETDEV_EXT

help

This driver supports wifi6 for ap6xxx HDF chipset.

This driver uses the kernel's wireless extensions subsystem.

If you choose to build a module, it'll be called dhd. Say M if unsure.

NOTE:此处为了保证框架侧与社区代码一致,不建议修改,设置CONFIG_AP6XXX_WIFI6_HDF的配置即可。

3.3 修改编译规则Makefile文件,添加ap6256驱动的源码位置

在drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile文件,添加如下内容:

ifneq ($(CONFIG_AP6XXX_WIFI6_HDF),)

#RKWIFI_PATH := (HDFVENDORPREFIX)/device/(���������������)/������/(product_company)/$(product_device)/wifi

RKWIFI_PATH := $(HDF_VENDOR_PREFIX)/device/kaihong/rk3568-khdvk/wifi //修改添加部分

obj-(CONFIGAP6XXXWIFI6HDF)+=(��������6�������6���)+=(RKWIFI_PATH)/

endif

ap6256驱动源码就位于源码/device/kaihong/rk3568-khdvk/wifi中,另外再根据ap6256的编译规则,修改wifi中的Makefile。

NOTE:此处也不建议修改,源码就位于device/(productcompany)/(��������������)/(product_device)/wifi中,但此处不能获取(productcompany)与(��������������)与(product_device)的值,还需要社区进行完善

WiFi驱动源码目录

驱动代码编译规则修改

参考device/kaihong/rk3568-khdvk/wifi/Makefile文件,内容如下:

obj-$(CONFIG_AP6XXX_WIFI6_HDF) += bcmdhd_hdf/

NOTE:可以修改目标规则指向不同的wifi驱动代码。

原生驱动代码存放于:

device/kaihong/rk3568-khdvk/patches/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/

在原生驱动上修改编译规则Makefile文件

由于驱动中添加了HDF框架代码,其中涉及到头文件位于drivers目录中,需要将相关路径加入编译规则中,主要是修改两点:

(1) 引用drivers/hdf/khdf/model/network/wifi/hdfwifi.mk中规则,在Makefile中添加语句如下:

include drivers/hdf/khdf/model/network/wifi/hdfwifi.mk

(2) 将hdfwifi.mk中涉及到的头文件定义添加到编译规则中,方便编译时引用,添加语句如下:

EXTRA_CFLAGS += $(HDF_FRAMEWORKS_INC) \

​ $(HDF_WIFI_FRAMEWORKS_INC) \

​ $(HDF_WIFI_ADAPTER_INC) \

​ $(HDF_WIFI_VENDOR_INC) \

​ $(SECURE_LIB_INC)

NOTE:如果有其他编译要求,可以修改Makefile中的相关规则

3.4.4 在原生驱动上增加以及修改的HDF驱动代码文件位于:

device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/

目录结构:

./device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf

├── hdf_bdh_mac80211.c

├── hdf_driver_bdh_register.c

├── hdfinit_bdh.c

├── hdf_mac80211_ap.c

├── hdf_mac80211_sta.c

├── hdf_mac80211_sta.h

├── hdf_mac80211_sta_event.c

├── hdf_mac80211_sta_event.h

├── hdf_mac80211_p2p.c

├── hdf_public_ap6256.h

├── net_bdh_adpater.c

├── net_bdh_adpater.h

其中hdf_bdh_mac80211.c主要对g_bdh6_baseOps所需函数的填充,hdf_mac80211_ap.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_sta.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_p2p.c主要对g_bdh6_p2pOps所需函数进行填充,在drivers/framework/include/wifi/wifi_mac80211_ops.h里有对wifi基本功能所需api的说明。

驱动文件编写

HDF WLAN驱动框架由Module、NetDevice、NetBuf、BUS、HAL、Client 和 Message 这七个部分组成。开发者在WiFi驱动HDF适配过程中主要实现以下几部分功能:

适配HDF WLAN框架的驱动模块初始化

代码流程框图如下:

img

HDF代码入口

HDF代码入口位于device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf_driver_bdh_register.c

struct HdfDriverEntry g_hdfBdh6ChipEntry = {

.moduleVersion = 1,

.Bind = HdfWlanBDH6DriverBind,

.Init = HdfWlanBDH6ChipDriverInit,

.Release = HdfWlanBDH6ChipRelease,

.moduleName = "HDF_WLAN_CHIPS"

};

HDF_INIT(g_hdfBdh6ChipEntry);

3.5.2 HDF驱动的注册

在函数HDFWlanRegBDH6DriverFactory中完成HDF驱动的注册,相关代码如下:

static int32_t HDFWlanRegBDH6DriverFactory(void)

{

static struct HdfChipDriverFactory BDH6Factory = { 0 }; // WiFi device chip driver

struct HdfChipDriverManager *driverMgr = NULL;

driverMgr = HdfWlanGetChipDriverMgr();

if (driverMgr == NULL) {

​ HDF_LOGE("%s fail: driverMgr is NULL!", func);

​ return HDF_FAILURE;

}

BDH6Factory.driverName = BDH6_DRIVER_NAME;

BDH6Factory.GetMaxIFCount = GetBDH6GetMaxIFCount;

BDH6Factory.InitChip = InitBDH6Chip;

BDH6Factory.DeinitChip = DeinitBDH6Chip;

BDH6Factory.Build = BuildBDH6Driver;

BDH6Factory.Release = ReleaseBDH6Driver;

BDH6Factory.ReleaseFactory = NULL;

if (driverMgr->RegChipDriver(&BDH6Factory) != HDF_SUCCESS) {

​ HDF_LOGE("%s fail: driverMgr is NULL!", func);

​ return HDF_FAILURE;

}

return HDF_SUCCESS;

}

在注册HDF驱动时,需要实现HDF的基本操作,对struct HdfChipDriverFactory结构体进行初始化,struct HdfChipDriverFactory结构体的内容如下:

struct HdfChipDriverFactory {

const char *driverName; /**< Driver name */

int32_t (*InitChip)(struct HdfWlanDevice *device);

int32_t (*DeinitChip)(struct HdfWlanDevice *device);

void (*ReleaseFactory)(struct HdfChipDriverFactory *factory);

struct HdfChipDriver *(*Build)(struct HdfWlanDevice *device, uint8_t ifIndex);

void (*Release)(struct HdfChipDriver *chipDriver);

uint8_t (*GetMaxIFCount)(struct HdfChipDriverFactory *factory);

};

相关函数接口说明:

函数功能
GetBDH6GetMaxIFCount无需实现具体操作
InitBDH6Chip芯片初始化
DeinitBDH6Chip芯片去初始化
BuildBDH6Driver实现芯片驱动侧绑定
ReleaseBDH6Driver释放WLAN芯片驱动
ReleaseFactory无需实现

3.5.3 芯片驱动初始化

芯片驱动初始化函数以及wifi相关的ap、sta、p2p操作函数的注册都在BuildBDH6Driver函数中实现,主要是实现struct HdfChipDriver结构体的初始化,struct HdfChipDriver结构体如下:

struct HdfChipDriver {

uint16_t type; /**< Chip type */

char name[MAX_WIFI_COMPONENT_NAME_LEN]; /**< Chip name */

struct HdfMac80211BaseOps *ops; /**< MAC address for the basic feature */

struct HdfMac80211STAOps *staOps; /**< MAC address for the STA feature */

struct HdfMac80211APOps *apOps; /**< MAC address for the AP feature */

struct HdfMac80211P2POps *p2pOps; /**< MAC address for the P2Pfeature */

void *priv; /**< Private data of the chip driver */

int32_t (*init)(struct HdfChipDriver *chipDriver, NetDevice *netDev);

int32_t (*deinit)(struct HdfChipDriver *chipDriver, NetDevice *netDev);

};

1)函数BuildBDH6Driver具体实现如下:

static struct HdfChipDriver *BuildBDH6Driver(struct HdfWlanDevice *device, uint8_t ifIndex)

{

struct HdfChipDriver *specificDriver = NULL;

if (device == NULL) {

​ HDF_LOGE("%s fail : channel is NULL", func);

​ return NULL;

}

(void)device;

(void)ifIndex;

specificDriver = (struct HdfChipDriver *)OsalMemCalloc(sizeof(struct HdfChipDriver)); //分配结构体地址空间

if (specificDriver == NULL) {

​ HDF_LOGE("%s fail: OsalMemCalloc fail!", func);

​ return NULL;

}

if (memset_s(specificDriver, sizeof(struct HdfChipDriver), 0, sizeof(struct HdfChipDriver)) != EOK) {

​ HDF_LOGE("%s fail: memset_s fail!", func);

​ OsalMemFree(specificDriver);

​ return NULL;

}

if (strcpy_s(specificDriver->name, MAX_WIFI_COMPONENT_NAME_LEN, BDH6_DRIVER_NAME) != EOK) {

​ HDF_LOGE("%s fail : strcpy_s fail", func);

​ OsalMemFree(specificDriver);

​ return NULL;

}

specificDriver->init = BDH6Init;

specificDriver->deinit = BDH6Deinit;

HDF_LOGW("bdh6: call BuildBDH6Driver %p", specificDriver);

BDH6Mac80211Init(specificDriver); //wifi相关的ap、sta、p2p操作接口初始化赋值

return specificDriver;

}

2)函数BDH6Mac80211Init实现wifi相关的ap、sta、p2p操作接口赋值到struct HdfChipDriver结构体中,具体实现如下

void BDH6Mac80211Init(struct HdfChipDriver *chipDriver)

{

HDF_LOGE("%s: start...", func);

if (chipDriver == NULL) {

​ HDF_LOGE("%s: input is NULL", func);

​ return;

}

chipDriver->ops = &g_bdh6_baseOps;

chipDriver->staOps = &g_bdh6_staOps;

chipDriver->apOps = &g_bdh6_apOps;

chipDriver->p2pOps = &g_bdh6_p2pOps;

}

3.5.4 Wifi芯片驱动初始化

Wifi芯片驱动初始化过程,由函数BDH6Init实现,主要涉及到wlan0网络节点的注册与p2p0网络节点的注册,以及芯片驱动的初始化过程。

整体流程如下:

img

下面对涉及的重要函数代码进行列举:

(1) 设置NetDevice对象的操作接口,函数主要通过全局结构体赋值给NetDevice对象的成员netDeviceIf指针来实现,具体代码如下:

img

(2) 给NetDevice对象分配私有数据空间,具体实现如下:

img

(3) 启动芯片初始化流程,请参考原生驱动的初始化流程,其中需要注意的是,需要进行wlan0的节点注册,代码在原生驱动函数dhd_register_if中进行实现,具体代码如下:

img

(4) 创建p2p0的NetDevice对象,具体代码实现如下:

(5) 重新设置p2p0的操作方法,并进行p2p0节点注册,具体代码实现如下:

img

3.5.5 HDF WlAN相关的控制接口

HDF WlAN相关的控制接口主要涉及到HdfMac80211BaseOps、HdfMac80211STAOps、HdfMac80211APOps、HdfMac80211P2POps结构体,通过将以上结构体的全局变量赋值给struct HdfChipDriver结构体的ops、staOps、apOps、p2pOps成员来实现。

1)HDF WLAN Base控制侧接口的实现

代码位于hdf_bdh_mac80211.c

static struct HdfMac80211BaseOps g_bdh6_baseOps = {

.SetMode = BDH6WalSetMode,

.AddKey = BDH6WalAddKey,

.DelKey = BDH6WalDelKey,

.SetDefaultKey = BDH6WalSetDefaultKey,

.GetDeviceMacAddr = BDH6WalGetDeviceMacAddr,

.SetMacAddr = BDH6WalSetMacAddr,

.SetTxPower = BDH6WalSetTxPower,

.GetValidFreqsWithBand = BDH6WalGetValidFreqsWithBand,

.GetHwCapability = BDH6WalGetHwCapability,

.SendAction = BDH6WalSendAction,

.GetIftype = BDH6WalGetIftype,

};

上述实现的接口供STA、AP、P2P三种模式中所调用。

2)HDF WLAN STA模式接口的实现

STA模式调用流程图如下:

img

代码位于hdf_mac80211_sta.c

struct HdfMac80211STAOps g_bdh6_staOps = {

.Connect = HdfConnect,

.Disconnect = HdfDisconnect,

.StartScan = HdfStartScan,

.AbortScan = HdfAbortScan,

.SetScanningMacAddress = HdfSetScanningMacAddress,

};

3) HDF WLAN AP模式接口的实现

AP模式调用流程图如下:

img

代码位于hdf_mac80211_ap.c

struct HdfMac80211APOps g_bdh6_apOps = {

.ConfigAp = WalConfigAp,

.StartAp = WalStartAp,

.StopAp = WalStopAp,

.ConfigBeacon = WalChangeBeacon,

.DelStation = WalDelStation,

.SetCountryCode = WalSetCountryCode,

.GetAssociatedStasCount = WalGetAssociatedStasCount,

.GetAssociatedStasInfo = WalGetAssociatedStasInfo

};

4)HDF WLAN P2P模式接口的实现

P2P模式调用流程图如下:

img

struct HdfMac80211P2POps g_bdh6_p2pOps = {

.RemainOnChannel = WalRemainOnChannel,

.CancelRemainOnChannel = WalCancelRemainOnChannel,

.ProbeReqReport = WalProbeReqReport,

.AddIf = WalAddIf,

.RemoveIf = WalRemoveIf,

.SetApWpsP2pIe = WalSetApWpsP2pIe,

.GetDriverFlag = WalGetDriverFlag,

};

5) HDF WLAN框架事件上报接口的实现

WiFi驱动需要通过上报事件给wpa_supplicant和hostapd应用程序,比如扫描热点结果上报,新STA终端关联完成事件上报等等,HDF WLAN事件上报的所有接口请参考drivers/framework/include/wifi/hdf_wifi_event.h:

事件上报HDF WLAN接口主要有:

头文件接口名称功能描述
hdf_wifi_event.hHdfWifiEventNewSta()上报一个新的sta事件
HdfWifiEventDelSta()上报一个删除sta事件
HdfWifiEventInformBssFrame()上报扫描Bss事件
HdfWifiEventScanDone()上报扫描完成事件
HdfWifiEventConnectResult()上报连接结果事件
HdfWifiEventDisconnected()上报断开连接事件
HdfWifiEventMgmtTxStatus()上报发送状态事件
HdfWifiEventRxMgmt()上报接受状态事件
HdfWifiEventCsaChannelSwitch()上报Csa频段切换事件
HdfWifiEventTimeoutDisconnected()上报连接超时事件
HdfWifiEventEapolRecv()上报Eapol接收事件
HdfWifiEventResetResult()上报wlan驱动复位结果事件
HdfWifiEventRemainOnChannel()上报保持信道事件
HdfWifiEventCancelRemainOnChannel上报取消保持信道事件
所有关键问题总结
调试AP模块时,启动AP模式的方法

调试AP模块时,无法正常开启AP功能的解决方法

需要使用到busybox和hostapd配置ap功能,操作步骤如下:

  1. ifconfig wlan0 up

  2. ifconfig wlan0 192.168.12.1 netmask 255.255.255.0

  3. ./busybox udhcpd /data/l2tool/udhcpd.conf

  4. hostapd -d /data/l2tool/hostapd.conf

调试STA模块时,启动STA模式的方法

NOTE:需要对busybox与dhcpc.sh设置成可执行权限

调试P2P模块时,启动P2P模式的方法

调试P2P模块时,模块可以作为GO模式或者GC模式,区别在于配置文件不同,操作步骤如下:

wpa_supplicant -i wlan0 -c /data/l2tool/p2p_supplicant.conf & 设置p2p模式

wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_find 启动p2p查找

wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_connect 06:86:29:e8:47:84 pbc 连接p2p设备

./busybox udhcpc -ip2p-wlan0-0 -s /data/l2tool/dhcpc.sh 启动p2p-wlan0-0的dhcp获取地址

NOTE:在GO模式下,连接上设备后,应该立即获取IP地址,否则,连接会自动断开。

扫描热点事件无法上报到wap_supplicant的解决办法

wpa_supplicant 这个应用程序启动时不能加 -B参数后台启动,-B后台启动的话,调用poll()等待接收事件的线程会退出,所以无法接收上报事件,

wpa_supplicant -iwlan0 -c /data/wpa_supplicant.conf & 这样后台启动就可以了。

wpa2psk方式无法认证超时问题解决方法

分析流程发现 hostapd没有接收到WIFI_WPA_EVENT_EAPOL_RECV = 13这个事件,原来是驱动没有将接收到的EAPOL报文通过HDF WiFi框架发送给hostapd进程,在驱动接收报文后,调用netif_rx()触发软中断前将EAPOL报文发送给HDF WiFi框架,认证通过了。

P2P模式连接不成功问题定位分析

在调试P2P连接接口时,发现手机P2P直连界面总是处于已邀请提示,无法连接成功,通过抓取手机和WiFi模组正常连接成功报文和HDF适配后连接失败的报文进行比对,在失败的报文组中,发现手机侧多回复了一帧ACTION报文,提示无效参数,然后终止了P2P连接。

img

最后比对WiFi模组向手机发送的ACTION报文内容,发现填充的P2P Device Info的MAC地址值不对,如下:

正确帧内容:

错误帧内容:

最后经过分析MAC地址的填充部分代码,这个MAC地址是wpa_supplicant 根据p2p0的MAC地址填充的,所以将wdev对象(即p2p-dev-wlan0)的MAC地址更新给p2p0接口,二者保持一致即可,见代码wl_get_vif_macaddr(cfg, 7, p2p_hnetdev->macAddr);的调用。

连接成功日志
STA模式连接成功日志

WPA: Key negotiation ccompleted with 50:eb:f6:02:8e6:d4 [PTK=CCMP GTK=CCMP]

06 wlan0: State: GROUP_HANDSHAKEc -> COMPLETED

wlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=]

WifiWpaReceid eEapol done

AP模式连接成功日志

wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port

wlan0: STA 96:27:b3:95:b7:6e WPA: pairwiseb key handshake completed (RSN)

WifiWpaReceiveEapol done

P2P模式连接成功日志

P2P: cli_channels:

EAPOL: External notification - portValid=1

EAPOL: External notifica:tion - EAP success=1

EAPOL: SUPP_PAE entering state AUTHENTIwCATING

EAPOL: SUPP_BE enterilng state SUCCESS

EAP: EAP ent_ering state DISABLED

EAPOL: SUPP_PAE entering state AUTHENTICATED

EAPOL:n Supplicant port status: Authoorized

EAPOL: SUPP_BE enteringtstate IDLE

WifiWpaReceiveEapol donepleted - result=SUCCESS

# ifconfig

lo Link encap:Local Loopback

​ inet addr:127.0.0.1 Mask:255.0.0.0

​ inet6 addr: ::1/128 Scope: Host

​ UP LOOPBACK RUNNING MTU:65536 Metric:1

​ RX packets:12 errors:0 dropped:0 overruns:0 frame:0

​ TX packets:12 errors:0 dropped:0 overruns:0 carrier:0

​ collisions:0 txqueuelen:1000

​ RX bytes:565 TX bytes:565

wlan0 Link encap:Ethernet HWaddr 10:2c:6b:11:61:e0 Driver bcmsdh_sdmmc

​ inet6 addr: fe80::122c:6bff:fe11:61e0/64 Scope: Link

​ UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

​ RX packets:0 errors:0 dropped:0 overruns:0 frame:0

​ TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

​ collisions:0 txqueuelen:1000

​ RX bytes:0 TX bytes:0

p2p0 Link encap:Ethernet HWaddr 12:2c:6b:11:61:e0

​ inet6 addr: fe80::102c:6bff:fe11:61e0/64 Scope: Link

​ UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

​ RX packets:0 errors:0 dropped:0 overruns:0 frame:0

​ TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

​ collisions:0 txqueuelen:1000

​ RX bytes:0 TX bytes:0

p2p-p2p0-0 Link encap:Ethernet HWaddr 12:2c:6b:11:21:e0 Driver bcmsdh_sdmmc

​ inet6 addr: fe80::102c:6bff:fe11:21e0/64 Scope: Link

​ UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

​ RX packets:0 errors:0 dropped:9 overruns:0 frame:0

​ TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

​ collisions:0 txqueuelen:1000

​ RX bytes:0 TX bytes:0

4G

EC20模块

EC20模块是移远的一款比较经典的4G通信模组,MCU可以通过USB或者串口来和4G模块进行通信,我们rk3566使用的则是USB接口。

4G模块作为usb device,在加载对应的驱动后会生成ttyUSBx节点,框架层可以通过这些节点使用AT指令或者模块的状态和信息,通过ppp拨号注册一个网卡设备,拨号成功后在命令行可以通过ifconfig -a,可以看到有pppx网卡生成。

硬件连接

从原理图中我们看到我们的4G模块使用的PCIE接口,细心的同学会发现36和38引脚是USBDN和USBDP,也就是说我们使用的是PCIE转USB接口,最终的表现和直接使用USB接口是一样的。

img

因为4G模块使用的是USB接口,对应USB的host功能一定要工作正常,比如USB VBUS的使能,USB设备树的正确配置,kernel config的一些配置都要相应的打开,有的4G模块还有电源使能引脚,也需要在设备树中配置。

Kennel修改

配置VID PID

在drivers/usb/serial/option.c,添加对应的vid pid,当插入一个新的usb设备,option里相关的USB虚拟串口驱动会匹配vid pid,如果匹配成功,就会生成ttysUSBx节点,具体模块的修改方法在供应商提供的模块的资料里一般都会有,如Linux_USB_Driver_User_Guide

1、option.c增加EC20的pid vid如下,在option_ids结构体中增加:

static const struct usb_device_id option_ids[] = {{ USB_DEVICE(0x2c7c, 0x6002) }, /* Quectel EC20 */

测试

1、 在/dev/查看有无ttyUSBx节点,有类似如下节点表明模块配置没有问题。

#ls /dev/ttyUSB*/dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3

2、 AT指令测试,使用microcom串口指令

#microcom /dev/ttyUSB2ATOK

Vibrator

Vibrator是振动器的意思,也可以被叫做马达,马达旋转或者做直线运动会产生振动。

驱动框架模型

Vibrator驱动模型

img

Vibrator驱动按HDF标准框架开发,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动。Vibrator驱动提供HDI能力接口,支持静态HCS配置的时间序列和动态配置持续时间两种振动效果。调用StartOnce接口动态配置持续振动时间,调用StartEffect接口启动静态配置的振动效果。

HDF驱动适配

HCS配置

配置设备描述信息,在device_info.hcs中添加device_linear_vibrator:

   vibrator :: host {hostName = "vibrator_host";device_vibrator :: device {device0 :: deviceNode {policy = 2;priority = 100;preload = 0;permission = 0664;moduleName = "HDF_VIBRATOR";serviceName = "hdf_misc_vibrator";deviceMatchAttr = "hdf_vibrator_driver";}}device_linear_vibrator :: device {device0 :: deviceNode {policy = 1;priority = 105;preload = 0;permission = 0664;moduleName = "HDF_LINEAR_VIBRATOR";serviceName = "hdf_misc_linear_vibrator";deviceMatchAttr = "hdf_linear_vibrator_driver";}}}

配置线性马达器件信息,在linear_vibrator_config.hcs和vibrator_config.hcs中添加器件的特性:

root{linearVibratorConfig {boardConfig {match_attr = "hdf_linear_vibrator_driver";vibratorChipConfig {busType = 1; // 0:i2c 1:gpiogpioNum = 154;startReg = 0;stopReg = 0;startMask = 0;}}}
}root {vibratorConfig {boardConfig {match_attr = "hdf_vibrator_driver";vibratorAttr {/* 0:rotor 1:linear */deviceType = 1;supportPreset = 1;}vibratorHapticConfig {haptic_clock_timer {effectName = "haptic.clock.timer";type = 1; // 0 means built-in, 1 time seriesseq = [600, 600, 200, 600]; // time seq}haptic_default_effect {effectName = "haptic.default.effect";type = 0;seq = [0, 3, 800, 1];}}}}
}

HDF适配

驱动入口函数实现:

struct VibratorOps {int32_t (*Start)(void);int32_t (*StartEffect)(uint32_t effectType);int32_t (*Stop)(void);
};int32_t InitLinearVibratorDriver(struct HdfDeviceObject *device)
{static struct VibratorOps ops;------ops.Start = StartLinearVibrator;ops.StartEffect = StartEffectLinearVibrator;ops.Stop = StopLinearVibrator;RegisterVibrator(&ops); ParserLinearConfig(device->property, drvData);GpioSetDir(drvData->gpioNum, GPIO_DIR_OUT);
}struct HdfDriverEntry g_linearVibratorDriverEntry = {.moduleVersion = 1,.moduleName = "HDF_LINEAR_VIBRATOR",.Bind = BindLinearVibratorDriver,.Init = InitLinearVibratorDriver,.Release = ReleaseLinearVibratorDriver,
};HDF_INIT(g_linearVibratorDriverEntry);

代码分布

./drivers/peripheral/misc/vibrator/chipset/vibrator_linear_driver.c
./vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs ./vendor/kaihong/khdvk_3566b/hdf_config/khdf/vibrator/linear_vibrator_config.hcs ./vendor/kaihong/khdvk_3566b/hdf_config/khdf/vibrator/vibrator_config.hcs

UT测试

代码路径

./drivers/peripheral/misc/vibrator/test/unittest/common/hdf_vibrator_test.cpp ./drivers/peripheral/misc/vibrator/test/unittest/hdi/hdf_vibrator_hdi_test.cpp

编译UT代码命令

./build.sh --product-name khdvk_3566b --build-target hdf_test_vibrator

生成目标文件路径

./out/khdvk_3566b/tests/unittest/hdf/vibrator/hdf_unittest_vibrator
./out/khdvk_3566b/tests/unittest/hdf/vibrator/hdf_unittest_hdi_vibrator

将编译生成的bin文件 push到开发板上system/bin目录,修改执行权限,执行结果如下

./hdfunittest_hdi_vibrator

Load parameter_contexts succes: /system/etc/selinux/targeted/contexts/parameter_contexts
Running main() from ../../third_party/googletest/googletest/src/gtest_main.cc
[==========] Running 14 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 14 tests from HdfVibratorHdiTest
[ RUN ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty
[ OK ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty (0 ms)
[ RUN ] HdfVibratorHdiTest.PerformOneShotVibratorDuration_001
[ OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration_001 (2002 ms)
[ RUN ] HdfVibratorHdiTest.PerformOneShotVibratorDuration_002
[ OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration_002 (2 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_001
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_001 (5002 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_002
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_002 (2002 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_004
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_004 (5005 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_005
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_005 (5002 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_006
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_006 (5002 ms)
[ RUN ] HdfVibratorHdiTest.ExecuteVibratorEffect_007
[ OK ] HdfVibratorHdiTest.ExecuteVibratorEffect_007 (3 ms)


./hdf_unittest_vibrator

Load parameter_contexts succes: /system/etc/selinux/targeted/contexts/parameter_contexts
Running main() from ../../third_party/googletest/googletest/src/gtest_main.cc
[==========] Running 16 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 16 tests from HdfVibratorTest
[ RUN ] HdfVibratorTest.CheckVibratorInstanceIsEmpty
[ OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms)
[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration_001
[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration_001 (2001 ms)
[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration_002
[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration_002 (0 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_001
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_001 (5000 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_002
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_002 (2001 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_003
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_003 (0 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_004
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_004 (5001 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_005
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_005 (5000 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_006
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_006 (5000 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect_007
[ OK ] HdfVibratorTest.ExecuteVibratorEffect_007 (1 ms)


最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/301449.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cali Linux上的PoshC2安装和使用

一、安装PoshC2 curl -sSL https://raw.githubusercontent.com/nettitude/PoshC2/master/Install-for-Docker.sh | sudo bash二、创建工程 posh-project -n test三、修改配置文件 posh-config将图中的baidu.com改为自己要攻击的域名或者IP地址 四、执行 posh-server 显示没…

kettle介绍-Step之CSV Input

CSV Input/CSV 文件输入介绍 CSV 文件输入步骤主要用于将 CSV 格式的文本文件按照一定的格式输入至 流中 Step name:步骤的名称&#xff0c;在单一转换中&#xff0c;名称必须唯一Filename:指定输入 CSV 文件的名称&#xff0c;或通过单击右边的“浏览”按钮指定本地的 CSV …

PicGo日志报错 image not found in clipboard

PicGo: image not found in clipboard 文章目录 PicGo: image not found in clipboard问题描述问题尝试解决方案 问题描述 背景&#xff1a;在剪切板中的图片无法通过 PicGo 的剪切板图片进行上传。 读取PicGo 日志报错&#xff0c;显示图片没有在剪切板中找到。 $ ------Erro…

【信贷后台管理之(五)】

文章目录 目录结构一、面包屑组件封装二、退出登录接口联调三、申请列表的菜单路由3.1 路由创建&#xff0c;表格编写3.2 列表接口调用3.3 出生日期转变3.4 申请状态3.5 申请列表的操作3.5.1 编辑删除提交操作3.5.2 禁用状态3.5.3 操作接口3.5.4 搜索查询3.5.5 申请列表分页功能…

预处理不求人!教你如何轻松搞定

1. 预定义符号 2. #define定义常量 3. #define定义宏 4. 带有副作⽤的宏参数 5. 宏替换的规则 6. 宏函数的对⽐ 7. #和## 8. 命名约定 9. #undef 10. 命令⾏定义 11. 条件编译 12. 头⽂件的包含 13. 其他预处理指令 正文开始&#xff1a; 1. 预定义符…

【C#】版本号

&#x1f4bb; 代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp16 {internal class Program{static void Main(string[] args){Version version01 new Version("4.0.0…

鲁大师2024年新能源汽车Q1季报:问界M7蝉联智能榜单第一

回顾2024年的开局&#xff0c;比亚迪掀桌子&#xff0c;高合下桌子&#xff0c;行业已经进入惨烈的淘汰赛阶段&#xff1b;理想MEGA首发失利&#xff0c;问界销量步步紧逼&#xff0c;蔚来宣布全系焕新&#xff0c;新势力们也在继续着「攻与守」&#xff1b;而我们的新玩家小米…

linux通配符

通配符&#xff0c;它是一种用于匹配文件名的特殊字符。通配符在Linux中可以帮助我们更加方便和快捷地查找和操作文件。

【Nuxt3】内置组件介绍

简言 介绍下nuxt3的内置组件用法。 ClientOnly 使用 <ClientOnly> 组件仅在客户端渲染组件。 属性&#xff1a; placeholderTag | fallbackTag — 指定要在服务器端呈现的标记。placeholder | fallback — 指定要在服务器端渲染的内容&#xff0c;并在浏览器挂载 <…

【无标题】nodejs+mogoodb数据库写注册接口

描述 本篇文章主要记录使用nodejs express搭建服务器&#xff0c;并链接mogoodb数据来书写简单的后台接口&#xff1b;前端项目使用的vue2的一个酒店管理项目。阅读本文章&#xff0c;可以了解如何连接mogoodb数据库&#xff0c;和一些对数据库进行操作的命令。前端如何进行跨…

RISC-V特权架构 - 模式切换与委托

RISC-V特权架构 - 模式切换与委托 1 导致模式切换的常见动作2 异常处理规则3 异常处理时模式切换3.1 在U模式下&#xff0c;发生异常3.2 在S模式下&#xff0c;发生异常3.3 在M模式下&#xff0c;发生异常 4 系统调用时模式切换5 中断处理时模式切换 本文属于《 RISC-V指令集基…

图像处理环境配置opencv-python

下载python&#xff0c;配置pip使用清华源下载镜像&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 切换到python目录下&#xff0c;右击cmd&#xff0c;执行pip升级指令: python -m pip install --upgrade pip 下载opencv&#x…

西圣、万魔、倍思开放式耳机值不值得买?测评对比探讨!

自开放式耳机问世以来&#xff0c;便凭借其独特魅力赢得了众多音乐爱好者的青睐。它不仅佩戴起来舒适无比&#xff0c;还能让用户随时聆听周围的环境声音&#xff0c;保持与外界的沟通。同时&#xff0c;在卫生方面也有着不俗的表现。相较于传统的入耳式耳机&#xff0c;这些优…

uniapp使用npm命令引入font-awesome图标库最新版本并解决APP和小程序不显示图标的问题

uniapp使用npm命令引入font-awesome图标库最新版本 图标库网址&#xff1a;https://fontawesome.com/search?qtools&or 命令行&#xff1a; 引入 npm i fortawesome/fontawesome-free 查看版本 npm list fortawesome在main.js文件中&#xff1a; import fortawesome/fo…

嵌入式Linux系统调用执行基本流程

内核态与用户态 什么是系统调用 系统调用是怎么实现的 库函数write 库函数扩展汇编宏 int 0x80中断 调用对应的中断处理函数 检索系统调用函数表 最终执行sys_write 内核态与用户态数据交互 内核态与用户态 早期工程师们在操作系统上编写程序的时候,自己写个程序可以访问别人…

3.5、文本显示(Text/Span)

创建文本 Text 可通过以下两种方式来创建: string 字符串 效果图 Text(我是一段文本)引用 Resource 资源 资源引用类型可以通过 $r 创建 Resource 类型对象,文件位置为 /resources/base/element/string.json。 引用的资源位于:src/main/resources/base/element/string…

红酒:按年份分类,探究不同类型红酒的品质

在红酒的世界里&#xff0c;年份是一个至关重要的因素&#xff0c;它对红酒的品质和价值有着深远的影响。云仓酒庄雷盛红酒深知这一点&#xff0c;从酿造的首年起&#xff0c;就严格把控每一个环节&#xff0c;力求为消费者提供品质的红酒。 首先&#xff0c;让我们了解一下什么…

飞企互联-FE企业运营管理平台 druid路径 弱口令漏洞复现

0x01 产品简介 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据、物联网、移动互联网等技术支撑的云工作台。这个平台可以连接人、链接端、联通内外,支持企业B2B、C2B与O2O等核心需求,为不同行业客户的互联网+转型提供支持。 0x02 漏洞概述 飞企互联-FE企业…

ctf刷题记录2(更新中)

因为csdn上内容过多编辑的时候会很卡&#xff0c;因此重开一篇&#xff0c;继续刷题之旅。 NewStarCTF 2023 WEEK3 Include &#x1f350; <?phperror_reporting(0);if(isset($_GET[file])) {$file $_GET[file];if(preg_match(/flag|log|session|filter|input|data/i, $…

QT----opencv4.8.0编译cuda版本,QTcreater使用

目录 1 编译opencv4.8.02 验证能否加载GPU cuda12.1 opencv4.8.0 vs2019 cmake3.29 1 编译opencv4.8.0 打开cmake&#xff0c;选择opencv480路径&#xff0c;build路径随意 点击configure后&#xff0c;选择这些选项&#xff0c;opencv_word&#xff0c;cuda全选&#xff0c;…