summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Schirmer <devnull@localhost>2003-01-21 18:43:34 +0000
committerFlorian Schirmer <devnull@localhost>2003-01-21 18:43:34 +0000
commite9064d09febdcd05bc998f46cb5138cd5bca6bf7 (patch)
treea64939d180e8b466beac58b2c1bb0456381e7f30
parentf5cd62dcaa10542a6ec8b046ac694dabf8a1e63a (diff)
downloadmediapointer-dvb-s2-e9064d09febdcd05bc998f46cb5138cd5bca6bf7.tar.gz
mediapointer-dvb-s2-e9064d09febdcd05bc998f46cb5138cd5bca6bf7.tar.bz2
Bt8xx based adapter driver
-rw-r--r--linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c473
-rw-r--r--linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h40
2 files changed, 513 insertions, 0 deletions
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 <schirmer@taytron.net>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
+ * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
+ *
+ * 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 <asm/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#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_SIZE<bt->framesize&&((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(rb<bt->buffersize&&tp<TS_SIZE) {
+ tsbuf[tp++]=((u8 *)bt->buffer)[rb++];
+ }
+ rb=0;
+ while(tp<TS_SIZE) {
+ tsbuf[tp++]=((u8 *)bt->buffer)[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&&count<bt->buffersize/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&&count2<bt->buffersize/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 <schirmer@taytron.net>");
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h
new file mode 100644
index 000000000..cfae087e9
--- /dev/null
+++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -0,0 +1,40 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <schirmer@taytron.net>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
+ * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
+ *
+ * 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 <linux/i2c.h>
+#include "dvbdev.h"
+
+struct dvb_bt8xx_card {
+
+ struct list_head list;
+ u8 active;
+ struct dvb_adapter *adapter;
+ unsigned int bttv_nr;
+ struct dvb_demux demux;
+ dmxdev_t dmxdev;
+ dmx_frontend_t fe_hw;
+ dmx_frontend_t fe_mem;
+ struct i2c_adapter *i2c_adap;
+
+};