
OpenHarmony设备开发 标准系统方案之瑞芯微RK3568移植案例(下)
TP
TP驱动模型
主要包含Input模块HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作input设备的驱动能力接口,HDI接口主要包括如下三大类:
- InputManager:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;
- InputReporter:负责输入事件的上报,包括注册、注销数据上报回调函数等;
- InputController:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。
图 1 INPUT模块HDI接口层框架图
相关目录下源代码目录结构如下所示
详细请参考input子系统README
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模块的框架如下:
因为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模型
器件驱动主要围绕如下结构体展开
ChipInit负责器件驱动的初始化动作
ChipDetect负责初始化后的器件有效性检测
SetAbility设置按键属性
ChipDataHandle负责解析键值
UpdateFirmware负责升级固件
ChipSuspend负责器件的休眠
ChipResume负责器件的唤醒
按照器件的特性实现如上接口回调,并将该结构体注册进input模型即可
HCS 配置
device_info.hcs中加入新的器件节点
input_config.hcs中加入器件的特性
显示适配
显示适配需要完成的工作:图形服务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文件中,可根据实际情况修改
该实现中存在一个海思的私有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文件中调用,属于海思的私有功能,适配时根据实际情况修改
display device适配
display device模块提供显示设备管理、layer管理、硬件加速等功能。
OpenHarmony提供了基于drm的Hi3516DV300芯片的参考实现,该实现默认支持硬件合成;
如开发板不支持硬件合成,需要在drm_display.cpp文件中跳过gfx的初始化,
同时在//drivers_peripheral/display/hal/default_standard/src/display_device/hdi_gfx_composition.cpp文件中修改set_layers方法,全部使用CPU合成显示
测试验证
hello_composer测试模块:Rosen图形框架提供的测试程序,主要显示流程,HDI接口等功能是否正常。默认随系统编译。
代码路径:
具体验证如下:
- 关闭render service
- 关闭 foundation进程
- 运行hello_composer 测试相关接口
devicetest测试:HDI显示模块提供的测试模块,主要测试HDI接口、显示buffer、驱动等能力,测试时也需要关闭render service和 foundation进程。
代码路径:/drivers/peripheral/display/test/unittest/standard
GPU
编译器clang
musl库
编译完成后,会在 out/{product_name}/obj/third_party/musl/usr/lib目录下生成对应的头文件和库:
源码目录:
GPU 编译参数参考
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配置
涉及的文件有:
panel驱动
器件驱动主要围绕如下接口展开:
驱动中在初始化接口中实例化该结构体:
PanelSimpleInit负责panel的软件初始化
PanelSimpleOn负责亮屏
PanelSimpleOff负责灭屏
PanelSimplePrepare负责亮屏的硬件时序初始化
PanelSimpleUnprepare负责灭屏的硬件时序初始化
实例化后使用RegisterPanel接口向display模型注册该panel驱动即可
需要说明的是,dayu200上的这款lcd 使用的是DRM显示框架
hcs配置
背光
基于HDF框架开发的 背光驱动模型
rk3568背光是通过pwm控制占空比实现的,具体使用的是pwm4
原生背光驱动代码路径
使用HDF框架下的背光驱动,需要关闭原生驱动
HDF实现
代码路径
HDF BL 入口函数
代码路径:
HDF PWM 入口函数
具体控制背光的接口:
其实使用的就是HDF PWM 实现的对接内核pwm的接口
在LCD HDF器件驱动注册背光
代码路径
HCS配置
驱动hcs配置
pwm背光的hcs配置
测试
cat /sys/kernel/debug/pwm 来查看hdf pwm 是否申请到pwm4
申请成功有如下结果:
requested 代表申请成功
enabled 代表pwm4使能成功
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/
目录结构:
其中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
在驱动初始化时会实现SDIO主控扫描探卡、WiFi芯片初始化、主接口的创建和初始化等工作。
- HDF WLAN Base控制侧接口的实现
代码位于hdf_bdh_mac80211.c
上述实现的接口供STA、AP、P2P三种模式中所调用。
- HDF WLAN STA模式接口的实现
STA模式调用流程图如下:
代码位于hdf_mac80211_sta.c
- HDF WLAN AP模式接口的实现
AP模式调用流程图如下:
代码位于hdf_mac80211_ap.c
5) HDF WLAN P2P模式接口的实现
P2P模式调用流程图如下:
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功能,操作步骤如下:
调试STA模块时,启动STA模式的方法
扫描热点事件无法上报到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模式连接成功日志
AP模式连接成功日志
P2P模式连接成功日志
BT
HCI接口
蓝牙整体硬件架构上分为主机(计算机或MCU)和主机控制器(实际蓝牙芯片组)两部分;主机和控制器之间的通信遵循主机控制器接口(HCI),如下所示:
HCI定义了如何交换命令,事件,异步和同步数据包。异步数据包(ACL)用于数据传输,而同步数据包(SCO)用于带有耳机和免提配置文件的语音。
硬件连接
从RK3568芯片描述中看,该芯片并不没有集成WIFI/蓝牙功能,都需要外接蓝牙芯片才能支持蓝牙功能,这也符合上述逻辑架构。那主机和控制器之间物理具体怎么连接呢?查看开发板规格书可以看的更清楚:
其中,28-36号管脚就是UART(串口);同时还可以看到有几个管脚分别做电源和休眠控制。
蓝牙VENDORLIB适配
vendorlib是什么
vendorlib部署在主机侧,可以认为是主机侧对蓝牙芯片驱动层,屏蔽不同蓝牙芯片的技术细节。从代码层面解读,其主要功能有两个:
1、为协议栈提供蓝牙芯片之间的通道(串口的文件描述符)
2、提供特定芯片的具体控制方法
代码层面解读vendorlib
bt_vendor_lib.h 路径:
该文件定义了协议栈和vendor_lib交互接口,分为两组:
1、 vendorlib实现,协议栈调用
协议栈启动时的基本流程如下:
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调用(回调函数)
init_cb在BT_OP_INIT完成后调用
alloc/dealloc用于发送HCI消息时申请/释放消息控件
xmit_cb发送HCI Commands
vendor_lib实现的几个重要函数
1、 init函数
vendorlib被调用的第一个函数,vendorlib保存好协议栈的callback和mac地址即可。
2、 BT_OP_POWER_ON对应处理
观名知意,这个操作理论上需要拉高电源管脚电平;该函数中使用rfill设备来处理,并没有直接调用驱动拉高电平
3、BT_OP_HCI_CHANNEL_OPEN对应处理
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函数置位
vendorlib移植问题
1、 vendorlib的so命名
vendorlib必须是libbt_vendor.z.so;因为协议栈打开动态链接库就是这个名字
2、 固件问题
开发时一定要关注芯片固件,有些蓝牙芯片可能无需升级固件,有些则必须升级固件;本次AP6257S适配过程中最开始没有下发固件,导致蓝牙接收信号很差。固件下发时需要注意如下两点:
2.1、对于AP6257S芯片,因为蓝牙芯片内并没有类似flash存储,要求芯片上下电后必须重新下发
2.2、按照芯片本身的要求处理,最好能找到厂商的参考代码;以Broadcom系列芯片为例,其固件下发过程比较复杂,通过一个状态机驱动;共如下9个状态
在收到BT_OP_INIT后初始化状态机,然后发送HCI_REST命令,切换状态为HW_CFG_START;
收到芯片返回的HCI_RESET完成事件后,继续切换到下一个状态机并发送下一个COMMAND,一直到状态机完成固件下发。
详细实现请参见hw_config_cback函数。
3、 关注系统间接口差异
不同系统的接口可能有一些细微差异,需要重点关注;对比其他系统和OHOS的接口,vendorlib调用xmit_cb发送HCI命令的函数定义略有差异
其他系统:
OHOS:
也就是说vendorlib中发送命令后,其他系统会直接调用callback通知芯片返回的消息,OHOS则是通过BT_OP_EVENT_CALLBACK操作码(参见bt_opcode_t定义)通知芯片返回的消息;vendorlib需要解析报文中的消息码确认芯片是处理的哪个消息,然后调用对应的处理函数。
另外,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
代码路径:
编译宏
Mxc6655xa 加速度计驱动入口函数实现
接下来就是差异化适配函数
获取x, y, z三轴数据接口
初始化
hcs配置
Mxc6655xa accel sensor 驱动HCS配置
Mxc6655xa accel sensor 寄存器组配置信息
测试
UT测试可以获取到sensor的三轴数据
测试代码路径
编译UT代码命令:
将hdf_test_sensor.bin push到system/bin目录,添加执行权限,执行
有如下结果代表sensor 测试成功
Vibrator
vibrator 模型
Vibrator驱动模型主要包含Vibrator(传感器)相关的HDI接口与实现,提供Vibrator HDI(Hardware Driver Interface)能力接口,支持静态HCS配置的时间序列和动态配置持续时间两种振动效果。调用StartOnce接口动态配置持续振动时间;调用StartEffect接口启动静态配置的振动效果。
图 1 Vibrator驱动模型图
rk3568 支持线性马达,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动即可。
HDF驱动实现
代码路径:
linear Vibrator加速度计驱动入口函数实现
hcs配置
驱动hcs配置
线性马达器件hcs配置
UT测试
测试代码路径
编译UT代码命令
将hdf_test_vibrator.bin push到system/bin目录,添加执行权限,执行
