在使用嵌入式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_DEBUG
static 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 = 9bv4l2 read reg f value = 10v4l2 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 512
struct 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 14
read register 0x14 value = 0x10
Enter cmd : w 14 13
write register 0x14 value = 0x13
Enter cmd : end
msgrcv failed with errno: 43

从上面的结果中,可以看出寄存器的读取和设置已经正常,但是在我的平台中执行:msgctl(msgid, IPC_RMID, 0) 删除消息队列的时候会出现问题,导致不能正常退出,该问题后续有时间再定位。

综上,已经可以实现V4L2 点对点传输图像的在线调试了,还遗留几个问题后续定位。

(一)struct v4l2_dbg_register 中的val变量值不能从应用层传输到驱动层。

(二)struct v4l2_dbg_register 中的变量值不能从驱动层反馈回应用层。

(三)应用层结束应用时删除消息队列出错。

这就是最近在弄的调试接口,记录下来方便大家参考,同时对于遗留问题,如果有哪位朋友有解决方案,欢迎一起讨论。

2017.01.20

linux 在线调试摄像头驱动相关推荐

  1. Linux 下摄像头驱动支持情况

    http://eatdrinkmanwoman.spaces.live.com/blog/cns!97719476F5BAEDA4!1336.entry http://weijb0606.blog.1 ...

  2. linux下摄像头驱动分类

    在Windows下,摄像头驱动由厂商开发并提供.但在Linux下,因商业利益有限,只有极少厂商愿意提供摄像头驱动支持.这并不妨碍Linux下摄像头的使用--广大第三方志愿者维护着大大小小的驱动.之前做 ...

  3. linux UVC摄像头驱动 简介

    1. 如何判断Camera是否为UVC Camera Linux UVC driver(uvc) 该驱动适用于符合USB视频类(USB Video Class)规范的摄像头设备,它包括V4L2内核设备 ...

  4. STM32MP157 Linux系统移植开发篇17:Linux内核摄像头驱动移植

    本文章为<STM32MP157 Linux系统移植开发篇>系列中的一篇,笔者使用的开发平台为华清远见FS-MP1A开发板(STM32MP157开发板).stm32mp157是ARM双核,2 ...

  5. linux usb摄像头 源码,Linux USB摄像头驱动实现源码分析

    Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组 ...

  6. Linux CMOS摄像头驱动

    1.CMOS摄像头原理 摄像头数据流向:自然景观 > 摄像头模块 > 接口 > S3C2440摄像头控制器 > LCD ov7740(摄像头模块) 输入信号:自然景观等的模拟信 ...

  7. linux mipi摄像头驱动,VS-RK3399 在linux系统下面调试Mipi camera接口介绍

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 debian系统目前支持Usb camera是没有问题,走UVC功能接口.那么mipi 接口camera和并口接口的camera,在Debian系统怎么设 ...

  8. linux uvc摄像头操作,Linux uvc摄像头驱动初探

    本文基于AM6C平台Linux3.0.8内核. 1.drivers/media/video/uvc/Makefile uvcvideo-objs := uvc_driver.o uvc_queue.o ...

  9. linux uvc 拍照程序,Linux uvc摄像头驱动初探

    本文基于AM6C平台Linux3.0.8内核. 1.drivers/media/video/uvc/Makefile uvcvideo-objs := uvc_driver.o uvc_queue.o ...

最新文章

  1. Hbase0.98的环境搭建
  2. 如何恢复XP系统中原来的Administrator用户
  3. 删除.svn文件夹方法(转)
  4. Ubuntu 创建文件夹时出现:用户名 不在 sudoers 文件中。此事将被报告。
  5. 如何限制用户在某一时间段多次访问接口
  6. linux下面桌面的安装
  7. Two sum 二刷
  8. 最全的期货交易术语在这里
  9. 有没有可以干一辈子的工作?
  10. 在线png转换成jpg、jpg转png、各种转
  11. click事件不生效
  12. HTML 与 CSS
  13. Interval数据类型
  14. 下载RoboWare Studio官网登录不上去
  15. rewrite break
  16. R语言时间序列分析之ARIMA模型预测
  17. 基于BSC测试网收益聚合器Beefy协议的编译、测试、部署
  18. 计算器并没有取代数学家,AI也不会取代人类
  19. 数十位行业高管讲述:自动化测试优势及解决的现实问题
  20. Java图形界面设计基础

热门文章

  1. 用linux系统做数字钟,大强学易之易语言实例:简单的易语言桌面电子时钟
  2. VK2C22A替代16C22,是段码低功耗LCD液晶显示驱动芯片/段码液晶驱动IC,44SEG*4COM/40*4,高抗干扰.稳定性强
  3. 上了公众号的“贼船”, 后悔吗?
  4. 申宝股票-煤炭等周期股大跌
  5. Android WebView混合开发实战演习
  6. 光电自动避障小车_智能化搬运的实现 AGV小车无人搬运车
  7. OpenBot--DIY自动驾驶智能小车
  8. 展馆720全景拍摄在线展示制作
  9. 地理位置查询——elasticsearch
  10. 【java毕业设计】基于java+Lucene+Tomcat的搜索引擎设计与实现(毕业论文+程序源码)——搜索引擎