• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# **标准系统方案之扬帆移植案例**
2
3​        本文章是基于瑞芯微RK3399芯片的yangfan开发板,进行标准系统相关功能的移植,主要包括产品配置添加,内核启动、升级,音频ADM化,Camera,TP,LCD,WIFI,BT,vibrator、sensor、图形显示模块的适配案例总结,以及相关功能的适配。
4开发板系统移植采用Board仓和SoC代码分离方案,Board仓保存板载驱动的模块,例如音频,Camera,TP,WIFI等驱动模块的适配代码。在SoC仓保存与SoC驱动相关模块,例如I2C,ISP,RGA等驱动模块的适配代码。
5
6## 产品配置和目录规划
7
8### 产品配置
9
10在产品`//vendor/yangfan`目录下创建config.json文件,并指定CPU的架构。`//vendor/yangfan/rk3399.json`配置如下:
11
12```
13{
14    "product_name": "yangfan",---产品名:yangfan
15    "device_company": "rockchip",---单板厂商:rockchip
16    "device_build_path": "device/board/isoftstone/yangfan",---设备构建路径:device/board/isoftstone/yangfan
17    "target_cpu": "arm",---目标cpu:arm
18    "type": "standard",---配置系统的级别:standard
19    "version": "3.0",---版本:3.0
20    "board": "yangfan",---单板名:yangfan
21    "enable_ramdisk": true,---启用内存虚拟盘:true
22    "build_selinux": true,---构建selinux:true
23    "inherit": [ "productdefine/common/inherit/rich.json", "productdefine/common/inherit/chipset_common.json" ],
24    "subsystems": [
25    {
26      "subsystem": "security",
27      "components": [
28        {
29          "component": "selinux",
30          "features": []
31        }
32      ]
33    },
34    {
35      "subsystem": "communication",
36      "components": [
37        {
38          "component": "netmanager_ext",
39          "features": []
40        }
41      ]
42    },
43    ...
44}
45```
46
47
48主要的配置内容包括:
49
501. "product_name": "yangfan",---产品名:yangfan
512. "device_company": "rockchip",---单板厂商:rockchip
523. "device_build_path": "device/board/isoftstone/yangfan",---设备构建路径:device/board/isoftstone/yangfan
534. "target_cpu": "arm",---目标cpu:arm
545. "type": "standard",---配置系统的级别:standard
556. "version": "3.0",---版本:3.0
567. "board": "yangfan",---单板名:yangfan
578. "enable_ramdisk": true,---启用内存虚拟盘:true
58
59已定义的子系统可以在`//build/subsystem_config.json`中找到。当然也可以定制子系统。
60
61建议先拷贝Hi3516DV300开发板的配置文件,删除掉hisilicon_products子系统。该子系统为Hi3516DV300 SOC编译内核,不适合RK3568。
62
63### 目录规划
64
65参考[Board和SoC解耦的设计思路](https://gitee.com/openharmony-sig/sig-content/blob/master/devboard/docs/board-soc-arch-design.md),并把芯片适配目录规划为:
66
67```
68device
69├── board                                --- 单板厂商目录
70│   └── isoftstone                       --- 单板厂商名字:
71│       └── yangfan                      --- 单板名:扬帆,主要放置开发板相关的驱动业务代码
72└── soc									 --- SoC厂商目录
73    └── rockchip                         --- SoC厂商名字:rockchip
74        └── rk3399						 --- SoC Series名:rk3399,主要为芯片原厂提供的一些方案,以及闭源库等
75
76
77```
78产品样例目录规划为:
79```
80vendor
81└── isoftstone
82    └── yangfan         			         --- 产品名字:产品、hcs以及demo相关
83```
84
85## **内核启动**
86
87### 二级启动
88
89二级启动简单来说就是将之前直接挂载system,从system下的init启动,改成先挂载ramdsik,从ramdsik中的init 启动,做些必要的初始化动作,如挂载system,vendor等分区,然后切到system下的init 。
90
91RK3399适配主要是将主线编译出来的ramdisk 打包到boot_linux.img中,主要有以下工作:
92
931. 使能二级启动
94
95在//vendor/yangfan/rk3399.json中使能enable_ramdisk。
96
97  ```
98  {
99      "product_name": "yangfan",
100      "device_company": "rockchip",
101      "device_build_path": "device/board/isoftstone/yangfan",
102      "target_cpu": "arm",
103      "type": "standard",
104      "version": "3.0",
105      "board": "yangfan",
106      "enable_ramdisk": true,
107      "build_selinux": true,
108      ...
109  }
110  ```
111
1122. 将主线编译出来的ramdsik.img 打包到boot_linux.img
113
114配置:
115
116由于rk 启动uboot 支持从ramdisk 启动,只需要在打包boot_linux.img 的配置文件中增加ramdisk.img ,因此没有使用主线的its格式,具体配置就是在内核编译脚本make-ohos.sh 中增加:
117
118```
119function make_extlinux_conf()
120{
121	dtb_path=$1
122	uart=$2
123	image=$3
124
125	echo "label rockchip-kernel-5.10" > ${EXTLINUX_CONF}
126	echo "	kernel /extlinux/${image}" >> ${EXTLINUX_CONF}
127	echo "	fdt /extlinux/${TOYBRICK_DTB}" >> ${EXTLINUX_CONF}
128	if [ "enable_ramdisk" == "${ramdisk_flag}" ]; then
129		echo "	initrd /extlinux/ramdisk.img" >> ${EXTLINUX_CONF}
130	fi
131	cmdline="append earlycon=uart8250,mmio32,${uart} root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4"
132	echo "  ${cmdline}" >> ${EXTLINUX_CONF}
133}
134```
135
136### 打包
137
138增加了打包boot镜像的脚本make-boot.sh,供编译完ramdisk,打包boot 镜像时调用,主要内容:
139
140```
141genext2fs -B ${blocks} -b ${block_size} -d boot_linux -i 8192 -U boot_linux.img
142```
143
144调用make-boot.sh的修改请参考[RK3568 适配二级启动]( https://gitee.com/openharmony/build/pulls/569/files )。
145
146### INIT配置
147
148init相关配置请参考[启动恢复子系统]( https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E5%90%AF%E5%8A%A8%E6%81%A2%E5%A4%8D%E5%AD%90%E7%B3%BB%E7%BB%9F.md )即可
149
150## **音频**
151
152### 简介
153
154本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Audio驱动框架,包括Audio驱动的架构组成、功能部件的实现和服务节点详细介绍。
155
156![Audio驱动框架图](figures/isoftstone/yangfan-Audio-ADM.png)
157
1581. ADM(Audio Driver Model)
159
160   音频驱动框架模型,向上服务于多媒体音频子系统,便于系统开发者能够更便捷的根据场景来开发应用。向下服务于具体的设备厂商,对于Codec和DSP设备厂商来说,可根据ADM模块提供的向下统一接口适配各自的驱动代码,就可以实现快速开发和适配HOS系统。
161
1622. Audio Control Dispatch
163
164   接收lib层的控制指令并将控制指令分发到驱动层。
165
1663. Audio Stream Dispatch
167
168   向上通过lib层完成数据流的接收,向下完成数据流对驱动层的分发。
169
1704. Card Manager
171
172   多声卡管理模块。每个声卡含有Dai、Platform、Codec、Accessory、Dsp、Sapm模块。
173
1745. Platform Driver
175
176   驱动适配层。
177
1786. SAPM(Smart Audio Power Manager)
179
180   电源管理模块,对整个ADM电源进行功耗策略优化。
181
182### Audio驱动介绍
183
184#### 代码目录
185
186```
187drivers
188	├── framework
189	│	└── model
190	│	│	└── audio					#框架代码
191	│	│		├─── common				#公共实现
192	│	│		├─── core				#核心
193	│	│		├─── dispatch			#控制流和数据流实现
194	│	│		└── sapm				#电源管理
195	│	└── include
196	│		└── audio					#对外接口
197	├── adapter
198    │	└──khdf
199	│		└── linux
200	│			└── model
201	│				└── audio			#编译文件
202	└── peripheral
203		└── audio
204			└── chipsets
205				└── rk3399				#驱动实现
206					├── accessory		#SmartPA驱动
207					├── dai				#I2S驱动
208					└── soc				#Dma驱动
209```
210
211#### Audio流程说明
212
213##### 启动流程
214
215![](figures/isoftstone/yangfan-Audio-start.png)
216
2171. 系统启动时audio模块的Platform、Codec、Accessory、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
2182. 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
2193. ADM模块读取hdf_audio_driver_0(音频card_0)和hdf_audio_driver_1(音频card_1)配置信息,加载各模块的具体设备。
2204. ADM模块调用各模块的初始化函数对各模块设备进行初始化。
2215. 将初始化成功的音频设备添加到cardManager链表。
222
223##### 播放流程
224
225![](figures/isoftstone/yangfan-Audio-play.png)
226
2271. 播放音频,首先Interface Lib层通过播放流服务下发Render Open指令,Render Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。
2282. Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。
2293. Interface Lib层通过播放流服务下发硬件参数,Render Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。
2304. Interface Lib层通过播放流服务下发播放启动指令,Render Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。
2315. Interface Lib层通过播放流服务下发音频数据,Render Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。
2326. Interface Lib层通过播放流服务下发播放停止指令,Render Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。
2337. Interface Lib层通过播放流服务下发Render Close指令,Render Stream Dispatch服务收到指令后调用Platform AudioRenderClose接口对已申请资源进行释放。
234
235##### 控制流程
236
237![](figures/isoftstone/yangfan-Audio-commond.png)
238
2391. 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Get函数接口获取可设置音量范围。
2402. Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析并调用Codec模块Set函数接口设置音量。
241
242#### 实现说明
243
2441. 驱动注册
245
246   以codec的注册函数为例,当codec驱动初始化时调用如下codec注册函数,将codec注册到codecController链表中。
247
248   ```c
249   int32_t AudioRegisterCodec(struct HdfDeviceObject *device, struct CodecData *codecData, struct DaiData *daiData)
250   {
251   ...
252
253       codec = (struct CodecDevice *)OsalMemCalloc(sizeof(*codec));
254   ...
255
256       OsalMutexInit(&codec->mutex);
257       codec->devCodecName = codecData->drvCodecName;
258       codec->devData = codecData;
259       codec->device = device;
260
261       ret = AudioSocRegisterDai(device, daiData);
262   ...
263       DListInsertHead(&codec->list, &codecController);
264   ...
265   }
266   ```
267
2682. 数据流数据分发
269
270   当录音或者播放时,上层lib层通过dispatch将数据下发或读取数据,此接口接收到lib层的请求后,将数据进行分发或将数据返回。
271
272   ```c
273   static int32_t StreamDispatch(struct HdfDeviceIoClient *client, int cmdId,
274       struct HdfSBuf *data, struct HdfSBuf *reply)
275   {
276       unsigned int count = sizeof(g_streamDispCmdHandle) / sizeof(g_streamDispCmdHandle[0]);
277       for (unsigned int i = 0; i < count; ++i) {
278           if ((cmdId == (int)(g_streamDispCmdHandle[i].cmd)) && (g_streamDispCmdHandle[i].func != NULL)) {
279               return g_streamDispCmdHandle[i].func(client, data, reply);
280           }
281       }
282       ADM_LOG_ERR("invalid [cmdId=%d]", cmdId);
283       return HDF_FAILURE;
284   }
285   ```
286
2873. 控制功能注册接口
288
289   音量控制、增益控制、通路控制等控制功能都是通过此接口添加到声卡控制列表。
290
291   ```c
292   int32_t AudioAddControls(struct AudioCard *audioCard, const struct AudioKcontrol *controls, int32_t controlMaxNum)
293   {
294   ...
295
296       for (i = 0; i < controlMaxNum; i++) {
297           control = AudioAddControl(audioCard, &controls[i]);
298           if (control == NULL) {
299               ADM_LOG_ERR("Add control fail!");
300               return HDF_FAILURE;
301           }
302           DListInsertHead(&control->list, &audioCard->controls);
303       }
304       ADM_LOG_DEBUG("Success.");
305       return HDF_SUCCESS;
306   }
307   ```
308
3094. 电源管理接口
310
311   添加组件实现:
312
313   ```c
314   int32_t AudioSapmNewComponents(struct AudioCard *audioCard,
315       const struct AudioSapmComponent *component, int32_t cptMaxNum)
316   {
317   ...
318
319       for (i = 0; i < cptMaxNum; i++) {
320           ret = AudioSapmNewComponent(audioCard, component);
321           if (ret != HDF_SUCCESS) {
322               ADM_LOG_ERR("AudioSapmNewComponent fail!");
323               return HDF_FAILURE;
324           }
325           component++;
326       }
327
328       return HDF_SUCCESS;
329   }
330
331   ```
332
333   添加通路实现:
334
335   ```c
336
337   int32_t AudioSapmAddRoutes(struct AudioCard *audioCard, const struct AudioSapmRoute *route, int32_t routeMaxNum)
338   {
339   ...
340
341       for (i = 0; i < routeMaxNum; i++) {
342           ret = AudioSapmAddRoute(audioCard, route);
343           if (ret != HDF_SUCCESS) {
344               ADM_LOG_ERR("AudioSapmAddRoute failed!");
345               return HDF_FAILURE;
346           }
347           route++;
348       }
349       return HDF_SUCCESS;
350   }
351
352   ```
353
354   添加控制功能实现:
355
356   ```c
357
358   int32_t AudioSapmNewControls(struct AudioCard *audioCard)
359   {
360   ...
361
362       DLIST_FOR_EACH_ENTRY(sapmComponent, &audioCard->components, struct AudioSapmComponent, list) {
363           if (sapmComponent->newCpt) {
364               continue;
365           }
366           if (sapmComponent->kcontrolsNum > 0) {
367               sapmComponent->kcontrols = OsalMemCalloc(sizeof(struct AudioKcontrol*) * sapmComponent->kcontrolsNum);
368               if (sapmComponent->kcontrols == NULL) {
369                   ADM_LOG_ERR("malloc kcontrols fail!");
370                   return HDF_FAILURE;
371               }
372           }
373
374           switch (sapmComponent->sapmType) {
375               case AUDIO_SAPM_ANALOG_SWITCH:
376               case AUDIO_SAPM_MIXER:
377               case AUDIO_SAPM_MIXER_NAMED_CTRL:
378               case AUDIO_SAPM_SPK:
379               case AUDIO_SAPM_PGA:
380                   ret = AudioSapmNewMixerControls(sapmComponent, audioCard);
381                   break;
382               case AUDIO_SAPM_MUX:
383               case AUDIO_SAPM_VIRT_MUX:
384               case AUDIO_SAPM_VALUE_MUX:
385                   ret = AudioSapmNewMuxControls(sapmComponent, audioCard);
386                   break;
387               default:
388                   ret = HDF_SUCCESS;
389                   break;
390           }
391   ...
392
393           ReadInitComponentPowerStatus(sapmComponent);
394           sapmComponent->newCpt = 1;
395           DListInsertTail(&sapmComponent->dirty, &audioCard->sapmDirty);
396       }
397
398       ret = AudioSapmPowerComponents(audioCard);
399   ...
400
401       return HDF_SUCCESS;
402   }
403
404   ```
405
4065. 控制流数据分发
407
408   当录音或者播放时,上层lib层通过dispatch将控制指令下发,此接口接收到lib层的控制指令后,将控制指令分发到各驱动模块。
409
410   ```c
411   static int32_t ControlDispatch(struct HdfDeviceIoClient *client, int cmdId,
412       struct HdfSBuf *data, struct HdfSBuf *reply)
413   {
414   ...
415
416       if (cmdId >= AUDIODRV_CTRL_IOCTRL_ELEM_BUTT || cmdId < 0) {
417           ADM_LOG_ERR("Invalid [cmdId=%d].", cmdId);
418           return HDF_FAILURE;
419       }
420
421       for (i = 0; i < HDF_ARRAY_SIZE(g_controlDispCmdHandle); ++i) {
422           if ((cmdId == (int)(g_controlDispCmdHandle[i].cmd)) && (g_controlDispCmdHandle[i].func != NULL)) {
423               return g_controlDispCmdHandle[i].func(client, data, reply);
424           }
425       }
426       return HDF_FAILURE;
427   }
428   ```
429
430### Audio服务介绍
431
432#### 服务节点
433
434基于ADM框架的audio驱动对HDI层提供三个服务hdf_audio_render、hdf_audio_capture、hdf_audio_control。
435开发板audio驱动服务节点如下:
436
437```
438console:/dev # ls -al hdf_audio_*
439crw------- 1 system system 249,   5 1970-01-01 00:21 hdf_audio_capture  //录音数据流服务。
440crw------- 1 system system 249,   3 1970-01-01 00:21 hdf_audio_codec_dev0  //音频设备名称。
441crw------- 1 system system 249,   4 1970-01-01 00:21 hdf_audio_control  //音频控制流服务。
442crw------- 1 system system 249,   6 1970-01-01 00:21 hdf_audio_render  //播放数据流务。
443```
444
4451. 音频控制流服务
446
447   用来接收上层lib层下发的控制指令,包括音量控制、增益控制、通路控制,这些控制指令都是通过控制流服务下发到驱动。
448
4492. 音频数据播放流服务
450
451   用来接收上层lib层下发的音频数据和播放相关的参数,还有播放的启动、暂停、恢复、停止指令,这些指令都是由播放数据流下发到驱动。
452
4533. 音频数据录音流服务
454
455   用来向上层lib层传输音频数据和接收上层lib层下发的录音相关的参数,还有录音的启动、暂停、恢复、停止指令,这些指令都是由录音数据流下发到驱动。
456
457#### 驱动服务
458
459每个audio设备包括如下服务:
460
461| **hdf_audio_codec_dev0** | **音频设备名称**       |
462| ------------------------ | ---------------------- |
463| dma_service_0            | dma 驱动服务           |
464| dai_service              | cpu dai 驱动服务       |
465| codec_service_0          | codec 驱动服务         |
466| dsp_service_0            | dsp 驱动服务(可选项) |
467
468| **hdf_audio_codec_dev1** | **音频设备名称**                  |
469| ------------------------ | --------------------------------- |
470| dma_service_0            | dma 驱动服务                      |
471| dai_service              | cpu dai 驱动服务                  |
472| codec_service_1          | accessory 驱动服务(特指smartPA) |
473| dsp_service_0            | dsp 驱动服务(可选项)            |
474
475#### 代码路径
476
477```
478vendor/rockchip/rk3399/hdf_config/khdf
479├── audio 							#audio私有配置文件
480├── device_info
481|	└── device_info.hcs				#设备配置文件
482└── hdf.hcs							#引用hcs配置文件
483```
484
485#### 配置节点说明
486
487以codec驱动为例,在device_info.hcs文件中的audio host节点下添加codec节点信息。
488
489```
490        audio :: host {
491            hostName = "audio_host";
492            priority = 60;
493...
494            device_codec :: device {
495                device0 :: deviceNode {
496                    policy = 1;
497                    priority = 50;
498                    preload = 0;
499                    permission = 0666;
500                    moduleName = "CODEC_ES8316";
501                    serviceName = "codec_service_0";
502                    deviceMatchAttr = "hdf_codec_driver";
503                }
504            }
505...
506        }
507```
508
509#### 实现驱动
510
511在驱动文件中实现与device_info.hcs配置节点moduleName相同的驱动逻辑。
512
513```
514/* HdfDriverEntry implementations */
515static int32_t Es8316DriverBind(struct HdfDeviceObject *device)
516{
517...
518    return HDF_SUCCESS;
519}
520
521static int32_t Es8316DriverInit(struct HdfDeviceObject *device)
522{
523...
524    return HDF_SUCCESS;
525}
526
527/* HdfDriverEntry definitions */
528struct HdfDriverEntry g_es8316DriverEntry = {
529    .moduleVersion = 1,
530    .moduleName = "CODEC_ES8316",
531    .Bind = Es8316DriverBind,
532    .Init = Es8316DriverInit,
533    .Release = NULL,
534};
535HDF_INIT(g_es8316DriverEntry);
536```
537
538### 总结
539
540​		基于HDF框架的ADM音频框架,为Open Harmony的音频开发提供了统一的架构基础,为各平台音频驱动适配提供了统一的接口。音频驱动可以一平台开发多平台适用,提高了开发效率。此文档对ADM框架进行了简单的介绍,希望有助于开发者开发和应用。
541
542## **Camera**
543
544### 简介
545
546本文以OpenHarmony 3.0为基础,讲解基于HDF(Hardware Driver Foundation)驱动框架开发的Camera驱动框架,包括Camera驱动的架构组成、功能部件的实现和服务节点详细介绍。
547
548### Camera驱动框架图
549
550​                                                                  OpenHarmony HDF Camera驱动模块架构图
551
552![img](https://gitee.com/openharmony/drivers_peripheral/raw/master/camera/figures/logic-view-of-modules-related-to-this-repository_zh.png)
553
554以Camera Host 部分做如下说明:
555
5561. HDI实现层(HDI Implementation):对上实现Open Harmony OS相机标准南向接口。
5572. 框架层(PipelineCore):对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能。
5583. 适配层(Platform Adaption):屏蔽底层芯片和OS差异,支持多平台适配。
559
560对于rk3399E/T的Usb Camera来分析,内核使用linux-4.19。Usb Camera依赖linux下的V4L2的uvc,从上面的框架图分析HDF Camera已经实现了兼容linux 的 V4L2 uvc,所以调试过程首先要保证uvc所涉及的USB和Camera的驱动正常。
561
562
563### Camera驱动介绍
564
565#### 配置信息
566
567arch/arm64/configs/rockchip_linux_defconfig
568
569```c
570CONFIG_VIDEO_V4L2_SUBDEV_API=y
571CONFIG_MEDIA_USB_SUPPORT=y
572CONFIG_USB_VIDEO_CLASS=y
573```
574
575#### 节点信息
576
577插入Usb Camera 前
578
579```
580# ls -l dev/video*
581crw-rw---- 1 root root 81,   0 2013-01-18 10:59 dev/video0
582crw-rw---- 1 root root 81,   1 2013-01-18 10:59 dev/video1
583crw-rw---- 1 root root 81,   2 2013-01-18 10:59 dev/video2
584crw-rw---- 1 root root 81,   3 2013-01-18 10:59 dev/video3
585crw-rw---- 1 root root 81,   4 2013-01-18 10:59 dev/video4
586crw-rw---- 1 root root 81,   5 2013-01-18 10:59 dev/video5
587crw-rw---- 1 root root 81,   6 2013-01-18 10:59 dev/video6
588crw-rw---- 1 root root 81,   7 2013-01-18 10:59 dev/video7
589crw-rw---- 1 root root 81,   8 2013-01-18 10:59 dev/video8
590crw-rw---- 1 root root 81,   9 2013-01-18 10:59 dev/video9
591#
592```
593
594插入Usb Camera后新增节点dev/video10dev/video11
595
596```
597# ls -l dev/video*
598crw-rw---- 1 root root 81,   0 2013-01-18 10:59 dev/video0
599crw-rw---- 1 root root 81,   1 2013-01-18 10:59 dev/video1
600crw------- 1 root root 81,  10 2013-01-18 11:01 dev/video10
601crw------- 1 root root 81,  11 2013-01-18 11:01 dev/video11
602crw-rw---- 1 root root 81,   2 2013-01-18 10:59 dev/video2
603crw-rw---- 1 root root 81,   3 2013-01-18 10:59 dev/video3
604crw-rw---- 1 root root 81,   4 2013-01-18 10:59 dev/video4
605crw-rw---- 1 root root 81,   5 2013-01-18 10:59 dev/video5
606crw-rw---- 1 root root 81,   6 2013-01-18 10:59 dev/video6
607crw-rw---- 1 root root 81,   7 2013-01-18 10:59 dev/video7
608crw-rw---- 1 root root 81,   8 2013-01-18 10:59 dev/video8
609crw-rw---- 1 root root 81,   9 2013-01-18 10:59 dev/video9
610#
611```
612
613#### 打开设备节点
614
615在Open Harmony OS的代码环境中,编译如下代码为可执行程序,在开发板测执行,无报错说明该节点open success。
616
617```c
618#include <stdio.h>
619#include <sys/types.h>
620#include <sys/stat.h>
621#include <fcntl.h>
622#include <stdlib.h>
623#include <unistd.h>
624#include <sys/ioctl.h>
625#include <linux/videodev2.h>
626#include <string.h>
627#include <sys/mman.h>
628
629int main(void)
630{
631	// 1. 打开设备
632	int fd = open("/dev/video10", O_RDWR);
633	if (fd < 0) {
634		printf("open device fail\n");
635		return -1;
636	}
637
638    close(fd);
639    return 0;
640}
641```
642
643#### 获取参数
644
645```c
646#include <stdio.h>
647#include <sys/types.h>
648#include <sys/stat.h>
649#include <fcntl.h>
650#include <stdlib.h>
651#include <unistd.h>
652#include <sys/ioctl.h>
653#include <linux/videodev2.h>
654#include <string.h>
655#include <sys/mman.h>
656
657int main(void)
658{
659	// 1. 打开设备
660	int fd = open("/dev/video10", O_RDWR);
661	if (fd < 0) {
662		printf("open device fail\n");
663		return -1;
664	}
665
666	// 2. 获取摄像头支持的格式 ioctl(文件描述符, 命令, 与命令对应的结构体)
667    struct v4l2_fmtdesc v4fmt;
668	v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
669    int i = 0;
670    while(1) {
671		v4fmt.index = i++;
672		int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
673		if (ret < 0) {
674			printf("get fmt fail\n");
675		}
676		unsigned char *p = (unsigned char*)&v4fmt.pixelformat;
677		printf("index=%d\n", v4fmt.index);
678		printf("flags=%d\n", v4fmt.flags);
679		printf("description=%s\n", v4fmt.description);
680		printf("pixelformat=%c%c%c%c\n", p[0], p[1], p[2], p[3]);
681		printf("reserved=%d\n", v4fmt.reserved[0]);
682	}
683
684    close(fd);
685    return 0;
686}
687```
688
689在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。结果显示支持YUYV和MJPEG 2种输出格式。
690
691		index=0
692		flags=0
693		description=YUYV 4:2:2
694		pixelformat=YUYV
695		reserved=0
696
697		index=1
698		flags=1
699		description=Motion-JPEG
700		pixelformat=MJPG
701		reserved=0
702#### 设置缓冲区队列
703
704```c
705#include <stdio.h>
706#include <sys/types.h>
707#include <sys/stat.h>
708#include <fcntl.h>
709#include <stdlib.h>
710#include <unistd.h>
711#include <sys/ioctl.h>
712#include <linux/videodev2.h>
713#include <string.h>
714#include <sys/mman.h>
715
716int main(void)
717{
718	// 1. 打开设备
719	int fd = open("/dev/video10", O_RDWR);
720	if (fd < 0) {
721		printf("open device fail\n");
722		return -1;
723	}
724
725	// 2. 设置采集格式
726	struct v4l2_format vfmt;
727	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
728	vfmt.fmt.pix.width = 640;
729	vfmt.fmt.pix.height = 480;
730	vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 设置的视频采集格式(与上面获取的格式一致)
731	int ret = ioctl(fd, VIDIOC_S_FMT, &vfmt); // 设置格式
732	if (ret < 0) {
733		printf("set fmt fail\n");
734        return -1;
735	}
736	memset(&vfmt, 0, sizeof(vfmt));
737	vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
738	ret = ioctl(fd, VIDIOC_G_FMT, &vfmt); // 获取格式
739	if (ret < 0) {
740		printf("set->get fmt fail\n");
741        return -1;
742	}
743
744    // 3. 申请内核缓冲区队列
745    struct v4l2_requestbuffers reqbuffer;
746    reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
747    reqbuffer.count = 4; // 申请4个缓冲区
748    reqbuffer.memory = V4L2_MEMORY_MMAP; // 内存映射方式 MMAP/USERPTR
749    ret = ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); // 分配内存
750    if (ret < 0) {
751        printf("req buffer fail\n");
752        return -1;
753    }
754
755    // 4. 关闭设备
756    close(fd);
757    return 0;
758}
759```
760
761在Open Harmony OS的代码环境中,编译如上代码为可执行程序,在开发板测执行。
762
763执行结果:req buffer fail
764
765原因分析:ioctl(fd, VIDIOC_REQBUFS, &reqbuffer); 失败
766
767定位方法1:在内核中加LOG定位VIDIOC_REQBUFS失败的地方。发现所有的ioctl命令下发后都会使用drivers/media/v4l2-core/v4l2-ioctl.c中的video_usercopy接口,但还是没有定位到具体的失败原因。
768
769定位方法2:求助视美泰验证linux-4.19内核debian版本的Usb Camera是否OK。结果:debian版本使用gst-launch-1.0 v4l2src device=/dev/video10 ! image/jpeg, width= 1280, height=720, framerate=30/1 ! jpegparse ! mppjpegdec ! kmssink sync=false命令后HDMI屏幕可以出来正常的预览画面。
770
771通过如上的操作后,基本可以确认linux的V4L2 uvc驱动和外设Usb Camera驱动都是正常的。接下来就该调试Open Harmony OS的HDF Camera了。
772
773#### 接口介绍
774
775查看现有Open Harmony OS上的关于camera的可执行程序:ohos_camera_demo、v4l2_main
776
777##### ohos_camera_demo
778
779执行结果:输入o后无预览画面,也无LOG报错信息。
780
781```
782# ohos_camera_demo
783GetUintParameter debug.bytrace.tags.enableflags error.
784Options:
785-h | --help          Print this message
786-o | --offline       stream offline test
787-c | --capture       capture one picture
788-w | --set WB        Set white balance Cloudy
789-v | --video         capture Viedeo of 10s
790-a | --Set AE        Set Auto exposure
791-f | --Set Flashlight        Set flashlight ON 5s OFF
792-q | --quit          stop preview and quit this app
793o
794Options:
795-h | --help          Print this message
796-o | --offline       stream offline test
797-c | --capture       capture one picture
798-w | --set WB        Set white balance Cloudy
799-v | --video         capture Viedeo of 10s
800-a | --Set AE        Set Auto exposure
801-f | --Set Flashlight        Set flashlight ON 5s OFF
802-q | --quit          stop preview and quit this app
803
804```
805
806原因分析:ohos_camera_demo目前仅支持MPP,不支持V4L2,故先放弃该demo调试。
807
808##### v4l2_main
809
810执行结果:输入u 报错:ERROR:main test:cannot open framebuffer /dev/fb0 file node
811
812```
813Options:
814-h | --help          Print this message
815-p | --preview       start preview on platform sensor
816-c | --capture       capture one picture
817-w | --set WB        Set white balance Cloudy
818-e | --Set AE        Set exposure time
819-v | --video         capture Viedeo of 10s
820-u | --uvc           start preview on uvc preview
821-a | --Set ATE       Set Auto exposure
822-q | --quit          stop preview and quit this app
823INFO:please input command(input -q exit this app)
824
825u
826ERROR:main test:cannot open framebuffer /dev/fb0 file node
827
828INFO:V4L2OpenDevice /dev/video10
829```
830
831原因分析:查看dev/fb0节点是否存在:不存在fb0节点。继续查找根目录下有无其他fb0节点,出现了dev/graphics/fb0节点。
832
833```
834# ls -l dev/fb0
835ls: dev/fb0: No such file or directory
836
837# find -name fb0
838./dev/graphics/fb0
839./sys/class/graphics/fb0
840./sys/devices/platform/display-subsystem/graphics/fb0
841```
842
843需把v4l2_main可执行程序中的dev/fb0改为dev/graphics/fb0。此处fb0为framebuffer,作用是在屏幕上显示预览画面。
844
845修改点:drivers/peripheral
846
847```diff
848diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
849index b351f49..d9c4cb3 100755
850--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
851+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
852@@ -186,9 +186,9 @@ RetCode FBInit()
853     if (g_fbFd)
854         return RC_OK;
855
856-    g_fbFd = open("/dev/fb0", O_RDWR);
857+    g_fbFd = open("/dev/graphics/fb0", O_RDWR);
858     if (g_fbFd < 0) {
859-        CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0");
860+        CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0");
861         return RC_ERROR;
862     }
863
864diff --git a/camera/hal/test/v4l2/src/test_display.cpp b/camera/hal/test/v4l2/src/test_display.cpp
865index db908e7..7025deb 100644
866--- a/camera/hal/test/v4l2/src/test_display.cpp
867+++ b/camera/hal/test/v4l2/src/test_display.cpp
868@@ -114,9 +114,9 @@ void TestDisplay::FBLog()
869
870 RetCode TestDisplay::FBInit()
871 {
872-    fbFd_ = open("/dev/fb0", O_RDWR);
873+    fbFd_ = open("/dev/graphics/fb0", O_RDWR);
874     if (fbFd_ < 0) {
875-        CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/fb0");
876+        CAMERA_LOGE("main test:cannot open framebuffer %s file node\n", "/dev/graphics/fb0");
877         return RC_ERROR;
878     }
879
880@@ -439,4 +439,4 @@ void TestDisplay::StopStream(std::vector<int>& captureIds, std::vector<int>& str
881             std::cout << "==========[test log]check Capture: ReleaseStreams fail, rc = " << rc << std::endl;
882         }
883     }
884-}
885\ No newline at end of file
886+}
887diff --git a/display/hal/default/display_layer.c b/display/hal/default/display_layer.c
888index ee7a825..e12a653 100644
889--- a/display/hal/default/display_layer.c
890+++ b/display/hal/default/display_layer.c
891@@ -24,7 +24,7 @@
892
893 #define DEV_ID             0
894 #define LAYER_ID           0
895-#define FB_PATH            "/dev/fb0"
896+#define FB_PATH            "/dev/graphics/fb0"
897 #define DISP_WIDTH         800
898 #define DISP_HEIGHT        480
899 #define BITS_PER_PIXEL     32
900```
901
902修改后重新编译v4l2_main,编译命令:./build.sh --product-name rk3399 --ccache --build-target v4l2_main
903
904编译成功后可执行程序路径:./out/rk3399/hdf/hdf/v4l2_main
905
906将新编译的v4l2_main推送到开发板测的system/bin路径下:
907
908```
909hdc shell "mount -o rw,remount /"
910hdc file send D:\cyyanl\work\RockChip\bin\v4l2_main /system/bin
911```
912
913继续执行v4l2_main结果后无framebuffer报错,说明该问题已解决。(另一种思路:分析fb0为何在/dev/graphics/fb0而不是常规的/dev/fb0,然后修改为/dev/fb0。后面有时间再调试该思路)
914
915新报错:ERROR:error: ioctl VIDIOC_QUERYBUF failed.
916
917```
918Options:
919-h | --help          Print this message
920-p | --preview       start preview on platform sensor
921-c | --capture       capture one picture
922-w | --set WB        Set white balance Cloudy
923-e | --Set AE        Set exposure time
924-v | --video         capture Viedeo of 10s
925-u | --uvc           start preview on uvc preview
926-a | --Set ATE       Set Auto exposure
927-q | --quit          stop preview and quit this app
928INFO:please input command(input -q exit this app)
929u
930INFO:the fixed information is as follow:
931INFO:id=
932INFO:sem_start=0
933INFO:smem_len=2457600
934...
935INFO:V4L2AllocBuffer
936INFO:V4L2AllocBuffer:memoryType_ = 2
937INFO:V4L2AllocBuffer:V4L2_MEMORY_USERPTR = 2
938INFO:V4L2AllocBuffer:VIDIOC_QUERYBUF = 3226490377
939ERROR:error: ioctl VIDIOC_QUERYBUF failed.
940ERROR:error: Creatbuffer: V4L2AllocBuffer error
941ERROR:main test:V4L2PreviewThread CreatBuffer fail i = 0
942```
943
944原因分析:ioctl(fd, VIDIOC_QUERYBUF, &buf)失败。回过头再看"调试linux L4V2 uvc驱动章节->设置格式申请缓冲区队列"中的报错也是
945
946ioctl(fd, VIDIOC_REQBUFS, &reqbuffer)。由此分析出Open Harmony OS上的ioctl VIDIOC_REQBUFS都会报错。再看两次失败的差异点:
947
948内存映射方式不同: V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR
949
950从OpenHarmony的issuse得知暂不支持V4L2_MEMORY_MMAP内存映射,映射方式就分析到这里,接下来还是用v4l2_main的V4L2_MEMORY_USERPTR进行调试分析。
951
952参考:[V4L2设备增加MMAP申请内存的方式](https://gitee.com/openharmony/drivers_peripheral/issues/I4EFWP)和下图
953
954![](figures/isoftstone/yangfan-ioctl.png)
955
956接着再分析ioctl(fd, VIDIOC_QUERYBUF, &buf)失败,查看VIDIOC_QUERYBUF的定义:videodev2.h
957
958```c
959#define VIDIOC_QUERYBUF_IOWR('V',  9, struct v4l2_buffer)
960```
961
962此处插入ioctl的定义:int ioctl(int fd, int cmd, …); VIDIOC_QUERYBUF作为cmd的入参,是int类型。也就是一个数字命令码,该命令码通过ioctl发送给内核后,会有与之对应的函数操作,故用户态下发的命令码应和内核接受的命令码一致。下面验证命令码一致性。
963
964videodev2.h中一共有77个和内核交互的命令码,把内核态和用户态的都打印出来做对比:
965
966用户态加打印:drivers/peripheral
967
968```diff
969diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
970index d7dd15f..f7254b4 100644
971--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
972+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
973@@ -162,37 +162,119 @@ RetCode HosV4L2Buffers::V4L2DequeueBuffer(int fd)
974     return RC_OK;
975 }
976
977+static void cyyanl_printf_cmd(void)
978+{
979+#if 1
980+    CAMERA_LOGD("*************************************************************************************");
981+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP           ) = 0x%x\n", VIDIOC_QUERYCAP           );
982+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT           ) = 0x%x\n", VIDIOC_ENUM_FMT           );
983+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT              ) = 0x%x\n", VIDIOC_G_FMT              );
984+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT              ) = 0x%x\n", VIDIOC_S_FMT              );
985+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS            ) = 0x%x\n", VIDIOC_REQBUFS            );
986+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF           ) = 0x%x\n", VIDIOC_QUERYBUF           );
987+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF             ) = 0x%x\n", VIDIOC_G_FBUF             );
988+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF             ) = 0x%x\n", VIDIOC_S_FBUF             );
989+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY            ) = 0x%x\n", VIDIOC_OVERLAY            );
990+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF               ) = 0x%x\n", VIDIOC_QBUF               );
991+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF             ) = 0x%x\n", VIDIOC_EXPBUF             );
992+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF              ) = 0x%x\n", VIDIOC_DQBUF              );
993+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON           ) = 0x%x\n", VIDIOC_STREAMON           );
994+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF          ) = 0x%x\n", VIDIOC_STREAMOFF          );
995+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM             ) = 0x%x\n", VIDIOC_G_PARM             );
996+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM             ) = 0x%x\n", VIDIOC_S_PARM             );
997+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD              ) = 0x%x\n", VIDIOC_G_STD              );
998+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD              ) = 0x%x\n", VIDIOC_S_STD              );
999+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD            ) = 0x%x\n", VIDIOC_ENUMSTD            );
1000+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT          ) = 0x%x\n", VIDIOC_ENUMINPUT          );
1001+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL             ) = 0x%x\n", VIDIOC_G_CTRL             );
1002+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL             ) = 0x%x\n", VIDIOC_S_CTRL             );
1003+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER            ) = 0x%x\n", VIDIOC_G_TUNER            );
1004+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER            ) = 0x%x\n", VIDIOC_S_TUNER            );
1005+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO            ) = 0x%x\n", VIDIOC_G_AUDIO            );
1006+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO            ) = 0x%x\n", VIDIOC_S_AUDIO            );
1007+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL          ) = 0x%x\n", VIDIOC_QUERYCTRL          );
1008+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU          ) = 0x%x\n", VIDIOC_QUERYMENU          );
1009+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT            ) = 0x%x\n", VIDIOC_G_INPUT            );
1010+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT            ) = 0x%x\n", VIDIOC_S_INPUT            );
1011+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID             ) = 0x%x\n", VIDIOC_G_EDID             );
1012+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID             ) = 0x%x\n", VIDIOC_S_EDID             );
1013+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT           ) = 0x%x\n", VIDIOC_G_OUTPUT           );
1014+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT           ) = 0x%x\n", VIDIOC_S_OUTPUT           );
1015+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT         ) = 0x%x\n", VIDIOC_ENUMOUTPUT         );
1016+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT           ) = 0x%x\n", VIDIOC_G_AUDOUT           );
1017+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT           ) = 0x%x\n", VIDIOC_S_AUDOUT           );
1018+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR        ) = 0x%x\n", VIDIOC_G_MODULATOR        );
1019+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR        ) = 0x%x\n", VIDIOC_S_MODULATOR        );
1020+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY        ) = 0x%x\n", VIDIOC_G_FREQUENCY        );
1021+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY        ) = 0x%x\n", VIDIOC_S_FREQUENCY        );
1022+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP            ) = 0x%x\n", VIDIOC_CROPCAP            );
1023+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP             ) = 0x%x\n", VIDIOC_G_CROP             );
1024+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP             ) = 0x%x\n", VIDIOC_S_CROP             );
1025+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP         ) = 0x%x\n", VIDIOC_G_JPEGCOMP         );
1026+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP         ) = 0x%x\n", VIDIOC_S_JPEGCOMP         );
1027+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD           ) = 0x%x\n", VIDIOC_QUERYSTD           );
1028+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT            ) = 0x%x\n", VIDIOC_TRY_FMT            );
1029+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO          ) = 0x%x\n", VIDIOC_ENUMAUDIO          );
1030+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT         ) = 0x%x\n", VIDIOC_ENUMAUDOUT         );
1031+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY         ) = 0x%x\n", VIDIOC_G_PRIORITY         );
1032+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY         ) = 0x%x\n", VIDIOC_S_PRIORITY         );
1033+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP   ) = 0x%x\n", VIDIOC_G_SLICED_VBI_CAP   );
1034+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS         ) = 0x%x\n", VIDIOC_LOG_STATUS         );
1035+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS        ) = 0x%x\n", VIDIOC_G_EXT_CTRLS        );
1036+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS        ) = 0x%x\n", VIDIOC_S_EXT_CTRLS        );
1037+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS      ) = 0x%x\n", VIDIOC_TRY_EXT_CTRLS      );
1038+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES    ) = 0x%x\n", VIDIOC_ENUM_FRAMESIZES    );
1039+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = 0x%x\n", VIDIOC_ENUM_FRAMEINTERVALS);
1040+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX        ) = 0x%x\n", VIDIOC_G_ENC_INDEX        );
1041+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD        ) = 0x%x\n", VIDIOC_ENCODER_CMD        );
1042+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD    ) = 0x%x\n", VIDIOC_TRY_ENCODER_CMD    );
1043+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK     ) = 0x%x\n", VIDIOC_S_HW_FREQ_SEEK     );
1044+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS        ) = 0x%x\n", VIDIOC_CREATE_BUFS        );
1045+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF        ) = 0x%x\n", VIDIOC_PREPARE_BUF        );
1046+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION        ) = 0x%x\n", VIDIOC_G_SELECTION        );
1047+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION        ) = 0x%x\n", VIDIOC_S_SELECTION        );
1048+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD        ) = 0x%x\n", VIDIOC_DECODER_CMD        );
1049+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD    ) = 0x%x\n", VIDIOC_TRY_DECODER_CMD    );
1050+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS    ) = 0x%x\n", VIDIOC_ENUM_DV_TIMINGS    );
1051+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS   ) = 0x%x\n", VIDIOC_QUERY_DV_TIMINGS   );
1052+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP     ) = 0x%x\n", VIDIOC_DV_TIMINGS_CAP     );
1053+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS    ) = 0x%x\n", VIDIOC_ENUM_FREQ_BANDS    );
1054+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO    ) = 0x%x\n", VIDIOC_DBG_G_CHIP_INFO    );
1055+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL     ) = 0x%x\n", VIDIOC_QUERY_EXT_CTRL     );
1056+    CAMERA_LOGD("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE       ) = 0x%x\n", BASE_VIDIOC_PRIVATE       );
1057+    CAMERA_LOGD("*************************************************************************************");
1058+#endif
1059+}
1060+
1061 RetCode HosV4L2Buffers::V4L2AllocBuffer(int fd, const std::shared_ptr<FrameSpec>& frameSpec)
1062 {
1063     struct v4l2_buffer buf = {};
1064     struct v4l2_plane planes[1] = {};
1065-    CAMERA_LOGD("V4L2AllocBuffer\n");
1066+    CAMERA_LOGD("V4L2AllocBuffer enter\n");
1067
1068+    cyyanl_printf_cmd();
1069     if (frameSpec == nullptr) {
1070         CAMERA_LOGE("V4L2AllocBuffer frameSpec is NULL\n");
1071         return RC_ERROR;
1072     }
1073-
1074     switch (memoryType_) {
1075         case V4L2_MEMORY_MMAP:
1076             // to do something
1077             break;
1078         case V4L2_MEMORY_USERPTR:
1079+            CAMERA_LOGD("V4L2AllocBuffer:V4L2_MEMORY_USERPTR = %d\n", V4L2_MEMORY_USERPTR);
1080             buf.type = bufferType_;
1081             buf.memory = memoryType_;
1082             buf.index = (uint32_t)frameSpec->buffer_->GetIndex();
1083-
1084             if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
1085+                CAMERA_LOGD("V4L2AllocBuffer:V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = %d\n", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
1086                 buf.m.planes = planes;
1087                 buf.length = 1;
1088             }
1089```
1090
1091内核态打印:kernel/linux/linux-4.19/
1092
1093```diff
1094diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
1095index 0842a47c6..8aa60407f 100644
1096--- a/drivers/media/usb/uvc/uvc_driver.c
1097+++ b/drivers/media/usb/uvc/uvc_driver.c
1098@@ -2902,10 +2902,93 @@ struct uvc_driver uvc_driver = {
1099 	},
1100 };
1101
1102+static void cyyanl_printk_cmd(void)
1103+{
1104+    printk("*************************************************************************************");
1105+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCAP           ) = %ld\n", VIDIOC_QUERYCAP           );
1106+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FMT           ) = %ld\n", VIDIOC_ENUM_FMT           );
1107+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FMT              ) = %ld\n", VIDIOC_G_FMT              );
1108+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FMT              ) = %ld\n", VIDIOC_S_FMT              );
1109+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_REQBUFS            ) = %ld\n", VIDIOC_REQBUFS            );
1110+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYBUF           ) = %ld\n", VIDIOC_QUERYBUF           );
1111+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FBUF             ) = %ld\n", VIDIOC_G_FBUF             );
1112+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FBUF             ) = %ld\n", VIDIOC_S_FBUF             );
1113+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_OVERLAY            ) = %ld\n", VIDIOC_OVERLAY            );
1114+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QBUF               ) = %ld\n", VIDIOC_QBUF               );
1115+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_EXPBUF             ) = %ld\n", VIDIOC_EXPBUF             );
1116+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_DQBUF              ) = %ld\n", VIDIOC_DQBUF              );
1117+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMON           ) = %ld\n", VIDIOC_STREAMON           );
1118+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_STREAMOFF          ) = %ld\n", VIDIOC_STREAMOFF          );
1119+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PARM             ) = %ld\n", VIDIOC_G_PARM             );
1120+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PARM             ) = %ld\n", VIDIOC_S_PARM             );
1121+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_STD              ) = %ld\n", VIDIOC_G_STD              );
1122+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_STD              ) = %ld\n", VIDIOC_S_STD              );
1123+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMSTD            ) = %ld\n", VIDIOC_ENUMSTD            );
1124+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMINPUT          ) = %ld\n", VIDIOC_ENUMINPUT          );
1125+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CTRL             ) = %ld\n", VIDIOC_G_CTRL             );
1126+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CTRL             ) = %ld\n", VIDIOC_S_CTRL             );
1127+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_TUNER            ) = %ld\n", VIDIOC_G_TUNER            );
1128+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_TUNER            ) = %ld\n", VIDIOC_S_TUNER            );
1129+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDIO            ) = %ld\n", VIDIOC_G_AUDIO            );
1130+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDIO            ) = %ld\n", VIDIOC_S_AUDIO            );
1131+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYCTRL          ) = %ld\n", VIDIOC_QUERYCTRL          );
1132+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYMENU          ) = %ld\n", VIDIOC_QUERYMENU          );
1133+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_INPUT            ) = %ld\n", VIDIOC_G_INPUT            );
1134+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_INPUT            ) = %ld\n", VIDIOC_S_INPUT            );
1135+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EDID             ) = %ld\n", VIDIOC_G_EDID             );
1136+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EDID             ) = %ld\n", VIDIOC_S_EDID             );
1137+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_OUTPUT           ) = %ld\n", VIDIOC_G_OUTPUT           );
1138+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_OUTPUT           ) = %ld\n", VIDIOC_S_OUTPUT           );
1139+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMOUTPUT         ) = %ld\n", VIDIOC_ENUMOUTPUT         );
1140+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_AUDOUT           ) = %ld\n", VIDIOC_G_AUDOUT           );
1141+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_AUDOUT           ) = %ld\n", VIDIOC_S_AUDOUT           );
1142+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_MODULATOR        ) = %ld\n", VIDIOC_G_MODULATOR        );
1143+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_MODULATOR        ) = %ld\n", VIDIOC_S_MODULATOR        );
1144+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_FREQUENCY        ) = %ld\n", VIDIOC_G_FREQUENCY        );
1145+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_FREQUENCY        ) = %ld\n", VIDIOC_S_FREQUENCY        );
1146+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_CROPCAP            ) = %ld\n", VIDIOC_CROPCAP            );
1147+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_CROP             ) = %ld\n", VIDIOC_G_CROP             );
1148+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_CROP             ) = %ld\n", VIDIOC_S_CROP             );
1149+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_JPEGCOMP         ) = %ld\n", VIDIOC_G_JPEGCOMP         );
1150+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_JPEGCOMP         ) = %ld\n", VIDIOC_S_JPEGCOMP         );
1151+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERYSTD           ) = %ld\n", VIDIOC_QUERYSTD           );
1152+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_FMT            ) = %ld\n", VIDIOC_TRY_FMT            );
1153+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDIO          ) = %ld\n", VIDIOC_ENUMAUDIO          );
1154+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUMAUDOUT         ) = %ld\n", VIDIOC_ENUMAUDOUT         );
1155+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_PRIORITY         ) = %ld\n", VIDIOC_G_PRIORITY         );
1156+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_PRIORITY         ) = %ld\n", VIDIOC_S_PRIORITY         );
1157+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SLICED_VBI_CAP   ) = %ld\n", VIDIOC_G_SLICED_VBI_CAP   );
1158+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_LOG_STATUS         ) = %ld\n", VIDIOC_LOG_STATUS         );
1159+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_EXT_CTRLS        ) = %ld\n", VIDIOC_G_EXT_CTRLS        );
1160+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_EXT_CTRLS        ) = %ld\n", VIDIOC_S_EXT_CTRLS        );
1161+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_EXT_CTRLS      ) = %ld\n", VIDIOC_TRY_EXT_CTRLS      );
1162+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMESIZES    ) = %ld\n", VIDIOC_ENUM_FRAMESIZES    );
1163+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FRAMEINTERVALS) = %ld\n", VIDIOC_ENUM_FRAMEINTERVALS);
1164+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_ENC_INDEX        ) = %ld\n", VIDIOC_G_ENC_INDEX        );
1165+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENCODER_CMD        ) = %ld\n", VIDIOC_ENCODER_CMD        );
1166+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_ENCODER_CMD    ) = %ld\n", VIDIOC_TRY_ENCODER_CMD    );
1167+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_HW_FREQ_SEEK     ) = %ld\n", VIDIOC_S_HW_FREQ_SEEK     );
1168+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_CREATE_BUFS        ) = %ld\n", VIDIOC_CREATE_BUFS        );
1169+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_PREPARE_BUF        ) = %ld\n", VIDIOC_PREPARE_BUF        );
1170+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_G_SELECTION        ) = %ld\n", VIDIOC_G_SELECTION        );
1171+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_S_SELECTION        ) = %ld\n", VIDIOC_S_SELECTION        );
1172+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_DECODER_CMD        ) = %ld\n", VIDIOC_DECODER_CMD        );
1173+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_TRY_DECODER_CMD    ) = %ld\n", VIDIOC_TRY_DECODER_CMD    );
1174+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_DV_TIMINGS    ) = %ld\n", VIDIOC_ENUM_DV_TIMINGS    );
1175+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_DV_TIMINGS   ) = %ld\n", VIDIOC_QUERY_DV_TIMINGS   );
1176+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_DV_TIMINGS_CAP     ) = %ld\n", VIDIOC_DV_TIMINGS_CAP     );
1177+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_ENUM_FREQ_BANDS    ) = %ld\n", VIDIOC_ENUM_FREQ_BANDS    );
1178+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_DBG_G_CHIP_INFO    ) = %ld\n", VIDIOC_DBG_G_CHIP_INFO    );
1179+    printk("cyyanl v4l2 ioctl cmd(VIDIOC_QUERY_EXT_CTRL     ) = %ld\n", VIDIOC_QUERY_EXT_CTRL     );
1180+    printk("cyyanl v4l2 ioctl cmd(BASE_VIDIOC_PRIVATE       ) = %ld\n", BASE_VIDIOC_PRIVATE       );
1181+    printk("*************************************************************************************");
1182+}
1183+
1184 static int __init uvc_init(void)
1185 {
1186 	int ret;
1187-
1188+    printk("cyyanl enter uvc_init\n");
1189+    cyyanl_printk_cmd();
1190 	uvc_debugfs_init();
1191
1192 	ret = usb_register(&uvc_driver.driver);
1193```
1194
1195打印结果对比:VIDIOC_QUERYBUF用户态为0xc0505609,内核态为0xc0585609  还有其他几处命令码也不同。
1196
1197![](figures/isoftstone/yangfan-print-01.png)
1198
1199分析命令码不一致问题,先找出VIDIOC_QUERYBUF用户态和内核态实际编译时所定义的地方差异:
1200
1201用户态:kernel/linux/patches/linux-5.10/prebuilts/usr/include/linux/videodev2.h +1358
1202
1203内核态:kernel/linux/linux-4.19/include/uapi/linux/videodev2.h +2361
1204
1205此时怀疑VIDIOC_QUERYBUF不一致是头文件不同所导致(目前没做修改验证同改为linux-4.19是否可行)。对现有问题求助客户,并在OpenHarmony上寻找有无相似问题。
1206
1207找到了相似的issuse:https://gitee.com/openharmony/drivers_peripheral/issues/I4NI4M?from=project-issue
1208
1209![](figures/isoftstone/yangfan-picture-02.png)
1210
1211接下来,调试思路分为2路:
1212
12131. 同意用户态和内核态的VIDIOC_QUERYBUF
1214
1215   方案:把用户态的0xc0505609改为内核态的0xc0585609
1216
1217   修改点:drivers/peripheral
1218
1219   ```diff
1220   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
1221   index d7dd15f..f7254b4 100644
1222   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
1223   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp
1224                    buf.m.planes = planes;
1225                    buf.length = 1;
1226                }
1227   -            CAMERA_LOGD("V4L2_MEMORY_USERPTR Print the cnt: %{public}d\n", buf.index);
1228   -
1229   -            if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
1230   -                CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno));
1231   +            if (ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) < 0) {
1232   +                CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed.\n");
1233                    return RC_ERROR;
1234                }
1235   ```
1236
1237   再次运行v4l2_main结果:Signal 4报错
1238
1239   原因分析:此时ioctl(fd, /*VIDIOC_QUERYBUF*/0xc0585609, &buf) 已经成功了。Signal 4分析可能又是cmd命令码的问题。
1240
1241   此路虽然解决了当前问题,但会有新问题Signal 4,故先暂停。
1242
1243   ```
1244   INFO:main test:allocating display buffer memory
1245   INFO:main test:do_mmap: pmem mmap fd 5 ptr 0xf7508000 len 2457600
1246   INFO:V4L2OpenDevice /dev/video10
1247   INFO:V4L2ReqBuffers buffCont 4
1248   INFO:Creatbuffer frameSpec->buffer index == 0
1249   INFO:V4L2AllocBuffer
1250   Signal 4
1251   ```
1252
12532. 分析命令码不一致的根因,并做修改
1254
1255   根因分析:结构体的大小根据32位与64位编译会产生差异,从而影响VIDIOC_QUERYBUF的值。
1256
1257   修改点:修改用户态编译v4l2_buffer结构体的timestamp定义,并将用户态编译的头文件#include <linux/videodev2.h>替换成
1258
1259   修改过的videodev2.h,即#include ”videodev2.h“。(实际操作为把修改后的videodev2.h拷贝到v4l2_main编译目录中)
1260
1261   kernel/linux/linux-4.19/
1262
1263   ```diff
1264   diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
1265   index ba548d7f0..b0fb48f65 100644
1266   --- a/include/uapi/linux/videodev2.h
1267   +++ b/include/uapi/linux/videodev2.h
1268   @@ -958,13 +958,20 @@ struct v4l2_plane {
1269     * Contains data exchanged by application and driver using one of the Streaming
1270     * I/O methods.
1271     */
1272   +
1273   +struct timeval_user {
1274   +    long tv_sec;
1275   +    long tv_usec;
1276   +};
1277   +
1278    struct v4l2_buffer {
1279           __u32                   index;
1280           __u32                   type;
1281           __u32                   bytesused;
1282           __u32                   flags;
1283           __u32                   field;
1284   -       struct timeval          timestamp;
1285   +       //struct timeval                timestamp;
1286   +       struct timeval_user             timestamp;
1287           struct v4l2_timecode    timecode;
1288           __u32                   sequence;
1289
1290   ```
1291
1292   drivers/peripheral
1293
1294   ```diff
1295   diff --git a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h
1296   index 00ddea7..962ebc3 100755
1297   --- a/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h
1298   +++ b/camera/hal/adapter/chipset/rpi/rpi3/device/camera/src/driver_adapter/main_test/project_v4l2_main.h
1299   @@ -15,7 +15,8 @@
1300
1301    #ifndef HOS_CAMERA_PROJET_HARDWARE_H
1302    #define HOS_CAMERA_PROJET_HARDWARE_H
1303   -#include <linux/videodev2.h>
1304   +//#include <linux/videodev2.h>
1305   +#include "videodev2.h"
1306
1307    namespace OHOS::Camera {
1308    #define PREVIEW_PIXEL_FORMAT V4L2_PIX_FMT_YUV420
1309   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h
1310   index 6f45882..a8d6819 100644
1311   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h
1312   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_buffer.h
1313   @@ -19,7 +19,8 @@
1314    #include <mutex>
1315    #include <map>
1316    #include <cstring>
1317   -#include <linux/videodev2.h>
1318   +//#include <linux/videodev2.h>
1319   +#include "videodev2.h"
1320    #include <sys/ioctl.h>
1321    #include "v4l2_common.h"
1322    #if defined(V4L2_UTEST) || defined (V4L2_MAIN_TEST)
1323   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h
1324   index 5b93f36..05191a7 100644
1325   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h
1326   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_control.h
1327   @@ -16,7 +16,8 @@
1328    #ifndef HOS_CAMERA_V4L2_CONTROL_H
1329    #define HOS_CAMERA_V4L2_CONTROL_H
1330
1331   -#include <linux/videodev2.h>
1332   +//#include <linux/videodev2.h>
1333   +#include "videodev2.h"
1334    #include <errno.h>
1335    #include <sys/ioctl.h>
1336    #include "v4l2_common.h"
1337   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h
1338   index 10dc9b4..e3b3056 100644
1339   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h
1340   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_dev.h
1341   @@ -19,7 +19,8 @@
1342    #include <mutex>
1343    #include <thread>
1344    #include <vector>
1345   -#include <linux/videodev2.h>
1346   +//#include <linux/videodev2.h>
1347   +#include "videodev2.h"
1348    #include <sys/epoll.h>
1349    #include <sys/ioctl.h>
1350    #include <sys/types.h>
1351   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h
1352   index de892e9..44bb1b4 100644
1353   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h
1354   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_fileformat.h
1355   @@ -19,7 +19,8 @@
1356    #include <vector>
1357    #include <cstring>
1358    #include <fcntl.h>
1359   -#include <linux/videodev2.h>
1360   +//#include <linux/videodev2.h>
1361   +#include "videodev2.h"
1362    #include <sys/ioctl.h>
1363    #include <sys/stat.h>
1364    #include <unistd.h>
1365   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h
1366   index 1a62f37..96c70aa 100644
1367   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h
1368   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/include/v4l2_uvc.h
1369   @@ -18,7 +18,8 @@
1370    #include <thread>
1371    #include <fcntl.h>
1372    #include <linux/netlink.h>
1373   -#include <linux/videodev2.h>
1374   +//#include <linux/videodev2.h>
1375   +#include "videodev2.h"
1376    #include <sys/ioctl.h>
1377    #include <sys/select.h>
1378    #include <linux/netlink.h>zz
1379   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1380   index b351f49..5483b85 100755
1381   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1382   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1383   @@ -22,7 +22,8 @@
1384    #include <sys/mman.h>
1385    #include <sys/time.h>
1386    #include <linux/fb.h>
1387   -#include <linux/videodev2.h>
1388   +//#include <linux/videodev2.h>
1389   +#include "videodev2.h"
1390    #include "securec.h"
1391    #include "v4l2_uvc.h"
1392    #include "v4l2_dev.h"
1393   diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp
1394   index 778cf05..96618be 100644
1395   --- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp
1396   +++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/src/v4l2_stream.cpp
1397   @@ -14,7 +14,8 @@
1398     */
1399
1400    #include <cstring>
1401   -#include <linux/videodev2.h>
1402   +//#include <linux/videodev2.h>
1403   +#include "videodev2.h"
1404    #include <sys/ioctl.h>
1405
1406    #include "v4l2_stream.h"
1407   diff --git a/camera/hal/test/v4l2/include/test_display.h b/camera/hal/test/v4l2/include/test_display.h
1408   index d437e26..8e5205e 100644
1409   --- a/camera/hal/test/v4l2/include/test_display.h
1410   +++ b/camera/hal/test/v4l2/include/test_display.h
1411   @@ -44,7 +44,8 @@
1412    #include <errno.h>
1413    #include <getopt.h>
1414    #include <linux/fb.h>
1415   -#include <linux/videodev2.h>
1416   +//#include <linux/videodev2.h>
1417   +#include "videodev2.h"
1418    #include <mutex>
1419    #include <pthread.h>
1420    #include <stdlib.h>
1421   @@ -138,4 +139,4 @@ public:
1422        void StartCapture(int streamId, int captureId, bool shutterCallback, bool isStreaming);
1423        float calTime(struct timeval start, struct timeval end);
1424    };
1425   -#endif
1426   \ No newline at end of file
1427   +#endif
1428   ```
1429
1430   再次编译v4l2_main后执行,log无报错。HDMI屏上就显示预览画面。![](figures/isoftstone/yangfan-picture-hdmi.png)
1431
1432
1433
1434回过头来再看整个调试流程,发现该摄像头支持YUYV格式和MJPEG格式,查看v4l2_main中默认预览用的是YUYV,改为MJPEG再次调试。
1435
1436修改点:
1437
1438```diff
1439diff --git a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1440index b351f49..5483b85 100755
1441--- a/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1442+++ b/camera/hal/adapter/platform/v4l2/src/driver_adapter/main_test/v4l2_main.cpp
1443@@ -394,7 +395,9 @@ void V4L2SetDeviceFormat(DeviceFormat& format, const std::string devname)
1444
1445     if (devname == "uvcvideo" || devname == "uvcvideo1") {
1446         if (g_isPreviewOnUvc) {
1447-            format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
1448+            CAMERA_LOGD("cyyanl enter V4L2SetDeviceFormat : g_isPreviewOnUvc\n");
1449+            //format.fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
1450+            format.fmtdesc.pixelformat = V4L2_PIX_FMT_MJPEG;
1451             format.fmtdesc.width = width;
1452             format.fmtdesc.height = height;
1453         }
1454```
1455
1456v4l2_main执行结果:该显示异常,目前先不分析MJPEG格式。
1457
1458![](figures/isoftstone/yangfan-picture-v4l2_main.png)
1459
1460
1461
1462到此为止,v4l2_main运行正常,可以预览(预览画面色彩异常与屏幕显示格式有关,暂不做修改)。接下来调试拍照和录像。
1463
1464执行v4l2_main后,先输入u进行uvc预览,再运行c进行拍照,再运行v进行录像,照片和录像文件生成到当前执行的路径下:
1465
1466```
1467# ls -l *.jpeg
1468-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC0.jpeg
1469-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC1.jpeg
1470-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC2.jpeg
1471-rwxrw-rw- 1 root 29034400 614400 2013-01-18 15:20 UVC3.jpeg
1472#
1473# ls -l *.h264
1474-rwxrw-rw- 1 root 29034400 85401600 2013-01-18 15:20 uvc.h264
1475```
1476
1477将如上文件导出到电脑端查看:
1478
1479照片uvc0.jpeg
1480
1481![](figures/isoftstone/yangfan-picture-uvc0.png)
1482
1483录像uvc.h264在手机端可查看,播放正常。
1484
1485### 附录
1486
1487```c
1488/**
1489 * struct v4l2_buffer - video buffer info
1490 * @index:  id number of the buffer
1491 * @type:   enum v4l2_buf_type; buffer type (type == *_MPLANE for
1492 *      multiplanar buffers);
1493 * @bytesused:  number of bytes occupied by data in the buffer (payload);
1494 *      unused (set to 0) for multiplanar buffers
1495 * @flags:  buffer informational flags
1496 * @field:  enum v4l2_field; field order of the image in the buffer
1497 * @timestamp:  frame timestamp
1498 * @timecode:   frame timecode
1499 * @sequence:   sequence count of this frame
1500 * @memory: enum v4l2_memory; the method, in which the actual video data is
1501 *      passed
1502 * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP;
1503 *      offset from the start of the device memory for this plane,
1504 *      (or a "cookie" that should be passed to mmap() as offset)
1505 * @userptr:    for non-multiplanar buffers with memory == V4L2_MEMORY_USERPTR;
1506 *      a userspace pointer pointing to this buffer
1507 * @fd:     for non-multiplanar buffers with memory == V4L2_MEMORY_DMABUF;
1508 *      a userspace file descriptor associated with this buffer
1509 * @planes: for multiplanar buffers; userspace pointer to the array of plane
1510 *      info structs for this buffer
1511 * @length: size in bytes of the buffer (NOT its payload) for single-plane
1512 *      buffers (when type != *_MPLANE); number of elements in the
1513 *      planes array for multi-plane buffers
1514 *
1515 * Contains data exchanged by application and driver using one of the Streaming
1516 * I/O methods.
1517 */
1518
1519struct v4l2_buffer {
1520    __u32           index;
1521    __u32           type;
1522    __u32           bytesused;
1523    __u32           flags;
1524    __u32           field;
1525    struct timeval    timestamp;
1526    struct v4l2_timecode    timecode;
1527    __u32           sequence;
1528
1529    /* memory location */
1530    __u32           memory;
1531    union {
1532        __u32           offset;
1533        unsigned long   userptr;
1534        struct v4l2_plane *planes;
1535        __s32       fd;
1536    } m;
1537    __u32           length;
1538    __u32           reserved2;
1539    __u32           reserved;
1540}
1541```
1542
1543##     TP
1544
1545### TP驱动模型
1546
1547主要包含Input模块HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作input设备的驱动能力接口,HDI接口主要包括如下三大类:
1548
1549- InputManager:管理输入设备,包括输入设备的打开、关闭、设备列表信息获取等;
1550- InputReporter:负责输入事件的上报,包括注册、注销数据上报回调函数等;
1551- InputController:提供input设备的业务控制接口,包括获取器件信息及设备类型、设置电源状态等。
1552
1553**图 1** INPUT模块HDI接口层框架图
1554
1555![dayu200-tp-01.png](figures/dayu200/dayu200-tp-01.png)
1556
1557相关目录下源代码目录结构如下所示
1558
1559```
1560/drivers/peripheral/input
1561├── hal                # input模块的hal层代码
1562│   └── include       # input模块hal层内部的头文件
1563│   └── src           # input模块hal层代码的具体实现
1564├── interfaces         # input模块对上层服务提供的驱动能力接口
1565│   └── include       # input模块对外提供的接口定义
1566├── test               # input模块的测试代码
1567│   └── unittest      # input模块的单元测试代码
1568```
1569
1570详细请参考input子系统[README](https://gitee.com/openharmony/drivers_peripheral/blob/master/input/README_zh.md)
1571
1572### TP HDF驱动适配
1573
1574#### 配置适配
1575
1576关闭linux tp驱动开关,在<rockchip_linux_defconfig>文件中关闭TOUCHSCREEN_GT9XX,具体操作如下,将**CONFIG_TOUCHSCREEN_GT9XX=y**修改为
1577```makefile
1578# CONFIG_TOUCHSCREEN_GT9XX is not set
1579```
1580#### 驱动适配
1581
15821. 在**vendor/rockchip/rk3399/hdf_config/khdf/device_info/device_info.hcs**修改或增加如下内容
1583
1584   ```json
1585   	device_touch_chip :: device {
1586               device0 :: deviceNode {
1587                   policy = 0;
1588                   priority = 130;
1589                   preload = 0;
1590                   permission = 0660;
1591                   moduleName = "HDF_TOUCH_GT911";
1592                   serviceName = "hdf_touch_gt911_service";
1593                   deviceMatchAttr = "zsj_gt911_5p5";
1594               }
1595           }
1596   ```
1597
1598
1599
16002. 在**vendor/rockchip/rk3399/hdf_config/khdf/input/input_config.hcs**修改或增加如下内容
1601
1602   ```json
1603    busConfig {
1604        // 0:i2c 1:spi
1605        busType = 0;                            // I2C通信方式
1606        busNum = 7;							 // cpu对应touch芯片的I2C编号
1607        clkGpio = 72;							 // I2C clk对应的IO引脚编号
1608        dataGpio = 71;							 // I2C data对应的IO引脚标号
1609        i2cClkIomux = [0xFF77E004, 0x2];		 // I2C [clk引脚对应的复用寄存器,配置为I2C7_clk功能]
1610        i2cDataIomux = [0xFF77E000, 0x8000];	 // I2C [data引脚对应的复用寄存器,配置为I2C7_DATA功能]
1611    }
1612
1613    pinConfig {
1614        rstGpio = 150;							// touch芯片rst对应的IO引脚编号
1615        intGpio = 52;							// touch芯片int对应的IO引脚编号
1616        rstRegCfg = [0xFF77E028, 0x00000000];	// rst引脚对应的复用功能寄存器,配置为普通io口
1617        intRegCfg = [0xFF770018, 0x00000000];	// int引脚对应的复用功能寄存器,配置为普通io口
1618    }
1619   ```
1620#### FAQ问题
1621
1622##### 解决触屏横竖反转问题
1623
1624修改**drivers/framework/model/input/driver/touchscreen/touch_gt911.c**中的**ParsePointData**函数,x和y对调即可
1625
1626##### InputPinMuxCfg函数修改
1627
1628```c
1629static int32_t InputPinMuxCfg(uint32_t regAddr, int32_t regSize, uint32_t regValue)
1630{
1631    uint8_t *base = NULL;
1632    uint32_t data = 0;
1633    if (regAddr == 0) {
1634        HDF_LOGE("%s: regAddr invalid", __func__);
1635        return HDF_FAILURE;
1636    }
1637
1638    HDF_LOGE("regAddr = 0x%x, regSize = 0x%x", regAddr, regSize);
1639    base = OsalIoRemap(regAddr, regSize);
1640    if (base == NULL) {
1641        HDF_LOGE("%s: ioremap failed", __func__);
1642        return HDF_FAILURE;
1643    }
1644    // 写入寄存器前先读出数据,保存原有的配置,方式修改原有的其他引脚功能配置。
1645    data = OSAL_READL(base);
1646    data |= regValue;
1647    OSAL_WRITEL(data, base);
1648    OsalIoUnmap((void *)base);
1649    return HDF_SUCCESS;
1650}
1651```
1652
1653##### RK3399引脚GPIOn_KX 编号号确认方法
1654
1655```shell
1656num = 32n+(K-A)8+X
1657# GPIO1_B2 = 1 * 32 + (B-A)*8 + 2 = 32 + 8 + 1 = 42
1658```
1659
1660| 功能   | IO名称   | IO编号 |
1661| ------ | -------- | ------ |
1662| TP_INT | GPIO1_C4 | 52     |
1663| TP_RST | GPIO4_C6 | 150    |
1664| TP_SDA | GPIO2_A7 | 71     |
1665| TP_SCL | GPIO2_B0 | 72     |
1666
1667
1668
1669##### RK3399引脚GPIOn_KX复用功能配置
1670
16711. GPIO复用功能寄存器基地址**FF77_0000**
16722. 找到GPIOn_K复用功能寄存器偏移量
16733. 确定GPIOn_K复用功能寄存器,**基地址+偏移量**
16744. 根据复用功能寄存器介绍,写入对应引脚的复用功能
1675
1676| 功能   | IO名称   | 复用寄存器地址 | 控制位 | 对应数值            |
1677| ------ | -------- | -------------- | ------ | ------------------- |
1678| TP_INT | GPIO1_C4 | 0xFF770018     | 9:8    | 2'b00:GPIO         |
1679| TP_RST | GPIO4_C6 | 0xFF77E028     | 13:12  | 2'b00:GPIO         |
1680| TP_SDA | GPIO2_A7 | 0xFF77E000     | 15:14  | 2'b10: i2c7nfc_sda |
1681| TP_SCL | GPIO2_B0 | 0xFF77E004     | 1:0    | 2'b10: i2c7nfc_scl |
1682
1683
1684
1685
1686