框架:

USB总线驱动程序的作用

1.识别USB设备

1.1分配地址

1.2并告诉USB设备(setaddress)

1.3发出命令获取描述符

2.查找并安装对应的设备驱动程序

3.提供USB读写函数

usb总线驱动程序在我们接入USB设备的时候会帮我们构造一个新的usb_device.注册到总线里面来。

本驱动程序总共需要构造

1个usb驱动结构体:usb_driver  /*用于操作接入的USB设备*/

2个设备结构体:usb_device /*存储识别后的USB的设备信息*/

video_device /*存储驱动程序对video_device设置和操作函数*/

USB摄像头插入之后,首先是作为一个USB设备被内核和驱动程序识别,USB总线驱动程序会查找到对应的设备驱动程序并调用驱动程序。驱动程序中的usb_drive结构体中的.id_table被调用,识别是否支持该USB设备,如果支持usb_drive结构体中的.probe被调用,驱动程序执行相关的USB设备操作,包括将识别的USB设备信息存放在usb_device结构体中,然后,驱动程序再设置该USB设备的“子”类型是video型设备,并将设置信息存储在video_device结构体中,然后在进行video设备操作。应用程序对USB摄像头设备的函数操作都会通过系统调用转化为对video_device结构体中的成员函数调用

因此本驱动程序应该包含三块内容:1、USB设备的操作;2、video设备的操作;3、USB设备与video设备之间的数据传输???

一、首先是USB设备的操作

1、构造USB设备结构体usb_driver,用于存储识别的usb设备信息和对此USB设备的操作函数,主要包括三个:

. id_table:插入设备时,内核程序会调用结构体中的.id_table,识别该USB设是否支持。

.probe:如果支持该设备类型内核程序会自动调用.probe函数执行具体对该USB设备的操作。

. disconnect:拔出USB设备

static struct video_device *myuvc_udev;

2、设置usb_driver结构体

staticstruct usb_driver myuvc_driver = {

.name                             ="myuvc",

.probe                             = myuvc_probe,   /*插入设备后,判断是否支持该设备*/

.disconnect           = myuvc_disconnect,

.id_table               = myuvc_ids,

};

3、注册/卸载usb_driver

static int myuvc_init(void)

{

usb_register(&myuvc_driver);

return0;

}

staticvoid myuvc_exit(void)

{

usb_deregister(&myuvc_driver);

}

4、修饰

module_init(myuvc_init);

module_exit(myuvc_exit);

MODULE_LICENSE("GPL");

以上是USB设备的整体框架

 

5、编写usb_driver成员函数:. id_table,只支持一种USB设备类型

          staticstruct usb_device_id myuvc_ids[] = {

/* Generic USB Video Class */

/*对于VCI和VSI,.prboe都会被调用*/

{ USB_INTERFACE_INFO(USB_CLASS_VIDEO,1, 0) },  /* VCI */

{USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /* VSI */

{}

};

 

 识别USB设备后调用.probe函数,编写.probe函数代码

structusb_device *myuvc_udev;        /*定义全局变量myuvc_udev*/

staticstruct usb_device *myuvc_vdev;  /*定义全局变量myuvc_vdev*/

staticint myuvc_probe(struct usb_interface *intf, const struct usb_device_id *id)

{

staticint cnt = 0;

printk("myuvc_probe: cnt = %d\n", cnt++);

if(cnt == 2){  /* VSI调用时*/

/*从接口获得USB设备信息,并赋给myuvc_udev*/

structusb_device *dev = interface_to_usbdev(intf);

myuvc_udev  =  dev;

/*申请video_device结构体内存myuvc_vdev*/

myuvc_vdev= video_device_alloc();

/*2. 设置video_device ,APP对USB摄像头的操作都会通过系统调用转

换为执行video_device结构体成员函数的操作*/

myuvc_vdev->release    = myuvc_release;

myuvc_vdev->fops        = &myuvc_fops;

myuvc_vdev->ioctl_ops          = &myuvc_ioctl_ops;

/*3. 注册video_device */

video_register_device(myuvc_vdev,VFL_TYPE_GRABBER, -1)

}

return0;

}

编写 . disconnect

staticvoid myuvc_disconnect(struct usb_interface *intf)

{

static int cnt = 0;

printk("myuvc_disconnect : cnt =%d\n", cnt++);

if (cnt == 2)

{

video_unregister_device(myuvc_vdev);

video_device_release(myuvc_vdev);

}

}

7. 编写video_device的成员函数,用于对video设备的操作

myuvc_release:/*释放*/

staticvoid myuvc_release(struct video_device *vdev)

{

}

myuvc_ioctl_ops:ioctl是设备驱动程序中对设备I/O通道进行管理的函数,用于向设备发控制和配置命令。包括:数据格式、设置内存、    启动/停止数据传输。

staticint myuvc_vidioc_querycap(struct file *file, void  *priv, struct v4l2_capability *cap) {

cap->capabilities =        V4L2_CAP_VIDEO_CAPTURE |  /*视频捕捉设备*/

V4L2_CAP_STREAMING;   /*获取视频数据的方式ioctl*/

return 0;

}

staticint myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,

structv4l2_fmtdesc *f)

{

return 0;

}

staticint myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv, structv4l2_format *f)

{

return (0);

}

staticint myuvc_vidioc_try_fmt_vid_cap(struct file *file, void *priv,

structv4l2_format *f)

{

return 0;

}

staticint myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,   /*设置格式*/

structv4l2_format *f)

{

return 0;

}

staticint myuvc_vidioc_reqbufs(struct file *file, void *priv,

struct v4l2_requestbuffers *p)

{

return 0;

}

staticint  myuvc_vidioc_querybuf(struct file*file, void *priv, struct v4l2_buffer *p)

{

return 0;

}

staticint  myuvc_vidioc_qbuf(struct file *file,void *priv, struct v4l2_buffer *p)

{

return 0;

}

staticint myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)

{

return 0;

}

staticint myuvc_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)

{

return 0;

}

staticint myuvc_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)

{

return 0;

}

 

/*APP调用ioctl(cmd)函数时,通过系统调用执行video_device中的video_ioctl2,根据cmd内核程序会转换为执行myuvc_ioctl_ops结构体中的某成员函数*/

staticconst 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,

};

myuvc_fops:暂时用不到的函数都写成空函数

/*APP调用open函数打开设备文件,系统调用调用此函数*/

staticint myuvc_open(struct file *file)

{

return 0;

}

staticint myuvc_close(struct file *file)

{

return 0;

}

staticint myuvc_mmap(struct file *file, struct vm_area_struct *vma)

{

return0;

}

staticunsigned int myuvc_poll(struct file *file, struct poll_table_struct *wait)

{

return0;

}

staticconst struct v4l2_file_operations myuvc_fops = {

.owner        = THIS_MODULE,

.open          = myuvc_open,

.release        =myuvc_close,

.mmap       =myuvc_mmap,

.ioctl           =video_ioctl2, /* V4L2 ioctl handler */

.poll            =myuvc_poll,

};

8、编写myuvc_ioctl_ops的成员函数

structframe_desc {

int width;

int height;

};

staticstruct v4l2_format         myuvc_format;  /*用于保存本驱动程序支持的数据格式*/

staticstruct frame_desc frames[] = {{640, 480}, {352, 288}, {320, 240}, {176, 144},{160, 120}};  /*本驱动支持的像素格式*/

staticint frame_idx = 1;  /*本驱动像素默认值是{640,480}*/

/*A2 参考 uvc_v4l2_do_ioctl */

staticint myuvc_vidioc_querycap(struct file *file, void  *priv, struct v4l2_capability *cap)

{

memset(cap,0, sizeof *cap);/*将传入的cap全部清零,然后重新设置cap*/

strcpy(cap->driver,"myuvc");

strcpy(cap->card,"myuvc");

cap->version= 1;

cap->capabilities= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;

return 0;

}

/*A3 列举本摄像头支持哪种数据格式:参考: uvc_fmts 数组*/

staticint myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,

structv4l2_fmtdesc *f)

{

/* 本驱动程序只支持一种数据格式*/

if (f->index >= 1)

return -EINVAL;

strcpy(f->description, "4:2:2,packed, YUYV");

f->pixelformat =V4L2_PIX_FMT_YUYV;

return 0;

}

/*A4 返回当前所使用的格式*/

staticint myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv,

structv4l2_format *f)

{

memcpy(f, &myuvc_format,sizeof(myuvc_format));

return (0);

}

/*A5 测试驱动程序是否支持某种格式,强制设置为一种格式*/

staticint myuvc_vidioc_try_fmt_vid_cap(struct file *file, void *priv,

struct v4l2_format*f)

{

if(f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)

return -EINVAL;

if(f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)

return-EINVAL;

/*人工查看描述符, 确定支持哪几种分辨率*/

f->fmt.pix.width  = frames[frame_idx].width;

f->fmt.pix.height= frames[frame_idx].height;

f->fmt.pix.bytesperline = (f->fmt.pix.width* bBitsPerPixel) >> 3;

f->fmt.pix.sizeimage = f->fmt.pix.height* f->fmt.pix.bytesperline;

return 0;

}

/*A6 参考myvivi_vidioc_s_fmt_vid_cap */

staticint myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,

structv4l2_format *f)

{

int ret =myuvc_vidioc_try_fmt_vid_cap(file, NULL, f);

if (ret < 0)

return ret;

memcpy(&myuvc_format, f,sizeof(myuvc_format));

return 0;

}

