📄 tm6000-video.c
字号:
}static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i){ struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (i != fh->type) return -EINVAL; videobuf_streamoff(&fh->vb_vidq); res_free(dev,fh); return (0);}static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm){ int rc=0; struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; rc=tm6000_set_standard (dev, norm);#if 1 fh->width = dev->width; fh->height = dev->height;#endif if (rc<0) return rc; tm6000_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm); return 0;}static int vidioc_enum_input (struct file *file, void *priv, struct v4l2_input *inp){ switch (inp->index) { case TM6000_INPUT_TV: inp->type = V4L2_INPUT_TYPE_TUNER; strcpy(inp->name,"Television"); break; case TM6000_INPUT_COMPOSITE: inp->type = V4L2_INPUT_TYPE_CAMERA; strcpy(inp->name,"Composite"); break; case TM6000_INPUT_SVIDEO: inp->type = V4L2_INPUT_TYPE_CAMERA; strcpy(inp->name,"S-Video"); break; default: return -EINVAL; } inp->std = TM6000_STD; return 0;}static int vidioc_g_input (struct file *file, void *priv, unsigned int *i){ struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; *i=dev->input; return 0;}static int vidioc_s_input (struct file *file, void *priv, unsigned int i){ struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; int rc=0; char buf[1]; switch (i) { case TM6000_INPUT_TV: dev->input=i; *buf=0; break; case TM6000_INPUT_COMPOSITE: case TM6000_INPUT_SVIDEO: dev->input=i; *buf=1; break; default: return -EINVAL; } rc=tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, REQ_03_SET_GET_MCU_PIN, 0x03, 1, buf, 1); if (!rc) { dev->input=i; rc=vidioc_s_std (file, priv, &dev->vfd->current_norm); } return (rc);} /* --- controls ---------------------------------------------- */static int vidioc_queryctrl (struct file *file, void *priv, struct v4l2_queryctrl *qc){ int i; for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) if (qc->id && qc->id == tm6000_qctrl[i].id) { memcpy(qc, &(tm6000_qctrl[i]), sizeof(*qc)); return (0); } return -EINVAL;}static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl){ struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; int val; /* FIXME: Probably, those won't work! Maybe we need shadow regs */ switch (ctrl->id) { case V4L2_CID_CONTRAST: val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x08, 0); break; case V4L2_CID_BRIGHTNESS: val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x09, 0); return 0; case V4L2_CID_SATURATION: val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x0a, 0); return 0; case V4L2_CID_HUE: val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x0b, 0); return 0; default: return -EINVAL; } if (val<0) return val; ctrl->value=val; return 0;}static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl){ struct tm6000_fh *fh =priv; struct tm6000_core *dev = fh->dev; u8 val=ctrl->value; switch (ctrl->id) { case V4L2_CID_CONTRAST: tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x08, val); return 0; case V4L2_CID_BRIGHTNESS: tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x09, val); return 0; case V4L2_CID_SATURATION: tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0a, val); return 0; case V4L2_CID_HUE: tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0b, val); return 0; } return -EINVAL;}static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *t){ struct tm6000_fh *fh =priv; struct tm6000_core *dev = fh->dev; if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; if (0 != t->index) return -EINVAL; strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM; t->rangehigh = 0xffffffffUL; t->rxsubchans = V4L2_TUNER_SUB_MONO; return 0;}static int vidioc_s_tuner (struct file *file, void *priv, struct v4l2_tuner *t){ struct tm6000_fh *fh =priv; struct tm6000_core *dev = fh->dev; if (UNSET == dev->tuner_type) return -EINVAL; if (0 != t->index) return -EINVAL; return 0;}static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *f){ struct tm6000_fh *fh =priv; struct tm6000_core *dev = fh->dev; if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; tm6000_i2c_call_clients(dev,VIDIOC_G_FREQUENCY,f); return 0;}static int vidioc_s_frequency (struct file *file, void *priv, struct v4l2_frequency *f){ struct tm6000_fh *fh =priv; struct tm6000_core *dev = fh->dev; if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) return -EINVAL; if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; if (unlikely(f->tuner != 0)) return -EINVAL;// mutex_lock(&dev->lock); dev->freq = f->frequency; tm6000_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,f);// mutex_unlock(&dev->lock); return 0;}/* ------------------------------------------------------------------ File operations for the device ------------------------------------------------------------------*/static int tm6000_open(struct inode *inode, struct file *file){ int minor = iminor(inode); struct tm6000_core *h,*dev = NULL; struct tm6000_fh *fh; struct list_head *list; enum v4l2_buf_type type = 0; int i,rc; printk(KERN_INFO "tm6000: open called (minor=%d)\n",minor); dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called " "(minor=%d)\n",minor); list_for_each(list,&tm6000_corelist) { h = list_entry(list, struct tm6000_core, tm6000_corelist); if (h->vfd->minor == minor) { dev = h; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } } if (NULL == dev) return -ENODEV;#if 0 /* Avoids an oops at read() - seems to be semaphore related */ if (dev->users) { printk(KERN_INFO "this driver can be opened only once (users=%d)\n",dev->users); return -EBUSY; }#endif /* If more than one user, mutex should be added */ dev->users++; dprintk(dev, V4L2_DEBUG_OPEN, "open minor=%d type=%s users=%d\n", minor,v4l2_type_names[type],dev->users); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) { dev->users--; return -ENOMEM; } file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dev->fourcc = format[0].fourcc; fh->fmt = format_by_fourcc(dev->fourcc); tm6000_get_std_res (dev); fh->width = dev->width; fh->height = dev->height; dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " "dev->vidq=0x%08lx\n", (unsigned long)fh,(unsigned long)dev,(unsigned long)&dev->vidq); dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " "queued=%d\n",list_empty(&dev->vidq.queued)); dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " "active=%d\n",list_empty(&dev->vidq.active));#if 0 /* Resets frame counters */ sprintf(dev->timestr,"%02d:%02d:%02d:%03d", dev->h,dev->m,dev->s,(dev->us+500)/1000);#endif /* initialize hardware on analog mode */ if (dev->mode!=TM6000_MODE_ANALOG) { rc=tm6000_init_analog_mode (dev); if (rc<0) return rc; /* Put all controls at a sane state */ for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) qctl_regs[i] =tm6000_qctrl[i].default_value; dev->mode=TM6000_MODE_ANALOG; } videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct tm6000_buffer),fh); return 0;}static ssize_ttm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos){ struct tm6000_fh *fh = file->private_data; if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (res_locked(fh->dev)) return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, file->f_flags & O_NONBLOCK);#if 0 return videobuf_read_one(&fh->vb_vidq, data, count, pos, file->f_flags & O_NONBLOCK);#endif } return 0;}static unsigned inttm6000_poll(struct file *file, struct poll_table_struct *wait){ struct tm6000_fh *fh = file->private_data; struct tm6000_buffer *buf; if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; if (res_get(fh->dev,fh)) { /* streaming capture */ if (list_empty(&fh->vb_vidq.stream)) return POLLERR; buf = list_entry(fh->vb_vidq.stream.next,struct tm6000_buffer,vb.stream); } else { /* read() capture */#if 0 buf = (struct tm6000_buffer*)fh->vb_vidq.read_buf; if (NULL == buf) return POLLERR;#else return videobuf_poll_stream(file, &fh->vb_vidq, wait);#endif } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) return POLLIN|POLLRDNORM; return 0;}static int tm6000_release(struct inode *inode, struct file *file){ struct tm6000_fh *fh = file->private_data; struct tm6000_core *dev = fh->dev; int minor = iminor(inode); dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (minor=%d, users=%d)\n",minor,dev->users); dev->users--; if (!dev->users) { tm6000_uninit_isoc(dev); videobuf_mmap_free(&fh->vb_vidq); } kfree (fh); return 0;}static int tm6000_mmap(struct file *file, struct vm_area_struct * vma){ struct tm6000_fh *fh = file->private_data; int ret; ret=videobuf_mmap_mapper(&fh->vb_vidq, vma); return ret;}static struct file_operations tm6000_fops = { .owner = THIS_MODULE, .open = tm6000_open, .release = tm6000_release, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ .read = tm6000_read, .poll = tm6000_poll, .mmap = tm6000_mmap, .llseek = no_llseek,};static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_s_std = vidioc_s_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf,#ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf,#endif};static struct video_device tm6000_template = { .name = "tm6000", .fops = &tm6000_fops, .ioctl_ops = &video_ioctl_ops, .minor = -1, .release = video_device_release, .tvnorms = TM6000_STD, .current_norm = V4L2_STD_NTSC_M,};/* ----------------------------------------------------------------- Initialization and module stuff ------------------------------------------------------------------*/int tm6000_v4l2_register(struct tm6000_core *dev){ int ret = -1; struct video_device *vfd; vfd = video_device_alloc(); if(!vfd) { return -ENOMEM; } dev->vfd = vfd; list_add_tail(&dev->tm6000_corelist,&tm6000_corelist); /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); memcpy (dev->vfd, &tm6000_template, sizeof(*(dev->vfd))); dev->vfd->debug=tm6000_debug; ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); printk(KERN_INFO "Trident TVMaster TM5600/TM6000 USB2 board (Load status: %d)\n", ret); return ret;}int tm6000_v4l2_unregister(struct tm6000_core *dev){ struct tm6000_core *h; struct list_head *pos, *tmp; video_unregister_device(dev->vfd); list_for_each_safe(pos, tmp, &tm6000_corelist) { h = list_entry(pos, struct tm6000_core, tm6000_corelist); if (h == dev) { list_del(pos); } } return 0;}int tm6000_v4l2_exit(void){#if 0 struct tm6000_core *h; struct list_head *list; while (!list_empty(&tm6000_corelist)) { list = tm6000_corelist.next; list_del(list); h = list_entry(list, struct tm6000_core, tm6000_corelist); video_unregister_device(&h->vfd); kfree (h); }#endif return 0;}module_param(video_nr, int, 0);MODULE_PARM_DESC(video_nr,"Allow changing video device number");module_param_named (debug, tm6000_debug, int, 0444);MODULE_PARM_DESC(debug,"activates debug info");module_param(vid_limit,int,0644);MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -