> 技术文档 > 【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093


往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录)

✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?

✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~

✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?

✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?

✏️ 记录一场鸿蒙开发岗位面试经历~

✏️ 持续更新中……


TP

TP 驱动模型

主要包含 Input 模块 HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作 input 设备的驱动能力接口,HDI 接口主要包括如下三大类:

  • InputManager:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;

  • InputReporter:负责输入事件的上报,包括注册、注销数据上报回调函数等;

  • InputController:提供 input 设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。
    图 1 INPUT 模块 HDI 接口层框架图

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

相关目录下源代码目录结构如下所示

/drivers/peripheral/input├── hal # input模块的hal层代码│ └── include # input模块hal层内部的头文件│ └── src  # input模块hal层代码的具体实现├── interfaces # input模块对上层服务提供的驱动能力接口│ └── include # input模块对外提供的接口定义├── test  # input模块的测试代码│ └── unittest # input模块的单元测试代码

详细请参考 input 子系统

TP HDF 驱动适配

TP 驱动涉及的文件及目录

dayu200 平台默认支持 GT5688 这颗 TP IC。
开发板移植 touch 驱动涉及的文件及目录:
1、 Makefile 文件: drivers\\adapter\\khdf\\linux\\model\\input\\Makefile
2、 vendor\\hihope\\rk3568\\hdf_config\\khdf\\device_info\\device_info.hcs
3、 vendor\\hihope\\rk3568\\hdf_config\\khdf\\input\\input_config.hcs
4、 drivers\\framework\\model\\input\\driver\\touchscreen
TP 驱动的适配涉及 TP 驱动和 hcs 配置
tp 驱动的适配依赖 hdf 的 input 模型,hdf 的 input 模型提供了 TP,KEY,HID 等场景的设备注册,管理,数据转发层,hcs 解析等场景的支持能力。hdf 的 input 模型可大致抽象为驱动管理层、公共驱动层以及器件驱动三层。
从功能的角度看 hdf input 模块的框架如下:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

因为 hdf input 模型的高度抽象集成,TP 驱动的适配驱动主要涉及器件驱动层的适配。
在适配前,需要先明确 tp 所需要的的资源。

对于硬件资源,tp 模组需要主机上的如下资源:
1.中断引脚
2.Reset 引脚
3.使用的哪一组 i2c,从设备的地址是什么
4.TP 的初始化固件(这个通常由 IC 厂商提供)
5.触摸屏的分辨率

对于软件资源,在 hdf 上适配 tp,需要依赖如下几个 hdf 基础模组:
1.Hdf gpio 子系统 用于设置 gpio pin 脚以及一些中断资源
2.Hdf i2c 子系统 用于进行 i2c 通信
3.Input 模型
器件驱动主要围绕如下结构体展开

static struct TouchChipOps g_gt911ChipOps = { .Init = ChipInit, .Detect = ChipDetect, .Resume = ChipResume, .Suspend = ChipSuspend, .DataHandle = ChipDataHandle, .UpdateFirmware = UpdateFirmware, .SetAbility = SetAbility,};

ChipInit 负责器件驱动的初始化动作
ChipDetect 负责初始化后的器件有效性检测
SetAbility 设置按键属性
ChipDataHandle 负责解析键值
UpdateFirmware 负责升级固件
ChipSuspend 负责器件的休眠
ChipResume 负责器件的唤醒
按照器件的特性实现如上接口回调,并将该结构体注册进 input 模型即可

HCS 配置

device_info.hcs 中加入新的器件节点

device_touch_chip :: device { device0 :: deviceNode {  policy = 0;  priority = 180;  preload = 0;//0表示默认加载  permission = 0660;  moduleName = \"HDF_TOUCH_GT911\";//需要和器件driver中保持一致  serviceName = \"hdf_touch_gt911_service\";  deviceMatchAttr = \"zsj_gt911_5p5\"; } }

input_config.hcs 中加入器件的特性

chipConfig {  template touchChip { match_attr = \"\"; chipName = \"gt911\"; vendorName = \"zsj\"; chipInfo = \"AAAA11222\"; // 4-ProjectName, 2-TP IC, 3-TP Module /* 0:i2c 1:spi*/ busType = 0; deviceAddr = 0x5D; /* 0:None 1:Rising 2:Failing 4:High-level 8:Low-level */ irqFlag = 2; maxSpeed = 400; chipVersion = 0; //parse Coord TypeA powerSequence { /* [type, status, dir , delay]  0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int  0:off or low 1:on or high 2:no ops  0:input 1:output 2:no ops  meanings delay xms, 20: delay 20ms */ powerOnSeq = [4, 0, 1, 5,  3, 0, 1, 10,  3, 1, 1, 60,  4, 2, 0, 50]; suspendSeq = [3, 0, 2, 10]; resumeSeq = [3, 1, 2, 10]; powerOffSeq = [3, 0, 2, 10,  1, 0, 2, 20]; }  }

显示适配

显示适配需要完成的工作:图形服务 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\";

该实现中存在一个海思的私有 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;...
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.cppint32_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\")); // 或者不判断返回值 ...}

同时在//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;}
测试验证

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

具体验证如下:

  1. 关闭 render service
service_control stop render_service
  1. 关闭 foundation 进程
service_control stop foundation
  1. 运行 hello_composer 测试相关接口

    ./hello_composer

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

GPU

编译器 clang

prebuilts/clang/ohos/linux-x86_64/llvm

musl 库

./build.sh --product-name rk3568 --build-target musl_all 

编译完成后,会在 out/{product_name}/obj/third_party/musl/usr/lib 目录下生成对应的头文件和库:

32位对应arm-linux-ohos64位对应aarch64-linux-ohos

源码目录:

third_party/musl

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\"

LCD

dayu200 平台默认支持一个 mipi 接口的 lcd 屏幕
LCD 的适配主要依赖于 HDF 显示模型,显示驱动模型基于 HDF 驱动框架、Platform 接口及 OSAL 接口开发,可以屏蔽不同内核形态(LiteOS、Linux)差异,适用于不同芯片平台,为显示屏器件提供统一的驱动平台。
如图为 HDF Display 驱动模型层次关系

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

当前驱动模型主要部署在内核态中,向上对接到 Display 公共 hal 层,辅助 HDI 的实现。显示驱动通过 Display-HDI 层对图形服务暴露显示屏驱动能力;向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。
所以 LCD 的适配主要在于 LCD panel 器件驱动的适配
器件驱动的适配分为 2 部分:panel 驱动和 hcs 配置
涉及的文件有:

drivers/framework/model/display/driver/panelvendor/hihope/rk3568/hdf_config/khdf/device_infovendor/hihope/rk3568/hdf_config/khdf/input

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;};

驱动中在初始化接口中实例化该结构体:
panelSimpleDev->panel.init = PanelSimpleInit;
panelSimpleDev->panel.on = PanelSimpleOn;
panelSimpleDev->panel.off = PanelSimpleOff;
panelSimpleDev->panel.prepare = PanelSimplePrepare;
panelSimpleDev->panel.unprepare = PanelSimpleUnprepare;
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\"; }

背光

基于 HDF 框架开发的 背光驱动模型

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

rk3568 背光是通过 pwm 控制占空比实现的,具体使用的是 pwm4
原生背光驱动代码路径

linux-5.10/drivers/video/backlight/pwm_bl.clinux-5.10/drivers/video/backlight/backlight.clinux-5.10/drivers/pwm/pwm-rockchip.c

使用 HDF 框架下的背光驱动,需要关闭原生驱动

# CONFIG_BACKLIGHT_PWM is not set

HDF 实现

代码路径

drivers/framework/model/display/driver/backlight/hdf_bl.c

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);

代码路径:

drivers/framework/model/display/driver/backlight/pwm_bl.c

HDF PWM 入口函数

struct HdfDriverEntry g_pwmBlDevEntry = { .moduleVersion = 1, .moduleName = \"PWM_BL\", .Init = BlPwmEntryInit,};HDF_INIT(g_pwmBlDevEntry);

具体控制背光的接口:

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,};

其实使用的就是 HDF PWM 实现的对接内核 pwm 的接口

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

在 LCD HDF 器件驱动注册背光
代码路径

drivers/framework/model/display/driver/panel/ili9881c_boe.c

ili9881cBoeDev->panel.blDev = GetBacklightDev(\"hdf_pwm\");if (ili9881cBoeDev->panel.blDev == NULL) { HDF_LOGE(\"%s GetBacklightDev fail\", __func__); goto FAIL;}

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\"; }}

pwm 背光的 hcs 配置

root { backlightConfig { pwmBacklightConfig { match_attr = \"pwm_bl_dev\"; pwmDevNum = 1; pwmMaxPeriod = 25000; backlightDevName = \"hdf_pwm\"; minBrightness = 0; defBrightness = 127; maxBrightness = 255; } }}

测试

cat /sys/kernel/debug/pwm 来查看 hdf pwm 是否申请到 pwm4
申请成功有如下结果:
requested 代表申请成功
enabled 代表 pwm4 使能成功

# cat /sys/kernel/debug/pwmplatform/fe6e0000.pwm, 1 PWM device pwm-0 ((null)  ): requested enabled period: 25000 ns duty: 9705 ns polarity: normal

WIFI

WIFI HDF 化思路

主要参考《OpenHarmony HDF WLAN 驱动分析》与使用 这篇文章,熟悉 HDF WLAN 的框架以及需要实现的主要接口,包括 HDF 驱动初始化接口、WLAN 控制侧接口集、AP 模式接口集、STA 模式接口集、网络侧接口集、事件上报接口的实现。
接下来熟悉 HCS 文件的格式以及\"HDF WIFI”核心驱动框架的代码启动初始化过程,参考 hi3881 的代码进行改造。
HDF WiFi 框架总体框架图

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

ap6275s 驱动代码流程分析

驱动模块初始化流程分析

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

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 

其中 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 适配过程中主要实现以下几部分功能:

  1. 适配 HDF WLAN 框架的驱动模块初始化
    代码流程框图如下:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

代码位于 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);

在驱动初始化时会实现 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,};

上述实现的接口供 STA、AP、P2P 三种模式中所调用。
3) HDF WLAN STA 模式接口的实现
STA 模式调用流程图如下:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

代码位于 hdf_mac80211_sta.c

struct HdfMac80211STAOps g_bdh6_staOps = { .Connect = HdfConnect, .Disconnect = HdfDisconnect, .StartScan = HdfStartScan, .AbortScan = HdfAbortScan, .SetScanningMacAddress = HdfSetScanningMacAddress,};
  1. HDF WLAN AP 模式接口的实现
    AP 模式调用流程图如下:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

代码位于 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};

5) HDF WLAN P2P 模式接口的实现
P2P 模式调用流程图如下:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

struct HdfMac80211P2POps g_bdh6_p2pOps = { .RemainOnChannel = WalRemainOnChannel, .CancelRemainOnChannel = WalCancelRemainOnChannel, .ProbeReqReport = WalProbeReqReport, .AddIf = WalAddIf, .RemoveIf = WalRemoveIf, .SetApWpsP2pIe = WalSetApWpsP2pIe, .GetDriverFlag = WalGetDriverFlag,}; 

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

头文件 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 upifconfig wlan0 192.168.12.1 netmask 255.255.255.0busybox udhcpd /data/udhcpd.conf./hostapd -d /data/hostapd.conf
调试 STA 模块时,启动 STA 模式的方法
wpa_supplicant -iwlan0 -c /data/l2tool/wpa_supplicant.conf -d &./busybox udhcpc -i wlan0 -s /data/l2tool/dhcpc.sh
扫描热点事件无法上报到 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 连接。

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

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

正确帧内容:
【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

错误帧内容:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

最后经过分析 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 -> COMPLETEDwlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=]WifiWpaReceived eEapol done 
AP 模式连接成功日志
wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing portwlan0: STA 96:27:b3:95:b7:6e WPA: pairwise key handshake completed (RSN)WifiWpaReceiveEapol done 
P2P 模式连接成功日志
P2P: cli_channels:EAPOL: External notificationtion - portValid=1EAPOL: External notification:tion - EAP success=1EAPOL: SUPP_PAE entering state AUTHENTIwCATINGEAPOL: SUPP_BE enterilng state SUCCESSEAP: EAP ent_ering state DISABLEDEAPOL: SUPP_PAE entering state AUTHENTICATEDEAPOL:n Supplicant port status: AuthoorizedEAPOL: SUPP_BE entertaining IDLEWifiWpaReceiveEapol 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 