staticint myuvc_free_buffers(void)

{

if (myuvc_queue.mem)

{

vfree(myuvc_queue.mem);

memset(&myuvc_queue, 0,sizeof(myuvc_queue));

myuvc_queue.mem = NULL;

}

return 0;

}

/*参考uvc_video_queue定义一些结构体*/

structmyuvc_buffer {

structv4l2_buffer buf;

intstate;

intvma_use_count; /* 表示是否已经被mmap */

wait_queue_head_twait;  /* APP要读某个缓冲区,如果无数据,在此休眠*/

struct list_head stream;

struct list_head irq;

};

structmyuvc_queue {

void*mem;

intcount;

intbuf_size;

structmyuvc_buffer buffer[32];

structlist_head mainqueue;   /* 供APP消费用*/

structlist_head irqqueue;    /* 供底层驱动生产用*/

};

staticstruct myuvc_queue myuvc_queue;

/*A7分配若干个缓存, APP将从这些缓存中读到视频数据

* 参考:uvc_alloc_buffers*/

staticint myuvc_vidioc_reqbufs(struct file *file, void *priv,

struct v4l2_requestbuffers *p)

{

int nbuffers = p->count;

int bufsize = PAGE_ALIGN(myuvc_format.fmt.pix.sizeimage);

unsigned int i;

void *mem = NULL;

int ret;

if ((ret = myuvc_free_buffers()) < 0)

goto done;

/* Bail out if no buffers should beallocated. */

if (nbuffers == 0)

goto done;

/* Decrement the number of buffers untilallocation succeeds. */

for (; nbuffers > 0; --nbuffers) {

mem = vmalloc_32(nbuffers * bufsize);

if (mem != NULL)

break;

}

if (mem == NULL) {

ret = -ENOMEM;

goto done;

}

/* 这些缓存是一次性作为一个整体来分配的*/

memset(&myuvc_queue, 0, sizeof(myuvc_queue));

INIT_LIST_HEAD(&myuvc_queue.mainqueue);

INIT_LIST_HEAD(&myuvc_queue.irqqueue);

for (i = 0; i < nbuffers; ++i) {

myuvc_queue.buffer[i].buf.index = i;

myuvc_queue.buffer[i].buf.m.offset = i* bufsize;

myuvc_queue.buffer[i].buf.length =myuvc_format.fmt.pix.sizeimage;

myuvc_queue.buffer[i].buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;

myuvc_queue.buffer[i].buf.sequence = 0;

myuvc_queue.buffer[i].buf.field =V4L2_FIELD_NONE;

myuvc_queue.buffer[i].buf.memory =V4L2_MEMORY_MMAP;

myuvc_queue.buffer[i].buf.flags = 0;

myuvc_queue.buffer[i].state     = VIDEOBUF_IDLE;

init_waitqueue_head(&myuvc_queue.buffer[i].wait);

}

myuvc_queue.mem = mem;

myuvc_queue.count = nbuffers;

myuvc_queue.buf_size = bufsize;

ret = nbuffers;

done:

returnret;

}

/*A8 查询缓存状态, 比如地址信息(APP可以用mmap进行映射)

参考uvc_query_buffe */

staticint myuvc_vidioc_querybuf(struct file *file, void *priv,

structv4l2_buffer *v4l2_buf)

{

int ret = 0;

if (v4l2_buf->index >=myuvc_queue.count) {

ret = -EINVAL;

goto done;

}

memcpy(v4l2_buf,&myuvc_queue.buffer[v4l2_buf->index].buf, sizeof(*v4l2_buf));

/* 更新flags*/

if(myuvc_queue.buffer[v4l2_buf->index].vma_use_count)

v4l2_buf->flags |=V4L2_BUF_FLAG_MAPPED;

switch(myuvc_queue.buffer[v4l2_buf->index].state) {

caseVIDEOBUF_ERROR:

caseVIDEOBUF_DONE:

v4l2_buf->flags|= V4L2_BUF_FLAG_DONE;

break;

caseVIDEOBUF_QUEUED:

caseVIDEOBUF_ACTIVE:

v4l2_buf->flags|= V4L2_BUF_FLAG_QUEUED;

break;

caseVIDEOBUF_IDLE:

default:

break;

}

done:

return ret;

}

/*A10 把缓冲区放入队列, 底层的硬件操作函数将会把数据放入这个队列的缓存

* 参考:uvc_queue_buffer*/

staticint myuvc_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer*v4l2_buf)

