📄 gspca.c
字号:
{ struct gspca_dev *gspca_dev = priv; const struct ctrl *ctrls; int i, ret; for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; i < gspca_dev->sd_desc->nctrls; i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; if (gspca_dev->ctrl_dis & (1 << i)) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; ret = ctrls->get(gspca_dev, &ctrl->value); mutex_unlock(&gspca_dev->usb_lock); return ret; } return -EINVAL;}static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu){ struct gspca_dev *gspca_dev = priv; if (!gspca_dev->sd_desc->querymenu) return -EINVAL; return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);}static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input){ struct gspca_dev *gspca_dev = priv; if (input->index != 0) return -EINVAL; memset(input, 0, sizeof *input); input->type = V4L2_INPUT_TYPE_CAMERA; strncpy(input->name, gspca_dev->sd_desc->name, sizeof input->name); return 0;}static int vidioc_g_input(struct file *file, void *priv, unsigned int *i){ *i = 0; return 0;}static int vidioc_s_input(struct file *file, void *priv, unsigned int i){ if (i > 0) return -EINVAL; return (0);}static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb){ struct gspca_dev *gspca_dev = priv; int i, ret = 0; if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; switch (rb->memory) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: break; default: return -EINVAL; } if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (gspca_dev->memory != GSPCA_MEMORY_NO && gspca_dev->memory != rb->memory) { ret = -EBUSY; goto out; } /* only one file may do the capture */ if (gspca_dev->capt_file != NULL && gspca_dev->capt_file != file) { ret = -EBUSY; goto out; } /* if allocated, the buffers must not be mapped */ for (i = 0; i < gspca_dev->nframes; i++) { if (gspca_dev->frame[i].vma_use_count) { ret = -EBUSY; goto out; } } /* stop streaming */ if (gspca_dev->streaming) { mutex_lock(&gspca_dev->usb_lock); gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } /* free the previous allocated buffers, if any */ if (gspca_dev->nframes != 0) { frame_free(gspca_dev); gspca_dev->capt_file = NULL; } if (rb->count == 0) /* unrequest */ goto out; gspca_dev->memory = rb->memory; ret = frame_alloc(gspca_dev, rb->count); if (ret == 0) { rb->count = gspca_dev->nframes; gspca_dev->capt_file = file; }out: mutex_unlock(&gspca_dev->queue_lock); PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); return ret;}static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf){ struct gspca_dev *gspca_dev = priv; struct gspca_frame *frame; if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || v4l2_buf->index < 0 || v4l2_buf->index >= gspca_dev->nframes) return -EINVAL; frame = &gspca_dev->frame[v4l2_buf->index]; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); return 0;}static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type buf_type){ struct gspca_dev *gspca_dev = priv; int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { ret = -ENODEV; goto out; } if (gspca_dev->nframes == 0) { ret = -EINVAL; goto out; } if (!gspca_dev->streaming) { ret = gspca_init_transfer(gspca_dev); if (ret < 0) goto out; }#ifdef GSPCA_DEBUG if (gspca_debug & D_STREAM) { PDEBUG_MODE("stream on OK", gspca_dev->pixfmt, gspca_dev->width, gspca_dev->height); }#endif ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type){ struct gspca_dev *gspca_dev = priv; int i, ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (!gspca_dev->streaming) return 0; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; /* stop streaming */ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { ret = -ERESTARTSYS; goto out; } gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); /* empty the application queues */ for (i = 0; i < gspca_dev->nframes; i++) gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; atomic_set(&gspca_dev->nevent, 0); ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp){ struct gspca_dev *gspca_dev = priv; int ret; if (!gspca_dev->sd_desc->get_jcomp) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); mutex_unlock(&gspca_dev->usb_lock); return ret;}static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp){ struct gspca_dev *gspca_dev = priv; int ret; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; if (!gspca_dev->sd_desc->set_jcomp) return -EINVAL; ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); mutex_unlock(&gspca_dev->usb_lock); return ret;}static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm){ struct gspca_dev *gspca_dev = priv; memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; return 0;}static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm){ struct gspca_dev *gspca_dev = priv; int n; n = parm->parm.capture.readbuffers; if (n == 0 || n > GSPCA_MAX_FRAMES) parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; return 0;}static int vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *parm){ return 0;}#ifdef CONFIG_VIDEO_V4L1_COMPATstatic int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf){ struct gspca_dev *gspca_dev = file->private_data; int i; PDEBUG(D_STREAM, "cgmbuf"); if (gspca_dev->nframes == 0) { int ret; { struct v4l2_format fmt; memset(&fmt, 0, sizeof fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; i = gspca_dev->cam.nmodes - 1; /* highest mode */ fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; ret = vidioc_s_fmt_vid_cap(file, priv, &fmt); if (ret != 0) return ret; } { struct v4l2_requestbuffers rb; memset(&rb, 0, sizeof rb); rb.count = 4; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; ret = vidioc_reqbufs(file, priv, &rb); if (ret != 0) return ret; } } mbuf->frames = gspca_dev->nframes; mbuf->size = gspca_dev->frsz * gspca_dev->nframes; for (i = 0; i < mbuf->frames; i++) mbuf->offsets[i] = gspca_dev->frame[i].v4l2_buf.m.offset; return 0;}#endifstatic int dev_mmap(struct file *file, struct vm_area_struct *vma){ struct gspca_dev *gspca_dev = file->private_data; struct gspca_frame *frame; struct page *page; unsigned long addr, start, size; int i, ret; start = vma->vm_start; size = vma->vm_end - vma->vm_start; PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { ret = -ENODEV; goto out; } if (gspca_dev->capt_file != file) { ret = -EINVAL; goto out; } frame = NULL; for (i = 0; i < gspca_dev->nframes; ++i) { if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { PDEBUG(D_STREAM, "mmap bad memory type"); break; } if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) { frame = &gspca_dev->frame[i]; break; } } if (frame == NULL) { PDEBUG(D_STREAM, "mmap no frame buffer found"); ret = -EINVAL; goto out; }#ifdef CONFIG_VIDEO_V4L1_COMPAT /* v4l1 maps all the buffers */ if (i != 0 || size != frame->v4l2_buf.length * gspca_dev->nframes)#endif if (size != frame->v4l2_buf.length) { PDEBUG(D_STREAM, "mmap bad size"); ret = -EINVAL; goto out; } /* * - VM_IO marks the area as being a mmaped region for I/O to a * device. It also prevents the region from being core dumped. */ vma->vm_flags |= VM_IO; addr = (unsigned long) frame->data; while (size > 0) { page = vmalloc_to_page((void *) addr); ret = vm_insert_page(vma, start, page); if (ret < 0) goto out; start += PAGE_SIZE; addr += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &gspca_vm_ops; vma->vm_private_data = frame; gspca_vm_open(vma); ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}/* * wait for a video frame * * If a frame is ready, its index is returned. */static int frame_wait(struct gspca_dev *gspca_dev, int nonblock_ing){ struct gspca_frame *frame; int i, j, ret; /* check if a frame is ready */ i = gspca_dev->fr_o; j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) { atomic_dec(&gspca_dev->nevent); goto ok; } if (nonblock_ing) /* no frame yet */ return -EAGAIN; /* wait till a frame is ready */ for (;;) { ret = wait_event_interruptible_timeout(gspca_dev->wq, atomic_read(&gspca_dev->nevent) > 0, msecs_to_jiffies(3000)); if (ret <= 0) { if (ret < 0) return ret; /* interrupt */ return -EIO; /* timeout */ } atomic_dec(&gspca_dev->nevent); if (!gspca_dev->streaming || !gspca_dev->present) return -EIO; i = gspca_dev->fr_o; j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) break; }ok: gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", gspca_dev->fr_q, gspca_dev->fr_i, gspca_dev->fr_o); if (gspca_dev->sd_desc->dq_callback) { mutex_lock(&gspca_dev->usb_lock); gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } return j;}/* * dequeue a video buffer * * If nonblock_ing is false, block until a buffer is available. */static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf){ struct gspca_dev *gspca_dev = priv; struct gspca_frame *frame; int i, ret; PDEBUG(D_FRAM, "dqbuf"); if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (v4l2_buf->memory != gspca_dev->memory) return -EINVAL; /* if not streaming, be sure the application will not loop forever */ if (!(file->f_flags & O_NONBLOCK) && !gspca_dev->streaming && gspca_dev->users == 1) return -EINVAL; /* only the capturing file may dequeue */ if (gspca_dev->capt_file != file) return -EINVAL; /* only one dequeue / read at a time */ if (mutex_lock_interruptible(&gspca_dev->read_lock)) return -ERESTARTSYS; ret = frame_wait(gspca_dev, file->f_flags & O_NONBLOCK); if (ret < 0) goto out; i = ret; /* frame index */ frame = &gspca_dev->frame[i]; if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, frame->v4l2_buf.bytesused)) { PDEBUG(D_ERR|D_STREAM, "dqbuf cp to user failed"); ret = -EFAULT; goto out; } } frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); PDEBUG(D_FRAM, "dqbuf %d", i); ret = 0;out: mutex_unlock(&gspca_dev->read_lock); return ret;}/* * queue a video buffer * * Attempting to queue a buffer that has already been * queued will return -EINVAL. */static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf){ struct gspca_dev *gspca_dev = priv; struct gspca_frame *frame; int i, index, ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -