1. 程式人生 > 其它 >裝置驅動-【轉載】v4l2子系統驅動架構 - ioctl

裝置驅動-【轉載】v4l2子系統驅動架構 - ioctl

下一篇:v4l2 應用流程呼叫流程

https://www.cnblogs.com/zhangzhiwei122/p/16156750.html 

 

原文連結:https://blog.csdn.net/lxllinux/article/details/81745452

  

ioctl詳解

進入ioctl都是從cdev->ops->ioctl進入的,一般的驅動cdev都是驅動自己初始化的,在v4l2架構中,cdev都已經初始化完成,不需要驅動開發者來初始化,下面是v4l2的cdev->ops結構體v4l2_fops :

  1.   static const struct file_operations v4l2_fops = {
  2.   .owner = THIS_MODULE,
  3.   .read = v4l2_read,
  4.   .write = v4l2_write,
  5.   .open = v4l2_open,
  6.   .get_unmapped_area = v4l2_get_unmapped_area,
  7.   .mmap = v4l2_mmap,
  8.   .unlocked_ioctl = v4l2_ioctl,
  9.   #ifdef CONFIG_COMPAT
  10.   .compat_ioctl = v4l2_compat_ioctl32,
  11.   #endif
  12.   .release = v4l2_release,
  13.   .poll = v4l2_poll,
  14.   .llseek = no_llseek,
  15.   };

可以看到ioctl有兩個,一個unlocked_ioctl,另一個是compat_ioctl 相容性的,其中的區別這裡不多說,我們來看函式v4l2_ioctl:

  1.   static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  2.   {
  3.   struct video_device *vdev = video_devdata(filp);
  4.   int ret = -ENODEV;
  5.    
  6.   if (vdev->fops->unlocked_ioctl) {
  7.   struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
  8.    
  9.   if (lock && mutex_lock_interruptible(lock))
  10.   return -ERESTARTSYS;
  11.   if (video_is_registered(vdev))
  12.   ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);--正常進入函式|video_device->fops
  13.   if (lock)
  14.   mutex_unlock(lock);
  15.   } else
  16.   ret = -ENOTTY;
  17.    
  18.   return ret;
  19.   }

從上可以看到要呼叫vdev->fops->unlocked_ioctl(filp, cmd, arg);具體驅動實現不同,以davinci平臺為例來介紹,在vpfe_capture.c檔案中,初始化過程中有

        vfd->fops       = &vpfe_fops;
 
  1.   static const struct v4l2_file_operations vpfe_fops = {
  2.   .owner = THIS_MODULE,
  3.   .open = vpfe_open,
  4.   .release = vpfe_release,
  5.   .unlocked_ioctl = video_ioctl2,
  6.   .mmap = vpfe_mmap,
  7.   .poll = vpfe_poll
  8.   };

那麼是要呼叫video_ioctl2:

  1.   long video_ioctl2(struct file *file,
  2.   unsigned int cmd, unsigned long arg)
  3.   {
  4.   return video_usercopy(file, cmd, arg, __video_do_ioctl);--這個函式最終是要呼叫__video_do_ioctl,當然他會做其他事,有興趣的可以去了解
  5.   }
  1.   static long __video_do_ioctl(struct file *file,
  2.   unsigned int cmd, void *arg)
  3.   {
  4.   struct video_device *vfd = video_devdata(file);
  5.   const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;----|video_device->ioctl_ops(注意 video_device這幾個函式的呼叫,要明白他們的作用)
  6.   bool write_only = false;
  7.   struct v4l2_ioctl_info default_info;
  8.   const struct v4l2_ioctl_info *info;
  9.   void *fh = file->private_data;
  10.   struct v4l2_fh *vfh = NULL;
  11.   int dev_debug = vfd->dev_debug;
  12.   long ret = -ENOTTY;
  13.    
  14.   if (ops == NULL) {
  15.   pr_warn("%s: has no ioctl_ops.\n",
  16.   video_device_node_name(vfd));
  17.   return ret;
  18.   }
  19.    
  20.   if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
  21.   vfh = file->private_data;
  22.    
  23.   if (v4l2_is_known_ioctl(cmd)) {------------------------檢查命令是否可用
  24.   info = &v4l2_ioctls[_IOC_NR(cmd)];
  25.    
  26.   if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
  27.   !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
  28.   goto done;
  29.    
  30.   if (vfh && (info->flags & INFO_FL_PRIO)) {
  31.   ret = v4l2_prio_check(vfd->prio, vfh->prio);
  32.   if (ret)
  33.   goto done;
  34.   }
  35.   } else {
  36.   default_info.ioctl = cmd;
  37.   default_info.flags = 0;
  38.   default_info.debug = v4l_print_default;
  39.   info = &default_info;
  40.   }
  41.    
  42.   write_only = _IOC_DIR(cmd) == _IOC_WRITE;
  43.   if (info->flags & INFO_FL_STD) {-----進入正題,這幾個if會判斷cmd的型別,然後呼叫不同的函式,這裡留個尾巴,詳情見下一節
  44.   typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
  45.   const void *p = vfd->ioctl_ops;
  46.   const vidioc_op *vidioc = p + info->u.offset;
  47.   /*呼叫驅動自定義的函式*/
  48.   ret = (*vidioc)(file, fh, arg);------------呼叫video_device->ioctl_ops
  49.   } else if (info->flags & INFO_FL_FUNC) {
  50.   /*呼叫架構定義的函式ops,ops最終還是要呼叫驅動自定義的函式*/
  51.   ret = info->u.func(ops, file, fh, arg);----呼叫video_device->ioctl_ops
  52.   } else if (!ops->vidioc_default) {
  53.   ret = -ENOTTY;
  54.   } else {
  55.   ret = ops->vidioc_default(file, fh,
  56.   vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
  57.   cmd, arg);----呼叫video_device->ioctl_ops->vidioc_default
  58.   }
  59.    
  60.   done:
  61.   if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
  62.   if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
  63.   (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
  64.   return ret;
  65.    
  66.   v4l_printk_ioctl(video_device_node_name(vfd), cmd);
  67.   if (ret < 0)
  68.   pr_cont(": error %ld", ret);
  69.   if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG))
  70.   pr_cont("\n");
  71.   else if (_IOC_DIR(cmd) == _IOC_NONE)
  72.   info->debug(arg, write_only);
  73.   else {
  74.   pr_cont(": ");
  75.   info->debug(arg, write_only);
  76.   }
  77.   }
  78.    
  79.   return ret;
  80.   }

