📄 saa7134-video.c
字号:
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int radio = 0; list_for_each(list,&saa7134_devlist) { h = list_entry(list, struct saa7134_dev, devlist); if (h->video_dev && (h->video_dev->minor == minor)) dev = h; if (h->radio_dev && (h->radio_dev->minor == minor)) { radio = 1; dev = h; } if (h->vbi_dev && (h->vbi_dev->minor == minor)) { type = V4L2_BUF_TYPE_VBI_CAPTURE; dev = h; } } if (NULL == dev) return -ENODEV; dprintk("open minor=%d radio=%d type=%s\n",minor,radio, v4l2_type_names[type]); /* allocate + initialize per filehandle data */ fh = kmalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) return -ENOMEM; memset(fh,0,sizeof(*fh)); file->private_data = fh; fh->dev = dev; fh->radio = radio; fh->type = type; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); fh->width = 720; fh->height = 576;#ifdef VIDIOC_G_PRIORITY v4l2_prio_open(&dev->prio,&fh->prio);#endif videobuf_queue_init(&fh->cap, &video_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct saa7134_buf)); init_MUTEX(&fh->cap.lock); saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); videobuf_queue_init(&fh->vbi, &saa7134_vbi_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct saa7134_buf)); init_MUTEX(&fh->vbi.lock); saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); if (fh->radio) { /* switch to radio mode */ saa7134_tvaudio_setinput(dev,&card(dev).radio); saa7134_i2c_call_clients(dev,AUDC_SET_RADIO,NULL); } else { /* switch to video/vbi mode */ video_mux(dev,dev->ctl_input); } return 0;}static ssize_tvideo_read(struct file *file, char __user *data, size_t count, loff_t *ppos){ struct saa7134_fh *fh = file->private_data; switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (res_locked(fh->dev,RESOURCE_VIDEO)) return -EBUSY; return videobuf_read_one(file, saa7134_queue(fh), data, count, ppos); case V4L2_BUF_TYPE_VBI_CAPTURE: if (!res_get(fh->dev,fh,RESOURCE_VBI)) return -EBUSY; return videobuf_read_stream(file, saa7134_queue(fh), data, count, ppos, 1); break; default: BUG(); return 0; }}static unsigned intvideo_poll(struct file *file, struct poll_table_struct *wait){ struct saa7134_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) return videobuf_poll_stream(file, &fh->vbi, wait); if (res_check(fh,RESOURCE_VIDEO)) { if (!list_empty(&fh->cap.stream)) buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); } else { down(&fh->cap.lock); if (UNSET == fh->cap.read_off) { /* need to capture a new frame */ if (res_locked(fh->dev,RESOURCE_VIDEO)) { up(&fh->cap.lock); return POLLERR; } if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf,fh->cap.field)) { up(&fh->cap.lock); return POLLERR; } fh->cap.ops->buf_queue(file,fh->cap.read_buf); fh->cap.read_off = 0; } up(&fh->cap.lock); buf = fh->cap.read_buf; } if (!buf) return POLLERR; poll_wait(file, &buf->done, wait); if (buf->state == STATE_DONE || buf->state == STATE_ERROR) return POLLIN|POLLRDNORM; return 0;}static int video_release(struct inode *inode, struct file *file){ struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; unsigned long flags; /* turn off overlay */ if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); res_free(dev,fh,RESOURCE_OVERLAY); } /* stop video capture */ if (res_check(fh, RESOURCE_VIDEO)) { videobuf_streamoff(file,&fh->cap); res_free(dev,fh,RESOURCE_VIDEO); } if (fh->cap.read_buf) { buffer_release(file,fh->cap.read_buf); kfree(fh->cap.read_buf); } /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { if (fh->vbi.streaming) videobuf_streamoff(file,&fh->vbi); if (fh->vbi.reading) videobuf_read_stop(file,&fh->vbi); res_free(dev,fh,RESOURCE_VBI); } saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi);#ifdef VIDIOC_G_PRIORITY v4l2_prio_close(&dev->prio,&fh->prio);#endif file->private_data = NULL; kfree(fh); return 0;}static intvideo_mmap(struct file *file, struct vm_area_struct * vma){ struct saa7134_fh *fh = file->private_data; return videobuf_mmap_mapper(vma,saa7134_queue(fh));}/* ------------------------------------------------------------------ */void saa7134_vbi_fmt(struct saa7134_dev *dev, struct v4l2_format *f){ struct saa7134_tvnorm *norm = dev->tvnorm; f->fmt.vbi.sampling_rate = 6750000 * 4; f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; f->fmt.vbi.offset = 64 * 4; f->fmt.vbi.start[0] = norm->vbi_v_start; f->fmt.vbi.count[0] = norm->vbi_v_stop - norm->vbi_v_start +1; f->fmt.vbi.start[1] = norm->video_v_stop + norm->vbi_v_start +1; f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */#if 0 if (V4L2_STD_PAL == norm->id) { /* FIXME */ f->fmt.vbi.start[0] += 3; f->fmt.vbi.start[1] += 3*2; }#endif}int saa7134_g_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_format *f){ switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); f->fmt.pix.width = fh->width; f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: f->fmt.win = fh->win; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: saa7134_vbi_fmt(dev,f); return 0; default: return -EINVAL; }}int saa7134_try_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_format *f){ int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { struct saa7134_format *fmt; enum v4l2_field field; unsigned int maxw, maxh; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) return -EINVAL; field = f->fmt.pix.field; maxw = min(dev->crop_current.width*4, dev->crop_bounds.width); maxh = min(dev->crop_current.height*4, dev->crop_bounds.height); if (V4L2_FIELD_ANY == field) { field = (f->fmt.pix.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; } switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: break; default: return -EINVAL; } f->fmt.pix.field = field; if (f->fmt.pix.width < 48) f->fmt.pix.width = 48; if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } case V4L2_BUF_TYPE_VIDEO_OVERLAY: err = verify_preview(dev,&f->fmt.win); if (0 != err) return err; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: saa7134_vbi_fmt(dev,f); return 0; default: return -EINVAL; }}int saa7134_s_fmt(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_format *f){ unsigned long flags; int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: err = saa7134_try_fmt(dev,fh,f); if (0 != err) return err; fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->cap.field = f->fmt.pix.field; return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: err = verify_preview(dev,&f->fmt.win); if (0 != err) return err; down(&dev->lock); fh->win = f->fmt.win; fh->nclips = f->fmt.win.clipcount; if (fh->nclips > 8) fh->nclips = 8; if (copy_from_user(fh->clips,f->fmt.win.clips, sizeof(struct v4l2_clip)*fh->nclips)) { up(&dev->lock); return -EFAULT; } if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); } up(&dev->lock); return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: saa7134_vbi_fmt(dev,f); return 0; default: return -EINVAL; }}int saa7134_common_ioctl(struct saa7134_dev *dev, unsigned int cmd, void *arg){ int err; switch (cmd) { case VIDIOC_QUERYCTRL: { const struct v4l2_queryctrl *ctrl; struct v4l2_queryctrl *c = arg; if ((c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) && (c->id < V4L2_CID_PRIVATE_BASE || c->id >= V4L2_CID_PRIVATE_LASTP1)) return -EINVAL; ctrl = ctrl_by_id(c->id); *c = (NULL != ctrl) ? *ctrl : no_ctrl; return 0; } case VIDIOC_G_CTRL: return get_control(dev,arg); case VIDIOC_S_CTRL: { down(&dev->lock); err = set_control(dev,NULL,arg); up(&dev->lock); return err; } /* --- input switching --------------------------------------- */ case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; unsigned int n; n = i->index; if (n >= SAA7134_INPUT_MAX) return -EINVAL; if (NULL == card_in(dev,i->index).name) return -EINVAL; memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name,card_in(dev,n).name); if (card_in(dev,n).tv) i->type = V4L2_INPUT_TYPE_TUNER; i->audioset = 1; if (n == dev->ctl_input) { int v1 = saa_readb(SAA7134_STATUS_VIDEO1); int v2 = saa_readb(SAA7134_STATUS_VIDEO2); if (0 != (v1 & 0x40)) i->status |= V4L2_IN_ST_NO_H_LOCK; if (0 != (v2 & 0x40)) i->status |= V4L2_IN_ST_NO_SYNC; if (0 != (v2 & 0x0e)) i->status |= V4L2_IN_ST_MACROVISION; } for (n = 0; n < TVNORMS; n++) i->std |= tvnorms[n].id; return 0; } case VIDIOC_G_INPUT: { int *i = arg; *i = dev->ctl_input; return 0; } case VIDIOC_S_INPUT: { int *i = arg; if (*i < 0 || *i >= SAA7134_INPUT_MAX) return -EINVAL; if (NULL == card_in(dev,*i).name) return -EINVAL; down(&dev->lock); video_mux(dev,*i); up(&dev->lock); return 0; } } return 0;}/* * This function is _not_ called directly, but from * video_generic_ioctl (and maybe others). userspace * copying is done already, arg is a kernel pointer. */static int video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; unsigned long flags; int err; if (video_debug > 1) saa7134_print_ioctl(dev->name,cmd);#ifdef VIDIOC_G_PRIORITY switch (cmd) { case VIDIOC_S_CTRL: case VIDIOC_S_STD: case VIDIOC_S_INPUT: case VIDIOC_S_TUNER: case VIDIOC_S_FREQUENCY: err = v4l2_prio_check(&dev->prio,&fh->prio); if (0 != err) return err; }#endif switch (cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; memset(cap,0,sizeof(*cap)); strcpy(cap->driver, "saa7134"); strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); cap->version = SAA7134_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; return 0; } /* --- tv standards ------------------------------------------ */ case VIDIOC_ENUMSTD: { struct v4l2_standard *e = arg; unsigned int i; i = e->index; if (i >= TVNORMS) return -EINVAL; err = v4l2_video_std_construct(e, tvnorms[e->index].id, tvnorms[e->index].name); e->index = i; if (err < 0) return err; return 0; } case VIDIOC_G_STD: { v4l2_std_id *id = arg; *id = dev->tvnorm->id; return 0; } case VIDIOC_S_STD: { v4l2_std_id *id = arg; unsigned int i; for (i = 0; i < TVNORMS; i++) if (*id == tvnorms[i].id) break; if (i == TVNORMS) for (i = 0; i < TVNORMS; i++) if (*id & tvnorms[i].id) break; if (i == TVNORMS) return -EINVAL; down(&dev->lock); if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); set_tvnorm(dev,&tvnorms[i]); start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); } else set_tvnorm(dev,&tvnorms[i]); saa7134_tvaudio_do_scan(dev); up(&dev->lock); return 0; } case VIDIOC_CROPCAP: { struct v4l2_cropcap *cap = arg; if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; cap->bounds = dev->crop_bounds; cap->defrect = dev->crop_defrect; cap->pixelaspect.numerator = 1; cap->pixelaspect.denominator = 1; if (dev->tvnorm->id & V4L2_STD_525_60) { cap->pixelaspect.numerator = 11; cap->pixelaspect.denominator = 10; } if (dev->tvnorm->id & V4L2_STD_625_50) { cap->pixelaspect.numerator = 54; cap->pixelaspect.denominator = 59; } return 0; } case VIDIOC_G_CROP: { struct v4l2_crop * crop = arg; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; crop->c = dev->crop_current; return 0; } case VIDIOC_S_CROP: { struct v4l2_crop *crop = arg; struct v4l2_rect *b = &dev->crop_bounds; if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) return -EINVAL; if (crop->c.height < 0) return -EINVAL; if (crop->c.width < 0) return -EINVAL; if (res_locked(fh->dev,RESOURCE_OVERLAY)) return -EBUSY; if (res_locked(fh->dev,RESOURCE_VIDEO)) return -EBUSY; if (crop->c.top < b->top) crop->c.top = b->top; if (crop->c.top > b->top + b->height)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -