寫博客還得寫開篇介紹,惋惜,這個不是我所擅長的.就按我本身的想法寫吧.html
話說camera模塊,從上層到底層一共包含着這麼幾個部分:java
一、apk------java語言linux
二、camera的java接口----java語言android
三、camera的java接口的具體實現,即所謂的JNI-----(java—>C++)c++
四、camera客戶端-----C++語言程序員
五、camera服務器----C++語言web
六、camera硬件抽象層,即所謂的HAL------C++語言windows
七、camera驅動服務器
如上也是camera的整個調用流程.ide
可是,如此泛泛而談,實在是太空了.想當初剛開始搞camera,單單驅動的V4L2已經夠頭大了,況且要涉及調試具體sensor的圖像幀率,頻率,輸出格式等等,更不要說什麼DMA對齊,圖像閃動,色彩不對之類的疑難雜症了.
仍是分別講講它們分別的做用吧
一、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
nativefinal 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,總算該說驅動了,但是真要開始介紹,又不知從哪裏開始講比較好.
暫且放着吧,我會有專門的博客來寫驅動的.
囉嗦了一大堆,就爲了這篇博客的最終目的----流程.那下面就用一個實例來簡單分析一下它們之間是如何調用到對方的.這裏僅僅經過一兩個功能分析其調用流程,這個搞通了以後再分別介紹一下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模塊都寫完吧.