Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig
new file mode 100644
index 0000000..a9a7b34
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Kconfig
@@ -0,0 +1,11 @@
+config DVB_CORE
+	tristate "DVB Core Support"
+	depends on DVB
+	select CRC32
+	help
+	  DVB core utility functions for device handling, software fallbacks etc.
+	  Say Y when you have a DVB card and want to use it. Say Y if your want
+	  to build your drivers outside the kernel, but need the DVB core. All 
+	  in-kernel drivers will select this automatically if needed.
+	  If unsure say N.
+
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
new file mode 100644
index 0000000..c6baac2
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
+	        dvb_ca_en50221.o dvb_frontend.o \
+		dvb_net.o dvb_ringbuffer.o
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
new file mode 100644
index 0000000..fb55eaa
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -0,0 +1,301 @@
+/*
+ * demux.h
+ *
+ * Copyright (c) 2002 Convergence GmbH
+ *
+ * based on code:
+ * Copyright (c) 2000 Nokia Research Center
+ *                    Tampere, FINLAND
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef __DEMUX_H
+#define __DEMUX_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/time.h>
+
+/*--------------------------------------------------------------------------*/
+/* Common definitions */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter.
+ */
+
+#ifndef DMX_MAX_FILTER_SIZE
+#define DMX_MAX_FILTER_SIZE 18
+#endif
+
+/*
+ * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter.
+ */
+
+#ifndef DMX_MAX_SECFEED_SIZE
+#define DMX_MAX_SECFEED_SIZE 4096
+#endif
+
+
+/*
+ * enum dmx_success: Success codes for the Demux Callback API.
+ */
+
+enum dmx_success {
+  DMX_OK = 0, /* Received Ok */
+  DMX_LENGTH_ERROR, /* Incorrect length */
+  DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
+  DMX_CRC_ERROR, /* Incorrect CRC */
+  DMX_FRAME_ERROR, /* Frame alignment error */
+  DMX_FIFO_ERROR, /* Receiver FIFO overrun */
+  DMX_MISSED_ERROR /* Receiver missed packet */
+} ;
+
+/*--------------------------------------------------------------------------*/
+/* TS packet reception */
+/*--------------------------------------------------------------------------*/
+
+/* TS filter type for set() */
+
+#define TS_PACKET       1   /* send TS packets (188 bytes) to callback (default) */
+#define	TS_PAYLOAD_ONLY 2   /* in case TS_PACKET is set, only send the TS
+			       payload (<=184 bytes per packet) to callback */
+#define TS_DECODER      4   /* send stream to built-in decoder (if present) */
+
+/* PES type for filters which write to built-in decoder */
+/* these should be kept identical to the types in dmx.h */
+
+enum dmx_ts_pes
+{  /* also send packets to decoder (if it exists) */
+        DMX_TS_PES_AUDIO0,
+	DMX_TS_PES_VIDEO0,
+	DMX_TS_PES_TELETEXT0,
+	DMX_TS_PES_SUBTITLE0,
+	DMX_TS_PES_PCR0,
+
+        DMX_TS_PES_AUDIO1,
+	DMX_TS_PES_VIDEO1,
+	DMX_TS_PES_TELETEXT1,
+	DMX_TS_PES_SUBTITLE1,
+	DMX_TS_PES_PCR1,
+
+        DMX_TS_PES_AUDIO2,
+	DMX_TS_PES_VIDEO2,
+	DMX_TS_PES_TELETEXT2,
+	DMX_TS_PES_SUBTITLE2,
+	DMX_TS_PES_PCR2,
+
+        DMX_TS_PES_AUDIO3,
+	DMX_TS_PES_VIDEO3,
+	DMX_TS_PES_TELETEXT3,
+	DMX_TS_PES_SUBTITLE3,
+	DMX_TS_PES_PCR3,
+
+	DMX_TS_PES_OTHER
+};
+
+#define DMX_TS_PES_AUDIO    DMX_TS_PES_AUDIO0
+#define DMX_TS_PES_VIDEO    DMX_TS_PES_VIDEO0
+#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0
+#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0
+#define DMX_TS_PES_PCR      DMX_TS_PES_PCR0
+
+
+struct dmx_ts_feed {
+        int is_filtering; /* Set to non-zero when filtering in progress */
+        struct dmx_demux *parent; /* Back-pointer */
+        void *priv; /* Pointer to private data of the API client */
+        int (*set) (struct dmx_ts_feed *feed,
+		    u16 pid,
+		    int type,
+		    enum dmx_ts_pes pes_type,
+		    size_t callback_length,
+		    size_t circular_buffer_size,
+		    int descramble,
+		    struct timespec timeout);
+        int (*start_filtering) (struct dmx_ts_feed* feed);
+        int (*stop_filtering) (struct dmx_ts_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Section reception */
+/*--------------------------------------------------------------------------*/
+
+struct dmx_section_filter {
+        u8 filter_value [DMX_MAX_FILTER_SIZE];
+        u8 filter_mask [DMX_MAX_FILTER_SIZE];
+        u8 filter_mode [DMX_MAX_FILTER_SIZE];
+        struct dmx_section_feed* parent; /* Back-pointer */
+        void* priv; /* Pointer to private data of the API client */
+};
+
+struct dmx_section_feed {
+        int is_filtering; /* Set to non-zero when filtering in progress */
+        struct dmx_demux* parent; /* Back-pointer */
+        void* priv; /* Pointer to private data of the API client */
+
+        int check_crc;
+	u32 crc_val;
+
+        u8 *secbuf;
+        u8 secbuf_base[DMX_MAX_SECFEED_SIZE];
+        u16 secbufp, seclen, tsfeedp;
+
+        int (*set) (struct dmx_section_feed* feed,
+		    u16 pid,
+		    size_t circular_buffer_size,
+		    int descramble,
+		    int check_crc);
+        int (*allocate_filter) (struct dmx_section_feed* feed,
+				struct dmx_section_filter** filter);
+        int (*release_filter) (struct dmx_section_feed* feed,
+			       struct dmx_section_filter* filter);
+        int (*start_filtering) (struct dmx_section_feed* feed);
+        int (*stop_filtering) (struct dmx_section_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Callback functions */
+/*--------------------------------------------------------------------------*/
+
+typedef int (*dmx_ts_cb) ( const u8 * buffer1,
+			   size_t buffer1_length,
+			   const u8 * buffer2,
+			   size_t buffer2_length,
+			   struct dmx_ts_feed* source,
+			   enum dmx_success success);
+
+typedef int (*dmx_section_cb) (	const u8 * buffer1,
+				size_t buffer1_len,
+				const u8 * buffer2,
+				size_t buffer2_len,
+				struct dmx_section_filter * source,
+				enum dmx_success success);
+
+/*--------------------------------------------------------------------------*/
+/* DVB Front-End */
+/*--------------------------------------------------------------------------*/
+
+enum dmx_frontend_source {
+	DMX_MEMORY_FE,
+	DMX_FRONTEND_0,
+	DMX_FRONTEND_1,
+	DMX_FRONTEND_2,
+	DMX_FRONTEND_3,
+	DMX_STREAM_0,    /* external stream input, e.g. LVDS */
+	DMX_STREAM_1,
+	DMX_STREAM_2,
+	DMX_STREAM_3
+};
+
+struct dmx_frontend {
+        struct list_head connectivity_list; /* List of front-ends that can
+					       be connected to a particular
+					       demux */
+        void* priv;     /* Pointer to private data of the API client */
+        enum dmx_frontend_source source;
+};
+
+/*--------------------------------------------------------------------------*/
+/* MPEG-2 TS Demux */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Flags OR'ed in the capabilites field of struct dmx_demux.
+ */
+
+#define DMX_TS_FILTERING                        1
+#define DMX_PES_FILTERING                       2
+#define DMX_SECTION_FILTERING                   4
+#define DMX_MEMORY_BASED_FILTERING              8    /* write() available */
+#define DMX_CRC_CHECKING                        16
+#define DMX_TS_DESCRAMBLING                     32
+#define DMX_SECTION_PAYLOAD_DESCRAMBLING        64
+#define DMX_MAC_ADDRESS_DESCRAMBLING            128
+
+/*
+ * Demux resource type identifier.
+*/
+
+/*
+ * DMX_FE_ENTRY(): Casts elements in the list of registered
+ * front-ends from the generic type struct list_head
+ * to the type * struct dmx_frontend
+ *.
+*/
+
+#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list)
+
+struct dmx_demux {
+        u32 capabilities;            /* Bitfield of capability flags */
+        struct dmx_frontend* frontend;    /* Front-end connected to the demux */
+        struct list_head reg_list;   /* List of registered demuxes */
+        void* priv;                  /* Pointer to private data of the API client */
+        int users;                   /* Number of users */
+        int (*open) (struct dmx_demux* demux);
+        int (*close) (struct dmx_demux* demux);
+        int (*write) (struct dmx_demux* demux, const char* buf, size_t count);
+        int (*allocate_ts_feed) (struct dmx_demux* demux,
+				 struct dmx_ts_feed** feed,
+				 dmx_ts_cb callback);
+        int (*release_ts_feed) (struct dmx_demux* demux,
+				struct dmx_ts_feed* feed);
+        int (*allocate_section_feed) (struct dmx_demux* demux,
+				      struct dmx_section_feed** feed,
+				      dmx_section_cb callback);
+        int (*release_section_feed) (struct dmx_demux* demux,
+				     struct dmx_section_feed* feed);
+        int (*descramble_mac_address) (struct dmx_demux* demux,
+				       u8* buffer1,
+				       size_t buffer1_length,
+				       u8* buffer2,
+				       size_t buffer2_length,
+				       u16 pid);
+        int (*descramble_section_payload) (struct dmx_demux* demux,
+					   u8* buffer1,
+					   size_t buffer1_length,
+					   u8* buffer2, size_t buffer2_length,
+					   u16 pid);
+        int (*add_frontend) (struct dmx_demux* demux,
+			     struct dmx_frontend* frontend);
+        int (*remove_frontend) (struct dmx_demux* demux,
+				struct dmx_frontend* frontend);
+        struct list_head* (*get_frontends) (struct dmx_demux* demux);
+        int (*connect_frontend) (struct dmx_demux* demux,
+				 struct dmx_frontend* frontend);
+        int (*disconnect_frontend) (struct dmx_demux* demux);
+
+        int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids);
+
+        int (*get_stc) (struct dmx_demux* demux, unsigned int num,
+			u64 *stc, unsigned int *base);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Demux directory */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_DIR_ENTRY(): Casts elements in the list of registered
+ * demuxes from the generic type struct list_head* to the type struct dmx_demux
+ *.
+ */
+
+#define DMX_DIR_ENTRY(list) list_entry(list, struct dmx_demux, reg_list)
+
+#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
new file mode 100644
index 0000000..1863f1d
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -0,0 +1,1137 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
+ *		  & Marcus Metzler <marcus@convergence.de>
+		      for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include "dmxdev.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk	if (debug) printk
+
+static inline struct dmxdev_filter *
+dvb_dmxdev_file_to_filter(struct file *file)
+{
+	return (struct dmxdev_filter *) file->private_data;
+}
+
+static inline void dvb_dmxdev_buffer_init(struct dmxdev_buffer *buffer)
+{
+	buffer->data=NULL;
+	buffer->size=8192;
+	buffer->pread=0;
+	buffer->pwrite=0;
+	buffer->error=0;
+	init_waitqueue_head(&buffer->queue);
+}
+
+static inline int dvb_dmxdev_buffer_write(struct dmxdev_buffer *buf, const u8 *src, int len)
+{
+	int split;
+	int free;
+	int todo;
+
+	if (!len)
+		return 0;
+	if (!buf->data)
+		return 0;
+
+	free=buf->pread-buf->pwrite;
+	split=0;
+	if (free<=0) {
+		free+=buf->size;
+		split=buf->size-buf->pwrite;
+	}
+	if (len>=free) {
+		dprintk("dmxdev: buffer overflow\n");
+		return -1;
+	}
+	if (split>=len)
+		split=0;
+	todo=len;
+	if (split) {
+		memcpy(buf->data + buf->pwrite, src, split);
+		todo-=split;
+		buf->pwrite=0;
+	}
+	memcpy(buf->data + buf->pwrite, src+split, todo);
+	buf->pwrite=(buf->pwrite+todo)%buf->size;
+	return len;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_buffer *src,
+		int non_blocking, char __user *buf, size_t count, loff_t *ppos)
+{
+	unsigned long todo=count;
+	int split, avail, error;
+
+	if (!src->data)
+		return 0;
+
+	if ((error=src->error)) {
+		src->pwrite=src->pread;
+		src->error=0;
+		return error;
+	}
+
+	if (non_blocking && (src->pwrite==src->pread))
+		return -EWOULDBLOCK;
+
+	while (todo>0) {
+		if (non_blocking && (src->pwrite==src->pread))
+			return (count-todo) ? (count-todo) : -EWOULDBLOCK;
+
+		if (wait_event_interruptible(src->queue,
+					     (src->pread!=src->pwrite) ||
+					     (src->error))<0)
+			return count-todo;
+
+		if ((error=src->error)) {
+			src->pwrite=src->pread;
+			src->error=0;
+			return error;
+		}
+
+		split=src->size;
+		avail=src->pwrite - src->pread;
+		if (avail<0) {
+			avail+=src->size;
+			split=src->size - src->pread;
+		}
+		if (avail>todo)
+			avail=todo;
+		if (split<avail) {
+			if (copy_to_user(buf, src->data+src->pread, split))
+				  return -EFAULT;
+			buf+=split;
+			src->pread=0;
+			todo-=split;
+			avail-=split;
+		}
+		if (avail) {
+			if (copy_to_user(buf, src->data+src->pread, avail))
+				return -EFAULT;
+			src->pread = (src->pread + avail) % src->size;
+			todo-=avail;
+			buf+=avail;
+		}
+	}
+	return count;
+}
+
+static struct dmx_frontend * get_fe(struct dmx_demux *demux, int type)
+{
+	struct list_head *head, *pos;
+
+	head=demux->get_frontends(demux);
+	if (!head)
+		return NULL;
+	list_for_each(pos, head)
+		if (DMX_FE_ENTRY(pos)->source==type)
+			return DMX_FE_ENTRY(pos);
+
+	return NULL;
+}
+
+static inline void dvb_dmxdev_dvr_state_set(struct dmxdev_dvr *dmxdevdvr, int state)
+{
+	spin_lock_irq(&dmxdevdvr->dev->lock);
+	dmxdevdvr->state=state;
+	spin_unlock_irq(&dmxdevdvr->dev->lock);
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	struct dmx_frontend *front;
+
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags&O_ACCMODE)==O_RDWR) {
+		if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) {
+			up(&dmxdev->mutex);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+	      dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+	      dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE;
+	      dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE);
+	      if (!dmxdev->dvr_buffer.data) {
+		      up(&dmxdev->mutex);
+		      return -ENOMEM;
+	      }
+	}
+
+	if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+		dmxdev->dvr_orig_fe=dmxdev->demux->frontend;
+
+		if (!dmxdev->demux->write) {
+			up(&dmxdev->mutex);
+			return -EOPNOTSUPP;
+		}
+
+		front=get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+		if (!front) {
+			up(&dmxdev->mutex);
+			return -EINVAL;
+		}
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux, front);
+	}
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux,
+						dmxdev->dvr_orig_fe);
+	}
+	if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+		if (dmxdev->dvr_buffer.data) {
+			void *mem=dmxdev->dvr_buffer.data;
+			mb();
+			spin_lock_irq(&dmxdev->lock);
+			dmxdev->dvr_buffer.data=NULL;
+			spin_unlock_irq(&dmxdev->lock);
+			vfree(mem);
+		}
+	}
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int ret;
+
+	if (!dmxdev->demux->write)
+		return -EOPNOTSUPP;
+	if ((file->f_flags&O_ACCMODE)!=O_WRONLY)
+		return -EINVAL;
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+	ret=dmxdev->demux->write(dmxdev->demux, buf, count);
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+		loff_t *ppos)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int ret;
+
+	//down(&dmxdev->mutex);
+	ret= dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+			      file->f_flags&O_NONBLOCK,
+			      buf, count, ppos);
+	//up(&dmxdev->mutex);
+	return ret;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state)
+{
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dmxdevfilter->state=state;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, unsigned long size)
+{
+	struct dmxdev_buffer *buf=&dmxdevfilter->buffer;
+	void *mem;
+
+	if (buf->size==size)
+		return 0;
+	if (dmxdevfilter->state>=DMXDEV_STATE_GO)
+		return -EBUSY;
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	mem=buf->data;
+	buf->data=NULL;
+	buf->size=size;
+	buf->pwrite=buf->pread=0;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+	vfree(mem);
+
+	if (buf->size) {
+		mem=vmalloc(dmxdevfilter->buffer.size);
+		if (!mem)
+			return -ENOMEM;
+		spin_lock_irq(&dmxdevfilter->dev->lock);
+		buf->data=mem;
+		spin_unlock_irq(&dmxdevfilter->dev->lock);
+	}
+	return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(unsigned long data)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *)data;
+
+	dmxdevfilter->buffer.error=-ETIMEDOUT;
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+	wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+	struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
+
+	del_timer(&dmxdevfilter->timer);
+	if (para->timeout) {
+		dmxdevfilter->timer.function=dvb_dmxdev_filter_timeout;
+		dmxdevfilter->timer.data=(unsigned long) dmxdevfilter;
+		dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000;
+		add_timer(&dmxdevfilter->timer);
+	}
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+			    const u8 *buffer2, size_t buffer2_len,
+			    struct dmx_section_filter *filter, enum dmx_success success)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) filter->priv;
+	int ret;
+
+	if (dmxdevfilter->buffer.error) {
+		wake_up(&dmxdevfilter->buffer.queue);
+		return 0;
+	}
+	spin_lock(&dmxdevfilter->dev->lock);
+	if (dmxdevfilter->state!=DMXDEV_STATE_GO) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+	del_timer(&dmxdevfilter->timer);
+	dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
+		buffer1[0], buffer1[1],
+		buffer1[2], buffer1[3],
+		buffer1[4], buffer1[5]);
+	ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len);
+	if (ret==buffer1_len) {
+		ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, buffer2_len);
+	}
+	if (ret<0) {
+		dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread;
+		dmxdevfilter->buffer.error=-EOVERFLOW;
+	}
+	if (dmxdevfilter->params.sec.flags&DMX_ONESHOT)
+		dmxdevfilter->state=DMXDEV_STATE_DONE;
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up(&dmxdevfilter->buffer.queue);
+	return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+		       const u8 *buffer2, size_t buffer2_len,
+		       struct dmx_ts_feed *feed, enum dmx_success success)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) feed->priv;
+	struct dmxdev_buffer *buffer;
+	int ret;
+
+	spin_lock(&dmxdevfilter->dev->lock);
+	if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output==DMX_OUT_TAP)
+		buffer=&dmxdevfilter->buffer;
+	else
+		buffer=&dmxdevfilter->dev->dvr_buffer;
+	if (buffer->error) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up(&buffer->queue);
+		return 0;
+	}
+	ret=dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+	if (ret==buffer1_len)
+		ret=dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+	if (ret<0) {
+		buffer->pwrite=buffer->pread;
+		buffer->error=-EOVERFLOW;
+	}
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up(&buffer->queue);
+	return 0;
+}
+
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	switch (dmxdevfilter->type) {
+	case DMXDEV_TYPE_SEC:
+		del_timer(&dmxdevfilter->timer);
+		dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+		break;
+	case DMXDEV_TYPE_PES:
+		dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/* start feed associated with the specified filter */
+
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+	dvb_dmxdev_filter_state_set (filter, DMXDEV_STATE_GO);
+
+	switch (filter->type) {
+	case DMXDEV_TYPE_SEC:
+		return filter->feed.sec->start_filtering(filter->feed.sec);
+		break;
+	case DMXDEV_TYPE_PES:
+		return filter->feed.ts->start_filtering(filter->feed.ts);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/* restart section feed if it has filters left associated with it,
+   otherwise release the feed */
+
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+	int i;
+	struct dmxdev *dmxdev = filter->dev;
+	u16 pid = filter->params.sec.pid;
+
+	for (i=0; i<dmxdev->filternum; i++)
+		if (dmxdev->filter[i].state>=DMXDEV_STATE_GO &&
+		    dmxdev->filter[i].type==DMXDEV_TYPE_SEC &&
+		    dmxdev->filter[i].pid==pid) {
+			dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+			return 0;
+		}
+
+	filter->dev->demux->release_section_feed(dmxdev->demux, filter->feed.sec);
+
+	return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+	if (dmxdevfilter->state<DMXDEV_STATE_GO)
+		return 0;
+
+	switch (dmxdevfilter->type) {
+	case DMXDEV_TYPE_SEC:
+		if (!dmxdevfilter->feed.sec)
+			break;
+		dvb_dmxdev_feed_stop(dmxdevfilter);
+		if (dmxdevfilter->filter.sec)
+			dmxdevfilter->feed.sec->
+				release_filter(dmxdevfilter->feed.sec,
+					       dmxdevfilter->filter.sec);
+		dvb_dmxdev_feed_restart(dmxdevfilter);
+		dmxdevfilter->feed.sec=NULL;
+		break;
+	case DMXDEV_TYPE_PES:
+		if (!dmxdevfilter->feed.ts)
+			break;
+		dvb_dmxdev_feed_stop(dmxdevfilter);
+		dmxdevfilter->dev->demux->
+			release_ts_feed(dmxdevfilter->dev->demux,
+					dmxdevfilter->feed.ts);
+		dmxdevfilter->feed.ts=NULL;
+		break;
+	default:
+		if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED)
+			return 0;
+		return -EINVAL;
+	}
+	dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0;
+	return 0;
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+	if (dmxdevfilter->state<DMXDEV_STATE_SET)
+		return 0;
+
+	dmxdevfilter->type=DMXDEV_TYPE_NONE;
+	dmxdevfilter->pid=0xffff;
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+	return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+	struct dmxdev *dmxdev = filter->dev;
+	void *mem;
+	int ret, i;
+
+	if (filter->state < DMXDEV_STATE_SET)
+		return -EINVAL;
+
+	if (filter->state >= DMXDEV_STATE_GO)
+		dvb_dmxdev_filter_stop(filter);
+
+	if (!(mem = filter->buffer.data)) {
+		mem = vmalloc(filter->buffer.size);
+		spin_lock_irq(&filter->dev->lock);
+		filter->buffer.data=mem;
+		spin_unlock_irq(&filter->dev->lock);
+		if (!filter->buffer.data)
+			return -ENOMEM;
+	}
+
+	filter->buffer.pwrite = filter->buffer.pread = 0;
+
+	switch (filter->type) {
+	case DMXDEV_TYPE_SEC:
+	{
+		struct dmx_sct_filter_params *para=&filter->params.sec;
+		struct dmx_section_filter **secfilter=&filter->filter.sec;
+		struct dmx_section_feed **secfeed=&filter->feed.sec;
+
+		*secfilter=NULL;
+		*secfeed=NULL;
+
+		/* find active filter/feed with same PID */
+		for (i=0; i<dmxdev->filternum; i++) {
+			if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+			    dmxdev->filter[i].pid == para->pid &&
+			    dmxdev->filter[i].type == DMXDEV_TYPE_SEC) {
+				*secfeed = dmxdev->filter[i].feed.sec;
+				break;
+			}
+		}
+
+		/* if no feed found, try to allocate new one */
+		if (!*secfeed) {
+			ret=dmxdev->demux->allocate_section_feed(dmxdev->demux,
+								 secfeed,
+						   dvb_dmxdev_section_callback);
+			if (ret<0) {
+				printk ("DVB (%s): could not alloc feed\n",
+					__FUNCTION__);
+				return ret;
+			}
+
+			ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0,
+					    (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+
+			if (ret<0) {
+				printk ("DVB (%s): could not set feed\n",
+					__FUNCTION__);
+				dvb_dmxdev_feed_restart(filter);
+				return ret;
+			}
+		} else {
+			dvb_dmxdev_feed_stop(filter);
+		}
+
+		ret=(*secfeed)->allocate_filter(*secfeed, secfilter);
+
+		if (ret < 0) {
+			dvb_dmxdev_feed_restart(filter);
+			filter->feed.sec->start_filtering(*secfeed);
+			dprintk ("could not get filter\n");
+			return ret;
+		}
+
+		(*secfilter)->priv = filter;
+
+		memcpy(&((*secfilter)->filter_value[3]),
+		       &(para->filter.filter[1]), DMX_FILTER_SIZE-1);
+		memcpy(&(*secfilter)->filter_mask[3],
+		       &para->filter.mask[1], DMX_FILTER_SIZE-1);
+		memcpy(&(*secfilter)->filter_mode[3],
+		       &para->filter.mode[1], DMX_FILTER_SIZE-1);
+
+		(*secfilter)->filter_value[0]=para->filter.filter[0];
+		(*secfilter)->filter_mask[0]=para->filter.mask[0];
+		(*secfilter)->filter_mode[0]=para->filter.mode[0];
+		(*secfilter)->filter_mask[1]=0;
+		(*secfilter)->filter_mask[2]=0;
+
+		filter->todo = 0;
+
+		ret = filter->feed.sec->start_filtering (filter->feed.sec);
+
+		if (ret < 0)
+			return ret;
+
+		dvb_dmxdev_filter_timer(filter);
+		break;
+	}
+
+	case DMXDEV_TYPE_PES:
+	{
+		struct timespec timeout = { 0 };
+		struct dmx_pes_filter_params *para = &filter->params.pes;
+		dmx_output_t otype;
+		int ret;
+		int ts_type;
+		enum dmx_ts_pes ts_pes;
+		struct dmx_ts_feed **tsfeed = &filter->feed.ts;
+
+		filter->feed.ts = NULL;
+		otype=para->output;
+
+		ts_pes=(enum dmx_ts_pes) para->pes_type;
+
+		if (ts_pes<DMX_PES_OTHER)
+			ts_type=TS_DECODER;
+		else
+			ts_type=0;
+
+		if (otype == DMX_OUT_TS_TAP)
+			ts_type |= TS_PACKET;
+
+		if (otype == DMX_OUT_TAP)
+			ts_type |= TS_PAYLOAD_ONLY|TS_PACKET;
+
+		ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux,
+						    tsfeed,
+						    dvb_dmxdev_ts_callback);
+		if (ret<0)
+			return ret;
+
+		(*tsfeed)->priv = (void *) filter;
+
+		ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes,
+				     188, 32768, 0, timeout);
+
+		if (ret < 0) {
+			dmxdev->demux->release_ts_feed(dmxdev->demux, *tsfeed);
+			return ret;
+		}
+
+		ret = filter->feed.ts->start_filtering(filter->feed.ts);
+
+		if (ret < 0)
+			return ret;
+
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+	return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int i;
+	struct dmxdev_filter *dmxdevfilter;
+
+	if (!dmxdev->filter)
+		return -EINVAL;
+
+	if (down_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	for (i=0; i<dmxdev->filternum; i++)
+		if (dmxdev->filter[i].state==DMXDEV_STATE_FREE)
+			break;
+
+	if (i==dmxdev->filternum) {
+		up(&dmxdev->mutex);
+		return -EMFILE;
+	}
+
+	dmxdevfilter=&dmxdev->filter[i];
+	sema_init(&dmxdevfilter->mutex, 1);
+	dmxdevfilter->dvbdev=dmxdev->dvbdev;
+	file->private_data=dmxdevfilter;
+
+	dvb_dmxdev_buffer_init(&dmxdevfilter->buffer);
+	dmxdevfilter->type=DMXDEV_TYPE_NONE;
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+	dmxdevfilter->feed.ts=NULL;
+	init_timer(&dmxdevfilter->timer);
+
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter)
+{
+	if (down_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if (down_interruptible(&dmxdevfilter->mutex)) {
+		up(&dmxdev->mutex);
+		return -ERESTARTSYS;
+	}
+
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+	dvb_dmxdev_filter_reset(dmxdevfilter);
+
+	if (dmxdevfilter->buffer.data) {
+		void *mem=dmxdevfilter->buffer.data;
+
+		spin_lock_irq(&dmxdev->lock);
+		dmxdevfilter->buffer.data=NULL;
+		spin_unlock_irq(&dmxdev->lock);
+		vfree(mem);
+	}
+
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+	wake_up(&dmxdevfilter->buffer.queue);
+	up(&dmxdevfilter->mutex);
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static inline void invert_mode(dmx_filter_t *filter)
+{
+	int i;
+
+	for (i=0; i<DMX_FILTER_SIZE; i++)
+		filter->mode[i]^=0xff;
+}
+
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+		struct dmxdev_filter *dmxdevfilter,
+		struct dmx_sct_filter_params *params)
+{
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+
+	dmxdevfilter->type=DMXDEV_TYPE_SEC;
+	dmxdevfilter->pid=params->pid;
+	memcpy(&dmxdevfilter->params.sec,
+	       params, sizeof(struct dmx_sct_filter_params));
+	invert_mode(&dmxdevfilter->params.sec.filter);
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	if (params->flags&DMX_IMMEDIATE_START)
+		return dvb_dmxdev_filter_start(dmxdevfilter);
+
+	return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+		   struct dmxdev_filter *dmxdevfilter,
+		   struct dmx_pes_filter_params *params)
+{
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+
+	if (params->pes_type>DMX_PES_OTHER || params->pes_type<0)
+		return -EINVAL;
+
+	dmxdevfilter->type=DMXDEV_TYPE_PES;
+	dmxdevfilter->pid=params->pid;
+	memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params));
+
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	if (params->flags&DMX_IMMEDIATE_START)
+		return dvb_dmxdev_filter_start(dmxdevfilter);
+
+	return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+		struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	int result, hcount;
+	int done=0;
+
+	if (dfil->todo<=0) {
+		hcount=3+dfil->todo;
+		if (hcount>count)
+			hcount=count;
+		result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+					buf, hcount, ppos);
+		if (result<0) {
+			dfil->todo=0;
+			return result;
+		}
+		if (copy_from_user(dfil->secheader-dfil->todo, buf, result))
+			return -EFAULT;
+		buf+=result;
+		done=result;
+		count-=result;
+		dfil->todo-=result;
+		if (dfil->todo>-3)
+			return done;
+		dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff;
+		if (!count)
+			return done;
+	}
+	if (count>dfil->todo)
+		count=dfil->todo;
+	result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+				buf, count, ppos);
+	if (result<0)
+		return result;
+	dfil->todo-=result;
+	return (result+done);
+}
+
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+	int ret=0;
+
+	if (down_interruptible(&dmxdevfilter->mutex))
+		return -ERESTARTSYS;
+
+	if (dmxdevfilter->type==DMXDEV_TYPE_SEC)
+		ret=dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+	else
+		ret=dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+				     file->f_flags&O_NONBLOCK,
+				     buf, count, ppos);
+
+	up(&dmxdevfilter->mutex);
+	return ret;
+}
+
+
+static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
+			      unsigned int cmd, void *parg)
+{
+	struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+	struct dmxdev *dmxdev=dmxdevfilter->dev;
+	unsigned long arg=(unsigned long) parg;
+	int ret=0;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case DMX_START:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		if (dmxdevfilter->state<DMXDEV_STATE_SET)
+			ret = -EINVAL;
+		else
+			ret = dvb_dmxdev_filter_start(dmxdevfilter);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_STOP:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_filter_stop(dmxdevfilter);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_FILTER:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter,
+				    (struct dmx_sct_filter_params *)parg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_PES_FILTER:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter,
+					       (struct dmx_pes_filter_params *)parg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_BUFFER_SIZE:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_GET_EVENT:
+		break;
+
+	case DMX_GET_PES_PIDS:
+		if (!dmxdev->demux->get_pes_pids) {
+			ret=-EINVAL;
+			break;
+		}
+		dmxdev->demux->get_pes_pids(dmxdev->demux, (u16 *)parg);
+		break;
+
+	case DMX_GET_STC:
+		if (!dmxdev->demux->get_stc) {
+		        ret=-EINVAL;
+			break;
+		}
+		ret = dmxdev->demux->get_stc(dmxdev->demux,
+				((struct dmx_stc *)parg)->num,
+				&((struct dmx_stc *)parg)->stc,
+				&((struct dmx_stc *)parg)->base);
+		break;
+
+	default:
+		ret=-EINVAL;
+	}
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+static int dvb_demux_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+
+static unsigned int dvb_demux_poll (struct file *file, poll_table *wait)
+{
+	struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+	unsigned int mask = 0;
+
+	if (!dmxdevfilter)
+		return -EINVAL;
+
+	poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+	if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+	    dmxdevfilter->state != DMXDEV_STATE_DONE &&
+	    dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+		return 0;
+
+	if (dmxdevfilter->buffer.error)
+		mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+	if (dmxdevfilter->buffer.pread != dmxdevfilter->buffer.pwrite)
+		mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+	return mask;
+}
+
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+	struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+	return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+}
+
+
+static struct file_operations dvb_demux_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_demux_read,
+	.ioctl		= dvb_demux_ioctl,
+	.open		= dvb_demux_open,
+	.release	= dvb_demux_release,
+	.poll		= dvb_demux_poll,
+};
+
+
+static struct dvb_device dvbdev_demux = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_demux_fops
+};
+
+
+static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+	int ret=0;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case DMX_SET_BUFFER_SIZE:
+		// FIXME: implement
+		ret=0;
+		break;
+
+	default:
+		ret=-EINVAL;
+	}
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+
+static int dvb_dvr_ioctl(struct inode *inode, struct file *file,
+		  unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+
+static unsigned int dvb_dvr_poll (struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev = (struct dmxdev *) dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+	if ((file->f_flags&O_ACCMODE) == O_RDONLY) {
+		if (dmxdev->dvr_buffer.error)
+			mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+		if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+			mask |= (POLLIN | POLLRDNORM | POLLPRI);
+	} else
+		mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+	return mask;
+}
+
+
+static struct file_operations dvb_dvr_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_dvr_read,
+	.write		= dvb_dvr_write,
+	.ioctl		= dvb_dvr_ioctl,
+	.open		= dvb_dvr_open,
+	.release	= dvb_dvr_release,
+	.poll		= dvb_dvr_poll,
+};
+
+static struct dvb_device dvbdev_dvr = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_dvr_fops
+};
+
+int
+dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+	int i;
+
+	if (dmxdev->demux->open(dmxdev->demux) < 0)
+		return -EUSERS;
+
+	dmxdev->filter = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_filter));
+	if (!dmxdev->filter)
+		return -ENOMEM;
+
+	dmxdev->dvr = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_dvr));
+	if (!dmxdev->dvr) {
+		vfree(dmxdev->filter);
+		dmxdev->filter = NULL;
+		return -ENOMEM;
+	}
+
+	sema_init(&dmxdev->mutex, 1);
+	spin_lock_init(&dmxdev->lock);
+	for (i=0; i<dmxdev->filternum; i++) {
+		dmxdev->filter[i].dev=dmxdev;
+		dmxdev->filter[i].buffer.data=NULL;
+		dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+		dmxdev->dvr[i].dev=dmxdev;
+		dmxdev->dvr[i].buffer.data=NULL;
+		dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+		dvb_dmxdev_dvr_state_set(&dmxdev->dvr[i], DMXDEV_STATE_FREE);
+	}
+
+	dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX);
+	dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR);
+
+	dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void
+dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+	dvb_unregister_device(dmxdev->dvbdev);
+	dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+	vfree(dmxdev->filter);
+	dmxdev->filter=NULL;
+	vfree(dmxdev->dvr);
+	dmxdev->dvr=NULL;
+	dmxdev->demux->close(dmxdev->demux);
+}
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
new file mode 100644
index 0000000..395a9cd
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -0,0 +1,128 @@
+/*
+ * dmxdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DMXDEV_H_
+#define _DMXDEV_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <asm/semaphore.h>
+
+#include <linux/dvb/dmx.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+
+enum dmxdevype {
+	DMXDEV_TYPE_NONE,
+	DMXDEV_TYPE_SEC,
+	DMXDEV_TYPE_PES,
+};
+
+enum dmxdev_state {
+	DMXDEV_STATE_FREE,
+	DMXDEV_STATE_ALLOCATED,
+	DMXDEV_STATE_SET,
+	DMXDEV_STATE_GO,
+	DMXDEV_STATE_DONE,
+	DMXDEV_STATE_TIMEDOUT
+};
+
+struct dmxdev_buffer {
+        u8 *data;
+        int size;
+        int pread;
+        int pwrite;
+	wait_queue_head_t queue;
+        int error;
+};
+
+struct dmxdev_filter {
+	struct dvb_device *dvbdev;
+
+        union {
+	        struct dmx_section_filter *sec;
+	} filter;
+
+        union {
+                struct dmx_ts_feed *ts;
+                struct dmx_section_feed *sec;
+	} feed;
+
+        union {
+	        struct dmx_sct_filter_params sec;
+	        struct dmx_pes_filter_params pes;
+	} params;
+
+        int type;
+        enum dmxdev_state state;
+        struct dmxdev *dev;
+        struct dmxdev_buffer buffer;
+
+	struct semaphore mutex;
+
+        /* only for sections */
+        struct timer_list timer;
+        int todo;
+        u8 secheader[3];
+
+        u16 pid;
+};
+
+
+struct dmxdev_dvr {
+        int state;
+        struct dmxdev *dev;
+        struct dmxdev_buffer buffer;
+};
+
+
+struct dmxdev {
+	struct dvb_device *dvbdev;
+	struct dvb_device *dvr_dvbdev;
+
+        struct dmxdev_filter *filter;
+        struct dmxdev_dvr *dvr;
+        struct dmx_demux *demux;
+
+        int filternum;
+        int capabilities;
+#define DMXDEV_CAP_DUPLEX 1
+        struct dmx_frontend *dvr_orig_fe;
+
+        struct dmxdev_buffer dvr_buffer;
+#define DVR_BUFFER_SIZE (10*188*1024)
+
+	struct semaphore mutex;
+	spinlock_t lock;
+};
+
+
+int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
+void dvb_dmxdev_release(struct dmxdev *dmxdev);
+
+#endif /* _DMXDEV_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 0000000..c1ea89f
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1778 @@
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/rwsem.h>
+
+#include "dvb_ca_en50221.h"
+#include "dvb_ringbuffer.h"
+
+static int dvb_ca_en50221_debug;
+
+module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
+MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
+
+#define dprintk if (dvb_ca_en50221_debug) printk
+
+#define INIT_TIMEOUT_SECS 5
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA      0
+#define CTRLIF_COMMAND   1
+#define CTRLIF_STATUS    1
+#define CTRLIF_SIZE_LOW  2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC        1	/* Host control */
+#define CMDREG_SW        2	/* Size write */
+#define CMDREG_SR        4	/* Size read */
+#define CMDREG_RS        8	/* Reset interface */
+#define CMDREG_FRIE   0x40	/* Enable FR interrupt */
+#define CMDREG_DAIE   0x80	/* Enable DA interrupt */
+#define IRQEN (CMDREG_DAIE)
+
+#define STATUSREG_RE     1	/* read error */
+#define STATUSREG_WE     2	/* write error */
+#define STATUSREG_FR  0x40	/* module free */
+#define STATUSREG_DA  0x80	/* data available */
+#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE)	/* general transfer error */
+
+
+#define DVB_CA_SLOTSTATE_NONE           0
+#define DVB_CA_SLOTSTATE_UNINITIALISED  1
+#define DVB_CA_SLOTSTATE_RUNNING        2
+#define DVB_CA_SLOTSTATE_INVALID        3
+#define DVB_CA_SLOTSTATE_WAITREADY      4
+#define DVB_CA_SLOTSTATE_VALIDATE       5
+#define DVB_CA_SLOTSTATE_WAITFR         6
+#define DVB_CA_SLOTSTATE_LINKINIT       7
+
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+
+	/* current state of the CAM */
+	int slot_state;
+
+	/* Number of CAMCHANGES that have occurred since last processing */
+	atomic_t camchange_count;
+
+	/* Type of last CAMCHANGE */
+	int camchange_type;
+
+	/* base address of CAM config */
+	u32 config_base;
+
+	/* value to write into Config Control register */
+	u8 config_option;
+
+	/* if 1, the CAM supports DA IRQs */
+	u8 da_irq_supported:1;
+
+	/* size of the buffer to use when talking to the CAM */
+	int link_buf_size;
+
+	/* semaphore for syncing access to slot structure */
+	struct rw_semaphore sem;
+
+	/* buffer for incoming packets */
+	struct dvb_ringbuffer rx_buffer;
+
+	/* timer used during various states of the slot */
+	unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+
+	/* pointer back to the public data structure */
+	struct dvb_ca_en50221 *pub;
+
+	/* the DVB device */
+	struct dvb_device *dvbdev;
+
+	/* Flags describing the interface (DVB_CA_FLAG_*) */
+	u32 flags;
+
+	/* number of slots supported by this CA interface */
+	unsigned int slot_count;
+
+	/* information on each slot */
+	struct dvb_ca_slot *slot_info;
+
+	/* wait queues for read() and write() operations */
+	wait_queue_head_t wait_queue;
+
+	/* PID of the monitoring thread */
+	pid_t thread_pid;
+
+	/* Wait queue used when shutting thread down */
+	wait_queue_head_t thread_queue;
+
+	/* Flag indicating when thread should exit */
+	unsigned int exit:1;
+
+	/* Flag indicating if the CA device is open */
+	unsigned int open:1;
+
+	/* Flag indicating the thread should wake up now */
+	unsigned int wakeup:1;
+
+	/* Delay the main thread should use */
+	unsigned long delay;
+
+	/* Slot to start looking for data to read from in the next user-space read operation */
+	int next_read_slot;
+};
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+
+
+/**
+ * Safely find needle in haystack.
+ *
+ * @param haystack Buffer to look in.
+ * @param hlen Number of bytes in haystack.
+ * @param needle Buffer to find.
+ * @param nlen Number of bytes in needle.
+ * @return Pointer into haystack needle was found at, or NULL if not found.
+ */
+static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen)
+{
+	int i;
+
+	if (hlen < nlen)
+		return NULL;
+
+	for (i = 0; i <= hlen - nlen; i++) {
+		if (!strncmp(haystack + i, needle, nlen))
+			return haystack + i;
+	}
+
+	return NULL;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 physical interface functions */
+
+
+/**
+ * Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
+{
+	int slot_status;
+	int cam_present_now;
+	int cam_changed;
+
+	/* IRQ mode */
+	if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
+		return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
+	}
+
+	/* poll mode */
+	slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
+
+	cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
+	cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
+	if (!cam_changed) {
+		int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
+		cam_changed = (cam_present_now != cam_present_old);
+	}
+
+	if (cam_changed) {
+		if (!cam_present_now) {
+			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+		} else {
+			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+		}
+		atomic_set(&ca->slot_info[slot].camchange_count, 1);
+	} else {
+		if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+		    (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+			// move to validate state if reset is completed
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+		}
+	}
+
+	return cam_changed;
+}
+
+
+/**
+ * Wait for flags to become set on the STATUS register on a CAM interface,
+ * checking for errors and timeout.
+ *
+ * @param ca CA instance.
+ * @param slot Slot on interface.
+ * @param waitfor Flags to wait for.
+ * @param timeout_ms Timeout in milliseconds.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
+					 u8 waitfor, int timeout_hz)
+{
+	unsigned long timeout;
+	unsigned long start;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* loop until timeout elapsed */
+	start = jiffies;
+	timeout = jiffies + timeout_hz;
+	while (1) {
+		/* read the status and check for error */
+		int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+		if (res < 0)
+			return -EIO;
+
+		/* if we got the flags, it was successful! */
+		if (res & waitfor) {
+			dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start);
+			return 0;
+		}
+
+		/* check for timeout */
+		if (time_after(jiffies, timeout)) {
+			break;
+		}
+
+		/* wait for a bit */
+		msleep(1);
+	}
+
+	dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+	/* if we get here, we've timed out */
+	return -ETIMEDOUT;
+}
+
+
+/**
+ * Initialise the link layer connection to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
+{
+	int ret;
+	int buf_size;
+	u8 buf[2];
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* we'll be determining these during this function */
+	ca->slot_info[slot].da_irq_supported = 0;
+
+	/* set the host link buffer size temporarily. it will be overwritten with the
+	 * real negotiated size later. */
+	ca->slot_info[slot].link_buf_size = 2;
+
+	/* read the buffer size from the CAM */
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
+		return -EIO;
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+		return ret;
+
+	/* store it, and choose the minimum of our buffer and the CAM's buffer size */
+	buf_size = (buf[0] << 8) | buf[1];
+	if (buf_size > HOST_LINK_BUF_SIZE)
+		buf_size = HOST_LINK_BUF_SIZE;
+	ca->slot_info[slot].link_buf_size = buf_size;
+	buf[0] = buf_size >> 8;
+	buf[1] = buf_size & 0xff;
+	dprintk("Chosen link buffer size of %i\n", buf_size);
+
+	/* write the buffer size to the CAM */
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2)
+		return -EIO;
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+		return ret;
+
+	/* success */
+	return 0;
+}
+
+/**
+ * Read a tuple from attribute memory.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ * @param address Address to read from. Updated.
+ * @param tupleType Tuple id byte. Updated.
+ * @param tupleLength Tuple length. Updated.
+ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
+				     int *address, int *tupleType, int *tupleLength, u8 * tuple)
+{
+	int i;
+	int _tupleType;
+	int _tupleLength;
+	int _address = *address;
+
+	/* grab the next tuple length and type */
+	if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0)
+		return _tupleType;
+	if (_tupleType == 0xff) {
+		dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
+		*address += 2;
+		*tupleType = _tupleType;
+		*tupleLength = 0;
+		return 0;
+	}
+	if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0)
+		return _tupleLength;
+	_address += 4;
+
+	dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
+
+	/* read in the whole tuple */
+	for (i = 0; i < _tupleLength; i++) {
+		tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
+		dprintk("  0x%02x: 0x%02x %c\n",
+			i, tuple[i] & 0xff,
+			((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+	}
+	_address += (_tupleLength * 2);
+
+	// success
+	*tupleType = _tupleType;
+	*tupleLength = _tupleLength;
+	*address = _address;
+	return 0;
+}
+
+
+/**
+ * Parse attribute memory of a CAM module, extracting Config register, and checking
+ * it is a DVB CAM module.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
+{
+	int address = 0;
+	int tupleLength;
+	int tupleType;
+	u8 tuple[257];
+	char *dvb_str;
+	int rasz;
+	int status;
+	int got_cftableentry = 0;
+	int end_chain = 0;
+	int i;
+	u16 manfid = 0;
+	u16 devid = 0;
+
+
+	// CISTPL_DEVICE_0A
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1D)
+		return -EINVAL;
+
+
+
+	// CISTPL_DEVICE_0C
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1C)
+		return -EINVAL;
+
+
+
+	// CISTPL_VERS_1
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x15)
+		return -EINVAL;
+
+
+
+	// CISTPL_MANFID
+	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						&tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x20)
+		return -EINVAL;
+	if (tupleLength != 4)
+		return -EINVAL;
+	manfid = (tuple[1] << 8) | tuple[0];
+	devid = (tuple[3] << 8) | tuple[2];
+
+
+
+	// CISTPL_CONFIG
+	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						&tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1A)
+		return -EINVAL;
+	if (tupleLength < 3)
+		return -EINVAL;
+
+	/* extract the configbase */
+	rasz = tuple[0] & 3;
+	if (tupleLength < (3 + rasz + 14))
+		return -EINVAL;
+	ca->slot_info[slot].config_base = 0;
+	for (i = 0; i < rasz + 1; i++) {
+		ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
+	}
+
+	/* check it contains the correct DVB string */
+	dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
+	if (dvb_str == NULL)
+		return -EINVAL;
+	if (tupleLength < ((dvb_str - (char *) tuple) + 12))
+		return -EINVAL;
+
+	/* is it a version we support? */
+	if (strncmp(dvb_str + 8, "1.00", 4)) {
+		printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
+		       ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
+		return -EINVAL;
+	}
+
+	/* process the CFTABLE_ENTRY tuples, and any after those */
+	while ((!end_chain) && (address < 0x1000)) {
+		if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						        &tupleLength, tuple)) < 0)
+			return status;
+		switch (tupleType) {
+		case 0x1B:	// CISTPL_CFTABLE_ENTRY
+			if (tupleLength < (2 + 11 + 17))
+				break;
+
+			/* if we've already parsed one, just use it */
+			if (got_cftableentry)
+				break;
+
+			/* get the config option */
+			ca->slot_info[slot].config_option = tuple[0] & 0x3f;
+
+			/* OK, check it contains the correct strings */
+			if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+			    (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL))
+				break;
+
+			got_cftableentry = 1;
+			break;
+
+		case 0x14:	// CISTPL_NO_LINK
+			break;
+
+		case 0xFF:	// CISTPL_END
+			end_chain = 1;
+			break;
+
+		default:	/* Unknown tuple type - just skip this tuple and move to the next one */
+			dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType,
+				tupleLength);
+			break;
+		}
+	}
+
+	if ((address > 0x1000) || (!got_cftableentry))
+		return -EINVAL;
+
+	dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+		manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
+
+	// success!
+	return 0;
+}
+
+
+/**
+ * Set CAM's configoption correctly.
+ *
+ * @param ca CA instance.
+ * @param slot Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
+{
+	int configoption;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* set the config option */
+	ca->pub->write_attribute_mem(ca->pub, slot,
+				     ca->slot_info[slot].config_base,
+				     ca->slot_info[slot].config_option);
+
+	/* check it */
+	configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+	dprintk("Set configoption 0x%x, read configoption 0x%x\n",
+		ca->slot_info[slot].config_option, configoption & 0x3f);
+
+	/* fine! */
+	return 0;
+
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It reads a buffer of
+ * data from the CAM. The data can either be stored in a supplied buffer, or
+ * automatically be added to the slot's rx_buffer.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to read from.
+ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal fragment.
+ * @param ecount Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * @return Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
+{
+	int bytes_read;
+	int status;
+	u8 buf[HOST_LINK_BUF_SIZE];
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* check if we have space for a link buf in the rx_buffer */
+	if (ebuf == NULL) {
+		int buf_free;
+
+		down_read(&ca->slot_info[slot].sem);
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			status = -EIO;
+			goto exit;
+		}
+		buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+		up_read(&ca->slot_info[slot].sem);
+
+		if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+			status = -EAGAIN;
+			goto exit;
+		}
+	}
+
+	/* check if there is data available */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (!(status & STATUSREG_DA)) {
+		/* no data */
+		status = 0;
+		goto exit;
+	}
+
+	/* read the amount of data */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
+		goto exit;
+	bytes_read = status << 8;
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
+		goto exit;
+	bytes_read |= status;
+
+	/* check it will fit */
+	if (ebuf == NULL) {
+		if (bytes_read > ca->slot_info[slot].link_buf_size) {
+			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+			       ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+			status = -EIO;
+			goto exit;
+		}
+		if (bytes_read < 2) {
+			printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+			       ca->dvbdev->adapter->num);
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+			status = -EIO;
+			goto exit;
+		}
+	} else {
+		if (bytes_read > ecount) {
+			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+			       ca->dvbdev->adapter->num);
+			status = -EIO;
+			goto exit;
+		}
+	}
+
+	/* fill the buffer */
+	for (i = 0; i < bytes_read; i++) {
+		/* read byte and check */
+		if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
+			goto exit;
+
+		/* OK, store it in the buffer */
+		buf[i] = status;
+	}
+
+	/* check for read error (RE should now be 0) */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (status & STATUSREG_RE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+		status = -EIO;
+		goto exit;
+	}
+
+	/* OK, add it to the receive buffer, or copy into external buffer if supplied */
+	if (ebuf == NULL) {
+		down_read(&ca->slot_info[slot].sem);
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			status = -EIO;
+			goto exit;
+		}
+		dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
+		up_read(&ca->slot_info[slot].sem);
+	} else {
+		memcpy(ebuf, buf, bytes_read);
+	}
+
+	dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
+		buf[0], (buf[1] & 0x80) == 0, bytes_read);
+
+	/* wake up readers when a last_fragment is received */
+	if ((buf[1] & 0x80) == 0x00) {
+		wake_up_interruptible(&ca->wait_queue);
+	}
+	status = bytes_read;
+
+exit:
+	return status;
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It writes a buffer of data
+ * to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to write to.
+ * @param ebuf The data in this buffer is treated as a complete link-level packet to
+ * be written.
+ * @param count Size of ebuf.
+ *
+ * @return Number of bytes written, or < 0 on error.
+ */
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write)
+{
+	int status;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+
+	// sanity check
+	if (bytes_write > ca->slot_info[slot].link_buf_size)
+		return -EINVAL;
+
+	/* check if interface is actually waiting for us to read from it, or if a read is in progress */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exitnowrite;
+	if (status & (STATUSREG_DA | STATUSREG_RE)) {
+		status = -EAGAIN;
+		goto exitnowrite;
+	}
+
+	/* OK, set HC bit */
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
+						 IRQEN | CMDREG_HC)) != 0)
+		goto exit;
+
+	/* check if interface is still free */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (!(status & STATUSREG_FR)) {
+		/* it wasn't free => try again later */
+		status = -EAGAIN;
+		goto exit;
+	}
+
+	/* send the amount of data */
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
+		goto exit;
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
+						 bytes_write & 0xff)) != 0)
+		goto exit;
+
+	/* send the buffer */
+	for (i = 0; i < bytes_write; i++) {
+		if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0)
+			goto exit;
+	}
+
+	/* check for write error (WE should now be 0) */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (status & STATUSREG_WE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+		status = -EIO;
+		goto exit;
+	}
+	status = bytes_write;
+
+	dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
+		buf[0], (buf[1] & 0x80) == 0, bytes_write);
+
+exit:
+	ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+
+exitnowrite:
+	return status;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
+
+
+
+/* ******************************************************************************** */
+/* EN50221 higher level functions */
+
+
+/**
+ * A CAM has been removed => shut it down.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to shut down.
+ */
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	down_write(&ca->slot_info[slot].sem);
+	ca->pub->slot_shutdown(ca->pub, slot);
+	ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+	vfree(ca->slot_info[slot].rx_buffer.data);
+	ca->slot_info[slot].rx_buffer.data = NULL;
+	up_write(&ca->slot_info[slot].sem);
+
+	/* need to wake up all processes to check if they're now
+	   trying to write to a defunct CAM */
+	wake_up_interruptible(&ca->wait_queue);
+
+	dprintk("Slot %i shutdown\n", slot);
+
+	/* success */
+	return 0;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
+
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+
+	dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+
+	switch (change_type) {
+	case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+	case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+		break;
+
+	default:
+		return;
+	}
+
+	ca->slot_info[slot].camchange_type = change_type;
+	atomic_inc(&ca->slot_info[slot].camchange_count);
+	dvb_ca_en50221_thread_wakeup(ca);
+}
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+
+	dprintk("CAMREADY IRQ slot:%i\n", slot);
+
+	if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+		dvb_ca_en50221_thread_wakeup(ca);
+	}
+}
+
+
+/**
+ * An FR or DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+	int flags;
+
+	dprintk("FR/DA IRQ slot:%i\n", slot);
+
+	switch (ca->slot_info[slot].slot_state) {
+	case DVB_CA_SLOTSTATE_LINKINIT:
+		flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+		if (flags & STATUSREG_DA) {
+			dprintk("CAM supports DA IRQ\n");
+			ca->slot_info[slot].da_irq_supported = 1;
+		}
+		break;
+
+	case DVB_CA_SLOTSTATE_RUNNING:
+		if (ca->open)
+			dvb_ca_en50221_read_data(ca, slot, NULL, 0);
+		break;
+	}
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 thread functions */
+
+/**
+ * Wake up the DVB CA thread
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
+{
+
+	dprintk("%s\n", __FUNCTION__);
+
+	ca->wakeup = 1;
+	mb();
+	wake_up_interruptible(&ca->thread_queue);
+}
+
+/**
+ * Used by the CA thread to determine if an early wakeup is necessary
+ *
+ * @param ca CA instance.
+ */
+static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private *ca)
+{
+	if (ca->wakeup) {
+		ca->wakeup = 0;
+		return 1;
+	}
+	if (ca->exit)
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * Update the delay used by the thread.
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
+{
+	int delay;
+	int curdelay = 100000000;
+	int slot;
+
+	for (slot = 0; slot < ca->slot_count; slot++) {
+		switch (ca->slot_info[slot].slot_state) {
+		default:
+		case DVB_CA_SLOTSTATE_NONE:
+		case DVB_CA_SLOTSTATE_INVALID:
+			delay = HZ * 60;
+			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+				delay = HZ / 10;
+			}
+			break;
+
+		case DVB_CA_SLOTSTATE_UNINITIALISED:
+		case DVB_CA_SLOTSTATE_WAITREADY:
+		case DVB_CA_SLOTSTATE_VALIDATE:
+		case DVB_CA_SLOTSTATE_WAITFR:
+		case DVB_CA_SLOTSTATE_LINKINIT:
+			delay = HZ / 10;
+			break;
+
+		case DVB_CA_SLOTSTATE_RUNNING:
+			delay = HZ * 60;
+			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+				delay = HZ / 10;
+			}
+			if (ca->open) {
+				if ((!ca->slot_info[slot].da_irq_supported) ||
+				    (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) {
+					delay = HZ / 10;
+				}
+			}
+			break;
+		}
+
+		if (delay < curdelay)
+			curdelay = delay;
+	}
+
+	ca->delay = curdelay;
+}
+
+
+
+/**
+ * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
+ */
+static int dvb_ca_en50221_thread(void *data)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) data;
+	char name[15];
+	int slot;
+	int flags;
+	int status;
+	int pktcount;
+	void *rxbuf;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* setup kernel thread */
+	snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id);
+
+	lock_kernel();
+	daemonize(name);
+	sigfillset(&current->blocked);
+	unlock_kernel();
+
+	/* choose the correct initial delay */
+	dvb_ca_en50221_thread_update_delay(ca);
+
+	/* main loop */
+	while (!ca->exit) {
+		/* sleep for a bit */
+		if (!ca->wakeup) {
+			flags = wait_event_interruptible_timeout(ca->thread_queue,
+								 dvb_ca_en50221_thread_should_wakeup(ca),
+								 ca->delay);
+			if ((flags == -ERESTARTSYS) || ca->exit) {
+				/* got signal or quitting */
+				break;
+			}
+		}
+		ca->wakeup = 0;
+
+		/* go through all the slots processing them */
+		for (slot = 0; slot < ca->slot_count; slot++) {
+
+			// check the cam status + deal with CAMCHANGEs
+			while (dvb_ca_en50221_check_camstatus(ca, slot)) {
+				/* clear down an old CI slot if necessary */
+				if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE)
+					dvb_ca_en50221_slot_shutdown(ca, slot);
+
+				/* if a CAM is NOW present, initialise it */
+				if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+				}
+
+				/* we've handled one CAMCHANGE */
+				dvb_ca_en50221_thread_update_delay(ca);
+				atomic_dec(&ca->slot_info[slot].camchange_count);
+			}
+
+			// CAM state machine
+			switch (ca->slot_info[slot].slot_state) {
+			case DVB_CA_SLOTSTATE_NONE:
+			case DVB_CA_SLOTSTATE_INVALID:
+				// no action needed
+				break;
+
+			case DVB_CA_SLOTSTATE_UNINITIALISED:
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
+				ca->pub->slot_reset(ca->pub, slot);
+				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+				break;
+
+			case DVB_CA_SLOTSTATE_WAITREADY:
+				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+					printk("dvb_ca adaptor %d: PC card did not respond :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				// no other action needed; will automatically change state when ready
+				break;
+
+			case DVB_CA_SLOTSTATE_VALIDATE:
+				if (dvb_ca_en50221_parse_attributes(ca, slot)
+				    != 0) {
+					printk("dvb_ca adapter %d: Invalid PC card inserted :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+					printk("dvb_ca adapter %d: Unable to initialise CAM :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				if (ca->pub->write_cam_control(ca->pub, slot,
+							       CTRLIF_COMMAND, CMDREG_RS) != 0) {
+					printk("dvb_ca adapter %d: Unable to reset CAM IF\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				dprintk("DVB CAM validated successfully\n");
+
+				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
+				ca->wakeup = 1;
+				break;
+
+			case DVB_CA_SLOTSTATE_WAITFR:
+				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+					printk("dvb_ca adapter %d: DVB CAM did not respond :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+
+				flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+				if (flags & STATUSREG_FR) {
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+					ca->wakeup = 1;
+				}
+				break;
+
+			case DVB_CA_SLOTSTATE_LINKINIT:
+				if (dvb_ca_en50221_link_init(ca, slot) != 0) {
+					printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+
+				rxbuf = vmalloc(RX_BUFFER_SIZE);
+				if (rxbuf == NULL) {
+					printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				down_write(&ca->slot_info[slot].sem);
+				dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
+				up_write(&ca->slot_info[slot].sem);
+
+				ca->pub->slot_ts_enable(ca->pub, slot);
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
+				dvb_ca_en50221_thread_update_delay(ca);
+				printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
+				break;
+
+			case DVB_CA_SLOTSTATE_RUNNING:
+				if (!ca->open)
+					continue;
+
+				// no need to poll if the CAM supports IRQs
+				if (ca->slot_info[slot].da_irq_supported)
+					break;
+
+				// poll mode
+				pktcount = 0;
+				while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) {
+					if (!ca->open)
+						break;
+
+					/* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
+					if (dvb_ca_en50221_check_camstatus(ca, slot)) {
+						// we dont want to sleep on the next iteration so we can handle the cam change
+						ca->wakeup = 1;
+						break;
+					}
+
+					/* check if we've hit our limit this time */
+					if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
+						// dont sleep; there is likely to be more data to read
+						ca->wakeup = 1;
+						break;
+					}
+				}
+				break;
+			}
+		}
+	}
+
+	/* completed */
+	ca->thread_pid = 0;
+	mb();
+	wake_up_interruptible(&ca->thread_queue);
+	return 0;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 IO interface functions */
+
+/**
+ * Real ioctl implementation.
+ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
+				      unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err = 0;
+	int slot;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	switch (cmd) {
+	case CA_RESET:
+		for (slot = 0; slot < ca->slot_count; slot++) {
+			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
+				dvb_ca_en50221_slot_shutdown(ca, slot);
+				if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+					dvb_ca_en50221_camchange_irq(ca->pub,
+								     slot,
+								     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+			}
+		}
+		ca->next_read_slot = 0;
+		dvb_ca_en50221_thread_wakeup(ca);
+		break;
+
+	case CA_GET_CAP: {
+		struct ca_caps *caps = (struct ca_caps *) parg;
+
+		caps->slot_num = ca->slot_count;
+		caps->slot_type = CA_CI_LINK;
+		caps->descr_num = 0;
+		caps->descr_type = 0;
+		break;
+	}
+
+	case CA_GET_SLOT_INFO: {
+		struct ca_slot_info *info = (struct ca_slot_info *) parg;
+
+		if ((info->num > ca->slot_count) || (info->num < 0))
+			return -EINVAL;
+
+		info->type = CA_CI_LINK;
+		info->flags = 0;
+		if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE)
+			&& (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
+			info->flags = CA_CI_MODULE_PRESENT;
+		}
+		if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+			info->flags |= CA_CI_MODULE_READY;
+		}
+		break;
+	}
+
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+
+/**
+ * Wrapper for ioctl implementation.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file,
+				   unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+}
+
+
+/**
+ * Implementation of write() syscall.
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_write(struct file *file,
+				       const char __user * buf, size_t count, loff_t * ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	u8 slot, connection_id;
+	int status;
+	char fragbuf[HOST_LINK_BUF_SIZE];
+	int fragpos = 0;
+	int fraglen;
+	unsigned long timeout;
+	int written;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+	if (count < 2)
+		return -EINVAL;
+
+	/* extract slot & connection id */
+	if (copy_from_user(&slot, buf, 1))
+		return -EFAULT;
+	if (copy_from_user(&connection_id, buf + 1, 1))
+		return -EFAULT;
+	buf += 2;
+	count -= 2;
+
+	/* check if the slot is actually running */
+	if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
+		return -EINVAL;
+
+	/* fragment the packets & store in the buffer */
+	while (fragpos < count) {
+		fraglen = ca->slot_info[slot].link_buf_size - 2;
+		if ((count - fragpos) < fraglen)
+			fraglen = count - fragpos;
+
+		fragbuf[0] = connection_id;
+		fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+		if ((status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen)) != 0)
+			goto exit;
+
+		timeout = jiffies + HZ / 2;
+		written = 0;
+		while (!time_after(jiffies, timeout)) {
+			/* check the CAM hasn't been removed/reset in the meantime */
+			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
+				status = -EIO;
+				goto exit;
+			}
+
+			status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+			if (status == (fraglen + 2)) {
+				written = 1;
+				break;
+			}
+			if (status != -EAGAIN)
+				goto exit;
+
+			msleep(1);
+		}
+		if (!written) {
+			status = -EIO;
+			goto exit;
+		}
+
+		fragpos += fraglen;
+	}
+	status = count + 2;
+
+exit:
+	return status;
+}
+
+
+/**
+ * Condition for waking up in dvb_ca_en50221_io_read_condition
+ */
+static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, int *result, int *_slot)
+{
+	int slot;
+	int slot_count = 0;
+	int idx;
+	int fraglen;
+	int connection_id = -1;
+	int found = 0;
+	u8 hdr[2];
+
+	slot = ca->next_read_slot;
+	while ((slot_count < ca->slot_count) && (!found)) {
+		if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
+			goto nextslot;
+
+		down_read(&ca->slot_info[slot].sem);
+
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			return 0;
+		}
+
+		idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+		while (idx != -1) {
+			dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+			if (connection_id == -1)
+				connection_id = hdr[0];
+			if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
+				*_slot = slot;
+				found = 1;
+				break;
+			}
+
+			idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+		}
+
+		if (!found)
+			up_read(&ca->slot_info[slot].sem);
+
+	      nextslot:
+		slot = (slot + 1) % ca->slot_count;
+		slot_count++;
+	}
+
+	ca->next_read_slot = slot;
+	return found;
+}
+
+
+/**
+ * Implementation of read() syscall.
+ *
+ * @param file File structure.
+ * @param buf Destination buffer.
+ * @param count Size of destination buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
+				      size_t count, loff_t * ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int status;
+	int result = 0;
+	u8 hdr[2];
+	int slot;
+	int connection_id = -1;
+	size_t idx, idx2;
+	int last_fragment = 0;
+	size_t fraglen;
+	int pktlen;
+	int dispose = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+	if (count < 2)
+		return -EINVAL;
+
+	/* wait for some data */
+	if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {
+
+		/* if we're in nonblocking mode, exit immediately */
+		if (file->f_flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		/* wait for some data */
+		status = wait_event_interruptible(ca->wait_queue,
+						  dvb_ca_en50221_io_read_condition
+						  (ca, &result, &slot));
+	}
+	if ((status < 0) || (result < 0)) {
+		if (result)
+			return result;
+		return status;
+	}
+
+	idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+	pktlen = 2;
+	do {
+		if (idx == -1) {
+			printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
+			status = -EIO;
+			goto exit;
+		}
+
+		dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+		if (connection_id == -1)
+			connection_id = hdr[0];
+		if (hdr[0] == connection_id) {
+			if (pktlen < count) {
+				if ((pktlen + fraglen - 2) > count) {
+					fraglen = count - pktlen;
+				} else {
+					fraglen -= 2;
+				}
+
+				if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2,
+								      buf + pktlen, fraglen, 1)) < 0) {
+					goto exit;
+				}
+				pktlen += fraglen;
+			}
+
+			if ((hdr[1] & 0x80) == 0)
+				last_fragment = 1;
+			dispose = 1;
+		}
+
+		idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+		if (dispose)
+			dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
+		idx = idx2;
+		dispose = 0;
+	} while (!last_fragment);
+
+	hdr[0] = slot;
+	hdr[1] = connection_id;
+	if ((status = copy_to_user(buf, hdr, 2)) != 0)
+		goto exit;
+	status = pktlen;
+
+      exit:
+	up_read(&ca->slot_info[slot].sem);
+	return status;
+}
+
+
+/**
+ * Implementation of file open syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (!try_module_get(ca->pub->owner))
+		return -EIO;
+
+	err = dvb_generic_open(inode, file);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ca->slot_count; i++) {
+
+		if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+			down_write(&ca->slot_info[i].sem);
+			if (ca->slot_info[i].rx_buffer.data != NULL) {
+				dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+			}
+			up_write(&ca->slot_info[i].sem);
+		}
+	}
+
+	ca->open = 1;
+	dvb_ca_en50221_thread_update_delay(ca);
+	dvb_ca_en50221_thread_wakeup(ca);
+
+	return 0;
+}
+
+
+/**
+ * Implementation of file close syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* mark the CA device as closed */
+	ca->open = 0;
+	dvb_ca_en50221_thread_update_delay(ca);
+
+	err = dvb_generic_release(inode, file);
+
+	module_put(ca->pub->owner);
+
+	return 0;
+}
+
+
+/**
+ * Implementation of poll() syscall.
+ *
+ * @param file File concerned.
+ * @param wait poll wait table.
+ *
+ * @return Standard poll mask.
+ */
+static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	unsigned int mask = 0;
+	int slot;
+	int result = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+		up_read(&ca->slot_info[slot].sem);
+		mask |= POLLIN;
+	}
+
+	/* if there is something, return now */
+	if (mask)
+		return mask;
+
+	/* wait for something to happen */
+	poll_wait(file, &ca->wait_queue, wait);
+
+	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+		up_read(&ca->slot_info[slot].sem);
+		mask |= POLLIN;
+	}
+
+	return mask;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+
+
+static struct file_operations dvb_ca_fops = {
+	.owner = THIS_MODULE,
+	.read = dvb_ca_en50221_io_read,
+	.write = dvb_ca_en50221_io_write,
+	.ioctl = dvb_ca_en50221_io_ioctl,
+	.open = dvb_ca_en50221_io_open,
+	.release = dvb_ca_en50221_io_release,
+	.poll = dvb_ca_en50221_io_poll,
+};
+
+static struct dvb_device dvbdev_ca = {
+	.priv = NULL,
+	.users = 1,
+	.readers = 1,
+	.writers = 1,
+	.fops = &dvb_ca_fops,
+};
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+
+/**
+ * Initialise a new DVB CA EN50221 interface device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
+			struct dvb_ca_en50221 *pubca, int flags, int slot_count)
+{
+	int ret;
+	struct dvb_ca_private *ca = NULL;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (slot_count < 1)
+		return -EINVAL;
+
+	/* initialise the system data */
+	if ((ca =
+	     (struct dvb_ca_private *) kmalloc(sizeof(struct dvb_ca_private),
+					       GFP_KERNEL)) == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	memset(ca, 0, sizeof(struct dvb_ca_private));
+	ca->pub = pubca;
+	ca->flags = flags;
+	ca->slot_count = slot_count;
+	if ((ca->slot_info = kmalloc(sizeof(struct dvb_ca_slot) * slot_count, GFP_KERNEL)) == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	memset(ca->slot_info, 0, sizeof(struct dvb_ca_slot) * slot_count);
+	init_waitqueue_head(&ca->wait_queue);
+	ca->thread_pid = 0;
+	init_waitqueue_head(&ca->thread_queue);
+	ca->exit = 0;
+	ca->open = 0;
+	ca->wakeup = 0;
+	ca->next_read_slot = 0;
+	pubca->private = ca;
+
+	/* register the DVB device */
+	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
+	if (ret)
+		goto error;
+
+	/* now initialise each slot */
+	for (i = 0; i < slot_count; i++) {
+		memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
+		ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
+		atomic_set(&ca->slot_info[i].camchange_count, 0);
+		ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+		init_rwsem(&ca->slot_info[i].sem);
+	}
+
+	if (signal_pending(current)) {
+		ret = -EINTR;
+		goto error;
+	}
+	mb();
+
+	/* create a kthread for monitoring this CA device */
+
+	ret = kernel_thread(dvb_ca_en50221_thread, ca, 0);
+
+	if (ret < 0) {
+		printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
+		goto error;
+	}
+	ca->thread_pid = ret;
+	return 0;
+
+      error:
+	if (ca != NULL) {
+		if (ca->dvbdev != NULL)
+			dvb_unregister_device(ca->dvbdev);
+		kfree(ca->slot_info);
+		kfree(ca);
+	}
+	pubca->private = NULL;
+	return ret;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_release);
+
+
+
+/**
+ * Release a DVB CA EN50221 interface device.
+ *
+ * @param ca_dev The dvb_device_t instance for the CA device.
+ * @param ca The associated dvb_ca instance.
+ */
+void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* shutdown the thread if there was one */
+	if (ca->thread_pid) {
+		if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) {
+			printk("dvb_ca_release adapter %d: thread PID %d already died\n",
+			       ca->dvbdev->adapter->num, ca->thread_pid);
+		} else {
+			ca->exit = 1;
+			mb();
+			dvb_ca_en50221_thread_wakeup(ca);
+			wait_event_interruptible(ca->thread_queue, ca->thread_pid == 0);
+		}
+	}
+
+	for (i = 0; i < ca->slot_count; i++) {
+		dvb_ca_en50221_slot_shutdown(ca, i);
+	}
+	kfree(ca->slot_info);
+	dvb_unregister_device(ca->dvbdev);
+	kfree(ca);
+	pubca->private = NULL;
+}
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
new file mode 100644
index 0000000..8467e63
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -0,0 +1,134 @@
+/*
+ * dvb_ca.h: generic DVB functions for EN50221 CA interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _DVB_CA_EN50221_H_
+#define _DVB_CA_EN50221_H_
+
+#include <linux/list.h>
+#include <linux/dvb/ca.h>
+
+#include "dvbdev.h"
+
+#define DVB_CA_EN50221_POLL_CAM_PRESENT	1
+#define DVB_CA_EN50221_POLL_CAM_CHANGED	2
+#define DVB_CA_EN50221_POLL_CAM_READY		4
+
+#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE	1
+#define DVB_CA_EN50221_FLAG_IRQ_FR		2
+#define DVB_CA_EN50221_FLAG_IRQ_DA		4
+
+#define DVB_CA_EN50221_CAMCHANGE_REMOVED		0
+#define DVB_CA_EN50221_CAMCHANGE_INSERTED		1
+
+
+
+/* Structure describing a CA interface */
+struct dvb_ca_en50221 {
+
+	/* the module owning this structure */
+	struct module* owner;
+
+	/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
+	 * they may be called from several threads at once */
+
+	/* functions for accessing attribute memory on the CAM */
+	int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
+	int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
+
+	/* functions for accessing the control interface on the CAM */
+	int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address);
+	int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value);
+
+	/* Functions for controlling slots */
+	int (*slot_reset)(struct dvb_ca_en50221* ca, int slot);
+	int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot);
+	int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot);
+
+	/*
+	* Poll slot status.
+	* Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
+	*/
+	int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open);
+
+	/* private data, used by caller */
+	void* data;
+
+	/* Opaque data used by the dvb_ca core. Do not modify! */
+	void* private;
+};
+
+
+
+
+/* ******************************************************************************** */
+/* Functions for reporting IRQ events */
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type);
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot);
+
+/**
+ * An FR or a DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot);
+
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+/**
+ * Initialise a new DVB CA device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count);
+
+/**
+ * Release a DVB CA device.
+ *
+ * @param ca The associated dvb_ca instance.
+ */
+extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca);
+
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
new file mode 100644
index 0000000..ac9889d
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -0,0 +1,1294 @@
+/*
+ * dvb_demux.c - DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Ralph  Metzler <ralph@convergence.de>
+ *		       & Marcus Metzler <marcus@convergence.de>
+ *			 for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/crc32.h>
+#include <asm/uaccess.h>
+
+#include "dvb_demux.h"
+
+#define NOBUFS
+/*
+** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
+*/
+// #define DVB_DEMUX_SECTION_LOSS_LOG
+
+
+static LIST_HEAD(dmx_muxs);
+
+
+static int dmx_register_demux(struct dmx_demux *demux)
+{
+	demux->users = 0;
+	list_add(&demux->reg_list, &dmx_muxs);
+	return 0;
+}
+
+static int dmx_unregister_demux(struct dmx_demux* demux)
+{
+	struct list_head *pos, *n, *head=&dmx_muxs;
+
+	list_for_each_safe (pos, n, head) {
+		if (DMX_DIR_ENTRY(pos) == demux) {
+			if (demux->users>0)
+				return -EINVAL;
+			list_del(pos);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+
+/******************************************************************************
+ * static inlined helper functions
+ ******************************************************************************/
+
+
+static inline u16 section_length(const u8 *buf)
+{
+	return 3+((buf[1]&0x0f)<<8)+buf[2];
+}
+
+
+static inline u16 ts_pid(const u8 *buf)
+{
+	return ((buf[1]&0x1f)<<8)+buf[2];
+}
+
+
+static inline u8 payload(const u8 *tsp)
+{
+	if (!(tsp[3] & 0x10)) // no payload?
+		return 0;
+	if (tsp[3] & 0x20) {  // adaptation field?
+		if (tsp[4] > 183)    // corrupted data?
+			return 0;
+		else
+			return 184-1-tsp[4];
+	}
+	return 184;
+}
+
+
+static u32 dvb_dmx_crc32 (struct dvb_demux_feed *f, const u8 *src, size_t len)
+{
+	return (f->feed.sec.crc_val = crc32_be (f->feed.sec.crc_val, src, len));
+}
+
+
+static void dvb_dmx_memcopy (struct dvb_demux_feed *f, u8 *d, const u8 *s, size_t len)
+{
+	memcpy (d, s, len);
+}
+
+
+/******************************************************************************
+ * Software filter functions
+ ******************************************************************************/
+
+static inline int dvb_dmx_swfilter_payload (struct dvb_demux_feed *feed, const u8 *buf)
+{
+	int count = payload(buf);
+	int p;
+	//int ccok;
+	//u8 cc;
+
+	if (count == 0)
+		return -1;
+
+	p = 188-count;
+
+	/*
+	cc=buf[3]&0x0f;
+	ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0;
+	dvbdmxfeed->cc=cc;
+	if (!ccok)
+		printk("missed packet!\n");
+	*/
+
+	if (buf[1] & 0x40)  // PUSI ?
+		feed->peslen = 0xfffa;
+
+	feed->peslen += count;
+
+	return feed->cb.ts (&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+
+static int dvb_dmx_swfilter_sectionfilter (struct dvb_demux_feed *feed,
+				    struct dvb_demux_filter *f)
+{
+	u8 neq = 0;
+	int i;
+
+	for (i=0; i<DVB_DEMUX_MASK_MAX; i++) {
+		u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i];
+
+		if (f->maskandmode[i] & xor)
+			return 0;
+
+		neq |= f->maskandnotmode[i] & xor;
+	}
+
+	if (f->doneq && !neq)
+		return 0;
+
+	return feed->cb.sec (feed->feed.sec.secbuf, feed->feed.sec.seclen,
+			     NULL, 0, &f->filter, DMX_OK);
+}
+
+
+static inline int dvb_dmx_swfilter_section_feed (struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct dvb_demux_filter *f = feed->filter;
+	struct dmx_section_feed *sec = &feed->feed.sec;
+	int section_syntax_indicator;
+
+	if (!sec->is_filtering)
+		return 0;
+
+	if (!f)
+		return 0;
+
+	if (sec->check_crc) {
+		section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
+		if (section_syntax_indicator &&
+		    demux->check_crc32(feed, sec->secbuf, sec->seclen))
+			return -1;
+	}
+
+	do {
+		if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0)
+			return -1;
+	} while ((f = f->next) && sec->is_filtering);
+
+	sec->seclen = 0;
+
+	return 0;
+}
+
+
+static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
+{
+	struct dmx_section_feed *sec = &feed->feed.sec;
+
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+	if(sec->secbufp < sec->tsfeedp)
+	{
+		int i, n = sec->tsfeedp - sec->secbufp;
+
+		/* section padding is done with 0xff bytes entirely.
+		** due to speed reasons, we won't check all of them
+		** but just first and last
+		*/
+		if(sec->secbuf[0] != 0xff || sec->secbuf[n-1] != 0xff)
+		{
+			printk("dvb_demux.c section ts padding loss: %d/%d\n",
+			       n, sec->tsfeedp);
+			printk("dvb_demux.c pad data:");
+			for(i = 0; i < n; i++)
+				printk(" %02x", sec->secbuf[i]);
+			printk("\n");
+		}
+	}
+#endif
+
+	sec->tsfeedp = sec->secbufp = sec->seclen = 0;
+	sec->secbuf = sec->secbuf_base;
+}
+
+/*
+** Losless Section Demux 1.4.1 by Emard
+** Valsecchi Patrick:
+**  - middle of section A  (no PUSI)
+**  - end of section A and start of section B
+**    (with PUSI pointing to the start of the second section)
+**
+**  In this case, without feed->pusi_seen you'll receive a garbage section
+**  consisting of the end of section A. Basically because tsfeedp
+**  is incemented and the use=0 condition is not raised
+**  when the second packet arrives.
+**
+** Fix:
+** when demux is started, let feed->pusi_seen = 0 to
+** prevent initial feeding of garbage from the end of
+** previous section. When you for the first time see PUSI=1
+** then set feed->pusi_seen = 1
+*/
+static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, const u8 *buf, u8 len)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct dmx_section_feed *sec = &feed->feed.sec;
+	u16 limit, seclen, n;
+
+	if(sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
+		return 0;
+
+	if(sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE)
+	{
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		printk("dvb_demux.c section buffer full loss: %d/%d\n",
+		       sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, DMX_MAX_SECFEED_SIZE);
+#endif
+		len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
+	}
+
+	if(len <= 0)
+		return 0;
+
+	demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
+	sec->tsfeedp += len;
+
+	/* -----------------------------------------------------
+	** Dump all the sections we can find in the data (Emard)
+	*/
+
+	limit = sec->tsfeedp;
+	if(limit > DMX_MAX_SECFEED_SIZE)
+		return -1; /* internal error should never happen */
+
+	/* to be sure always set secbuf */
+	sec->secbuf = sec->secbuf_base + sec->secbufp;
+
+	for(n = 0; sec->secbufp + 2 < limit; n++)
+	{
+		seclen = section_length(sec->secbuf);
+		if(seclen <= 0 || seclen > DMX_MAX_SECFEED_SIZE
+		   || seclen + sec->secbufp > limit)
+			return 0;
+		sec->seclen = seclen;
+		sec->crc_val = ~0;
+		/* dump [secbuf .. secbuf+seclen) */
+		if(feed->pusi_seen)
+			dvb_dmx_swfilter_section_feed(feed);
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		else
+			printk("dvb_demux.c pusi not seen, discarding section data\n");
+#endif
+		sec->secbufp += seclen; /* secbufp and secbuf moving together is */
+		sec->secbuf += seclen; /* redundand but saves pointer arithmetic */
+	}
+
+	return 0;
+}
+
+
+static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, const u8 *buf)
+{
+	u8 p, count;
+	int ccok, dc_i = 0;
+	u8 cc;
+
+	count = payload(buf);
+
+	if (count == 0)  /* count == 0 if no payload or out of range */
+		return -1;
+
+	p = 188 - count; /* payload start */
+
+	cc = buf[3] & 0x0f;
+	ccok = ((feed->cc + 1) & 0x0f) == cc;
+	feed->cc = cc;
+
+	if (buf[3] & 0x20) {
+		/* adaption field present, check for discontinuity_indicator */
+		if ((buf[4] > 0) && (buf[5] & 0x80))
+			dc_i = 1;
+	}
+
+	if (!ccok || dc_i) {
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		printk("dvb_demux.c discontinuity detected %d bytes lost\n", count);
+		/* those bytes under sume circumstances will again be reported
+		** in the following dvb_dmx_swfilter_section_new
+		*/
+#endif
+		/* Discontinuity detected. Reset pusi_seen = 0 to
+		** stop feeding of suspicious data until next PUSI=1 arrives
+		*/
+		feed->pusi_seen = 0;
+		dvb_dmx_swfilter_section_new(feed);
+		return 0;
+	}
+
+	if (buf[1] & 0x40) {
+		// PUSI=1 (is set), section boundary is here
+		if (count > 1 && buf[p] < count) {
+			const u8 *before = buf+p+1;
+			u8 before_len = buf[p];
+			const u8 *after = before+before_len;
+			u8 after_len = count-1-before_len;
+
+			dvb_dmx_swfilter_section_copy_dump(feed, before, before_len);
+			/* before start of new section, set pusi_seen = 1 */
+			feed->pusi_seen = 1;
+			dvb_dmx_swfilter_section_new(feed);
+			dvb_dmx_swfilter_section_copy_dump(feed, after, after_len);
+		}
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		else
+			if (count > 0)
+				printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
+#endif
+	} else {
+		// PUSI=0 (is not set), no section boundary
+		const u8 *entire = buf+p;
+		u8 entire_len = count;
+
+		dvb_dmx_swfilter_section_copy_dump(feed, entire, entire_len);
+	}
+	return 0;
+}
+
+
+static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, const u8 *buf)
+{
+	switch(feed->type) {
+	case DMX_TYPE_TS:
+		if (!feed->feed.ts.is_filtering)
+			break;
+		if (feed->ts_type & TS_PACKET) {
+			if (feed->ts_type & TS_PAYLOAD_ONLY)
+				dvb_dmx_swfilter_payload(feed, buf);
+			else
+				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		}
+		if (feed->ts_type & TS_DECODER)
+			if (feed->demux->write_to_decoder)
+				feed->demux->write_to_decoder(feed, buf, 188);
+		break;
+
+	case DMX_TYPE_SEC:
+		if (!feed->feed.sec.is_filtering)
+			break;
+		if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
+			feed->feed.sec.seclen = feed->feed.sec.secbufp=0;
+		break;
+
+	default:
+		break;
+	}
+}
+
+#define DVR_FEED(f)							\
+	(((f)->type == DMX_TYPE_TS) &&					\
+	((f)->feed.ts.is_filtering) &&					\
+	(((f)->ts_type & (TS_PACKET|TS_PAYLOAD_ONLY)) == TS_PACKET))
+
+static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+{
+	struct dvb_demux_feed *feed;
+	struct list_head *pos, *head=&demux->feed_list;
+	u16 pid = ts_pid(buf);
+	int dvr_done = 0;
+
+	list_for_each(pos, head) {
+		feed = list_entry(pos, struct dvb_demux_feed, list_head);
+
+		if ((feed->pid != pid) && (feed->pid != 0x2000))
+			continue;
+
+		/* copy each packet only once to the dvr device, even
+		 * if a PID is in multiple filters (e.g. video + PCR) */
+		if ((DVR_FEED(feed)) && (dvr_done++))
+			continue;
+
+		if (feed->pid == pid) {
+			dvb_dmx_swfilter_packet_type(feed, buf);
+			if (DVR_FEED(feed))
+				continue;
+		}
+
+		if (feed->pid == 0x2000)
+			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+	}
+}
+
+void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	spin_lock(&demux->lock);
+
+	while (count--) {
+		if(buf[0] == 0x47) {
+		        dvb_dmx_swfilter_packet(demux, buf);
+		}
+		buf += 188;
+	}
+
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
+
+
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	int p = 0, i, j;
+
+	spin_lock(&demux->lock);
+
+	if ((i = demux->tsbufp)) {
+		if (count < (j=188-i)) {
+			memcpy(&demux->tsbuf[i], buf, count);
+			demux->tsbufp += count;
+			goto bailout;
+		}
+		memcpy(&demux->tsbuf[i], buf, j);
+		if (demux->tsbuf[0] == 0x47)
+			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+		demux->tsbufp = 0;
+		p += j;
+	}
+
+	while (p < count) {
+		if (buf[p] == 0x47) {
+			if (count-p >= 188) {
+				dvb_dmx_swfilter_packet(demux, buf+p);
+				p += 188;
+			} else {
+				i = count-p;
+				memcpy(demux->tsbuf, buf+p, i);
+				demux->tsbufp=i;
+				goto bailout;
+			}
+		} else
+			p++;
+	}
+
+bailout:
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter);
+
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	int p = 0,i, j;
+	u8 tmppack[188];
+	spin_lock(&demux->lock);
+
+	if ((i = demux->tsbufp)) {
+		if (count < (j=204-i)) {
+			memcpy(&demux->tsbuf[i], buf, count);
+			demux->tsbufp += count;
+			goto bailout;
+		}
+		memcpy(&demux->tsbuf[i], buf, j);
+		if ((demux->tsbuf[0] == 0x47)|(demux->tsbuf[0]==0xB8))  {
+			memcpy(tmppack, demux->tsbuf, 188);
+			if (tmppack[0] == 0xB8) tmppack[0] = 0x47;
+			dvb_dmx_swfilter_packet(demux, tmppack);
+		}
+		demux->tsbufp = 0;
+		p += j;
+	}
+
+	while (p < count) {
+		if ((buf[p] == 0x47)|(buf[p] == 0xB8)) {
+			if (count-p >= 204) {
+				memcpy(tmppack, buf+p, 188);
+				if (tmppack[0] == 0xB8) tmppack[0] = 0x47;
+				dvb_dmx_swfilter_packet(demux, tmppack);
+				p += 204;
+			} else {
+				i = count-p;
+				memcpy(demux->tsbuf, buf+p, i);
+				demux->tsbufp=i;
+				goto bailout;
+			}
+		} else {
+			p++;
+		}
+	}
+
+bailout:
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+
+
+static struct dvb_demux_filter * dvb_dmx_filter_alloc(struct dvb_demux *demux)
+{
+	int i;
+
+	for (i=0; i<demux->filternum; i++)
+		if (demux->filter[i].state == DMX_STATE_FREE)
+			break;
+
+	if (i == demux->filternum)
+		return NULL;
+
+	demux->filter[i].state = DMX_STATE_ALLOCATED;
+
+	return &demux->filter[i];
+}
+
+static struct dvb_demux_feed * dvb_dmx_feed_alloc(struct dvb_demux *demux)
+{
+	int i;
+
+	for (i=0; i<demux->feednum; i++)
+		if (demux->feed[i].state == DMX_STATE_FREE)
+			break;
+
+	if (i == demux->feednum)
+		return NULL;
+
+	demux->feed[i].state = DMX_STATE_ALLOCATED;
+
+	return &demux->feed[i];
+}
+
+static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux_feed *entry;
+
+	list_for_each_entry(entry, &feed->demux->feed_list, list_head)
+		if (entry == feed)
+			return 1;
+
+	return 0;
+}
+
+static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
+{
+	spin_lock_irq(&feed->demux->lock);
+	if (dvb_demux_feed_find(feed)) {
+		printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
+				__FUNCTION__, feed->type, feed->state, feed->pid);
+		goto out;
+	}
+
+	list_add(&feed->list_head, &feed->demux->feed_list);
+out:
+	spin_unlock_irq(&feed->demux->lock);
+}
+
+static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
+{
+	spin_lock_irq(&feed->demux->lock);
+	if (!(dvb_demux_feed_find(feed))) {
+		printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
+				__FUNCTION__, feed->type, feed->state, feed->pid);
+		goto out;
+	}
+
+	list_del(&feed->list_head);
+out:
+	spin_unlock_irq(&feed->demux->lock);
+}
+
+static int dmx_ts_feed_set (struct dmx_ts_feed* ts_feed, u16 pid, int ts_type,
+		     enum dmx_ts_pes pes_type, size_t callback_length,
+		     size_t circular_buffer_size, int descramble,
+		     struct timespec timeout)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+
+	if (pid > DMX_MAX_PID)
+		return -EINVAL;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (ts_type & TS_DECODER) {
+		if (pes_type >= DMX_TS_PES_OTHER) {
+			up(&demux->mutex);
+			return -EINVAL;
+		}
+
+		if (demux->pesfilter[pes_type] &&
+		    demux->pesfilter[pes_type] != feed) {
+			up(&demux->mutex);
+			return -EINVAL;
+		}
+
+		demux->pesfilter[pes_type] = feed;
+		demux->pids[pes_type] = pid;
+	}
+
+	dvb_demux_feed_add(feed);
+
+	feed->pid = pid;
+	feed->buffer_size = circular_buffer_size;
+	feed->descramble = descramble;
+	feed->timeout = timeout;
+	feed->cb_length = callback_length;
+	feed->ts_type = ts_type;
+	feed->pes_type = pes_type;
+
+	if (feed->descramble) {
+		up(&demux->mutex);
+		return -ENOSYS;
+	}
+
+	if (feed->buffer_size) {
+#ifdef NOBUFS
+		feed->buffer=NULL;
+#else
+		feed->buffer = vmalloc(feed->buffer_size);
+		if (!feed->buffer) {
+			up(&demux->mutex);
+			return -ENOMEM;
+		}
+#endif
+	}
+
+	feed->state = DMX_STATE_READY;
+	up(&demux->mutex);
+
+	return 0;
+}
+
+
+static int dmx_ts_feed_start_filtering(struct dmx_ts_feed* ts_feed)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+	int ret;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+	if (!demux->start_feed) {
+		up(&demux->mutex);
+		return -ENODEV;
+	}
+
+	if ((ret = demux->start_feed(feed)) < 0) {
+		up(&demux->mutex);
+		return ret;
+	}
+
+	spin_lock_irq(&demux->lock);
+	ts_feed->is_filtering = 1;
+	feed->state = DMX_STATE_GO;
+	spin_unlock_irq(&demux->lock);
+	up(&demux->mutex);
+
+	return 0;
+}
+
+static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed* ts_feed)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+	int ret;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state < DMX_STATE_GO) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+	if (!demux->stop_feed) {
+		up(&demux->mutex);
+		return -ENODEV;
+	}
+
+	ret = demux->stop_feed(feed);
+
+	spin_lock_irq(&demux->lock);
+	ts_feed->is_filtering = 0;
+	feed->state = DMX_STATE_ALLOCATED;
+	spin_unlock_irq(&demux->lock);
+	up(&demux->mutex);
+
+	return ret;
+}
+
+static int dvbdmx_allocate_ts_feed (struct dmx_demux *dmx, struct dmx_ts_feed **ts_feed,
+			     dmx_ts_cb callback)
+{
+	struct dvb_demux *demux = (struct dvb_demux *) dmx;
+	struct dvb_demux_feed *feed;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (!(feed = dvb_dmx_feed_alloc(demux))) {
+		up(&demux->mutex);
+		return -EBUSY;
+	}
+
+	feed->type = DMX_TYPE_TS;
+	feed->cb.ts = callback;
+	feed->demux = demux;
+	feed->pid = 0xffff;
+	feed->peslen = 0xfffa;
+	feed->buffer = NULL;
+
+	(*ts_feed) = &feed->feed.ts;
+	(*ts_feed)->parent = dmx;
+	(*ts_feed)->priv = NULL;
+	(*ts_feed)->is_filtering = 0;
+	(*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
+	(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
+	(*ts_feed)->set = dmx_ts_feed_set;
+
+
+	if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
+		feed->state = DMX_STATE_FREE;
+		up(&demux->mutex);
+		return -EBUSY;
+	}
+
+	feed->filter->type = DMX_TYPE_TS;
+	feed->filter->feed = feed;
+	feed->filter->state = DMX_STATE_READY;
+
+	up(&demux->mutex);
+
+	return 0;
+}
+
+static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, struct dmx_ts_feed *ts_feed)
+{
+	struct dvb_demux *demux = (struct dvb_demux *) dmx;
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state == DMX_STATE_FREE) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+#ifndef NOBUFS
+	vfree(feed->buffer);
+	feed->buffer=0;
+#endif
+
+	feed->state = DMX_STATE_FREE;
+	feed->filter->state = DMX_STATE_FREE;
+
+	dvb_demux_feed_del(feed);
+
+	feed->pid = 0xffff;
+
+	if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
+		demux->pesfilter[feed->pes_type] = NULL;
+
+	up(&demux->mutex);
+	return 0;
+}
+
+
+/******************************************************************************
+ * dmx_section_feed API calls
+ ******************************************************************************/
+
+static int dmx_section_feed_allocate_filter(struct dmx_section_feed* feed,
+				     struct dmx_section_filter** filter)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
+	struct dvb_demux_filter *dvbdmxfilter;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
+	if (!dvbdmxfilter) {
+		up(&dvbdemux->mutex);
+		return -EBUSY;
+	}
+
+	spin_lock_irq(&dvbdemux->lock);
+	*filter = &dvbdmxfilter->filter;
+	(*filter)->parent = feed;
+	(*filter)->priv = NULL;
+	dvbdmxfilter->feed = dvbdmxfeed;
+	dvbdmxfilter->type = DMX_TYPE_SEC;
+	dvbdmxfilter->state = DMX_STATE_READY;
+	dvbdmxfilter->next = dvbdmxfeed->filter;
+	dvbdmxfeed->filter = dvbdmxfilter;
+	spin_unlock_irq(&dvbdemux->lock);
+
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dmx_section_feed_set(struct dmx_section_feed* feed,
+			 u16 pid, size_t circular_buffer_size,
+			 int descramble, int check_crc)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	if (pid > 0x1fff)
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	dvb_demux_feed_add(dvbdmxfeed);
+
+	dvbdmxfeed->pid = pid;
+	dvbdmxfeed->buffer_size = circular_buffer_size;
+	dvbdmxfeed->descramble = descramble;
+	if (dvbdmxfeed->descramble) {
+		up(&dvbdmx->mutex);
+		return -ENOSYS;
+	}
+
+	dvbdmxfeed->feed.sec.check_crc = check_crc;
+
+#ifdef NOBUFS
+	dvbdmxfeed->buffer = NULL;
+#else
+	dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size);
+	if (!dvbdmxfeed->buffer) {
+		up(&dvbdmx->mutex);
+		return -ENOMEM;
+	}
+#endif
+
+	dvbdmxfeed->state = DMX_STATE_READY;
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
+{
+	int i;
+	struct dvb_demux_filter *f;
+	struct dmx_section_filter *sf;
+	u8 mask, mode, doneq;
+
+	if (!(f=dvbdmxfeed->filter))
+		return;
+	do {
+		sf = &f->filter;
+		doneq = 0;
+		for (i=0; i<DVB_DEMUX_MASK_MAX; i++) {
+			mode = sf->filter_mode[i];
+			mask = sf->filter_mask[i];
+			f->maskandmode[i] = mask & mode;
+			doneq |= f->maskandnotmode[i] = mask & ~mode;
+		}
+		f->doneq = doneq ? 1 : 0;
+	} while ((f = f->next));
+}
+
+
+static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	int ret;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->is_filtering) {
+		up(&dvbdmx->mutex);
+		return -EBUSY;
+	}
+
+	if (!dvbdmxfeed->filter) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	dvbdmxfeed->feed.sec.tsfeedp = 0;
+	dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+	dvbdmxfeed->feed.sec.secbufp = 0;
+	dvbdmxfeed->feed.sec.seclen = 0;
+
+	if (!dvbdmx->start_feed) {
+		up(&dvbdmx->mutex);
+		return -ENODEV;
+	}
+
+	prepare_secfilters(dvbdmxfeed);
+
+	if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+		up(&dvbdmx->mutex);
+		return ret;
+	}
+
+	spin_lock_irq(&dvbdmx->lock);
+	feed->is_filtering = 1;
+	dvbdmxfeed->state = DMX_STATE_GO;
+	spin_unlock_irq(&dvbdmx->lock);
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+static int dmx_section_feed_stop_filtering(struct dmx_section_feed* feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	int ret;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (!dvbdmx->stop_feed) {
+		up(&dvbdmx->mutex);
+		return -ENODEV;
+	}
+
+	ret = dvbdmx->stop_feed(dvbdmxfeed);
+
+	spin_lock_irq(&dvbdmx->lock);
+	dvbdmxfeed->state = DMX_STATE_READY;
+	feed->is_filtering = 0;
+	spin_unlock_irq(&dvbdmx->lock);
+
+	up(&dvbdmx->mutex);
+	return ret;
+}
+
+
+static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
+				struct dmx_section_filter* filter)
+{
+	struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *) filter, *f;
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (dvbdmxfilter->feed != dvbdmxfeed) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	if (feed->is_filtering)
+		feed->stop_filtering(feed);
+
+	spin_lock_irq(&dvbdmx->lock);
+	f = dvbdmxfeed->filter;
+
+	if (f == dvbdmxfilter) {
+		dvbdmxfeed->filter = dvbdmxfilter->next;
+	} else {
+		while(f->next != dvbdmxfilter)
+			f = f->next;
+		f->next = f->next->next;
+	}
+
+	dvbdmxfilter->state = DMX_STATE_FREE;
+	spin_unlock_irq(&dvbdmx->lock);
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
+					struct dmx_section_feed **feed,
+					dmx_section_cb callback)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *) demux;
+	struct dvb_demux_feed *dvbdmxfeed;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+		up(&dvbdmx->mutex);
+		return -EBUSY;
+	}
+
+	dvbdmxfeed->type = DMX_TYPE_SEC;
+	dvbdmxfeed->cb.sec = callback;
+	dvbdmxfeed->demux = dvbdmx;
+	dvbdmxfeed->pid = 0xffff;
+	dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+	dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
+	dvbdmxfeed->feed.sec.tsfeedp = 0;
+	dvbdmxfeed->filter = NULL;
+	dvbdmxfeed->buffer = NULL;
+
+	(*feed)=&dvbdmxfeed->feed.sec;
+	(*feed)->is_filtering = 0;
+	(*feed)->parent = demux;
+	(*feed)->priv = NULL;
+
+	(*feed)->set = dmx_section_feed_set;
+	(*feed)->allocate_filter = dmx_section_feed_allocate_filter;
+	(*feed)->start_filtering = dmx_section_feed_start_filtering;
+	(*feed)->stop_filtering = dmx_section_feed_stop_filtering;
+	(*feed)->release_filter = dmx_section_feed_release_filter;
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+static int dvbdmx_release_section_feed(struct dmx_demux *demux,
+				       struct dmx_section_feed *feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = (struct dvb_demux *) demux;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (dvbdmxfeed->state==DMX_STATE_FREE) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+#ifndef NOBUFS
+	vfree(dvbdmxfeed->buffer);
+	dvbdmxfeed->buffer=0;
+#endif
+	dvbdmxfeed->state=DMX_STATE_FREE;
+
+	dvb_demux_feed_del(dvbdmxfeed);
+
+	dvbdmxfeed->pid = 0xffff;
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+/******************************************************************************
+ * dvb_demux kernel data API calls
+ ******************************************************************************/
+
+static int dvbdmx_open(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)
+		return -EUSERS;
+
+	dvbdemux->users++;
+	return 0;
+}
+
+
+static int dvbdmx_close(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (dvbdemux->users == 0)
+		return -ENODEV;
+
+	dvbdemux->users--;
+	//FIXME: release any unneeded resources if users==0
+	return 0;
+}
+
+
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
+{
+	struct dvb_demux *dvbdemux=(struct dvb_demux *) demux;
+
+	if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+	dvb_dmx_swfilter(dvbdemux, buf, count);
+	up(&dvbdemux->mutex);
+
+	if (signal_pending(current))
+		return -EINTR;
+	return count;
+}
+
+
+static int dvbdmx_add_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+	struct list_head *head = &dvbdemux->frontend_list;
+
+	list_add(&(frontend->connectivity_list), head);
+
+	return 0;
+}
+
+
+static int dvbdmx_remove_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+	struct list_head *pos, *n, *head = &dvbdemux->frontend_list;
+
+	list_for_each_safe (pos, n, head) {
+		if (DMX_FE_ENTRY(pos) == frontend) {
+			list_del(pos);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+
+static struct list_head * dvbdmx_get_frontends(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (list_empty(&dvbdemux->frontend_list))
+		return NULL;
+	return &dvbdemux->frontend_list;
+}
+
+
+static int dvbdmx_connect_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (demux->frontend)
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	demux->frontend = frontend;
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	demux->frontend = NULL;
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 *pids)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	memcpy(pids, dvbdemux->pids, 5*sizeof(u16));
+	return 0;
+}
+
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux)
+{
+	int i, err;
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dvbdemux->users = 0;
+	dvbdemux->filter = vmalloc(dvbdemux->filternum*sizeof(struct dvb_demux_filter));
+
+	if (!dvbdemux->filter)
+		return -ENOMEM;
+
+	dvbdemux->feed = vmalloc(dvbdemux->feednum*sizeof(struct dvb_demux_feed));
+	if (!dvbdemux->feed) {
+		vfree(dvbdemux->filter);
+		return -ENOMEM;
+	}
+	for (i=0; i<dvbdemux->filternum; i++) {
+		dvbdemux->filter[i].state = DMX_STATE_FREE;
+		dvbdemux->filter[i].index = i;
+	}
+	for (i=0; i<dvbdemux->feednum; i++) {
+		dvbdemux->feed[i].state = DMX_STATE_FREE;
+		dvbdemux->feed[i].index = i;
+	}
+	dvbdemux->frontend_list.next=
+	  dvbdemux->frontend_list.prev=
+	    &dvbdemux->frontend_list;
+	for (i=0; i<DMX_TS_PES_OTHER; i++) {
+		dvbdemux->pesfilter[i] = NULL;
+		dvbdemux->pids[i] = 0xffff;
+	}
+
+	INIT_LIST_HEAD(&dvbdemux->feed_list);
+
+	dvbdemux->playing = 0;
+	dvbdemux->recording = 0;
+	dvbdemux->tsbufp = 0;
+
+	if (!dvbdemux->check_crc32)
+		dvbdemux->check_crc32 = dvb_dmx_crc32;
+
+	 if (!dvbdemux->memcopy)
+		 dvbdemux->memcopy = dvb_dmx_memcopy;
+
+	dmx->frontend = NULL;
+	dmx->reg_list.prev = dmx->reg_list.next = &dmx->reg_list;
+	dmx->priv = (void *) dvbdemux;
+	dmx->open = dvbdmx_open;
+	dmx->close = dvbdmx_close;
+	dmx->write = dvbdmx_write;
+	dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
+	dmx->release_ts_feed = dvbdmx_release_ts_feed;
+	dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
+	dmx->release_section_feed = dvbdmx_release_section_feed;
+
+	dmx->descramble_mac_address = NULL;
+	dmx->descramble_section_payload = NULL;
+
+	dmx->add_frontend = dvbdmx_add_frontend;
+	dmx->remove_frontend = dvbdmx_remove_frontend;
+	dmx->get_frontends = dvbdmx_get_frontends;
+	dmx->connect_frontend = dvbdmx_connect_frontend;
+	dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
+	dmx->get_pes_pids = dvbdmx_get_pes_pids;
+
+	sema_init(&dvbdemux->mutex, 1);
+	spin_lock_init(&dvbdemux->lock);
+
+	if ((err = dmx_register_demux(dmx)) < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_init);
+
+
+int dvb_dmx_release(struct dvb_demux *dvbdemux)
+{
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dmx_unregister_demux(dmx);
+	vfree(dvbdemux->filter);
+	vfree(dvbdemux->feed);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
new file mode 100644
index 0000000..c09beb3
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -0,0 +1,146 @@
+/*
+ * dvb_demux.h: DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
+ *                         for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#ifndef _DVB_DEMUX_H_
+#define _DVB_DEMUX_H_
+
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+
+#include "demux.h"
+
+#define DMX_TYPE_TS  0
+#define DMX_TYPE_SEC 1
+#define DMX_TYPE_PES 2
+
+#define DMX_STATE_FREE      0
+#define DMX_STATE_ALLOCATED 1
+#define DMX_STATE_SET       2
+#define DMX_STATE_READY     3
+#define DMX_STATE_GO        4
+
+#define DVB_DEMUX_MASK_MAX 18
+
+struct dvb_demux_filter {
+        struct dmx_section_filter filter;
+        u8 maskandmode    [DMX_MAX_FILTER_SIZE];
+        u8 maskandnotmode [DMX_MAX_FILTER_SIZE];
+	int doneq;
+
+        struct dvb_demux_filter *next;
+        struct dvb_demux_feed *feed;
+        int index;
+        int state;
+        int type;
+	int pesto;
+
+        u16 handle;
+        u16 hw_handle;
+        struct timer_list timer;
+	int ts_state;
+};
+
+
+#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head)
+
+struct dvb_demux_feed {
+        union {
+	        struct dmx_ts_feed ts;
+	        struct dmx_section_feed sec;
+	} feed;
+
+        union {
+	        dmx_ts_cb ts;
+	        dmx_section_cb sec;
+	} cb;
+
+        struct dvb_demux *demux;
+	void *priv;
+        int type;
+        int state;
+        u16 pid;
+        u8 *buffer;
+        int buffer_size;
+        int descramble;
+
+        struct timespec timeout;
+        struct dvb_demux_filter *filter;
+        int cb_length;
+
+        int ts_type;
+        enum dmx_ts_pes pes_type;
+
+        int cc;
+        int pusi_seen; /* prevents feeding of garbage from previous section */
+
+        u16 peslen;
+
+	struct list_head list_head;
+		int index; /* a unique index for each feed (can be used as hardware pid filter index) */
+};
+
+struct dvb_demux {
+        struct dmx_demux dmx;
+        void *priv;
+        int filternum;
+        int feednum;
+        int (*start_feed) (struct dvb_demux_feed *feed);
+        int (*stop_feed) (struct dvb_demux_feed *feed);
+        int (*write_to_decoder) (struct dvb_demux_feed *feed,
+				 const u8 *buf, size_t len);
+	u32 (*check_crc32) (struct dvb_demux_feed *feed,
+			    const u8 *buf, size_t len);
+	void (*memcopy) (struct dvb_demux_feed *feed, u8 *dst,
+			 const u8 *src, size_t len);
+
+        int users;
+#define MAX_DVB_DEMUX_USERS 10
+        struct dvb_demux_filter *filter;
+        struct dvb_demux_feed *feed;
+
+        struct list_head frontend_list;
+
+        struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER];
+        u16 pids[DMX_TS_PES_OTHER];
+        int playing;
+        int recording;
+
+#define DMX_MAX_PID 0x2000
+	struct list_head feed_list;
+        u8 tsbuf[204];
+        int tsbufp;
+
+	struct semaphore mutex;
+	spinlock_t lock;
+};
+
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux);
+int dvb_dmx_release(struct dvb_demux *dvbdemux);
+void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, size_t count);
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count);
+
+#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.c b/drivers/media/dvb/dvb-core/dvb_filter.c
new file mode 100644
index 0000000..bd51439
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_filter.c
@@ -0,0 +1,603 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "dvb_filter.h"
+
+#if 0
+static unsigned int bitrates[3][16] =
+{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};
+#endif
+
+static u32 freq[4] = {480, 441, 320, 0};
+
+static unsigned int ac3_bitrates[32] =
+    {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
+     0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+static u32 ac3_frames[3][32] =
+    {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
+      1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
+     {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
+      1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
+     {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
+      1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+
+
+#if 0
+static void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv,
+		  void (*pes_write)(u8 *buf, int count, void *data),
+		  void *priv)
+{
+	dvb_filter_ipack_init(pa, IPACKS, pes_write);
+	dvb_filter_ipack_init(pv, IPACKS, pes_write);
+	pa->pid = pida;
+	pv->pid = pidv;
+	pa->data = priv;
+	pv->data = priv;
+}
+#endif
+
+#if 0
+static void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188)
+{
+	u8 off = 0;
+
+	if (!buf || !p ){
+		printk("NULL POINTER IDIOT\n");
+		return;
+	}
+	if (buf[1]&PAY_START) {
+		if (p->plength == MMAX_PLENGTH-6 && p->found>6){
+			p->plength = p->found-6;
+			p->found = 0;
+			send_ipack(p);
+			dvb_filter_ipack_reset(p);
+		}
+	}
+	if (buf[3] & ADAPT_FIELD) {  // adaptation field?
+		off = buf[4] + 1;
+		if (off+4 > 187) return;
+	}
+	dvb_filter_instant_repack(buf+4+off, TS_SIZE-4-off, p);
+}
+#endif
+
+#if 0
+/* needs 5 byte input, returns picture coding type*/
+static int read_picture_header(u8 *headr, struct mpg_picture *pic, int field, int pr)
+{
+	u8 pct;
+
+	if (pr) printk( "Pic header: ");
+        pic->temporal_reference[field] = (( headr[0] << 2 ) |
+					  (headr[1] & 0x03) )& 0x03ff;
+	if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]);
+
+	pct = ( headr[1] >> 2 ) & 0x07;
+        pic->picture_coding_type[field] = pct;
+	if (pr) {
+		switch(pct){
+			case I_FRAME:
+				printk( "  I-FRAME");
+				break;
+			case B_FRAME:
+				printk( "  B-FRAME");
+				break;
+			case P_FRAME:
+				printk( "  P-FRAME");
+				break;
+		}
+	}
+
+
+        pic->vinfo.vbv_delay  = (( headr[1] >> 5 ) | ( headr[2] << 3) |
+				 ( (headr[3] & 0x1F) << 11) ) & 0xffff;
+
+	if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay);
+
+        pic->picture_header_parameter = ( headr[3] & 0xe0 ) |
+		((headr[4] & 0x80) >> 3);
+
+        if ( pct == B_FRAME ){
+                pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f;
+        }
+	if (pr) printk( " pic head param: 0x%x",
+			pic->picture_header_parameter);
+
+	return pct;
+}
+#endif
+
+#if 0
+/* needs 4 byte input */
+static int read_gop_header(u8 *headr, struct mpg_picture *pic, int pr)
+{
+	if (pr) printk("GOP header: ");
+
+	pic->time_code  = (( headr[0] << 17 ) | ( headr[1] << 9) |
+			   ( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff;
+
+	if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F,
+		       ((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F),
+		       ((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F));
+
+        if ( ( headr[3] & 0x40 ) != 0 ){
+                pic->closed_gop = 1;
+        } else {
+                pic->closed_gop = 0;
+        }
+	if (pr) printk("closed: %d", pic->closed_gop);
+
+        if ( ( headr[3] & 0x20 ) != 0 ){
+                pic->broken_link = 1;
+        } else {
+                pic->broken_link = 0;
+        }
+	if (pr) printk(" broken: %d\n", pic->broken_link);
+
+	return 0;
+}
+#endif
+
+#if 0
+/* needs 8 byte input */
+static int read_sequence_header(u8 *headr, struct dvb_video_info *vi, int pr)
+{
+        int sw;
+	int form = -1;
+
+	if (pr) printk("Reading sequence header\n");
+
+	vi->horizontal_size	= ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+	vi->vertical_size	= ((headr[1] &0x0F) << 8) | (headr[2]);
+
+        sw = (int)((headr[3]&0xF0) >> 4) ;
+
+        switch( sw ){
+	case 1:
+		if (pr)
+			printk("Videostream: ASPECT: 1:1");
+		vi->aspect_ratio = 100;
+		break;
+	case 2:
+		if (pr)
+			printk("Videostream: ASPECT: 4:3");
+                vi->aspect_ratio = 133;
+		break;
+	case 3:
+		if (pr)
+			printk("Videostream: ASPECT: 16:9");
+                vi->aspect_ratio = 177;
+		break;
+	case 4:
+		if (pr)
+			printk("Videostream: ASPECT: 2.21:1");
+                vi->aspect_ratio = 221;
+		break;
+
+        case 5 ... 15:
+		if (pr)
+			printk("Videostream: ASPECT: reserved");
+                vi->aspect_ratio = 0;
+		break;
+
+        default:
+                vi->aspect_ratio = 0;
+                return -1;
+	}
+
+	if (pr)
+		printk("  Size = %dx%d",vi->horizontal_size,vi->vertical_size);
+
+        sw = (int)(headr[3]&0x0F);
+
+        switch ( sw ) {
+	case 1:
+		if (pr)
+			printk("  FRate: 23.976 fps");
+                vi->framerate = 23976;
+		form = -1;
+		break;
+	case 2:
+		if (pr)
+			printk("  FRate: 24 fps");
+                vi->framerate = 24000;
+		form = -1;
+		break;
+	case 3:
+		if (pr)
+			printk("  FRate: 25 fps");
+                vi->framerate = 25000;
+		form = VIDEO_MODE_PAL;
+		break;
+	case 4:
+		if (pr)
+			printk("  FRate: 29.97 fps");
+                vi->framerate = 29970;
+		form = VIDEO_MODE_NTSC;
+		break;
+	case 5:
+		if (pr)
+			printk("  FRate: 30 fps");
+                vi->framerate = 30000;
+		form = VIDEO_MODE_NTSC;
+		break;
+	case 6:
+		if (pr)
+			printk("  FRate: 50 fps");
+                vi->framerate = 50000;
+		form = VIDEO_MODE_PAL;
+		break;
+	case 7:
+		if (pr)
+			printk("  FRate: 60 fps");
+                vi->framerate = 60000;
+		form = VIDEO_MODE_NTSC;
+		break;
+	}
+
+	vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03);
+
+        vi->vbv_buffer_size
+                = (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5);
+
+	if (pr){
+		printk("  BRate: %d Mbit/s",4*(vi->bit_rate)/10000);
+		printk("  vbvbuffer %d",16*1024*(vi->vbv_buffer_size));
+		printk("\n");
+	}
+
+        vi->video_format = form;
+
+	return 0;
+}
+#endif
+
+
+#if 0
+static int get_vinfo(u8 *mbuf, int count, struct dvb_video_info *vi, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+
+	while (found < 4 && c+4 < count){
+		u8 *b;
+
+		b = mbuf+c;
+		if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+		     && b[3] == 0xb3) found = 4;
+		else {
+			c++;
+		}
+	}
+
+	if (! found) return -1;
+	c += 4;
+	if (c+12 >= count) return -1;
+	headr = mbuf+c;
+	if (read_sequence_header(headr, vi, pr) < 0) return -1;
+	vi->off = c-4;
+	return 0;
+}
+#endif
+
+
+#if 0
+static int get_ainfo(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+	int fr = 0;
+
+	while (found < 2 && c < count){
+		u8 b[2];
+		memcpy( b, mbuf+c, 2);
+
+		if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+			found = 2;
+		else {
+			c++;
+		}
+	}
+
+	if (!found) return -1;
+
+	if (c+3 >= count) return -1;
+        headr = mbuf+c;
+
+	ai->layer = (headr[1] & 0x06) >> 1;
+
+	if (pr)
+		printk("Audiostream: Layer: %d", 4-ai->layer);
+
+
+	ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
+
+	if (pr){
+		if (ai->bit_rate == 0)
+			printk("  Bit rate: free");
+		else if (ai->bit_rate == 0xf)
+			printk("  BRate: reserved");
+		else
+			printk("  BRate: %d kb/s", ai->bit_rate/1000);
+	}
+
+	fr = (headr[2] & 0x0c ) >> 2;
+	ai->frequency = freq[fr]*100;
+	if (pr){
+		if (ai->frequency == 3)
+			printk("  Freq: reserved\n");
+		else
+			printk("  Freq: %d kHz\n",ai->frequency);
+
+	}
+	ai->off = c;
+	return 0;
+}
+#endif
+
+
+int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+	u8 frame = 0;
+	int fr = 0;
+
+	while ( !found  && c < count){
+		u8 *b = mbuf+c;
+
+		if ( b[0] == 0x0b &&  b[1] == 0x77 )
+			found = 1;
+		else {
+			c++;
+		}
+	}
+
+	if (!found) return -1;
+	if (pr)
+		printk("Audiostream: AC3");
+
+	ai->off = c;
+	if (c+5 >= count) return -1;
+
+	ai->layer = 0;  // 0 for AC3
+        headr = mbuf+c+2;
+
+	frame = (headr[2]&0x3f);
+	ai->bit_rate = ac3_bitrates[frame >> 1]*1000;
+
+	if (pr)
+		printk("  BRate: %d kb/s", (int) ai->bit_rate/1000);
+
+	ai->frequency = (headr[2] & 0xc0 ) >> 6;
+	fr = (headr[2] & 0xc0 ) >> 6;
+	ai->frequency = freq[fr]*100;
+	if (pr) printk ("  Freq: %d Hz\n", (int) ai->frequency);
+
+
+	ai->framesize = ac3_frames[fr][frame >> 1];
+	if ((frame & 1) &&  (fr == 1)) ai->framesize++;
+	ai->framesize = ai->framesize << 1;
+	if (pr) printk ("  Framesize %d\n",(int) ai->framesize);
+
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_filter_get_ac3info);
+
+
+#if 0
+static u8 *skip_pes_header(u8 **bufp)
+{
+        u8 *inbuf = *bufp;
+        u8 *buf = inbuf;
+        u8 *pts = NULL;
+        int skip = 0;
+
+	static const int mpeg1_skip_table[16] = {
+		1, 0xffff,      5,     10, 0xffff, 0xffff, 0xffff, 0xffff,
+	        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+	};
+
+
+        if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */
+                if (buf[7] & PTS_ONLY)
+                        pts = buf+9;
+                else pts = NULL;
+                buf = inbuf + 9 + inbuf[8];
+        } else {        /* mpeg1 */
+                for (buf = inbuf + 6; *buf == 0xff; buf++)
+                        if (buf == inbuf + 6 + 16) {
+                                break;
+                        }
+                if ((*buf & 0xc0) == 0x40)
+                        buf += 2;
+                skip = mpeg1_skip_table [*buf >> 4];
+                if (skip == 5 || skip == 10) pts = buf;
+                else pts = NULL;
+
+                buf += mpeg1_skip_table [*buf >> 4];
+        }
+
+        *bufp = buf;
+        return pts;
+}
+#endif
+
+#if 0
+static void initialize_quant_matrix( u32 *matrix )
+{
+        int i;
+
+        matrix[0]  = 0x08101013;
+        matrix[1]  = 0x10131616;
+        matrix[2]  = 0x16161616;
+        matrix[3]  = 0x1a181a1b;
+        matrix[4]  = 0x1b1b1a1a;
+        matrix[5]  = 0x1a1a1b1b;
+        matrix[6]  = 0x1b1d1d1d;
+        matrix[7]  = 0x2222221d;
+        matrix[8]  = 0x1d1d1b1b;
+        matrix[9]  = 0x1d1d2020;
+        matrix[10] = 0x22222526;
+        matrix[11] = 0x25232322;
+        matrix[12] = 0x23262628;
+        matrix[13] = 0x28283030;
+        matrix[14] = 0x2e2e3838;
+        matrix[15] = 0x3a454553;
+
+        for ( i = 16 ; i < 32 ; i++ )
+                matrix[i] = 0x10101010;
+}
+#endif
+
+#if 0
+static void initialize_mpg_picture(struct mpg_picture *pic)
+{
+        int i;
+
+        /* set MPEG1 */
+        pic->mpeg1_flag = 1;
+        pic->profile_and_level = 0x4A ;        /* MP@LL */
+        pic->progressive_sequence = 1;
+        pic->low_delay = 0;
+
+        pic->sequence_display_extension_flag = 0;
+        for ( i = 0 ; i < 4 ; i++ ){
+                pic->frame_centre_horizontal_offset[i] = 0;
+                pic->frame_centre_vertical_offset[i] = 0;
+        }
+        pic->last_frame_centre_horizontal_offset = 0;
+        pic->last_frame_centre_vertical_offset = 0;
+
+        pic->picture_display_extension_flag[0] = 0;
+        pic->picture_display_extension_flag[1] = 0;
+        pic->sequence_header_flag = 0;
+	pic->gop_flag = 0;
+        pic->sequence_end_flag = 0;
+}
+#endif
+
+#if 0
+static void mpg_set_picture_parameter( int32_t field_type, struct mpg_picture *pic )
+{
+        int16_t last_h_offset;
+        int16_t last_v_offset;
+
+        int16_t *p_h_offset;
+        int16_t *p_v_offset;
+
+        if ( pic->mpeg1_flag ){
+                pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE;
+                pic->top_field_first = 0;
+                pic->repeat_first_field = 0;
+                pic->progressive_frame = 1;
+                pic->picture_coding_parameter = 0x000010;
+        }
+
+        /* Reset flag */
+        pic->picture_display_extension_flag[field_type] = 0;
+
+        last_h_offset = pic->last_frame_centre_horizontal_offset;
+        last_v_offset = pic->last_frame_centre_vertical_offset;
+        if ( field_type == FIRST_FIELD ){
+                p_h_offset = pic->frame_centre_horizontal_offset;
+                p_v_offset = pic->frame_centre_vertical_offset;
+                *p_h_offset = last_h_offset;
+                *(p_h_offset + 1) = last_h_offset;
+                *(p_h_offset + 2) = last_h_offset;
+                *p_v_offset = last_v_offset;
+                *(p_v_offset + 1) = last_v_offset;
+                *(p_v_offset + 2) = last_v_offset;
+        } else {
+                pic->frame_centre_horizontal_offset[3] = last_h_offset;
+                pic->frame_centre_vertical_offset[3] = last_v_offset;
+        }
+}
+#endif
+
+#if 0
+static void init_mpg_picture( struct mpg_picture *pic, int chan, int32_t field_type)
+{
+        pic->picture_header = 0;
+        pic->sequence_header_data
+                = ( INIT_HORIZONTAL_SIZE << 20 )
+                        | ( INIT_VERTICAL_SIZE << 8 )
+                        | ( INIT_ASPECT_RATIO << 4 )
+                        | ( INIT_FRAME_RATE );
+        pic->mpeg1_flag = 0;
+        pic->vinfo.horizontal_size
+                = INIT_DISP_HORIZONTAL_SIZE;
+        pic->vinfo.vertical_size
+                = INIT_DISP_VERTICAL_SIZE;
+        pic->picture_display_extension_flag[field_type]
+                = 0;
+        pic->pts_flag[field_type] = 0;
+
+        pic->sequence_gop_header = 0;
+        pic->picture_header = 0;
+        pic->sequence_header_flag = 0;
+        pic->gop_flag = 0;
+        pic->sequence_end_flag = 0;
+        pic->sequence_display_extension_flag = 0;
+        pic->last_frame_centre_horizontal_offset = 0;
+        pic->last_frame_centre_vertical_offset = 0;
+	pic->channel = chan;
+}
+#endif
+
+void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
+			    dvb_filter_pes2ts_cb_t *cb, void *priv)
+{
+	unsigned char *buf=p2ts->buf;
+
+	buf[0]=0x47;
+	buf[1]=(pid>>8);
+	buf[2]=pid&0xff;
+	p2ts->cc=0;
+	p2ts->cb=cb;
+	p2ts->priv=priv;
+}
+EXPORT_SYMBOL(dvb_filter_pes2ts_init);
+
+int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
+		      int len, int payload_start)
+{
+	unsigned char *buf=p2ts->buf;
+	int ret=0, rest;
+
+	//len=6+((pes[4]<<8)|pes[5]);
+
+	if (payload_start)
+		buf[1]|=0x40;
+	else
+		buf[1]&=~0x40;
+	while (len>=184) {
+		buf[3]=0x10|((p2ts->cc++)&0x0f);
+		memcpy(buf+4, pes, 184);
+		if ((ret=p2ts->cb(p2ts->priv, buf)))
+			return ret;
+		len-=184; pes+=184;
+		buf[1]&=~0x40;
+	}
+	if (!len)
+	        return 0;
+	buf[3]=0x30|((p2ts->cc++)&0x0f);
+	rest=183-len;
+	if (rest) {
+	        buf[5]=0x00;
+		if (rest-1)
+			memset(buf+6, 0xff, rest-1);
+	}
+	buf[4]=rest;
+	memcpy(buf+5+rest, pes, len);
+	return p2ts->cb(p2ts->priv, buf);
+}
+EXPORT_SYMBOL(dvb_filter_pes2ts);
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.h b/drivers/media/dvb/dvb-core/dvb_filter.h
new file mode 100644
index 0000000..b0848f7
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_filter.h
@@ -0,0 +1,246 @@
+/*
+ * dvb_filter.h
+ *
+ * Copyright (C) 2003 Convergence GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _DVB_FILTER_H_
+#define _DVB_FILTER_H_
+
+#include <linux/slab.h>
+
+#include "demux.h"
+
+typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *);
+
+struct dvb_filter_pes2ts {
+	unsigned char buf[188];
+        unsigned char cc;
+        dvb_filter_pes2ts_cb_t *cb;
+	void *priv;
+};
+
+void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
+			    dvb_filter_pes2ts_cb_t *cb, void *priv);
+
+int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
+		      int len, int payload_start);
+
+
+#define PROG_STREAM_MAP  0xBC
+#define PRIVATE_STREAM1  0xBD
+#define PADDING_STREAM   0xBE
+#define PRIVATE_STREAM2  0xBF
+#define AUDIO_STREAM_S   0xC0
+#define AUDIO_STREAM_E   0xDF
+#define VIDEO_STREAM_S   0xE0
+#define VIDEO_STREAM_E   0xEF
+#define ECM_STREAM       0xF0
+#define EMM_STREAM       0xF1
+#define DSM_CC_STREAM    0xF2
+#define ISO13522_STREAM  0xF3
+#define PROG_STREAM_DIR  0xFF
+
+#define DVB_PICTURE_START    0x00
+#define DVB_USER_START       0xb2
+#define DVB_SEQUENCE_HEADER  0xb3
+#define DVB_SEQUENCE_ERROR   0xb4
+#define DVB_EXTENSION_START  0xb5
+#define DVB_SEQUENCE_END     0xb7
+#define DVB_GOP_START        0xb8
+#define DVB_EXCEPT_SLICE     0xb0
+
+#define SEQUENCE_EXTENSION           0x01
+#define SEQUENCE_DISPLAY_EXTENSION   0x02
+#define PICTURE_CODING_EXTENSION     0x08
+#define QUANT_MATRIX_EXTENSION       0x03
+#define PICTURE_DISPLAY_EXTENSION    0x07
+
+#define I_FRAME 0x01
+#define B_FRAME 0x02
+#define P_FRAME 0x03
+
+/* Initialize sequence_data */
+#define INIT_HORIZONTAL_SIZE        720
+#define INIT_VERTICAL_SIZE          576
+#define INIT_ASPECT_RATIO          0x02
+#define INIT_FRAME_RATE            0x03
+#define INIT_DISP_HORIZONTAL_SIZE   540
+#define INIT_DISP_VERTICAL_SIZE     576
+
+
+//flags2
+#define PTS_DTS_FLAGS    0xC0
+#define ESCR_FLAG        0x20
+#define ES_RATE_FLAG     0x10
+#define DSM_TRICK_FLAG   0x08
+#define ADD_CPY_FLAG     0x04
+#define PES_CRC_FLAG     0x02
+#define PES_EXT_FLAG     0x01
+
+//pts_dts flags
+#define PTS_ONLY         0x80
+#define PTS_DTS          0xC0
+
+#define TS_SIZE        188
+#define TRANS_ERROR    0x80
+#define PAY_START      0x40
+#define TRANS_PRIO     0x20
+#define PID_MASK_HI    0x1F
+//flags
+#define TRANS_SCRMBL1  0x80
+#define TRANS_SCRMBL2  0x40
+#define ADAPT_FIELD    0x20
+#define PAYLOAD        0x10
+#define COUNT_MASK     0x0F
+
+// adaptation flags
+#define DISCON_IND     0x80
+#define RAND_ACC_IND   0x40
+#define ES_PRI_IND     0x20
+#define PCR_FLAG       0x10
+#define OPCR_FLAG      0x08
+#define SPLICE_FLAG    0x04
+#define TRANS_PRIV     0x02
+#define ADAP_EXT_FLAG  0x01
+
+// adaptation extension flags
+#define LTW_FLAG       0x80
+#define PIECE_RATE     0x40
+#define SEAM_SPLICE    0x20
+
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (256*MAX_PLENGTH)
+
+#ifndef IPACKS
+#define IPACKS 2048
+#endif
+
+struct ipack {
+	int size;
+	int found;
+	u8 *buf;
+	u8 cid;
+	u32 plength;
+	u8 plen[2];
+	u8 flag1;
+	u8 flag2;
+	u8 hlength;
+	u8 pts[5];
+	u16 *pid;
+	int mpeg;
+	u8 check;
+	int which;
+	int done;
+	void *data;
+	void (*func)(u8 *buf,  int size, void *priv);
+	int count;
+	int repack_subids;
+};
+
+struct dvb_video_info {
+	u32 horizontal_size;
+	u32 vertical_size;
+	u32 aspect_ratio;
+	u32 framerate;
+	u32 video_format;
+	u32 bit_rate;
+	u32 comp_bit_rate;
+	u32 vbv_buffer_size;
+        s16 vbv_delay;
+	u32 CSPF;
+	u32 off;
+};
+
+#define OFF_SIZE 4
+#define FIRST_FIELD 0
+#define SECOND_FIELD 1
+#define VIDEO_FRAME_PICTURE 0x03
+
+struct mpg_picture {
+        int       channel;
+	struct dvb_video_info vinfo;
+        u32      *sequence_gop_header;
+        u32      *picture_header;
+        s32       time_code;
+        int       low_delay;
+        int       closed_gop;
+        int       broken_link;
+        int       sequence_header_flag;
+        int       gop_flag;
+        int       sequence_end_flag;
+
+        u8        profile_and_level;
+        s32       picture_coding_parameter;
+        u32       matrix[32];
+        s8        matrix_change_flag;
+
+        u8        picture_header_parameter;
+  /* bit 0 - 2: bwd f code
+     bit 3    : fpb vector
+     bit 4 - 6: fwd f code
+     bit 7    : fpf vector */
+
+        int       mpeg1_flag;
+        int       progressive_sequence;
+        int       sequence_display_extension_flag;
+        u32       sequence_header_data;
+        s16       last_frame_centre_horizontal_offset;
+        s16       last_frame_centre_vertical_offset;
+
+        u32       pts[2]; /* [0] 1st field, [1] 2nd field */
+        int       top_field_first;
+        int       repeat_first_field;
+        int       progressive_frame;
+        int       bank;
+        int       forward_bank;
+        int       backward_bank;
+        int       compress;
+        s16       frame_centre_horizontal_offset[OFF_SIZE];
+                  /* [0-2] 1st field, [3] 2nd field */
+        s16       frame_centre_vertical_offset[OFF_SIZE];
+                  /* [0-2] 1st field, [3] 2nd field */
+        s16       temporal_reference[2];
+                  /* [0] 1st field, [1] 2nd field */
+
+        s8        picture_coding_type[2];
+                  /* [0] 1st field, [1] 2nd field */
+        s8        picture_structure[2];
+                  /* [0] 1st field, [1] 2nd field */
+        s8        picture_display_extension_flag[2];
+                  /* [0] 1st field, [1] 2nd field */
+                  /* picture_display_extenion() 0:no 1:exit*/
+        s8        pts_flag[2];
+                  /* [0] 1st field, [1] 2nd field */
+};
+
+struct dvb_audio_info {
+	int layer;
+	u32 bit_rate;
+	u32 frequency;
+	u32 mode;
+	u32 mode_extension ;
+	u32 emphasis;
+	u32 framesize;
+	u32 off;
+};
+
+int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr);
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
new file mode 100644
index 0000000..59a9adf
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -0,0 +1,915 @@
+/*
+ * dvb_frontend.c: DVB frontend tuning interface/thread
+ *
+ *
+ * Copyright (C) 1999-2001 Ralph  Metzler
+ *			   Marcus Metzler
+ *			   Holger Waechtler
+ *				      for convergence integrated media GmbH
+ *
+ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/suspend.h>
+#include <asm/processor.h>
+#include <asm/semaphore.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+
+static int dvb_frontend_debug;
+static int dvb_shutdown_timeout = 5;
+static int dvb_force_auto_inversion;
+static int dvb_override_tune_delay;
+static int dvb_powerdown_on_sleep = 1;
+
+module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
+MODULE_PARM_DESC(dvb_frontend_debug, "Turn on/off frontend core debugging (default:off).");
+module_param(dvb_shutdown_timeout, int, 0444);
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
+module_param(dvb_force_auto_inversion, int, 0444);
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+module_param(dvb_override_tune_delay, int, 0444);
+MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
+module_param(dvb_powerdown_on_sleep, int, 0444);
+MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volatage off on sleep (default)");
+
+#define dprintk if (dvb_frontend_debug) printk
+
+#define FESTATE_IDLE 1
+#define FESTATE_RETUNE 2
+#define FESTATE_TUNING_FAST 4
+#define FESTATE_TUNING_SLOW 8
+#define FESTATE_TUNED 16
+#define FESTATE_ZIGZAG_FAST 32
+#define FESTATE_ZIGZAG_SLOW 64
+#define FESTATE_DISEQC 128
+#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
+#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
+#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
+#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
+/*
+ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
+ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
+ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
+ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
+ * FESTATE_TUNED. The frontend has successfully locked on.
+ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
+ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
+ * FESTATE_DISEQC. A DISEQC command has just been issued.
+ * FESTATE_WAITFORLOCK. When we're waiting for a lock.
+ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
+ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
+ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
+ */
+
+static DECLARE_MUTEX(frontend_mutex);
+
+struct dvb_frontend_private {
+
+	struct dvb_device *dvbdev;
+	struct dvb_frontend_parameters parameters;
+	struct dvb_fe_events events;
+	struct semaphore sem;
+	struct list_head list_head;
+	wait_queue_head_t wait_queue;
+	pid_t thread_pid;
+	unsigned long release_jiffies;
+	int state;
+	int bending;
+	int lnb_drift;
+	int inversion;
+	int auto_step;
+	int auto_sub_step;
+	int started_auto_step;
+	int min_delay;
+	int max_drift;
+	int step_size;
+	int exit;
+	int wakeup;
+	fe_status_t status;
+};
+
+
+static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	struct dvb_fe_events *events = &fepriv->events;
+	struct dvb_frontend_event *e;
+	int wp;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (down_interruptible (&events->sem))
+		return;
+
+	wp = (events->eventw + 1) % MAX_EVENT;
+
+	if (wp == events->eventr) {
+		events->overflow = 1;
+		events->eventr = (events->eventr + 1) % MAX_EVENT;
+	}
+
+	e = &events->events[events->eventw];
+
+	memcpy (&e->parameters, &fepriv->parameters,
+		sizeof (struct dvb_frontend_parameters));
+
+	if (status & FE_HAS_LOCK)
+		if (fe->ops->get_frontend)
+			fe->ops->get_frontend(fe, &e->parameters);
+
+	events->eventw = wp;
+
+	up (&events->sem);
+
+	e->status = status;
+
+	wake_up_interruptible (&events->wait_queue);
+}
+
+static int dvb_frontend_get_event(struct dvb_frontend *fe,
+			    struct dvb_frontend_event *event, int flags)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	struct dvb_fe_events *events = &fepriv->events;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (events->overflow) {
+		events->overflow = 0;
+		return -EOVERFLOW;
+	}
+
+	if (events->eventw == events->eventr) {
+		int ret;
+
+		if (flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		up(&fepriv->sem);
+
+		ret = wait_event_interruptible (events->wait_queue,
+						events->eventw != events->eventr);
+
+		if (down_interruptible (&fepriv->sem))
+			return -ERESTARTSYS;
+
+		if (ret < 0)
+			return ret;
+	}
+
+	if (down_interruptible (&events->sem))
+		return -ERESTARTSYS;
+
+	memcpy (event, &events->events[events->eventr],
+		sizeof(struct dvb_frontend_event));
+
+	events->eventr = (events->eventr + 1) % MAX_EVENT;
+
+	up (&events->sem);
+
+	return 0;
+}
+
+static void dvb_frontend_init(struct dvb_frontend *fe)
+{
+	dprintk ("DVB: initialising frontend %i (%s)...\n",
+		 fe->dvb->num,
+		 fe->ops->info.name);
+
+	if (fe->ops->init)
+		fe->ops->init(fe);
+}
+
+static void update_delay(int *quality, int *delay, int min_delay, int locked)
+{
+	    int q2;
+
+	    dprintk ("%s\n", __FUNCTION__);
+
+	    if (locked)
+		      (*quality) = (*quality * 220 + 36*256) / 256;
+	    else
+		      (*quality) = (*quality * 220 + 0) / 256;
+
+	    q2 = *quality - 128;
+	    q2 *= q2;
+
+	    *delay = min_delay + q2 * HZ / (128*128);
+}
+
+/**
+ * Performs automatic twiddling of frontend parameters.
+ *
+ * @param fe The frontend concerned.
+ * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
+ * @returns Number of complete iterations that have been performed.
+ */
+static int dvb_frontend_autotune(struct dvb_frontend *fe, int check_wrapped)
+{
+	int autoinversion;
+	int ready = 0;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int original_inversion = fepriv->parameters.inversion;
+	u32 original_frequency = fepriv->parameters.frequency;
+
+	/* are we using autoinversion? */
+	autoinversion = ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) &&
+			 (fepriv->parameters.inversion == INVERSION_AUTO));
+
+	/* setup parameters correctly */
+	while(!ready) {
+		/* calculate the lnb_drift */
+		fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
+
+		/* wrap the auto_step if we've exceeded the maximum drift */
+		if (fepriv->lnb_drift > fepriv->max_drift) {
+			fepriv->auto_step = 0;
+			fepriv->auto_sub_step = 0;
+			fepriv->lnb_drift = 0;
+		}
+
+		/* perform inversion and +/- zigzag */
+		switch(fepriv->auto_sub_step) {
+		case 0:
+			/* try with the current inversion and current drift setting */
+			ready = 1;
+			break;
+
+		case 1:
+			if (!autoinversion) break;
+
+			fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+			ready = 1;
+			break;
+
+		case 2:
+			if (fepriv->lnb_drift == 0) break;
+
+			fepriv->lnb_drift = -fepriv->lnb_drift;
+			ready = 1;
+			break;
+
+		case 3:
+			if (fepriv->lnb_drift == 0) break;
+			if (!autoinversion) break;
+
+			fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+			fepriv->lnb_drift = -fepriv->lnb_drift;
+			ready = 1;
+			break;
+
+		default:
+			fepriv->auto_step++;
+			fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
+			break;
+		}
+
+		if (!ready) fepriv->auto_sub_step++;
+	}
+
+	/* if this attempt would hit where we started, indicate a complete
+	 * iteration has occurred */
+	if ((fepriv->auto_step == fepriv->started_auto_step) &&
+	    (fepriv->auto_sub_step == 0) && check_wrapped) {
+		return 1;
+	}
+
+	dprintk("%s: drift:%i inversion:%i auto_step:%i "
+		"auto_sub_step:%i started_auto_step:%i\n",
+		__FUNCTION__, fepriv->lnb_drift, fepriv->inversion,
+		fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step);
+
+	/* set the frontend itself */
+	fepriv->parameters.frequency += fepriv->lnb_drift;
+	if (autoinversion)
+		fepriv->parameters.inversion = fepriv->inversion;
+	if (fe->ops->set_frontend)
+		fe->ops->set_frontend(fe, &fepriv->parameters);
+
+	fepriv->parameters.frequency = original_frequency;
+	fepriv->parameters.inversion = original_inversion;
+
+	fepriv->auto_sub_step++;
+	return 0;
+}
+
+static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	if (fepriv->exit)
+		return 1;
+
+	if (fepriv->dvbdev->writers == 1)
+		if (jiffies - fepriv->release_jiffies > dvb_shutdown_timeout * HZ)
+			return 1;
+
+	return 0;
+}
+
+static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	if (fepriv->wakeup) {
+		fepriv->wakeup = 0;
+		return 1;
+	}
+	return dvb_frontend_is_exiting(fe);
+}
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	fepriv->wakeup = 1;
+	wake_up_interruptible(&fepriv->wait_queue);
+}
+
+/*
+ * FIXME: use linux/kthread.h
+ */
+static int dvb_frontend_thread(void *data)
+{
+	struct dvb_frontend *fe = (struct dvb_frontend *) data;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	unsigned long timeout;
+	char name [15];
+	int quality = 0, delay = 3*HZ;
+	fe_status_t s;
+	int check_wrapped = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	snprintf (name, sizeof(name), "kdvb-fe-%i", fe->dvb->num);
+
+        lock_kernel();
+        daemonize(name);
+        sigfillset(&current->blocked);
+        unlock_kernel();
+
+	fepriv->status = 0;
+	dvb_frontend_init(fe);
+	fepriv->wakeup = 0;
+
+	while (1) {
+		up(&fepriv->sem);	    /* is locked when we enter the thread... */
+
+		timeout = wait_event_interruptible_timeout(fepriv->wait_queue,
+							   dvb_frontend_should_wakeup(fe),
+							   delay);
+		if (0 != dvb_frontend_is_exiting(fe)) {
+			/* got signal or quitting */
+			break;
+		}
+
+		if (current->flags & PF_FREEZE)
+			refrigerator(PF_FREEZE);
+
+		if (down_interruptible(&fepriv->sem))
+			break;
+
+		/* if we've got no parameters, just keep idling */
+		if (fepriv->state & FESTATE_IDLE) {
+			delay = 3*HZ;
+			quality = 0;
+			continue;
+		}
+
+		/* get the frontend status */
+		if (fepriv->state & FESTATE_RETUNE) {
+			s = 0;
+		} else {
+			if (fe->ops->read_status)
+				fe->ops->read_status(fe, &s);
+			if (s != fepriv->status) {
+				dvb_frontend_add_event(fe, s);
+				fepriv->status = s;
+			}
+		}
+		/* if we're not tuned, and we have a lock, move to the TUNED state */
+		if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			fepriv->state = FESTATE_TUNED;
+
+			/* if we're tuned, then we have determined the correct inversion */
+			if ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) &&
+			    (fepriv->parameters.inversion == INVERSION_AUTO)) {
+				fepriv->parameters.inversion = fepriv->inversion;
+			}
+			continue;
+		}
+
+		/* if we are tuned already, check we're still locked */
+		if (fepriv->state & FESTATE_TUNED) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+
+			/* we're tuned, and the lock is still good... */
+			if (s & FE_HAS_LOCK)
+				continue;
+			else {
+				/* if we _WERE_ tuned, but now don't have a lock,
+				 * need to zigzag */
+				fepriv->state = FESTATE_ZIGZAG_FAST;
+				fepriv->started_auto_step = fepriv->auto_step;
+				check_wrapped = 0;
+			}
+		}
+
+		/* don't actually do anything if we're in the LOSTLOCK state,
+		 * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
+		if ((fepriv->state & FESTATE_LOSTLOCK) &&
+		    (fe->ops->info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			continue;
+		}
+
+		/* don't do anything if we're in the DISEQC state, since this
+		 * might be someone with a motorized dish controlled by DISEQC.
+		 * If its actually a re-tune, there will be a SET_FRONTEND soon enough.	*/
+		if (fepriv->state & FESTATE_DISEQC) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			continue;
+		}
+
+		/* if we're in the RETUNE state, set everything up for a brand
+		 * new scan, keeping the current inversion setting, as the next
+		 * tune is _very_ likely to require the same */
+		if (fepriv->state & FESTATE_RETUNE) {
+			fepriv->lnb_drift = 0;
+			fepriv->auto_step = 0;
+			fepriv->auto_sub_step = 0;
+			fepriv->started_auto_step = 0;
+			check_wrapped = 0;
+		}
+
+		/* fast zigzag. */
+		if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
+			delay = fepriv->min_delay;
+
+			/* peform a tune */
+			if (dvb_frontend_autotune(fe, check_wrapped)) {
+				/* OK, if we've run out of trials at the fast speed.
+				 * Drop back to slow for the _next_ attempt */
+				fepriv->state = FESTATE_SEARCHING_SLOW;
+				fepriv->started_auto_step = fepriv->auto_step;
+				continue;
+			}
+			check_wrapped = 1;
+
+			/* if we've just retuned, enter the ZIGZAG_FAST state.
+			 * This ensures we cannot return from an
+			 * FE_SET_FRONTEND ioctl before the first frontend tune
+			 * occurs */
+			if (fepriv->state & FESTATE_RETUNE) {
+				fepriv->state = FESTATE_TUNING_FAST;
+			}
+		}
+
+		/* slow zigzag */
+		if (fepriv->state & FESTATE_SEARCHING_SLOW) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+
+			/* Note: don't bother checking for wrapping; we stay in this
+			 * state until we get a lock */
+			dvb_frontend_autotune(fe, 0);
+		}
+	}
+
+	if (dvb_shutdown_timeout) {
+		if (dvb_powerdown_on_sleep)
+			if (fe->ops->set_voltage)
+				fe->ops->set_voltage(fe, SEC_VOLTAGE_OFF);
+		if (fe->ops->sleep)
+			fe->ops->sleep(fe);
+	}
+
+	fepriv->thread_pid = 0;
+	mb();
+
+	dvb_frontend_wakeup(fe);
+	return 0;
+}
+
+static void dvb_frontend_stop(struct dvb_frontend *fe)
+{
+	unsigned long ret;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	fepriv->exit = 1;
+	mb();
+
+	if (!fepriv->thread_pid)
+		return;
+
+	/* check if the thread is really alive */
+	if (kill_proc(fepriv->thread_pid, 0, 1) == -ESRCH) {
+		printk("dvb_frontend_stop: thread PID %d already died\n",
+				fepriv->thread_pid);
+		/* make sure the mutex was not held by the thread */
+		init_MUTEX (&fepriv->sem);
+		return;
+	}
+
+	/* wake up the frontend thread, so it notices that fe->exit == 1 */
+	dvb_frontend_wakeup(fe);
+
+	/* wait until the frontend thread has exited */
+	ret = wait_event_interruptible(fepriv->wait_queue,0 == fepriv->thread_pid);
+	if (-ERESTARTSYS != ret) {
+		fepriv->state = FESTATE_IDLE;
+		return;
+	}
+	fepriv->state = FESTATE_IDLE;
+
+	/* paranoia check in case a signal arrived */
+	if (fepriv->thread_pid)
+		printk("dvb_frontend_stop: warning: thread PID %d won't exit\n",
+				fepriv->thread_pid);
+}
+
+static int dvb_frontend_start(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (fepriv->thread_pid) {
+		if (!fepriv->exit)
+			return 0;
+		else
+			dvb_frontend_stop (fe);
+	}
+
+	if (signal_pending(current))
+		return -EINTR;
+	if (down_interruptible (&fepriv->sem))
+		return -EINTR;
+
+	fepriv->state = FESTATE_IDLE;
+	fepriv->exit = 0;
+	fepriv->thread_pid = 0;
+	mb();
+
+	ret = kernel_thread (dvb_frontend_thread, fe, 0);
+
+	if (ret < 0) {
+		printk("dvb_frontend_start: failed to start kernel_thread (%d)\n", ret);
+		up(&fepriv->sem);
+		return ret;
+	}
+	fepriv->thread_pid = ret;
+
+	return 0;
+}
+
+static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int err = -EOPNOTSUPP;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (!fe || fepriv->exit)
+		return -ENODEV;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
+	    (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT ||
+	     cmd == FE_DISEQC_RECV_SLAVE_REPLY))
+		return -EPERM;
+
+	if (down_interruptible (&fepriv->sem))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case FE_GET_INFO: {
+		struct dvb_frontend_info* info = (struct dvb_frontend_info*) parg;
+		memcpy(info, &fe->ops->info, sizeof(struct dvb_frontend_info));
+
+		/* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
+		 * do it, it is done for it. */
+		info->caps |= FE_CAN_INVERSION_AUTO;
+		err = 0;
+		break;
+	}
+
+	case FE_READ_STATUS:
+		if (fe->ops->read_status)
+			err = fe->ops->read_status(fe, (fe_status_t*) parg);
+		break;
+
+	case FE_READ_BER:
+		if (fe->ops->read_ber)
+			err = fe->ops->read_ber(fe, (__u32*) parg);
+		break;
+
+	case FE_READ_SIGNAL_STRENGTH:
+		if (fe->ops->read_signal_strength)
+			err = fe->ops->read_signal_strength(fe, (__u16*) parg);
+		break;
+
+	case FE_READ_SNR:
+		if (fe->ops->read_snr)
+			err = fe->ops->read_snr(fe, (__u16*) parg);
+		break;
+
+	case FE_READ_UNCORRECTED_BLOCKS:
+		if (fe->ops->read_ucblocks)
+			err = fe->ops->read_ucblocks(fe, (__u32*) parg);
+		break;
+
+
+	case FE_DISEQC_RESET_OVERLOAD:
+		if (fe->ops->diseqc_reset_overload) {
+			err = fe->ops->diseqc_reset_overload(fe);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_SEND_MASTER_CMD:
+		if (fe->ops->diseqc_send_master_cmd) {
+			err = fe->ops->diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_SEND_BURST:
+		if (fe->ops->diseqc_send_burst) {
+			err = fe->ops->diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_SET_TONE:
+		if (fe->ops->set_tone) {
+			err = fe->ops->set_tone(fe, (fe_sec_tone_mode_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_SET_VOLTAGE:
+		if (fe->ops->set_voltage) {
+			err = fe->ops->set_voltage(fe, (fe_sec_voltage_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISHNETWORK_SEND_LEGACY_CMD:
+		if (fe->ops->dishnetwork_send_legacy_command) {
+			err = fe->ops->dishnetwork_send_legacy_command(fe, (unsigned int) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_RECV_SLAVE_REPLY:
+		if (fe->ops->diseqc_recv_slave_reply)
+			err = fe->ops->diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg);
+		break;
+
+	case FE_ENABLE_HIGH_LNB_VOLTAGE:
+		if (fe->ops->enable_high_lnb_voltage)
+			err = fe->ops->enable_high_lnb_voltage(fe, (int) parg);
+		break;
+
+	case FE_SET_FRONTEND: {
+		struct dvb_frontend_tune_settings fetunesettings;
+
+		memcpy (&fepriv->parameters, parg,
+			sizeof (struct dvb_frontend_parameters));
+
+		memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
+		memcpy(&fetunesettings.parameters, parg,
+		       sizeof (struct dvb_frontend_parameters));
+
+		/* force auto frequency inversion if requested */
+		if (dvb_force_auto_inversion) {
+			fepriv->parameters.inversion = INVERSION_AUTO;
+			fetunesettings.parameters.inversion = INVERSION_AUTO;
+		}
+		if (fe->ops->info.type == FE_OFDM) {
+			/* without hierachical coding code_rate_LP is irrelevant,
+			 * so we tolerate the otherwise invalid FEC_NONE setting */
+			if (fepriv->parameters.u.ofdm.hierarchy_information == HIERARCHY_NONE &&
+			    fepriv->parameters.u.ofdm.code_rate_LP == FEC_NONE)
+				fepriv->parameters.u.ofdm.code_rate_LP = FEC_AUTO;
+		}
+
+		/* get frontend-specific tuning settings */
+		if (fe->ops->get_tune_settings && (fe->ops->get_tune_settings(fe, &fetunesettings) == 0)) {
+			fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+			fepriv->max_drift = fetunesettings.max_drift;
+			fepriv->step_size = fetunesettings.step_size;
+		} else {
+			/* default values */
+			switch(fe->ops->info.type) {
+			case FE_QPSK:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = fepriv->parameters.u.qpsk.symbol_rate / 16000;
+				fepriv->max_drift = fepriv->parameters.u.qpsk.symbol_rate / 2000;
+				break;
+
+			case FE_QAM:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = 0; /* no zigzag */
+				fepriv->max_drift = 0;
+				break;
+
+			case FE_OFDM:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = fe->ops->info.frequency_stepsize * 2;
+				fepriv->max_drift = (fe->ops->info.frequency_stepsize * 2) + 1;
+				break;
+			case FE_ATSC:
+				printk("dvb-core: FE_ATSC not handled yet.\n");
+				break;
+			}
+		}
+		if (dvb_override_tune_delay > 0)
+			fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+
+		fepriv->state = FESTATE_RETUNE;
+		dvb_frontend_wakeup(fe);
+		dvb_frontend_add_event(fe, 0);
+		fepriv->status = 0;
+		err = 0;
+		break;
+	}
+
+	case FE_GET_EVENT:
+		err = dvb_frontend_get_event (fe, parg, file->f_flags);
+		break;
+
+	case FE_GET_FRONTEND:
+		if (fe->ops->get_frontend) {
+			memcpy (parg, &fepriv->parameters, sizeof (struct dvb_frontend_parameters));
+			err = fe->ops->get_frontend(fe, (struct dvb_frontend_parameters*) parg);
+		}
+		break;
+	};
+
+	up (&fepriv->sem);
+	return err;
+}
+
+static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	poll_wait (file, &fepriv->events.wait_queue, wait);
+
+	if (fepriv->events.eventw != fepriv->events.eventr)
+		return (POLLIN | POLLRDNORM | POLLPRI);
+
+	return 0;
+}
+
+static int dvb_frontend_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int ret;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if ((ret = dvb_generic_open (inode, file)) < 0)
+		return ret;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		ret = dvb_frontend_start (fe);
+		if (ret)
+			dvb_generic_release (inode, file);
+
+		/*  empty event queue */
+		fepriv->events.eventr = fepriv->events.eventw = 0;
+	}
+
+	return ret;
+}
+
+static int dvb_frontend_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		fepriv->release_jiffies = jiffies;
+
+	return dvb_generic_release (inode, file);
+}
+
+static struct file_operations dvb_frontend_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= dvb_generic_ioctl,
+	.poll		= dvb_frontend_poll,
+	.open		= dvb_frontend_open,
+	.release	= dvb_frontend_release
+};
+
+int dvb_register_frontend(struct dvb_adapter* dvb,
+			  struct dvb_frontend* fe)
+{
+	struct dvb_frontend_private *fepriv;
+	static const struct dvb_device dvbdev_template = {
+		.users = ~0,
+		.writers = 1,
+		.readers = (~0)-1,
+		.fops = &dvb_frontend_fops,
+		.kernel_ioctl = dvb_frontend_ioctl
+	};
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (down_interruptible (&frontend_mutex))
+		return -ERESTARTSYS;
+
+	fe->frontend_priv = kmalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
+	if (fe->frontend_priv == NULL) {
+		up(&frontend_mutex);
+		return -ENOMEM;
+	}
+	fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	memset(fe->frontend_priv, 0, sizeof(struct dvb_frontend_private));
+
+	init_MUTEX (&fepriv->sem);
+	init_waitqueue_head (&fepriv->wait_queue);
+	init_waitqueue_head (&fepriv->events.wait_queue);
+	init_MUTEX (&fepriv->events.sem);
+	fe->dvb = dvb;
+	fepriv->inversion = INVERSION_OFF;
+
+	printk ("DVB: registering frontend %i (%s)...\n",
+		fe->dvb->num,
+		fe->ops->info.name);
+
+	dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+			     fe, DVB_DEVICE_FRONTEND);
+
+	up (&frontend_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_register_frontend);
+
+int dvb_unregister_frontend(struct dvb_frontend* fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	dprintk ("%s\n", __FUNCTION__);
+
+	down (&frontend_mutex);
+	dvb_unregister_device (fepriv->dvbdev);
+	dvb_frontend_stop (fe);
+	if (fe->ops->release)
+		fe->ops->release(fe);
+	else
+		printk("dvb_frontend: Demodulator (%s) does not have a release callback!\n", fe->ops->info.name);
+	/* fe is invalid now */
+	kfree(fepriv);
+	up (&frontend_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_frontend);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
new file mode 100644
index 0000000..d2b0217
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -0,0 +1,126 @@
+/*
+ * dvb_frontend.h
+ *
+ * Copyright (C) 2001 convergence integrated media GmbH
+ * Copyright (C) 2004 convergence GmbH
+ *
+ * Written by Ralph Metzler
+ * Overhauled by Holger Waechtler
+ * Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVB_FRONTEND_H_
+#define _DVB_FRONTEND_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dvbdev.h"
+
+/* FIXME: Move to i2c-id.h */
+#define I2C_DRIVERID_DVBFE_SP8870	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX22700	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_AT76C651	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX24110	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX22702	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DIB3000MB	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DST		I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DUMMY	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_L64781	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_MT312	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_MT352	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_NXT6000	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_SP887X	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_STV0299	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA1004X	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA8083	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_VES1820	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_VES1X93	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA80XX	I2C_DRIVERID_EXP2
+
+
+struct dvb_frontend_tune_settings {
+        int min_delay_ms;
+        int step_size;
+        int max_drift;
+        struct dvb_frontend_parameters parameters;
+};
+
+struct dvb_frontend;
+
+struct dvb_frontend_ops {
+
+	struct dvb_frontend_info info;
+
+	void (*release)(struct dvb_frontend* fe);
+
+	int (*init)(struct dvb_frontend* fe);
+	int (*sleep)(struct dvb_frontend* fe);
+
+	int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+	int (*get_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+	int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
+
+	int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
+	int (*read_ber)(struct dvb_frontend* fe, u32* ber);
+	int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength);
+	int (*read_snr)(struct dvb_frontend* fe, u16* snr);
+	int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks);
+
+	int (*diseqc_reset_overload)(struct dvb_frontend* fe);
+	int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
+	int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply);
+	int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
+	int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
+	int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+	int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, int arg);
+	int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd);
+};
+
+#define MAX_EVENT 8
+
+struct dvb_fe_events {
+	struct dvb_frontend_event events[MAX_EVENT];
+	int			  eventw;
+	int			  eventr;
+	int			  overflow;
+	wait_queue_head_t	  wait_queue;
+	struct semaphore	  sem;
+};
+
+struct dvb_frontend {
+	struct dvb_frontend_ops* ops;
+	struct dvb_adapter *dvb;
+	void* demodulator_priv;
+	void* frontend_priv;
+};
+
+extern int dvb_register_frontend(struct dvb_adapter* dvb,
+				 struct dvb_frontend* fe);
+
+extern int dvb_unregister_frontend(struct dvb_frontend* fe);
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
new file mode 100644
index 0000000..44892e7
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -0,0 +1,1381 @@
+/*
+ * dvb_net.c
+ *
+ * Copyright (C) 2001 Convergence integrated media GmbH
+ *                    Ralph Metzler <ralph@convergence.de>
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * ULE Decapsulation code:
+ * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH.
+ *                      and Department of Scientific Computing
+ *                          Paris Lodron University of Salzburg.
+ *                          Hilmar Linder <hlinder@cosy.sbg.ac.at>
+ *                      and Wolfram Stering <wstering@cosy.sbg.ac.at>
+ *
+ * ULE Decaps according to draft-ietf-ipdvb-ule-03.txt.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+ * ULE ChangeLog:
+ * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt
+ *
+ * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt:
+ *                       ULE Extension header handling.
+ *                     Bugreports by Moritz Vieth and Hanno Tersteegen,
+ *                       Fraunhofer Institute for Open Communication Systems
+ *                       Competence Center for Advanced Satellite Communications.
+ *                     Bugfixes and robustness improvements.
+ *                     Filtering on dest MAC addresses, if present (D-Bit = 0)
+ *                     ULE_DEBUG compile-time option.
+ */
+
+/*
+ * FIXME / TODO (dvb_net.c):
+ *
+ * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero.
+ *
+ * TS_FEED callback is called once for every single TS cell although it is
+ * registered (in dvb_net_feed_start()) for 100 TS cells (used for dvb_net_ule()).
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/dvb/net.h>
+#include <linux/uio.h>
+#include <asm/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/version.h>
+
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+static int dvb_net_debug;
+module_param(dvb_net_debug, int, 0444);
+MODULE_PARM_DESC(dvb_net_debug, "enable debug messages");
+
+#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)
+
+
+static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
+{
+	unsigned int j;
+	for (j = 0; j < cnt; j++)
+		c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+	return c;
+}
+
+
+#define DVB_NET_MULTICAST_MAX 10
+
+#undef ULE_DEBUG
+
+#ifdef ULE_DEBUG
+
+#define isprint(c)	((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+
+static void hexdump( const unsigned char *buf, unsigned short len )
+{
+	char str[80], octet[10];
+	int ofs, i, l;
+
+	for (ofs = 0; ofs < len; ofs += 16) {
+		sprintf( str, "%03d: ", ofs );
+
+		for (i = 0; i < 16; i++) {
+			if ((i + ofs) < len)
+				sprintf( octet, "%02x ", buf[ofs + i] );
+			else
+				strcpy( octet, "   " );
+
+			strcat( str, octet );
+		}
+		strcat( str, "  " );
+		l = strlen( str );
+
+		for (i = 0; (i < 16) && ((i + ofs) < len); i++)
+			str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
+
+		str[l] = '\0';
+		printk( KERN_WARNING "%s\n", str );
+	}
+}
+
+#endif
+
+struct dvb_net_priv {
+	int in_use;
+	struct net_device_stats stats;
+	u16 pid;
+	struct dvb_net *host;
+	struct dmx_demux *demux;
+	struct dmx_section_feed *secfeed;
+	struct dmx_section_filter *secfilter;
+	struct dmx_ts_feed *tsfeed;
+	int multi_num;
+	struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
+	unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
+	int rx_mode;
+#define RX_MODE_UNI 0
+#define RX_MODE_MULTI 1
+#define RX_MODE_ALL_MULTI 2
+#define RX_MODE_PROMISC 3
+	struct work_struct set_multicast_list_wq;
+	struct work_struct restart_net_feed_wq;
+	unsigned char feedtype;			/* Either FEED_TYPE_ or FEED_TYPE_ULE */
+	int need_pusi;				/* Set to 1, if synchronization on PUSI required. */
+	unsigned char tscc;			/* TS continuity counter after sync on PUSI. */
+	struct sk_buff *ule_skb;		/* ULE SNDU decodes into this buffer. */
+	unsigned char *ule_next_hdr;		/* Pointer into skb to next ULE extension header. */
+	unsigned short ule_sndu_len;		/* ULE SNDU length in bytes, w/o D-Bit. */
+	unsigned short ule_sndu_type;		/* ULE SNDU type field, complete. */
+	unsigned char ule_sndu_type_1;		/* ULE SNDU type field, if split across 2 TS cells. */
+	unsigned char ule_dbit;			/* Whether the DestMAC address present
+						 * or not (bit is set). */
+	unsigned char ule_bridged;		/* Whether the ULE_BRIDGED extension header was found. */
+	int ule_sndu_remain;			/* Nr. of bytes still required for current ULE SNDU. */
+	unsigned long ts_count;			/* Current ts cell counter. */
+};
+
+
+/**
+ *	Determine the packet's protocol ID. The rule here is that we
+ *	assume 802.3 if the type field is short enough to be a length.
+ *	This is normal practice and works for any 'now in use' protocol.
+ *
+ *  stolen from eth.c out of the linux kernel, hacked for dvb-device
+ *  by Michael Holzt <kju@debian.org>
+ */
+static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb->mac.raw=skb->data;
+	skb_pull(skb,dev->hard_header_len);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
+	eth = skb->mac.ethernet;
+#else
+	eth = eth_hdr(skb);
+#endif
+
+	if (*eth->h_dest & 1) {
+		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+			skb->pkt_type=PACKET_BROADCAST;
+		else
+			skb->pkt_type=PACKET_MULTICAST;
+	}
+
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/**
+	 *	This is a magic hack to spot IPX packets. Older Novell breaks
+	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *	won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *)rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+
+	/**
+	 *	Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+#define TS_SZ	188
+#define TS_SYNC	0x47
+#define TS_TEI	0x80
+#define TS_SC	0xC0
+#define TS_PUSI	0x40
+#define TS_AF_A	0x20
+#define TS_AF_D	0x10
+
+/* ULE Extension Header handlers. */
+
+#define ULE_TEST	0
+#define ULE_BRIDGED	1
+
+static int ule_test_sndu( struct dvb_net_priv *p )
+{
+	return -1;
+}
+
+static int ule_bridged_sndu( struct dvb_net_priv *p )
+{
+	/* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt.
+	 * This has to be the last extension header, otherwise it won't work.
+	 * Blame the authors!
+	 */
+	p->ule_bridged = 1;
+	return 0;
+}
+
+
+/** Handle ULE extension headers.
+ *  Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding.
+ *  Returns: >= 0: nr. of bytes consumed by next extension header
+ *	     -1:   Mandatory extension header that is not recognized or TEST SNDU; discard.
+ */
+static int handle_one_ule_extension( struct dvb_net_priv *p )
+{
+	/* Table of mandatory extension header handlers.  The header type is the index. */
+	static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) =
+		{ [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL,  };
+
+	/* Table of optional extension header handlers.  The header type is the index. */
+	static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = { NULL, };
+
+	int ext_len = 0;
+	unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8;
+	unsigned char htype = p->ule_sndu_type & 0x00FF;
+
+	/* Discriminate mandatory and optional extension headers. */
+	if (hlen == 0) {
+		/* Mandatory extension header */
+		if (ule_mandatory_ext_handlers[htype]) {
+			ext_len = ule_mandatory_ext_handlers[htype]( p );
+			p->ule_next_hdr += ext_len;
+			if (! p->ule_bridged) {
+				p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr );
+				p->ule_next_hdr += 2;
+			} else {
+				p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)) );
+				/* This assures the extension handling loop will terminate. */
+			}
+		} else
+			ext_len = -1;	/* SNDU has to be discarded. */
+	} else {
+		/* Optional extension header.  Calculate the length. */
+		ext_len = hlen << 2;
+		/* Process the optional extension header according to its type. */
+		if (ule_optional_ext_handlers[htype])
+			(void)ule_optional_ext_handlers[htype]( p );
+		p->ule_next_hdr += ext_len;
+		p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr );
+		p->ule_next_hdr += 2;
+	}
+
+	return ext_len;
+}
+
+static int handle_ule_extensions( struct dvb_net_priv *p )
+{
+	int total_ext_len = 0, l;
+
+	p->ule_next_hdr = p->ule_skb->data;
+	do {
+		l = handle_one_ule_extension( p );
+		if (l == -1) return -1;	/* Stop extension header processing and discard SNDU. */
+		total_ext_len += l;
+
+	} while (p->ule_sndu_type < 1536);
+
+	return total_ext_len;
+}
+
+
+/** Prepare for a new ULE SNDU: reset the decoder state. */
+static inline void reset_ule( struct dvb_net_priv *p )
+{
+	p->ule_skb = NULL;
+	p->ule_next_hdr = NULL;
+	p->ule_sndu_len = 0;
+	p->ule_sndu_type = 0;
+	p->ule_sndu_type_1 = 0;
+	p->ule_sndu_remain = 0;
+	p->ule_dbit = 0xFF;
+	p->ule_bridged = 0;
+}
+
+/**
+ * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of
+ * TS cells of a single PID.
+ */
+static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv *)dev->priv;
+	unsigned long skipped = 0L;
+	u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1;
+	struct ethhdr *ethh = NULL;
+
+#ifdef ULE_DEBUG
+	/* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */
+	static unsigned char ule_hist[100*TS_SZ];
+	static unsigned char *ule_where = ule_hist, ule_dump = 0;
+#endif
+
+	if (dev == NULL) {
+		printk( KERN_ERR "NO netdev struct!\n" );
+		return;
+	}
+
+	/* For all TS cells in current buffer.
+	 * Appearently, we are called for every single TS cell.
+	 */
+	for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; /* no default incr. */ ) {
+
+		if (new_ts) {
+			/* We are about to process a new TS cell. */
+
+#ifdef ULE_DEBUG
+			if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist;
+			memcpy( ule_where, ts, TS_SZ );
+			if (ule_dump) {
+				hexdump( ule_where, TS_SZ );
+				ule_dump = 0;
+			}
+			ule_where += TS_SZ;
+#endif
+
+			/* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
+			if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) {
+				printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
+				       priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6);
+
+				/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+				if (priv->ule_skb) {
+					dev_kfree_skb( priv->ule_skb );
+					/* Prepare for next SNDU. */
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+				}
+				reset_ule(priv);
+				priv->need_pusi = 1;
+
+				/* Continue with next TS cell. */
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+
+			ts_remain = 184;
+			from_where = ts + 4;
+		}
+		/* Synchronize on PUSI, if required. */
+		if (priv->need_pusi) {
+			if (ts[1] & TS_PUSI) {
+				/* Find beginning of first ULE SNDU in current TS cell. */
+				/* Synchronize continuity counter. */
+				priv->tscc = ts[3] & 0x0F;
+				/* There is a pointer field here. */
+				if (ts[4] > ts_remain) {
+					printk(KERN_ERR "%lu: Invalid ULE packet "
+					       "(pointer field %d)\n", priv->ts_count, ts[4]);
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+				}
+				/* Skip to destination of pointer field. */
+				from_where = &ts[5] + ts[4];
+				ts_remain -= 1 + ts[4];
+				skipped = 0;
+			} else {
+				skipped++;
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+		}
+
+		/* Check continuity counter. */
+		if (new_ts) {
+			if ((ts[3] & 0x0F) == priv->tscc)
+				priv->tscc = (priv->tscc + 1) & 0x0F;
+			else {
+				/* TS discontinuity handling: */
+				printk(KERN_WARNING "%lu: TS discontinuity: got %#x, "
+				       "exptected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc);
+				/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+				if (priv->ule_skb) {
+					dev_kfree_skb( priv->ule_skb );
+					/* Prepare for next SNDU. */
+					// reset_ule(priv);  moved to below.
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+				}
+				reset_ule(priv);
+				/* skip to next PUSI. */
+				priv->need_pusi = 1;
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+			/* If we still have an incomplete payload, but PUSI is
+			 * set; some TS cells are missing.
+			 * This is only possible here, if we missed exactly 16 TS
+			 * cells (continuity counter wrap). */
+			if (ts[1] & TS_PUSI) {
+				if (! priv->need_pusi) {
+					if (*from_where > 181) {
+						/* Pointer field is invalid.  Drop this TS cell and any started ULE SNDU. */
+						printk(KERN_WARNING "%lu: Invalid pointer "
+						       "field: %u.\n", priv->ts_count, *from_where);
+
+						/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+						if (priv->ule_skb) {
+							dev_kfree_skb( priv->ule_skb );
+							((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+							((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+						}
+						reset_ule(priv);
+						priv->need_pusi = 1;
+						ts += TS_SZ;
+						priv->ts_count++;
+						continue;
+					}
+					/* Skip pointer field (we're processing a
+					 * packed payload). */
+					from_where += 1;
+					ts_remain -= 1;
+				} else
+					priv->need_pusi = 0;
+
+				if (priv->ule_sndu_remain > 183) {
+					/* Current SNDU lacks more data than there could be available in the
+					 * current TS cell. */
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++;
+					printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
+					       "got PUSI (pf %d, ts_remain %d).  Flushing incomplete payload.\n",
+					       priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain);
+					dev_kfree_skb(priv->ule_skb);
+					/* Prepare for next SNDU. */
+					reset_ule(priv);
+					/* Resync: go to where pointer field points to: start of next ULE SNDU. */
+					from_where += ts[4];
+					ts_remain -= ts[4];
+				}
+			}
+		}
+
+		/* Check if new payload needs to be started. */
+		if (priv->ule_skb == NULL) {
+			/* Start a new payload with skb.
+			 * Find ULE header.  It is only guaranteed that the
+			 * length field (2 bytes) is contained in the current
+			 * TS.
+			 * Check ts_remain has to be >= 2 here. */
+			if (ts_remain < 2) {
+				printk(KERN_WARNING "Invalid payload packing: only %d "
+				       "bytes left in TS.  Resyncing.\n", ts_remain);
+				priv->ule_sndu_len = 0;
+				priv->need_pusi = 1;
+				continue;
+			}
+
+			if (! priv->ule_sndu_len) {
+				/* Got at least two bytes, thus extrace the SNDU length. */
+				priv->ule_sndu_len = from_where[0] << 8 | from_where[1];
+				if (priv->ule_sndu_len & 0x8000) {
+					/* D-Bit is set: no dest mac present. */
+					priv->ule_sndu_len &= 0x7FFF;
+					priv->ule_dbit = 1;
+				} else
+					priv->ule_dbit = 0;
+
+				if (priv->ule_sndu_len > 32763) {
+					printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+					       "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
+					priv->ule_sndu_len = 0;
+					priv->need_pusi = 1;
+					new_ts = 1;
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+				}
+				ts_remain -= 2;	/* consume the 2 bytes SNDU length. */
+				from_where += 2;
+			}
+
+			/*
+			 * State of current TS:
+			 *   ts_remain (remaining bytes in the current TS cell)
+			 *   0	ule_type is not available now, we need the next TS cell
+			 *   1	the first byte of the ule_type is present
+			 * >=2	full ULE header present, maybe some payload data as well.
+			 */
+			switch (ts_remain) {
+				case 1:
+					priv->ule_sndu_type = from_where[0] << 8;
+					priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */
+					ts_remain -= 1; from_where += 1;
+					/* Continue w/ next TS. */
+				case 0:
+					new_ts = 1;
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+
+				default: /* complete ULE header is present in current TS. */
+					/* Extract ULE type field. */
+					if (priv->ule_sndu_type_1) {
+						priv->ule_sndu_type |= from_where[0];
+						from_where += 1; /* points to payload start. */
+						ts_remain -= 1;
+					} else {
+						/* Complete type is present in new TS. */
+						priv->ule_sndu_type = from_where[0] << 8 | from_where[1];
+						from_where += 2; /* points to payload start. */
+						ts_remain -= 2;
+					}
+					break;
+			}
+
+			/* Allocate the skb (decoder target buffer) with the correct size, as follows:
+			 * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */
+			priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN );
+			if (priv->ule_skb == NULL) {
+				printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+				       dev->name);
+				((struct dvb_net_priv *)dev->priv)->stats.rx_dropped++;
+				return;
+			}
+
+			/* This includes the CRC32 _and_ dest mac, if !dbit. */
+			priv->ule_sndu_remain = priv->ule_sndu_len;
+			priv->ule_skb->dev = dev;
+			/* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */
+			skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN );
+		}
+
+		/* Copy data into our current skb. */
+		how_much = min(priv->ule_sndu_remain, (int)ts_remain);
+		memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much);
+		priv->ule_sndu_remain -= how_much;
+		ts_remain -= how_much;
+		from_where += how_much;
+
+		/* Check for complete payload. */
+		if (priv->ule_sndu_remain <= 0) {
+			/* Check CRC32, we've got it in our skb already. */
+			unsigned short ulen = htons(priv->ule_sndu_len);
+			unsigned short utype = htons(priv->ule_sndu_type);
+			struct kvec iov[3] = {
+				{ &ulen, sizeof ulen },
+				{ &utype, sizeof utype },
+				{ priv->ule_skb->data, priv->ule_skb->len - 4 }
+			};
+			unsigned long ule_crc = ~0L, expected_crc;
+			if (priv->ule_dbit) {
+				/* Set D-bit for CRC32 verification,
+				 * if it was set originally. */
+				ulen |= 0x0080;
+			}
+
+			ule_crc = iov_crc32(ule_crc, iov, 3);
+			expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 |
+				       *((u8 *)priv->ule_skb->tail - 3) << 16 |
+				       *((u8 *)priv->ule_skb->tail - 2) << 8 |
+				       *((u8 *)priv->ule_skb->tail - 1);
+			if (ule_crc != expected_crc) {
+				printk(KERN_WARNING "%lu: CRC32 check FAILED: %#lx / %#lx, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
+				       priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
+
+#ifdef ULE_DEBUG
+				hexdump( iov[0].iov_base, iov[0].iov_len );
+				hexdump( iov[1].iov_base, iov[1].iov_len );
+				hexdump( iov[2].iov_base, iov[2].iov_len );
+
+				if (ule_where == ule_hist) {
+					hexdump( &ule_hist[98*TS_SZ], TS_SZ );
+					hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+				} else if (ule_where == &ule_hist[TS_SZ]) {
+					hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+					hexdump( ule_hist, TS_SZ );
+				} else {
+					hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ );
+					hexdump( ule_where - TS_SZ, TS_SZ );
+				}
+				ule_dump = 1;
+#endif
+
+				((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+				((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++;
+				dev_kfree_skb(priv->ule_skb);
+			} else {
+				/* CRC32 verified OK. */
+				/* Handle ULE Extension Headers. */
+				if (priv->ule_sndu_type < 1536) {
+					/* There is an extension header.  Handle it accordingly. */
+					int l = handle_ule_extensions( priv );
+					if (l < 0) {
+						/* Mandatory extension header unknown or TEST SNDU.  Drop it. */
+						// printk( KERN_WARNING "Dropping SNDU, extension headers.\n" );
+						dev_kfree_skb( priv->ule_skb );
+						goto sndu_done;
+					}
+					skb_pull( priv->ule_skb, l );
+				}
+
+				/* CRC32 was OK. Remove it from skb. */
+				priv->ule_skb->tail -= 4;
+				priv->ule_skb->len -= 4;
+
+				/* Filter on receiver's destination MAC address, if present. */
+				if (!priv->ule_dbit) {
+					/* The destination MAC address is the next data in the skb. */
+					if (memcmp( priv->ule_skb->data, dev->dev_addr, ETH_ALEN )) {
+						/* MAC addresses don't match.  Drop SNDU. */
+						// printk( KERN_WARNING "Dropping SNDU, MAC address.\n" );
+						dev_kfree_skb( priv->ule_skb );
+						goto sndu_done;
+					}
+					if (! priv->ule_bridged) {
+						skb_push( priv->ule_skb, ETH_ALEN + 2 );
+						ethh = (struct ethhdr *)priv->ule_skb->data;
+						memcpy( ethh->h_dest, ethh->h_source, ETH_ALEN );
+						memset( ethh->h_source, 0, ETH_ALEN );
+						ethh->h_proto = htons( priv->ule_sndu_type );
+					} else {
+						/* Skip the Receiver destination MAC address. */
+						skb_pull( priv->ule_skb, ETH_ALEN );
+					}
+				} else {
+					if (! priv->ule_bridged) {
+						skb_push( priv->ule_skb, ETH_HLEN );
+						ethh = (struct ethhdr *)priv->ule_skb->data;
+						memcpy( ethh->h_dest, dev->dev_addr, ETH_ALEN );
+						memset( ethh->h_source, 0, ETH_ALEN );
+						ethh->h_proto = htons( priv->ule_sndu_type );
+					} else {
+						/* skb is in correct state; nothing to do. */
+					}
+				}
+				priv->ule_bridged = 0;
+
+				/* Stuff into kernel's protocol stack. */
+				priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev);
+				/* If D-bit is set (i.e. destination MAC address not present),
+				 * receive the packet anyhow. */
+				/* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
+					priv->ule_skb->pkt_type = PACKET_HOST; */
+				((struct dvb_net_priv *) dev->priv)->stats.rx_packets++;
+				((struct dvb_net_priv *) dev->priv)->stats.rx_bytes += priv->ule_skb->len;
+				netif_rx(priv->ule_skb);
+			}
+			sndu_done:
+			/* Prepare for next SNDU. */
+			reset_ule(priv);
+		}
+
+		/* More data in current TS (look at the bytes following the CRC32)? */
+		if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) {
+			/* Next ULE SNDU starts right there. */
+			new_ts = 0;
+			priv->ule_skb = NULL;
+			priv->ule_sndu_type_1 = 0;
+			priv->ule_sndu_len = 0;
+			// printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n",
+			//	*(from_where + 0), *(from_where + 1),
+			//	*(from_where + 2), *(from_where + 3));
+			// printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0);
+			// hexdump(ts, 188);
+		} else {
+			new_ts = 1;
+			ts += TS_SZ;
+			priv->ts_count++;
+			if (priv->ule_skb == NULL) {
+				priv->need_pusi = 1;
+				priv->ule_sndu_type_1 = 0;
+				priv->ule_sndu_len = 0;
+			}
+		}
+	}	/* for all available TS cells */
+}
+
+static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
+			       const u8 *buffer2, size_t buffer2_len,
+			       struct dmx_ts_feed *feed, enum dmx_success success)
+{
+	struct net_device *dev = (struct net_device *)feed->priv;
+
+	if (buffer2 != 0)
+		printk(KERN_WARNING "buffer2 not 0: %p.\n", buffer2);
+	if (buffer1_len > 32768)
+		printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len);
+	/* printk("TS callback: %u bytes, %u TS cells @ %p.\n",
+	          buffer1_len, buffer1_len / TS_SZ, buffer1); */
+	dvb_net_ule(dev, buffer1, buffer1_len);
+	return 0;
+}
+
+
+static void dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
+{
+        u8 *eth;
+        struct sk_buff *skb;
+	struct net_device_stats *stats = &(((struct dvb_net_priv *) dev->priv)->stats);
+
+	/* note: pkt_len includes a 32bit checksum */
+	if (pkt_len < 16) {
+		printk("%s: IP/MPE packet length = %d too small.\n",
+			dev->name, pkt_len);
+		stats->rx_errors++;
+		stats->rx_length_errors++;
+		return;
+	}
+/* it seems some ISPs manage to screw up here, so we have to
+ * relax the error checks... */
+#if 0
+	if ((pkt[5] & 0xfd) != 0xc1) {
+		/* drop scrambled or broken packets */
+#else
+	if ((pkt[5] & 0x3c) != 0x00) {
+		/* drop scrambled */
+#endif
+		stats->rx_errors++;
+		stats->rx_crc_errors++;
+		return;
+	}
+	if (pkt[5] & 0x02) {
+		//FIXME: handle LLC/SNAP
+                stats->rx_dropped++;
+                return;
+        }
+	if (pkt[7]) {
+		/* FIXME: assemble datagram from multiple sections */
+		stats->rx_errors++;
+		stats->rx_frame_errors++;
+		return;
+	}
+
+	/* we have 14 byte ethernet header (ip header follows);
+	 * 12 byte MPE header; 4 byte checksum; + 2 byte alignment
+	 */
+	if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2))) {
+		//printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+		stats->rx_dropped++;
+		return;
+	}
+	skb_reserve(skb, 2);    /* longword align L3 header */
+	skb->dev = dev;
+
+	/* copy L3 payload */
+	eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14);
+	memcpy(eth + 14, pkt + 12, pkt_len - 12 - 4);
+
+	/* create ethernet header: */
+        eth[0]=pkt[0x0b];
+        eth[1]=pkt[0x0a];
+        eth[2]=pkt[0x09];
+        eth[3]=pkt[0x08];
+        eth[4]=pkt[0x04];
+        eth[5]=pkt[0x03];
+
+        eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0;
+
+	eth[12] = 0x08;	/* ETH_P_IP */
+	eth[13] = 0x00;
+
+	skb->protocol = dvb_net_eth_type_trans(skb, dev);
+
+	stats->rx_packets++;
+	stats->rx_bytes+=skb->len;
+        netif_rx(skb);
+}
+
+static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
+		 const u8 *buffer2, size_t buffer2_len,
+		 struct dmx_section_filter *filter,
+		 enum dmx_success success)
+{
+        struct net_device *dev=(struct net_device *) filter->priv;
+
+	/**
+	 * we rely on the DVB API definition where exactly one complete
+	 * section is delivered in buffer1
+	 */
+	dvb_net_sec (dev, (u8*) buffer1, buffer1_len);
+	return 0;
+}
+
+static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00};
+static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
+static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static int dvb_net_filter_sec_set(struct net_device *dev,
+		   struct dmx_section_filter **secfilter,
+		   u8 *mac, u8 *mac_mask)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	int ret;
+
+	*secfilter=NULL;
+	ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter);
+	if (ret<0) {
+		printk("%s: could not get filter\n", dev->name);
+		return ret;
+	}
+
+	(*secfilter)->priv=(void *) dev;
+
+	memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE);
+	memset((*secfilter)->filter_mask,  0x00, DMX_MAX_FILTER_SIZE);
+	memset((*secfilter)->filter_mode,  0xff, DMX_MAX_FILTER_SIZE);
+
+	(*secfilter)->filter_value[0]=0x3e;
+	(*secfilter)->filter_value[3]=mac[5];
+	(*secfilter)->filter_value[4]=mac[4];
+	(*secfilter)->filter_value[8]=mac[3];
+	(*secfilter)->filter_value[9]=mac[2];
+	(*secfilter)->filter_value[10]=mac[1];
+	(*secfilter)->filter_value[11]=mac[0];
+
+	(*secfilter)->filter_mask[0] = 0xff;
+	(*secfilter)->filter_mask[3] = mac_mask[5];
+	(*secfilter)->filter_mask[4] = mac_mask[4];
+	(*secfilter)->filter_mask[8] = mac_mask[3];
+	(*secfilter)->filter_mask[9] = mac_mask[2];
+	(*secfilter)->filter_mask[10] = mac_mask[1];
+	(*secfilter)->filter_mask[11]=mac_mask[0];
+
+	dprintk("%s: filter mac=%02x %02x %02x %02x %02x %02x\n",
+	       dev->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	dprintk("%s: filter mask=%02x %02x %02x %02x %02x %02x\n",
+	       dev->name, mac_mask[0], mac_mask[1], mac_mask[2],
+	       mac_mask[3], mac_mask[4], mac_mask[5]);
+
+	return 0;
+}
+
+static int dvb_net_feed_start(struct net_device *dev)
+{
+	int ret, i;
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+        struct dmx_demux *demux = priv->demux;
+        unsigned char *mac = (unsigned char *) dev->dev_addr;
+
+	dprintk("%s: rx_mode %i\n", __FUNCTION__, priv->rx_mode);
+	if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
+		printk("%s: BUG %d\n", __FUNCTION__, __LINE__);
+
+	priv->secfeed=NULL;
+	priv->secfilter=NULL;
+	priv->tsfeed = NULL;
+
+	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+		dprintk("%s: alloc secfeed\n", __FUNCTION__);
+		ret=demux->allocate_section_feed(demux, &priv->secfeed,
+					 dvb_net_sec_callback);
+		if (ret<0) {
+			printk("%s: could not allocate section feed\n", dev->name);
+			return ret;
+		}
+
+		ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 0, 1);
+
+		if (ret<0) {
+			printk("%s: could not set section feed\n", dev->name);
+			priv->demux->release_section_feed(priv->demux, priv->secfeed);
+			priv->secfeed=NULL;
+			return ret;
+		}
+
+		if (priv->rx_mode != RX_MODE_PROMISC) {
+			dprintk("%s: set secfilter\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
+		}
+
+		switch (priv->rx_mode) {
+		case RX_MODE_MULTI:
+			for (i = 0; i < priv->multi_num; i++) {
+				dprintk("%s: set multi_secfilter[%d]\n", __FUNCTION__, i);
+				dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
+						       priv->multi_macs[i], mask_normal);
+			}
+			break;
+		case RX_MODE_ALL_MULTI:
+			priv->multi_num=1;
+			dprintk("%s: set multi_secfilter[0]\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
+					       mac_allmulti, mask_allmulti);
+			break;
+		case RX_MODE_PROMISC:
+			priv->multi_num=0;
+			dprintk("%s: set secfilter\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
+			break;
+		}
+
+		dprintk("%s: start filtering\n", __FUNCTION__);
+		priv->secfeed->start_filtering(priv->secfeed);
+	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+		struct timespec timeout = { 0, 30000000 }; // 30 msec
+
+		/* we have payloads encapsulated in TS */
+		dprintk("%s: alloc tsfeed\n", __FUNCTION__);
+		ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
+		if (ret < 0) {
+			printk("%s: could not allocate ts feed\n", dev->name);
+			return ret;
+		}
+
+		/* Set netdevice pointer for ts decaps callback. */
+		priv->tsfeed->priv = (void *)dev;
+		ret = priv->tsfeed->set(priv->tsfeed, priv->pid,
+					TS_PACKET, DMX_TS_PES_OTHER,
+					188 * 100, /* nr. of bytes delivered per callback */
+					32768,     /* circular buffer size */
+					0,         /* descramble */
+					timeout);
+
+		if (ret < 0) {
+			printk("%s: could not set ts feed\n", dev->name);
+			priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+			priv->tsfeed = NULL;
+			return ret;
+		}
+
+		dprintk("%s: start filtering\n", __FUNCTION__);
+		priv->tsfeed->start_filtering(priv->tsfeed);
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int dvb_net_feed_stop(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+		if (priv->secfeed) {
+			if (priv->secfeed->is_filtering) {
+				dprintk("%s: stop secfeed\n", __FUNCTION__);
+				priv->secfeed->stop_filtering(priv->secfeed);
+			}
+
+			if (priv->secfilter) {
+				dprintk("%s: release secfilter\n", __FUNCTION__);
+				priv->secfeed->release_filter(priv->secfeed,
+							      priv->secfilter);
+				priv->secfilter=NULL;
+			}
+
+			for (i=0; i<priv->multi_num; i++) {
+				if (priv->multi_secfilter[i]) {
+					dprintk("%s: release multi_filter[%d]\n",
+						__FUNCTION__, i);
+					priv->secfeed->release_filter(priv->secfeed,
+								      priv->multi_secfilter[i]);
+					priv->multi_secfilter[i] = NULL;
+				}
+			}
+
+			priv->demux->release_section_feed(priv->demux, priv->secfeed);
+			priv->secfeed = NULL;
+		} else
+			printk("%s: no feed to stop\n", dev->name);
+	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+		if (priv->tsfeed) {
+			if (priv->tsfeed->is_filtering) {
+				dprintk("%s: stop tsfeed\n", __FUNCTION__);
+				priv->tsfeed->stop_filtering(priv->tsfeed);
+			}
+			priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+			priv->tsfeed = NULL;
+		}
+		else
+			printk("%s: no ts feed to stop\n", dev->name);
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+
+static int dvb_set_mc_filter (struct net_device *dev, struct dev_mc_list *mc)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	if (priv->multi_num == DVB_NET_MULTICAST_MAX)
+		return -ENOMEM;
+
+	memcpy(priv->multi_macs[priv->multi_num], mc->dmi_addr, 6);
+
+	priv->multi_num++;
+	return 0;
+}
+
+
+static void wq_set_multicast_list (void *data)
+{
+	struct net_device *dev = data;
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	dvb_net_feed_stop(dev);
+
+	priv->rx_mode = RX_MODE_UNI;
+
+	if (dev->flags & IFF_PROMISC) {
+		dprintk("%s: promiscuous mode\n", dev->name);
+		priv->rx_mode = RX_MODE_PROMISC;
+	} else if ((dev->flags & IFF_ALLMULTI)) {
+		dprintk("%s: allmulti mode\n", dev->name);
+		priv->rx_mode = RX_MODE_ALL_MULTI;
+	} else if (dev->mc_count) {
+		int mci;
+		struct dev_mc_list *mc;
+
+		dprintk("%s: set_mc_list, %d entries\n",
+			dev->name, dev->mc_count);
+
+		priv->rx_mode = RX_MODE_MULTI;
+		priv->multi_num = 0;
+
+		for (mci = 0, mc=dev->mc_list;
+		     mci < dev->mc_count;
+		     mc = mc->next, mci++) {
+			dvb_set_mc_filter(dev, mc);
+		}
+	}
+
+	dvb_net_feed_start(dev);
+}
+
+
+static void dvb_net_set_multicast_list (struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	schedule_work(&priv->set_multicast_list_wq);
+}
+
+
+static void wq_restart_net_feed (void *data)
+{
+	struct net_device *dev = data;
+
+	if (netif_running(dev)) {
+		dvb_net_feed_stop(dev);
+		dvb_net_feed_start(dev);
+	}
+}
+
+
+static int dvb_net_set_mac (struct net_device *dev, void *p)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	struct sockaddr *addr=p;
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	if (netif_running(dev))
+		schedule_work(&priv->restart_net_feed_wq);
+
+	return 0;
+}
+
+
+static int dvb_net_open(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	priv->in_use++;
+	dvb_net_feed_start(dev);
+	return 0;
+}
+
+
+static int dvb_net_stop(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	priv->in_use--;
+        return dvb_net_feed_stop(dev);
+}
+
+static struct net_device_stats * dvb_net_get_stats(struct net_device *dev)
+{
+        return &((struct dvb_net_priv*) dev->priv)->stats;
+}
+
+static void dvb_net_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->open		= dvb_net_open;
+	dev->stop		= dvb_net_stop;
+	dev->hard_start_xmit	= dvb_net_tx;
+	dev->get_stats		= dvb_net_get_stats;
+	dev->set_multicast_list = dvb_net_set_multicast_list;
+	dev->set_mac_address    = dvb_net_set_mac;
+	dev->mtu		= 4096;
+	dev->mc_count           = 0;
+	dev->hard_header_cache  = NULL;
+	dev->flags |= IFF_NOARP;
+}
+
+static int get_if(struct dvb_net *dvbnet)
+{
+	int i;
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+		if (!dvbnet->state[i])
+			break;
+
+	if (i == DVB_NET_DEVICES_MAX)
+		return -1;
+
+	dvbnet->state[i]=1;
+	return i;
+}
+
+static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
+{
+	struct net_device *net;
+	struct dvb_net_priv *priv;
+	int result;
+	int if_num;
+
+	if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE)
+		return -EINVAL;
+	if ((if_num = get_if(dvbnet)) < 0)
+		return -EINVAL;
+
+	net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup);
+	if (!net)
+		return -ENOMEM;
+
+	if (dvbnet->dvbdev->id)
+		snprintf(net->name, IFNAMSIZ, "dvb%d%u%d",
+			 dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num);
+	else
+		/* compatibility fix to keep dvb0_0 format */
+		snprintf(net->name, IFNAMSIZ, "dvb%d_%d",
+			 dvbnet->dvbdev->adapter->num, if_num);
+
+	net->addr_len = 6;
+	memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6);
+
+	dvbnet->device[if_num] = net;
+
+	priv = net->priv;
+	priv->demux = dvbnet->demux;
+	priv->pid = pid;
+	priv->rx_mode = RX_MODE_UNI;
+	priv->need_pusi = 1;
+	priv->tscc = 0;
+	priv->feedtype = feedtype;
+	reset_ule(priv);
+
+	INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list, net);
+	INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed, net);
+
+	net->base_addr = pid;
+
+	if ((result = register_netdev(net)) < 0) {
+		dvbnet->device[if_num] = NULL;
+		free_netdev(net);
+		return result;
+	}
+	printk("dvb_net: created network interface %s\n", net->name);
+
+	return if_num;
+}
+
+static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned int num)
+{
+	struct net_device *net = dvbnet->device[num];
+	struct dvb_net_priv *priv;
+
+	if (!dvbnet->state[num])
+		return -EINVAL;
+	priv = net->priv;
+	if (priv->in_use)
+		return -EBUSY;
+
+	dvb_net_stop(net);
+	flush_scheduled_work();
+	printk("dvb_net: removed network interface %s\n", net->name);
+	unregister_netdev(net);
+	dvbnet->state[num]=0;
+	dvbnet->device[num] = NULL;
+	free_netdev(net);
+
+	return 0;
+}
+
+static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
+		  unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_net *dvbnet = (struct dvb_net *) dvbdev->priv;
+
+	if (((file->f_flags&O_ACCMODE)==O_RDONLY))
+		return -EPERM;
+
+	switch (cmd) {
+	case NET_ADD_IF:
+	{
+		struct dvb_net_if *dvbnetif=(struct dvb_net_if *)parg;
+		int result;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!try_module_get(dvbdev->adapter->module))
+			return -EPERM;
+
+		result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
+		if (result<0) {
+			module_put(dvbdev->adapter->module);
+			return result;
+		}
+		dvbnetif->if_num=result;
+		break;
+	}
+	case NET_GET_IF:
+	{
+		struct net_device *netdev;
+		struct dvb_net_priv *priv_data;
+		struct dvb_net_if *dvbnetif=(struct dvb_net_if *)parg;
+
+		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+		    !dvbnet->state[dvbnetif->if_num])
+			return -EINVAL;
+
+		netdev = dvbnet->device[dvbnetif->if_num];
+
+		priv_data=(struct dvb_net_priv*)netdev->priv;
+		dvbnetif->pid=priv_data->pid;
+		dvbnetif->feedtype=priv_data->feedtype;
+		break;
+	}
+	case NET_REMOVE_IF:
+	{
+		int ret;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if ((unsigned int) parg >= DVB_NET_DEVICES_MAX)
+			return -EINVAL;
+		ret = dvb_net_remove_if(dvbnet, (unsigned int) parg);
+		if (!ret)
+			module_put(dvbdev->adapter->module);
+		return ret;
+	}
+
+	/* binary compatiblity cruft */
+	case __NET_ADD_IF_OLD:
+	{
+		struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+		int result;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!try_module_get(dvbdev->adapter->module))
+			return -EPERM;
+
+		result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
+		if (result<0) {
+			module_put(dvbdev->adapter->module);
+			return result;
+		}
+		dvbnetif->if_num=result;
+		break;
+	}
+	case __NET_GET_IF_OLD:
+	{
+		struct net_device *netdev;
+		struct dvb_net_priv *priv_data;
+		struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+
+		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+		    !dvbnet->state[dvbnetif->if_num])
+			return -EINVAL;
+
+		netdev = dvbnet->device[dvbnetif->if_num];
+
+		priv_data=(struct dvb_net_priv*)netdev->priv;
+		dvbnetif->pid=priv_data->pid;
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int dvb_net_ioctl(struct inode *inode, struct file *file,
+	      unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl);
+}
+
+static struct file_operations dvb_net_fops = {
+	.owner = THIS_MODULE,
+	.ioctl = dvb_net_ioctl,
+	.open =	dvb_generic_open,
+	.release = dvb_generic_release,
+};
+
+static struct dvb_device dvbdev_net = {
+        .priv = NULL,
+        .users = 1,
+        .writers = 1,
+        .fops = &dvb_net_fops,
+};
+
+
+void dvb_net_release (struct dvb_net *dvbnet)
+{
+	int i;
+
+	dvb_unregister_device(dvbnet->dvbdev);
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
+		if (!dvbnet->state[i])
+			continue;
+		dvb_net_remove_if(dvbnet, i);
+	}
+}
+EXPORT_SYMBOL(dvb_net_release);
+
+
+int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
+		  struct dmx_demux *dmx)
+{
+	int i;
+
+	dvbnet->demux = dmx;
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+		dvbnet->state[i] = 0;
+
+	dvb_register_device (adap, &dvbnet->dvbdev, &dvbdev_net,
+			     dvbnet, DVB_DEVICE_NET);
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_net_init);
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
new file mode 100644
index 0000000..f14e4ca
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -0,0 +1,46 @@
+/*
+ * dvb_net.h
+ *
+ * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVB_NET_H_
+#define _DVB_NET_H_
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "dvbdev.h"
+
+#define DVB_NET_DEVICES_MAX 10
+
+struct dvb_net {
+	struct dvb_device *dvbdev;
+	struct net_device *device[DVB_NET_DEVICES_MAX];
+	int state[DVB_NET_DEVICES_MAX];
+	struct dmx_demux *demux;
+};
+
+
+void dvb_net_release(struct dvb_net *);
+int  dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
new file mode 100644
index 0000000..fb6d94a
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -0,0 +1,270 @@
+/*
+ *
+ * dvb_ringbuffer.c: ring buffer implementation for the dvb driver
+ *
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+
+#define __KERNEL_SYSCALLS__
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include "dvb_ringbuffer.h"
+
+#define PKT_READY 0
+#define PKT_DISPOSED 1
+
+
+void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
+{
+        rbuf->pread=rbuf->pwrite=0;
+        rbuf->data=data;
+        rbuf->size=len;
+
+        init_waitqueue_head(&rbuf->queue);
+
+        spin_lock_init(&(rbuf->lock));
+}
+
+
+
+int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
+{
+        return (rbuf->pread==rbuf->pwrite);
+}
+
+
+
+ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
+{
+        ssize_t free;
+
+        free = rbuf->pread - rbuf->pwrite;
+        if (free <= 0)
+                free += rbuf->size;
+        return free-1;
+}
+
+
+
+ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
+{
+        ssize_t avail;
+
+        avail = rbuf->pwrite - rbuf->pread;
+        if (avail < 0)
+                avail += rbuf->size;
+        return avail;
+}
+
+
+
+void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
+{
+        rbuf->pread = rbuf->pwrite;
+}
+
+
+
+void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
+{
+        unsigned long flags;
+
+        spin_lock_irqsave(&rbuf->lock, flags);
+        dvb_ringbuffer_flush(rbuf);
+        spin_unlock_irqrestore(&rbuf->lock, flags);
+
+        wake_up(&rbuf->queue);
+}
+
+
+
+ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem)
+{
+        size_t todo = len;
+        size_t split;
+
+        split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+        if (split > 0) {
+                if (!usermem)
+                        memcpy(buf, rbuf->data+rbuf->pread, split);
+                else
+                        if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
+                                return -EFAULT;
+                buf += split;
+                todo -= split;
+                rbuf->pread = 0;
+        }
+        if (!usermem)
+                memcpy(buf, rbuf->data+rbuf->pread, todo);
+        else
+                if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
+                        return -EFAULT;
+
+        rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+
+        return len;
+}
+
+
+
+ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
+{
+        size_t todo = len;
+        size_t split;
+
+        split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+
+        if (split > 0) {
+                memcpy(rbuf->data+rbuf->pwrite, buf, split);
+                buf += split;
+                todo -= split;
+                rbuf->pwrite = 0;
+        }
+        memcpy(rbuf->data+rbuf->pwrite, buf, todo);
+        rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+        return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
+{
+        int status;
+        ssize_t oldpwrite = rbuf->pwrite;
+
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
+        status = dvb_ringbuffer_write(rbuf, buf, len);
+
+        if (status < 0) rbuf->pwrite = oldpwrite;
+        return status;
+}
+
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+                                int offset, u8* buf, size_t len, int usermem)
+{
+        size_t todo;
+        size_t split;
+        size_t pktlen;
+
+        pktlen = rbuf->data[idx] << 8;
+        pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+        if (offset > pktlen) return -EINVAL;
+        if ((offset + len) > pktlen) len = pktlen - offset;
+
+        idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+        todo = len;
+        split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+        if (split > 0) {
+                if (!usermem)
+                        memcpy(buf, rbuf->data+idx, split);
+                else
+                        if (copy_to_user(buf, rbuf->data+idx, split))
+                                return -EFAULT;
+                buf += split;
+                todo -= split;
+                idx = 0;
+        }
+        if (!usermem)
+                memcpy(buf, rbuf->data+idx, todo);
+        else
+                if (copy_to_user(buf, rbuf->data+idx, todo))
+                        return -EFAULT;
+
+        return len;
+}
+
+void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
+{
+        size_t pktlen;
+
+        rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
+
+        // clean up disposed packets
+        while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+                if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
+                        pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
+                        pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
+                        DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
+                } else {
+                        // first packet is not disposed, so we stop cleaning now
+                        break;
+                }
+        }
+}
+
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
+{
+        int consumed;
+        int curpktlen;
+        int curpktstatus;
+
+        if (idx == -1) {
+	       idx = rbuf->pread;
+	} else {
+                curpktlen = rbuf->data[idx] << 8;
+                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+	        idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+	}
+
+        consumed = (idx - rbuf->pread) % rbuf->size;
+
+        while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+
+                curpktlen = rbuf->data[idx] << 8;
+                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+                curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
+
+                if (curpktstatus == PKT_READY) {
+                        *pktlen = curpktlen;
+                        return idx;
+                }
+
+                consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
+                idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+        }
+
+        // no packets available
+        return -1;
+}
+
+
+
+EXPORT_SYMBOL(dvb_ringbuffer_init);
+EXPORT_SYMBOL(dvb_ringbuffer_empty);
+EXPORT_SYMBOL(dvb_ringbuffer_free);
+EXPORT_SYMBOL(dvb_ringbuffer_avail);
+EXPORT_SYMBOL(dvb_ringbuffer_flush);
+EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
+EXPORT_SYMBOL(dvb_ringbuffer_read);
+EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
new file mode 100644
index 0000000..d18e9c4
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * dvb_ringbuffer.h: ring buffer implementation for the dvb driver
+ *
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
+ *                         for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DVB_RINGBUFFER_H_
+#define _DVB_RINGBUFFER_H_
+
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+struct dvb_ringbuffer {
+        u8               *data;
+        ssize_t           size;
+        ssize_t           pread;
+        ssize_t           pwrite;
+
+        wait_queue_head_t queue;
+        spinlock_t        lock;
+};
+
+#define DVB_RINGBUFFER_PKTHDRSIZE 3
+
+
+/*
+** Notes:
+** ------
+** (1) For performance reasons read and write routines don't check buffer sizes
+**     and/or number of bytes free/available. This has to be done before these
+**     routines are called. For example:
+**
+**     *** write <buflen> bytes ***
+**     free = dvb_ringbuffer_free(rbuf);
+**     if (free >= buflen)
+**         count = dvb_ringbuffer_write(rbuf, buffer, buflen);
+**     else
+**         ...
+**
+**     *** read min. 1000, max. <bufsize> bytes ***
+**     avail = dvb_ringbuffer_avail(rbuf);
+**     if (avail >= 1000)
+**         count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize), 0);
+**     else
+**         ...
+**
+** (2) If there is exactly one reader and one writer, there is no need
+**     to lock read or write operations.
+**     Two or more readers must be locked against each other.
+**     Flushing the buffer counts as a read operation.
+**     Two or more writers must be locked against each other.
+*/
+
+/* initialize ring buffer, lock and queue */
+extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len);
+
+/* test whether buffer is empty */
+extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf);
+
+/* return the number of free bytes in the buffer */
+extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf);
+
+/* return the number of bytes waiting in the buffer */
+extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf);
+
+
+/* read routines & macros */
+/* ---------------------- */
+/* flush buffer */
+extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf);
+
+/* flush buffer protected by spinlock and wake-up waiting task(s) */
+extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
+
+/* peek at byte <offs> in the buffer */
+#define DVB_RINGBUFFER_PEEK(rbuf,offs)	\
+			(rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size]
+
+/* advance read ptr by <num> bytes */
+#define DVB_RINGBUFFER_SKIP(rbuf,num)	\
+			(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
+
+/*
+** read <len> bytes from ring buffer into <buf>
+** <usermem> specifies whether <buf> resides in user space
+** returns number of bytes transferred or -EFAULT
+*/
+extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
+                                   size_t len, int usermem);
+
+
+/* write routines & macros */
+/* ----------------------- */
+/* write single byte to ring buffer */
+#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte)	\
+			{ (rbuf)->data[(rbuf)->pwrite]=(byte); \
+			(rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; }
+/*
+** write <len> bytes to ring buffer
+** <usermem> specifies whether <buf> resides in user space
+** returns number of bytes transferred or -EFAULT
+*/
+extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
+                                    size_t len);
+
+
+/**
+ * Write a packet into the ringbuffer.
+ *
+ * <rbuf> Ringbuffer to write to.
+ * <buf> Buffer to write.
+ * <len> Length of buffer (currently limited to 65535 bytes max).
+ * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
+ */
+extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
+                                        size_t len);
+
+/**
+ * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
+ * does NOT update the read pointer in the ringbuffer. You must use
+ * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ * <offset> Offset into packet to read from.
+ * <buf> Destination buffer for data.
+ * <len> Size of destination buffer.
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes read, or -EFAULT.
+ */
+extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+                                       int offset, u8* buf, size_t len, int usermem);
+
+/**
+ * Dispose of a packet in the ring buffer.
+ *
+ * <rbuf> Ring buffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ */
+extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
+
+/**
+ * Get the index of the next packet in a ringbuffer.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Previous packet index, or -1 to return the first packet index.
+ * <pktlen> On success, will be updated to contain the length of the packet in bytes.
+ * returns Packet index (if >=0), or -1 if no packets available.
+ */
+extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+
+
+#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
new file mode 100644
index 0000000..cf4ffe3
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -0,0 +1,449 @@
+/*
+ * dvbdev.c
+ *
+ * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
+ *                  & Marcus Metzler <marcus@convergence.de>
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#include "dvbdev.h"
+
+static int dvbdev_debug;
+
+module_param(dvbdev_debug, int, 0644);
+MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
+
+#define dprintk if (dvbdev_debug) printk
+
+static LIST_HEAD(dvb_adapter_list);
+static DECLARE_MUTEX(dvbdev_register_lock);
+
+static const char * const dnames[] = {
+        "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+	"net", "osd"
+};
+
+#define DVB_MAX_ADAPTERS	8
+#define DVB_MAX_IDS		4
+#define nums2minor(num,type,id)	((num << 6) | (id << 4) | type)
+#define MAX_DVB_MINORS		(DVB_MAX_ADAPTERS*64)
+
+struct class_simple *dvb_class;
+EXPORT_SYMBOL(dvb_class);
+
+static struct dvb_device* dvbdev_find_device (int minor)
+{
+	struct list_head *entry;
+
+	list_for_each (entry, &dvb_adapter_list) {
+		struct list_head *entry0;
+		struct dvb_adapter *adap;
+		adap = list_entry (entry, struct dvb_adapter, list_head);
+		list_for_each (entry0, &adap->device_list) {
+			struct dvb_device *dev;
+			dev = list_entry (entry0, struct dvb_device, list_head);
+			if (nums2minor(adap->num, dev->type, dev->id) == minor)
+				return dev;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int dvb_device_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev;
+
+	dvbdev = dvbdev_find_device (iminor(inode));
+
+	if (dvbdev && dvbdev->fops) {
+		int err = 0;
+		struct file_operations *old_fops;
+
+		file->private_data = dvbdev;
+		old_fops = file->f_op;
+                file->f_op = fops_get(dvbdev->fops);
+                if(file->f_op->open)
+                        err = file->f_op->open(inode,file);
+                if (err) {
+                        fops_put(file->f_op);
+                        file->f_op = fops_get(old_fops);
+                }
+                fops_put(old_fops);
+                return err;
+	}
+	return -ENODEV;
+}
+
+
+static struct file_operations dvb_device_fops =
+{
+	.owner =	THIS_MODULE,
+	.open =		dvb_device_open,
+};
+
+static struct cdev dvb_device_cdev = {
+	.kobj   = {.name = "dvb", },
+	.owner  =       THIS_MODULE,
+};
+
+int dvb_generic_open(struct inode *inode, struct file *file)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+        if (!dvbdev)
+                return -ENODEV;
+
+	if (!dvbdev->users)
+                return -EBUSY;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+                if (!dvbdev->readers)
+		        return -EBUSY;
+		dvbdev->readers--;
+	} else {
+                if (!dvbdev->writers)
+		        return -EBUSY;
+		dvbdev->writers--;
+	}
+
+	dvbdev->users--;
+	return 0;
+}
+EXPORT_SYMBOL(dvb_generic_open);
+
+
+int dvb_generic_release(struct inode *inode, struct file *file)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+	if (!dvbdev)
+                return -ENODEV;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		dvbdev->readers++;
+	} else {
+		dvbdev->writers++;
+	}
+
+	dvbdev->users++;
+	return 0;
+}
+EXPORT_SYMBOL(dvb_generic_release);
+
+
+int dvb_generic_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+        if (!dvbdev)
+	        return -ENODEV;
+
+	if (!dvbdev->kernel_ioctl)
+		return -EINVAL;
+
+	return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
+}
+EXPORT_SYMBOL(dvb_generic_ioctl);
+
+
+static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
+{
+	u32 id = 0;
+
+	while (id < DVB_MAX_IDS) {
+		struct list_head *entry;
+		list_for_each (entry, &adap->device_list) {
+			struct dvb_device *dev;
+			dev = list_entry (entry, struct dvb_device, list_head);
+			if (dev->type == type && dev->id == id)
+				goto skip;
+		}
+		return id;
+skip:
+		id++;
+	}
+	return -ENFILE;
+}
+
+
+int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
+			const struct dvb_device *template, void *priv, int type)
+{
+	struct dvb_device *dvbdev;
+	int id;
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+
+	if ((id = dvbdev_get_free_id (adap, type)) < 0) {
+		up (&dvbdev_register_lock);
+		*pdvbdev = NULL;
+		printk ("%s: could get find free device id...\n", __FUNCTION__);
+		return -ENFILE;
+	}
+
+	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
+
+	if (!dvbdev) {
+		up(&dvbdev_register_lock);
+		return -ENOMEM;
+	}
+
+	up (&dvbdev_register_lock);
+
+	memcpy(dvbdev, template, sizeof(struct dvb_device));
+	dvbdev->type = type;
+	dvbdev->id = id;
+	dvbdev->adapter = adap;
+	dvbdev->priv = priv;
+
+	dvbdev->fops->owner = adap->module;
+
+	list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+	devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
+			S_IFCHR | S_IRUSR | S_IWUSR,
+			"dvb/adapter%d/%s%d", adap->num, dnames[type], id);
+
+	class_simple_device_add(dvb_class, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
+				NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
+
+	dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
+		adap->num, dnames[type], id, nums2minor(adap->num, type, id),
+		nums2minor(adap->num, type, id));
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_register_device);
+
+
+void dvb_unregister_device(struct dvb_device *dvbdev)
+{
+	if (!dvbdev)
+		return;
+
+	devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num,
+			dnames[dvbdev->type], dvbdev->id);
+
+	class_simple_device_remove(MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
+					dvbdev->type, dvbdev->id)));
+
+	list_del (&dvbdev->list_head);
+	kfree (dvbdev);
+}
+EXPORT_SYMBOL(dvb_unregister_device);
+
+
+static int dvbdev_get_free_adapter_num (void)
+{
+	int num = 0;
+
+	while (num < DVB_MAX_ADAPTERS) {
+		struct list_head *entry;
+		list_for_each (entry, &dvb_adapter_list) {
+			struct dvb_adapter *adap;
+			adap = list_entry (entry, struct dvb_adapter, list_head);
+			if (adap->num == num)
+				goto skip;
+		}
+		return num;
+skip:
+		num++;
+	}
+
+	return -ENFILE;
+}
+
+
+int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct module *module)
+{
+	struct dvb_adapter *adap;
+	int num;
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+
+	if ((num = dvbdev_get_free_adapter_num ()) < 0) {
+		up (&dvbdev_register_lock);
+		return -ENFILE;
+	}
+
+	if (!(*padap = adap = kmalloc(sizeof(struct dvb_adapter), GFP_KERNEL))) {
+		up(&dvbdev_register_lock);
+		return -ENOMEM;
+	}
+
+	memset (adap, 0, sizeof(struct dvb_adapter));
+	INIT_LIST_HEAD (&adap->device_list);
+
+	printk ("DVB: registering new adapter (%s).\n", name);
+
+	devfs_mk_dir("dvb/adapter%d", num);
+	adap->num = num;
+	adap->name = name;
+	adap->module = module;
+
+	list_add_tail (&adap->list_head, &dvb_adapter_list);
+
+	up (&dvbdev_register_lock);
+
+	return num;
+}
+EXPORT_SYMBOL(dvb_register_adapter);
+
+
+int dvb_unregister_adapter(struct dvb_adapter *adap)
+{
+	devfs_remove("dvb/adapter%d", adap->num);
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+	list_del (&adap->list_head);
+	up (&dvbdev_register_lock);
+	kfree (adap);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_adapter);
+
+/* if the miracle happens and "generic_usercopy()" is included into
+   the kernel, then this can vanish. please don't make the mistake and
+   define this as video_usercopy(). this will introduce a dependecy
+   to the v4l "videodev.o" module, which is unnecessary for some
+   cards (ie. the budget dvb-cards don't need the v4l module...) */
+int dvb_usercopy(struct inode *inode, struct file *file,
+	             unsigned int cmd, unsigned long arg,
+		     int (*func)(struct inode *inode, struct file *file,
+		     unsigned int cmd, void *arg))
+{
+        char    sbuf[128];
+        void    *mbuf = NULL;
+        void    *parg = NULL;
+        int     err  = -EINVAL;
+
+        /*  Copy arguments into temp kernel buffer  */
+        switch (_IOC_DIR(cmd)) {
+        case _IOC_NONE:
+		/*
+		 * For this command, the pointer is actually an integer
+		 * argument.
+		 */
+		parg = (void *) arg;
+		break;
+        case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+        case _IOC_WRITE:
+        case (_IOC_WRITE | _IOC_READ):
+                if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+                        parg = sbuf;
+                } else {
+                        /* too big to allocate from stack */
+                        mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
+                        if (NULL == mbuf)
+                                return -ENOMEM;
+                        parg = mbuf;
+                }
+
+                err = -EFAULT;
+                if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+                        goto out;
+                break;
+        }
+
+        /* call driver */
+        if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
+                err = -EINVAL;
+
+        if (err < 0)
+                goto out;
+
+        /*  Copy results into user buffer  */
+        switch (_IOC_DIR(cmd))
+        {
+        case _IOC_READ:
+        case (_IOC_WRITE | _IOC_READ):
+                if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+                        err = -EFAULT;
+                break;
+        }
+
+out:
+        kfree(mbuf);
+        return err;
+}
+
+static int __init init_dvbdev(void)
+{
+	int retval;
+	dev_t dev = MKDEV(DVB_MAJOR, 0);
+
+	if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
+		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
+		return retval;
+	}
+
+	cdev_init(&dvb_device_cdev, &dvb_device_fops);
+	if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
+		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
+		goto error;
+	}
+
+	devfs_mk_dir("dvb");
+
+	dvb_class = class_simple_create(THIS_MODULE, "dvb");
+	if (IS_ERR(dvb_class)) {
+		retval = PTR_ERR(dvb_class);
+		goto error;
+	}
+	return 0;
+
+error:
+	cdev_del(&dvb_device_cdev);
+	unregister_chrdev_region(dev, MAX_DVB_MINORS);
+	return retval;
+}
+
+
+static void __exit exit_dvbdev(void)
+{
+        devfs_remove("dvb");
+	class_simple_destroy(dvb_class);
+	cdev_del(&dvb_device_cdev);
+        unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
+}
+
+module_init(init_dvbdev);
+module_exit(exit_dvbdev);
+
+MODULE_DESCRIPTION("DVB Core Driver");
+MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
new file mode 100644
index 0000000..184edba
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -0,0 +1,104 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+
+#define DVB_MAJOR 212
+
+#define DVB_DEVICE_VIDEO      0
+#define DVB_DEVICE_AUDIO      1
+#define DVB_DEVICE_SEC        2
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+#define DVB_DEVICE_NET        7
+#define DVB_DEVICE_OSD        8
+
+
+struct dvb_adapter {
+	int num;
+	struct list_head list_head;
+	struct list_head device_list;
+	const char *name;
+	u8 proposed_mac [6];
+	void* priv;
+
+	struct module *module;
+};
+
+
+struct dvb_device {
+	struct list_head list_head;
+	struct file_operations *fops;
+	struct dvb_adapter *adapter;
+	int type;
+	u32 id;
+
+	/* in theory, 'users' can vanish now,
+	   but I don't want to change too much now... */
+	int readers;
+	int writers;
+	int users;
+
+        /* don't really need those !? -- FIXME: use video_usercopy  */
+        int (*kernel_ioctl)(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg);
+
+	void *priv;
+};
+
+
+extern int dvb_register_adapter (struct dvb_adapter **padap, const char *name, struct module *module);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+				struct dvb_device **pdvbdev,
+				const struct dvb_device *template,
+				void *priv,
+				int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
+			      unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy()  someday... */
+
+extern int dvb_usercopy(struct inode *inode, struct file *file,
+	                    unsigned int cmd, unsigned long arg,
+			    int (*func)(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg));
+
+#endif /* #ifndef _DVBDEV_H_ */