📄 bttv-driver.c
字号:
{ struct bttv *btv=(struct bttv *)dev; unsigned long irq_flags; int need_wait; down(&btv->lock); btv->user--; spin_lock_irqsave(&btv->s_lock, irq_flags); need_wait = (-1 != btv->gq_grab); btv->gq_start = 0; btv->gq_in = 0; btv->gq_out = 0; btv->gq_grab = -1; btv->scr_on = 0; btv->risc_cap_odd = 0; btv->risc_cap_even = 0; bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); /* * A word of warning. At this point the chip * is still capturing because its FIFO hasn't emptied * and the DMA control operations are posted PCI * operations. */ btread(BT848_I2C); /* This fixes the PCI posting delay */ if (need_wait) { /* * This is sucky but right now I can't find a good way to * be sure its safe to free the buffer. We wait 5-6 fields * which is more than sufficient to be sure. */ current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); /* Wait 1/10th of a second */ } /* * We have allowed it to drain. */ if(btv->fbuffer) rvfree((void *) btv->fbuffer, gbuffers*gbufsize); btv->fbuffer=0; up(&btv->lock);}/***********************************//* ioctls and supporting functions *//***********************************/static inline void bt848_bright(struct bttv *btv, uint bright){ btwrite(bright&0xff, BT848_BRIGHT);}static inline void bt848_hue(struct bttv *btv, uint hue){ btwrite(hue&0xff, BT848_HUE);}static inline void bt848_contrast(struct bttv *btv, uint cont){ unsigned int conthi; conthi=(cont>>6)&4; btwrite(cont&0xff, BT848_CONTRAST_LO); btaor(conthi, ~4, BT848_E_CONTROL); btaor(conthi, ~4, BT848_O_CONTROL);}static inline void bt848_sat_u(struct bttv *btv, unsigned long data){ u32 datahi; datahi=(data>>7)&2; btwrite(data&0xff, BT848_SAT_U_LO); btaor(datahi, ~2, BT848_E_CONTROL); btaor(datahi, ~2, BT848_O_CONTROL);}static inline void bt848_sat_v(struct bttv *btv, unsigned long data){ u32 datahi; datahi=(data>>8)&1; btwrite(data&0xff, BT848_SAT_V_LO); btaor(datahi, ~1, BT848_E_CONTROL); btaor(datahi, ~1, BT848_O_CONTROL);}/* * ioctl routine */static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg){ struct bttv *btv=(struct bttv *)dev; unsigned long irq_flags; int ret = 0; if (bttv_debug > 1) printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd); switch (cmd) { case VIDIOCGCAP: { struct video_capability b; strcpy(b.name,btv->video_dev.name); b.type = VID_TYPE_CAPTURE| ((bttv_tvcards[btv->type].tuner != UNSET) ? VID_TYPE_TUNER : 0) | VID_TYPE_OVERLAY| VID_TYPE_CLIPPING| VID_TYPE_FRAMERAM| VID_TYPE_SCALES; b.channels = bttv_tvcards[btv->type].video_inputs; b.audios = bttv_tvcards[btv->type].audio_inputs; b.maxwidth = tvnorms[btv->win.norm].swidth; b.maxheight = tvnorms[btv->win.norm].sheight; b.minwidth = 48; b.minheight = 32; if(copy_to_user(arg,&b,sizeof(b))) return -EFAULT; return 0; } case VIDIOCGCHAN: { struct video_channel v; unsigned int channel; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; channel = v.channel; if (channel>=bttv_tvcards[btv->type].video_inputs) return -EINVAL; v.flags=VIDEO_VC_AUDIO; v.tuners=0; v.type=VIDEO_TYPE_CAMERA; v.norm = btv->win.norm; if(channel==bttv_tvcards[btv->type].tuner) { strcpy(v.name,"Television"); v.flags|=VIDEO_VC_TUNER; v.type=VIDEO_TYPE_TV; v.tuners=1; } else if (channel==bttv_tvcards[btv->type].svhs) strcpy(v.name,"S-Video"); else if (bttv_tvcards[btv->type].muxsel[v.channel] < 0) strcpy(v.name,"Digital Video"); else sprintf(v.name,"Composite%d",v.channel); if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; } /* * Each channel has 1 tuner */ case VIDIOCSCHAN: { struct video_channel v; unsigned int channel; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; channel = v.channel; if (channel>bttv_tvcards[btv->type].video_inputs) return -EINVAL; if (v.norm > TVNORMS) return -EOPNOTSUPP; bttv_call_i2c_clients(btv,cmd,&v); down(&btv->lock); bt848_muxsel(btv, channel); bttv_set_norm(btv, v.norm); up(&btv->lock); return 0; } case VIDIOCGTUNER: { struct video_tuner v; if(copy_from_user(&v,arg,sizeof(v))!=0) return -EFAULT;#if 0 /* tuner.signal might be of intrest for non-tuner sources too ... */ if(v.tuner||btv->channel) /* Only tuner 0 */ return -EINVAL;#endif strcpy(v.name, "Television"); v.rangelow=0; v.rangehigh=0xFFFFFFFF; v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; v.mode = btv->win.norm; v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; bttv_call_i2c_clients(btv,cmd,&v); if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; } /* We have but one tuner */ case VIDIOCSTUNER: { struct video_tuner v; unsigned int tuner; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; tuner = v.tuner; /* Only one channel has a tuner */ if(tuner!=bttv_tvcards[btv->type].tuner) return -EINVAL; if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC &&v.mode!=VIDEO_MODE_SECAM) return -EOPNOTSUPP; bttv_call_i2c_clients(btv,cmd,&v); if (btv->win.norm != v.mode) { down(&btv->lock); bttv_set_norm(btv,v.mode); up(&btv->lock); } return 0; } case VIDIOCGPICT: { struct video_picture p=btv->picture; if(copy_to_user(arg, &p, sizeof(p))) return -EFAULT; return 0; } case VIDIOCSPICT: { struct video_picture p; if (copy_from_user(&p, arg,sizeof(p))) return -EFAULT; if (p.palette > PALETTEFMT_MAX) return -EINVAL; if (UNSET == palette2fmt[p.palette]) return -EINVAL; down(&btv->lock); /* We want -128 to 127 we get 0-65535 */ bt848_bright(btv, (p.brightness>>8)-128); /* 0-511 for the colour */ bt848_sat_u(btv, p.colour>>7); bt848_sat_v(btv, ((p.colour>>7)*201L)/237); /* -128 to 127 */ bt848_hue(btv, (p.hue>>8)-128); /* 0-511 */ bt848_contrast(btv, p.contrast>>7); btv->picture = p; up(&btv->lock); return 0; } case VIDIOCSWIN: { struct video_window vw; struct video_clip *vcp = NULL; if(copy_from_user(&vw,arg,sizeof(vw))) return -EFAULT; down(&btv->lock); if(vw.flags || vw.width < 16 || vw.height < 16) { spin_lock_irqsave(&btv->s_lock, irq_flags); btv->scr_on = 0; bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); up(&btv->lock); return -EINVAL; } if (btv->win.bpp < 4) { /* adjust and align writes */ vw.x = (vw.x + 3) & ~3; vw.width &= ~3; } if (btv->needs_restart) bt848_restart(btv); btv->win.x=vw.x; btv->win.y=vw.y; btv->win.width=vw.width; btv->win.height=vw.height; spin_lock_irqsave(&btv->s_lock, irq_flags); bt848_set_risc_jmps(btv,0); bt848_set_winsize(btv); spin_unlock_irqrestore(&btv->s_lock, irq_flags); /* * Do any clips. */ if(vw.clipcount<0) { if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) { up(&btv->lock); return -ENOMEM; } if(copy_from_user(vcp, vw.clips, VIDEO_CLIPMAP_SIZE)) { up(&btv->lock); vfree(vcp); return -EFAULT; } } else if (vw.clipcount > 2048) { up(&btv->lock); return -EINVAL; } else if (vw.clipcount) { if((vcp=vmalloc(sizeof(struct video_clip)* (vw.clipcount))) == NULL) { up(&btv->lock); return -ENOMEM; } if(copy_from_user(vcp,vw.clips, sizeof(struct video_clip)* vw.clipcount)) { up(&btv->lock); vfree(vcp); return -EFAULT; } } make_clip_tab(btv, vcp, vw.clipcount); if (vw.clipcount != 0) vfree(vcp); spin_lock_irqsave(&btv->s_lock, irq_flags); bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); up(&btv->lock); return 0; } case VIDIOCGWIN: { struct video_window vw; memset(&vw,0,sizeof(vw)); vw.x=btv->win.x; vw.y=btv->win.y; vw.width=btv->win.width; vw.height=btv->win.height; if(btv->win.interlace) vw.flags|=VIDEO_WINDOW_INTERLACE; if(copy_to_user(arg,&vw,sizeof(vw))) return -EFAULT; return 0; } case VIDIOCCAPTURE: { int v; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; if(btv->win.vidadr == 0) return -EINVAL; if (btv->win.width==0 || btv->win.height==0) return -EINVAL; if (1 == no_overlay) return -EIO; spin_lock_irqsave(&btv->s_lock, irq_flags); if (v == 1 && btv->win.vidadr != 0) btv->scr_on = 1; if (v == 0) btv->scr_on = 0; bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); return 0; } case VIDIOCGFBUF: { struct video_buffer v; v.base=(void *)btv->win.vidadr; v.height=btv->win.sheight; v.width=btv->win.swidth; v.depth=btv->win.depth; v.bytesperline=btv->win.bpl; if(copy_to_user(arg, &v,sizeof(v))) return -EFAULT; return 0; } case VIDIOCSFBUF: { struct video_buffer v; if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; if(v.depth!=8 && v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32 && v.width > 16 && v.height > 16 && v.bytesperline > 16) return -EINVAL; down(&btv->lock); if (v.base) btv->win.vidadr=(unsigned long)v.base; btv->win.sheight=v.height; btv->win.swidth=v.width; btv->win.bpp=((v.depth+7)&0x38)/8; btv->win.depth=v.depth; btv->win.bpl=v.bytesperline;#if 0 /* was broken for ages and nobody noticed. Looks like we don't need it any more as everybody explicitly sets the palette using VIDIOCSPICT these days */ /* set sefault color format */ switch (v.depth) { case 8: btv->picture.palette = VIDEO_PALETTE_HI240; break; case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break; case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break; case 24: btv->picture.palette = VIDEO_PALETTE_RGB24; break; case 32: btv->picture.palette = VIDEO_PALETTE_RGB32; break; }#endif if (bttv_debug) printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", v.base, v.width,v.height, btv->win.bpp, btv->win.bpl); spin_lock_irqsave(&btv->s_lock, irq_flags); bt848_set_winsize(btv); spin_unlock_irqrestore(&btv->s_lock, irq_flags); up(&btv->lock); return 0; } case VIDIOCKEY: { /* Will be handled higher up .. */ return 0; } case VIDIOCGFREQ: { unsigned long v=btv->win.freq; if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; } case VIDIOCSFREQ: { unsigned long v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; btv->win.freq=v; bttv_call_i2c_clients(btv,cmd,&v);#if 1 if (btv->radio && btv->has_matchbox) tea5757_set_freq(btv,v);#endif return 0; } case VIDIOCGAUDIO: { struct video_audio v; v=btv->audio_dev; v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); v.flags|=VIDEO_AUDIO_MUTABLE; strcpy(v.name,"TV"); v.mode = VIDEO_SOUND_MONO; bttv_call_i2c_clients(btv,cmd,&v); /* card specific hooks */ if (btv->audio_hook) btv->audio_hook(btv,&v,0); if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; } case VIDIOCSAUDIO: { struct video_audio v; unsigned int n; if(copy_from_user(&v,arg, sizeof(v))) return -EFAULT; n = v.audio; if(n >= bttv_tvcards[btv->type].audio_inputs) return -EINVAL; down(&btv->lock); if(v.flags&VIDEO_AUDIO_MUTE) audio(btv, AUDIO_MUTE); /* bt848_muxsel(btv,v.audio); */ if(!(v.flags&VIDEO_AUDIO_MUTE)) audio(btv, AUDIO_UNMUTE); bttv_call_i2c_clients(btv,cmd,&v); /* card specific hooks */ if (btv->audio_hook) btv->audio_hook(btv,&v,1); btv->audio_dev=v; up(&btv->lock); return 0; } case VIDIOCSYNC: { DECLARE_WAITQUEUE(wait, current); unsigned int i; if(copy_from_user((void *)&i,arg,sizeof(int))) return -EFAULT; if (i >= gbuffers) return -EINVAL; switch (btv->gbuf[i].stat) { case GBUFFER_UNUSED:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -