/* * Bt8xx based DVB adapter driver * * Copyright (C) 2002,2003 Florian Schirmer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include "dmxdev.h" #include "dvbdev.h" #include "dvb_demux.h" #include "dvb_frontend.h" #include "dvb-bt8xx.h" #include "bt878.h" #define dprintk if (debug) printk extern int bttv_get_cardinfo(unsigned int card, int *type, int *cardid); extern struct i2c_adapter *bttv_get_i2c_adap(unsigned int card); extern struct pci_dev* bttv_get_pcidev(unsigned int card); static LIST_HEAD(card_list); static int debug = 0; static void dvb_bt8xx_task(unsigned long data) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; //printk("%d ", finished_block); while (card->bt->last_block != card->bt->finished_block) { dvb_dmx_swfilter(&card->demux, &card->bt->buf_cpu[card->bt->last_block * card->bt->block_bytes], card->bt->block_bytes); card->bt->last_block = (card->bt->last_block + 1) % card->bt->block_count; } } static int dvb_bt8xx_master_xfer(struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[], int num) { struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)i2c->data; return card->i2c_adap->algo->master_xfer(card->i2c_adap, (struct i2c_msg *)msgs, num); } static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct dvb_bt8xx_card *card = dvbdmx->priv; dprintk("dvb_bt8xx: start_feed\n"); if (!dvbdmx->dmx.frontend) return -EINVAL; if (card->active) return 0; card->active = 1; // bt878_start(card->bt, card->gpio_mode); return 0; } static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct dvb_bt8xx_card *card = dvbdmx->priv; dprintk("dvb_bt8xx: stop_feed\n"); if (!dvbdmx->dmx.frontend) return -EINVAL; if (!card->active) return 0; // bt878_stop(card->bt); card->active = 0; return 0; } static struct bt878 *dvb_bt8xx_get_bt(unsigned int bttv_nr) { struct pci_dev* bttv_pci_dev; unsigned int card_nr; if (!(bttv_pci_dev = bttv_get_pcidev(bttv_nr))) return NULL; for (card_nr = 0; card_nr < bt878_num; card_nr++) { if ((bt878[card_nr].dev->subsystem_vendor == bttv_pci_dev->subsystem_vendor) && (bt878[card_nr].dev->subsystem_device == bttv_pci_dev->subsystem_device) && (bt878[card_nr].dev->bus->number == bttv_pci_dev->bus->number) && (PCI_SLOT(bt878[card_nr].dev->devfn) == PCI_SLOT(bttv_pci_dev->devfn))) return &bt878[card_nr]; } return NULL; } static int __init dvb_bt8xx_load(unsigned int bttv_nr, char *card_name, u32 gpio_mode) { struct dvb_bt8xx_card *card; int result; dprintk("dvb_bt8xx: identified card%d as %s\n", bttv_nr, card_name); if (!(card = kmalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) return -ENOMEM; card->bttv_nr = bttv_nr; if (!(card->bt = dvb_bt8xx_get_bt(card->bttv_nr))) { printk("dvb_bt8xx: unable to determine DMA core of card%d\n", card->bttv_nr); return -EFAULT; } card->gpio_mode = gpio_mode; if (!(card->i2c_adap = bttv_get_i2c_adap(card->bttv_nr))) { printk("dvb_bt8xx: unable to determine i2c adapter of card%d\n", card->bttv_nr); kfree(card); return -EFAULT; } if ((result = dvb_register_adapter(&card->adapter, card_name)) < 0) { printk("dvb_bt8xx: dvb_register_adapter failed (errno = %d)\n", result); kfree(card); return result; } if (!(dvb_register_i2c_bus(dvb_bt8xx_master_xfer, card, card->adapter, 0))) { printk("dvb_bt8xx: dvb_register_i2c_bus of card%d failed\n", card->bttv_nr); dvb_unregister_adapter(card->adapter); kfree(card); return -EFAULT; } memset(&card->demux, 0, sizeof(struct dvb_demux)); card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; card->demux.priv = card; card->demux.filternum = 256; card->demux.feednum = 256; card->demux.start_feed = dvb_bt8xx_start_feed; card->demux.stop_feed = dvb_bt8xx_stop_feed; card->demux.write_to_decoder = NULL; if ((result = dvb_dmx_init(&card->demux)) < 0) { printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); return result; } card->dmxdev.filternum = 256; card->dmxdev.demux = &card->demux.dmx; card->dmxdev.capabilities = 0; if ((result = dvb_dmxdev_init(&card->dmxdev, card->adapter)) < 0) { printk("dvb_bt8xx: dvb_dmxdev_init failed (errno = %d)\n", result); dvb_dmx_release(&card->demux); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); return result; } card->fe_hw.source = DMX_FRONTEND_0; if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw)) < 0) { printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); return result; } card->fe_mem.source = DMX_MEMORY_FE; if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem)) < 0) { printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); return result; } if ((result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw)) < 0) { printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); return result; } dvb_net_init(card->adapter, &card->dvbnet, &card->demux.dmx); tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); bt878_start(card->bt, card->gpio_mode); list_add_tail(&card->list, &card_list); return 0; } static void dvb_bt8xx_close(void) { struct dvb_bt8xx_card *card; struct list_head *entry, *entry_safe; list_for_each_safe(entry, entry_safe, &card_list) { card = list_entry(entry, struct dvb_bt8xx_card, list); dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); bt878_stop(card->bt); tasklet_kill(&card->bt->tasklet); dvb_net_release(&card->dvbnet); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); dvb_dmxdev_release(&card->dmxdev); dvb_dmx_release(&card->demux); dvb_unregister_i2c_bus(dvb_bt8xx_master_xfer, card->adapter, 0); dvb_unregister_adapter(card->adapter); kfree(card); list_del(&card->list); } } static int __init dvb_bt8xx_init(void) { int card_id; unsigned int card_nr; int card_type; dprintk("dvb_bt8xx: enumerating availible bttv cards...\n"); for (card_nr = 0; !bttv_get_cardinfo(card_nr, &card_type, &card_id); card_nr++) { switch(card_id) { case 0x001C11BD: dvb_bt8xx_load(card_nr, "Pinnacle PCTV DVB-S", 0x0400C060); break; case 0x01010071: dvb_bt8xx_load(card_nr, "Nebula DigiTV DVB-T", (1 << 26) | (1 << 14) | (1 << 5)); break; } } return 0; } static void __exit dvb_bt8xx_exit(void) { dvb_bt8xx_close(); } module_init(dvb_bt8xx_init); module_exit(dvb_bt8xx_exit); MODULE_DESCRIPTION("Bt8xx based DVB adapter driver"); MODULE_AUTHOR("Florian Schirmer "); MODULE_LICENSE("GPL"); MODULE_PARM(debug, "i");