2. master和slave的匹配過程
(一)master的註冊過程
1. 首先來看看master的註冊過程,在mxc_v4l2_capture.c檔案中,從module_init(camera_init)函式開始,在camera_init函式中通過
err= platform_driver_register(&mxc_v4l2_driver)
來將mxc_v4l2_driver這個驅動註冊到platform平臺上面,如果有匹配的裝置的話,就會呼叫到mxc_v4l2_driver裡面的probe函式。
(下面的分析都是從流程上面來分析,去掉了函式中一些與分析無關的程式碼,關於函式的詳細分析,看《mxc_v4l2_capture.c
2. 下面來看看這個probe函式
static int mxc_v4l2_probe(struct platform_device *pdev) { /* Create cam and initialize it. */ cam_data *cam = kmalloc(sizeof(cam_data), GFP_KERNEL); init_camera_struct(cam, pdev); //初始化cam_data結構體,分析見2.1 pdev->dev.release = camera_platform_release; /* Set up the v4l2 device and register it*/ cam->self->priv = cam; //將cam_data結構體裡面的self裡面的priv指標指向自己,在下面的 v4l2_int_device_register 函式中就會用到。 v4l2_int_device_register(cam->self); //分析見2.2 /* register v4l video device */ if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) { //註冊video裝置 kfree(cam); cam = NULL; pr_err("ERROR: v4l2 capture: video_register_device failed\n"); return -1; } }
在這個函式中,首先是為cam_data結構體分配了記憶體,然後就呼叫init_camera_struct函式來初始化這個cam_data結構體。
2.1 我們來看看這個init_camera_struct函式裡面都做了什麼:
static int init_camera_struct(cam_data *cam, struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxc_v4l2_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; int ipu_id, csi_id, mclk_source; int ret = 0; struct v4l2_device *v4l2_dev; /* Default everything to 0 */ memset(cam, 0, sizeof(cam_data)); init_MUTEX(&cam->param_lock); init_MUTEX(&cam->busy_lock); cam->video_dev = video_device_alloc(); //分配一個video_device結構體 *(cam->video_dev) = mxc_v4l_template; //設定ops操作 video_set_drvdata(cam->video_dev, cam); //將cam設定為cam->video_dev的私有資料 dev_set_drvdata(&pdev->dev, (void *)cam); cam->video_dev->minor = -1; v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL); if (v4l2_device_register(&pdev->dev, v4l2_dev) < 0) { dev_err(&pdev->dev, "register v4l2 device failed\n"); video_device_release(cam->video_dev); kfree(v4l2_dev); return -ENODEV; } cam->video_dev->v4l2_dev = v4l2_dev; init_waitqueue_head(&cam->enc_queue); init_waitqueue_head(&cam->still_queue); /* setup cropping */ cam->crop_bounds.left = 0; cam->crop_bounds.width = 640; cam->crop_bounds.top = 0; cam->crop_bounds.height = 480; cam->crop_current = cam->crop_defrect = cam->crop_bounds; ipu_csi_set_window_size(cam->ipu, cam->crop_current.width, cam->crop_current.height, cam->csi); ipu_csi_set_window_pos(cam->ipu, cam->crop_current.left, cam->crop_current.top, cam->csi); cam->streamparm.parm.capture.capturemode = 0; cam->standard.index = 0; cam->standard.id = V4L2_STD_UNKNOWN; cam->standard.frameperiod.denominator = 30; cam->standard.frameperiod.numerator = 1; cam->standard.framelines = 480; cam->standard_autodetect = true; cam->streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->streamparm.parm.capture.timeperframe = cam->standard.frameperiod; cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME; cam->overlay_on = false; cam->capture_on = false; cam->v4l2_fb.flags = V4L2_FBUF_FLAG_OVERLAY; cam->v2f.fmt.pix.sizeimage = 352 * 288 * 3 / 2; cam->v2f.fmt.pix.bytesperline = 288 * 3 / 2; cam->v2f.fmt.pix.width = 288; cam->v2f.fmt.pix.height = 352; cam->v2f.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; cam->win.w.width = 160; cam->win.w.height = 160; cam->win.w.left = 0; cam->win.w.top = 0; cam->ipu_id = ipu_id; cam->csi = csi_id; cam->mclk_source = mclk_source; cam->mclk_on[cam->mclk_source] = false; cam->enc_callback = camera_callback; //設定回撥函式,這個函式很重要 init_waitqueue_head(&cam->power_queue); spin_lock_init(&cam->queue_int_lock); spin_lock_init(&cam->dqueue_int_lock); cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL); cam->self->module = THIS_MODULE; sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi); cam->self->type = v4l2_int_type_master; cam->self->u.master = &mxc_v4l2_master; return 0; }
在這個函式中,對cam_data結構體裡面的一些變數進行了初始化,設定了一些預設值。同時注意這個函式的最後幾步操作,它為cam->self分配了記憶體,然後設定cam->self->type為v4l2_int_type_master,指定了cam->self->u.master為&mxc_v4l2_master。這幾步有什麼作用呢?這個在2.2中分析。
2.2 v4l2_int_device_register函數分析
int v4l2_int_device_register(struct v4l2_int_device *d)
{
if (d->type == v4l2_int_type_slave)
sort(d->u.slave->ioctls, d->u.slave->num_ioctls,
sizeof(struct v4l2_int_ioctl_desc),
&ioctl_sort_cmp, NULL);
mutex_lock(&mutex);
list_add(&d->head, &int_list);
v4l2_int_device_try_attach_all();
mutex_unlock(&mutex);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_int_device_register);
在這個函式中,首先通過list_add將這個cam->self結構體新增到int_list連結串列中,最重要的操作就是v4l2_int_device_try_attach_all()函式,
void v4l2_int_device_try_attach_all(void)
{
struct v4l2_int_device *m, *s;
list_for_each_entry(m, &int_list, head) {
if (m->type != v4l2_int_type_master)
continue;
list_for_each_entry(s, &int_list, head) {
if (s->type != v4l2_int_type_slave)
continue;
/* Slave is connected? */
if (s->u.slave->master)
continue;
/* Slave wants to attach to master? */
if (s->u.slave->attach_to[0] != 0
&& strncmp(m->name, s->u.slave->attach_to,
V4L2NAMESIZE))
continue;
if (!try_module_get(m->module))
continue;
s->u.slave->master = m;
if (m->u.master->attach(s)) {
s->u.slave->master = NULL;
module_put(m->module);
continue;
}
}
}
}
EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all);
這個函式首先從int_list連結串列中取出type型別為v4l2_int_type_master的結構體儲存在m中,然後再取出type型別為v4l2_int_type_slave的結構體儲存在s中。然後判斷s->u.slave->master是否存在,如果存在的話就跳過繼續尋找。這個變數就代表了這個slave裝置已經設定了它所對應的master。那麼這個變數是在哪設定的呢?繼續看這個函式,就在後面設定了:s->u.slave->master= m; 。
這兩個list_for_each_entry的最終結果就是找到master裝置和第一個沒有設定master的slave裝置。
然後將這個slave裝置的u.slave->master設定成找到的master裝置m,然後呼叫m的u.master->atach(s)函式,這個函式就是在init_camera_struct函式最後設定的:cam->self->u.master= &mxc_v4l2_master;
static struct v4l2_int_master mxc_v4l2_master = {
.attach = mxc_v4l2_master_attach,
.detach = mxc_v4l2_master_detach,
};
所以最終會呼叫到mxc_v4l2_master_attach函式,同時這個函式的行參是這個slave裝置s。
static int mxc_v4l2_master_attach(struct v4l2_int_device *slave)
{
cam_data *cam = slave->u.slave->master->priv;
struct v4l2_format cam_fmt;
int i;
struct sensor_data *sdata = slave->priv;
pr_debug("In MVC: mxc_v4l2_master_attach\n");
pr_debug(" slave.name = %s\n", slave->name);
pr_debug(" master.name = %s\n", slave->u.slave->master->name);
if (slave == NULL) {
pr_err("ERROR: v4l2 capture: slave parameter not valid.\n");
return -1;
}
if (sdata->csi != cam->csi) {
pr_debug("%s: csi doesn't match\n", __func__);
return -1;
}
cam->sensor = slave;
if (cam->sensor_index < MXC_SENSOR_NUM) {
cam->all_sensors[cam->sensor_index] = slave;
cam->sensor_index++;
} else {
pr_err("ERROR: v4l2 capture: slave number exceeds "
"the maximum.\n");
return -1;
}
for (i = 0; i < cam->sensor_index; i++) {
vidioc_int_dev_exit(cam->all_sensors[i]);
vidioc_int_s_power(cam->all_sensors[i], 0);
}
cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vidioc_int_g_fmt_cap(cam->sensor, &cam_fmt);
/* Used to detect TV in (type 1) vs. camera (type 0)*/
cam->device_type = cam_fmt.fmt.pix.priv;
/* Set the input size to the ipu for this device */
cam->crop_bounds.top = cam->crop_bounds.left = 0;
cam->crop_bounds.width = cam_fmt.fmt.pix.width;
cam->crop_bounds.height = cam_fmt.fmt.pix.height;
/* This also is the max crop size for this device. */
cam->crop_defrect.top = cam->crop_defrect.left = 0;
cam->crop_defrect.width = cam_fmt.fmt.pix.width;
cam->crop_defrect.height = cam_fmt.fmt.pix.height;
/* At this point, this is also the current image size. */
cam->crop_current.top = cam->crop_current.left = 0;
cam->crop_current.width = cam_fmt.fmt.pix.width;
cam->crop_current.height = cam_fmt.fmt.pix.height;
return 0;
}
在這個函式中設定了cam->sensor= slave;然後又設定了crop的一些引數。而這些crop引數都是根據slave結構體來設定的。在init_camera_struct函式中只是給了這些crop引數一些初始預設值,在這個函式中才是根據真正從slave裝置中獲取到的引數來填充crop引數。
(二)slave的註冊過程
3. 以上是master的註冊過程,再來看看slave的註冊過程。以ov5640.c為例:
3.1 module_i2c_driver(ov5640_i2c_driver);
static struct i2c_driver ov5640_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "ov564x",
},
.probe = ov5640_probe,
.remove = ov5640_remove,
.id_table = ov5640_id,
};
之後就會呼叫到ov5640_probe函式。
3.2 ov5640_probe函式
static int ov5640_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pinctrl *pinctrl;
struct device *dev = &client->dev;
int retval;
u8 chip_id_high, chip_id_low;
/* ov5640 pinctrl */
pinctrl = devm_pinctrl_get_select_default(dev);
if (IS_ERR(pinctrl)) {
dev_err(dev, "setup pinctrl failed\n");
return PTR_ERR(pinctrl);
}
/* request power down pin */
pwn_gpio = of_get_named_gpio(dev->of_node, "pwn-gpios", 0);
if (!gpio_is_valid(pwn_gpio)) {
dev_err(dev, "no sensor pwdn pin available\n");
return -ENODEV;
}
retval = devm_gpio_request_one(dev, pwn_gpio, GPIOF_OUT_INIT_HIGH,
"ov5640_pwdn");
if (retval < 0)
return retval;
/* request reset pin */
rst_gpio = of_get_named_gpio(dev->of_node, "rst-gpios", 0);
if (!gpio_is_valid(rst_gpio)) {
dev_err(dev, "no sensor reset pin available\n");
return -EINVAL;
}
retval = devm_gpio_request_one(dev, rst_gpio, GPIOF_OUT_INIT_HIGH,
"ov5640_reset");
if (retval < 0)
return retval;
/* Set initial values for the sensor struct. */
memset(&ov5640_data, 0, sizeof(ov5640_data));
ov5640_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
if (IS_ERR(ov5640_data.sensor_clk)) {
dev_err(dev, "get mclk failed\n");
return PTR_ERR(ov5640_data.sensor_clk);
}
retval = of_property_read_u32(dev->of_node, "mclk",
&ov5640_data.mclk);
if (retval) {
dev_err(dev, "mclk frequency is invalid\n");
return retval;
}
retval = of_property_read_u32(dev->of_node, "mclk_source",
(u32 *) &(ov5640_data.mclk_source));
if (retval) {
dev_err(dev, "mclk_source invalid\n");
return retval;
}
retval = of_property_read_u32(dev->of_node, "csi_id",
&(ov5640_data.csi));
if (retval) {
dev_err(dev, "csi_id invalid\n");
return retval;
}
clk_prepare_enable(ov5640_data.sensor_clk);
ov5640_data.io_init = ov5640_reset;
ov5640_data.i2c_client = client;
ov5640_data.pix.pixelformat = V4L2_PIX_FMT_YUYV;
ov5640_data.pix.width = 640;
ov5640_data.pix.height = 480;
ov5640_data.streamcap.capability = V4L2_MODE_HIGHQUALITY |
V4L2_CAP_TIMEPERFRAME;
ov5640_data.streamcap.capturemode = 0;
ov5640_data.streamcap.timeperframe.denominator = DEFAULT_FPS;
ov5640_data.streamcap.timeperframe.numerator = 1;
ov5640_regulator_enable(&client->dev);
ov5640_reset();
ov5640_power_down(0);
retval = ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high);
if (retval < 0 || chip_id_high != 0x56) {
clk_disable_unprepare(ov5640_data.sensor_clk);
pr_warning("camera ov5640 is not found\n");
return -ENODEV;
}
retval = ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);
if (retval < 0 || chip_id_low != 0x40) {
clk_disable_unprepare(ov5640_data.sensor_clk);
pr_warning("camera ov5640 is not found\n");
return -ENODEV;
}
ov5640_power_down(1);
clk_disable_unprepare(ov5640_data.sensor_clk);
ov5640_int_device.priv = &ov5640_data;
retval = v4l2_int_device_register(&ov5640_int_device);
pr_info("camera ov5640 is found\n");
return retval;
}
這個函式主要是設定ov5640_data結構體的一些值,通過幾個of函式來獲取到的資訊填充到這個結構體中。然後最後呼叫到了v4l2_int_device_register這個函式。這個函式同樣在上面的mxc_v4l2_probe中呼叫了。不同的是註冊的ov5640_int_device結構體,在mxc_v4l2_probe中註冊的是cam->self結構體,這兩個結構體都是v4l2_int_device型別的:
static struct v4l2_int_device ov5640_int_device = {
.module = THIS_MODULE,
.name = "ov564x",
.type = v4l2_int_type_slave,
.u = {
.slave = &ov5640_slave,
},
};
可以看到,這個ov5640_int_device結構體中設定的type型別是v4l2_int_type_slave,同時u設定的是slave。再次對比init_camera_struct函式中master是怎麼設定的:
cam->self = kmalloc(sizeof(struct v4l2_int_device), GFP_KERNEL);
cam->self->module = THIS_MODULE;
sprintf(cam->self->name, "mxc_v4l2_cap%d", cam->csi);
cam->self->type = v4l2_int_type_master;
cam->self->u.master = &mxc_v4l2_master;
然後呼叫v4l2_int_device_register函式,在裡面通過list_add將這個裝置新增到int_list連結串列中,所以這個連結串列中的裝置既包括master裝置,同時也包括slave裝置。在這裡,每新增進去一個裝置,這個int_list連結串列中都會增加一個slave裝置,同時master裝置就是cam->self結構體。從這個連結串列中取出裝置的話,需要根據這個type型別來區分。
之後在v4l2_int_device_register函式繼續呼叫v4l2_int_device_try_attach_all()函式,這個函式在上面已經分析很清楚了,會從int_list連結串列找到master裝置和第一個沒有設定master的slave裝置,然後將這個slave裝置的u.slave->master設定成找到的master裝置m,然後呼叫m的u.master->attach(s)函式,完成匹配過程。
這個v4l2_int_device_try_attach_all()函式在master裝置或者slave設備註冊進連結串列的時候,都會呼叫到,都會互相去匹配。
4.當在應用程式執行open的時候,再次通過vidioc_int_g_fmt_cap函式獲取了cam->sensor的資訊,此時經過上面那些步驟,cam->sensor已經指向了找到的slave裝置。重新設定crop的一些值,在這裡通過ipu_csi_set_window_size和ipu_csi_set_window_pos等操作將這些值寫到了暫存器中。
關於這個流程,我畫了一個思維導圖來輔助理解:
相關推薦
2. master和slave的匹配過程
(一)master的註冊過程 1. 首先來看看master的註冊過程,在mxc_v4l2_capture.c檔案中,從module_init(camera_init)函式開始,在camera_init函式中通過 err= platform_driver_register(&
面試題(redis master和slave是怎麼實現資料同步的)
Redis的主從同步機制可以確保redis的master和slave之間的資料同步。按照同步內容的多少可以分為全同步和部分同步;按照同步的時機可以分為slave剛啟動時的初始化同步和正常執行過程中的資料修改同步;本文將對這兩種機制的流程進行分析。 全備份過程中,在sla
Jenkins : 安裝 master 和 slave
目錄 Jenkins 是一個可擴充套件的持續整合引擎。主要用於持續、自動地構建、測試軟體專案。本文介紹在 windows 平臺上安裝 Jenkins master 和 slave。 安裝 master 請從 Jenkins 的官網下載安裝包,直接執行,一路 "next" 就可以了。安裝包執行完成後會啟動你機
輸出redis cluster 主從的對應關係,如果同一個主從關係的master和slave在同一個node節點上,在輸出的對應關係末尾輸出提示
需求:輸出redis cluster 主從的對應關係,如果同一個主從關係的master和slave在同一個node節點上,在輸出的對應關係末尾輸出提示。 為什麼會有這樣的需求呢?在重新搭建redis cluster的時候,建立叢集期間,發現: redis-trib.rb create --
jenkins 配置master 和 slave實踐
今天在自己本機上配置了jenkins主從節點,現記錄如下: 1.瀏覽器開啟http://localhost:8080/jenkins/,登入jenkins後臺,點選系統管理,進入系統管理介面 2. 點選 “管理節點” ,進入節點管理介面 3. 點選 “新建節點”
閱讀Hadoop 原始碼最重要的就是明白rpc機制,client與master,master和slave的通訊
本部落格微信公共賬號:hadoop123(微訊號為:hadoop-123),分享hadoop技術內幕,hadoop最新技術進展,釋出hadoop相關職位和求職資訊,hadoop技術交流聚會、講座以及會議等。二維碼如下: 個人談談閱讀hadoop原始碼的經驗。
mysql master 和slave的replication
.主從mysql server的工作原理:(如圖及其過程分析) 過程: Mysql的複製(replication)是一個非同步的複製,從一個Mysql instace(稱之為Master)複製到另一個Mysql instance(稱之Slave)。實現整個複製操作主
WebService—CXF整合Spring實現接口發布和調用過程2
creat tco win [] exception onf del tac xml配置 一、CXF整合Spring實現接口發布 發布過程如下: 1、引入jar包(基於maven管理) <!-- cxf --> <dependency>
2. MySql更新語句執行過程redolog和binlog
一句update的語句:Update T set C=c+1 where id = 2; 和查詢語句一樣會走一遍如下的流程: 與查詢語句不一樣的是,更新語句設計上有兩個重要的模組:redo log 和 binlog 一、重要日誌模組: redo log
Jenkins : 分佈安裝 master主 和 slave從
目錄 安裝 master 安裝 slave 設定 master 與 slave 的通訊方式 新增 slave 配置 在 salve 上安裝 jre 安裝並配置 Jenkins salve Jenkins 是一個可擴充套件的持續整合引擎。主要用於持續、自動地構建、測試軟體專案。本文介紹在
python3.6 和python 2.7版本安裝pycrypto過程及問題(不需要安裝vs)
方式 導入模塊 ges pac scrip http color sta 下一步 python安裝pycrypto真的是踩到很多坑,說起來一把辛酸淚,好在最後成功解決了,總結了一下遇到的問題,寫出來與大家共享 首先要明白的是crypto和pycrypto是兩個不同的模塊
Linux裝置和驅動的匹配過程
一、bus_type結構體及涉及的函式: (1)bus_type結構體 struct bus_type { const char *name; const char *dev_name; struct device *dev_root; struct
我自己配置Mysql Master/Slave的過程
B伺服器上的www.sns.com.err [[email protected] var]# cat www.sns.com.err 080822 15:00:01 mysqld started 080822 15:00:01 [Warning] The syntax for replicatio
iOS稽核5.2.1和3.2拒絕解決過程
The seller and company names associated with your app do not reflect the name of a financial institution in the app or its metadata, as required by Guidel
Qt入門之基礎篇 ( 二 ) :Qt項目建立、編譯、運行和發布過程解析
qt 5 對話 讓我 進度 qmake ctr deploy 設定 設置 轉載請註明出處:CN_Simo。 題解: 本篇內容主講Qt應用從創建到發布的整個過程,旨在幫助讀者能夠快速走進Qt的世界。 本來計劃是講解Qt源碼靜態編譯,如此的話讀者可能並不能清楚地知
[C#學習筆記之異步編程模式2]BeginInvoke和EndInvoke方法 (轉載)
cti otf 函數返回 編程模式 catch 數值 gin 單線程 blog 為什麽要進行異步回調?眾所周知,普通方法運行,是單線程的,如果中途有大型操作(如:讀取大文件,大批量操作數據庫,網絡傳輸等),都會導致方法阻塞,表現在界面上就是,程序卡或者死掉,界面元素不動了,
【轉】1.2 CDN的基本工作過程
過程 業務 用戶服 fit lan 進行 發的 傳統 告訴 1.2 CDN的基本工作過程 使用CDN會極大地簡化網站的系統維護工作量,網站維護人員只需將網站內容註入CDN的系統,通過CDN部署在各個物理位置的服務器進行全網分發,就可以實現跨運營商、跨地域的用戶覆蓋。由於C
EF執行SQL語句和存儲過程
div spa rip mman code rom {0} base result EF雖然使用對象化的方式避免了我們寫SQL,但是對於部分SQL,例如需要復雜的查詢、執行插入和刪除等可以操作,直接執行SQL可以減少減少性能上的損失。 使用EF執行SQL可以通過Execut
1.3—一個典型的JAVA程序的編寫和運行過程
java程序JAVA語言應用範圍 桌面應用編程 WEB客戶端編程 WEB服務器編程 手機編程 機器人編程 第一個JAVA程序JAVA開發環境搭建 下載:Download J2SDK (Java 2 Software Development Kit) from http://java.sun.com 安裝
ELF文件的格式和加載過程
pos ram glob ios sel 執行 log iar section http://blog.csdn.net/lingfong_cool/article/details/7832896 (一) ELF 文件的格式 ELF 文件類型 (1) 可重定位文