ioctl命令分類

ioctl命令在檔案v4l2-ioctl.c檔案中實現,上面分析的函式__video_do_ioctl就在此檔案中,在分析這個函式的過程中留了個尾巴,下面來分析一下

從上面的分析過程可以看出最終都是要呼叫video_device->ioctl_ops,而且呼叫時還需判斷是INFO_FL_STD還是INFO_FL_FUNC,怎麼判斷呢,v4l2-ioctl.c有定義:

IOCTL_INFO_FNC(命令, 實現函式, 列印資訊(debug使用), flag),同樣 
IOCTL_INFO_STD(命令, 實現函式, 列印資訊(debug使用), flag)

  1.   static struct v4l2_ioctl_info v4l2_ioctls[] = {
  2.   IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
  3.   IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
  4.   IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0),
  5.   IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
  6.   IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
  7.   IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
  8.   IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
  9.   IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
  10.   IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
  11.   IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
  12.   IOCTL_INFO_STD(VIDIOC_EXPBUF, vidioc_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
  13.   IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
  14.   IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
  15.   IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
  16.   IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
  17.   IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
  18.   IOCTL_INFO_STD(VIDIOC_G_STD, vidioc_g_std, v4l_print_std, 0),
  19.   IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
  20.   IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
  21.   IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
  22.   IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
  23.   IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
  24.   IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
  25.   IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
  26.   IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
  27.   IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
  28.   IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
  29.   IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
  30.   IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
  31.   IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
  32.   IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0),
  33.   IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO),
  34.   IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
  35.   IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
  36.   IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
  37.   IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
  38.   IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
  39.   IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
  40.   IOCTL_INFO_FNC(VIDIOC_S_MODULATOR, v4l_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
  41.   IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
  42.   IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
  43.   IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
  44.   IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
  45.   IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
  46.   IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)),
  47.   IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)),
  48.   IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
  49.   IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
  50.   IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
  51.   IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
  52.   IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
  53.   IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
  54.   IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
  55.   IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
  56.   IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
  57.   IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
  58.   IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
  59.   IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
  60.   IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
  61.   IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
  62.   IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
  63.   IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
  64.   IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
  65.   IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
  66.   IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
  67.   IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
  68.   IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
  69.   IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
  70.   IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
  71.   IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
  72.   IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
  73.   IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
  74.   IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
  75.   IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
  76.   IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
  77.   IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
  78.   IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
  79.   IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
  80.   IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
  81.   IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
  82.   IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
  83.   IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
  84.   };

上面的分析中一直說到video_device->ioctl_ops,那麼ioctl_ops有多少函式呢。。很多見下: 
算了還是不貼上了自己去程式碼中看吧,這裡只粘一點:

  1.   struct v4l2_ioctl_ops {
  2.   /* ioctl callbacks */
  3.    
  4.   /* VIDIOC_QUERYCAP handler */
  5.   int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap);
  6.    
  7.   /* VIDIOC_ENUM_FMT handlers */
  8.   int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh,
  9.   struct v4l2_fmtdesc *f);
  10.   int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh,
  11.   struct v4l2_fmtdesc *f);
  12.   int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh,
  13.   struct v4l2_fmtdesc *f);
  14.   int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
  15.   struct v4l2_fmtdesc *f);
  16.   int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
  17.   struct v4l2_fmtdesc *f);
  18.   int (*vidioc_enum_fmt_sdr_cap) (struct file *file, void *fh,
  19.   struct v4l2_fmtdesc *f);
  20.   int (*vidioc_enum_fmt_sdr_out) (struct file *file, void *fh,
  21.   struct v4l2_fmtdesc *f);
  22.   。
  23.   。
  24.   。
  25.   /* DV Timings IOCTLs */
  26.   int (*vidioc_s_dv_timings) (struct file *file, void *fh,
  27.   struct v4l2_dv_timings *timings);
  28.   int (*vidioc_g_dv_timings) (struct file *file, void *fh,
  29.   struct v4l2_dv_timings *timings);
  30.   int (*vidioc_query_dv_timings) (struct file *file, void *fh,
  31.   struct v4l2_dv_timings *timings);
  32.   int (*vidioc_enum_dv_timings) (struct file *file, void *fh,
  33.   struct v4l2_enum_dv_timings *timings);
  34.   int (*vidioc_dv_timings_cap) (struct file *file, void *fh,
  35.   struct v4l2_dv_timings_cap *cap);
  36.   int (*vidioc_g_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
  37.   int (*vidioc_s_edid) (struct file *file, void *fh, struct v4l2_edid *edid);
  38.    
  39.   int (*vidioc_subscribe_event) (struct v4l2_fh *fh,
  40.   const struct v4l2_event_subscription *sub);
  41.   int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh,
  42.   const struct v4l2_event_subscription *sub);
  43.    
  44.   /* For other private ioctls */
  45.   long (*vidioc_default) (struct file *file, void *fh,
  46.   bool valid_prio, unsigned int cmd, void *arg);
  47.   };

ioctl呼叫總圖

 

 

---------------------------------------------------------------------------------------------------------------------------------------

在應用程式獲取視訊資料的流程中,都是通過 ioctl 命令與驅動程式進行互動,常見的 ioctl 命令有:

 

  1.   VIDIOC_QUERYCAP /* 獲取裝置支援的操作 */
  2.    
  3.   VIDIOC_G_FMT /* 獲取設定支援的視訊格式 */
  4.    
  5.   VIDIOC_S_FMT /* 設定捕獲視訊的格式 */
  6.    
  7.   VIDIOC_REQBUFS /* 向驅動提出申請記憶體的請求 */
  8.    
  9.   VIDIOC_QUERYBUF /* 向驅動查詢申請到的記憶體 */
  10.    
  11.   VIDIOC_QBUF /* 將空閒的記憶體加入可捕獲視訊的佇列 */
  12.    
  13.   VIDIOC_DQBUF /* 將已經捕獲好視訊的記憶體拉出已捕獲視訊的佇列 */
  14.    
  15.   VIDIOC_STREAMON /* 開啟視訊流 */
  16.    
  17.   VIDIOC_STREAMOFF /* 關閉視訊流 */
  18.    
  19.   VIDIOC_QUERYCTRL /* 查詢驅動是否支援該命令 */
  20.    
  21.   VIDIOC_G_CTRL /* 獲取當前命令值 */
  22.    
  23.   VIDIOC_S_CTRL /* 設定新的命令值 */
  24.    
  25.   VIDIOC_G_TUNER /* 獲取調諧器資訊 */
  26.    
  27.   VIDIOC_S_TUNER /* 設定調諧器資訊 */
  28.    
  29.   VIDIOC_G_FREQUENCY /* 獲取調諧器頻率 */
  30.    
  31.   VIDIOC_S_FREQUENCY /* 設定調諧器頻率 */

 

1、struct v4l2_capability 與 VIDIOC_QUERYCAP
VIDIOC_QUERYCAP 命令通過結構 v4l2_capability 獲取裝置支援的操作模式

  1.   struct v4l2_capability {
  2.    
  3.   __u8 driver[16]; /* i.e. "bttv" */
  4.    
  5.   __u8 card[32]; /* i.e. "Hauppauge WinTV" */
  6.    
  7.   __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
  8.    
  9.   __u32 version; /* should use KERNEL_VERSION() */
  10.    
  11.   __u32 capabilities; /* Device capabilities */
  12.    
  13.   __u32 reserved[4];
  14.    
  15.   };

 

其中域 capabilities 代表裝置支援的操作模式,常見的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一個視訊捕捉裝置並且具有資料流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。
2、struct v4l2_format 與 VIDIOC_G_FMT、VIDIOC_S_FMT、VIDIOC_TRY_FMT

通常用 VIDIOC_S_FMT 命令通過結構 v4l2_format 初始化捕獲視訊的格式,如果要改變格式則用 VIDIOC_TRY_FMT 命令:

 

  1.   struct v4l2_format {
  2.    
  3.   enum v4l2_buf_type type;
  4.    
  5.   union {
  6.    
  7.   struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
  8.    
  9.   struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
  10.    
  11.   struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
  12.    
  13.   struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
  14.    
  15.   __u8 raw_data[200]; /* user-defined */
  16.    
  17.   } fmt;
  18.    
  19.   };
  20.    
  21.   其中
  22.    
  23.   enum v4l2_buf_type {
  24.    
  25.   V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
  26.    
  27.   V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,
  28.    
  29.   V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
  30.    
  31.   ...
  32.    
  33.   V4L2_BUF_TYPE_PRIVATE = 0x80,
  34.    
  35.   };
  36.    
  37.    
  38.   struct v4l2_pix_format {
  39.    
  40.   __u32 width;
  41.    
  42.   __u32 height;
  43.    
  44.   __u32 pixelformat;
  45.    
  46.   enum v4l2_field field;
  47.    
  48.   __u32 bytesperline; /* for padding, zero if unused */
  49.    
  50.   __u32 sizeimage;
  51.    
  52.   enum v4l2_colorspace colorspace;
  53.    
  54.   __u32 priv; /* private data, depends on pixelformat */
  55.    
  56.   };

 

常見的捕獲模式為 V4L2_BUF_TYPE_VIDEO_CAPTURE 即視訊捕捉模式,在此模式下 fmt 聯合體採用域 v4l2_pix_format:其中 width 為視訊的寬、height 為視訊的高、pixelformat 為視訊資料格式(常見的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、bytesperline 為一行影象佔用的位元組數、sizeimage 則為影象佔用的總位元組數、colorspace 指定裝置的顏色空間。
3、struct v4l2_requestbuffers 與 VIDIOC_REQBUFS

VIDIOC_REQBUFS 命令通過結構 v4l2_requestbuffers 請求驅動申請一片連續的記憶體用於快取視訊資訊:

​​​​​​​

  1.   struct v4l2_requestbuffers {
  2.    
  3.   __u32 count;
  4.    
  5.   enum v4l2_buf_type type;
  6.    
  7.   enum v4l2_memory memory;
  8.    
  9.   __u32 reserved[2];
  10.    
  11.   };
  12.    
  13.   其中
  14.    
  15.   enum v4l2_memory {
  16.    
  17.   V4L2_MEMORY_MMAP = 1,
  18.    
  19.   V4L2_MEMORY_USERPTR = 2,
  20.    
  21.   V4L2_MEMORY_OVERLAY = 3,
  22.    
  23.   };

 

count 指定根據影象佔用空間大小申請的快取區個數,type 為視訊捕獲模式,memory 為記憶體區的使用方式。

4、struct v4l2_buffer與 VIDIOC_QUERYBUF

VIDIOC_QUERYBUF 命令通過結構 v4l2_buffer 查詢驅動申請的記憶體區資訊:

​​​​​​​

  1.   struct v4l2_buffer {
  2.    
  3.   __u32 index;
  4.    
  5.   enum v4l2_buf_type type;
  6.    
  7.   __u32 bytesused;
  8.    
  9.   __u32 flags;
  10.    
  11.   enum v4l2_field field;
  12.    
  13.   struct timeval timestamp;
  14.    
  15.   struct v4l2_timecode timecode;
  16.    
  17.   __u32 sequence;
  18.    
  19.    
  20.   /* memory location */
  21.    
  22.   enum v4l2_memory memory;
  23.    
  24.   union {
  25.    
  26.   __u32 offset;
  27.    
  28.   unsigned long userptr;
  29.    
  30.   } m;
  31.    
  32.   __u32 length;
  33.    
  34.   __u32 input;
  35.    
  36.   __u32 reserved;
  37.    
  38.   };

 

index 為快取編號,type 為視訊捕獲模式,bytesused 為快取已使用空間大小,flags 為快取當前狀態(常見值有 V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE,分別代表當前快取已經對映、快取可以採集資料、快取可以提取資料),timestamp 為時間戳,sequence為快取序號,memory 為快取使用方式,offset 為當前快取與記憶體區起始地址的偏移,length 為快取大小,reserved 一般用於傳遞實體地址值。
另外 VIDIOC_QBUF 和 VIDIOC_DQBUF 命令都採用結構 v4l2_buffer 與驅動通訊:VIDIOC_QBUF 命令向驅動傳遞應用程式已經處理完的快取,即將快取加入空閒可捕獲視訊的佇列,傳遞的主要引數為 index;VIDIOC_DQBUF 命令向驅動獲取已經存放有視訊資料的快取,v4l2_buffer 的各個域幾乎都會被更新,但主要的引數也是 index,應用程式會根據 index 確定可用資料的起始地址和範圍。

 

5、enum v4l2_buf_type 與 VIDIOC_STREAMON、VIDIOC_STREAMOFF

這兩個命令使用的只是一個整形資料,即 v4l2_buf_type,一般只要指定其值為 V4L2_BUF_TYPE_VIDEO_CAPTURE 即可。

6、struct v4l2_queryctrl 與 VIDIOC_QUERYCTRL

VIDIOC_QUERYCTRL 命令通過結構 v4l2_queryctrl 查詢驅動是否支援該 id 代表的命令,並返回該命令的各種引數:

​​​​​​​

  1.   struct v4l2_queryctrl {
  2.    
  3.   __u32 id; /* 命令編號 */
  4.    
  5.   enum v4l2_ctrl_type type; /* 命令值的型別 */
  6.    
  7.   __u8 name[32]; /* 命令名稱*/
  8.    
  9.   __s32 minimum; /* 最小的命令值 */
  10.    
  11.   __s32 maximum; /* 最大的命令值 */
  12.    
  13.   __s32 step; /* 命令值變化的步長 */
  14.    
  15.   __s32 default_value; /* 預設的命令值 */
  16.    
  17.   __u32 flags; /* 命令的標誌 */
  18.    
  19.   __u32 reserved[2]; /* 命令值的位圖表示 */
  20.    
  21.   };
  22.    
  23.   其中
  24.    
  25.   enum v4l2_ctrl_type {
  26.    
  27.   V4L2_CTRL_TYPE_INTEGER = 1, /* 整形 */
  28.    
  29.   V4L2_CTRL_TYPE_BOOLEAN = 2, /* 真值 */
  30.    
  31.   V4L2_CTRL_TYPE_MENU = 3, /* 選單 */
  32.    
  33.   V4L2_CTRL_TYPE_BUTTON = 4, /* 無值 */
  34.    
  35.   V4L2_CTRL_TYPE_INTEGER64 = 5, /* 後面三種不常用 */
  36.    
  37.   V4L2_CTRL_TYPE_CTRL_CLASS = 6,
  38.    
  39.   V4L2_CTRL_TYPE_STRING = 7,
  40.    
  41.   };
  42.    
  43.   命令的標誌取值如下:
  44.    
  45.   /* Control flags */
  46.    
  47.   #define V4L2_CTRL_FLAG_DISABLED 0x0001
  48.    
  49.   #define V4L2_CTRL_FLAG_GRABBED 0x0002
  50.    
  51.   #define V4L2_CTRL_FLAG_READ_ONLY 0x0004
  52.    
  53.   #define V4L2_CTRL_FLAG_UPDATE 0x0008
  54.    
  55.   #define V4L2_CTRL_FLAG_INACTIVE 0x0010
  56.    
  57.   #define V4L2_CTRL_FLAG_SLIDER 0x0020
  58.    
  59.   #define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040
  60.    
  61.    
  62.   /* Query flag, to be ORed with the control ID */
  63.    
  64.   #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000

 

id 是命令的編號,常見的命令有兩種:一種以 V4L2_CID_BASE 為起始值,是公用命令;一種以 V4L2_CID_PRIVATE_BASE 為起始值,是私有命令。在一般的應用中命令值可見如下:

​​​​​​​

  1.   V4L2_CID_CONTRAST (V4L2_CID_BASE+1) /* 對比度調節 */
  2.    
  3.   V4L2_CID_SATURATION (V4L2_CID_BASE+2) /* 飽和度調節 */
  4.    
  5.   V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5) /* 音量調節 */
  6.    
  7.   V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9) /* 靜音設定 */
  8.    
  9.   V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13) /* 白平衡調節 */
  10.    
  11.   V4L2_CID_GAMMA (V4L2_CID_BASE+16) /* 伽馬值調節 */
  12.    
  13.   V4L2_CID_EXPOSURE (V4L2_CID_BASE+17) /* 曝光度調節 */
  14.    
  15.    
  16.   V4L2_CID_PRIVATE_ATXX_FLASH (V4L2_CID_PRIVATE_BASE + 2) /* 閃光燈控制 */
  17.    
  18.   V4L2_CID_PRIVATE_ATXX_FRAME (V4L2_CID_PRIVATE_BASE + 12) /* 幀率調節 */

 

 type 為命令值的型別(總共有7中型別的值),name 是命令的名稱,reserved 則是命令值的位圖表示,驅動會將所有的命令值都以 bit 的形式寫到 64 位的域中,上層應用查詢時可以根據點陣圖判斷命令支援的值。

7、struct v4l2_control 與 VIDIOC_G_CTRL、VIDIOC_S_CTRL

VIDIOC_S_CTRL 或 VIDIOC_G_CTRL 命令通過結構 v4l2_control 設定或者獲取 id 命令的值:

  1.    
  2.   struct v4l2_control {
  3.    
  4.   __u32 id;
  5.    
  6.   __s32 value;
  7.    
  8.   };

​​​​​​​這個結構只有 2 個域,id 是命令編號,value 則是命令的值。

8、struct v4l2_tuner 與 VIDIOC_G_TUNER、VIDIOC_S_TUNER

VIDIOC_S_TUNER 或 VIDIOC_G_TUNER 命令通過結構 v4l2_tuner 設定調諧器的資訊:

​​​​​​​

  1.   struct v4l2_tuner {
  2.    
  3.   __u32 index; /* 調諧器編號,由應用程式設定 */
  4.    
  5.   __u8 name[32]; /* 調諧器名稱 */
  6.    
  7.   enum v4l2_tuner_type type; /* 調諧器型別 */
  8.    
  9.   __u32 capability; /* 調諧器支援的操作 */
  10.    
  11.   __u32 rangelow; /* 最低頻率值,單位為62.5Hz或者62.5KHz */
  12.    
  13.   __u32 rangehigh; /* 最高頻率值 */
  14.    
  15.   __u32 rxsubchans; /* 接收的音訊訊號型別 */
  16.    
  17.   __u32 audmode; /* 當前音訊播放形式 */
  18.    
  19.   __s32 signal; /* 訊號強度 */
  20.    
  21.   __s32 afc; /* 自動頻率控制 */
  22.    
  23.   __u32 reserved[4]; /* 保留備用 */
  24.    
  25.   };
  26.    
  27.   其中
  28.    
  29.   enum v4l2_tuner_type {
  30.    
  31.   V4L2_TUNER_RADIO = 1, /* 調頻收音機 */
  32.    
  33.   V4L2_TUNER_ANALOG_TV = 2, /* 模擬電視高頻頭 */
  34.    
  35.   V4L2_TUNER_DIGITAL_TV = 3, /* 數字電視高頻頭 */
  36.    
  37.   };

 

其中域 type 有三種類型;capability 域一般為 V4L2_TUNER_CAP_LOW,表明頻率調節的步長是62.5Hz,如果沒有這個標誌位則步長為62.5KHz;rangelow 與 rangehigh 是調諧器可以調頻率的最高值和最低值,但都以步長為單位表示;rxsubchans 表示調諧器接收的音訊訊號型別,常見值有 V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO  即單聲道與立體聲;audmode 表示以何種方式播放聲音,常見值有 V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO,即以單聲道還是立體聲的方式播放;signal 為當前訊號強度,一般取值範圍為 0 - 65535。
9、struct v4l2_frequency 與 VIDIOC_G_FREQUENCY、VIDIOC_S_FREQUENCY

VIDIOC_S_FREQUENCY 或 VIDIOC_G_FREQUENCY 命令通過結構 v4l2_frequency 設定或獲取當前頻率值:

​​​​​​​

  1.   struct v4l2_frequency {
  2.    
  3.   __u32 tuner; /* 調諧器編號 */
  4.    
  5.   enum v4l2_tuner_type type; /* 調諧器型別 */
  6.    
  7.   __u32 frequency; /* 調諧器頻率 */
  8.    
  9.   __u32 reserved[8];
  10.    
  11.   };

 

注意:frequency 的值是以62.5Hz 或者 62.5KHZ 為單位的。

 

附:_IO、_IOR、_IOW、_IOWR 巨集的使用說明

驅動程式中 ioctl  函式傳遞的變數 cmd 是應用程式向驅動程式請求處理的命令。cmd 除了用於區別不同命令的數值,還可包含有助於處理的幾種資訊。cmd 的大小為 32 bit,共分 4 個域:

bit29 ~ bit31: 3bit  為 “讀寫” 區,作用是區分是讀命令還是寫命令。
bit16 ~ bit28:13bit 為 "資料大小" 區,表示 ioctl 中的 arg 變數傳遞的資料大小;有時候為 14bit 即將 bit29 覆蓋。
bit8 ~ bit15:   8bit  為 “魔數"(也稱為"幻數")區,這個值用以與其它裝置驅動程式的 ioctl 命令進行區別。
bit0 ~ bit7:     8bit  為 "序號" 區,是區分命令的命令順序序號。

魔數(magic number)
魔數範圍為 0~255 。通常,用英文字元 'A' ~ 'Z' 或者 'a' ~ 'z' 來表示。裝置驅動程式從傳遞進來的命令獲取魔數,然後與自身處理的魔數想比較,如果相同則處理,不同則不處理。魔數是拒絕誤使用的初步輔助引數。裝置驅動程式可以通過巨集 _IOC_TYPE (cmd) 來獲取魔數。不同的裝置驅動程式最好設定不同的魔數,但並不是要求絕對,也是可以使用其他裝置驅動程式已用過的魔數。

基數(序號)
基數用於區別各種命令。通常,從 0開始遞增,相同裝置驅動程式上可以重複使用該值。例如,讀和寫命令中使用了相同的基數,裝置驅動程式也能分辨出來,原因在於裝置驅動程式區分命令時使用 switch ,且直接使用命令變數 cmd 值。建立命令的巨集生成的值由多個域組合而成,所以即使是相同的基數,也會判斷為不同的命令。裝置驅動程式想要從命令中獲取該基數,就使用巨集 _IOC_NR (cmd)。

下面我們看一下上述巨集在核心中的原型:

​​​​​​​

  1.   /*
  2.    
  3.   * Our DIR and SIZE overlap in order to simulteneously provide
  4.    
  5.   * a non-zero _IOC_NONE (for binary compatibility) and
  6.    
  7.   * 14 bits of size as on i386. Here's the layout:
  8.    
  9.   *
  10.    
  11.   * 0xE0000000 DIR 3bit
  12.    
  13.   * 0x80000000 DIR = WRITE bit31
  14.    
  15.   * 0x40000000 DIR = READ bit30
  16.    
  17.   * 0x20000000 DIR = NONE bit29
  18.    
  19.   * 0x3FFF0000 SIZE (overlaps NONE bit) 13bit
  20.    
  21.   * 0x0000FF00 TYPE 8bit
  22.    
  23.   * 0x000000FF NR (CMD) 8bit
  24.    
  25.   */
  26.    
  27.   /* 各個域的長度 */
  28.    
  29.   #define _IOC_NRBITS 8
  30.    
  31.   #define _IOC_TYPEBITS 8
  32.    
  33.   #define _IOC_SIZEBITS 13 /* Actually 14, see below. */
  34.    
  35.   #define _IOC_DIRBITS 3
  36.    
  37.   /* 各個域的掩碼 */
  38.    
  39.   #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
  40.    
  41.   #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
  42.    
  43.   #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
  44.    
  45.   #define _IOC_XSIZEMASK ((1 << (_IOC_SIZEBITS+1))-1)
  46.    
  47.   #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
  48.    
  49.   /* 各個域的偏移 */
  50.    
  51.   #define _IOC_NRSHIFT 0
  52.    
  53.   #define _IOC_TYPESHIFT (_IOC_NRSHIFT + _IOC_NRBITS) /* 8 */
  54.    
  55.   #define _IOC_SIZESHIFT (_IOC_TYPESHIFT + _IOC_TYPEBITS) /* 16 */
  56.    
  57.   #define _IOC_DIRSHIFT (_IOC_SIZESHIFT + _IOC_SIZEBITS) /* 29 */
  58.    
  59.   /* 讀寫域的值 */
  60.    
  61.   #define _IOC_NONE 1U
  62.    
  63.   #define _IOC_READ 2U
  64.    
  65.   #define _IOC_WRITE 4U
  66.    
  67.    
  68.   #define _IOC(dir,type,nr,size) \
  69.    
  70.   (((dir) << _IOC_DIRSHIFT) | \ /* 讀寫方向左移 29bit */
  71.    
  72.   ((type) << _IOC_TYPESHIFT) | \ /* 幻數左移 8bit */
  73.    
  74.   ((nr) << _IOC_NRSHIFT) | \ /* 命令序號 */
  75.    
  76.   ((size) << _IOC_SIZESHIFT)) /* 引數大小左移 16bit */
  77.    
  78.   /* 巨集原型,這裡將會根據傳遞的資料型別取其長度 */
  79.    
  80.   #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
  81.    
  82.   #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
  83.    
  84.   #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
  85.    
  86.   #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
  87.    
  88.   /* 獲取各個域的值 */
  89.    
  90.   #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
  91.    
  92.   #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
  93.    
  94.   #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
  95.    
  96.   #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

 

這裡特別說明一下 _IO 巨集,該巨集沒有可傳遞的變數,只用於傳送命令。這是因為變數需要可變資料,只作為命令(比如 reset)使用時,沒有必要判斷裝置上的資料,因此裝置驅動程式沒有必要執行檔案相關的處理。在 v4l2 中使用示例如下:

​​​​​​​

  1.   #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability)
  2.    
  3.   #define VIDIOC_RESERVED _IO('V', 1)
  4.    
  5.   #define VIDIOC_S_FMT _IOWR('V', 5, struct v4l2_format)
  6.    
  7.   #define VIDIOC_STREAMON _IOW('V', 18, int)

 

v4l2 中對上述巨集命令的處理在 video_ioctl2 函式中:​​​​​​​

  1.   static unsigned long cmd_input_size(unsigned int cmd)
  2.    
  3.   {
  4.    
  5.   #define CMDINSIZE(cmd, type, field) \
  6.    
  7.   case VIDIOC_##cmd: \
  8.    
  9.   return offsetof(struct v4l2_##type, field) + \ /* 域的偏移 */
  10.    
  11.   sizeof(((struct v4l2_##type *)0)->field); /* 域的長度 */
  12.    
  13.    
  14.   switch (cmd) {
  15.    
  16.   CMDINSIZE(ENUM_FMT, fmtdesc, type);
  17.    
  18.   CMDINSIZE(G_FMT, format, type);
  19.    
  20.   ...
  21.    
  22.   CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
  23.    
  24.   CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
  25.    
  26.   default:
  27.    
  28.   return _IOC_SIZE(cmd); /* 剩下的是需要全部拷貝的命令 */
  29.    
  30.   }
  31.    
  32.   }
  33.    
  34.    
  35.   long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
  36.    
  37.   {
  38.    
  39.   char sbuf[128]; /* 在棧中分配128個位元組空間用來儲存命令的引數 */
  40.    
  41.   void *mbuf = NULL;
  42.    
  43.   void *parg = NULL; /* 引數存放的首地址 */
  44.    
  45.   long err = -EINVAL;
  46.    
  47.   int is_ext_ctrl;
  48.    
  49.   size_t ctrls_size = 0;
  50.    
  51.   void __user *user_ptr = NULL;
  52.    
  53.    
  54.   ...
  55.    
  56.   /* 判斷是否包含讀寫命令,如果是則將使用者空間的引數值拷貝到核心 */
  57.    
  58.   if (_IOC_DIR(cmd) != _IOC_NONE) {
  59.    
  60.   /* 判斷引數大小是否超過128位元組 */
  61.    
  62.   if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
  63.    
  64.   parg = sbuf;
  65.    
  66.   } else {
  67.    
  68.   /* 如果超過128位元組則從堆中申請 */
  69.    
  70.   mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
  71.    
  72.   if (NULL == mbuf)
  73.    
  74.   return -ENOMEM;
  75.    
  76.   parg = mbuf;
  77.    
  78.   }
  79.    
  80.    
  81.   err = -EFAULT;
  82.    
  83.   /* 如果包含寫命令 */
  84.    
  85.   if (_IOC_DIR(cmd) & _IOC_WRITE) {
  86.    
  87.   /* 計算需要拷貝的有效資料長度,有的命令不需要全部拷貝 */
  88.    
  89.   unsigned long n = cmd_input_size(cmd);
  90.    
  91.   /* 從使用者空間拷貝引數值 */
  92.    
  93.   if (copy_from_user(parg, (void __user *)arg, n))
  94.    
  95.   goto out;
  96.    
  97.    
  98.   /* 將剩下的空間清零 */
  99.    
  100.   if (n < _IOC_SIZE(cmd))
  101.    
  102.   memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
  103.    
  104.   } else {
  105.    
  106.   /* 如果是隻讀命令則將整個buffer清零 */
  107.    
  108.   memset(parg, 0, _IOC_SIZE(cmd));
  109.    
  110.   }
  111.    
  112.   }
  113.    
  114.    
  115.   ...
  116.    
  117.   /* 呼叫 v4l2_ioctl_ops 的成員函式處理命令 */
  118.    
  119.   err = __video_do_ioctl(file, cmd, parg);
  120.    
  121.   if (err == -ENOIOCTLCMD)
  122.    
  123.   err = -EINVAL;
  124.    
  125.   ...
  126.    
  127.   if (err < 0)
  128.    
  129.   goto out;
  130.    
  131.    
  132.   out_ext_ctrl:
  133.    
  134.   /* 如果包含讀命令則將引數值拷貝到使用者空間 */
  135.    
  136.   switch (_IOC_DIR(cmd)) {
  137.    
  138.   case _IOC_READ:
  139.    
  140.   case (_IOC_WRITE | _IOC_READ):
  141.    
  142.   if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
  143.    
  144.   err = -EFAULT;
  145.    
  146.   break;
  147.    
  148.   }
  149.    
  150.    
  151.   out:
  152.    
  153.   kfree(mbuf);
  154.    
  155.   return err;
  156.    
  157.   }
  158.    
  159.   EXPORT_SYMBOL(video_ioctl2);

 

然後我們在 struct v4l2_file_operations 中將 ioctl 成員設定為 video_ioctl2 即可。

 

Ioctl框架是由v4l2_ioctl.c檔案實現,檔案中定義結構體陣列v4l2_ioctls,可以看做是ioctl指令和回撥函式的關係表。使用者空間呼叫系統呼叫ioctl,傳遞下來ioctl指令,然後通過查詢此關係表找到對應回撥函式

IOCTL呼叫的流程圖如下:

 

 

  使用者空間通過開啟/dev/目錄下的裝置節點,獲取到檔案的file結構體,通過系統呼叫ioctl把cmd和arg傳入到核心。通過一系列的呼叫後最終會呼叫到__video_do_ioctl函式,然後通過cmd檢索v4l2_ioctls[],判斷是INFO_FL_STD還是INFO_FL_FUNC。如果是INFO_FL_STD會直接呼叫到視訊裝置驅動中video_device->v4l2_ioctl_ops函式集。如果是INFO_FL_FUNC會先呼叫到v4l2自己實現的標準回撥函式,然後根據arg再呼叫到video_device->v4l2_ioctl_ops或v4l2_fh->v4l2_ctrl_handler函式集。