Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59810
  • 博文数量: 25
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 101
  • 用 户 组: 普通用户
  • 注册时间: 2014-06-07 19:38
文章分类

全部博文(25)

文章存档

2014年(25)

我的朋友

分类: 嵌入式

2014-06-10 09:16:36

   最近要在开源的平板上做Linux的项目,需要用到视频流服务器,首选肯定是MJPG-Streamer,但是按照之前的调试记录发现有问题,总是报一个I2C的错误,错误信息如下所示:

[plain] view plaincopy
  1. MJPG-streamer [81]: starting application  
  2.   
  3. MJPG Streamer Version.: 2.0  
  4. MJPG-streamer [81]: MJPG Streamer Version.: 2.0  
  5.   
  6.   
  7.  i: Using V4L2 device.: /dev/video0  
  8. MJPG-streamer [81]: Using V4L2 device.: /dev/video0  
  9.   
  10.   
  11.  i: Desired Resolution: 640 x 480  
  12. MJPG-streamer [81]: Desired Resolution: 640 x 480  
  13.   
  14.   
  15.  i: Frames Per Second.: 5  
  16. MJPG-streamer [81]: Frames Per Second.: 5  
  17.   
  18.   
  19.  i: Format............: YUV  
  20. MJPG-streamer [81]: Format............: YUV  
  21.   
  22.   
  23.  i: JPEG Quality......: 80  
  24. MJPG-streamer [81]: JPEG Quality......: 80  
  25.   
  26.   
  27. [   37.075219] [i2c2] incomplete xfer (0x20)  
  28. [   37.079274] [CSI_ERR][GC0308]sensor_write error!  
  29. [   37.087464] [CSI_ERR][GC0308]sensor_write_err! reg_num = $? value = ?  
  30. [   37.094063] [CSI]buffer_setup, buffer count=4, size=614400  
  31.   
  32. Unable to map buffer: Invalid argument  
  33.  Init v4L2 failed !! exit fatal   
  34.  i: init_VideoIn failed  
  35. MJPG-streamer [81]: init_VideoIn failed  
        实际上这里面包含两个错误,最后四行报的错误很明显,就是mmap的时候参数出错了。


[plain] view plaincopy
  1. Unable to map buffer: Invalid argument  
  2.  Init v4L2 failed !! exit fatal   
  3.  i: init_VideoIn failed  
  4. MJPG-streamer [81]: init_VideoIn failed  
        在plugins/input_uvc/v4l2uvc.c源码的第200行static int init_v4l2(struct vdIn *vd)函数中找到mmap函数:


  1.  vd->mem[i] = mmap(0 /* start anywhere */ ,  
  2.                    //vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,  
  3. /* FSPAD_702 Linux added by LeeSheen */  
  4.                    vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,  
  5.                    vd->buf.m.offset);  

        在第96行我们找到 init_v4l2函数中open函数:


  1. if ((vd->fd = open(vd->videodevice, O_RDWR)) == -1) {  
  2.   perror("ERROR opening V4L interface");  
  3.   return -1;  
  4. }  

        我们发现打开的时候是使用读写方式打开的,而mmap的时候参数是以只读的方式打开的。在man手册里描述mmap()函数有这么一段话:


[plain] view plaincopy
  1. The  prot argument describes the desired memory protection of the mapping (and must not conflict with the open mode of the file).  It is either PROT_NONE or the bitwise OR of one or more of the following flags:  
        所以看的出我们用读写打开,mmap的时候也必须以读写的方式映射,所以mmap函数应该改成:



  1. vd->mem[i] = mmap(0 /* start anywhere */ ,  
  2. * FSPAD_702 Linux added by LeeSheen */  
  3.                   vd->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd,  
  4.                   vd->buf.m.offset);  
改完之后测试,发现mmap的错误消失了,但I2C的错误仍然存在。错误信息是:



[plain] view plaincopy
  1. [   37.075219] [i2c2] incomplete xfer (0x20)  
  2. [   37.079274] [CSI_ERR][GC0308]sensor_write error!  
  3. [   37.087464] [CSI_ERR][GC0308]sensor_write_err! reg_num = $? value = ?  
        I2C跟到驱动的sensor_write函数中,位置是drivers/media/video/sun5i_csi/device/gc0308.c:



  1. static int sensor_write(struct v4l2_subdev *sd, unsigned char *reg,  
  2.         unsigned char *value)  
  3. {  
  4.     struct i2c_client *client = v4l2_get_subdevdata(sd);  
  5.     struct i2c_msg msg;  
  6.     unsigned char data[REG_STEP];  
  7.     int ret,i;  
  8.       
  9.     for(i = 0; i < REG_ADDR_STEP; i++)  
  10.             data[i] = reg[i];  
  11.     for(i = REG_ADDR_STEP; i < REG_STEP; i++)  
  12.             data[i] = value[i-REG_ADDR_STEP];  
  13.       
  14.     msg.addr = client->addr;  
  15.     msg.flags = 0;  
  16.     msg.len = REG_STEP;  
  17.     msg.buf = data;  
  18.   
  19.     //printk("addr = %d, len = %d, buf = %d, %d, %d\n", msg.addr, msg.len, msg.buf[0], msg.buf[1], msg.buf[2]);  
  20.       
  21.     ret = i2c_transfer(client->adapter, &msg, 1);  
  22.     if (ret > 0) {  
  23.         ret = 0;  
  24.     }  
  25.     else if (ret < 0) {  
  26.         csi_dev_err("sensor_write error!\n");  
  27.     }  
  28.     return ret;  
  29. }  
        发现就是I2C写函数出错了,可是安卓中摄像头是正常的,所以猜想问题还是应该出现摄像头初始化上,我看了看csi驱动,发现了这样一个函数:



  1. static int internal_s_input(struct csi_dev *dev, unsigned int i)  
  2. {  
  3.     struct v4l2_control ctrl;  
  4.     int ret;  
  5.       
  6.     if (i > dev->dev_qty-1) {  
  7.         csi_err("set input error!\n");  
  8.         return -EINVAL;  
  9.     }  
  10.       
  11.     if (i == dev->input)  
  12.         return 0;  
  13.       
  14.     csi_dbg(0,"input_num = %d\n",i);  
  15.   
  16.     if(dev->input != -1) {  
  17.         /*Power down current device*/  
  18.         ret = v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_ON);  
  19.         if(ret < 0)  
  20.             goto altend;  
  21.     }  
  22.     /* Alternate the device info and select target device*/  
  23.   ret = update_ccm_info(dev, dev->ccm_cfg[i]);  
  24.   if (ret < 0)  
  25.     {  
  26.         csi_err("Error when set ccm info when selecting input!,input_num = %d\n",i);  
  27.         goto recover;  
  28.     }  
  29.       
  30.     /* change the csi setting */  
  31.     csi_dbg(0,"dev->ccm_info->vref = %d\n",dev->ccm_info->vref);  
  32.     csi_dbg(0,"dev->ccm_info->href = %d\n",dev->ccm_info->href);  
  33.     csi_dbg(0,"dev->ccm_info->clock = %d\n",dev->ccm_info->clock);  
  34.     csi_dbg(0,"dev->ccm_info->mclk = %d\n",dev->ccm_info->mclk);  
  35.       
  36.     dev->csi_mode.vref       = dev->ccm_info->vref;  
  37.     dev->csi_mode.href       = dev->ccm_info->href;  
  38.     dev->csi_mode.clock      = dev->ccm_info->clock;  
  39.     
  40.     csi_clk_out_set(dev);  
  41.       
  42.     /* Initial target device */  
  43.     ret = v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_OFF);  
  44.     if (ret!=0) {  
  45.       csi_err("sensor standby off error when selecting target device!\n");  
  46.       goto recover;  
  47.     }  
  48.       
  49.     ret = v4l2_subdev_call(dev->sd,core, init, 0);  
  50.     if (ret!=0) {  
  51.         csi_err("sensor initial error when selecting target device!\n");  
  52.         goto recover;  
  53.     }  
  54.       
  55.     /* Set the initial flip */  
  56.     ctrl.id = V4L2_CID_VFLIP;  
  57.     ctrl.value = dev->vflip;  
  58.     ret = v4l2_subdev_call(dev->sd,core, s_ctrl, &ctrl);  
  59.     if (ret!=0) {  
  60.         csi_err("sensor sensor_s_ctrl V4L2_CID_VFLIP error when vidioc_s_input!input_num = %d\n",i);  
  61.     }  
  62.       
  63.     ctrl.id = V4L2_CID_HFLIP;  
  64.     ctrl.value = dev->hflip;  
  65.     ret = v4l2_subdev_call(dev->sd,core, s_ctrl, &ctrl);  
  66.     if (ret!=0) {  
  67.         csi_err("sensor sensor_s_ctrl V4L2_CID_HFLIP error when vidioc_s_input!input_num = %d\n",i);  
  68.     }  
  69.       
  70.     dev->input = i;  
  71.   ret = 0;  
  72.     
  73.     ……  
        果然,驱动中把本应该写在open里函数初始化流程写到了vidioc_s_input函数中,那下面的步骤就简单了,要么就是改驱动,要么就是改MJPG-Streamer,在MJPG-Streamer中只需要加一个ioctl函数即可,所以我们选择修改MJPG-Streamer。


        在MJPG-Streamer源码中plugins/input_uvc/v4l2uvc.c第99行,init_v4l2函数中,open函数之后,添加下面代码:


  1. /* FSPAD_702 Linux added by LeeSheen */  
  2. #if 1  
  3.   
  4.   struct v4l2_input inp;  
  5.   
  6.   inp.index = 0;  
  7.   
  8.   
  9.    if (-1 == ioctl(vd->fd, VIDIOC_S_INPUT, &inp))  
  10.        printf("VIDIOC_S_INPUT error\n");  
  11. #endif  

 再次运行,果然没有I2C传输错误的提示了。



阅读(2331) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~