linux 線上調試網路攝影機驅動__linux

來源:互聯網
上載者:User

    在使用嵌入式linux裝置做點到點之間的映像傳輸,比如linux平台採集網路攝影機資料,然後通過wifi或是藍芽等裝置將映像資料發送到手機端,最後使用手機顯示出映像。影像處理和映像傳輸是在應用程式層完成,在應用程式層寫代碼和調試代碼都是比較容易的。但是如果需要調試網路攝影機驅動的一些參數,涉及到驅動層的東西就會比較的麻煩。在我使用的這個平台,假如我要調試網路攝影機驅動gc0308的寄存器,它需要每改一次寄存器值然後重現燒如一次韌體,最後再看映像效果,這樣的調試方法是在會讓人奔潰。

    其實,在linux的驅動中,它已經提供了一個調試v4l2 裝置的介面。在linux原始碼中,我們可以看到如下的定義:

struct v4l2_subdev_core_ops {int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);int (*log_status)(struct v4l2_subdev *sd);int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n,      struct v4l2_subdev_io_pin_config *pincfg);int (*init)(struct v4l2_subdev *sd, u32 val);int (*load_fw)(struct v4l2_subdev *sd);int (*reset)(struct v4l2_subdev *sd, u32 val);int (*s_gpio)(struct v4l2_subdev *sd, u32 val);int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm);int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);#ifdef CONFIG_VIDEO_ADV_DEBUGint (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);int (*s_register)(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg);#endifint (*s_power)(struct v4l2_subdev *sd, int on);int (*interrupt_service_routine)(struct v4l2_subdev *sd,u32 status, bool *handled);int (*subscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh,       struct v4l2_event_subscription *sub);int (*unsubscribe_event)(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub);};
    在v4l2_subdev_core_ops中已經為我們提供了直接操作v4l2 裝置寄存器的介面,只需要我們定義了宏CONFIG_VIDEO_ADV_DEBUG 就可以了。下面就直接介紹該如何配置和設計線上調試。

(1)開啟CONFIG_VIDEO_ADV_DEBUG 宏,該宏直接在linux的設定檔中配置就可以了,入下圖:


使能debug選項就可以了。

    (2)驅動裝置添加操作介面

    在初始化struct v4l2_subdev_core_ops 結構體的時候,添加一個直接讀寄存器和一個寫寄存器的介面,如下:

static const struct v4l2_subdev_core_ops sensor_core_ops = {.g_chip_ident = sensor_g_chip_ident,.g_ctrl = sensor_g_ctrl,.s_ctrl = sensor_s_ctrl,.queryctrl = sensor_queryctrl,.reset = sensor_reset,.init = sensor_init,.s_power = sensor_power,.ioctl = sensor_ioctl,#ifdef CONFIG_VIDEO_ADV_DEBUG.g_register = sensor_g_register,.s_register = sensor_s_register,#endif};
sensor_g_register 是讀取寄存器的值,sensor_s_register 是寫入寄存器的值,這兩個函數的定義如下:

#ifdef CONFIG_VIDEO_ADV_DEBUGstatic int sensor_g_register(struct v4l2_subdev *sd,  struct v4l2_dbg_register *para){int ret;unsigned short reg_addr  = 0;unsigned short reg_value = 0;reg_addr  = para->size;ret = sensor_read(sd,reg_addr,&reg_value);if(ret < 0){printk("sensor_g_register ret=%d; reg_addr=%d; reg_value=%d\n ",ret, reg_addr, reg_value);return ret;}return reg_value;}static int sensor_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *para){int ret;unsigned short reg_addr;unsigned short reg_val;__u32 size;__u64 reg; size = para->size;reg  = para->reg; reg_addr = (unsigned short)size;reg_val  = (unsigned short)reg;ret = sensor_write(sd, reg_addr, reg_val);if(ret < 0){printk("sensor_s_register ret = %d, reg_addr=%d, reg_val=%d\n",ret,reg_addr,reg_val);}return ret;}#endif
上面的代碼需要注意,在資料類型中struct v4l2_dbg_register  除了裝置配對之外,它定義了三個變數,size,reg,val 。在我的平台上val 的值是怎麼都傳輸不到這裡,還有一個問題就是在核心的參數不能夠返回到應用程式層,具體的原因沒有去做更深入的分析。在這裡,為了實現可以調試的功能,我使用了一種投機取巧的方法來實現。我使用size 參數來傳遞地址,使用reg參數來傳遞需要設定的寄存器的值,最後使用函數的傳回值來實現讀取的寄存器值返回到應用程式層。

    (3)應用程式層直接操作寄存器

      下面寫了一個測試小程式,通過ioctl 直接存取上面驅動中實現的介面

/*=============================================================================#     FileName: test.c#         Desc: ioctl to set/get vedio driver register#       Author: licaibiao#      Version: =============================================================================*/#include <stdio.h>#include <fcntl.h>#include <stdlib.h>#include <sys/types.h>#include <sys/ioctl.h>#include <linux/types.h>#include <linux/videodev2.h>#define FILE_VIDEO  "/dev/video0"int open_camera(void){int fd;struct v4l2_input inp;fd = open(FILE_VIDEO, O_RDWR | O_NONBLOCK,0);if(fd < 0){fprintf(stderr, "%s open err \n", FILE_VIDEO);exit(EXIT_FAILURE);};inp.index = 0;if (-1 == ioctl (fd, VIDIOC_S_INPUT, &inp)){fprintf(stderr, "VIDIOC_S_INPUT \n");}return fd;}//dbg.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;//strcpy(dbg.match.name,"gc0308");//dbg.match.type = V4L2_CHIP_MATCH_I2C_ADDR;//dbg.match.addr = 0x21;void v4l2_write_reg(int fd, int reg_addr, int reg_val){int ret;struct v4l2_dbg_register dbg;dbg.match.type = 4;dbg.match.addr = 1;dbg.size = (__u32)reg_addr;dbg.reg  = (__u64)reg_val;dbg.val = 0;ret = ioctl(fd, VIDIOC_DBG_S_REGISTER, &dbg);if(ret < 0){printf("sensor IOCTL data ERR ret = %d\n",ret);}}int v4l2_read_reg(int fd, int reg_addr){int ret;struct v4l2_dbg_register dbg;dbg.match.type = 4;dbg.match.addr = 1;dbg.size = (__u32)reg_addr;dbg.reg  = 0;dbg.val  = 0;ret = ioctl(fd, VIDIOC_DBG_G_REGISTER, &dbg);if(ret < 0){ printf("sensor IOCTL data ERR reg_addr= %x ret = %d\n",reg_addr,ret);}return ret;}void main(void){int fd;int reg_addr= 0;int reg_val = 0;fd = open_camera();printf("fd = %d\n ",fd);reg_addr = 0x00;reg_val  = v4l2_read_reg(fd,reg_addr);printf(" v4l2 read reg %x value = %x\n",reg_addr,reg_val);reg_addr = 0x0f;reg_val  = v4l2_read_reg(fd,reg_addr);printf(" v4l2 read reg %x value = %x\n",reg_addr,reg_val);reg_addr = 0x0f;reg_val  = 0x00;v4l2_write_reg(fd, reg_addr, reg_val);usleep(10000);reg_addr = 0x0f;reg_val  = v4l2_read_reg(fd,reg_addr);printf(" v4l2 read reg %x value = %x\n",reg_addr,reg_val);close(fd);}
上面的代碼需要注意一下的是struct v4l2_dbg_register 的初始化。我的是linux3.10.65 版本,在videodev2.h中有如下的定義:

/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */#define V4L2_CHIP_MATCH_BRIDGE      0  /* Match against chip ID on the bridge (0 for the bridge) */#define V4L2_CHIP_MATCH_HOST V4L2_CHIP_MATCH_BRIDGE#define V4L2_CHIP_MATCH_I2C_DRIVER  1  /* Match against I2C driver name */#define V4L2_CHIP_MATCH_I2C_ADDR    2  /* Match against I2C 7-bit address */#define V4L2_CHIP_MATCH_AC97        3  /* Match against anciliary AC97 chip */#define V4L2_CHIP_MATCH_SUBDEV      4  /* Match against subdev index */struct v4l2_dbg_match {__u32 type; /* Match type */union {     /* Match this chip, meaning determined by type */__u32 addr;char name[32];};} __attribute__ ((packed));struct v4l2_dbg_register {struct v4l2_dbg_match match;__u32 size;/* register size in bytes */__u64 reg;__u64 val;} __attribute__ ((packed));
它這裡需要做裝置的匹配,在我的平台,我分別使用過chip ID,I2C driver name,I2C address來匹配,都沒能實現裝置的匹配匹配,只能使用V4L2_CHIP_MATCH_SUBDEV 來匹配,但是在我的交叉編譯工具鏈中,前面三種方法它都定義了,唯獨V4L2_CHIP_MATCH_SUBDEV沒有定義,所以在上面的測試程式中我直接將   dbg.match.type = 4;

測試小程式的執行結果如下:

/tmp # ./test  v4l2 read reg 0 value = 9b v4l2 read reg f value = 10 v4l2 read reg f value = 0/tmp # /tmp # 
    (4)將測試程式移植到映像傳輸工程中

    實現思路是這樣的,在映像傳輸的進程中,建立一個線程,線上程中建立一個訊息佇列,用來接收操作指令。另外再寫一個小程式,往訊息佇列中發送操作指令,這樣就可以即時的看到映像的效果。接收端的程式碼如下:

 /**********************************************************************   licaibiao add interface to debug v4l2 camera*********************************************************************/void v4l2_write_reg(int fd, int reg_addr, int reg_val){    int ret;    struct v4l2_dbg_register dbg;        dbg.match.type = 4;    dbg.match.addr = 1;    dbg.size = (__u32)reg_addr;    dbg.reg  = (__u64)reg_val;    dbg.val = 0;      ret = ioctl(fd, VIDIOC_DBG_S_REGISTER, &dbg);     if(ret < 0)     {         printf("!!!!! sensor write data ERR !!!!! ret = %d\n",ret);     }     else     {         printf("write register 0x%x value = 0x%x \n", reg_addr, reg_val);     } }  void v4l2_read_reg(int fd, int reg_addr) {     int ret;     struct v4l2_dbg_register dbg;          dbg.match.type = 4;     dbg.match.addr = 1;     dbg.size = (__u32)reg_addr;     dbg.reg  = 0;     dbg.val  = 0;     ret = ioctl(fd, VIDIOC_DBG_G_REGISTER, &dbg);     if(ret < 0)     {          printf("sensor IOCTL data ERR reg_addr= %x ret = %d\n",reg_addr,ret);     }     else     {         printf("read register 0x%x value = 0x%x \n", reg_addr, ret);     } }   struct msg_st {     long int msg_type;     char text[BUFSIZ]; };  void *main_v4l2_debug_camera(void* arg) {     int running = 1;     int msgid = -1;     int reg_addr  = 0;     int reg_val = 0;     int len = 0;     int i = 0;     int fd;     struct msg_st data;     long int msgtype = 0;           fd = *(int*)arg;          printf("\n\n\n enter main_v4l2_debug_camera thread \n\n\n");          msgid = msgget((key_t)1234, 0666 | IPC_CREAT);     if(msgid == -1)     {         fprintf(stderr, "msgget failed with error: %d\n", errno);         //exit(EXIT_FAILURE);     }     while(running)     {         len = msgrcv(msgid, (void*)&data, BUFSIZ, msgtype, 0);         if(len < 0)         {             fprintf(stderr, "msgrcv failed with errno: %d\n", errno);             if(msgctl(msgid, IPC_RMID, 0))             {                 printf("remove msg ERR \n ");             }             pthread_exit(0);             //exit(EXIT_FAILURE);         }else         {                if(strncmp(data.text, "end", 3) == 0)                       {                 running = 0;                 if(msgctl(msgid, IPC_RMID, 0))                 {                     printf("remove msg ERR \n ");                 }                 pthread_exit(0);             }                          for(i=1; i<len-1; i++)             {                 if(((data.text[i] >= '0')&&(data.text[i] <= '9'))                     ||((data.text[i] >= 'A')&&(data.text[i] <= 'F'))                         ||((data.text[i] >= 'a')&&(data.text[i] <= 'f'))                             ||(data.text[i] == ' '))                 {                    }else                 {                        printf("1plase enter: read/write: reg_addr: reg_value e.g. w 12 34 \n");                     goto err;                 }             }                               if((data.text[0]=='W')||(data.text[0]=='w'))             {                              if((data.text[1] != ' ')||(data.text[4] != ' '))                 {                     printf("2plase enter: read/write: reg_addr: reg_value e.g. w 12 34 \n");                     goto err;                 }                                  sscanf(&data.text[2], "%x", &reg_addr);                  sscanf(&data.text[5], "%x", &reg_val);                   //printf("write: addr = %x val = %x\n",reg_addr, reg_val);                 v4l2_write_reg(fd, reg_addr,reg_val);             }              if((data.text[0]=='R')||(data.text[0]=='r'))             {                              if(data.text[1] != ' ')                 {                     printf("3plase enter: read/write: reg_addr: reg_value e.g. w 12 34 \n");                     goto err;                 }                                                sscanf(&data.text[2], "%x", &reg_addr);                  //printf("read : addr = %x \n",reg_addr);                 v4l2_read_reg(fd, reg_addr);             }         } err:     i = i;     } }  /********************************************************************/
    添加線程:

if(pthread_create(&V4L2_Contect->msg_id, NULL, main_v4l2_debug_camera, &V4L2_Contect->mCamFd)){    printf("V4L2 pthread create ERR !!!!\n");} 

發送訊息佇列端的代碼實現如下:

/*=============================================================================#     FileName: msg_test.c#         Desc: send message to camera process to ioctl v4l2#       Author: Licaibiao#      Version: #   LastChange: 2017-01-20 #      History:=============================================================================*/#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/msg.h>#include <errno.h>#define MAX_TEXT 512struct msg_st{long int msg_type;char text[MAX_TEXT];};int main(){int running = 1;struct msg_st data;char buffer[BUFSIZ];int msgid = -1;int len;msgid = msgget((key_t)1234, 0666 | IPC_CREAT);if(msgid == -1){fprintf(stderr, "msgget failed with error: %d\n", errno);exit(EXIT_FAILURE);}printf("Enter cmd format,R/W,addr,val:  r/w 12 33 \n");while(running){printf("Enter cmd : ");fgets(buffer, BUFSIZ, stdin);data.msg_type = 1;    strcpy(data.text, buffer);    len = strlen(data.text);if(msgsnd(msgid, (void*)&data, len, 0) == -1){fprintf(stderr, "msgsnd failed\n");exit(EXIT_FAILURE);}if(strncmp(buffer, "end", 3) == 0)running = 0;usleep(100000);}exit(EXIT_SUCCESS);}

運行結果如下:

/tmp # ./send Enter cmd format,R/W,addr,val:  r/w 12 33 Enter cmd : r 14read register 0x14 value = 0x10 Enter cmd : w 14 13write register 0x14 value = 0x13 Enter cmd : endmsgrcv failed with errno: 43
從上面的結果中,可以看出寄存器的讀取和設定已經正常,但是在我的平台中執行:msgctl(msgid, IPC_RMID, 0) 刪除訊息佇列的時候會出現問題,導致不能正常退出,該問題後續有時間再定位。


    綜上,已經可以實現V4L2 點對點傳輸映像的線上調試了,還遺留幾個問題後續定位。

    (一)struct v4l2_dbg_register 中的val變數值不能從應用程式層傳輸到驅動層。

    (二)struct v4l2_dbg_register 中的變數值不能從驅動層反饋回應用程式層。

    (三)應用程式層結束應用時刪除訊息佇列出錯。


    這就是最近在弄的調試介面,記錄下來方便大家參考,同時對於遺留問題,如果有哪位朋友有解決方案,歡迎一起討論。

                                                                                                                                                                                                         

                                                                                                                                                                                2017.01.20













聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.