视频传输方式一:以IIC协议,通过IO口传输视频流:
视频传输方式二:以usb接口传输视频流:
驱动:字符设备驱动
怎么写驱动程序:构造file_operations
告诉内核
入口函数
出口函数
v4l2驱动框架:核心层 -- > file_operations
硬件相关层:
1、将usb摄像头设备插到装有虚拟机的pc上,dmesg会打印出一些发现usb摄像头设备的信息,在内核中grep找到打印信息的具体位置;
2、查找打印信息
3、用source insight或其他阅读软件打开uvc_driver.c文件
3.1 -->struct uvc_driver
--> uvc_probe:
v4l2_device_register // 只是做一些初始化工作,初始化链表、自旋锁、互斥锁等
uvc_register_chains ---uvc_register_video--video_device video_register_device
---struct video_device *vdev; // 分配video_device结构体,设置并通过video_register_device注册 video_device结构体有两个重要的成员 ->fops和->ioctl_ops
struct uvc_device *dev,
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
media_device_register
4、在内核中有v4l2-framework.txt文档可以参考
5、可以分析虚拟摄像头驱动:vivi.c
vivi_init-->vivi_create_instance-->video_device_alloc-->video_register_device-->__video_register_device-->vdev->cdev = cdev_alloc();--> cdev_add()
6、在内核中已经提供了file_operations v4l2_fops 的一些基本函数,所以我们不需要写字符设备函数。当应用层调用ioctl函数时,调用file_operations中的ioctl---.unlocked_ioctl = v4l2_ioctl, -->video_usercopy(file, cmd, arg, __video_do_ioctl) -->__video_do_ioctl --> 根据应用层ioctl传入的参数调用video_device->ioctl_ops中对应的函数
v4l2_ioctl------
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
如果video_device已经被注册了,就调用video_device里面的fops函数里面的unlocked_ioctl函数。
而unlocked_ioctl函数内核中已经写好了,我们不需要再写---video_usercopy
----cmd_input_size(cmd);
----check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
----得到我们想要的
static unsigned long cmd_input_size(unsigned int cmd) { /* Size of structure up to and including 'field' */ #define CMDINSIZE(cmd, type, field) case VIDIOC_##cmd: return offsetof(struct v4l2_##type, field) + sizeof(((struct v4l2_##type *)0)->field); switch (cmd) { CMDINSIZE(ENUM_FMT, fmtdesc, type); CMDINSIZE(G_FMT, format, type); CMDINSIZE(QUERYBUF, buffer, length); CMDINSIZE(G_PARM, streamparm, type); CMDINSIZE(ENUMSTD, standard, index); CMDINSIZE(ENUMINPUT, input, index); CMDINSIZE(G_CTRL, control, id); CMDINSIZE(G_TUNER, tuner, index); CMDINSIZE(QUERYCTRL, queryctrl, id); CMDINSIZE(QUERYMENU, querymenu, index); CMDINSIZE(ENUMOUTPUT, output, index); CMDINSIZE(G_MODULATOR, modulator, index); CMDINSIZE(G_FREQUENCY, frequency, tuner); CMDINSIZE(CROPCAP, cropcap, type); CMDINSIZE(G_CROP, crop, type); CMDINSIZE(ENUMAUDIO, audio, index); CMDINSIZE(ENUMAUDOUT, audioout, index); CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); default: return _IOC_SIZE(cmd); } }
和
static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, void * __user *user_ptr, void ***kernel_ptr) { int ret = 0; switch (cmd) { case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: { struct v4l2_buffer *buf = parg; if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) { if (buf->length > VIDEO_MAX_PLANES) { ret = -EINVAL; break; } *user_ptr = (void __user *)buf->m.planes; *kernel_ptr = (void *)&buf->m.planes; *array_size = sizeof(struct v4l2_plane) * buf->length; ret = 1; } break; } case VIDIOC_S_EXT_CTRLS: case VIDIOC_G_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: { struct v4l2_ext_controls *ctrls = parg; if (ctrls->count != 0) { if (ctrls->count > V4L2_CID_MAX_CTRLS) { ret = -EINVAL; break; } *user_ptr = (void __user *)ctrls->controls; *kernel_ptr = (void *)&ctrls->controls; *array_size = sizeof(struct v4l2_ext_control) * ctrls->count; ret = 1; } break; } } return ret; }
7、用strace 可以获得程序执行过程中的系统调用
strace -o xawtv.log xawtv
xawtv所涉及的系统调用就会记录在xawtv.log中