高通新的camera驱动架构设计发生了一些变化,借用互联网上常用的一种结构,大致的原理如此:将camera的所有功能划分为不同的模块,让模块自己来决定自己的事情(高内聚,低耦合),模块需要有统一的接口和格式。模块中有端口,通过端口把模块连接起来,又把模块挂在总线上。每一个端口的连接就是一个流,把这些流用pipeline来管理。每次启动一个camera就创建一个会话,由这个会话来管理此camera的一切事物。对于每一个会话,模块是共享的,它可以是camera的硬件资源也可以是其它资源(如一些软件算法等资源)。
那么如何来定义这个模块的结构呢?
1.端口——端口属于模块,如果这个模块只有source端口,那么它就是一个src模块;如果只有sink端口就是sink模块,如果都有就是中间模块。没有端口的模块是不能连接到流中的,但他可以完成一些其他的功能,比如接收引擎的设置,报告事件到bus等。连接到流中的端口,也就是说流事件(set/get)主要通过端口来处理。而来自于引擎的(set/get)通过模块来处理,当然端口也可以把事件交给模块来处理。模块内部的端口可以通过模块来建立关系,也可以建立内部的连接,端口有关get/set process。
2.模块线程——每个模块可以有一个线程来处理模块的事情。一个线程对应一个队列,线程就是从队列中取出数据处理,然后应答回去。
3.总线回调——挡一个模块向总线注册时,总线向其提供一个回调函数,当模块有事件发生时,调用这个函数向bus发消息,然后总线把这个消息提交给管道,管道把这个消息顺着流发下去。
4.模块的get、set以及process。
管道、引擎与会话
管道有两端,一端用于读,一端用于写。camera引擎负责对管道的监控,而会话管理camera引擎。
从代码结构上来看这种新的驱动架构,高通的camera deamon代码放置在vendor\qcom\proprietary\mm-camera目录下,而此目录下的mm-camera2就是新的camera架构位置,进入里面可以看到media-controller、server-imaging、server-tuning及其它几个目录,我们这里需要关注的就是media-controller目录。
media-controller
|- mct——应该就是camera的引擎?里面包含了引擎、pipiline、bus、module、stream及event等定义及封装。
|- modules——这里面就是划分好的一些模块代码,各模块大致功能如下
|- sensor —— sensor 的驱动模块? —— src模块
|- iface —— ISP interface模块 —— inter模块
|- isp —— 主要是ISP的处理,其内部又包含了众多的模块 —— inter模块
|- stats —— 一些统计算法模块,如3A,ASD,AFD,IS,GRRO等数据统计的处理 —— sink模块
|- pproc —— post process处理 —— inter模块
|- imglib —— 主要是图片的一些后端处理,如HDR等 —— sink模块
以上各模块内部又包含了众多的模块,具体需要看代码分析。
高通camera daemon进程
1.概述
高通在Android的camera架构中,依旧沿用了其传统的方式,将其自身的一些处理放在一个daemon进程中。这部分内容出于应用于driver之间,是为了保护自身及硬件厂商的利益而专门弄出来的一个东东。其它各家平台也采用类似的方式将这部分的处理放在HAL层处理。
2.进程的入口
daemon 进程作为单一进程,main 函数的入口,位置如下:
/vendor/qcom/proprietary/mm-camera/mm-camera2/server-imaging/server.c
/vendor/qcom/proprietary/mm-camera/mm-camera2/serverimaging/server_process.c
在vendor\qcom\proprietary\mm-camera\mm-camera2\server-imaging\server.c文件中可以看到这个main函数。在这个函数中主要做了以下几件事情:
1.找到服务节点的名字并打开此节点
get_server_node_name(serv_hal_node_name)
......
hal_fd->fd[0] = open(dev_name, O_RDWR | O_NONBLOCK); //这里dev_name为节点名如"/dev/serv_hal_node_name"
2.初始化模块。目前有sensor、iface、isp、stats、pproc及imglib六个模块(见笔记一)
server_process_module_init();
3.进入主循环来处理来自HAL及MCT的事件及消息,处理完之后的结果反馈给kernel(msm.c)
RD_FD_HAL
----> server_process_hal_event(&event)。此函数如果返回真,表示事件消息已经传给了MCT,这时不需要发送CMD ACK给kernel,因为MCT处理结束后会发出通知。如果返回假,表示没有传到MCT,此时需要立即发送CMD ACK到kernel,以便HAL发送此消息的线程阻塞住。
RD_DS_FD_HAL —— 通过domain socket传自HAL的消息
----> server_process_hal_ds_packet(fd_info->fd
RD_PIPE_FD_MCT —— 来自media controller的消息
media controller线程
1.概述
MCT线程是camera新架构的引擎部分,负责对管道的监控,由此来完成一个camera设备的控制运转。
它运行在daemon进程空间,由MSM_CAMERA_NEW_SESSION事件来开启,具体开启函数为mct_controller_new()。
2.mct_controller_new()函数
此函数创建一个新的MCT引擎,这将对应一个事务的pipeline。我们知道上层可以创建多个事务,每个对应一个camera,也对应自己的MCT及pipeline等。因此这个函数的主要完成以下几件事情:
1.mct_pipeline_new()
---->创建一个Pipeline及其bus,并完成pipeline函数的映射。
2.mct_pipeline_start_session()
---->开启camera的所有模块并查询其能力
3.pthread_create(..., mct_controller_thread_run, ...)
---->创建mct线程并开始执行
4.pthread_create(..., mct_bus_handler_thread_run, ...)
---->创建bus处理线程
3.mct_list_traverse()函数
此函数在整个mct线程中大量使用,主要用来遍历所有模块并执行一些处理工作。结合前面所讲,camera各模块都具有统一的接口,通过流来连接,模块中又包含模块,根据这种特性高通使用链表结构来保存这些模块并设计了此函数用来完成遍历操作。
1.先来看看此链表的节点结构。链表的节点其实也是一个链表,整个链表就好像是一串串同级的节点搭建而成,整个数据结构组成一颗树结构。
struct _mct_list {
void *data; // 节点数据
mct_list_t *prev; // 上一个节点地址
mct_list_t **next; // 下一个节点节点元素数组首地址
uint32_t next_num; // 下一个节点节点元素数,大部分情况下为1
}mct_list_t;
2.通过递归的深度优先算法来遍历整棵树。
4.MCT线程运行
MCT整个引擎部分主要处理server及bus两类事情,对应前面提到的MCT及bus两个线程。MCT线程主要用来处理来自image server的消息,先pop MCT queue,查看是否有消息,如果有则执行mct_controller_proc_serv_msg_internal()函数来处理。
mct_controller_proc_serv_msg_internal函数用来处理来自image server的消息,并返回类型MCT_PROCESS_RET_SERVER_MSG。这里处理的消息类型主要有SERV_MSG_DS与SERV_MSG_HAL两种,分别在pipline中给出了相应的处理函数,具体查看源码可知。
5.bus线程运行
bus线程跟MCT线程流程一样。从代码上我们看到两个线程都是从同一个queue上面pop消息,他们是通过各自的线程条件变量来进行区分,完成线程的阻塞及运行工作。MCT的条件变量mctl_cond可以看到是在server_process.c文件中标记的,而bus的条件变量mctl_bus_handle_cond未在源码中找到标志的位置?
sensor模块
1.概述
sensor模块是众多模块中的一个,主要是由模组的各个硬件模块组成,包括sensor、Flash、Af、EEprom、OIS、CSI等。这个模块主要描述了模组硬件的一些工作原理及部分驱动相关部分。
Probe 函数在 sensor_init.c 文件中,主要调用流程为:先去 probe eebin,再去 probe sensor,最后解析其 OTP 的具体信息。
其 probe 函数位于 module_sensor.c 的 module_sensor_init( )。
2.module_sensor_init()函数
在前面讲到的server process中提到,服务进程开始后会初始化各个模块,其中就包括sensor模块,sensor初始化入口函数即为module_sensor_init(...)。这个函数将创建sensor模块并返回其指针,另外将创建它的端口,填充一些功能函数等。它的主要执行流程如下:
1.创建sensor的MCT module。 —— mct_module_create(name)
创建完之后填充set mode、query mode、start session、stop session及set session data五个接口函数。
2.创建module_sensro_ctrl_t结构体,此结构体包含bundle信息,用来构建前面提到的模块树(方便添加、遍历等操作)。
3.sensor模块是source模块,所以其numsinkports应该设置为0。
4.eebin相关的操作
5.sensor的探测操作,用来探测有效的sensor。
6.填入所有已探测到sensor的信息。
7.填入所以sensor的其它信息(Actuator,Flash,CSID,OIS等)。
8.初始化sensor模块。
9.创建基于CID info的端口
10.初始化eeprom