From e9064d09febdcd05bc998f46cb5138cd5bca6bf7 Mon Sep 17 00:00:00 2001 From: Florian Schirmer Date: Tue, 21 Jan 2003 18:43:34 +0000 Subject: Bt8xx based adapter driver --- linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c | 473 ++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c (limited to 'linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c') diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c new file mode 100644 index 000000000..6eed67fc6 --- /dev/null +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -0,0 +1,473 @@ +/* + * Bt8xx based DVB adapter driver + * + * Copyright (C) 2002,2003 Florian Schirmer + * Copyright (C) 2002 Peter Hettkamp + * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH + * Copyright (C) 1998,1999 Christian Theiss + * + * 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); + +static LIST_HEAD(card_list); +static int debug = 0; + +#define TS_SIZE 188 + +static void dvb_bt8xx_task(unsigned long data) +{ + + struct bt878 *bt = &bt878[0]; + struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; + int rb; + u8 *rp1=0, *rp2=0; + int count=0, count2=0, tp=0; + int i, ii; + int sync; + u8 tsbuf[TS_SIZE]; /* a buffer to make a single TS packet crossing the wraparound */ + /* point of the circular buffer contiguous in memory again */ + + if (!card->active) + return; + + dprintk("pctv debug: tasklet called, data=0x%8.8lx\n",data); +// bt=dvb->bt; + +// if(!dvb->feeding) return; + + spin_lock_irq(&bt->s_lock); + + /* the dma overtook us, or we overtook the dma? */ + if(bt->readbuf==bt->writebuf|| + bt->readbuf==(bt->writebuf+1)%bt->nbuffers) { + spin_unlock_irq(&bt->s_lock); + dprintk("pctv debug: tasklet: no do r=%d, w=%d\n",bt->readbuf, bt->writebuf); + return; + } + + /* are we frame synced? */ + if(((unsigned char *)bt->buffer)[bt->readbuf*bt->framesize+bt->readptr]!=0x47) { + dprintk("pctv debug: tasklet: searching for sync\n"); + sync=TS_SIZE*2; + do { + if(sync--<=0) break; + if(++bt->readptr>=bt->framesize) { + bt->readptr=0; + if(++bt->readbuf>=bt->nbuffers) bt->readbuf=0; + if(bt->readbuf==bt->writebuf|| + bt->readbuf==(bt->writebuf+1)%bt->nbuffers) { + spin_unlock_irq(&bt->s_lock); + printk("pctv debug: tasklet: dma overtook us. r=%d, w=%d\n",bt->readbuf,bt->writebuf); + return; + } + } + if(((u8 *)bt->buffer)[bt->readbuf*bt->framesize+bt->readptr]!=0x47) continue; + /* ok, we have found a 0x47. See whether at TS_SIZE there is another 0x47 */ + if(bt->readptr+TS_SIZEframesize&&((u8 *)bt->buffer)[bt->readbuf*bt->framesize+bt->readptr+TS_SIZE]==0x47) { + sync=0; + break; + } + if(bt->readptr+TS_SIZE>=bt->framesize) { + /* the payload area of a TS packet may contain a 0x47. To be sure we + are not looking at such a 0x47, check if there is another one exactly + TS_SIZE further. But if this crosses a frame boundary, check if the + frame is free to access, and handle buffer wraparound. I did not yet + have the patience to do this :-( + */ + printk("pctv: panic, less than 188 Bytes left and no sync. Program me!\n"); + bt->readptr++; + spin_unlock_irq(&bt->s_lock); + return; + } + } while(sync); + dprintk("pctv tasklet: end of sync search @%d[%x]=%x\n", + bt->readbuf,bt->readptr, + ((u8 *)bt->buffer)[bt->readbuf*bt->framesize+bt->readptr]); + } + + /* now find out how many TS frames are available in the buffer */ + rp1=(u8 *)bt->buffer+(bt->readbuf*bt->framesize+bt->readptr); + if(*rp1!=0x47) { + spin_unlock_irq(&bt->s_lock); + return; + } + + count=bt->writebuf-bt->readbuf; + if(count<0) { + count=bt->nbuffers-bt->readbuf; + count2=bt->writebuf; + } + count=count*bt->framesize-bt->readptr; + count/=TS_SIZE; + if(count2) { + rb=bt->readbuf*bt->framesize+bt->readptr+count*TS_SIZE; + while(rbbuffersize&&tpbuffer)[rb++]; + } + rb=0; + while(tpbuffer)[rb++]; + } + count2=count2*bt->framesize-rb; + count2/=TS_SIZE; + rp2=(u8 *)bt->buffer+rb; + } + + i=bt->readbuf*bt->framesize+bt->readptr+TS_SIZE*(count+count2+(tp!=0)); + dprintk("pctv debug: tasklet: i=%d (%d ts frames)\n",i,(i-bt->readbuf*bt->framesize-bt->readptr)/TS_SIZE); + if(i>=bt->buffersize) i-=bt->buffersize; + + /* update the bt878 read pointers, release the lock */ + ii=bt->readbuf; + bt->readbuf=(i/bt->framesize); + if(bt->readbuf>=bt->nbuffers) { + /* this should not happen any more */ + printk("pctv tasklet: i miscomputed bt->readbuf=0x%x>nbuffers=0x%x\n", + bt->readbuf,bt->nbuffers); + printk("pctv tasklet: readbuf %x frsize %x readptr %x count %x count2 %x\n",ii,bt->framesize,bt->readptr,count,count2); + printk("pctv tasklet: TS_SZ %x span %x i %x res readbuf %x\n",TS_SIZE,(tp!=0),i,bt->readbuf); + bt->readbuf-=bt->nbuffers; + } + bt->readptr=(i%bt->framesize); + if(bt->readptr>=bt->framesize) { + /* this should not happen at all... */ + printk("pctv tasklet: i miscomputed bt->readptr=0x%x>framesize=0x%x\n", + bt->readptr,bt->framesize); + bt->readptr-=bt->framesize; + } + + spin_unlock_irq(&bt->s_lock); + + dprintk("pctv tasklet: count1=%d, count2=%d, %sr=%d[%x]\n",count,count2,tp?"span, ":"",bt->readbuf,bt->readptr); + + /* send the first (or only) part of the buffer */ + if(count>0&&countbuffersize/TS_SIZE&&*rp1==0x47) + dvb_dmx_swfilter_packets(&card->demux,rp2,count2); + else { + if(count>0)dprintk("pctv debug: tasklet: error calling demux, c=%d, *buf=0x%x\n",count,*rp1); + } + + /* if the buffer crosses the buffer-wrap, copy the one TS into a local buffer, + then send it. Copy has been done above. */ + if(tp&&tsbuf[0]==0x47) { + dvb_dmx_swfilter_packets(&card->demux,rp2,count2); + tp=0; + } else { + if(tp)dprintk("pctv debug: tasklet: error calling demux1, tsbuf[0]=0x%x\n",tsbuf[0]); + } + + /* send the third and last part of the buffer */ + if(count2>0&&count2buffersize/TS_SIZE&&*rp2==0x47) + dvb_dmx_swfilter_packets(&card->demux,rp2,count2); + else { + if(count2>0)dprintk("pctv debug: tasklet: error calling demux2, c=%d, *buf=0x%x\n",count2,*rp2); + } + +} + +static DECLARE_TASKLET_DISABLED(dvb_bt8xx_tasklet, dvb_bt8xx_task, 0); + +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; + + printk("dvb_bt8xx: start_feed\n"); + + if (!dvbdmx->dmx.frontend) + return -EINVAL; + + if (card->active) + return 0; + + card->active = 1; + + bt878_start(&bt878[0], (0 << 26) | (0 << 15) | (1 << 14) | (0 << 6) | (1 << 5)); + + 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; + + printk("dvb_bt8xx: stop_feed\n"); + + if (!dvbdmx->dmx.frontend) + return -EINVAL; + + if (!card->active) + return 0; + + bt878_stop(&bt878[0]); + card->active = 0; + + return 0; + +} + +static int __init dvb_bt8xx_load(unsigned int bttv_nr) +{ + + struct dvb_bt8xx_card *card; + int result; + + if (!(card = kmalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) + return -ENOMEM; + + card->bttv_nr = bttv_nr; + + 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, "Bt8xx based DVB adapter")) < 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.vendor = "Connexant"; + card->demux.dmx.model = "Software"; + card->demux.dmx.id = "demux0_0"; + 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.id = "hw_frontend"; + card->fe_hw.vendor = "Connexant"; + card->fe_hw.model = "hw"; + 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.id = "mem_frontend"; + card->fe_mem.vendor = "memory"; + card->fe_mem.model = "sw"; + 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_bt8xx_tasklet.data = (long)card; + bt878[0].tasklet = &dvb_bt8xx_tasklet; + + bt878_get_buffers(&bt878[0], 0xC00, 8, 4, 0); + + 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); + + 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 result; + 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++) { + + if (card_id == 0x01010071) { + + dprintk("dvb_bt8xx: identified card%d as Nebula DigiTV DVB-T card\n", card_nr); + + if ((result = dvb_bt8xx_load(card_nr)) < 0) { + + dvb_bt8xx_close(); + + return result; + + } + + } + + } + + 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"); -- cgit v1.2.3