android从应用到驱动之—camera(1)---程序调用流程

1、开篇

写博客还得写开篇介绍,惋惜,这个不是我所擅长的.就按我本身的想法写吧.html

话说camera模块,从上层到底层一共包含着这么几个部分:java

一、apk------java语言linux

二、camera的java接口----java语言android

三、camera的java接口的具体实现,即所谓的JNI-----(java—>C++)c++

四、camera客户端-----C++语言程序员

五、camera服务器----C++语言windows

六、camera硬件抽象层,即所谓的HAL------C++语言服务器

七、camera驱动ide

如上也是camera的整个调用流程.函数

可是,如此泛泛而谈,实在是太空了.想当初刚开始搞camera,单单驱动的V4L2已经够头大了,况且要涉及调试具体sensor的图像帧率,频率,输出格式等等,更不要说什么DMA对齐,图像闪动,色彩不对之类的疑难杂症了.

仍是分别讲讲它们分别的做用吧

2、介绍

一、apk

APK是AndroidPackage的缩写,即Android安装包(apk)。(摘自百度百科)

说白了,也就像windows系统上咱们常用的qq等应用程序。它们经过调用系统提供的API(极个别的简单程序不调用)来实现某项或某几项功能。

二、camera的java接口

上文刚提到API,这里就已经到了介绍它的地方。没错,这里这个所谓的接口就是传说中的API.

做为程序员,最通俗的说法无非是举例子.在apk中要想操做camera,必需要以下获取一个具体的camera对象.

Camera camera = Camera.open(int cameraId);

这个open是哪来的呢.它就是系统已经实现好的,你不须要管它哪来的,尽管调用就行了,这就是接口,这就是API.

固然,应用开发者不须要管它是哪来的,咱们得知道.对于android4.0来讲.跟camera相关的接口是在这个文件中写好的:

frameworks/base/core/java/android/hardware/Camera.java

三、camera的java接口的具体实现

对应用程序来讲,接口已经提供出来了,直接调用就是了.可是这个接口是怎么实现的呢?

众所周知,android系统(不包括内核)主要是由java和C++来写的.而内核(linux)是由c和汇编语言来写的(越底层,程序也就越面向机器,效率通常也越高).它们是怎么互相调用的呢?

这里先说java是如何调用C++的.请你们稍微看一下我另外一篇博客---JNI的实现方式.这里也稍微举一个小例子来简单说明java是怎么调用c++的.

仍是刚才那句:

Camera camera = Camera.open(int cameraId);

open的具体实现以下:frameworks/base/core/java/android/hardware/Camera.java

public static Camera open(int cameraId) {
return new Camera(cameraId);//新建一个camera(id);对象
}

以下是camera(id)的具体实现:

Camera(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mZoomListener = null;
 
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
 
native_setup(new WeakReference<Camera>(this), cameraId);    //此处camera的初始化
}

让咱们来看一下camera的native_setup函数(通常android中native这个字段就代表了调用的是本地接口):

private
native
final void native_setup(Object camera_this, int cameraId);

此处经过红色native字段能够肯定native_setup调用的就是jni接口.即frameworks/base/core/jni/android_hardware_Camera.cpp中具体实现了这个函数,让咱们来看看它是是如何转接这个函数的.

{ "native_setup",
"(Ljava/lang/Object;I)V",
(void*)android_hardware_Camera_native_setup },

对,就是这里,它表示java中native_setup()真正的实现是android_hardware_Camera_native_setup() .就这样,java语言就直接调用了C++来实现.

四、camera客户端+五、camera服务器

client:frameworks/base/libs/camera/Camera.cpp

service:frameworks/base/services/camera/libcameraservice/CameraService.cpp

Service Manager在Binder机制中既充当守护进程的角色,同时它也充当着Server角色,然而它又与通常的Server不同。对于普通的Server来讲,Client若是想要得到Server的远程接口,那么必须经过Service Manager远程接口提供的getService接口来得到,这自己就是一个使用Binder机制来进行进程间通讯的过程。而对于Service Manager这个Server来讲,Client若是想要得到Service Manager远程接口,却没必要经过进程间通讯机制来得到,由于Service Manager远程接口是一个特殊的Binder引用,它的引用句柄必定是0。

这是网上直接搜来的。不是太通俗,并且牵扯到binder通讯。下面说说个人理解。

如今正值春运,就拿乘坐火车回家来讲吧。client(客户端)其实就至关于咱们每一个人,咱们都想回家,都想买到理想中的火车票(最好是卧铺).可是火车的运力是必定的.不可能每一个人都买到回家的票.若是没人管,那坐火车就变成谁的块头大谁能上车了.铁道部(今年刚改革了)就是这个管理者(service),我买了今天的A火车B车箱C座.其余人要是还想坐这个座位,对不起,应经被占用了.要不您就不坐或者等一会看我退票了您再来抢(注意这个抢字,说多了都是泪)。

对应到android.一个android手机中有可能有好几个照相机应用.每个应用实现一个client.若是A应用从service申请到了camera,而且在后台录像.这时候B也要打开照相机,那service就会直接回复说,很差意思,设备正使用中(BUZY).您晚些时候再来吧.

六、camera硬件抽象层

HAL是干什么的呢?

由client和service可知,火车的管理是掌握在铁道部手里的,其余人想不通过铁道部直接上车,一个字:难!  两个字:拼爹。

要说这HAL,其实对应的就是这个火车的车长。哪一个车箱是卧铺哪一个车箱是硬座,都是它来搞定的.

service是领导,若是要它直接跟每一个驱动打交道,那还不得累死,经过HAL多好,有什么需求,直接发个HAL就行了,它来调用跟内核跟驱动协商的具体实现步骤.

固然,官方一点的解释是这样的(从这里偷来的):

Android的HAL是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚。思路是把控制硬件的动做都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互做用,甚至把硬件寄存器空间直接映射到user space。而Android是基于Aparch的license,所以硬件厂商能够只提供二进制代码,因此说Android只是一个开放的平台,并非一个开源的平台。也许也正是由于Android不听从GPL,因此Greg Kroah-Hartman才在2.6.33内核将Andorid驱动从linux中删除。GPL和硬件厂商目前仍是有着没法弥合的裂痕。Android想要把这个问题处理好也是不容易的。

    总结下来,Android HAL存在的缘由主要有:

    1. 并非全部的硬件设备都有标准的linux kernel的接口

    2. KERNEL DRIVER涉及到GPL的版权。某些设备制造商并不缘由公开硬件驱动,因此才去用HAL方 式绕过GPL。

    3. 针对某些硬件,An有一些特殊的需求

七、camera驱动

说完hal,总算该说驱动了,但是真要开始介绍,又不知从哪里开始讲比较好.

暂且放着吧,我会有专门的博客来写驱动的.

 

3、调用流程

啰嗦了一大堆,就为了这篇博客的最终目的----流程.那下面就用一个实例来简单分析一下它们之间是如何调用到对方的.这里仅仅经过一两个功能分析其调用流程,这个搞通了以后再分别介绍一下cameraHAL的实现和驱动的调试(也仅仅是这两个模块了,毕竟主要是搞底层开发,java不是个人擅长)

仍是从应用程序开始:

 
private void initCamera()
{
Camera camera;  // 定义系统所用的照相机
if (!isPreview)
{
camera = Camera.open();  //1.调用Camera的open()方法打开相机。
}
if (camera != null && !isPreview)
{
try
{
//2.调用Camera的setParameters()方法获取拍照参数。该方法返回一个Camera.Parameters对象。
Camera.Parameters parameters = camera.getParameters();
//3.调用Camera.Paramers对象方法设置拍照参数
// 设置预览照片的大小
parameters.setPreviewSize(screenWidth, screenHeight);
// 每秒显示4帧
parameters.setPreviewFrameRate(4);
// 设置图片格式
parameters.setPictureFormat(PixelFormat.JPEG);
// 设置JPG照片的质量
parameters.set("jpeg-quality",85);
//设置照片的大小
parameters.setPictureSize(screenWidth, screenHeight);
 
//4.调用Camera的setParameters,并将Camera.Paramers做为参数传入,这样便可对相机的拍照参数进行控制
camera.setParameters(parameters);
/**
*  5.调用Camera的startPreview()方法开始预览取景,在预览取景以前须要调用
*  Camera的setPreViewDisplay(SurfaceHolder holder)方法设置使用哪一个SurfaceView来显示取景图片。
*  经过SurfaceView显示取景画面
*/
camera.setPreviewDisplay(surfaceHolder);
// 6.开始预览
camera.startPreview();
// 7.自动对焦
camera.autoFocus(afcb);
// 8.调用Camera的takePicture()方法进行拍照.
camera.takePicture(null, null , myjpegCallback);
}
catch (Exception e)
{
e.printStackTrace();
}
isPreview = true;
}
}

别期望上边的程序能运行,这里只是为了介绍其流程,预览界面什么都没有.这里咱们只看open的过程,它调用

--->frameworks/base/core/java/android/hardware/Camera.java

public static Camera open() {
int numberOfCameras = getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {        //此处强制打开后置,遍历存在的摄像头以后仍是没有后置则会返回null
return new Camera(i);        //此处新建一个camera(id)对象
}
}
return null;
}

getNumberOfCameras和getCameraInfo这两个介绍cameraHAL的时候再具体介绍.下面看new  camera的实现.

Camera(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mZoomListener = null;
 
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
 
native_setup(new WeakReference<Camera>(this), cameraId);    //此处camera的初始化
}
private native final void native_setup(Object camera_this, int cameraId);

上文咱们就已经知道native_setup调用的是jni接口.即

frameworks/base/core/jni/android_hardware_Camera.cpp的android_hardware_Camera_native_setup

static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jint cameraId)
{
sp<Camera> camera = Camera::connect(cameraId);    //调用的是frameworks/base/libs/camera/Camera.cpp
...
}

而后来看client的调用(注意调用时sp<Camera> ,有玄机哦,自行百度,我还不大懂).

frameworks/base/libs/camera/Camera.cpp

sp<Camera> Camera::connect(int cameraId)
{
LOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
c->mCamera = cs->connect(c, cameraId);        //此处调用frameworks/base/services/camera/libcameraservice/CameraService.cpp
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
c->mStatus = NO_ERROR;
} else {
c.clear();
}
return c;
}

就是这个connect,它直接调用了cameraservice.

frameworks/base/services/camera/libcameraservice/CameraService.cpp

sp<ICamera> CameraService::connect(
const sp<ICameraClient>& cameraClient, int cameraId) {
.....
hardware = new CameraHardwareInterface(camera_device_name);
if (hardware->initialize(&mModule->common) != OK) {                //此处调用frameworks/base/services/camera/libcameraservice/CameraHardwareInterface.h初始化camera
hardware.clear();
return NULL;
}
client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);
mClient[cameraId] = client;
LOG1("CameraService::connect X");
return client;
}

继续往下调用,它应该去调用HAL层的初始化函数,刚开始找的时候怎么也找不到,原来是在头文件中调用的,我嘞个去.

frameworks/base/services/camera/libcameraservice/CameraHardwareInterface.h

status_t initialize(hw_module_t *module)
{
LOGI("Opening camera %s", mName.string());
int rc = module->methods->open(module, mName.string(),                //module就是hardware所创建的module. 经过hardware.h能够看出最终调用的是厂商的hardware
(hw_device_t **)&mDevice);
if (rc != OK) {
LOGE("Could not open camera %s: %d", mName.string(), rc);
return rc;
}
initHalPreviewWindow();
return rc;
}

下面让咱们看看这个open.如下就是所谓的HAL

它是这样定义的

static hw_module_methods_t camera_module_methods = {
open : HAL_camera_device_open
};
static int HAL_camera_device_open(const struct hw_module_t* module,
const char *id,
struct hw_device_t** device)
{
int cameraId = atoi(id);
...//此处省略参数的配置,介绍HAL的时候再详细介绍
g_cam_device->priv = new CameraHardwareSec(cameraId, g_cam_device);                //此处初始化具体的camera设备
 
done:
*device = (hw_device_t *)g_cam_device;
return 0;
}

camera设备的初始化:

CameraHardwareSec::CameraHardwareSec(int cameraId, camera_device_t *dev)
:
mCaptureInProgress(false),
mParameters(),
mFrameSizeDelta(0),
mCameraSensorName(NULL),
mUseInternalISP(false),
mSkipFrame(0),
mNotifyCb(0),
mDataCb(0),
mDataCbTimestamp(0),
mCallbackCookie(0),
mMsgEnabled(CAMERA_MSG_RAW_IMAGE),
mRecordRunning(false),
mPostViewWidth(0),
mPostViewHeight(0),
mPostViewSize(0),
mCapIndex(0),
mCurrentIndex(-1),
mOldRecordIndex(-1),
mRecordHint(false),
mRunningSetParam(0),
mTouched(0),
mFDcount(0),
mRunningThread(0),
mHalDevice(dev)
{
.....
ret = mSecCamera->CreateCamera(cameraId);        //Step1:此处新建camera
if (ret < 0) {
mSecCamera->DestroyCamera();
}
initDefaultParameters(cameraId);//使用默认的参数初始化指定摄像头
mPreviewThread = new PreviewThread(this);//预览线程
mPreviewFimcThread = new PreviewFimcThread(this);//预览线程
mRecordFimcThread = new RecordFimcThread(this);//录像线程
mSnapshotFimcThread = new SnapshotFimcThread(this);//快照线程
mCallbackThread = new CallbackThread(this);//回调线程
mAutoFocusThread = new AutoFocusThread(this);//自动对焦线程
mPictureThread = new PictureThread(this);//照相线程
}

介绍CreateCamera以前要先知道一些定义,这里也是三星驱动中FIMCx是怎么被配置成不一样功能的:

#define CAMERA_DEV_NAME   "/dev/video0"
#define CAMERA_DEV_NAME2  "/dev/video2"
#define CAMERA_DEV_NAME3  "/dev/video1"

具体实现:

bool SecCamera::CreateCamera(int index){
...    //此处省略n行
/* FIMC0 open */
ret = createFimc(&m_cam_fd, CAMERA_DEV_NAME, V4L2_BUF_TYPE_VIDEO_CAPTURE, index);    //Step1-1:和底层打交道开始    打开FIMC0做为取景输入(##define CAMERA_DEV_NAME   "/dev/video0")
CHECK(ret);
m_camera_use_ISP = getUseInternalISP();
if (m_camera_use_ISP) {
if (!m_recording_en)
fimc_v4l2_s_fmt_is(m_cam_fd, m_preview_max_width, m_preview_max_height,
m_preview_v4lformat, (enum v4l2_field) IS_MODE_PREVIEW_STILL);
else
fimc_v4l2_s_fmt_is(m_cam_fd, m_preview_max_width, m_preview_max_height,
m_preview_v4lformat, (enum v4l2_field) IS_MODE_PREVIEW_VIDEO);
}
ret = fimc_v4l2_s_fmt(m_cam_fd, m_preview_max_width, m_preview_max_height,
m_preview_v4lformat, V4L2_FIELD_ANY, PREVIEW_NUM_PLANE);
CHECK(ret);
initParameters(m_camera_use_ISP);
if (m_camera_use_ISP) {        //使用4412自带的ISP
/* FIMC2 open for recording and zsl, video snapshot m2m */
ret = createFimc(&m_cam_fd3, CAMERA_DEV_NAME2, V4L2_BUF_TYPE_VIDEO_OUTPUT, index);    //Step1-2:打开FIMC2做为输出设备,(将图像数据输出到FIMC2进行处理)
CHECK(ret);
/* FIMC1 open for preview m2m */
ret = createFimc(&m_cam_fd2, CAMERA_DEV_NAME3, V4L2_BUF_TYPE_VIDEO_OUTPUT, index);    //Step1-3:打开FIMC1做为输出设备,(将图像数据输出到FIMC2进行处理)
CHECK(ret);
} else {                    //不使用4412自带的ISP
/* FIMC1 open */    //Step1-4:打开FIMC1做为输入设备
ret = createFimc(&m_cam_fd2, CAMERA_DEV_NAME3, V4L2_BUF_TYPE_VIDEO_CAPTURE, index);
CHECK(ret);
}
setExifFixedAttribute();
if (m_camera_use_ISP) {            //若是使用4412自带ISP,则定义以下
m_prev_fd = m_cam_fd2;        //此处定义fimc1为预览        fimc0为取景
m_cap_fd = m_cam_fd3;        //此处定义fimc2为照相
m_rec_fd = m_cam_fd3;        //此处定义fimc2为录像
m_num_capbuf = CAP_BUFFERS;
} else {                        //若是不使用4412自带ISP,则定义以下
m_prev_fd = m_cam_fd;        //此处定义fimc0为预览
m_cap_fd = m_cam_fd;        //此处定义fimc0为照相
m_rec_fd = m_cam_fd2;        //此处定义fimc1为录像
m_num_capbuf = 1;
}
}
return 0;
}

看到了吧,如上就是配置不一样的FIMC做为不一样功能的地方.这里指跟踪createFimc函数

int SecCamera::createFimc(int *fp, char *dev_name, int mode, int index)
{
struct v4l2_format fmt;
int ret = 0;
*fp = open(dev_name, O_RDWR);        //打开指定的FIMCx 即对应设备节点/dev/videox
if (fp < 0) {
return -1;
}
if (mode == V4L2_BUF_TYPE_VIDEO_CAPTURE) {        //若是buf类型为v4l2 capture(捕获、输入) 此处对应Step1-1和Step1-4
ret = fimc_v4l2_querycap(*fp);        //查询设备是否支持V4L2_BUF_TYPE_VIDEO_CAPTURE    Step1-1-1
CHECK(ret);
if (!fimc_v4l2_enuminput(*fp, index)) {        //查询是否存在此摄像头Step1-1-2
return -1;
}
ret = fimc_v4l2_s_input(*fp, index);        //选择此摄像头做为输入设备Step1-1-3
CHECK(ret);
} else if (mode == V4L2_BUF_TYPE_VIDEO_OUTPUT) {    //若是缓冲区类型为V4L2_BUF_TYPE_VIDEO_OUTPUT(输出)    此处对应Step1-2和Step1-3
ret = fimc_v4l2_querycap_m2m(*fp);            //查询是否存在此设备及其能力是否支持Step1-2-1
CHECK(ret);
/* malloc fimc_outinfo structure */
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (ioctl(*fp, VIDIOC_G_FMT, &fmt) < 0) { //查询设备是否支持视频输出(处理图像)  V4L2接口哦
return -1;
}
}
return ret;
}

具体函数:

Step1-1-1:
static int fimc_v4l2_querycap(int fp)
{
struct v4l2_capability cap;
if (ioctl(fp, VIDIOC_QUERYCAP, &cap) < 0) {         //查询设备能力    //Step1-1-1-1 ioctl的底层实现
return -1;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {    //判断设备是不是图像采集设备
return -1;
}
return 0;
}
Step1-1-2:
static const __u8* fimc_v4l2_enuminput(int fp, int index)
{
static struct v4l2_input input;
 
input.index = index;
if (ioctl(fp, VIDIOC_ENUMINPUT, &input) != 0) {    //查询是否存在此摄像头设备
return NULL;
}
LOGI("Name of input channel[%d] is %s", input.index, input.name);
return input.name;
}

Step1-1-1-1 ioctl的底层实现:这里就该调用到内核层了,让咱们先看一下V4L2基本定义:

VIDIOC_QUERYCAP     /* 获取设备支持的操做 */
VIDIOC_G_FMT        /* 获取设置支持的视频格式 */
VIDIOC_S_FMT        /* 设置捕获视频的格式 */
VIDIOC_REQBUFS      /* 向驱动提出申请内存的请求 */
VIDIOC_QUERYBUF     /* 向驱动查询申请到的内存 */
VIDIOC_QBUF         /* 将空闲的内存加入可捕获视频的队列 */
VIDIOC_DQBUF        /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
VIDIOC_STREAMON     /* 打开视频流 */
VIDIOC_STREAMOFF    /* 关闭视频流 */
VIDIOC_QUERYCTRL    /* 查询驱动是否支持该命令 */
VIDIOC_G_CTRL       /* 获取当前命令值 */
VIDIOC_S_CTRL       /* 设置新的命令值 */
VIDIOC_G_TUNER      /* 获取调谐器信息 */
VIDIOC_S_TUNER      /* 设置调谐器信息 */
VIDIOC_G_FREQUENCY  /* 获取调谐器频率 */
VIDIOC_S_FREQUENCY  /* 设置调谐器频率 */
VIDIOC_CROPCAP        /* 视频裁剪和缩放功能信息 */
VIDIOC_G_CROP        /* 获取当前的裁剪矩形 */
VIDIOC_S_CROP        /* 设置当前的裁剪矩形 */
VIDIOC_ENUM_INPUT    /* 枚举视频输入 */
VIDIOC_G_INPUT        /* 查询当前视频输入设备是哪一个 */
VIDIOC_S_INPUT        /* 选择当前视频输入设备是哪一个 */
VIDIOC_G_PARM        /* 获取流参数 */
VIDIOC_S_PARM        /* 设置流参数 */
VIDIOC_QUERYCTRL    /* 枚举控制项目 */
VIDIOC_QUERYMENU    /* 枚举菜单控制项目 */
VIDIOC_G_FBUF        /* 获取帧缓冲区 */        获取参数?
VIDIOC_S_FBUF        /* 覆盖帧缓冲区 */        设置参数?

后边这两个暂时没看.

最终调用的函数是这样定义的.至于如何调用到这里,稍后专门讲驱动的时候再详细写.

drivers/media/video/samsung/fimc/fimc_v4l2.c

const struct v4l2_ioctl_ops fimc_v4l2_ops = {
.vidioc_querycap                = fimc_querycap,
.vidioc_reqbufs                 = fimc_reqbufs,
.vidioc_querybuf                = fimc_querybuf,
.vidioc_g_ctrl                  = fimc_g_ctrl,
.vidioc_g_ext_ctrls             = fimc_g_ext_ctrls,
.vidioc_s_ctrl                  = fimc_s_ctrl,
.vidioc_s_ext_ctrls             = fimc_s_ext_ctrls,
.vidioc_cropcap                 = fimc_cropcap,
.vidioc_g_crop                  = fimc_g_crop,
.vidioc_s_crop                  = fimc_s_crop,
.vidioc_streamon                = fimc_streamon,
.vidioc_streamoff               = fimc_streamoff,
.vidioc_qbuf                    = fimc_qbuf,
.vidioc_dqbuf                   = fimc_dqbuf,
.vidioc_enum_fmt_vid_cap        = fimc_enum_fmt_vid_capture,
.vidioc_g_fmt_vid_cap           = fimc_g_fmt_vid_capture,
.vidioc_s_fmt_vid_cap           = fimc_s_fmt_vid_capture,
.vidioc_s_fmt_type_private      = fimc_s_fmt_vid_private,
.vidioc_try_fmt_vid_cap         = fimc_try_fmt_vid_capture,
.vidioc_enum_input              = fimc_enum_input,
.vidioc_g_input                 = fimc_g_input,
.vidioc_s_input                 = fimc_s_input,
.vidioc_g_parm                  = fimc_g_parm,
.vidioc_s_parm                  = fimc_s_parm,
.vidioc_queryctrl               = fimc_queryctrl,
.vidioc_querymenu               = fimc_querymenu,
.vidioc_g_fmt_vid_out           = fimc_g_fmt_vid_out,
.vidioc_s_fmt_vid_out           = fimc_s_fmt_vid_out,
.vidioc_try_fmt_vid_out         = fimc_try_fmt_vid_out,
.vidioc_g_fbuf                  = fimc_g_fbuf,
.vidioc_s_fbuf                  = fimc_s_fbuf,
.vidioc_try_fmt_vid_overlay     = fimc_try_fmt_overlay,
.vidioc_g_fmt_vid_overlay       = fimc_g_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay       = fimc_s_fmt_vid_overlay,
.vidioc_enum_framesizes         = fimc_enum_framesizes,
.vidioc_enum_frameintervals     = fimc_enum_frameintervals,
};

以下是HAL层调用的具体查询camera属性的实现.

static int fimc_querycap(struct file *filp, void *fh,
struct v4l2_capability *cap)
{
struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;
fimc_info1("%s: called\n", __func__);
strcpy(cap->driver, "SEC FIMC Driver");
strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card));        //Step1-1-1-1-1:name是直接取值于具体的驱动
sprintf(cap->bus_info, "FIMC AHB-bus");
cap->version = 0;
cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_STREAMING);    //真把本身当神器了,什么属性都有....
return 0;
}

最后再贴两个函数,解释name是如何获取到的

drivers/media/video/s5k4ea.c        //摄像头的驱动文件

static int s5k4ea_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct s5k4ea_state *state;
struct v4l2_subdev *sd;
state = kzalloc(sizeof(struct s5k4ea_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
sd = &state->sd;
strcpy(sd->name, S5K4EA_DRIVER_NAME);        //此处赋值成摄像头的具体名字
/* Registering subdev */
v4l2_i2c_subdev_init(sd, client, &s5k4ea_ops);    //此处注册摄像头设备,此函数中得到摄像头的名字
dev_info(&client->dev, "s5k4ea has been probed\n");
return 0;
}

v4l2_i2c_subdev_init函数将具体摄像头的驱动中获取的名字加工后搞到设备名里边供返回给上层应用(HAL)

drivers/media/video/v4l2-common.c

void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
/* the owner is the same as the i2c_client's driver owner */
sd->owner = client->driver->driver.owner;
/* i2c_client and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, client);
i2c_set_clientdata(client, sd);
/* initialize name */
snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
client->driver->driver.name, i2c_adapter_id(client->adapter),
client->addr);            //这里将摄像头名字更新到sd->name中,上层调用时返回sd->name(位置Step1-1-1-1-1).
}

至此,终于将整个流程链接到了一起.

很抱歉的是没有彻底介绍初始化时整个camera模块每一步的具体实现.仍是留到HAL层和驱动层分别介绍吧,明天写HAL,不知道一天时间可否写完.但愿春节回家以前把camera模块都写完吧.

相关文章
相关标签/搜索