v4l2 编程接口(一) — ioctl
在应用程序获取视频数据的流程中,都是通过 ioctl 命令与驱动程序进行交互,常见的 ioctl 命令有:
static unsigned long cmd_input_size(unsigned int cmd){#define CMDINSIZE(cmd, type, field) \case VIDIOC_##cmd: \return offsetof(struct v4l2_##type, field) + \ /* 域的偏移 */sizeof(((struct v4l2_##type *)0)->field); /* 域的长度 */switch (cmd) {CMDINSIZE(ENUM_FMT,fmtdesc,type);CMDINSIZE(G_FMT,format,type);...CMDINSIZE(ENUM_FRAMESIZES,frmsizeenum,pixel_format);CMDINSIZE(ENUM_FRAMEINTERVALS,frmivalenum,height);default:return _IOC_SIZE(cmd); /* 剩下的是需要全部拷贝的命令 */}}long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg){charsbuf[128]; /* 在栈中分配128个字节空间用来储存命令的参数 */void *mbuf = NULL;void*parg = NULL; /* 参数存放的首地址 */longerr = -EINVAL;int is_ext_ctrl;size_t ctrls_size = 0;void __user *user_ptr = NULL;.../* 判断是否包含读写命令,如果是则将用户空间的参数值拷贝到内核 */if (_IOC_DIR(cmd) != _IOC_NONE) {/* 判断参数大小是否超过128字节 */if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {parg = sbuf;} else {/* 如果超过128字节则从堆中申请 */mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);if (NULL == mbuf)return -ENOMEM;parg = mbuf;}err = -EFAULT;/* 如果包含写命令 */if (_IOC_DIR(cmd) & _IOC_WRITE) {/* 计算需要拷贝的有效数据长度,有的命令不需要全部拷贝 */unsigned long n = cmd_input_size(cmd);/* 从用户空间拷贝参数值 */if (copy_from_user(parg, (void __user *)arg, n))goto out;/* 将剩下的空间清零 */if (n < _IOC_SIZE(cmd))memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);} else {/* 如果是只读命令则将整个buffer清零 */memset(parg, 0, _IOC_SIZE(cmd));}}.../* 调用 v4l2_ioctl_ops 的成员函数处理命令 */err = __video_do_ioctl(file, cmd, parg);if (err == -ENOIOCTLCMD)err = -EINVAL;...if (err < 0)goto out;out_ext_ctrl:/* 如果包含读命令则将参数值拷贝到用户空间 */switch (_IOC_DIR(cmd)) {case _IOC_READ:case (_IOC_WRITE | _IOC_READ):if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))err = -EFAULT;break;}out:kfree(mbuf);return err;}EXPORT_SYMBOL(video_ioctl2);然后我们在 struct v4l2_file_operations 中将 ioctl 成员设置为 video_ioctl2 即可。