USB摄像头驱动框架
1.分配usb_driver结构体
2.设置
probe
2.1分配video_device结构体
2.2设置
.ops
.ioctl_ops
2.3注册video_device结构体
uvc_init
1)result = usb_register(&uvc_driver.driver); 注册usb_driver结构体
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
uvc_register_video(dev) 1.注册uvc_device结构体
vdev = video_device_alloc() 1)分配uvc_device结构体
vdev->fops = &uvc_fops 2)设置v4l2_file_operations
vdev->release = video_device_release
video_register_device(vdev, VFL_TYPE_GRABBER, -1) 3)注册video_device结构体
cdev_alloc() 1)分配cdev
vdev->cdev->ops = &v4l2_fops 2)设置cdev结构体成员
1.VideoControl Interface(里面包含了各个功能单元Video Function,如亮度、对比度、色彩等)
2.VideoStreaming Interface(负责传输数据)
.unit:代表摄像头的一个功能模块
.Terminals:(1)Input Terminal(IT)-->represents a starting point for data streams inside the video
function
(2)Output Terminal(OT)-->represents an ending point for data streams
分析调用过程:
1.open
uvc_v4l2_open()
2.ioctl
uvc_v4l2_ioctl()
video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl) 从用户空间复制cmd,并执行uvc_v4l2_do_ioctl
在该函数中完成了11个关键ioctl函数的调用
1.VIDIOC_QUERYCAP
if(video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
else
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
2.VIDIOC_ENUM_FMT
format = &video->streaming->format[fmt->index];
fmt->pixelformat = format->fcc;
3.VIDIOC_G_FMT
uvc_v4l2_get_format(video, arg)
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 = video->streaming->ctrl.dwMaxVideoFrameSize;
fmt->fmt.pix.colorspace = format->colorspace;
fmt->fmt.pix.priv = 0;
4.VIDIOC_TRY_FMT
uvc_v4l2_try_format(video, arg, &probe, NULL, NULL)
/* Check if the hardware supports the requested format. */
for (i = 0; i < video->streaming->nformats; ++i) {
format = &video->streaming->format[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
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;
5.VIDIOC_S_FMT
uvc_v4l2_set_format(video, arg)
uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
6.VIDIOC_REQBUFS
uvc_alloc_buffers(&video->queue, rb->count, bufsize); Allocate the video buffers.
for (i = 0; i < nbuffers; ++i) {
memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
queue->buffer[i].buf.index = i;
queue->buffer[i].buf.m.offset = i * bufsize;
queue->buffer[i].buf.length = buflength;
queue->buffer[i].buf.type = queue->type;
queue->buffer[i].buf.sequence = 0;
queue->buffer[i].buf.field = V4L2_FIELD_NONE;
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
queue->buffer[i].buf.flags = 0;
init_waitqueue_head(&queue->buffer[i].wait);
}
queue->mem = mem;
queue->count = nbuffers;
queue->buf_size = bufsize;
ret = nbuffers;
7.VIDIOC_QUERYBUF
uvc_query_buffer(&video->queue, buf)
__uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf)
8.VIDIOC_QBUF(将空的缓冲区放入输入队列)
uvc_queue_buffer(&video->queue, arg)
buf = &queue->buffer[v4l2_buf->index]
if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
buf->buf.bytesused = 0;
else
buf->buf.bytesused = v4l2_buf->bytesused;
list_add_tail(&buf->stream, &queue->mainqueue)
__list_add(new, head->prev, head)
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
list_add_tail(&buf->queue, &queue->irqqueue);
9.VIDIOC_STREAMON(打开摄像头)
ret = uvc_video_enable(video, 1)
ret = uvc_queue_enable(&video->queue, 1)
ret = uvc_commit_video(video, &video->streaming->ctrl 将设置好的参数提交给硬件
__uvc_query_ctrl(video->dev(哪一个设备), SET_CUR, 0,
video->streaming->intfnum(哪一个接口),
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);
uvc_init_video(video, GFP_KERNEL)
ret = uvc_init_video_isoc(video, ep, gfp_flags);
1.分配URB
2.设置URB
urb->complete = uvc_video_complete; 收到数据后,该函数被调用
if (!list_empty(&queue->irqqueue)) 如果队列非空,则遍历整个队列
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,queue);
video->decode(urb, video, buf);
decode在uvc_video_init(struct uvc_video_device *video)函数中初始化
if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
video->decode = uvc_video_decode_isight;
else if (video->streaming->intf->num_altsetting > 1)
video->decode = uvc_video_decode_isoc;
uvc_queue_next_buffer(&video->queue, buf);
wake_up(&buf->wait);
else
video->decode = uvc_video_decode_bulk;
3.注册URB
/* Submit the URBs. */
for (i = 0; i < UVC_URBS; ++i) {
if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0)
uvc_printk(KERN_ERR, "Failed to submit URB %u "
"(%d).\n", i, ret);
uvc_uninit_video(video, 1);
return ret;
10.poll函数(查询是否有数据)
uvc_v4l2_poll
uvc_queue_poll(&video->queue, file, wait);
poll_wait(file, &buf->wait, wait); 在此休眠等待数据就绪
11.VIDIOC_DQBUF(从视频缓冲区输出队列中取出一个)
uvc_dequeue_buffer(&video->queue, arg,file->f_flags & O_NONBLOCK);
list_del(&buf->stream); 取出缓冲区后就从该列表中删除
12.VIDIOC_STREAMOFF(关闭摄像头)
ret = uvc_video_enable(video, 0)
ret = uvc_queue_enable(&video->queue, 0)
亮度的设置过程
应用程序调用ioctl函数
ioctl:VIDIOC_S_CTRL
ret = uvc_ctrl_set(video, &xctrl); 设置参数
ret = uvc_ctrl_commit(video); 提交
__uvc_ctrl_commit(video, 0);
ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
ret = uvc_query_ctrl(dev(哪一个设备), SET_CUR, ctrl->entity->id,
dev->intfnum(哪一个接口), ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
VideoControl Interface接口访问:
uvc_query_ctrl(dev(哪一个设备), SET_CUR, ctrl->entity->id,
dev->intfnum(哪一个接口), ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
VideoStreaming Interface接口访问:
__uvc_query_ctrl(video->dev(哪一个设备), SET_CUR, 0,
video->streaming->intfnum(哪一个接口),
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);
摄像头属性的设置:VideoControl Interface
摄像头数据格式的设置:VideoStreaming Interface
摄像头数据的获得:VideoStreaming Interface的URB获得
1.分配usb_driver结构体
2.设置
probe
2.1分配video_device结构体
2.2设置
.ops
.ioctl_ops
2.3注册video_device结构体
3.注册usb_driver
uvc_init
1)result = usb_register(&uvc_driver.driver); 注册usb_driver结构体
usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
driver_register(&new_driver->drvwrap.driver); 注册device_driver(设备驱动)
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
该驱动以平台总线设备为框架,当检测到同名设备时,调用probe函数,完成设备的注册
2).probe = uvc_probe,uvc_register_video(dev) 1.注册uvc_device结构体
vdev = video_device_alloc() 1)分配uvc_device结构体
vdev->fops = &uvc_fops 2)设置v4l2_file_operations
vdev->release = video_device_release
video_register_device(vdev, VFL_TYPE_GRABBER, -1) 3)注册video_device结构体
cdev_alloc() 1)分配cdev
vdev->cdev->ops = &v4l2_fops 2)设置cdev结构体成员
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1) 3)注册cdev结构体
USB摄像头属于字符设备,最终工作是完成描述字符设备属性的结构体(cdev)的注册。
USB摄像头组成:1.VideoControl Interface(里面包含了各个功能单元Video Function,如亮度、对比度、色彩等)
2.VideoStreaming Interface(负责传输数据)
.unit:代表摄像头的一个功能模块
.Terminals:(1)Input Terminal(IT)-->represents a starting point for data streams inside the video
function
(2)Output Terminal(OT)-->represents an ending point for data streams
分析调用过程:
1.open
uvc_v4l2_open()
2.ioctl
uvc_v4l2_ioctl()
video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl) 从用户空间复制cmd,并执行uvc_v4l2_do_ioctl
在该函数中完成了11个关键ioctl函数的调用
1.VIDIOC_QUERYCAP
if(video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
else
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
2.VIDIOC_ENUM_FMT
format = &video->streaming->format[fmt->index];
fmt->pixelformat = format->fcc;
3.VIDIOC_G_FMT
uvc_v4l2_get_format(video, arg)
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 = video->streaming->ctrl.dwMaxVideoFrameSize;
fmt->fmt.pix.colorspace = format->colorspace;
fmt->fmt.pix.priv = 0;
4.VIDIOC_TRY_FMT
uvc_v4l2_try_format(video, arg, &probe, NULL, NULL)
/* Check if the hardware supports the requested format. */
for (i = 0; i < video->streaming->nformats; ++i) {
format = &video->streaming->format[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
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;
5.VIDIOC_S_FMT
uvc_v4l2_set_format(video, arg)
uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
video->streaming->cur_format = format;
video->streaming->cur_frame = frame;
6.VIDIOC_REQBUFS
uvc_alloc_buffers(&video->queue, rb->count, bufsize); Allocate the video buffers.
for (i = 0; i < nbuffers; ++i) {
memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
queue->buffer[i].buf.index = i;
queue->buffer[i].buf.m.offset = i * bufsize;
queue->buffer[i].buf.length = buflength;
queue->buffer[i].buf.type = queue->type;
queue->buffer[i].buf.sequence = 0;
queue->buffer[i].buf.field = V4L2_FIELD_NONE;
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
queue->buffer[i].buf.flags = 0;
init_waitqueue_head(&queue->buffer[i].wait);
}
queue->mem = mem;
queue->count = nbuffers;
queue->buf_size = bufsize;
ret = nbuffers;
7.VIDIOC_QUERYBUF
uvc_query_buffer(&video->queue, buf)
__uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf)
8.VIDIOC_QBUF(将空的缓冲区放入输入队列)
uvc_queue_buffer(&video->queue, arg)
buf = &queue->buffer[v4l2_buf->index]
if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
buf->buf.bytesused = 0;
else
buf->buf.bytesused = v4l2_buf->bytesused;
list_add_tail(&buf->stream, &queue->mainqueue)
__list_add(new, head->prev, head)
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
list_add_tail(&buf->queue, &queue->irqqueue);
9.VIDIOC_STREAMON(打开摄像头)
ret = uvc_video_enable(video, 1)
ret = uvc_queue_enable(&video->queue, 1)
ret = uvc_commit_video(video, &video->streaming->ctrl 将设置好的参数提交给硬件
__uvc_query_ctrl(video->dev(哪一个设备), SET_CUR, 0,
video->streaming->intfnum(哪一个接口),
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);
uvc_init_video(video, GFP_KERNEL)
ret = uvc_init_video_isoc(video, ep, gfp_flags);
1.分配URB
2.设置URB
urb->complete = uvc_video_complete; 收到数据后,该函数被调用
if (!list_empty(&queue->irqqueue)) 如果队列非空,则遍历整个队列
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,queue);
video->decode(urb, video, buf);
decode在uvc_video_init(struct uvc_video_device *video)函数中初始化
if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
video->decode = uvc_video_decode_isight;
else if (video->streaming->intf->num_altsetting > 1)
video->decode = uvc_video_decode_isoc;
uvc_queue_next_buffer(&video->queue, buf);
wake_up(&buf->wait);
else
video->decode = uvc_video_decode_bulk;
3.注册URB
/* Submit the URBs. */
for (i = 0; i < UVC_URBS; ++i) {
if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0)
uvc_printk(KERN_ERR, "Failed to submit URB %u "
"(%d).\n", i, ret);
uvc_uninit_video(video, 1);
return ret;
10.poll函数(查询是否有数据)
uvc_v4l2_poll
uvc_queue_poll(&video->queue, file, wait);
poll_wait(file, &buf->wait, wait); 在此休眠等待数据就绪
11.VIDIOC_DQBUF(从视频缓冲区输出队列中取出一个)
uvc_dequeue_buffer(&video->queue, arg,file->f_flags & O_NONBLOCK);
list_del(&buf->stream); 取出缓冲区后就从该列表中删除
12.VIDIOC_STREAMOFF(关闭摄像头)
ret = uvc_video_enable(video, 0)
ret = uvc_queue_enable(&video->queue, 0)
亮度的设置过程
应用程序调用ioctl函数
ioctl:VIDIOC_S_CTRL
ret = uvc_ctrl_set(video, &xctrl); 设置参数
ret = uvc_ctrl_commit(video); 提交
__uvc_ctrl_commit(video, 0);
ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
ret = uvc_query_ctrl(dev(哪一个设备), SET_CUR, ctrl->entity->id,
dev->intfnum(哪一个接口), ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
VideoControl Interface接口访问:
uvc_query_ctrl(dev(哪一个设备), SET_CUR, ctrl->entity->id,
dev->intfnum(哪一个接口), ctrl->info->selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info->size);
VideoStreaming Interface接口访问:
__uvc_query_ctrl(video->dev(哪一个设备), SET_CUR, 0,
video->streaming->intfnum(哪一个接口),
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
uvc_timeout_param);
摄像头属性的设置:VideoControl Interface
摄像头数据格式的设置:VideoStreaming Interface
摄像头数据的获得:VideoStreaming Interface的URB获得