/* * 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");