当前位置:   article > 正文

linux设备驱动——UVC驱动程序_linux uvc驱动 c源码

linux uvc驱动 c源码

1.uvc驱动框架

首先,因为要用USB传数据,入口函数中注册usb_driver结构体,出口函数反之

  1. static int myuvc_init(void)
  2. {
  3. usb_register(&myuvc_driver);
  4. return 0;
  5. }
  6. static void myuvc_exit(void)
  7. {
  8. usb_deregister(&myuvc_driver);
  9. }

usb_driver结构体中提供probe函数和id_table

  1. static struct usb_driver myuvc_driver = {
  2. .name = "myuvc",
  3. .probe = myuvc_probe,
  4. .disconnect = myuvc_disconnect,
  5. .id_table = myuvc_ids,
  6. };

id_table用来判断此驱动是否支持某USB设备

  1. static struct usb_device_id myuvc_ids[] = {
  2. /* Generic USB Video Class */
  3. { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /* VideoControl Interface */
  4. { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /* VideoStreaming Interface */
  5. {}
  6. };

如果插入一个USB设备,判断id_table通过则会调用此驱动的probe函数

UVC的probe函数主要做这几件事:

1.分配video_device结构体

video_device_alloc()

2. 设置video_device结构体

3. 注册video_device结构体

video_register_device(myuvc_vdev, VFL_TYPE_GRABBER, -1)

这里UVC驱动程序把video_device结构体注册给上一层的v4l2-dev.c文件,在v4l2-dev.c中会注册字符设备等一些操作,上一层的操作我们不关心,我们只需要在UVC设备驱动层提供一个video_device结构体就行了

我们来看看需要设置video_device结构体的哪些内容

  1. struct video_device
  2. {
  3. /* device ops */
  4. const struct v4l2_file_operations *fops;
  5. /* sysfs */
  6. struct device dev; /* v4l device */
  7. struct cdev *cdev; /* character device */
  8. /* Set either parent or v4l2_dev if your driver uses v4l2_device */
  9. struct device *parent; /* device parent */
  10. struct v4l2_device *v4l2_dev; /* v4l2_device parent */
  11. /* device info */
  12. char name[32];
  13. int vfl_type;
  14. /* 'minor' is set to -1 if the registration failed */
  15. int minor;
  16. u16 num;
  17. /* use bitops to set/clear/test flags */
  18. unsigned long flags;
  19. /* attribute to differentiate multiple indices on one physical device */
  20. int index;
  21. int debug; /* Activates debug level*/
  22. /* Video standard vars */
  23. v4l2_std_id tvnorms; /* Supported tv norms */
  24. v4l2_std_id current_norm; /* Current tvnorm */
  25. /* callbacks */
  26. void (*release)(struct video_device *vdev);
  27. /* ioctl callbacks */
  28. const struct v4l2_ioctl_ops *ioctl_ops;
  29. };

别惊讶video_device有这么多内容,其实只有设置其中几项就可以工作了

1.

void (*release)(struct video_device *vdev)

这项要设置,不然装载驱动的时候会报错,写个空函数就行了

2.

const struct v4l2_file_operations *fops;

应用程序open等,会直接调用fops中的open

3.

const struct v4l2_ioctl_ops *ioctl_ops

应用程序ioctl,会直接调用这里的ioctl


很明显,我们只要把fops和ioctl_ops这两个结构体设置好就OK了


2.ioctl_ops结构体

ioctl_ops的定义里有好多项,但是必须的只有以下11项

  1. static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
  2. // 表示它是一个摄像头设备
  3. .vidioc_querycap = myuvc_vidioc_querycap,
  4. /* 用于列举、获得、测试、设置摄像头的数据的格式 */
  5. .vidioc_enum_fmt_vid_cap = myuvc_vidioc_enum_fmt_vid_cap,
  6. .vidioc_g_fmt_vid_cap = myuvc_vidioc_g_fmt_vid_cap,
  7. .vidioc_try_fmt_vid_cap = myuvc_vidioc_try_fmt_vid_cap,
  8. .vidioc_s_fmt_vid_cap = myuvc_vidioc_s_fmt_vid_cap,
  9. /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
  10. .vidioc_reqbufs = myuvc_vidioc_reqbufs,
  11. .vidioc_querybuf = myuvc_vidioc_querybuf,
  12. .vidioc_qbuf = myuvc_vidioc_qbuf,
  13. .vidioc_dqbuf = myuvc_vidioc_dqbuf,
  14. // 启动/停止
  15. .vidioc_streamon = myuvc_vidioc_streamon,
  16. .vidioc_streamoff = myuvc_vidioc_streamoff,
  17. };

2.1 vidioc_querycap

应用程序会调用它来判断此设备设否是一个摄像头设备

我们只要把传进来的v4l2_capability设置相应的参数就可以了

  1. static int myuvc_vidioc_querycap(struct file *file, void *priv,
  2. struct v4l2_capability *cap)
  3. {
  4. memset(cap, 0, sizeof *cap);
  5. strcpy(cap->driver, "myuvc");
  6. strcpy(cap->card, "myuvc");
  7. cap->version = 1;
  8. cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
  9. return 0;
  10. }

2.2 vidioc_enum_fmt_vid_cap

应用程序会调用它来获取摄像头所支持的格式(数据格式),这里我们只返回一种格式

  1. static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
  2. struct v4l2_fmtdesc *f)
  3. {
  4. /* 人工查看描述符可知我们用的摄像头只支持1种格式 */
  5. if (f->index >= 1)
  6. return -EINVAL;
  7. /* 支持什么格式呢?
  8. * 查看VideoStreaming Interface的描述符,
  9. * 得到GUID为"59 55 59 32 00 00 10 00 80 00 00 aa 00 38 9b 71"
  10. */
  11. strcpy(f->description, "4:2:2, packed, YUYV");
  12. f->pixelformat = V4L2_PIX_FMT_YUYV;
  13. return 0;
  14. }


2.3 vidioc_g_fmt_vid_cap

获取当前格式

驱动程序会把当前的格式存在一个内存的一个v4l2_format结构体中,应用程序要获取当前格式只需要把这个结构体返回给应用程序

  1. static int myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
  2. struct v4l2_format *f)
  3. {
  4. memcpy(f, &myuvc_format, sizeof(myuvc_format));
  5. return (0);
  6. }

2.4 vidioc_try_fmt_vid_cap

测试是否支持此格式

应用程序有可能会设置某摄像头不支持的格式(分辨率等),所以在设置之前要先测试一下是否支持

此函数中会进行判断,如果某些参数不支持则返回错误,,支持的话会把一些其他参数强制设成摄像头所支持的

  1. static int myuvc_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
  2. struct v4l2_format *f)
  3. {
  4. if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
  5. {
  6. return -EINVAL;
  7. }
  8. if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
  9. return -EINVAL;
  10. /* 调整format的width, height,
  11. * 计算bytesperline, sizeimage
  12. */
  13. /* 人工查看描述符, 确定支持哪几种分辨率 */
  14. f->fmt.pix.width = frames[frame_idx].width;
  15. f->fmt.pix.height = frames[frame_idx].height;
  16. f->fmt.pix.bytesperline =
  17. (f->fmt.pix.width * bBitsPerPixel) >> 3;
  18. f->fmt.pix.sizeimage =
  19. f->fmt.pix.height * f->fmt.pix.bytesperline;
  20. return 0;
  21. }

2.5 myuvc_vidioc_s_fmt_vid_cap

设置参数

应用程序设置参数,这里先要测试一下参数是否可用,然后把这些参数保存在内存中,留着以后用

  1. static int myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
  2. struct v4l2_format *f)
  3. {
  4. int ret = myuvc_vidioc_try_fmt_vid_cap(file, NULL, f);
  5. if (ret < 0)
  6. return ret;
  7. memcpy(&myuvc_format, f, sizeof(myuvc_format));
  8. return 0;
  9. }

2.6 vidioc_reqbufs

分配缓冲区,并放入队列

1.USB传来的数据会存到这些缓冲区里,应用程序读数据再从这些缓冲区里读

2.缓冲区会分配几个,但是会一次性分配,给出缓冲区的起始地址,不同的缓冲区会有不同的偏移值

3.这些缓冲区的描述在一个结构体中,在myuvc_queue中有结构体myuvc_buffer,是描述这些buffer用的

4.两个队列,队列mainqueue供APP读数据用,队列irqqueue供USB传输数据用,队列的定义也在myuvc_queue中


  1. struct myuvc_queue {
  2. void *mem;
  3. int count;
  4. int buf_size;
  5. struct myuvc_buffer buffer[32];
  6. struct urb *urb[32];
  7. char *urb_buffer[32];
  8. dma_addr_t urb_dma[32];
  9. unsigned int urb_size;
  10. struct list_head mainqueue; /* 供APP消费用 */
  11. struct list_head irqqueue; /* 供底层驱动生产用 */
  12. };

  1. struct myuvc_buffer {
  2. struct v4l2_buffer buf;
  3. int state;
  4. int vma_use_count; /* 表示是否已经被mmap */
  5. wait_queue_head_t wait; /* APP要读某个缓冲区,如果无数据,在此休眠 */
  6. struct list_head stream;
  7. struct list_head irq;
  8. };

下面是分配缓冲区的具体函数

  1. static int myuvc_vidioc_reqbufs(struct file *file, void *priv,
  2. struct v4l2_requestbuffers *p)
  3. {
  4. int nbuffers = p->count;
  5. int bufsize = PAGE_ALIGN(myuvc_format.fmt.pix.sizeimage);
  6. unsigned int i;
  7. void *mem = NULL;
  8. int ret;
  9. if ((ret = myuvc_free_buffers()) < 0)
  10. goto done;
  11. /* Bail out if no buffers should be allocated. */
  12. if (nbuffers == 0)
  13. goto done;
  14. /* Decrement the number of buffers until allocation succeeds. */
  15. for (; nbuffers > 0; --nbuffers) {
  16. mem = vmalloc_32(nbuffers * bufsize);
  17. if (mem != NULL)
  18. break;
  19. }
  20. if (mem == NULL) {
  21. ret = -ENOMEM;
  22. goto done;
  23. }
  24. /* 这些缓存是一次性作为一个整体来分配的 */
  25. memset(&myuvc_queue, 0, sizeof(myuvc_queue));
  26. INIT_LIST_HEAD(&myuvc_queue.mainqueue);
  27. INIT_LIST_HEAD(&myuvc_queue.irqqueue);
  28. for (i = 0; i < nbuffers; ++i) {
  29. myuvc_queue.buffer[i].buf.index = i;
  30. myuvc_queue.buffer[i].buf.m.offset = i * bufsize;
  31. myuvc_queue.buffer[i].buf.length = myuvc_format.fmt.pix.sizeimage;
  32. myuvc_queue.buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  33. myuvc_queue.buffer[i].buf.sequence = 0;
  34. myuvc_queue.buffer[i].buf.field = V4L2_FIELD_NONE;
  35. myuvc_queue.buffer[i].buf.memory = V4L2_MEMORY_MMAP;
  36. myuvc_queue.buffer[i].buf.flags = 0;
  37. myuvc_queue.buffer[i].state = VIDEOBUF_IDLE;
  38. init_waitqueue_head(&myuvc_queue.buffer[i].wait);
  39. }
  40. myuvc_queue.mem = mem;
  41. myuvc_queue.count = nbuffers;
  42. myuvc_queue.buf_size = bufsize;
  43. ret = nbuffers;
  44. done:
  45. return ret;
  46. }

2.6 vidioc_querybuf

查询缓存的信息

