📄 em28xx-video.c
字号:
(dev->state & DEV_DISCONNECTED)); if (ret) { mutex_unlock(&dev->fileop_lock); return ret; } if (dev->state & DEV_DISCONNECTED) { mutex_unlock(&dev->fileop_lock); return -ENODEV; } } f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame); spin_lock_irqsave(&dev->queue_lock, lock_flags); list_for_each_entry(i, &dev->outqueue, frame) i->state = F_UNUSED; INIT_LIST_HEAD(&dev->outqueue); spin_unlock_irqrestore(&dev->queue_lock, lock_flags); em28xx_queue_unusedframes(dev); if (count > f->buf.length) count = f->buf.length; if (copy_to_user(buf, f->bufmem, count)) { mutex_unlock(&dev->fileop_lock); return -EFAULT; } *f_pos += count; mutex_unlock(&dev->fileop_lock); return count;}/* * em28xx_v4l2_poll() * will allocate buffers when called for the first time */static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait){ unsigned int mask = 0; struct em28xx *dev = filp->private_data; if (mutex_lock_interruptible(&dev->fileop_lock)) return POLLERR; if (dev->state & DEV_DISCONNECTED) { em28xx_videodbg("device not present\n"); } else if (dev->state & DEV_MISCONFIGURED) { em28xx_videodbg("device is misconfigured; close and open it again\n"); } else { if (dev->io == IO_NONE) { if (!em28xx_request_buffers (dev, EM28XX_NUM_READ_FRAMES)) { em28xx_warn ("poll() failed, not enough memory\n"); } else { dev->io = IO_READ; dev->stream = STREAM_ON; } } if (dev->io == IO_READ) { em28xx_queue_unusedframes(dev); poll_wait(filp, &dev->wait_frame, wait); if (!list_empty(&dev->outqueue)) mask |= POLLIN | POLLRDNORM; mutex_unlock(&dev->fileop_lock); return mask; } } mutex_unlock(&dev->fileop_lock); return POLLERR;}/* * em28xx_vm_open() */static void em28xx_vm_open(struct vm_area_struct *vma){ struct em28xx_frame_t *f = vma->vm_private_data; f->vma_use_count++;}/* * em28xx_vm_close() */static void em28xx_vm_close(struct vm_area_struct *vma){ /* NOTE: buffers are not freed here */ struct em28xx_frame_t *f = vma->vm_private_data; f->vma_use_count--;}static struct vm_operations_struct em28xx_vm_ops = { .open = em28xx_vm_open, .close = em28xx_vm_close,};/* * em28xx_v4l2_mmap() */static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma){ unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; u32 i; struct em28xx *dev = filp->private_data; if (mutex_lock_interruptible(&dev->fileop_lock)) return -ERESTARTSYS; if (dev->state & DEV_DISCONNECTED) { em28xx_videodbg("mmap: device not present\n"); mutex_unlock(&dev->fileop_lock); return -ENODEV; } if (dev->state & DEV_MISCONFIGURED) { em28xx_videodbg ("mmap: Device is misconfigured; close and " "open it again\n"); mutex_unlock(&dev->fileop_lock); return -EIO; } if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || size != PAGE_ALIGN(dev->frame[0].buf.length)) { mutex_unlock(&dev->fileop_lock); return -EINVAL; } for (i = 0; i < dev->num_frames; i++) { if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } if (i == dev->num_frames) { em28xx_videodbg("mmap: user supplied mapping address is out of range\n"); mutex_unlock(&dev->fileop_lock); return -EINVAL; } /* VM_IO is eventually going to replace PageReserved altogether */ vma->vm_flags |= VM_IO; vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ pos = dev->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { em28xx_videodbg("mmap: vm_insert_page failed\n"); mutex_unlock(&dev->fileop_lock); return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &em28xx_vm_ops; vma->vm_private_data = &dev->frame[i]; em28xx_vm_open(vma); mutex_unlock(&dev->fileop_lock); return 0;}/* * em28xx_get_ctrl() * return the current saturation, brightness or contrast, mute state */static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl){ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = dev->mute; return 0; case V4L2_CID_AUDIO_VOLUME: ctrl->value = dev->volume; return 0; default: return -EINVAL; }}/* * em28xx_set_ctrl() * mute or set new saturation, brightness or contrast */static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl){ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value != dev->mute) { dev->mute = ctrl->value; em28xx_audio_usb_mute(dev, ctrl->value); return em28xx_audio_analog_set(dev); } return 0; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; return em28xx_audio_analog_set(dev); default: return -EINVAL; }}/* * em28xx_stream_interrupt() * stops streaming */static int em28xx_stream_interrupt(struct em28xx *dev){ int ret = 0; /* stop reading from the device */ dev->stream = STREAM_INTERRUPT; ret = wait_event_timeout(dev->wait_stream, (dev->stream == STREAM_OFF) || (dev->state & DEV_DISCONNECTED), EM28XX_URB_TIMEOUT); if (dev->state & DEV_DISCONNECTED) return -ENODEV; else if (ret) { dev->state |= DEV_MISCONFIGURED; em28xx_videodbg("device is misconfigured; close and " "open /dev/video%d again\n", dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN); return ret; } return 0;}static int em28xx_set_norm(struct em28xx *dev, int width, int height){ unsigned int hscale, vscale; unsigned int maxh, maxw; maxw = norm_maxw(dev); maxh = norm_maxh(dev); /* width must even because of the YUYV format */ /* height must be even because of interlacing */ height &= 0xfffe; width &= 0xfffe; if (height < 32) height = 32; if (height > maxh) height = maxh; if (width < 48) width = 48; if (width > maxw) width = maxw; if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) hscale = 0x3fff; width = (((unsigned long)maxw) << 12) / (hscale + 4096L); if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) vscale = 0x3fff; height = (((unsigned long)maxh) << 12) / (vscale + 4096L); /* set new image size */ dev->width = width; dev->height = height; dev->frame_size = dev->width * dev->height * 2; dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */ dev->bytesperline = dev->width * 2; dev->hscale = hscale; dev->vscale = vscale; em28xx_resolution_set(dev); return 0;}static int em28xx_get_fmt(struct em28xx *dev, struct v4l2_format *format){ em28xx_videodbg("VIDIOC_G_FMT: type=%s\n", (format->type ==V4L2_BUF_TYPE_VIDEO_CAPTURE) ? "V4L2_BUF_TYPE_VIDEO_CAPTURE" : (format->type ==V4L2_BUF_TYPE_VBI_CAPTURE) ? "V4L2_BUF_TYPE_VBI_CAPTURE" : (format->type ==V4L2_CAP_SLICED_VBI_CAPTURE) ? "V4L2_BUF_TYPE_SLICED_VBI_CAPTURE " : "not supported"); switch (format->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { format->fmt.pix.width = dev->width; format->fmt.pix.height = dev->height; format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; format->fmt.pix.bytesperline = dev->bytesperline; format->fmt.pix.sizeimage = dev->frame_size; format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; format->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ em28xx_videodbg("VIDIOC_G_FMT: %dx%d\n", dev->width, dev->height); break; } case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: { format->fmt.sliced.service_set=0; em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); if (format->fmt.sliced.service_set==0) return -EINVAL; break; } default: return -EINVAL; } return (0);}static int em28xx_set_fmt(struct em28xx *dev, unsigned int cmd, struct v4l2_format *format){ u32 i; int ret = 0; int width = format->fmt.pix.width; int height = format->fmt.pix.height; unsigned int hscale, vscale; unsigned int maxh, maxw; maxw = norm_maxw(dev); maxh = norm_maxh(dev); em28xx_videodbg("%s: type=%s\n", cmd == VIDIOC_TRY_FMT ? "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT", format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ? "V4L2_BUF_TYPE_VIDEO_CAPTURE" : format->type == V4L2_BUF_TYPE_VBI_CAPTURE ? "V4L2_BUF_TYPE_VBI_CAPTURE " : "not supported"); if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { em28xx_i2c_call_clients(dev,VIDIOC_G_FMT,format); if (format->fmt.sliced.service_set==0) return -EINVAL; return 0; } if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; em28xx_videodbg("%s: requested %dx%d\n", cmd == VIDIOC_TRY_FMT ? "VIDIOC_TRY_FMT" : "VIDIOC_S_FMT", format->fmt.pix.width, format->fmt.pix.height); /* FIXME: Move some code away from here */ /* width must even because of the YUYV format */ /* height must be even because of interlacing */ height &= 0xfffe; width &= 0xfffe; if (height < 32) height = 32; if (height > maxh) height = maxh; if (width < 48) width = 48; if (width > maxw) width = maxw; if(dev->is_em2800){ /* the em2800 can only scale down to 50% */ if(height % (maxh / 2)) height=maxh; if(width % (maxw / 2)) width=maxw; /* according to empiatech support */ /* the MaxPacketSize is to small to support */ /* framesizes larger than 640x480 @ 30 fps */ /* or 640x576 @ 25 fps. As this would cut */ /* of a part of the image we prefer */ /* 360x576 or 360x480 for now */ if(width == maxw && height == maxh) width /= 2; } if ((hscale = (((unsigned long)maxw) << 12) / width - 4096L) >= 0x4000) hscale = 0x3fff; width = (((unsigned long)maxw) << 12) / (hscale + 4096L); if ((vscale = (((unsigned long)maxh) << 12) / height - 4096L) >= 0x4000) vscale = 0x3fff; height = (((unsigned long)maxh) << 12) / (vscale + 4096L); format->fmt.pix.width = width; format->fmt.pix.height = height; format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; format->fmt.pix.bytesperline = width * 2; format->fmt.pix.sizeimage = width * 2 * height; format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; format->fmt.pix.field = V4L2_FIELD_INTERLACED; em28xx_videodbg("%s: returned %dx%d (%d, %d)\n", cmd == VIDIOC_TRY_FMT ? "VIDIOC_TRY_FMT" :"VIDIOC_S_FMT", format->fmt.pix.width, format->fmt.pix.height, hscale, vscale); if (cmd == VIDIOC_TRY_FMT) return 0; for (i = 0; i < dev->num_frames; i++) if (dev->frame[i].vma_use_count) { em28xx_videodbg("VIDIOC_S_FMT failed. " "Unmap the buffers first.\n"); return -EINVAL; } /* stop io in case it is already in progress */ if (dev->stream == STREAM_ON) { em28xx_videodbg("VIDIOC_SET_FMT: interupting stream\n"); if ((ret = em28xx_stream_interrupt(dev))) return ret; } em28xx_release_buffers(dev); dev->io = IO_NONE; /* set new image size */ dev->width = width; dev->height = height; dev->frame_size = dev->width * dev->height * 2; dev->field_size = dev->frame_size >> 1; dev->bytesperline = dev->width * 2; dev->hscale = hscale; dev->vscale = vscale; em28xx_uninit_isoc(dev); em28xx_set_alternate(dev); em28xx_capture_start(dev, 1); em28xx_resolution_set(dev); em28xx_init_isoc(dev); return 0;}/* * em28xx_v4l2_do_ioctl() * This function is _not_ called directly, but from * em28xx_v4l2_ioctl. Userspace * copying is done already, arg is a kernel pointer. */static int em28xx_do_ioctl(struct inode *inode, struct file *filp, struct em28xx *dev, unsigned int cmd, void *arg, v4l2_kioctl driver_ioctl){ int ret; switch (cmd) { /* ---------- tv norms ---------- */ case VIDIOC_ENUMSTD: {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -