sn9c102_core.c
字号:
kfree(cam->control_buffer);}/*****************************************************************************/static int sn9c102_open(struct inode* inode, struct file* filp){ struct sn9c102_device* cam; int err = 0; /* This the only safe way to prevent race conditions with disconnect */ if (!down_read_trylock(&sn9c102_disconnect)) return -ERESTARTSYS; cam = video_get_drvdata(video_devdata(filp)); if (down_interruptible(&cam->dev_sem)) { up_read(&sn9c102_disconnect); return -ERESTARTSYS; } if (cam->users) { DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor) if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; goto out; } up(&cam->dev_sem); err = wait_event_interruptible_exclusive(cam->open, cam->state & DEV_DISCONNECTED || !cam->users); if (err) { up_read(&sn9c102_disconnect); return err; } if (cam->state & DEV_DISCONNECTED) { up_read(&sn9c102_disconnect); return -ENODEV; } down(&cam->dev_sem); } if (cam->state & DEV_MISCONFIGURED) { err = sn9c102_init(cam); if (err) { DBG(1, "Initialization failed again. " "I will retry on next open().") goto out; } cam->state &= ~DEV_MISCONFIGURED; } if ((err = sn9c102_start_transfer(cam))) goto out; filp->private_data = cam; cam->users++; cam->io = IO_NONE; cam->stream = STREAM_OFF; cam->nbuffers = 0; cam->frame_count = 0; sn9c102_empty_framequeues(cam); DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor)out: up(&cam->dev_sem); up_read(&sn9c102_disconnect); return err;}static int sn9c102_release(struct inode* inode, struct file* filp){ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); down(&cam->dev_sem); /* prevent disconnect() to be called */ sn9c102_stop_transfer(cam); sn9c102_release_buffers(cam); if (cam->state & DEV_DISCONNECTED) { sn9c102_release_resources(cam); up(&cam->dev_sem); kfree(cam); return 0; } cam->users--; wake_up_interruptible_nr(&cam->open, 1); DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor) up(&cam->dev_sem); return 0;}static ssize_tsn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos){ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); struct sn9c102_frame_t* f, * i; unsigned long lock_flags; int err = 0; if (down_interruptible(&cam->fileop_sem)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present") up(&cam->fileop_sem); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it again.") up(&cam->fileop_sem); return -EIO; } if (cam->io == IO_MMAP) { DBG(3, "Close and open the device again to choose " "the read method") up(&cam->fileop_sem); return -EINVAL; } if (cam->io == IO_NONE) { if (!sn9c102_request_buffers(cam, 2)) { DBG(1, "read() failed, not enough memory") up(&cam->fileop_sem); return -ENOMEM; } cam->io = IO_READ; cam->stream = STREAM_ON; sn9c102_queue_unusedframes(cam); } if (!count) { up(&cam->fileop_sem); return 0; } if (list_empty(&cam->outqueue)) { if (filp->f_flags & O_NONBLOCK) { up(&cam->fileop_sem); return -EAGAIN; } err = wait_event_interruptible ( cam->wait_frame, (!list_empty(&cam->outqueue)) || (cam->state & DEV_DISCONNECTED) ); if (err) { up(&cam->fileop_sem); return err; } if (cam->state & DEV_DISCONNECTED) { up(&cam->fileop_sem); return -ENODEV; } } f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); spin_lock_irqsave(&cam->queue_lock, lock_flags); list_for_each_entry(i, &cam->outqueue, frame) i->state = F_UNUSED; INIT_LIST_HEAD(&cam->outqueue); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); sn9c102_queue_unusedframes(cam); if (count > f->buf.length) count = f->buf.length; if (copy_to_user(buf, f->bufmem, count)) { up(&cam->fileop_sem); return -EFAULT; } *f_pos += count; PDBGG("Frame #%lu, bytes read: %zu", (unsigned long)f->buf.index,count) up(&cam->fileop_sem); return count;}static unsigned int sn9c102_poll(struct file *filp, poll_table *wait){ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); unsigned int mask = 0; if (down_interruptible(&cam->fileop_sem)) return POLLERR; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present") goto error; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it again.") goto error; } if (cam->io == IO_NONE) { if (!sn9c102_request_buffers(cam, 2)) { DBG(1, "poll() failed, not enough memory") goto error; } cam->io = IO_READ; cam->stream = STREAM_ON; } if (cam->io == IO_READ) sn9c102_queue_unusedframes(cam); poll_wait(filp, &cam->wait_frame, wait); if (!list_empty(&cam->outqueue)) mask |= POLLIN | POLLRDNORM; up(&cam->fileop_sem); return mask;error: up(&cam->fileop_sem); return POLLERR;}static void sn9c102_vm_open(struct vm_area_struct* vma){ struct sn9c102_frame_t* f = vma->vm_private_data; f->vma_use_count++;}static void sn9c102_vm_close(struct vm_area_struct* vma){ /* NOTE: buffers are not freed here */ struct sn9c102_frame_t* f = vma->vm_private_data; f->vma_use_count--;}static struct vm_operations_struct sn9c102_vm_ops = { .open = sn9c102_vm_open, .close = sn9c102_vm_close,};static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma){ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start, pos, page; u32 i; if (down_interruptible(&cam->fileop_sem)) return -ERESTARTSYS; if (cam->state & DEV_DISCONNECTED) { DBG(1, "Device not present") up(&cam->fileop_sem); return -ENODEV; } if (cam->state & DEV_MISCONFIGURED) { DBG(1, "The camera is misconfigured. Close and open it again.") up(&cam->fileop_sem); return -EIO; } if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || size != PAGE_ALIGN(cam->frame[0].buf.length)) { up(&cam->fileop_sem); return -EINVAL; } for (i = 0; i < cam->nbuffers; i++) { if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) break; } if (i == cam->nbuffers) { up(&cam->fileop_sem); return -EINVAL; } pos = (unsigned long)cam->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ page = kvirt_to_pa(pos); if (remap_page_range(vma, start, page, PAGE_SIZE, vma->vm_page_prot)) { up(&cam->fileop_sem); return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &sn9c102_vm_ops; vma->vm_flags &= ~VM_IO; /* not I/O memory */ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ vma->vm_private_data = &cam->frame[i]; sn9c102_vm_open(vma); up(&cam->fileop_sem); return 0;}static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg){ struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); switch (cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability cap = { .driver = "sn9c102", .version = SN9C102_MODULE_VERSION_CODE, .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, }; strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info)); if (copy_to_user(arg, &cap, sizeof(cap))) return -EFAULT; return 0; } case VIDIOC_ENUMINPUT: { struct v4l2_input i; if (copy_from_user(&i, arg, sizeof(i))) return -EFAULT; if (i.index) return -EINVAL; memset(&i, 0, sizeof(i)); strcpy(i.name, "USB"); if (copy_to_user(arg, &i, sizeof(i))) return -EFAULT; return 0; } case VIDIOC_G_INPUT: case VIDIOC_S_INPUT: { int index; if (copy_from_user(&index, arg, sizeof(index))) return -EFAULT; if (index != 0) return -EINVAL; return 0; } case VIDIOC_QUERYCTRL: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_queryctrl qc; u8 i, n; if (copy_from_user(&qc, arg, sizeof(qc))) return -EFAULT; n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); for (i = 0; i < n; i++) if (qc.id && qc.id == s->qctrl[i].id) { memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); if (copy_to_user(arg, &qc, sizeof(qc))) return -EFAULT; return 0; } return -EINVAL; } case VIDIOC_G_CTRL: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_control ctrl; int err = 0; if (!s->get_ctrl) return -EINVAL; if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; err = s->get_ctrl(cam, &ctrl); if (copy_to_user(arg, &ctrl, sizeof(ctrl))) return -EFAULT; return err; } case VIDIOC_S_CTRL: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_control ctrl; u8 i, n; int err = 0; if (!s->set_ctrl) return -EINVAL; if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; if ((err = s->set_ctrl(cam, &ctrl))) return err; n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); for (i = 0; i < n; i++) if (ctrl.id == s->qctrl[i].id) { s->_qctrl[i].default_value = ctrl.value; break; } return 0; } case VIDIOC_CROPCAP: { struct v4l2_cropcap* cc = &(cam->sensor->cropcap); cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; cc->pixelaspect.numerator = 1; cc->pixelaspect.denominator = 1; if (copy_to_user(arg, cc, sizeof(*cc))) return -EFAULT; return 0; } case VIDIOC_G_CROP: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_crop crop = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, }; memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); if (copy_to_user(arg, &crop, sizeof(crop))) return -EFAULT; return 0; } case VIDIOC_S_CROP: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_crop crop; struct v4l2_rect* rect; struct v4l2_rect* bounds = &(s->cropcap.bounds); struct v4l2_pix_format* pix_format = &(s->pix_format); u8 scale; const enum sn9c102_stream_state stream = cam->stream; const u32 nbuffers = cam->nbuffers; u32 i; int err = 0; if (copy_from_user(&crop, arg, sizeof(crop))) return -EFAULT; rect = &(crop.c); if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first.") return -EINVAL; } if (rect->width < 16) rect->width = 16; if (rect->height < 16) rect->height = 16; if (rect->width > bounds->width) rect->width = bounds->width; if (rect->height > bounds->height) rect->height = bounds->height; if (rect->left < bounds->left) rect->left = bounds->left; if (rect->top < bounds->top) rect->top = bounds->top; if (rect->left + rect->width > bounds->left + bounds->width) rect->left = bounds->left+bounds->width - rect->width; if (rect->top + rect->height > bounds->top + bounds->height) rect->top = bounds->top+bounds->height - rect->height; rect->width &= ~15L; rect->height &= ~15L; { /* calculate the scaling factor */ u32 a, b; a = rect->width * rect->height; b = pix_format->width * pix_format->height; scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 : ((a / b) > 4 ? 4 : (a / b)))) : 1; } if (cam->stream == STREAM_ON) { cam->stream = STREAM_INTERRUPT; err = wait_event_interruptible ( cam->wait_stream, (cam->stream == STREAM_OFF) || (cam->state & DEV_DISCONNECTED) ); if (err) { cam->state |= DEV_MISCONFIGURED; DBG(1, "The camera is misconfigured. To use " "it, close and open /dev/video%d " "again.", cam->v4ldev->minor) return err; } if (cam->state & DEV_DISCONNECTED) return -ENODEV; } if (copy_to_user(arg, &crop, sizeof(crop))) { cam->stream = stream; return -EFAULT; } sn9c102_release_buffers(cam); err = sn9c102_set_crop(cam, rect); if (s->set_crop) err += s->set_crop(cam, rect); err += sn9c102_set_scale(cam, scale); if (err) { /* atomic, no rollback in ioctl() */ cam->state |= DEV_MISCONFIGURED; DBG(1, "VIDIOC_S_CROP failed because of hardware " "problems. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) return err; } s->pix_format.width = rect->width/scale; s->pix_format.height = rect->height/scale; memcpy(&(s->_rect), rect, sizeof(*rect)); if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) { cam->state |= DEV_MISCONFIGURED; DBG(1, "VIDIOC_S_CROP failed because of not enough " "memory. To use the camera, close and open " "/dev/video%d again.", cam->v4ldev->minor) return -ENOMEM; } cam->stream = stream; return 0; } case VIDIOC_ENUM_FMT: { struct sn9c102_sensor* s = cam->sensor; struct v4l2_fmtdesc fmtd; if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; if (fmtd.index != 0) return -EINVAL; memset(&fmtd, 0, sizeof(fmtd)); fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strcpy(fmtd.description, "bayer rgb"); fmtd.pixelformat = s->pix_format.pixelformat; if (copy_to_user(arg, &fmtd, sizeof(fmtd))) return -EFAULT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -