diff --git a/zh-cn/application-dev/quick-start/arkts-appstorage.md b/zh-cn/application-dev/quick-start/arkts-appstorage.md index 778dba37b9b36aed2d4e7d315e618f0610930336..932186e02b1497af6b3df8212f3d674104d07e4b 100644 --- a/zh-cn/application-dev/quick-start/arkts-appstorage.md +++ b/zh-cn/application-dev/quick-start/arkts-appstorage.md @@ -192,9 +192,85 @@ struct CompA { } ``` -### 以持久化方式订阅某个事件并接收事件回调 +### 不建议借助@StorageLink的双向同步机制实现事件通知 -推荐使用持久化方式订阅某个事件并接收事件回调,可以减少开销,增强代码的可读性。 +不建议开发者使用@StorageLink和AppStorage的双向同步的机制来实现事件通知,AppStorage是和UI相关的数据存储,改变会带来UI的刷新,相对于一般的事件通知,UI刷新的成本较大。 + +TapImage中的点击事件,会触发AppStorage中tapIndex对应属性的改变。因为@StorageLink是双向同步,修改会同步会AppStorage中,所以,所有绑定AppStorage的tapIndex可见自定义组件都会被通知UI刷新。UI刷新带来的成本是巨大的,因此不建议开发者使用此方式来实现基本的事件通知功能。 + + +```ts +// xxx.ets +class ViewData { + title: string; + uri: Resource; + color: Color = Color.Black; + + constructor(title: string, uri: Resource) { + this.title = title; + this.uri = uri + } +} + +@Entry +@Component +struct Gallery2 { + dataList: Array = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))] + scroller: Scroller = new Scroller() + + build() { + Column() { + Grid(this.scroller) { + ForEach(this.dataList, (item: ViewData, index?: number) => { + GridItem() { + TapImage({ + uri: item.uri, + index: index + }) + }.aspectRatio(1) + + }, (item: ViewData, index?: number) => { + return JSON.stringify(item) + index; + }) + }.columnsTemplate('1fr 1fr') + } + + } +} + +@Component +export struct TapImage { + @StorageLink('tapIndex') @Watch('onTapIndexChange') tapIndex: number = -1; + @State tapColor: Color = Color.Black; + private index: number; + private uri: Resource; + + // 判断是否被选中 + onTapIndexChange() { + if (this.tapIndex >= 0 && this.index === this.tapIndex) { + console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, red`) + this.tapColor = Color.Red; + } else { + console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, black`) + this.tapColor = Color.Black; + } + } + + build() { + Column() { + Image(this.uri) + .objectFit(ImageFit.Cover) + .onClick(() => { + this.tapIndex = this.index; + }) + .border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor }) + } + + } +} +``` + +开发者可以使用emit订阅某个事件并接收事件回调,可以减少开销,增强代码的可读性。 ```ts @@ -293,10 +369,9 @@ export struct TapImage { } ``` -以下示例为消息机制方式订阅事件,会导致回调监听的节点数较多,非常耗时,不推荐以此来实现应用代码。 - +以上通知事件逻辑简单,也可以简化成三元表达式。 -```ts +``` // xxx.ets class ViewData { title: string; @@ -337,22 +412,11 @@ struct Gallery2 { @Component export struct TapImage { - @StorageLink('tapIndex') @Watch('onTapIndexChange') tapIndex: number = -1; + @StorageLink('tapIndex') tapIndex: number = -1; @State tapColor: Color = Color.Black; private index: number; private uri: Resource; - // 判断是否被选中 - onTapIndexChange() { - if (this.tapIndex >= 0 && this.index === this.tapIndex) { - console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, red`) - this.tapColor = Color.Red; - } else { - console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, black`) - this.tapColor = Color.Black; - } - } - build() { Column() { Image(this.uri) @@ -360,14 +424,18 @@ export struct TapImage { .onClick(() => { this.tapIndex = this.index; }) - .border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor }) + .border({ + width: 5, + style: BorderStyle.Dotted, + color: (this.tapIndex >= 0 && this.index === this.tapIndex) ? Color.Red : Color.Black + }) } - } } ``` + ## 限制条件 AppStorage与[PersistentStorage](arkts-persiststorage.md)以及[Environment](arkts-environment.md)配合使用时,需要注意以下几点: diff --git a/zh-cn/application-dev/reference/apis/js-apis-installer.md b/zh-cn/application-dev/reference/apis/js-apis-installer.md index b166cc4639cc186f88ce39d2bc13244014bcdaeb..029205001615d39074719c03481a2079e3f85d70 100644 --- a/zh-cn/application-dev/reference/apis/js-apis-installer.md +++ b/zh-cn/application-dev/reference/apis/js-apis-installer.md @@ -19,6 +19,8 @@ import installer from '@ohos.bundle.installer'; | ohos.permission.INSTALL_ENTERPRISE_BUNDLE | system_core | 允许应用安装企业InHouse应用。 | | ohos.permission.INSTALL_ENTERPRISE_MDM_BUNDLE | system_core | 允许在企业设备上安装企业MDM应用包。 | | ohos.permission.INSTALL_ENTERPRISE_NORMAL_BUNDLE | system_core | 允许在企业设备上安装企业NORMAL应用包。 | +| ohos.permission.UNINSTALL_BUNDLE | system_core | 允许应用卸载应用。 | +| ohos.permission.RECOVER_BUNDLE | system_core | 允许应用恢复预置应用。 | | ohos.permission.INSTALL_SELF_BUNDLE | system_core | 允许企业MDM应用在企业设备上自升级。| @@ -322,7 +324,7 @@ uninstall(bundleName: string, installParam: InstallParam, callback: AsyncCallbac **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.UNINSTALL_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -382,7 +384,7 @@ uninstall(bundleName: string, callback: AsyncCallback<void>): void; **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.UNINSTALL_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -434,7 +436,7 @@ uninstall(bundleName: string, installParam?: InstallParam) : Promise\; **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.UNINSTALL_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -497,7 +499,7 @@ recover(bundleName: string, installParam: InstallParam, callback: AsyncCallback& **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.RECOVER_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -555,7 +557,7 @@ recover(bundleName: string, callback: AsyncCallback<void>): void; **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.RECOVER_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -605,7 +607,7 @@ recover(bundleName: string, installParam?: InstallParam) : Promise\; **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.RECOVER_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -665,7 +667,7 @@ uninstall(uninstallParam: UninstallParam, callback : AsyncCallback\) : voi **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.UNINSTALL_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core @@ -719,7 +721,7 @@ uninstall(uninstallParam: UninstallParam) : Promise\; **系统接口:** 此接口为系统接口。 -**需要权限:** ohos.permission.INSTALL_BUNDLE +**需要权限:** ohos.permission.INSTALL_BUNDLE 或 ohos.permission.UNINSTALL_BUNDLE10+ **系统能力:** SystemCapability.BundleManager.BundleFramework.Core diff --git a/zh-cn/device-dev/porting/porting-smallchip-driver-oom.md b/zh-cn/device-dev/porting/porting-smallchip-driver-oom.md index 2c7e246f02abd06bc5137cfa4e244f64a6ec136d..f79d43e6da68b7436ab2c52395aa2b3e155f4c42 100644 --- a/zh-cn/device-dev/porting/porting-smallchip-driver-oom.md +++ b/zh-cn/device-dev/porting/porting-smallchip-driver-oom.md @@ -8,9 +8,10 @@ 移植LCD驱动的主要工作是编写一个驱动,在驱动中生成模型的实例,并完成注册。 -这些LCD的驱动被放置在源码目录//drivers/hdf_core/framework/model/display/driver/panel中。 +这些LCD的驱动被放置在源码目录`//drivers/hdf_core/framework/model/display/driver/panel`中。 1. 创建Panel驱动 + 创建HDF驱动,在驱动初始化中调用RegisterPanel接口注册模型实例。如: @@ -36,7 +37,8 @@ ``` 2. 配置加载panel驱动 - 产品的所有设备信息被定义在源码文件//vendor/vendor_name/product_name/config/device_info/device_info.hcs中。修改该文件,在display的host中,名为device_lcd的device中增加配置。 + + 产品的所有设备信息被定义在源码文件`//vendor/vendor_name/product_name/config/device_info/device_info.hcs`中。修改该文件,在display的host中,名为device_lcd的device中增加配置。 > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:** > moduleName 要与panel驱动中的moduleName相同。 @@ -61,12 +63,13 @@ ## TP驱动移植 -本节描述如何移植触摸屏驱动。触摸屏的器件驱动被放置在源码目录//drivers/hdf_core/framework/model/input/driver/touchscreen中。 移植触摸屏驱动主要工作是向系统注册ChipDevice模型实例。 +本节描述如何移植触摸屏驱动。触摸屏的器件驱动被放置在源码目录`//drivers/hdf_core/framework/model/input/driver/touchscreen`中。 移植触摸屏驱动主要工作是向系统注册ChipDevice模型实例。 详细的驱动开发指导,请参考 [TOUCHSCREEN开发指导](../driver/driver-peripherals-touch-des.md)。 1. 创建触摸屏器件驱动 - 在上述touchscreen目录中创建名为touch_ic_name.c的文件。编写如下内容 + + 在上述touchscreen目录中创建名为`touch_ic_name.c`的文件。编写如下内容 ``` @@ -103,21 +106,22 @@ | int32_t (\*UpdateFirmware)(ChipDevice \*device) | 实现固件升级 | 2. 配置产品,加载器件驱动 - 产品的所有设备信息被定义在源码文件//vendor/vendor_name/product_name/config/device_info/device_info.hcs中。修改该文件,在名为input的host中,名为device_touch_chip的device中增加配置。 + + 产品的所有设备信息被定义在源码文件`//vendor/vendor_name/product_name/config/device_info/device_info.hcs`中。修改该文件,在名为input的host中,名为device_touch_chip的device中增加配置。 > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** > moduleName 要与触摸屏驱动中的moduleName相同。 ``` - deviceN :: deviceNode { - policy = 0; - priority = 130; - preload = 0; - permission = 0660; - moduleName = "HDF_TOUCH_XXXX"; - deviceMatchAttr = "touch_XXXX_configs"; - } + deviceN :: deviceNode { + policy = 0; + priority = 130; + preload = 0; + permission = 0660; + moduleName = "HDF_TOUCH_XXXX"; + deviceMatchAttr = "touch_XXXX_configs"; + } ``` @@ -126,6 +130,7 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负责处理WLAN流量。 **图1** OpenHarmony WLAN结构示意图 + ![zh-cn_image_0000001207756867](figures/zh-cn_image_0000001207756867.png) 如图1,左半部分负责管理WLAN设备,右半部分负责WLAN流量。HDF WLAN分别为这两部分做了抽象,驱动的移植过程可以看做分别实现这两部分所需接口。这些接口有: @@ -141,8 +146,9 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 具体的移植步骤如下: -1. 创建HDF WLAN 芯片驱动 - 在目录/device/vendor_name/peripheral/wifi/chip_name/ 创建文件 hdf_wlan_chip_name.c。内容模板如下: +1. 创建HDF WLAN芯片驱动 + + 在目录`/device/vendor_name/peripheral/wifi/chip_name/`创建文件`hdf_wlan_chip_name.c`。内容模板如下: ``` @@ -166,7 +172,7 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 HDF_INIT(g_hdfXXXChipEntry); ``` - 在上述代码的CreateChipDriverFactory方法中,需要创建一个HdfChipDriverFactory类型的对象。该对象提供如下方法 + 在上述代码的CreateChipDriverFactory方法中,需要创建一个HdfChipDriverFactory类型的对象。该对象提供如下方法: | 接口 | 说明 | | -------- | -------- | @@ -178,7 +184,7 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 | void (\*Release)(struct HdfChipDriver \*chipDriver) | 释放chipDriver | | uint8_t (\*GetMaxIFCount)(struct HdfChipDriverFactory \*factory) | 获取当前芯片支持的最大接口数 | - 其中Build方法负责创建一个管理指定网络接口的对象HdfChipDriver 。该对象需要提供方法: + 其中Build方法负责创建一个管理指定网络接口的对象HdfChipDriver。该对象需要提供方法: | 接口 | 说明 | | -------- | -------- | @@ -189,7 +195,8 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 | struct HdfMac80211APOps \*apOps | 支持AP模式所需要的接口集 | 2. 编写配置文件描述驱动支持的芯片 - 在产品配置目录下创建芯片的配置文件,保存至源码路径//vendor/vendor_name/product_name/config/wifi/wlan_chip_chip_name.hcs + + 在产品配置目录下创建芯片的配置文件,保存至源码路径`//vendor/vendor_name/product_name/config/wifi/wlan_chip_chip_name.hcs` 该文件模板如下: @@ -212,29 +219,32 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 ``` > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** - > 路径和文件中的vendor_name、product_name、chip_name请替换成实际名称 + > + > 路径和文件中的vendor_name、product_name、chip_name请替换成实际名称。 > > vendorId 和 deviceId需要根据实际芯片的识别码进行填写。 3. 编写配置文件,加载驱动 - 产品的所有设备信息被定义在源码文件//vendor/vendor_name/product_name/config/device_info/device_info.hcs中。修改该文件,在名为network的host中,名为device_wlan_chips的device中增加配置。模板如下: + + 产品的所有设备信息被定义在源码文件`//vendor/vendor_name/product_name/config/device_info/device_info.hcs`中。修改该文件,在名为network的host中,名为device_wlan_chips的device中增加配置。模板如下: ``` - deviceN :: deviceNode { - policy = 0; - preload = 2; - moduleName = "HDF_WLAN_CHIPS"; - deviceMatchAttr = "hdf_wlan_chips_chip_name"; - serviceName = "driverName"; - } + deviceN :: deviceNode { + policy = 0; + preload = 2; + moduleName = "HDF_WLAN_CHIPS"; + deviceMatchAttr = "hdf_wlan_chips_chip_name"; + serviceName = "driverName"; + } ``` > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** > moduleName 要与HDF WLAN 芯片驱动中的moduleName相同。 4. 修改Kconfig文件,让移植的WLAN模组出现再内核配置中 - 在device/vendor_name/drivers/Kconfig中增加配置菜单,模板如下 + + 在`device/vendor_name/drivers/Kconfig`中增加配置菜单,模板如下 ``` @@ -246,10 +256,11 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 ``` > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** - > 请替换模板中的chip_name为实际的芯片名称 + > 请替换模板中的chip_name为实际的芯片名称。 5. 修改构建脚本,让驱动参与内核构建 - 在源码文件//device/vendor_name/drivers/lite.mk末尾追加如下内容 + + 在源码文件`//device/vendor_name/drivers/lite.mk`末尾追加如下内容。 ``` @@ -262,4 +273,4 @@ WLAN驱动分为两部分,一部分负责管理WLAN设备,另一个部分负 ``` > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** - > 请替换模板中的chip_name为实际的芯片名称 + > 请替换模板中的chip_name为实际的芯片名称。 diff --git a/zh-cn/device-dev/porting/porting-smallchip-kernel-linux.md b/zh-cn/device-dev/porting/porting-smallchip-kernel-linux.md index 2b19663ca9314d684662e40ed273229aa72da186..c64e9e4eff3a918ea445f42cda16ad30e335eb9a 100644 --- a/zh-cn/device-dev/porting/porting-smallchip-kernel-linux.md +++ b/zh-cn/device-dev/porting/porting-smallchip-kernel-linux.md @@ -8,7 +8,7 @@ Linux内核移植主要涉及基于linux内核基线合入三方芯片补丁后 ### 基本信息 -当前Linux内核基线是基于Linux社区 4.19 LTS版本演进,合入CVE及bugfix补丁。具体信息参考[代码库](https://gitee.com/openharmony/kernel_linux),对应repo工程代码路径为kernel/linux-4.19。 +当前Linux内核基线是基于Linux社区 4.19 LTS版本演进,合入CVE及bugfix补丁。具体信息参考[代码库](https://gitee.com/openharmony/kernel_linux),对应repo工程代码路径为`kernel/linux-4.19`。 ### Bootloader @@ -19,17 +19,20 @@ Linux内核移植主要涉及基于linux内核基线合入三方芯片补丁后 ## 适配编译和烧录启动 1. 准备内核config(特别是芯片相关的config)。 - config文件所在源码目录:kernel/linux/config/ - 以hi3516dv300芯片为例,可在对应的linux-4.19/arch/arm/configs/目录下新建<YOUR_CHIP>_small_defconfig,如hi3516dv300_small_defconfig表示针对hi3516dv300小型系统的defconfig。该config文件可以由基础defconfig文件small_common_defconfig与该芯片相关的config组合生成。 + config文件所在源码目录:`kernel/linux/config/` + + 以hi3516dv300芯片为例,可在对应的`linux-4.19/arch/arm/configs/`目录下新建<YOUR_CHIP>_small_defconfig,如`hi3516dv300_small_defconfig`表示针对hi3516dv300小型系统的defconfig。该config文件可以由基础defconfig文件`small_common_defconfig`与该芯片相关的config组合生成。 2. 准备芯片补丁。 - 补丁文件所在源码目录:kernel/linux/patches/linux-4.19 + + 补丁文件所在源码目录:`kernel/linux/patches/linux-4.19` 以hi3516dv300芯片为例,参考已有的patch目录hi3516dv300_small_patch目录,新建<YOUR_CHIP>_patch目录,放置相关芯片补丁,注意hdf.patch等驱动补丁。 3. 编译。 - 具体内核编译入口脚本位于工程目录kernel/linux/patches/下面,版本级整编命令会通过BUILD.gn进入kernel_module_build.sh和kernel.mk,需要在这2个文件中针对性进行patch及defconfig文件路径、编译器、芯片架构、内核Image格式等的适配。 + + 具体内核编译入口脚本位于工程目录`kernel/linux/patches/`下面,版本级整编命令会通过BUILD.gn进入`kernel_module_build.sh`和`kernel.mk`,需要在这2个文件中针对性进行patch及defconfig文件路径、编译器、芯片架构、内核Image格式等的适配。 通过编译错误日志调整补丁,典型错误场景: @@ -38,11 +41,12 @@ Linux内核移植主要涉及基于linux内核基线合入三方芯片补丁后 (2)编译失败,内核版本差异(函数实现调整等)需要针对性进行内核适配。 > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:** - > - 参考kernel.mk,在OpenHarmony工程的编译构建流程中会拷贝kernel/linux-4.19的代码环境后进行打补丁动作,在使用版本级编译命令前,需要kernel/linux-4.19保持原代码环境。 + > - 参考`kernel.mk`,在OpenHarmony工程的编译构建流程中会拷贝kernel/linux-4.19的代码环境后进行打补丁动作,在使用版本级编译命令前,需要kernel/linux-4.19保持原代码环境。 > - > - 对应拷贝后的目录位于: out/<\*\*\*>/kernel/linux-4.19,可以在该目录下进行补丁的修改适配。 + > - 对应拷贝后的目录位于:`out/<***>/kernel/linux-4.19`,可以在该目录下进行补丁的修改适配。 4. 烧录启动。 + 由于不同芯片的开发板的烧录方式不一样,此处不表述具体的烧录方式。需要注意烧录的各镜像的大小及启动参数的配置,参考hi3516dv300采用uboot启动参数: @@ -56,43 +60,56 @@ Linux内核移植主要涉及基于linux内核基线合入三方芯片补丁后 调试init进程、启动shell和运行简单的用户态程序,验证内核移植是否成功。OpenHarmony小型系统的OS镜像结构以及linux用户态的启动流程如下图1所示: **图1** 基于linux内核的OS镜像结构和用户态程序启动流程 + ![zh-cn_image_0000001154372318](figures/zh-cn_image_0000001154372318.png) 基于上述流程,推荐按以下步骤完成验证: 1. 制作根文件系统镜像。 - 请参考[新建芯片解决方案和产品解决方案](../subsystems/subsys-build-all.md)生成根文件系统镜像rootfs.img。从上图可以看到启动过程与产品配置强相关,在制作rootfs.img过程中请完成如下四种配置: + + 请参考[新建芯片解决方案和产品解决方案](../subsystems/subsys-build-all.md)生成根文件系统镜像`rootfs.img`。从上图可以看到启动过程与产品配置强相关,在制作rootfs.img过程中请完成如下四种配置: - 组件配置 - 产品组件配置文件vendor/{company}/{product}/config.json需配置启动恢复子系统(startup)的init_lite组件和内核子系统的linux_4_1_9组件。 + + 产品组件配置文件`vendor/{company}/{product}/config.json`需配置启动恢复子系统(startup)的init_lite组件和内核子系统的linux_4_1_9组件。 + - 系统服务配置 - 系统服务配置文件vendor/{company}/{product}/init_configs/init_xxx.cfg需要启动shell服务。 + + 系统服务配置文件`vendor/{company}/{product}/init_configs/init_xxx.cfg`需要启动shell服务。 + - 文件系统配置 - 文件系统配置vendor/{company}/{product}/fs.yml中需要创建“/bin/sh -> mksh“和“/lib/ld-musl-arm.so.1 -> libc.so“软连接,这两个文件分别是shell可执行程序和可执行程序依赖的c库。 + + 文件系统配置`vendor/{company}/{product}/fs.yml`中需要创建`/bin/sh -> mksh`和`/lib/ld-musl-arm.so.1 -> libc.so`软连接,这两个文件分别是shell可执行程序和可执行程序依赖的c库。 + - 启动配置 - 启动配置在vendor/{company}/{product}/init_configs/etc目录下,包括fstab、rsS和Sxxx文件,请按开发板实际情况配置。 + + 启动配置在`vendor/{company}/{product}/init_configs/etc`目录下,包括fstab、rsS和Sxxx文件,请按开发板实际情况配置。 编译完成后,可通过检查产品编译输出目录下的rootfs内容,确认rootfs.img文件生成是否符合预期。 2. 调试init进程和shell。 - 烧录rootfs.img并调试init进程和shell,不同厂商的开发板的烧录工具和流程可能不同,请按芯片解决方案提供的流程进行烧录。烧录rootfs.img前请确认bootloader和linux内核启动正常。如果rootfs.img被内核正常挂载,接着将运行/bin/init程序,init进程为用户态的第一个应用程序,它的运行意味着用户态的开始。 - init程序首先会调用/etc/init.d/rcS脚本,rcS脚本执行第一条命令为"/bin/mount -a”,该命令会加载fstab文件,在fstab中的命令执行完后rcS将顺序调用Sxxx脚本完成设备节点创建和扫描、文件权限配置等操作。 + 烧录`rootfs.img`并调试init进程和shell,不同厂商的开发板的烧录工具和流程可能不同,请按芯片解决方案提供的流程进行烧录。烧录`rootfs.img`前请确认bootloader和linux内核启动正常。如果`rootfs.img`被内核正常挂载,接着将运行`/bin/init`程序,init进程为用户态的第一个应用程序,它的运行意味着用户态的开始。 - 最后,init程序会读取init.cfg系统服务配置文件。根据步骤1中的设置,init程序将会启动shell。如果上述流程运行正常,系统则会进入shell。 + init程序首先会调用`/etc/init.d/rcS`脚本,rcS脚本执行第一条命令为`/bin/mount -a`,该命令会加载fstab文件,在fstab中的命令执行完后rcS将顺序调用Sxxx脚本完成设备节点创建和扫描、文件权限配置等操作。 + + 最后,init程序会读取`init.cfg`系统服务配置文件。根据步骤1中的设置,init程序将会启动shell。如果上述流程运行正常,系统则会进入shell。 若串口有如下版本号日志打印,则表示init程序启动正常: **图2** init启动正常日志 + ![zh-cn_image_0000001154212516](figures/zh-cn_image_0000001154212516.png) 正常进入shell后执行ls命令,串口打印信息如下图: **图3** 正常进入shell后输入ls命令串口打印 + ![zh-cn_image_0000001200171991](figures/zh-cn_image_0000001200171991.png) 3. 配置NFS。 - init进程和shell正常启动后,以服务端IP为192.168.1.22、客户端IP为192.168.1.4为例,可在根目录执行如下命令开启NFS: + + init进程和shell正常启动后,以服务端IP为192.168.1.22、客户端IP为192.168.1.4为例,可在根目录执行如下命令开启NFS: ``` ifconfig eth0 192.168.1.4 netmask 255.255.255.0 diff --git a/zh-cn/device-dev/porting/porting-smallchip-prepare-building.md b/zh-cn/device-dev/porting/porting-smallchip-prepare-building.md index 3f2930abbdb933d16d32022117ab4986c9e961ca..2cb0437484d41d372caf08c437a5db7f6919d058 100644 --- a/zh-cn/device-dev/porting/porting-smallchip-prepare-building.md +++ b/zh-cn/device-dev/porting/porting-smallchip-prepare-building.md @@ -20,7 +20,8 @@ sudo apt-get install gcc-arm-linux-gnueabi 了解编译框架和搭建完编译环境后,请参考如下步骤新建芯片解决方案: 1. 新建目录 - 芯片解决方案的目录规则为:device/{芯片解决方案厂商}/{开发板}。以海思的hispark_taurus开发板为例,在代码根目录执行如下命令建立目录: + + 芯片解决方案的目录规则为:`device/{芯片解决方案厂商}/{开发板}`。以海思的hispark_taurus开发板为例,在代码根目录执行如下命令建立目录: ``` @@ -59,7 +60,8 @@ sudo apt-get install gcc-arm-linux-gnueabi 目录树建立后开发板相关的源码放到hispark_taurus目录下。 2. 配置开发板编译选项 - 步骤1中的config.gni可配置开发板相关的编译选项,编译构建框架将会遵照该配置文件中的参数编译所有用户态OS组件。其中关键的字段说明如下: + + 步骤1中的`config.gni`可配置开发板相关的编译选项,编译构建框架将会遵照该配置文件中的参数编译所有用户态OS组件。其中关键的字段说明如下: ``` @@ -75,7 +77,7 @@ sudo apt-get install gcc-arm-linux-gnueabi board_ld_flags: 开发板配置的链接选项。 ``` - 还以海思的hispark_taurus开发板为例,对应的device/hisilicon/hispark_taurus/config.gni内容如下: + 还以海思的hispark_taurus开发板为例,对应的`device/hisilicon/hispark_taurus/config.gni`内容如下: ``` # Board CPU type, e.g. "cortex-a7", "riscv32". @@ -121,9 +123,10 @@ sudo apt-get install gcc-arm-linux-gnueabi ``` 3. 编写开发板编译脚本 - 步骤1中的BUILD.gn为新增的开发板的编译入口,主要用于编译开发板相关的代码,主要为设备侧驱动、设备侧接口适配(媒体,图形等)和开发板的SDK等等。 - 海思的hispark_taurus开发板的device/hisilicon/hispark_taurus/BUILD.gn可写成: + 步骤1中的`BUILD.gn`为新增的开发板的编译入口,主要用于编译开发板相关的代码,主要为设备侧驱动、设备侧接口适配(媒体,图形等)和开发板的SDK等等。 + + 海思的hispark_taurus开发板的`device/hisilicon/hispark_taurus/BUILD.gn`可写成: ``` @@ -137,4 +140,5 @@ sudo apt-get install gcc-arm-linux-gnueabi ``` 4. 编译调试 - 在开发板目录下执行hb set和hb build即可启动芯片解决方案的编译,编译框架会以开发板下的BUILD.gn为入口启动编译。 + + 在开发板目录下执行`hb set`和`hb build`即可启动芯片解决方案的编译,编译框架会以开发板下的`BUILD.gn`为入口启动编译。 diff --git a/zh-cn/device-dev/porting/standard-system-porting-guide.md b/zh-cn/device-dev/porting/standard-system-porting-guide.md index a9ffb4db04f67dbb3e572a68210a3d17c1ce43b8..72db49201ee862d04e4dee379d1e5424bfff4c9f 100644 --- a/zh-cn/device-dev/porting/standard-system-porting-guide.md +++ b/zh-cn/device-dev/porting/standard-system-porting-guide.md @@ -11,7 +11,7 @@ ### 定义产品 -在“//vendor/MyProductVendor/{product_name}名称的目录下创建一个config.json文件,该文件用于描述产品所使用的SOC 以及所需的子系统。配置如下: +在`//vendor/MyProductVendor/{product_name}`名称的目录下创建一个config.json文件,该文件用于描述产品所使用的SOC 以及所需的子系统。配置如下: //vendor/MyProductVendor/MyProduct/config.json @@ -41,29 +41,20 @@ ``` 主要的配置内容 -product_name:产品名称 必填 - -version:版本 必填 - -type:配置的系统级别,包含(small,standard …) 必填 - -target_cpu :设备的CPU类型(根据实际情况,这里的target_cpu也可能是arm64 、riscv、 x86等。) 必填 - -ohos_version:操作系统版本 选填 - -device_company:device厂商名 必填 - -board:开发板名称 必填 - -enable_ramdisk:是否启动ramdisk 必填 - -kernel_type 选填 - -kernel_version 选填 kernel_type与 kernel_version在 standard 是固定的不需要写。 - -subsystems:系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。必填 - -product_company:不体现在配置中,而是目录名,vendor下一级目录就是product_company,BUILD.gn脚本依然可以访问。 +| 配置项 | 说明 | +|-------|----------| +|product_name |(必填)产品名称| +|version|(必填)版本 | +|type|(必填)配置的系统级别,包含(small、standard等) | +|target_cpu |(必填)设备的CPU类型(根据实际情况,这里的target_cpu也可能是arm64 、riscv、 x86等)| +|ohos_version|(选填)操作系统版本| +|device_company|(必填)device厂商名| +|board|(必填)开发板名称| +|enable_ramdisk|(必填)是否启动ramdisk| +|kernel_type|(选填)内核类型| +|kernel_version|(选填)kernel_type与kernel_version在standard是固定的不需要写| +|subsystems|(必填)系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。| +|product_company|不体现在配置中,而是目录名,vendor下一级目录就是product_company,BUILD.gn脚本依然可以访问。| 已定义的子系统可以在“//build/subsystem_config.json”中找到。当然你也可以定制子系统。 @@ -79,7 +70,7 @@ product_company:不体现在配置中,而是目录名,vendor下一级目 ./build.sh --product-name MyProduct ``` -构建完成后,可以在“//out/{device_name}/packages/phone/images”目录下看到构建出来的OpenHarmony镜像文件。 +构建完成后,可以在`//out/{device_name}/packages/phone/images`目录下看到构建出来的OpenHarmony镜像文件。 ## 内核移植 @@ -89,7 +80,7 @@ product_company:不体现在配置中,而是目录名,vendor下一级目 ### 为SOC添加内核构建的子系统 -修改文件 //build/subsystem_config.json增加一个子系统. 配置如下: +修改文件`//build/subsystem_config.json`增加一个子系统。配置如下: ``` @@ -101,16 +92,16 @@ product_company:不体现在配置中,而是目录名,vendor下一级目 }, ``` -接着需要修改定义产品的配置文件//vendor/MyProductVendor/MyProduct/config.json,将刚刚定义的子系统加入到产品中。 +接着需要修改定义产品的配置文件`//vendor/MyProductVendor/MyProduct/config.json`,将刚刚定义的子系统加入到产品中。 ### 编译内核 -源码中提供了Linux 4.19的内核,归档在//kernel/linux-4.19。本节以该内核版本为例,讲解如何编译内核。 +源码中提供了Linux 4.19的内核,归档在`//kernel/linux-4.19`。本节以该内核版本为例,讲解如何编译内核。 在子系统的定义中,描述了子系统构建的路径path,即`//device/MySOCVendor/MySOC/build`。这一节会在这个目录创建构建脚本,告诉构建系统如何构建内核。 -建议的目录结构 +建议的目录结构: ``` @@ -126,9 +117,8 @@ BUILD.gn是subsystem构建的唯一入口。 期望的构建结果 - | | | -| -------- | -------- | | 文件 | 文件说明 | +| -------- | -------- | | $root_build_dir/packages/phone/images/uImage | 内核镜像 | | $root_build_dir/packages/phone/images/uboot | bootloader镜像 | @@ -156,11 +146,11 @@ BUILD.gn是subsystem构建的唯一入口。 2. init。 - init启动引导组件配置文件包含了所有需要由init进程启动的系统关键服务的服务名、可执行文件路径、权限和其他信息。每个系统服务各自安装其启动脚本到/system/etc/init目录下。 + init启动引导组件配置文件包含了所有需要由init进程启动的系统关键服务的服务名、可执行文件路径、权限和其他信息。每个系统服务各自安装其启动脚本到`/system/etc/init`目录下。 - 新芯片平台移植时,平台相关的初始化配置需要增加平台相关的初始化配置文件/vendor/etc/init/init.{hardware}.cfg;该文件完成平台相关的初始化设置,如安装ko驱动,设置平台相关的/proc节点信息。 + 新芯片平台移植时,平台相关的初始化配置需要增加平台相关的初始化配置文件`/vendor/etc/init/init.{hardware}.cfg`;该文件完成平台相关的初始化设置,如安装ko驱动,设置平台相关的`/proc`节点信息。 - init相关进程代码在//base/startup/init_lite目录下,该进程是系统第一个进程,无其它依赖。 + init相关进程代码在`//base/startup/init_lite`目录下,该进程是系统第一个进程,无其它依赖。 初始化配置文件具体的开发指导请参考 [init启动子系统概述](../subsystems/subsys-boot-overview.md)。 @@ -172,115 +162,117 @@ BUILD.gn是subsystem构建的唯一入口。 HDF为LCD设计了驱动模型。支持一块新的LCD,需要编写一个驱动,在驱动中生成模型的实例,并完成注册。 -这些LCD的驱动被放置在//drivers/hdf_core/framework/model/display/driver/panel目录中。 +这些LCD的驱动被放置在`//drivers/hdf_core/framework/model/display/driver/panel`目录中。 -- 创建Panel驱动 +1. 创建Panel驱动 -在驱动的Init方法中,需要调用RegisterPanel接口注册模型实例。如: + 在驱动的Init方法中,需要调用RegisterPanel接口注册模型实例。如: -``` -int32_t XXXInit(struct HdfDeviceObject *object) -{ - struct PanelData *panel = CreateYourPanel(); - - // 注册 - if (RegisterPanel(panel) != HDF_SUCCESS) { - HDF_LOGE("%s: RegisterPanel failed", __func__); - return HDF_FAILURE; - } - return HDF_SUCCESS; -} + ``` + int32_t XXXInit(struct HdfDeviceObject *object) + { + struct PanelData *panel = CreateYourPanel(); -struct HdfDriverEntry g_xxxxDevEntry = { - .moduleVersion = 1, - .moduleName = "LCD_XXXX", - .Init = XXXInit, -}; + // 注册 + if (RegisterPanel(panel) != HDF_SUCCESS) { + HDF_LOGE("%s: RegisterPanel failed", __func__); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } -HDF_INIT(g_xxxxDevEntry); -``` + struct HdfDriverEntry g_xxxxDevEntry = { + .moduleVersion = 1, + .moduleName = "LCD_XXXX", + .Init = XXXInit, + }; -- 配置加载panel驱动产品的所有设备信息被定义在文件//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs中。修改该文件,在display的host中,名为device_lcd的device中增加配置。注意:moduleName 要与panel驱动中的moduleName相同。 + HDF_INIT(g_xxxxDevEntry); + ``` -``` -root { - ... - display :: host { - device_lcd :: device { - deviceN :: deviceNode { - policy = 0; - priority = 100; - preload = 2; - moduleName = "LCD_XXXX"; - } - } - } -} -``` +2. 配置加载panel驱动产品的所有设备信息被定义在文件`//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs`中。修改该文件,在display的host中,名为device_lcd的device中增加配置。 + + 注意:moduleName要与panel驱动中的moduleName相同。 + + ``` + root { + ... + display :: host { + device_lcd :: device { + deviceN :: deviceNode { + policy = 0; + priority = 100; + preload = 2; + moduleName = "LCD_XXXX"; + } + } + } + } + ``` -更详细的驱动开发指导,请参考 [LCD](../driver/driver-peripherals-lcd-des.md)。 + 更详细的驱动开发指导,请参考[LCD](../driver/driver-peripherals-lcd-des.md)。 ### 触摸屏 -本节描述如何移植触摸屏驱动。触摸屏的驱动被放置在//drivers/hdf_core/framework/model/input/driver/touchscreen目录中。移植触摸屏驱动主要工作是向系统注册ChipDevice模型实例。 +本节描述如何移植触摸屏驱动。触摸屏的驱动被放置在`//drivers/hdf_core/framework/model/input/driver/touchscreen`目录中。移植触摸屏驱动主要工作是向系统注册ChipDevice模型实例。 -- 创建触摸屏器件驱动 +1. 创建触摸屏器件驱动 -在目录中创建名为touch_ic_name.c的文件。代码模板如下:注意:请替换ic_name为你所适配芯片的名称。 + 在目录中创建名为touch_ic_name.c的文件。代码模板如下:注意:请替换ic_name为你所适配芯片的名称。 -``` -#include "hdf_touch.h" + ``` + #include "hdf_touch.h" -static int32_t HdfXXXXChipInit(struct HdfDeviceObject *device) -{ - ChipDevice *tpImpl = CreateXXXXTpImpl(); - if(RegisterChipDevice(tpImpl) != HDF_SUCCESS) { - ReleaseXXXXTpImpl(tpImpl); - return HDF_FAILURE; - } - return HDF_SUCCESS; -} + static int32_t HdfXXXXChipInit(struct HdfDeviceObject *device) + { + ChipDevice *tpImpl = CreateXXXXTpImpl(); + if(RegisterChipDevice(tpImpl) != HDF_SUCCESS) { + ReleaseXXXXTpImpl(tpImpl); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } -struct HdfDriverEntry g_touchXXXXChipEntry = { - .moduleVersion = 1, - .moduleName = "HDF_TOUCH_XXXX", - .Init = HdfXXXXChipInit, -}; + struct HdfDriverEntry g_touchXXXXChipEntry = { + .moduleVersion = 1, + .moduleName = "HDF_TOUCH_XXXX", + .Init = HdfXXXXChipInit, + }; -HDF_INIT(g_touchXXXXChipEntry); -``` + HDF_INIT(g_touchXXXXChipEntry); + ``` -其中ChipDevice中要提供若干方法。 + 其中ChipDevice中要提供若干方法。 - | | | -| -------- | -------- | -| 方法 | 实现说明 | -| int32_t (\*Init)(ChipDevice \*device) | 器件初始化 | -| int32_t (\*Detect)(ChipDevice \*device) | 器件探测 | -| int32_t (\*Suspend)(ChipDevice \*device) | 器件休眠 | -| int32_t (\*Resume)(ChipDevice \*device) | 器件唤醒 | -| int32_t (\*DataHandle)(ChipDevice \*device) | 从器件读取数据,将触摸点数据填写入device->driver->frameData中 | -| int32_t (\*UpdateFirmware)(ChipDevice \*device) | 固件升级 | + | 方法 | 实现说明 | + | -------- | -------- | + | int32_t (\*Init)(ChipDevice \*device) | 器件初始化 | + | int32_t (\*Detect)(ChipDevice \*device) | 器件探测 | + | int32_t (\*Suspend)(ChipDevice \*device) | 器件休眠 | + | int32_t (\*Resume)(ChipDevice \*device) | 器件唤醒 | + | int32_t (\*DataHandle)(ChipDevice \*device) | 从器件读取数据,将触摸点数据填写入device->driver->frameData中 | + | int32_t (\*UpdateFirmware)(ChipDevice \*device) | 固件升级 | -- 配置产品,加载器件驱动 - 产品的所有设备信息被定义在文件//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs中。修改该文件,在名为input的host中,名为device_touch_chip的device中增加配置。注意:moduleName 要与触摸屏驱动中的moduleName相同。 +2. 配置产品,加载器件驱动 + + 产品的所有设备信息被定义在文件`//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs`中。修改该文件,在名为input的host中,名为device_touch_chip的device中增加配置。注意:moduleName 要与触摸屏驱动中的moduleName相同。 -``` - deviceN :: deviceNode { - policy = 0; - priority = 130; - preload = 0; - permission = 0660; - moduleName = "HDF_TOUCH_XXXX"; - deviceMatchAttr = "touch_XXXX_configs"; - } -``` + ``` + deviceN :: deviceNode { + policy = 0; + priority = 130; + preload = 0; + permission = 0660; + moduleName = "HDF_TOUCH_XXXX"; + deviceMatchAttr = "touch_XXXX_configs"; + } + ``` -更详细的驱动开发指导,请参考 [TOUCHSCREEN](../driver/driver-peripherals-touch-des.md)。 + 更详细的驱动开发指导,请参考[TOUCHSCREEN](../driver/driver-peripherals-touch-des.md)。 ### WLAN @@ -293,135 +285,134 @@ Wi-Fi驱动分为两部分,一部分负责管理WLAN设备,另一个部分 支持一款芯片的主要工作是实现一个ChipDriver驱动。实现HDF_WLAN_CORE和NetDevice提供的接口。主要需要实现的接口有: -| | | | -| -------- | -------- | -------- | | 接口 | 定义头文件 | 说明 | +| -------- | -------- | -------- | | HdfChipDriverFactory | //drivers/hdf_core/framework/include/wifi/hdf_wlan_chipdriver_manager.h | ChipDriver的Factory,用于支持一个芯片多个Wi-Fi端口 | | HdfChipDriver | //drivers/hdf_core/framework/include/wifi/wifi_module.h | 每个WLAN端口对应一个HdfChipDriver,用来管理一个特定的WLAN端口 | | NetDeviceInterFace | //drivers/hdf_core/framework/include/net/net_device.h | 与协议栈之间的接口,如发送数据、设置网络接口状态等 | 建议适配按如下步骤操作: -1.创建HDF驱动建议将代码放置在//device/MySoCVendor/peripheral/wifi/chip_name/,文件模板如下: - - -``` -static int32_t HdfWlanXXXChipDriverInit(struct HdfDeviceObject *device) { - static struct HdfChipDriverFactory factory = CreateChipDriverFactory(); - struct HdfChipDriverManager *driverMgr = HdfWlanGetChipDriverMgr(); - if (driverMgr->RegChipDriver(&factory) != HDF_SUCCESS) { - HDF_LOGE("%s fail: driverMgr is NULL!", __func__); - return HDF_FAILURE; - } - return HDF_SUCCESS; -} - -struct HdfDriverEntry g_hdfXXXChipEntry = { - .moduleVersion = 1, - .Init = HdfWlanXXXChipDriverInit, - .Release = HdfWlanXXXChipRelease, - .moduleName = "HDF_WIFI_CHIP_XXX" -}; - -HDF_INIT(g_hdfXXXChipEntry); -``` - -在CreateChipDriverFactory中,需要创建一个HdfChipDriverFactory,接口如下: - - | | | -| -------- | -------- | -| 接口 | 说明 | -| const char \*driverName | 当前driverName | -| int32_t (\*InitChip)(struct HdfWlanDevice \*device) | 初始化芯片 | -| int32_t (\*DeinitChip)(struct HdfWlanDevice \*device) | 去初始化芯片 | -| void (_ReleaseFactory)(struct HdfChipDriverFactory _factory) | 释放HdfChipDriverFactory对象 | -| struct HdfChipDriver _(_Build)(struct HdfWlanDevice \*device, uint8_t ifIndex) | 创建一个HdfChipDriver;输入参数中,device是设备信息,ifIndex是当前创建的接口在这个芯片中的序号 | -| void (_Release)(struct HdfChipDriver _chipDriver) | 释放chipDriver | -| uint8_t (\*GetMaxIFCount)(struct HdfChipDriverFactory \*factory) | 获取当前芯片支持的最大接口数 | - -HdfChipDriver需要实现的接口有 - - | | | -| -------- | -------- | -| 接口 | 说明 | -| int32_t (\*init)(struct HdfChipDriver \*chipDriver, NetDevice \*netDev) | 初始化当前网络接口,这里需要向netDev提供接口NetDeviceInterFace | -| int32_t (\*deinit)(struct HdfChipDriver \*chipDriver, NetDevice \*netDev) | 去初始化当前网络接口 | -| struct HdfMac80211BaseOps \*ops | WLAN基础能力接口集 | -| struct HdfMac80211STAOps \*staOps | 支持STA模式所需的接口集 | -| struct HdfMac80211APOps \*apOps | 支持AP模式所需要的接口集 | - -2.编写配置文件,描述驱动支持的设备 - -在产品配置目录下创建芯片的配置文件//vendor/MyProductVendor/MyProduct/config/wifi/wlan_chip_chip_name.hcs。 - -注意: 路径中的vendor_name、product_name、chip_name请替换成实际名称。 - -模板如下: - - -``` -root { - wlan_config { - chip_name :& chipList { - chip_name :: chipInst { - match_attr = "hdf_wlan_chips_chip_name"; /* 这是配置匹配属性,用于提供驱动的配置根 */ - driverName = "driverName"; /* 需要与HdfChipDriverFactory中的driverName相同*/ - sdio { - vendorId = 0x0296; - deviceId = [0x5347]; - } - } - } - } -} -``` - -3.编写配置文件,加载驱动 - -产品的所有设备信息被定义在文件//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs中。修改该文件,在名为network的host中,名为device_wlan_chips的device中增加配置。注意:moduleName 要与触摸屏驱动中的moduleName相同。 - - -``` - deviceN :: deviceNode { - policy = 0; - preload = 2; - moduleName = "HDF_WLAN_CHIPS"; - deviceMatchAttr = "hdf_wlan_chips_chip_name"; - serviceName = "driverName"; - } -``` - -4.构建驱动 - -- 创建内核菜单在//device/MySoCVendor/peripheral目录中创建Kconfig文件,内容模板如下: - +1. 创建HDF驱动建议将代码放置在`//device/MySoCVendor/peripheral/wifi/chip_name/`,文件模板如下: + + + ``` + static int32_t HdfWlanXXXChipDriverInit(struct HdfDeviceObject *device) { + static struct HdfChipDriverFactory factory = CreateChipDriverFactory(); + struct HdfChipDriverManager *driverMgr = HdfWlanGetChipDriverMgr(); + if (driverMgr->RegChipDriver(&factory) != HDF_SUCCESS) { + HDF_LOGE("%s fail: driverMgr is NULL!", __func__); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + + struct HdfDriverEntry g_hdfXXXChipEntry = { + .moduleVersion = 1, + .Init = HdfWlanXXXChipDriverInit, + .Release = HdfWlanXXXChipRelease, + .moduleName = "HDF_WIFI_CHIP_XXX" + }; + + HDF_INIT(g_hdfXXXChipEntry); + ``` + + 在CreateChipDriverFactory中,需要创建一个HdfChipDriverFactory,接口如下: + + + + | 接口 | 说明 | + | -------- | -------- | + | const char \*driverName | 当前driverName | + | int32_t (\*InitChip)(struct HdfWlanDevice \*device) | 初始化芯片 | + | int32_t (\*DeinitChip)(struct HdfWlanDevice \*device) | 去初始化芯片 | + | void (_ReleaseFactory)(struct HdfChipDriverFactory _factory) | 释放HdfChipDriverFactory对象 | + | struct HdfChipDriver _(_Build)(struct HdfWlanDevice \*device, uint8_t ifIndex) | 创建一个HdfChipDriver;输入参数中,device是设备信息,ifIndex是当前创建的接口在这个芯片中的序号 | + | void (_Release)(struct HdfChipDriver _chipDriver) | 释放chipDriver | + | uint8_t (\*GetMaxIFCount)(struct HdfChipDriverFactory \*factory) | 获取当前芯片支持的最大接口数 | + + HdfChipDriver需要实现的接口有: + + | 接口 | 说明 | + | -------- | -------- | + | int32_t (\*init)(struct HdfChipDriver \*chipDriver, NetDevice \*netDev) | 初始化当前网络接口,这里需要向netDev提供接口NetDeviceInterFace | + | int32_t (\*deinit)(struct HdfChipDriver \*chipDriver, NetDevice \*netDev) | 去初始化当前网络接口 | + | struct HdfMac80211BaseOps \*ops | WLAN基础能力接口集 | + | struct HdfMac80211STAOps \*staOps | 支持STA模式所需的接口集 | + | struct HdfMac80211APOps \*apOps | 支持AP模式所需要的接口集 | + +2. 编写配置文件,描述驱动支持的设备。 + + 在产品配置目录下创建芯片的配置文件`//vendor/MyProductVendor/MyProduct/config/wifi/wlan_chip_chip_name.hcs`。 + + 注意: 路径中的vendor_name、product_name、chip_name请替换成实际名称。 + + 模板如下: + + ``` + root { + wlan_config { + chip_name :& chipList { + chip_name :: chipInst { + match_attr = "hdf_wlan_chips_chip_name"; /* 这是配置匹配属性,用于提供驱动的配置根 */ + driverName = "driverName"; /* 需要与HdfChipDriverFactory中的driverName相同*/ + sdio { + vendorId = 0x0296; + deviceId = [0x5347]; + } + } + } + } + } + ``` + +3. 编写配置文件,加载驱动。 + + 产品的所有设备信息被定义在文件`//vendor/MyProductVendor/MyProduct/config/device_info/device_info.hcs`中。修改该文件,在名为network的host中,名为device_wlan_chips的device中增加配置。 + + 注意:moduleName 要与触摸屏驱动中的moduleName相同。 + + ``` + deviceN :: deviceNode { + policy = 0; + preload = 2; + moduleName = "HDF_WLAN_CHIPS"; + deviceMatchAttr = "hdf_wlan_chips_chip_name"; + serviceName = "driverName"; + } + ``` + +4. 构建驱动 + + - 创建内核菜单在`//device/MySoCVendor/peripheral`目录中创建Kconfig文件,内容模板如下: -``` -config DRIVERS_WLAN_XXX - bool "Enable XXX WLAN Host driver" - default n - depends on DRIVERS_HDF_WIFI - help - Answer Y to enable XXX Host driver. Support chip xxx -``` + ``` + config DRIVERS_WLAN_XXX + bool "Enable XXX WLAN Host driver" + default n + depends on DRIVERS_HDF_WIFI + help + Answer Y to enable XXX Host driver. Support chip xxx + ``` -接着修改文件//drivers/hdf_core/adapter/khdf/linux/model/network/wifi/Kconfig,在文件末尾加入如下代码将配置菜单加入内核中,如: + 接着修改文件`//drivers/hdf_core/adapter/khdf/linux/model/network/wifi/Kconfig`,在文件末尾加入如下代码将配置菜单加入内核中,如: -``` -source "../../../../../device/MySoCVendor/peripheral/Kconfig" -``` + ``` + source "../../../../../device/MySoCVendor/peripheral/Kconfig" + ``` -- 创建构建脚本 - 在//drivers/hdf_core/adapter/khdf/linux/model/network/wifi/Makefile文件末尾增加配置,模板如下: + - 创建构建脚本 + + 在`//drivers/hdf_core/adapter/khdf/linux/model/network/wifi/Makefile`文件末尾增加配置,模板如下: -``` -HDF_DEVICE_ROOT := $(HDF_DIR_PREFIX)/../device -obj-$(CONFIG_DRIVERS_WLAN_XXX) += $(HDF_DEVICE_ROOT)/MySoCVendor/peripheral/build/standard/ -``` + ``` + HDF_DEVICE_ROOT := $(HDF_DIR_PREFIX)/../device + obj-$(CONFIG_DRIVERS_WLAN_XXX) += $(HDF_DEVICE_ROOT)/MySoCVendor/peripheral/build/standard/ + ``` -当在内核中开启DRIVERS_WLAN_XXX开关时,会调用//device/MySoCVendor/peripheral/build/standard/中的makefile。更多详细的开发手册,请参考[WLAN开发](../guide/device-wlan-led-control.md)。 + 当在内核中开启DRIVERS_WLAN_XXX开关时,会调用`//device/MySoCVendor/peripheral/build/standard/`中的makefile。更多详细的开发手册,请参考[WLAN开发](../guide/device-wlan-led-control.md)。 ### 开发移植示例 diff --git a/zh-cn/release-notes/changelogs/OpenHarmony_4.0.10.1/changelog-ArkUI.md b/zh-cn/release-notes/changelogs/OpenHarmony_4.0.10.1/changelog-ArkUI.md new file mode 100644 index 0000000000000000000000000000000000000000..0fdc45cc5433250d9478b120d76969595e1ca577 --- /dev/null +++ b/zh-cn/release-notes/changelogs/OpenHarmony_4.0.10.1/changelog-ArkUI.md @@ -0,0 +1,375 @@ +# ArkUI子系统changelog + +## cl.arkui.1 状态变量支持undefined和null + +**变更影响** + +API version 9:状态变量不支持undefined和null,当开发者给状态变量设置undefined或者null时,设置失败,即状态变量还是上一次的值。 + +API version 10:状态变量支持undefined和null,当开发者给状态变量设置undefined和null时,ArkUI框架会接受该值,即下一次读状态变量的是undefined和null,开发者要注意做判空保护。 + +**适配指导** + +API version 9,当开发者给状态变量设置undefined时,设置无效,会导致开发者忽略对undefined的校验。 +```ts +@Entry +@Component +struct Page3 { + @State messages: string[] = ['Hello World'] + + aboutToAppear() { + // AppStorage里没有对应的key,返回undefined + // API version 9:赋值不生效,ArkUI框架会拒绝undefined,this.messages还为其初始值['Hello World'] + // API version 10: 赋值生效,ArkUI框架会接受undefined,this.messages为undefined + this.messages = AppStorage.Get("aProp") + } + + build() { + Row() { + Column() { + // API version 9: 应用没有crash,length为1 + // API version 10:应用crash, Error message: Cannot read property length of undefined + Text(`the messages length: ${this.messages.length}`) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} +``` + +对于上述情况,当每一次给状态变量赋值undefined和null时,需要对状态变量是否为undefined做校验。 + +```ts +Text(`the messages length: ${this.messages?.length}`) +``` + +API version 10,ArkUI框架增强对状态变量类型和初始化的校验,ArkUI框架会抛出运行时报错。具体有以下两种情况: +1. @Link必须被父组件初始化。 + +对于以下示例,当前会抛出运行时报错,提示开发者需要初始化@Link。 +```ts +@Entry +@Component +struct Page3 { + @State aProp: boolean = true + + build() { + Row() { + Column() { + // crash: SynchedPropertyObjectTwoWayPU[9, 'linkProp']: constructor @Link/@Consume source variable in + // parent/ancestor @Component must be defined. Application error! + LinkChild() + // 错误的用常规变量初始化linkProp,ArkUI框架无认为没有初始化,和上述一样的报错 + LinkChild({ aProp: false }) + // 正确,用状态变量this.aProp初始化@Link + LinkChild({ aProp: this.aProp }) + } + .width('100%') + } + .height('100%') + } +} + +@Component +struct LinkChild { + @Link aProp: boolean + + build() { + Text(`linkProp: ${this.aProp}`) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } +} +``` + +2. 校验状态变量不支持的类型。 + +状态变量对于不支持的类型,比如function,抛出运行时报错来提示开发者。 +```ts +@Entry +@Component +struct Page3 { + // API version 10:运行时报错:@Component 'Page3': Illegal variable value error with decorated variable @State/@Provide 'functionProp': failed + // validation: 'undefined, null, number, boolean, string, or Object but not function, attempt to assign value type: 'function', + @State functionProp: () => void = () => { + console.info("123") + } + + aboutToAppear() { + this.functionProp() + } + + build() { + Row() { + Column() { + Text("hello") + } + .width('100%') + } + .height('100%') + } +} +``` + +## cl.arkui.2 @StorageLink在页面跳转和页签场景行为优化 + +**变更影响** + +AppStorage是应用全局的UI状态存储,@StorageLink是对AppStorage中对应key的双向同步,即@StorageLink修饰的变量的改变会同步回AppStorage,AppStorage的修改也会同步给@StorageLink。 +当AppStorage某一个key发生改变时,会通知所有绑定该key的@StorageLink,@StorageLink装饰的变量的改变,会触发组件的重新渲染,包括不可见页面或者TabContent组件,这就造成了不必要的更新和性能浪费。 + +本次优化针对两个场景做出了优化: +1. 不可见页面的@StorageLink不会被通知刷新。 +2. 不可见TabContent的@StorageLink不会被通知刷新。 +AppStorage的属性改变,将不立即通知这两个场景下的@StorageLink更新和其@Watch回调,而是当页面或TabContent重新回到激活状态时,即可见状态,再去触发UI更新和其@Watch回调。 + +可见切换不可见状态或不可见切换可见状态流程如下: +1. 页面或TabContent内的自定义组件从可见到不可见,即标记组件为inActive。 +2. 当AppStorage的属性变化时,标记不可见节点的@StorageLink装饰的变量已经变化,但不通知UI刷新。 +3. 当页面或TabContent重新可见,即从inActive回到Active状态,将@StorageLink的dependentElementIds添加到其所属自定义组件的脏节点,调用其@Watch方法,刷新UI。 + +这次优化带来的最重要的应用行为变更是:不可见的页面和TabContent的@StorageLink的watch方法将不被回调。当其回到可见状态时,才会触发@Watch方法回调和UI更新。 + +**适配指导** + +不建议开发者通过借助@StorageLink和@Watch作为事件通知机制,因为@StorageLink是和UI强相关的装饰器,更新成本相较于一般的事件通知机制emit较高。 + +TapImage中的点击事件,会触发AppStorage中tapIndex对应属性的改变。因为@StorageLink是双向同步,修改会同步会AppStorage中,所以,所有绑定AppStorage的tapIndex可见自定义组件都会被通知UI刷新。UI刷新带来的成本是巨大的,因此不建议开发者使用此方式来实现基本的事件通知功能。 + +```ts +// xxx.ets +class ViewData { + title: string; + uri: Resource; + color: Color = Color.Black; + + constructor(title: string, uri: Resource) { + this.title = title; + this.uri = uri + } +} + +@Entry +@Component +struct Gallery2 { + dataList: Array = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))] + scroller: Scroller = new Scroller() + + build() { + Column() { + Grid(this.scroller) { + ForEach(this.dataList, (item: ViewData, index?: number) => { + GridItem() { + TapImage({ + uri: item.uri, + index: index + }) + }.aspectRatio(1) + + }, (item: ViewData, index?: number) => { + return JSON.stringify(item) + index; + }) + }.columnsTemplate('1fr 1fr') + } + + } +} + +@Component +export struct TapImage { + @StorageLink('tapIndex') @Watch('onTapIndexChange') tapIndex: number = -1; + @State tapColor: Color = Color.Black; + private index: number; + private uri: Resource; + + // 判断是否被选中 + onTapIndexChange() { + if (this.tapIndex >= 0 && this.index === this.tapIndex) { + console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, red`) + this.tapColor = Color.Red; + } else { + console.info(`tapindex: ${this.tapIndex}, index: ${this.index}, black`) + this.tapColor = Color.Black; + } + } + + build() { + Column() { + Image(this.uri) + .objectFit(ImageFit.Cover) + .onClick(() => { + this.tapIndex = this.index; + }) + .border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor }) + } + + } +} +``` + +开发者可以使用emit订阅某个事件并接收事件回调,可以减少开销,增强代码的可读性。 + + +```ts +// xxx.ets +import emitter from '@ohos.events.emitter'; + +let NextID: number = 0; + +class ViewData { + title: string; + uri: Resource; + color: Color = Color.Black; + id: number; + + constructor(title: string, uri: Resource) { + this.title = title; + this.uri = uri + this.id = NextID++; + } +} + +@Entry +@Component +struct Gallery2 { + dataList: Array = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))] + scroller: Scroller = new Scroller() + private preIndex: number = -1 + + build() { + Column() { + Grid(this.scroller) { + ForEach(this.dataList, (item: ViewData) => { + GridItem() { + TapImage({ + uri: item.uri, + index: item.id + }) + }.aspectRatio(1) + .onClick(() => { + if (this.preIndex === item.id) { + return + } + var innerEvent = { eventId: item.id } + // 选中态:黑变红 + var eventData = { + data: { + "colorTag": 1 + } + } + emitter.emit(innerEvent, eventData) + + if (this.preIndex != -1) { + console.info(`preIndex: ${this.preIndex}, index: ${item.id}, black`) + var innerEvent = { eventId: this.preIndex } + // 取消选中态:红变黑 + var eventData = { + data: { + "colorTag": 0 + } + } + emitter.emit(innerEvent, eventData) + } + this.preIndex = item.id + }) + + }, (item: ViewData) => JSON.stringify(item)) + }.columnsTemplate('1fr 1fr') + } + + } +} + +@Component +export struct TapImage { + @State tapColor: Color = Color.Black; + private index: number; + private uri: Resource; + + onTapIndexChange(colorTag: emitter.EventData) { + this.tapColor = colorTag.data.colorTag ? Color.Red : Color.Black + } + + aboutToAppear() { + //定义事件ID + var innerEvent = { eventId: this.index } + emitter.on(innerEvent, this.onTapIndexChange.bind(this)) + } + + build() { + Column() { + Image(this.uri) + .objectFit(ImageFit.Cover) + .border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor }) + } + } +} +``` + +以上通知事件逻辑简单,也可以简化成三元表达式。 + +``` +// xxx.ets +class ViewData { + title: string; + uri: Resource; + color: Color = Color.Black; + + constructor(title: string, uri: Resource) { + this.title = title; + this.uri = uri + } +} + +@Entry +@Component +struct Gallery2 { + dataList: Array = [new ViewData('flower', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon')), new ViewData('OMG', $r('app.media.icon'))] + scroller: Scroller = new Scroller() + + build() { + Column() { + Grid(this.scroller) { + ForEach(this.dataList, (item: ViewData, index?: number) => { + GridItem() { + TapImage({ + uri: item.uri, + index: index + }) + }.aspectRatio(1) + + }, (item: ViewData, index?: number) => { + return JSON.stringify(item) + index; + }) + }.columnsTemplate('1fr 1fr') + } + + } +} + +@Component +export struct TapImage { + @StorageLink('tapIndex') tapIndex: number = -1; + @State tapColor: Color = Color.Black; + private index: number; + private uri: Resource; + + build() { + Column() { + Image(this.uri) + .objectFit(ImageFit.Cover) + .onClick(() => { + this.tapIndex = this.index; + }) + .border({ + width: 5, + style: BorderStyle.Dotted, + color: (this.tapIndex >= 0 && this.index === this.tapIndex) ? Color.Red : Color.Black + }) + } + } +} +``` diff --git a/zh-cn/release-notes/changelogs/OpenHarmony_4.0.9.5/changelogs-arkui.md b/zh-cn/release-notes/changelogs/OpenHarmony_4.0.9.5/changelogs-arkui.md index 30dc13cecc8f6eb436cd2af57fce8ee21837c41f..033db916826f6a21b81f93fa3e7ea526f5cd70cf 100644 --- a/zh-cn/release-notes/changelogs/OpenHarmony_4.0.9.5/changelogs-arkui.md +++ b/zh-cn/release-notes/changelogs/OpenHarmony_4.0.9.5/changelogs-arkui.md @@ -105,6 +105,7 @@ struct GridRowExample { .border({ color: '#880606', width: 2 }) } } +``` API Version 9:上方示例中的GridRow设定width中的"80pv"会等同于width设定字符串"80"