uvc驱动ioctl分析上

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
数据传输服务 DTS,数据同步 1个月
简介: uvc驱动ioctl分析上

uvc_ioctl_querycap查询设备的能力

这个函数是用于处理V4L2设备的查询能力(querycap)的ioctl操作,它的作用是填充并返回设备的能力信息。

函数的主要步骤和功能概述如下:

从file中获取相关的结构体指针,包括video_device结构体指针vdev、uvc_fh结构体指针handle和uvc_video_chain结构体指针chain。

从stream中获取uvc_streaming结构体指针stream。

使用strlcpy函数设置设备的驱动名称、设备名称和总线信息,分别存储在cap->driver、cap->card和cap->bus_info中。

设置设备的能力信息,包括capabilities和device_caps。capabilities是设备的通用能力,包括V4L2_CAP_DEVICE_CAPS、V4L2_CAP_STREAMING以及chain->caps(从chain中获取的能力信息)。device_caps是设备的具体能力,根据stream的类型(是视频捕获还是视频输出)来设置对应的能力信息。

返回0,表示ioctl操作成功。

总的来说,这个函数主要用于填充并返回V4L2设备的能力信息,包括驱动名称、设备名称、总线信息以及通用和具体的能力信息。它是查询设备能力的一部分,可以通过调用相应的ioctl命令来获取设备的能力信息。

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;
}

uvc_ioctl_enum_fmt处理V4L2设备的枚举格式(enum_fmt)的ioctl操作

这个函数是用于处理V4L2设备的枚举格式(enum_fmt)的ioctl操作,它的作用是返回指定视频流类型和索引的视频格式信息。

函数的主要步骤和功能概述如下:

获取函数参数中的视频流类型type和视频流格式索引index。

首先检查视频流类型是否匹配以及视频流格式索引是否超出范围,如果不匹配或索引超出范围,则返回无效参数错误-EINVAL。

使用memset函数清空fmt结构体。

将视频流格式索引和视频流类型设置回fmt结构体。

根据视频流格式索引获取相应的视频流格式信息。

根据视频流格式的标志位,设置fmt结构体中的标志位。如果视频流格式是压缩格式,则设置V4L2_FMT_FLAG_COMPRESSED标志位。

使用strlcpy函数将视频流格式的描述复制到fmt结构体的description字段,并确保字符串以零结尾。

将视频流格式的像素格式(FourCC码)设置到fmt结构体的pixelformat字段。

返回0,表示ioctl操作成功。

总的来说,这个函数主要用于返回指定视频流类型和索引的视频格式信息。它会检查参数的有效性,并将对应的视频流格式信息填充到v4l2_fmtdesc结构体中,包括格式描述、像素格式和相关标志位。该函数是查询视频流格式的一部分,通过调用相应的ioctl命令来获取视频流的格式信息。

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; // 返回成功
}

uvc_ioctl_enum_fmt_vid_out枚举视频输出格式

uvc_ioctl_enum_fmt_vid_cap枚举视频捕获格式

这两个函数 uvc_ioctl_enum_fmt_vid_cap 和 uvc_ioctl_enum_fmt_vid_out 都是用于处理 V4L2 设备的枚举视频格式(enum_fmt)的 ioctl 操作。

这两个函数的步骤和功能概述如下:

首先,从参数 fh 中获取 uvc_fh 结构体指针 handle,该结构体存储了与文件句柄相关的信息。

从 handle 中获取 uvc_streaming 结构体指针 stream,该结构体存储了与视频流相关的信息。

调用 uvc_ioctl_enum_fmt 函数,并将 stream 和 fmt 作为参数传递进去,执行枚举视频格式的操作。

返回 uvc_ioctl_enum_fmt 函数的返回值,表示 ioctl 操作的结果。

这两个函数的实现非常相似,只是在获取 stream 结构体指针的时候使用的是不同的 ioctl 类型(VIDIOC_ENUM_FMT_VID_CAP 和 VIDIOC_ENUM_FMT_VID_OUT)。

总的来说,这两个函数是封装函数,它们通过获取文件句柄的相关信息,然后将参数传递给底层的 uvc_ioctl_enum_fmt 函数来处理枚举视频格式的 ioctl 操作。这样做可以简化代码结构,提高代码的可读性和维护性

static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
                      struct v4l2_fmtdesc *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    return uvc_ioctl_enum_fmt(stream, fmt);
}
static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
                      struct v4l2_fmtdesc *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    return uvc_ioctl_enum_fmt(stream, fmt);
}

uvc_v4l2_get_format获取当前流的视频格式

这个函数 uvc_v4l2_get_format 用于获取当前流的视频格式(get_format)。

函数的步骤和功能概述如下:

首先,从参数 stream 中获取当前流的 uvc_format 结构体指针 format 和 uvc_frame 结构体指针 frame,它们分别表示当前流的格式和帧。

检查请求的格式类型是否与当前流的类型匹配,如果不匹配,则返回无效参数错误。

上锁以保证多线程安全性,获取当前流的格式和帧。

检查当前流的格式和帧是否为空,如果为空,则返回无效参数错误。

根据当前流的格式和帧,设置请求的格式信息到参数 fmt 中。包括像素格式、宽度、高度、采样方式、每行字节数、图像大小、颜色空间等。

解锁并返回操作结果。

总的来说,这个函数通过获取当前流的格式和帧的信息,然后将这些信息填充到参数 fmt 中,从而实现获取当前流视频格式的操作。

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;
}

uvc_ioctl_g_fmt_vid_out获取视频输出格式

uvc_ioctl_g_fmt_vid_cap获取视频捕获格式

这两个函数 uvc_ioctl_g_fmt_vid_cap 和 uvc_ioctl_g_fmt_vid_out 是对应于不同的视频流类型(视频输入和视频输出)的 IOCTL 命令的处理函数。它们都调用了 uvc_v4l2_get_format 函数来获取当前流的视频格式。

函数的步骤和功能概述如下:

从参数 fh 中获取 uvc_fh 结构体指针 handle,然后从 handle 中获取 uvc_streaming 结构体指针 stream,表示当前的流。

调用 uvc_v4l2_get_format 函数,将 stream 和 fmt 作为参数传递给它,以获取当前流的视频格式。

返回 uvc_v4l2_get_format 的执行结果。

总的来说,这两个函数是用于处理 IOCTL 命令,通过调用 uvc_v4l2_get_format 函数来获取当前流的视频格式。其中,uvc_ioctl_g_fmt_vid_cap 用于处理视频输入流的 IOCTL 命令,而 uvc_ioctl_g_fmt_vid_out 用于处理视频输出流的 IOCTL 命令。

static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
                   struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    return uvc_v4l2_get_format(stream, fmt);
}
static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
                   struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    return uvc_v4l2_get_format(stream, fmt);
}

uvc_v4l2_set_format设置V4L2设备的视频格式

这个函数是用于设置V4L2设备的视频格式的函数。

函数的主要步骤和功能概述如下:

定义了一些临时变量,包括探测结构体probe、格式指针format、帧指针frame以及返回值ret。

首先检查请求的格式是否与当前流的格式匹配,如果不匹配,则返回无效参数错误-EINVAL。

调用uvc_v4l2_try_format函数,尝试设置指定的格式,并获取探测结构体、格式和帧的信息。

在设置格式之前,通过调用mutex_lock函数对流的互斥锁进行上锁,确保操作的互斥性。

检查流的队列是否已经分配,如果已经分配,则返回设备忙错误-EBUSY。

将探测结构体、当前格式和当前帧分别设置为流的控制结构体、当前格式和当前帧。

在设置完成后,通过调用mutex_unlock函数解锁流的互斥锁,释放锁资源。

返回设置结果ret,表示设置操作的结果。

总的来说,这个函数主要用于设置V4L2设备的视频格式。它会检查请求的格式是否合法,并尝试设置指定的格式。在设置之前,会检查流的队列是否已经分配,以及使用互斥锁确保操作的互斥性。设置完成后,将相应的格式和帧信息保存在流的结构体中,并返回设置的结果。

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;

}

uvc_ioctl_s_fmt_vid_out设置视频输出格式

uvc_ioctl_s_fmt_vid_cap设置视频捕获格式

这两个函数 uvc_ioctl_s_fmt_vid_cap 和 uvc_ioctl_s_fmt_vid_out 是用于处理设置视频格式的 IOCTL 命令的函数。它们调用了 uvc_acquire_privileges 函数来获取权限,并调用 uvc_v4l2_set_format 函数来设置流的视频格式。

函数的步骤和功能概述如下:

从参数 fh 中获取 uvc_fh 结构体指针 handle,然后从 handle 中获取 uvc_streaming 结构体指针 stream,表示当前的流。

调用 uvc_acquire_privileges 函数来获取权限,以确保可以设置视频格式。如果获取权限失败,直接返回错误。

调用 uvc_v4l2_set_format 函数,将 stream 和 fmt 作为参数传递给它,以设置流的视频格式。

返回 uvc_v4l2_set_format 的执行结果。

总的来说,这两个函数是用于处理设置视频格式的 IOCTL 命令。它们先获取权限,然后调用 uvc_v4l2_set_format 函数来设置流的视频格式。其中,uvc_ioctl_s_fmt_vid_cap 用于处理视频输入流的 IOCTL 命令,而 uvc_ioctl_s_fmt_vid_out 用于处理视频输出流的 IOCTL 命令。

static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
                   struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    int ret;
    ret = uvc_acquire_privileges(handle);
    if (ret < 0)
        return ret;
    return uvc_v4l2_set_format(stream, fmt);
}
static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
                   struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    int ret;
    ret = uvc_acquire_privileges(handle);
    if (ret < 0)
        return ret;
    return uvc_v4l2_set_format(stream, fmt);
}

uvc_v4l2_try_format设置视频格式并进行探测

uvc_v4l2_try_format 函数用于尝试设置视频格式并进行探测,它的功能如下:

初始化一些变量,包括指向 uvc_format 和 uvc_frame 结构体指针的指针。

检查请求的格式类型是否与当前流的类型匹配,如果不匹配则返回无效参数错误。

遍历当前流支持的格式,查找与请求格式匹配的格式,如果找到则跳出循环,否则使用默认格式。

计算最接近请求大小的帧,通过比较请求的宽度和高度与每个帧的宽度和高度之间的非重叠区域的大小来选择最接近的帧。

如果没有找到匹配的帧,返回无效参数错误。

设置探测结构体 probe 的相关参数,包括格式索引、帧索引和帧间隔。

如果设备具有 UVC_QUIRK_PROBE_EXTRAFIELDS 标志,设置探测结构体的最大视频帧大小。

加锁以保护对流的访问,并调用 uvc_probe_video 函数来进行设备探测。

解锁流。

如果探测失败,跳转到标签 done 并返回错误。

根据探测结果,设置请求的格式的各个参数,包括宽度、高度、字节每行、图像大小、颜色空间等。

如果 uvc_format 不为空,则将当前流的格式赋值给它。

如果 uvc_frame 不为空,则将当前流的帧赋值给它。

返回探测结果。

总结来说,该函数用于尝试设置视频格式,并通过比较请求的大小与每个帧的大小来选择最接近的帧。它还进行设备探测,并返回探测结果。

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;
}

uvc_ioctl_try_fmt_vid_cap尝试设置视频捕获格式

uvc_ioctl_try_fmt_vid_out尝试设置视频输出格式

uvc_ioctl_try_fmt_vid_cap 函数和 uvc_ioctl_try_fmt_vid_out 函数是用于尝试设置视频格式的 ioctl 操作的处理函数。

这两个函数的实现非常相似,它们的功能如下:

获取流的相关信息,包括指向 uvc_streaming 结构体的指针。

创建 uvc_streaming_control 结构体 probe。

调用 uvc_v4l2_try_format 函数进行视频格式的尝试设置和设备探测,并传递相关参数。

返回 uvc_v4l2_try_format 函数的执行结果。

这两个函数的作用是在 ioctl 操作中调用 uvc_v4l2_try_format 函数,尝试设置视频格式并进行设备探测。它们将控制结构体 probe 传递给 uvc_v4l2_try_format 函数,并将该函数的执行结果作为自己的返回值

static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
                     struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    struct uvc_streaming_control probe;
    return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}
static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
                     struct v4l2_format *fmt)
{
    struct uvc_fh *handle = fh;
    struct uvc_streaming *stream = handle->stream;
    struct uvc_streaming_control probe;
    return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}

uvc_request_buffers请求缓冲区

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;
}

__reqbufs

uvc_ioctl_reqbufs->uvc_request_buffers->vb2_reqbufs->__reqbufs

函数 __reqbufs 的目标是在视频缓冲队列中请求和分配缓冲区。

该函数的主要步骤如下:

检查队列是否正在进行流传输。如果是,则返回错误码 -EBUSY,表示设备忙碌。

检查是否需要重新分配内存。如果请求的缓冲区数量为0,或者队列已经有缓冲区分配,或者请求的内存类型与队列的内存类型不一致,则需要释放已有的缓冲区。这部分会调用 __vb2_queue_cancel 取消队列中处于 PREPARED 或 QUEUED 状态的缓冲区,并调用 __vb2_queue_free 释放所有缓冲区。

如果请求的缓冲区数量为0,直接返回,不继续进行后续的处理。

对请求的缓冲区数量进行合理性检查,并根据驱动程序的要求设置缓冲区的数量和平面数。这部分会调用 call_qop 函数,并传递参数给 queue_setup 回调函数。

根据驱动程序的要求,分配缓冲区和视频内存。调用 __vb2_queue_alloc 函数进行分配,分配的数量由前面步骤确定。

检查是否成功分配了足够数量的缓冲区。如果分配的数量小于队列所需的最小缓冲区数量,返回错误码 -ENOMEM。

检查驱动程序是否能处理分配的缓冲区数量。如果驱动程序接受了较小数量的缓冲区,或者 queue_setup 函数返回了错误码,会将分配的缓冲区数量更新,并返回错误码 -ENOMEM。

锁定互斥锁,更新队列中的缓冲区数量。

如果之前的步骤中出现了错误,释放所有已分配的缓冲区,并返回错误码。

解锁互斥锁。

将成功分配的缓冲区数量更新到用户空间的请求结构体中。

返回成功分配缓冲区的数量。

总的来说,__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;
}

uvc_query_buffer查询缓冲区

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);
}

vb2_querybuf

uvc_ioctl_querybuf->uvc_query_buffer->vb2_querybuf

函数 vb2_querybuf 的目标是查询视频缓冲区的信息并填充到 v4l2_buffer 结构体中。

该函数的主要步骤如下:

根据给定的索引值 b->index,从队列的缓冲区数组 q->bufs 中获取对应的 vb2_buffer 结构体。

检查 v4l2_buffer 结构体的类型字段 b->type 是否与队列的类型 q->type 匹配,如果不匹配,则返回错误码 -EINVAL。

检查 v4l2_buffer 结构体的索引字段 b->index 是否超出了缓冲区数组的范围 q->num_buffers,如果超出范围,则返回错误码 -EINVAL。

验证 v4l2_buffer 结构体中的 planes 数组是否正确。这一步调用 __verify_planes_array 函数进行验证。

如果前面的验证步骤通过,调用 __fill_v4l2_buffer 函数将 vb2_buffer 结构体的信息填充到 v4l2_buffer 结构体中。

返回验证结果。如果验证步骤中出现了错误,返回相应的错误码;否则返回 0,表示查询成功。

总的来说,vb2_querybuf 函数用于查询指定索引的视频缓冲区信息,并将信息填充到 v4l2_buffer 结构体中。函数会进行一系列的验证步骤,确保输入参数的正确性,并根据缓冲区的实际信息填充到结构体中。

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_qbuf将缓冲区放入队列

uvc_ioctl_qbuf->uvc_queue_buffer->vb2_qbuf->vb2_internal_qbuf

函数 vb2_internal_qbuf 的作用是将一个缓冲区放入队列中。

该函数的主要步骤如下:

调用 vb2_queue_or_prepare_buf 函数对缓冲区进行预处理。这个函数是驱动程序提供的函数,用于执行一些预处理操作。如果预处理失败,则直接返回相应的错误码。

根据缓冲区的索引获取对应的 vb2_buffer 结构体,并根据缓冲区的状态执行相应的操作:

如果缓冲区的状态为 VB2_BUF_STATE_DEQUEUED,调用 __buf_prepare 函数对缓冲区进行准备操作。

如果缓冲区的状态为 VB2_BUF_STATE_PREPARED,表示已经准备好,无需额外操作。

如果缓冲区的状态为 VB2_BUF_STATE_PREPARING,表示缓冲区正在准备中,返回错误码 -EINVAL。

如果缓冲区的状态无效,返回错误码 -EINVAL。

将缓冲区加入到队列的尾部,通过 list_add_tail 函数实现,并更新队列的计数。

设置缓冲区的状态为 VB2_BUF_STATE_QUEUED,表示已经加入队列。

如果队列是输出类型(Output),则根据需要进行时间戳和时间码字段的拷贝操作。

如果队列已经开始流传输(streaming),则将缓冲区交给驱动程序进行处理,调用 __enqueue_in_driver 函数。

调用 __fill_v4l2_buffer 函数填充缓冲区结构体中与用户空间相关的信息。

如果已经调用了流传输的启动函数 vb2_start_streaming,且还未调用过 start_streaming() 函数,并且已经达到了最小所需的缓冲区数量,那么现在可以调用 start_streaming() 函数。

打印相关信息,表示成功将缓冲区放入队列。

返回成功的状态码 0。

总的来说,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;
}

uvc_ioctl_dqbuf从队列中取出缓冲区

uvc_ioctl_dqbuf->uvc_dequeue_buffer->vb2_dqbuf->vb2_internal_dqbuf

函数 vb2_internal_dqbuf 的作用是从队列中取出一个缓冲区,并将其状态设置为已出队列状态。

该函数的主要步骤如下:

检查传递给函数的 v4l2_buffer 结构体的类型字段 b->type 是否与队列的类型 q->type 匹配,如果不匹配,则返回错误码 -EINVAL。

调用 __vb2_get_done_vb 函数获取已完成的缓冲区 vb。该函数会根据 nonblocking 参数决定是否等待有可用缓冲区,如果获取失败,则返回相应的错误码。

根据缓冲区的状态 vb->state 执行不同的操作:

如果状态为 VB2_BUF_STATE_DONE,表示缓冲区已完成,打印相应的信息。

如果状态为 VB2_BUF_STATE_ERROR,表示缓冲区存在错误,打印相应的信息。

如果状态无效,返回错误码 -EINVAL。

调用 buf_finish 回调函数(如果提供),其中驱动程序可以在将缓冲区返回到用户空间之前执行任何其他操作,例如缓存同步。

调用 __fill_v4l2_buffer 函数填充缓冲区结构体 b 中与用户空间相关的信息。

从 videobuf 队列中删除缓冲区,并更新队列的计数。

将缓冲区状态设置为已出队列状态,调用 __vb2_dqbuf 函数。

打印相关信息,表示已成功将缓冲区出队列。

返回成功的状态码 0。

总的来说,vb2_internal_dqbuf 函数用于从队列中取出一个缓冲区,并将其状态设置为已出队列状态。函数会进行一系列的验证步骤,确保输入参数的正确性,并执行相应的操作,包括调用回调函数、填充缓冲区结构体、更新队列状态等。最后,函数会返回相应的状态码,指示操作的结果。

/*
 * 从队列中取出一个缓冲区,将其状态设置为已出队列状态
 * @q: videobuf2队列
 * @b: 从用户空间传递给驱动程序的缓冲区结构
 * @nonblocking: 如果为true,则此调用不会等待缓冲区,如果没有准备好的缓冲区等待出队列。通常,驱动程序将传递(file->f_flags&O_NONBLOCK)。
 *
 * 应该从驱动程序的vidioc_dqbuf ioctl处理程序中调用此函数。
 * 此函数:
 * 1)验证传递的缓冲区,
 * 2)在驱动程序中调用buf_finish回调(如果提供),其中驱动程序可以在将缓冲区返回到用户空间之前执行任何其他操作,例如缓存同步,
 * 3)填充缓冲区结构成员与用户空间相关的信息。
 *
 * 此函数的返回值旨在直接从驱动程序中的vidioc_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; // 返回成功
}

uvc_ioctl_create_bufs创建缓冲区

uvc_ioctl_create_bufs->uvc_create_buffers->vb2_create_bufs->__create_bufs

函数 __create_bufs 用于创建缓冲区。

函数的主要步骤如下:

首先检查是否已经分配了最大数量的缓冲区。如果是,则返回错误码 -ENOBUFS。

如果还没有分配缓冲区,则初始化一些变量,包括 q->plane_sizes 数组和 q->alloc_ctx 数组。同时,根据传入的参数设置队列的内存类型和等待标志。

计算需要分配的缓冲区数量,取传入的参数 count 和剩余可分配的数量 VIDEO_MAX_FRAME - q->num_buffers 中的较小值。

检查驱动程序是否能够处理到目前为止已分配的缓冲区数量。如果分配的数量小于请求的数量,则将 num_buffers 的值调整为已分配的数量,并调用队列操作函数 queue_setup 进行进一步的设置。如果返回值不为 0 或分配的数量仍然小于请求的数量,则返回错误码 -ENOMEM。

使用互斥锁保护共享资源,在临界区内更新队列的缓冲区数量 q->num_buffers。

如果设置步骤中出现错误,调用 __vb2_queue_free 函数释放已分配的缓冲区,并返回错误码 -ENOMEM。

解锁互斥锁。

将成功分配的缓冲区数量 allocated_buffers 更新到用户空间传入的参数 count 中。

返回成功的状态码 0。

总的来说,__create_bufs 函数用于创建缓冲区,并进行一系列的参数验证和设置操作。函数根据驱动程序的能力和用户空间的请求,分配相应数量的缓冲区,并更新队列的状态和缓冲区数量。最后,函数返回相应的状态码,指示操作的结果

static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
{
    unsigned int num_planes = 0, num_buffers, allocated_buffers;
    int ret;
    // 如果已经分配了最大数量的缓冲区,则返回错误
    if (q->num_buffers == VIDEO_MAX_FRAME) {
        dprintk(1, "maximum number of buffers already allocated\n");
        return -ENOBUFS;
    }
    // 如果还没有分配缓冲区,则初始化一些变量
    if (!q->num_buffers) {
        memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
        memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
        q->memory = create->memory;
        q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type);
    }
    // 计算需要分配的缓冲区数量
    num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
    /*
     * Check if driver can handle the so far allocated number of buffers.
     */
    if (allocated_buffers < num_buffers) {
        num_buffers = allocated_buffers;
        /*
         * q->num_buffers contains the total number of buffers, that the
         * queue driver has set up
         */
        ret = call_qop(q, queue_setup, q, &create->format, &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 -ENOMEM;
    }
    mutex_unlock(&q->mmap_lock);
    /*
     * Return the number of successfully allocated buffers
     * to the userspace.
     */
    create->count = allocated_buffers;
    return 0;
}

uvc_ioctl_streamon 开始视频流

这段代码实现了 uvc_ioctl_streamon 函数,用于启动流数据传输。

函数的功能是根据指定的流类型 type,启动相应的数据传输。它接收一个 file 文件结构体指针,一个 fh 文件句柄指针,以及一个 v4l2_buf_type 枚举类型参数 type。

函数的概述如下:

获取文件句柄

handle 和流

stream,通过

fh 获取。

检查是否具有权限访问流数据,如果没有权限,返回忙错误

-EBUSY。

上锁流的互斥锁,确保同一时间只有一个线程操作流。

调用

uvc_queue_streamon 函数,传递流的队列和指定的流类型

type,开始数据传输。

解锁流的互斥锁。

返回

uvc_queue_streamon 函数的结果。

该函数用于启动指定流类型的数据传输。它会检查权限,上锁流的互斥锁,然后调用 uvc_queue_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_ioctl_streamon->uvc_queue_streamon

这段代码实现了 uvc_queue_streamon 函数,用于启动视频流数据传输。

函数的功能是根据指定的流类型 type,调用 vb2_streamon 函数来启动视频流的数据传输。它接收一个 uvc_video_queue 结构体指针 queue 和一个 v4l2_buf_type 枚举类型参数 type。

函数的概述如下:

获取队列的互斥锁,确保同一时间只有一个线程操作队列。

调用 vb2_streamon 函数,传递队列和指定的流类型 type,启动视频流的数据传输。

释放队列的互斥锁。

返回 vb2_streamon 函数的结果。

该函数用于启动视频流的数据传输。它会获取队列的互斥锁,然后调用 vb2_streamon 函数来启动数据传输,并返回相应的结果。

// uvc_queue_streamon函数,用于启动视频流
int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
    int ret;
    // 获取互斥锁
    mutex_lock(&queue->mutex);
    // 调用vb2_streamon函数启动视频流
    ret = vb2_streamon(&queue->queue, type);
    // 释放互斥锁
    mutex_unlock(&queue->mutex);
    // 返回结果
    return ret;
}

uvc_ioctl_streamon->uvc_queue_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_ioctl_streamon->uvc_queue_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_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_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;
}
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);
}
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;
}

如果文章对您有帮助,点赞👍支持,感谢🤝


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
|
6月前
|
存储 Linux 开发工具
Rockchip系列之浅度分析UART接口系列(1)
Rockchip系列之浅度分析UART接口系列(1)
334 1
|
存储 Linux API
嵌入式Linux 字符设备驱动标准ioctl接口
嵌入式Linux 字符设备驱动标准ioctl接口
|
存储 缓存 索引
uvc驱动ioctl分析下
uvc驱动ioctl分析下
222 0
|
存储 Linux
uvc驱动中的v4l2
uvc驱动中的v4l2
129 0
|
监控 Linux
uvc摄像头驱动uvc设备的注册分析
uvc摄像头驱动uvc设备的注册分析
385 0
|
Linux Android开发 芯片
Linux驱动分析之Framebuffer驱动
前面我们了解了LCD的基本架构《Linux驱动分析之LCD驱动架构》,接下来我们拿个具体的实例来分析分析。这样可以了解其大概是如何使用和工作的。
UART子系统(十)UART驱动情景分析_read
UART子系统(十)UART驱动情景分析_read
193 2
UART子系统(十)UART驱动情景分析_read
|
缓存 Linux 芯片
Linux驱动分析之Uart驱动
之前对Uart驱动的整体架构做了介绍,现在来分析具体的驱动程序。我们以NXP 的 IMX6来进行分析。
|
索引
UVC 基础学习(3):基础描述符介绍
UVC 基础学习(3):基础描述符介绍
562 0
|
Ubuntu Linux Shell
UART子系统(三) UART属于TTY体系之一
UART子系统(三) UART属于TTY体系之一
236 1
UART子系统(三) UART属于TTY体系之一