摄像头的一些基础知识:
逆光是一种由于被摄主体恰好处于光源和照相机之间的状况,这种状况极易造成使被摄主体曝光不充分。
高通平台打开更多的camera相关log:
adb root
adb remount
adb shell setprop persist.camera.hal.debug 5 -> persist.vendor.camera.hal.debug
adb shell setprop persist.camera.mci.debug 5 ->没搜索到
adb shell setprop persist.vendor.camera.hal.debug 5 -> 一样
adb shell setprop persist.vendor.camera.mci.debug 5 -> 没搜索到
adb logcat -v threadtime > xxx/logcat.txt
adb logcat -b kernel > xxx/kernel.txt
摄像头原理:
摄像头主要由镜头,影像传感器(主要是CCD/CMOS),DSP等组成,被摄物体经过镜头聚焦至CCD上,CCD由多个X-Y纵横排列的像素点组成,每个像素点都由一个光电二极管及相关电路组成,光电二极管将光线转变成电荷,收集到的电荷总量与光线强度成正比,
所积累的电荷在相关电路的控制下,逐点移出,经滤波,放大,再经过DSP处理后形成视频信号输出.
1.LENS(镜头)
一般camera的镜头结构是有几片透镜组成,分有塑胶透镜(Plastic)和玻璃透镜(Glass),通常Camera用的镜头结构有:1P,2P,1G1P,1G3P,2G2P,4G等.透镜越多,成本越高;玻璃透镜比塑料透镜贵,但是玻璃透镜的成像效果比塑料透镜的效果要好,
目前市场上针对mobile phone配置的camera以1G3P(一片玻璃透镜和3片塑胶透镜组成)为主,目的是降低成本.
2.SENSOR(图像传感器)
图像传感器(Sensor)是一种半导体芯片,其表面包含有几十万到几百万的光电二极管.光电二极管受到光照射时,就会产生电荷.目前sensor类型有两种:
CCD(Charge Couple Device),电荷耦合器件
CMOS(Complementary Metal Oxide Semiconductor),互补金属氧化物
CCD组件与结构
CCD结构一般分为三层:
(1)第一层"LENS"
Camera的成像关键在于sensor,为了扩大CCD的采光率必须扩大单一像素的受光面积,在提高采光率的同时会导致画面质量下降.LENS就是相当于在sensor前面增加一副眼镜,sensor的采光率就不是由sensor的开口面积决定而是由LENS的表面积决定.
(2)第二层"分色率色片"
目前分色率色片有两种分色方法:
A.RGB原色分色法.就是三原色分色法,几乎所有的人类眼睛可以识别到的颜色都可以通过RGB来组成,RGB就是通过这三个通道的颜色调节而成.
B.CMYK补色分色法,由四个通道的颜色配合而成,分别是青(C),洋红(M),黄(Y),黑(K),但是调节出来的颜色不如RGB的颜色多.
(3)第三层"感光层(SENSOR)"
CCD的第三层是处理芯片(Sensor,sensor主要是将穿过滤色层的光源转换成电子信号,并将信号传送到影像DSP),将影像还原.
CCD与CMOS的差异:
A.总体的比较
CCD的优点是灵敏度高,噪音小,信噪比大.但是生产工艺复杂,成本高,功耗高.
CMOS的优点是集成度高(将AADC与讯号处理器整合,可以大幅度缩小体积),功耗低,成本低.但是噪声比较大,灵敏度较低,对光源要求高.
B.成像效果
在相同像素下CCD的成像往往通透性,明锐度都很好,色彩还原,曝光可以保证基本准确
CMOS的产品往往通透性一般,对实物的色彩还原能力偏弱,曝光也都不太好,在采用CMOS为感光元器件的产品中,通过采用影像光源自动增益补强技术,自动亮度,白平衡控制技术,色饱和度,对比度,边缘增强以及伽马矫正等先进的影像控制技术,完全可以达到与CCD摄像头相媲美的效果.
C.功耗比较
CCD功耗比较高,为使电荷传输顺畅,噪声降低,需要高压差改善传输效果:另外由于CCD没有ADC和讯号处理器,导致需要使用3-4组电源
CMOS功耗比较低,不到CCD的1/3,CMOS影像传感器将每一画素的电荷转换成电压,读取前就将其放大,利用3.3V的电源即可驱动,只需要一组电源.(实际上不止啊,有1.2,1.8,3.3多种电源啊?)
由于CCD图像传感器成本高的影响,在手机电脑上使用的camera还是以CMOS为主,不仅是价格,体积也是影响的另外一个因素.
ADC的两个重要指标是转换速度和量化精度,量化精度对应的ADC转换器将每一个像素的亮度和色彩值量化为若干的等级,这个等级就是Camera的色彩深度,由于camera system中高分辨率图像的像素量庞大,因此对转换速度要求很高.
技术指标:
1.图像压缩方式:JPEG(joint photographic expert group)静态图像压缩方式,一种有损图像的压缩方式,压缩比越大,图像质量也就越差.当图像精度要求不高,存储空间有限时,可以选择这种格式,目前大部分相机都使用JPEG格式.
2.图像噪音:指的是图像中的杂点干扰,表现为图像中有固定的彩色杂点
3.视角:简单说就是成像范围
4.白平衡处理技术(AWB)
定义:要求在不同色温环境下,照白色的物体,屏幕中的图像也应该是白色的.色温表示光谱成分,光的颜色.色温低表示长波光成分多.当色温改变时,光源中三基色(红\绿\蓝)的比例会发生变化,需要调节三基色的比例来达到彩色的平衡,
这就是白平衡调节的实际.图像传感器的图像数据被读取后,系统将对其进行针对镜头的边缘畸变的运算修正,然后经过坏像处理后被系统送进去进行白平衡处理(在不同的环境光照下,人类的眼睛可以把一些"白色"的物体都看成白色,是以为人眼进行了修正,
但是sensor没有这种功能,因此需要对sensor输出的信号进行一定的修正,这就就是白平衡处理技术)
5.摄像头内部需要两种工作电压:3.3V和2.5V,因此好的摄像头内部电源也是保证摄像头稳定工作的一个因素.
6.色彩深度(色彩位数):反映对色彩的识别能力和成像的色彩表现能力,就是用多少位的二进制数字来记录三种原色,实际上就是AD的转换量化精度,非专业的一般是24位,专业型至少是36位
非专业的一个原色用8位记录,最多就是256(2的8次方)*256*256=1677万种颜色
专业的的一个原色用12位记录,最多就是4096(2的12次方)*4096*4096=68.7亿种颜色
7.图像格式(image Format/Color space):最常用的两种:RGB24(RGB各8bit),I420(YUV格式之一)
8.分辨率:
图像解析度/分辨率:
SXGA(1280*1024)又称130万像素
XGA(1024*768)又称80万像素
SVGA(800*600)又称50万像素
VGA(640*480)又称30万像素(35万是指648*488)
CIF(352*288)又称10万像素
SIF/QVGA(320*240)
QCIF(176*144)
QSIF/QQVGA(160*120)
Camera原理:外部光线穿过lens后,经过color filter滤波后照射到sensor面上,sensor将从lens上传到过来的光线转换成电信号,再通过内部的AD转换为数字信号,如果sensor没有集成DSP,则通过DVP的方式传输到baseband,此时的数据格式是RAW DATA。
必须通过平台的isp来处理。如果集成了DSP,这RAW DATA数据经过AWB,color matrix,lens shading,gamma,sharpness,AE和de-noise处理后输出YUV或者RGB格式的数据。最后会由CPU送到framebuffer中进行显示。
=========================
linux shell:
特殊变量列表
变量
含义
$0
当前脚本的文件名
$n
传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$#
传递给脚本或函数的参数个数。
$*
传递给脚本或函数的所有参数。
$@
传递给脚本或函数的所有参数。被双引号(" ")包含时,与 $* 稍有不同,下面将会讲到。
$?
上个命令的退出状态,或函数的返回值。
$$
当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。
举个例子,写个脚本:
wanghl@wanghl-HP:~/code/sdm439_android$ cat 1.sh
#!/bin/bash
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"
测试一下脚本!
wanghl@wanghl-HP:~/code/sdm439_android$ ./1.sh huawei ali
File Name: ./1.sh
First Parameter : huawei
First Parameter : ali
Quoted Values: huawei ali
Quoted Values: huawei ali
Total Number of Parameters : 2
wanghl@wanghl-HP:~/code/sdm439_android$ echo $?//打印上个命令脚本的退出状态
0
===============================================================
CMMI全称是Capability Maturity Model Integration,是能力成熟度集成模型,是由美国国防部与卡内基-梅隆大学和美国国防工业协会共同开发和研制的。CMMI是一套融合多学科的、可扩充的产品集合,其研制的初步动机是为了利用两个或多个单一学科的模型实现一个组织的集成化过程改进。
等级:
1. 初始级 软件过程是无序的,有时甚至是混乱的,对过程几乎没有定义,成功取决于个人努力。管理是反应式的。
2. 已管理级 建立了基本的项目管理过程来跟踪费用、进度和功能特性。制定了必要的过程纪律,能重复早先类似应用项目取得的成功经验。
3. 已定义级 已将软件管理和工程两方面的过程文档化、标准化,并综合成该组织的标准软件过程。所有项目均使用经批准、剪裁的标准软件过程来开发和维护软件,软件产品的生产在整个软件过程是可见的。
4. 量化管理级 分析对软件过程和产品质量的详细度量数据,对软件过程和产品都有定量的理解与控制。管理有一个作出结论的客观依据,管理能够在定量的范围内预测性能。
5. 优化管理级 过程的量化反馈和先进的新思想、新技术促使过程持续不断改进。
每个等级都被分解为过程域,特殊目标和特殊实践,通用目标、通用实践和共同特性:
每个等级都有几个过程区域组成,这几个过程域共同形成一种软件过程能力。每个过程域,都有一些特殊目标和通用目标,通过相应的特殊实践和通用实践来实现这些目标。当一个过程域的所有特殊实践和通用实践都按要求得到实施,就能实现该过程域的目标。
===================================================
人脸识别考勤机IR摄像头概率性挂掉,IIC通信失败,没有预览的问题
先关红外,再关RGB的kernel log:(基本是同一时间关),首先说明主摄RGB和副摄IR摄像头共用两路电压,分别是DOVDD1.8(PMIC的L6)和AVDD3.3(GPIO35)
console:/ $
[ 4533.247857] audit: audit_lost=101356 audit_rate_limit=5 audit_backlog_limit=64
[ 4533.247905] audit: rate limit exceeded
[ 4533.672954] init: starting service 'vendor.vm_bms'...
[ 4533.738866] init: Service 'vendor.vm_bms' (pid 13055) exited with status 255
[ 4533.738966] init: Sending signal 9 to service 'vendor.vm_bms' (pid 13055) process group...
[ 4533.756116] libprocessgroup: Successfully killed process cgroup uid 0 pid 13055 in 0ms
[ 4534.288316] audit: audit_lost=101382 audit_rate_limit=5 audit_backlog_limit=64
[ 4534.288372] audit: rate limit exceeded
[ 4535.303876] audit: audit_lost=101405 audit_rate_limit=5 audit_backlog_limit=64
[ 4535.303942] audit: rate limit exceeded
[ 4535.311704] IRQ6 no longer affine to CPU0
[ 4535.314449] IRQ6 no longer affine to CPU2
[ 4535.991813] IRQ6 no longer affine to CPU1
[ 4537.984911] msm_private_ioctl:Notifying subdevs about potential sof freeze
[ 4537.985171] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR0 = 0x10
[ 4537.985183] MSM-SENSOR-INIT msm_sensor_init_subdev_ioctl:122 default
[ 4537.985183]
[ 4537.986020] type=1400 audit(1562218147.529:121618): avc: denied { read } for pid=531 comm="CAM_AECAWB" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15497 sconte0
[ 4537.986084] type=1400 audit(1562218150.419:121628): avc: denied { read } for pid=531 comm="CAM_mct_freeze" name="clk" dev="debugfs" ino=3379 scontext=u:r:hal_camera_0
[ 4538.026645] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR1 = 0x10
[ 4538.047005] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR2 = 0x0
[ 4538.052471] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR3 = 0x0
[ 4538.057941] msm_csiphy_irq CSIPHY0_IRQ_CLK_STATUS_ADDR0 = 0xc0
[ 4538.063323] msm_csiphy_irq CSIPHY0_IRQ_CLK_STATUS_ADDR1 = 0x0
[ 4538.069140] msm_csid_irq CSID0_IRQ_STATUS_ADDR = 0x33
[ 4538.074914] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR0 = 0x10
[ 4538.079901] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR1 = 0x10
[ 4538.085284] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR2 = 0x0
[ 4538.090752] msm_csiphy_irq CSIPHY0_IRQ_STATUS_ADDR3 = 0x0
[ 4538.096221] msm_csiphy_irq CSIPHY0_IRQ_CLK_STATUS_ADDR0 = 0xc0
[ 4538.101603] msm_csiphy_irq CSIPHY0_IRQ_CLK_STATUS_ADDR1 = 0x0
[ 4538.683088] init: starting service 'vendor.vm_bms'...
[ 4538.740272] init: Service 'vendor.vm_bms' (pid 13096) exited with status 255
[ 4538.740377] init: Sending signal 9 to service 'vendor.vm_bms' (pid 13096) process group...
[ 4538.748073] libprocessgroup: Successfully killed process cgroup uid 0 pid 13096 in 0ms//关闭红外摄像头结束,正常没什么异常log,关闭后AVDD3.3(GPIO35)会被关闭拉低,主摄像的预览也会被强行停止,因为IC已经掉电不工作了
[ 4543.299858] msm_cci_irq:1807 MASTER_0 error 0x8000000//从这里开始关闭IR摄像头,但是因为C2395_SUB的IC的AVDD的电源被掐掉了,(L6是一直保持为高,不影响),所以IIC通信的指令也无法正常执行了
[ 4543.391381] msm_cci_wait: 306 wait for queue: 0
[ 4543.391445] msm_cci_transfer_end: 558 failed rc -110
[ 4543.394743] msm_cci_data_queue: 816 failed rc -110
[ 4543.399954] msm_camera_cci_i2c_write_table_cmd: line 216 rc = -110//IIC写失败,如下STOP_REG_ARRAY指令
[ 4543.561090] MSM-CPP cpp_release_hardware:1214 cpp hw release done//至少有release完成
[ 4543.583619] IRQ1 no longer affine to CPU0
[ 4543.583675] IRQ66 no longer affine to CPU0
[ 4543.689765] init: starting service 'vendor.vm_bms'...
[ 4543.736127] init: Service 'vendor.vm_bms' (pid 13108) exited with status 255
[ 4543.736226] init: Sending signal 9 to service 'vendor.vm_bms' (pid 13108) process group...
[ 4543.744527] libprocessgroup: Successfully killed process cgroup uid 0 pid 13108 in 1ms
[ 4548.700601] init: starting service 'vendor.vm_bms'...
[ 4548.752780] init: Service 'vendor.vm_bms' (pid 13109) exited with status 255
[ 4548.752869] init: Sending signal 9 to service 'vendor.vm_bms' (pid 13109) process group...
[ 4548.759207] libprocessgroup: Successfully killed process cgroup uid 0 pid 13109 in 0ms
[ 4553.713069] init: starting service 'vendor.vm_bms'...
[ 4553.766758] init: Service 'vendor.vm_bms' (pid 13111) exited with status 255
[ 4553.766847] init: Sending signal 9 to service 'vendor.vm_bms' (pid 13111) process group...
[ 4553.773682] libprocessgroup: Successfully killed process cgroup uid 0 pid 13111 in 0ms
先开红外,再开主RGB摄像头:
console:/ $ [ 94.716046] type=1400 audit(1562213688.519:999): avc: denied { read } for pid=531 comm="CAM_mct_freeze" name="clk" dev="debugfs" ino=2351 scontext=u:r:ha0
[ 94.716210] type=1400 audit(1562213707.229:1000): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=150
[ 94.737228] type=1400 audit(1562213707.229:1002): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=15d
[ 94.762370] type=1400 audit(1562213707.229:1003): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=10
[ 94.832174] msm_pm_qos_update_request: update request 100[ 94.832347] msm_pm_qos_update_request: update request -1
[ 94.840619] msm_csid_init: CSID_VERSION = 0x30040002
[ 94.843222] msm_csid_irq CSID1_IRQ_STATUS_ADDR = 0x800
[ 94.851106] MSM-CPP cpp_init_hardware:1133 CPP HW Version: 0x40030002
[ 94.851876] MSM-CPP cpp_init_hardware:1151 stream_cnt:0
[ 94.890159] type=1400 audit(1562213707.229:1003): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=10
[ 94.890405] type=1400 audit(1562213707.399:1004): avc: denied { read } for pid=531 comm="HwBinder:531_4" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scon0
[ 94.913537] type=1400 audit(1562213707.409:1005): avc: denied { read } for pid=531 comm="HwBinder:531_4" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scon0
[ 94.927380] msm_cci_init:1442: hw_version = 0x10020004
[ 94.958095] type=1400 audit(1562213707.419:1006): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=150
[ 95.014530] msm_csid_irq CSID1_IRQ_STATUS_ADDR = 0x800
[ 95.101404] type=1400 audit(1562213707.419:1007): avc: denied { read } for pid=3313 comm="pubtron.fastest" name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=150
[ 95.101510] type=1400 audit(1562213707.619:1008): avc: denied { read } for pid=531 comm="CAM_AECAWB" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scontext0
[ 95.105365] audit: audit_lost=794 audit_rate_limit=5 audit_backlog_limit=64
[ 95.105372] audit: rate limit exceeded
[ 95.173763] ispif_process_irq: PIX0 frame id: 0
[ 95.240422] ispif_process_irq: PIX0 frame id: 1
[ 95.307070] ispif_process_irq: PIX0 frame id: 2
[ 95.374089] ispif_process_irq: PIX0 frame id: 3
[ 95.712672] ispif_process_irq: PIX0 frame id: 0
[ 96.248483] audit: audit_lost=799 audit_rate_limit=5 audit_backlog_limit=64
[ 96.248528] audit: rate limit exceeded
[ 97.314908] audit: audit_lost=810 audit_rate_limit=5 audit_backlog_limit=64
[ 97.314971] audit: rate limit exceeded
[ 98.382314] audit: audit_lost=821 audit_rate_limit=5 audit_backlog_limit=64
[ 98.382375] audit: rate limit exceeded
[ 98.954849] init: starting service 'vendor.vm_bms'...
[ 99.006792] init: Service 'vendor.vm_bms' (pid 3917) exited with status 255
[ 99.006884] init: Sending signal 9 to service 'vendor.vm_bms' (pid 3917) process group...
[ 99.013458] libprocessgroup: Successfully killed process cgroup uid 0 pid 3917 in 0ms
[ 99.447832] audit: audit_lost=832 audit_rate_limit=5 audit_backlog_limit=64
[ 99.447881] audit: rate limit exceeded
[ 100.515554] audit: audit_lost=843 audit_rate_limit=5 audit_backlog_limit=64
[ 100.515616] audit: rate limit exceeded
//打开看着挺正常,没有报什么error
//再打开RGB主摄
console:/ $ [ 269.270635] init: starting service 'vendor.vm_bms'...
[ 269.328086] init: Service 'vendor.vm_bms' (pid 5310) exited with status 255
[ 269.328199] init: Sending signal 9 to service 'vendor.vm_bms' (pid 5310) process group...
[ 269.335350] libprocessgroup: Successfully killed process cgroup uid 0 pid 5310 in 0ms
[ 269.347052] audit: audit_lost=3176 audit_rate_limit=5 audit_backlog_limit=64
[ 269.350078] audit: rate limit exceeded
[ 269.475102] msm_pm_qos_update_request: update request 100[ 269.475157] msm_pm_qos_update_request: update request -1
[ 269.502612] msm_csid_init: CSID_VERSION = 0x30040002
[ 269.504407] msm_csid_irq CSID0_IRQ_STATUS_ADDR = 0x800
[ 269.517552] msm_camera_request_gpio_table:774 gpio 35:CAM_VANA request fails
[ 269.517644] msm8937-pinctrl 1000000.pinctrl: pin GPIO_35 already requested by 1b0c000.qcom,cci:qcom,camera@2; cannot claim for 1b0c000.qcom,cci:qcom,camera@0
[ 269.523882] msm8937-pinctrl 1000000.pinctrl: pin-35 (1b0c000.qcom,cci:qcom,camera@0) status -22
[ 269.537877] msm8937-pinctrl 1000000.pinctrl: could not request pin 35 (GPIO_35) from group gpio35 on device 1000000.pinctrl
[ 269.546688] qcom,camera 1b0c000.qcom,cci:qcom,camera@0: Error applying setting, reverse things back
[ 269.557967] msm_camera_power_up:1454 cannot set pin to active state[ 269.648086] msm_csid_irq CSID0_IRQ_STATUS_ADDR = 0x800//虽然会报出GPIO35申请失败的log,但是camera能够正常打开
[ 270.211521] type=1400 audit(1562213882.659:4140): avc: denied { read } for pid=531 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconted
[ 270.216431] type=1400 audit(1562213882.729:4141): avc: denied { read } for pid=531 comm="CAM_AECAWB" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scontext0
[ 270.238662] type=1400 audit(1562213882.729:4141): avc: denied { read } for pid=531 comm="CAM_AECAWB" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scontext0
[ 270.258503] type=1400 audit(1562213882.729:4142): avc: denied { read } for pid=531 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconte0
[ 270.286460] type=1400 audit(1562213882.729:4142): avc: denied { read } for pid=531 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconte0
[ 270.303264] type=1400 audit(1562213882.729:4143): avc: denied { read } for pid=531 comm="CAM_MctServ_1" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scont0
[ 270.415159] audit: audit_lost=3201 audit_rate_limit=5 audit_backlog_limit=64
[ 270.415204] audit: rate limit exceeded
[ 271.091734] type=1400 audit(1562213882.729:4143): avc: denied { read } for pid=531 comm="CAM_MctServ_1" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 scont0
[ 271.091898] type=1400 audit(1562213883.599:4164): avc: denied { read } for pid=531 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconte0
[ 271.113910] type=1400 audit(1562213883.599:4164): avc: denied { read } for pid=531 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconte0
[ 271.143050] type=1400 audit(1562213883.599:4165): avc: denied { read } for pid=531 comm="CAM_MctBus_1" name="u:object_r:default_prop:s0" dev="tmpfs" ino=15505 sconte0
[ 271.479635] audit: audit_lost=3226 audit_rate_limit=5 audit_backlog_limit=64
[ 271.479681] audit: rate limit exceeded
[ 272.486037] audit: audit_lost=3252 audit_rate_limit=5 audit_backlog_limit=64
[ 272.486077] audit: rate limit exceeded
[ 272.770888] CPU1: update max cpu_capacity 994
AVDD共用一路电源GPIO35会导致APP重启的时候或者安装APP的时候出现概率性打不开的IR的现象,IR摄像头没有预览画面,可能在RGB释放资源,对象销毁的时候,IR刚好在下power off的休眠指令,
那电突然被掐掉,肯定导致IIC通信不上,读取不到ID,指令不能正常下达,IR没有预览,后来建议硬件修改两路AVDD电源分开,RGB用pmic的l22供电,ir用gpio35使能LDO供电,测试了一天半之后,
都没有出现IR摄像头失去预览画面的现象,另外咨询高通FAE,他们也有客户遇到这种共用的问题,他们也强烈建议电源不共用,否则会出现一些无法预料的现象。所以这个问题通过硬件改板来解决。
-------------------------
开机camera probe过程,是按照vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/configs/msm8937_SDM439_camera_ov2718_c2395.xml文件定义的顺序来探测,顺序分别是ov2718,c2395,ov2718_sub,c2395_sub,
相关的log在kernel/msm-4.9/drivers/media/platform/msm/camera_v2/common/msm_camera_io_util.c里的msm_camera_request_gpio_table函数:
- CDBG("%s:%d i %d, gpio %d dir %ld\n", __func__, __LINE__, i,
- gpio_tbl[i].gpio, gpio_tbl[i].flags);
+ pr_err("[wanghl]%s:%d i %d, gpio %d dir %ld, %s\n", __func__, __LINE__, i,
+ gpio_tbl[i].gpio, gpio_tbl[i].flags, gpio_tbl[i].label);
其他模块如马达,eeprom,如果xml文件里没有配置,对应的驱动如kernel/msm-4.9/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c是不会去跑到对应的GPIO的操作,即使他们是共用的。
ov2718读取三次ID失败判定ov2718 power up failed
//主RGB摄像头probe成功
//kernel/msm-4.9/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
[ 14.511543] msm_camera_power_up:1444 [wanghl]===3//上电开始
[ 14.511583] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 26 dir 1, CAMIF_MCLK0//主摄像的接口
[ 14.515243] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 36 dir 0, CAM_RESET0
[ 14.550918] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 35 dir 0, CAM_VANA0
[ 14.550959] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 50 dir 0, CAM_STANDBY0
//kernel/msm-4.9/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
[ 14.631008] msm_cci_init:1385 [wanghl]===6//I2C接口请求
[ 14.631046] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 29 dir 1, CCI_I2C_DATA0
[ 14.634013] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 30 dir 1, CCI_I2C_CLK0
[ 14.660960] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 31 dir 1, CCI_I2C_DATA1
[ 14.667440] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 32 dir 1, CCI_I2C_CLK1
[ 14.677468] c2395 probe succeeded//读取到ID,通信成功
[ 14.682255] msm_cci_release:1586 [wanghl]===8//I2C接口释放
[ 14.682263] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 29 dir 1, CCI_I2C_DATA0
[ 14.682269] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 30 dir 1, CCI_I2C_CLK0
[ 14.682274] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 31 dir 1, CCI_I2C_DATA1
[ 14.682280] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 32 dir 1, CCI_I2C_CLK1
[ 14.713154] msm_camera_power_down:1751 [wanghl]===5//下电开始,读完ID就下电了
[ 14.713160] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 26 dir 1, CAMIF_MCLK0
[ 14.713164] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 36 dir 0, CAM_RESET0
[ 14.713168] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 35 dir 0, CAM_VANA0
[ 14.713172] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 50 dir 0, CAM_STANDBY0
然后ov2718_sub读取三次ID失败判定ov2718_sub power up failed
//IR摄像头成功
[ 15.020929] msm_camera_power_up:1444 [wanghl]===3
[ 15.020936] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 27 dir 1, CAMIF_MCLK1
[ 15.020940] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 38 dir 0, CAM_RESET1
[ 15.020944] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 39 dir 0, CAM_STANDBY1
[ 15.020948] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 35 dir 0, CAM_VANA1
[ 15.020952] [wanghl]msm_camera_request_gpio_table:760 i 4, gpio 45 dir 0, CAM_VDIG1
[ 15.068840] msm_cci_init:1385 [wanghl]===6
[ 15.068846] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 29 dir 1, CCI_I2C_DATA0
[ 15.068850] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 30 dir 1, CCI_I2C_CLK0
[ 15.068854] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 31 dir 1, CCI_I2C_DATA1
[ 15.068858] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 32 dir 1, CCI_I2C_CLK1
[ 15.071761] c2395_sub probe succeeded
[ 15.071762] msm_cci_release:1586 [wanghl]===8
[ 15.071769] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 29 dir 1, CCI_I2C_DATA0
[ 15.071773] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 30 dir 1, CCI_I2C_CLK0
[ 15.071777] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 31 dir 1, CCI_I2C_DATA1
[ 15.071781] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 32 dir 1, CCI_I2C_CLK1
[ 15.100639] msm_camera_power_down:1751 [wanghl]===5
[ 15.100644] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 27 dir 1, CAMIF_MCLK1
[ 15.100648] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 38 dir 0, CAM_RESET1
[ 15.100652] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 39 dir 0, CAM_STANDBY1
[ 15.100656] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 35 dir 0, CAM_VANA1
[ 15.100660] [wanghl]msm_camera_request_gpio_table:760 i 4, gpio 45 dir 0, CAM_VDIG1
开机打开摄像头:
[ 41.579297] msm_camera_power_up:1444 [wanghl]===3
[ 41.579304] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 27 dir 1, CAMIF_MCLK1//先打开辅摄像IR摄像头
[ 41.579310] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 38 dir 0, CAM_RESET1
[ 41.579315] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 39 dir 0, CAM_STANDBY1
[ 41.579320] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 35 dir 0, CAM_VANA1
[ 41.579326] [wanghl]msm_camera_request_gpio_table:760 i 4, gpio 45 dir 0, CAM_VDIG1
/*
与dts对应:
qcom,camera@2{
...
gpios = <&tlmm 27 0>,
<&tlmm 38 0>,
<&tlmm 39 0>,
<&tlmm 35 0>,//共用
<&tlmm 45 0>;
qcom,gpio-reset = <1>;
qcom,gpio-standby = <2>;
qcom,gpio-vana = <3>;
qcom,gpio-vdig = <4>;
qcom,gpio-req-tbl-num = <0 1 2 3 4>;
qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
"CAM_RESET1",
"CAM_STANDBY1",
"CAM_VANA1",
"CAM_VDIG1";
...
}
*/
[ 41.612065] audit: audit_lost=17 audit_rate_limit=5 audit_backlog_limit=64
[ 41.612071] audit: rate limit exceeded
[ 41.627510] msm_cci_init:1385 [wanghl]===6
[ 41.627518] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 29 dir 1, CCI_I2C_DATA0
[ 41.627524] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 30 dir 1, CCI_I2C_CLK0
[ 41.627529] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 31 dir 1, CCI_I2C_DATA1
[ 41.627534] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 32 dir 1, CCI_I2C_CLK1
[ 41.627719] msm_cci_init:1443: hw_version = 0x10020004
[ 41.830375] msm_csid_irq CSID1_IRQ_STATUS_ADDR = 0x800
[ 41.983874] ispif_process_irq: PIX0 frame id: 0
[ 42.050534] ispif_process_irq: PIX0 frame id: 1
[ 42.117177] ispif_process_irq: PIX0 frame id: 2
[ 42.185892] ispif_process_irq: PIX0 frame id: 3
[ 42.188598] type=1400 audit(1562213653.939:65): avc: denied { read } for pid=3154 comm="pubtron.fastest" name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=13573 scontext=u:r:system_app:s0 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
[ 42.190329] type=1400 audit(1562213654.739:73): avc: denied { read } for pid=535 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=13477 scontext=u:r:hal_camera_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
[ 42.317859] type=1400 audit(1562213654.739:73): avc: denied { read } for pid=535 comm="CAM_MctBus_2" name="u:object_r:default_prop:s0" dev="tmpfs" ino=13477 scontext=u:r:hal_camera_default:s0 tcontext=u:object_r:default_prop:s0 tclass=file permissive=0
[ 42.318048] type=1400 audit(1562213654.869:74): avc: denied { read } for pid=3154 comm="pubtron.fastest" name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=13564 scontext=u:r:system_app:s0 tcontext=u:object_r:vendor_camera_prop:s0 tclass=file permissive=0
[ 42.327137] msm_pm_qos_update_request: update request 100
[ 42.334189] msm_pm_qos_update_request: update request -1
[ 42.334190] msm_csid_init: CSID_VERSION = 0x30040002
[ 42.340169] msm_csid_irq CSID0_IRQ_STATUS_ADDR = 0x800
[ 42.352306] msm_camera_power_up:1444 [wanghl]===3//再打开主摄像RGB摄像头
[ 42.352314] [wanghl]msm_camera_request_gpio_table:760 i 0, gpio 26 dir 1, CAMIF_MCLK0
[ 42.352319] [wanghl]msm_camera_request_gpio_table:760 i 1, gpio 36 dir 0, CAM_RESET0
[ 42.352325] [wanghl]msm_camera_request_gpio_table:760 i 2, gpio 35 dir 0, CAM_VANA0
[ 42.352330] [wanghl]msm_camera_request_gpio_table:760 i 3, gpio 50 dir 0, CAM_STANDBY0
/*
qcom,camera@0 {
...
gpios = <&tlmm 26 0>,
<&tlmm 36 0>,
<&tlmm 35 0>,//共用
<&tlmm 50 0>;
qcom,gpio-reset = <1>;
qcom,gpio-vana = <2>;
qcom,gpio-standby = <3>;
qcom,gpio-req-tbl-num = <0 1 2 3>;
qcom,gpio-req-tbl-flags = <1 0 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK0",
"CAM_RESET0",
"CAM_VANA0",
"CAM_STANDBY0";
...
}
*/
[ 42.352378] msm_camera_request_gpio_table:774 gpio 35:CAM_VANA0 request fails//请求的时候冲突了,因为gpio35在之前打开了又没有释放,但是这个只是一个警告,不影响预览打开
[ 42.352436] msm8937-pinctrl 1000000.pinctrl: pin GPIO_35 already requested by 1b0c000.qcom,cci:qcom,camera@2; cannot claim for 1b0c000.qcom,cci:qcom,camera@0//GPIO_35已经被qcom,camera@2(IR摄像头所在CSI)申请未释放,无法声明给qcom,camera@0(主RGB)使用
[ 42.352442] msm8937-pinctrl 1000000.pinctrl: pin-35 (1b0c000.qcom,cci:qcom,camera@0) status -22
[ 42.352450] msm8937-pinctrl 1000000.pinctrl: could not request pin 35 (GPIO_35) from group gpio35 on device 1000000.pinctrl
[ 42.352456] qcom,camera 1b0c000.qcom,cci:qcom,camera@0: Error applying setting, reverse things back
[ 42.403008] msm_camera_power_up:1455 cannot set pin to active state
-------------------------------------------------
写IIC地址的时候,vendor是怎么和kernel层的驱动联系的:
当关闭预览的时候,会调用到:
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor/module/sensor.c
@@ -2181,7 +2181,7 @@ static int32_t sensor_set_stop_stream(void *sctrl)
sensor_lib_params_t *lib = (sensor_lib_params_t *)ctrl->lib_params;
struct sensorb_cfg_data cfg;
- SHIGH("Sensor stream OFF for %s \n",
+ SERR("Sensor stream OFF for %s \n",
lib->sensor_lib_ptr->sensor_slave_info.sensor_name);//当关闭预览的时候会打印出来:07-05 16:50:07.662 531 3410 E mm-camera: <SENSOR><ERROR> 2185: sensor_set_stop_stream: Sensor stream OFF for c2395 这样的log,或者07-05 16:50:19.582 531 3345 E mm-camera: <SENSOR><ERROR> 2185: sensor_set_stop_stream: Sensor stream OFF for c2395_sub
然后接着会调用
rc = sensor_write_i2c_setting_array(ctrl,
&(ctrl->lib_params->sensor_lib_ptr->stop_settings));
int32_t sensor_write_i2c_setting_array(
{
...
cfg.cfgtype = CFG_WRITE_I2C_ARRAY;
cfg.cfg.setting = &setting_k;
if (LOG_IOCTL(ctrl->s_data->fd, VIDIOC_MSM_SENSOR_CFG, &cfg, "write_i2c") < 0) {//通过ioctl接口下一个VIDIOC_MSM_SENSOR_CFG命令到kernel/msm-4.9/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
SERR("failed");
PTHREAD_MUTEX_UNLOCK(&ctrl->s_data->mutex);
return -EIO;
}
...
}
kernel/msm-4.9/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -353,9 +353,14 @@ static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd,
}
switch (cmd) {
case VIDIOC_MSM_SENSOR_CFG://kernel里对该命令的处理
+ pr_err("[wanghl]%s VIDIOC_MSM_SENSOR_CFG\n", __func__);
#ifdef CONFIG_COMPAT
+ pr_err("[wanghl]%s CONFIG_COMPAT\n", __func__);
if (is_compat_task())
+ {
+ pr_err("[wanghl]%s sensor_config32\n", __func__);
rc = s_ctrl->func_tbl->sensor_config32(s_ctrl, argp);//最终跑到这个对应的命令,.sensor_config32 = msm_sensor_config32,msm_sensor_config32会去处理对应的cfgtype,比如写寄存器指令CFG_WRITE_I2C_ARRAY
+ }
static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl,
void *argp)
{
...
if (cdata->cfgtype == CFG_WRITE_I2C_ARRAY)
{
rc = s_ctrl->sensor_i2c_client->i2c_func_tbl->
i2c_write_table(s_ctrl->sensor_i2c_client,//驱动里的i2c_write_table写IIC的接口
&conf_array);
pr_err("[wanghl]%s:%d addr:%x\n", __func__, __LINE__, conf_array.reg_setting->reg_addr);//对应的寄存器地址
pr_err("[wanghl]%s:%d data:%x\n", __func__, __LINE__, conf_array.reg_setting->reg_data);//对应的寄存器的值
}
...
}
=======================================
可否将不同的屏幕不更新驱动,就通过写入不同的配置信息bring up?
[660] [wanghl] cp panel info to panelstruct//先拷贝panel配置信息 -bootable/bootloader/lk/target/msm8952/oem_panel.c
[1750] [wanghl]splash_screen_mmc: enter//再读取splash分区的开机logo数据-bootable/bootloader/lk/app/aboot/aboot.c
所以我要在bootable/bootloader/lk/target/msm8952/oem_panel.c进行splash分区的读取,读取到panel的配置信息,然后填充到panelstruct->paneldata这个结构体
kernel dts
qcom,mdss-dsi-panel-width = <1024>;//1.屏幕宽度
qcom,mdss-dsi-panel-height = <600>;//2.屏幕高度
qcom,mdss-dsi-h-front-porch = <160>;//3.行同步信号的前肩,单位为1VCLK的时间。
qcom,mdss-dsi-h-back-porch = <160>;//4.行同步信号的后肩,单位为1VCLK的时间。
qcom,mdss-dsi-h-pulse-width = <20>;//5.行同步信号的脉宽,单位为1VCLK的时间。
qcom,mdss-dsi-v-back-porch = <23>;//6.帧同步信号的后肩,单位为1行(Line)的时间。
qcom,mdss-dsi-v-front-porch = <12>;//7.帧同步信号的前肩,单位为1行(Line)的时间。
qcom,mdss-dsi-v-pulse-width = <5>;//8.帧同步信号的脉宽,单位为1行(Line)的时间。
qcom,mdss-dsi-bpp = <24>;//一般也不改,不开放
qcom,mdss-dsi-panel-orientation = "180";//9.是否反转180度,取值范围:180:MDP_ROT_180(3), hflip:MDP_FLIP_LR(1), vflip:MDP_FLIP_UD(2)
共10个参数
lk:
static struct panel_config icn6211_video_panel_data = {
"qcom,mdss_dsi_icn6211_video", "dsi:0:", "qcom,mdss-dsi-panel",
10, 0, "DISPLAY_1", 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL
};
struct panel_config{
char *panel_node_id;//"qcom,mdss_dsi_icn6211_video"
char *panel_controller;//"dsi:0:"
char *panel_compatible;//"qcom,mdss-dsi-panel"
uint16_t panel_interface;//10
uint16_t panel_type;//0
char *panel_destination;//"DISPLAY_1"
uint32_t panel_orientation;//0 该选项需要开放出来给客户配置,9.是否反转180度,取值范围:0,1,2,4,3,7
/*
#define MDP_ROT_NOP 0//0:不翻转
#define MDP_FLIP_LR 0x1
#define MDP_FLIP_UD 0x2
#define MDP_ROT_90 0x4
#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR)//也就是3,翻转180度
#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR)
*/
/* panel_clockrate is deprecated in favor of panel_bitclock_freq */
uint32_t panel_clockrate;//0
uint16_t panel_framerate;//60
uint16_t panel_channelid;//0
uint16_t dsi_virtualchannel_id;//0
uint16_t panel_broadcast_mode;//0
uint16_t panel_lp11_init;//0
uint16_t panel_init_delay;//0
uint16_t dsi_stream;//0
uint8_t interleave_mode;//0
uint32_t panel_bitclock_freq;//0
uint32_t panel_operating_mode;//0
uint32_t panel_with_enable_gpio;//0
uint8_t mode_gpio_state;//0
char *slave_panel_node_id;//NULL
};
static struct panel_resolution icn6211_video_panel_res = {
1024, 600, 160, 160, 20, 0, 12, 23, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
struct panel_resolution{
uint16_t panel_width;//1024 该选项需要开放出来给客户配置,1.屏幕宽度
uint16_t panel_height;//600 该选项需要开放出来给客户配置,2.屏幕高度
uint16_t hfront_porch;//160 该选项需要开放出来给客户配置,3.行同步信号的前肩,单位为1VCLK的时间。
uint16_t hback_porch;//160 该选项需要开放出来给客户配置,4.行同步信号的后肩,单位为1VCLK的时间。
uint16_t hpulse_width;//20 该选项需要开放出来给客户配置,5.行同步信号的脉宽,单位为1VCLK的时间。
uint16_t hsync_skew;//0
uint16_t vfront_porch;//12 该选项需要开放出来给客户配置,6.帧同步信号的后肩,单位为1行(Line)的时间。
uint16_t vback_porch;//23 该选项需要开放出来给客户配置,7.帧同步信号的前肩,单位为1行(Line)的时间。
uint16_t vpulse_width;//5 该选项需要开放出来给客户配置,8.帧同步信号的脉宽,单位为1行(Line)的时间。
uint16_t hleft_border;//0
uint16_t hright_border;//0
uint16_t vtop_border;//0
uint16_t vbottom_border;//0
uint16_t hactive_res;//0
uint16_t vactive_res;//0
uint16_t invert_data_polarity;//0
uint16_t invert_vsync_polarity;//0
uint16_t invert_hsync_polarity;//0
};
static struct color_info icn6211_video_color = {
24, 0, 0xff, 0, 0, 0
};
struct color_info{
uint8_t color_format;//24 一般也不改,不开放
uint8_t color_order;//0
uint8_t underflow_color;//0xff
uint8_t border_color;//0
uint8_t pixel_packing;//0
uint8_t pixel_alignment;//0
};
//定义分区数据结构和代码数据结构
//P A N E L ! !
0x50 0x41 0x4E 0x45 0x4C 0x21 0x21 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
//1024 w //600 h //160 hfp //160 hbp //20 hpw //12 vfp //23 vbp //vpw 5
0x04 0x00 0x02 0x58 0x00 0xA0 0x00 0xA0 0x00 0x14 0x00 0x0c 0x00 0x17 0x00 0x05
//lk //kernel
0x00 0x03 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
struct panel_cfg{
uint16_t cfg_panel_width;//1024 1.屏幕宽度
uint16_t cfg_panel_height;//600 2.屏幕高度
uint16_t cfg_hfront_porch;//160 3.行同步信号的前肩,单位为1VCLK的时间。
uint16_t cfg_hback_porch;//160 4.行同步信号的后肩,单位为1VCLK的时间。
uint16_t cfg_hpulse_width;//20 5.行同步信号的脉宽,单位为1VCLK的时间。
uint16_t cfg_vfront_porch;//12 6.帧同步信号的后肩,单位为1行(Line)的时间。
uint16_t cfg_vback_porch;//23 7.帧同步信号的前肩,单位为1行(Line)的时间。
uint16_t cfg_vpulse_width;//5 8.帧同步信号的脉宽,单位为1行(Line)的时间。
uint32_t cfg_panel_orientation;//0 9.是否反转180度,取值范围:0,1,2,4,3,7
};
====================================
LT9611和LT86102以及LT8619C调试问题汇总:
问题现象:只要不开启Launcher,停留在动画之后的“正在启动”界面,屏幕就能一直显示,只要开启Launcher或命令启动任意一个APP,显示就消失黑屏了,
LT9611对mipi信号的要求是Video模式,关闭展频,强制高速输出,关闭动态帧率(否则跑到Launcher会掉图,和PCR实时矫正有关,帧率变化了,除非实时地调整PCR,否则会引起异常),mipi信号杂波影响不大,因为mipi是差分信号,抗干扰能力比较强
LT86102碰到的问题是工作时候温度比较高,正常工作都能跑到50-60度,所以要加散热片,否则的话工作一段时间后,发热太厉害,会影响LT86102输入信号的信号质量,导致输出信号异常,也是会掉图
LT8619C当LVDS屏幕是1920*1080分辨率的时候,前端信号的HDMI信号必须是1920*1080,否则timing不匹配,肯定是亮不了的
PCR(program clock reference),中文可以翻译为节目参考时钟,是DVB传输流里面的一个基础时钟。PCR在解码端有两个非常关键的作用:一是同步头端和终端的27M时钟,并借此同步头端和终端的色度平衡和帧率。二是达到音频和视频相对于系统时间的同步,也在客观上实现了音频和视频之间的同步,但是LT9611的时钟不是这个意思,LT9611的PCR(pixel clock recovery)指的是像素时钟恢复,mipi和hdmi是跨时钟域的信号,PCR模块主要是把mipi信号的timing恢复出来。
For sdm439, it use timing SDM439 DPHY Timing in excel:
必须是使用:qcom,mdss-dsi-panel-timings-phy-12nm = [09 07 0A 02 00 05 02 08];
相关定义如下:
第一个: HSTX_CLKLANE_HS0STATE_TIM_CTRL: 表格第一个数
第二个: HSTX_CLKLANE_TRALSTATE_TIM_CTRL: 表格第三个数
第三个:HSTX_CLKLANE_CLKPOSTSTATE_TIM_CTRL: 表格第五个数
第四个:HSTX_CLKLANE_REQSTATE_TIM_CTRL: 表格第六个数
第五个:HSTX_DATALANE_HS0STATE_TIM_CTRL: 表格第二个数
第六个:HSTX_DATALANE_TRAILSTATE_TIM_CTRL: 表格第四个数
第七个:HSTX_DATALANE_REQSTATE_TIM_CTRL: 表格第七个数
第八个:HSTX_DATALANE_EXITSTATE_TIM_CTRL:表格第九个数
kernel/msm-4.9/drivers/gpu/drm/bridge/lt9611.c 源码自带了一份LT9611的驱动,不过走的是DRM架构的不是FB架构的,所以不能用
struct panel_resolution{
uint16_t panel_width;//1920
uint16_t panel_height;//1080
uint16_t hfront_porch;//88
uint16_t hback_porch;148
uint16_t hpulse_width;//44
uint16_t hsync_skew;
uint16_t vfront_porch;//4
uint16_t vback_porch;//36
uint16_t vpulse_width;//5
uint16_t hleft_border;
uint16_t hright_border;
uint16_t vtop_border;
uint16_t vbottom_border;
uint16_t hactive_res;
uint16_t vactive_res;
uint16_t invert_data_polarity;
uint16_t invert_vsync_polarity;
uint16_t invert_hsync_polarity;
};
The value of invert_vsync_polarity & invert_hsync_polarity has no used in platform side. You don't need to config them.
EDID: Extended Display Identification Data (外部显示设备标识数据)----指DDC通讯中传输的显示设备数据, DDC的全称是Display Data Channel(显示数据通道),顾名思义,它是一个通道(I2C接口)。我们可以说DDC是用来传送EDID信息的,
也可以说EDID信息是通过DDC传送的
LT86102 乐工18126281679
LT8619C 梁工18126281685
LT9611 余工18126281687
LT86102进入fastboot原因:
[3800] [wanghl]thread dtb_overlay_handler() started
[3830] [wanghl] Couldn't find 'cam_sensor_rear_pwdn' symbol in main dtb//真正原因是找不到cam_sensor_rear_pwdn这个节点,编译没检查出来
[3830] [wanghl2]failed to perform fixups in overlay
[3840] [wanghl]Failed to ufdt overlay apply
[3850] [wanghl]ERROR: UFDT apply overlay failed//正常这里应该跑到[3860] DTB overlay is successful
[3850] DTBO Overlay count done: 156304
[3850] ERROR: Appended Device Tree Blob not found
[3860] ERROR: Could not do normal boot. Reverting to fastboot mode.
[3870] recovery() Reverting to default 0x0
[3870] system() Found EXT FS
[3870] cache() Found EXT FS
[3870] userdata() Found EXT FS
[3880] smem ram ptable found: ver: 1 len: 5
[3880] ptn[0]:Name[modem] Size[172032] Type[0] First[131072] Last[303103]
[3890] ptn[1]:Name[fsc] Size[2] Type[0] First[393216] Last[393217]
[3890] ptn[2]:Name[ssd] Size[16] Type[0] First[393224] Last[393239]
[3900] ptn[3]:Name[sbl1] Size[1024] Type[0] First[393240] Last[394263]
[3910] ptn[4]:Name[sbl1bak] Size[1024] Type[0] First[394264] Last[395287]
[3910] ptn[5]:Name[rpm] Size[1024] Type[0] First[395288] Last[396311]
[3920] ptn[6]:Name[rpmbak] Size[1024] Type[0] First[396312] Last[397335]
[3930] ptn[7]:Name[tz] Size[4096] Type[0] First[397336] Last[401431]
[3930] ptn[8]:Name[tzbak] Size[4096] Type[0] First[401432] Last[405527]
[3940] ptn[9]:Name[devcfg] Size[512] Type[0] First[405528] Last[406039]
[3940] ptn[10]:Name[devcfgbak] Size[512] Type[0] First[406040] Last[406551]
[3950] ptn[11]:Name[dsp] Size[32768] Type[0] First[406552] Last[439319]
[3960] ptn[12]:Name[dspbak] Size[32768] Type[0] First[439320] Last[472087]
[3960] ptn[13]:Name[modemst1] Size[3072] Type[0] First[472088] Last[475159]
[3970] ptn[14]:Name[modemst2] Size[3072] Type[0] First[475160] Last[478231]
[3980] ptn[15]:Name[DDR] Size[64] Type[0] First[524288] Last[524351]
[3980] ptn[16]:Name[fsg] Size[3072] Type[0] First[524352] Last[527423]
[3990] ptn[17]:Name[sec] Size[32] Type[0] First[527424] Last[527455]
[4000] ptn[18]:Name[splash] Size[22528] Type[0] First[655360] Last[677887]
[4000] ptn[19]:Name[aboot] Size[2048] Type[0] First[786432] Last[788479]
[4010] ptn[20]:Name[abootbak] Size[2048] Type[0] First[788480] Last[790527]
[4020] ptn[21]:Name[dtbo] Size[16384] Type[0] First[790528] Last[806911]
[4020] ptn[22]:Name[dtbobak] Size[16384] Type[0] First[806912] Last[823295]
[4030] ptn[23]:Name[vbmeta] Size[128] Type[0] First[823296] Last[823423]
[4040] ptn[24]:Name[vbmetabak] Size[128] Type[0] First[823424] Last[823551]
[4040] ptn[25]:Name[boot] Size[131072] Type[0] First[823552] Last[954623]
[4050] ptn[26]:Name[recovery] Size[131072] Type[0] First[954624] Last[1085695]
[4060] ptn[27]:Name[devinfo] Size[2048] Type[0] First[1085696] Last[1087743]
[4060] ptn[28]:Name[system] Size[8388608] Type[0] First[1087744] Last[9476351]
[4070] ptn[29]:Name[vendor] Size[2097152] Type[0] First[9476352] Last[11573503]
[4080] ptn[30]:Name[cache] Size[524288] Type[0] First[11665408] Last[12189695]
[4080] ptn[31]:Name[persist] Size[65536] Type[0] First[12189696] Last[12255231]
[4090] ptn[32]:Name[misc] Size[2048] Type[0] First[12255232] Last[12257279]
[4100] ptn[33]:Name[keystore] Size[1024] Type[0] First[12257280] Last[12258303]
[4100] ptn[34]:Name[config] Size[64] Type[0] First[12258304] Last[12258367]
[4110] ptn[35]:Name[oem] Size[524288] Type[0] First[12258368] Last[12782655]
[4120] ptn[36]:Name[limits] Size[64] Type[0] First[12845056] Last[12845119]
[4120] ptn[37]:Name[mota] Size[1024] Type[0] First[12976128] Last[12977151]
[4130] ptn[38]:Name[dip] Size[2048] Type[0] First[12977152] Last[12979199]
[4140] ptn[39]:Name[mdtp] Size[65536] Type[0] First[12979200] Last[13044735]
[4140] ptn[40]:Name[mdtpbak] Size[65536] Type[0] First[13044736] Last[13110271]
[4150] ptn[41]:Name[syscfg] Size[1024] Type[0] First[13110272] Last[13111295]
[4160] ptn[42]:Name[mcfg] Size[8192] Type[0] First[13111296] Last[13119487]
[4160] ptn[43]:Name[cmnlib] Size[2048] Type[0] First[13238272] Last[13240319]
[4170] ptn[44]:Name[cmnlibbak] Size[2048] Type[0] First[13240320] Last[13242367]
[4180] ptn[45]:Name[cmnlib64] Size[2048] Type[0] First[13242368] Last[13244415]
[4190] ptn[46]:Name[cmnlib64bak] Size[2048] Type[0] First[13244416] Last[13246463]
[4190] ptn[47]:Name[keymaster] Size[2048] Type[0] First[13246464] Last[13248511]
[4200] ptn[48]:Name[keymasterbak] Size[2048] Type[0] First[13248512] Last[13250559]
[4210] ptn[49]:Name[apdp] Size[512] Type[0] First[13369344] Last[13369855]
[4210] ptn[50]:Name[msadp] Size[512] Type[0] First[13369856] Last[13370367]
[4220] ptn[51]:Name[dpo] Size[16] Type[0] First[13370368] Last[13370383]
[4230] ptn[52]:Name[logdump] Size[131072] Type[0] First[13370384] Last[13501455]
[4230] ptn[53]:Name[userdata] Size[17034191] Type[0] First[13501456] Last[30535646]
[4240] smem ram ptable found: ver: 1 len: 5
[4250] smem ram ptable found: ver: 1 len: 5
[4250] fastboot_init()
[4250] Loading keystore failed status 5 [4250] target_serialno: input buf size is 13, board_serial_number_len 11
[4260] serial number: 00000000001
[4360] USB init ept @ 0x8f6fa000
[4380] udc_start()
[4500] -- reset --
[4510] -- portchange --
[4620] -- reset --
[4630] -- portchange --
[4760] fastboot: processing commands
原因:git diff kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8937-pinctrl-SDM439.dtsi
- cam_sensor_rear_pwdn: cam_sensor_rear_pwdn {//果然误删了cam_sensor_rear_pwdn这个节点
- /* RESET */
- mux {
- pins = "gpio50";
- function = "gpio";
- };
-
- config {
- pins = "gpio50";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
- };
-
- cam_sensor_rear_pwdn_sleep: cam_sensor_rear_pwdn_sleep {
- /* RESET */
- mux {
- pins = "gpio50";
- function = "gpio";
- };
-
- config {
- pins = "gpio50";
- bias-disable; /* No PULL */
- drive-strength = <2>; /* 2 MA */
- };
- };
把这个改回去之后,编译make dtboimage还是不行,但是如果在 kernel/msm-4.9/arch/arm64/boot/dts/qcom/sdm439-camera-sensor-SDM439.dtsi
qcom,camera@0 {
...
pinctrl-0 = <&cam_sensor_mclk0_default
&cam_sensor_rear_reset
&cam_sensor_rear_vana
/*&cam_sensor_rear_pwdn*/>;
pinctrl-1 = <&cam_sensor_mclk0_sleep
&cam_sensor_rear_reset_sleep
&cam_sensor_rear_vana_sleep
/*&cam_sensor_rear_pwdn_sleep*/>;
注释掉这个pin的引用却可以正常开机
因为:msm8937-pinctrl-SDM439.dtsi是被主dts(base):sdm439-base-SDM439.dts层层包含的,包含关系为:sdm439-base-SDM439.dts->sdm439-base-SDM439.dtsi->msm8937-SDM439.dtsi->msm8937-pinctrl-SDM439.dtsi
kernel/msm-4.9/arch/arm64/boot/dts/qcom/Makefile
...
ifeq ($(CONFIG_BUILD_ARM64_DT_SELECT_ONLY_SDM439_QRD_5),y)
sdm439-SDM439-overlay.dtbo-base := sdm439-base-SDM439.dtb
else
...
编译生成目录在:out/target/product/SDM439/obj/kernel/msm-4.9/arch/arm64/boot/dts/qcom/sdm439-base-SDM439.dtb
所以修改了主设备树,可能要通过make bootimage -j12编译才会生效
而sdm439-camera-sensor-SDM439.dtsi是被叠加设备树sdm439-SDM439-overlay.dts包含,包含关系为:sdm439-SDM439-overlay.dts->sdm439-SDM439.dtsi->sdm439-camera-sensor-SDM439.dtsi
而make dtboimage就是能编译到叠加设备树的,这也就是为什么修改sdm439-camera-sensor-SDM439.dtsi后make dtboimage -j12后能够正常跑下去的原因
kernel/msm-4.9/arch/arm64/boot/dts/qcom/Makefile
...
ifeq ($(CONFIG_BUILD_ARM64_DT_SELECT_ONLY_SDM439_QRD_5),y)
dtbo-$(CONFIG_ARCH_SDM439) += sdm439-SDM439-overlay.dtbo
else
...
编译生成目录在:out/target/product/SDM439/obj/kernel/msm-4.9/arch/arm64/boot/dts/qcom/sdm439-SDM439-overlay.dtbo
对比make bootimage 和make dtboimage的编译log,基本一样,所以最好修改dts之后,两个都要编译
====================
单独编译launcher3出错的问题!
mmm packages/apps/Launcher3
PRODUCT_COPY_FILES device/qcom/common/media/media_profiles.xml:system/etc/media_profiles.xml ignored.
PRODUCT_COPY_FILES device/qcom/common/media/media_codecs.xml:system/etc/media_codecs.xml ignored.
No private recovery resources for TARGET_DEVICE msm8909
vendor/qcom/build/tasks/generate_extra_images.mk:283: warning for parse error in an unevaluated line: *** commands commence before first target.
Starting build with ninja
ninja: Entering directory `.'
ninja: error: 'out/host/linux-x86/framework/host-libprotobuf-java-nano.jar', needed by 'out/host/common/obj/JAVA_LIBRARIES/launcher_proto_lib_intermediates/classes-full-debug.jar', missing and no known rule to make it
make: *** [ninja_wrapper] 错误 1
make:离开目录“/home/wanghl/code/msm8909w_android”
解决办法:
mmma packages/apps/Launcher3(加需要的库文件)
===================================
关于在内核实现timer
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
struct timer_list mytimer;
static void myfunc(unsigned long data)
{
static u32 cnt = 0;
printk("%s,cnt = %d\n", (char *)data, cnt);
cnt++;
mod_timer(&mytimer, jiffies + 2*HZ);//修改定时器定时时间为2S,注意这个timer里面不允许做休眠操作,否则的会系统会崩溃:BUG: sleeping function called from invalid context at /home/wanghl/code/sdm439_android/kernel/msm-4.9/kernel/locking/mutex.c:98
}
static int __init mytimer_init(void)//这步骤可以在驱动probe的时候调用
{
setup_timer(&mytimer, myfunc, (unsigned long)"Hello, world!");
mytimer.expires = jiffies + HZ;//初始化,第一次定时时间为1S
add_timer(&mytimer);
return 0;
}
static void __exit mytimer_exit(void)
{
del_timer(&mytimer);
}
module_init(mytimer_init);
module_exit(mytimer_exit);
打印log如下:
[ 10.533360] [LT86102][Info]setup_timer
[ 11.530785] Hello, world!,cnt = 0//第一次定时1S
[ 13.530782] Hello, world!,cnt = 1//后面定时2S
[ 15.530780] Hello, world!,cnt = 2
[ 17.530788] Hello, world!,cnt = 3
[ 19.610821] Hello, world!,cnt = 4
[ 21.610810] Hello, world!,cnt = 5
//有一条log没有打印出来,说明不是所有log都会打印出来
[ 25.610843] Hello, world!,cnt = 7
[ 27.610827] Hello, world!,cnt = 8
[ 29.610830] Hello, world!,cnt = 9
[ 31.610825] Hello, world!,cnt = 10
[ 33.610843] Hello, world!,cnt = 11
[ 35.610816] Hello, world!,cnt = 12
[ 37.610855] Hello, world!,cnt = 13
[ 39.611014] Hello, world!,cnt = 14
[ 41.610847] Hello, world!,cnt = 15
[ 43.610932] Hello, world!,cnt = 16
[ 45.610888] Hello, world!,cnt = 17
[ 47.610896] Hello, world!,cnt = 18
[ 49.610888] Hello, world!,cnt = 19
[ 51.612314] Hello, world!,cnt = 20
[ 53.612243] Hello, world!,cnt = 21
[ 55.611968] Hello, world!,cnt = 22
[ 57.611059] Hello, world!,cnt = 23
[ 59.612309] Hello, world!,cnt = 24
[ 61.612319] Hello, world!,cnt = 25
关于HZ的大小:
LT86102_INFO("setup_timer, HZ = %d", HZ);
//[ 10.373354] [LT86102][Info]setup_timer, HZ = 100
jiffies记录了系统启动以来,经过了多少tick。
一个tick代表多长时间,在内核的CONFIG_HZ中定义。比如CONFIG_HZ=100,则一个jiffies对应10ms时间。所以内核基于jiffies的定时器精度也是10ms。
//修改定时时间为10ms
static void myfunc(unsigned long data)
{
static u32 cnt = 0;
if(cnt % 100 == 0)//每(100*10ms=1000ms=1s)一秒打印一次
printk("%s,cnt = %d\n", (char *)data, cnt);
cnt++;
//mod_timer(<86102_timer_10ms, jiffies + 2*HZ);//set 2s
mod_timer(<86102_timer_10ms, jiffies + msecs_to_jiffies(10));//设置定时器时间为10ms
}
[ 10.373354] [LT86102][Info]setup_timer, HZ = 100
[ 11.370770] Hello, world!,cnt = 0
[ 12.370777] Hello, world!,cnt = 100
[ 13.390778] Hello, world!,cnt = 200
[ 14.392231] Hello, world!,cnt = 300
[ 15.390792] Hello, world!,cnt = 400
===================================================================================================
关于内核线程:让驱动模块在加载后能一直运行下去的方法——内核线程
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
MODULE_LICENSE("Dual BSD/GPL");
static struct task_struct *tsk;//用户定义进程描述符,linux中并不对进程和线程做强制区分
static int thread_function(void *data)
{
int time_count = 0;
do {
printk(KERN_INFO "thread_function: %d times", ++time_count);//每秒打印一次
msleep(1000);//为了不让创建的内核线程一直运行浪费CPU,代码中采用周期性延迟的方式,每次循环用msleep(1000)延迟1s。
}while(!kthread_should_stop() && time_count<=30);//打印30次退出,kthread_should_stop()返回should_stop标志。它用于创建的线程检查结束标志,并决定是否退出。
return time_count;
}
static int hello_init(void)
{
printk(KERN_INFO "Hello, world!\n");
tsk = kthread_run(thread_function, NULL, "mythread%d", 1);//kthread_run()负责内核线程的创建,参数包括入口函数threadfn,参数data,线程名称namefmt。可以看到线程的名字可以是类似sprintf方式组成的字符串。如果实际看到kthread.h文件,就会发现kthread_run实际是一个宏定义,它由kthread_create()和wake_up_process()两部分组成,这样的好处是用kthread_run()创建的线程可以直接运行,使用方便。在运行过程中使用ps -e命令,可以看到有名字位mythread1的内核线程在运行。
if (IS_ERR(tsk)) {
printk(KERN_INFO "create kthread failed!\n");
}
else {
printk(KERN_INFO "create ktrhead ok!\n");
}
return 0;
}
static void hello_exit(void)//rmmod 卸载模块,线程将消失。
{
printk(KERN_INFO "Hello, exit!\n");
if (!IS_ERR(tsk)){
int ret = kthread_stop(tsk);//kthread_stop()负责结束创建的线程,参数是创建时返回的task_struct指针。kthread设置标志should_stop,并等待线程主动结束,返回线程的返回值。线程可能在kthread_stop()调用前就结束。(经过实际验证,如果线程在kthread_stop()调用之前就结束,之后kthread_stop()再调用会发生可怕地事情—调用kthread_stop()的进程crash!!之所以如此,是由于kthread实现上的弊端)
printk(KERN_INFO "thread function has run %ds\n", ret);//打印线程运行的时间
}
}
module_init(hello_init);
module_exit(hello_exit);
---------------------------
函数原型:
struct task_struct kthread_run(int (*threadfn)(void *data),
void *data, const char namefmt[],...);//用户创建一个线程并运行函数原型如下kthread_run(threadfn, data, namefmt, ...),threadfn是线程被唤醒后执行的方法,
int kthread_stop(struct task_struct *k);//用于结束一个线程的运行,需要注意的是调用此方法时,该线程必须不能已经结束,否则后果严重
int kthread_should_stop(void);//用户返回结束标志
tsk = kthread_run(thread_function, lt86102_data, "hdmi_splitter");
ps -e | grep "hdmi"
UID PID PPID C STIME TTY TIME CMD
root 174 2 0 0 0 0 R [hdmi_splitter]//[]包起来表示内核线程
过程中发现了特殊的2号进程,顺带说明一下,主要是原理性东东。Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2)
* idle进程由系统自动创建, 运行在内核态,idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。完成加载系统后,演变为进程调度、交换
* init进程由idle通过kernel_thread创建,在内核空间完成初始化后, 加载init程序, 并最终用户空间由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。
* kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理.它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程
所有其它的内核线程的ppid 都是 2,也就是说它们都是由kthreadd thread创建的
内核API的实现原理:
kthread的实现在kernel/kthread.c中,头文件是include/linux/kthread.h。内核中一直运行一个线程kthreadd,它运行kthread.c中的kthreadd函数。在kthreadd()中,不断检查一个kthread_create_list链表。
kthread_create_list中的每个节点都是一个创建内核线程的请求,kthreadd()发现链表不为空,就将其第一个节点退出链表,并调用create_kthread()创建相应的线程。create_kthread()则进一步调用更深层的kernel_thread()创建线程,
入口函数设在kthread()中。
外界调用kthread_run创建运行线程。kthread_run是个宏定义,首先调用kthread_create()创建线程,如果创建成功,再调用wake_up_process()唤醒新创建的线程。kthread_create()根据参数向kthread_create_list中发送一个请求,
并唤醒kthreadd,之后会调用wait_for_completion(&create.done)等待线程创建完成。新创建的线程开始运行后,入口在kthread(),kthread()调用complete(&create->done)唤醒阻塞的模块进程,并使用schedule()调度出去。
kthread_create()被唤醒后,设置新线程的名称,并返回到kthread_run中。kthread_run调用wake_up_process()重新唤醒新创建线程,此时新线程才开始运行kthread_run参数中的入口函数。
外界调用kthread_stop()删除线程。kthread_stop首先设置结束标志should_stop,然后调用wake_for_completion(&kthread->exited)上,这个其实是新线程task_struct上的vfork_done,会在线程结束调用do_exit()时设置。
==================================
HDMI一转二使用MCU控制,但是LT8619(HDMI转LVDS)还是没有显示:
HDMI信号输入(CLK和DATA)有信号
HPD也是拉高的
输出端的HS,VS,DE,都没有波形,这个是正常的TTL电平才需要,因为我们是使用LVDS差分信号,所以不需要是正常的
LVDS输出波形(CLK有信号(频率为74MHz的正弦波),DATA输出也是有波形的)
LVDS屏幕的座子上只有LVDS差分信号和VDD(查规格书是5V),那唯一的可能就是VDD没给过来,示波器一量果然没有信号,一查原理图发现是供电的跳线帽没接,一接就有图像输出了
==========================
三色RGB呼吸灯控制文件节点:
sys/class/leds/red/
sys/class/leds/green/
sys/class/leds/blue/
驱动:
修改: kernel/msm-4.9/drivers/leds/led-class.c
修改: kernel/msm-4.9/drivers/leds/led-core.c
修改: kernel/msm-4.9/drivers/leds/leds-qti-tri-led.c
======================
use GPIO42 to generate CLK
1.msm8937-pinctrl-SDM439.dtsi
/*for PWM CLK test start*/
pmx_gpio_pwm_default{
gpio_pwm_default: gpio_pwm_default {
mux {
pins = "gpio42";
function = "gcc_gp1_clk_a";
};
config {
pins = "gpio42";
drive-strength = <16>;// 2mA
bias-disable;// No PULL
};
};
};
/*for PWM CLK test stop*/
2.sdm439-SDM439.dtsi
&i2c_5 { /* BLSP2 QUP1 (NFC) */
status = "ok";
/*nq@28 {
compatible = "qcom,nq-nci";
reg = <0x28>;
qcom,nq-irq = <&tlmm 17 0x00>;
qcom,nq-ven = <&tlmm 16 0x00>;
//qcom,nq-firm = <&tlmm 130 0x00>;
qcom,nq-clkreq = <&pm8953_gpios 2 0x00>;
qcom,nq-esepwr = <&tlmm 93 0x00>;
interrupt-parent = <&tlmm>;
qcom,clk-src = "BBCLK2";
interrupts = <17 0>;
interrupt-names = "nfc_irq";
pinctrl-names = "nfc_active", "nfc_suspend";
pinctrl-0 = <&nfc_int_active &nfc_disable_active
&nfc_clk_default>;
pinctrl-1 = <&nfc_int_suspend &nfc_disable_suspend>;
clocks = <&clock_gcc clk_bb_clk2_pin>;
clock-names = "ref_clk";
};*/
lontium@0x39{
compatible = "lt9611,mipi2hdmi";
reg = <0x39>;//write:0x72,read:0x73
interrupt-parent = <&tlmm>;
interrupts = <43 0x2008>;
lontium,irq-gpio = <&tlmm 43 0x2008>;
interrupt-names = "lt9611_irq";
vcc_i2c-supply = <&pm8953_l6>;//VREG_CTP_1P8 used for I2C pull up voltage
lontium,rest-gpio = <&tlmm 20 0x00>;
pinctrl-names = "pmx_lt9611_active","pmx_lt9611_suspend","pmx_lt9611_release", "pmx_gpio_pwm_default";//add pmx_gpio_pwm_default
pinctrl-0 = <<9611_int_active <9611_rst_active>;
pinctrl-1 = <<9611_int_suspend <9611_rst_suspend>;
pinctrl-2 = <<9611_release>;
//add for gpio42 clk output
pinctrl-3 = <&gpio_pwm_default>;
clocks = <&clock_gcc clk_gp1_clk_src>;
clock-names = "gpio-pwm-clk";
};
};
3.clock-gcc-8952.c
//37500 = xo(19200000)/16(预分频)*1(M)/32(N)
static struct clk_freq_tbl ftbl_gcc_gp1_3_clk[] = {
F( 37500, xo, 16, 1, 32),
F( 25000000, gpll0, 2, 1, 16), //M is 1, N is 16
F_END
};
4.驱动里面添加头文件:
#include <linux/clk.h>
#include <soc/qcom/clock-local2.h>
#include <linux/io.h>
添加宏定义
#define CBCR_OFFSET 0x0
#define CMD_RCGR_OFFSET 0x4
#define D_OFFSET 0x14
#define GP1_CLK_BASE 0x8000
其他代码添加:
probe函数添加设置流程:
...
//test PWM--start
struct clk *pclk;
struct rcg_clk *gp1_rcg_clk;
unsigned int level = 33; //range of level is 0~100,占空比
//test PWM--end
...
ret = lt9611_pinctrl_init(lt9611_data);
if (0 == ret) {
lt9611_pinctrl_select_normal(lt9611_data);
}
//test PWM -------------------start
//pclk = devm_clk_get(&client->dev, "gpio-pwm-clk");
pclk = clk_get(&client->dev, "gpio-pwm-clk");
if(pclk != NULL)
{
ret = clk_set_rate(pclk, 37500);
if (ret)
LT9611_ERROR("clk set rate fail, ret = %d\n", ret);
}
else
{
LT9611_ERROR("gpio-pwm-clk not available\n");
}
ret = clk_prepare_enable(pclk);
if (ret)
LT9611_ERROR("%s: clk_prepare error!!!\n", __func__);
else
LT9611_ERROR("%s: clk_prepare success!\n", __func__);
gp1_rcg_clk = to_rcg_clk(pclk);
writel_relaxed(((~(32 * level / 50)) & 0x0ff), *gp1_rcg_clk->base + GP1_CLK_BASE + D_OFFSET); //16 is the value of N, if just output clock, please remove this line.
mb();
writel_relaxed(0x3, *gp1_rcg_clk->base+ GP1_CLK_BASE + CMD_RCGR_OFFSET); //RCGR
mb();
writel_relaxed(0x1, *gp1_rcg_clk->base + GP1_CLK_BASE + CBCR_OFFSET); //CBCR
//test PWM -------------------------end
几个调用到的子函数:
static int lt9611_pinctrl_init(struct lt9611_data *lt9611)
{
int ret = 0;
struct i2c_client *client = lt9611->client;
lt9611->pinctrl = devm_pinctrl_get(&client->dev);
if (IS_ERR_OR_NULL(lt9611->pinctrl)) {
LT9611_ERROR("Failed to get pinctrl, please check dts");
ret = PTR_ERR(lt9611->pinctrl);
goto err_pinctrl_get;
}
lt9611->pins_active = pinctrl_lookup_state(lt9611->pinctrl, "pmx_lt9611_active");
if (IS_ERR_OR_NULL(lt9611->pins_active)) {
LT9611_ERROR("Pin state[active] not found");
ret = PTR_ERR(lt9611->pins_active);
goto err_pinctrl_lookup;
}
lt9611->pins_suspend = pinctrl_lookup_state(lt9611->pinctrl, "pmx_lt9611_suspend");
if (IS_ERR_OR_NULL(lt9611->pins_suspend)) {
LT9611_ERROR("Pin state[suspend] not found");
ret = PTR_ERR(lt9611->pins_suspend);
goto err_pinctrl_lookup;
}
lt9611->pins_release = pinctrl_lookup_state(lt9611->pinctrl, "pmx_lt9611_release");
if (IS_ERR_OR_NULL(lt9611->pins_release)) {
LT9611_ERROR("Pin state[release] not found");
ret = PTR_ERR(lt9611->pins_release);
}
//test PWM
lt9611->pins_default= pinctrl_lookup_state(lt9611->pinctrl, "pmx_gpio_pwm_default");
if (IS_ERR_OR_NULL(lt9611->pins_default)) {
LT9611_ERROR("Pin state[default] not found");
ret = PTR_ERR(lt9611->pins_default);
}
return 0;
err_pinctrl_lookup:
if (lt9611->pinctrl) {
devm_pinctrl_put(lt9611->pinctrl);
}
err_pinctrl_get:
lt9611->pinctrl = NULL;
lt9611->pins_release = NULL;
lt9611->pins_suspend = NULL;
lt9611->pins_active = NULL;
return ret;
}
static int lt9611_pinctrl_select_normal(struct lt9611_data *lt9611)
{
int ret = 0;
if (lt9611->pinctrl && lt9611->pins_active) {
ret = pinctrl_select_state(lt9611->pinctrl, lt9611->pins_active);
if (ret < 0) {
LT9611_ERROR("Set normal pin state error:%d", ret);
}
}
if (lt9611->pinctrl && lt9611->pins_default) {
ret = pinctrl_select_state(lt9611->pinctrl, lt9611->pins_default);
if (ret < 0) {
LT9611_ERROR("Set default pin state error:%d", ret);
}
}
return ret;
}
=====================================
测试:
u16 test;
u8 regval[2];
regval[0] = 0x12;
regval[1] = 0x34;
test = (regval[0]<<8) + /*HDMI_ReadI2C_Byte(0x87)*/regval[1];
LT9611_INFO("test = 0x%x", test);
结果打印如下:
[ 5.450872] [LT9611][Info]test = 0x1234
说明u8的数据左移这样没有问题
==================================
LT86102 EDID分析:
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x32, 0x8D, 0x01, 0x04, 0x90, 0x01, 0x03, 0x6A,
0x2F, 0x16, 0x01, 0x03, 0x80, 0x59, 0x32, 0x78, 0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27,
0x12, 0x48, 0x4C, 0x21, 0x08, 0x00, 0x81, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1D, 0x00, 0x72, 0x51, 0xD0, 0x1E, 0x20, 0x6E, 0x28,
0x55, 0x00, 0x76, 0xF2, 0x31, 0x00, 0x00, 0x1E, 0x02, 0x3A, 0x80, 0xD0, 0x72, 0x38, 0x2D, 0x40,
0x10, 0x2C, 0x45, 0x80, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4C,
0x4F, 0x4E, 0x54, 0x49, 0x55, 0x4D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
0x00, 0x30, 0x3E, 0x0E, 0x46, 0x0F, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xEA,
0x02, 0x03, 0x35, 0xC1, 0x53, 0x1F, 0x90, 0x14, 0x05, 0x13, 0x04, 0x20, 0x22, 0x00, 0x00, 0x12,
0x16, 0x03, 0x07, 0x11, 0x15, 0x02, 0x06, 0x01, 0x26, 0x09, 0x07, 0x01, 0x83, 0x01, 0x00, 0x00,
0x74, 0x03, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x2F, 0xC0, 0x0A, 0x01, 0x40, 0x00, 0x7F, 0x20,
0x30, 0x70, 0x80, 0x90, 0x76, 0x02, 0x3A, 0x80, 0xD0, 0x72, 0x38, 0x2D, 0x40, 0x10, 0x2C, 0x45,
0x80, 0x10, 0x09, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x1D, 0x00, 0xBC, 0x52, 0xD0, 0x1E, 0x20, 0xB8,
0x28, 0x55, 0x40, 0x76, 0xF2, 0x31, 0x00, 0x00, 0x1E, 0x01, 0x1D, 0x80, 0xD0, 0x72, 0x1C, 0x16,
0x20, 0x10, 0x2C, 0x25, 0x80, 0x76, 0xF2, 0x31, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA
关于散热的问题:做实验如下:
使用video_1024x768_60Hz这个分辨率的情况下:1)pattern模式可以稳定输出 2)正常模式也可以稳定输出(甚至不用加散热片)
使用video_1920x1080_60Hz这个分辨率的情况下:1)pattern模式无输出(这个需要余工用单片机验证下) 2)正常模式下工作两三分钟后无图像,当图像消失的时候,交流分量没有波形,直流分量还有一个3.3V左右的直流分量
发热挂掉的时候的log:
[ 88.900840] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 88.971668] [LT9611][Info]
[ 88.971668] port A byte clk = 111296
[ 89.041128] [LT9611][Info]
[ 89.041128] port B byte clk unstable
[ 90.891254] [LT9611][Info]timer_func_chk
[ 91.931423] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 91.993395] [LT9611][Info]
[ 91.993395] port A byte clk = 111296
[ 92.051990] [LT9611][Info]
[ 92.051990] port B byte clk unstable
[ 93.931004] [LT9611][Info]timer_func_chk
[ 94.489607] [LT9611][Info]lontium_lt9611_interrupt
[ 94.489661] [LT9611][Info]
[ 94.489661] Enter IRQ Task:
[ 94.495408] [LT9611][Info]HDMI disconnected//发烫厉害的时候,如果没有风扇,图像消失,打印HDMI断开,量波形LT86102的HPD_RX从3.3V被拉低,而且不是代码拉低的,没看到LT86102拉低相关的log输出,是被意外拉低的
[ 94.971482] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 95.042804] [LT9611][Info]
[ 95.042804] port A byte clk = 111296//但是图像消失的时候CLK还是稳定的
[ 95.114600] [LT9611][Info]
[ 95.114600] port B byte clk unstable
[ 96.971164] [LT9611][Info]timer_func_chk
[ 98.011459] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 98.081789] [LT9611][Info]
[ 98.081789] port A byte clk = 111296//但是图像消失的时候CLK还是稳定的
[ 98.151249] [LT9611][Info]
[ 98.151249] port B byte clk unstable
[ 100.011165] [LT9611][Info]timer_func_chk
[ 101.060840] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 101.131938] [LT9611][Info]
[ 101.131938] port A byte clk = 111296//但是图像消失的时候CLK还是稳定的
[ 101.201230] [LT9611][Info]
[ 101.201230] port B byte clk unstable
图像消失的时候,打印log:加入Pcr打印里的MK值的0x97寄存器的值的bit4为1,说明mipi转HDMI的频率转换,timing获取,转换输出已经正常由TX模块输出了,转换是没有问题的!
...
初始化的时候打印:
[ 6.021170] [LT9611][Info]
[ 6.021170] M:40 36 fb 98 f2
[ 6.021170]
[ 7.052078] [LT9611][Info]
[ 7.052078] M:40 36 fb 98 f2
[ 7.052078]
[ 8.092002] [LT9611][Info]
[ 8.092002] M:40 36 fb 98 f2
[ 8.092002]
[ 9.132012] [LT9611][Info]
[ 9.132012] M:40 36 fb 98 f2
[ 9.132012]
[ 10.172100] [LT9611][Info]
[ 10.172100] M:40 36 fb 98 f2
[ 10.172100]
[ 11.212172] [LT9611][Info]
[ 11.212172] M:40 36 fb 98 f2
[ 11.212172]
[ 12.252149] [LT9611][Info]
[ 12.252149] M:40 36 fb 98 f2
[ 12.252149]
[ 13.292226] [LT9611][Info]
[ 13.292226] M:40 36 fb 98 f2
...
后面跑起来后和挂掉之后的打印:
[ 199.531204] [LT9611][Info]timer_func_chk
[ 200.571309] [LT9611][Info]LT9611_Pcr_MK_Print
[ 200.575310] [LT9611][Info]
[ 200.575310] M:0x52 0x36 0xf6 0x23 0xe,bit4 0f value(0x97) is 1
[ 202.571161] [LT9611][Info]timer_func_chk
[ 203.612447] [LT9611][Info]LT9611_Pcr_MK_Print
[ 203.616684] [LT9611][Info]
[ 203.616684] M:0x52 0x36 0xf6 0x26 0x8,bit4 0f value(0x97) is 1
[ 203.616684]
[ 205.611160] [LT9611][Info]timer_func_chk
[ 206.651126] [LT9611][Info]LT9611_Pcr_MK_Print
[ 206.654328] [LT9611][Info]
[ 206.654328] M:0x52 0x36 0xf6 0x23 0x15,bit4 0f value(0x97) is 1
[ 206.654328]
[ 206.972567] healthd: battery l=50 v=4173 t=25.1 h=2 st=3 c=88 fc=3045000 cc=0 chg=
[ 208.651162] [LT9611][Info]timer_func_chk
[ 209.691009] [LT9611][Info]LT9611_Pcr_MK_Print
[ 209.693864] [LT9611][Info]
[ 209.693864] M:0x52 0x36 0xf6 0x26 0x76,bit4 0f value(0x97) is 1
[ 211.691154] [LT9611][Info]timer_func_chk
[ 212.731350] [LT9611][Info]LT9611_Pcr_MK_Print
[ 212.734929] [LT9611][Info]
[ 212.734929] M:0x52 0x36 0xf6 0x24 0xf7,bit4 0f value(0x97) is 1
//正常拔出HDMI线,量LT86102的RX HPD波形先拉低500ms又拉高
[ 55.183223] [LT86102][Info]Tx HPD Change
[ 55.183267] [LT86102][Info]g_Old_Tx_Hpd = 8
[ 55.385310] [LT86102][Info]TX0 HDMI MODE
[ 55.386126] [LT86102][Info]TX1 HDMI MODE
[ 55.389081] [LT86102][Info]TX2 HDMI MODE
[ 55.393718] [LT86102][Info]TX3 HDMI MODE
[ 55.450209] [LT86102][Info]RX HPD LOW//LT86102手动拉低RX HPD
[ 55.451645] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 55.532299] [LT9611][Info]
[ 55.532299] port A byte clk = 111296
[ 55.601294] [LT9611][Info]
[ 55.601294] port B byte clk unstable
[ 55.762273] [LT9611][Info]lontium_lt9611_interrupt//LT9611的HPD_GPIO2(直连LT86102的RX HPD)被拉低,所以产生中断
[ 55.762342] [LT9611][Info]
[ 55.762342] Enter IRQ Task:
[ 55.768651] [LT9611][Info]HDMI disconnected//中断原因是:LT9611 检测到HDMI断开
[ 55.949368] sps: BAM device 0x0000000007884000 is not registered yet.
[ 55.950415] sps:BAM 0x0000000007884000 is registered.[ 55.958788] sps:BAM 0x0000000007884000 (va:0x0000000000000000) enabled: ver:0x19, number of pipes:12
[ 55.979051] [LT86102][Info]RX HPD HIGH//LT86102手动又拉高RX HPD
[ 56.291853] [LT9611][Info]lontium_lt9611_interrupt
[ 56.291983] [LT9611][Info]
[ 56.291983] Enter IRQ Task:
[ 56.297908] [LT9611][Info]HDMI connected//LT9611 检测到HDMI又连接上
[ 56.306010] [LT86102][Info]Enter IRQ Task:
[ 56.312218] [LT86102][Info]CLK RISE INT
[ 56.312628] [LT86102][Info]clear interrupt flag, do nothing
[ 57.451181] [LT9611][Info]timer_func_chk
[ 58.500910] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 58.573658] [LT9611][Info]
[ 58.573658] port A byte clk = 111297
[ 58.641342] [LT9611][Info]
[ 58.641342] port B byte clk unstable
[ 59.742817] init: starting service 'vendor.vm_bms'...
[ 59.793026] init: Service 'vendor.vm_bms' (pid 3111) exited with status 255
[ 59.793126] init: Sending signal 9 to service 'vendor.vm_bms' (pid 3111) process group...
[ 59.799116] libprocessgroup: Successfully killed process cgroup uid 0 pid 3111 in 0ms
[ 60.491777] [LT9611][Info]timer_func_chk
[ 61.531556] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 61.593227] [LT9611][Info]
[ 61.593227] port A byte clk = 111296
[ 61.651730] [LT9611][Info]
[ 61.651730] port B byte clk unstable
[ 63.531029] [LT9611][Info]timer_func_chk
[ 64.580901] [LT9611][Info]LT9611_Frequency_Meter_Byte_Clk
[ 64.653669] [LT9611][Info]
[ 64.653669] port A byte clk = 111296
[ 64.711557] [LT9611][Info]
[ 64.711557] port B byte clk unstable
如果LT9611中断检测到LT86102的HPD_RX拉低之后,就会关闭HDMI输出,那如果不关闭HDMI输出行不行??
如果注释掉LT9611中断里,HDMI断掉后的寄存器操作,就会不停地进入中断,这是因为没有清除中断标志,只注释掉关闭HDMI的操作,不注释掉清除寄存器操作就可以持续输出HDMI波形,但是LT86102仍然没办法正常输出波形,
RX_HPD没办法正常拉高,正常代码里是拉低500ms之后就会再拉高,但是LT86102一直没有看到被拉高,代码log也没打印出拉高的log,所以感觉还是LT86102挂掉了!
后来CTO怀疑温度升高导致电压跌落,于是看了供电LDO的跌落电压与温度的曲线图,发现温度升高,电压确实会跌落,后来硬件老大就用示波器的触发模式来抓取问题复现的电压波形
实际上是LT86102的1.8V供电电压从1.8V跌落到1.32V了,正常工作最低电压要到1.62V,所以是硬件掉电了,然后HPD就一直被拉低了,换成用稳压源供电就不会,因为LDO的特性是温度高了会有电压跌落,就是会有压降,压降导致LT86102瞬间掉电了。
=================================================
64bit-kernel启动log:
[5010] Channel alloc freed
[5020] free_verified_boot_resource
[5020] booting linux @ 0x80080000, ramdisk @ 0x83600000 (0), tags/device tree @ 0x83400000
[5030] Jumping to kernel via monitor//64bit的会多打印这一句
[ 0.000000] Booting Linux on physical CPU 0x100
[ 0.000000] Linux version 4.9.112 (wanghl@wanghl-HP) (gcc version 4.9.x 20150123 (prerelease) (GCC) ) #24 SMP PREEMPT Mon Jul 8 17:35:26 CST 2019
[ 0.000000] Boot CPU: AArch64 Processor [410fd034]
[ 0.000000] Machine: QRD, pubtron SDM439
[ 0.000000] earlycon: msm_serial_dm0 at MMIO 0x00000000078b0000 (options '')
[ 0.000000] bootconsole [msm_serial_dm0] enabled
32bit-kernel启动log:
[4540] Channel alloc freed
[4550] free_verified_boot_resource
[4550] booting linux @ 0x80008000, ramdisk @ 0x83600000 (0), tags/device tree @ 0x83400000//现在的问题是加入i2c节点后log卡在这里了
[ 0.000000] Booting Linux on physical CPU 0x100
[ 0.000000] Linux version 4.9.112 (wanghl@wanghl-HP) (gcc version 4.9.x 20150123 (prerelease) (GCC) ) #1 SMP PREEMPT Mon Aug 12 09:39:33 CST 2019
[ 0.000000] CPU: ARMv7 Processor [410fd034] revision 4 (ARMv7), cr=10c0383d
[ 0.000000] CPU: div instructions available: patching division code
[ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[ 0.000000] OF: fdt:Machine model: QRD, pubtron A60
===================================================================================================
将产品编译成平板电脑的方法:
build/make/core/product_config.mk
@@ -317,9 +317,12 @@ ifndef PRODUCT_MANUFACTURER
endif
ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CHARACTERISTICS),)
+ $(warning [wanghl1])
TARGET_AAPT_CHARACTERISTICS := default
else
- TARGET_AAPT_CHARACTERISTICS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CHARACTERISTICS))
+ $(warning [wanghl2])
+ #TARGET_AAPT_CHARACTERISTICS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CHARACTERISTICS))
+ TARGET_AAPT_CHARACTERISTICS := tablet
endif
======================
SDM439打开recovery 下uart的打印:
1)bootable/recovery/recovery.cpp
-static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
+//static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
+static const char *TEMPORARY_LOG_FILE = "/dev/ttyMSM0";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
@@ -1191,7 +1192,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
ui->SetProgressType(RecoveryUI::EMPTY);
int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), false, 0, device);
-
+ ui->Print("[wanghl]chosen_item = %d\n", chosen_item);
2)device/qcom/sepolicy/vendor/SDM439/recovery.te
recovery_only(`
allow recovery shell_exec:file x_file_perms;
+ allow recovery console_device:chr_file rw_file_perms;
')
========================================================
关于Linux内核中CONFIG_OF宏,OF是Open Firmware的缩写。CONFIG-OF是和设备树相关的选项。kernel的配置文件里面没有定义,但是在编译后的out目录下生成的.config文件里面有定义。
Open Firmware. This was invented long time ago when Apple was producing laptops based on PowerPC CPUs. Openfirmware provides a good description of the devices connected to the platform. In Linux kernel the part that works with
device data is called Device Tree (DT).
=======================================================================
A5人脸识别终端 2.0的底板,配置DSI1通道的8寸LCD点不亮,mipi cmd下成功了,因为RST接了反向MOS管,所以RST的波形也从 1 0 1反向成了0 1 0,初始电平也通过修改BSP端的代码的bootloader改成了初始化为高,反向后默认为低,
但是还是点不亮,后来量波形,发现模块出来的mipi波形还在,但是过了FPC到LCD那头信号就没了,硬件检查是MIPI lane上串接的电感贴错方向,导致mipi lane不通,修改之后LCD能正常出图。
====================================================================
编译A5底板(模块为SDM439的核心板模块)项目的时候,选择panel的注意事项:
<0>.默认编译A5 1.0的底板,选择的是7 inch RGB屏幕:./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true -j24
<1>.编译A5 2.0底板的时候,选择7 inch RGB屏幕:
1)在bootable/bootloader/lk/project/SDM439.mk打开以下开关,因为A5 2.0板子的RESET脚接了一个反向MOS管,控制的时候需要反向控制
#reset pin need a reverse operation for A5 main board v2.0
DEFINES += A5_MAIN_BOARD_V20=1
2)在kernel/msm-4.9/arch/arm64/configs/SDM439_defconfig打开以下开关
CONFIG_A5_MAINBOARD_V20=y
然后按正常流程编译即可:./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true -j24
<2>.编译A5 2.0底板,选择8 inch MIPI屏幕:
1)在bootable/bootloader/lk/project/SDM439.mk打开以下开关,LK必须进行选择,DSI1如果在LK没有进行指定将会没有输出
# 8 inch MIPI panel connect to dsi1
DEFINES += FEATURE_PANEL_JD9365DA=1
关闭其他屏幕开关
然后添加环境变量:ENABLE_DSI1=true,按照./oem_build.sh SDM439 -v userdebug -e SDM439_DEMO=true,ENABLE_DSI1=true -j24
进行编译即可
<3>.编译A5 2.0底板,选择21.5 inch LVDS屏幕:
1)在bootable/bootloader/lk/project/SDM439.mk打开以下开关,LK必须进行选择,DSI1如果在LK没有进行指定将会没有输出
# 21.5 inch LVDS panel connect to dsi
DEFINES += FEATURE_PANEL_LT9211=1
关闭其他屏幕开关
2)由于LVDS的背光enable是接到RGB灯的Red,pwm是接到RGB灯的Green,所以需要配置RGB_RED的状态:
kernel/msm-4.9/arch/arm64/configs/SDM439_defconfig
CONFIG_TRI_LED_FULL_DEFAULT=y
然后添加环境变量:ENABLE_DSI1=true,按照./oem_build.sh SDM439 -v userdebug -e SDM439_DEMO=true,ENABLE_DSI1=true -j24
进行编译即可
========================================================================================================
SD卡的检测:frameworks/base/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
public class StorageNotification extends SystemUI {
private final StorageEventListener mListener = new StorageEventListener() {//storage事件监听mListener
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
onVolumeStateChangedInternal(vol);//监听回调
}
@Override
public void onVolumeRecordChanged(VolumeRecord rec) {
// Avoid kicking notifications when getting early metadata before
// mounted. If already mounted, we're being kicked because of a
// nickname or init'ed change.
final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid());
if (vol != null && vol.isMountedReadable()) {
onVolumeStateChangedInternal(vol);//监听回调
}
}
...
};
...
public void start() {
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mStorageManager = mContext.getSystemService(StorageManager.class);
mStorageManager.registerListener(mListener);//注册监听事件
...
}
}
//具体的回调处理:
private void onVolumeStateChangedInternal(VolumeInfo vol) {
switch (vol.getType()) {
case VolumeInfo.TYPE_PRIVATE://内部存储设备
onPrivateVolumeStateChangedInternal(vol);
break;
case VolumeInfo.TYPE_PUBLIC://外部存储设备
onPublicVolumeStateChangedInternal(vol);//当插入SD card的时候触发这里
break;
}
}
//继续跟踪:
private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
Log.d(TAG, "Notifying about public volume: " + vol.toString());
......
}
下面我们来看下具体的打印信息:
//
09-04 15:13:28.404 1690 1690 D StorageNotification: Notifying about private volume: VolumeInfo{private}://检测到有内部存储设备
09-04 15:13:28.404 1690 1690 D StorageNotification: type=PRIVATE diskId=null partGuid=null mountFlags=0 mountUserId=-1
09-04 15:13:28.404 1690 1690 D StorageNotification: state=MOUNTED
09-04 15:13:28.404 1690 1690 D StorageNotification: fsType=null fsUuid=null fsLabel=null
09-04 15:13:28.404 1690 1690 D StorageNotification: path=/data internalPath=null
09-04 15:13:28.406 1690 1690 D StorageNotification: Notifying about public volume: VolumeInfo{public:179,65}://检测到有外部存储设备
09-04 15:13:28.406 1690 1690 D StorageNotification: type=PUBLIC diskId=disk:179,64 partGuid= mountFlags=VISIBLE mountUserId=0
09-04 15:13:28.406 1690 1690 D StorageNotification: state=MOUNTED
09-04 15:13:28.406 1690 1690 D StorageNotification: fsType=vfat fsUuid=0000-0000 fsLabel=
09-04 15:13:28.406 1690 1690 D StorageNotification: path=/storage/0000-0000 internalPath=/mnt/media_rw/0000-000
打印出来的信息定义:
public VolumeInfo(Parcel parcel) {
id = parcel.readString();
type = parcel.readInt();//PUBLIC
if (parcel.readInt() != 0) {//disk:179,64
disk = DiskInfo.CREATOR.createFromParcel(parcel);
} else {
disk = null;
}
partGuid = parcel.readString();//partGuid=
mountFlags = parcel.readInt();//mountFlags=VISIBLE
mountUserId = parcel.readInt();//mountUserId=0
state = parcel.readInt();//state=MOUNTED,挂载成功
fsType = parcel.readString();//fsType=vfat
fsUuid = parcel.readString();//fsUuid=0000-0000
fsLabel = parcel.readString();//fsLabel=
path = parcel.readString();//path=/storage/0000-0000
internalPath = parcel.readString();//internalPath=/mnt/media_rw/0000-000
}
至此我们可以看到framework层能够检测出来SD的插入事件以及路径,接下来我们来看怎么在拿到SD卡信息之后进入recovery模式,还是在onPublicVolumeStateChangedInternal这个回调里面加逻辑处理:
private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
Log.d(TAG, "Notifying about public volume: " + vol.toString());
@@ -299,6 +363,62 @@ public class StorageNotification extends SystemUI {
break;
case VolumeInfo.STATE_MOUNTED://成功挂载之后
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
final String customLcdConfigFileName = Resources.getSystem().getString(
com.android.internal.R.string.config_customLcdConfigFileName);//<string name="config_customLcdConfigFileName">display_param.cfg</string>
boolean customLcdConfigUpdateEanble = Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_customLcdConfigUpdateEanble);//<bool name="config_customLcdConfigUpdateEanble">true</bool>
int customLcdConfigFileUpdateTimeOutMS = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_customLcdConfigFileUpdateTimeOutMS);//<integer name="config_customLcdConfigFileUpdateTimeOutMS">30000</integer>
final String customLcdConfigFileUpdateResultLedDeviceName = Resources.getSystem().getString(
com.android.internal.R.string.config_customLcdConfigFileUpdateResultLedDeviceName);//<string name="config_customLcdConfigFileUpdateResultLedDeviceName">/sys/devices/platform/soc/200f000.qcom,spmi/spmi-0/spmi0-03/200f000.qcom,spmi:qcom,pmi632@3:qcom,leds@d000/leds/red/brightness</string>
boolean doLcdConfigUpdateEnable = false;
if((customLcdConfigUpdateEanble == true)
&& (customLcdConfigFileName != null))//如果customLcdConfigFileName不为空,打印出来:
{
//File SDCardPath = Environment.getExternalStorageDirectory();
File SDCardPath = vol.getPath();//path=/storage/0000-0000
if(customLcdConfigFileUpdateTimeOutMS >= 0)//30S
{
Timer timer;
timer = new Timer();
timer.schedule(new lcdConfigUpdateTimerTask(SDCardPath), customLcdConfigFileUpdateTimeOutMS);//起一个定时器,30S之后进入定时器回调处理
}
else
{
doLcdConfigUpdateEnable = false;
Log.d(TAG, "customLcdConfigFileName: " + customLcdConfigFileName);
if(SDCardPath != null && SDCardPath.exists())//路径不为空且路径存在
{
File[] files = SDCardPath.listFiles();
for (File file : files)
{
Log.d(TAG, "SDCardPath file name: " + file.getName());
if((false == file.isDirectory()) && (file.getName().equals(customLcdConfigFileName)))
{
doLcdConfigUpdateEnable = true;
break;
}
}
}
else
{
Log.d(TAG, "file SDCardPath is null");
}
if(true == doLcdConfigUpdateEnable)
{
try {
RecoverySystem.rebootUpdateLcdConfigData(mContext,
"/sdcard/"+customLcdConfigFileName /*"/sdcard/display_param.cfg"*/,
customLcdConfigFileUpdateResultLedDeviceName);
} catch (IOException e) {
Log.d(TAG, "Failed rebootUpdateLcdConfigData ", e);
}
}
}
}
然后看下定时器回调里做了什么:
类里面增加了一个对象:
class lcdConfigUpdateTimerTask extends TimerTask {
private File SDCardPath;
lcdConfigUpdateTimerTask(File file)
{
SDCardPath = file;
}
public void run(){
synchronized(mLock) {
final String customLcdConfigFileName = Resources.getSystem().getString(
com.android.internal.R.string.config_customLcdConfigFileName);
final String customLcdConfigFileUpdateResultLedDeviceName = Resources.getSystem().getString(
com.android.internal.R.string.config_customLcdConfigFileUpdateResultLedDeviceName);
boolean doLcdConfigUpdateEnable = false;
if(customLcdConfigFileName != null)
{
doLcdConfigUpdateEnable = false;
Log.d(TAG, "customLcdConfigFileName: " + customLcdConfigFileName);
if(SDCardPath != null && SDCardPath.exists())
{
File[] files = SDCardPath.listFiles();
for (File file : files)
{
Log.d(TAG, "SDCardPath file name: " + file.getName());//打印出来目录下的所有文件/文件夹,具体打印内容看下面
if((false == file.isDirectory()) && (file.getName().equals(customLcdConfigFileName)))//如果找到display_param.cfg且不是目录
{
doLcdConfigUpdateEnable = true;//置位标志位
break;
}
}
}
else
{
Log.d(TAG, "file SDCardPath is null");
}
//doLcdConfigUpdateEnable = false;
if(true == doLcdConfigUpdateEnable)//标志如果为真
{
try {
RecoverySystem.rebootUpdateLcdConfigData(mContext, //调用的RecoverySystem对象rebootUpdateLcdConfigData这个新增加的方法
"/sdcard/"+customLcdConfigFileName /*"/sdcard/display_param.cfg"*/,
customLcdConfigFileUpdateResultLedDeviceName);
} catch (IOException e) {
Log.d(TAG, "Failed rebootUpdateLcdConfigData ", e);
}
}
}
return;
}
}
}
09-04 15:13:58.408 1690 2124 D StorageNotification: customLcdConfigFileName: display_param.cfg//对比时间戳,确实是30S后打印出来
09-04 15:13:58.410 1690 2124 D StorageNotification: SDCardPath file name: DCIM
09-04 15:13:58.410 1690 2124 D StorageNotification: SDCardPath file name: .Favorites
09-04 15:13:58.411 1690 2124 D StorageNotification: SDCardPath file name: System Volume Information
09-04 15:13:58.411 1690 2124 D StorageNotification: SDCardPath file name: MISC
09-04 15:13:58.411 1690 2124 D StorageNotification: SDCardPath file name: LOST.DIR
09-04 15:13:58.411 1690 2124 D StorageNotification: SDCardPath file name: Android
09-04 15:13:58.411 1690 2124 D StorageNotification: SDCardPath file name: display_param2.cfg
09-04 15:13:58.412 1690 2124 D StorageNotification: SDCardPath file name: display_param.cfg.bak
09-04 15:13:58.412 1690 2124 D StorageNotification: SDCardPath file name: display_param.cfg
来看下具体重启的操作做了什么:
frameworks/base/core/java/android/os/RecoverySystem.java
新增一个方法:
* Reboots the device and update lcdconfig
* partitions.
* {@link android.Manifest.permission#REBOOT} permission.
*
* @param context the Context to use
* @throws IOException if writing the recovery command file
* fails, or if the reboot itself fails.
* @throws SecurityException if the current user is not allowed to wipe data.
*
* @hide
*/
public static void rebootUpdateLcdConfigData(Context context, String filename, String stateLedDeviceName)
throws IOException {
synchronized (sRequestLock) {
Log.w(TAG, "!!! lcd config, REBOOTING TO INSTALL " + filename + " !!!");//09-04 15:13:58.413 1690 2124 W RecoverySystem: !!! lcd config, REBOOTING TO INSTALL /sdcard/display_param.cfg !!!
//拼接对应的升级命令,这些命令将会在Recovery模式下中使用。
final String filenameArg = "--update_lcdconfig=" + filename + "\n";
final String showtextArg = "--show_text\n";
String command = filenameArg + showtextArg;
if(null != stateLedDeviceName)
{
command = command + "--lcdconfig_statusled=" + stateLedDeviceName + "\n";
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(
Context.RECOVERY_SERVICE);//调用RecoverySystemService远程服务的方法将命令写入到BCB中
if (!rs.setupBcb(command)) {//最关键就是这步骤,写BCB
throw new IOException("Setup BCB failed for update lcdconfig");
}
// Having set up the BCB (bootloader control block), go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
String reason = PowerManager.REBOOT_RECOVERY;
pm.reboot(reason);//写完BCB重启到recovery模式
throw new IOException("Reboot failed (no permissions?) for update lcdconfig");
}
}
Recovery相关概念:
RecoverySystem:Android系统内部实现的一个工具类,Android应用层操作Recovery模式的一个重要途径,它提供了几个重要的API,用于实现OTA包校验、升级以及恢复出厂设置(格式化数据和缓存)。
Main System:主系统模式,即Android正常开机所进入的Android系统
Bootloader:Bootloader是嵌入式系统在加电后执行的第一段代码,在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装在到内存中然后跳转到操作系统所在的空间,启动操作系统运行。
BCB:Bootloader Control Block,启动控制信息块,位于misc分区,从代码上看,就是一个结构体。
Android 各个分区介绍
Boot:包含Linux内核和一个最小的root文件系统(装载到ramdisk中),用于挂载系统和其他的分区,并开始Runtime。正如名字所代表的意思(注:boot的意思是启动),这个分区使Android设备可以启动。如果没有这个分区,Android设备通常无法启动到Android系统。
Recovery:包括了一个完整Linux内核和一些特殊的recovery binary,可以读取升级文件用这些文件来更新其他的分区。
Misc:一个非常小的分区,4 MB左右(高通是1024Kb)。recovery用这个分区来保存一些关于升级的信息,应对升级过程中的设备掉电重启的状况,Bootloader启动的时候,会读取这个分区里面的信息,以决定系统是否进Recovery System 或 Main System。
Android有三种启动模式:Fastboot模式,Recovery System 以及Main System
Fastboot:在这种模式下,可以修改手机的硬件,并且允许我们发送一些命令给Bootloader。如使用电脑刷机,则需要进入fastboot模式,通过电脑执行命令将系统镜像刷到通过USB刷到Android设备中中。
Recovery:Recovery是一个小型的操作系统,并且会加载部分文件系统,这样才能从sdcard中读取升级包。
Main System: 即我们平时正常开机后所使用的手机操作系统模式
开机的时候,bootloade会解析BCB控制块(位于misc分区)来决定是否进入recovery模式
正常的启动模式是从boot.img启动系统(Main System),而recovery模式则是从reovery.img启动系统;(reovery.img只包含内核、简单的文件管理系统和图形系统)
Boot分区包括linux内核和Ramdisk,Recovery分区也包括Linux内核和Ramdisk,一般来说内核是一样的,但Ramdisk区别则非常大,recovery中的ramdisk会有更多的recovery需要使用的程序和数据。这里说到的boot.img和recovery.img,其实就分别对应了Android设备中的boot和recovery分区。
-------------------------------------------------------------------
Android recovery支持adb shell
recovery模式下,到recovery不支持adb shell,为了便于调试方便,决定增加此功能。:
wanghl@wanghl-HP:~/code/sdm439_android$ adb shell
error: exec '/system/bin/sh' failed: No such file or directory
(1).
表示没有sh这个文件,无法进入shell,检查ramdisk 文件系统 system 目录(out/target/product/SDM439/recovery/root/system)不存在,但我们知道boot.img下是可以的(out/target/product/SDM439/system/bin/sh命令存在),看system/core/rootdir/init.rc检查boot.img 启动的init.rc
service console /system/bin/sh
class core
console
disabled
user shell
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
可知是启动了sh这控制台的,所以需要在bootable\recovery\etc\init.rc增加相应的内容:
+++ b/bootable/recovery/etc/init.rc
@@ -92,6 +92,16 @@ service adbd /sbin/adbd --root_seclabel=u:r:su:s0 --device_banner=recovery
socket adbd stream 660 system system
seclabel u:r:adbd:s0
+# enable adb shell in recovery mode
+service console /system/bin/sh
+ class core
+ console
+ disabled
+ user shell
+ group shell log readproc
+ seclabel u:r:shell:s0
+ setenv HOSTNAME console
+
# Always start adbd on userdebug and eng builds
on property:ro.debuggable=1
write /sys/class/android_usb/android0/enable 1
(2)我们知道out/target/product/SDM439/recovery/root/system下没有bin文件夹(连system都没有,只有system_root,当然也没有sh),所以需要在编译的时候创建,需要build/core/Makefile增加创建目录和把out目录下的/system/bin/sh拷贝到out/target/product/SDM439/recovery/root/system/bin目录下
增加:
$(hide) mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/system/bin
$(hide) cp -r $(PRODUCT_OUT)/system/bin/sh $(TARGET_RECOVERY_ROOT_OUT)/system/bin
实际上这样增加没有效果,但是高通其实已经做好了一个菜单,在recovery 菜单选择Mount /system即可以使用adb shell了:
wanghl@wanghl-HP:~/code/sdm439_android$ adb shell
SDM439:/ #
--------------------------------------------------------------------------------------------------------
recovery下的log会从复制到在/cache/recovery/last_log,
1. get_args(&argc, &argv)
get_args的原理流程图如下所示:
get_args()函数的主要作用是获取系统的启动参数,并回写到bootloader control block(BCB)块中。来看下函数原型:
// command line args come from, in decreasing precedence:
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
static std::vector<std::string> get_args(const int argc, char** const argv) {
CHECK_GT(argc, 0);
bootloader_message boot = {};//下面可以看下数据结构原型
std::string err;
if (!read_bootloader_message(&boot, &err)) {//如果系统在启动recovery时已经传递了启动参数,那么这个函数只是把启动参数的内容复制到函数的参数boot对象中,首先通过read_bootloader_message()函数从/misc分区的BCB中获取命令字符串来构建启动参数。
LOG(ERROR) << err;
// If fails, leave a zeroed bootloader_message.
boot = {};
}
stage = std::string(boot.stage);
if (boot.command[0] != 0) {
std::string boot_command = std::string(boot.command, sizeof(boot.command));
LOG(INFO) << "Boot command: " << boot_command;//I:Boot command: boot-recovery
}
if (boot.status[0] != 0) {
std::string boot_status = std::string(boot.status, sizeof(boot.status));
LOG(INFO) << "Boot status: " << boot_status;//无打印
}
std::vector<std::string> args(argv, argv + argc);
// --- if arguments weren't supplied, look in the bootloader control block
if (args.size() == 1) {
boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
std::string boot_recovery(boot.recovery);
std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n");
if (!tokens.empty() && tokens[0] == "recovery") {
for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from boot message";//真实log打印出来的是:I:Got 4 arguments from boot message
} else if (boot.recovery[0] != 0) {
LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
}
}
// --- if that doesn't work, try the command file (if we have /cache).
if (args.size() == 1 && has_cache) {
std::string content;
if (ensure_path_mounted(COMMAND_FILE) == 0 &&//如果/misc分区下没有内容,则会尝试解析/cache/recovery/command文件并读取文件的内容来建立启动参数。static const char *COMMAND_FILE = "/cache/recovery/command";
android::base::ReadFileToString(COMMAND_FILE, &content)) {
std::vector<std::string> tokens = android::base::Split(content, "\n");
// All the arguments in COMMAND_FILE are needed (unlike the BCB message,
// COMMAND_FILE doesn't use filename as the first argument).
for (auto it = tokens.begin(); it != tokens.end(); it++) {
// Skip empty and '\0'-filled tokens.
if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
}
LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
}
}
// Write the arguments (excluding the filename in args[0]) back into the
// bootloader control block. So the device will always boot into recovery to
// finish the pending work, until finish_recovery() is called.
std::vector<std::string> options(args.cbegin() + 1, args.cend());
if (!update_bootloader_message(options, &err)) {//接着,会把启动参数的信息通过set_bootloader_message()函数又保存到了BCB块中。这样做的目的是防止一旦升级或擦除数据的过程中发生崩溃或不正常断电,下次重启,Bootloader会依据BCB的指示,引导进入Recovery模式,从/misc分区中读取更新的命令,继续进行更新操作。因此,可以说是一种掉电保护机制。
LOG(ERROR) << "Failed to set BCB message: " << err;
}
return args;
}
这里需要说一下BCB,即bootloader control block, 中文可以呼之为“启动控制模块信息”,位于/misc分区,从代码上看,就是一个struct 结构体 :
struct bootloader_message {
char command[32];//command 字段中存储的是命令,它有以下几个可能值:boot-recovery:系统将启动进入Recovery模式;update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成;NULL:空值,系统将启动进入Main System主系统,正常启动。
char status[32];//status 字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。
char recovery[768];//recovery 字段存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:第一行存放字符串“recovery”,第二行存放路径信息“–update_package=/mnt/sdcard/update.zip”等。 因此,参数之间是以“\n”分割的。
// The 'recovery' field used to be 1024 bytes. It has only ever
// been used to store the recovery command line, so 768 bytes
// should be plenty. We carve off the last 256 bytes to store the
// stage string (for multistage packages) and possible future
// expansion.
char stage[32];
// The 'reserved' field used to be 224 bytes when it was initially
// carved off from the 1024-byte recovery field. Bump it up to
// 1184-byte so that the entire bootloader_message struct rounds up
// to 2048-byte.
char reserved[1184];
};
int main(int argc, char **argv) {
...
std::vector<std::string> args = get_args(argc, argv);
...
printf("Command:");
for (const auto& arg : args) {
printf(" \"%s\"", arg.c_str());
}
printf("\n\n");
...
}
看下这段的具体打印信息
[ 1.853432] Command: "/sbin/recovery" "--update_lcdconfig=/sdcard/display_param.cfg" "--show_text" "--lcdconfig_statusled=/sys/devices/platform/soc/200f000.qcom,spmi/spmi-0/spmi0-03/200f000.qcom,spmi:qcom,pmi632@3:qcom,leds@d000/leds/red/brightness"
总共有四个参数
与个关键函数分析一下:
getopt_long() //处理命令行选项参数的,的确,正规点的大型程序一般第一步就是处理命令行参数的,接着才是主干程序。
#include<getopt.h> /*所在头文件 */
struct option {
const char *name; /* 参数名称 */
int has_arg; /* 指明是否带有参数 */
int *flag; /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int val; /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
};
int getopt_long(int argc, char * const argv[], const char *optstring,
const struct option *longopts, int*longindex);
longopts 指明了长参数的名称和属性
longindex 如果longindex非空,它指向的变量将记录当前找到参数符合longopts里的第几个元素的描述,即是longopts的下标值
返回值:对于短选项,返回值同getopt函数;对于长选项,如果flag是NULL,返回val,否则返回0;对于错误情况返回值同getopt函数
真实代码在:bionic/libc/include/getopt.h
struct option {
/* name of long option */
const char *name;
/*
* one of no_argument, required_argument, and optional_argument:
* whether option takes an argument
*/
int has_arg;
/* if not NULL, set *flag to val when option found */
int *flag;
/* if flag not NULL, value to set *flag to; else return value */
int val;
};
int getopt_long(int __argc, char* const* __argv, const char* __options, const struct option* __long_options, int* __long_index);
解释一下:
其中:has_arg包含以下几种可能的值,
no_argument 表明长选项不带参数,如:--name, --help
required_argument 表明长选项必须带参数,如:--prefix /root或 --prefix=/root
optional_argument 表明长选项的参数是可选的,如:--help或 –prefix=/root,其它都是错误
static const struct option OPTIONS[] = {
{ "update_package", required_argument, NULL, 'u' },
{ "retry_count", required_argument, NULL, 'n' },
{ "wipe_data", no_argument, NULL, 'w' },
{ "wipe_cache", no_argument, NULL, 'c' },
{ "show_text", no_argument, NULL, 't' },//原本就有的
{ "sideload", no_argument, NULL, 's' },
{ "sideload_auto_reboot", no_argument, NULL, 'a' },
{ "just_exit", no_argument, NULL, 'x' },
{ "locale", required_argument, NULL, 'l' },
{ "shutdown_after", no_argument, NULL, 'p' },
{ "reason", required_argument, NULL, 'r' },
{ "security", no_argument, NULL, 'e'},
{ "wipe_ab", no_argument, NULL, 0 },
{ "wipe_package_size", required_argument, NULL, 0 },
{ "prompt_and_wipe_data", no_argument, NULL, 0 },
#ifdef OEM_LCD_CONFIG_FILE_SUPPORT//新增加的
{ "update_lcdconfig", required_argument, NULL, 0 },
{ "lcdconfig_statusled", required_argument, NULL, 0 },
#endif
{ NULL, 0, NULL, 0 },
};
调用实例:recovery.cpp
int main(int argc, char **argv) {
...
std::vector<std::string> args = get_args(argc, argv);//get_args()函数的主要作用是获取系统的启动参数,并回写到bootloader control block(BCB)块中。
std::vector<char*> args_to_parse(args.size());
std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),
[](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
...
while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS,
&option_index)) != -1)
...
}
分析一下: finish_recovery
OTA升级成功,清空misc分区(BCB置零),并将保存到内存系统的升级日志/tmp/recovery.log保存到/cache/recovery/last_log。重启设备进入Main System,升级完成
// Clear the recovery command and prepare to boot a (hopefully working) system,
// copy our log file to cache as well (for the system to read). This function is
// idempotent: call it as many times as you like.
static void finish_recovery() {
// Save the locale to cache, so if recovery is next started up without a '--locale' argument
// (e.g., directly from the bootloader) it will use the last-known locale.
if (!locale.empty() && has_cache) {
LOG(INFO) << "Saving locale \"" << locale << "\"";
if (ensure_path_mounted(LOCALE_FILE) != 0) {
LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
} else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) {
PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
}
copy_logs();///tmp/recovery.log保存到/cache/recovery/last_log。一般来说分析Recovery问题的时候,经常会分析这份log
// Reset to normal system boot so recovery won't cycle indefinitely.
std::string err;
if (!clear_bootloader_message(&err)) {//清空misc分区(BCB置零)
LOG(ERROR) << "Failed to clear BCB message: " << err;
}
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {//删除static const char *COMMAND_FILE = "/cache/recovery/command";
LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
ensure_path_unmounted(CACHE_ROOT);
}
sync(); // For good measure.
}
其次,Bootloader与Recovery和Main System之间也是存在交互的: Bootloader会通过解析BCB模块,决定启动系统到Recovery或Main System。而Recovery或Main System也能够操作BCB,进而影响到Bootloader的行为.
当Main System系统关键进程崩溃太多次的时候,系统还会自发启动进入到Recovery模式。另外,部分平台的Android设备,在Recovery模式下,也能够对Bootloader进行升级。Bootloader、BCB、Recovery与Main System四者相互影响,又独立工作。Recovery和Main System主要通过cache分区来进行交互,cache系统缓存区,临时的保存应用数据(要把数据保存在这里,需要特地的app permission), OTA的升级包也可以保存在这里。OTA升级过程可能会清楚这个分区的数据。一般来讲,Android差分包升级也需要依赖此分区存放一些中间文件。
有一个特别要注意的问题是:
//static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_LOG_FILE = "/dev/ttyMSM0";
我们之前打开recovery模式下的打印的时候,是这么改的,有可能导致finish_recovery()在执行copy_logs();发生了异常,导致recovery模式没办法正常退出并清理misc分区,所以会反复进入recovery分区,
[ 0.035100] I:Boot command: boot-recovery
[ 0.060018] I:Got 4 arguments from boot message//说明BCB数据存在,读取到又进入recovery了。
所以最好不要通过修改代码来修改打开log的打印,可以去分析/cache/recovery/last_log文件来分析问题
----------------------------------------------------------------------
DSI1通道卡顿的问题:
Setting ---->developer options------->Disable HW overlays (选项勾选后所有的图像混叠都由GPU实现) 。对于一般用户这样做就可以了。 但是这种闪屏现象很容易让人感觉系统不稳定,或是lcd驱动有什么问题。
这就要求在Android源码编译时就完成Disable HW overlays选项的配置。中文菜单是:停用HW叠加层(始终使用GPU进行屏幕合成)
wanghl@wanghl-HP:~/code/sdm439_android$ git diff frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
diff --git a/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp b/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
index 2383933..0150250 100644
--- a/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
@@ -256,7 +256,7 @@ SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag)
mBuiltInBitmask(0),
mDebugRegion(0),
mDebugDDMS(0),
- mDebugDisableHWC(0),
+ mDebugDisableHWC(1),
mDebugDisableTransformHint(0),
mDebugInSwapBuffers(0),
mLastSwapBufferTime(0),
01-01 12:58:31.794 556 601 E SDM : HWCSession::UEventHandler: HWCSession::UEventHandler uevent_data = remove@/devices/platform/soc/1c00000.qcom,kgsl-3d0/kgsl/kgsl-3d0/a530_pfp.fw
01-01 12:58:31.800 556 601 E SDM : HWCSession::UEventHandler: HWCSession::UEventHandler uevent_data = add@/devices/platform/soc/4080000.qcom,mss/firmware/msadp
01-01 12:58:31.827 556 601 E SDM : HWCSession::UEventHandler: HWCSession::UEventHandler uevent_data = remove@/devices/platform/soc/4080000.qcom,mss/firmware/msadp
01-01 12:58:31.827 682 682 D WLAN_PSA: PTT_SOCKET_APP Start
01-01 12:58:31.827 682 682 E WLAN_PSA: Set as Blocked Operation
01-01 12:58:31.827 682 682 E Diag_Lib: Diag_LSM_Init: Failed to open handle to diag driver, error = 13
01-01 12:58:31.834 556 601 E SDM : HWCSession::UEventHandler: HWCSession::UEventHandler uevent_data = add@/devices/platform/soc/soc:qcom,kgsl-hyp/firmware/A56_zap.mdt
01-01 12:58:31.834 556 601 I SDM : HWCSession::UEventHandler: Uevent = add@/devices/platform/soc/soc:qcom,kgsl-hyp/firmware/A56_zap.mdt//利用这个uevent通知HAL层
01-01 12:58:31.834 556 601 I SDM : HWCSession::UEventHandler: MIPI2EDP = connected
------------------------------------------------------
git reset HEAD 文件名//已经git add的文件,可以用这个取消add
git reset --soft HEAD^//取消git commit -m "xxx"
---------------------------------------
Android.bp是用来替换Android.mk的配置文件,它使用Blueprint框架来解析。Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义,最终转换成Ninja文件.
1.无流控制的宏开关添加可以在Android.bp文件里搜索cppflags或者cflags追加相应的宏定义:
cflags: ["-DFAKE_LOG_DEVICE=1"],
cppflags
2.对C/C++代码, 如果需要使用宏开关时, 由于android整个编译系统还没完全切换过来, 导致 在项目mk文件定义的开关, 还不能生效.对于条件编译, 需要添加go文件进行控制.
一是,通过export命令, 把相应的开关设置到环境变量, go文件就能读取到了.
二是, 把开关集中放到某一个文件中, 然后在go文件中直接读取这个文件.
go文件判断逻辑举例:
if a>=0 && b>=0{
sum=a+b
return
}
if num >= 0 && num <= 50 {
fmt.Println("number is greater than 50")
} else if num >= 51 && num <= 100 {
fmt.Println("number is between 51 and 100")
} else {
fmt.Println("number is greater than 100")
}
或操作符(||): d := a || b
---------------------------------
Makefile的ifeq逻辑或,逻辑与的变通实现
(1)ifeq的用法
ifeq ($(变量名),变量值)
........
else ifeq ($(..), ..)
.........
else
.........
endif
(2)学习makefile的过程中遇到需要用ifeq进行逻辑与判断,但是ifeq并没有像其他编程语言那样有 逻辑或(||) 逻辑与(&&) 的符号可用。这时候需要变通一下。
1)逻辑与变通实现:
举例说明:比如需要判断两个变量 VALUE1 和 VALUE2 的值都存在才执行某个动作,这需要逻辑与的判断
C语言的逻辑:
if ( VALUE1 && VALUE2){
do something....
}
没有&&符号,我们可以这样变通:将两个变量链接起来再判断
ifneq ($(VALUE1)$(VALUE2),)
do something....
endif
比如:
如果变量 VALUE1 和 VALUE2 都有具体的值,比如需要进行这样的判断: VALUE1 == V1 && VALUE2 == V2, 可以按如下的写法;
ifeq ($(VALUE1)_$(VALUE2), V1_V2) ### 当然中间的下划线 "_" 可以用其他字符代替
do something....
endif
比如:
ifeq ($(OEM_PANEL_JD9365DA)_$(OEM_PANEL_LT9211),true_true)
do something
endif
2) 逻辑或变通实现,同样是上面的两个变量
if( VALUE1 == V1 || VALUE2 == V2 ) {...} 可以用findstring函数做如下变通实现:
#如果VALUE1或者VALUE2为V1或V2,则findstring 不会返回空。
ifneq ($(findstring $(VALUE1)$(VALUE2), V1V2),)
do something...
endif
比如:
ifneq ($(findstring $(OEM_PANEL_JD9365DA)$(OEM_PANEL_LT9211), truetrue),)
do something
endif
插播一下:makefile的findstring函数用法
#$(findstring <find>,<in> )
#功能:在字串<in>中查找<find>字串。
#返回:如果找到,那么返回<find>,否则返回空字符串。
str1 := a b c
str2 := b c
#第一个函数返回“a”字符串,第二个返回空字符串
all:
@echo $(findstring a,$(str1))
@echo $(findstring a,$(str2))
--------------------------------------------------------------------------------------
mdss_dsi_find_panel_of_node: cmdline:0:qcom,mdss_dsi_icn6211_video:1:none:cfg:single_dsi panel_name:none
(1)1.0主板,7 inch RGB屏幕:
./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true -j24
(2)2.0主板,7 inch RGB屏幕:
./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true,OEM_PANEL_ICN6211_V20=true -j24
(3)2.0主板,8 inch MIPI屏幕:
./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true,OEM_PANEL_JD9365DA=true -j24
(4)2.0主板,21.5 inch LVDS屏幕:
./oem_build.sh SDM439 -v userdebug -e SDM439_CUSTOM=true,OEM_PANEL_LT9211=true -j24
====================================================================================
对于lcd而言,决定显示效果的有几个因素:pwm,gama(屏幕灰度),饱和度,对比度
JD9365DA:
旧基线正常唤醒串口log:
[ 267.486909] goodix-ts 3-005d: Try resume from sleep mode
[ 267.486923] goodix-ts 3-005d: Guitar reset
[ 267.564857] goodix-ts 3-005d: ESD on
[ 267.582943] msm8937-pinctrl 1000000.pinctrl: pin GPIO_60 already requested by 1a94000.qcom,mdss_dsi_ctrl0; cannot claim for 1a96000.qcom,mdss_dsi_ctrl1
[ 267.582958] msm8937-pinctrl 1000000.pinctrl: pin-60 (1a96000.qcom,mdss_dsi_ctrl1) status -22
[ 267.582972] msm8937-pinctrl 1000000.pinctrl: could not request pin 60 (GPIO_60) from group gpio60 on device 1000000.pinctrl
[ 267.582984] mdss_dsi_ctrl 1a96000.qcom,mdss_dsi_ctrl1: Error applying setting, reverse things back
[ 267.582993] mdss_dsi_pinctrl_set_state: can not set mdss_default pins
[ 267.742479] mdss_dsi_panel_bl_ctrl: Unknown bl_ctrl configuration
[ 267.820663] mdss_dsi_panel_bl_ctrl: Unknown bl_ctrl configuration
[ 270.518497] init: starting service 'vendor.vm_bms'...
[ 270.573986] init: Service 'vendor.vm_bms' (pid 3450) exited with status 255
[ 270.574149] init: Sending signal 9 to service 'vendor.vm_bms' (pid 3450) process group...
[ 270.580163] libprocessgroup: Successfully killed process cgroup uid 0 pid 3450 in 0ms
唤不醒是因为RST pin没有配置
===============================
log打印技巧:
diff --git a/kernel/msm-4.9/drivers/video/fbdev/msm/mdss_dsi_panel.c b/kernel/msm-4.9/drivers/video/fbdev/msm/mdss_dsi_panel.c
index 66834eb..010e89b 100644
--- a/kernel/msm-4.9/drivers/video/fbdev/msm/mdss_dsi_panel.c
+++ b/kernel/msm-4.9/drivers/video/fbdev/msm/mdss_dsi_panel.c
@@ -9,7 +9,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-//#define DEBUG
+#define DEBUG
+#define pr_fmt(fmt) "mdss_dsi_panel:[%s:%d]--" fmt, __func__, __LINE__
#include <linux/module.h>
#include <linux/interrupt.h>
1)在#include 头文件的最前面打开宏开关:
#define DEBUG
就能打开本文件里所有pr_debug级别的log:
例如这个log:
pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n",
__func__, ctrl->bklt_ctrl, ctrl->pwm_period,
ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan);
就能被打印出来
2)格式化串口打印内容,方便模块化查找log:
1)在#include 头文件的最前面打开宏开关:
#define pr_fmt(fmt) "mdss_dsi_panel:[%s:%d]--" fmt, __func__, __LINE__
"mdss_dsi_panel:[%s:%d]--"和fmt之间加不加空格都可以,最终log打印出来都是直接连接在一起的,没有空格:
例如刚刚那条log打印出来就是:
[ 20.091704] mdss_dsi_panel:[mdss_dsi_panel_bklt_pwm:184]--mdss_dsi_panel_bklt_pwm: bklt_ctrl=0 pwm_period=100 pwm_gpio=1279 pwm_lpg_chan=0//--后面没有空格
===================================================
配置DSI1屏幕jd9365da,recovery模式下不显示的问题:
我们可以看到:
系统创建了两个framebuffer设备文件:
A58F:/ # ls dev/graphics/fb*
dev/graphics/fb0 dev/graphics/fb1
mdss_fb_probe->register_framebuffer->do_register_framebuffer->device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
单独打开DSI1的panel之后的开机log为:
[ 4.794452] msm_mdss_dsi:[dsi_panel_device_register:4459]--dsi_panel_device_register: Continuous splash disabled
[ 4.810705] mdss_register_panel: adding framebuffer device 1a96000.qcom,mdss_dsi_ctrl1//mdss_dsi.c注册DSI panel
[ 4.821589] msm_mdss_dsi:[dsi_panel_device_register:4484]--dsi_panel_device_register: Panel data initialized
[ 4.849551] msm_mdss_dsi:[mdss_dsi_ctrl_probe:3509]--mdss_dsi_ctrl_probe: Dsi Ctrl->1 initialized, DSI rev:0x10040002, PHY rev:0x3
[ 4.849589] msm_mdss_dsi:[mdss_dsi_pm_qos_add_request:157]--mdss_dsi_pm_qos_add_request: add request irq
[ 4.860240] msm_mdss_dsi:[mdss_dsi_config_clk_src:239]--mdss_dsi_config_clk_src: default: DSI0 <--> PLL0, DSI1 <--> PLL1[ 4.870732] mdss_dsi_status_init: DSI status check interval:5000
[ 4.882340] mdss_register_panel: adding framebuffer device soc:qcom,mdss_wb_panel//mdss_wb.c注册了WRITEBACK_PANEL,#define WRITEBACK_PANEL 10 /* Wifi display */
[ 4.888974] mdss_fb_probe: fb0: split_mode:0 left:0 right:0
[ 4.895863] mdss_fb_register: FrameBuffer[0] 800x1280 registered successfully!//fb0注册的是jd9365da这个连接到dsi1上面的屏幕
[ 4.899353] msm_mdss_dsi:[mdss_dsi_event_handler:2749]--mdss_dsi_event_handler+: ctrl=1 event=15
[ 4.906732] msm_mdss_dsi:[mdss_dsi_debugfs_init:1221]--mdss_dsi_debugfs_init: Initialized mdss_dsi_debugfs_init
[ 4.915503] msm_mdss_dsi:[mdss_dsi_event_handler:2899]--mdss_dsi_event_handler-:event=15, rc=0
[ 4.942516] msm_mdss_dsi:[mdss_dsi_event_handler:2749]--mdss_dsi_event_handler+: ctrl=1 event=9
[ 4.942553] msm_mdss_dsi:[mdss_dsi_event_handler:2896]--mdss_dsi_event_handler: unhandled event=9
[ 4.950020] msm_mdss_dsi:[mdss_dsi_event_handler:2899]--mdss_dsi_event_handler-:event=9, rc=0
[ 4.959140] msm_mdss_dsi:[mdss_dsi_event_handler:2749]--mdss_dsi_event_handler+: ctrl=1 event=10
[ 4.967575] msm_mdss_dsi:[mdss_dsi_event_handler:2896]--mdss_dsi_event_handler: unhandled event=10
[ 4.976432] msm_mdss_dsi:[mdss_dsi_event_handler:2899]--mdss_dsi_event_handler-:event=10, rc=0
[ 4.985300] mdss_fb_probe: fb1: split_mode:0 left:0 right:0
[ 4.994106] mdss_fb_register: FrameBuffer[1] 640x640 registered successfully!//fb1注册的是wifi display 屏幕
[ 4.999495] mdss_mdp_splash_parse_dt: splash mem child node is not present
[ 5.006790] msm_mdss_dsi:[mdss_dsi_event_handler:2749]--mdss_dsi_event_handler+: ctrl=1 event=9
[ 5.013333] msm_mdss_dsi:[mdss_dsi_event_handler:2896]--mdss_dsi_event_handler: unhandled event=9
fb1这个设备是与dtsi文件对应的
msm8937-mdss.dtsi:
qcom,mdss_wb_panel {
compatible = "qcom,mdss_wb";
qcom,mdss_pan_res = <640 640>;//所以才会注册了一个640*640的 wifi display设备
qcom,mdss_pan_bpp = <24>;
qcom,mdss-fb-map = <&mdss_fb1>;
};
mdss_fb1: qcom,mdss_fb_wfd {
cell-index = <1>;//fb1
compatible = "qcom,mdss-fb";
};
所以如果recovery模式下open设备/dev/graphics/fb1也是无法正常显示的:
diff --git a/bootable/recovery/minui/graphics_fbdev.cpp b/bootable/recovery/minui/graphics_fbdev.cpp
index 746f42a..03dda42 100644
--- a/bootable/recovery/minui/graphics_fbdev.cpp
+++ b/bootable/recovery/minui/graphics_fbdev.cpp
@@ -48,7 +48,7 @@ void MinuiBackendFbdev::SetDisplayedFramebuffer(unsigned n) {
}
GRSurface* MinuiBackendFbdev::Init() {
- int fd = open("/dev/graphics/fb0", O_RDWR);
+ int fd = open("/dev/graphics/fb1", O_RDWR);
if (fd == -1) {
perror("cannot open fb0");
return nullptr;
会打印以下的log:
[ 12.321183] mdss_mdp_overlay_pan_display: writeback update not supported through pan display
mdss_mdp_overlay.c
static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
{
...
/*
* Ignore writeback updates through pan_display as output
* buffer is not available.
*/
if (mfd->panel_info->type == WRITEBACK_PANEL) {//不支持wifi display,#define WRITEBACK_PANEL 10 /* Wifi display */
pr_err_once("writeback update not supported through pan display\n");
return;
}
...
}
开机过程中不停的打印以下log:
[ 29.098660] mdss_mdp_layer_atomic_validate_wfd: fail to validate the input layers = -22
[ 29.098666] mdss_fb_atomic_commit_ioctl: atomic commit failed ret:-22
[ 29.115587] __validate_layers: unable to get right mixer
甚至导致一直处于开机启动画面开不了机:
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
...
if (mfd->panel_info->type == WRITEBACK_PANEL) {//就是wifi display起作用了
mdp5_interface->atomic_validate =
mdss_mdp_layer_atomic_validate_wfd;//就是这个函数打印的
mdp5_interface->pre_commit = mdss_mdp_layer_pre_commit_wfd;
mdp5_interface->is_config_same = mdss_mdp_wfd_is_config_same;
}
...
}
所以最好注释掉:
qcom,mdss_wb_panel {
compatible = "qcom,mdss_wb";
qcom,mdss_pan_res = <640 640>;//所以才会注册了一个640*640的 wifi display设备
qcom,mdss_pan_bpp = <24>;
qcom,mdss-fb-map = <&mdss_fb1>;
};
开机过程中不要去注册fb1:
[ 4.994106] mdss_fb_register: FrameBuffer[1] 640x640 registered successfully!//fb1注册的是wifi display 屏幕
A58F:/ $ ls dev/graphics/
fb0//只有注册了一个fb0
由于单独开DSI1的时候,注册的framebuffer就是fb0,而recovery模式open的就是fb0,所以理论上recovery模式应该是能够显示的,实际上重新编译之后,之前编译可能没有编译到,recovery模式显示正常!
==============================================================================================================================================================================
关于sensor的缩放存在两种模式 binning mode skipping mode
skipping mode 就是把想要的数据采集上来 把其余的数据扔掉
binning mode 就是把相邻的像素合成一个像素,然后再输出
ISP pipeline的作用:ISP 算法从前端CMOS拿到 RAW 数据,经过ISP pipeline 的处理之后,交给后端图像识别算法或人眼查看,以实现好的识别效果或好的主观体验。ISP pipeline的具体处理包括图像细节的处理(NR、demosaic)、图像色彩的处理(WB,CC)和 压 bit 处理(Gamma、HDR、Local Tone Mapping)等。
Demosaic就是ISP Pipeline的关键模块之一,CMOS/CCD在成像时,感光面阵列前通常会有CFA(color filter array),CFA过滤不同频段的光(RGB三个频段的光),因此,Sensor的输出的RAW数据信号包含了3个通道的信息。由于人眼对绿色(大约550nm波长)光更敏感,因此CFA阵列包含1/2的G分量,1/4和R和1/4的B分量.Sensor输出RAW数据后,需要经过Demosaic模块(ISP中)将其转成RGB图像。Bayer2RGB的转换方法有许多,可以基于简单插值的代码支持BGGR、RGGB、GBRG三种bayer格式转换为RGB数据。
cmos YUV的ISP pipeline一般处理流程为:
_______________________________________________________________________________
| |
|Sensor |
| | ____________________________ |
| | __|______________________ | |
| \|/ | | | | |
| ' | \|/ | | |
| BLC--->Lens Shading--->BPC--->Demosaic--->Bayer denoise ----->AWB |
| ^ | | | |
| | |_________________________| | |
| | | |
| ------------------------ AEC <---------Y gamma<-----------| |
| | |
| | |
| \|/ |
| ' |
| HEQ/Saturation<---Color denoise/<---RGB to YUV<--Gamma<--Color Correction |
| | sharpness |
| | |
| | |
| \|/ |
| ' |
| Formater------>YUV420------->I/O Control-------> |
|_______________________________________________________________________________|
图1.ISP pipeline一般处理流程
总述:
Bayer format 图像,经过黑电平补偿 ( black level compensation)、镜头矫正 ( lens shading correction)、坏像素矫正 ( bad pixel correction)、颜色插值 ( demosaic)、Bayer 噪声去除、 白平衡( awb) 矫正、 色彩矫正( color correction) 、 gamma 矫正、 色彩空间转换( RGB 转换为 YUV) 、 在 YUV 色彩空间上彩噪去除与边缘加强/锐化、 色彩与对比度加强,中间还要进行自动曝光控制(这个是需要实时不断地下寄存器调整的)等, 然后输出 YUV( 或者 RGB) 格式的数据。
在C2395 sensor(拥有片上ISP)的功能框图中呢也有这么一个ISP Pipeline模块,这里的特性有提到它这个ISP支持了我们上面框图中的前两个功能模块:ABLC和BPC
(1)黑电平校正(暗电流校正)
暗电流指传感器在没有入射光的情况下,存在一定的信号输出,这是由于半导体的热运动造成的,它的大小和传感器结构及温度有关,大概每升高9 ℃,其暗电流会增加1 倍。由于每个像素存在不平衡性,因此像素间暗电流也会不一致,造成电流噪声。一般情况下,在传感器中,实际像素要比有效像素多,像素区最靠边的行和列为不感光区,一般用作自动黑电平校正,其平均值作为校正值。C2395的Active Pixel Array(1080P)是1936H*1096V,有效的输出图像是1920*1080,因为上下左右都是8个像素的dummy lines
(4)Gamma 校正
Gamma 校正主要依据色度学原理进行调整,色彩在不同显示设备中频谱响应度不一样,造成颜色失真。失真成幂指数关系,因此调节相对简单,分别对R、G、B 调节即可。
(5)RGB2YUV 转换
Y =16 +(0. 257 × R +0. 504 × G +0. 098 × B)
Cb =128 +(-0. 148 × R-0. 291 × G +0. 439 × B)
Cr =128 +(0. 439 × R-0. 368 × G-0. 071 × B)
(6)自动白平衡处理
如果使用过没有白平衡的数码相机,会发现荧光灯的光人眼看起来是白色的,但用数码相机拍摄出来却有点偏绿。同样,如果在白炽灯下,拍出图像的色彩就会明显偏红。人类的眼睛之所以把它们都看成白色,是因为人眼进行了修正。由于图像传感器本身没有这种功能,因此有必要对它输出的信号进行一定的修正,这种修正叫作白平衡。
白平衡控制通过图像色彩调整(一般调节R、B 增益实现),使在各种光线条件下拍摄出的照片色彩和人眼所看到的景物色彩完全相同。从理论上我们知道,随着色温的升高,要对色温进行较正,否则,物体在这样的光线条件下所表现出来的颜色就会偏离其正常的颜色,因此需要降低 sensor对红色的增益,增加sersor对蓝光的增益。同时在调整参数时一定程度上要考虑到整体亮度的要保持大致的不变,即以YUV来衡量时,Y值要 基本保持不变,理论上认为可以参考RGB->YUV变换公式中,RGB三分量对Y值的贡献,从而确定RGAIN和BGAIN的变化的比例关系。但实 际情况比这还要复杂一些,要考虑到不同sensor对R,B的感光的交叉影响和非线性,所以最佳值可能和理论值会有一些偏差。
(7)自动曝光、AE 评估
自动曝光,简单地说就是自动控制曝光时间,达到曝光恰到好处的效果。曝光过度,图像传感器就会产生溢出,造成对比度下降,动态灵敏度降低。曝光不够,同样会造成对比度下降,动态灵敏度降低,信噪比下降,画面效果不好。因此在不同的场景必须对曝光时间进行控制。自动曝光主要是对某可选区域内画面亮度分量(y 信号)进行评估,若y 偏小,增大曝光量,反之减少。
感光宽容度
从最明亮到最黑暗,假设人眼能够看到一定的范围,那么胶片(或CCD等电子感光器件)所能表现的远比人眼看到的范围小的多,而这个有限的范围就是感光宽容度。人眼的感光宽容度比胶片要高很多,而胶片的感光宽容度要比数码相机的ccd高出很多!了解这个概念之后,我们就不难了解,为什么在逆光的条件 下,人眼能看清背光的建筑物以及耀眼的天空云彩。而一旦拍摄出来,要么就是云彩颜色绚烂而建筑物变成了黑糊糊的剪影,要么就是建筑物色彩细节清楚而原本美 丽的云彩却成了白色的一片再看人眼的结构,有瞳孔可以控制通光量,有杆状感光细胞和椎状感光细胞以适应不同的光强,可见即使人眼有着很高的感光宽容度,依然有亮度调节系统,以适应光强变化。那么对于camera sensor来说,正确的曝光就更为重要了!
自动曝光和18%灰
对于sensor来说,又是如何来判断曝光是否正确呢?很标准的做法就是在YUV空间计算当前图像的Y值的均值。调节各种曝光参数设定(自动或手动),使得该均值落在一个目标值附近的时候,就认为得到了正确的曝光。那么如何确定这个Y的均值,以及如何调整参数使得sensor能够将当前图像的亮度调整到这个范围呢?这就涉及到一个概念 18%灰,一般认为室内室外的景物,在通常的情况下,其平均的反光系数大约为18%,而色彩均值,如前所述,可以认为是一种中灰的色调。这样,可以通过对 反光率为18%的灰板拍摄,调整曝光参数,使其颜色接近为中等亮度的灰色(Y值为128)。然后,对于通常的景物,就能自动的得到正确的曝光了。当然这种自动判断曝光参数的AE功能不是万能的,对于反光率偏离通常均值的场景,比如雪景,夜景等,用这种方法就无法得到正确的曝光量了。所以在sensor的软件处理模块中,通常还会提供曝光级别的设定功能,强制改变自动曝光的判断标准。比如改变预期的亮度均值等。
曝光参数的调整
曝光强度的调整,可以通过改变曝光时间,也可以通过改变亮度增益AG来实现。曝光时间受到桢频的限制,比如摄像时要求15帧每秒的话,这时候曝光时间最长就不能超过1/15s,可能还有别的条件限制,实际的曝光时间还要短,在光线弱的情况下,单独调整曝光时间就无法满足帧频的需要了。这时候还可以调整增益AG,来控制曝光的增益,降低曝光时间。但是,这样做的缺点是以牺牲图像质量为代价的,AG的增强,伴随的必然是信噪比的降低,图像噪声的增强。所以,以图像质量为优先考虑的时候,曝光参数的调节通常是优先考虑调节曝光时间,其次在考虑曝光增益。当然曝光时间也不能过长以免由于抖动造成图像的模糊,而在拍摄运动场景时,对曝光时间的要求就更高了。
抗噪处理
AG 的增大,不可避免的带来噪点的增多,此外,如果光线较暗,曝光时间过长,也会增加噪点的数目(从数码相机上看,主要是因为长时间曝光,感光元件温度升高, 电流噪声造成感光元件噪点的增多),而感光元件本身的缺陷也是噪点甚至坏点的来源之一。因此,通常sensor集成或后端的ISP都带有降噪功能的相关设置。
==========================================================================================
如果在人脸识别机器上,kenel和xml配置文件,ID不变,在kernel里面默认高通的硬件资源指定id为0是后摄,2是前摄像,1是后辅摄,这个与xml配置文件一致,这个ID信息不可都强制改成qcom,camera@2,这样的话DTS编译会报错,ID重复有冲突,
现在只把位置信息从后摄像改为前摄像头,开机能够正常probe,也能正常点亮,正常枚举出两个摄像头,一个ID是0,一个ID是1,只是信息都成了前置摄像头!
以下是两个摄像头切换的时候的log:
//关闭红外(camera id 1),打开RGB(camera id 0)
10-23 10:14:47.642 3828 3828 V CAM_PhotoModule: Start to switch camera. cameraId=0
10-23 10:14:47.647 559 1315 I QCamera : <HAL><INFO> stop_preview: 418: [KPI Perf]: E PROFILE_STOP_PREVIEW camera id 1//红外关闭预览
10-23 10:14:47.689 559 4261 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: command=8000009
10-23 10:14:47.689 559 4261 I mm-camera: <MCT >< INFO> 4285: mct_pipeline_process_set: STREAM-OFF on stream 0x20004 stream type=11
...
10-23 10:14:47.748 3828 3828 V CAM_PhotoModule: Start to switch camera. id=0
10-23 10:14:47.749 3828 3828 V CAM_PhotoModule: Close camera device.
10-23 10:14:47.753 559 1315 I QCamera : <HAL><INFO> stop_preview: 418: [KPI Perf]: E PROFILE_STOP_PREVIEW camera id 1
10-23 10:14:47.761 559 1315 I QCamera : <HAL><INFO> cancel_picture: 1057: [KPI Perf]: E PROFILE_CANCEL_PICTURE camera id 1
10-23 10:14:47.761 559 1315 I QCamera : <HAL><INFO> cancel_picture: 1066: [KPI Perf]: X camera id 1 ret = 0
10-23 10:14:47.762 559 1315 I CamDev@1.0-impl: Closing camera 1
10-23 10:14:47.762 559 1315 I QCamera : <HAL><INFO> close_camera_device: 1527: [KPI Perf]: E camera id 1
10-23 10:14:47.766 559 1315 I QCamera : <HAL><INFO> closeCamera: 2335: [KPI Perf]: E PROFILE_CLOSE_CAMERA camera id 1
...
10-23 10:14:47.835 559 1315 I QCamera : <HAL><INFO> closeCamera: 2406: [KPI Perf]: X PROFILE_CLOSE_CAMERA camera id 1, rc: 0
10-23 10:14:47.838 559 1315 I QCamera : <HAL><INFO> close_camera_device: 1545: [KPI Perf]: X
10-23 10:14:47.843 693 2281 I CameraService: disconnect: Disconnected client for camera 1 for PID 3828
10-23 10:14:47.849 3828 3828 V CameraHolder: open camera 0//打开RGB
10-23 10:14:47.851 3828 3943 E libc : Access denied finding property "vendor.camera.hal1.packagelist"
10-23 10:14:47.851 693 1648 I CameraService: CameraService::connect call (PID -1 "org.codeaurora.snapcam", camera ID 0) for HAL version 256 and Camera API version 1
10-23 10:14:47.873 739 4331 E FileSource: Failed to open file '/product/media/audio/ui/camera_click.ogg'. (No such file or directory)
10-23 10:14:47.874 693 1648 E CameraService: Failed to load CameraService sounds: /product/media/audio/ui/camera_click.ogg
10-23 10:14:47.996 693 1648 I CameraHardwareInterface: Opening camera 0
10-23 10:14:47.996 559 1315 I CamDev@1.0-impl: Opening camera 0
10-23 10:14:47.996 559 1315 I QCamera : <HAL><INFO> openLegacy: 503: openLegacy halVersion: 256 cameraId = 0
10-23 10:14:47.997 555 1009 D audio_hw_primary: adev_set_parameters: enter: cameraFacing=front//RGB是前摄像头
10-23 10:14:48.001 559 1315 I QCamera : <HAL><INFO> openCamera: 1898: [KPI Perf]: E PROFILE_OPEN_CAMERA camera id 0
...
10-23 10:14:48.060 559 1315 I Thermal-Lib: Thermal-Lib-Client: Registration successful for camera with handle:1
10-23 10:14:48.060 559 1315 I QCamera : <HAL><INFO> openCamera: 1928: [KPI Perf]: X PROFILE_OPEN_CAMERA camera id 0, rc: 0
...
--------------------------------------------------------------------------------------------------------------------------------------
//关闭RGB(camera id 1),打开红外(camera id 0)
10-23 10:14:51.139 3828 3828 V CAM_PhotoModule: Start to switch camera. cameraId=1
10-23 10:14:51.144 559 1315 I QCamera : <HAL><INFO> stop_preview: 418: [KPI Perf]: E PROFILE_STOP_PREVIEW camera id 0
...
10-23 10:14:51.247 559 4350 I mm-camera: <MCT >< INFO> 4388: mct_pipeline_process_set: Stream 0x10004 and stream type=11, successfully deleted
10-23 10:14:51.255 3828 3828 V CAM_PhotoModule: Start to switch camera. id=1
10-23 10:14:51.256 3828 3828 V CAM_PhotoModule: Close camera device.
10-23 10:14:51.260 559 1315 I QCamera : <HAL><INFO> stop_preview: 418: [KPI Perf]: E PROFILE_STOP_PREVIEW camera id 0//RGB摄像头关闭预览
10-23 10:14:51.263 559 1315 I QCamera : <HAL><INFO> cancel_picture: 1057: [KPI Perf]: E PROFILE_CANCEL_PICTURE camera id 0
10-23 10:14:51.266 559 1315 I QCamera : <HAL><INFO> cancel_picture: 1066: [KPI Perf]: X camera id 0 ret = 0
10-23 10:14:51.267 559 1315 I CamDev@1.0-impl: Closing camera 0
10-23 10:14:51.267 559 1315 I QCamera : <HAL><INFO> close_camera_device: 1527: [KPI Perf]: E camera id 0
10-23 10:14:51.272 559 1315 I QCamera : <HAL><INFO> closeCamera: 2335: [KPI Perf]: E PROFILE_CLOSE_CAMERA camera id 0
...
10-23 10:14:51.342 559 1315 I mm-camera: <MCT >< INFO> 422: mct_controller_destroy: X Successfully closed mct_controller session 1
10-23 10:14:51.345 559 1315 I QCamera : <HAL><INFO> closeCamera: 2406: [KPI Perf]: X PROFILE_CLOSE_CAMERA camera id 0, rc: 0
10-23 10:14:51.350 559 1315 I QCamera : <HAL><INFO> close_camera_device: 1545: [KPI Perf]: X
10-23 10:14:51.354 693 1648 I CameraService: disconnect: Disconnected client for camera 0 for PID 3828
10-23 10:14:51.361 3828 3828 V CameraHolder: open camera 1//打开红外摄像头
10-23 10:14:51.362 3828 3943 E libc : Access denied finding property "vendor.camera.hal1.packagelist"
10-23 10:14:51.363 693 2281 I CameraService: CameraService::connect call (PID -1 "org.codeaurora.snapcam", camera ID 1) for HAL version 256 and Camera API version 1
10-23 10:14:51.391 739 4411 E FileSource: Failed to open file '/product/media/audio/ui/camera_click.ogg'. (No such file or directory)
10-23 10:14:51.392 693 2281 E CameraService: Failed to load CameraService sounds: /product/media/audio/ui/camera_click.ogg
10-23 10:14:51.514 693 2281 I CameraHardwareInterface: Opening camera 1
10-23 10:14:51.515 559 1315 I CamDev@1.0-impl: Opening camera 1
10-23 10:14:51.515 559 1315 I QCamera : <HAL><INFO> openLegacy: 503: openLegacy halVersion: 256 cameraId = 1
10-23 10:14:51.516 555 1009 D audio_hw_primary: adev_set_parameters: enter: cameraFacing=front//也是作为前摄像头
10-23 10:14:51.520 559 1315 I QCamera : <HAL><INFO> openCamera: 1898: [KPI Perf]: E PROFILE_OPEN_CAMERA camera id 1
================================================================================================================================
1)have a look at sensor_pick_resolution() for how the res is selected.
2)
#define MAX_RESOLUTION_MODES 32
447 struct sensor_lib_out_info_array {
448 /* sensor output for each resolutions */
449 struct sensor_lib_out_info_t out_info[MAX_RESOLUTION_MODES];
...
关于camera sensor驱动的关键结构:
/* Sensor Handler */
static sensor_lib_t sensor_lib_ptr =
...
}
typedef struct {
sensor_lib_params_t *lib_params;//展开
sensor_data_t *s_data;
int32_t readfd;
int32_t writefd;
int32_t session_count;
uint32_t last_frame_id;
sen_delay_api_t delay_api;
sensor_set_expo_t expo;
PD_HANDLE pd_handle;
PD_CAMIF_HANDLE pd_camif_handle;
boolean is_pdaf_initiated;
} sensor_ctrl_t;
typedef struct {
void *sensor_lib_handle;
sensor_lib_t *sensor_lib_ptr;//展开
sensor_custom_API_t sensor_custom_API;
} sensor_lib_params_t;
//sensor_lib_t结构体
typedef struct {
/* sensor slave info */
struct camera_sensor_slave_info sensor_slave_info;
/* sensor output settings */
sensor_output_t sensor_output;
/* sensor output register address */
struct sensor_output_reg_addr_t output_reg_addr;
/* sensor exposure gain register address */
struct sensor_exp_gain_info_t exp_gain_info;
/* sensor aec info */
sensor_aec_data_t aec_info;
/* number of frames to skip after start stream info */
unsigned short sensor_num_frame_skip;
/* number of frames to skip after start HDR stream info */
unsigned short sensor_num_HDR_frame_skip;
/* sensor pipeline delay */
unsigned int sensor_max_pipeline_frame_delay;
/* sensor lens info */
sensor_property_t sensor_property;
/* imaging pixel array size info */
sensor_imaging_pixel_array_size pixel_array_size_info;
/* Sensor color level information */
sensor_color_level_info color_level_info;
/* sensor port info that consists of cid mask and fourcc mapaping */
sensor_stream_info_array_t sensor_stream_info_array;
/* Sensor Settings */
struct camera_i2c_reg_setting_array start_settings;
struct camera_i2c_reg_setting_array stop_settings;
struct camera_i2c_reg_setting_array groupon_settings;
struct camera_i2c_reg_setting_array groupoff_settings;
struct camera_i2c_reg_setting_array embedded_data_enable_settings;
struct camera_i2c_reg_setting_array embedded_data_disable_settings;
struct camera_i2c_reg_setting_array aec_enable_settings;
struct camera_i2c_reg_setting_array aec_disable_settings;
struct camera_i2c_reg_setting_array dualcam_master_settings;
struct camera_i2c_reg_setting_array dualcam_slave_settings;
/* sensor test pattern info */
sensor_test_info test_pattern_info;
/* sensor effects info */
struct sensor_effect_info effect_info;
/* Sensor Settings Array */
struct sensor_lib_reg_settings_array init_settings_array;
struct sensor_lib_reg_settings_array res_settings_array;
struct sensor_lib_out_info_array out_info_array;
struct sensor_csi_params csi_params;
struct sensor_csid_lut_params_array csid_lut_params_array;
struct sensor_lib_crop_params_array crop_params_array;
/* Exposure Info */
sensor_exposure_table_t exposure_func_table;
/* video_hdr mode info*/
struct sensor_lib_meta_data_info_array meta_data_out_info_array;
/* sensor optical black regions */
sensor_optical_black_region_t optical_black_region_info;
/* sensor_capability */
sensor_capability_t sensor_capability;
/* sensor_awb_table_t */
sensor_awb_table_t awb_func_table;
/* sensor_awb_table_t */
sensor_fps_table_t fps_func_table;
/* Parse RDI stats callback function */
sensor_RDI_parser_stats_t parse_RDI_stats;
/* full size info */
sensor_rolloff_config rolloff_config;
/* analog-digital conversion time */
long long adc_readout_time;
/* number of frames to skip for fast AEC use case */
unsigned short sensor_num_fast_aec_frame_skip;
/* add soft delay for sensor settings like exposure, gain ...*/
unsigned char app_delay[SENSOR_DELAY_MAX];
/* for noise profile calculation
Tuning team must update with proper values. */
struct sensor_noise_coefficient_t noise_coeff;
/* Flag to be set if any external library are to be loaded */
unsigned char external_library;
sensorlib_pdaf_apis_t sensorlib_pdaf_api;
pdaf_lib_t pdaf_config;
/* sensor orientation flag */
sensor_orientation_type_t sensor_orientation;
} sensor_lib_t;
Camera的几个名词解释:
Linux OS 所作的工作是加载和执行camera驱动软件,比如控制camera相关的硬件块,设置和控制块,例如控制camera sensor的GPIO
Video Front End(VFE)用于处理camera框架数据的图像处理硬件
Camera Postprocessor(CPP) Camera后期处理器,用于支持翻转/旋转,降噪,平滑/锐化,裁切,用于全尺寸的VFE输出帧的upscale特性
VPE(VideoPreprocessingEngine) 视频预处理引擎
ASD(AutoSenceDetection)自动检测
AFD(AutoFlikerDetection)自动闪烁检测
IS(imageStablization)图像稳定化
pproc-new |---snapshot-->JPEG
硬件流程:相机sensor-->ISPIF---->VFE----->VPE/CPP-->imglib-- |
Stats flip |--preview -->Display
sensor AF rotate
actuator AWB denoise
LED flash ASD crop
EEPROM AFD
IS
Color Processing
Sensor Processing
Frame Processing
高通安卓平台的camera方案分为:
Open source(开源)
Kernel space(内核空间):基于V4L2的图像流驱动框架
1)所有的硬件驱动都作为V4L2的子设备来实现;那些硬件子设备可以被打开和关闭,并且可以被拥有系统访问权限的守护进程域控制
2)开源代码位于kernel/msm-4.9/drivers/media/platform/msm/camera_v2/
3).dtsi文件位于kernel/msm-4.9/arch/arm64/boot/dts/
4)dtsi文件位于kernel/msm-4.9/Documentation/devicetree/bindings/media/video
User space(用户空间):高通HAL层源码
1)安卓使用这一接口与高通的相机子系统集成在一起
2)源码在hardware/qcom/camera/QCamera2/
高通私有的代码
1)user space-所有的高通私有的解决方案都是作为linux的守护进程来运行,包括以下几个主要模块:
(1)camera server,相机服务器
(2)Camera Media Control(MCTL) framework,相机多媒体控制框架/媒体控制器模块
(3)独立模块像ISP,sensor,stats,postprocessing等等
源码位于vendor/qcom/proprietary/mm-camera/mm-camera2/
JPEG源码位于:vendor/qcom/proprietary/mm-still/
VB2 queue
|
cameraserver process: |-----> M1 Sensor<------|------->Sensor driver
dlopen/dlsym | M2 Iface <------|------->Iface driver
Camera HAL->MM camera Interface--------------->MCT shim layer->Media Controller-- ... |
Mn ImgLib<------|------->Fd driver
|
User space | Kernel
dlopen的是这个库文件:
#define SHIMLAYER_LIB "/system/vendor/lib/libmmcamera2_mct_shimlayer.so"//MCT shim layer APIs
补充:
videobuf2 用于连接 V4L2 驱动层与用户空间层,提供数据交流的通道,它可以分配并管理视频帧数据。videobuf 层实现了很多 ioctl 函数,包括 buffer 分配、入队、出队和数据流控制。
videobuf2要解决的问题
a. 更正 V4L2 的 API,修复 videobuf1 的问题与缺点
b. 把队列管理与内存管理完全分离,采用分层的设计
c. 更多专门的驱动回调,不同类型驱动特定的,同时又满足可定制化的拆分,不必要的不使用,必要的按需使用
d. 支持新的V4L2 API扩展,比如多 planar 视频 buffer,比如 YUV 就是多 planer 的格式,它们的 Y、U、V 数据可能不在同一个连续的地址段内
HAL connect to /dev/videoX,高通相机HAL和kenel通过在/dev/videoX节点上使用V4L2 ICOTL命令来通信
Imaging server connect to /dev/config
高通相机HAL代码(QCamera2HWI.cpp)将安卓相机调用翻译成高通相机子系统相关调用
定制化的参数暴露在类QCameraParameters,这个类定义在QCameraParameters.h
Camera imaging server core process的session---------Open--------> Media Controller的stream manager,Media bus arbitor,Event/Command
----Add stream--> |
----Sync--------> |
\|/
Media pipeline+Media bus
int QCamera2Factory::get_number_of_cameras()开机的时候调用一次,现在要搞清楚MSM_CAMERA_SET_PARM这个事件是从哪里发过来的?
int QCamera2Factory::get_number_of_cameras()->QCamera2Factory->get_num_of_cameras->mm_camera_load_shim_lib->mct_shimlayer_process_module_init->mct_shimlayer_process_event->mct_shimlayer_handle_parm
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
调查设置骁龙相机的picture size切换分辨率大小的时候,sensor_pick_resolution()的调用流程,以及存储分辨率信息的数据结构是怎么传递的?
int QCamera2HardwareInterface::openCamera()//QCamera2HWI.cpp
rc = camera_open((uint8_t)mCameraId, &mCameraHandle);
int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl)//mm_camera_interface.c
cam_obj = (mm_camera_obj_t *)malloc(sizeof(mm_camera_obj_t));//cam_obj也是在这个函数内才创建的,也就是open camera的时候才会去创建出这个结构体
rc = mm_camera_open(cam_obj);//开双摄的时候会在mm_camera_muxer_camera_open调用mm_camera_open,双摄不是我们研究对象,暂时不理会
int32_t mm_camera_open(mm_camera_obj_t *my_obj)//mm_camera.c
open(dev_name, O_RDWR | O_NONBLOCK);//打开“/dev/video%d”设备
mm_camera_get_session_id(my_obj, &my_obj->sessionid);//拿到sessionid:&my_obj->sessionid
cam_status = mm_camera_module_open_session(my_obj->sessionid, mm_camera_module_event_handler);//带参数&my_obj->sessionid往下执行
mm_camera_cmd_thread_launch-->mm_camera_cmd_thread //创建了一个cmd线程
mm_camera_poll_thread_launch-->mm_camera_poll_thread //创建了一个poll线程,这是一个很关键的线程,最后获取数据也是靠这个线程但是现在开启的这个线程不是用来处理数据的,而是用来处理kernel返回的event,比如相机挂了
cam_status_t mm_camera_module_open_session(int sessionid, mm_camera_shim_event_handler_func evt_cb)//mm_camera_interface.c
rc = g_cam_ctrl.cam_shim_ops.mm_camera_shim_open_session(sessionid, evt_cb);//首个参数sessionid传入,往下调用就从hardware目录跳转到vendor目录下的接口了,高通的用户空间的私有代码
//-----------------这是一条hardware通往vendor的分割线----------------------------------------------------
int mct_shimlayer_process_module_init(mm_camera_shim_ops_t *shim_ops_tbl)//mct_shim_layer.c
shim_ops_tbl->mm_camera_shim_open_session = mct_shimlayer_start_session;//函数指针指定了mm_camera_shim_open_session接口的入口函数是mct_shimlayer_start_session,为hardware层提供了调用接口
cam_status_t mct_shimlayer_start_session(int session, mm_camera_shim_event_handler_func event_cb)//mct_shim_layer.c
ret = mct_controller_new(modules, session, config_fd, event_cb);//传入的第一个参数将创建mct
cam_status_t mct_controller_new(mct_list_t *mods, unsigned int session_idx, int serv_fd, void *event_cb)//mct_controller.c
mct = (mct_controller_t *)malloc(sizeof(mct_controller_t));//mct是函数内新创建的,不是从上层传下来的,也就是打开camera的时候创建了一次
mct->pipeline = mct_pipeline_new(session_idx, mct);//根据第二个参数session_idx创建了pipeline
if (pthread_create(&tid, NULL, mct_controller_thread_run, mct)) {//创建了一个CAM_MctServ的线程,但是每次关闭打开一个新的摄像头都会重新创建线程,最后一个参数mct(mct_controller_t *)是运行函数的参数。
if (pthread_create(&bus_hdl_tid, NULL, mct_bus_handler_thread_run, mct)) {//创建了一个CAM_MctBus的线程,这个先不管他
static void* mct_controller_thread_run(void *data)//mct_controller.c
{
mct_this = (mct_controller_t *)data;//mct_this就来自于线程参数data
//处理和线程间通信有关的信号、锁等机制
do{//就在这个线程死循环接收来自HAL层的或DS(DAEMON_PRESENT没有宏定义,所以domain socket实际没有使用)的消息
while(1){
msg = (mct_serv_msg_t *)mct_queue_pop_head(mct_this->serv_cmd_q);//不断从mct_queue_t命令队列里取出消息命令,这个是由Imaging Server发送给MCT/MediaController的消息
if (!msg) {//没有取到消息就会break掉退出第一层处理消息的死循环
break;
}
*mct_ret = mct_controller_proc_serv_msg_internal(mct_this, msg);//mct_this就包含分辨率信息
}
}while(1);
}
static mct_process_ret_t mct_controller_proc_serv_msg_internal(mct_controller_t *mct, mct_serv_msg_t *msg)//mct_controller.c mct object处理来自imaging server的消息
pipeline = mct->pipeline;//pipeline(mct_pipeline_t *)来自mct包含的pipeline成员
case SERV_MSG_HAL://每次修改分辨率都会接收到来自HAL层的新的消息
ret.u.serv_msg_ret.error = pipeline->process_serv_msg(&msg->u.hal_msg, pipeline);
mct_pipeline_t* mct_pipeline_new (unsigned int session_idx, mct_controller_t *pController)//mct_pipeline.c
pipeline->process_serv_msg= mct_pipeline_process_serv_msg;//函数指针指定函数入口 mct_pipeline_process_serv_msg()
static boolean mct_pipeline_process_serv_msg(void *message, mct_pipeline_t *pipeline)//mct_pipeline.c
case MSM_CAMERA_SET_PARM://修改分辨率大小或打开其他camera会收到msg->id = MSM_CAMERA_SET_PARM,
ret = mct_pipeline_process_set(data, pipeline);//pipeline是参数传进来的
static boolean mct_pipeline_process_set(struct msm_v4l2_event_data *data, mct_pipeline_t *pipeline)//mct_pipeline.c
stream = mct_pipeline_get_stream(pipeline, &info);//根据mct_pipeline_t *pipeline入参拿到mct_stream_t * stream
//展开看下:
mct_stream_t* mct_pipeline_get_stream(mct_pipeline_t *pipeline, mct_pipeline_get_stream_info_t *get_info)
find_list = mct_list_find_custom(MCT_PIPELINE_CHILDREN(pipeline),//拿到mct_list_t *find_list
return MCT_STREAM_CAST(find_list->data);//(mct_stream_t *)(find_list->data): mct_list_t链表的data就是mct_stream_t (steam对象)
case CAM_PRIV_STREAM_INFO_SYNC: {//预览过程中修改分辨率大小会收到这个cmd(V4L2_CID_PRIVATE_BASE + 24),然后出发module linking
CLOGE(CAM_MCT_MODULE, "[wanghl]1. MCT_STREAM_LINK");//每次修改picture size就会调用到这里,log打印:mm-camera: <MCT ><ERROR> 4235: mct_pipeline_process_set: [wanghl]1. MCT_STREAM_LINK
(MCT_STREAM_LINK(stream)) ? (ret = (MCT_STREAM_LINK(stream))(stream)) : (ret = FALSE);
//展开这个宏函数MCT_STREAM_LINK(stream),切换打开camera的时候mct_pipeline_start_stream_internal也会调用MCT_STREAM_LINK(stream),暂且不管
#define MCT_STREAM_LINK(stream) (MCT_STREAM_CAST(stream)->link) 太猥琐了,差点找不到,
#define MCT_STREAM_CAST(stream) ((mct_stream_t *)(stream))
MCT_STREAM_LINK(stream) (mct_stream_t *)(stream)->link
(MCT_STREAM_LINK(stream))(stream) (mct_stream_t *)(stream)->link(stream)//stream先强转成(mct_stream_t *)类型,然后调用函数指针link函数,参数是stream,分辨率信息就在这个参数里
--------------------------------------------------------------------------------------------------------------------------------------------补充一下:mct_pipeline_process_set函数里处理的命令的定义:
11-06 11:27:30.316 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.337 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.341 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.358 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.370 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.383 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.396 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.400 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.424 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.426 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.427 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.431 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.433 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.439 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.442 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.443 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.445 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.448 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.449 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.452 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.457 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:30.458 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:30.459 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:30.482 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:30.492 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:30.595 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:30.607 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:30.631 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.639 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.643 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.661 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000009//MSM_CAMERA_PRIV_STREAM_OFF
11-06 11:27:30.672 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.684 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.694 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.697 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000b//MSM_CAMERA_PRIV_DEL_STREAM
11-06 11:27:30.778 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.779 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.780 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.785 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.787 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.793 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.796 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.798 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.800 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.803 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x800000a//MSM_CAMERA_PRIV_NEW_STREAM
11-06 11:27:30.805 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000018//CAM_PRIV_STREAM_INFO_SYNC,收到该事件
11-06 11:27:30.808 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000004//MSM_CAMERA_PRIV_S_FMT
11-06 11:27:30.812 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:30.813 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:30.814 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000019//CAM_PRIV_STREAM_PARM
11-06 11:27:31.149 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:31.163 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:31.256 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
11-06 11:27:31.267 566 4592 I mm-camera: <MCT >< INFO> 4146: mct_pipeline_process_set: [wanghl]mct_pipeline_process_set,command=0x8000008//MSM_CAMERA_PRIV_STREAM_ON
消息类型的定义在:kernel/msm-4.9/include/uapi/media/msmb_camera.h
/* data.command */
/* IDs reserved for driver specific controls */
#define V4L2_CID_PRIVATE_BASE 0x08000000
...
#define MSM_CAMERA_PRIV_S_CROP (V4L2_CID_PRIVATE_BASE + 1)
#define MSM_CAMERA_PRIV_G_CROP (V4L2_CID_PRIVATE_BASE + 2)
#define MSM_CAMERA_PRIV_G_FMT (V4L2_CID_PRIVATE_BASE + 3)
#define MSM_CAMERA_PRIV_S_FMT (V4L2_CID_PRIVATE_BASE + 4)
#define MSM_CAMERA_PRIV_TRY_FMT (V4L2_CID_PRIVATE_BASE + 5)
#define MSM_CAMERA_PRIV_METADATA (V4L2_CID_PRIVATE_BASE + 6)
#define MSM_CAMERA_PRIV_QUERY_CAP (V4L2_CID_PRIVATE_BASE + 7)
#define MSM_CAMERA_PRIV_STREAM_ON (V4L2_CID_PRIVATE_BASE + 8)
#define MSM_CAMERA_PRIV_STREAM_OFF (V4L2_CID_PRIVATE_BASE + 9)
#define MSM_CAMERA_PRIV_NEW_STREAM (V4L2_CID_PRIVATE_BASE + 10)
#define MSM_CAMERA_PRIV_DEL_STREAM (V4L2_CID_PRIVATE_BASE + 11)
#define MSM_CAMERA_PRIV_SHUTDOWN (V4L2_CID_PRIVATE_BASE + 12)
#define MSM_CAMERA_PRIV_STREAM_INFO_SYNC \
(V4L2_CID_PRIVATE_BASE + 13)
#define MSM_CAMERA_PRIV_G_SESSION_ID (V4L2_CID_PRIVATE_BASE + 14)
#define MSM_CAMERA_PRIV_CMD_MAX 20
另外有一部分是在:
hardware/qcom/camera/QCamera2/stack/common/cam_intf.h
#define CAM_PRIV_IOCTL_BASE (V4L2_CID_PRIVATE_BASE + MSM_CAMERA_PRIV_CMD_MAX)//0x08000000 + 20
typedef enum {
/* session based parameters */
CAM_PRIV_PARM = CAM_PRIV_IOCTL_BASE,//(V4L2_CID_PRIVATE_BASE + 20)
/* session based action: do auto focus.*/
CAM_PRIV_DO_AUTO_FOCUS,//(V4L2_CID_PRIVATE_BASE + 21)
/* session based action: cancel auto focus.*/
CAM_PRIV_CANCEL_AUTO_FOCUS,//(V4L2_CID_PRIVATE_BASE + 22)
/* session based action: prepare for snapshot.*/
CAM_PRIV_PREPARE_SNAPSHOT,//(V4L2_CID_PRIVATE_BASE + 23)
/* sync stream info.*/
CAM_PRIV_STREAM_INFO_SYNC,//(V4L2_CID_PRIVATE_BASE + 24)
/* stream based parameters*/
CAM_PRIV_STREAM_PARM,//(V4L2_CID_PRIVATE_BASE + 25)
/* start ZSL snapshot.*/
CAM_PRIV_START_ZSL_SNAPSHOT,//(V4L2_CID_PRIVATE_BASE + 26)
/* stop ZSL snapshot.*/
CAM_PRIV_STOP_ZSL_SNAPSHOT,//(V4L2_CID_PRIVATE_BASE + 27)
/* event for related sensors synchronization. */
CAM_PRIV_DUAL_CAM_CMD,//(V4L2_CID_PRIVATE_BASE + 28)
/* flush */
CAM_PRIV_FLUSH//(V4L2_CID_PRIVATE_BASE + 29)
} cam_private_ioctl_enum_t;
--------------------------------------------------------------------------------------------------------------补充定义完毕
mct_stream_t* mct_stream_new(uint32_t stream_id)//mct_stream.c
stream->link = mct_stream_start_link;
static boolean mct_stream_start_link(mct_stream_t *stream)//mct_stream.c,分辨率信息在参数里
ret = mct_stream_link_module_array(stream,link_mod);//分辨率信息在stream从参数传到这里
boolean mct_stream_link_module_array(mct_stream_t *stream, char *(*stream_type_mod)[MAX_STREAM_MODULES])//mct_stream.c
mct_module_t *module[MAX_STREAM_MODULES];
mct_list_t *modules = MCT_PIPELINE_MODULES(MCT_OBJECT_PARENT(stream)->data);//反正就是modules是根据stream得到
#define MCT_OBJECT_PARENT(obj) (MCT_OBJECT_CAST(obj)->parent)
MCT_OBJECT_PARENT(stream) (MCT_OBJECT_CAST(stream)->parent)
#define MCT_OBJECT_CAST(obj) ((mct_object_t*)(obj))
MCT_OBJECT_CAST(stream)->parent (mct_object_t*)(stream)->parent//实际上应该是得到了一个mct_list_t
MCT_OBJECT_PARENT(stream)->data (mct_object_t*)(stream)->parent->data实际上应该是得到了一个mct_list_t链表的data数据域
再套个壳 MCT_PIPELINE_MODULES(MCT_OBJECT_PARENT(stream)->data)
#define MCT_PIPELINE_MODULES(mod) (MCT_PIPELINE_CAST(mod)->modules)
#define MCT_PIPELINE_CAST(mod) ((mct_pipeline_t *)(mod))
MCT_PIPELINE_MODULES(mod) ((mct_pipeline_t *)(mod)->modules)//()优先级高于.(对象.成员名)高于->(对象指针->成员名),实际得到一个mct_list_t *
(mct_pipeline_t *)((mct_object_t*)(stream)->parent->data)->modules
虽然整的不是很明白,但大概就是stream强转成mct_object_t*,然后拿到parent成员,再拿到data成员,然后强转成mct_pipeline_t *,最后拿到modules成员,反正最终就是modules是从stream转换而来
Stream's parent should be pipeline,//stream的父亲节点是pipeline
包含关系是:
* Parents and Children information:
*
* One Object matches to One Pipeline;//一个Object匹配一个pipeline
* One Pipeline may have multiple children(Streams);//一个pipeline可能包含多个Stream子节点,Stream只有一个父亲
* One Stream may have multiple children(Modules);//一个Stream可能包含多个Module子节点,Module可能有多个父亲
* One Module may have multiple children(Ports);//一个Module可能包含多个Port子节点,一个Port只有一个父亲
* One Port may have multiple children(Stream and Session
* Indics sets);//一个Port可能包含多个Stream和Session Indic set
Object
|
pipeline
/\
/ \
stream stream
/\ /\
/ \ / \
module module module
/\ /\ /\
/ \ / \ / \
port port port port port port
/\
/ \
session stream?
while(1) {//死循环里面一直在拿mct_module_t并处理MCT发来的set mod事件
module[num_mod] = mct_stream_get_module(modules,stream_type_mod[mod_row][mod_col]);//module[0]是从modules拿到的
module[num_mod]->set_mod(module[num_mod],mod_type,stream->streaminfo.identity);//分辨率信息就在第一个参数里module[num_mod],log打印:module[0]->object.name : sensor, num_mod: 0,所以就是第一个参数是module[0]
}
mct_module_t *module_sensor_init(const char *name)//module_sensor.c
s_module = mct_module_create(name);//name : sensor,所以s_module->object.name : sensor
s_module->set_mod = module_sensor_set_mod;//填充MCT模块功能表
static void module_sensor_set_mod(mct_module_t *module, uint32_t module_type, uint32_t identity)//module_sensor.c
if (module_type == MCT_MODULE_FLAG_SOURCE) {
mct_module_set_process_event_func(module, module_sensor_module_process_event);//从第一个参数传入module,处理MCT发来的set mod事件
}
static boolean module_sensor_module_process_event(mct_module_t *module, mct_event_t *event)//module_sensor.c
RETURN_ON_FALSE(sensor_util_get_sbundle(module, event->identity, &bundle_info));
//展开看下是怎么从module得到bundle_info
boolean sensor_util_get_sbundle(mct_module_t *s_module, uint32_t identity, sensor_bundle_info_t *bundle_info)//sensor_util.c
module_ctrl = (module_sensor_ctrl_t *)s_module->module_private;//拿到module_sensor_ctrl_t *module_ctrl
s_list = mct_list_find_custom(module_ctrl->sensor_bundle, &session_id, sensor_util_find_bundle);//拿到链表结构:mct_list_t *s_list
s_bundle = (module_sensor_bundle_info_t *)s_list->data;//取出链表data就是s_bundle(module_sensor_bundle_info_t *)
bundle_info->s_bundle = s_bundle;//拿到s_bundle到处到输出参数,搞定!
SERR("[wanghl]after max_width:%d", bundle_info.s_bundle->max_height);//到这步就已经能到打印出来分辨率的高度了,所以分辨率信息在这步之前就已经拿到了
case MCT_EVENT_CONTROL_STREAMON:
ret = port_sensor_handle_stream_on(module, event, &bundle_info);//分辨率信息在bundle_info
boolean port_sensor_handle_stream_on(mct_module_t *module, mct_event_t *event, sensor_bundle_info_t *bundle_info)//port_sensor.c,分辨率信息在最后一个参数sensor_bundle_info_t *bundle_info
ret = port_sensor_handle_stream_on_normal(module, event, bundle_info);//分辨率信息:bundle_info
static boolean port_sensor_handle_stream_on_normal(mct_module_t *module, mct_event_t *event, sensor_bundle_info_t *bundle_info)//port_sensor.c,分辨率信息在最后一个参数sensor_bundle_info_t *bundle_info
ret = module_sensor_stream_on(module, event, bundle_info->s_bundle);//分辨率信息在bundle_info->s_bundle
boolean module_sensor_stream_on(mct_module_t *module, mct_event_t *event, module_sensor_bundle_info_t *s_bundle)//module_sensor.c,分辨率信息在最后一个参数s_bundle
stream_on_flag = module_sensor_is_ready_for_stream_on(port, event, &stream_on_cfg, s_bundle, bundle_id);//分辨率信息:stream_on_cfg是在module_sensor_stream_on函数内新创建的,分辨率信息肯定肯定是在这个函数里被更新的,比如分辨率的更新,展开这个函数也确定是在这个函数更新的分辨率信息
//展开一下module_sensor_is_ready_for_stream_on这个函数
static boolean module_sensor_is_ready_for_stream_on(mct_port_t *port,
mct_event_t *event, sensor_set_res_cfg_t *res_cfg,
module_sensor_bundle_info_t *s_bundle, int32_t bundle_id)//module_sensor.c
res_cfg->width = s_bundle->max_width;//从s_bundle->max_width拿到要设置的分辨率的宽
res_cfg->height = s_bundle->max_height;//从s_bundle->max_height拿到要设置的分辨率的高
res_cfg->stream_mask = s_bundle->stream_mask;
SERR("[wanghl]after config: dim=%dx%d, mask=0x%x stream type: %d", stream_on_cfg.width, stream_on_cfg.height, stream_on_cfg.stream_mask, stream_info->stream_type);
//11-15 17:38:49.689 563 4586 E mm-camera: <SENSOR><ERROR> 3664: module_sensor_stream_on: [wanghl]config: dim=592x480, mask=0x80a stream type: 1
//11-15 17:38:49.785 563 4586 E mm-camera: <SENSOR><ERROR> 3664: module_sensor_stream_on: [wanghl]config: dim=592x480, mask=0x80a stream type: 3
//11-15 17:38:49.800 563 4586 E mm-camera: <SENSOR><ERROR> 3664: module_sensor_stream_on: [wanghl]config: dim=592x480, mask=0x80a stream type: 11
//stream type为 1:preview, 3:snapshot, 11:analysis stream会被打印出来:
CAM_STREAM_TYPE_DEFAULT, /* 0:default stream type */
CAM_STREAM_TYPE_PREVIEW, /* 1:preview */
CAM_STREAM_TYPE_POSTVIEW, /* 2:postview */
CAM_STREAM_TYPE_SNAPSHOT, /* 3:snapshot */
CAM_STREAM_TYPE_VIDEO, /* 4:video */
CAM_STREAM_TYPE_CALLBACK, /* 5:app requested callback */
CAM_STREAM_TYPE_IMPL_DEFINED, /* 6:opaque format: could be display, video enc, ZSL YUV */
CAM_STREAM_TYPE_METADATA, /* 7:meta data */
CAM_STREAM_TYPE_RAW, /* 8:raw dump from camif */
CAM_STREAM_TYPE_OFFLINE_PROC, /* 9:offline process */
CAM_STREAM_TYPE_PARM, /* 10:mct internal stream */
CAM_STREAM_TYPE_ANALYSIS, /* 11:analysis stream */
CAM_STREAM_TYPE_DEPTH, /* 12:Depth stream for depth sensor*/
CAM_STREAM_TYPE_MAX,
ret = module_sensor_set_new_resolution_stream_on(module, event, s_bundle, module_sensor_params, &stream_on_cfg, stream_info);//分辨率信息:stream_on_cfg就被当作参数往下传了
boolean module_sensor_set_new_resolution_stream_on(mct_module_t *module,
mct_event_t *event, module_sensor_bundle_info_t *s_bundle,
module_sensor_params_t* module_sensor_params,
sensor_set_res_cfg_t *stream_on_cfg, mct_stream_info_t* stream_info)//module_sensor.c
retVal = modules_sensor_set_new_resolution(module, event, s_bundle, module_sensor_params, stream_on_cfg, &is_retry, stream_info);//分辨率信息在stream_on_cfg
boolean modules_sensor_set_new_resolution(mct_module_t *module,
mct_event_t *event,
module_sensor_bundle_info_t *s_bundle,
module_sensor_params_t *module_sensor_params,
sensor_set_res_cfg_t *stream_on_cfg,
boolean *is_retry,
mct_stream_info_t *stream_info)//module_sensor.c
set_res.res_cfg = *stream_on_cfg;//分辨率信息来自于第五个参数stream_on_cfg
rc = module_sensor_params->func_tbl.process(module_sensor_params->sub_module_private, SENSOR_SET_RESOLUTION, &set_res);//分辨率信息在set_res
static int32_t sensor_process(void *sctrl, sensor_submodule_event_type_t event, void *data)//sensor.c
case SENSOR_SET_RESOLUTION:
rc = sensor_set_resolution(sctrl, data);//分辨率信息传到这个data里面
static int32_t sensor_set_resolution(void *sctrl, void *data)//sensor.c
rc = sensor_pick_resolution(sctrl, res_cfg, &res);
int32_t sensor_pick_resolution(void *sctrl, sensor_set_res_cfg_t *res_cfg, int32_t *pick_res)//sensor_pick_res.c
ret = sensor_pick_res_idx(sensor_pick);
static int32_t sensor_pick_res_idx(sensor_pick_dev_t *sensor_pick)//sensor_pick_res.c
ret = (*sensor_pick->check)[i](&sensor_pick->pick_data);
static int32_t sensor_pick_fill_data(void *sctrl, sensor_set_res_cfg_t *res_cfg)
sensor_pick->check = (check_func_t *)check_v1;//sensor_pick_res.c
const check_func_t check_v1 = {
[SEN_COND_FPS] = sensor_pick_check_fps,//
[SEN_COND_BOUNDED_FPS] = sensor_pick_check_bounded_fps,
[SEN_COND_ASPR] = sensor_pick_check_aspect_ratio,
[SEN_COND_W] = sensor_pick_check_width,//
[SEN_COND_H] = sensor_pick_check_height,//
[SEN_COND_CLK] = sensor_pick_check_pixclk,
[SEN_COND_MODE_QUADRA] = sensor_pick_check_mode,
[SEN_COND_MODE_HFR] = sensor_pick_check_mode,
[SEN_COND_MODE_DEF] = sensor_pick_check_mode,
[SEN_COND_MODE_IHDR] = sensor_pick_check_mode,
[SEN_COND_MODE_RHDR] = sensor_pick_check_mode,
[SEN_COND_MODE_MPIX] = sensor_pick_check_mpix,
[SEN_COND_MODE_BEST_RES] = sensor_pick_check_best_res
};
------------------------------------------
这样我们大概就有一个概念,是高通的imaging server的mct_controller_thread_run线程里往下调用的mct_pipeline_process_set里收到了上层HAL层一个CAM_PRIV_STREAM_INFO_SYNC的命令字,然后触发了link流程,修改了分辨率,
接着我们看HAL层是从哪里发的CAM_PRIV_STREAM_INFO_SYNC就能知道上层大概是怎么发设置分辨率的参数下来的?结果是发现上层骁龙相机调用了Camera类的stopPreview方法停止了预览,导致一系列的流程变化
mCamera.stopPreview();->//骁龙相机的packages/apps/SnapdragonCamera/src/com/android/camera/AndroidCameraManagerImpl.java的函数handleMessage处理了STOP_PREVIEW的消息,用了private android.hardware.Camera类的stopPreview方法
_stopPreview();// public final void stopPreview() { 来自frameworks/base/core/java/android/hardware/Camera.java的Camera类的stopPreview方法,也就是APP的api了
{ "_stopPreview",//来自frameworks/base/core/jni/android_hardware_Camera.cpp的android_hardware_Camera.cpp
"()V",
(void *)android_hardware_Camera_stopPreview }
c->stopPreview();//sp<Camera> c = get_native_camera(env, thiz, NULL);调用的就是Camera::stopPreview(),来自frameworks/base/core/jni/android_hardware_Camera.cpp的static void android_hardware_Camera_stopPreview(JNIEnv *env, jobject thiz),也就是传说中的JNI接口
c->stopPreview();//sp <::android::hardware::ICamera> c = mCamera;mCamera 就是在 CameraService::connect(&mCamera) 时得到的。所以 mCamera = CameraClient(代理类对象),来自frameworks/av/camera/Camera.cpp的void Camera::stopPreview()
mHardware->stopPreview(); //就是调用 CameraHardwareInterface::startPreview(),来自frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp的void CameraClient::stopPreview() {
mHidlDevice->stopPreview();//mHidlDevice 是 CameraDevice,来自frameworks/av/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp的void CameraHardwareInterface::stopPreview()
mDevice->ops->stop_preview(mDevice);->//hardware/interfaces/camera/device/1.0/default/CameraDevice.cpp文件里的Return<void> CameraDevice::stopPreview() {
.stop_preview = QCamera2HardwareInterface::stop_preview,//hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp定义的结构体有一个成员函数指针指定了QCamera2HardwareInterface::stop_preview
int32_t ret = hw->processAPI(QCAMERA_SM_EVT_STOP_PREVIEW, NULL);//更新状态机状态为:QCAMERA_SM_EVT_STOP_PREVIEW in hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp: void QCamera2HardwareInterface::stop_preview(struct camera_device *device)
下面是创建状态机的地方
//hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp
QCameraStateMachine::QCameraStateMachine(QCamera2HardwareInterface *ctrl) :
api_queue(),
evt_queue()
{
m_parent = ctrl;
m_state = QCAMERA_SM_STATE_PREVIEW_STOPPED;
cmd_pid = 0;
cam_sem_init(&cmd_sem, 0);
pthread_create(&cmd_pid,
NULL,
smEvtProcRoutine,//创建了一个状态机处理线程,从QCameraStateMachine::stateMachine的log上看确实不停地在跑
this);
pthread_setname_np(cmd_pid, "CAM_stMachine");
m_bDelayPreviewMsgs = false;
m_DelayedMsgs = 0;
m_RestoreZSL = TRUE;
m_bPreviewCallbackNeeded = TRUE;
m_bPreviewRestartedInternal = FALSE;
}
pme->stateMachine(node->evt, node->evt_payload);//hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp:void *QCameraStateMachine::smEvtProcRoutine(void *data)
rc = procEvtPreviewingState(evt, payload);->//hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp:int32_t QCameraStateMachine::stateMachine(qcamera_sm_evt_enum_t evt, void *payload)
case QCAMERA_SM_EVT_STOP_PREVIEW://由HAL发来的更新状态机的状态值
m_state = QCAMERA_SM_STATE_PREVIEW_STOPPED;
LOGE("[wanghl]2.stopPreview for QCAMERA_SM_EVT_STOP_PREVIEW");//11-08 09:55:24.611 561 4207 E QCamera : <HAL><ERROR> procEvtPreviewingState: 1342: [wanghl]2.stopPreview for QCAMERA_SM_EVT_STOP_PREVIEW
rc = m_parent->stopPreview();
rc = m_parent->stopPreview();->//hardware/qcom/camera/QCamera2/HAL/QCameraStateMachine.cpp:int32_t QCameraStateMachine::procEvtPreviewingState(qcamera_sm_evt_enum_t evt, void *payload)
stopChannel(QCAMERA_CH_TYPE_ZSL);//hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp:int QCamera2HardwareInterface::stopPreview() 内部调用
rc = m_channels[ch_type]->stop();//hardware/qcom/camera/QCamera2/HAL/QCamera2HWI.cpp:int32_t QCamera2HardwareInterface::stopChannel(qcamera_ch_type_enum_t ch_type)
//看log打印:m_channels[0]->stop(),ch_type = 0 = QCAMERA_CH_TYPE_ZSL,
rc = m_camOps->stop_channel(m_camHandle, m_handle);->//hardware/qcom/camera/QCamera2/HAL/QCameraChannel.cpp: int32_t QCameraChannel::stop()
.stop_channel = mm_camera_intf_stop_channel,->//hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c
mm_camera_intf_stop_channel->// rc = mm_camera_stop_channel(my_obj, chid); hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c
mm_camera_stop_channel->//位于:hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera.c, rc = mm_channel_fsm_fn(ch_obj, MM_CHANNEL_EVT_STOP, NULL, NULL);
mm_channel_fsm_fn->//case MM_CHANNEL_STATE_STOPPED:
mm_channel_fsm_fn_stopped->//case MM_CHANNEL_EVT_ADD_STREAM:
mm_channel_add_stream->//hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_channel.c: rc = mm_stream_fsm_fn(stream_obj, MM_STREAM_EVT_ACQUIRE, NULL, NULL);
mm_stream_fsm_fn->//位于:hardware/qcom/camera/QCamera2/stack/mm-camera-interface/src/mm_camera_stream.c,case MM_STREAM_STATE_ACQUIRED:rc = mm_stream_fsm_acquired(my_obj, evt, in_val, out_val);
mm_stream_fsm_acquired->//mm_stream_fsm_cfg不调用
mm_stream_config->
int32_t mm_stream_sync_info(mm_stream_t *my_obj)->// This call will let server to synchornize the stream information with HAL.让server同步hal的stream信息
rc = mm_camera_util_s_ctrl(cam_obj, stream_id, my_obj->fd, CAM_PRIV_STREAM_INFO_SYNC, &value);
//展开:
int32_t mm_camera_util_s_ctrl(__unused mm_camera_obj_t *my_obj, __unused int stream_id, int32_t fd, uint32_t id, int32_t *value)//mm_camera.c
shim_cmd_data.command = id;//这里就把传进来的参数CAM_PRIV_STREAM_INFO_SYNC作为命令发出去了,发到s_ctrl
shim_cmd_data.stream_id = stream_id;
shim_cmd_data.value = NULL;
shim_cmd = mm_camera_create_shim_cmd_packet(CAM_SHIM_SET_PARM, my_obj->sessionid,&shim_cmd_data);
rc = mm_camera_module_send_cmd(shim_cmd);
mm_camera_destroy_shim_cmd_packet(shim_cmd);
rc = mm_stream_set_fmt(my_obj);
//展开:
int32_t mm_stream_set_fmt(mm_stream_t *my_obj)
rc = ioctl(my_obj->fd, VIDIOC_S_FMT, &fmt);//这是下发ioctl命令到kernel了
static struct v4l2_ioctl_info v4l2_ioctls[] = {//v4l2-ioctl.c
IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),//跑到处理函数v4l_s_fmt
static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg)//v4l2-ioctl.c
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE://9
return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
msm_fmt.width = (unsigned int)my_obj->stream_info->dim.width;//宽度还是由mm_stream_t *my_obj拿到的,也就是说跑到这里,已经拿到分辨率了
msm_fmt.height = (unsigned int)my_obj->stream_info->dim.height;
msm_fmt.pixelformat = mm_stream_get_v4l2_fmt(my_obj->stream_info->fmt);
LOGE("[wanghl]mm_stream_set_fmt,msm_fmt.width: %d, msm_fmt.height = %d", msm_fmt.width, msm_fmt.height);
//11-19 17:20:26.422 564 4232 E QCamera : <MCI><ERROR> mm_stream_set_fmt: 4974: [wanghl]mm_stream_set_fmt,msm_fmt.width: 1280, msm_fmt.height = 960
//11-19 17:20:26.428 564 4232 E QCamera : <MCI><ERROR> mm_stream_set_fmt: 4974: [wanghl]mm_stream_set_fmt,msm_fmt.width: 320, msm_fmt.height = 240
//11-19 17:20:26.435 564 4232 E QCamera : <MCI><ERROR> mm_stream_set_fmt: 4974: [wanghl]mm_stream_set_fmt,msm_fmt.width: 640, msm_fmt.height = 480
memcpy(fmt.fmt.raw_data, &msm_fmt, sizeof(msm_fmt));
rc = ioctl(my_obj->fd, VIDIOC_S_FMT, &fmt);
memset(&shim_cmd_data, 0, sizeof(shim_cmd_data));
shim_cmd_data.command = MSM_CAMERA_PRIV_S_FMT;
shim_cmd_data.stream_id = my_obj->server_stream_id;
shim_cmd_data.value = NULL;
LOGE("[wanghl]mm_stream_set_fmt,mm_camera_module_send_cmd");//有打印出来
shim_cmd = mm_camera_create_shim_cmd_packet(CAM_SHIM_SET_PARM, cam_obj->sessionid, &shim_cmd_data);
rc = mm_camera_module_send_cmd(shim_cmd);
mm_camera_destroy_shim_cmd_packet(shim_cmd);
----------------------------------------------------------------------------------------
[Android O] Camera 服务启动流程简析
看下开机logcat log分析下流程:
01-10 23:22:41.388 721 721 I cameraserver: ServiceManager: 0xf0c101c0
01-10 23:22:41.389 721 721 I CameraService: CameraService started (pid=721)
01-10 23:22:41.389 721 721 I CameraService: CameraService process starting
01-10 23:22:41.398 721 721 I ServiceManagement: getService: Trying again for android.hardware.camera.provider@2.4::ICameraProvider/legacy/0...
...
01-10 23:22:37.050 495 495 I android.hardware.camera.provider@2.4-service: Camera provider Service is starting.
...
------------------ 495 495 E QCameraCommon: [wanghl]is_target_QM215()
01-10 23:22:37.795 495 495 E QCameraCommon: parseHWID: Got HW_ID = 354
01-10 23:22:37.796 495 495 I QCamera : <MCI><INFO> needHAL1Support: 331: HAL1/HAL3 IS SUPPORTED//bool QCameraCommon::needHAL1Support()
01-10 23:22:37.796 495 495 E QCamera : <HAL><ERROR> get_number_of_cameras: 178: [wanghl]new QCamera2Factory()
01-10 23:22:37.796 495 495 E QCamera : <MCI><ERROR> get_num_of_cameras: 2650: [wanghl]get_num_of_cameras
01-10 23:22:37.796 495 495 E QCamera : <MCI><ERROR> get_num_of_cameras: 2660: [wanghl] mm_camera_load_shim_lib
01-10 23:22:37.796 495 495 E QCamera : <MCI><ERROR> mm_camera_load_shim_lib: 3258: [wanghl]mm_camera_load_shim_lib
/*===========================================================================
* FUNCTION : parseHWID
*
* DESCRIPTION: get SOC id of current platform
*
* PARAMETERS : None
*
* RETURN : Return Soc Id if successfull else -1
*==========================================================================*/
int QCameraCommon::parseHWID()
{
static int nHW_ID = -1;
if (nHW_ID == -1)
{
#ifdef ANDROID
int result = -1;
char buffer[PATH_MAX];
FILE *device = NULL;
device = fopen("/sys/devices/soc0/soc_id", "r");
if(device)
{
/* 4 = 3 (MAX_SOC_ID_LENGTH) + 1 */
result = fread(buffer, 1, 4, device);
fclose(device);
}
else
{
device = fopen("/sys/devices/system/soc/soc0/id", "r");
if(device)
{
result = fread(buffer, 1, 4, device);
fclose(device);
}
}
if(result > 0)
{
nHW_ID = atoi(buffer);
}
ALOGE("%s: Got HW_ID = %d",__func__, nHW_ID);
#endif
}
return nHW_ID;
}
V5:/ # cat sys/devices/soc0/soc_id
354
hardware/qcom/camera/QCamera2/util/QCameraCommon.cpp
bool QCameraCommon::needHAL1Support()
{
#ifndef HAS_LOW_RAM
// QM215 non-GO supports only HAL3
if (is_target_QM215()) {
LOGI("ONLY HAL3 SUPPORTED");
return FALSE;
}
#endif
// HAL1/HAL3 is supported
LOGI("HAL1/HAL3 IS SUPPORTED");//这个打印出来了
return TRUE;
}
hardware/qcom/camera/QCamera2/QCamera2Hal.cpp
// Camera dependencies
#include "QCamera2Factory.h"
#include "HAL3/QCamera3VendorTags.h"
#include "util/QCameraCommon.h"
static hw_module_t camera_common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,//根据以往sensor流程分析的经验,上层肯定要通过这个id来关联到HAL层这里的接口,先放个疑问在这里,后面分析确认?
.name = "QCamera Module",
.author = "Qualcomm Innovation Center Inc",
.methods = &qcamera::QCamera2Factory::mModuleMethods,
.dso = NULL,
.reserved = {0}
};
camera_module_t HAL_MODULE_INFO_SYM = {
.common = camera_common,
.get_number_of_cameras = qcamera::QCamera2Factory::get_number_of_cameras,//从log上看QCamera2Factory的这个方法肯定也是有被调用,只是不知道怎么调用的?
.get_camera_info = qcamera::QCamera2Factory::get_camera_info,
.set_callbacks = qcamera::QCamera2Factory::set_callbacks,
.get_vendor_tag_ops = qcamera::QCamera3VendorTags::get_vendor_tag_ops,
.open_legacy = (qcamera::QCameraCommon::needHAL1Support()) ?//needHAL1Support这个方法被调用了,说明开机过程中上层肯定是调用了HAL层这个结构的这个open接口
qcamera::QCamera2Factory::open_legacy : NULL,
.set_torch_mode = qcamera::QCamera2Factory::set_torch_mode,
.init = NULL,
.reserved = {0}
};
hardware/interfaces/camera/provider/2.4/default/service.cpp
#define LOG_TAG "android.hardware.camera.provider@2.4-service"
...
int main()
{
ALOGI("Camera provider Service is starting.");
// The camera HAL may communicate to other vendor components via
// /dev/vndbinder
android::ProcessState::initWithDriver("/dev/vndbinder");//vendor/vendor 进程与 AIDL 接口之间的 IPC,与 /dev/vndbinder 进行某种关联,注释表明 Camera HAL 可能会通过它与其它 vendor 组件进行通信。
return defaultPassthroughServiceImplementation<ICameraProvider>("legacy/0", /*maxThreads*/ 6);
}
这个main怎么启动呢?启动脚本:hardware/interfaces/camera/provider/2.4/default/android.hardware.camera.provider@2.4-service.rc
service vendor.camera-provider-2-4 /vendor/bin/hw/android.hardware.camera.provider@2.4-service
class hal
user cameraserver
group audio camera input drmrpc
ioprio rt 4
capabilities SYS_NICE
writepid /dev/cpuset/camera-daemon/tasks /dev/stune/foreground/tasks
CameraProvider 守护进程由 init 进程启动:
hardware/interfaces/camera/provider/2.4/default/Android.bp:49: init_rc: ["android.hardware.camera.provider@2.4-service.rc"],
这个进程被kill -9 进程号杀掉后又会被自动创建,cameraserver进程也是被杀后立马绝地重生,牛的一批!!!
//-----------------------------补充下Android系统启动知识点--------------------
那cameraserver.rc文件什么时候执行呢?这里我们需要理解下Android系统启动过程,Android系统启动包括两大块:Linux内核启动,Android框架启动。
1,Linux内核启动:
1)BootLoader启动
内核启动首先装载BootLoader引导程序,执行完进入kthreadd内核进程(PID = 2),这是所有内核进程的父进程。
2)加载Linux内核
初始化驱动、安装根文件系统等,最后启动第一个用户进程init进程(PID = 1),它是所有用户进程的父进程。这样就进入了Android框架的启动阶段。
2,Android框架启动
init进程启动后会加载init.rc(源码system/core/rootdir/init.rc)脚本,当它执行mount_all指令挂载分区时,会加载当前系统/{system,vendor,odm}/etc/init目录下的所有rc脚本,这样就会启动cameraserver进程,
同时也会启动zygote进程(第一个Java层进程,也是Java层所有进程的父进程)、ServiceManager、mediaserver(多媒体服务进程)、surfaceflinger(屏幕渲染相关的进程)等。之后zygote会孵化出启动system_server进程,
Android framework里面的所有service(ActivityManagerService、WindowManagerService等)都是由system_server启动,这里就不细讲了。
//------------------------------------知识点补充结束--------------------------
分析下defaultPassthroughServiceImplementation的原型:
system/libhidl/transport/include/hidl/LegacySupport.h
/* 模板函数,Interface = ICameraProvider */
template<class Interface>
__attribute__((warn_unused_result))
status_t defaultPassthroughServiceImplementation(std::string name,
size_t maxThreads = 1) {
//第一次执行这个函数会open(/dev/hwbinder)
configureRpcThreadpool(maxThreads, true);//配置 RPC 线程池(当前设置最大线程为 6)
/*
* 核心在这个函数
* 这个函数做了两件事:
* 1、得到了 CameraProvider 实例化对象
* 2、注册 CameraProvider 实例化对象
* 后面分析
*/
status_t result = registerPassthroughServiceImplementation<Interface>(name);//将 Interface(即 CameraProvider)以入参 legacy/0 为名注册到相应的管理服务中。
if (result != OK) {
return result;
}
joinRpcThreadpool();//连接到线程池
return UNKNOWN_ERROR;
}
template<class Interface>/* 模板函数,Interface = ICameraProvider */
__attribute__((warn_unused_result))
status_t registerPassthroughServiceImplementation(
std::string name = "default") {
/*
* 就是调用 ICameraProvider::getService()
* getStub = ture 时,getservice 以 passthrough 模式打开 HAL 实现
* 所以这个会得到 CameraProvider 实例化对象(不是binder代理)
* 后面分析
*/
sp<Interface> service = Interface::getService(name, true /* getStub */);
if (service == nullptr) {
ALOGE("Could not get passthrough implementation for %s/%s.",
Interface::descriptor, name.c_str());
return EXIT_FAILURE;
}
LOG_FATAL_IF(service->isRemote(), "Implementation of %s/%s is remote!",
Interface::descriptor, name.c_str());
/*
* 将 CameraProvider 注册为一个服务
* 其他进程需要使用 camera 的 hal 层时通过 binder 得到 CameraProvider 代理类即可操作camera hal 层
* 不需要像以前一样每次都 dlopen(xxx.so)
* 后面 2.4 分析
*/
status_t status = service->registerAsService(name);
if (status == OK) {
ALOGI("Registration complete for %s/%s.",
Interface::descriptor, name.c_str());
} else {
ALOGE("Could not register service %s/%s (%d).",
Interface::descriptor, name.c_str(), status);
}
return status;
}
得到 CameraProvider 实例化对象的过程,太变态了,居然要分析out下生成的代码文件:
out/soong/.intermediates/hardware/interfaces/camera/provider/2.4/android.hardware.camera.provider@2.4_genc++/gen/android/hardware/camera/provider/2.4/CameraProviderAll.cpp
分析不来,简单记下这个文件主要功能:
/*
* 这里会根据传入的 descriptor 字符串,找到 CameraProvider.cpp 生成的 so 文件
* 就是 android.hardware.camera.provider@2.4-impl.so
* 然后使用这个库中的 HIDL_FETCH_ICameraProvider() 函数得到 CameraProvider 实例化对象
* 后面会分析这个函数
* out/target/product/V5/vendor/lib/hw/android.hardware.camera.provider@2.4-impl.so
* out/target/product/V5/vendor/lib64/hw/android.hardware.camera.provider@2.4-impl.so
*/
看下怎么打开.so文件
system/libhidl/transport/ServiceManagement.cpp
static void openLibs(
const std::string& fqName,
const std::function<bool /* continue */ (void* /* handle */, const std::string& /* lib */,
const std::string& /* sym */)>& eachLib) {
//fqName looks like android.hardware.foo@1.0::IFoo
/* fqName = "android.hardware.camera.provider@2.4::ICameraProvider" */
size_t idx = fqName.find("::");
if (idx == std::string::npos ||
idx + strlen("::") + 1 >= fqName.size()) {
LOG(ERROR) << "Invalid interface name passthrough lookup: " << fqName;
return;
}
/* 得到 "android.hardware.camera.provider@2.4" 字符串 */
std::string packageAndVersion = fqName.substr(0, idx);
/* 得到 "ICameraProvider" 字符串 */
std::string ifaceName = fqName.substr(idx + strlen("::"));
/* 得到 "android.hardware.camera.provider@2.4-impl" 字符串 */
const std::string prefix = packageAndVersion + "-impl";
/* 得到 "HIDL_FETCH_ICameraProvider" 字符串 */
const std::string sym = "HIDL_FETCH_" + ifaceName;
constexpr int dlMode = RTLD_LAZY;
void* handle = nullptr;
dlerror(); // clear
static std::string halLibPathVndkSp = android::base::StringPrintf(
HAL_LIBRARY_PATH_VNDK_SP_FOR_VERSION, details::getVndkVersionStr().c_str());
/* 在这四个目录下搜索 "android.hardware.camera.provider@2.4-impl" */
std::vector<std::string> paths = {HAL_LIBRARY_PATH_ODM, HAL_LIBRARY_PATH_VENDOR,
halLibPathVndkSp, HAL_LIBRARY_PATH_SYSTEM};
#ifdef LIBHIDL_TARGET_DEBUGGABLE
const char* env = std::getenv("TREBLE_TESTING_OVERRIDE");
const bool trebleTestingOverride = env && !strcmp(env, "true");
if (trebleTestingOverride) {
// Load HAL implementations that are statically linked
handle = dlopen(nullptr, dlMode);
if (handle == nullptr) {
const char* error = dlerror();
LOG(ERROR) << "Failed to dlopen self: "
<< (error == nullptr ? "unknown error" : error);
} else if (!eachLib(handle, "SELF", sym)) {
return;
}
const char* vtsRootPath = std::getenv("VTS_ROOT_PATH");
if (vtsRootPath && strlen(vtsRootPath) > 0) {
const std::string halLibraryPathVtsOverride =
std::string(vtsRootPath) + HAL_LIBRARY_PATH_SYSTEM;
paths.insert(paths.begin(), halLibraryPathVtsOverride);
}
}
#endif
for (const std::string& path : paths) {
std::vector<std::string> libs = search(path, prefix, ".so");
for (const std::string &lib : libs) {
const std::string fullPath = path + lib;
if (path == HAL_LIBRARY_PATH_SYSTEM) {
handle = dlopen(fullPath.c_str(), dlMode);
} else {
/* 打开库 */
handle = android_load_sphal_library(fullPath.c_str(), dlMode);
}
if (handle == nullptr) {
const char* error = dlerror();
LOG(ERROR) << "Failed to dlopen " << lib << ": "
<< (error == nullptr ? "unknown error" : error);
continue;
}
/* 调用回调函数。 这个回调是匿名的, 在上面 openLibs() 有说到 */
if (!eachLib(handle, lib, sym)) {
return;
}
}
}
}
----------------------------------------------------------
frameworks/base/core/jni/Android.bp:12: name: "libandroid_runtime",编译出libandroid_runtime.so
为了保证android应用的底层牵涉到的大量夸进程间的通信的高效性,Android提供了binder机制,binder核心是linux驱动:kernel/msm-4.9/drivers/android/binder.c(CONFIG_ANDROID_BINDER_IPC=y),Binder机制有两层含义:
1)是一种夸进程通信手段(IPC,Inter-Process Communication)
2)是一种远程过程调用手段(RPC,Remote Procedure Call)
Binder是一种跨进程通信机制,需要一个专门的管理器为通信牵线搭桥,这个管理器就是ServiceManagerService
Java AP端作为客户端,作为一个client进程(由frameworks/av/camera/Android.bp:18: name: "libcamera_client"编译成libcamera_client.so)<--------通过binder通信------->native c/c++端作为服务端,
作为一个service(由frameworks/av/services/camera/libcameraservice/Android.mk:98:LOCAL_MODULE:= libcameraservice 变成成libcameraservice.so,几十个源码)
cameraserver进程(由frameworks/av/camera/cameraserver/main_cameraserver.cpp编译成一个可执行文件,一开机就启动)创建了一个CameraService并注册到ServiceManager以备client引用,用来做binder通信。
具体代码查看:
frameworks/av/camera/cameraserver/main_cameraserver.cpp
#define LOG_TAG "cameraserver"
...
int main(int argc __unused, char** argv __unused)
{
signal(SIGPIPE, SIG_IGN);
// Set 3 threads for HIDL calls
hardware::configureRpcThreadpool(3, /*willjoin*/ false);
sp<ProcessState> proc(ProcessState::self());//获取一个ProcessState(进程状态)跟Binder驱动通信,用来记录binder驱动对应的句柄值,以便随时和binder驱动通信,每个进程只有一个ProcessState,被进程内的多进程共用
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());//cameraserver: ServiceManager: 0xf0c101c0,ServiceManager 这个服务管理器是用来跟上层(client端)交互的,获取ServiceManager用以注册该服务
CameraService::instantiate();//在该进程中实例化了 CameraService,实例化只有简单的一行代码,但实例化的过程并不那么简单。这个 instantiate() 接口并不是定义在 CameraService 类中的,而是定义在父类 BinderService 类里(而 CameraService 继承了它)。在此处,它的作用是创建一个 CameraService(通过 new 的方式),并将其加入到 ServiceManager 中(注意,在这一过程中,CameraService 被强指针引用了)。
//线程池管理
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
同样的道理,这个main也由启动脚本来启动:
frameworks/av/camera/cameraserver/cameraserver.rc
//这表示由init进程启动名字为cameraserver的进程,这是独立进程,路径为/system/bin/cameraserver
service cameraserver /system/bin/cameraserver
//class表示类别,同一类别(这里是main类别)的进程同时启动,同时停止
class main
//用户名及分组
user cameraserver
group audio camera input drmrpc
ioprio rt 4
writepid /dev/cpuset/camera-daemon/tasks /dev/stune/foreground/tasks
cameraserver.rc由Android.mk文件来打包到指定位置:
//LOCAL_INIT_RC会将cameraserver.rc放在/system/etc/init/目录中,这个目录下的脚本会由init进程来启动。
LOCAL_INIT_RC := cameraserver.rc
//-----------------------------这里补充一个历史知识点-------------------------
Android 7.0之前CameraService是在mediaserver进程中注册的,看下Android 6.0的代码
frameworks/av/media/mediaserver/main_mediaserver.cpp
int main()
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
//初始化相机服务
CameraService::instantiate();
AudioPolicyService::instantiate();
SoundTriggerHwService::instantiate();
RadioService::instantiate(); registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
接着看下Android 9.0中main_mediaserver.cpp的代码,发现没有了CameraService::instantiate(); 也就是说Android 7.0之后就不在main_mediaserver.cpp中注册了。
//没有CameraService::instantiate(),也少了几个别的服务,这里只关注CameraService
int main()
{
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
ALOGI("ServiceManager: %p", sm.get());
MediaDrmService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
那为什么Android 7.0之前cameraservice是运行在mediaserver进程中的,而从Android 7.0开始将cameraservice分离出来成一个单独的cameraserver进程?这是为了安全性,因为mediaserver 进程中有很多其它的Service,如AudioFlinger、MediaPlayerService等,如果这其中有一个Service挂掉就会导致mediaserver进程重启,如果相机正在执行,这样就会挂掉,用户体验很差。
//--------------------------------------补充完毕-----------------------------
强指针(智能指针)android中的sp句柄类实际上就是google实现的一种强引用的智能指针,智能指针是c++中的一个概念,因为c++本身不具备垃圾回收机制,而且指针也不具备构造函数和析构函数,所以为了实现内存(动态存储区)的安全回收,必须对指针进行一层封装,而这个封装就是智能指针,说白了,智能指针就是具备指针功能同时提供安全内存回收的一个类。
CameraService继承了BinderService和BnCameraService类。CameraService::instantiate()函数是调用其父类BinderService类的方法。
frameworks/native/libs/binder/include/binder/BinderService.h
/*
* BinderService 是 CameraService 的父类
* 模板类 SERVICE = CameraService
*/
template<typename SERVICE>
class BinderService
{
public:
static status_t publish(bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
/* 得到 ServiceManager 的代理类 BpServiceManager */
sp<IServiceManager> sm(defaultServiceManager());
/*
* SERVICE = CameraService
* 注册 CameraService 实例化对象
*/
return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
dumpFlags); /* new SERVICE()=new CameraService() 得到实例化对象,智能指针第一次引用会调用 onFirstRef() 函数,sp初始化时,会调用其包装的对象的onFirstRef()函数 后面分析 */
}
static void publishAndJoinThreadPool(
bool allowIsolated = false,
int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
publish(allowIsolated, dumpFlags);
joinThreadPool();
}
static void instantiate() { publish(); }/* 类属性的, 所以可以直接调用,这就内部调用到了publish */
static status_t shutdown() { return NO_ERROR; }
private:
static void joinThreadPool() {
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
IPCThreadState::self()->joinThreadPool();
}
};
由于首次被强指针引用时,就会调用 onFirstRef() 函数执行初始化之类的业务逻辑,所以现在就看看 CameraService 在此处实现了什么逻辑。
frameworks/av/services/camera/libcameraservice/CameraService.cpp
void CameraService::onFirstRef()
{
ALOGI("CameraService process starting");
BnCameraService::onFirstRef();
// Update battery life tracking if service is restarting
BatteryNotifier& notifier(BatteryNotifier::getInstance());
notifier.noteResetCamera();
notifier.noteResetFlashlight();
status_t res = INVALID_OPERATION;
res = enumerateProviders();//重点关注部分,分析这个方法
if (res == OK) {
mInitialized = true;
}
CameraService::pingCameraServiceProxy();
mUidPolicy = new UidPolicy(this);
mUidPolicy->registerSelf();
}
status_t CameraService::enumerateProviders() {
status_t res;
std::vector<std::string> deviceIds;
{
Mutex::Autolock l(mServiceLock);
if (nullptr == mCameraProviderManager.get()) {
mCameraProviderManager = new CameraProviderManager();//首先将 CameraProviderManager 实例化,
res = mCameraProviderManager->initialize(this);//然后调用 initialize() 接口将其初始化,传入的参数是 this 指针,指向当前 CameraService 实例的地址。
if (res != OK) {
ALOGE("%s: Unable to initialize camera provider manager: %s (%d)",
__FUNCTION__, strerror(-res), res);
return res;
}
}
// Setup vendor tags before we call get_camera_info the first time
// because HAL might need to setup static vendor keys in get_camera_info
// TODO: maybe put this into CameraProviderManager::initialize()?
mCameraProviderManager->setUpVendorTags();
if (nullptr == mFlashlight.get()) {
mFlashlight = new CameraFlashlight(mCameraProviderManager, this);
}
res = mFlashlight->findFlashUnits();
if (res != OK) {
ALOGE("Failed to enumerate flash units: %s (%d)", strerror(-res), res);
}
deviceIds = mCameraProviderManager->getCameraDeviceIds();
}
for (auto& cameraId : deviceIds) {
String8 id8 = String8(cameraId.c_str());
onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT);
}
return OK;
}
接着分析下mCameraProviderManager->initialize(this)做的事情
frameworks/av/services/camera/libcameraservice/common/CameraProviderManager.h
/**
* Initialize the manager and give it a status listener; optionally accepts a service
* interaction proxy.
*
* The default proxy communicates via the hardware service manager; alternate proxies can be
* used for testing. The lifetime of the proxy must exceed the lifetime of the manager.
*/
status_t initialize(wp<StatusListener> listener,
ServiceInteractionProxy *proxy = &sHardwareServiceInteractionProxy);
在分析具体实现之前,可以先看看它在头文件中的声明: 用于初始化管理器,并给它设置一个状态监听(即 CameraService 实例)。选择性地接受一个与服务交互的代理。默认的代理通过 Hardware 服务管理器进行通信。
备用的代理可以用来进行测试。代理的生命周期必须要超过管理器的生命周期。 注意到在 enumerateProviders 中调用该接口时,只有一个入参,说明当前用的是默认代理。
接下来看看具体实现的逻辑:
//frameworks/av/services/camera/libcameraservice/common/CameraProviderManager.cpp-->最终被编译成libcameraservice.so
status_t CameraProviderManager::initialize(wp<CameraProviderManager::StatusListener> listener,
/* proxy 是默认值, proxy = sHardwareServiceInteractionProxy */
ServiceInteractionProxy* proxy) {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
if (proxy == nullptr) {
ALOGE("%s: No valid service interaction proxy provided", __FUNCTION__);
return BAD_VALUE;
}
/* listener = CameraService 对象 */
mListener = listener;
/* proxy = sHardwareServiceInteractionProxy */
mServiceProxy = proxy;
// Registering will trigger notifications for all already-known providers
//通过服务代理作出一个注册动作。根据注释,注册会触发一个给所有已知 Provider 进行通知的动作。
bool success = mServiceProxy->registerForNotifications(
/* instance name, empty means no filter */ "",
this);
if (!success) {
ALOGE("%s: Unable to register with hardware service manager for notifications "
"about camera providers", __FUNCTION__);
return INVALID_OPERATION;
}
// See if there's a passthrough HAL, but let's not complain if there's not
addProviderLocked(kLegacyProviderName, /*expected*/ false);//这是我们主要关注的函数。注释翻译过来是这样,看看这是否为一个直通的 HAL,如果不是也没关系。注意传入的参数 kLegacyProviderName,在文件开头有它的定义,即为字符串 legacy/0。const std::string kLegacyProviderName("legacy/0");
addProviderLocked(kExternalProviderName, /*expected*/ false);//const std::string kExternalProviderName("external/0");
return OK;
}
/* CameraProviderManager.cpp */
//addProviderLocked这个函数主要作用是将找到的这个 Provider 通过 ProviderInfo 记录下来并初始化。
status_t CameraProviderManager::addProviderLocked(const std::string& newProvider, bool expected) {
for (const auto& providerInfo : mProviders) {
if (providerInfo->mProviderName == newProvider) {//检查已知的 Provider 中是否已有名为 legacy/0 的
ALOGW("%s: Camera provider HAL with name '%s' already registered", __FUNCTION__,
newProvider.c_str());
return ALREADY_EXISTS;
}
}
//根据 legacy/0 从服务代理处获取 CameraProvider 接口,这里需要特别注意,因为此处真正地初始化了对应的 CameraProvider
sp<provider::V2_4::ICameraProvider> interface;
/*
* 上面分析过 mServiceProxy = sHardwareServiceInteractionProxy
* 这里 getService(newProvider) 其实就是得到 CameraProvider 的代理类
* 所以 CameraService 与 CameraProvider 联系起来了
* 接着分析这个函数 HardwareServiceInteractionProxy::getService()
*/
interface = mServiceProxy->getService(newProvider);//该接口最终会调用到一个名为 HIDL_FETCH_ICameraProvider 的函数。
if (interface == nullptr) {
if (expected) {
ALOGE("%s: Camera provider HAL '%s' is not actually available", __FUNCTION__,
newProvider.c_str());
return BAD_VALUE;
} else {
return OK;
}
}
//通过 ProviderInfo 来保存当前 Provider 相关信息。
sp<ProviderInfo> providerInfo =
new ProviderInfo(newProvider, interface, this);
status_t res = providerInfo->initialize();
if (res != OK) {
return res;
}
mProviders.push_back(providerInfo);//记录当前 Provider。
return OK;
}
接着分析这个函数 HardwareServiceInteractionProxy::getService()
frameworks/av/services/camera/libcameraservice/common/CameraProviderManager.h
// Standard use case - call into the normal generated static methods which invoke
// the real hardware service manager
struct HardwareServiceInteractionProxy : public ServiceInteractionProxy {
virtual bool registerForNotifications(
const std::string &serviceName,
const sp<hidl::manager::V1_0::IServiceNotification>
¬ification) override {
return hardware::camera::provider::V2_4::ICameraProvider::registerForNotifications(
serviceName, notification);
}
//getService方法在这里,在这里,在这里!!!!
virtual sp<hardware::camera::provider::V2_4::ICameraProvider> getService(
const std::string &serviceName) override {
/*
* 调用了 ICameraProvider::getService()
* 且 getStub 为默认值 getStub = flase
* 之前分析过,getStub = flase 会得到 CameraProvider 的代理类(binder)
*/
return hardware::camera::provider::V2_4::ICameraProvider::getService(serviceName);//该接口最终会调用到一个名为 HIDL_FETCH_ICameraProvider 的函数。
}
};
接下来我们分析下:
hardware/interfaces/camera/provider/2.4/default/CameraProvider.cpp-->最后是编译生成android.hardware.camera.provider@2.4-impl.so
//out/target/product/EC16/vendor/lib/hw/android.hardware.camera.provider@2.4-impl.so
#define LOG_TAG "CamProvider@2.4-impl"
...
ICameraProvider* HIDL_FETCH_ICameraProvider(const char* name) {
if (strcmp(name, kLegacyProviderName) == 0) {
/*
* 很简单,就是 new CameraProvider 实例
* 但是要注意,在构造函数中打开了 HAL 层
* CameraProvider 与 Camera HAL 联系了起来
*
*/
CameraProvider* provider = new CameraProvider();
if (provider == nullptr) {
ALOGE("%s: cannot allocate camera provider!", __FUNCTION__);
return nullptr;
}
if (provider->isInitFailed()) {
ALOGE("%s: camera provider init failed!", __FUNCTION__);
delete provider;
return nullptr;
}
/* 返回 CameraProvider 实例化对象 */
return provider;
} else if (strcmp(name, kExternalProviderName) == 0) {
ExternalCameraProvider* provider = new ExternalCameraProvider();
return provider;
}
ALOGE("%s: unknown instance name: %s", __FUNCTION__, name);
return nullptr;
}
//构造函数
CameraProvider::CameraProvider() :
/*
* 调用父类的构造函数
* 这个回调会在后面使用到,请留意
*/
camera_module_callbacks_t({sCameraDeviceStatusChange,
sTorchModeStatusChange}) {
/* 继续分析 */
mInitFailed = initialize();
}
bool CameraProvider::initialize() {
camera_module_t *rawModule;
/*
* 就是这里了,打开 HAL
* 这里就不分析了
*/
int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,//这个就对应到前面我们在怀疑的ID的匹配上了
(const hw_module_t **)&rawModule);
if (err < 0) {
ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
return true;
}
/*
* 将得到的 hw_module_t 结构体再封装多一层,
* 之后会使用这个对象调用 HAL 层接口
*/
mModule = new CameraModule(rawModule);
/* 得到一些 Camera 的基本信息 */
err = mModule->init();
if (err != OK) {
ALOGE("Could not initialize camera HAL module: %d (%s)", err, strerror(-err));
mModule.clear();
return true;
}
ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());//01-11 01:10:41.521 495 495 I CamProvider@2.4-impl: Loaded "QCamera Module" camera module
// Setup vendor tags here so HAL can setup vendor keys in camera characteristics
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
if (!setUpVendorTags()) {
ALOGE("%s: Vendor tag setup failed, will not be available.", __FUNCTION__);
}
// Setup callback now because we are going to try openLegacy next
/*
* 设置回调,在 CameraProvider() 构造函数中构造的回调
* class CameraProvider 继承于 class camera_module_callbacks_t 所以可以这样调用
* 看意思应该就是 camera 状态改变的时候 HAL 层会调用这个回调通知 CameraProvider
*/
err = mModule->setCallbacks(this);
if (err != OK) {
ALOGE("Could not set camera module callback: %d (%s)", err, strerror(-err));
mModule.clear();
return true;
}
mPreferredHal3MinorVersion =
property_get_int32("ro.vendor.camera.wrapper.hal3TrebleMinorVersion", 3);
ALOGV("Preferred HAL 3 minor version is %d", mPreferredHal3MinorVersion);
switch(mPreferredHal3MinorVersion) {
case 2:
case 3:
// OK
break;
default:
ALOGW("Unknown minor camera device HAL version %d in property "
"'camera.wrapper.hal3TrebleMinorVersion', defaulting to 3",
mPreferredHal3MinorVersion);
mPreferredHal3MinorVersion = 3;
}
/* 得到 camera 的个数 */
mNumberOfLegacyCameras = mModule->getNumberOfCameras();
for (int i = 0; i < mNumberOfLegacyCameras; i++) {
struct camera_info info;
auto rc = mModule->getCameraInfo(i, &info);
if (rc != NO_ERROR) {
ALOGE("%s: Camera info query failed!", __func__);
mModule.clear();
return true;
}
if (checkCameraVersion(i, info) != OK) {
ALOGE("%s: Camera version check failed!", __func__);
mModule.clear();
return true;
}
char cameraId[kMaxCameraIdLen];
snprintf(cameraId, sizeof(cameraId), "%d", i);
std::string cameraIdStr(cameraId);
mCameraStatusMap[cameraIdStr] = CAMERA_DEVICE_STATUS_PRESENT;
addDeviceNames(i);//关键再分析下这个函数
}
return false; // mInitFailed
}
void CameraProvider::addDeviceNames(int camera_id, CameraDeviceStatus status, bool cam_new)
{
char cameraId[kMaxCameraIdLen];
snprintf(cameraId, sizeof(cameraId), "%d", camera_id);
std::string cameraIdStr(cameraId);
mCameraIds.add(cameraIdStr);
// initialize mCameraDeviceNames and mOpenLegacySupported
mOpenLegacySupported[cameraIdStr] = false;
int deviceVersion = mModule->getDeviceVersion(camera_id);
auto deviceNamePair = std::make_pair(cameraIdStr,
getHidlDeviceName(cameraIdStr, deviceVersion));
mCameraDeviceNames.add(deviceNamePair);
if (cam_new) {
mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status);
}
if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
mModule->isOpenLegacyDefined()) {
// try open_legacy to see if it actually works
struct hw_device_t* halDev = nullptr;
int ret = mModule->openLegacy(cameraId, CAMERA_DEVICE_API_VERSION_1_0, &halDev);
if (ret == 0) {
mOpenLegacySupported[cameraIdStr] = true;
halDev->close(halDev);
deviceNamePair = std::make_pair(cameraIdStr,
getHidlDeviceName(cameraIdStr, CAMERA_DEVICE_API_VERSION_1_0));
mCameraDeviceNames.add(deviceNamePair);
if (cam_new) {
mCallbacks->cameraDeviceStatusChange(deviceNamePair.second, status);
}
} else if (ret == -EBUSY || ret == -EUSERS) {
// Looks like this provider instance is not initialized during
// system startup and there are other camera users already.
// Not a good sign but not fatal.
ALOGW("%s: open_legacy try failed!", __FUNCTION__);
}
}
}
new一个对象的时候mModule = new CameraModule(rawModule);
就会跑到CameraModule类的构造函数
hardware/interfaces/camera/common/1.0/default/CameraModule.cpp
CameraModule::CameraModule(camera_module_t *module) {
if (module == NULL) {
ALOGE("%s: camera hardware module must not be null", __FUNCTION__);
assert(0);
}
mModule = module;
}
mModule是CameraModule类的一个成员变量,再看下类的成员变量的定义:
camera_module_t *mModule;
所以当上面执行到mModule->openLegacy(cameraId, CAMERA_DEVICE_API_VERSION_1_0, &halDev);
这个就是调用了CameraModule这个类的openLegacy方法
/*CameraModule.cpp*/
int CameraModule::openLegacy(
const char* id, uint32_t halVersion, struct hw_device_t** device) {
int res;
ATRACE_BEGIN("camera_module->open_legacy");
res = mModule->open_legacy(&mModule->common, id, halVersion, device);//于是我们就知道最后就是调用到camera_module_t结构体的open_legacy函数
ATRACE_END();
return res;
}
mModule->open_legacy
我们再来回顾下这个函数的原型:
hardware/qcom/camera/QCamera2/QCamera2Hal.cpp //根据LOCAL_MODULE := camera.$(TARGET_BOARD_PLATFORM)编译成camera.msm8953.so
static hw_module_t camera_common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CAMERA_MODULE_API_VERSION_2_4,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CAMERA_HARDWARE_MODULE_ID,
.name = "QCamera Module",
.author = "Qualcomm Innovation Center Inc",
.methods = &qcamera::QCamera2Factory::mModuleMethods,
.dso = NULL,
.reserved = {0}
};
camera_module_t HAL_MODULE_INFO_SYM = {
.common = camera_common,
.get_number_of_cameras = qcamera::QCamera2Factory::get_number_of_cameras,
.get_camera_info = qcamera::QCamera2Factory::get_camera_info,
.set_callbacks = qcamera::QCamera2Factory::set_callbacks,
.get_vendor_tag_ops = qcamera::QCamera3VendorTags::get_vendor_tag_ops,
.open_legacy = (qcamera::QCameraCommon::needHAL1Support()) ?
qcamera::QCamera2Factory::open_legacy : NULL,//camera_module_t结构体的open_legacy函数就是映射到QCamera2Factory的open_legacy
.set_torch_mode = qcamera::QCamera2Factory::set_torch_mode,
.init = NULL,
.reserved = {0}
};
这样一来就和我们的一开始的怀疑对上了,包括.id,.name,.open_legacy接口都对上了,log也对上了
同样的道理,mModule->getNumberOfCameras();就是CameraModule对象的getNumberOfCameras方法:
int CameraModule::getNumberOfCameras() {
int numCameras;
ATRACE_BEGIN("[wanghl]CameraModule::getNumberOfCameras");
numCameras = mModule->get_number_of_cameras();//camera_module_t结构体的get_number_of_cameras函数就是映射到QCamera2Factory的get_number_of_cameras
ATRACE_END();
return numCameras;
}
.get_number_of_cameras = qcamera::QCamera2Factory::get_number_of_cameras,
hardware/qcom/camera/QCamera2/QCamera2Factory.cpp//也是根据LOCAL_MODULE := camera.$(TARGET_BOARD_PLATFORM)编译成camera.msm8953.so
/*===========================================================================
* FUNCTION : get_number_of_cameras
*
* DESCRIPTION: static function to query number of cameras detected
*
* PARAMETERS : none
*
* RETURN : number of cameras detected
*==========================================================================*/
int QCamera2Factory::get_number_of_cameras()
{
int numCameras = 0;
if (!gQCamera2Factory) {
LOGE("[wanghl]new QCamera2Factory()");//01-11 01:10:33.285 495 495 E QCamera : <HAL><ERROR> get_number_of_cameras: 178: [wanghl]new QCamera2Factory()
gQCamera2Factory = new QCamera2Factory();
if (!gQCamera2Factory) {
LOGE("Failed to allocate Camera2Factory object");
return 0;
}
}
#ifdef QCAMERA_HAL1_SUPPORT
if(gQCameraMuxer)
{
LOGE("[wanghl]gQCameraMuxer->get_number_of_cameras()");
numCameras = gQCameraMuxer->get_number_of_cameras();
}
else
#endif
{
LOGE("[wanghl]gQCamera2Factory->getNumberOfCameras()");//01-11 01:10:41.521 495 495 E QCamera : <HAL><ERROR> get_number_of_cameras: 194: [wanghl]gQCamera2Factory->getNumberOfCameras()
numCameras = gQCamera2Factory->getNumberOfCameras();
}
LOGH("num of cameras: %d", numCameras);
return numCameras;
}
这就和一开始的log给对应上了:
01-11 01:10:33.285 495 495 E QCamera : <HAL><ERROR> get_number_of_cameras: 178: [wanghl]new QCamera2Factory()
01-11 01:10:33.286 495 495 E QCamera : <MCI><ERROR> get_num_of_cameras: 2650: [wanghl]get_num_of_cameras
01-11 01:10:33.286 495 495 E QCamera : <MCI><ERROR> get_num_of_cameras: 2660: [wanghl] mm_camera_load_shim_lib
01-11 01:10:33.286 495 495 E QCamera : <MCI><ERROR> mm_camera_load_shim_lib: 3258: [wanghl]mm_camera_load_shim_lib
至此,我们就完全明白了 CameraProvider::getService(name, true) 是如何一步步得到 CameraProvider 实例化对象的。
总结来说:
在 CameraService 的初始化过程中,CameraProvider 才开始进行初始化,或者说我们发现 CameraService 会使用 CameraProvider 服务,只不过这个初始化是通过服务代理进行远端调用而进行的。
在 CameraProviderManager::addProviderLocked 函数的实现逻辑中,调用了 ICameraProvider::getService 接口,该接口最终会调用到一个名为 HIDL_FETCH_ICameraProvider 的函数。
简单了解一下cameraserver和android.hardware.camera.provider@2.4-service这两个进程的关系:
系统启动时,就会启动 CameraProvider 服务。它将 Camera HAL 从 cameraserver 进程中分离出来,作为一个独立进程 android.hardware.camera.provider@2.4-service 来控制 HAL。
这两个进程之间通过 HIDL 机制进行通信。这样的改动源自于 Android O 版本加入的 Treble 机制,它的主要功能是将 service 与 HAL 隔离,以方便 HAL 部分进行独立升级(android 9.0才有了vendor.img,android 7.1是没有vendor.img,
只有一个system.img)。这其实和 APP 与 Framework 之间的 Binder 机制类似,通过引入一个进程间通信机制而针对不同层级进行解耦(从 Local call 变成了 Remote call)。
cameraserver 与 provider 这两个进程启动、初始化的调用逻辑:
cameraserver android.hardware.camera.provider@2.4-service
main_cameraserver.cpp service.cpp
main() main()
| |
\|/ \|/
CameraService defaultPassthroughServiceImplementation
\|/ ICameraProvider \|/
CameraProviderManager--->Service name "legacy/0"<-----registerPassthroughServiceImplementation
|
|_________________________>HIDL_FETCH_ICameraProvider(new CameraProvider();)
|
|
\|/
CameraProvider initialize
|<camera_module_t>
\|/
mModule
mModule->get_number_of_cameras();
mModule->open_legacy();
总体逻辑顺序:
provider 进程启动,注册;
cameraserver 进程启动,注册,初始化;
cameraserver 获取远端 provider(此时实例化 CameraProvider 并初始化)。
左边是 cameraserver 进程中的动作,右边则是 provider 进程中的动作,它们之间通过 ICameraProvider 联系在了一起,而这个东西与 HIDL 相关,我们可以不用关心它的实现方式。
由图可见:
cameraserver 一侧,Cameraservice 类依旧是主体。它通过 CameraProviderManager 来管理对 CameraProvider 的操作。此处初始化的最终目的是连接上 CameraProvider。
provider 一侧,最终主体是 CameraProvider。初始化最终目的是得到一个 mModule,通过它可以直接与 HAL 接口定义层进行交互。
至此,我们就能对 Android O 上的 Camera 服务启动流程有一个大致的了解。(虽然目前我们用的是Android P,但是先凑合着看,相信差异也不大)
同上述分析,显然上层 framework 通过 ServiceManager( /dev/binder ) 得到 CameraService 服务,而 CameraService 通过 HwServiceManager( /dev/hwbinder ) 得到 CameraProvider 服务,而 CameraProvider 与 Camera HAL 绑定。
这样上层 framework 就能够访问 Camera HAL 层了
- - - - - - -插播 - - - - - - -
vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server.c->main->server_process_hal_event->mct_controller_new->mct_pipeline_new->mct_pipeline_process_serv_msg
本以为创建了一个服务来处理hal事件的监听,server_process_hal_event在死循环里一直在跑,守护进程,其实错了,这个文件都不会被编译
bionic/libc/kernel/uapi/linux/videodev2.h
struct v4l2_event {
__u32 type;
union {
struct v4l2_event_vsync vsync;
struct v4l2_event_ctrl ctrl;
struct v4l2_event_frame_sync frame_sync;
struct v4l2_event_src_change src_change;
struct v4l2_event_motion_det motion_det;
__u8 data[64];
} u;
__u32 pending;
__u32 sequence;
struct timespec timestamp;
__u32 id;
__u32 reserved[8];
};
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/mct_shim_layer/mct_shim_layer.h驱动包含该头文件的时候:
#include <linux/videodev2.h>
定义:
struct v4l2_event ev = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
#define V4L2_EVENT_ALL 0
#define V4L2_EVENT_VSYNC 1
#define V4L2_EVENT_EOS 2
#define V4L2_EVENT_CTRL 3
#define V4L2_EVENT_FRAME_SYNC 4
#define V4L2_EVENT_SOURCE_CHANGE 5
#define V4L2_EVENT_MOTION_DET 6
#define V4L2_EVENT_PRIVATE_START 0x08000000
#define V4L2_EVENT_SRC_CH_RESOLUTION (1 << 0)
struct v4l2_event_src_change {
__u32 changes;
};
kernel空间:
.unlocked_ioctl = video_ioctl2,->video_ioctl2->__video_do_ioctl->v4l2_is_known_ioctl->static struct v4l2_ioctl_info v4l2_ioctls[]-> IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),->v4l_s_parm---->.vidioc_s_parm = camera_v4l2_s_parm
看下怎么open camera相关子模块设备,上层怎么通过ioctl发送I2C命令到驱动:
mct_shimlayer_module_sensor_init/mct_shimlayer_module_init(两个在开机过程中都有调用,一前一后)->modules_list
static mct_module_init_name_t modules_list[] = {
{"sensor", module_sensor_init, module_sensor_deinit, NULL},//module_sensor_init->module_sensors_subinit->sub_module_init[i](&s_bundle->module_sensor_params[i]->func_tbl);
{"iface", module_iface_init, module_iface_deinit, NULL},
{"isp", module_isp_init, module_isp_deinit, NULL},
{"stats", stats_module_init, stats_module_deinit, NULL},
{"pproc", pproc_module_init, pproc_module_deinit, NULL},
{"imglib", module_imglib_init, module_imglib_deinit, NULL},
};
01-10 23:22:38.709 495 495 E mm-camera: <SHIM ><ERROR> 42: mct_shimlayer_module_sensor_init: [wanghl]mct_shimlayer_module_sensor_init,Sensor module init
01-10 23:22:40.738 495 495 E mm-camera: <SHIM ><ERROR> 128: mct_shimlayer_module_init: [wanghl]mct_shimlayer_module_init ,Module Init
修改: vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server.c
修改: vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server_process.c
这两个文件不会被编译到,可能是框架有修改--是的,SDM439开始,UNIX Domain Socket通信机制就被弃用了
看下sub_module_init这个table表格
/** Initialization table **/
static int32_t (*sub_module_init[SUB_MODULE_MAX])(sensor_func_tbl_t *) = {
[SUB_MODULE_SENSOR] = sensor_sub_module_init,
[SUB_MODULE_CHROMATIX] = chromatix_sub_module_init,
[SUB_MODULE_ACTUATOR] = actuator_sub_module_init,
[SUB_MODULE_EEPROM] = eeprom_sub_module_init,
[SUB_MODULE_LED_FLASH] = led_flash_sub_module_init,
[SUB_MODULE_CSIPHY] = csiphy_sub_module_init,
[SUB_MODULE_CSIPHY_3D] = csiphy_sub_module_init,
[SUB_MODULE_CSID] = csid_sub_module_init,
[SUB_MODULE_CSID_3D] = csid_sub_module_init,
[SUB_MODULE_OIS] = ois_sub_module_init,
[SUB_MODULE_EXT] = external_sub_module_init,
[SUB_MODULE_IR_LED] = ir_led_sub_module_init,
[SUB_MODULE_IR_CUT] = ir_cut_sub_module_init,
[SUB_MODULE_LASER_LED] = laser_led_sub_module_init
};
其中一个模块:
int32_t sensor_sub_module_init(sensor_func_tbl_t *func_tbl)
{
if (!func_tbl) {
SERR("failed");
return SENSOR_FAILURE;
}
func_tbl->open = sensor_open;//在这里打印出子设备名
func_tbl->process = sensor_process;
func_tbl->close = sensor_close;
return SENSOR_SUCCESS;
}
打开APP的时候,打开这些子设备,然后才能进行相对应的ioctl操作:
10-24 07:49:40.753 500 7291 E mm-camera: <SENSOR><ERROR> 2491: sensor_open: [wanghl] sensor_open, subdev_string = /dev/v4l-subdev17
10-24 07:49:40.754 500 7291 E mm-camera: <SENSOR><ERROR> 913: actuator_open: [wanghl] actuator_open, subdev_string = /dev/v4l-subdev6
10-24 07:49:40.754 500 7291 E mm-camera: <SENSOR><ERROR> 627: eeprom_open: [wanghl] eeprom_open, subdev_string = /dev/v4l-subdev8
10-24 07:49:40.754 500 7291 E mm-camera: <SENSOR><ERROR> 57: flash_open: [wanghl] flash_open, subdev_string = /dev/v4l-subdev10
10-24 07:49:40.754 500 7292 E mm-camera: <SENSOR><ERROR> 196: csid_open: [wanghl] csid_open, subdev_string = /dev/v4l-subdev3
10-24 07:49:40.758 500 7292 E mm-camera: <SENSOR><ERROR> 108: csiphy_open: [wanghl] csiphy_open, subdev_string = /dev/v4l-subdev1
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor/module/sensor.c
static int32_t sensor_open(void **sctrl, void* data)
{
...
/* Open subdev */
SERR("[wanghl] sensor_open, subdev_string = %s", subdev_string);//subdev_string = /dev/v4l-subdev17
ctrl->s_data->fd = open(subdev_string, O_RDWR);//打开/dev/v4l-subdev17,获得fd
if (ctrl->s_data->fd < 0) {
SERR("failed");
rc = SENSOR_FAILURE;
goto ERROR2;
}
...
}
int32_t sensor_write_i2c_setting_array(
sensor_ctrl_t *ctrl,
struct camera_i2c_reg_setting_array *settings)
{
uint32_t i = 0;
struct sensorb_cfg_data cfg;
struct msm_camera_i2c_reg_setting setting_k;
PTHREAD_MUTEX_LOCK(&ctrl->s_data->mutex);
setting_k.addr_type =
sensor_sdk_util_get_kernel_i2c_addr_type(settings->addr_type);
setting_k.data_type =
sensor_sdk_util_get_kernel_i2c_data_type(settings->data_type);
setting_k.delay = settings->delay;
setting_k.size = settings->size;
setting_k.reg_setting = (struct msm_camera_i2c_reg_array *)
&(settings->reg_setting_a[0]);
cfg.cfgtype = CFG_WRITE_I2C_ARRAY;
cfg.cfg.setting = &setting_k;
SERR("[wanghl]sensor_write_i2c_setting_array");
if (LOG_IOCTL(ctrl->s_data->fd, VIDIOC_MSM_SENSOR_CFG, &cfg, "write_i2c") < 0) {//通过ioctl写I2C寄存器指令进行分辨率的切换等等
SERR("failed");
PTHREAD_MUTEX_UNLOCK(&ctrl->s_data->mutex);
return -EIO;
}
PTHREAD_MUTEX_UNLOCK(&ctrl->s_data->mutex);
return SENSOR_SUCCESS;
}
- - - - - - - -插播结束 - - - - - -- - -- - - - - - - - - - - - - - - - - - - - - - - -