1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
显示适配
显示适配需要完成的工作:图形服务HDI接口适配、GPU适配、LCD驱动适配
显示HDI
显示HDI对图形服务提供显示驱动能力,包括显示图层的管理、显示内存的管理及硬件加速等。 显示HDI需要适配两部分:gralloc 和 display_device。
gralloc适配
gralloc模块提供显示内存管理功能,OpenHarmony提供了使用与Hi3516DV300参考实现,厂商可根据实际情况参考适配,该实现基于drm开发。
drm设备节点定义在//drivers_peripheral/display/hal/default_standard/srd/display_gralloc/display_gralloc_gbm.c文件中,可根据实际情况修改
const char *g_drmFileNode = "/dev/dri/card0";
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
该实现中存在一个海思的私有ioctl命令码 DRM_IOCTL_HISILICON_GEM_FD_TO_PHYADDR 定义在//drivers_peripheral/display/hal/default_standard/src/display_gralloc/hisilicon_drm.h 文件中, 在//drivers_peripheral/display/hal/default_standard/src/display_gralloc/display_gralloc_gbm.c文件中调用,属于海思的私有功能,适配时根据实际情况修改
...
InitBufferHandle(bo, fd, info, priBuffer);
priBuffer->hdl.phyAddr = GetPhysicalAddr(grallocManager->drmFd, fd);
*buffer = &priBuffer->hdl;
...
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
display device适配
display device模块提供显示设备管理、layer管理、硬件加速等功能。
OpenHarmony提供了 基于drm的Hi3516DV300芯片的参考实现,该实现默认支持硬件合成;
如开发板不支持硬件合成,需要在drm_display.cpp文件中跳过gfx的初始化,
drivers_peripheral/blob/master/display/hal/default_standard/src/display_device/drm/drm_display.cpp
int32_t DrmDisplay::Init()
{
...
...
ret = HdiDisplay::Init();
DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("init failed"));
auto preComp = std::make_unique();
DISPLAY_CHK_RETURN((preComp == nullptr), DISPLAY_FAILURE,
DISPLAY_LOGE("can not new HdiGfxComposition errno %{public}d", errno));
ret = preComp->Init(); // gfx初始化,这里需要跳过
DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not init HdiGfxComposition")); // 或者不判断返回值
...
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
同时在//drivers_peripheral/display/hal/default_standard/src/display_device/hdi_gfx_composition.cpp文件中修改set_layers方法,全部使用CPU合成显示
int32_t HdiGfxComposition::SetLayers(std::vector &layers, HdiLayer &clientLayer)
{
DISPLAY_LOGD("layers size %{public}zd", layers.size());
mClientLayer = &clientLayer;
mCompLayers.clear();
for (auto &layer : layers) {
if (CanHandle(*layer)) {
#if 0 // CPU合成
layer->SetDeviceSelect(COMPOSITION_CLIENT);
#else
if ((layer->GetCompositionType() != COMPOSITION_VIDEO) &&
(layer->GetCompositionType() != COMPOSITION_CURSOR)) {
layer->SetDeviceSelect(COMPOSITION_DEVICE);
} else {
layer->SetDeviceSelect(layer->GetCompositionType());
}
#endif
mCompLayers.push_back(layer);
}
}
DISPLAY_LOGD("composer layers size %{public}zd", mCompLayers.size());
return DISPLAY_SUCCESS;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
测试验证
hello_composer测试模块:Rosen图形框架提供的测试程序,主要显示流程,HDI接口等功能是否正常。默认随系统编译。
代码路径:
foundation/graphic/graphic/rosen/samples/composer/
├── BUILD.gn
├── hello_composer.cpp
├── hello_composer.h
├── layer_context.cpp
├── layer_context.h
└── main.cpp
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
具体验证如下:
关闭render service
service_control stop render_service
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
关闭 foundation进程
service_control stop foundation
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
运行hello_composer 测试相关接口
./hello_composer
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
devicetest测试:HDI显示模块提供的测试模块,主要测试HDI接口、显示buffer、驱动等能力,测试时也需要关闭render service和 foundation进程。
代码路径:/drivers/peripheral/display/test/unittest/standard
├── BUILD.gn
├── common
│ ├── display_test.h
│ ├── display_test_utils.cpp
│ └── display_test_utils.h
├── display_device
│ ├── hdi_composition_check.cpp
│ ├── hdi_composition_check.h
│ ├── hdi_device_test.cpp
│ ├── hdi_device_test.h
│ ├── hdi_test_device_common.h
│ ├── hdi_test_device.cpp
│ ├── hdi_test_device.h
│ ├── hdi_test_display.cpp
│ ├── hdi_test_display.h
│ ├── hdi_test_layer.cpp
│ ├── hdi_test_layer.h
│ ├── hdi_test_render_utils.cpp
│ └── hdi_test_render_utils.h
└── display_gralloc
├── display_gralloc_test.cpp
└── display_gralloc_test.h
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
GPU
编译器clang
prebuilts/clang/ohos/linux-x86_64/llvm
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
musl库
./build.sh --product-name rk3568 --build-target musl_all
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
编译完成后,会在 out/{product_name}/obj/third_party/musl/usr/lib目录下生成对应的头文件和库:
32位对应arm-linux-ohos
64位对应aarch64-linux-ohos
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
源码目录:
third_party/musl
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
GPU 编译参数参考
TARGET_CFLAGS=" -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb --target=arm-linux-ohosmusl -fPIC -ftls-model=global-dynamic -mtls-direct-seg-refs -DUSE_MUSL"
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
LCD
dayu200平台默认支持一个mipi接口的lcd屏幕
LCD的适配主要依赖于HDF显示模型,显示驱动模型基于 HDF 驱动框架、Platform 接口及 OSAL 接口开发,可以屏蔽不同内核形态(LiteOS 、Linux)差异,适用于不同芯片平台,为显示屏器件提供统一的驱动平台。
如图为 HDF Display驱动模型层次关系
当前驱动模型主要部署在内核态中,向上对接到 Display 公共 hal 层,辅助 HDI 的实现。显示驱动通过 Display-HDI 层对图形服务暴露显示屏驱动能力;向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。
所以LCD的适配主要在于LCD panel器件驱动的适配
器件驱动的适配分为2部分:panel驱动和hcs配置
涉及的文件有:
drivers/framework/model/display/driver/panel
vendor/hihope/rk3568/hdf_config/khdf/device_info
vendor/hihope/rk3568/hdf_config/khdf/input
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
panel驱动
器件驱动主要围绕如下接口展开:
struct PanelData {
struct HdfDeviceObject *object;
int32_t (*init)(struct PanelData *panel);
int32_t (*on)(struct PanelData *panel);
int32_t (*off)(struct PanelData *panel);
int32_t (*prepare)(struct PanelData *panel);
int32_t (*unprepare)(struct PanelData *panel);
struct PanelInfo *info;
enum PowerStatus powerStatus;
struct PanelEsd *esd;
struct BacklightDev *blDev;
void *priv;
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
驱动中在初始化接口中实例化该结构体:
panelSimpleDev->panel.init = PanelSimpleInit;
panelSimpleDev->panel.on = PanelSimpleOn;
panelSimpleDev->panel.off = PanelSimpleOff;
panelSimpleDev->panel.prepare = PanelSimplePrepare;
panelSimpleDev->panel.unprepare = PanelSimpleUnprepare;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
PanelSimpleInit负责panel的软件初始化
PanelSimpleOn负责亮屏
PanelSimpleOff负责灭屏
PanelSimplePrepare负责亮屏的硬件时序初始化
PanelSimpleUnprepare负责灭屏的硬件时序初始化
实例化后使用RegisterPanel接口向display模型注册该panel驱动即可
需要说明的是,dayu200上的这款lcd 使用的是DRM显示框架
hcs配置
device4 :: deviceNode {
policy = 0;
priority = 100;
preload = 0;
moduleName = "LCD_PANEL_SIMPLE";
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
背光
基于HDF框架开发的 背光驱动模型
rk3568背光是通过pwm控制占空比实现的,具体使用的是pwm4
原生背光驱动代码路径
linux-5.10/drivers/video/backlight/pwm_bl.c
linux-5.10/drivers/video/backlight/backlight.c
linux-5.10/drivers/pwm/pwm-rockchip.c
c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
使用HDF框架下的背光驱动,需要关闭原生驱动
# CONFIG_BACKLIGHT_PWM is not set
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
HDF实现
代码路径
drivers/framework/model/display/driver/backlight/hdf_bl.c
c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
HDF BL 入口函数
static int32_t BacklightInit(struct HdfDeviceObject *object)
{
if (object == NULL) {
HDF_LOGE("%s: object is null!", __func__);
return HDF_FAILURE;
}
HDF_LOGI("%s success", __func__);
return HDF_SUCCESS;
}
struct HdfDriverEntry g_blDevEntry = {
.moduleVersion = 1,
.moduleName = "HDF_BL",
.Init = BacklightInit,
.Bind = BacklightBind,
};
HDF_INIT(g_blDevEntry);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
代码路径:
drivers/framework/model/display/driver/backlight/pwm_bl.c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
HDF PWM 入口函数
struct HdfDriverEntry g_pwmBlDevEntry = {
.moduleVersion = 1,
.moduleName = "PWM_BL",
.Init = BlPwmEntryInit,
};
HDF_INIT(g_pwmBlDevEntry);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
具体控制背光的接口:
static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness)
{
int32_t ret;
uint32_t duty;
struct BlPwmDev *blPwmDev = NULL;
blPwmDev = ToBlDevPriv(blDev);
if (blPwmDev == NULL) {
HDF_LOGE("%s blPwmDev is null", __func__);
return HDF_FAILURE;
}
if (blPwmDev->props.maxBrightness == 0) {
HDF_LOGE("%s maxBrightness is 0", __func__);
return HDF_FAILURE;
}
if (brightness == 0) {
return PwmDisable(blPwmDev->pwmHandle);
}
duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness;
ret = PwmSetDuty(blPwmDev->pwmHandle, duty);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret);
return HDF_FAILURE;
}
return PwmEnable(blPwmDev->pwmHandle);
}
static struct BacklightOps g_blDevOps = {
.updateBrightness = BlPwmUpdateBrightness,
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
其实使用的就是HDF PWM 实现的对接内核pwm的接口
在LCD HDF器件驱动注册背光
代码路径
drivers/framework/model/display/driver/panel/ili9881c_boe.c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
ili9881cBoeDev->panel.blDev = GetBacklightDev("hdf_pwm");
if (ili9881cBoeDev->panel.blDev == NULL) {
HDF_LOGE("%s GetBacklightDev fail", __func__);
goto FAIL;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
HCS配置
驱动hcs配置
device_pwm_bl :: device {
device0 :: deviceNode {
policy = 0;
priority = 95;
preload = 0;
moduleName = "PWM_BL";
deviceMatchAttr = "pwm_bl_dev";
}
}
device_backlight :: device {
device0 :: deviceNode {
policy = 2;
priority = 90;
preload = 0;
permission = 0660;
moduleName = "HDF_BL";
serviceName = "hdf_bl";
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
测试
cat /sys/kernel /debug/pwm 来查看hdf pwm 是否申请到pwm4
申请成功有如下结果:
requested 代表申请成功
enabled 代表pwm4使能成功
# cat /sys/kernel/debug/pwm
platform/fe6e0000.pwm, 1 PWM device
pwm-0 ((null) ): requested enabled period: 25000 ns duty: 9705 ns polarity: normal
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
WIFI
WIFI HDF化思路
主要参考《OpenHarmony HDF WLAN驱动分析》与使用 这篇文章,熟悉HDF WLAN的框架以及需要实现的主要接口,包括HDF驱动初始化接口、WLAN控制侧接口集、AP模式接口集、STA模式接口集、网络侧接口集、事件上报接口的实现。
接下来熟悉HCS文件的格式以及"HDF WIFI”核心驱动框架的代码启动初始化过程,参考hi3881的代码进行改造。
HDF WiFi框架总体框架图
ap6275s驱动代码流程分析
驱动模块初始化流程分析
Ap6275s 是一款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内核协议栈中。
l 创建net_device网络接口wlan0对象
dhd_allocate_if()会调用alloc_etherdev()创建net_device对象,即wlan0网络接口。
l 将wlan0注册到内核协议栈
调用dhd_register_if()函数,这里调用register_netdev(net);将wlan0网络接口注册到协议栈。
整改代码适配HDF WiFi框架 对于系统WiFi功能的使用,需要实现AP模式、STA模式、P2P三种主流模式,这里使用wpa_supplicant应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现STA模式和P2P模式的功能,使用hostapd应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现AP模式和P2P模式的功能。
Ap6275s WiFi6内核驱动依赖platform能力,主要包括SDIO总线的通讯能力;与用户态通信依赖HDF WiFi框架的能力,在确保上述能力功能正常后,即可开始本次WiFi驱动的HDF适配移植工作。本文档基于已经开源的rk3568开源版代码为基础版本,来进行此次移植。
适配移植ap6275s WiFi6驱动涉及到的文件和目录如下:
1). 编译配置文件
drivers/adapter/khdf/linux/model/network/wifi/Kconfig
drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile
2). WiFi驱动源码目录
原生驱动代码存放于:
linux-5.10/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd_wifi6/
在原生驱动上增加以及修改的代码文件位于:
device/hihope/rk3568/wifi/bcmdhd_wifi6/
目录结构:
./device/hihope/rk3568/wifi/bcmdhd_wifi6/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_ap6275s.h
├── net_bdh_adpater.c
├── net_bdh_adpater.h
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
其中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所需函数进行填充,在openharmony/drivers/framework/include/wifi/wifi_mac80211_ops.h里有对wifi基本功能所需api的说明。
驱动文件编写
HDF WLAN驱动框架由Module、NetDevice、NetBuf、BUS、HAL、Client 和 Message 这七个部分组成。开发者在WiFi驱动HDF适配过程中主要实现以下几部分功能:
适配HDF WLAN框架的驱动模块初始化
代码流程框图如下:
代码位于device/hihope/rk3568/wifi/bcmdhd_wifi6/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);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
在驱动初始化时会实现SDIO主控扫描探卡、WiFi芯片初始化、主接口的创建和初始化等工作。
2.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,
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
上述实现的接口供STA、AP、P2P三种模式中所调用。
3.HDF WLAN STA模式接口的实现 STA模式调用流程图如下:
代码位于hdf_mac80211_sta.c
struct HdfMac80211STAOps g_bdh6_staOps = {
.Connect = HdfConnect,
.Disconnect = HdfDisconnect,
.StartScan = HdfStartScan,
.AbortScan = HdfAbortScan,
.SetScanningMacAddress = HdfSetScanningMacAddress,
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
HDF WLAN AP模式接口的实现
AP模式调用流程图如下:
代码位于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
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
5) HDF WLAN P2P模式接口的实现
P2P模式调用流程图如下:
struct HdfMac80211P2POps g_bdh6_p2pOps = {
.RemainOnChannel = WalRemainOnChannel,
.CancelRemainOnChannel = WalCancelRemainOnChannel,
.ProbeReqReport = WalProbeReqReport,
.AddIf = WalAddIf,
.RemoveIf = WalRemoveIf,
.SetApWpsP2pIe = WalSetApWpsP2pIe,
.GetDriverFlag = WalGetDriverFlag,
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
6) HDF WLAN框架事件上报接口的实现
WiFi驱动需要通过上报事件给wpa_supplicant和hostapd应用程序,比如扫描热点结果上报,新STA终端关联完成事件上报等等,HDF WLAN事件上报的所有接口请参考drivers/framework/include/wifi/hdf_wifi_event.h:
事件上报HDF WLAN接口主要有:
class="table-box">头文件 hdf_wifi_event.h接口名称 功能描述 HdfWifiEventNewSta() 上报一个新的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功能,操作步骤如下:
ifconfig wlan0 up
ifconfig wlan0 192.168.12.1 netmask 255.255.255.0
busybox udhcpd /data/udhcpd.conf
./hostapd -d /data/hostapd.conf
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
调试STA模块时,启动STA模式的方法
wpa_supplicant -iwlan0 -c /data/l2tool/wpa_supplicant.conf -d &
./busybox udhcpc -i wlan0 -s /data/l2tool/dhcpc.sh
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
扫描热点事件无法上报到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连接。
最后比对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 completed 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=]
WifiWpaReceived eEapol done
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
AP模式连接成功日志
wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port
wlan0: STA 96:27:b3:95:b7:6e WPA: pairwise key handshake completed (RSN)
WifiWpaReceiveEapol done
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
P2P模式连接成功日志
P2P: cli_channels:
EAPOL: External notificationtion - portValid=1
EAPOL: External notification: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 entertaining 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
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
BT
HCI接口
蓝牙整体硬件架构上分为主机(计算机或MCU)和主机控制器(实际蓝牙芯片组)两部分;主机和控制器之间的通信遵循主机控制器接口(HCI),如下所示:
HCI定义了如何交换命令,事件,异步和同步数据包。异步数据包(ACL)用于数据传输,而同步数据包(SCO)用于带有耳机和免提配置文件的语音。
硬件连接
从RK3568芯片描述中看,该芯片并不没有集成WIFI/蓝牙功能,都需要外接蓝牙芯片才能支持蓝牙功能,这也符合上述逻辑架构。那主机和控制器之间物理具体怎么连接呢?查看开发板规格书可以看的更清楚:
其中,28-36号管脚就是UART(串口);同时还可以看到有几个管脚分别做电源和休眠控制。
蓝牙VENDORLIB适配
vendorlib是什么
vendorlib部署在主机侧,可以认为是主机侧对蓝牙芯片驱动层,屏蔽不同蓝牙芯片的技术细节。从代码层面解读,其主要功能有两个:
1、为协议栈提供蓝牙芯片之间的通道(串口的文件描述符)
2、提供特定芯片的具体控制方法
代码层面解读vendorlib
bt_vendor_lib.h 路径:
foundation/communication/bluetooth/services/bluetooth_standard/hardware/include
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
该文件定义了协议栈和vendor_lib交互接口,分为两组:
1、 vendorlib实现,协议栈调用
typedef struct {
/**
* Set to sizeof(bt_vendor_interface_t)
*/
size_t size;
/**
* Caller will open the interface and pass in the callback routines
* to the implementation of this interface.
*/
int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr);
/**
* Vendor specific operations
*/
int (*op)(bt_opcode_t opcode, void* param);
/**
* Closes the interface
*/
void (*close)(void);
} bt_vendor_interface_t;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
协议栈启动时的基本流程如下:
1.1、协议栈动态打开libbt_vendor.z.so,并调用init函数,初始化vendorlib
1.2、协议栈调用op函数,分别调用BT_OP_POWER_ON、BT_OP_HCI_CHANNEL_OPEN、BT_OP_INIT三个opcode;原则上BT_OP_INIT成功后说明芯片初始化完成。
2、协议栈实现,vendorlib调用(回调函数)
typedef struct {
/**
* set to sizeof(bt_vendor_callbacks_t)
*/
size_t size;
/* notifies caller result of init request */
init_callback init_cb;
/* buffer allocation request */
malloc_callback alloc;
/* buffer free request */
free_callback dealloc;
/* hci command packet transmit request */
cmd_xmit_callback xmit_cb;
} bt_vendor_callbacks_t;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
init_cb在BT_OP_INIT完成后调用
alloc/dealloc用于发送HCI消息时申请/释放消息控件
xmit_cb发送HCI Commands
vendor_lib实现的几个重要函数
1、 init函数
static int init(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr)
{
/* * ... */
userial_vendor_init();
upio_init();
vnd_load_conf(VENDOR_LIB_CONF_FILE);
/* store reference to user callbacks */
bt_vendor_cbacks = (bt_vendor_callbacks_t *)p_cb;
/* This is handed over from the stack */
return memcpy_s(vnd_local_bd_addr, BD_ADDR_LEN, local_bdaddr, BD_ADDR_LEN);
}
c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
vendorlib被调用的第一个函数,vendorlib保存好协议栈的callback和mac地址即可。
2、 BT_OP_POWER_ON对应处理
观名知意,这个操作理论上需要拉高电源管脚电平;该函数中使用rfill设备来处理,并没有直接调用驱动拉高电平
int upio_set_bluetooth_power(int on)
{
int sz;
int fd = -1;
int ret = -1;
char buffer = '0';
switch (on) {
case UPIO_BT_POWER_OFF:
buffer = '0';
break;
case UPIO_BT_POWER_ON:
buffer = '1';
break;
default:
return 0;
}
/* check if we have rfkill interface */
if (is_rfkill_disabled()) {
return 0;
}
if (rfkill_id == -1) {
if (init_rfkill()) {
return ret;
}
}
fd = open(rfkill_state_path, O_WRONLY);
if (fd < 0) {
return ret;
}
sz = write(fd, &buffer, 1);
/* ... */
return ret;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
3、BT_OP_HCI_CHANNEL_OPEN对应处理
case BT_OP_HCI_CHANNEL_OPEN: { // BT_VND_OP_USERIAL_OPEN
int(*fd_array)[] = (int(*)[])param;
int fd, idx;
fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg);
if (fd != -1) {
for (idx = 0; idx < HCI_MAX_CHANNEL; idx++)
(*fd_array)[idx] = fd;
retval = 1;
}
/* retval contains numbers of open fd of HCI channels */
break;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
userial_vendor_open函数打开串口设备(UART)得到文件描述符(fd),通过op的参数param返回该fd
该串口设备在系统中的名字应该在开发板中预定义了,本次开发板上设备为/dev/ttyS8
4、BT_OP_INIT对应处理
该操作码要求对蓝牙芯片进行初始化,具体要进行的处理和蓝牙芯片强相关。以本次调测的AP6257S芯片为例,初始化过程中主要是下发蓝牙固件。
初始化结束后,必须调用init_cb回调函数(参见bt_vendor_callbacks_t)通知协议栈初始化结果,否则会阻塞协议栈线程导致蓝牙相关功能无法正常使用。协议栈的具体处理如下:
协议栈调用BT_OP_INIT后会等待信号量,该信号量由init_cb函数置位
static int HciInitHal()
{
int result = BT_NO_ERROR;
g_waitHdiInit = SemaphoreCreate(0);
int ret = g_hdiLib->hdiInit(&g_hdiCallbacks);
if (ret == SUCCESS) {
SemaphoreWait(g_waitHdiInit);
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
vendorlib移植问题 1、 vendorlib的so命名
vendorlib必须是libbt_vendor.z.so;因为协议栈打开动态链接库就是这个名字
2、 固件问题
开发时一定要关注芯片固件,有些蓝牙芯片可能无需升级固件,有些则必须升级固件;本次AP6257S适配过程中最开始没有下发固件,导致蓝牙接收信号很差。固件下发时需要注意如下两点:
2.1、对于AP6257S芯片,因为蓝牙芯片内并没有类似flash存储,要求芯片上下电后必须重新下发
2.2、按照芯片本身的要求处理,最好能找到厂商的参考代码;以Broadcom系列芯片为例,其固件下发过程比较复杂,通过一个状态机驱动;共如下9个状态
/ Hardware Configuration State */
enum {
HW_CFG_START = 1,
HW_CFG_SET_UART_CLOCK,
HW_CFG_SET_UART_BAUD_1,
HW_CFG_READ_LOCAL_NAME,
HW_CFG_DL_MINIDRIVER,
HW_CFG_DL_FW_PATCH,
HW_CFG_SET_UART_BAUD_2,
HW_CFG_SET_BD_ADDR,
HW_CFG_READ_BD_ADDR
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
在收到BT_OP_INIT后初始化状态机,然后发送HCI_REST命令,切换状态为HW_CFG_START;
void hw_config_start(void)
{
HC_BT_HDR *p_buf = NULL;
uint8_t *p;
hw_cfg_cb.state = 0;
hw_cfg_cb.fw_fd = -1;
hw_cfg_cb.f_set_baud_2 = FALSE;
if (bt_vendor_cbacks) {
p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE +
HCI_CMD_PREAMBLE_SIZE);
}
if (p_buf) {
p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
p_buf->offset = 0;
p_buf->layer_specific = 0;
p_buf->len = HCI_CMD_PREAMBLE_SIZE;
p = (uint8_t *)(p_buf + 1);
UINT16_TO_STREAM(p, HCI_RESET);
*p = 0;
hw_cfg_cb.state = HW_CFG_START;
bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf);
} else {
if (bt_vendor_cbacks) {
HILOGE("vendor lib fw conf aborted [no buffer]");
bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL);
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
收到芯片返回的HCI_RESET完成事件后,继续切换到下一个状态机并发送下一个COMMAND,一直到状态机完成固件下发。
3、 关注系统间接口差异
不同系统的接口可能有一些细微差异,需要重点关注;对比其他系统和OHOS的接口,vendorlib调用xmit_cb发送HCI命令的函数定义略有差异
其他系统:
/* define callback of the cmd_xmit_cb
*
The callback function which HCI lib will call with the return of command
complete packet. Vendor lib is responsible for releasing the buffer passed
in at the p_mem parameter by calling dealloc callout function.
*/
typedef void (*tINT_CMD_CBACK)(void* p_mem);
typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void* p_buf, tINT_CMD_CBACK p_cback);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
OHOS:
/**
hci command packet transmit callback
Vendor lib calls cmd_xmit_cb function in order to send a HCI Command
packet to BT Controller.
*
The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
packet. */
typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
也就是说vendorlib中发送命令后,其他系统会直接调用callback通知芯片返回的消息,OHOS则是通过BT_OP_EVENT_CALLBACK操作码(参见bt_opcode_t定义)通知芯片返回的消息;vendorlib需要解析报文中的消息码确认芯片是处理的哪个消息,然后调用对应的处理函数。
void hw_process_event(HC_BT_HDR *p_buf)
{
uint16_t opcode;
uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
STREAM_TO_UINT16(opcode, p);
switch (opcode) {
case HCI_VSC_WRITE_BD_ADDR:
#if (USE_CONTROLLER_BDADDR == TRUE)
case HCI_READ_LOCAL_BDADDR:
#endif
case HCI_READ_LOCAL_NAME:
case HCI_VSC_DOWNLOAD_MINIDRV:
case HCI_VSC_WRITE_FIRMWARE:
case HCI_VSC_LAUNCH_RAM:
case HCI_RESET:
case HCI_VSC_WRITE_UART_CLOCK_SETTING:
case HCI_VSC_UPDATE_BAUDRATE:
hw_config_cback(p_buf);
break;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
另外,OHOS返回的是发送消息的字节数,<=0为发送失败,和其他系统接口的返回值也不同
4、 snoop日志
其他系统中记录了HCI交互消息,OHOS同样有记录;OHOS系统生成文件为/data/log/bluetooth/snoop.log,通过wireshark或其它报文分析工具可以看到Host和Controller之间的交互流程,有助于问题分析
Sensor
基于HDF(Hardware Driver Foundation)驱动框架开发的Sensor驱动模型
rk3568 支持accel sensor,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动即可。
mcx5566xa HDF驱动实现
RK3568平台支持加速度传感器,型号是MXC6655XA,具体配置可以查看该器件的datasheet。 移植HDF前,需要确认内核该sensor的编译使能是关闭的。
配置文件路径kernel/linux/config/linux-5.10/arch/arm64/configs/rk3568_standard_defconfig
# CONFIG_GS_MXC6655XA is not set
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
代码路径:
drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.c
drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.h
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
编译宏
CONFIG_DRIVERS_HDF_SENSOR_ACCEL_MXC6655XA=y
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
Mxc6655xa 加速度计驱动入口函数实现
struct HdfDriverEntry g_accelMxc6655xaDevEntry = {
.moduleVersion = 1,
.moduleName = "HDF_SENSOR_ACCEL_MXC6655XA",
.Bind = Mxc6655xaBindDriver,
.Init = Mxc6655xaInitDriver,
.Release = Mxc6655xaReleaseDriver,
};
HDF_INIT(g_accelMxc6655xaDevEntry);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
接下来就是差异化适配函数
struct AccelOpsCall {
int32_t (*Init)(struct SensorCfgData *data);
int32_t (*ReadData)(struct SensorCfgData *data);
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
获取x, y, z三轴数据接口
int32_t ReadMxc6655xaData(struct SensorCfgData *cfg, struct SensorReportEvent *event)
{
int32_t ret;
struct AccelData rawData = { 0, 0, 0 };
static int32_t tmp[ACCEL_AXIS_NUM];
CHECK_NULL_PTR_RETURN_VALUE(cfg, HDF_ERR_INVALID_PARAM);
CHECK_NULL_PTR_RETURN_VALUE(event, HDF_ERR_INVALID_PARAM);
ret = ReadMxc6655xaRawData(cfg, &rawData, &event->timestamp);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: MXC6655XA read raw data failed", __func__);
return HDF_FAILURE;
}
event->sensorId = SENSOR_TAG_ACCELEROMETER;
event->option = 0;
event->mode = SENSOR_WORK_MODE_REALTIME;
rawData.x = rawData.x * MXC6655XA_ACC_SENSITIVITY_2G;
rawData.y = rawData.y * MXC6655XA_ACC_SENSITIVITY_2G;
rawData.z = rawData.z * MXC6655XA_ACC_SENSITIVITY_2G;
tmp[ACCEL_X_AXIS] = (rawData.x * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
tmp[ACCEL_Y_AXIS] = (rawData.y * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
tmp[ACCEL_Z_AXIS] = (rawData.z * SENSOR_CONVERT_UNIT) / SENSOR_CONVERT_UNIT;
ret = SensorRawDataToRemapData(cfg->direction, tmp, sizeof(tmp) / sizeof(tmp[0]));
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: MXC6655XA convert raw data failed", __func__);
return HDF_FAILURE;
}
event->dataLen = sizeof(tmp);
event->data = (uint8_t *)&tmp;
return ret;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
初始化
static int32_t InitMxc6655xa(struct SensorCfgData *data)
{
int32_t ret;
CHECK_NULL_PTR_RETURN_VALUE(data, HDF_ERR_INVALID_PARAM);
ret = SetSensorRegCfgArray(&data->busCfg, data->regCfgGroup[SENSOR_INIT_GROUP]);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: MXC6655XA sensor init config failed", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
hcs配置
Mxc6655xa accel sensor 驱动HCS配置
device_sensor_mxc6655xa :: device {
device0 :: deviceNode {
policy = 1;
priority = 120;
preload = 0;
permission = 0664;
moduleName = "HDF_SENSOR_ACCEL_MXC6655XA";
serviceName = "hdf_accel_mxc6655xa";
deviceMatchAttr = "hdf_sensor_accel_mxc6655xa_driver";
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
Mxc6655xa accel sensor 寄存器组配置信息
#include "../sensor_common.hcs"
root {
accel_mxc6655xa_chip_config : sensorConfig {
match_attr = "hdf_sensor_accel_mxc6655xa_driver";
sensorInfo :: sensorDeviceInfo {
sensorName = "accelerometer";
vendorName = "memsi_mxc6655xa"; // max string length is 16 bytes
sensorTypeId = 1; // enum SensorTypeTag
sensorId = 1; // user define sensor id
power = 230;
}
sensorBusConfig :: sensorBusInfo {
busType = 0; // 0:i2c 1:spi
busNum = 5;
busAddr = 0x15;
regWidth = 1; // 1byte
}
sensorIdAttr :: sensorIdInfo {
chipName = "mxc6655xa";
chipIdRegister = 0x0f;
chipIdValue = 0x05;
}
sensorDirection {
direction = 5; // chip direction range of value:0-7
/* 1:negative 0:positive
0:AXIS_X 1:AXIS_Y 2:AXIS_Z
*/
/* sign[AXIS_X], sign[AXIS_Y], sign[AXIS_Z], map[AXIS_X], map[AXIS_Y], map[AXIS_Z] */
convert = [
0, 0, 0, 0, 1, 2,
1, 0, 0, 1, 0, 2,
0, 0, 1, 0, 1, 2,
0, 1, 0, 1, 0, 2,
1, 0, 1, 0, 1, 2,
0, 0, 1, 1, 0, 2,
0, 1, 1, 0, 1, 2,
1, 1, 1, 1, 0, 2
];
}
sensorRegConfig {
/* regAddr: register address
value: config register value
len: size of value
mask: mask of value
delay: config register delay time (ms)
opsType: enum SensorOpsType 0-none 1-read 2-write 3-read_check 4-update_bit
calType: enum SensorBitCalType 0-none 1-set 2-revert 3-xor 4-left shift 5-right shift
shiftNum: shift bits
debug: 0-no debug 1-debug
save: 0-no save 1-save
*/
/* regAddr, value, mask, len, delay, opsType, calType, shiftNum, debug, save */
initSeqConfig = [
0x7e, 0xb6, 0xff, 1, 5, 2, 0, 0, 0, 0,
0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0
];
enableSeqConfig = [
0x7e, 0x11, 0xff, 1, 5, 2, 0, 0, 0, 0,
0x41, 0x03, 0xff, 1, 0, 2, 0, 0, 0, 0,
0x40, 0x08, 0xff, 1, 0, 2, 0, 0, 0, 0
];
disableSeqConfig = [
0x7e, 0x10, 0xff, 1, 5, 2, 0, 0, 0, 0
];
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
测试
UT测试可以获取到sensor的三轴数据
测试代码路径
drivers/peripheral/sensor/test/unittest/common/hdf_sensor_test.cpp
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
编译UT代码命令:
./build.sh --product-name rk3568 --build-target hdf_test_sensor
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
将hdf_test_sensor.bin push到system/bin目录,添加执行权限,执行
有如下结果代表sensor 测试成功
SensorTestDataCallback enter
sensor id :[1], data[1]: 0.001877
sensor id :[1], data[2]: 0.160823
sensor id :[1], data[3]: 0.046122
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
Vibrator
vibrator 模型
Vibrator驱动模型主要包含Vibrator(传感器)相关的HDI接口与实现,提供Vibrator HDI(Hardware Driver Interface)能力接口,支持静态HCS配置的时间序列和动态配置持续时间两种振动效果。调用StartOnce接口动态配置持续振动时间;调用StartEffect接口启动静态配置的振动效果。
图 1 Vibrator驱动模型图
rk3568 支持线性马达,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动即可。
HDF驱动实现
代码路径:
drivers/framework/model/misc/vibrator/driver/chipset/vibrator_linear_driver.c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
linear Vibrator加速度计驱动入口函数实现
struct HdfDriverEntry g_linearVibratorDriverEntry = {
.moduleVersion = 1,
.moduleName = "HDF_LINEAR_VIBRATOR",
.Bind = BindLinearVibratorDriver,
.Init = InitLinearVibratorDriver,
.Release = ReleaseLinearVibratorDriver,
};
HDF_INIT(g_linearVibratorDriverEntry);
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
hcs配置
驱动hcs配置
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";
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
线性马达器件hcs配置
root {
linearVibratorConfig {
boardConfig {
match_attr = "hdf_linear_vibrator_driver";
vibratorChipConfig {
busType = 1; // 0:i2c 1:gpio
gpioNum = 154;
startReg = 0;
stopReg = 0;
startMask = 0;
}
}
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
UT测试
测试代码路径
drivers/peripheral/misc/vibrator/test/unittest/common/hdf_vibrator_test.cpp
c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
编译UT代码命令
./build.sh --product-name rk3568 --build-target hdf_test_vibrator
c
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
将hdf_test_vibrator.bin push到system/bin目录,添加执行权限,执行
[ RUN ] HdfVibratorTest.CheckVibratorInstanceIsEmpty
[ OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms)
[ RUN ] HdfVibratorTest.PerformOneShotVibratorDuration001
[ OK ] HdfVibratorTest.PerformOneShotVibratorDuration001 (2001 ms)
[ RUN ] HdfVibratorTest.ExecuteVibratorEffect001
[ OK ] HdfVibratorTest.ExecuteVibratorEffect001 (5001 ms)
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
如果大家想更加深入的学习 OpenHarmony(鸿蒙南向) 开发的全栈内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:
搭建开发环境 Windows 开发环境的搭建 Ubuntu 开发环境搭建 Linux 与 Windows 之间的文件共享 ……
构建子系统 启动流程 子系统 分布式任务调度子系统 分布式通信子系统 驱动子系统 ……
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙: 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://qr21.cn/FV7h05
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/140859405","extend1":"pc","ab":"new"}">>
id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension">
class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}">
class="blog_extension_card_left">
class="blog_extension_card_cont">
鸿蒙开发学习资料领取!!!
class="blog_extension_card_cont_r">
微信名片
评论记录:
回复评论: