通过【2.1 视频监控—V4L2框架的简单分析】和【2.3 视频监控—uvc驱动框架分析】两篇博文的介绍我们可以知道,对于一个USB摄像头驱动,编写步骤主要如下:
构造结构体:usb_driver
结构体
设置结构体:设置其成员变量
.name
= xxx,
.disconnect
= xxx_disconnet,
.id_table
= xxx,
.probe
= xxx_probe,
在xxx_probe()
中进行:
2.1. 分配video_device
结构体 = video_device_alloc
2.2. 设置video_device
结构体
.fops
.ioctl_ops (里面需要设置11项)
如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops
2.3. 注册video_device
结构体: video_register_device
注册结构体:usb_register()
在实际的工作和应用中,尽量使用内核带有的驱动程序,对于没有适配的驱动程序才自己开发,这个usb驱动程序只是学习的时候使用。
usb_driver
结构体struct usb_driver myuvc_driver = {
.name = "myuvcvideo",
.probe = myuvc_probe,
.disconnect = myuvc_disconnect,
.id_table = myuvc_ids,
};
usb_driver
结构体/*!
* 所支持usb设备类的接口
*/
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /**< VideoControl interface */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /**< VideoStreaming interface */
{}
};
static int myuvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static int s_cnt = 0;
printk("myuvc_probe : cnt = %d\n", s_cnt++);
/*!
* myuvc_probe()调用第二次后执行
*/
if (s_cnt == 2) {
/* 1、分配一个video_device结构体 */
s_myuvc_vdev = video_device_alloc();
/* 2、设置 */
/* 注册过程需要用到release,必须设置 */
s_myuvc_vdev->release = myuvc_release;
s_myuvc_vdev->fops = &myuvc_fops;
s_myuvc_vdev->ioctl_ops = &myuvc_ioctl_ops;
/* 3、注册结构体
* -1 - 自动分配次设备号
*/
video_register_device(s_myuvc_vdev, VFL_TYPE_GRABBER, -1);
}
return 0;
}
static void myuvc_disconnect(struct usb_interface *intf)
{
static int s_cnt = 0;
printk("myuvc_disconnect : cnt = %d\n", s_cnt++);
/*!
* myuvc_disconnect()调用第二次后执行
*/
if (s_cnt == 2) {
/* 注销结构体 */
video_unregister_device(s_myuvc_vdev);
/* 释放结构体 */
video_device_release(s_myuvc_vdev);
}
}
usb_driver
结构体static int myuvc_init(void)
{
int result;
result = usb_register(&myuvc_driver);
if (result < 0)
printk("USB register error!\n");
return result;
}
/*******************************************************************************
* Copyleft (c) 2021 Kcode
*
* @file myuvc.c
* @brief 实现USB摄像头的数据传输(框架)
* @author K
* @version 0.0.1
* @date 2021-07-21
* @license MulanPSL-1.0
*
* 文件修改历史:
* <时间> | <版本> | <作者> | <描述>
* 2021-07-21 | v0.0.1 | Kcode | 实现USB摄像头的数据传输(框架)
* -----------------------------------------------------------------------------
******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct video_device *s_myuvc_vdev; /**< video */
/*!
* 所支持usb设备类的接口
*/
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /**< VideoControl interface */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /**< VideoStreaming interface */
{}
};
/*!
* Step1 - 打开myuvc_fops设备文件
*/
static int myuvc_open(struct file *file)
{
return 0;
}
/*!
* 关闭myuvc_fops设备文件
*/
static int myuvc_close(struct file *file)
{
return 0;
}
/*!
* Step2 - 查询是否为USB摄像头设备
*/
static int myuvc_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
return 0;
}
/*!
* Step3 - 列举USB摄像头设备所支持的格式format
*/
static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_fmtdesc *f)
{
return 0;
}
/*!
* Step4 - 返回当前所使用的格式
*/
static int myuvc_vidioc_g_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
return (0);
}
/*!
* Step5 - 测试驱动程序是否支持某种格式
*/
static int myuvc_vidioc_try_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
return 0;
}
/*!
* Step6 - 设置所支持的格式
*/
static int myuvc_vidioc_s_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
return 0;
}
/*!
* Step7 - 为该设备申请若干个缓冲区,分配头部信息
*/
static int myuvc_vidioc_reqbufs(struct file *file,
void *priv, struct v4l2_requestbuffers *p)
{
return 0;
}
/*!
* Step8 - 查询缓冲区的信息,如大小、偏移地址等
* 得到信息后,APP可mmap进行地址映射,分配真正的存储数据的缓冲区
*/
static int myuvc_vidioc_querybuf(struct file *file,
void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*!
* Step9 - APPmmap进行地址映射可直接操作这块内存,分配真正的存储数据的缓冲区
*/
static int myuvc_mmap(struct file *file, struct vm_area_struct *vma)
{
return 0;
}
/*!
* Step10 - 把申请的缓冲区放入队列,底层的硬件操作函数将会把数据放入队列
*/
static int myuvc_vidioc_qbuf(struct file *file,
void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*!
* Step11 - 启动数据传输
*/
static int myuvc_vidioc_streamon(struct file *file,
void *priv, enum v4l2_buf_type i)
{
return 0;
}
/*!
* Step12 - APP调用poll/select确定缓存是否有数据
*/
static unsigned int myuvc_poll(struct file *file,
struct poll_table_struct *wait)
{
return 0;
}
/*!
* Step13 - APP通过poll/select确定缓冲区有数据后,从队列中取出并删除缓冲区
*/
static int myuvc_vidioc_dqbuf(struct file *file,
void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*!
* Step14 - APP已经mmap映射缓存,可直接读数据
* Step15 - 再次调用myuvc_vidioc_dqbuf(),把缓存尾插法放入队列
* Step16 - 在其调用myuvc_poll()
*/
/*!
* Step17 - 不使用时,停止摄像头数据传输
*/
static int myuvc_vidioc_streamoff(struct file *file,
void *priv, enum v4l2_buf_type i)
{
return 0;
}
/*!
* 所支持的ioclt函数
*/
static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
// 表示它是一个摄像头设备
.vidioc_querycap = myuvc_vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = myuvc_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = myuvc_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = myuvc_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = myuvc_vidioc_s_fmt_vid_cap,
/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs = myuvc_vidioc_reqbufs,
.vidioc_querybuf = myuvc_vidioc_querybuf,
.vidioc_qbuf = myuvc_vidioc_qbuf,
.vidioc_dqbuf = myuvc_vidioc_dqbuf,
/* 启动/停止 */
.vidioc_streamon = myuvc_vidioc_streamon,
.vidioc_streamoff = myuvc_vidioc_streamoff,
};
static const struct v4l2_file_operations myuvc_fops = {
.owner = THIS_MODULE,
.open = myuvc_open,
.release = myuvc_close,
.mmap = myuvc_mmap,
.unlocked_ioctl = video_ioctl2,
.poll = myuvc_poll,
};
static void myuvc_release(struct video_device *vdev)
{}
static int myuvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static int s_cnt = 0;
printk("myuvc_probe : cnt = %d\n", s_cnt++);
/*!
* myuvc_probe()调用第二次后执行
*/
if (s_cnt == 2) {
/* 1、分配一个video_device结构体 */
s_myuvc_vdev = video_device_alloc();
/* 2、设置 */
/* 注册过程需要用到release,必须设置 */
s_myuvc_vdev->release = myuvc_release;
s_myuvc_vdev->fops = &myuvc_fops;
s_myuvc_vdev->ioctl_ops = &myuvc_ioctl_ops;
/* 3、注册结构体
* -1 - 自动分配次设备号
*/
video_register_device(s_myuvc_vdev, VFL_TYPE_GRABBER, -1);
}
return 0;
}
static void myuvc_disconnect(struct usb_interface *intf)
{
static int s_cnt = 0;
printk("myuvc_disconnect : cnt = %d\n", s_cnt++);
/*!
* myuvc_disconnect()调用第二次后执行
*/
if (s_cnt == 2) {
/* 注销结构体 */
video_unregister_device(s_myuvc_vdev);
/* 释放结构体 */
video_device_release(s_myuvc_vdev);
}
}
struct usb_driver myuvc_driver = {
.name = "myuvcvideo",
.probe = myuvc_probe,
.disconnect = myuvc_disconnect,
.id_table = myuvc_ids,
};
static int myuvc_init(void)
{
int result;
result = usb_register(&myuvc_driver);
if (result < 0)
printk("USB register error!\n");
return result;
}
static void myuvc_cleanup(void)
{
usb_deregister(&myuvc_driver);
}
module_init(myuvc_init);
module_exit(myuvc_cleanup);
MODULE_LICENSE("GPL");
usb_driver
结构体,把驱动加载到内核中;usb_driver
结构体的.id_table
中找到是否支持这个接口;usb_driver
结构体的.probe
函数:video_device
结构体.vidioc_streamon
的ioclt函数:测试、设置、打印USB摄像头的参数通过打印出来的USB摄像头参数来调试程序。
/*!
* 缓冲区的信息
*/
typedef struct myuvc_buffer {
int state; /**< 状态位 */
int vma_use_count; /**< 是否已经被mmap */
struct v4l2_buffer buf; /**< 存储每个缓冲区的查询信息 */
wait_queue_head_t wait; /**< APP读取某个缓冲区,如果无数据,在此休眠 */
struct list_head stream; /**< mainqueue队列结点,供APP消费用 */
struct list_head irq; /**< irqqueue队列结点,供底层驱动生成数据用*/
}MYUVC_BUFFER_S;
/*!
* 存储分配的整块缓冲区
*/
typedef struct mvuvc_video_queue {
int count; /**< 分配缓冲区个数 */
int buf_size; /**< 每个缓冲区(页对齐)大小 */
void *mem; /**< 存储分配的内存 */
MYUVC_BUFFER_S buffer[32]; /**< 存储每个缓冲区的信息 */
struct list_head mainqueue; /**< mainqueue队列头结点,供APP消费用 */
struct list_head irqqueue; /**< irqqueue队列头结点,供底层驱动生成用*/
}MYUVC_VIDEO_QUEUE_S;
在.vidioc_reqbufs
函数中分配的是整个大的缓存区MYUVC_VIDEO_QUEUE_S.mem
:缓冲区的数量MYUVC_VIDEO_QUEUE_S.count
x 每个缓冲区头部MYUVC_VIDEO_QUEUE_S.buffer[ ]
每个缓冲区都需要进入两个队列:mainqueue队列
(供APP消费用)和irqqueue队列
(供底层驱动生成用),其实际操作如下:
/*!
* @brief Step7 - 为该设备申请若干个缓冲区,分配头部信息
* @return 正数:返回成功分配内存的大小,负数:分配失败
*/
static int myuvc_vidioc_reqbufs(struct file *file,
void *priv, struct v4l2_requestbuffers *p)
{
int buf_num = p->count ;
int buf_size_unalign = s_myuvc_format.fmt.pix.sizeimage;
int buf_size_align = PAGE_ALIGN(s_myuvc_format.fmt.pix.sizeimage);
unsigned int i;
void *mem = NULL;
int ret;
if (buf_num > UVC_MAX_VIDEO_BUFFERS)
buf_num = UVC_MAX_VIDEO_BUFFERS;
/* 释放之前分配的缓存 */
if ((ret = myuvc_free_buffers()) < 0)
goto done;
/* 如果不分配缓冲区,则退出 */
if (buf_num == 0)
goto done;
/* 减少缓冲区的数量,直到分配成功 */
for (; buf_num > 0; --buf_num) {
mem = vmalloc_32(buf_num * buf_size_align);
if (mem != NULL)
break;
}
if (mem == NULL) {
ret = -ENOMEM;
goto done;
}
memset(&s_myuvc_queue, 0, sizeof(s_myuvc_queue));
/*!
* 初始化mainqueue和irqqueue队列
*/
INIT_LIST_HEAD(&s_myuvc_queue.mainqueue);
INIT_LIST_HEAD(&s_myuvc_queue.irqqueue);
/*!
* 缓存是一次性分配一个大的整体
* 需分别设置每个缓存的信息
*/
for (i = 0; i < buf_num; ++i) {
s_myuvc_queue.buffer[i].buf.index = i;
s_myuvc_queue.buffer[i].buf.m.offset = i * buf_size_align;
s_myuvc_queue.buffer[i].buf.length = buf_size_unalign;
s_myuvc_queue.buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
s_myuvc_queue.buffer[i].buf.sequence = 0;
s_myuvc_queue.buffer[i].buf.field = V4L2_FIELD_NONE;
s_myuvc_queue.buffer[i].buf.memory = V4L2_MEMORY_MMAP;
s_myuvc_queue.buffer[i].buf.flags = 0;
/* 空闲状态 */
s_myuvc_queue.buffer[i].state = VIDEOBUF_IDLE;
/* 初始化队列 */
init_waitqueue_head(&s_myuvc_queue.buffer[i].wait);
}
s_myuvc_queue.mem = mem;
s_myuvc_queue.count = buf_num;
s_myuvc_queue.buf_size = buf_size_align;
ret = buf_num;
done:
return ret;
}
/*!
* @brief Step10 - 把申请的缓冲区放入队列,底层的硬件操作函数将会把数据放入队列
*/
static int myuvc_vidioc_qbuf(struct file *file,
void *priv, struct v4l2_buffer *v4l2_buf)
{
struct myuvc_buffer *buf = &s_myuvc_queue.buffer[v4l2_buf->index];
/*!
* 修改状态:处于队列状态,且缓冲区数据为空
*/
buf->state = VIDEOBUF_QUEUED;
v4l2_buf->bytesused = 0;
/*!
* 队列一:供APP使用
* 当缓冲区没有数据时,放入mainqueue队列
* 当缓冲区有数据时,APP从mainqueue队列中取出
*/
list_add_tail(&buf->stream, &s_myuvc_queue.mainqueue);
/*!
* 队列二:供产生数据的函数使用
* 当采集到数据时,从irqqueue队列中取出第一个缓冲区,存入数据
*/
list_add_tail(&buf->irq, &s_myuvc_queue.irqqueue);
return 0;
}
对于一个usb设备,需要调用usb_control_msg()
:允许一个驱动发送和结束USB控制信息。
usb_control_msg()
发送这个设置好的数据包/*!
* @brief 发送数据包参数,测试设备是否支持
* @return 0:成功,负数:错误
*/
static int myuvc_try_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u16 size;
int ret;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
memset(ctrl, 0, sizeof *ctrl);
ctrl->bmHint = 1; /* dwFrameInterval */
ctrl->bFormatIndex = 1;
ctrl->bFrameIndex = s_frame_idx + 1;
ctrl->dwFrameInterval = 333333;
size = uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/*!
* 先设置数据包的参数
*/
*(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
data[2] = ctrl->bFormatIndex;
data[3] = ctrl->bFrameIndex;
*(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
*(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
*(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
*(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
*(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
*(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
data[33] = ctrl->bMaxVersion;
}
pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
/*!
* 后发送数据
*/
ret = usb_control_msg(s_myuvc_udev, pipe, SET_CUR, type, VS_PROBE_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 5000);
kfree(data);
return (ret < 0) ? ret : 0;
}
usb_control_msg()
获得数据包/*!
* @brief 根据数据包的数据获得当前设备的参数
* @return 0:成功,负数:错误
*/
static int myuvc_get_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
__u16 size;
int ret = 0;
unsigned int pipe; /**< 端点 */
/*!
* 根据uvc设备版本设置数据宽度
*/
size = uvc_version >= 0x0110 ? 34 : 26;
data = kmalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/* 确定端点 */
pipe = (GET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
/* 确定类型 */
type |= (GET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
ret = usb_control_msg(s_myuvc_udev, pipe, GET_CUR, type, VS_PROBE_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 500);
if (ret < 0)
goto done;
ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
ctrl->bFormatIndex = data[2];
ctrl->bFrameIndex = data[3];
ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
if (size == 34) {
ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
ctrl->bMinVersion = data[32];
ctrl->bMaxVersion = data[33];
} else {
//ctrl->dwClockFrequency = video->dev->clock_frequency;
ctrl->bmFramingInfo = 0;
ctrl->bPreferedVersion = 0;
ctrl->bMinVersion = 0;
ctrl->bMaxVersion = 0;
}
done:
kfree(data);
return (ret < 0) ? ret : 0;
}
usb_control_msg()
,发送数据包/*!
* @brief 设置数据包参数,测试设备是否支持
* @return 0:成功,负数:错误
*/
static int myuvc_set_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u16 size;
int ret;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
size = uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/*!
* 先设置数据包的参数
*/
*(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
data[2] = ctrl->bFormatIndex;
data[3] = ctrl->bFrameIndex;
*(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
*(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
*(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
*(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
*(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
*(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
data[33] = ctrl->bMaxVersion;
}
pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
/*!
* 后发送数据
*/
ret = usb_control_msg(s_myuvc_udev, pipe, SET_CUR, type, VS_COMMIT_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 5000);
kfree(data);
return (ret < 0) ? ret : 0;
}
由于这些参数已经在【2.4 视频监控—从0写USB摄像头驱动(1)-描述符的分析与打印】中已获得,直接计算好对应参数,调用usb_set_interface()
函数即可
/*!
* 手工确定:
* bandwidth = s_myuvc_params.dwMaxPayloadTransferSize = 1024
* 观察lsusb -v -d 0x1e4e:的结果:
* wMaxPacketSize 0x0400 1x 1024 bytes
* bAlternateSetting 8
*/
usb_set_interface(s_myuvc_udev, s_myuvc_streaming_intf, s_myuvc_streaming_setting);
/*******************************************************************************
* Copyleft (c) 2021 Kcode
*
* @file myuvc.c
* @brief 实现USB摄像头的数据传输(设置参数)
* @author K
* @version 0.0.1
* @date 2021-07-22
* @license MulanPSL-1.0
*
* 文件修改历史:
* <时间> | <版本> | <作者> | <描述>
* 2021-07-22 | v0.0.1 | Kcode | 实现USB摄像头的数据传输(设置参数)
* -----------------------------------------------------------------------------
******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "uvcvideo.h"
#define MYDRIVER_VERSION_NUMBER 1 /**< myuvc版本 */
/*!
* 分辨率描述
*/
typedef struct frame_desc {
int width; /**< x分辨率 */
int height; /**< y分辨率 */
} FRAME_DESC_S;
/*!
* uvc数据流控制结构体
*/
typedef struct myuvc_streaming_control {
__u16 bmHint;
__u8 bFormatIndex;
__u8 bFrameIndex;
__u32 dwFrameInterval;
__u16 wKeyFrameRate;
__u16 wPFrameRate;
__u16 wCompQuality;
__u16 wCompWindowSize;
__u16 wDelay;
__u32 dwMaxVideoFrameSize;
__u32 dwMaxPayloadTransferSize;
__u32 dwClockFrequency;
__u8 bmFramingInfo;
__u8 bPreferedVersion;
__u8 bMinVersion;
__u8 bMaxVersion;
}MYUVC_STREAMING_CONTROL_S;
/*!
* 缓冲区的信息
*/
typedef struct myuvc_buffer {
int state; /**< 状态位 */
int vma_use_count; /**< 是否已经被mmap */
struct v4l2_buffer buf; /**< 存储每个缓冲区的查询信息 */
wait_queue_head_t wait; /**< APP读取某个缓冲区,如果无数据,在此休眠 */
struct list_head stream; /**< mainqueue队列结点,供APP消费用 */
struct list_head irq; /**< irqqueue队列结点,供底层驱动生成用*/
}MYUVC_BUFFER_S;
/*!
* 存储分配的整块缓冲区
*/
typedef struct mvuvc_video_queue {
int count; /**< 分配缓冲区个数 */
int buf_size; /**< 每个缓冲区(页对齐)大小 */
void *mem; /**< 存储分配的内存 */
MYUVC_BUFFER_S buffer[32]; /**< 存储每个缓冲区的信息 */
struct list_head mainqueue; /**< mainqueue队列头结点,供APP消费用 */
struct list_head irqqueue; /**< irqqueue队列头结点,供底层驱动生成用*/
}MYUVC_VIDEO_QUEUE_S;
static int uvc_version = 0x100; /**< 手工查看知道 */
static int s_myuvc_streaming_intf;
static int s_myuvc_streaming_setting = 8;
static int s_myuvc_control_intf;
static struct video_device *s_myuvc_vdev;
static struct v4l2_format s_myuvc_format; /**< USB摄像头的format */
static struct usb_device *s_myuvc_udev;
static MYUVC_VIDEO_QUEUE_S s_myuvc_queue; /**< 存放分配的一整块缓冲区 */
static MYUVC_STREAMING_CONTROL_S s_myuvc_params; /**< uvc数据流解析 */
static int s_frame_idx = 1;
static int s_pixel_bits = 16; /**< USB摄像头像素位 */
static int s_frame_index = 1; /**< 指定分辨率数组下标 */
static FRAME_DESC_S s_frame_arr[] = {
{640, 480},
{352, 288},
{320, 240},
{176, 144},
{160, 120},
}; /**< 该USB摄像头所有支持的分辨率 */
/*!
* 所支持usb设备类的接口
*/
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /**< VideoControl interface */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /**< VideoStreaming interface */
{}
};
/*!
* @brief Step1 - 打开myuvc_fops设备文件
*/
static int myuvc_open(struct file *file)
{
return 0;
}
/*!
* 关闭myuvc_fops设备文件
*/
static int myuvc_close(struct file *file)
{
return 0;
}
/*!
* @brief Step2 - 查询是否为USB摄像头设备
*/
static int myuvc_vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
/*!
* 清空内存、设置版本号和名字
*/
memset(cap, 0, sizeof *cap);
strcpy(cap->driver, "myuvc");
strcpy(cap->card, "myuvc");
cap->version = MYDRIVER_VERSION_NUMBER;
/*!
* V4L2_CAP_VIDEO_CAPTURE - 设备为视频捕捉设备
* V4L2_CAP_STREAMING - 使用ioctl来读视频数据
*/
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
/*!
* @brief Step3 - 列举USB摄像头设备所支持的格式format
*/
static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_fmtdesc *f)
{
/*!
* 当前USB摄像头只支持一种格式format(命令查看描述符信息可知)
*/
if (f->index >= 1)
return -EINVAL;
/*!
* 格式:VS_FORMAT_UNCOMPRESSED(不压缩原始数据)
* GUID:59 55 59 32 00 00 10 00 80 00 00 aa 00 38 9b 71 10
* 参考:uvc_fmts[]得到 最终格式宏定义V4L2_PIX_FMT_YUYV
*/
strcpy(f->description, "4:2:2, packed, YUYV");
f->pixelformat = V4L2_PIX_FMT_YUYV;
return 0;
}
/*!
* @brief Step4 - 返回当前所使用的格式
*/
static int myuvc_vidioc_g_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
memcpy(f, &s_myuvc_format, sizeof(s_myuvc_format));
return 0;
}
/*!
* @brief Step5 - 测试驱动程序是否支持某种格式,强制设定格式
*/
static int myuvc_vidioc_try_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
if ((f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || \
(f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV))
return -EINVAL;
/*!
* 手工确定分辨率、像素位、图片大小信息
*/
f->fmt.pix.width = s_frame_arr[s_frame_index].width;
f->fmt.pix.height = s_frame_arr[s_frame_index].height;
f->fmt.pix.bytesperline = (f->fmt.pix.width * s_pixel_bits) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.width;
return 0;
}
/*!
* @brief Step6 - 设置所支持的格式
*/
static int myuvc_vidioc_s_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_format *f)
{
int ret;
/*!
* 测试是否支持该格式(强制设置格式)
*/
ret = myuvc_vidioc_try_fmt_vid_cap(file, NULL, f);
if (ret < 0)
return ret;
memcpy(&s_myuvc_format, f, sizeof(s_myuvc_format));
return 0;
}
/*!
* @brief 释放分配的缓冲区
*/
int myuvc_free_buffers(void)
{
kfree(s_myuvc_queue.mem);
memset(&s_myuvc_queue, 0, sizeof(s_myuvc_queue));
return 0;
}
/*!
* @brief Step7 - 为该设备申请若干个缓冲区,分配头部信息
* @return 正数:返回成功分配内存的大小,负数:分配失败
*/
static int myuvc_vidioc_reqbufs(struct file *file,
void *priv, struct v4l2_requestbuffers *p)
{
int buf_num = p->count ;
int buf_size_unalign = s_myuvc_format.fmt.pix.sizeimage;
int buf_size_align = PAGE_ALIGN(s_myuvc_format.fmt.pix.sizeimage);
unsigned int i;
void *mem = NULL;
int ret;
if (buf_num > UVC_MAX_VIDEO_BUFFERS)
buf_num = UVC_MAX_VIDEO_BUFFERS;
/* 释放之前分配的缓存 */
if ((ret = myuvc_free_buffers()) < 0)
goto done;
/* 如果不分配缓冲区,则退出 */
if (buf_num == 0)
goto done;
/* 减少缓冲区的数量,直到分配成功 */
for (; buf_num > 0; --buf_num) {
mem = vmalloc_32(buf_num * buf_size_align);
if (mem != NULL)
break;
}
if (mem == NULL) {
ret = -ENOMEM;
goto done;
}
memset(&s_myuvc_queue, 0, sizeof(s_myuvc_queue));
/*!
* 初始化mainqueue和irqqueue队列
*/
INIT_LIST_HEAD(&s_myuvc_queue.mainqueue);
INIT_LIST_HEAD(&s_myuvc_queue.irqqueue);
/*!
* 缓存是一次性分配一个大的整体
* 需分别设置每个缓存的信息
*/
for (i = 0; i < buf_num; ++i) {
s_myuvc_queue.buffer[i].buf.index = i;
s_myuvc_queue.buffer[i].buf.m.offset = i * buf_size_align;
s_myuvc_queue.buffer[i].buf.length = buf_size_unalign;
s_myuvc_queue.buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
s_myuvc_queue.buffer[i].buf.sequence = 0;
s_myuvc_queue.buffer[i].buf.field = V4L2_FIELD_NONE;
s_myuvc_queue.buffer[i].buf.memory = V4L2_MEMORY_MMAP;
s_myuvc_queue.buffer[i].buf.flags = 0;
/* 空闲状态 */
s_myuvc_queue.buffer[i].state = VIDEOBUF_IDLE;
/* 初始化队列 */
init_waitqueue_head(&s_myuvc_queue.buffer[i].wait);
}
s_myuvc_queue.mem = mem;
s_myuvc_queue.count = buf_num;
s_myuvc_queue.buf_size = buf_size_align;
ret = buf_num;
done:
return ret;
}
/*!
* @brief Step8 - 查询指定缓冲区的信息,如大小、偏移地址等
* 得到信息后,APP可mmap进行地址映射,分配真正的存储数据的缓冲区
* @return 0:成功,负数:失败
*/
static int myuvc_vidioc_querybuf(struct file *file,
void *priv, struct v4l2_buffer *v4l2_buf)
{
int ret = 0;
if (v4l2_buf->index >= s_myuvc_queue.count) {
ret = -EINVAL;
goto done;
}
/* 拷贝该缓冲区的状态信息 */
memcpy(v4l2_buf, &s_myuvc_queue.buffer[v4l2_buf->index].buf,
sizeof(*v4l2_buf));
/*!
* 若已经该缓冲区已被mmap,则更新状态
*/
if (s_myuvc_queue.buffer[v4l2_buf->index].vma_use_count)
v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
/*!
* 更新状态
*/
switch (s_myuvc_queue.buffer[v4l2_buf->index].state) {
case VIDEOBUF_ERROR:
case VIDEOBUF_DONE:
v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
break;
case VIDEOBUF_QUEUED:
case VIDEOBUF_ACTIVE:
v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
break;
case VIDEOBUF_IDLE:
default:
break;
}
done:
return ret;
}
/*!
* @brief Step9 - APPmmap进行地址映射可直接操作这块内存,分配真正的存储数据的缓冲区
* @return 0:成功
*/
static int myuvc_mmap(struct file *file, struct vm_area_struct *vma)
{
return 0;
}
/*!
* @brief Step10 - 把申请的缓冲区放入队列,底层的硬件操作函数将会把数据放入队列
*/
static int myuvc_vidioc_qbuf(struct file *file,
void *priv, struct v4l2_buffer *v4l2_buf)
{
struct myuvc_buffer *buf = &s_myuvc_queue.buffer[v4l2_buf->index];
/*!
* 修改状态:处于队列状态,且缓冲区数据为空
*/
buf->state = VIDEOBUF_QUEUED;
v4l2_buf->bytesused = 0;
/*!
* 队列一:供APP使用
* 当缓冲区没有数据时,放入mainqueue队列
* 当缓冲区有数据时,APP从mainqueue队列中取出
*/
list_add_tail(&buf->stream, &s_myuvc_queue.mainqueue);
/*!
* 队列二:供产生数据的函数使用
* 当采集到数据时,从irqqueue队列中取出第一个缓冲区,存入数据
*/
list_add_tail(&buf->irq, &s_myuvc_queue.irqqueue);
return 0;
}
/*!
* 打印设置参数
*/
static void myuvc_print_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
printk("video params:\n");
printk("bmHint = %d\n", ctrl->bmHint);
printk("bFormatIndex = %d\n", ctrl->bFormatIndex);
printk("bFrameIndex = %d\n", ctrl->bFrameIndex);
printk("dwFrameInterval = %d\n", ctrl->dwFrameInterval);
printk("wKeyFrameRate = %d\n", ctrl->wKeyFrameRate);
printk("wPFrameRate = %d\n", ctrl->wPFrameRate);
printk("wCompQuality = %d\n", ctrl->wCompQuality);
printk("wCompWindowSize = %d\n", ctrl->wCompWindowSize);
printk("wDelay = %d\n", ctrl->wDelay);
printk("dwMaxVideoFrameSize = %d\n", ctrl->dwMaxVideoFrameSize);
printk("dwMaxPayloadTransferSize = %d\n", ctrl->dwMaxPayloadTransferSize);
printk("dwClockFrequency = %d\n", ctrl->dwClockFrequency);
printk("bmFramingInfo = %d\n", ctrl->bmFramingInfo);
printk("bPreferedVersion = %d\n", ctrl->bPreferedVersion);
printk("bMinVersion = %d\n", ctrl->bMinVersion);
printk("bMinVersion = %d\n", ctrl->bMinVersion);
}
/*!
* @brief 根据数据包的数据获得当前设备的参数
* @return 0:成功,负数:错误
*/
static int myuvc_get_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
__u16 size;
int ret = 0;
unsigned int pipe; /**< 端点 */
/*!
* 根据uvc设备版本设置数据宽度
*/
size = uvc_version >= 0x0110 ? 34 : 26;
data = kmalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/* 确定端点 */
pipe = (GET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
/* 确定类型 */
type |= (GET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
ret = usb_control_msg(s_myuvc_udev, pipe, GET_CUR, type, VS_PROBE_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 500);
if (ret < 0)
goto done;
ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
ctrl->bFormatIndex = data[2];
ctrl->bFrameIndex = data[3];
ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
if (size == 34) {
ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
ctrl->bmFramingInfo = data[30];
ctrl->bPreferedVersion = data[31];
ctrl->bMinVersion = data[32];
ctrl->bMaxVersion = data[33];
} else {
//ctrl->dwClockFrequency = video->dev->clock_frequency;
ctrl->bmFramingInfo = 0;
ctrl->bPreferedVersion = 0;
ctrl->bMinVersion = 0;
ctrl->bMaxVersion = 0;
}
done:
kfree(data);
return (ret < 0) ? ret : 0;
}
/*!
* @brief 发送数据包参数,测试设备是否支持
* @return 0:成功,负数:错误
*/
static int myuvc_try_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u16 size;
int ret;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
memset(ctrl, 0, sizeof *ctrl);
ctrl->bmHint = 1; /* dwFrameInterval */
ctrl->bFormatIndex = 1;
ctrl->bFrameIndex = s_frame_idx + 1;
ctrl->dwFrameInterval = 333333;
size = uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/*!
* 先设置数据包的参数
*/
*(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
data[2] = ctrl->bFormatIndex;
data[3] = ctrl->bFrameIndex;
*(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
*(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
*(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
*(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
*(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
*(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
data[33] = ctrl->bMaxVersion;
}
pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
/*!
* 后发送数据
*/
ret = usb_control_msg(s_myuvc_udev, pipe, SET_CUR, type, VS_PROBE_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 5000);
kfree(data);
return (ret < 0) ? ret : 0;
}
/*!
* @brief 设置数据包参数,测试设备是否支持
* @return 0:成功,负数:错误
*/
static int myuvc_set_streaming_params(MYUVC_STREAMING_CONTROL_S *ctrl)
{
__u8 *data;
__u16 size;
int ret;
__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe;
size = uvc_version >= 0x0110 ? 34 : 26;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
/*!
* 先设置数据包的参数
*/
*(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
data[2] = ctrl->bFormatIndex;
data[3] = ctrl->bFrameIndex;
*(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
*(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
*(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
*(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
*(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
*(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
if (size == 34) {
put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
data[30] = ctrl->bmFramingInfo;
data[31] = ctrl->bPreferedVersion;
data[32] = ctrl->bMinVersion;
data[33] = ctrl->bMaxVersion;
}
pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(s_myuvc_udev, 0)
: usb_sndctrlpipe(s_myuvc_udev, 0);
type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
/*!
* 后发送数据
*/
ret = usb_control_msg(s_myuvc_udev, pipe, SET_CUR, type, VS_COMMIT_CONTROL << 8,
0 << 8 | s_myuvc_streaming_intf, data, size, 5000);
kfree(data);
return (ret < 0) ? ret : 0;
}
/*!
* @brief Step11 - 启动数据传输
*/
static int myuvc_vidioc_streamon(struct file *file,
void *priv, enum v4l2_buf_type i)
{
int ret = 0;
/*!
* 1. 向USB摄像头设置参数: 比如使用哪个format, 使用这个format下的哪个frame(分辨率)
* 参考: uvc_set_video_ctrl / uvc_get_video_ctrl
* 1.1 根据一个结构体uvc_streaming_control设置数据包: 可以手工设置,也可以读出后再修改
* 1.2 调用usb_control_msg发出数据包
*/
/* a.测试参数 */
ret = myuvc_try_streaming_params(&s_myuvc_params);
if (ret < 0)
printk("myuvc_try_streaming_params ret : %d\n", ret);
/* b.取出参数 */
ret = myuvc_get_streaming_params(&s_myuvc_params);
if (ret < 0)
printk("myuvc_get_streaming_params ret : %d\n", ret);
/* c.设置参数 */
ret = myuvc_set_streaming_params(&s_myuvc_params);
if (ret < 0)
printk("myuvc_set_streaming_params ret : %d\n", ret);
myuvc_print_streaming_params(&s_myuvc_params);
/*!
* d. 设置VideoStreaming Interface所使用的setting
* d.1 从myuvc_params确定带宽
* d.2 根据setting的endpoint能传输的wMaxPacketSize
* 找到能满足该带宽的setting
*/
/*!
* 手工确定:
* bandwidth = s_myuvc_params.dwMaxPayloadTransferSize = 1024
* 观察lsusb -v -d 0x1e4e:的结果:
* wMaxPacketSize 0x0400 1x 1024 bytes
* bAlternateSetting 8
*/
usb_set_interface(s_myuvc_udev, s_myuvc_streaming_intf, s_myuvc_streaming_setting);
/* 2. 分配设置URB */
/* 3. 提交URB以接收数据 */
return 0;
}
/*!
* @brief Step12 - APP调用poll/select确定缓存是否有数据
*/
static unsigned int myuvc_poll(struct file *file,
struct poll_table_struct *wait)
{
return 0;
}
/*!
* @brief Step13 - APP通过poll/select确定缓冲区有数据后,从队列中取出并删除缓冲区
*/
static int myuvc_vidioc_dqbuf(struct file *file,
void *priv, struct v4l2_buffer *v4l2_buf)
{
struct myuvc_buffer *buf = &s_myuvc_queue.buffer[v4l2_buf->index];
/* APP发现数据后,从mianqueue中取出buffer */
list_del(&buf->stream);
return 0;
}
/*!
* @brief Step14 - APP已经mmap映射缓存,可直接读数据
* Step15 - 再次调用myuvc_vidioc_dqbuf(),把缓存尾插法放入队列
* Step16 - 在其调用myuvc_poll()
*/
/*!
* @brief Step17 - 不使用时,停止摄像头数据传输
*/
static int myuvc_vidioc_streamoff(struct file *file,
void *priv, enum v4l2_buf_type i)
{
return 0;
}
/*!
* 所支持的ioclt函数
*/
static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
// 表示它是一个摄像头设备
.vidioc_querycap = myuvc_vidioc_querycap,
/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap = myuvc_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = myuvc_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = myuvc_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = myuvc_vidioc_s_fmt_vid_cap,
/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs = myuvc_vidioc_reqbufs,
.vidioc_querybuf = myuvc_vidioc_querybuf,
.vidioc_qbuf = myuvc_vidioc_qbuf,
.vidioc_dqbuf = myuvc_vidioc_dqbuf,
/* 启动/停止 */
.vidioc_streamon = myuvc_vidioc_streamon,
.vidioc_streamoff = myuvc_vidioc_streamoff,
};
static const struct v4l2_file_operations myuvc_fops = {
.owner = THIS_MODULE,
.open = myuvc_open,
.release = myuvc_close,
.mmap = myuvc_mmap,
.unlocked_ioctl = video_ioctl2,
.poll = myuvc_poll,
};
static void myuvc_release(struct video_device *vdev)
{}
static int myuvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
static int s_cnt = 0;
struct usb_device *dev = interface_to_usbdev(intf);
s_myuvc_udev = dev;
printk("myuvc_probe : cnt = %d\n", s_cnt++);
if (s_cnt == 1) {
s_myuvc_control_intf = intf->cur_altsetting->desc.bInterfaceNumber;
} else if (s_cnt == 2) {
s_myuvc_streaming_intf = intf->cur_altsetting->desc.bInterfaceNumber;
}
/*!
* myuvc_probe()调用第二次后执行
*/
if (s_cnt == 2) {
/* 1、分配一个video_device结构体 */
s_myuvc_vdev = video_device_alloc();
/* 2、设置 */
/* 注册过程需要用到release,必须设置 */
s_myuvc_vdev->release = myuvc_release;
s_myuvc_vdev->fops = &myuvc_fops;
s_myuvc_vdev->ioctl_ops = &myuvc_ioctl_ops;
/* 3、注册结构体
* -1 - 自动分配次设备号
*/
printk("1\n");
video_register_device(s_myuvc_vdev, VFL_TYPE_GRABBER, -1);
printk("1\n");
myuvc_vidioc_streamon(NULL, NULL, 0);
}
return 0;
}
static void myuvc_disconnect(struct usb_interface *intf)
{
static int s_cnt = 0;
printk("myuvc_disconnect : cnt = %d\n", s_cnt++);
/*!
* myuvc_disconnect()调用第二次后执行
*/
if (s_cnt == 2) {
/* 注销结构体 */
video_unregister_device(s_myuvc_vdev);
/* 释放结构体 */
video_device_release(s_myuvc_vdev);
}
}
struct usb_driver myuvc_driver = {
.name = "myuvcvideo",
.probe = myuvc_probe,
.disconnect = myuvc_disconnect,
.id_table = myuvc_ids,
};
static int myuvc_init(void)
{
int result;
result = usb_register(&myuvc_driver);
if (result)
printk("USB register error!\n");
return result;
}
static void myuvc_cleanup(void)
{
usb_deregister(&myuvc_driver);
}
module_init(myuvc_init);
module_exit(myuvc_cleanup);
MODULE_LICENSE("GPL")
make
,生成驱动文件并插入USB摄像头之后sudo rmmod uvcvideo
,装载新驱动sudo insmod myuvc.ko
dmesg
查看输出信息。