📄 zc0301_v4l2.c
字号:
/* This file contains operations about V4L2 spec * * The most effective way to understand V4L2 spec is read manual carefully more and more * * It's created by WE.XCC at 2008.05.06 *
* Author's E-mail:84318391@163.com
* */#include <linux/fs.h>#include <linux/version.h>#include <linux/errno.h>#include <linux/module.h>#include <asm/uaccess.h>#include "zc0301_struct.h"#include "zc0301_debug.h"int zc0301_request_buffers(struct zc0301_device *cam, u32 count){ void *buff = NULL; const unsigned int imagesize = 640 * 480 * 8 / 8; u32 i = 0; if (cam == NULL) { debug_error("zc0301_request_buffers:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (count > ZC0301_MAX_FRAMES) count = ZC0301_MAX_FRAMES; cam->nbuffers = count; buff = vmalloc_32_user(cam->nbuffers * PAGE_ALIGN(imagesize)); for (i = 0; i < cam->nbuffers; i++) { cam->frame[i].bufmem = buff + i * PAGE_ALIGN(imagesize); cam->frame[i].buf.index = i; cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cam->frame[i].buf.bytesused = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.sequence = 0; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; cam->frame[i].buf.m.offset = i * PAGE_ALIGN(imagesize); cam->frame[i].buf.length = imagesize; cam->frame[i].buf.flags = 0; } return cam->nbuffers;}/*************************************************************/int zc0301_vidioc_querycap(struct zc0301_device *cam, void __user *arg){ u8 i = 0; struct v4l2_capability cap; if (cam == NULL) { debug_error("zc0301_vidioc_querycap:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); strlcpy(cap.driver, "webcam", sizeof(cap.driver)); strlcpy(cap.card, "zc0301[P] PC webcam", sizeof(cap.card)); cap.bus_info[0] = 0; cap.version = KERNEL_VERSION(0, 9, 0); cap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; for (i = 0; i < ARRAY_SIZE(cap.reserved); i++) cap.reserved[i] = 0; if (copy_to_user(arg, &cap, sizeof(struct v4l2_capability))) return -EFAULT; return 0;}int zc0301_vidioc_enuminput(struct zc0301_device *cam, void __user *arg){ struct v4l2_input i; if (cam == NULL) { debug_error("zc0301_vidioc_enuminput:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&i, arg, sizeof(struct v4l2_input))) return -EFAULT; if (i.index) { debug_error("zc0301_vidioc_enuminput:index must begin from zero\n"); return -EINVAL; } memset(&i, 0, sizeof(struct v4l2_input)); strlcpy(i.name, "zc0301_camera", sizeof(i.name)); i.type = V4L2_INPUT_TYPE_CAMERA; if (copy_to_user(arg, &i, sizeof(struct v4l2_input))) return -EFAULT; return 0;}int zc0301_vidioc_g_input(struct zc0301_device *cam, void __user *arg){ int index = 0; if (cam == NULL) { debug_error("zc0301_vidioc_g_input:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&index, arg, sizeof(int))) return -EFAULT; if (index) { debug_error("zc0301_vidioc_g_input:index should be zero\n"); return -EINVAL; } if (copy_to_user(arg, &index, sizeof(int))) return -EFAULT; return 0;}int zc0301_vidioc_s_input(struct zc0301_device *cam, void __user *arg){ int index = 0; if (cam == NULL) { debug_error("zc0301_vidioc_s_input:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&index, arg, sizeof(int))) return -EFAULT; if (index) { debug_error("zc0301_vidioc_s_input:index should be zero\n"); return -EFAULT; } if (copy_to_user(arg, &index, sizeof(int))) return -EFAULT; return 0;}int zc0301_vidioc_query_ctrl(struct zc0301_device *cam, void __user *arg){ //debug_param("%s\n", __FUNCTION__); return -EINVAL;}int zc0301_vidioc_g_fmt(struct zc0301_device *cam, void __user *arg){ struct v4l2_format format; if (cam == NULL) { debug_error("zc0301_vidioc_g_fmt:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&format, arg, sizeof(struct v4l2_format))) return -EFAULT; if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; format.fmt.pix.width = 640; format.fmt.pix.height = 480; format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; format.fmt.pix.field = V4L2_FIELD_NONE; format.fmt.pix.bytesperline = 0; format.fmt.pix.sizeimage = 640 * 480 * 8 / 8; format.fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; format.fmt.pix.priv = 8; if (copy_to_user(arg, &format, sizeof(struct v4l2_format))) return -EFAULT; return 0;}int zc0301_vidioc_s_fmt(struct zc0301_device *cam, void __user *arg){ struct v4l2_format format; if (cam == NULL) { debug_error("zc0301_vidioc_s_fmt:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&format, arg, sizeof(struct v4l2_format))) return -EFAULT; if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; format.fmt.pix.width = 640; format.fmt.pix.height = 480; format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; format.fmt.pix.field = V4L2_FIELD_NONE; format.fmt.pix.bytesperline = 0; format.fmt.pix.sizeimage = 640 * 480 * 8 / 8; format.fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; format.fmt.pix.priv = 8; if (copy_to_user(arg, &format, sizeof(struct v4l2_format))) return -EFAULT; return 0;}int zc0301_vidioc_reqbufs(struct zc0301_device *cam, void __user *arg){ struct v4l2_requestbuffers rb; u8 i = 0; if (cam == NULL) { debug_error("zc0301_vidioc_reqbufs:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&rb, arg, sizeof(struct v4l2_requestbuffers))) return -EFAULT; if ((rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (rb.memory != V4L2_MEMORY_MMAP)) { debug_error("zc0301_vidioc_reqbufs:type or memory is wrong\n"); return -EINVAL; } //before alloc buffers, should check whether there is a vma still mmaped. //if some one still mmaped, operation will fail. //But here, I didn't add check function. if (rb.count == 0) { debug_error("zc0301_vidioc_reqbufs:count must > 0\n"); return -EINVAL; } rb.count = zc0301_request_buffers(cam, rb.count); for (i = 0; i < ARRAY_SIZE(rb.reserved); i++) rb.reserved[i] = 0; if (copy_to_user(arg, &rb, sizeof(struct v4l2_requestbuffers))) return -EFAULT; return 0;}int zc0301_vidioc_querybuf(struct zc0301_device *cam, void __user *arg){ struct v4l2_buffer b; struct zc0301_frame *f = NULL; if (cam == NULL) { debug_error("zc0301_vidioc_querybuf:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&b, arg, sizeof(struct v4l2_buffer))) return -EFAULT; if ((b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (b.index >= cam->nbuffers)) { if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) debug_error("zc0301_vidioc_querybuf:type is wrong\n"); else debug_error("zc0301_vidioc_querybuf:index is wrong\n"); return -EINVAL; } f = &cam->frame[b.index]; memcpy(&b, &cam->frame[b.index].buf, sizeof(struct v4l2_buffer)); if (f->f_state == F_DONE) b.flags |= V4L2_BUF_FLAG_DONE; if (f->f_state == F_QUEUED) b.flags |= V4L2_BUF_FLAG_QUEUED; b.flags |= V4L2_BUF_FLAG_MAPPED; if (copy_to_user(arg, &b, sizeof(struct v4l2_buffer))) return -EFAULT; return 0;}int zc0301_vidioc_qbuf(struct zc0301_device *cam, void __user *arg){ struct v4l2_buffer b; unsigned long lock_flags = 0; if (cam == NULL) { debug_error("zc0301_vidioc_qbuf:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&b, arg, sizeof(struct v4l2_buffer))) return -EFAULT; if ((b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (b.index >= cam->nbuffers) || (b.memory != V4L2_MEMORY_MMAP)) { debug_error("zc0301_vidioc_qbuf:type or index or memory is wrong\n"); return -EINVAL; } if (cam->frame[b.index].f_state != F_UNUSED) { debug_error("zc0301_vidioc_qbuf:buffer queued into inqueue must be UNUSED!\n"); return -EINVAL; } cam->frame[b.index].f_state = F_QUEUED; spin_lock_irqsave(&cam->queue_lock, lock_flags); list_add_tail(&cam->frame[b.index].queue, &cam->inqueue); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); b.flags |= V4L2_BUF_FLAG_QUEUED; b.flags &= ~V4L2_BUF_FLAG_DONE; if (copy_to_user(arg, &b, sizeof(struct v4l2_buffer))) return -EFAULT; return 0;}int zc0301_vidioc_dqbuf(struct zc0301_device *cam, struct file *filp, void __user *arg){ struct v4l2_buffer b; struct zc0301_frame *f = NULL; unsigned long lock_flags = 0; long timeout = 0; if ((cam == NULL) || (filp == NULL)) { debug_error("zc0301_vidioc_dqbuf:cam or filp is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&b, arg, sizeof(struct v4l2_buffer))) return -EFAULT; if ((b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || (b.memory != V4L2_MEMORY_MMAP)) { debug_error("zc0301_vidioc_dqbuf:type or memory is wrong\n"); return -EINVAL; } if (list_empty(&cam->outqueue)) { //if (cam->s_state == STREAM_OFF) //{ // debug_info("stream's state is STREAM_OFF in dqbuf\n"); // return -EINVAL; //} if (filp->f_flags & O_NONBLOCK) { debug_info("zc0301_vidioc_dqbuf:this operation will non block\n"); return -EAGAIN; } timeout = wait_event_interruptible_timeout(cam->wait_frame, !list_empty(&cam->outqueue) || cam->d_state & DISCONNECTED, 2000); } spin_lock_irqsave(&cam->queue_lock, lock_flags); f = list_entry(cam->outqueue.next, struct zc0301_frame, queue); list_del(cam->outqueue.next); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); f->f_state = F_UNUSED; memcpy(&b, &f->buf, sizeof(struct v4l2_buffer)); b.flags |= V4L2_BUF_FLAG_MAPPED; b.flags &= ~V4L2_BUF_FLAG_DONE; b.flags &= ~V4L2_BUF_FLAG_QUEUED; if (copy_to_user(arg, &b, sizeof(struct v4l2_buffer))) return -EFAULT; return 0;}int zc0301_vidioc_streamon(struct zc0301_device *cam, void __user *arg){ int type = 0; if (cam == NULL) { debug_error("zc0301_vidioc_streamon:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&type, arg, sizeof(int))) return -EFAULT; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { debug_error("zc0301_vidioc_streamon:type is wrong\n"); return -EINVAL; } if (list_empty(&cam->inqueue)) { debug_error("zc0301_vidioc_streamon:inqueue cann't be empty\n"); return -EINVAL; } cam->s_state = STREAM_ON; if (copy_to_user(arg, &type, sizeof(int))) return -EFAULT; return 0;}int zc0301_vidioc_streamoff(struct zc0301_device *cam, void __user *arg){ int type = 0; if (cam == NULL) { debug_error("zc0301_vidioc_streamoff:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&type, arg, sizeof(int))) return -EFAULT; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { debug_error("zc0301_vidioc_streamoff:type is wrong\n"); return -EINVAL; } cam->s_state = STREAM_OFF; INIT_LIST_HEAD(&cam->inqueue); INIT_LIST_HEAD(&cam->outqueue); if (copy_to_user(arg, &type, sizeof(int))) return -EFAULT; return 0;}int zc0301_vidioc_g_parm(struct zc0301_device *cam, void __user *arg){ struct v4l2_streamparm sp; if (cam == NULL) { debug_error("zc0301_vidioc_g_parm:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&sp, arg, sizeof(struct v4l2_streamparm))) return -EFAULT; if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { debug_error("zc0301_vidioc_g_parm:type is wrong\n"); return -EINVAL; } sp.parm.capture.extendedmode = 0; if (copy_to_user(arg, &sp, sizeof(struct v4l2_streamparm))) return -EFAULT; return 0;}int zc0301_vidioc_s_parm(struct zc0301_device *cam, void __user *arg){ struct v4l2_streamparm sp; if (cam == NULL) { debug_error("zc0301_vidioc_s_parm:cam is null\n"); return -ENODEV; } //debug_param("%s\n", __FUNCTION__); if (copy_from_user(&sp, arg, sizeof(struct v4l2_streamparm))) return -EFAULT; if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { debug_error("zc0301_vidioc_s_parm:type is wrong\n"); return -EINVAL; } sp.parm.capture.extendedmode = 0; if (copy_to_user(arg, &sp, sizeof(struct v4l2_streamparm))) return -EFAULT; return 0;}int zc0301_ioctl_v4l2(struct inode *inode, struct file *filp, unsigned int cmd, void __user *arg){ struct zc0301_device *cam = NULL; if (filp == NULL) { debug_error("zc0301_ioctl_v4l2:filp is null\n"); return -EIO; } //debug_param("%s\n", __FUNCTION__); cam = filp->private_data; switch (cmd) { case VIDIOC_QUERYCAP: return zc0301_vidioc_querycap(cam, arg); case VIDIOC_ENUMINPUT: return zc0301_vidioc_enuminput(cam, arg); case VIDIOC_G_INPUT: return zc0301_vidioc_g_input(cam, arg); case VIDIOC_S_INPUT: return zc0301_vidioc_s_input(cam, arg); case VIDIOC_QUERYCTRL: return zc0301_vidioc_query_ctrl(cam, arg); case VIDIOC_G_FMT: return zc0301_vidioc_g_fmt(cam, arg); case VIDIOC_S_FMT: return zc0301_vidioc_s_fmt(cam, arg); case VIDIOC_REQBUFS: return zc0301_vidioc_reqbufs(cam, arg); case VIDIOC_QUERYBUF: return zc0301_vidioc_querybuf(cam, arg); case VIDIOC_QBUF: return zc0301_vidioc_qbuf(cam, arg); case VIDIOC_DQBUF: return zc0301_vidioc_dqbuf(cam, filp, arg); case VIDIOC_STREAMON: return zc0301_vidioc_streamon(cam, arg); case VIDIOC_STREAMOFF: return zc0301_vidioc_streamoff(cam, arg); case VIDIOC_G_PARM: return zc0301_vidioc_g_parm(cam, arg); case VIDIOC_S_PARM: return zc0301_vidioc_s_parm(cam, arg); default: //debug_info("zc0301_ioctl_v4l2:other ioctl for v4l2\n"); return -EINVAL; }}EXPORT_SYMBOL(zc0301_vidioc_querycap);EXPORT_SYMBOL(zc0301_vidioc_enuminput);EXPORT_SYMBOL(zc0301_vidioc_g_input);EXPORT_SYMBOL(zc0301_vidioc_s_input);EXPORT_SYMBOL(zc0301_vidioc_query_ctrl);EXPORT_SYMBOL(zc0301_vidioc_g_fmt);EXPORT_SYMBOL(zc0301_vidioc_s_fmt);EXPORT_SYMBOL(zc0301_vidioc_reqbufs);EXPORT_SYMBOL(zc0301_vidioc_querybuf);EXPORT_SYMBOL(zc0301_vidioc_qbuf);EXPORT_SYMBOL(zc0301_vidioc_dqbuf);EXPORT_SYMBOL(zc0301_vidioc_streamon);EXPORT_SYMBOL(zc0301_vidioc_streamoff);EXPORT_SYMBOL(zc0301_vidioc_g_parm);EXPORT_SYMBOL(zc0301_vidioc_s_parm);EXPORT_SYMBOL(zc0301_ioctl_v4l2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -