📄 cx88-video.c
字号:
c = &cx8800_ctls[i]; if (NULL == c) return -EINVAL; if (ctl->value < c->v.minimum) return -ERANGE; if (ctl->value > c->v.maximum) return -ERANGE; switch (ctl->id) { case V4L2_CID_AUDIO_BALANCE: value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; break; case V4L2_CID_AUDIO_VOLUME: value = 0x3f - (ctl->value & 0x3f); break; case V4L2_CID_SATURATION: /* special v_sat handling */ v_sat_value = ctl->value - (0x7f - 0x5a); if (v_sat_value > 0xff) v_sat_value = 0xff; if (v_sat_value < 0x00) v_sat_value = 0x00; cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8); /* fall through to default route for u_sat */ default: value = ((ctl->value - c->off) << c->shift) & c->mask; break; } dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n", ctl->id, c->reg, value, c->sreg ? " [shadowed]" : ""); if (c->sreg) { cx_sandor(c->sreg, c->reg, c->mask, value); } else { cx_andor(c->reg, c->mask, value); } return 0;}static void init_controls(struct cx8800_dev *dev){ static struct v4l2_control mute = { .id = V4L2_CID_AUDIO_MUTE, .value = 1, }; static struct v4l2_control volume = { .id = V4L2_CID_AUDIO_VOLUME, .value = 0x3f, }; set_control(dev,&mute); set_control(dev,&volume);}/* ------------------------------------------------------------------ */static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_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->vidq.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_VBI_CAPTURE: cx8800_vbi_fmt(dev, f); return 0; default: return -EINVAL; }}static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_format *f){ switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { struct cx8800_fmt *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 = norm_maxw(dev->core->tvnorm); maxh = norm_maxh(dev->core->tvnorm); 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.height < 32) f->fmt.pix.height = 32; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; if (f->fmt.pix.width < 48) f->fmt.pix.width = 48; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; f->fmt.pix.width &= ~0x03; 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_VBI_CAPTURE: cx8800_vbi_fmt(dev, f); return 0; default: return -EINVAL; }}static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_format *f){ int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: err = cx8800_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->vidq.field = f->fmt.pix.field; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: cx8800_vbi_fmt(dev, f); return 0; default: return -EINVAL; }}/* * 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 cx8800_fh *fh = file->private_data; struct cx8800_dev *dev = fh->dev; struct cx88_core *core = dev->core;#if 0 unsigned long flags;#endif int err; if (video_debug > 1) cx88_print_ioctl(core->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; memset(cap,0,sizeof(*cap)); strcpy(cap->driver, "cx8800"); strlcpy(cap->card, cx88_boards[core->board].name, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); cap->version = CX88_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE |#if 0 V4L2_CAP_VIDEO_OVERLAY |#endif 0; if (UNSET != core->tuner_type) cap->capabilities |= V4L2_CAP_TUNER; return 0; } /* ---------- tv norms ---------- */ case VIDIOC_ENUMSTD: { struct v4l2_standard *e = arg; unsigned int i; i = e->index; if (i >= ARRAY_SIZE(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 = core->tvnorm->id; return 0; } case VIDIOC_S_STD: { v4l2_std_id *id = arg; unsigned int i; for(i = 0; i < ARRAY_SIZE(tvnorms); i++) if (*id & tvnorms[i].id) break; if (i == ARRAY_SIZE(tvnorms)) return -EINVAL; down(&dev->lock); cx88_set_tvnorm(dev->core,&tvnorms[i]); up(&dev->lock); return 0; } /* ------ input switching ---------- */ case VIDIOC_ENUMINPUT: { static const char *iname[] = { [ CX88_VMUX_COMPOSITE1 ] = "Composite1", [ CX88_VMUX_COMPOSITE2 ] = "Composite2", [ CX88_VMUX_COMPOSITE3 ] = "Composite3", [ CX88_VMUX_COMPOSITE4 ] = "Composite4", [ CX88_VMUX_SVIDEO ] = "S-Video", [ CX88_VMUX_TELEVISION ] = "Television", [ CX88_VMUX_CABLE ] = "Cable TV", [ CX88_VMUX_DVB ] = "DVB", [ CX88_VMUX_DEBUG ] = "for debug only", }; struct v4l2_input *i = arg; unsigned int n; n = i->index; if (n >= 4) return -EINVAL; if (0 == INPUT(n)->type) return -EINVAL; memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name,iname[INPUT(n)->type]); if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || (CX88_VMUX_CABLE == INPUT(n)->type)) i->type = V4L2_INPUT_TYPE_TUNER; for (n = 0; n < ARRAY_SIZE(tvnorms); n++) i->std |= tvnorms[n].id; return 0; } case VIDIOC_G_INPUT: { unsigned int *i = arg; *i = dev->core->input; return 0; } case VIDIOC_S_INPUT: { unsigned int *i = arg; if (*i >= 4) return -EINVAL; down(&dev->lock); video_mux(dev,*i); up(&dev->lock); return 0; }#if 0 /* needs review */ case VIDIOC_G_AUDIO: { struct v4l2_audio *a = arg; unsigned int n = a->index; memset(a,0,sizeof(*a)); a->index = n; switch (n) { case 0: if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || (CX88_VMUX_CABLE == INPUT(n)->type)) { strcpy(a->name,"Television"); // FIXME figure out if stereo received and set V4L2_AUDCAP_STEREO. return 0; } break; case 1: if (CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD == core->board) { strcpy(a->name,"Line In"); a->capability = V4L2_AUDCAP_STEREO; return 0; } break; } // Audio input not available. return -EINVAL; }#endif /* --- capture ioctls ---------------------------------------- */ case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; enum v4l2_buf_type type; unsigned int index; index = f->index; type = f->type; switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (index >= ARRAY_SIZE(formats)) return -EINVAL; memset(f,0,sizeof(*f)); f->index = index; f->type = type; strlcpy(f->description,formats[index].name,sizeof(f->description)); f->pixelformat = formats[index].fourcc; break; default: return -EINVAL; } return 0; } case VIDIOC_G_FMT: { struct v4l2_format *f = arg; return cx8800_g_fmt(dev,fh,f); } case VIDIOC_S_FMT: { struct v4l2_format *f = arg; return cx8800_s_fmt(dev,fh,f); } case VIDIOC_TRY_FMT: { struct v4l2_format *f = arg; return cx8800_try_fmt(dev,fh,f); } /* --- controls ---------------------------------------------- */ case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *c = arg; int i; if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) return -EINVAL; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == c->id) break; if (i == CX8800_CTLS) { *c = no_ctl; return 0; } *c = cx8800_ctls[i].v; return 0; } case VIDIOC_G_CTRL: return get_control(dev,arg); case VIDIOC_S_CTRL: return set_control(dev,arg); /* --- tuner ioctls ------------------------------------------ */ case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; u32 reg; if (UNSET == core->tuner_type) return -EINVAL; if (0 != t->index) return -EINVAL; memset(t,0,sizeof(*t)); strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; cx88_get_stereo(core ,t); reg = cx_read(MO_DEVICE_STATUS); t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; return 0; } case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; if (UNSET == core->tuner_type) return -EINVAL; if (0 != t->index) return -EINVAL; cx88_set_stereo(core, t->audmode); return 0; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *f = arg; if (UNSET == core->tuner_type) return -EINVAL; if (f->tuner != 0) return -EINVAL; memset(f,0,sizeof(*f)); f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; return 0; } case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *f = arg; if (UNSET == core->tuner_type) return -EINVAL; if (f->tuner != 0) return -EINVAL; if (0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV) return -EINVAL; if (1 == fh->radio && f->type != V4L2_TUNER_RADIO) return -EINVAL; down(&dev->lock); dev->freq = f->frequency;#ifdef V4L2_I2C_CLIENTS cx88_call_i2c_clients(dev->core,VIDIOC_S_FREQUENCY,f);#else cx88_call_i2c_clients(dev->core,VIDIOCSFREQ,&dev->freq);#endif up(&dev->lock); return 0; } /* --- streaming capture ------------------------------------- */ case VIDIOCGMBUF: { struct video_mbuf *mbuf = arg; struct videobuf_queue *q; struct v4l2_requestbuffers req; unsigned int i; q = get_queue(fh); memset(&req,0,sizeof(req)); req.type = q->type; req.count = 8; req.memory = V4L2_MEMORY_MMAP; err = videobuf_reqbufs(q,&req); if (err < 0) return err; memset(mbuf,0,sizeof(*mbuf)); mbuf->frames = req.count; mbuf->size = 0; for (i = 0; i < mbuf->frames; i++) { mbuf->offsets[i] = q->bufs[i]->boff; mbuf->size += q->bufs[i]->bsize; } return 0; } case VIDIOC_REQBUFS: return videobuf_reqbufs(get_queue(fh), arg); case VIDIOC_QUERYBUF: return videobuf_querybuf(get_queue(fh), arg); case VIDIOC_QBUF: return videobuf_qbuf(get_queue(fh), arg); case VIDIOC_DQBUF: return videobuf_dqbuf(get_queue(fh), arg, file->f_flags & O_NONBLOCK); case VIDIOC_STREAMON: { int res = get_ressource(fh); if (!res_get(dev,fh,res)) return -EBUSY; return videobuf_streamon(get_queue(fh)); } case VIDIOC_STREAMOFF: { int res = get_ressource(fh); err = videobuf_streamoff(get_queue(fh)); if (err < 0) return err; res_free(dev,fh,res); return 0; } default: return v4l_compat_translate_ioctl(inode,file,cmd,arg, video_do_ioctl); } return 0;}static int video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return video_usercopy(inode, file, cmd, arg, video_do_ioctl);}/* ----------------------------------------------------------- */static int radio_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct cx8800_fh *fh = file->private_data; struct cx8800_dev *dev = fh->dev; struct cx88_core *core = dev->core; if (video_debug > 1) cx88_print_ioctl(core->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; memset(cap,0,sizeof(*cap)); strcpy(cap->driver, "cx8800"); strlcpy(cap->card, cx88_boards[core->board].name, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); cap->version = CX88_VERSION_CODE; cap->capabilities = V4L2_CAP_TUNER; return 0; } case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; if (t->index > 0) return -EINVAL; memset(t,0,sizeof(*t)); strcpy(t->name, "Radio"); t->rangelow = (int)(65*16);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -