自己写的uvc驱动支持IP2977/ip2970
Posted Jarry_le
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自己写的uvc驱动支持IP2977/ip2970相关的知识,希望对你有一定的参考价值。
- /* 作者: 453411484@qq.com
- * 此驱动程序是基于linux2.6.31.14内核
- * 上一篇自己写的uvc驱动程序是针对环宇飞扬6190来写的,有一些缺点,这些缺点在本次的驱动
- * 中进行了修改此uvc驱动是针对IP2977进行了支持,根据IP2977芯片厂商提供的修改手册进行了修改。
- * 此驱动程序虽然支持IP2977摄像头,但是显示效果不太好,此驱动只是针对学习使用。
- */
- #include <linux kernel="" h="">
- #include <linux list="" h="">
- #include <linux module="" h="">
- #include <linux usb="" h="">
- #include <linux videodev2="" h="">
- #include <linux vmalloc="" h="">
- #include <linux wait="" h="">
- #include <linux mm="" h="">
- #include <asm atomic="" h="">
- #include <asm unaligned="" h="">
- #include <media v4l2-common="" h="">
- #include <media v4l2-ioctl="" h="">
- #include <media videobuf-core="" h="">
- #include "uvcvideo.h"
- /* 参考 drivers/media/video/uvc */
- #define MYUVC_URBS 5
- /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
- #define UVC_STREAM_EOH (1 << 7)
- #define UVC_STREAM_ERR (1 << 6)
- #define UVC_STREAM_STI (1 << 5)
- #define UVC_STREAM_RES (1 << 4)
- #define UVC_STREAM_SCR (1 << 3)
- #define UVC_STREAM_PTS (1 << 2)
- #define UVC_STREAM_EOF (1 << 1)
- #define UVC_STREAM_FID (1 << 0)
- 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;
- ;
- struct frame_desc
- int width;
- int height;
- ;
- /* 参考uvc_video_queue定义一些结构体 */
- struct myuvc_buffer
- struct v4l2_buffer buf;
- int state;
- int vma_use_count; /* 表示是否已经被mmap */
- wait_queue_head_t wait; /* APP要读某个缓冲区,如果无数据,在此休眠 */
- struct list_head stream;
- struct list_head irq;
- ;
- struct myuvc_queue
- void *mem;
- int count;
- int buf_size;
- struct myuvc_buffer buffer[32];
- struct urb *urb[32];
- char *urb_buffer[32];
- dma_addr_t urb_dma[32];
- unsigned int urb_size;
- struct list_head mainqueue; /* 供APP消费用 */
- struct list_head irqqueue; /* 供底层驱动生产用 */
- ;
- static struct myuvc_queue myuvc_queue;
- static struct video_device *myuvc_vdev;
- static struct usb_device *myuvc_udev;
- static int myuvc_bEndpointAddress = 0x82;
- static int myuvc_streaming_intf;
- static int myuvc_control_intf;
- static struct v4l2_format myuvc_format;
- static struct frame_desc frames[] = 640, 480, 320, 240, 160, 120;
- static int frame_idx = 1;
- static int bBitsPerPixel = 0; /* lsusb -v -d 0x1e4e: "bBitsPerPixel" */
- static int uvc_version = 0x0100; /* lsusb -v -d 0x1b3b: bcdUVC */
- static int ProcessingUnitID = 3; /* lsusb -v -d 0x1b3b: PROCESSING_UNIT */
- /* 以后再设置 */
- static int wMaxPacketSize = 800;
- static int myuvc_streaming_bAlternateSetting = 5;
- static int dwMaxVideoFrameSize = 77312;
- static struct myuvc_streaming_control myuvc_params;
- static int last_fid = -1;
- /* A2 参考 uvc_v4l2_do_ioctl */
- 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 = 1;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- return 0;
- /* A3 列举支持哪种格式
- * 参考: uvc_fmts 数组
- */
- static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
- /* 人工查看描述符可知我们用的摄像头只支持1种格式 */
- if (f->index >= 1)
- return -EINVAL;
- strcpy(f->description, "MJPEG");
- f->pixelformat = V4L2_PIX_FMT_MJPEG;
- //f->flags = ;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- return 0;
- /* A4 返回当前所使用的格式 */
- static int myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
- memcpy(f, &myuvc_format, sizeof(myuvc_format));
- return (0);
- /* A5 测试驱动程序是否支持某种格式, 强制设置该格式
- * 参考: uvc_v4l2_try_format
- * myvivi_vidioc_try_fmt_vid_cap
- */
- 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)
- return -EINVAL;
- if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
- /* 调整format的width, height,
- * 计算bytesperline, sizeimage
- */
- /* 人工查看描述符, 确定支持哪几种分辨率 */
- f->fmt.pix.width = frames[frame_idx].width;
- f->fmt.pix.height = frames[frame_idx].height;
- /* 对于MJPEG数据类型来说,bBitsPerPixel是无法知道的。因此我们设为0 */
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * bBitsPerPixel) >> 3;
- /* 一帧数据最大为dwMaxVideoFrameSize=77312,是由确定带宽时确定出来的。 */
- f->fmt.pix.sizeimage = dwMaxVideoFrameSize;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- /* 由描述符bColorPrimaries 1 (BT.709,sRGB)可知 */
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
- f->fmt.pix.priv = 0; /* private data, depends on pixelformat */
- return 0;
- /* A6 参考 myvivi_vidioc_s_fmt_vid_cap */
- static int myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_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;
- static int 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;
- /* A7 APP调用该ioctl让驱动程序分配若干个缓存, APP将从这些缓存中读到视频数据
- * 参考: uvc_alloc_buffers
- */
- static int 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 be allocated. */
- if (nbuffers == 0)
- goto done;
- /* Decrement the number of buffers until allocation 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:
- return ret;
- /* A8 查询缓存状态, 比如地址信息(APP可以用mmap进行映射)
- * 参考 uvc_query_buffer
- */
- static int myuvc_vidioc_querybuf(struct file *file, void *priv, struct v4l2_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)
- 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;
- /* A10 把缓冲区放入队列, 底层的硬件操作函数将会把数据放入这个队列的缓存
- * 参考: uvc_queue_buffer
- */
- static int myuvc_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
- struct myuvc_buffer *buf;
- /* 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;
- static void myuvc_print_streaming_params(struct myuvc_streaming_control *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);
- /* 参考: uvc_get_video_ctrl
- (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR))
- static int uvc_get_video_ctrl(struct uvc_video_device *video,
- struct uvc_streaming_control *ctrl, int probe, __u8 query)
- */
- static int 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)
- 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;
- /* 参考: uvc_v4l2_try_format ∕uvc_probe_video
- * uvc_set_video_ctrl(video, probe, 1)
- */
- static int myuvc_try_streaming_params(struct myuvc_streaming_control *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 = 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(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);
- kfree(data);
- return (ret < 0) ? ret : 0;
- /* 参考: uvc_v4l2_try_format ∕uvc_probe_video
- * uvc_set_video_ctrl(video, probe, 1)
- */
- static int 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;
- static void myuvc_uninit_urbs(void)
- int i;
- for (i = 0; i < MYUVC_URBS; ++i)
- if (myuvc_queue.urb_buffer[i])
- usb_buffer_free(myuvc_udev, myuvc_queue.urb_size, myuvc_queue.urb_buffer[i], myuvc_queue.urb_dma[i]);
- myuvc_queue.urb_buffer[i] = NULL;
- if (myuvc_queue.urb[i])
- usb_free_urb(myuvc_queue.urb[i]);
- myuvc_queue.urb[i] = NULL;
- /* 参考: uvc_video_complete / uvc_video_decode_isoc */
- static void myuvc_video_complete(struct urb *urb)
- u8 *src;
- u8 *dest;
- int ret, i;
- int len;
- int maxlen;
- int nbytes;
- struct myuvc_buffer *buf;
- static int fid;
- static int wake_cnt = 0;
- // 要修改影像資料,必須先宣告一個特別型態的指標變數,才能正確存取記憶體中的資料
- unsigned char *point_mem;
- static unsigned char *mem_temp = NULL;
- // 初始化暫存用的記憶體位置
- static unsigned int nArrayTemp_Size = 1000;
- u8* mem;
- int data_len;
- switch (urb->status)
- case 0:
- break;
- default:
- printk("Non-zero status (%d) in video "
- "completion handler.\\n", urb->status);
- return;
- /* 如果irqqueue队列不空的话,从irqqueue队列中取出第1个缓冲区,用于存放urb的数据 */
- if (!list_empty(&myuvc_queue.irqqueue))
- buf = list_first_entry(&myuvc_queue.irqqueue, struct myuvc_buffer, irq);
- else
- /* 什么情况下会导致irqqueue队列为空呢?
- * usb设备填充数据太快,但是应用程序没能及时处理
- */
- buf = NULL;
- /* 开始处理urb中的每一个小包 */
- 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;
- src = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- 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 the error bit ("error frames"). */
- if (src[1] & UVC_STREAM_ERR)
- //printk("Dropping payload (error bit set).\\n");
- continue;
- /* 根据ip2970/ip2977厂家提供的修改手册,如果想要支持ip2970/ip2977就必须
- * 添加如下代码
- */
- if (myuvc_udev->descriptor.idVendor == 0x1B3B)
- if ( len >= 16 ) // have data in buffer
- // 資料必須從data[12]開始判斷,是因為前面的資料是封包專用
- if ( (src[12]==0xFF && src[13]==0xD8 && src[14]==0xFF) ||
- (src[12]==0xD8 && src[13]==0xFF && src[14]==0xC4))
- if(last_fid)
- fid &= ~UVC_STREAM_FID;
- else
- fid |= UVC_STREAM_FID;
- else
- fid = src[1] & UVC_STREAM_FID;
- /* Store the payload FID bit and return immediately when the buffer is
- * NULL.
- */
- if (buf == NULL)
- last_fid = fid;
- continue;
- /* 根据FID判断当前帧的数据是否结束 */
- /* VIDEOBUF_ACTIVE表示正在接收数据,buf->state!= VIDEOBUF_ACTIVE, 表示"之前还未接收数据",
- * 因此就是说即将接收数据
- */
- if (buf->state != VIDEOBUF_ACTIVE)
- if (fid == last_fid)
- /* 既然你刚开始接收数据, 那么FID应该是一个新的值,不应该等于原来的last_fid */
- continue;
- /* 表示开始接收第1个数据 */
- buf->state = VIDEOBUF_ACTIVE;
- /* fid表示当前的fid。
- * fid != last_fid 表示开始新一帧了,当前的buffer已经被填充完了。
- * 这个地方是从fid来判断是否一帧数据已经结束,下面还有从数据是否填充不下了来
- * 强制认为一帧数据已经结束
- */
- if (fid != last_fid && buf->buf.bytesused != 0)
- buf->state = VIDEOBUF_DONE;
- /* 从队列中删除, 唤醒进程 */
- list_del(&buf->irq);
- wake_up(&buf->wait);
- mem = myuvc_queue.mem + buf->buf.m.offset;
- data_len = buf->buf.bytesused;
- //printk("wake_up %d : %s %d, start data: %02x %02x, end data: %02x %02x", wake_cnt++, __FUNCTION__, __LINE__, mem[0], mem[1], mem[data_len - 2], mem[data_len - 1]);
- /* 取出下一个buf */
- if (!list_empty(&myuvc_queue.irqqueue))
- buf = list_first_entry(&myuvc_queue.irqqueue, struct myuvc_buffer, irq);
- else
- buf = NULL;
- continue;
- last_fid = fid;
- dest = myuvc_queue.mem + buf->buf.m.offset + buf->buf.bytesused;
- /* 除去头部后的数据长度 */
- len -= src[0];
- /* 缓冲区最多还能存多少数据 */
- maxlen = buf->buf.length - buf->buf.bytesused;
- nbytes = min(len, maxlen);
- /* 复制数据 */
- memcpy(dest, src + src[0], nbytes);
- buf->buf.bytesused += nbytes;
- /* ip2970/ip2977 */
- if (myuvc_udev->descriptor.idVendor == 0x1B3B)
- if(mem_temp == NULL)
- mem_temp = kmalloc(nArrayTemp_Size, GFP_KERNEL);
- else if(nArrayTemp_Size <= nbytes) // 當收到的資料長度大於上一次的資料長度,則重新分配所需的空間+
- kfree(mem_temp);
- nArrayTemp_Size += 500;
- mem_temp = kmalloc(nArrayTemp_Size, GFP_KERNEL);
- memset(mem_temp, 0x00, nArrayTemp_Size);
- // 指向資料儲存的記憶體位置
- point_mem = (unsigned char *)dest;
- if( *(point_mem) == 0xD8 && *(point_mem + 1) == 0xFF && *(point_mem + 2) == 0xC4)
- memcpy( mem_temp + 1, point_mem, nbytes);
- mem_temp[0] = 0xFF;
- memcpy(point_mem, mem_temp, nbytes + 1);
- /* 判断一帧数据是否已经全部接收到
- * 如果数据已经满了,就强制认为已经结束了
- */
- if (len > maxlen)
- buf->state = VIDEOBUF_DONE;
- /* Mark the buffer as done if the EOF 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 empty payload.\\n");
- buf->state = VIDEOBUF_DONE;
- /* 当接收完一帧数据,
- * 从irqqueue中删除这个缓冲区
- * 唤醒等待数据的进程
- */
- if (buf->state == VIDEOBUF_DONE ||
- buf->state == VIDEOBUF_ERROR)
- list_del(&buf->irq);
- wake_up(&buf->wait);
- mem = myuvc_queue.mem + buf->buf.m.offset;
- data_len = buf->buf.bytesused;
- //printk("wake_up %d : %s %d, start data: %02x %02x, end data: %02x %02x", wake_cnt++, __FUNCTION__, __LINE__, mem[0], mem[1], mem[data_len - 2], mem[data_len - 1]);
- /* 取出下一个buf */
- if (!list_empty(&myuvc_queue.irqqueue))
- buf = list_first_entry(&myuvc_queue.irqqueue, struct myuvc_buffer, irq);
- else
- buf = NULL;
- /* 再次提交URB */
- if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0)
- printk("Failed to resubmit video URB (%d).\\n", ret);
- /* 参考: uvc_init_video_isoc */
- static int myuvc_alloc_init_urbs(void)
- u16 psize;
- u32 size;
- int npackets;
- int i;
- int j;
- struct urb *urb;
- psize = wMaxPacketSize; /* 实时传输端点一次能传输的最大字节数 */
- size = myuvc_params.dwMaxVideoFrameSize; /* 一帧数据的最大长度 */
- npackets = DIV_ROUND_UP(size, psize);
- if (npackets > 32)
- npackets = 32;
- size = myuvc_queue.urb_size = psize * npackets;
- for (i = 0; i < MYUVC_URBS; ++i)
- /* 1. 分配usb_buffers */
- myuvc_queue.urb_buffer[i] = usb_buffer_alloc(
- myuvc_udev, size,
- GFP_KERNEL | __GFP_NOWARN, &myuvc_queue.urb_dma[i]);
- /* 2. 分配urb */
- 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];
- 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;
- /* A11 启动传输
- * 参考: uvc_video_enable(video, 1):
- * uvc_commit_video
- * uvc_init_video
- */
- static int myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
- int ret;
- 以上是关于自己写的uvc驱动支持IP2977/ip2970的主要内容,如果未能解决你的问题,请参考以下文章