{

struct myuvc_buffer *buf;

int ret;

/* 0. APP传入的v4l2_buf可能有问题,要做判断 */

if (v4l2_buf->type !=V4L2_BUF_TYPE_VIDEO_CAPTURE ||

v4l2_buf->memory != V4L2_MEMORY_MMAP) {

return -EINVAL;

}

if (v4l2_buf->index >=myuvc_queue.count) {

return -EINVAL;

}

buf =&myuvc_queue.buffer[v4l2_buf->index];

if (buf->state != VIDEOBUF_IDLE) {

return -EINVAL;

}

/* 1. 修改状态*/

buf->state = VIDEOBUF_QUEUED;

buf->buf.bytesused = 0;

/* 2. 放入2个队列*/

/* 队列1:供APP使用

* 当缓冲区没有数据时,放入mainqueue队列

* 当缓冲区有数据时,APP从mainqueue队列中取出

*/

list_add_tail(&buf->stream,&myuvc_queue.mainqueue);

/* 队列2:供产生数据的函数使用

* 当采集到数据时,从irqqueue队列中取出第1个缓冲区,存入数据

*/

list_add_tail(&buf->irq,&myuvc_queue.irqqueue);

return 0;

}

/*A11 启动传输

* 参考:uvc_video_enable(video, 1): uvc_commit_video /uvc_init_video*/

staticint myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)

{

/* 1. 向USB摄像头设置参数*/

/* 2. 分配设置URB*/

/* 3. 提交URB以接收数据*/

return0;

}

/*A13 APP通过poll/select确定有数据后,把缓存从队列中取出来

* 参考:uvc_dequeue_buffer*/

staticint myuvc_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer*v4l2_buf)

{

/* APP发现数据就绪后,从mainqueue里取出这个buffer*/

struct myuvc_buffer *buf;

int ret = 0;

if(list_empty(&myuvc_queue.mainqueue)) {

ret = -EINVAL;

goto done;

}

buf =list_first_entry(&myuvc_queue.mainqueue, struct myuvc_buffer, stream);

switch (buf->state) {

case VIDEOBUF_ERROR:

ret = -EIO;

case VIDEOBUF_DONE:

buf->state =VIDEOBUF_IDLE;

break;

case VIDEOBUF_IDLE:

case VIDEOBUF_QUEUED:

case VIDEOBUF_ACTIVE:

default:

ret = -EINVAL;

goto done;

}

list_del(&buf->stream);

done:

return ret;

}

/*A13 APP通过poll/select确定有数据后,把缓存从队列中取出来

* 参考:uvc_dequeue_buffer */

staticint myuvc_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer*v4l2_buf)

{

/*APP发现数据就绪后, 从mainqueue里取出这个buffer*/

structmyuvc_buffer *buf;

intret = 0;

if(list_empty(&myuvc_queue.mainqueue)) {

ret = -EINVAL;

goto done;

}

buf =list_first_entry(&myuvc_queue.mainqueue, struct myuvc_buffer, stream);

switch (buf->state) {

case VIDEOBUF_ERROR:

ret = -EIO;

case VIDEOBUF_DONE:

buf->state =VIDEOBUF_IDLE;

break;

case VIDEOBUF_IDLE:

case VIDEOBUF_QUEUED:

case VIDEOBUF_ACTIVE:

default:

ret = -EINVAL;

goto done;

}

list_del(&buf->stream);

done:

return ret;

}

/*A17 停止

* 参考: uvc_video_enable(video, 0) */

staticint myuvc_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)

{

/* 1. kill URB */

/* 2. free URB */

/* 3. 设置VideoStreamingInterface为setting 0 */

usb_set_interface(myuvc_udev,myuvc_streaming_intf, 0);

return 0;

}

9、编写数据传输函数myuvc_vidioc_streamon

9.1设置USB摄像头参数:比如使用哪个format,format下的哪一个framt,设置USB使用的接口和altsetting等

/*uvc_streaming_control结构体描述摄像头需要设置的参数种类*/

structmyuvc_streaming_control {

__u16 bmHint;

__u8 bFormatIndex; /* Video format index from a Formatdescriptor for this video interface. */

__u8 bFrameIndex; /* Video frame index from a framedescriptor */

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

};

staticint      myuvc_streaming_intf; /*VSI接口下当前设置使用的接口索引值,在.probe中初始化*/

staticint      myuvc_control_intf; /*/*VCI接口下当前设置使用的接口索引值*/*/

/*参考: uvc_v4l2_try_format/uvc_probe_video/uvc_set_video_ctrl(video, probe, 1) */

staticint myuvc_try_streaming_params(struct myuvc_streaming_control *ctrl)

{

__u8 *data; /*存储USB摄像头设置参数的数据*/

__u16 size; /*数据大小*/

intret;

__u8 type = USB_TYPE_CLASS|USB_RECIP_INTERFACE;/*USB TYPE*/

unsigned int pipe; /*USB设备的通信通道*/

memset(ctrl, 0, sizeof *ctrl);  /*将所有摄像头参数设置为0*/

ctrl->bmHint = 1; /* dwFrameInterval */

ctrl->bFormatIndex = 1;

ctrl->bFrameIndex  = frame_idx + 1;

ctrl->dwFrameInterval = 333333;

size= uvc_version >= 0x0110 ? 34 : 26;

/*申请内存*/

data = kzalloc(size, GFP_KERNEL);

if(data == NULL)

return-ENOMEM;

/*将设置的摄像头参数保存到数据包data中,用于发送*/

*(__le16 *)&data[0] =cpu_to_le16(ctrl->bmHint);/* CPU数据格式转为小端格式*/

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;

}

/*设置USB数据管道*/

pipe = (SET_CUR & 0x80) ?usb_rcvctrlpipe(myuvc_udev, 0)

: usb_sndctrlpipe(myuvc_udev,0);

type |= (SET_CUR & 0x80) ? USB_DIR_IN :USB_DIR_OUT;

/*发送参数数据包*/

ret= usb_control_msg(myuvc_udev, pipe, SET_CUR, type,

VS_PROBE_CONTROL<< 8, 0 << 8 | myuvc_streaming_intf, data, size, 5000);

/*发送数据包后释放data内存空间*/

kfree(data);

return (ret < 0) ? ret : 0;

}

staticint myuvc_get_streaming_params(struct myuvc_streaming_control *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 = kmalloc(size, GFP_KERNEL);

if (data == NULL)

return -ENOMEM;

pipe = (GET_CUR & 0x80) ?usb_rcvctrlpipe(myuvc_udev, 0)

: usb_sndctrlpipe(myuvc_udev, 0);

type |= (GET_CUR & 0x80) ?USB_DIR_IN : USB_DIR_OUT;

ret = usb_control_msg(myuvc_udev,pipe, GET_CUR, type,

VS_PROBE_CONTROL<< 8,

0 << 8 |myuvc_streaming_intf, data, size, 5000);

if(ret < 0)

gotodone;

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;

}

staticint myuvc_set_streaming_params(struct myuvc_streaming_control *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(myuvc_udev, 0)

: usb_sndctrlpipe(myuvc_udev,0);

type |= (SET_CUR & 0x80) ? USB_DIR_IN :USB_DIR_OUT;

ret= usb_control_msg(myuvc_udev, pipe, SET_CUR, type,

VS_COMMIT_CONTROL<< 8,

0 << 8 | myuvc_streaming_intf, data,size, 5000);

kfree(data);

return (ret < 0) ? ret : 0;

}

/*A11 启动传输

* 参考:uvc_video_enable(video, 1):  uvc_commit_video/uvc_init_video */

staticint myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)

{

int ret;

/*1. 向USB摄像头设置参数:比如使用哪个format, 使用这个format下的哪个

frame(分辨率); 参考: uvc_set_video_ctrl /uvc_get_video_ctrl

* 1.1 根据结构体uvc_streaming_control设置数据包data:手工设置/读出后再修改

* 1.2 调用usb_control_msg发出摄像头参数数据包data

*/

/* a. 测试想要设置的摄像头参数是否被支持*/

myuvc_try_streaming_params(&myuvc_params);

/* b. 取出参数*/

myuvc_get_streaming_params(&myuvc_params);

/* c. 设置参数*/

myuvc_set_streaming_params(&myuvc_params);

/* d. 设置VideoStreamingInterface所使用的setting*/

* d.1 从myuvc_params确定带宽

* d.2 根据setting的endpoint.wMaxPacketSize找到能满足该带宽的setting

* 手工确定:

*调用myuvc_get_streaming_params()获得摄像头当前dwMaxPayloadTransferSize

* bandwidth =myuvc_params.dwMaxPayloadTransferSize = 800

* 观察lsusb-v -d 0x1b3b:的结果:

*                wMaxPacketSize     0x0320 1x 800 bytes

* bAlternateSetting       5

*/

usb_set_interface(myuvc_udev,myuvc_streaming_intf,

myuvc_streaming_bAlternateSetting);

/* 2. 分配设置URB*/

/* 3. 提交URB以接收数据*/

return 0;

}

staticint myuvc_probe(struct usb_interface *intf,

const struct usb_device_id *id)

{

static int cnt = 0;

structusb_device *dev = interface_to_usbdev(intf);

myuvc_udev= dev

printk("myuvc_probe: cnt = %d\n", cnt++);

if (cnt == 1)

{

myuvc_control_intf= intf->cur_altsetting->desc.bInterfaceNumber;

}

else if (cnt == 2)

{

myuvc_streaming_intf= intf->cur_altsetting->desc.bInterfaceNumber;

myuvc_vdev= video_device_alloc();

myuvc_vdev->release= myuvc_release;

myuvc_vdev->fops    = &myuvc_fops;

myuvc_vdev->ioctl_ops= &myuvc_ioctl_ops;

video_register_device(myuvc_vdev,VFL_TYPE_GRABBER, -1);

}

return0;

}

