bttv-driver.c
来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 2,527 行 · 第 1/5 页
C
2,527 行
return 0; } case VIDIOC_G_FMT: { struct v4l2_format *f = arg; memset(f,0,sizeof(*f)); f->type = V4L2_BUF_TYPE_CAPTURE; f->fmt.pix.width = fh->buf.vb.width; f->fmt.pix.height = fh->buf.vb.height; f->fmt.pix.depth = fh->buf.fmt->depth; f->fmt.pix.pixelformat = fh->buf.fmt->fourcc; f->fmt.pix.sizeimage = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8; return 0; } case VIDIOC_S_FMT: { struct v4l2_format *f = arg; const struct bttv_format *fmt; if ((f->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) return -EINVAL; if (f->fmt.pix.width < 48 || f->fmt.pix.height < 32) return -EINVAL; if (f->fmt.pix.flags & V4L2_FMT_FLAG_BYTESPERLINE) /* FIXME -- not implemented yet */ return -EINVAL; down(&fh->lock); /* fixup format */ if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth) f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth; if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight) f->fmt.pix.height = bttv_tvnorms[btv->tvnorm].sheight; if (!(f->fmt.pix.flags & V4L2_FMT_FLAG_INTERLACED) && f->fmt.pix.height>bttv_tvnorms[btv->tvnorm].sheight/2) f->fmt.pix.height=bttv_tvnorms[btv->tvnorm].sheight/2; if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight/2) { /* must interlace -- no field splitting available */ f->fmt.pix.flags &= ~(V4L2_FMT_FLAG_TOPFIELD| V4L2_FMT_FLAG_BOTFIELD); } else { /* one field is enouth -- no interlace needed */ f->fmt.pix.flags &= ~V4L2_FMT_FLAG_INTERLACED; } /* update our state informations */ fh->buf.fmt = fmt; fh->buf.vb.width = f->fmt.pix.width; fh->buf.vb.height = f->fmt.pix.height; btv->init.buf.fmt = fmt; btv->init.buf.vb.width = f->fmt.pix.width; btv->init.buf.vb.height = f->fmt.pix.height; /* update data for the application */ f->fmt.pix.depth = fmt->depth; f->fmt.pix.sizeimage = (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8; up(&fh->lock); return 0; } case VIDIOC_G_FBUF: { struct v4l2_framebuffer *fb = arg; memset(fb,0,sizeof(*fb)); fb->base[0] = btv->fbuf.base; fb->fmt.width = btv->fbuf.width; fb->fmt.height = btv->fbuf.height; fb->fmt.bytesperline = btv->fbuf.bytesperline; fb->fmt.flags = V4L2_FMT_FLAG_BYTESPERLINE; fb->capability = V4L2_FBUF_CAP_CLIPPING; if (fh->ovfmt) { fb->fmt.depth = fh->ovfmt->depth; fb->fmt.pixelformat = fh->ovfmt->fourcc; } return 0; } case VIDIOC_S_FBUF: { struct v4l2_framebuffer *fb = arg; const struct bttv_format *fmt; unsigned long end; if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; /* check args */ end = (unsigned long)fb->base[0] + fb->fmt.height * fb->fmt.bytesperline; if (0 == find_videomem((unsigned long)fb->base[0],end)) return -EINVAL; fmt = format_by_fourcc(fb->fmt.pixelformat); if (NULL == fmt) return -EINVAL; if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; down(&fh->lock); retval = -EINVAL; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) goto fh_unlock_and_return; if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight) goto fh_unlock_and_return; } /* ok, accept it */ btv->fbuf.base = fb->base[0]; btv->fbuf.width = fb->fmt.width; btv->fbuf.height = fb->fmt.height; btv->fbuf.depth = fmt->depth; if (fb->fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE) btv->fbuf.bytesperline = fb->fmt.bytesperline; else btv->fbuf.bytesperline = btv->fbuf.width*fmt->depth/8; retval = 0; fh->ovfmt = fmt; btv->init.ovfmt = fmt; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { fh->ov.x = 0; fh->ov.y = 0; fh->ov.width = fb->fmt.width; fh->ov.height = fb->fmt.height; btv->init.ov.width = fb->fmt.width; btv->init.ov.height = fb->fmt.height; if (fh->ov.clips) kfree(fh->ov.clips); fh->ov.clips = NULL; fh->ov.nclips = 0; if (check_btres(fh, RESOURCE_OVERLAY)) { struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new), V4L2_BUF_TYPE_CAPTURE); bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); retval = bttv_switch_overlay(btv,fh,new); } } up(&fh->lock); return retval; } case VIDIOC_G_WIN: { struct v4l2_window *win = arg; memset(win,0,sizeof(*win)); win->x = fh->ov.x; win->y = fh->ov.y; win->width = fh->ov.width; win->height = fh->ov.height; return 0; } case VIDIOC_S_WIN: { struct v4l2_window *win = arg; return setup_window(fh,btv,win->x,win->y, win->width,win->height, (struct video_clip*)win->clips, win->clipcount); } case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; int size,count; if (!mmap) return -EINVAL; if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; if (req->count < 0) return -EINVAL; down(&fh->lock); retval = -EINVAL; if (NULL == fh->buf.fmt || 0 == fh->buf.vb.width || 0 == fh->buf.vb.height) goto fh_unlock_and_return; size = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth) >> 3; size = (size + PAGE_SIZE - 1) & PAGE_MASK; count = req->count; if (count > VIDEO_MAX_FRAME) count = VIDEO_MAX_FRAME; while (size * count > gbuffers * gbufsize) count--; retval = videobuf_mmap_setup(file, (struct videobuf_buffer**)fh->bufs, sizeof(struct bttv_buffer), count,size,V4L2_BUF_TYPE_CAPTURE, release_buffer); if (retval < 0) goto fh_unlock_and_return; req->type = V4L2_BUF_TYPE_CAPTURE; req->count = count; up(&fh->lock); return 0; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *b = arg; if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; if (b->index < 0 || b->index > VIDEO_MAX_FRAME) return -EINVAL; if (NULL == fh->bufs[b->index]) return -EINVAL; videobuf_status(b,&fh->bufs[b->index]->vb); return 0; } case VIDIOC_QBUF: { struct v4l2_buffer *b = arg; struct bttv_buffer *buf; int field = 0; if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; if (b->index < 0 || b->index > VIDEO_MAX_FRAME) return -EINVAL; down(&fh->lock); retval = -EINVAL; buf = fh->bufs[b->index]; if (NULL == buf) goto fh_unlock_and_return; if (0 == buf->vb.baddr) goto fh_unlock_and_return; if (buf->vb.state == STATE_QUEUED || buf->vb.state == STATE_ACTIVE) goto fh_unlock_and_return; if (b->flags & V4L2_BUF_FLAG_TOPFIELD) field |= VBUF_FIELD_ODD; if (b->flags & V4L2_BUF_FLAG_BOTFIELD) field |= VBUF_FIELD_EVEN; retval = bttv_prepare_buffer(btv,buf,fh->buf.fmt, fh->buf.vb.width,fh->buf.vb.height,field); if (0 != retval) goto fh_unlock_and_return; list_add_tail(&buf->vb.stream,&fh->stream); if (check_btres(fh, RESOURCE_STREAMING)) bttv_queue_buffer(btv,buf); up(&fh->lock); return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *b = arg; struct bttv_buffer *buf; if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; down(&fh->lock); retval = -EINVAL; if (list_empty(&fh->stream)) goto fh_unlock_and_return; buf = list_entry(fh->stream.next, struct bttv_buffer, vb.stream); retval = videobuf_waiton(&buf->vb,0,1); if (retval < 0) goto fh_unlock_and_return; switch (buf->vb.state) { case STATE_ERROR: retval = -EIO; /* fall through */ case STATE_DONE: videobuf_dma_pci_sync(btv->dev,&buf->vb.dma); buf->vb.state = STATE_IDLE; break; default: retval = -EINVAL; goto fh_unlock_and_return; } list_del(&buf->vb.stream); memset(b,0,sizeof(*b)); videobuf_status(b,&buf->vb); up(&fh->lock); return retval; } case VIDIOC_STREAMON: { struct list_head *list; struct bttv_buffer *buf; if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING)) return -EBUSY; bttv_field_count(btv); down(&fh->lock); list_for_each(list,&fh->stream) { buf = list_entry(list, struct bttv_buffer, vb.stream); if (buf->vb.state == STATE_PREPARED) bttv_queue_buffer(btv,buf); } up(&fh->lock); return 0; } case VIDIOC_STREAMOFF: { down(&fh->lock); retval = -EINVAL; if (!check_btres(fh, RESOURCE_STREAMING)) goto fh_unlock_and_return; bttv_stop_streaming(btv,fh); up(&fh->lock); return 0; } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *c = arg; int i; v4l2_fill_ctrl_category(c); 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; for (i = 0; i < BTTV_CTLS; i++) if (bttv_ctls[i].id == c->id) break; if (i == BTTV_CTLS) { *c = no_ctl; return 0; } *c = bttv_ctls[i]; if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { struct video_audio va; memset(&va,0,sizeof(va)); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (bttv_tvcards[btv->type].audio_hook) bttv_tvcards[btv->type].audio_hook(btv,&va,0); switch (bttv_ctls[i].id) { case V4L2_CID_AUDIO_VOLUME: if (!(va.flags & VIDEO_AUDIO_VOLUME)) *c = no_ctl; break; case V4L2_CID_AUDIO_BALANCE: if (!(va.flags & VIDEO_AUDIO_BALANCE)) *c = no_ctl; break; case V4L2_CID_AUDIO_BASS: if (!(va.flags & VIDEO_AUDIO_BASS)) *c = no_ctl; break; case V4L2_CID_AUDIO_TREBLE: if (!(va.flags & VIDEO_AUDIO_TREBLE)) *c = no_ctl; break; } } return 0; } case VIDIOC_G_CTRL: return get_control(btv,arg); case VIDIOC_S_CTRL: return set_control(btv,arg); case VIDIOC_G_PARM: { struct v4l2_streamparm *parm = arg; struct v4l2_standard s; if (parm->type != V4L2_BUF_TYPE_CAPTURE) return -EINVAL; memset(parm,0,sizeof(*parm)); v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, 0); parm->parm.capture.timeperframe = v4l2_video_std_tpf(&s); return 0; } case VIDIOC_ENUMSTD: case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_ENUMINPUT: case VIDIOC_G_INPUT: case VIDIOC_S_INPUT: case VIDIOC_G_TUNER: case VIDIOC_S_TUNER: case VIDIOC_G_FREQ: case VIDIOC_S_FREQ: return bttv_common_ioctls(btv,cmd,arg);#endif /* HAVE_V4L2 */ default: return -ENOIOCTLCMD; } return 0; fh_unlock_and_return: up(&fh->lock); return retval;}static int bttv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);}/* start capture to a kernel bounce buffer */static int bttv_read_capture(struct bttv_fh *fh){ struct bttv *btv = fh->btv; int rc; dprintk("bttv%d: read start\n",btv->nr); rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt, fh->buf.vb.width,fh->buf.vb.height,0); if (0 != rc) return rc; bttv_queue_buffer(btv,&fh->read_buf); fh->read_off = 0; return 0;}/* * blocking read for a complete video frame * => no kernel bounce buffer needed. */static ssize_tbttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv, char *data,size_t count, loff_t *ppos){ int rc; /* setup stuff */ dprintk("bttv%d: read zerocopy\n",btv->nr); fh->read_buf.vb.baddr = (unsigned long)data; fh->read_buf.vb.bsize = count; rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt, fh->buf.vb.width,fh->buf.vb.height,0); if (0 != rc) goto done; /* start capture & wait */ bttv_queue_buffer(btv,&fh->read_buf); rc = videobuf_waiton(&fh->read_buf.vb,0,1); if (0 == rc) { videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma); rc = fh->read_buf.vb.size; } done: /* cleanup */ bttv_dma_free(btv,&fh->read_buf); fh->read_buf.vb.baddr = 0; fh->read_buf.vb.size = 0; return rc;}static ssize_t bttv_read(struct file *file, char *data, size_t count, loff_t *ppos){ struct bttv_fh *fh = file->private_data; struct bttv *btv = fh->btv; int rc, bytes, size; if (btv->errors) bttv_reinit_bt848(btv); if (locked_btres(btv,RESOURCE_STREAMING)) return -EBUSY; down(&fh->lock); size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3; if (-1 == fh->read_off && count >= size && !(file->f_flags & O_NONBLOCK)) { rc = bttv_read_zerocopy(fh,btv,data,count,ppos); if (rc >= 0) /* ok, all done */ goto unlock_out; /* fallback to kernel bounce buffer on failures */ } if (-1 == fh->read_off) { /* need to capture a new frame */ rc = bttv_read_capture(fh); if (0 != rc) goto unlock_out; } /* wait until capture is done */ rc = videobuf_waiton(&fh->read_buf.vb, file->f_flags & O_NONBLOCK,1); if (0 != rc) goto unlock_
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?