概括
通过VideoControl Interface来控制,
通过VideoStreaming Interface来读视频数据,
VC里含有多个Unit/Terminal等功能模块,可以通过访问这些模块进行控制,比如调亮度
总结:
UVC设备有2个interface: VideoControl Interface, VideoStreaming Interface
VideoControl Interface用于控制,比如设置亮度。它内部有多个Unit/Terminal(在程序里Unit/Terminal都称为entity)
ret = uvc_query_ctrl(dev /* 哪一个USB设备 /, SET_CUR, ctrl->entity->id / 哪一个unit/terminal /,dev->intfnum / 哪一个接口: VC interface */, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),ctrl->info->size);
可以通过类似的函数来访问:
ret = uvc_query_ctrl(dev /* 哪一个USB设备 /, SET_CUR, ctrl->entity->id / 哪一个unit/terminal /,dev->intfnum / 哪一个接口: VC interface */, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size);
VideoStreaming Interface用于获得视频数据,也可以用来选择fromat/frame(VS可能有多种format, 一个format支持多种frame, frame用来表示分辨率等信息)
可以通过类似的函数来访问:
ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0,video->streaming->intfnum / 哪一个接口: VS */, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, uvc_timeout_param);
我们在设置FORMAT时只是简单的使用format = &stream->format[fmt->index]; // 获取视频流格式,等数据,
这些数据哪来的?
应是设备被枚举时设置的,也就是分析它的描述符时设置的。
UVC驱动的重点在于:
描述符的分析
属性的控制: 通过VideoControl Interface来设置
格式的选择:通过VideoStreaming Interface来设置
数据的获得:通过VideoStreaming Interface的URB来获得
分析UVC驱动调用过程
const struct v4l2_file_operations uvc_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, .unlocked_ioctl = video_ioctl2, #ifdef CONFIG_COMPAT .compat_ioctl32 = uvc_v4l2_compat_ioctl32, #endif .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, #ifndef CONFIG_MMU .get_unmapped_area = uvc_v4l2_get_unmapped_area, #endif };
1.open:
uvc_v4l2_open
static int uvc_v4l2_open(struct file *file) { struct uvc_streaming *stream; struct uvc_fh *handle; int ret = 0; // 打印函数调用信息 uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); // 获取视频设备的私有数据 stream = video_drvdata(file); // 如果设备已经断开连接,返回错误码 if (stream->dev->state & UVC_DEV_DISCONNECTED) return -ENODEV; // 获取接口的使用权 ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) return ret; // 创建设备句柄 handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); return -ENOMEM; } // 加锁,防止多个进程同时访问 mutex_lock(&stream->dev->lock); // 如果当前没有用户使用设备,启动设备状态 if (stream->dev->users == 0) { ret = uvc_status_start(stream->dev, GFP_KERNEL); if (ret < 0) { mutex_unlock(&stream->dev->lock); usb_autopm_put_interface(stream->dev->intf); kfree(handle); return ret; } } // 增加用户数 stream->dev->users++; mutex_unlock(&stream->dev->lock); // 初始化文件句柄 v4l2_fh_init(&handle->vfh, &stream->vdev); // 添加文件句柄 v4l2_fh_add(&handle->vfh); handle->chain = stream->chain; handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; return 0; }
ioctl
video_ioctl2将应用中提供的参数拷贝到内核,调用__video_do_ioctl
long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(file, cmd, arg, __video_do_ioctl); }
video_ioctl2->__video_do_ioctl
static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { // 获取video_device结构体 struct video_device *vfd = video_devdata(file); // 获取v4l2_ioctl_ops结构体 const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; // 判断是否为只写操作 bool write_only = false; // 定义默认的ioctl信息 struct v4l2_ioctl_info default_info; // 定义ioctl信息 const struct v4l2_ioctl_info *info; // 获取file结构体的私有数据 void *fh = file->private_data; // 获取v4l2_fh结构体 struct v4l2_fh *vfh = NULL; // 获取dev_debug int dev_debug = vfd->dev_debug; // 定义返回值 long ret = -ENOTTY; // 判断是否有ioctl_ops if (ops == NULL) { pr_warn("%s: has no ioctl_ops.\n", video_device_node_name(vfd)); return ret; } // 判断是否使用v4l2_fh if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; // 判断是否为已知的ioctl if (v4l2_is_known_ioctl(cmd)) { // 获取ioctl信息 info = &v4l2_ioctls[_IOC_NR(cmd)]; // 判断是否为有效的ioctl if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) goto done; // 判断是否需要检查优先级 if (vfh && (info->flags & INFO_FL_PRIO)) { ret = v4l2_prio_check(vfd->prio, vfh->prio); if (ret) goto done; } } else { // 设置默认的ioctl信息 default_info.ioctl = cmd; default_info.flags = 0; default_info.debug = v4l_print_default; info = &default_info; } write_only = _IOC_DIR(cmd) == _IOC_WRITE; // 判断是否为标准的ioctl if (info->flags & INFO_FL_STD) { // 定义vidioc_op函数指针类型 typedef int (*vidioc_op)(struct file *file, void *fh, void *p); // 获取ioctl_ops const void *p = vfd->ioctl_ops; // 获取vidioc函数指针 const vidioc_op *vidioc = p + info->u.offset; // 调用vidioc函数 ret = (*vidioc)(file, fh, arg); } // 判断是否为函数ioctl else if (info->flags & INFO_FL_FUNC) { // 调用函数ioctl ret = info->u.func(ops, file, fh, arg); } // 判断是否有默认的ioctl else if (!ops->vidioc_default) { ret = -ENOTTY; } // 调用默认的ioctl else { ret = ops->vidioc_default(file, fh, vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, cmd, arg); } done: // 判断是否需要打印调试信息 if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) { // 判断是否需要打印流信息 if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) && (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF)) return ret; // 打印ioctl信息 v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) pr_cont(": error %ld", ret); if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG)) pr_cont("\n"); else if (_IOC_DIR(cmd) == _IOC_NONE) info->debug(arg, write_only); else { pr_cont(": "); info->debug(arg, write_only); } } return ret; } 根据传入的参数调用对应的ioctl write_only = _IOC_DIR(cmd) == _IOC_WRITE; // 判断是否为标准的ioctl if (info->flags & INFO_FL_STD) { // 定义vidioc_op函数指针类型 typedef int (*vidioc_op)(struct file *file, void *fh, void *p); // 获取ioctl_ops const void *p = vfd->ioctl_ops; // 获取vidioc函数指针 const vidioc_op *vidioc = p + info->u.offset; // 调用vidioc函数 ret = (*vidioc)(file, fh, arg); } // 判断是否为函数ioctl else if (info->flags & INFO_FL_FUNC) { // 调用函数ioctl ret = info->u.func(ops, file, fh, arg); } // 判断是否有默认的ioctl else if (!ops->vidioc_default) { ret = -ENOTTY; } // 调用默认的ioctl else { ret = ops->vidioc_default(file, fh, vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, cmd, arg); }
/driver/media/usb/unc/uvc_v4l2.c
const struct v4l2_ioctl_ops uvc_ioctl_ops = { .vidioc_querycap = uvc_ioctl_querycap, .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap, .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out, .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap, .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out, .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap, .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out, .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap, .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out, .vidioc_reqbufs = uvc_ioctl_reqbufs, .vidioc_querybuf = uvc_ioctl_querybuf, .vidioc_qbuf = uvc_ioctl_qbuf, .vidioc_dqbuf = uvc_ioctl_dqbuf, .vidioc_create_bufs = uvc_ioctl_create_bufs, .vidioc_streamon = uvc_ioctl_streamon, .vidioc_streamoff = uvc_ioctl_streamoff, .vidioc_enum_input = uvc_ioctl_enum_input, .vidioc_g_input = uvc_ioctl_g_input, .vidioc_s_input = uvc_ioctl_s_input, .vidioc_queryctrl = uvc_ioctl_queryctrl, .vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl, .vidioc_g_ctrl = uvc_ioctl_g_ctrl, .vidioc_s_ctrl = uvc_ioctl_s_ctrl, .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls, .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls, .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls, .vidioc_querymenu = uvc_ioctl_querymenu, .vidioc_g_selection = uvc_ioctl_g_selection, .vidioc_g_parm = uvc_ioctl_g_parm, .vidioc_s_parm = uvc_ioctl_s_parm, .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes, .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals, .vidioc_subscribe_event = uvc_ioctl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_default = uvc_ioctl_default, };
2.VIDIOC_QUERYCAP
video->streaming->type 应该是在设备被枚举时分析描述符时设置的
.vidioc_querycap = uvc_ioctl_querycap,
uvc_ioctl_querycap
static int uvc_ioctl_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { // 获取video_device结构体 struct video_device *vdev = video_devdata(file); // 获取uvc_fh结构体 struct uvc_fh *handle = file->private_data; // 获取uvc_video_chain结构体 struct uvc_video_chain *chain = handle->chain; // 获取uvc_streaming结构体 struct uvc_streaming *stream = handle->stream; // 设置驱动名称 strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver)); // 设置设备名称 strlcpy(cap->card, vdev->name, sizeof(cap->card)); // 设置总线信息 usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); // 设置设备能力 cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING | chain->caps; // 设置设备类型 if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; return 0; }
// 设置设备类型 if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
3.VIDIOC_ENUM_FMT
format数组应是在设备被枚举时设置的
static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, struct v4l2_fmtdesc *fmt) { struct uvc_format *format; enum v4l2_buf_type type = fmt->type; // 获取视频流类型 __u32 index = fmt->index; // 获取视频流格式索引 if (fmt->type != stream->type || fmt->index >= stream->nformats) // 如果视频流类型不匹配或者视频流格式索引超出范围 return -EINVAL; // 返回无效参数错误 memset(fmt, 0, sizeof(*fmt)); // 清空fmt结构体 fmt->index = index; // 设置视频流格式索引 fmt->type = type; // 设置视频流类型 format = &stream->format[fmt->index]; // 获取视频流格式 fmt->flags = 0; // 清空标志位 if (format->flags & UVC_FMT_FLAG_COMPRESSED) // 如果视频流格式是压缩格式 fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; // 设置标志位 strlcpy(fmt->description, format->name, sizeof(fmt->description)); // 设置视频流格式描述 fmt->description[sizeof(fmt->description) - 1] = 0; // 设置视频流格式描述 fmt->pixelformat = format->fcc; // 设置视频流像素格式 return 0; // 返回成功 }
format = &stream->format[fmt->index]; // 获取视频流格式
4.VIDIOC_G_FMT
uvc_v4l2_get_format // USB摄像头支持多种格式fromat, 每种格式下有多种frame(比如分辨率)
static int uvc_v4l2_get_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { // 获取当前流的格式和帧 struct uvc_format *format; struct uvc_frame *frame; int ret = 0; // 如果请求的格式不是当前流的格式,返回错误 if (fmt->type != stream->type) return -EINVAL; // 获取当前流的格式和帧 mutex_lock(&stream->mutex); format = stream->cur_format; frame = stream->cur_frame; // 如果当前流的格式或帧为空,返回错误 if (format == NULL || frame == NULL) { ret = -EINVAL; goto done; } // 设置请求的格式 fmt->fmt.pix.pixelformat = format->fcc; fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; done: mutex_unlock(&stream->mutex); return ret; }
// 获取当前流的格式和帧 struct uvc_format *format; struct uvc_frame *frame; // 获取当前流的格式和帧 mutex_lock(&stream->mutex); format = stream->cur_format; frame = stream->cur_frame;
5.VIDIOC_TRY_FMT
uvc_v4l2_try_format
/* Check if the hardware supports the requested format. */ /* Find the closest image size. The distance between image sizes is * the size in pixels of the non-overlapping regions between the * requested size and the frame-specified size. */ uvc_v4l2_try_format static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) { struct uvc_format *format = NULL; // 初始化uvc_format结构体指针 struct uvc_frame *frame = NULL; // 初始化uvc_frame结构体指针 __u16 rw, rh; // 定义宽度和高度 unsigned int d, maxd; // 定义距离和最大距离 unsigned int i; // 定义无符号整型变量i __u32 interval; // 定义无符号32位整型变量interval int ret = 0; // 定义整型变量ret并初始化为0 __u8 *fcc; // 定义指向无符号8位整型变量的指针fcc if (fmt->type != stream->type) // 如果格式类型不匹配 return -EINVAL; // 返回无效参数错误 fcc = (__u8 *)&fmt->fmt.pix.pixelformat; // 将fcc指针指向像素格式 uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", fmt->fmt.pix.pixelformat, fcc[0], fcc[1], fcc[2], fcc[3], fmt->fmt.pix.width, fmt->fmt.pix.height); // 打印正在尝试的格式 /* Check if the hardware supports the requested format, use the default * format otherwise. */ for (i = 0; i < stream->nformats; ++i) { // 遍历所有格式 format = &stream->format[i]; // 将format指针指向当前格式 if (format->fcc == fmt->fmt.pix.pixelformat) // 如果当前格式与请求格式匹配 break; // 跳出循环 } if (i == stream->nformats) { // 如果没有匹配的格式 format = stream->def_format; // 使用默认格式 fmt->fmt.pix.pixelformat = format->fcc; // 将请求格式设置为默认格式 } /* 寻找最接近的图像大小。图像大小之间的距离是请求大小和帧指定大小之间的非重叠区域的大小。*/ rw = fmt->fmt.pix.width; // 获取请求的宽度 rh = fmt->fmt.pix.height; // 获取请求的高度 maxd = (unsigned int)-1; // 初始化最大距离 for (i = 0; i < format->nframes; ++i) { // 遍历所有帧 __u16 w = format->frame[i].wWidth; // 获取当前帧的宽度 __u16 h = format->frame[i].wHeight; // 获取当前帧的高度 d = min(w, rw) * min(h, rh); // 计算非重叠区域的大小 d = w*h + rw*rh - 2*d; // 计算距离 if (d < maxd) { // 如果距离小于最大距离 maxd = d; // 更新最大距离 frame = &format->frame[i]; // 更新帧 } if (maxd == 0) // 如果最大距离为0 break; // 跳出循环 } if (frame == NULL) { // 如果没有找到匹配的帧 uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", fmt->fmt.pix.width, fmt->fmt.pix.height); // 打印不支持的大小 return -EINVAL; // 返回无效参数错误 } /* Use the default frame interval. */ // 使用默认帧间隔 interval = frame->dwDefaultFrameInterval; // 打印使用的帧间隔 uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, (100000000/interval)%10); /* Set the format index, frame index and frame interval. */ // 设置格式索引、帧索引和帧间隔 memset(probe, 0, sizeof *probe); probe->bmHint = 1; /* dwFrameInterval */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Some webcams stall the probe control set request when the * dwMaxVideoFrameSize field is set to zero. The UVC specification * clearly states that the field is read-only from the host, so this * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by * the webcam to work around the problem. * * The workaround could probably be enabled for all webcams, so the * quirk can be removed if needed. It's currently useful to detect * webcam bugs and fix them before they hit the market (providing * developers test their webcams with the Linux driver as well as with * the Windows driver). */ // 加锁 mutex_lock(&stream->mutex); // 如果设备有 UVC_QUIRK_PROBE_EXTRAFIELDS 标志,设置探测结构体的最大视频帧大小 if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) probe->dwMaxVideoFrameSize = stream->ctrl.dwMaxVideoFrameSize; /* Probe the device. */ // 探测设备 ret = uvc_probe_video(stream, probe); // 解锁 mutex_unlock(&stream->mutex); // 如果探测失败,跳转到 done 标签 if (ret < 0) goto done; // 设置请求的格式 fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; // 如果 uvc_format 不为空,将当前流的格式赋值给它 if (uvc_format != NULL) *uvc_format = format; // 如果 uvc_frame 不为空,将当前流的帧赋值给它 if (uvc_frame != NULL) *uvc_frame = frame; done: // 返回探测结果 return ret; }
6.VIDIOC_S_FMT /
只是把参数保存起来,还没有发给USB摄像头
uvc_v4l2_set_format uvc_v4l2_try_format video->streaming->cur_format = format; video->streaming->cur_frame = frame;
static int uvc_v4l2_set_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { // 定义探测结构体、格式、帧和返回值 struct uvc_streaming_control probe; struct uvc_format *format; struct uvc_frame *frame; int ret; // 如果请求的格式不是当前流的格式,返回错误 if (fmt->type != stream->type) return -EINVAL; // 尝试设置格式 ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); if (ret < 0) return ret; // 上锁 mutex_lock(&stream->mutex); // 如果队列已经分配,返回错误 if (uvc_queue_allocated(&stream->queue)) { ret = -EBUSY; goto done; } // 设置控制结构体、当前格式和当前帧 stream->ctrl = probe; stream->cur_format = format; stream->cur_frame = frame; done: // 解锁 mutex_unlock(&stream->mutex); return ret;
}
7.VIDIOC_REQBUFS
uvc_ioctl_reqbufs
static int uvc_ioctl_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; int ret; // 获取权限 ret = uvc_acquire_privileges(handle); if (ret < 0) return ret; // 上锁 mutex_lock(&stream->mutex); // 请求缓冲区 ret = uvc_request_buffers(&stream->queue, rb); // 解锁 mutex_unlock(&stream->mutex); if (ret < 0) return ret; // 如果请求成功,解除权限 if (ret == 0) uvc_dismiss_privileges(handle); return 0; }
uvc_ioctl_reqbufs->uvc_request_buffers
int uvc_request_buffers(struct uvc_video_queue *queue, struct v4l2_requestbuffers *rb) { int ret; mutex_lock(&queue->mutex); ret = vb2_reqbufs(&queue->queue, rb); mutex_unlock(&queue->mutex); return ret ? ret : rb->count; }
uvc_ioctl_reqbufs->uvc_request_buffers->vb2_reqbuf
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { int ret = __verify_memory_type(q, req->memory, req->type); return ret ? ret : __reqbufs(q, req); }
uvc_ioctl_reqbufs->uvc_request_buffers->vb2_reqbuf->__reqbufs分配缓冲区
static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { unsigned int num_buffers, allocated_buffers, num_planes = 0; int ret; // 检查是否正在流传输 if (q->streaming) { dprintk(1, "streaming active\n"); return -EBUSY; } // 检查是否需要重新分配内存 if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { /* * We already have buffers allocated, so first check if they * are not in use and can be freed. */ mutex_lock(&q->mmap_lock); // 检查内存是否正在使用 if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { mutex_unlock(&q->mmap_lock); dprintk(1, "memory in use, cannot free\n"); return -EBUSY; } /* * Call queue_cancel to clean up any buffers in the PREPARED or * QUEUED state which is possible if buffers were prepared or * queued without ever calling STREAMON. */ // 取消队列中所有处于 PREPARED 或 QUEUED 状态的缓冲区 __vb2_queue_cancel(q); // 释放所有缓冲区 ret = __vb2_queue_free(q, q->num_buffers); mutex_unlock(&q->mmap_lock); if (ret) return ret; /* * In case of REQBUFS(0) return immediately without calling * driver's queue_setup() callback and allocating resources. */ // 如果请求的缓冲区数量为 0,则直接返回 if (req->count == 0) return 0; } /* * Make sure the requested values and current defaults are sane. */ // 确保请求的值和当前默认值是合理的 num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed); memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); q->memory = req->memory; /* * Ask the driver how many buffers and planes per buffer it requires. * Driver also sets the size and allocator context for each plane. */ // 询问驱动程序需要多少缓冲区和每个缓冲区的平面数 // 驱动程序还为每个平面设置大小和分配器上下文 ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); if (ret) return ret; /* Finally, allocate buffers and video memory */ // 最后,分配缓冲区和视频内存 allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes); if (allocated_buffers == 0) { dprintk(1, "memory allocation failed\n"); return -ENOMEM; } /* * There is no point in continuing if we can't allocate the minimum * number of buffers needed by this vb2_queue. */ // 如果我们无法为此 vb2_queue 分配所需的最小缓冲区数量,则继续没有意义 if (allocated_buffers < q->min_buffers_needed) ret = -ENOMEM; /* * Check if driver can handle the allocated number of buffers. */ // 检查驱动程序是否可以处理分配的缓冲区数量 if (!ret && allocated_buffers < num_buffers) { num_buffers = allocated_buffers; // 询问驱动程序需要多少缓冲区和每个缓冲区的平面数 // 驱动程序还为每个平面设置大小和分配器上下文 ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); // 如果分配的缓冲区数量小于请求的数量,则返回错误 if (!ret && allocated_buffers < num_buffers) ret = -ENOMEM; /* * Either the driver has accepted a smaller number of buffers, * or .queue_setup() returned an error */ } // 获取互斥锁 mutex_lock(&q->mmap_lock); q->num_buffers = allocated_buffers; if (ret < 0) { /* * Note: __vb2_queue_free() will subtract 'allocated_buffers' * from q->num_buffers. */ // 如果分配失败,则释放所有缓冲区 __vb2_queue_free(q, allocated_buffers); mutex_unlock(&q->mmap_lock); return ret; } // 释放互斥锁 mutex_unlock(&q->mmap_lock); /* * Return the number of successfully allocated buffers * to the userspace. */ // 返回成功分配的缓冲区数量 req->count = allocated_buffers; q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); return 0; }
8.VIDIOC_QUERYBUF
uvc_ioctl_querybuf
static int uvc_ioctl_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; // 如果没有权限,返回错误 if (!uvc_has_privileges(handle)) return -EBUSY; // 查询缓冲区 return uvc_query_buffer(&stream->queue, buf); }
uvc_ioctl_querybuf
uvc_query_buffer
int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf) { int ret; mutex_lock(&queue->mutex); ret = vb2_querybuf(&queue->queue, buf); mutex_unlock(&queue->mutex); return ret; }
uvc_ioctl_querybuf uvc_query_buffer vb2_querybuf
int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) { // 获取对应的vb2_buffer struct vb2_buffer *vb; int ret; // 检查buffer类型是否正确 if (b->type != q->type) { dprintk(1, "wrong buffer type\n"); return -EINVAL; } // 检查buffer index是否越界 if (b->index >= q->num_buffers) { dprintk(1, "buffer index out of range\n"); return -EINVAL; } vb = q->bufs[b->index]; // 验证planes数组是否正确 ret = __verify_planes_array(vb, b); // 填充v4l2_buffer结构体 if (!ret) __fill_v4l2_buffer(vb, b); return ret; }
uvc_ioctl_querybuf uvc_query_buffer vb2_querybuf __fill_v4l2_buffer
复制参数
memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
__fill_v4l2_buffer
static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; /* 复制回时间戳、标志等数据 */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); b->reserved2 = vb->v4l2_buf.reserved2; b->reserved = vb->v4l2_buf.reserved; if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { /* * 如果用户空间提供了一个数组,填充与平面相关的数据。 * 调用者已经验证了内存和大小。 */ b->length = vb->num_planes; memcpy(b->m.planes, vb->v4l2_planes, b->length * sizeof(struct v4l2_plane)); } else { /* * 即使对于单平面缓冲区,我们也使用v4l2_planes数组中的长度和偏移量, * 但用户空间不使用。 */ b->length = vb->v4l2_planes[0].length; b->bytesused = vb->v4l2_planes[0].bytesused; if (q->memory == V4L2_MEMORY_MMAP) b->m.offset = vb->v4l2_planes[0].m.mem_offset; else if (q->memory == V4L2_MEMORY_USERPTR) b->m.userptr = vb->v4l2_planes[0].m.userptr; else if (q->memory == V4L2_MEMORY_DMABUF) b->m.fd = vb->v4l2_planes[0].m.fd; } /* * Clear any buffer state related flags. */ // 清除任何与缓冲区状态相关的标志。 b->flags &= ~V4L2_BUFFER_MASK_FLAGS; // 将时间戳标志设置为队列的时间戳标志 b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK; // 如果时间戳标志不是V4L2_BUF_FLAG_TIMESTAMP_COPY,则删除时间戳源位并从队列中获取时间戳源。 if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_COPY) { b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; } switch (vb->state) { case VB2_BUF_STATE_QUEUED: case VB2_BUF_STATE_ACTIVE: // 如果缓冲区状态为VB2_BUF_STATE_QUEUED或VB2_BUF_STATE_ACTIVE,则将V4L2_BUF_FLAG_QUEUED标志设置为1。 b->flags |= V4L2_BUF_FLAG_QUEUED; break; case VB2_BUF_STATE_ERROR: // 如果缓冲区状态为VB2_BUF_STATE_ERROR,则将V4L2_BUF_FLAG_ERROR标志设置为1。 b->flags |= V4L2_BUF_FLAG_ERROR; /* fall through */ case VB2_BUF_STATE_DONE: // 如果缓冲区状态为VB2_BUF_STATE_DONE,则将V4L2_BUF_FLAG_DONE标志设置为1。 b->flags |= V4L2_BUF_FLAG_DONE; break; case VB2_BUF_STATE_PREPARED: // 如果缓冲区状态为VB2_BUF_STATE_PREPARED,则将V4L2_BUF_FLAG_PREPARED标志设置为1。 b->flags |= V4L2_BUF_FLAG_PREPARED; break; case VB2_BUF_STATE_PREPARING: case VB2_BUF_STATE_DEQUEUED: // 如果缓冲区状态为VB2_BUF_STATE_PREPARING或VB2_BUF_STATE_DEQUEUED,则不做任何操作。 break; } // 如果缓冲区正在使用,则将V4L2_BUF_FLAG_MAPPED标志设置为1。 if (__buffer_in_use(q, vb)) b->flags |= V4L2_BUF_FLAG_MAPPED; }
9.mmap
uvc_v4l2_mmap
static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); return uvcg_queue_mmap(&uvc->video.queue, vma); }
uvc_v4l2_mmap
uvcg_queue_mmap
vb2_mmap
int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) { // 将虚拟内存地址转换为物理内存地址 unsigned long off = vma->vm_pgoff << PAGE_SHIFT; // 定义一个vb2_buffer结构体指针 struct vb2_buffer *vb; // 定义一个unsigned int类型的变量buffer和plane unsigned int buffer = 0, plane = 0; // 定义一个int类型的变量ret int ret; // 定义一个unsigned long类型的变量length // 判断内存类型是否为V4L2_MEMORY_MMAP if (q->memory != V4L2_MEMORY_MMAP) { dprintk(1, "queue is not currently set up for mmap\n"); return -EINVAL; } /* * 检查内存区域访问模式。 */ if (!(vma->vm_flags & VM_SHARED)) { dprintk(1, "invalid vma flags, VM_SHARED needed\n"); return -EINVAL; } if (V4L2_TYPE_IS_OUTPUT(q->type)) { if (!(vma->vm_flags & VM_WRITE)) { dprintk(1, "invalid vma flags, VM_WRITE needed\n"); return -EINVAL; } } else { if (!(vma->vm_flags & VM_READ)) { dprintk(1, "invalid vma flags, VM_READ needed\n"); return -EINVAL; } } if (vb2_fileio_is_active(q)) { dprintk(1, "mmap: file io in progress\n"); return -EBUSY; } /* * Find the plane corresponding to the offset passed by userspace. */ // 找到与用户空间传递的偏移量对应的平面 ret = __find_plane_by_offset(q, off, &buffer, &plane); if (ret) return ret; vb = q->bufs[buffer]; /* * MMAP requires page_aligned buffers. * The buffer length was page_aligned at __vb2_buf_mem_alloc(), * so, we need to do the same here. */ // MMAP需要页面对齐的缓冲区。 // 缓冲区长度在__vb2_buf_mem_alloc()中进行了页面对齐, // 因此我们需要在此处执行相同的操作。 length = PAGE_ALIGN(vb->v4l2_planes[plane].length); if (length < (vma->vm_end - vma->vm_start)) { dprintk(1, "MMAP invalid, as it would overflow buffer length\n"); return -EINVAL; } mutex_lock(&q->mmap_lock); // 调用内存操作函数mmap ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma); mutex_unlock(&q->mmap_lock); if (ret) return ret; dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane); return 0; }
10.VIDIOC_QBUF
uvc_ioctl_qbuf
static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; // 如果没有权限,返回错误 if (!uvc_has_privileges(handle)) return -EBUSY; // 将缓冲区加入队列 return uvc_queue_buffer(&stream->queue, buf); }
uvc_queue_buffer
uvc_queue_buffer
vb2_qbuf
vb2_internal_qbuf
将缓冲区加入队列
static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { /* 调用驱动程序提供的函数做些预处理 */ int ret = vb2_queue_or_prepare_buf(q, b, "qbuf"); struct vb2_buffer *vb; if (ret) return ret; vb = q->bufs[b->index]; switch (vb->state) { case VB2_BUF_STATE_DEQUEUED: ret = __buf_prepare(vb, b); if (ret) return ret; break; case VB2_BUF_STATE_PREPARED: break; case VB2_BUF_STATE_PREPARING: dprintk(1, "buffer still being prepared\n"); return -EINVAL; default: dprintk(1, "invalid buffer state %d\n", vb->state); return -EINVAL; } /* * Add to the queued buffers list, a buffer will stay on it until * dequeued in dqbuf. */ /* 把缓冲区放入队列的尾部 */ list_add_tail(&vb->queued_entry, &q->queued_list); q->queued_count++; q->waiting_for_buffers = false; vb->state = VB2_BUF_STATE_QUEUED; if (V4L2_TYPE_IS_OUTPUT(q->type)) { /* * For output buffers copy the timestamp if needed, * and the timecode field and flag if needed. */ if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) == V4L2_BUF_FLAG_TIMESTAMP_COPY) vb->v4l2_buf.timestamp = b->timestamp; vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE; if (b->flags & V4L2_BUF_FLAG_TIMECODE) vb->v4l2_buf.timecode = b->timecode; } /* * If already streaming, give the buffer to driver for processing. * If not, the buffer will be given to driver on next streamon. */ if (q->start_streaming_called) __enqueue_in_driver(vb); /* Fill buffer information for the userspace */ __fill_v4l2_buffer(vb, b); /* * If streamon has been called, and we haven't yet called * start_streaming() since not enough buffers were queued, and * we now have reached the minimum number of queued buffers, * then we can finally call start_streaming(). */ if (q->streaming && !q->start_streaming_called && q->queued_count >= q->min_buffers_needed) { ret = vb2_start_streaming(q); if (ret) return ret; } dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); return 0;
}
11.VIDIOC_STREAMON
uvc_ioctl_streamon
uvc_video_enable(video, 1) // 把所设置的参数发给硬件,然后启动摄像头 /* Commit the streaming parameters. */ uvc_commit_video uvc_set_video_ctrl /* 设置格式fromat, frame */ ret = __uvc_query_ctrl(video->dev /* 哪一个USB设备 */, SET_CUR, 0, video->streaming->intfnum /* 哪一个接口: VS */, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, uvc_timeout_param); /* 启动:Initialize isochronous/bulk URBs and allocate transfer buffers. */ uvc_init_video(video, GFP_KERNEL); uvc_init_video_isoc / uvc_init_video_bulk urb->complete = uvc_video_complete; (收到数据后此函数被调用,它又调用video->decode(urb, video, buf); ==> uvc_video_decode_isoc/uvc_video_encode_bulk => uvc_queue_next_buffer => wake_up(&buf->wait);) usb_submit_urb
uvc_ioctl_streamon
static int uvc_ioctl_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; int ret; // 如果没有权限,返回错误 if (!uvc_has_privileges(handle)) return -EBUSY; // 上锁 mutex_lock(&stream->mutex); // 开始流 ret = uvc_queue_streamon(&stream->queue, type); // 解锁 mutex_unlock(&stream->mutex); return ret; }
uvc_queue_streamon vb2_streamon vb2_internal_streamon
static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) { // 检查type是否合法 if (type != q->type) { dprintk(1, "invalid stream type\n"); return -EINVAL; } // 如果已经在streaming了,直接返回0 if (q->streaming) { dprintk(3, "already streaming\n"); return 0; } // 如果没有分配buffer,返回错误 if (!q->num_buffers) { dprintk(1, "no buffers have been allocated\n"); return -EINVAL; } // 如果分配的buffer数量小于最小需要的数量,返回错误 if (q->num_buffers < q->min_buffers_needed) { dprintk(1, "need at least %u allocated buffers\n", q->min_buffers_needed); return -EINVAL; } /* * 如果已经有足够的buffer在队列中,就通知驱动开始streaming */ if (q->queued_count >= q->min_buffers_needed) { int ret = vb2_start_streaming(q); if (ret) { __vb2_queue_cancel(q); return ret; } } // 设置streaming标志位 q->streaming = 1; dprintk(3, "successful\n"); return 0; }
uvc_queue_streamon vb2_streamon vb2_internal_streamon vb2_start_streaming
static int vb2_start_streaming(struct vb2_queue *q) { struct vb2_buffer *vb; int ret; /* 如果有任何缓冲区在流开启之前被排队,我们现在可以将它们传递给驱动程序进行处理。 */ list_for_each_entry(vb, &q->queued_list, queued_entry) __enqueue_in_driver(vb); /* 告诉驱动程序开始流 */ q->start_streaming_called = 1; ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count)); if (!ret) return 0; q->start_streaming_called = 0; dprintk(1, "driver refused to start streaming\n"); /* * 如果您看到此警告,则表示驱动程序在失败的start_streaming()之后没有正确清理。 * 有关如何在start_streaming()中将缓冲区返回给vb2的更多信息,请参见videobuf2-core.h中的start_streaming()文档。 */ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { unsigned i; /* * 如果驱动程序没有正确将它们返回给vb2,则强制回收缓冲区。 */ for (i = 0; i < q->num_buffers; ++i) { vb = q->bufs[i]; if (vb->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED); } /* 必须现在为零 */ WARN_ON(atomic_read(&q->owned_by_drv_count)); } /* * 如果done_list不为空,则start_streaming()没有调用vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED),而是STATE_ERROR或STATE_DONE。 */ WARN_ON(!list_empty(&q->done_list)); return ret; }
uvc_queue_streamon vb2_streamon vb2_internal_streamon __vb2_queue_cancel
static void __vb2_queue_cancel(struct vb2_queue *q) { unsigned int i; /* * 停止所有事务并释放所有排队的缓冲区。 */ if (q->start_streaming_called) call_void_qop(q, stop_streaming, q); /* * 如果您看到此警告,则表示驱动程序在stop_streaming()中没有正确清理。 * 有关如何在stop_streaming()中将缓冲区返回给vb2的更多信息,请参见videobuf2-core.h中的stop_streaming()文档。 */ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { for (i = 0; i < q->num_buffers; ++i) if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR); /* 现在必须为零 */ WARN_ON(atomic_read(&q->owned_by_drv_count)); } q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; q->error = 0; /* * 从videobuf的列表中删除所有缓冲区... */ INIT_LIST_HEAD(&q->queued_list); /* * ...和完成列表;在启动取消之前,用户空间将不会收到任何它尚未出队的缓冲区。 */ INIT_LIST_HEAD(&q->done_list); atomic_set(&q->owned_by_drv_count, 0); wake_up_all(&q->done_wq); /* * Reinitialize all buffers for next use. * Make sure to call buf_finish for any queued buffers. Normally * that's done in dqbuf, but that's not going to happen when we * cancel the whole queue. Note: this code belongs here, not in * __vb2_dqbuf() since in vb2_internal_dqbuf() there is a critical * call to __fill_v4l2_buffer() after buf_finish(). That order can't * be changed, so we can't move the buf_finish() to __vb2_dqbuf(). */ for (i = 0; i < q->num_buffers; ++i) { // 获取队列中的缓冲区 struct vb2_buffer *vb = q->bufs[i]; // 如果缓冲区状态不是VB2_BUF_STATE_DEQUEUED,将其状态设置为VB2_BUF_STATE_PREPARED if (vb->state != VB2_BUF_STATE_DEQUEUED) { vb->state = VB2_BUF_STATE_PREPARED; // 调用buf_finish回调函数,以便在取消队列时完成任何排队的缓冲区 call_void_vb_qop(vb, buf_finish, vb); } // 调用__vb2_dqbuf函数,将缓冲区状态设置为VB2_BUF_STATE_ERROR __vb2_dqbuf(vb); } }
12.poll
uvc_v4l2_poll
static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { // 获取文件句柄 struct uvc_fh *handle = file->private_data; // 获取流 struct uvc_streaming *stream = handle->stream; // 打印调试信息 uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); // 调用队列的poll函数 return uvc_queue_poll(&stream->queue, file, wait); }
uvc_v4l2_poll
uvc_queue_poll
unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait) { // 定义返回值 unsigned int ret; // 获取互斥锁 mutex_lock(&queue->mutex); // 调用vb2_poll函数 ret = vb2_poll(&queue->queue, file, wait); // 释放互斥锁 mutex_unlock(&queue->mutex); // 返回结果 return ret; }
uvc_v4l2_poll
uvc_queue_poll
vb2_poll
unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { // 获取文件描述符对应的video_device结构体 struct video_device *vfd = video_devdata(file); // 获取用户请求的事件 unsigned long req_events = poll_requested_events(wait); // 初始化vb指针 struct vb2_buffer *vb = NULL; // 初始化返回值 unsigned int res = 0; // 初始化标志位 unsigned long flags; // 如果驱动程序使用struct v4l2_fh结构体,则检查是否有挂起的事件 if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { struct v4l2_fh *fh = file->private_data; // 如果有挂起的事件,则返回POLLPRI if (v4l2_event_pending(fh)) res = POLLPRI; // 如果用户请求的事件中包含POLLPRI,则等待事件 else if (req_events & POLLPRI) poll_wait(file, &fh->wait, wait); } if (!V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLIN | POLLRDNORM))) return res; if (V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLOUT | POLLWRNORM))) return res; /* * 如果队列中没有缓冲区,且文件I/O模拟器未启动,则启动文件I/O模拟器。 */ if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) { /* * 如果队列类型不是输出类型,且I/O模式为读,且用户请求的事件中包含POLLIN或POLLRDNORM,则启动文件I/O模拟器。 */ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && (req_events & (POLLIN | POLLRDNORM))) { /* * 如果文件I/O模拟器初始化失败,则返回POLLERR。 */ if (__vb2_init_fileio(q, 1)) return res | POLLERR; } /* * 如果队列类型是输出类型,且I/O模式为写,且用户请求的事件中包含POLLOUT或POLLWRNORM,则启动文件I/O模拟器。 */ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && (req_events & (POLLOUT | POLLWRNORM))) { /* * 如果文件I/O模拟器初始化失败,则返回POLLERR。 */ if (__vb2_init_fileio(q, 0)) return res | POLLERR; /* * 对于输出队列,可以立即进行写操作。 */ return res | POLLOUT | POLLWRNORM; } } /* * 如果队列不在流式传输状态,或者错误标志被设置,则没有等待的内容。 */ if (!vb2_is_streaming(q) || q->error) return res | POLLERR; /* * 为了与vb1兼容:如果尚未调用QBUF,则返回POLLERR。这仅影响捕获队列,输出队列将始终将waiting_for_buffers初始化为false。 */ if (q->waiting_for_buffers) return res | POLLERR; /* * 对于输出流,只要排队的缓冲区少于可用的缓冲区,就可以写入。 */ if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers) return res | POLLOUT | POLLWRNORM; // 如果done_list为空,则等待 if (list_empty(&q->done_list)) poll_wait(file, &q->done_wq, wait); /* * 取出第一个可用于出队的缓冲区。 */ spin_lock_irqsave(&q->done_lock, flags); if (!list_empty(&q->done_list)) vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); spin_unlock_irqrestore(&q->done_lock, flags); /* * 如果缓冲区存在且状态为VB2_BUF_STATE_DONE或VB2_BUF_STATE_ERROR,则返回POLLOUT | POLLWRNORM或POLLIN | POLLRDNORM。 */ if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) { return (V4L2_TYPE_IS_OUTPUT(q->type)) ? res | POLLOUT | POLLWRNORM : res | POLLIN | POLLRDNORM; } return res; }
13.VIDIOC_DQBUF
uvc_ioctl_dqbuf
// 从队列中取出缓冲区 static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; // 如果没有权限,返回错误 if (!uvc_has_privileges(handle)) return -EBUSY; // 从队列中取出缓冲区 return uvc_dequeue_buffer(&stream->queue, buf, file->f_flags & O_NONBLOCK); }
uvc_ioctl_dqbuf
uvc_dequeue_buffer
// 从视频队列中取出一个缓冲区 int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf, int nonblocking) { // 定义返回值 int ret; // 获取互斥锁 mutex_lock(&queue->mutex); // 调用vb2_dqbuf函数从视频队列中取出一个缓冲区 ret = vb2_dqbuf(&queue->queue, buf, nonblocking); // 释放互斥锁 mutex_unlock(&queue->mutex); // 返回结果 return ret; }
uvc_ioctl_dqbuf
uvc_dequeue_buffer
vb2_dqbuf
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { // 如果文件IO正在进行,则返回忙 if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); // 打印信息 return -EBUSY; // 返回忙 } // 调用内部dqbuf函数 return vb2_internal_dqbuf(q, b, nonblocking); }
uvc_ioctl_dqbuf
uvc_dequeue_buffer
vb2_dqbuf
vb2_internal_dqbuf
static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) { struct vb2_buffer *vb = NULL; int ret; if (b->type != q->type) { // 如果缓冲区类型与队列类型不匹配 dprintk(1, "invalid buffer type\n"); // 打印错误信息 return -EINVAL; // 返回无效参数错误 } ret = __vb2_get_done_vb(q, &vb, b, nonblocking); // 获取已完成的缓冲区 if (ret < 0) // 如果获取失败 return ret; // 返回获取失败的错误码 switch (vb->state) { // 根据缓冲区状态进行不同的操作 case VB2_BUF_STATE_DONE: // 如果缓冲区状态为已完成 dprintk(3, "returning done buffer\n"); // 打印信息 break; case VB2_BUF_STATE_ERROR: // 如果缓冲区状态为错误 dprintk(3, "returning done buffer with errors\n"); // 打印信息 break; default: // 如果缓冲区状态无效 dprintk(1, "invalid buffer state\n"); // 打印错误信息 return -EINVAL; // 返回无效参数错误 } call_void_vb_qop(vb, buf_finish, vb); // 调用buf_finish回调 /* 填充缓冲区信息以供用户空间使用 */ __fill_v4l2_buffer(vb, b); /* 从videobuf队列中删除 */ list_del(&vb->queued_entry); q->queued_count--; /* 回到已出队列状态 */ __vb2_dqbuf(vb); dprintk(1, "dqbuf of buffer %d, with state %d\n", vb->v4l2_buf.index, vb->state); // 打印信息 return 0; // 返回成功 }
14.VIDIOC_STREAMOFF
uvc_ioctl_streamoff
// 停止流 static int uvc_ioctl_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取流 struct uvc_streaming *stream = handle->stream; // 如果没有权限,返回错误 if (!uvc_has_privileges(handle)) return -EBUSY; // 上锁 mutex_lock(&stream->mutex); // 停止流 uvc_queue_streamoff(&stream->queue, type); // 解锁 mutex_unlock(&stream->mutex); return 0; }
uvc_ioctl_streamoff
uvc_queue_streamoff
// uvc_queue_streamoff函数,用于停止视频流 int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type) { int ret; // 获取互斥锁 mutex_lock(&queue->mutex); // 调用vb2_streamoff函数停止视频流 ret = vb2_streamoff(&queue->queue, type); // 释放互斥锁 mutex_unlock(&queue->mutex); // 返回结果 return ret; }
uvc_ioctl_streamoff
uvc_queue_streamoff
vb2_streamoff
int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { // 如果文件IO正在进行中,则返回EBUSY if (vb2_fileio_is_active(q)) { dprintk(1, "file io in progress\n"); return -EBUSY; } // 调用vb2_internal_streamoff函数停止streaming return vb2_internal_streamoff(q, type); }
uvc_ioctl_streamoff
uvc_queue_streamoff
vb2_streamoff
vb2_internal_streamoff
static int vb2_internal_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) { // 如果type不等于当前队列的type,返回错误 if (type != q->type) { dprintk(1, "invalid stream type\n"); return -EINVAL; } /* * Cancel will pause streaming and remove all buffers from the driver * and videobuf, effectively returning control over them to userspace. * * Note that we do this even if q->streaming == 0: if you prepare or * queue buffers, and then call streamoff without ever having called * streamon, you would still expect those buffers to be returned to * their normal dequeued state. */ // 取消streaming并将所有缓冲区从驱动程序和videobuf中删除,将控制权返回给用户空间 __vb2_queue_cancel(q); // 如果队列类型不是输出类型,则等待缓冲区 q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type); dprintk(3, "successful\n"); return 0; }
uvc_ioctl_streamoff
uvc_queue_streamoff
vb2_streamoff
vb2_internal_streamoff
__vb2_queue_cancel
static void __vb2_queue_cancel(struct vb2_queue *q) { unsigned int i; /* * 停止所有事务并释放所有排队的缓冲区。 */ if (q->start_streaming_called) call_void_qop(q, stop_streaming, q); /* * 如果您看到此警告,则表示驱动程序在stop_streaming()中没有正确清理。 * 有关如何在stop_streaming()中将缓冲区返回给vb2的更多信息,请参见videobuf2-core.h中的stop_streaming()文档。 */ if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { for (i = 0; i < q->num_buffers; ++i) if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE) vb2_buffer_done(q->bufs[i], VB2_BUF_STATE_ERROR); /* 现在必须为零 */ WARN_ON(atomic_read(&q->owned_by_drv_count)); } q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; q->error = 0; /* * 从videobuf的列表中删除所有缓冲区... */ INIT_LIST_HEAD(&q->queued_list); /* * ...和完成列表;在启动取消之前,用户空间将不会收到任何它尚未出队的缓冲区。 */ INIT_LIST_HEAD(&q->done_list); atomic_set(&q->owned_by_drv_count, 0); wake_up_all(&q->done_wq); /* * Reinitialize all buffers for next use. * Make sure to call buf_finish for any queued buffers. Normally * that's done in dqbuf, but that's not going to happen when we * cancel the whole queue. Note: this code belongs here, not in * __vb2_dqbuf() since in vb2_internal_dqbuf() there is a critical * call to __fill_v4l2_buffer() after buf_finish(). That order can't * be changed, so we can't move the buf_finish() to __vb2_dqbuf(). */ for (i = 0; i < q->num_buffers; ++i) { // 获取队列中的缓冲区 struct vb2_buffer *vb = q->bufs[i]; // 如果缓冲区状态不是VB2_BUF_STATE_DEQUEUED,将其状态设置为VB2_BUF_STATE_PREPARED if (vb->state != VB2_BUF_STATE_DEQUEUED) { vb->state = VB2_BUF_STATE_PREPARED; // 调用buf_finish回调函数,以便在取消队列时完成任何排队的缓冲区 call_void_vb_qop(vb, buf_finish, vb); } // 调用__vb2_dqbuf函数,将缓冲区状态设置为VB2_BUF_STATE_ERROR __vb2_dqbuf(vb); } }
分析设置亮度过程:
ioctl: VIDIOC_S_CTRL
uvc_ctrl_set uvc_ctrl_commit __uvc_ctrl_commit(video, 0); uvc_ctrl_commit_entity(video->dev, entity, rollback); ret = uvc_query_ctrl(dev /* 哪一个USB设备 /, SET_CUR, ctrl->entity->id / 哪一个unit/terminal /,dev->intfnum / 哪一个接口: VC interface */, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size);
uvc_ioctl_s_ctrl
static int uvc_ioctl_s_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) { // 获取文件句柄 struct uvc_fh *handle = fh; // 获取视频链 struct uvc_video_chain *chain = handle->chain; // 定义一个v4l2_ext_control结构体 struct v4l2_ext_control xctrl; // 定义返回值 int ret; // 初始化xctrl结构体 memset(&xctrl, 0, sizeof(xctrl)); // 将ctrl结构体中的id和value赋值给xctrl结构体 xctrl.id = ctrl->id; xctrl.value = ctrl->value; // 开始控制 ret = uvc_ctrl_begin(chain); if (ret < 0) return ret; // 设置控制 ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { // 回滚控制 uvc_ctrl_rollback(handle); return ret; } // 提交控制 ret = uvc_ctrl_commit(handle, &xctrl, 1); if (ret < 0) return ret; // 将xctrl结构体中的value赋值给ctrl结构体 ctrl->value = xctrl.value; return 0; }
uvc_ioctl_s_ctrl
uvc_ctrl_commit
__uvc_ctrl_commit
uvc_ctrl_commit_entity
static int uvc_ctrl_commit_entity(struct uvc_device *dev, struct uvc_entity *entity, int rollback) { // 如果entity为空,直接返回0 if (entity == NULL) return 0; // 遍历entity的所有控制项 for (unsigned int i = 0; i < entity->ncontrols; ++i) { // 获取控制项 struct uvc_control *ctrl = &entity->controls[i]; // 如果控制项未初始化,则跳过 if (!ctrl->initialized) continue; /* Reset the loaded flag for auto-update controls that were * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent * uvc_ctrl_get from using the cached value, and for write-only * controls to prevent uvc_ctrl_set from setting bits not * explicitly set by the user. */ // 如果控制项是自动更新或者不支持获取当前值,则将loaded标志位重置为0 if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE || !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) ctrl->loaded = 0; // 如果控制项没有被修改,则跳过 if (!ctrl->dirty) continue; // 如果不是回滚操作,则将控制项的当前值写入硬件 if (!rollback) ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id, dev->intfnum, ctrl->info.selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info.size); else ret = 0; // 如果是回滚操作或者写入硬件失败,则将控制项的当前值重置为备份值 if (rollback || ret < 0) memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), ctrl->info.size); // 将控制项的dirty标志位重置为0 ctrl->dirty = 0; // 如果写入硬件失败,则返回错误码 if (ret < 0) return ret; } return 0; }
if (!rollback)
ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id,
dev->intfnum, ctrl->info.selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
else
ret = 0;
// 如果是回滚操作或者写入硬件失败,则将控制项的当前值重置为备份值 if (rollback || ret < 0) memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), ctrl->info.size);
dev:哪一个USB设备
ctrl->entity->id:哪一个,unit/terminal
dev->intfnum:哪一个接口, VC interface
ret = uvc_query_ctrl(dev /* 哪一个USB设备 /, SET_CUR, ctrl->entity->id / 哪一个unit/terminal /,dev->intfnum / 哪一个接口: VC interface */, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size);
如果文章对您有帮助,点赞👍支持,感谢🤝