这很简单只需要把myuvc_queue中相应的buffer中的buf赋给传入的参数就可以了,其余的操作是设置缓冲区状态

  1. static int myuvc_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
  2. {
  3. int ret = 0;
  4. if (v4l2_buf->index >= myuvc_queue.count) {
  5. ret = -EINVAL;
  6. goto done;
  7. }
  8. memcpy(v4l2_buf, &myuvc_queue.buffer[v4l2_buf->index].buf, sizeof(*v4l2_buf));
  9. /* 更新flags */
  10. if (myuvc_queue.buffer[v4l2_buf->index].vma_use_count)
  11. v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
  12. switch (myuvc_queue.buffer[v4l2_buf->index].state) {
  13. case VIDEOBUF_ERROR:
  14. case VIDEOBUF_DONE:
  15. v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
  16. break;
  17. case VIDEOBUF_QUEUED:
  18. case VIDEOBUF_ACTIVE:
  19. v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
  20. break;
  21. case VIDEOBUF_IDLE:
  22. default:
  23. break;
  24. }
  25. done:
  26. return ret;
  27. }

2.7 vidioc_qbuf

把缓冲区放入两个队列,主要是

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

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

这两个函数,其余的都是判断和修改缓冲区状态

  1. static int myuvc_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
  2. {
  3. struct myuvc_buffer *buf;
  4. int ret;
  5. /* 0. APP传入的v4l2_buf可能有问题, 要做判断 */
  6. if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
  7. v4l2_buf->memory != V4L2_MEMORY_MMAP) {
  8. return -EINVAL;
  9. }
  10. if (v4l2_buf->index >= myuvc_queue.count) {
  11. return -EINVAL;
  12. }
  13. buf = &myuvc_queue.buffer[v4l2_buf->index];
  14. if (buf->state != VIDEOBUF_IDLE) {
  15. return -EINVAL;
  16. }
  17. /* 1. 修改状态 */
  18. buf->state = VIDEOBUF_QUEUED;
  19. buf->buf.bytesused = 0;
  20. /* 2. 放入2个队列 */
  21. /* 队列1: 供APP使用
  22. * 当缓冲区没有数据时,放入mainqueue队列
  23. * 当缓冲区有数据时, APP从mainqueue队列中取出
  24. */
  25. list_add_tail(&buf->stream, &myuvc_queue.mainqueue);
  26. /* 队列2: 供产生数据的函数使用
  27. * 当采集到数据时,从irqqueue队列中取出第1个缓冲区,存入数据
  28. */
  29. list_add_tail(&buf->irq, &myuvc_queue.irqqueue);
  30. return 0;
  31. }

2.8 vidioc_dqbuf

取出队列,供APP使用,APP调用此函数把缓冲区从mainqueue队列中取出

  1. static int myuvc_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
  2. {
  3. /* APP发现数据就绪后, 从mainqueue里取出这个buffer */
  4. struct myuvc_buffer *buf;
  5. int ret = 0;
  6. if (list_empty(&myuvc_queue.mainqueue)) {
  7. ret = -EINVAL;
  8. goto done;
  9. }
  10. buf = list_first_entry(&myuvc_queue.mainqueue, struct myuvc_buffer, stream);
  11. switch (buf->state) {
  12. case VIDEOBUF_ERROR:
  13. ret = -EIO;
  14. case VIDEOBUF_DONE:
  15. buf->state = VIDEOBUF_IDLE;
  16. break;
  17. case VIDEOBUF_IDLE:
  18. case VIDEOBUF_QUEUED:
  19. case VIDEOBUF_ACTIVE:
  20. default:
  21. ret = -EINVAL;
  22. goto done;
  23. }
  24. list_del(&buf->stream);
  25. done:
  26. return ret;
  27. }

2.9 vidioc_streamon

启动摄像头传输

要做的工作有:

1. 向USB摄像头设置参数: 比如使用哪个format

2. 分配设置URB

3. 提交URB以接收数据


2.9.1 设置参数

设置streaming_control

参数存在intf->cur_altsetting->desc.bInterfaceNumber中,已被USB总线驱动读到内存,可以直接读取
streaming_control不是标准的USB描述符,USB总线驱动里没有定义结构体,我们要先参考uvcvideo.c定义streaming_control结构体

1.测试参数
设置参数前要先把参数发给摄像头,让摄像头判断这些参数可用否
把streaming_control结构体的数据赋值给数组data
用usb_control_msg发送数据测试

  1. static int myuvc_try_streaming_params(struct myuvc_streaming_control *ctrl)
  2. {
  3. __u8 *data;
  4. __u16 size;
  5. int ret;
  6. __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
  7. unsigned int pipe;
  8. memset(ctrl, 0, sizeof *ctrl);
  9. ctrl->bmHint = 1; /* dwFrameInterval */
  10. ctrl->bFormatIndex = 1;
  11. ctrl->bFrameIndex = frame_idx + 1;
  12. ctrl->dwFrameInterval = 333333;
  13. size = uvc_version >= 0x0110 ? 34 : 26;
  14. data = kzalloc(size, GFP_KERNEL);
  15. if (data == NULL)
  16. return -ENOMEM;
  17. *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
  18. data[2] = ctrl->bFormatIndex;
  19. data[3] = ctrl->bFrameIndex;
  20. *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
  21. *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
  22. *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
  23. *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
  24. *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
  25. *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
  26. put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
  27. put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
  28. if (size == 34) {
  29. put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
  30. data[30] = ctrl->bmFramingInfo;
  31. data[31] = ctrl->bPreferedVersion;
  32. data[32] = ctrl->bMinVersion;
  33. data[33] = ctrl->bMaxVersion;
  34. }
  35. pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(myuvc_udev, 0)
  36. : usb_sndctrlpipe(myuvc_udev, 0);
  37. type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
  38. ret = usb_control_msg(myuvc_udev, pipe, SET_CUR, type, VS_PROBE_CONTROL << 8,
  39. 0 << 8 | myuvc_streaming_intf, data, size, 5000);
  40. kfree(data);
  41. return (ret < 0) ? ret : 0;
  42. }


2.读参数
如果测试成功,摄像头里的有些数据会更新,所以我们先把数据读到内存中我们定义的streaming_control结构体中
读出数据到数组data里,再赋值给streaming_control结构体
用usb_control_msg获得数据
  1. static int myuvc_get_streaming_params(struct myuvc_streaming_control *ctrl)
  2. {
  3. __u8 *data;
  4. __u16 size;
  5. int ret;
  6. __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
  7. unsigned int pipe;
  8. size = uvc_version >= 0x0110 ? 34 : 26;
  9. data = kmalloc(size, GFP_KERNEL);
  10. if (data == NULL)
  11. return -ENOMEM;
  12. pipe = (GET_CUR & 0x80) ? usb_rcvctrlpipe(myuvc_udev, 0)
  13. : usb_sndctrlpipe(myuvc_udev, 0);
  14. type |= (GET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
  15. ret = usb_control_msg(myuvc_udev, pipe, GET_CUR, type, VS_PROBE_CONTROL << 8,
  16. 0 << 8 | myuvc_streaming_intf, data, size, 5000);
  17. if (ret < 0)
  18. goto done;
  19. ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
  20. ctrl->bFormatIndex = data[2];
  21. ctrl->bFrameIndex = data[3];
  22. ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
  23. ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
  24. ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
  25. ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
  26. ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
  27. ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
  28. ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
  29. ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
  30. if (size == 34) {
  31. ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
  32. ctrl->bmFramingInfo = data[30];
  33. ctrl->bPreferedVersion = data[31];
  34. ctrl->bMinVersion = data[32];
  35. ctrl->bMaxVersion = data[33];
  36. } else {
  37. //ctrl->dwClockFrequency = video->dev->clock_frequency;
  38. ctrl->bmFramingInfo = 0;
  39. ctrl->bPreferedVersion = 0;
  40. ctrl->bMinVersion = 0;
  41. ctrl->bMaxVersion = 0;
  42. }
  43. done:
  44. kfree(data);
  45. return (ret < 0) ? ret : 0;
  46. }


3.设置参数
设置参数的函数几乎与测试参数的函数相同,
区别是少了设置的部分和usb_control_msg的参数不同

  1. static int myuvc_set_streaming_params(struct myuvc_streaming_control *ctrl)
  2. {
  3. __u8 *data;
  4. __u16 size;
  5. int ret;
  6. __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
  7. unsigned int pipe;
  8. size = uvc_version >= 0x0110 ? 34 : 26;
  9. data = kzalloc(size, GFP_KERNEL);
  10. if (data == NULL)
  11. return -ENOMEM;
  12. *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
  13. data[2] = ctrl->bFormatIndex;
  14. data[3] = ctrl->bFrameIndex;
  15. *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
  16. *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
  17. *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
  18. *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
  19. *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
  20. *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
  21. put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
  22. put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
  23. if (size == 34) {
  24. put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
  25. data[30] = ctrl->bmFramingInfo;
  26. data[31] = ctrl->bPreferedVersion;
  27. data[32] = ctrl->bMinVersion;
  28. data[33] = ctrl->bMaxVersion;
  29. }
  30. pipe = (SET_CUR & 0x80) ? usb_rcvctrlpipe(myuvc_udev, 0)
  31. : usb_sndctrlpipe(myuvc_udev, 0);
  32. type |= (SET_CUR & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
  33. ret = usb_control_msg(myuvc_udev, pipe, SET_CUR, type, VS_COMMIT_CONTROL << 8,
  34. 0 << 8 | myuvc_streaming_intf, data, size, 5000);
  35. kfree(data);
  36. return (ret < 0) ? ret : 0;
  37. }


2.9.2 URB

1. 分配usb_buffers

2. 分配urb

3. 设置urb

4. 提交urb

  1. static int myuvc_alloc_init_urbs(void)
  2. {
  3. u16 psize;
  4. u32 size;
  5. int npackets;
  6. int i;
  7. int j;
  8. struct urb *urb;
  9. psize = wMaxPacketSize; /* 实时传输端点一次能传输的最大字节数 */
  10. size = myuvc_params.dwMaxVideoFrameSize; /* 一帧数据的最大长度 */
  11. npackets = DIV_ROUND_UP(size, psize);
  12. if (npackets > 32)
  13. npackets = 32;
  14. size = myuvc_queue.urb_size = psize * npackets;
  15. for (i = 0; i < MYUVC_URBS; ++i) {
  16. /* 1. 分配usb_buffers */
  17. myuvc_queue.urb_buffer[i] = usb_buffer_alloc(
  18. myuvc_udev, size,
  19. GFP_KERNEL | __GFP_NOWARN, &myuvc_queue.urb_dma[i]);
  20. /* 2. 分配urb */
  21. myuvc_queue.urb[i] = usb_alloc_urb(npackets, GFP_KERNEL);
  22. if (!myuvc_queue.urb_buffer[i] || !myuvc_queue.urb[i])
  23. {
  24. myuvc_uninit_urbs();
  25. return -ENOMEM;
  26. }
  27. }
  28. /* 3. 设置urb */
  29. for (i = 0; i < MYUVC_URBS; ++i) {
  30. urb = myuvc_queue.urb[i];
  31. urb->dev = myuvc_udev;
  32. urb->context = NULL;
  33. urb->pipe = usb_rcvisocpipe(myuvc_udev,myuvc_bEndpointAddress);
  34. urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
  35. urb->interval = 1;
  36. urb->transfer_buffer = myuvc_queue.urb_buffer[i];
  37. urb->transfer_dma = myuvc_queue.urb_dma[i];
  38. urb->complete = myuvc_video_complete;
  39. urb->number_of_packets = npackets;
  40. urb->transfer_buffer_length = size;
  41. for (j = 0; j < npackets; ++j) {
  42. urb->iso_frame_desc[j].offset = j * psize;
  43. urb->iso_frame_desc[j].length = psize;
  44. }
  45. }
  46. /* 4. 提交URB以接收数据 */
  47. for (i = 0; i < MYUVC_URBS; ++i) {
  48. if ((ret = usb_submit_urb(myuvc_queue.urb[i], GFP_KERNEL)) < 0) {
  49. printk("Failed to submit URB %u (%d).\n", i, ret);
  50. myuvc_uninit_urbs();
  51. return ret;
  52. }
  53. }
  54. return 0;
  55. }







声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/444484
推荐阅读
相关标签
  

闽ICP备14008679号