9.2   分配设置URB

linux内核中的 USB 代码和所有的USB 设备通讯使用称为 urb( USBrequest block). 这个请求块用 struct urb 结构描述并且可在include/linux/usb.h 中找到.

一个urb用来发送或接受数据到或者从一个特定USB 设备上的特定的 USB 端点,以一种异步的方式.一个USB 设备驱动可能分配许多urb 给一个端点或者可能重用单个urb 给多个不同的端点, 根据驱动的需要.设备中的每个端点都处理一个urb 队列, 以至于多个urb 可被发送到相同的端点,在队列清空之前. 一个urb 的典型生命循环如下:

被一个USB 设备驱动创建.

安排给一个特定USB 设备的特定端点.

提交给USB 核心, 被USB 设备驱动.

提交给特定设备的被USB 核心指定的 USB 主机控制器驱动.

被USB 主机控制器处理, 它做一个USB 传送到设备.

当urb 完成, USB 主机控制器驱动通知USB 设备驱动.

urb也可被提交这个 urb 的驱动在任何时间取消,或者被 USB 核心如果设备被从系统中移出.urb 被动态创建并且包含一个内部引用计数,使它们在这个 urb 的最后一个用户释放它时被自动释放.

structmyuvc_queue {

void*mem;

intcount;

intbuf_size;

structmyuvc_buffer buffer[32];/*存储视频数据*/

structurb *urb[32];   /*在内存中设置urb缓存*/

          char*urb_buffer[32];

          dma_addr_turb_dma[32];

          unsignedint urb_size;

struct list_head mainqueue;   /* 供APP消费用*/

struct list_head irqqueue;    /* 供底层驱动生产用*/

};

/*参考: uvc_video_complete /uvc_video_decode_isoc */

staticvoid myuvc_video_complete(struct urb *urb)

{

u8 *src; /*数据源地址*/

u8 *dest; /*数据目的地址*/

int ret, i;

int len;

int maxlen;

int nbytes;

struct myuvc_buffer *buf;

switch (urb->status) {

case 0:

break;

default:

printk("Non-zero status(%d) in video "

"completionhandler.\n", urb->status);

return;

}

/* 从irqqueue队列中取出第1个缓冲区*/

if(!list_empty(&myuvc_queue.irqqueue))

{

buf =list_first_entry(&myuvc_queue.irqqueue, struct myuvc_buffer, irq);

for (i = 0; i <urb->number_of_packets; ++i) {

if (urb->iso_frame_desc[i].status< 0) {

printk("USB isochronous frame"

"lost(%d).\n", urb->iso_frame_desc[i].status);

continue;

}

/*摄像头获得的画面数据存放在urb_buffers中*/

src = urb->transfer_buffer + urb->iso_frame_desc[i].offset;

dest = myuvc_queue.mem +buf->buf.m.offset + buf->buf.bytesused;

len =urb->iso_frame_desc[i].actual_length;

/* 判断数据是否有效*/

/* URB数据含义:

* data[0] : 头部长度

* data[1] : 错误状态

*/

if (len < 2 || src[0] < 2 ||src[0] > len)

continue;

/* Skip payloads marked with theerror bit ("error frames"). */

if (src[1] & UVC_STREAM_ERR) {

printk("Dropping payload(error bit set).\n");

continue;

}

/* 除去头部后的数据长度*/

len -= src[0];

/* 缓冲区最多还能存多少数据*/

maxlen = buf->buf.length -buf->buf.bytesused;

nbytes = min(len, maxlen);

/* 复制数据*/

memcpy(dest, src + src[0], nbytes);

buf->buf.bytesused += nbytes;

/* 判断一帧数据是否已经全部接收到*/

if (len > maxlen) {

buf->state = VIDEOBUF_DONE;

}

/* Mark the buffer as done if theEOF marker is set. */

if (src[1] & UVC_STREAM_EOF&& buf->buf.bytesused != 0) {

printk("Frame complete(EOF found).\n");

if (len == 0)

printk("EOF in emptypayload.\n");

buf->state = VIDEOBUF_DONE;

}

}

/* 当接收完一帧数据,

* 从irqqueue中删除这个缓冲区

* 唤醒等待数据的进程

*/

if (buf->state == VIDEOBUF_DONE ||

buf->state == VIDEOBUF_ERROR)

{

list_del(&buf->irq);

wake_up(&buf->wait);

}

}

/* 再次提交URB*/

if ((ret = usb_submit_urb(urb,GFP_ATOMIC)) < 0) {

printk("Failed toresubmit video URB (%d).\n", ret);

}

/*urb初始化参考:uvc_init_video_isoc */

staticint myuvc_alloc_init_urbs(void)

{

u16 psize;           /* 实时传输端点一次能传输的最大字节数,即每个packet的大小*/

u32 size; /*一帧数据的最大长度 */

int i,  j, npackets;

struct urb           *urb;

psize= wMaxPacketSize;

size = myuvc_params.dwMaxVideoFrameSize;

npackets = DIV_ROUND_UP(size, psize);

if (npackets > 32)

npackets= 32;

myuvc_queue.urb_size= psize * npackets;

/* 1.申请usb_buffers*/

for (i = 0; i < MYUVC_URBS; ++i) {

/*1. 以packet为单位申请第i个packet的usb_buffers

用于存储数据的缓冲区,在myuvc_queue结构体中定义*/

myuvc_queue.urb_buffer[i]= usb_buffer_alloc(

myuvc_udev,size,GFP_KERNEL | __GFP_NOWARN,

&myuvc_queue.urb_dma[i]);

/*2. 分配urb结构构体,结构体成员transfer_buffer指向urb缓冲区,将urb结构体提交给usb总线驱动程序*/

myuvc_queue.urb[i]= usb_alloc_urb(npackets, GFP_KERNEL);

if(!myuvc_queue.urb_buffer[i] || !myuvc_queue.urb[i])

{

myuvc_uninit_urbs();

return-ENOMEM;

}

}

/* 3. 设置urb*/

for (i = 0; i < MYUVC_URBS; ++i) {

urb = myuvc_queue.urb[i];

urb->dev = myuvc_udev;

urb->context = NULL;

urb->pipe =usb_rcvisocpipe(myuvc_udev,myuvc_bEndpointAddress);

urb->transfer_flags = URB_ISO_ASAP |URB_NO_TRANSFER_DMA_MAP;

urb->interval = 1;

urb->transfer_buffer =myuvc_queue.urb_buffer[i];

urb->transfer_dma =myuvc_queue.urb_dma[i];

/*驱动程序收完一帧数据之后会产生一个中断,myuvc_video_complete是中断处理函数*/

urb->complete =myuvc_video_complete;

urb->number_of_packets = npackets;

urb->transfer_buffer_length = size;

for (j = 0; j < npackets; ++j) {

urb->iso_frame_desc[j].offset = j * psize;

urb->iso_frame_desc[j].length= psize;

}

}

return 0;

}

static int myuvc_vidioc_streamon(struct file *file, void *priv, enumv4l2_buf_type i)

{

int ret;

myuvc_try_streaming_params(&myuvc_params);

myuvc_get_streaming_params(&myuvc_params);

myuvc_set_streaming_params(&myuvc_params);

myuvc_print_streaming_params(&myuvc_params);

/*d. 设置VSI所使用的setting

*d.1 从myuvc_params确定带宽

*d.2 根据altsetting端描述符的wMaxPacketSize找到满足该带宽的setting

* 手工确定带宽:

* bandwidth =myubc_params.dwMaxPayloadTransferSize = 800

* 观察lsusb-v -d 0x1b3b:的结果

*                wMaxPacketSize     0x0320 1x 800 bytes

*                         bAlternateSetting       5*/

usb_set_interface(myuvc_udev,myuvc_streaming_intf,

myuvc_streaming_bAlternateSetting);

/*2、分配设置URB(USBRequest Block)*/

myuvc_alloc_init_urbs();

/*3、提交向URB接受数据*/

return 0;

}

9.3向USB总线驱动程序提交URB

staticint myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)

{

int ret;

myuvc_try_streaming_params(&myuvc_params);

myuvc_get_streaming_params(&myuvc_params);

myuvc_set_streaming_params(&myuvc_params);

myuvc_print_streaming_params(&myuvc_params);

usb_set_interface(myuvc_udev,myuvc_streaming_intf,

myuvc_streaming_bAlternateSetting);

/*2、分配设置URB(USBRequest Block)*/

myuvc_alloc_init_urbs();

/*3、提交向URB接受数据*/

for(i = 0; i < MYUVC_URBS; ++i) {

if ((ret =usb_submit_urb(myuvc_queue.urb[i], GFP_KERNEL)) < 0) {

printk("Failedto submit URB %u (%d).\n", i, ret);

myuvc_uninit_urbs();

return ret;

}

}

return 0;

}

简单的摄像头自写驱动程序总结相关推荐

  1. 三、使用HM进行简单的视频隐写demo

    三.使用HM进行简单的视频隐写 前言 一.实验环境 二.实验思路 三.实验过程 3.1 提取原始载体 3.2 使用LSB隐写算法进行隐写 3.3 放回含密载体 3.4 提取含密载体 3.5 使用LSB ...

  2. 梯形图调用c语言编写程序,简单使用C语言写梯形图精简V2.0.pdf

    简单使用C语言写梯形图精简V2.0 C语言实现梯形图写法 1: AND 2: LDI 3: ANDB 4: ANDP 5: ORB 6: C1 7: T0 8: MPS1 9: LDF 10-1: M ...

  3. android监控摄像头,监控其实很简单 给摄像头装个安卓系统

    如果你认为家庭监控摄像机和马路上专门拍车牌的摄像机一样,那你就错了.尽管这两种产品都被叫做监控摄像机,其功能和效果上还是有很大区别.如何给家庭监控摄像机一个更清晰.直观的定义?今天笔者就来跟您聊一聊. ...

  4. [Unity实战]一个简单的unity手写摇杆[入门级][手写demo][开箱可用]

    一个简单的unity手写摇杆 1.摇杆是什么 2.常见的unity摇杆插件 3.如何做一个简单摇杆(代码) 4.效果展示 5.github 1.摇杆是什么 固定移动摇杆的意思指固定一个摇杆的贴图,操作 ...

  5. 用简单的Python语句写了个模拟双色球摇奖,希望能中。。

    用简单的Python语句写了个模拟双色球摇奖,希望能中.. import random; #利用Python中的random.sample()函数实现 resultList = random.samp ...

  6. 瑞芯微RK3399K简单介绍及烧写镜像

    瑞芯微RK3399K简单介绍及烧写镜像 文章目录 瑞芯微RK3399K简单介绍及烧写镜像 1. 前言 2. 官网及相关资料 3. 烧写固件所需软件 4. 直接烧写单一固件步骤 5. 固件文件 6. W ...

  7. Python:简单的摄像头程序实现

    昨天安装了pygame,还没有具体学习如何用,先写了个最简单且原始的摄像头程序,画面还算流畅,不过还存在较多缺陷,后面对pygame熟悉了再一一优化. 1.实现: #!/usr/bin/env pyt ...

  8. 寒江独钓Windows内核安全编程__一个简单的Windows串口过滤驱动程序的开发

    在Windows系统上与安全软件相关的驱动开发过程中,"过滤(filter)"是极其重要的一个概念.过滤是在不影响上层和下层接口的情况下,在Windows系统内核中加入新的层,从而 ...

  9. android studio之简单调用摄像头并且获取其照片

    1.首先让我们来理清一下其中的逻辑:拍一张照片,获取其路径,根据路径进行展示. 2.好了我们已经理清好逻辑了:那我们就想如何实现,首先我们要实现一个app调用拍照功能,很简单,我们学过用一个活动调用另 ...

  10. 2.3 摄像头驱动_vivi驱动程序分析

    学习目标:熟悉vivi的调用过程,分析vivi程序源码的ioctl函数: 一.vivi虚拟视频驱动测试方法 当我们接上usb摄像头设备时,系统会自动给我们安装对应的usb设备驱动程序.如果下次直接测试 ...

最新文章

  1. Python xrange() 函数
  2. 更加安全的存取账户密码
  3. [改善Java代码]养成良好习惯,显式声明UID
  4. 【OS学习笔记】三十二 保护模式九:分页机制对应的汇编代码之---内核代码
  5. nacos1.2(1.3)的下载及CentOS7下安装nacos
  6. 运算放大器基本公式_还在被三阶/四阶/运算放大器滤波器PLL这些概念困扰?这篇文章帮你搞懂它...
  7. 八种常用排序算法参考
  8. Layer下拉框监听
  9. react 使用rewired_使用react-app-rewired和customize-cra对默认webpack自定义配置
  10. ArangoDB查询语言(AQL) 基本语法用法
  11. 浅谈机器学习评估中的穿越问题
  12. cf div2 #444 (ABC)(D已补)
  13. 实验4-1-5 统计素数并求和 (20 分)
  14. 第十三章:项目相关方管理 - (13.1 识别相关方)
  15. 连接字符串的几种方式
  16. 软件开发项目经理岗位职责
  17. Paint in 3D 基本使用方法(Unity 3D)
  18. MOS管的知识,看这一篇就可以了
  19. vue项目的优化(一)
  20. 北京理工大学复试上机题汇总

热门文章

  1. HTTP:实时消息推送
  2. Linux内核 触摸板,【教程】给Linux(elementary os)配置更多的触摸板手势
  3. python怎么跳出while循环_怎么跳出while循环
  4. CAD绘制填充并设置填充透明度
  5. 老调新弹,也玩Linq To Sql批操作
  6. 实例总结C#反射基础知识
  7. 社交网络时代下的网络营销
  8. qq企业邮箱怎么删除邮件服务器,腾讯企业邮箱删除邮件方法
  9. CSS精灵优化Retina显示屏下的网站图像
  10. 青出于蓝而胜于蓝 — Vue.js对Angular.js的那些进步