BT

HCI 接口

蓝牙整体硬件架构上分为主机(计算机或 MCU)和主机控制器(实际蓝牙芯片组)两部分;主机和控制器之间的通信遵循主机控制器接口(HCI),如下所示:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

HCI 定义了如何交换命令,事件,异步和同步数据包。异步数据包(ACL)用于数据传输,而同步数据包(SCO)用于带有耳机和免提配置文件的语音。

硬件连接

从 RK3568 芯片描述中看,该芯片并不没有集成 WIFI/蓝牙功能,都需要外接蓝牙芯片才能支持蓝牙功能,这也符合上述逻辑架构。那主机和控制器之间物理具体怎么连接呢?查看开发板规格书可以看的更清楚:

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

其中,28-36 号管脚就是 UART(串口);同时还可以看到有几个管脚分别做电源和休眠控制。

蓝牙 VENDORLIB 适配

vendorlib 是什么

vendorlib 部署在主机侧,可以认为是主机侧对蓝牙芯片驱动层,屏蔽不同蓝牙芯片的技术细节。从代码层面解读,其主要功能有两个:
1、为协议栈提供蓝牙芯片之间的通道(串口的文件描述符)
2、提供特定芯片的具体控制方法

代码层面解读 vendorlib

bt_vendor_lib.h 路径:

foundation/communication/bluetooth/services/bluetooth_standard/hardware/include

该文件定义了协议栈和 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;

协议栈启动时的基本流程如下:
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;

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);}

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;}

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;

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); }}

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};

在收到 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); } }}

收到芯片返回的 HCI_RESET 完成事件后,继续切换到下一个状态机并发送下一个 COMMAND,一直到状态机完成固件下发。
详细实现请参见 hw_config_cback 函数。
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 commandcomplete packet. Vendor lib is responsible for releasing the buffer passedin 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);

OHOS:

/**hci command packet transmit callbackVendor lib calls cmd_xmit_cb function in order to send a HCI Commandpacket to BT Controller. *The opcode parameter gives the HCI OpCode (combination of OGF and OCF) ofHCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET commandpacket. */typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf);

也就是说 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;

另外,OHOS 返回的是发送消息的字节数,<=0 为发送失败,和其他系统接口的返回值也不同
4、 snoop 日志
其他系统中记录了 HCI 交互消息,OHOS 同样有记录;OHOS 系统生成文件为/data/log/bluetooth/snoop.log,通过 wireshark 或其它报文分析工具可以看到 Host 和 Controller 之间的交互流程,有助于问题分析

Sensor

基于 HDF(Hardware Driver Foundation)驱动框架开发的 Sensor 驱动模型

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

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

代码路径:

drivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.cdrivers/framework/model/sensor/driver/chipset/accel/accel_mxc6655xa.h

编译宏

CONFIG_DRIVERS_HDF_SENSOR_ACCEL_MXC6655XA=y

Mxc6655xa 加速度计驱动入口函数实现

struct HdfDriverEntry g_accelMxc6655xaDevEntry = { .moduleVersion = 1, .moduleName = \"HDF_SENSOR_ACCEL_MXC6655XA\", .Bind = Mxc6655xaBindDriver, .Init = Mxc6655xaInitDriver, .Release = Mxc6655xaReleaseDriver,};HDF_INIT(g_accelMxc6655xaDevEntry);

接下来就是差异化适配函数

struct AccelOpsCall {int32_t (*Init)(struct SensorCfgData *data);int32_t (*ReadData)(struct SensorCfgData *data);};

获取 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;}

初始化

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;}

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\"; }}

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 ]; } }}

测试

UT 测试可以获取到 sensor 的三轴数据
测试代码路径

drivers/peripheral/sensor/test/unittest/common/hdf_sensor_test.cpp

编译 UT 代码命令:

./build.sh --product-name rk3568 --build-target hdf_test_sensor

将 hdf_test_sensor.bin push 到 system/bin 目录,添加执行权限,执行
有如下结果代表 sensor 测试成功

SensorTestDataCallback entersensor id :[1], data[1]: 0.001877sensor id :[1], data[2]: 0.160823sensor id :[1], data[3]: 0.046122

Vibrator

vibrator 模型

Vibrator 驱动模型主要包含 Vibrator(传感器)相关的 HDI 接口与实现,提供 Vibrator HDI(Hardware Driver Interface)能力接口,支持静态 HCS 配置的时间序列和动态配置持续时间两种振动效果。调用 StartOnce 接口动态配置持续振动时间;调用 StartEffect 接口启动静态配置的振动效果。
图 1 Vibrator 驱动模型图

【移植】OpenHarmony 标准系统方案之瑞芯微RK3568移植案例(三)_openharmony rk3568 gc2093

rk3568 支持线性马达,整体的驱动框架 openharmony 主线已经具备,只需要实现具体的器件驱动即可。

HDF 驱动实现

代码路径:

drivers/framework/model/misc/vibrator/driver/chipset/vibrator_linear_driver.c

linear Vibrator 加速度计驱动入口函数实现

struct HdfDriverEntry g_linearVibratorDriverEntry = { .moduleVersion = 1, .moduleName = \"HDF_LINEAR_VIBRATOR\", .Bind = BindLinearVibratorDriver, .Init = InitLinearVibratorDriver, .Release = ReleaseLinearVibratorDriver,};HDF_INIT(g_linearVibratorDriverEntry);

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\"; } } }

线性马达器件 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; } } }}

UT 测试

测试代码路径

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

编译 UT 代码命令

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

将 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)