📄 bttv-driver.c
字号:
} case BTTV_VERSION: { return BTTV_VERSION_CODE; } case BTTV_PICNR: { /* return picture;*/ return 0; } default: return -ENOIOCTLCMD; } return 0;}/* * This maps the vmalloced and reserved fbuffer to user space. * * FIXME: * - PAGE_READONLY should suffice!? * - remap_page_range is kind of inefficient for page by page remapping. * But e.g. pte_alloc() does not work in modules ... :-( */static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size){ unsigned long start=(unsigned long) adr; unsigned long page,pos; if (size>gbuffers*gbufsize) return -EINVAL; if (!btv->fbuffer) { if(fbuffer_alloc(btv)) return -EINVAL; } pos=(unsigned long) btv->fbuffer; while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start+=PAGE_SIZE; pos+=PAGE_SIZE; size-=PAGE_SIZE; } return 0;}static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size){ struct bttv *btv=(struct bttv *)dev; int r; down(&btv->lock); r=do_bttv_mmap(btv, adr, size); up(&btv->lock); return r;}static struct video_device bttv_template={ owner: THIS_MODULE, name: "UNSET", type: VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT, hardware: VID_HARDWARE_BT848, open: bttv_open, close: bttv_close, read: bttv_read, write: bttv_write, ioctl: bttv_ioctl, mmap: bttv_mmap, minor: -1,};static long vbi_read(struct video_device *v, char *buf, unsigned long count, int nonblock){ struct bttv *btv=(struct bttv *)(v-2); int q,todo; DECLARE_WAITQUEUE(wait, current); todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) { if (btv->needs_restart) { down(&btv->lock); bt848_restart(btv); up(&btv->lock); } if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) return -EFAULT; todo-=q; buf+=q; add_wait_queue(&btv->vbiq, &wait); current->state = TASK_INTERRUPTIBLE; if (todo && q==VBIBUF_SIZE-btv->vbip) { if(nonblock) { remove_wait_queue(&btv->vbiq, &wait); current->state = TASK_RUNNING; if(count==todo) return -EWOULDBLOCK; return count-todo; } schedule(); if(signal_pending(current)) { remove_wait_queue(&btv->vbiq, &wait); current->state = TASK_RUNNING; if(todo==count) return -EINTR; else return count-todo; } } remove_wait_queue(&btv->vbiq, &wait); current->state = TASK_RUNNING; } if (todo) { if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) return -EFAULT; btv->vbip+=todo; } return count;}static unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait){ struct bttv *btv=(struct bttv *)(dev-2); unsigned int mask = 0; poll_wait(file, &btv->vbiq, wait); if (btv->vbip < VBIBUF_SIZE) mask |= (POLLIN | POLLRDNORM); return mask;}static int vbi_open(struct video_device *dev, int flags){ struct bttv *btv=(struct bttv *)(dev-2); unsigned long irq_flags; down(&btv->lock); if (btv->needs_restart) bt848_restart(btv); set_pll(btv); btv->vbip=VBIBUF_SIZE; spin_lock_irqsave(&btv->s_lock, irq_flags); btv->vbi_on = 1; bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags); up(&btv->lock); return 0; }static void vbi_close(struct video_device *dev){ struct bttv *btv=(struct bttv *)(dev-2); unsigned long irq_flags; spin_lock_irqsave(&btv->s_lock, irq_flags); btv->vbi_on = 0; bt848_set_risc_jmps(btv,-1); spin_unlock_irqrestore(&btv->s_lock, irq_flags);}static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg){ struct bttv *btv=(struct bttv *)(dev-2); switch (cmd) { case VIDIOCGCAP: { struct video_capability b; strcpy(b.name,btv->vbi_dev.name); b.type = ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | VID_TYPE_TELETEXT; b.channels = 0; b.audios = 0; b.maxwidth = 0; b.maxheight = 0; b.minwidth = 0; b.minheight = 0; if(copy_to_user(arg,&b,sizeof(b))) return -EFAULT; return 0; } case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGTUNER: case VIDIOCSTUNER: case VIDIOCGCHAN: case VIDIOCSCHAN: case BTTV_VERSION: return bttv_ioctl(dev-2,cmd,arg); case BTTV_VBISIZE: /* make alevt happy :-) */ return VBIBUF_SIZE; default: return -EINVAL; }}static struct video_device vbi_template={ owner: THIS_MODULE, name: "bttv vbi", type: VID_TYPE_CAPTURE|VID_TYPE_TELETEXT, hardware: VID_HARDWARE_BT848, open: vbi_open, close: vbi_close, read: vbi_read, write: bttv_write, poll: vbi_poll, ioctl: vbi_ioctl, minor: -1,};static int radio_open(struct video_device *dev, int flags){ struct bttv *btv = (struct bttv *)(dev-1); unsigned long v; down(&btv->lock); if (btv->user) goto busy_unlock; btv->user++; btv->radio = 1; v = 400*16; bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v); bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); bt848_muxsel(btv,0); up(&btv->lock); return 0; busy_unlock: up(&btv->lock); return -EBUSY;}static void radio_close(struct video_device *dev){ struct bttv *btv=(struct bttv *)(dev-1); down(&btv->lock); btv->user--; btv->radio = 0; up(&btv->lock);}static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock){ return -EINVAL;}static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg){ struct bttv *btv=(struct bttv *)(dev-1); switch (cmd) { case VIDIOCGCAP: { struct video_capability v; strcpy(v.name,btv->video_dev.name); v.type = VID_TYPE_TUNER; v.channels = 1; v.audios = 1; /* No we don't do pictures */ v.maxwidth = 0; v.maxheight = 0; v.minwidth = 0; v.minheight = 0; if (copy_to_user(arg, &v, sizeof(v))) return -EFAULT; return 0; break; } case VIDIOCGTUNER: { struct video_tuner v; if(copy_from_user(&v,arg,sizeof(v))!=0) return -EFAULT; if(v.tuner||btv->channel) /* Only tuner 0 */ return -EINVAL; strcpy(v.name, "Radio"); /* japan: 76.0 MHz - 89.9 MHz western europe: 87.5 MHz - 108.0 MHz russia: 65.0 MHz - 108.0 MHz */ v.rangelow=(int)(65*16); v.rangehigh=(int)(108*16); v.flags= 0; /* XXX */ v.mode = 0; /* XXX */ bttv_call_i2c_clients(btv,cmd,&v); if(copy_to_user(arg,&v,sizeof(v))) return -EFAULT; return 0; } case VIDIOCSTUNER: { struct video_tuner v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; /* Only channel 0 has a tuner */ if(v.tuner!=0 || btv->channel) return -EINVAL; /* XXX anything to do ??? */ return 0; } case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: bttv_ioctl((struct video_device *)btv,cmd,arg); break; default: return -ENOIOCTLCMD; } return 0;}static struct video_device radio_template={ owner: THIS_MODULE, name: "bttv radio", type: VID_TYPE_TUNER, hardware: VID_HARDWARE_BT848, open: radio_open, close: radio_close, read: radio_read, /* just returns -EINVAL */ write: bttv_write, /* just returns -EINVAL */ ioctl: radio_ioctl, minor: -1,};static void bt848_set_risc_jmps(struct bttv *btv, int flags){ if (-1 == flags) { /* defaults */ flags = 0; if (btv->scr_on) flags |= 0x03; if (btv->vbi_on) flags |= 0x0c; } if (bttv_debug > 1) printk("bttv%d: set_risc_jmp %08lx:", btv->nr,virt_to_bus(btv->risc_jmp)); /* Sync to start of odd field */ btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC |BT848_FIFO_STATUS_VRE); btv->risc_jmp[1]=cpu_to_le32(0); /* Jump to odd vbi sub */ btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20)); if (flags&8) { if (bttv_debug > 1) printk(" ev=%08lx",virt_to_bus(btv->vbi_odd)); btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd)); } else { if (bttv_debug > 1) printk(" -----------"); btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4)); } /* Jump to odd sub */ btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20)); if (0 != btv->risc_cap_odd) { if (bttv_debug > 1) printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd); flags |= 3; btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd); } else if ((flags&2) && (!btv->win.interlace || 0 == btv->risc_cap_even)) { if (bttv_debug > 1) printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd)); btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd)); } else { if (bttv_debug > 1) printk(" -----------"); btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_jmp+6)); } /* Sync to start of even field */ btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC |BT848_FIFO_STATUS_VRO); btv->risc_jmp[7]=cpu_to_le32(0); /* Jump to even vbi sub */ btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); if (flags&4) { if (bttv_debug > 1) printk(" ov=%08lx",virt_to_bus(btv->vbi_even)); btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even)); } else { if (bttv_debug > 1) printk(" -----------"); btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->risc_jmp+10)); } /* Jump to even sub */ btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20)); if (0 != btv->risc_cap_even) { if (bttv_debug > 1) printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even); flags |= 3; btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even); } else if ((flags&1) && btv->win.interlace) { if (bttv_debug > 1) printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even)); btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even)); } else { if (bttv_debug > 1) printk(" -----------"); btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12)); } if (btv->gq_start) { btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); } else { btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); } btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp)); /* enable cpaturing and DMA */ if (bttv_debug > 1) printk(" flags=0x%x dma=%s\n", flags,(flags&0x0f) ? "on" : "off"); btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); else bt848_dma(btv, 0);}# define do_video_register(dev,type,nr) video_register_device(dev,type,nr)static int __devinit init_video_dev(struct bttv *btv){ audio(btv, AUDIO_MUTE, 1); if(do_video_register(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) return -1; if(do_video_register(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) { video_unregister_device(&btv->video_dev); return -1; } if (btv->has_radio) { if(do_video_register(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0) { video_unregister_device(&btv->vbi_dev); video_unregister_device(&btv->video_dev); return -1; } } return 1;}static int __devinit init_bt848(struct bttv *btv){ int j; unsigned long irq_flags; btv->user=0; init_MUTEX(&btv->lock); /* dump current state of the gpio registers before changing them, * might help to make a new card work */ if (bttv_gpio) bttv_gpio_tracking(btv,"init #1"); /* reset the bt848 */ btwrite(0, BT848_SRESET); DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem)); /* not registered yet */ btv->video_dev.minor = -1; btv->radio_dev.minor = -1; btv->vbi_dev.minor = -1; /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */ btv->win.norm=0; /* change this to 1 for NTSC,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -