- /* 作者: 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.