📄 tm6000-video.c
字号:
if (!dev) return; spin_lock_irqsave(&dev->slock, flags); get_next_buf(dma_q, &buf); if (buf) tm6000_isoc_copy(urb, &buf); spin_unlock_irqrestore(&dev->slock, flags);#if 0 /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; }#endif urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) tm6000_err("urb resubmit failed (error=%i)\n", urb->status);}/* * Stop and Deallocate URBs */static void tm6000_uninit_isoc(struct tm6000_core *dev){ struct urb *urb; int i; dev->isoc_ctl.nfields = -1; dev->isoc_ctl.buf = NULL; for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb=dev->isoc_ctl.urb[i]; if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); if (dev->isoc_ctl.transfer_buffer[i]) { usb_buffer_free(dev->udev, urb->transfer_buffer_length, dev->isoc_ctl.transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } kfree (dev->isoc_ctl.urb); kfree (dev->isoc_ctl.transfer_buffer); dev->isoc_ctl.urb=NULL; dev->isoc_ctl.transfer_buffer=NULL; dev->isoc_ctl.num_bufs = 0; dev->isoc_ctl.num_bufs=0;}/* * Allocate URBs and start IRQ */static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize){ struct tm6000_dmaqueue *dma_q = &dev->vidq; int i, j, sb_size, pipe, size, max_packets, num_bufs = 5; struct urb *urb; /* De-allocates all pending stuff */ tm6000_uninit_isoc(dev); pipe = usb_rcvisocpipe(dev->udev, dev->isoc_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); if (size > dev->max_isoc_in) size = dev->max_isoc_in; dev->isoc_ctl.max_pkt_size = size; max_packets = ( framesize + size - 1) / size; if (max_packets > TM6000_MAX_ISO_PACKETS) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; dev->isoc_ctl.num_bufs = num_bufs; dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); if (!dev->isoc_ctl.urb) { tm6000_err("cannot alloc memory for usb buffers\n"); return -ENOMEM; } dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); if (!dev->isoc_ctl.urb) { tm6000_err("cannot allocate memory for usbtransfer\n"); kfree(dev->isoc_ctl.urb); return -ENOMEM; } dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" " (%d bytes) of %d bytes each to handle %u size\n", max_packets, num_bufs, sb_size, dev->max_isoc_in, size);#if 0 /* reset streaming vars */ dev->isoc_ctl.frame_current = NULL; dev->isoc_ctl.frame_count = 0;#endif /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); if (!urb) { tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); tm6000_uninit_isoc(dev); usb_free_urb(urb); return -ENOMEM; } dev->isoc_ctl.urb[i] = urb; dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); if (!dev->isoc_ctl.transfer_buffer[i]) { tm6000_err ("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, in_interrupt()?" while in int":""); tm6000_uninit_isoc(dev); return -ENOMEM; } memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, tm6000_irq_callback, dma_q); urb->interval = dev->isoc_in->desc.bInterval; urb->number_of_packets = max_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; for (j = 0; j < max_packets; j++) { urb->iso_frame_desc[j].offset = size * j; urb->iso_frame_desc[j].length = size; } } return 0;}static int tm6000_start_thread( struct tm6000_core *dev){ struct tm6000_dmaqueue *dma_q = &dev->vidq; int i; dma_q->frame=0; dma_q->ini_jiffies=jiffies; init_waitqueue_head(&dma_q->wq); /* submit urbs and enables IRQ */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); if (rc) { tm6000_err("submit of urb %i failed (error=%i)\n", i, rc); tm6000_uninit_isoc(dev); return rc; } } return 0;}/* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/static intbuffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size){ struct tm6000_fh *fh = vq->priv_data; *size = fh->fmt->depth * fh->width * fh->height >> 3; if (0 == *count) *count = TM6000_DEF_BUF; if (*count < TM6000_MIN_BUF) { *count=TM6000_MIN_BUF; } while (*size * *count > vid_limit * 1024 * 1024) (*count)--; return 0;}static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf){ struct tm6000_fh *fh = vq->priv_data; struct tm6000_core *dev = fh->dev; unsigned long flags; if (in_interrupt()) BUG(); /* We used to wait for the buffer to finish here, but this didn't work because, as we were keeping the state as VIDEOBUF_QUEUED, videobuf_queue_cancel marked it as finished for us. (Also, it could wedge forever if the hardware was misconfigured.) This should be safe; by the time we get here, the buffer isn't queued anymore. If we ever start marking the buffers as VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); if (dev->isoc_ctl.buf == buf) dev->isoc_ctl.buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); buf->vb.state = VIDEOBUF_NEEDS_INIT;}static intbuffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field){ struct tm6000_fh *fh = vq->priv_data; struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); struct tm6000_core *dev = fh->dev; int rc = 0, urb_init = 0; BUG_ON(NULL == fh->fmt);#if 0 if (fh->width < norm_minw(core) || fh->width > norm_maxw(core) || fh->height < norm_minh(core) || fh->height > norm_maxh(core)) { dprintk(dev, V4L2_DEBUG_QUEUE, "Window size (%dx%d) is out of " "supported range\n", fh->width, fh->height); dprintk(dev, V4L2_DEBUG_QUEUE, "Valid range is from (%dx%d) to " "(%dx%d)\n", norm_minw(core), norm_minh(core), norm_maxw(core),norm_maxh(core)); return -EINVAL; }#endif /* FIXME: It assumes depth=2 */ /* The only currently supported format is 16 bits/pixel */ buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; if (buf->fmt != fh->fmt || buf->vb.width != fh->width || buf->vb.height != fh->height || buf->vb.field != field) { buf->fmt = fh->fmt; buf->vb.width = fh->width; buf->vb.height = fh->height; buf->vb.field = field; buf->vb.state = VIDEOBUF_NEEDS_INIT; } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { if (0 != (rc = videobuf_iolock(vq, &buf->vb, NULL))) goto fail; urb_init = 1; } if (!dev->isoc_ctl.num_bufs) urb_init = 1; if (urb_init) { rc = tm6000_prepare_isoc(dev, buf->vb.size); if (rc < 0) goto fail; rc = tm6000_start_thread(dev); if (rc < 0) goto fail; } buf->vb.state = VIDEOBUF_PREPARED; return 0;fail: free_buffer(vq, buf); return rc;}static voidbuffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){ struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); struct tm6000_fh *fh = vq->priv_data; struct tm6000_core *dev = fh->dev; struct tm6000_dmaqueue *vidq = &dev->vidq; buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active);}static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb){ struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); free_buffer(vq,buf);}static struct videobuf_queue_ops tm6000_video_qops = { .buf_setup = buffer_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .buf_release = buffer_release,};/* ------------------------------------------------------------------ IOCTL handling ------------------------------------------------------------------*/static int res_get(struct tm6000_core *dev, struct tm6000_fh *fh){ /* is it free? */ mutex_lock(&dev->lock); if (dev->resources) { /* no, someone else uses it */ mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ dev->resources =1; dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); mutex_unlock(&dev->lock); return 1;}static int res_locked(struct tm6000_core *dev){ return (dev->resources);}static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh){ mutex_lock(&dev->lock); dev->resources = 0; dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); mutex_unlock(&dev->lock);}/* ------------------------------------------------------------------ IOCTL vidioc handling ------------------------------------------------------------------*/static int vidioc_querycap (struct file *file, void *priv, struct v4l2_capability *cap){ // struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card,"Trident TVMaster TM5600/6000", sizeof(cap->card)); // strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); cap->version = TM6000_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_TUNER | V4L2_CAP_READWRITE; return 0;}static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, struct v4l2_fmtdesc *f){ if (unlikely(f->index >= ARRAY_SIZE(format))) return -EINVAL; strlcpy(f->description,format[f->index].name,sizeof(f->description)); f->pixelformat = format[f->index].fourcc; return 0;}static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f){ struct tm6000_fh *fh=priv; f->fmt.pix.width = fh->width; f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->vb_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);}static struct tm6000_fmt* format_by_fourcc(unsigned int fourcc){ unsigned int i; for (i = 0; i < ARRAY_SIZE(format); i++) if (format[i].fourcc == fourcc) return format+i; return NULL;}static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f){ struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; struct tm6000_fmt *fmt; enum v4l2_field field; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) { dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" " invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } field = f->fmt.pix.field; if (field == V4L2_FIELD_ANY) {// field=V4L2_FIELD_INTERLACED; field=V4L2_FIELD_SEQ_TB; } else if (V4L2_FIELD_INTERLACED != field) { dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); return -EINVAL; }#if 1 tm6000_get_std_res (dev); f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height;#else /* Scaling is not yet supported */ if (f->fmt.pix.width < norm_minw(core)) f->fmt.pix.width = norm_minw(core); if (f->fmt.pix.width > norm_maxw(core)) f->fmt.pix.width = norm_maxw(core); if (f->fmt.pix.height < norm_minh(core)) f->fmt.pix.height = norm_minh(core); if (f->fmt.pix.height > norm_maxh(core)) f->fmt.pix.height = norm_maxh(core);#endif f->fmt.pix.width &= ~0x01; f->fmt.pix.field = field; 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;}/*FIXME: This seems to be generic enough to be at videodev2 */static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f){ struct tm6000_fh *fh=priv; struct tm6000_core *dev = fh->dev; int ret = vidioc_try_fmt_vid_cap(file,fh,f); if (ret < 0) return (ret); fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; dev->fourcc = f->fmt.pix.pixelformat; tm6000_set_fourcc_format(dev); return (0);}static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p){ struct tm6000_fh *fh=priv; return (videobuf_reqbufs(&fh->vb_vidq, p));}static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p){ struct tm6000_fh *fh=priv; return (videobuf_querybuf(&fh->vb_vidq, p));}static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p){ struct tm6000_fh *fh=priv; return (videobuf_qbuf(&fh->vb_vidq, p));}static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p){ struct tm6000_fh *fh=priv; return (videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK));}#ifdef CONFIG_VIDEO_V4L1_COMPATstatic int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf){ struct tm6000_fh *fh=priv; return videobuf_cgmbuf (&fh->vb_vidq, mbuf, 8);}#endifstatic int vidioc_streamon(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; if (!res_get(dev,fh)) return -EBUSY; return (videobuf_streamon(&fh->vb_vidq));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -