• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 标准系统方案之瑞芯微RK3566移植案例
2
3本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。
4
5## 产品配置和目录规划
6
7### 产品配置
8
9在产品`//vendor/`目录下创建以kaihong名字命名的文件夹,并在kaihong文件夹下面新建产品命的文件夹khdvk_3566b。
10
11在`//vendor/kaihong/khdvk_3566b`目录下创建config.json文件。该文件用于描述产品所使用的SOC以及所需的子系统。配置如下
12
13```
14{
15  "product_name": "khdvk_3566b",
16  "device_company": "kaihong",
17  "device_build_path": "device/board/kaihong/build",
18  "target_cpu": "arm",
19  "type": "standard",
20  "version": "3.0",
21  "board": "khdvk_3566b",
22  "enable_ramdisk": true,//是否支持ramdisk二级启动
23  "build_selinux": true,// 是否支持selinux权限管理
24  "subsystems": [
25    {
26      "subsystem": "arkui",
27      "components": [
28        {
29          "component": "ace_engine_standard",
30          "features": []
31        },
32        {
33          "component": "napi",
34          "features": []
35        }
36      ]
37    },
38    .
39    .
40    .
41    {
42      "subsystem": "thirdparty",
43      "components": [
44        {
45          "component": "musl",
46          "features": []
47        }
48      ]
49    }
50  ]
51}
52```
53
54主要的配置内容包括:
55
561. product_device:配置所使用的SOC。
572. type:配置系统的级别,这里直接standard即可。
583. subsystems:系统需要启用的子系统。子系统可以简单理解为一块独立构建的功能块。
59
60已定义的子系统可以在`//build/subsystem_config.json`中找到。当然你也可以定制子系统。
61
62这里建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products这个子系统。这个子系统为Hi3516DV300 SOC编译内核,不适合rk3566
63
64### 目录规划
65
66参考https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md,并把芯片适配目录规划为:
67
68```
69device
70├── board                                --- 单板厂商目录
71│   └── kaihong                          --- 单板厂商名字:
72│       └── khdvk_3566b                  --- 单板名:khdvk_3566b,主要放置开发板相关的驱动业务代码
73└── soc                                  --- SoC厂商目录
74    └── rockchip                         --- SoC厂商名字:rockchip
75        └── rk3566                       --- SoC Series名:rk3566,主要为芯片原厂提供的一些方案,以及闭源库等
76
77
78vendor
79└── kaihong                              --- 开发产品样例厂商目录
80    └── khdvk_3566b                      --- 产品名字:产品、hcs以及demo相关
81```
82
83## 内核启动
84
85### 二级启动
86
87二级启动简单来说就是将之前直接挂载sytem,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init。
88
89Rk3566适配主要是将主线编译出来的ramdisk打包到boot.img中,主要有以下工作:
90
911.使能二级启动
92
93在//vendor/kaihong/khdvk_3566b/config.json中使能enable_ramdisk。
94
95```
96{
97  "product_name": "khdvk_3566b",
98  "device_company": "kaihong",
99  "device_build_path": "device/board/kaihong/build",
100  "target_cpu": "arm",
101  "type": "standard",
102  "version": "3.0",
103  "board": "khdvk_3566b",
104  "enable_ramdisk": true,//是否支持ramdisk二级启动
105  "build_selinux": true,// 是否支持selinux权限管理
106```
107
1082.把主线编译出来的ramdsik.img 打包到boot.img
109
110配置:
111
112由于rk 启动uboot 支持从ramdisk 启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img,因此没有使用主线的its格式,具体配置就是在内核编译脚本make-ohos.sh中增加:
113
114```
115function make_extlinux_conf()
116{
117    dtb_path=$1
118    uart=$2
119    image=$3
120
121    echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF}
122    echo "    kernel /extlinux/${image}" >> ${EXTLINUX_CONF}
123    echo "    fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF}
124    if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then
125        echo "    initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF}
126    fi
127    cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4"
128    echo "  ${cmdline}" >> ${EXTLINUX_CONF}
129}
130```
131
132### 打包
133
134增加了打包boot镜像的脚本make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容:
135
136```
137genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img
138```
139
140调用make-boot.sh的修改可以参考如下pr:
141
142https://gitee.com/openharmony/build/pulls/569/files
143
144### INIT配置
145
146init相关配置请参考[启动子系统的规范要求](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/启动恢复子系统.md)即可
147
148## **音频**
149
150##### khdvk_3566b Audio硬件结构图
151
152![](../figures/khdvk_3566b_audio_01.png)
153
154##### khdvk_3566b平台Audio驱动框架图
155
156![](../figures/khdvk_3566b_audio_02.png)
157
1581. HDI adapter
159
160实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。
161
1622. Audio Interface Lib
163
164配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入。它里面包括Stream_ctrl_common 通用层,主要是为了和上层的audio HDI adapter层进行对接。
165
1663. ADM(Audio Driver Model)
167
168音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配OpenHarmony系统。
169
1704. Audio Control Dispatch
171
172接收lib层的控制指令并将控制指令分发到驱动层。
173
1745. Audio Stream Dispatch
175
176接收lib层的数据并将数据分发到驱动层
177
1786. Card Manager
179
180多声卡管理模块,每个声卡含有Dai、Platform、Codec、Accessory、Dsp、SAPM模块。
181
1827. Platform Drivers
183
184驱动适配层。
185
1868. SAPM(Smart Audio Power Manager)
187
188电源管理模块,对整个ADM电源进行功耗策略优化。
189
190### Audio 驱动开发
191
192这里以khdvk_3566b为例,讲述Audio驱动开发,其涉及到的模块驱动主要有:Codec驱动、platform驱动、Dai驱动。
193相关代码路径如下:
194
195    device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/
196    device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/
197    device/board/kaihong/khdvk_3566b/audio_drivers/codec/soc/
198
199HDF HCS配置路径如下:
200
201    vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/
202    vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/
203
204Audio 驱动开发流程:
205
206    step1:配置各个模块的HCS
207    step2:修改各个模块的编译文件
208    step3:配置各个模块的函数操作集
209    step4:进行功能调试
210
211#### Audio驱动开发实例
212
213##### codec驱动开发实例
214
215代码路径:
216    device/board/kaihong/khdvk_3566b/audio_drivers/codec/rk809_codec/
217
2181. 将codec注册绑定到HDF框架中,moduleName与device_info.hcs中的moduleName匹配
219
220    struct HdfDriverEntry g_Rk809DriverEntry = {
221
222        .moduleVersion = 1,
223        .moduleName = "CODEC_RK809",
224        .Bind = Rk809DriverBind,
225        .Init = Rk809DriverInit,
226        .Release = RK809DriverRelease,
227
228    };
229
230    HDF_INIT(g_Rk809DriverEntry);
231
2322. Codec模块需要填充下面三个结构体:
233
234g_codecData:codec设备的操作函数集和私有数据集。
235
236g_codecDaiDeviceOps:codecDai的操作函数集,包括启动传输和参数配置等函数接口。
237
238g_codecDaiData:codec的数字音频接口的操作函数集和私有数据集。
239
240    struct CodecData g_rk809Data = {
241        .Init = Rk809DeviceInit,
242        .Read = RK809CodecReadReg,
243        .Write = Rk809CodecWriteReg,
244    };
245
246    struct AudioDaiOps g_rk809DaiDeviceOps = {
247        .Startup = Rk809DaiStartup,
248        .HwParams = Rk809DaiHwParams,
249        .Trigger = Rk809NormalTrigger,
250    };
251
252    struct DaiData g_rk809DaiData = {
253        .DaiInit = Rk809DaiDeviceInit,
254        .ops = &g_rk809DaiDeviceOps,
255    };
256
2571>  CodecData结构体操作函数的实现
258
259    int32_t Rk809DeviceInit(struct AudioCard *audioCard, const struct CodecDevice *device)
260    {
261         ......
262        //get和set功能注册
263        if (CodecSetCtlFunc(device->devData, RK809GetCtrlOps, RK809SetCtrlOps) != HDF_SUCCESS) {
264           AUDIO_DRIVER_LOG_ERR("AudioCodecSetCtlFunc failed.");
265           return HDF_FAILURE;
266        }
267       //codec默认寄存器的初始化
268       ret = RK809RegDefaultInit(device->devData->regCfgGroup);
269       ......
270       if (AudioAddControls(audioCard, device->devData->controls, device->devData->numControls) != HDF_SUCCESS) {
271           AUDIO_DRIVER_LOG_ERR("add controls failed.");
272           return HDF_FAILURE;
273       }
274       ......
275    }
276    /*读寄存器接口*/
277    int32_t RK809CodecReadReg(const struct CodecDevice *codec, uint32_t reg, uint32_t *val)
278    {
279        ......
280        if (Rk809DeviceRegRead(reg, val)) {
281            AUDIO_DRIVER_LOG_ERR("read register fail: [%04x]", reg);
282            return HDF_FAILURE;
283        }
284
285       return HDF_SUCCESS;
286    }
287     /*写寄存器接口*/
288    int32_t Rk809CodecWriteReg(const struct CodecDevice *codec, uint32_t reg, uint32_t value)
289    {
290        if (Rk809DeviceRegWrite(reg, value)) {
291            AUDIO_DRIVER_LOG_ERR("write register fail: [%04x] = %04x", reg, value);
292            return HDF_FAILURE;
293        }
294
295        return HDF_SUCCESS;
296    }
297
2982> g_rk809DaiDeviceOps结构体的具体实现
299
300    /*Rk809DaiStartup为启动时的一些设置*/
301    int32_t Rk809DaiStartup(const struct AudioCard *card, const struct DaiDevice *device)
302    {
303        ......
304        ret = RK809WorkStatusEnable(device->devData->regCfgGroup);
305        ......
306    }
307    /*Rk809DaiHwParams为参数配置,包括采样率、位宽等。*/
308    int32_t Rk809DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
309    {
310        ......
311        ret = AudioFormatToBitWidth(param->format, &bitWidth);
312        codecDaiParamsVal.frequencyVal = param->rate;
313        codecDaiParamsVal.DataWidthVal = bitWidth;
314
315        ret =  RK809DaiParamsUpdate(card->rtd->codecDai->devData->regCfgGroup, codecDaiParamsVal);
316        ......
317    }
318    /*PCM流控制寄存器相关配置*/
319    int32_t Rk809NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
320    {
321        g_cuurentcmd = cmd;
322        switch (cmd) {
323            case AUDIO_DRV_PCM_IOCTL_RENDER_START:
324            case AUDIO_DRV_PCM_IOCTL_RENDER_RESUME:
325                RK809DeviceRegConfig(rk817_render_start_regmap_config);
326            break;
327
328           case AUDIO_DRV_PCM_IOCTL_RENDER_STOP:
329           case AUDIO_DRV_PCM_IOCTL_RENDER_PAUSE:
330               RK809DeviceRegConfig(rk817_render_stop_regmap_config);
331               break;
332
333           case AUDIO_DRV_PCM_IOCTL_CAPTURE_START:
334           case AUDIO_DRV_PCM_IOCTL_CAPTURE_RESUME:
335              RK809DeviceRegConfig(rk817_capture_start_regmap_config);
336              break;
337
338           case AUDIO_DRV_PCM_IOCTL_CAPTURE_STOP:
339           case AUDIO_DRV_PCM_IOCTL_CAPTURE_PAUSE:
340                RK809DeviceRegConfig(rk817_capture_stop_regmap_config);
341             break;
342
343           default:
344             break;
345      }
346
347      return HDF_SUCCESS;
348    }
349
3503. 完成 bind、init和release函数的实现
351
352HdfDriverEntry结构体的具体填充:
353
354    /*获取codec service,以及注册codec*/
355    static int32_t Rk809DriverInit(struct HdfDeviceObject *device)
356    {
357       ......
358       CodecGetConfigInfo(device, &(g_chip->codec))
359       CodecSetConfigInfo(&(g_chip->codec),  &(g_chip->dai)
360       GetServiceName(device)
361       CodecGetDaiName(device,  &(g_chip->dai.drvDaiName)
362       OsalMutexInit(&g_rk809Data.mutex);
363       AudioRegisterCodec(device, &(g_chip->codec), &(g_chip->dai)
364       ......
365    }
366    /*将codec service绑定到HDF*/
367    static int32_t Rk809DriverBind(struct HdfDeviceObject *device)
368    {
369        struct CodecHost *codecHost;
370        ......
371        codecHost = (struct CodecHost *)OsalMemCalloc(sizeof(*codecHost));
372        ......
373        codecHost->device = device;
374        device->service = &codecHost->service;
375       return HDF_SUCCESS;
376    }
377    /*释放资源*/
378    static void RK809DriverRelease(struct HdfDeviceObject *device)
379    {
380       struct CodecHost *codecHost;
381       ......
382       codecHost = (struct CodecHost *)device->service;
383       if (codecHost == NULL) {
384           HDF_LOGE("CodecDriverRelease: codecHost is NULL");
385           return;
386       }
387       OsalMemFree(codecHost);
388    }
389
3904. 配置codec hcs文件
391
392   1> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs
393
394相关配置如下:
395
396    device_codec :: device {
397                device0 :: deviceNode {
398                    policy = 1;
399                    priority = 50;
400                    preload = 0;
401                    permission = 0666;
402                    moduleName = "CODEC_RK809";
403                    serviceName = "codec_service_0";
404                    deviceMatchAttr = "hdf_codec_driver";
405                }
406    }
407
4082> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/codec_config.hcs
409
410该文件涉及音量、静音模式、mic、通道模式等相关寄存器配置
411
412##### DAI驱动开发实例
413
414   代码路径:
415
416    device/board/kaihong/khdvk_3566b/audio_drivers/codec/dai/
417
4181. 将I2S驱动注册绑定到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致
419
420    struct HdfDriverEntry g_daiDriverEntry = {
421
422        .moduleVersion = 1,
423        .moduleName = "DAI_RK3568",
424        .Bind = DaiDriverBind,
425        .Init = DaiDriverInit,
426        .Release = DaiDriverRelease,
427
428    };
429    HDF_INIT(g_daiDriverEntry);
430
4312. DAI模块需要填充下面两个结构体
432
433g_daiData:dai设备私有配置,其中包含dai设备的初始化、读写寄存器、操作函数。
434
435g_daiDeviceOps:dai设备操作函数集,包含了dai的参数设置、触发、启动。
436
437    struct AudioDaiOps g_daiDeviceOps = {
438        .Startup = Rk3568DaiStartup,
439        .HwParams = Rk3568DaiHwParams,
440        .Trigger = Rk3568NormalTrigger,
441    };
442
443    struct DaiData g_daiData = {
444        .Read = Rk3568DeviceReadReg,
445        .Write = Rk3568DeviceWriteReg,
446        .DaiInit = Rk3568DaiDeviceInit,
447        .ops = &g_daiDeviceOps,
448    };
449
4501> AudioDaiOps结构体的具体填充
451
452    /*Rk3568DaiHwParams中主要完成一些pcm流信息的设置*/
453    int32_t Rk3568DaiHwParams(const struct AudioCard *card, const struct AudioPcmHwParams *param)
454    {
455         ......
456         data->pcmInfo.channels = param->channels;
457
458         if (AudioFormatToBitWidth(param->format, &bitWidth) != HDF_SUCCESS) {
459             AUDIO_DEVICE_LOG_ERR("AudioFormatToBitWidth error");
460             return HDF_FAILURE;
461         }
462
463         data->pcmInfo.bitWidth = bitWidth;
464         data->pcmInfo.rate = param->rate;
465         data->pcmInfo.streamType = param->streamType;
466
467         i2sTdm = dev_get_drvdata(&platformdev->dev);
468         ret = RK3568I2sTdmSetSysClk(i2sTdm, param);
469         if (ret != HDF_SUCCESS) {
470             AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetSysClk error");
471             return HDF_FAILURE;
472         }
473         ret = RK3568I2sTdmSetMclk(i2sTdm, &mclk, param);
474         if (ret != HDF_SUCCESS) {
475             AUDIO_DEVICE_LOG_ERR("RK3568I2sTdmSetMclk error");
476             return HDF_FAILURE;
477         }
478         AUDIO_DEVICE_LOG_DEBUG("success");
479         return HDF_SUCCESS;
480    }
481    int32_t Rk3568NormalTrigger(const struct AudioCard *card, int cmd, const struct DaiDevice *device)
482    {
483        ......
484        Rk3568TxAndRxSetReg(i2sTdm, streamType, triggerFlag);
485        ......
486    }
487
4882> DaiData结构体的具体填充
489
490    /*封装linux内核的读寄存器接口*/
491    int32_t Rk3568DeviceReadReg(const struct DaiDevice *dai, uint32_t reg, uint32_t *val)
492    {
493        ......
494        if (regmap_read(i2sTdm->regmap, reg, val)) {
495        ......
496    }
497    /*封装linux内核的写寄存器接口*/
498    int32_t Rk3568DeviceWriteReg(const struct DaiDevice *dai, uint32_t reg, uint32_t value)
499    {
500        ......
501        if (regmap_write(i2sTdm->regmap, reg, value)) {
502        ......
503    }
504    /*dai 设备的初始化*/
505    int32_t Rk3568DaiDeviceInit(struct AudioCard *card, const struct DaiDevice *dai)
506
5073. 完成 bind、init和release函数的实现
508
509HdfDriverEntry结构体中的bind、init、release具体填充:
510
511    static int32_t DaiDriverInit(struct HdfDeviceObject *device)
512    {
513        ......
514        DaiGetConfigInfo(device, &g_daiData)
515        DaiGetServiceName(device)
516        AudioSocRegisterDai(device, (void *)&g_daiData);
517        ......
518    }
519    static int32_t DaiDriverBind(struct HdfDeviceObject *device)
520    {
521        ......
522        daiHost->device = device;
523        device->service = &daiHost->service;
524        g_daiData.daiInitFlag = false;
525        ......
526    }
527    static void DaiDriverRelease(struct HdfDeviceObject *device)
528    {
529        ......
530        OsalMutexDestroy(&g_daiData.mutex);
531        daiHost = (struct DaiHost *)device->service;
532        OsalMemFree(daiHost);
533        ......
534    }
535
5364.配置dai hcs文件
537
5381> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs
539
540    device_dai0 :: device {
541        device0 :: deviceNode {
542            policy = 1;
543            priority = 50;
544            preload = 0;
545            permission = 0666;
546            moduleName = "DAI_RK3568";
547            serviceName = "dai_service";
548            deviceMatchAttr = "hdf_dai_driver";
549        }
550    }
551
5522> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dai_config.hcs
553
554该文件涉及I2S时序、配置参数以及rk809使能等相关寄存器配置
555
556##### Platform驱动开发实例
557
5581. 将DMA驱动注册到HDF框架中,代码片段如下,启动moduleName与HCS文件的中moduleName一致
559
560    struct HdfDriverEntry g_platformDriverEntry = {
561
562        .moduleVersion = 1,
563        .moduleName = "DMA_RK3568",
564        .Bind = PlatformDriverBind,
565        .Init = PlatformDriverInit,
566        .Release = PlatformDriverRelease,
567
568    };
569    HDF_INIT(g_platformDriverEntry);
570
5712. DMA模块需要填充下面两个结构体
572
573    struct AudioDmaOps g_dmaDeviceOps = {
574
575        .DmaBufAlloc = Rk3568DmaBufAlloc, //dma内存申请函数接口
576        .DmaBufFree = Rk3568DmaBufFree,   // dma内存释放函数接口
577        .DmaRequestChannel = Rk3568DmaRequestChannel,  // dma申请通道函数接口
578        .DmaConfigChannel = Rk3568DmaConfigChannel,    // dma通道配置函数接口
579        .DmaPrep = Rk3568DmaPrep,             // dma准备函数接口
580        .DmaSubmit = Rk3568DmaSubmit,         // dma submit函数接口
581        .DmaPending = Rk3568DmaPending,       // dma pending函数接口
582        .DmaPause = Rk3568DmaPause,           // dma暂停、停止函数接口
583        .DmaResume = Rk3568DmaResume,         // dma恢复函数接口
584        .DmaPointer = Rk3568PcmPointer,       // dma获取当前播放或录音位置函数接口
585
586    };
587
588    struct PlatformData g_platformData = {
589
590        .PlatformInit = AudioDmaDeviceInit,   // dma设备初始化接口
591        .ops = &g_dmaDeviceOps,
592
593    };
594
5953. 完成 bind、init和release函数的实现
596
597HdfDriverEntry结构体中的bind、init、release具体填充:
598
599    static int32_t PlatformDriverInit(struct HdfDeviceObject *device)
600    {
601        ......
602        PlatformGetServiceName(device);
603        AudioSocRegisterPlatform(device, &g_platformData)
604        ......
605    }
606    static int32_t PlatformDriverBind(struct HdfDeviceObject *device)
607    {
608        ......
609        platformHost->device = device;
610        device->service = &platformHost->service;
611        ......
612    }
613    static void PlatformDriverRelease(struct HdfDeviceObject *device)
614    {
615       ......
616       platformHost = (struct PlatformHost *)device->service;
617       OsalMemFree(platformHost);
618       ......
619    }
620
6214. 配置dma hcs文件
622
6231> vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs
624
625相关配置如下:
626
627     device_dma :: device {
628         device0 :: deviceNode {
629             policy = 1;
630             priority = 50;
631             preload = 0;
632             permission = 0666;
633             moduleName = "DMA_RK3568";
634             serviceName = "dma_service_0";
635             deviceMatchAttr = "hdf_dma_driver";
636         }
637     }
638
6392> vendor/kaihong/khdvk_3566b/hdf_config/khdf/audio/dma_config.hcs
640
641没有特殊参数需要配置,一般情况下不需改动。
642
643Makefile和Kconfig配置文件
644
645文件路径:
646
647    drivers/adapter/khdf/linux/model/audio
648
649Makefile文件相关内容:
650
651    obj-$(CONFIG_DRIVERS_HDF_AUDIO_RK3566) += \
652          $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_adapter.o \
653          $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_impl.o \
654          $(KHDF_AUDIO_RK3566_DIR)/codec/rk809_codec/src/rk809_codec_linux_driver.o \
655          $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_adapter.o \
656          $(KHDF_AUDIO_RK3566_DIR)/dsp/src/rk3568_dsp_ops.o \
657          $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_adapter.o \
658          $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_ops.o \
659          $(KHDF_AUDIO_RK3566_DIR)/dai/src/rk3568_dai_linux_driver.o \
660          $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_adapter.o \
661          $(KHDF_AUDIO_RK3566_DIR)/soc/src/rk3568_dma_ops.o
662
663Kconfig相关内容:
664
665    config DRIVERS_HDF_AUDIO_RK3566
666        bool "Enable HDF Audio Codec driver"
667        default n
668        depends on DRIVERS_HDF_AUDIO
669        help
670           Answer Y to choice HDF Audio Codec driver.
671
672## **LCD**
673
674khdvk_3566b平台默认支持一个mipi接口的lcd屏幕
675
676LCD的适配主要依赖于HDF显示模型,显示驱动模型基于 HDF 驱动框架、Platform 接口及 OSAL 接口开发,可以屏蔽不同内核形态(LiteOS、Linux)差异,适用于不同芯片平台,为显示屏器件提供统一的驱动平台。
677
678如图为 HDF Display驱动模型层次关系
679
680![640](../figures/khdvk_3566b_lcd_01.png)
681
682当前驱动模型主要部署在内核态中,向上对接到 Display 公共 hal 层,辅助 HDI 的实现。显示驱动通过 Display-HDI 层对图形服务暴露显示屏驱动能力;向下对接显示屏 panel 器件,驱动屏幕正常工作,自上而下打通显示全流程通路。
683
684所以LCD的适配主要在于LCD panel器件驱动的适配
685
686器件驱动的适配分为2部分:panel驱动和hcs配置
687
688涉及的文件有:
689
690    drivers/framework/model/display/driver/panel
691    vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info
692    vendor/kaihong/khdvk_3566b/hdf_config/khdf/input
693
694### panel驱动
695
696器件驱动主要围绕如下接口展开:
697
698    struct PanelData {
699        struct HdfDeviceObject *object;
700        int32_t (*init)(struct PanelData *panel);
701        int32_t (*on)(struct PanelData *panel);
702        int32_t (*off)(struct PanelData *panel);
703        int32_t (*prepare)(struct PanelData *panel);
704        int32_t (*unprepare)(struct PanelData *panel);
705        struct PanelInfo *info;
706        enum PowerStatus powerStatus;
707        struct PanelEsd *esd;
708        struct BacklightDev *blDev;
709        void *priv;
710    };
711
712驱动中在初始化接口中实例化该结构体:
713
714    panelSimpleDev->panel.init = PanelSimpleInit;
715    panelSimpleDev->panel.on = PanelSimpleOn;
716    panelSimpleDev->panel.off = PanelSimpleOff;
717    panelSimpleDev->panel.prepare = PanelSimplePrepare;
718    panelSimpleDev->panel.unprepare = PanelSimpleUnprepare;
719
720    static void PanelResInit(struct panel_jdi_gt911_dev *panel_dev)
721    {
722       ......
723       panel_dev->panel.info = &g_panelInfo;
724       panel_dev->panel.init = PanelInit;
725       panel_dev->panel.on = PanelOn;
726       panel_dev->panel.off = PanelOff;
727       panel_dev->panel.prepare = PanelPrepare;
728       panel_dev->panel.unprepare = PanelUnprepare;
729       ......
730    }
731
732g_panelInfo配置panel基础参数
733
734PanelInit负责panel的软件初始化
735
736PanelOn负责亮屏
737
738PanelOff负责灭屏
739
740PanelPrepare负责亮屏的硬件时序初始化
741
742PanelUnprepare负责灭屏的硬件时序初始化
743
744实例化后使用RegisterPanel接口向display模型注册该panel驱动即可
745
746需要说明的是,khdvk_3566b上的这款lcd使用的时候DRM显示框架
747
748### hcs配置
749
750    device3 :: deviceNode {
751           policy = 0;
752           priority = 100;
753           preload = 0;
754           moduleName = "LCD_MIPI_JDI_GT911";
755    }
756
757### 背光
758
759背光控制分为原生linux内核框架下背光驱动以及基于HDF框架开发的背光驱动模型。
760
761rk3566背光是通过pwm控制占空比实现的,具体使用的是pwm4
762
763linux背光驱动代码路径:
764
765    linux-5.10/drivers/video/backlight/pwm_bl.c
766    linux-5.10/drivers/video/backlight/backlight.c
767    linux-5.10/drivers/pwm/pwm-rockchip.c
768
769使用HDF框架下的背光驱动,需要关闭原生驱动
770
771    # CONFIG_BACKLIGHT_PWM is not set
772
773### HDF实现
774
775基于HDF框架开发的背光驱动模型,如下图:
776
777<img title="" src="../figures/khdvk_3566b_backlight-02.png" alt="" style="zoom:80%;">
778
779代码路径:
780
781    drivers/framework/model/display/driver/backlight/hdf_bl.c
782
783HDF BL入口函数:
784
785    static int32_t BacklightInit(struct HdfDeviceObject *object)
786    {
787         if (object == NULL) {
788         HDF_LOGE("%s: object is null!", __func__);
789         return HDF_FAILURE;
790         }
791
792         HDF_LOGI("%s success", __func__);
793         return HDF_SUCCESS;
794    }
795
796    struct HdfDriverEntry g_blDevEntry = {
797        .moduleVersion = 1,
798        .moduleName = "HDF_BL",
799        .Init = BacklightInit,
800        .Bind = BacklightBind,
801    };
802
803    HDF_INIT(g_blDevEntry);
804
805代码路径:
806
807    drivers/framework/model/display/driver/backlight/pwm_bl.c
808
809HDF PWM入口函数:
810
811    struct HdfDriverEntry g_pwmBlDevEntry = {
812    .moduleVersion = 1,
813    .moduleName = "PWM_BL",
814    .Init = BlPwmEntryInit,
815    };
816    HDF_INIT(g_pwmBlDevEntry);
817
818具体控制背光的接口:
819
820    static int32_t BlPwmUpdateBrightness(struct BacklightDev *blDev, uint32_t brightness)
821    {
822        int32_t ret;
823        uint32_t duty;
824        struct BlPwmDev *blPwmDev = NULL;
825
826        blPwmDev = ToBlDevPriv(blDev);
827        if (blPwmDev == NULL) {
828            HDF_LOGE("%s blPwmDev is null", __func__);
829            return HDF_FAILURE;
830        }
831
832        if (blPwmDev->props.maxBrightness == 0) {
833            HDF_LOGE("%s maxBrightness is 0", __func__);
834            return HDF_FAILURE;
835        }
836
837        if (brightness == 0) {
838             return PwmDisable(blPwmDev->pwmHandle);
839        }
840
841        duty = (brightness * blPwmDev->config.period) / blPwmDev->props.maxBrightness;
842        ret = PwmSetDuty(blPwmDev->pwmHandle, duty);
843        if (ret != HDF_SUCCESS) {
844            HDF_LOGE("%s: PwmSetDuty failed, ret %d", __func__, ret);
845            return HDF_FAILURE;
846        }
847        return PwmEnable(blPwmDev->pwmHandle);
848    }
849
850    static struct BacklightOps g_blDevOps = {
851         .updateBrightness = BlPwmUpdateBrightness,
852    };
853
854HDF PWM实现的调用的就是内核pwm的接口。
855
856<img title="" src="../figures/khdvk_3566b_backlight_03.png" alt="" style="zoom:80%;">
857
858代码路径:
859
860    drivers/framework/model/display/driver/panel/mipi_jdi_gt911.c
861
862在LCD HDF器件驱动注册背光:
863
864    panel_dev->panel.blDev = GetBacklightDev("hdf_pwm");
865    if (panel_dev->panel.blDev == NULL) {
866        HDF_LOGE("%s GetBacklightDev fail", __func__);
867        goto FAIL;
868    }
869
870### HCS配置
871
872驱动hcs配置:
873
874    device_pwm_bl :: device {
875        device0 :: deviceNode {
876            policy = 0;
877            priority = 95;
878            preload = 0;
879            moduleName = "PWM_BL";
880            deviceMatchAttr = "pwm_bl_dev";
881        }
882    }
883    device_backlight :: device {
884        device0 :: deviceNode {
885            policy = 2;
886            priority = 90;
887            preload = 0;
888            permission = 0660;
889            moduleName = "HDF_BL";
890            serviceName = "hdf_bl";
891        }
892    }
893
894pwm背光的hcs配置:
895
896    root {
897        backlightConfig {
898            pwmBacklightConfig {
899                match_attr = "pwm_bl_dev";
900                pwmDevNum = 1;
901                pwmMaxPeroid = 25000;
902                backlightDevName = "hdf_pwm";
903                minBrightness = 0;
904                defBrightness = 127;
905                maxBrightness = 255;
906           }
907       }
908    }
909
910### 测试
911
912cat /sys/kernel/debug/pwm 来查看hdf pwm是否申请到pwm4
913
914申请成功有如下结果:
915
916requested 代表申请成功
917
918enabled 代表pwm4使能成功
919
920    # cat /sys/kernel/debug/pwm
921    platform/fe6e0000.pwm, 1 PWM device
922    pwm-0   (backlight           ): requested period: 25000 ns duty: 0 ns polarity: normal
923
924## 显示适配
925
926显示适配需要完成的工作:图形服务HDI接口适配、GPU适配、mipi dsi驱动适配
927
928### 显示HDI
929
930[显示HDI](https://gitee.com/openharmony/drivers_peripheral/blob/master/display/README_zh.md)对图形服务提供显示驱动能力,包括显示图层的管理、显示内存的管理及硬件加速等。 显示HDI需要适配两部分:gralloc 和 display_device。
931
932OpenHarmony提供了使用与Hi3516DV300参考实现,厂商可根据实际情况参考适配,khdvk_3566b display适配是在//device/soc/rockchip/hardware/display目录下,仓名为[device_soc_rockchip](https://gitee.com/openharmony-sig/device_soc_rockchip)933
934#### display gralloc适配
935
936gralloc模块提供显示内存管理功能,该实现基于drm开发。
937
938drm设备节点定义在//device/soc/rockchip/hardware/display/src/display_gralloc/display_gralloc_gbm.c文件中,根据khdvk_3566b实际情况修改了drm文件节点。
939
940```
941const char *g_drmFileNode = "/dev/dri/renderD128";
942```
943
944#### display device适配
945
946display device模块提供显示设备管理、layer管理、硬件加速等功能。
947
9481. display drm设备节点初始化,根据khdvk_3566b实际情况修改了drm设备名称。
949
950```
951//device/soc/rockchip/hardware/display/src/display_device/drm/drm_device.cpp
952std::shared_ptr<HdiDeviceInterface> DrmDevice::Create()
953{
954    DISPLAY_DEBUGLOG();
955    if (mDrmFd == nullptr) {
956        const std::string name("rockchip");    // 将drm驱动设备名称修改为“rockchip”
957        int drmFd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);  // 将drm驱动设备文件句柄修改为"/dev/dri/card0"
958        if (drmFd < 0) {
959            DISPLAY_LOGE("drm file:%{public}s open failed %{public}s", name.c_str(), strerror(errno));
960            return nullptr;
961        }
962        DISPLAY_DEBUGLOG("the drm fd is %{public}d", drmFd);
963        mDrmFd = std::make_shared<HdiFd>(drmFd);
964    }
965    if (mInstance == nullptr) {
966        mInstance = std::make_shared<DrmDevice>();
967    }
968    return mInstance;
969}
970```
971
9722. display硬件合成的修改
973
974```
975//device/soc/rockchip/hardware/display/src/display_gfx/display_gfx.c
976```
977
978硬件合成文件添加了颜色空间的支持模式
979
980```
981RgaSURF_FORMAT colorSpaceModeChange(PixelFormat color, uint8_t *isYuv)
982{
983    RgaSURF_FORMAT rkFormat;
984    switch (color) {
985        case PIXEL_FMT_RGB_565:          /**< RGB565 format */
986            rkFormat = RK_FORMAT_RGB_565;
987            *isYuv = 0;
988            break;
989        case PIXEL_FMT_RGBA_4444:        /**< RGBA4444 format */
990            rkFormat = RK_FORMAT_RGBA_4444;
991            *isYuv = 0;
992            break;
993        case PIXEL_FMT_RGBA_5551:        /**< RGBA5551 format */
994            rkFormat = RK_FORMAT_RGBA_5551;
995            *isYuv = 0;
996            break;
997        case PIXEL_FMT_RGBX_8888:        /**< RGBX8888 format */
998            rkFormat = RK_FORMAT_RGBX_8888;
999            *isYuv = 0;
1000            break;
1001        case PIXEL_FMT_RGBA_8888:        /**< RGBA8888 format */
1002            rkFormat = RK_FORMAT_RGBA_8888;
1003            *isYuv = 0;
1004            break;
1005        case PIXEL_FMT_RGB_888:          /**< RGB888 format */
1006            rkFormat = RK_FORMAT_RGB_888;
1007            *isYuv = 0;
1008            break;
1009        case PIXEL_FMT_BGR_565:          /**< BGR565 format */
1010            rkFormat = RK_FORMAT_BGR_565;
1011            *isYuv = 0;
1012            break;
1013        case PIXEL_FMT_BGRA_4444:        /**< BGRA4444 format */
1014            rkFormat = RK_FORMAT_BGRA_4444;
1015            *isYuv = 0;
1016            break;
1017        case PIXEL_FMT_BGRA_5551:        /**< BGRA5551 format */
1018            rkFormat = RK_FORMAT_BGRA_5551;
1019            *isYuv = 0;
1020            break;
1021        case PIXEL_FMT_BGRX_8888:        /**< BGRX8888 format */
1022            rkFormat = RK_FORMAT_BGRX_8888;
1023            *isYuv = 0;
1024            break;
1025        case PIXEL_FMT_BGRA_8888:        /**< BGRA8888 format */
1026            rkFormat = RK_FORMAT_BGRA_8888;
1027            *isYuv = 0;
1028            break;
1029        case PIXEL_FMT_YCBCR_422_SP:     /**< YCBCR422 semi-planar format */
1030            rkFormat = RK_FORMAT_YCbCr_420_SP;
1031            *isYuv = 1;
1032            break;
1033        case PIXEL_FMT_YCRCB_422_SP:     /**< YCRCB422 semi-planar format */
1034            rkFormat = RK_FORMAT_YCrCb_422_SP;
1035            *isYuv = 1;
1036            break;
1037        case PIXEL_FMT_YCBCR_420_SP:     /**< YCBCR420 semi-planar format */
1038            rkFormat = RK_FORMAT_YCbCr_420_SP;
1039            *isYuv = 1;
1040            break;
1041        case PIXEL_FMT_YCRCB_420_SP:     /**< YCRCB420 semi-planar format */
1042            rkFormat = RK_FORMAT_YCrCb_420_SP;
1043            *isYuv = 1;
1044            break;
1045        case PIXEL_FMT_YCBCR_422_P:      /**< YCBCR422 planar format */
1046            rkFormat = RK_FORMAT_YCbCr_422_P;
1047            *isYuv = 1;
1048            break;
1049        case PIXEL_FMT_YCRCB_422_P:      /**< YCRCB422 planar format */
1050            rkFormat = RK_FORMAT_YCrCb_422_P;
1051            *isYuv = 1;
1052            break;
1053        case PIXEL_FMT_YCBCR_420_P:      /**< YCBCR420 planar format */
1054            rkFormat = RK_FORMAT_YCbCr_420_P;
1055            *isYuv = 1;
1056            break;
1057        case PIXEL_FMT_YCRCB_420_P:      /**< YCRCB420 planar format */
1058            rkFormat = RK_FORMAT_YCrCb_420_P;
1059            *isYuv = 1;
1060            break;
1061        case PIXEL_FMT_YUYV_422_PKG:     /**< YUYV422 packed format */
1062            rkFormat = RK_FORMAT_YUYV_422;
1063            *isYuv = 1;
1064            break;
1065        case PIXEL_FMT_UYVY_422_PKG:     /**< UYVY422 packed format */
1066            rkFormat = RK_FORMAT_UYVY_422;
1067            *isYuv = 1;
1068            break;
1069        case PIXEL_FMT_YVYU_422_PKG:     /**< YVYU422 packed format */
1070            rkFormat = RK_FORMAT_YUYV_422;
1071            *isYuv = 1;
1072            break;
1073        case PIXEL_FMT_VYUY_422_PKG:     /**< VYUY422 packed format */
1074            rkFormat = RK_FORMAT_VYUY_422;
1075            *isYuv = 1;
1076            break;
1077        default:
1078            rkFormat = RK_FORMAT_UNKNOWN;
1079            break;
1080    }
1081    return rkFormat;
1082}
1083```
1084
1085在合成时增加了旋转90、180、270度
1086
1087```
1088int32_t TransformTypeChange(TransformType type)
1089{
1090    int32_t rkRotateType;
1091    switch (type) {
1092        case ROTATE_90:            /**< Rotation by 90 degrees */
1093            rkRotateType = IM_HAL_TRANSFORM_ROT_90;
1094            break;
1095        case ROTATE_180:             /**< Rotation by 180 degrees */
1096            rkRotateType = IM_HAL_TRANSFORM_ROT_180;
1097            break;
1098        case ROTATE_270:             /**< Rotation by 270 degrees */
1099            rkRotateType = IM_HAL_TRANSFORM_ROT_270;
1100            break;
1101        default:
1102            rkRotateType = 0;        /**< No rotation */
1103            break;
1104    }
1105    return rkRotateType;
1106}
1107```
1108
1109#### 测试验证
1110
1111[hello_composer](https://gitee.com/openharmony/graphic_graphic_2d/tree/master/rosen/samples/composer)测试模块:Rosen图形框架提供的测试程序,主要显示流程,HDI接口等功能是否正常,默认随系统编译。
1112
1113代码路径:
1114
1115```
1116foundation/graphic/graphic_2d/rosen/samples/composer/
1117├── BUILD.gn
1118├── hello_composer.cpp
1119├── hello_composer.h
1120├── layer_context.cpp
1121├── layer_context.h
1122└── main.cpp
1123```
1124
1125具体验证如下:
1126
11271. 关闭render service
1128
1129   ```
1130   service_control stop render_service
1131   ```
1132
11332. 关闭 fondation进程
1134
1135   ```
1136   service_control stop fondation
1137   ```
1138
11393. 运行hello_composer测试相关接口
1140   切换到/system/bin目录下,运行hello_composer测试命令
1141
1142   ```
1143   #cd /system/bin
1144   #./hello_composer
1145   rga_api version 1.3.0_[1] (df26244 build: 2021-09-01 11:23:31 base: )
1146   ```
1147
1148   查看mipi显示屏幕上的变化
1149
1150https://gitee.com/openharmony/drivers_peripheral/tree/master/display/test/unittest/standard单元测试:HDI显示模块提供的测试模块,主要测试HDI接口、显示buffer、驱动等能力,测试时也需要关闭render service和fondation进程。
1151
1152代码路径:/drivers/peripheral/display/test/unittest/standard
1153
1154```
1155├── BUILD.gn
1156├── common
1157│   ├── display_test.h
1158│   ├── display_test_utils.cpp
1159│   └── display_test_utils.h
1160├── display_device
1161│   ├── hdi_composition_check.cpp
1162│   ├── hdi_composition_check.h
1163│   ├── hdi_device_test.cpp
1164│   ├── hdi_device_test.h
1165│   ├── hdi_test_device_common.h
1166│   ├── hdi_test_device.cpp
1167│   ├── hdi_test_device.h
1168│   ├── hdi_test_display.cpp
1169│   ├── hdi_test_display.h
1170│   ├── hdi_test_layer.cpp
1171│   ├── hdi_test_layer.h
1172│   ├── hdi_test_render_utils.cpp
1173│   └── hdi_test_render_utils.h
1174│── display_gfx
1175│   │── display_gfx_test.cpp
1176│   │── display_gfx_test.h
1177│   │── soft_blit.cpp
1178│   │── soft_blit.h
1179└── display_gralloc
1180    ├── display_gralloc_test.cpp
1181    └── display_gralloc_test.h
1182```
1183
1184具体验证如下:
1185
11861. 添加编译模块
1187   修改drivers/peripheral/display/test/BUILD.gn
1188
1189```
1190  group("hdf_test_display") {
1191    testonly = true
1192    deps = [
1193    "fuzztest:hdf_display_fuzztest",
1194    "unittest/standard:hdf_unittest_display",        //添加display单元测试
1195    ]
1196  }
1197```
1198
11992. 添加缺失的文件包含
1200   修改drivers/peripheral/display/test/unittest/standard/BUILD.gn
1201   在第63行处,添加包含目录//device/soc/rockchip/hardware/display/src/display_gralloc,如果不修改此处有可能编译报错。
1202
1203```
1204ohos_unittest("gralloctest") {
1205  module_out_path = module_output_path
1206  sources = [ "display_gralloc/display_gralloc_test.cpp" ]
1207  deps = [
1208    "//drivers/peripheral/display/hal:hdi_display_gralloc",
1209    "//third_party/googletest:gtest_main",
1210  ]
1211  include_dirs = [
1212    "common",
1213    "//drivers/peripheral/display/hal/default_standard/include",
1214    "//drivers/peripheral/display/hal/default_standard/src/display_gralloc",
1215    "//device/soc/rockchip/hardware/display/src/display_gralloc",        //添加这行,将display_gralloc包含进编译
1216    "//drivers/peripheral/display/interfaces/include",
1217    "//drivers/peripheral/base",
1218    "//drivers/peripheral/display/interfaces/include",
1219    "//foundation/graphic/standard/utils/include",
1220  ]
1221  external_deps = [
1222    "device_driver_framework:libhdf_utils",
1223    "utils_base:utils",
1224  ]
1225}
1226```
1227
12283. 编译命令
1229   编译hdf_test_display的命令如下:
1230
1231```
1232   ./build.sh --product-name khdvk_3566b --build-target hdf_test_display
1233```
1234
12354. 编译结果
1236   编译结果路径在out/khdvk_3566b/tests/unittest/hdf/display目录下,该目录下有三个可执行文件devicetest、gfxtest、gralloctest,可将这三文件通过hdc发送到khdvk_3566b开发板上运行测试。
1237
12385. 运行测试
1239   通过hdc下载到开发板/system/bin/目录下,并修改测试程序的可执行属性,在终端下输入如下命令
1240
1241```
1242hdc_std.exe file send D:\hdc\devicetest /system/bin/
1243hdc_std.exe file send D:\hdc\gfxtest /system/bin/
1244hdc_std.exe file send D:\hdc\gralloctest /system/bin/
1245```
1246
1247进入hdc命令hdc_std.exe shell后
1248
1249先关闭render service和foundation:
1250
1251```
1252service_control stop render_service
1253service_control stop fondation
1254```
1255
1256再分别执行命令,查看mipi屏显示结果:
1257
1258```
1259cd /system/bin/
1260```
1261
1262执行devicetest
1263
1264```
1265chmod -R 777 devicetest
1266devicetest
1267```
1268
1269执行gfxtest
1270
1271```
1272chmod -R 777 gfxtest
1273gfxtest
1274```
1275
1276执行gralloctest
1277
1278```
1279chmod -R 777 gralloctest
1280gralloctest
1281```
1282
1283### GPU
1284
1285GPU图形处理器, khdvk_3566b GPU适配是在//device/soc/rockchip/hardware/gpu目录下,目前采用的是rockchip提供闭源的bifrost gpu方案。
1286
1287目录结构:
1288
1289```
1290├── BUILD.gn
1291├── lib64
1292│   └── libmali-bifrost-g52-g2p0-ohos.so
1293├── lib
1294│   └── libmali-bifrost-g52-g2p0-ohos.so
1295└── include
1296    └── gbm.h
1297```
1298
1299gpu编译的内容,我们来看下BUILD.gn的内容,其中我们预编译了libmali-bifrost-g52-g2p0-ohos.so动态库,khdvk_3566b是arm64位的,所以编译了lib64目录下的libmali-bifrost-g52-g2p0-ohos.so动态库。其中gup模块符号链接libEGL.solibGLESv1.solibGLESv2.solibGLESv3.solibmali.so.0libmali.so.1动态库的符号。
1300
1301```
1302import("//build/ohos.gni")
1303import("//build/ohos/ndk/ndk.gni")
1304
1305config("libmali-bifrost-g52-g2p0-ohos") {
1306  include_dirs = [ "include" ]
1307  cflags = [
1308    "-Wno-incompatible-pointer-types",
1309    "-Werror",
1310    "-Wimplicit-function-declaration",
1311    "-Wno-error=unused-variable",
1312  ]
1313  cflags = []
1314}
1315
1316ohos_prebuilt_shared_library("mali-bifrost-g52-g2p0-ohos") {
1317  if (target_cpu == "arm") {
1318    source = "lib/libmali-bifrost-g52-g2p0-ohos.so"
1319  } else if (target_cpu == "arm64") {
1320    source = "lib64/libmali-bifrost-g52-g2p0-ohos.so"
1321  }
1322
1323  # decoupling system.img and vendor.img
1324  install_images = [ chipset_base_dir ]
1325  relative_install_dir = "chipsetsdk"
1326  subsystem_name = "rockchip_products"
1327  part_name = "rockchip_products"
1328  install_enable = true
1329  symlink_target_name = [
1330    "libEGL.so",
1331    "libGLESv1.so",
1332    "libGLESv2.so",
1333    "libGLESv3.so",
1334    "libmali.so.0",
1335    "libmali.so.1",
1336  ]
1337}
1338```
1339
1340## TOUCH PANEL
1341
1342常见的INPUT设备有键盘、鼠标、游戏杆、Touch Screen等。Touch 设备与主机通讯采用标准 I2C 总线,触屏 IC 提供中断支持,提高了触屏数据的实时性。本项目的触摸屏器件IC 为 GT911。
1343
1344### **驱动框架模型**
1345
1346#### INPUT驱动模型
1347
1348![img](../figures/khdvk_3566b_tp_1.png)
1349
1350INPUT 驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。
1351
1352(1)设备管理层:为各类输入设备驱动提供input设备的注册、注销接口,同时统一管理 input 设备列表;
1353
1354(2)平台驱动层:指各类input设备的公共抽象驱动(例如触摸屏的公共驱动),负责对板级硬件进行初始化、硬件中断处理、向manager注册input设备等;
1355
1356(3)器件驱动层:指各器件厂家的差异化驱动,通过适配平台驱动预留的差异化接口,实现器件驱动开发量最小化。
1357
1358#### HDI接口层框架图
1359
1360INPUT驱动提供给系统服务Input Service可直接调用的驱动能力接口,按照属性分类三类:input设备管理模块、input数据上报模块、input业务控制模块,HDI接口主要包括如下三大类:
1361
1362- input设备管理模块:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;
1363- input数据上报模块:负责输入事件的上报,包括注册、注销数据上报回调函数等;
1364- input业务控制模块:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。
1365
1366![image-20220704102754050](../figures/khdvk_3566b_tp_2.png)
1367
1368### **HDF驱动适配**
1369
1370#### **HCS配置**
1371
1372配置设备描述信息,在device_info.hcs中添加device_touch_chip:
1373
1374```
1375 input :: host {
1376            hostName = "input_host";
1377            priority = 100;
1378            device_input_manager :: device {   // Input管理层设备描述信息
1379                device0 :: deviceNode {
1380                    policy = 2;
1381                    priority = 100;
1382                    preload = 0;
1383                    permission = 0660;
1384                    moduleName = "HDF_INPUT_MANAGER";
1385                    serviceName = "hdf_input_host";
1386                    deviceMatchAttr = "";
1387                }
1388            }
1389            device_hdf_touch :: device {      // Input公共驱动层设备描述信息
1390                device0 :: deviceNode {
1391                    policy = 2;
1392                    priority = 120;
1393                    preload = 0;
1394                    permission = 0660;
1395                    moduleName = "HDF_TOUCH";
1396                    serviceName = "hdf_input_event1";
1397                    deviceMatchAttr = "touch_device1";
1398                }
1399            }
1400            device_touch_chip :: device {     // Input器件驱动层信息
1401                device0 :: deviceNode {
1402                    policy = 0;
1403                    priority = 130;
1404                    preload = 0;
1405                    permission = 0660;
1406                    moduleName = "HDF_TOUCH_GT911";
1407                    serviceName = "hdf_touch_gt911_service";
1408                    deviceMatchAttr = "zsj_gt911_5p5";
1409                }
1410            }
1411            device_hdf_hid :: device {
1412                device0 :: deviceNode {
1413                    policy = 2;
1414                    priority = 200;
1415                    preload = 0;
1416                    permission = 0660;
1417                    moduleName = "HDF_HID";
1418                }
1419            }
1420        }
1421```
1422
1423配置Touch器件信息,在input_config.hcs中添加器件的特性:
1424
1425```
1426 chipConfig {
1427                    template touchChip {
1428                        match_attr = "";
1429                        chipName = "gt911";
1430                        vendorName = "zsj";
1431                        chipInfo = "AAAA11222";
1432                        busType = 0;
1433                        deviceAddr = 0x5D;
1434                        irqFlag = 2;
1435                        maxSpeed = 400;
1436                        chipVersion = 0; //parse Coord TypeA
1437                        powerSequence {
1438                            /* [type, status, dir , delay]
1439                                <type> 0:none 1:vcc-1.8v 2:vci-3.3v 3:reset 4:int
1440                                <status> 0:off or low  1:on or high  2:no ops
1441                                <dir> 0:input  1:output  2:no ops
1442                                <delay> meanings delay xms, 20: delay 20ms
1443                             */
1444                            powerOnSeq = [4, 0, 1, 5,
1445                                         3, 0, 1, 10,
1446                                         3, 1, 1, 60,
1447                                         4, 2, 0, 50];
1448                            suspendSeq = [3, 0, 2, 10];
1449                            resumeSeq = [3, 1, 2, 10];
1450                            powerOffSeq = [3, 0, 2, 10,
1451                                           1, 0, 2, 20];
1452                        }
1453                    }
1454
1455                    chip0 :: touchChip {
1456                        match_attr = "zsj_gt911_5p5";
1457                        chipInfo = "ZIDN45100";
1458                        chipVersion = 0;
1459                    }
1460                }
1461```
1462
1463#### 适配文件
1464
1465Touch驱动适配涉及的文件及目录:
1466
14671、 编辑 Makefile 文件:./drivers/adapter/khdf/linux/model/input/Makefile
1468
14692、 公共配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/device_info/device_info.hcs
1470
14713、 私有配置文件:./vendor/kaihong/khdvk_3566b/hdf_config/khdf/input/input_config.hcs
1472
14734、 驱动:drivers\framework\model\input\driver\touchscreen
1474
1475HDF驱动模型高度抽象集成,TP驱动的适配主要是器件驱动层的适配,首先需要明确TP所需要的软硬件资源。
1476
1477TP模组需要主机上的如下硬件资源:
1478
14791.中断引脚
1480
14812.Reset引脚
1482
14833.使用的哪一组i2c,从设备的地址是什么
1484
14854.TP的初始化固件(通常由IC厂商提供)
1486
14875.触摸屏的分辨率
1488
1489TP模组需要依赖主机上的如下软件资源:
1490
14911.Hdf gpio子系统 用于设置gpio pin脚以及一些中断资源
1492
14932.Hdf i2c 子系统 用于进行i2c通信
1494
14953.Input模型
1496
1497器件差异化接口适配,示例代码路径:
1498
1499```
1500./drivers/framework/model/input/driver/touchscreen/Touch_gdi_gt911.c
1501
1502static struct TouchChipOps g_gt911ChipOps = {     // 器件IC接口
1503    .Init = ChipInit,                             // 初始化
1504    .Detect = ChipDetect,                         // 器件检测
1505    .Resume = ChipResume,                         // 唤醒
1506    .Suspend = ChipSuspend,                       // 休眠
1507    .DataHandle = ChipDataHandle,                 // 器件数据读取
1508    .UpdateFirmware = UpdateFirmware,             // 固件升级
1509    .SetAbility = SetAbility,                     // 配置
1510};
1511```
1512
1513器件驱动初始化及HDF注册,示例代码路径:
1514
1515```
1516./drivers/framework/model/input/driver/touchscreen/touch_jdi_gt911.c
1517
1518static int32_t HdfGoodixChipInit(struct HdfDeviceObject *device)
1519{
1520    ...
1521    /* 器件配置结构体内存申请、配置信息解析及挂载 */
1522    chipCfg = ChipConfigInstance(device);
1523    ...
1524    /* 器件实例化 */
1525    chipDev = ChipDeviceInstance();
1526    ...
1527    /* 器件信息挂载及器件私有操作挂载 */
1528    chipDev->chipCfg = chipCfg;
1529    chipDev->ops = &g_gt911ChipOps;
1530    ...
1531    /* 注册器件驱动至平台驱动 */
1532    RegisterChipDevice(chipDev);
1533    ...
1534}
1535
1536struct HdfDriverEntry g_touchGoodixChipEntry = {
1537    .moduleVersion = 1,
1538    .moduleName = "HDF_TOUCH_GT911",
1539    .Init = HdfGoodixChipInit,          // 器件驱动初始化函数
1540    .Release = HdfGoodixChipRelease,
1541};
1542HDF_INIT(g_touchGoodixChipEntry);       // 注册器件驱动至HDF框架
1543```
1544
1545#### 代码分布
1546
1547```c
1548/drivers/peripheral/input
1549/drivers/framework/model/input
1550```
1551
1552## OpenHarmony Camera HDF驱动框架概述
1553
1554OpenHarmony Camera驱动模型结构
1555
1556![图1](../figures/khdvk_3566b_camera_arch.png)
1557
1558- HDI Implementation:对上实现HDI接口,向下调用框架层的接口,完成HDI接口任务的转发。
1559- Buffer Manager:屏蔽不同内存管理的差异,为子系统提供统一的操作接口,同时提供buffer轮转的功能。
1560- Pipeline Core:解析HCS配置完成pipeline的搭建,调度pipeline中的各个node完成流的处理
1561- Device Manager:通过调用底层硬件适配层接口,实现查询控制底层设备、枚举监听底层设备的功能
1562- Platform Adaption:屏蔽硬件差异,为Device Manager提供统一的操作底层硬件的能力
1563
1564### CameraService 进程
1565
1566CameraService源码目录为:foundation/multimedia/camera_standard,camera app通过camera service与hal层进行交互
1567
1568    ├── bundle.json
1569    ├── figures
1570    ├── frameworks                            camera frameworks部分,支持js和native转换
1571    │   ├── js
1572    │   └── native
1573    ├── hisysevent.yaml
1574    ├── interfaces                            CameraService接口
1575    │   ├── inner_api
1576    │   └── kits
1577    ├── LICENSE
1578    ├── OAT.xml
1579    ├── README.md
1580    ├── README_zh.md
1581    ├── sa_profile                            CameraService进程加载配置文件
1582    │   ├── 3008.xml
1583    │   └── BUILD.gn
1584    └── services                            CameraService启动相关
1585        ├── camera_service
1586        └── etc
1587
1588CameraService启动入口在foundation/multimedia/camera_standard/services/etc/camera_service.cfg进行启动配置
1589
1590    "services" : [{
1591            "name" : "camera_service",
1592            "path" : ["/system/bin/sa_main", "/system/profile/camera_service.xml"],
1593            "uid" : "cameraserver",
1594            "gid" : ["system", "shell"],
1595            "secon" : "u:r:camera_service:s0"
1596        }
1597    ]
1598
1599### Camera驱动框架介绍
1600
1601###Camera驱动整体架构
1602
1603![图2](../figures/khdvk_3566b_camera_driver.jpg)
1604
1605#### camera驱动源码分布
1606
1607Camera 驱动框架所在的仓为:drivers_peripheral,源码目录为:“drivers/peripheral/camera”。
1608
1609    ├── bundle.json
1610    ├── figures
1611    │   ├── Camera模块驱动模型.png
1612    │   └── logic-view-of-modules-related-to-this-repository_zh.png
1613    ├── hal
1614    │   ├── adapter                    #平台适配层,适配平台
1615    │   ├── buffer_manager
1616    │   ├── BUILD.gn                    #Camera驱动框架构建入口
1617    │   ├── camera.gni                #定义组件所使用的全局变量
1618    │   ├── device_manager
1619    │   ├── hdi_impl
1620    │   ├── include
1621    │   ├── init                        #demo sample
1622    │   ├── pipeline_core
1623    │   ├── test                        #测试代码
1624    │   └── utils
1625    ├── hal_c                        #为海思平台提供专用C接口
1626    │   ├── BUILD.gn
1627    │   ├── camera.gni
1628    │   ├── hdi_cif
1629    │   └── include
1630    ├── interfaces                    #HDI接口
1631    │   ├── hdi_ipc
1632    │   ├── hdi_passthrough
1633    │   ├── include
1634    │   └── metadata
1635    └── README_zh.md
1636
1637#### Camera Host HDF驱动
1638
1639###配置文件
1640
1641Camera Host HDF配置相关在“vendor/kaihong/khdvk_3566b/hdf_config/uhdf/device_info.hcs1642
1643        hdi_server :: host {
1644            hostName = "camera_host";
1645            priority = 50;
1646            caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];
1647            camera_device :: device {
1648                 device0 :: deviceNode {
1649                     policy = 2;
1650                     priority = 100;
1651                     moduleName = "libcamera_hdi_impl.z.so";
1652                     serviceName = "camera_service";
1653                 }
1654             }
1655            ...
1656        }
1657
1658其中主要参数说明如下:
1659
1660- hostName = "camera_host":camera host节点,该节点为一个独立进程,如果需要独立进程,新增属于自己的host节点
1661- policy = 2:服务发布策略,Camera使用HDI服务,需设置为2
1662- moduleName:camera host驱动实现库名
1663- serviceName:服务名称,请保持全局唯一性,后面HDF Manager会根据这个名称拉起camera hdf
1664
1665###camera host服务启动
1666camera host 服务由hdf_devhost启动,配置文件存放于vendor/etc/init/hdf_devhost.cfg
1667
1668        {
1669            "name" : "camera_host",
1670            "path" : ["/vendor/bin/hdf_devhost", "8", "camera_host"],
1671            "uid" : "camera_host",
1672            "gid" : ["camera_host"],
1673            "caps" : ["DAC_OVERRIDE", "DAC_READ_SEARCH"],
1674            "secon" : "u:r:camera_host:s0"
1675        }
1676
1677###Camera host驱动实现
1678代码路径:drivers/peripheral/camera/interfaces/hdi_ipc/server/src/camera_host_driver.cpp
1679
1680驱动入口结构体,后面将该结构体注册进HDF框架中
1681
1682    struct HdfDriverEntry g_cameraHostDriverEntry = {
1683        .moduleVersion = 1,
1684        .moduleName = "camera_service",
1685        .Bind = HdfCameraHostDriverBind,
1686        .Init = HdfCameraHostDriverInit,
1687        .Release = HdfCameraHostDriverRelease,
1688    };
1689
1690消息发布服务
1691
1692    static int32_t CameraServiceDispatch(struct HdfDeviceIoClient *client, int cmdId,
1693        struct HdfSBuf *data, struct HdfSBuf *reply)
1694    {
1695        HdfCameraService *hdfCameraService = CONTAINER_OF(client->device->service, HdfCameraService, ioservice);
1696        return CameraHostServiceOnRemoteRequest(hdfCameraService->instance, cmdId, data, reply);
1697    }
1698
1699参数说明:
1700
1701    client:HdfDeviceIoClient设备句柄
1702    cmdId:请求消息命令字
1703    data:其他服务或者IO请求数据
1704    reply:存储返回消息内容数据
1705
1706绑定服务:初始化设备服务对象和资源对象
1707
1708    int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject)
1709    {
1710        ...
1711        hdfCameraService->ioservice.Dispatch = CameraServiceDispatch;
1712        hdfCameraService->ioservice.Open = nullptr;
1713        hdfCameraService->ioservice.Release = nullptr;
1714        hdfCameraService->instance = CameraHostStubInstance();
1715
1716        deviceObject->service = &hdfCameraService->ioservice;
1717        return HDF_SUCCESS;
1718    }
1719
1720相关说明:
1721
1722    hdfCameraService->ioservice.Dispatch:注册消息分发服务接口
1723    hdfCameraService->instance:创建camerahost实例
1724
1725驱动初始化函数: 探测并初始化驱动程序
1726
1727    int HdfCameraHostDriverInit(struct HdfDeviceObject *deviceObject)
1728    {
1729        return HDF_SUCCESS;
1730    }
1731
1732驱动资源释放函数 : 如已经绑定的设备服务对象
1733
1734    void HdfCameraHostDriverRelease(HdfDeviceObject *deviceObject)
1735    {
1736        if (deviceObject == nullptr || deviceObject->service == nullptr) {
1737            HDF_LOGE("%{public}s deviceObject or deviceObject->service  is NULL!", __FUNCTION__);
1738            return;
1739        }
1740        HdfCameraService *hdfCameraService = CONTAINER_OF(deviceObject->service, HdfCameraService, ioservice);
1741        if (hdfCameraService == nullptr) {
1742            HDF_LOGE("%{public}s hdfCameraService is NULL!", __FUNCTION__);
1743            return;
1744        }
1745        OsalMemFree(hdfCameraService);
1746    }
1747
1748设备创建不成功,关闭服务,释放相关资源
1749
1750#### DeviceManager
1751
1752创建SensorManager、FlashManager、ISPManager管理相应的设备。
1753![图3](../figures/khdvk_3566b_devicemanger.png)
1754
1755####SensorManager
1756sensor Manager结构如下
1757
1758    class SensorManager : public IManager {
1759    public:
1760        SensorManager();
1761        explicit SensorManager(ManagerId managerId);
1762        virtual ~SensorManager();
1763        RetCode CreateController(ControllerId controllerId, std::string hardwareName);
1764        RetCode DestroyController(ControllerId controllerId, std::string hardwareName);
1765        std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName);
1766
1767        void Configure(std::shared_ptr<CameraMetadata> meta);
1768        RetCode Start(std::string hardwareName, int buffCont, DeviceFormat& format);
1769        RetCode Stop(std::string hardwareName);
1770        RetCode PowerUp(std::string hardwareName);
1771        RetCode PowerDown(std::string hardwareName);
1772        std::shared_ptr<ISensor> GetSensor(std::string sensorName);
1773
1774        RetCode SendFrameBuffer(std::shared_ptr<FrameSpec> buffer, std::string hardwareName);
1775        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName);
1776        void SetNodeCallBack(const NodeBufferCb cb, std::string hardwareName);
1777        void SetMetaDataCallBack(const MetaDataCb cb, std::string hardwareName);
1778
1779    private:
1780        bool CheckCameraIdList(std::string hardwareName);
1781        std::vector<std::shared_ptr<SensorController>> sensorList_;
1782    };
1783    }
1784
1785PowerUp为上电接口,OpenCamera时调用此接口进行设备上电操作
1786
1787PowerDown为下电接口,CloseCamera时调用此接口进行设备下电操作
1788
1789Configures为Metadata下发接口,如需设置metadata参数到硬件设备,可实现此接口进行解析及下发
1790
1791Start为硬件模块使能接口,pipeline中的各个node进行使能的时候,会去调用,可根据需要定义实现,比如sensor的起流操作就可放在此处进行实现,Stop和Start为相反操作,可实现停流操作
1792
1793SendFrameBuffer为每一帧buffer下发接口,所有和驱动进行buffer交互的操作,都是通过此接口进行的
1794
1795SetNodeCallBack为pipeline,通过此接口将buffer回调函数设置到devicemanager
1796
1797SetMetaDataCallBack为metadata回调接口,通过此接口将从底层获取的metadata数据上报给上层
1798
1799BufferCallback上传每一帧已填充数据buffer的接口,通过此接口将buffer上报给pipeline
1800
1801SetAbilityMetaDataTag设置需要从底层获取哪些类型的metadata数据,因为框架支持单独获取某一类型或多类型的硬件设备信息,所以可以通过此接口,获取想要的metadata数据
1802
1803Camera Sensor Controller结构如下:
1804
1805    class SensorController : public IController {
1806    public:
1807        SensorController();
1808        explicit SensorController(std::string hardwareName);
1809        virtual ~SensorController();
1810        RetCode Init();
1811        RetCode PowerUp();
1812        RetCode PowerDown();
1813        RetCode Configure(std::shared_ptr<CameraMetadata> meta);
1814        RetCode Start(int buffCont, DeviceFormat& format);
1815        RetCode Stop();
1816        ...
1817        void SetMetaDataCallBack(MetaDataCb cb) override;
1818        void BufferCallback(std::shared_ptr<FrameSpec> buffer);
1819
1820        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag);
1821        RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta);
1822        RetCode Flush(int32_t streamId);
1823        ...
1824
1825    };
1826
1827PowerUp下发命令给v4l2 dev去操作实际设备进行上电操作
1828PowerDown下发命令给v4l2 dev去操作实际设备进行下电操作
1829同理其他操作参考SensorManager.
1830####FlashManager
1831Flash Manger结构如下:
1832
1833    class FlashManager : public IManager {
1834    public:
1835        FlashManager();
1836        explicit FlashManager(ManagerId managerId);
1837        virtual ~FlashManager();
1838        RetCode CreateController(ControllerId controllerId, std::string hardwareName);
1839        std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName);
1840        RetCode PowerUp(std::string hardwareName);
1841        RetCode PowerDown(std::string hardwareName);
1842        void Configure(std::shared_ptr<CameraMetadata> meta);
1843        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName)
1844        {
1845            (void)abilityMetaDataTag;
1846            (void)hardwareName;
1847            return;
1848        }
1849        RetCode SetFlashlight(FlashMode flashMode, bool enable, std::string hardwareName);
1850    private:
1851        bool CheckCameraIdList(std::string hardwareName);
1852        std::vector<std::shared_ptr<FlashController>> flashList_;
1853    }
1854
1855Flash controller结构如下:
1856
1857    class FlashController : public IController {
1858    public:
1859        FlashController();
1860        explicit FlashController(std::string hardwareName);
1861        virtual ~FlashController();
1862        RetCode Init();
1863        RetCode PowerUp();
1864        RetCode PowerDown();
1865        RetCode Configure(std::shared_ptr<CameraMetadata> meta)
1866        {
1867            (void)meta;
1868            return RC_OK;
1869        }
1870        RetCode SetFlashlight(FlashMode flashMode, bool enable);
1871        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag);
1872        RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta);
1873    private:
1874        std::mutex startVolock_;
1875        bool startVoState_ = false;
1876    }
1877
1878####ISPManager
1879ISP Manager结构如下
1880
1881    class IspManager : public IManager {
1882    public:
1883        IspManager();
1884        explicit IspManager(ManagerId managerId);
1885        virtual ~IspManager();
1886        RetCode CreateController(ControllerId controllerId, std::string hardwareName);
1887        std::shared_ptr<IController> GetController(ControllerId controllerId, std::string hardwareName);
1888        void Configure(std::shared_ptr<CameraMetadata> meta);
1889        RetCode Start(std::string hardwareName);
1890        RetCode Stop(std::string hardwareName);
1891        RetCode PowerUp(std::string hardwareName);
1892        RetCode PowerDown(std::string hardwareName);
1893        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag, std::string hardwareName)
1894        {
1895            (void)abilityMetaDataTag;
1896            (void)hardwareName;
1897            return;
1898        }
1899    private:
1900        bool CheckCameraIdList(std::string hardwareName);
1901        std::vector<std::shared_ptr<IspController>> ispList_;
1902    };
1903
1904ISP controller结构如下
1905
1906    class IspController : public IController {
1907    public:
1908        IspController();
1909        explicit IspController(std::string hardwareName);
1910        virtual ~IspController();
1911        RetCode Init();
1912        RetCode Configure(std::shared_ptr<CameraMetadata> meta);
1913        RetCode PowerUp();
1914        RetCode PowerDown();
1915        RetCode Stop();
1916        RetCode Start();
1917        void SetAbilityMetaDataTag(std::vector<int32_t> abilityMetaDataTag)
1918        {
1919            (void)abilityMetaDataTag;
1920            return;
1921        }
1922        RetCode GetAbilityMetaData(std::shared_ptr<CameraMetadata> meta)
1923        {
1924            (void)meta;
1925            return RC_OK;
1926        }
1927    private:
1928        std::mutex startIsplock_;
1929        bool startIspState_ = false;
1930    }
1931
1932#### PlatForm Adapter
1933
1934这部分通过V4l2框架对video设备进行管理,包括对相应设备的打开、启动/关闭数据流、设置/获取图像格式等等
1935![图4](../figures/khdvk_3566b_adapter_platform.png)
1936
1937####源代码
1938V4l2 Adapter 源码位于driver/peripheral/camera/hal/adapter/platform/v4l2/src/driver_adapter
1939部分关键函数如下:
1940
1941    class HosV4L2Dev {
1942    public:
1943        ...
1944        RetCode start(const std::string& cameraID);
1945
1946        RetCode stop(const std::string& cameraID);
1947        RetCode CreatBuffer(const std::string& cameraID, const std::shared_ptr<FrameSpec>& frameSpec);
1948
1949        RetCode StartStream(const std::string& cameraID);
1950
1951        RetCode QueueBuffer(const std::string& cameraID, const std::shared_ptr<FrameSpec>& frameSpec);
1952
1953        RetCode ReleaseBuffers(const std::string& cameraID);
1954
1955        RetCode StopStream(const std::string& cameraID);
1956
1957        RetCode SetCallback(BufCallback cb);
1958
1959        static RetCode Init(std::vector<std::string>& cameraIDs);
1960
1961        static std::map<std::string, std::string> deviceMatch;
1962
1963    private:
1964        std::shared_ptr<HosV4L2Buffers> myBuffers_ = nullptr;
1965        std::shared_ptr<HosV4L2Streams> myStreams_ = nullptr;
1966        std::shared_ptr<HosFileFormat> myFileFormat_ = nullptr;
1967        std::shared_ptr<HosV4L2Control> myControl_ = nullptr;
1968        ...
1969        enum v4l2_memory memoryType_ = V4L2_MEMORY_USERPTR;
1970        enum v4l2_buf_type bufferType_ = V4L2_BUF_TYPE_PRIVATE;
1971    };
1972
1973#### PipeLineCore
1974
1975这个模块解析HCS配置完成pipeline的搭建,调度pipeline中的各个node完成流的处理
1976![图5](../figures/khdvk_3566b_pipelinecore.png)
1977
1978###IPP算法加载
1979IPP是pipeline 中的一个算法插件模块,由ippnode加载,对流数据进行算法处理,ippnode支持同时多路数据输入,只支持一路数据输出。
1980
1981vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/ipp_algo_config.hcs为算法插件配置文件,后面有新的算法库需要在这里添加相关内容,添加模板如下:
1982
1983    root {
1984        module="sample";
1985        ipp_algo_config {
1986            algo1 {
1987                name = "example";
1988                description = "example algorithm";
1989                path = "libcamera_ipp_algo_example.z.so";
1990                mode = "IPP_ALGO_MODE_NORMAL";
1991            }
1992        }
1993    }
1994
1995name:算法插件名称 description:描述算法插件的功能 path:算法插件所在路径 mode:算法插件所运行的模式
1996
1997算法插件可运行的模式由 drivers/peripheral/camera/hal/pipeline_core/ipp/include/ipp_algo.h中的IppAlgoMode提供,可以根据需要进行扩展。
1998
1999      enum IppAlgoMode {
2000          IPP_ALGO_MODE_BEGIN,
2001          IPP_ALGO_MODE_NORMAL = IPP_ALGO_MODE_BEGIN,
2002          IPP_ALGO_MODE_BEAUTY,
2003          IPP_ALGO_MODE_HDR,
2004          IPP_ALGO_MODE_END
2005      };
2006
2007算法插件由device/board/kaihong/khdvk_3566b/camera/BUILD.gn文件进行编译,算法插件需实现如下接口(接口由ipp_algo.h指定)供ippnode调用:
2008
2009    typedef struct IppAlgoFunc {
2010        int (*Init)(IppAlgoMeta* meta);
2011        int (*Start)();
2012        int (*Flush)();
2013        int (*Process)(IppAlgoBuffer* inBuffer[], int inBufferCount, IppAlgoBuffer* outBuffer, IppAlgoMeta* meta);
2014        int (*Stop)();
2015    } IppAlgoFunc;
2016
2017Init : 算法插件初始化接口,在起流前被ippnode调用,其中IppAlgoMeta定义在ipp_algo.h 中,为ippnode和算法插件提供非图像数据的传递通道,如当前运行的场景,算法处理后输出的人脸坐标等等,可根据实际需求进行扩展
2018
2019Start:开始接口,起流时被ippnode调用
2020
2021Flush:刷新数据的接口,停流之前被ippnode调用。此接口被调用时,算法插件需尽可能快地停止处理
2022
2023Process: 数据处理接口,每帧数据都通过此接口输入至算法插件进行处理。inBuffer是一组输入buffer,inBufferCount是输入buffer的个数,outBuffer是输出buffer,meta是算法处理时产生的非图像数据,IppAlgoBuffer在ipp_algo.h中定义
2024
2025Stop:停止处理接口,停流时被ippnode调用
2026
2027下边代码中的id指的是和ippnode对应的port口id,比如inBuffer[0]的id为0,则对应的是ippnode 的第0个输入port口。需要注意的是outBuffer可以为空,此时其中一个输入buffer 被ippnode作为输出buffer传递到下个node,inBuffer至少有一个buffer不为空。输入输出buffer 由pipeline配置决定。 比如在普通预览场景无算法处理且只有一路拍照数据传递到ippnode的情况下,输入buffer只有一个,输出buffer为空,即对于算法插件输入buffer 进行了透传; 比如算法插件进行两路预览图像数据进行合并的场景,第一路buffer需要预览送显示。把第二路图像拷贝到第一路的buffer即可,此时输入buffer有两个,输出buffer为空; 比如在算法插件中进行预览数据格式转换的场景,yuv转换为RGBA,那么只有一个yuv格式的输入buffer的情况下无法完成RGBA格式buffer的输出,此时需要一个新的buffer,那么ippnode的输出port口buffer作为outBuffer传递到算法插件。也即输入buffer只有一个,输出buffer也有一个。
2028
2029    typedef struct IppAlgoBuffer {
2030          void* addr;
2031          unsigned int width;
2032          unsigned int height;
2033          unsigned int stride;
2034          unsigned int size;
2035          int id;
2036      } IppAlgoBuffer;
2037
2038##camera HDF驱动适配
2039###rk3566rp camera HDF驱动编译选项添加
2040camera HDF驱动的配置位于drivers/peripheral/camera/hal/camera.gni中,内容如下:
2041
2042    if (defined(ohos_lite)) {
2043      import("//build/lite/config/component/lite_component.gni")
2044      import("//device/board/hisilicon/hispark_taurus/device.gni")
2045    } else {
2046      import("//build/ohos.gni")
2047      import("//vendor/$product_company/$product_name/product.gni")
2048    }
2049
2050    camera_path = "//drivers/peripheral/camera/hal"
2051    current_path = "."
2052    enable_camera_device_utest = false
2053
2054    use_hitrace = false
2055    if (use_hitrace) {
2056      defines += [ "HITRACE_LOG_ENABLED" ]
2057    }
2058
2059    if (defined(ohos_lite)) {
2060      defines += [ "CAMERA_BUILT_ON_OHOS_LITE" ]
2061    }
2062
2063根据编译配置可以找到对应的vendor/kaihong/khdvk_3566b/product.gni,从中获取到实际的文件是device/board/kaihong/khdvk_3566b/device.gni,后面修改入口基于这里
2064
2065    soc_company = "rockchip"
2066    soc_name = "rk3566"
2067
2068    import("//device/soc/${soc_company}/${soc_name}/soc.gni")
2069
2070    import("//build/ohos.gni")
2071    if (!defined(defines)) {
2072      defines = []
2073    }
2074    product_config_path = "//vendor/${product_company}/${device_name}"
2075    board_camera_path = "//device/board/${product_company}/khdvk_3566b/camera"
2076
2077    camera_product_name_path = "//vendor/${product_company}/${device_name}"
2078    camera_device_name_path = "//device/board/${product_company}/khdvk_3566b"
2079    is_support_v4l2 = true
2080    if (is_support_v4l2) {
2081      is_support_mpi = false
2082      defines += [ "SUPPORT_V4L2" ]
2083      chipset_build_deps = "$camera_device_name_path/camera:chipset_build"
2084      camera_device_manager_deps = "$camera_device_name_path/camera/device_manager:camera_device_manager"
2085      camera_pipeline_core_deps = "$camera_device_name_path/camera/pipeline_core:camera_pipeline_core"
2086    }
2087
2088最终这里的配置文件里的参数将被drivers/peripheral/camera/hal/BUILD.gn使用。
2089###HCS配置文件介绍
2090camera的配置文件位于vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/
2091
2092目录结构如下:
2093
2094    ├── hdi_impl
2095    │   ├── camera_host_config.hcs
2096    └── pipeline_core
2097        ├── config.hcs
2098        ├── ipp_algo_config.hcs
2099        └── params.hcs
2100
2101Camera所有配置文件使用系统支持的HCS类型的配置文件,HCS类型的配置文件,在编译时,会转成HCB文件,最终烧录到开发板里的配置文件即为HCB格式,代码中通过HCS解析接口解析HCB文件,获取配置文件中的信息。
2102
2103    ohos_prebuilt_etc("camera_host_config.hcb") {
2104      deps = [ ":build_camera_host_config" ]
2105      hcs_outputs = get_target_outputs(":build_camera_host_config")
2106      source = hcs_outputs[0]
2107      relative_install_dir = "hdfconfig"
2108      install_images = [ chipset_base_dir ]
2109      subsystem_name = "hdf"
2110      part_name = "camera_device_driver"
2111    }
2112
2113camera_host_config.hcs:配置当前camera支持的能力集,物理/逻辑Camera配置、能力配置,此处的物理/逻辑Camera配置,需要在hal内部使用,逻辑Camera及能力配置需要上报给上层,这里需要根据设备实际支持的属性进行相应的修改。
2114这里的键值对参考文件drivers/peripheral/camera/hal/hdi_impl/include/camera_host/metadata_enum_map.h
2115
2116       ability_01 :: ability {
2117            logicCameraId = "lcam001";
2118            physicsCameraIds = [
2119                "CAMERA_FIRST",
2120                "CAMERA_SECOND"
2121            ];
2122            metadata {
2123                aeAvailableAntiBandingModes = [
2124                    "OHOS_CAMERA_AE_ANTIBANDING_MODE_OFF"
2125                ];
2126                aeAvailableModes = ["OHOS_CAMERA_AE_MODE_OFF"];
2127                availableFpsRange = [30, 30];
2128                cameraPosition = "OHOS_CAMERA_POSITION_FRONT";
2129                cameraType = "OHOS_CAMERA_TYPE_WIDE_ANGLE";
2130                cameraConnectionType ="OHOS_CAMERA_CONNECTION_TYPE_BUILTIN";
2131                faceDetectMaxNum = "10";
2132                aeCompensationRange = [0, 0];
2133                aeCompensationSteps = [0, 0];
2134                availableAwbModes = [
2135                    "OHOS_CAMERA_AWB_MODE_OFF"
2136                ];
2137                ...
2138        }
2139
2140vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/config.hcs为pipeline的连接方式,按场景划分每一路流由哪些Node组成,其连接方式是怎样的。
2141
2142    normal_preview :: pipeline_spec {
2143        name = "normal_preview";
2144        v4l2_source :: node_spec {
2145            name = "v4l2_source#0";
2146            status = "new";
2147            out_port_0 :: port_spec {
2148                name = "out0";
2149                peer_port_name = "in0";
2150                peer_port_node_name = "sink#0";
2151                direction = 1;
2152                width = 0;
2153                height = 0;
2154                format = 0;
2155            }
2156        }
2157        sink :: node_spec {
2158            name = "sink#0";
2159            status = "new";
2160            stream_type = "preview";
2161            in_port_0 :: port_spec {
2162                name = "in0";
2163                peer_port_name = "out0";
2164                peer_port_node_name = "v4l2_source#0";
2165                direction = 0;
2166            }
2167        }
2168    }
2169
2170上面为preview场景的示例,normal_preview为该场景的名称,source和sink为Node,source为数据数据源端,sink为末端,source为第一个node,node的名称是source#0,status、in/out_port分别为Node状态及输入/输出口的配置。
2171
2172以in_port_0为例,name = “in0”代表它的输入为“port0”,它的对端为source node的port口out0口,direction为它的源Node和对端Node是否为直连方式。如新添加芯片产品,必须按实际连接方式配置此文件。
2173
2174新增功能node时需继承NodeBase类,且在cpp文件中注册该node。具体可参考//drivers/peripheral/camera/hal/pipeline_core/nodes/src下已经实现的node。
2175
2176vendor/kaihong/khdvk_3566b/hdf_config/uhdf/camera/pipeline_core/param.hcs为场景、流类型名及其id定义,pipeline内部是以流id区分流类型的,所以此处需要添加定义。
2177
2178     root {
2179        module = "";
2180        template stream_info {
2181            id = 0;
2182            name = "";
2183        }
2184        template scene_info {
2185            id = 0;
2186            name = "";
2187        }
2188        priview :: stream_info {
2189            id = 0;
2190            name = "preview";
2191        }
2192        video :: stream_info {
2193            id = 1;
2194            name = "video";
2195        }
2196        snapshot :: stream_info {
2197            id = 2;
2198            name = "snapshot";
2199        }
2200        normal :: scene_info {
2201            id = 0;
2202            name = "normal";
2203        }
2204        dual :: scene_info {
2205            id = 1;
2206            name = "dual";
2207        }
2208    }
2209
2210##适配过程中遇到的问题
2211###camera启动时无法出图排查方向
2212
2213首先排查camera sensor有没有正常的上下电,初始化序列是否正确。
2214如果上述都正常,需要到HDF层面,看看设备配置是否正确,具体操作如下:
2215在ohos系统的上电启动过程中,camera host 服务进程调用InitSensors() -->SensorController::Init()-->HosV4L2Dev::Init()->HosFileFormat::V4L2MatchDevice()既ohos在初始化过程中就会去匹配camera实例与linux 驱动系统中的camera硬件,如果匹配则记录存下cameraId与/dev/videox的关系;所以在camera drive中一般需要修改的地方就是camera hardware的name与linux驱动的/dev/videox关系;
2216代码如下:
2217cameraIDs向量组内是hdf支持的所以camera 的名称(string);
2218./drivers/peripheral/camera/hal/adapter/platform/v4l2/src/device_manager/include/v4l2_device_manager.h定义的cameraId
2219
2220    std::vector<HardwareConfiguration> hardware = {
2221        {CAMERA_FIRST, DM_M_SENSOR, DM_C_SENSOR, (std::string) "bm2835 mmal"},
2222        {CAMERA_FIRST, DM_M_ISP, DM_C_ISP, (std::string) "isp"},
2223        {CAMERA_FIRST, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"},
2224        {CAMERA_SECOND, DM_M_SENSOR, DM_C_SENSOR, (std::string) "Imx600"},
2225        {CAMERA_SECOND, DM_M_ISP, DM_C_ISP, (std::string) "isp"},
2226        {CAMERA_SECOND, DM_M_FLASH, DM_C_FLASH, (std::string) "flash"}
2227    };
2228
2229每个名称应与/dev/videox其中任意一个的capabilities中的driver name是一样的,只有一样的名称才能将hdf的camera name与/dev/videox绑定;
2230
2231    void HosFileFormat::V4L2MatchDevice(std::vector<std::string>& cameraIDs)
2232    {
2233        struct stat st = {};
2234        char devName[16] = {0};
2235        std::string name = DEVICENAMEX;
2236        int fd = 0;
2237        int rc = 0;
2238
2239        for (auto &it : cameraIDs) {
2240            for (int i = 0; i < MAXVIDEODEVICE; ++i) {
2241                if ((sprintf_s(devName, sizeof(devName), "%s%d", name.c_str(), i)) < 0) {
2242                    CAMERA_LOGE("%s: sprintf devName failed", __func__);
2243                }
2244                ...
2245                rc = V4L2GetCapability(fd, devName, it);
2246                if (rc == RC_ERROR) {
2247                    close(fd);
2248                    continue;
2249                }
2250                ...
2251            }
2252        }
2253    }
2254
2255注意“(cameraId != std::string((char*)cap.driver)”比较cap中的名称是否相同。
2256
2257    RetCode HosFileFormat::V4L2GetCapability(int fd, const std::string& devName, std::string& cameraId)
2258    {
2259        struct v4l2_capability cap = {};
2260
2261        int rc = ioctl(fd, VIDIOC_QUERYCAP, &cap);
2262        if (rc < 0) {
2263            return RC_ERROR;
2264        }
2265
2266        if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
2267            return RC_ERROR;
2268        }
2269
2270        if (!((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) || (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))) {
2271            return RC_ERROR;
2272        }
2273
2274        if (cameraId != std::string((char*)cap.driver)) {
2275            return RC_ERROR;
2276        }
2277
2278        std::lock_guard<std::mutex> l(HosV4L2Dev::deviceFdLock_);
2279        HosV4L2Dev::deviceMatch.insert(std::make_pair(std::string((char*)cap.driver), devName));
2280        ...
2281        return RC_OK;
2282    }
2283
2284## **BT**
2285
2286### **HCI接口**
2287
2288蓝牙整体硬件架构上分为主机(计算机或MCU)和主机控制器(BT蓝牙模组)两部分;通信遵循主机控制器接口(HCI),通常使用串口进行通信,如下所示:
2289
2290![img](../figures/khdvk_3566b_bt_1.png)
2291
2292HCI定义了如何交换命令,事件,异步和同步数据包。异步数据包(ACL)用于数据传输,而同步数据包(SCO)用于带有耳机和免提配置文件的语音。
2293
2294#### **硬件连接**
2295
2296从RK3566芯片描述中看,该芯片并不没有集成WIFI/蓝牙功能,都需要外接蓝牙芯片才能支持蓝牙功能,这也符合上述逻辑架构。串口使用普通带流控串口即可,一般在原理图中可以看到对应的串口引脚:
2297
2298![img](../figures/khdvk_3566b_bt_2.png)
2299
2300可以看到使用的是UART1 M0,在设备树里就要使能对应的串口和pinctrl,同时还可以看到有几个管脚分别做电源和休眠控制。
2301
2302```
2303     wireless_bluetooth: wireless-bluetooth {
2304        compatible = "bluetooth-platdata";
2305        clocks = <&rk817 1>;
2306        clock-names = "ext_clock";
2307        //wifi-bt-power-toggle;
2308        uart_rts_gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
2309        pinctrl-names = "default", "rts_gpio";
2310        pinctrl-0 = <&uart1m0_rtsn &bt_host_wake_gpio &bt_poweren &bt_host_wake_irq>;
2311        pinctrl-1 = <&uart1_gpios>;
2312        BT,reset_gpio   = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>;
2313        BT,wake_gpio   = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
2314        BT,wake_host_irq = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>;
2315        status = "okay";
2316    };
2317
2318    wireless-bluetooth {
2319        uart1_gpios: uart1-gpios {
2320          rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
2321        };
2322
2323        bt_host_wake_irq: bt-host-wake-irq {
2324          rockchip,pins = <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_down>;
2325        };
2326        bt_host_wake_gpio: bt-host-wake-gpio {
2327          rockchip,pins = <0 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>;
2328        };
2329        bt_poweren: bt-poweren {
2330          rockchip,pins = <0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>;
2331        };
2332      };
2333
2334
2335    &uart1 {
2336      status = "okay";
2337      pinctrl-names = "default";
2338      pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>;
2339    };
2340```
2341
2342### **蓝牙VENDORLIB适配**
2343
2344#### **vendorlib是什么**
2345
2346vendorlib部署在主机侧,可以认为是主机侧对蓝牙芯片驱动层,屏蔽不同蓝牙芯片的技术细节。从代码层面解读,其主要功能有两个:
2347
23481、为协议栈提供蓝牙芯片之间的通道(串口的文件描述符)
2349
23502、提供特定芯片的具体控制方法
2351
2352#### **代码层面解读vendorlib**
2353
2354bt_vendor_lib.h 路径:
2355
2356```
2357foundation/communication/bluetooth/services/bluetooth_standard/hardware/include
2358```
2359
2360该文件定义了协议栈和vendor_lib交互接口,分为两组:
2361
23621、 vendorlib实现,协议栈调用
2363
2364```
2365    typedef struct {
2366      /**
2367       \* Set to sizeof(bt_vndor_interface_t)
2368       */
2369      size_t size;
2370      /**
2371       \* Caller will open the interface and pass in the callback routines
2372       \* to the implemenation of this interface.
2373       */
2374      int (*init)(const bt_vendor_callbacks_t* p_cb, unsigned char* local_bdaddr);
2375
2376      /**
2377       \* Vendor specific operations
2378       */
2379      int (*op)(bt_opcode_t opcode, void* param);
2380
2381      /**
2382       \* Closes the interface
2383       */
2384      void (*close)(void);
2385    } bt_vendor_interface_t;
2386```
2387
2388协议栈启动时的基本流程如下:
2389
23901.1、协议栈动态打开libbt_vendor.z.so,并调用init函数,初始化vendorlib
2391
23921.2、协议栈调用op函数,分别调用BT_OP_POWER_ON、BT_OP_HCI_CHANNEL_OPEN、BT_OP_INIT三个opcode;原则上BT_OP_INIT成功后说明芯片初始化完成。
2393
23942、协议栈实现,vendorlib调用(回调函数)
2395
2396```
2397    typedef struct {
2398      /**
2399      \* set to sizeof(bt_vendor_callbacks_t)
2400      */
2401      size_t size;
2402
2403      /* notifies caller result of init request */
2404      init_callback init_cb;
2405
2406      /* buffer allocation request */
2407      malloc_callback alloc;
2408
2409      /* buffer free request */
2410      free_callback dealloc;
2411
2412      /* hci command packet transmit request */
2413      cmd_xmit_callback xmit_cb;
2414    } bt_vendor_callbacks_t;
2415```
2416
2417init_cb在BT_OP_INIT完成后调用
2418
2419alloc/dealloc用于发送HCI消息时申请/释放消息控件
2420
2421xmit_cb发送HCI Commands
2422
2423##### vendor_lib实现的几个重要函数
2424
24251、 init函数
2426
2427```
2428   static int init(const bt_vendor_callbacks_t *p_cb, unsigned char *local_bdaddr)
2429   {
2430      /* * ... */
2431     userial_vendor_init();
2432     upio_init();
2433
2434   vnd_load_conf(VENDOR_LIB_CONF_FILE);
2435
2436     /* store reference to user callbacks */
2437     bt_vendor_cbacks = (bt_vendor_callbacks_t *)p_cb;
2438       /* This is handed over from the stack */
2439     return memcpy_s(vnd_local_bd_addr, BD_ADDR_LEN, local_bdaddr, BD_ADDR_LEN);
2440   }
2441```
2442
2443vendorlib被调用的第一个函数,vendorlib保存好协议栈的callback和mac地址即可。
2444
24452、 BT_OP_POWER_ON对应处理
2446
2447观名知意,这个操作理论上需要拉高电源管脚电平;该函数中使用rfill设备来处理,并没有直接调用驱动拉高电平
2448
2449```
2450   int upio_set_bluetooth_power(int on)
2451   {
2452     int sz;
2453     int fd = -1;
2454     int ret = -1;
2455     char buffer = '0';
2456
2457     switch (on) {
2458       case UPIO_BT_POWER_OFF:
2459         buffer = '0';
2460         break;
2461
2462       case UPIO_BT_POWER_ON:
2463         buffer = '1';
2464         break;
2465       default:
2466         return 0;
2467     }
2468
2469     /* check if we have rfkill interface */
2470     if (is_rfkill_disabled()) {
2471       return 0;
2472     }
2473
2474     if (rfkill_id == -1) {
2475       if (init_rfkill()) {
2476         return ret;
2477       }
2478     }
2479
2480     fd = open(rfkill_state_path, O_WRONLY);
2481     if (fd < 0) {
2482       return ret;
2483     }
2484
2485     sz = write(fd, &buffer, 1);
2486     /* ... */
2487     return ret;
2488   }
2489```
2490
24913、BT_OP_HCI_CHANNEL_OPEN对应处理
2492
2493```
2494   case BT_OP_HCI_CHANNEL_OPEN: { // BT_VND_OP_USERIAL_OPEN
2495         int(*fd_array)[] = (int(*)[])param;
2496         int fd, idx;
2497         fd = userial_vendor_open((tUSERIAL_CFG *)&userial_init_cfg);
2498         if (fd != -1) {
2499           for (idx = 0; idx < HCI_MAX_CHANNEL; idx++)
2500             (*fd_array)[idx] = fd;
2501           retval = 1;
2502       }
2503       /* retval contains numbers of open fd of HCI channels */
2504       break;
2505```
2506
2507userial_vendor_open函数打开串口设备(UART)得到文件描述符(fd),通过op的参数param返回该fd
2508
2509该串口设备在系统中的名字在vendor下的bluetooth相关目录中的bt_vendor_brcm.h文件定义了,本次开发板上设备为/dev/ttyS1
2510
25114、BT_OP_INIT对应处理
2512
2513该操作码要求对蓝牙芯片进行初始化,具体要进行的处理和蓝牙芯片强相关。以本次调测的AP6xxx芯片为例,初始化过程中主要是下发蓝牙固件。
2514
2515初始化结束后,必须调用init_cb回调函数(参见bt_vendor_callbacks_t)通知协议栈初始化结果,否则会阻塞协议栈线程导致蓝牙相关功能无法正常使用。协议栈的具体处理如下:
2516
2517协议栈调用BT_OP_INIT后会等待信号量,该信号量由init_cb函数置位
2518
2519```
2520   static int HciInitHal()
2521   {
2522     int result = BT_NO_ERROR;
2523
2524     g_waitHdiInit = SemaphoreCreate(0);
2525     int ret = g_hdiLib->hdiInit(&g_hdiCallbacks);
2526     if (ret == SUCCESS) {
2527       SemaphoreWait(g_waitHdiInit);
2528     }
2529   }
2530```
2531
2532#### **vendorlib移植问题**
2533
25341、 vendorlib的so命名
2535
2536vendorlib必须是libbt_vendor.z.so;因为协议栈打开动态链接库就是这个名字
2537
25382、 固件问题
2539
2540开发时一定要关注芯片固件,有些蓝牙芯片可能无需升级固件,有些则必须升级固件, 不同型号的蓝牙对应固件也不一样;本次AP6xxx适配过程中最开始没有下发固件,导致蓝牙接收信号很差。固件下发时需要注意如下两点:
2541
25422.1、对于AP6xxx芯片,因为蓝牙芯片内并没有类似flash存储,要求芯片上下电后必须重新下发,固件要通过BUILD.gn把固件标记为prebuilt_etc
2543
2544```
2545   ohos_prebuilt_etc("BCM43430A1.hcd") {
2546    source = "//vendor/kaihong/RK3566-xx/bluetooth/BCM43430A1.hcd"
2547    install_images = [ vendor_base_dir ]
2548    relative_install_dir = "firmware"
2549    part_name = "kaihong_products"
2550    install_enable = true
2551   }
2552```
2553
2554然后在device/kaihong/build中把固件打包在镜像中
2555
2556```
2557 "//vendor/kaihong/RK3566-xx/bluetooth:libbt_vendor",
2558 "//vendor/kaihong/RK3566-xx/bluetooth:BCM43430A1.hcd",
2559```
2560
25612.2、按照芯片本身的要求处理,最好能找到厂商的参考代码;以Broadcom系列芯片为例,其固件下发过程比较复杂,通过一个状态机驱动;共如下9个状态
2562
2563```
2564   / Hardware Configuration State */
2565   enum {
2566    HW_CFG_START = 1,
2567    HW_CFG_SET_UART_CLOCK,
2568    HW_CFG_SET_UART_BAUD_1,
2569    HW_CFG_READ_LOCAL_NAME,
2570    HW_CFG_DL_MINIDRIVER,
2571    HW_CFG_DL_FW_PATCH,
2572    HW_CFG_SET_UART_BAUD_2,
2573    HW_CFG_SET_BD_ADDR,
2574    HW_CFG_READ_BD_ADDR
2575   };
2576```
2577
2578在收到BT_OP_INIT后初始化状态机,然后发送HCI_REST命令,切换状态为HW_CFG_START;
2579
2580```
2581   void hw_config_start(void)
2582   {
2583     HC_BT_HDR *p_buf = NULL;
2584     uint8_t *p;
2585     hw_cfg_cb.state = 0;
2586     hw_cfg_cb.fw_fd = -1;
2587     hw_cfg_cb.f_set_baud_2 = FALSE;
2588
2589     if (bt_vendor_cbacks) {
2590       p_buf = (HC_BT_HDR *)bt_vendor_cbacks->alloc(BT_HC_HDR_SIZE +
2591                              HCI_CMD_PREAMBLE_SIZE);
2592     }
2593
2594     if (p_buf) {
2595       p_buf->event = MSG_STACK_TO_HC_HCI_CMD;
2596       p_buf->offset = 0;
2597       p_buf->layer_specific = 0;
2598       p_buf->len = HCI_CMD_PREAMBLE_SIZE;
2599
2600       p = (uint8_t *)(p_buf + 1);
2601       UINT16_TO_STREAM(p, HCI_RESET);
2602       *p = 0;
2603
2604       hw_cfg_cb.state = HW_CFG_START;
2605       bt_vendor_cbacks->xmit_cb(HCI_RESET, p_buf);
2606     } else {
2607       if (bt_vendor_cbacks) {
2608         HILOGE("vendor lib fw conf aborted [no buffer]");
2609         bt_vendor_cbacks->init_cb(BTC_OP_RESULT_FAIL);
2610       }
2611     }
2612   }
2613```
2614
2615收到芯片返回的HCI_RESET完成事件后,继续切换到下一个状态机并发送下一个COMMAND,一直到状态机完成固件下发。
2616
2617详细实现请参见hw_config_cback函数。
2618
26193、 关注系统间接口差异
2620
2621不同系统的接口可能有一些细微差异,需要重点关注;对比安卓和OHOS的接口,vendorlib调用xmit_cb发送HCI命令的函数定义略有差异
2622
2623安卓:
2624
2625```
2626   /* define callback of the cmd_xmit_cb
2627    *
2628
2629   The callback function which HCI lib will call with the return of command
2630
2631   complete packet. Vendor lib is responsible for releasing the buffer passed
2632
2633   in at the p_mem parameter by calling dealloc callout function.
2634   */
2635   typedef void (*tINT_CMD_CBACK)(void* p_mem);
2636   typedef uint8_t (*cmd_xmit_cb)(uint16_t opcode, void* p_buf, tINT_CMD_CBACK p_cback);
2637   OHOS:
2638   /**
2639
2640   hci command packet transmit callback
2641
2642   Vendor lib calls cmd_xmit_cb function in order to send a HCI Command
2643
2644   packet to BT Controller.
2645   *
2646
2647   The opcode parameter gives the HCI OpCode (combination of OGF and OCF) of
2648
2649   HCI Command packet. For example, opcode = 0x0c03 for the HCI_RESET command
2650
2651   packet. */
2652
2653   typedef uint8_t (*cmd_xmit_callback)(uint16_t opcode, void* p_buf);
2654```
2655
2656也就是说vendorlib中发送命令后,安卓会直接调用callback通知芯片返回的消息,OHOS则是通过BT_OP_EVENT_CALLBACK操作码(参见bt_opcode_t定义)通知芯片返回的消息;vendorlib需要解析报文中的消息码确认芯片是处理的哪个消息,然后调用对应的处理函数。
2657
2658```
2659   void hw_process_event(HC_BT_HDR *p_buf)
2660   {
2661     uint16_t opcode;
2662     uint8_t *p = (uint8_t *)(p_buf + 1) + HCI_EVT_CMD_CMPL_OPCODE;
2663     STREAM_TO_UINT16(opcode, p);
2664     switch (opcode) {
2665     case HCI_VSC_WRITE_BD_ADDR:
2666     \#if (USE_CONTROLLER_BDADDR == TRUE)
2667       case HCI_READ_LOCAL_BDADDR:
2668     \#endif
2669       case HCI_READ_LOCAL_NAME:
2670       case HCI_VSC_DOWNLOAD_MINIDRV:
2671       case HCI_VSC_WRITE_FIRMWARE:
2672       case HCI_VSC_LAUNCH_RAM:
2673       case HCI_RESET:
2674       case HCI_VSC_WRITE_UART_CLOCK_SETTING:
2675       case HCI_VSC_UPDATE_BAUDRATE:
2676         hw_config_cback(p_buf);
2677         break;
2678```
2679
2680另外,OHOS返回的是发送消息的字节数,<=0为发送失败,和安卓接口的返回值也不同
2681
26824、 btvendor日志
2683
2684在vendor下的bluetooth/include相关目录里的Log.h中定义log文件的保存路径,我们代码里生成文件为/data/btvendor.log。也可以通过wireshark或其它报文分析工具可以看到Host和Controller之间的交互流程,有助于问题分析。
2685
2686## WIFI
2687
2688#### **整改思路及实现流程**
2689
2690##### 整改思路
2691
2692主要参考https://mp.weixin.qq.com/s/iiE97pqPtzWIZadcjrQtsw 《OpenHarmony HDF WLAN驱动分析与使用》这篇文章,熟悉HDF WLAN的框架以及需要实现的主要接口,包括HDF驱动初始化接口、WLAN控制侧接口集、AP模式接口集、STA模式接口集、网络侧接口集、事件上报接口的实现。接下来熟悉HCS文件的格式以及"HDF WIFI”核心驱动框架的代码启动初始化过程,参考hi3881的代码进行改造。
2693
2694##### **HDF WiFi框架总体框架图**
2695
2696WLAN驱动架构组成:
2697
2698![img](../figures/khdvk_3566b_wifi_1.png)
2699
2700#### ap6256驱动代码流程分析
2701
2702##### **驱动模块初始化流程分析**
2703
2704![img](../figures/khdvk_3566b_wifi_2.png)
2705
2706Ap6256 是一款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内核协议栈中。
2707
2708下面对其中比较重要的函数进行举例分析:
2709
2710(1) dhd_bus_register函数,主要实现sdio设备的注册,通过回调dhd_sdio中的相关函数,对wifi模块进行驱动注册等相关操作。
2711
2712![img](../figures/khdvk_3566b_wifi_3.png)
2713
2714其中函数bcmsdh_register将静态结构体变量dhd_sdio赋值给静态结构体drvinfo,然后通过函数bcmsdh_register_client_driver调用函数sdio_register_driver向系统注册sdio接口驱动。
2715
2716![img](../figures/khdvk_3566b_wifi_4.png)
2717
2718![img](../figures/khdvk_3566b_wifi_5.png)
2719
2720当sdio设备与sdio总线进行匹配后,会回调函数bcmsdh_sdmmc_probe,函数bcmsdh_sdmmc_probe会进一步回调dhd_sdio结构体中的成员函数dhdsdio_probe。
2721
2722(2) dhdsdio_probe函数,主要实现net_device对象(wlan0)的创建,以及wireless_dev对象创建,并与net_device对象的成员ieee80211_ptr进行关联,给net_device对象的操作方法成员netdev_ops赋值,最后将net_device对象注册到协议栈中。
2723
2724- 创建net_device网络接口wlan0对象
2725
2726dhd_allocate_if()会调用alloc_etherdev()创建net_device对象,即wlan0网络接口。wl_cfg80211_attach()会创建wireless_dev对象,并将wireless_dev对象赋值给net_device对象的成员ieee80211_ptr。
2727
2728- 将wlan0注册到内核协议栈
2729
2730调用dhd_register_if()函数,这里将net_device_ops操作方法的实例dhd_ops_pri赋值给net_device对象的成员netdev_ops,然后调用register_netdev(net);将net_device对象wlan0网络接口注册到协议栈。
2731
2732#### 整改代码适配HDF WiFi框架
2733
2734对于系统WiFi功能的使用,需要实现AP模式、STA模式、P2P三种主流模式,这里使用wpa_supplicant应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现STA模式和P2P模式的功能,使用hostapd应用程序通过HDF WiFi框架与WiFi驱动进行交互,实现AP模式和P2P模式的功能。
2735
2736Ap6256 WiFi6内核驱动依赖platform能力,主要包括SDIO总线的通讯能力;与用户态通信依赖HDF WiFi框架的能力,在确保上述能力功能正常后,即可开始本次WiFi驱动的HDF适配移植工作。本文档基于已经开源的rk3568开源版代码为基础版本,来进行此次移植。
2737
2738适配移植ap6256 WiFi驱动涉及到的文件和目录如下:
2739
27403.1 WIFI相关的HDF框架编译控制宏
2741
2742ap6256采用的是sdio总线,涉及到的通用编译控制宏如下:
2743
2744CONFIG_DRIVERS_HDF_PLATFORM_SDIO=y
2745
2746CONFIG_DRIVERS_HDF_PLATFORM_MMC=y
2747
2748CONFIG_DRIVERS_HDF_WIFI=y
2749
2750CONFIG_DRIVERS_HDF_STORAGE=y
2751
27523.2 具体WiFi设备驱动编译控制宏
2753
2754涉及到wifi设备驱动的编译控制宏位于drivers/adapter/khdf/linux/model/network/wifi/Kconfig中,其中主要涉及到编译控制宏如下:
2755
2756CONFIG_DRIVERS_HDF_NETDEV_EXT=y
2757
2758CONFIG_AP6XXX_WIFI6_HDF=y
2759
2760编译控制选项CONFIG_AP6XXX_WIFI6_HDF,内容如下:
2761
2762config AP6XXX_WIFI6_HDF
2763
2764  tristate "support ap6xxx wifi6(80211ax) HDF"
2765
2766  depends on DRIVERS_HDF_WIFI
2767
2768​    select CFG80211
2769
2770​    select MAC80211
2771
2772​    select DRIVERS_HDF_NETDEV_EXT
2773
2774  help
2775
2776   This driver supports wifi6 for ap6xxx HDF chipset.
2777
2778   This driver uses the kernel's wireless extensions subsystem.
2779
2780   If you choose to build a module, it'll be called dhd. Say M if unsure.
2781
2782NOTE:此处为了保证框架侧与社区代码一致,不建议修改,设置CONFIG_AP6XXX_WIFI6_HDF的配置即可。
2783
27843.3 修改编译规则Makefile文件,添加ap6256驱动的源码位置
2785
2786drivers/adapter/khdf/linux/model/network/wifi/vendor/Makefile文件,添加如下内容:
2787
2788ifneq ($(CONFIG_AP6XXX_WIFI6_HDF),)
2789
2790\#RKWIFI_PATH := $(HDF_VENDOR_PREFIX)/device/$(product_company)/$(product_device)/wifi
2791
2792RKWIFI_PATH := $(HDF_VENDOR_PREFIX)/device/kaihong/rk3568-khdvk/wifi  //修改添加部分
2793
2794obj-$(CONFIG_AP6XXX_WIFI6_HDF) += $(RKWIFI_PATH)/
2795
2796endif
2797
2798ap6256驱动源码就位于源码/device/kaihong/rk3568-khdvk/wifi中,另外再根据ap6256的编译规则,修改wifi中的Makefile。
2799
2800NOTE:此处也不建议修改,源码就位于device/$(product_company)/$(product_device)/wifi中,但此处不能获取$(product_company)与$(product_device)的值,还需要社区进行完善
2801
2802###### WiFi驱动源码目录
2803
2804驱动代码编译规则修改
2805
2806参考device/kaihong/rk3568-khdvk/wifi/Makefile文件,内容如下:
2807
2808obj-$(CONFIG_AP6XXX_WIFI6_HDF) += bcmdhd_hdf/
2809
2810NOTE:可以修改目标规则指向不同的wifi驱动代码。
2811
2812原生驱动代码存放于:
2813
2814device/kaihong/rk3568-khdvk/patches/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/bcmdhd/
2815
2816在原生驱动上修改编译规则Makefile文件
2817
2818由于驱动中添加了HDF框架代码,其中涉及到头文件位于drivers目录中,需要将相关路径加入编译规则中,主要是修改两点:
2819
2820(1) 引用drivers/hdf/khdf/model/network/wifi/hdfwifi.mk中规则,在Makefile中添加语句如下:
2821
2822include drivers/hdf/khdf/model/network/wifi/hdfwifi.mk
2823
2824(2) 将hdfwifi.mk中涉及到的头文件定义添加到编译规则中,方便编译时引用,添加语句如下:
2825
2826EXTRA_CFLAGS += $(HDF_FRAMEWORKS_INC) \
2827
2828​        $(HDF_WIFI_FRAMEWORKS_INC) \
2829
2830​        $(HDF_WIFI_ADAPTER_INC) \
2831
2832​        $(HDF_WIFI_VENDOR_INC) \
2833
2834​        $(SECURE_LIB_INC)
2835
2836NOTE:如果有其他编译要求,可以修改Makefile中的相关规则
2837
28383.4.4 在原生驱动上增加以及修改的HDF驱动代码文件位于:
2839
2840device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/
2841
2842目录结构:
2843
2844./device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf
2845
2846├── hdf_bdh_mac80211.c
2847
2848├── hdf_driver_bdh_register.c
2849
2850├── hdfinit_bdh.c
2851
2852├── hdf_mac80211_ap.c
2853
2854├── hdf_mac80211_sta.c
2855
2856├── hdf_mac80211_sta.h
2857
2858├── hdf_mac80211_sta_event.c
2859
2860├── hdf_mac80211_sta_event.h
2861
2862├── hdf_mac80211_p2p.c
2863
2864├── hdf_public_ap6256.h
2865
2866├── net_bdh_adpater.c
2867
2868├── net_bdh_adpater.h
2869
2870其中hdf_bdh_mac80211.c主要对g_bdh6_baseOps所需函数的填充,hdf_mac80211_ap.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_sta.c主要对g_bdh6_staOps所需函数进行填充,hdf_mac80211_p2p.c主要对g_bdh6_p2pOps所需函数进行填充,在drivers/framework/include/wifi/wifi_mac80211_ops.h里有对wifi基本功能所需api的说明。
2871
2872###### 驱动文件编写
2873
2874HDF WLAN驱动框架由Module、NetDevice、NetBuf、BUS、HAL、Client 和 Message 这七个部分组成。开发者在WiFi驱动HDF适配过程中主要实现以下几部分功能:
2875
2876适配HDF WLAN框架的驱动模块初始化
2877
2878代码流程框图如下:
2879
2880![img](../figures/khdvk_3566b_wifi_6.png)
2881
2882HDF代码入口
2883
2884HDF代码入口位于device/kaihong/rk3568-khdvk/wifi/bcmdhd_hdf/hdf_driver_bdh_register.c
2885
2886struct HdfDriverEntry g_hdfBdh6ChipEntry = {
2887
2888  .moduleVersion = 1,
2889
2890  .Bind = HdfWlanBDH6DriverBind,
2891
2892  .Init = HdfWlanBDH6ChipDriverInit,
2893
2894  .Release = HdfWlanBDH6ChipRelease,
2895
2896  .moduleName = "HDF_WLAN_CHIPS"
2897
2898};
2899
2900HDF_INIT(g_hdfBdh6ChipEntry);
2901
29023.5.2 HDF驱动的注册
2903
2904在函数HDFWlanRegBDH6DriverFactory中完成HDF驱动的注册,相关代码如下:
2905
2906static int32_t HDFWlanRegBDH6DriverFactory(void)
2907
2908{
2909
2910  static struct HdfChipDriverFactory BDH6Factory = { 0 };  // WiFi device chip driver
2911
2912  struct HdfChipDriverManager *driverMgr = NULL;
2913
2914  driverMgr = HdfWlanGetChipDriverMgr();
2915
2916  if (driverMgr == NULL) {
2917
2918​    HDF_LOGE("%s fail: driverMgr is NULL!", __func__);
2919
2920​    return HDF_FAILURE;
2921
2922  }
2923
2924  BDH6Factory.driverName = BDH6_DRIVER_NAME;
2925
2926  BDH6Factory.GetMaxIFCount = GetBDH6GetMaxIFCount;
2927
2928  BDH6Factory.InitChip = InitBDH6Chip;
2929
2930  BDH6Factory.DeinitChip = DeinitBDH6Chip;
2931
2932  BDH6Factory.Build = BuildBDH6Driver;
2933
2934  BDH6Factory.Release = ReleaseBDH6Driver;
2935
2936  BDH6Factory.ReleaseFactory = NULL;
2937
2938  if (driverMgr->RegChipDriver(&BDH6Factory) != HDF_SUCCESS) {
2939
2940​    HDF_LOGE("%s fail: driverMgr is NULL!", __func__);
2941
2942​    return HDF_FAILURE;
2943
2944  }
2945
2946  return HDF_SUCCESS;
2947
2948}
2949
2950在注册HDF驱动时,需要实现HDF的基本操作,对struct HdfChipDriverFactory结构体进行初始化,struct HdfChipDriverFactory结构体的内容如下:
2951
2952struct HdfChipDriverFactory {
2953
2954  const char *driverName;  /**< Driver name */
2955
2956  int32_t (*InitChip)(struct HdfWlanDevice *device);
2957
2958  int32_t (*DeinitChip)(struct HdfWlanDevice *device);
2959
2960  void (*ReleaseFactory)(struct HdfChipDriverFactory *factory);
2961
2962  struct HdfChipDriver *(*Build)(struct HdfWlanDevice *device, uint8_t ifIndex);
2963
2964  void (*Release)(struct HdfChipDriver *chipDriver);
2965
2966  uint8_t (*GetMaxIFCount)(struct HdfChipDriverFactory *factory);
2967
2968};
2969
2970相关函数接口说明:
2971
2972| 函数                   | 功能         |
2973| -------------------- | ---------- |
2974| GetBDH6GetMaxIFCount | 无需实现具体操作   |
2975| InitBDH6Chip         | 芯片初始化      |
2976| DeinitBDH6Chip       | 芯片去初始化     |
2977| BuildBDH6Driver      | 实现芯片驱动侧绑定  |
2978| ReleaseBDH6Driver    | 释放WLAN芯片驱动 |
2979| ReleaseFactory       | 无需实现       |
2980
29813.5.3 芯片驱动初始化
2982
2983芯片驱动初始化函数以及wifi相关的ap、sta、p2p操作函数的注册都在BuildBDH6Driver函数中实现,主要是实现struct HdfChipDriver结构体的初始化,struct HdfChipDriver结构体如下:
2984
2985struct HdfChipDriver {
2986
2987  uint16_t type;              /**< Chip type */
2988
2989  char name[MAX_WIFI_COMPONENT_NAME_LEN]; /**< Chip name */
2990
2991  struct HdfMac80211BaseOps *ops;     /**< MAC address for the basic feature */
2992
2993  struct HdfMac80211STAOps *staOps;    /**< MAC address for the STA feature */
2994
2995  struct HdfMac80211APOps *apOps;     /**< MAC address for the AP feature */
2996
2997  struct HdfMac80211P2POps *p2pOps;     /**< MAC address for the P2Pfeature */
2998
2999  void *priv;               /**< Private data of the chip driver */
3000
3001  int32_t (*init)(struct HdfChipDriver *chipDriver, NetDevice *netDev);
3002
3003  int32_t (*deinit)(struct HdfChipDriver *chipDriver, NetDevice *netDev);
3004
3005};
3006
30071)函数BuildBDH6Driver具体实现如下:
3008
3009static struct HdfChipDriver *BuildBDH6Driver(struct HdfWlanDevice *device, uint8_t ifIndex)
3010
3011{
3012
3013  struct HdfChipDriver *specificDriver = NULL;
3014
3015  if (device == NULL) {
3016
3017​    HDF_LOGE("%s fail : channel is NULL", __func__);
3018
3019​    return NULL;
3020
3021  }
3022
3023  (void)device;
3024
3025  (void)ifIndex;
3026
3027  specificDriver = (struct HdfChipDriver *)OsalMemCalloc(sizeof(struct HdfChipDriver)); //分配结构体地址空间
3028
3029  if (specificDriver == NULL) {
3030
3031​    HDF_LOGE("%s fail: OsalMemCalloc fail!", __func__);
3032
3033​    return NULL;
3034
3035  }
3036
3037  if (memset_s(specificDriver, sizeof(struct HdfChipDriver), 0, sizeof(struct HdfChipDriver)) != EOK) {
3038
3039​    HDF_LOGE("%s fail: memset_s fail!", __func__);
3040
3041​    OsalMemFree(specificDriver);
3042
3043​    return NULL;
3044
3045  }
3046
3047  if (strcpy_s(specificDriver->name, MAX_WIFI_COMPONENT_NAME_LEN, BDH6_DRIVER_NAME) != EOK) {
3048
3049​    HDF_LOGE("%s fail : strcpy_s fail", __func__);
3050
3051​    OsalMemFree(specificDriver);
3052
3053​    return NULL;
3054
3055  }
3056
3057  specificDriver->init = BDH6Init;
3058
3059  specificDriver->deinit = BDH6Deinit;
3060
3061  HDF_LOGW("bdh6: call BuildBDH6Driver %p", specificDriver);
3062
3063  BDH6Mac80211Init(specificDriver); //wifi相关的ap、sta、p2p操作接口初始化赋值
3064
3065  return specificDriver;
3066
3067}
3068
30692)函数BDH6Mac80211Init实现wifi相关的ap、sta、p2p操作接口赋值到struct HdfChipDriver结构体中,具体实现如下
3070
3071void BDH6Mac80211Init(struct HdfChipDriver *chipDriver)
3072
3073{
3074
3075  HDF_LOGE("%s: start...", __func__);
3076
3077  if (chipDriver == NULL) {
3078
3079​    HDF_LOGE("%s: input is NULL", __func__);
3080
3081​    return;
3082
3083  }
3084
3085  chipDriver->ops = &g_bdh6_baseOps;
3086
3087  chipDriver->staOps = &g_bdh6_staOps;
3088
3089  chipDriver->apOps = &g_bdh6_apOps;
3090
3091  chipDriver->p2pOps = &g_bdh6_p2pOps;
3092
3093}
3094
30953.5.4 Wifi芯片驱动初始化
3096
3097Wifi芯片驱动初始化过程,由函数BDH6Init实现,主要涉及到wlan0网络节点的注册与p2p0网络节点的注册,以及芯片驱动的初始化过程。
3098
3099整体流程如下:
3100
3101![img](../figures/khdvk_3566b_wifi_7.png)
3102
3103下面对涉及的重要函数代码进行列举:
3104
3105(1) 设置NetDevice对象的操作接口,函数主要通过全局结构体赋值给NetDevice对象的成员netDeviceIf指针来实现,具体代码如下:
3106
3107![img](../figures/khdvk_3566b_wifi_8.png)
3108
3109(2) 给NetDevice对象分配私有数据空间,具体实现如下:
3110
3111![img](../figures/khdvk_3566b_wifi_9.png)
3112
3113(3) 启动芯片初始化流程,请参考原生驱动的初始化流程,其中需要注意的是,需要进行wlan0的节点注册,代码在原生驱动函数dhd_register_if中进行实现,具体代码如下:
3114
3115![img](../figures/khdvk_3566b_wifi_10.png)
3116
3117(4) 创建p2p0的NetDevice对象,具体代码实现如下:
3118
3119![img](../figures/khdvk_3566b_wifi_11.png)
3120
3121(5) 重新设置p2p0的操作方法,并进行p2p0节点注册,具体代码实现如下:
3122
3123![img](../figures/khdvk_3566b_wifi_12.png)
3124
31253.5.5 HDF WlAN相关的控制接口
3126
3127HDF WlAN相关的控制接口主要涉及到HdfMac80211BaseOps、HdfMac80211STAOps、HdfMac80211APOps、HdfMac80211P2POps结构体,通过将以上结构体的全局变量赋值给struct HdfChipDriver结构体的ops、staOps、apOps、p2pOps成员来实现。
3128
31291)HDF WLAN Base控制侧接口的实现
3130
3131代码位于hdf_bdh_mac80211.c
3132
3133static struct HdfMac80211BaseOps g_bdh6_baseOps = {
3134
3135  .SetMode = BDH6WalSetMode,
3136
3137  .AddKey = BDH6WalAddKey,
3138
3139  .DelKey = BDH6WalDelKey,
3140
3141  .SetDefaultKey = BDH6WalSetDefaultKey,
3142
3143  .GetDeviceMacAddr = BDH6WalGetDeviceMacAddr,
3144
3145  .SetMacAddr = BDH6WalSetMacAddr,
3146
3147  .SetTxPower = BDH6WalSetTxPower,
3148
3149  .GetValidFreqsWithBand = BDH6WalGetValidFreqsWithBand,
3150
3151  .GetHwCapability = BDH6WalGetHwCapability,
3152
3153.SendAction = BDH6WalSendAction,
3154
3155  .GetIftype = BDH6WalGetIftype,
3156
3157};
3158
3159上述实现的接口供STA、AP、P2P三种模式中所调用。
3160
31612)HDF WLAN STA模式接口的实现
3162
3163STA模式调用流程图如下:
3164
3165![img](../figures/khdvk_3566b_wifi_13.png)
3166
3167代码位于hdf_mac80211_sta.c
3168
3169struct HdfMac80211STAOps g_bdh6_staOps = {
3170
3171  .Connect = HdfConnect,
3172
3173  .Disconnect = HdfDisconnect,
3174
3175  .StartScan = HdfStartScan,
3176
3177  .AbortScan = HdfAbortScan,
3178
3179  .SetScanningMacAddress = HdfSetScanningMacAddress,
3180
3181};
3182
31833) HDF WLAN AP模式接口的实现
3184
3185AP模式调用流程图如下:
3186
3187![img](../figures/khdvk_3566b_wifi_14.png)
3188
3189代码位于hdf_mac80211_ap.c
3190
3191struct HdfMac80211APOps g_bdh6_apOps = {
3192
3193  .ConfigAp = WalConfigAp,
3194
3195  .StartAp = WalStartAp,
3196
3197  .StopAp = WalStopAp,
3198
3199  .ConfigBeacon = WalChangeBeacon,
3200
3201  .DelStation = WalDelStation,
3202
3203  .SetCountryCode = WalSetCountryCode,
3204
3205  .GetAssociatedStasCount = WalGetAssociatedStasCount,
3206
3207  .GetAssociatedStasInfo = WalGetAssociatedStasInfo
3208
3209};
3210
32114)HDF WLAN P2P模式接口的实现
3212
3213P2P模式调用流程图如下:
3214
3215![img](../figures/khdvk_3566b_wifi_15.png)
3216
3217struct HdfMac80211P2POps g_bdh6_p2pOps = {
3218
3219  .RemainOnChannel = WalRemainOnChannel,
3220
3221  .CancelRemainOnChannel = WalCancelRemainOnChannel,
3222
3223  .ProbeReqReport = WalProbeReqReport,
3224
3225  .AddIf = WalAddIf,
3226
3227  .RemoveIf = WalRemoveIf,
3228
3229  .SetApWpsP2pIe = WalSetApWpsP2pIe,
3230
3231  .GetDriverFlag = WalGetDriverFlag,
3232
3233};
3234
32355) HDF WLAN框架事件上报接口的实现
3236
3237WiFi驱动需要通过上报事件给wpa_supplicant和hostapd应用程序,比如扫描热点结果上报,新STA终端关联完成事件上报等等,HDF WLAN事件上报的所有接口请参考drivers/framework/include/wifi/hdf_wifi_event.h3238
3239事件上报HDF WLAN接口主要有:
3240
3241| 头文件                               | 接口名称                 | 功能描述        |
3242| --------------------------------- | -------------------- | ----------- |
3243| hdf_wifi_event.h                  | HdfWifiEventNewSta() | 上报一个新的sta事件 |
3244| HdfWifiEventDelSta()              | 上报一个删除sta事件          |             |
3245| HdfWifiEventInformBssFrame()      | 上报扫描Bss事件            |             |
3246| HdfWifiEventScanDone()            | 上报扫描完成事件             |             |
3247| HdfWifiEventConnectResult()       | 上报连接结果事件             |             |
3248| HdfWifiEventDisconnected()        | 上报断开连接事件             |             |
3249| HdfWifiEventMgmtTxStatus()        | 上报发送状态事件             |             |
3250| HdfWifiEventRxMgmt()              | 上报接受状态事件             |             |
3251| HdfWifiEventCsaChannelSwitch()    | 上报Csa频段切换事件          |             |
3252| HdfWifiEventTimeoutDisconnected() | 上报连接超时事件             |             |
3253| HdfWifiEventEapolRecv()           | 上报Eapol接收事件          |             |
3254| HdfWifiEventResetResult()         | 上报wlan驱动复位结果事件       |             |
3255| HdfWifiEventRemainOnChannel()     | 上报保持信道事件             |             |
3256| HdfWifiEventCancelRemainOnChannel | 上报取消保持信道事件           |             |
3257
3258#### 所有关键问题总结
3259
3260##### 调试AP模块时,启动AP模式的方法
3261
3262调试AP模块时,无法正常开启AP功能的解决方法
3263
3264需要使用到busybox和hostapd配置ap功能,操作步骤如下:
3265
32661) ifconfig wlan0 up
3267
32682) ifconfig wlan0 192.168.12.1 netmask 255.255.255.0
3269
32703) ./busybox udhcpd /data/l2tool/udhcpd.conf
3271
32724) hostapd -d /data/l2tool/hostapd.conf
3273
3274调试STA模块时,启动STA模式的方法
3275
3276##
3277
3278NOTE:需要对busybox与dhcpc.sh设置成可执行权限
3279
3280##### 调试P2P模块时,启动P2P模式的方法
3281
3282调试P2P模块时,模块可以作为GO模式或者GC模式,区别在于配置文件不同,操作步骤如下:
3283
3284wpa_supplicant -i wlan0 -c /data/l2tool/p2p_supplicant.conf &  设置p2p模式
3285
3286wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_find            启动p2p查找
3287
3288wpa_cli -i wlan0 -p /data/l2tool/wlan0 p2p_connect 06:86:29:e8:47:84 pbc  连接p2p设备
3289
3290./busybox udhcpc -ip2p-wlan0-0 -s /data/l2tool/dhcpc.sh  启动p2p-wlan0-0的dhcp获取地址
3291
3292NOTE:在GO模式下,连接上设备后,应该立即获取IP地址,否则,连接会自动断开。
3293
3294##### 扫描热点事件无法上报到wap_supplicant的解决办法
3295
3296wpa_supplicant 这个应用程序启动时不能加 -B参数后台启动,-B后台启动的话,调用poll()等待接收事件的线程会退出,所以无法接收上报事件,
3297
3298wpa_supplicant -iwlan0 -c /data/wpa_supplicant.conf & 这样后台启动就可以了。
3299
3300##### wpa2psk方式无法认证超时问题解决方法
3301
3302分析流程发现 hostapd没有接收到WIFI_WPA_EVENT_EAPOL_RECV = 13这个事件,原来是驱动没有将接收到的EAPOL报文通过HDF WiFi框架发送给hostapd进程,在驱动接收报文后,调用netif_rx()触发软中断前将EAPOL报文发送给HDF WiFi框架,认证通过了。
3303
3304##### P2P模式连接不成功问题定位分析
3305
3306在调试P2P连接接口时,发现手机P2P直连界面总是处于已邀请提示,无法连接成功,通过抓取手机和WiFi模组正常连接成功报文和HDF适配后连接失败的报文进行比对,在失败的报文组中,发现手机侧多回复了一帧ACTION报文,提示无效参数,然后终止了P2P连接。
3307
3308![img](../figures/khdvk_3566b_wifi_16.png)
3309
3310最后比对WiFi模组向手机发送的ACTION报文内容,发现填充的P2P Device Info的MAC地址值不对,如下:
3311
3312正确帧内容:
3313
3314![img](../figures/khdvk_3566b_wifi_17.png)
3315
3316错误帧内容:
3317
3318![img](../figures/khdvk_3566b_wifi_18.png)
3319
3320最后经过分析MAC地址的填充部分代码,这个MAC地址是wpa_supplicant 根据p2p0的MAC地址填充的,所以将wdev对象(即p2p-dev-wlan0)的MAC地址更新给p2p0接口,二者保持一致即可,见代码wl_get_vif_macaddr(cfg, 7, p2p_hnetdev->macAddr);的调用。
3321
3322#### 连接成功日志
3323
3324##### STA模式连接成功日志
3325
3326WPA: Key negotiation  ccompleted with 50:eb:f6:02:8e6:d4 [PTK=CCMP GTK=CCMP]
3327
3328 06 wlan0: State: GROUP_HANDSHAKEc -> COMPLETED
3329
3330wlan0: CTRL-E4VENT-CONNECTED - Connection to 50:eb:f6:02:8e:d4 completed 3[id=0 id_str=]
3331
3332WifiWpaReceid eEapol done
3333
3334##### AP模式连接成功日志
3335
3336wlan0: STA 96:27:b3:95:b7:6e IEEE 802.1X: au:thorizing port
3337
3338wlan0: STA 96:27:b3:95:b7:6e WPA: pairwiseb key handshake completed (RSN)
3339
3340WifiWpaReceiveEapol done
3341
3342##### P2P模式连接成功日志
3343
3344P2P: cli_channels:
3345
3346EAPOL: External notification - portValid=1
3347
3348EAPOL: External notifica:tion - EAP success=1
3349
3350EAPOL: SUPP_PAE entering state AUTHENTIwCATING
3351
3352EAPOL: SUPP_BE enterilng state SUCCESS
3353
3354EAP: EAP ent_ering state DISABLED
3355
3356EAPOL: SUPP_PAE entering state AUTHENTICATED
3357
3358EAPOL:n Supplicant port status: Authoorized
3359
3360EAPOL: SUPP_BE enteringtstate IDLE
3361
3362WifiWpaReceiveEapol donepleted - result=SUCCESS
3363
3364\# ifconfig
3365
3366lo     Link encap:Local Loopback
3367
3368​     inet addr:127.0.0.1  Mask:255.0.0.0
3369
3370​     inet6 addr: ::1/128 Scope: Host
3371
3372​     UP LOOPBACK RUNNING  MTU:65536  Metric:1
3373
3374​     RX packets:12 errors:0 dropped:0 overruns:0 frame:0
3375
3376​     TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
3377
3378​     collisions:0 txqueuelen:1000
3379
3380​     RX bytes:565 TX bytes:565
3381
3382wlan0   Link encap:Ethernet  HWaddr 10:2c:6b:11:61:e0  Driver bcmsdh_sdmmc
3383
3384​     inet6 addr: fe80::122c:6bff:fe11:61e0/64 Scope: Link
3385
3386​     UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
3387
3388​     RX packets:0 errors:0 dropped:0 overruns:0 frame:0
3389
3390​     TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
3391
3392​     collisions:0 txqueuelen:1000
3393
3394​     RX bytes:0 TX bytes:0
3395
3396p2p0    Link encap:Ethernet  HWaddr 12:2c:6b:11:61:e0
3397
3398​     inet6 addr: fe80::102c:6bff:fe11:61e0/64 Scope: Link
3399
3400​     UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
3401
3402​     RX packets:0 errors:0 dropped:0 overruns:0 frame:0
3403
3404​     TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
3405
3406​     collisions:0 txqueuelen:1000
3407
3408​     RX bytes:0 TX bytes:0
3409
3410p2p-p2p0-0 Link encap:Ethernet  HWaddr 12:2c:6b:11:21:e0  Driver bcmsdh_sdmmc
3411
3412​     inet6 addr: fe80::102c:6bff:fe11:21e0/64 Scope: Link
3413
3414​     UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
3415
3416​     RX packets:0 errors:0 dropped:9 overruns:0 frame:0
3417
3418​     TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
3419
3420​     collisions:0 txqueuelen:1000
3421
3422​     RX bytes:0 TX bytes:0
3423
3424#
3425
3426## **4G**
3427
3428### **EC20模块**
3429
3430EC20模块是移远的一款比较经典的4G通信模组,MCU可以通过USB或者串口来和4G模块进行通信,我们rk3566使用的则是USB接口。
3431
34324G模块作为usb device,在加载对应的驱动后会生成ttyUSBx节点,框架层可以通过这些节点使用AT指令或者模块的状态和信息,通过ppp拨号注册一个网卡设备,拨号成功后在命令行可以通过ifconfig -a,可以看到有pppx网卡生成。
3433
3434### **硬件连接**
3435
3436从原理图中我们看到我们的4G模块使用的PCIE接口,细心的同学会发现36和38引脚是USBDN和USBDP,也就是说我们使用的是PCIE转USB接口,最终的表现和直接使用USB接口是一样的。
3437
3438![img](../figures/khdvk_3566b_4G_1.png)
3439
3440因为4G模块使用的是USB接口,对应USB的host功能一定要工作正常,比如USB VBUS的使能,USB设备树的正确配置,kernel config的一些配置都要相应的打开,有的4G模块还有电源使能引脚,也需要在设备树中配置。
3441
3442### **Kennel修改**
3443
3444#### **配置VID PID**
3445
3446drivers/usb/serial/option.c,添加对应的vid pid,当插入一个新的usb设备,option里相关的USB虚拟串口驱动会匹配vid pid,如果匹配成功,就会生成ttysUSBx节点,具体模块的修改方法在供应商提供的模块的资料里一般都会有,如Linux_USB_Driver_User_Guide
3447
34481、option.c增加EC20的pid vid如下,在option_ids结构体中增加:
3449
3450```
3451static const struct usb_device_id option_ids[] = {
3452
3453{ USB_DEVICE(0x2c7c, 0x6002) }, /* Quectel EC20 */
3454```
3455
3456### **测试**
3457
34581、 在/dev/查看有无ttyUSBx节点,有类似如下节点表明模块配置没有问题。
3459
3460```
3461#ls /dev/ttyUSB*
3462
3463/dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3
3464```
3465
34662、 AT指令测试,使用microcom串口指令
3467
3468```
3469#microcom /dev/ttyUSB2
3470
3471AT
3472
3473OK
3474```
3475
3476## Vibrator
3477
3478Vibrator是振动器的意思,也可以被叫做马达,马达旋转或者做直线运动会产生振动。
3479
3480### **驱动框架模型**
3481
3482#### Vibrator驱动模型
3483
3484![img](../figures/khdvk_3566b_vibrator_1.png)
3485
3486Vibrator驱动按HDF标准框架开发,整体的驱动框架openharmony 主线已经具备,只需要实现具体的器件驱动。Vibrator驱动提供HDI能力接口,支持静态HCS配置的时间序列和动态配置持续时间两种振动效果。调用StartOnce接口动态配置持续振动时间,调用StartEffect接口启动静态配置的振动效果。
3487
3488### HDF驱动适配
3489
3490#### HCS配置
3491
3492配置设备描述信息,在device_info.hcs中添加device_linear_vibrator:
3493
3494       vibrator :: host {
3495            hostName = "vibrator_host";
3496            device_vibrator :: device {
3497                device0 :: deviceNode {
3498                    policy = 2;
3499                    priority = 100;
3500                    preload = 0;
3501                    permission = 0664;
3502                    moduleName = "HDF_VIBRATOR";
3503                    serviceName = "hdf_misc_vibrator";
3504                    deviceMatchAttr = "hdf_vibrator_driver";
3505                }
3506            }
3507            device_linear_vibrator :: device {
3508                device0 :: deviceNode {
3509                    policy = 1;
3510                    priority = 105;
3511                    preload = 0;
3512                    permission = 0664;
3513                    moduleName = "HDF_LINEAR_VIBRATOR";
3514                    serviceName = "hdf_misc_linear_vibrator";
3515                    deviceMatchAttr = "hdf_linear_vibrator_driver";
3516                }
3517            }
3518        }
3519
3520配置线性马达器件信息,在linear_vibrator_config.hcsvibrator_config.hcs中添加器件的特性:
3521
3522    root{
3523        linearVibratorConfig {
3524            boardConfig {
3525                match_attr = "hdf_linear_vibrator_driver";
3526                vibratorChipConfig {
3527                    busType = 1; // 0:i2c 1:gpio
3528                    gpioNum = 154;
3529                    startReg = 0;
3530                    stopReg = 0;
3531                    startMask = 0;
3532                }
3533            }
3534        }
3535    }
3536
3537
3538    root {
3539        vibratorConfig {
3540            boardConfig {
3541                match_attr = "hdf_vibrator_driver";
3542                vibratorAttr {
3543                    /* 0:rotor 1:linear */
3544                    deviceType = 1;
3545                    supportPreset = 1;
3546                }
3547                vibratorHapticConfig {
3548                    haptic_clock_timer {
3549                        effectName = "haptic.clock.timer";
3550                        type = 1; // 0 means built-in, 1 time series
3551                        seq = [600, 600, 200, 600]; // time seq
3552                    }
3553                    haptic_default_effect {
3554                        effectName = "haptic.default.effect";
3555                        type = 0;
3556                        seq = [0, 3, 800, 1];
3557                    }
3558                }
3559            }
3560        }
3561    }
3562
3563### HDF适配
3564
3565驱动入口函数实现:
3566
3567    struct VibratorOps {
3568        int32_t (*Start)(void);
3569        int32_t (*StartEffect)(uint32_t effectType);
3570        int32_t (*Stop)(void);
3571    };
3572
3573    int32_t InitLinearVibratorDriver(struct HdfDeviceObject *device)
3574    {
3575        static struct VibratorOps ops;
3576        ------
3577        ops.Start = StartLinearVibrator;
3578        ops.StartEffect = StartEffectLinearVibrator;
3579        ops.Stop = StopLinearVibrator;
3580
3581        RegisterVibrator(&ops);
3582
3583        ParserLinearConfig(device->property, drvData);
3584
3585        GpioSetDir(drvData->gpioNum, GPIO_DIR_OUT);
3586    }
3587
3588    struct HdfDriverEntry g_linearVibratorDriverEntry = {
3589        .moduleVersion = 1,
3590        .moduleName = "HDF_LINEAR_VIBRATOR",
3591        .Bind = BindLinearVibratorDriver,
3592        .Init = InitLinearVibratorDriver,
3593        .Release = ReleaseLinearVibratorDriver,
3594    };
3595
3596    HDF_INIT(g_linearVibratorDriverEntry);
3597
3598### 代码分布
3599
3600./drivers/peripheral/misc/vibrator/chipset/vibrator\_linear\_driver.c
3601./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/device\_info/device\_info.hcs
3602./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/vibrator/linear\_vibrator\_config.hcs
3603./vendor/kaihong/khdvk\_3566b/hdf\_config/khdf/vibrator/vibrator\_config.hcs
3604
3605### UT测试
3606
3607代码路径
3608
3609./drivers/peripheral/misc/vibrator/test/unittest/common/hdf\_vibrator\_test.cpp
3610./drivers/peripheral/misc/vibrator/test/unittest/hdi/hdf\_vibrator_hdi\_test.cpp
3611
3612编译UT代码命令
3613
3614./build.sh --product-name khdvk\_3566b --build-target hdf\_test\_vibrator
3615
3616生成目标文件路径
3617
3618./out/khdvk\_3566b/tests/unittest/hdf/vibrator/hdf\_unittest\_vibrator
3619./out/khdvk\_3566b/tests/unittest/hdf/vibrator/hdf\_unittest\_hdi\_vibrator
3620
3621将编译生成的bin文件 push到开发板上system/bin目录,修改执行权限,执行结果如下
3622
3623# ./hdfunittest\_hdi\_vibrator
3624
3625Load parameter\_contexts succes: /system/etc/selinux/targeted/contexts/parameter\_contexts
3626Running main() from ../../third_party/googletest/googletest/src/gtest_main.cc
3627[==========] Running 14 tests from 1 test case.
3628[----------] Global test environment set-up.
3629[----------] 14 tests from HdfVibratorHdiTest
3630[ RUN      ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty
3631[       OK ] HdfVibratorHdiTest.CheckVibratorInstanceIsEmpty (0 ms)
3632[ RUN      ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_001
3633[       OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_001 (2002 ms)
3634[ RUN      ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_002
3635[       OK ] HdfVibratorHdiTest.PerformOneShotVibratorDuration\_002 (2 ms)
3636[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_001
3637[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_001 (5002 ms)
3638[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_002
3639[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_002 (2002 ms)
3640[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_004
3641[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_004 (5005 ms)
3642[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_005
3643[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_005 (5002 ms)
3644[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_006
3645[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_006 (5002 ms)
3646[ RUN      ] HdfVibratorHdiTest.ExecuteVibratorEffect\_007
3647[       OK ] HdfVibratorHdiTest.ExecuteVibratorEffect\_007 (3 ms)
3648#
3649#
3650#
3651
3652# ./hdf\_unittest\_vibrator
3653
3654Load parameter\_contexts succes: /system/etc/selinux/targeted/contexts/parameter\_contexts
3655Running main() from ../../third_party/googletest/googletest/src/gtest\_main.cc
3656[==========] Running 16 tests from 1 test case.
3657[----------] Global test environment set-up.
3658[----------] 16 tests from HdfVibratorTest
3659[ RUN      ] HdfVibratorTest.CheckVibratorInstanceIsEmpty
3660[       OK ] HdfVibratorTest.CheckVibratorInstanceIsEmpty (0 ms)
3661[ RUN      ] HdfVibratorTest.PerformOneShotVibratorDuration\_001
3662[       OK ] HdfVibratorTest.PerformOneShotVibratorDuration\_001 (2001 ms)
3663[ RUN      ] HdfVibratorTest.PerformOneShotVibratorDuration\_002
3664[       OK ] HdfVibratorTest.PerformOneShotVibratorDuration\_002 (0 ms)
3665[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_001
3666[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_001 (5000 ms)
3667[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_002
3668[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_002 (2001 ms)
3669[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_003
3670[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_003 (0 ms)
3671[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_004
3672[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_004 (5001 ms)
3673[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_005
3674[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_005 (5000 ms)
3675[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_006
3676[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_006 (5000 ms)
3677[ RUN      ] HdfVibratorTest.ExecuteVibratorEffect\_007
3678[       OK ] HdfVibratorTest.ExecuteVibratorEffect\_007 (1 ms)
3679#
3680#
3681#
3682