summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/dvb-core
diff options
context:
space:
mode:
authorHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
committerHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
commit4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7 (patch)
tree23af98b0e1da37f97558e63953011e23d1e360a5 /linux/drivers/media/dvb/dvb-core
downloadmediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.gz
mediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.bz2
the 2.5 tree
Diffstat (limited to 'linux/drivers/media/dvb/dvb-core')
-rw-r--r--linux/drivers/media/dvb/dvb-core/Config.help13
-rw-r--r--linux/drivers/media/dvb/dvb-core/Config.in7
-rw-r--r--linux/drivers/media/dvb/dvb-core/Makefile13
-rw-r--r--linux/drivers/media/dvb/dvb-core/compat.h24
-rw-r--r--linux/drivers/media/dvb/dvb-core/demux.h301
-rw-r--r--linux/drivers/media/dvb/dvb-core/dmxdev.c1113
-rw-r--r--linux/drivers/media/dvb/dvb-core/dmxdev.h128
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_demux.c1179
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_demux.h134
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_filter.c962
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_filter.h249
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_frontend.c931
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_frontend.h117
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_i2c.c301
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_i2c.h63
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ksyms.c49
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_net.c507
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_net.h63
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvbdev.c434
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvbdev.h91
20 files changed, 6679 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/dvb-core/Config.help b/linux/drivers/media/dvb/dvb-core/Config.help
new file mode 100644
index 000000000..ad3c23d74
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/Config.help
@@ -0,0 +1,13 @@
+CONFIG_DVB_CORE
+ DVB core utility functions for device handling, software fallbacks etc.
+
+ Say Y when you have a DVB card and want to use it. If unsure say N.
+
+
+CONFIG_DVB_DEVFS_ONLY
+ Drop support for old major/minor device scheme and support only devfs
+ systems. This saves some code.
+
+ If unsure say N.
+
+
diff --git a/linux/drivers/media/dvb/dvb-core/Config.in b/linux/drivers/media/dvb/dvb-core/Config.in
new file mode 100644
index 000000000..9c43c6498
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/Config.in
@@ -0,0 +1,7 @@
+
+dep_tristate ' DVB Core Support' CONFIG_DVB_CORE $CONFIG_DVB
+
+if [ "$CONFIG_DVB_CORE" != "n" ]; then
+ dep_bool ' devfs only' CONFIG_DVB_DEVFS_ONLY $CONFIG_DVB_CORE $CONFIG_DEVFS_FS
+fi
+
diff --git a/linux/drivers/media/dvb/dvb-core/Makefile b/linux/drivers/media/dvb/dvb-core/Makefile
new file mode 100644
index 000000000..a32a5f553
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+export-objs := dvb_ksyms.o
+
+dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
+ dvb_frontend.o dvb_i2c.o dvb_net.o dvb_ksyms.o
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
+
+include $(TOPDIR)/Rules.make
+
diff --git a/linux/drivers/media/dvb/dvb-core/compat.h b/linux/drivers/media/dvb/dvb-core/compat.h
new file mode 100644
index 000000000..25af8145b
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/compat.h
@@ -0,0 +1,24 @@
+#ifndef __CRAP_H
+#define __CRAP_H
+
+/**
+ * compatibility crap for old kernels. No guarantee for a working driver
+ * even when everything compiles.
+ */
+
+
+#include <linux/module.h>
+#include <linux/list.h>
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef list_for_each_safe
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+#endif
+
+#endif
+
diff --git a/linux/drivers/media/dvb/dvb-core/demux.h b/linux/drivers/media/dvb/dvb-core/demux.h
new file mode 100644
index 000000000..ce769ebf1
--- /dev/null
+++ b/linux/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
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/errno.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_success_t: Success codes for the Demux Callback API.
+ */
+
+typedef enum {
+ 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 */
+} dmx_success_t;
+
+/*--------------------------------------------------------------------------*/
+/* 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 */
+
+typedef enum
+{ /* 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
+} dmx_ts_pes_t;
+
+#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_s {
+ int is_filtering; /* Set to non-zero when filtering in progress */
+ struct dmx_demux_s *parent; /* Back-pointer */
+ void *priv; /* Pointer to private data of the API client */
+ int (*set) (struct dmx_ts_feed_s *feed,
+ uint16_t pid,
+ int type,
+ dmx_ts_pes_t pes_type,
+ size_t callback_length,
+ size_t circular_buffer_size,
+ int descramble,
+ struct timespec timeout);
+ int (*start_filtering) (struct dmx_ts_feed_s* feed);
+ int (*stop_filtering) (struct dmx_ts_feed_s* feed);
+};
+
+typedef struct dmx_ts_feed_s dmx_ts_feed_t;
+
+/*--------------------------------------------------------------------------*/
+/* Section reception */
+/*--------------------------------------------------------------------------*/
+
+typedef struct {
+ __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_s* parent; /* Back-pointer */
+ void* priv; /* Pointer to private data of the API client */
+} dmx_section_filter_t;
+
+struct dmx_section_feed_s {
+ int is_filtering; /* Set to non-zero when filtering in progress */
+ struct dmx_demux_s* parent; /* Back-pointer */
+ void* priv; /* Pointer to private data of the API client */
+ int (*set) (struct dmx_section_feed_s* feed,
+ __u16 pid,
+ size_t circular_buffer_size,
+ int descramble,
+ int check_crc);
+ int (*allocate_filter) (struct dmx_section_feed_s* feed,
+ dmx_section_filter_t** filter);
+ int (*release_filter) (struct dmx_section_feed_s* feed,
+ dmx_section_filter_t* filter);
+ int (*start_filtering) (struct dmx_section_feed_s* feed);
+ int (*stop_filtering) (struct dmx_section_feed_s* feed);
+};
+typedef struct dmx_section_feed_s dmx_section_feed_t;
+
+/*--------------------------------------------------------------------------*/
+/* Callback functions */
+/*--------------------------------------------------------------------------*/
+
+typedef int (*dmx_ts_cb) ( __u8 * buffer1,
+ size_t buffer1_length,
+ __u8 * buffer2,
+ size_t buffer2_length,
+ dmx_ts_feed_t* source,
+ dmx_success_t success);
+
+typedef int (*dmx_section_cb) ( __u8 * buffer1,
+ size_t buffer1_len,
+ __u8 * buffer2,
+ size_t buffer2_len,
+ dmx_section_filter_t * source,
+ dmx_success_t success);
+
+/*--------------------------------------------------------------------------*/
+/* DVB Front-End */
+/*--------------------------------------------------------------------------*/
+
+typedef enum {
+ 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
+} dmx_frontend_source_t;
+
+typedef struct {
+ /* The following char* fields point to NULL terminated strings */
+ char* id; /* Unique front-end identifier */
+ char* vendor; /* Name of the front-end vendor */
+ char* model; /* Name of the front-end model */
+ 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 */
+ dmx_frontend_source_t source;
+} dmx_frontend_t;
+
+/*--------------------------------------------------------------------------*/
+/* MPEG-2 TS Demux */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Flags OR'ed in the capabilites field of struct dmx_demux_s.
+ */
+
+#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 * dmx_frontend_t
+ *.
+*/
+
+#define DMX_FE_ENTRY(list) list_entry(list, dmx_frontend_t, connectivity_list)
+
+struct dmx_demux_s {
+ /* The following char* fields point to NULL terminated strings */
+ char* id; /* Unique demux identifier */
+ char* vendor; /* Name of the demux vendor */
+ char* model; /* Name of the demux model */
+ __u32 capabilities; /* Bitfield of capability flags */
+ dmx_frontend_t* 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_s* demux);
+ int (*close) (struct dmx_demux_s* demux);
+ int (*write) (struct dmx_demux_s* demux, const char* buf, size_t count);
+ int (*allocate_ts_feed) (struct dmx_demux_s* demux,
+ dmx_ts_feed_t** feed,
+ dmx_ts_cb callback);
+ int (*release_ts_feed) (struct dmx_demux_s* demux,
+ dmx_ts_feed_t* feed);
+ int (*allocate_section_feed) (struct dmx_demux_s* demux,
+ dmx_section_feed_t** feed,
+ dmx_section_cb callback);
+ int (*release_section_feed) (struct dmx_demux_s* demux,
+ dmx_section_feed_t* feed);
+ int (*descramble_mac_address) (struct dmx_demux_s* demux,
+ __u8* buffer1,
+ size_t buffer1_length,
+ __u8* buffer2,
+ size_t buffer2_length,
+ __u16 pid);
+ int (*descramble_section_payload) (struct dmx_demux_s* demux,
+ __u8* buffer1,
+ size_t buffer1_length,
+ __u8* buffer2, size_t buffer2_length,
+ __u16 pid);
+ int (*add_frontend) (struct dmx_demux_s* demux,
+ dmx_frontend_t* frontend);
+ int (*remove_frontend) (struct dmx_demux_s* demux,
+ dmx_frontend_t* frontend);
+ struct list_head* (*get_frontends) (struct dmx_demux_s* demux);
+ int (*connect_frontend) (struct dmx_demux_s* demux,
+ dmx_frontend_t* frontend);
+ int (*disconnect_frontend) (struct dmx_demux_s* demux);
+
+ int (*get_pes_pids) (struct dmx_demux_s* demux, __u16 *pids);
+};
+typedef struct dmx_demux_s dmx_demux_t;
+
+/*--------------------------------------------------------------------------*/
+/* Demux directory */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_DIR_ENTRY(): Casts elements in the list of registered
+ * demuxes from the generic type struct list_head* to the type dmx_demux_t
+ *.
+ */
+
+#define DMX_DIR_ENTRY(list) list_entry(list, dmx_demux_t, reg_list)
+
+int dmx_register_demux (dmx_demux_t* demux);
+int dmx_unregister_demux (dmx_demux_t* demux);
+struct list_head* dmx_get_demuxes (void);
+
+#endif /* #ifndef __DEMUX_H */
+
diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c
new file mode 100644
index 000000000..d388a1b62
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c
@@ -0,0 +1,1113 @@
+/*
+ * 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/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+
+#include "dmxdev.h"
+
+
+//MODULE_DESCRIPTION("");
+//MODULE_AUTHOR("Ralph Metzler, Marcus Metzler");
+//#ifdef MODULE_LICENSE
+//MODULE_LICENSE("GPL");
+//#endif
+MODULE_PARM(debug,"i");
+static int debug = 0;
+
+#define dprintk if (debug) printk
+
+inline dmxdev_filter_t *
+DmxDevFile2Filter(struct file *file)
+{
+ return (dmxdev_filter_t *) file->private_data;
+}
+
+inline dmxdev_dvr_t *
+DmxDevFile2DVR(dmxdev_t *dmxdev, struct file *file)
+{
+ return (dmxdev_dvr_t *) file->private_data;
+}
+
+static inline void
+DmxDevBufferInit(dmxdev_buffer_t *buffer)
+{
+ buffer->data=0;
+ buffer->size=8192;
+ buffer->pread=0;
+ buffer->pwrite=0;
+ buffer->error=0;
+ init_waitqueue_head(&buffer->queue);
+}
+
+static inline int
+DmxDevBufferWrite(dmxdev_buffer_t *buf, uint8_t *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
+DmxDevBufferRead(dmxdev_buffer_t *src, int non_blocking,
+ char *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->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->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 dmx_frontend_t *
+get_fe(dmx_demux_t *demux, int type)
+{
+ struct list_head *head, *pos;
+
+ head=demux->get_frontends(demux);
+ if (!head)
+ return 0;
+ list_for_each(pos, head)
+ if (DMX_FE_ENTRY(pos)->source==type)
+ return DMX_FE_ENTRY(pos);
+
+ return 0;
+}
+
+static inline void
+DmxDevDVRStateSet(dmxdev_dvr_t *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)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
+ dmx_frontend_t *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) {
+ DmxDevBufferInit(&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)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) 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=0;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+ }
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+static ssize_t
+dvb_dvr_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) 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 *buf, size_t count, loff_t *ppos)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
+ int ret;
+
+ //down(&dmxdev->mutex);
+ ret= DmxDevBufferRead(&dmxdev->dvr_buffer,
+ file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+ //up(&dmxdev->mutex);
+ return ret;
+}
+
+static inline void
+DmxDevFilterStateSet(dmxdev_filter_t *dmxdevfilter, int state)
+{
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state=state;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int
+DmxDevSetBufferSize(dmxdev_filter_t *dmxdevfilter, unsigned long size)
+{
+ dmxdev_buffer_t *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=0;
+ buf->size=size;
+ buf->pwrite=buf->pread=0;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ if (mem)
+ 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
+DmxDevFilterTimeout(unsigned long data)
+{
+ dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *)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
+DmxDevFilterTimer(dmxdev_filter_t *dmxdevfilter)
+{
+ struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
+
+ del_timer(&dmxdevfilter->timer);
+ if (para->timeout) {
+ dmxdevfilter->timer.function=DmxDevFilterTimeout;
+ dmxdevfilter->timer.data=(unsigned long) dmxdevfilter;
+ dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000;
+ add_timer(&dmxdevfilter->timer);
+ }
+}
+
+static int
+DmxDevSectionCallback(u8 *buffer1, size_t buffer1_len,
+ u8 *buffer2, size_t buffer2_len,
+ dmx_section_filter_t *filter,
+ dmx_success_t success)
+{
+ dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) 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=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer1, buffer1_len);
+ if (ret==buffer1_len) {
+ ret=DmxDevBufferWrite(&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
+DmxDevTSCallback(u8 *buffer1, size_t buffer1_len,
+ u8 *buffer2, size_t buffer2_len,
+ dmx_ts_feed_t *feed,
+ dmx_success_t success)
+{
+ dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) feed->priv;
+ dmxdev_buffer_t *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=DmxDevBufferWrite(buffer, buffer1, buffer1_len);
+ if (ret==buffer1_len)
+ ret=DmxDevBufferWrite(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
+DmxDevFeedStop(dmxdev_filter_t *dmxdevfilter)
+{
+ DmxDevFilterStateSet(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
+DmxDevFeedStart(dmxdev_filter_t *dmxdevfilter)
+{
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO);
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ dmxdevfilter->feed.sec->start_filtering(dmxdevfilter->feed.sec);
+ break;
+ case DMXDEV_TYPE_PES:
+ dmxdevfilter->feed.ts->start_filtering(dmxdevfilter->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
+DmxDevFeedRestart(dmxdev_filter_t *dmxdevfilter)
+{
+ int i;
+ dmxdev_t *dmxdev=dmxdevfilter->dev;
+ uint16_t pid=dmxdevfilter->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) {
+ DmxDevFeedStart(&dmxdev->filter[i]);
+ return 0;
+ }
+
+ dmxdevfilter->dev->demux->
+ release_section_feed(dmxdev->demux,
+ dmxdevfilter->feed.sec);
+
+ return 0;
+}
+
+static int
+DmxDevFilterStop(dmxdev_filter_t *dmxdevfilter)
+{
+ if (dmxdevfilter->state<DMXDEV_STATE_GO)
+ return 0;
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ if (!dmxdevfilter->feed.sec)
+ break;
+ DmxDevFeedStop(dmxdevfilter);
+ if (dmxdevfilter->filter.sec)
+ dmxdevfilter->feed.sec->
+ release_filter(dmxdevfilter->feed.sec,
+ dmxdevfilter->filter.sec);
+ DmxDevFeedRestart(dmxdevfilter);
+ dmxdevfilter->feed.sec=0;
+ break;
+ case DMXDEV_TYPE_PES:
+ if (!dmxdevfilter->feed.ts)
+ break;
+ DmxDevFeedStop(dmxdevfilter);
+ dmxdevfilter->dev->demux->
+ release_ts_feed(dmxdevfilter->dev->demux,
+ dmxdevfilter->feed.ts);
+ dmxdevfilter->feed.ts=0;
+ break;
+ default:
+ if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED)
+ return 0;
+ return -EINVAL;
+ }
+ dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0;
+ return 0;
+}
+
+static inline int
+DmxDevFilterReset(dmxdev_filter_t *dmxdevfilter)
+{
+ if (dmxdevfilter->state<DMXDEV_STATE_SET)
+ return 0;
+
+ dmxdevfilter->type=DMXDEV_TYPE_NONE;
+ dmxdevfilter->pid=0xffff;
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ return 0;
+}
+
+static int
+DmxDevFilterStart(dmxdev_filter_t *dmxdevfilter)
+{
+ dmxdev_t *dmxdev=dmxdevfilter->dev;
+ void *mem;
+ int ret, i;
+
+ if (dmxdevfilter->state<DMXDEV_STATE_SET)
+ return -EINVAL;
+ if (dmxdevfilter->state>=DMXDEV_STATE_GO)
+ DmxDevFilterStop(dmxdevfilter);
+
+ mem=dmxdevfilter->buffer.data;
+ if (!mem) {
+ mem=vmalloc(dmxdevfilter->buffer.size);
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->buffer.data=mem;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ if (!dmxdevfilter->buffer.data)
+ return -ENOMEM;
+ }
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ {
+ struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
+ dmx_section_filter_t **secfilter=&dmxdevfilter->filter.sec;
+ dmx_section_feed_t **secfeed=&dmxdevfilter->feed.sec;
+
+ *secfilter=0;
+ *secfeed=0;
+
+ /* 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) {
+ if (dmxdev->filter[i].type!=DMXDEV_TYPE_SEC)
+ return -EBUSY;
+ *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,
+ DmxDevSectionCallback);
+ 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__);
+ DmxDevFeedRestart(dmxdevfilter);
+ return ret;
+ }
+ }
+ else
+ DmxDevFeedStop(dmxdevfilter);
+
+ ret=(*secfeed)->allocate_filter(*secfeed, secfilter);
+ if (ret<0) {
+ DmxDevFeedRestart(dmxdevfilter);
+ dmxdevfilter->feed.sec->
+ start_filtering(*secfeed);
+ dprintk ("could not get filter\n");
+ return ret;
+ }
+
+ (*secfilter)->priv=(void *) dmxdevfilter;
+
+ 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;
+
+ dmxdevfilter->todo=0;
+ dmxdevfilter->feed.sec->
+ start_filtering(dmxdevfilter->feed.sec);
+ DmxDevFilterTimer(dmxdevfilter);
+ break;
+ }
+
+ case DMXDEV_TYPE_PES:
+ {
+ struct timespec timeout = {0 };
+ struct dmx_pes_filter_params *para=&dmxdevfilter->params.pes;
+ dmx_output_t otype;
+ int ret;
+ int ts_type;
+ dmx_ts_pes_t ts_pes;
+ dmx_ts_feed_t **tsfeed=&dmxdevfilter->feed.ts;
+
+ dmxdevfilter->feed.ts=0;
+ otype=para->output;
+
+ ts_pes=(dmx_ts_pes_t) 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,
+ DmxDevTSCallback);
+ if (ret<0)
+ return ret;
+
+ (*tsfeed)->priv=(void *) dmxdevfilter;
+ 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;
+ }
+ dmxdevfilter->feed.ts->
+ start_filtering(dmxdevfilter->feed.ts);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO);
+ return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
+ int i;
+ dmxdev_filter_t *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];
+ dmxdevfilter->dvbdev=dmxdev->dvbdev;
+ file->private_data=dmxdevfilter;
+
+ DmxDevBufferInit(&dmxdevfilter->buffer);
+ dmxdevfilter->type=DMXDEV_TYPE_NONE;
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ dmxdevfilter->feed.ts=0;
+ init_timer(&dmxdevfilter->timer);
+
+ up(&dmxdev->mutex);
+ return 0;
+}
+
+int
+DmxDevFilterFree(dmxdev_t *dmxdev, dmxdev_filter_t *dmxdevfilter)
+{
+ if (down_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ DmxDevFilterStop(dmxdevfilter);
+ DmxDevFilterReset(dmxdevfilter);
+
+ if (dmxdevfilter->buffer.data) {
+ void *mem=dmxdevfilter->buffer.data;
+
+ spin_lock_irq(&dmxdev->lock);
+ dmxdevfilter->buffer.data=0;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_FREE);
+ wake_up(&dmxdevfilter->buffer.queue);
+ 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
+DmxDevFilterSet(dmxdev_t *dmxdev,
+ dmxdev_filter_t *dmxdevfilter,
+ struct dmx_sct_filter_params *params)
+{
+ dprintk ("function : %s\n", __FUNCTION__);
+
+ DmxDevFilterStop(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);
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags&DMX_IMMEDIATE_START)
+ return DmxDevFilterStart(dmxdevfilter);
+
+ return 0;
+}
+
+static int
+DmxDevPesFilterSet(dmxdev_t *dmxdev,
+ dmxdev_filter_t *dmxdevfilter,
+ struct dmx_pes_filter_params *params)
+{
+ DmxDevFilterStop(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));
+
+ DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags&DMX_IMMEDIATE_START)
+ return DmxDevFilterStart(dmxdevfilter);
+
+ return 0;
+}
+
+static ssize_t
+DmxDevReadSec(dmxdev_filter_t *dfil, struct file *file,
+ char *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=DmxDevBufferRead(&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=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+ if (result<0)
+ return result;
+ dfil->todo-=result;
+ return (result+done);
+}
+
+
+ssize_t
+dvb_demux_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
+ //dmxdev_t *dmxdev=dmxdevfilter->dev;
+ int ret=0;
+
+ // semaphore should not be necessary (I hope ...)
+ //down(&dmxdev->mutex);
+ if (dmxdevfilter->type==DMXDEV_TYPE_SEC)
+ ret=DmxDevReadSec(dmxdevfilter, file, buf, count, ppos);
+ else
+ ret=DmxDevBufferRead(&dmxdevfilter->buffer,
+ file->f_flags&O_NONBLOCK,
+ buf, count, ppos);
+ //up(&dmxdev->mutex);
+ return ret;
+}
+
+
+static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
+ dmxdev_t *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 (dmxdevfilter->state<DMXDEV_STATE_SET)
+ ret=-EINVAL;
+ else
+ ret=DmxDevFilterStart(dmxdevfilter);
+ break;
+
+ case DMX_STOP:
+ ret=DmxDevFilterStop(dmxdevfilter);
+ break;
+
+ case DMX_SET_FILTER:
+ ret=DmxDevFilterSet(dmxdev, dmxdevfilter,
+ (struct dmx_sct_filter_params *)parg);
+ break;
+
+ case DMX_SET_PES_FILTER:
+ ret=DmxDevPesFilterSet(dmxdev, dmxdevfilter,
+ (struct dmx_pes_filter_params *)parg);
+ break;
+
+ case DMX_SET_BUFFER_SIZE:
+ ret=DmxDevSetBufferSize(dmxdevfilter, arg);
+ 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, (uint16_t *)parg);
+ 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 generic_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+
+static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
+{
+ dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
+
+ if (!dmxdevfilter)
+ return -EINVAL;
+
+ if (dmxdevfilter->state==DMXDEV_STATE_FREE)
+ return 0;
+
+ if (dmxdevfilter->buffer.error)
+ return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ if (dmxdevfilter->state!=DMXDEV_STATE_GO)
+ return 0;
+
+ poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+ if (dmxdevfilter->state==DMXDEV_STATE_FREE)
+ return 0;
+
+ if (dmxdevfilter->buffer.error)
+ return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ return 0;
+}
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+ dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
+ dmxdev_t *dmxdev=dmxdevfilter->dev;
+
+ return DmxDevFilterFree(dmxdev, dmxdevfilter);
+}
+
+static struct file_operations dvb_demux_fops = {
+ owner: THIS_MODULE,
+ read: dvb_demux_read,
+ write: 0,
+ ioctl: dvb_demux_ioctl,
+ open: dvb_demux_open,
+ release: dvb_demux_release,
+ poll: dvb_demux_poll,
+};
+
+static dvb_device_t dvbdev_demux = {
+ priv: 0,
+ 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)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) 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 generic_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+
+static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
+
+ dprintk ("function : %s\n", __FUNCTION__);
+
+ if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+ if (dmxdev->dvr_buffer.error)
+ return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+ if (dmxdev->dvr_buffer.error)
+ return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ return 0;
+ } else
+ return (POLLOUT | POLLWRNORM | POLLPRI);
+}
+
+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 dvb_device_t dvbdev_dvr = {
+ priv: 0,
+ users: 1,
+ writers: 1,
+ fops: &dvb_dvr_fops
+};
+
+int
+DmxDevInit(dmxdev_t *dmxdev, dvb_adapter_t *dvb_adapter)
+{
+ int i;
+
+ if (dmxdev->demux->open(dmxdev->demux)<0)
+ return -EUSERS;
+
+ dmxdev->filter=vmalloc(dmxdev->filternum*sizeof(dmxdev_filter_t));
+ if (!dmxdev->filter)
+ return -ENOMEM;
+
+ dmxdev->dvr=vmalloc(dmxdev->filternum*sizeof(dmxdev_dvr_t));
+ if (!dmxdev->dvr) {
+ vfree(dmxdev->filter);
+ dmxdev->filter=0;
+ 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=0;
+ DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+ dmxdev->dvr[i].dev=dmxdev;
+ dmxdev->dvr[i].buffer.data=0;
+ DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+ DmxDevDVRStateSet(&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);
+
+ DmxDevBufferInit(&dmxdev->dvr_buffer);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+void
+DmxDevRelease(dmxdev_t *dmxdev)
+{
+ dvb_unregister_device(dmxdev->dvbdev);
+ dvb_unregister_device(dmxdev->dvr_dvbdev);
+ if (dmxdev->filter) {
+ vfree(dmxdev->filter);
+ dmxdev->filter=0;
+ }
+ if (dmxdev->dvr) {
+ vfree(dmxdev->dvr);
+ dmxdev->dvr=0;
+ }
+ dmxdev->demux->close(dmxdev->demux);
+ MOD_DEC_USE_COUNT;
+}
+
+
diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.h b/linux/drivers/media/dvb/dvb-core/dmxdev.h
new file mode 100644
index 000000000..c5fa89d51
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dmxdev.h
@@ -0,0 +1,128 @@
+/*
+ * dmxdev.h
+ *
+ * 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.
+ *
+ */
+
+#ifndef _DMXDEV_H_
+#define _DMXDEV_H_
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#include <linux/dvb/dmx.h>
+
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+
+typedef enum {
+ DMXDEV_TYPE_NONE,
+ DMXDEV_TYPE_SEC,
+ DMXDEV_TYPE_PES,
+} dmxdev_type_t;
+
+typedef enum {
+ DMXDEV_STATE_FREE,
+ DMXDEV_STATE_ALLOCATED,
+ DMXDEV_STATE_SET,
+ DMXDEV_STATE_GO,
+ DMXDEV_STATE_DONE,
+ DMXDEV_STATE_TIMEDOUT
+} dmxdev_state_t;
+
+typedef struct dmxdev_buffer_s {
+ uint8_t *data;
+ uint32_t size;
+ int32_t pread;
+ int32_t pwrite;
+ wait_queue_head_t queue;
+ int error;
+} dmxdev_buffer_t;
+
+
+typedef struct dmxdev_filter_s {
+ dvb_device_t *dvbdev;
+
+ union {
+ dmx_section_filter_t *sec;
+ } filter;
+
+ union {
+ dmx_ts_feed_t *ts;
+ dmx_section_feed_t *sec;
+ } feed;
+
+ union {
+ struct dmx_sct_filter_params sec;
+ struct dmx_pes_filter_params pes;
+ } params;
+
+ int type;
+ dmxdev_state_t state;
+ struct dmxdev_s *dev;
+ dmxdev_buffer_t buffer;
+
+ // only for sections
+ struct timer_list timer;
+ int todo;
+ uint8_t secheader[3];
+
+ u16 pid;
+} dmxdev_filter_t;
+
+
+typedef struct dmxdev_dvr_s {
+ int state;
+ struct dmxdev_s *dev;
+ dmxdev_buffer_t buffer;
+} dmxdev_dvr_t;
+
+
+typedef struct dmxdev_s {
+ dvb_device_t *dvbdev;
+ dvb_device_t *dvr_dvbdev;
+
+ dmxdev_filter_t *filter;
+ dmxdev_dvr_t *dvr;
+ dmx_demux_t *demux;
+
+ int filternum;
+ int capabilities;
+#define DMXDEV_CAP_DUPLEX 1
+ dmx_frontend_t *dvr_orig_fe;
+
+ dmxdev_buffer_t dvr_buffer;
+#define DVR_BUFFER_SIZE (10*188*1024)
+
+ struct semaphore mutex;
+ spinlock_t lock;
+} dmxdev_t;
+
+
+int DmxDevInit(dmxdev_t *dmxdev, dvb_adapter_t *);
+void DmxDevRelease(dmxdev_t *dmxdev);
+
+#endif /* _DMXDEV_H_ */
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
new file mode 100644
index 000000000..3a3e3834a
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -0,0 +1,1179 @@
+/*
+ * 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/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include "compat.h"
+#include "dvb_demux.h"
+
+#define NOBUFS
+
+LIST_HEAD(dmx_muxs);
+
+int dmx_register_demux(dmx_demux_t *demux)
+{
+ struct list_head *pos, *head=&dmx_muxs;
+
+ if (!(demux->id && demux->vendor && demux->model))
+ return -EINVAL;
+ list_for_each(pos, head)
+ {
+ if (!strcmp(DMX_DIR_ENTRY(pos)->id, demux->id))
+ return -EEXIST;
+ }
+ demux->users=0;
+ list_add(&(demux->reg_list), head);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+int dmx_unregister_demux(dmx_demux_t* 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);
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+
+struct list_head *dmx_get_demuxes(void)
+{
+ if (list_empty(&dmx_muxs))
+ return NULL;
+
+ return &dmx_muxs;
+}
+
+/******************************************************************************
+ * 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 int
+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_crc_table[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4};
+
+u32 dvb_crc32(u8 *data, int len)
+{
+ int i;
+ u32 crc = 0xffffffff;
+
+ for (i=0; i<len; i++)
+ crc = (crc << 8) ^ dvb_crc_table[((crc >> 24) ^ *data++) & 0xff];
+ return crc;
+}
+
+void dvb_set_crc32(u8 *data, int length)
+{
+ u32 crc;
+
+ crc=dvb_crc32(data,length);
+ data[length] = (crc>>24)&0xff;
+ data[length+1] = (crc>>16)&0xff;
+ data[length+2] = (crc>>8)&0xff;
+ data[length+3] = (crc)&0xff;
+}
+
+
+/******************************************************************************
+ * Software filter functions
+ ******************************************************************************/
+
+static inline int
+DvbDmxSWFilterPayload(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf)
+{
+ int p, count;
+ //int ccok;
+ //u8 cc;
+
+ if (!(count=payload(buf)))
+ 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 ?
+ dvbdmxfeed->peslen=0xfffa;
+ dvbdmxfeed->peslen+=count;
+
+ return dvbdmxfeed->cb.ts((u8 *)&buf[p], count, 0, 0,
+ &dvbdmxfeed->feed.ts, DMX_OK);
+}
+
+
+static int
+DvbDmxSWFilterSectionFilter(dvb_demux_feed_t *dvbdmxfeed,
+ dvb_demux_filter_t *f)
+{
+ dmx_section_filter_t *filter=&f->filter;
+ int i;
+ u8 xor, neq=0;
+
+ for (i=0; i<DVB_DEMUX_MASK_MAX; i++) {
+ xor=filter->filter_value[i]^dvbdmxfeed->secbuf[i];
+ if (f->maskandmode[i]&xor)
+ return 0;
+ neq|=f->maskandnotmode[i]&xor;
+ }
+ if (f->doneq & !neq)
+ return 0;
+
+ return dvbdmxfeed->cb.sec(dvbdmxfeed->secbuf, dvbdmxfeed->seclen,
+ 0, 0, filter, DMX_OK);
+}
+
+static inline int
+DvbDmxSWFilterSectionFeed(dvb_demux_feed_t *dvbdmxfeed)
+{
+ u8 *buf=dvbdmxfeed->secbuf;
+ dvb_demux_filter_t *f;
+
+ if (dvbdmxfeed->secbufp!=dvbdmxfeed->seclen)
+ return -1;
+ if (!dvbdmxfeed->feed.sec.is_filtering)
+ return 0;
+ if (!(f=dvbdmxfeed->filter))
+ return 0;
+ do
+ if (DvbDmxSWFilterSectionFilter(dvbdmxfeed, f)<0)
+ return -1;
+ while ((f=f->next) && dvbdmxfeed->feed.sec.is_filtering);
+
+ dvbdmxfeed->secbufp=dvbdmxfeed->seclen=0;
+ memset(buf, 0, DVB_DEMUX_MASK_MAX);
+ return 0;
+}
+
+static inline int
+DvbDmxSWFilterSectionPacket(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf)
+{
+ int p, count;
+ int ccok, rest;
+ u8 cc;
+
+ if (!(count=payload(buf)))
+ return -1;
+ p=188-count;
+
+ cc=buf[3]&0x0f;
+ ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0;
+ dvbdmxfeed->cc=cc;
+
+ if (buf[1]&0x40) { // PUSI set
+ // offset to start of first section is in buf[p]
+ if (p+buf[p]>187) // trash if it points beyond packet
+ return -1;
+ if (buf[p] && ccok) { // rest of previous section?
+ // did we have enough data in last packet to calc length?
+ int tmp=3-dvbdmxfeed->secbufp;
+ if (tmp>0 && tmp!=3) {
+ if (p+tmp>=187)
+ return -1;
+ memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp,
+ buf+p+1, tmp);
+ dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf);
+ if (dvbdmxfeed->seclen>4096)
+ return -1;
+ }
+ rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp;
+ if (rest==buf[p] && dvbdmxfeed->seclen) {
+ memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp,
+ buf+p+1, buf[p]);
+ dvbdmxfeed->secbufp+=buf[p];
+ DvbDmxSWFilterSectionFeed(dvbdmxfeed);
+ }
+ }
+ p+=buf[p]+1; // skip rest of last section
+ count=188-p;
+ while (count>0) {
+ if ((count>2) && // enough data to determine sec length?
+ ((dvbdmxfeed->seclen=section_length(buf+p))<=count)) {
+ if (dvbdmxfeed->seclen>4096)
+ return -1;
+ memcpy(dvbdmxfeed->secbuf, buf+p,
+ dvbdmxfeed->seclen);
+ dvbdmxfeed->secbufp=dvbdmxfeed->seclen;
+ p+=dvbdmxfeed->seclen;
+ count=188-p;
+ DvbDmxSWFilterSectionFeed(dvbdmxfeed);
+
+ // filling bytes until packet end?
+ if (count && buf[p]==0xff)
+ count=0;
+ } else { // section continues to following TS packet
+ memcpy(dvbdmxfeed->secbuf, buf+p, count);
+ dvbdmxfeed->secbufp+=count;
+ count=0;
+ }
+ }
+ return 0;
+ }
+
+ // section continued below
+ if (!ccok)
+ return -1;
+ if (!dvbdmxfeed->secbufp) // any data in last ts packet?
+ return -1;
+ // did we have enough data in last packet to calc section length?
+ if (dvbdmxfeed->secbufp<3) {
+ int tmp=3-dvbdmxfeed->secbufp;
+
+ if (tmp>count)
+ return -1;
+ memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, tmp);
+ dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf);
+ if (dvbdmxfeed->seclen>4096)
+ return -1;
+ }
+ rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp;
+ if (rest<0)
+ return -1;
+ if (rest<=count) { // section completed in this TS packet
+ memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, rest);
+ dvbdmxfeed->secbufp+=rest;
+ DvbDmxSWFilterSectionFeed(dvbdmxfeed);
+ } else { // section continues in following ts packet
+ memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, count);
+ dvbdmxfeed->secbufp+=count;
+ }
+ return 0;
+}
+
+static inline void
+DvbDmxSWFilterPacketType(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf)
+{
+ switch(dvbdmxfeed->type) {
+ case DMX_TYPE_TS:
+ if (!dvbdmxfeed->feed.ts.is_filtering)
+ break;
+ if (dvbdmxfeed->ts_type & TS_PACKET) {
+ if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
+ DvbDmxSWFilterPayload(dvbdmxfeed, buf);
+ else
+ dvbdmxfeed->cb.ts((u8 *)buf, 188, 0, 0,
+ &dvbdmxfeed->feed.ts, DMX_OK);
+ }
+ if (dvbdmxfeed->ts_type & TS_DECODER)
+ if (dvbdmxfeed->demux->write_to_decoder)
+ dvbdmxfeed->demux->
+ write_to_decoder(dvbdmxfeed, (u8 *)buf, 188);
+ break;
+
+ case DMX_TYPE_SEC:
+ if (!dvbdmxfeed->feed.sec.is_filtering)
+ break;
+ if (DvbDmxSWFilterSectionPacket(dvbdmxfeed, buf)<0)
+ dvbdmxfeed->seclen=dvbdmxfeed->secbufp=0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void inline
+DvbDmxSWFilterPacket(dvb_demux_t *dvbdmx, const u8 *buf)
+{
+ dvb_demux_feed_t *dvbdmxfeed;
+
+ if (!(dvbdmxfeed=dvbdmx->pid2feed[ts_pid(buf)]))
+ return;
+ DvbDmxSWFilterPacketType(dvbdmxfeed, buf);
+}
+
+void
+DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count)
+{
+ dvb_demux_feed_t *dvbdmxfeed;
+
+ spin_lock(&dvbdmx->lock);
+ if ((dvbdmxfeed=dvbdmx->pid2feed[0x2000]))
+ dvbdmxfeed->cb.ts((u8 *)buf, count*188, 0, 0,
+ &dvbdmxfeed->feed.ts, DMX_OK);
+ while (count) {
+ DvbDmxSWFilterPacket(dvbdmx, buf);
+ count--;
+ buf+=188;
+ }
+ spin_unlock(&dvbdmx->lock);
+}
+
+static inline void
+DvbDmxSWFilter(dvb_demux_t *dvbdmx, const u8 *buf, size_t count)
+{
+ int p=0,i, j;
+
+ if ((i=dvbdmx->tsbufp)) {
+ if (count<(j=188-i)) {
+ memcpy(&dvbdmx->tsbuf[i], buf, count);
+ dvbdmx->tsbufp+=count;
+ return;
+ }
+ memcpy(&dvbdmx->tsbuf[i], buf, j);
+ DvbDmxSWFilterPacket(dvbdmx, dvbdmx->tsbuf);
+ dvbdmx->tsbufp=0;
+ p+=j;
+ }
+
+ while (p<count) {
+ if (buf[p]==0x47) {
+ if (count-p>=188) {
+ DvbDmxSWFilterPacket(dvbdmx, buf+p);
+ p+=188;
+ } else {
+ i=count-p;
+ memcpy(dvbdmx->tsbuf, buf+p, i);
+ dvbdmx->tsbufp=i;
+ return;
+ }
+ } else
+ p++;
+ }
+}
+
+
+/******************************************************************************
+ ******************************************************************************
+ * DVB DEMUX API LEVEL FUNCTIONS
+ ******************************************************************************
+ ******************************************************************************/
+
+static dvb_demux_filter_t *
+DvbDmxFilterAlloc(dvb_demux_t *dvbdmx)
+{
+ int i;
+
+ for (i=0; i<dvbdmx->filternum; i++)
+ if (dvbdmx->filter[i].state==DMX_STATE_FREE)
+ break;
+ if (i==dvbdmx->filternum)
+ return 0;
+ dvbdmx->filter[i].state=DMX_STATE_ALLOCATED;
+ return &dvbdmx->filter[i];
+}
+
+static dvb_demux_feed_t *
+DvbDmxFeedAlloc(dvb_demux_t *dvbdmx)
+{
+ int i;
+
+ for (i=0; i<dvbdmx->feednum; i++)
+ if (dvbdmx->feed[i].state==DMX_STATE_FREE)
+ break;
+ if (i==dvbdmx->feednum)
+ return 0;
+ dvbdmx->feed[i].state=DMX_STATE_ALLOCATED;
+ return &dvbdmx->feed[i];
+}
+
+
+/******************************************************************************
+ * dmx_ts_feed API calls
+ ******************************************************************************/
+
+static int
+dmx_pid_set(u16 pid, dvb_demux_feed_t *dvbdmxfeed)
+{
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+ dvb_demux_feed_t **pid2feed=dvbdmx->pid2feed;
+
+ if (pid>DMX_MAX_PID)
+ return -EINVAL;
+ if (dvbdmxfeed->pid!=0xffff) {
+ if (dvbdmxfeed->pid<=DMX_MAX_PID)
+ pid2feed[dvbdmxfeed->pid]=0;
+ dvbdmxfeed->pid=0xffff;
+ }
+ if (pid2feed[pid]) {
+ return -EBUSY;
+ }
+ pid2feed[pid]=dvbdmxfeed;
+ dvbdmxfeed->pid=pid;
+ return 0;
+}
+
+
+static int
+dmx_ts_feed_set(struct dmx_ts_feed_s* feed,
+ u16 pid,
+ int ts_type,
+ dmx_ts_pes_t pes_type,
+ size_t callback_length,
+ size_t circular_buffer_size,
+ int descramble,
+ struct timespec timeout
+ )
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+ int ret;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (ts_type & TS_DECODER) {
+ if (pes_type >= DMX_TS_PES_OTHER) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+ if (dvbdmx->pesfilter[pes_type] &&
+ (dvbdmx->pesfilter[pes_type]!=dvbdmxfeed)) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+ if ((pes_type != DMX_TS_PES_PCR0) &&
+ (pes_type != DMX_TS_PES_PCR1) &&
+ (pes_type != DMX_TS_PES_PCR2) &&
+ (pes_type != DMX_TS_PES_PCR3)) {
+ if ((ret=dmx_pid_set(pid, dvbdmxfeed))<0) {
+ up(&dvbdmx->mutex);
+ return ret;
+ } else
+ dvbdmxfeed->pid=pid;
+ }
+ dvbdmx->pesfilter[pes_type]=dvbdmxfeed;
+ dvbdmx->pids[pes_type]=dvbdmxfeed->pid;
+ } else
+ if ((ret=dmx_pid_set(pid, dvbdmxfeed))<0) {
+ up(&dvbdmx->mutex);
+ return ret;
+ }
+
+ dvbdmxfeed->buffer_size=circular_buffer_size;
+ dvbdmxfeed->descramble=descramble;
+ dvbdmxfeed->timeout=timeout;
+ dvbdmxfeed->cb_length=callback_length;
+ dvbdmxfeed->ts_type=ts_type;
+ dvbdmxfeed->pes_type=pes_type;
+
+ if (dvbdmxfeed->descramble) {
+ up(&dvbdmx->mutex);
+ return -ENOSYS;
+ }
+
+ if (dvbdmxfeed->buffer_size) {
+#ifdef NOBUFS
+ dvbdmxfeed->buffer=0;
+#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 int
+dmx_ts_feed_start_filtering(struct dmx_ts_feed_s* feed)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+ int ret;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state!=DMX_STATE_READY ||
+ dvbdmxfeed->type!=DMX_TYPE_TS) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+ if (!dvbdmx->start_feed) {
+ up(&dvbdmx->mutex);
+ return -1;
+ }
+ ret=dvbdmx->start_feed(dvbdmxfeed);
+ if (ret<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_ts_feed_stop_filtering(struct dmx_ts_feed_s* feed)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+ int ret;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state<DMX_STATE_GO) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+ if (!dvbdmx->stop_feed) {
+ up(&dvbdmx->mutex);
+ return -1;
+ }
+ ret=dvbdmx->stop_feed(dvbdmxfeed);
+ spin_lock_irq(&dvbdmx->lock);
+ feed->is_filtering=0;
+ dvbdmxfeed->state=DMX_STATE_ALLOCATED;
+
+ spin_unlock_irq(&dvbdmx->lock);
+ up(&dvbdmx->mutex);
+ return ret;
+}
+
+static int dvbdmx_allocate_ts_feed(dmx_demux_t *demux,
+ dmx_ts_feed_t **feed,
+ dmx_ts_cb callback)
+{
+ dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
+ dvb_demux_feed_t *dvbdmxfeed;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) {
+ up(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+ dvbdmxfeed->type=DMX_TYPE_TS;
+ dvbdmxfeed->cb.ts=callback;
+ dvbdmxfeed->demux=dvbdmx;
+ dvbdmxfeed->pid=0xffff;
+ dvbdmxfeed->peslen=0xfffa;
+ dvbdmxfeed->buffer=0;
+
+ (*feed)=&dvbdmxfeed->feed.ts;
+ (*feed)->is_filtering=0;
+ (*feed)->parent=demux;
+ (*feed)->priv=0;
+ (*feed)->set=dmx_ts_feed_set;
+ (*feed)->start_filtering=dmx_ts_feed_start_filtering;
+ (*feed)->stop_filtering=dmx_ts_feed_stop_filtering;
+
+
+ if (!(dvbdmxfeed->filter=DvbDmxFilterAlloc(dvbdmx))) {
+ dvbdmxfeed->state=DMX_STATE_FREE;
+ up(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ dvbdmxfeed->filter->type=DMX_TYPE_TS;
+ dvbdmxfeed->filter->feed=dvbdmxfeed;
+ dvbdmxfeed->filter->state=DMX_STATE_READY;
+
+ up(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dvbdmx_release_ts_feed(dmx_demux_t *demux, dmx_ts_feed_t *feed)
+{
+ dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state==DMX_STATE_FREE) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ if (dvbdmxfeed->buffer) {
+ vfree(dvbdmxfeed->buffer);
+ dvbdmxfeed->buffer=0;
+ }
+#endif
+ dvbdmxfeed->state=DMX_STATE_FREE;
+ dvbdmxfeed->filter->state=DMX_STATE_FREE;
+ if (dvbdmxfeed->pid<=DMX_MAX_PID) {
+ dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
+ dvbdmxfeed->pid=0xffff;
+ }
+
+ up(&dvbdmx->mutex);
+ return 0;
+}
+
+
+/******************************************************************************
+ * dmx_section_feed API calls
+ ******************************************************************************/
+
+static int
+dmx_section_feed_allocate_filter(struct dmx_section_feed_s* feed,
+ dmx_section_filter_t** filter)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdemux=dvbdmxfeed->demux;
+ dvb_demux_filter_t *dvbdmxfilter;
+
+ if (down_interruptible (&dvbdemux->mutex))
+ return -ERESTARTSYS;
+
+ dvbdmxfilter=DvbDmxFilterAlloc(dvbdemux);
+ if (!dvbdmxfilter) {
+ up(&dvbdemux->mutex);
+ return -ENOSPC;
+ }
+ spin_lock_irq(&dvbdemux->lock);
+ *filter=&dvbdmxfilter->filter;
+ (*filter)->parent=feed;
+ (*filter)->priv=0;
+ 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_s* feed,
+ u16 pid, size_t circular_buffer_size,
+ int descramble, int check_crc)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+
+ if (pid>0x1fff)
+ return -EINVAL;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->pid!=0xffff) {
+ dvbdmx->pid2feed[dvbdmxfeed->pid]=0;
+ dvbdmxfeed->pid=0xffff;
+ }
+ if (dvbdmx->pid2feed[pid]) {
+ up(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+ dvbdmx->pid2feed[pid]=dvbdmxfeed;
+ dvbdmxfeed->pid=pid;
+
+ dvbdmxfeed->buffer_size=circular_buffer_size;
+ dvbdmxfeed->descramble=descramble;
+ if (dvbdmxfeed->descramble) {
+ up(&dvbdmx->mutex);
+ return -ENOSYS;
+ }
+
+ dvbdmxfeed->check_crc=check_crc;
+#ifdef NOBUFS
+ dvbdmxfeed->buffer=0;
+#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(dvb_demux_feed_t *dvbdmxfeed)
+{
+ int i;
+ dmx_section_filter_t *sf;
+ dvb_demux_filter_t *f;
+ 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(dmx_section_feed_t *feed)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *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->secbufp=0;
+ dvbdmxfeed->seclen=0;
+
+ if (!dvbdmx->start_feed) {
+ up(&dvbdmx->mutex);
+ return -1;
+ }
+ prepare_secfilters(dvbdmxfeed);
+ ret=dvbdmx->start_feed(dvbdmxfeed);
+ if (ret<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_s* feed)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=dvbdmxfeed->demux;
+ int ret;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (!dvbdmx->stop_feed) {
+ up(&dvbdmx->mutex);
+ return -1;
+ }
+ 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(dmx_section_feed_t *feed,
+ dmx_section_filter_t* filter)
+{
+ dvb_demux_filter_t *dvbdmxfilter=(dvb_demux_filter_t *) filter, *f;
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *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(dmx_demux_t *demux,
+ dmx_section_feed_t **feed,
+ dmx_section_cb callback)
+{
+ dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
+ dvb_demux_feed_t *dvbdmxfeed;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) {
+ up(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+ dvbdmxfeed->type=DMX_TYPE_SEC;
+ dvbdmxfeed->cb.sec=callback;
+ dvbdmxfeed->demux=dvbdmx;
+ dvbdmxfeed->pid=0xffff;
+ dvbdmxfeed->secbufp=0;
+ dvbdmxfeed->filter=0;
+ dvbdmxfeed->buffer=0;
+
+ (*feed)=&dvbdmxfeed->feed.sec;
+ (*feed)->is_filtering=0;
+ (*feed)->parent=demux;
+ (*feed)->priv=0;
+ (*feed)->set=dmx_section_feed_set;
+ (*feed)->allocate_filter=dmx_section_feed_allocate_filter;
+ (*feed)->release_filter=dmx_section_feed_release_filter;
+ (*feed)->start_filtering=dmx_section_feed_start_filtering;
+ (*feed)->stop_filtering=dmx_section_feed_stop_filtering;
+
+ up(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dvbdmx_release_section_feed(dmx_demux_t *demux,
+ dmx_section_feed_t *feed)
+{
+ dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed;
+ dvb_demux_t *dvbdmx=(dvb_demux_t *) demux;
+
+ if (down_interruptible (&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (dvbdmxfeed->state==DMX_STATE_FREE) {
+ up(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ if (dvbdmxfeed->buffer) {
+ vfree(dvbdmxfeed->buffer);
+ dvbdmxfeed->buffer=0;
+ }
+#endif
+ dvbdmxfeed->state=DMX_STATE_FREE;
+ dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
+ if (dvbdmxfeed->pid!=0xffff)
+ dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0;
+ up(&dvbdmx->mutex);
+ return 0;
+}
+
+
+/******************************************************************************
+ * dvb_demux kernel data API calls
+ ******************************************************************************/
+
+static int dvbdmx_open(dmx_demux_t *demux)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ if (dvbdemux->users>=MAX_DVB_DEMUX_USERS)
+ return -EUSERS;
+ dvbdemux->users++;
+ return 0;
+}
+
+static int dvbdmx_close(struct dmx_demux_s *demux)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ if (dvbdemux->users==0)
+ return -ENODEV;
+ dvbdemux->users--;
+ //FIXME: release any unneeded resources if users==0
+ return 0;
+}
+
+static int dvbdmx_write(dmx_demux_t *demux, const char *buf, size_t count)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ if ((!demux->frontend) ||
+ (demux->frontend->source!=DMX_MEMORY_FE))
+ return -EINVAL;
+
+ if (down_interruptible (&dvbdemux->mutex))
+ return -ERESTARTSYS;
+
+ DvbDmxSWFilter(dvbdemux, buf, count);
+ up(&dvbdemux->mutex);
+ return count;
+}
+
+
+static int dvbdmx_add_frontend(dmx_demux_t *demux,
+ dmx_frontend_t *frontend)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+ struct list_head *pos, *head=&dvbdemux->frontend_list;
+
+ if (!(frontend->id && frontend->vendor && frontend->model))
+ return -EINVAL;
+ list_for_each(pos, head)
+ {
+ if (!strcmp(DMX_FE_ENTRY(pos)->id, frontend->id))
+ return -EEXIST;
+ }
+
+ list_add(&(frontend->connectivity_list), head);
+ return 0;
+}
+
+static int
+dvbdmx_remove_frontend(dmx_demux_t *demux,
+ dmx_frontend_t *frontend)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) 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(dmx_demux_t *demux)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ if (list_empty(&dvbdemux->frontend_list))
+ return NULL;
+ return &dvbdemux->frontend_list;
+}
+
+static int dvbdmx_connect_frontend(dmx_demux_t *demux,
+ dmx_frontend_t *frontend)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) 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(dmx_demux_t *demux)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ if (down_interruptible (&dvbdemux->mutex))
+ return -ERESTARTSYS;
+
+ demux->frontend=NULL;
+ up(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dvbdmx_get_pes_pids(dmx_demux_t *demux, u16 *pids)
+{
+ dvb_demux_t *dvbdemux=(dvb_demux_t *) demux;
+
+ memcpy(pids, dvbdemux->pids, 5*sizeof(u16));
+ return 0;
+}
+
+int
+DvbDmxInit(dvb_demux_t *dvbdemux)
+{
+ int i;
+ dmx_demux_t *dmx=&dvbdemux->dmx;
+
+ dvbdemux->users=0;
+ dvbdemux->filter=vmalloc(dvbdemux->filternum*sizeof(dvb_demux_filter_t));
+ if (!dvbdemux->filter)
+ return -ENOMEM;
+
+ dvbdemux->feed=vmalloc(dvbdemux->feednum*sizeof(dvb_demux_feed_t));
+ 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->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;
+ }
+ dvbdemux->playing=dvbdemux->recording=0;
+ memset(dvbdemux->pid2feed, 0, (DMX_MAX_PID+1)*sizeof(dvb_demux_feed_t *));
+ dvbdemux->tsbufp=0;
+
+ dmx->frontend=0;
+ dmx->reg_list.next=dmx->reg_list.prev=&dmx->reg_list;
+ dmx->priv=(void *) dvbdemux;
+ //dmx->users=0; // reset in dmx_register_demux()
+ 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 (dmx_register_demux(dmx)<0)
+ return -1;
+
+ return 0;
+}
+
+int
+DvbDmxRelease(dvb_demux_t *dvbdemux)
+{
+ dmx_demux_t *dmx=&dvbdemux->dmx;
+
+ dmx_unregister_demux(dmx);
+ if (dvbdemux->filter)
+ vfree(dvbdemux->filter);
+ if (dvbdemux->feed)
+ vfree(dvbdemux->feed);
+ return 0;
+}
+
+#if 0
+MODULE_DESCRIPTION("Software MPEG Demultiplexer");
+MODULE_AUTHOR("Ralph Metzler, Markus Metzler");
+MODULE_LICENSE("GPL");
+#endif
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.h b/linux/drivers/media/dvb/dvb-core/dvb_demux.h
new file mode 100644
index 000000000..7fb008c4b
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -0,0 +1,134 @@
+/*
+ * dvb_demux.h - DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Marcus Metzler <marcus@convergence.de>
+ * & Ralph Metzler <ralph@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.
+ *
+ */
+
+
+#ifndef _DVB_DEMUX_H_
+#define _DVB_DEMUX_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
+
+typedef struct dvb_demux_filter_s {
+ dmx_section_filter_t filter;
+ u8 maskandmode [DMX_MAX_FILTER_SIZE];
+ u8 maskandnotmode [DMX_MAX_FILTER_SIZE];
+ int doneq;
+
+ struct dvb_demux_filter_s *next;
+ struct dvb_demux_feed_s *feed;
+ int index;
+ int state;
+ int type;
+ int pesto;
+
+ u16 handle;
+ u16 hw_handle;
+ struct timer_list timer;
+ int ts_state;
+
+ //u16 pid; //to be removed
+} dvb_demux_filter_t;
+
+typedef struct dvb_demux_feed_s {
+ union {
+ dmx_ts_feed_t ts;
+ dmx_section_feed_t sec;
+ } feed;
+
+ union {
+ dmx_ts_cb ts;
+ dmx_section_cb sec;
+ } cb;
+
+ struct dvb_demux_s *demux;
+ int type;
+ int state;
+ u16 pid;
+ u8 *buffer;
+ int buffer_size;
+ int descramble;
+ int check_crc;
+
+ struct timespec timeout;
+ dvb_demux_filter_t *filter;
+ int cb_length;
+
+ int ts_type;
+ dmx_ts_pes_t pes_type;
+
+ u8 secbuf[4096];
+ int secbufp;
+ int seclen;
+ int cc;
+
+ u16 peslen;
+} dvb_demux_feed_t;
+
+typedef struct dvb_demux_s {
+ dmx_demux_t dmx;
+ void *priv;
+ int filternum;
+ int feednum;
+ int (*start_feed)(dvb_demux_feed_t *);
+ int (*stop_feed)(dvb_demux_feed_t *);
+ int (*write_to_decoder)(dvb_demux_feed_t *, u8 *, size_t);
+
+
+ int users;
+#define MAX_DVB_DEMUX_USERS 10
+ dvb_demux_filter_t *filter;
+ dvb_demux_feed_t *feed;
+
+ struct list_head frontend_list;
+
+ dvb_demux_feed_t *pesfilter[DMX_TS_PES_OTHER];
+ u16 pids[DMX_TS_PES_OTHER];
+ int playing;
+ int recording;
+
+#define DMX_MAX_PID 0x2000
+ dvb_demux_feed_t *pid2feed[DMX_MAX_PID+1];
+ u8 tsbuf[188];
+ int tsbufp;
+
+ struct semaphore mutex;
+ spinlock_t lock;
+} dvb_demux_t;
+
+
+int DvbDmxInit(dvb_demux_t *dvbdemux);
+int DvbDmxRelease(dvb_demux_t *dvbdemux);
+void DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count);
+
+#endif /* _DVB_DEMUX_H_ */
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_filter.c b/linux/drivers/media/dvb/dvb-core/dvb_filter.c
new file mode 100644
index 000000000..4b6ef6d32
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_filter.c
@@ -0,0 +1,962 @@
+#include <linux/module.h>
+#include <linux/videodev.h>
+#include "dvb_filter.h"
+
+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}};
+
+uint32_t freq[4] = {441, 480, 320, 0};
+
+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};
+
+uint32_t ac3_freq[4] = {480, 441, 320, 0};
+uint32_t 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}};
+
+
+
+void pes2ts_init(pes2ts_t *p2ts, unsigned short pid,
+ 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;
+}
+
+int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len)
+{
+ unsigned char *buf=p2ts->buf;
+ int ret=0, rest;
+
+ //len=6+((pes[4]<<8)|pes[5]);
+
+ 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);
+}
+
+void reset_ipack(ipack *p)
+{
+ p->found = 0;
+ p->cid = 0;
+ p->plength = 0;
+ p->flag1 = 0;
+ p->flag2 = 0;
+ p->hlength = 0;
+ p->mpeg = 0;
+ p->check = 0;
+ p->which = 0;
+ p->done = 0;
+ p->count = 0;
+}
+
+void init_ipack(ipack *p, int size,
+ void (*func)(u8 *buf, int size, void *priv))
+{
+ if ( !(p->buf = vmalloc(size*sizeof(u8))) ){
+ printk ("Couldn't allocate memory for ipack\n");
+ }
+ p->size = size;
+ p->func = func;
+ p->repack_subids = 0;
+ reset_ipack(p);
+}
+
+void free_ipack(ipack * p)
+{
+ if (p->buf) vfree(p->buf);
+}
+
+void send_ipack(ipack *p)
+{
+ int off;
+ AudioInfo ai;
+ int ac3_off = 0;
+ int streamid=0;
+ int nframes= 0;
+ int f=0;
+
+ switch ( p->mpeg ){
+ case 2:
+ if (p->count < 10) return;
+ p->buf[3] = p->cid;
+
+ p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8);
+ p->buf[5] = (u8)((p->count-6) & 0x00FF);
+ if (p->repack_subids && p->cid == PRIVATE_STREAM1){
+
+ off = 9+p->buf[8];
+ streamid = p->buf[off];
+ if ((streamid & 0xF8) == 0x80){
+ ai.off = 0;
+ ac3_off = ((p->buf[off+2] << 8)|
+ p->buf[off+3]);
+ if (ac3_off < p->count)
+ f=get_ac3info(p->buf+off+3+ac3_off,
+ p->count-ac3_off, &ai,0);
+ if ( !f ){
+ nframes = (p->count-off-3-ac3_off)/
+ ai.framesize + 1;
+ p->buf[off+2] = (ac3_off >> 8)& 0xFF;
+ p->buf[off+3] = (ac3_off)& 0xFF;
+ p->buf[off+1] = nframes;
+
+ ac3_off += nframes * ai.framesize -
+ p->count;
+ }
+ }
+ }
+ p->func(p->buf, p->count, p->data);
+
+ p->buf[6] = 0x80;
+ p->buf[7] = 0x00;
+ p->buf[8] = 0x00;
+ p->count = 9;
+ if (p->repack_subids && p->cid == PRIVATE_STREAM1
+ && (streamid & 0xF8)==0x80 ){
+ p->count += 4;
+ p->buf[9] = streamid;
+ p->buf[10] = (ac3_off >> 8)& 0xFF;
+ p->buf[11] = (ac3_off)& 0xFF;
+ p->buf[12] = 0;
+ }
+
+ break;
+ case 1:
+ if (p->count < 8) return;
+ p->buf[3] = p->cid;
+
+ p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8);
+ p->buf[5] = (u8)((p->count-6) & 0x00FF);
+ p->func(p->buf, p->count, p->data);
+
+ p->buf[6] = 0x0F;
+ p->count = 7;
+ break;
+ }
+}
+
+void send_ipack_rest(ipack *p)
+{
+ if (p->plength != MMAX_PLENGTH-6 || p->found<=6)
+ return;
+ p->plength = p->found-6;
+ p->found = 0;
+ send_ipack(p);
+ reset_ipack(p);
+}
+
+static void write_ipack(ipack *p, u8 *data, int count)
+{
+ u8 headr[3] = { 0x00, 0x00, 0x01} ;
+
+ if (p->count < 6){
+ memcpy(p->buf, headr, 3);
+ p->count = 6;
+ }
+
+ if (p->count + count < p->size){
+ memcpy(p->buf+p->count, data, count);
+ p->count += count;
+ } else {
+ int rest = p->size - p->count;
+ memcpy(p->buf+p->count, data, rest);
+ p->count += rest;
+ send_ipack(p);
+ if (count - rest > 0)
+ write_ipack(p, data+rest, count-rest);
+ }
+}
+
+int instant_repack(u8 *buf, int count, ipack *p)
+{
+ int l;
+ int c=0;
+
+ while (c < count && (p->mpeg == 0 ||
+ (p->mpeg == 1 && p->found < 7) ||
+ (p->mpeg == 2 && p->found < 9))
+ && (p->found < 5 || !p->done)){
+ switch ( p->found ){
+ case 0:
+ case 1:
+ if (buf[c] == 0x00) p->found++;
+ else p->found = 0;
+ c++;
+ break;
+ case 2:
+ if (buf[c] == 0x01) p->found++;
+ else if (buf[c] == 0) {
+ p->found = 2;
+ } else p->found = 0;
+ c++;
+ break;
+ case 3:
+ p->cid = 0;
+ switch (buf[c]){
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ p->done = 1;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ p->found++;
+ p->cid = buf[c];
+ c++;
+ break;
+ default:
+ p->found = 0;
+ break;
+ }
+ break;
+
+ case 4:
+ if (count-c > 1){
+ p->plen[0] = buf[c];
+ c++;
+ p->plen[1] = buf[c];
+ c++;
+ p->found+=2;
+ p->plength=(p->plen[0]<<8)|p->plen[1];
+ } else {
+ p->plen[0] = buf[c];
+ p->found++;
+ return count;
+ }
+ break;
+ case 5:
+ p->plen[1] = buf[c];
+ c++;
+ p->found++;
+ p->plength=(p->plen[0]<<8)|p->plen[1];
+ break;
+ case 6:
+ if (!p->done){
+ p->flag1 = buf[c];
+ c++;
+ p->found++;
+ if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2;
+ else {
+ p->hlength = 0;
+ p->which = 0;
+ p->mpeg = 1;
+ p->flag2 = 0;
+ }
+ }
+ break;
+
+ case 7:
+ if ( !p->done && p->mpeg == 2) {
+ p->flag2 = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ case 8:
+ if ( !p->done && p->mpeg == 2) {
+ p->hlength = buf[c];
+ c++;
+ p->found++;
+ }
+ break;
+
+ default:
+
+ break;
+ }
+ }
+
+ if (c == count) return count;
+
+ if (!p->plength) p->plength = MMAX_PLENGTH-6;
+
+ if ( p->done || ((p->mpeg == 2 && p->found >= 9) ||
+ (p->mpeg == 1 && p->found >= 7)) ){
+ switch (p->cid){
+
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (p->mpeg == 2 && p->found == 9) {
+ write_ipack(p, &p->flag1, 1);
+ write_ipack(p, &p->flag2, 1);
+ write_ipack(p, &p->hlength, 1);
+ }
+
+ if (p->mpeg == 1 && p->found == 7)
+ write_ipack(p, &p->flag1, 1);
+
+ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+ p->found < 14) {
+ while (c < count && p->found < 14) {
+ p->pts[p->found-9] = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ }
+ if (c == count) return count;
+ }
+
+ if (p->mpeg == 1 && p->which < 2000) {
+
+ if (p->found == 7) {
+ p->check = p->flag1;
+ p->hlength = 1;
+ }
+
+ while (!p->which && c < count &&
+ p->check == 0xFF){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ }
+
+ if ( c == count) return count;
+
+ if ( (p->check & 0xC0) == 0x40 && !p->which){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+
+ p->which = 1;
+ if ( c == count) return count;
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return count;
+ }
+
+ if (p->which == 1){
+ p->check = buf[c];
+ write_ipack(p, buf+c, 1);
+ c++;
+ p->found++;
+ p->hlength++;
+ p->which = 2;
+ if ( c == count) return count;
+ }
+
+ if ( (p->check & 0x30) && p->check != 0xFF){
+ p->flag2 = (p->check & 0xF0) << 2;
+ p->pts[0] = p->check;
+ p->which = 3;
+ }
+
+ if ( c == count) return count;
+ if (p->which > 2){
+ if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_ONLY){
+ while (c < count &&
+ p->which < 7){
+ p->pts[p->which-2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return count;
+ } else if ((p->flag2 & PTS_DTS_FLAGS)
+ == PTS_DTS){
+ while (c < count &&
+ p->which< 12){
+ if (p->which< 7)
+ p->pts[p->which
+ -2] =
+ buf[c];
+ write_ipack(p,buf+c,1);
+ c++;
+ p->found++;
+ p->which++;
+ p->hlength++;
+ }
+ if ( c == count) return count;
+ }
+ p->which = 2000;
+ }
+
+ }
+
+ while (c < count && p->found < p->plength+6){
+ l = count -c;
+ if (l+p->found > p->plength+6)
+ l = p->plength+6-p->found;
+ write_ipack(p, buf+c, l);
+ p->found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+
+ if ( p->done ){
+ if( p->found + count - c < p->plength+6){
+ p->found += count-c;
+ c = count;
+ } else {
+ c += p->plength+6 - p->found;
+ p->found = p->plength+6;
+ }
+ }
+
+ if (p->plength && p->found == p->plength+6) {
+ send_ipack(p);
+ reset_ipack(p);
+ if (c < count)
+ instant_repack(buf+c, count-c, p);
+ }
+ }
+ return count;
+}
+
+
+
+void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv,
+ void (*pes_write)(u8 *buf, int count, void *data),
+ void *priv)
+{
+ init_ipack(pa, IPACKS, pes_write);
+ init_ipack(pv, IPACKS, pes_write);
+ pa->pid = pida;
+ pv->pid = pidv;
+ pa->data = priv;
+ pv->data = priv;
+}
+
+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);
+ reset_ipack(p);
+ }
+ }
+ if (buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = buf[4] + 1;
+ if (off+4 > 187) return;
+ }
+ instant_repack(buf+4+off, TS_SIZE-4-off, p);
+}
+
+/* needs 5 byte input, returns picture coding type*/
+int read_picture_header(uint8_t *headr, mpg_picture *pic, int field, int pr)
+{
+ uint8_t 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;
+}
+
+
+/* needs 4 byte input */
+int read_gop_header(uint8_t *headr, 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;
+}
+
+/* needs 8 byte input */
+int read_sequence_header(uint8_t *headr, VideoInfo *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;
+}
+
+int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+
+ while (found < 4 && c+4 < count){
+ uint8_t *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;
+}
+
+int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ int fr = 0;
+
+ while (found < 2 && c < count){
+ uint8_t 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;
+}
+
+int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr)
+{
+ uint8_t *headr;
+ int found = 0;
+ int c = 0;
+ uint8_t frame = 0;
+ int fr = 0;
+
+ while ( !found && c < count){
+ uint8_t *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", 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", 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", ai->framesize);
+
+
+ return 0;
+}
+
+uint8_t *skip_pes_header(uint8_t **bufp)
+{
+ uint8_t *inbuf = *bufp;
+ uint8_t *buf = inbuf;
+ uint8_t *pts = NULL;
+ int skip = 0;
+
+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;
+}
+
+
+void initialize_quant_matrix( uint32_t *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;
+}
+
+void initialize_mpg_picture(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;
+}
+
+
+void mpg_set_picture_parameter( int32_t field_type, 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;
+ }
+}
+
+void init_mpg_picture( 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;
+}
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_filter.h b/linux/drivers/media/dvb/dvb-core/dvb_filter.h
new file mode 100644
index 000000000..062fdcae4
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_filter.h
@@ -0,0 +1,249 @@
+#ifndef _DVB_FILTER_H_
+#define _DVB_FILTER_H_
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "demux.h"
+
+typedef int (pes2ts_cb_t) (void *, unsigned char *);
+
+typedef struct pes2ts_s {
+ unsigned char buf[188];
+ unsigned char cc;
+ pes2ts_cb_t *cb;
+ void *priv;
+} pes2ts_t;
+
+void pes2ts_init(pes2ts_t *p2ts, unsigned short pid,
+ pes2ts_cb_t *cb, void *priv);
+int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len);
+
+
+#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 PICTURE_START 0x00
+#define USER_START 0xb2
+#define SEQUENCE_HEADER 0xb3
+#define SEQUENCE_ERROR 0xb4
+#define EXTENSION_START 0xb5
+#define SEQUENCE_END 0xb7
+#define GOP_START 0xb8
+#define 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
+
+typedef struct ipack_s {
+ int size;
+ int found;
+ u8 *buf;
+ u8 cid;
+ uint32_t 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;
+} ipack;
+
+typedef struct video_i{
+ uint32_t horizontal_size;
+ uint32_t vertical_size;
+ uint32_t aspect_ratio;
+ uint32_t framerate;
+ uint32_t video_format;
+ uint32_t bit_rate;
+ uint32_t comp_bit_rate;
+ uint32_t vbv_buffer_size;
+ int16_t vbv_delay;
+ uint32_t CSPF;
+ uint32_t off;
+} VideoInfo;
+
+
+#define OFF_SIZE 4
+#define FIRST_FIELD 0
+#define SECOND_FIELD 1
+#define VIDEO_FRAME_PICTURE 0x03
+
+typedef struct mpg_picture_s{
+ int channel;
+ VideoInfo vinfo;
+ uint32_t *sequence_gop_header;
+ uint32_t *picture_header;
+ int32_t time_code;
+ int low_delay;
+ int closed_gop;
+ int broken_link;
+ int sequence_header_flag;
+ int gop_flag;
+ int sequence_end_flag;
+
+ uint8_t profile_and_level;
+ int32_t picture_coding_parameter;
+ uint32_t matrix[32];
+ int8_t matrix_change_flag;
+
+ uint8_t 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;
+ uint32_t sequence_header_data;
+ int16_t last_frame_centre_horizontal_offset;
+ int16_t last_frame_centre_vertical_offset;
+
+ uint32_t 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;
+ int16_t frame_centre_horizontal_offset[OFF_SIZE];
+ /* [0-2] 1st field, [3] 2nd field */
+ int16_t frame_centre_vertical_offset[OFF_SIZE];
+ /* [0-2] 1st field, [3] 2nd field */
+ int16_t temporal_reference[2];
+ /* [0] 1st field, [1] 2nd field */
+
+ int8_t picture_coding_type[2];
+ /* [0] 1st field, [1] 2nd field */
+ int8_t picture_structure[2];
+ /* [0] 1st field, [1] 2nd field */
+ int8_t picture_display_extension_flag[2];
+ /* [0] 1st field, [1] 2nd field */
+ /* picture_display_extenion() 0:no 1:exit*/
+ int8_t pts_flag[2];
+ /* [0] 1st field, [1] 2nd field */
+} mpg_picture;
+
+
+
+
+typedef struct audio_i{
+ int layer ;
+ uint32_t bit_rate ;
+ uint32_t frequency ;
+ uint32_t mode ;
+ uint32_t mode_extension ;
+ uint32_t emphasis ;
+ uint32_t framesize;
+ uint32_t off;
+} AudioInfo;
+
+
+void reset_ipack(ipack *p);
+int instant_repack(u8 *buf, int count, ipack *p);
+void init_ipack(ipack *p, int size,
+ void (*func)(u8 *buf, int size, void *priv));
+void free_ipack(ipack * p);
+void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv,
+ void (*pes_write)(u8 *buf, int count, void *data),
+ void *priv);
+void ts_to_pes(ipack *p, u8 *buf);
+void send_ipack(ipack *p);
+void send_ipack_rest(ipack *p);
+int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr);
+int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr);
+uint8_t *skip_pes_header(uint8_t **bufp);
+void initialize_quant_matrix( uint32_t *matrix );
+void initialize_mpg_picture(mpg_picture *pic);
+void init_mpg_picture( mpg_picture *pic, int chan, int32_t field_type);
+void mpg_set_picture_parameter( int32_t field_type, mpg_picture *pic );
+int read_sequence_header(uint8_t *headr, VideoInfo *vi, int pr);
+int read_gop_header(uint8_t *headr, mpg_picture *pic, int pr);
+int read_picture_header(uint8_t *headr, mpg_picture *pic, int field, int pr);
+#endif
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
new file mode 100644
index 000000000..8a6d2b79f
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -0,0 +1,931 @@
+/*
+ * dvb-core.c: DVB core driver
+ *
+ * Copyright (C) 1999-2001 Ralph Metzler
+ * Marcus Metzler
+ * Holger Waechtler
+ * 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/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/compatmac.h>
+#include <linux/list.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+
+
+static int dvb_frontend_debug = 0;
+static int dvb_shutdown_timeout = 0;
+
+#define dprintk if (dvb_frontend_debug) printk
+
+#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_fe_notifier_callbacks {
+ struct list_head list_head;
+ void (*callback) (fe_status_t s, void *data);
+ void *data;
+};
+
+
+struct dvb_frontend_data {
+ struct dvb_frontend_info *info;
+ struct dvb_frontend frontend;
+ struct dvb_device *dvbdev;
+ struct list_head notifier_callbacks;
+ struct dvb_frontend_parameters parameters;
+ struct dvb_fe_events events;
+ struct semaphore sem;
+ struct list_head list_head;
+ wait_queue_head_t wait_queue;
+ struct task_struct *thread;
+ unsigned long release_jiffies;
+ unsigned long lost_sync_jiffies;
+ int bending;
+ int lnb_drift;
+ int timeout_count;
+ int lost_sync_count;
+ int exit;
+ fe_status_t status;
+};
+
+
+struct dvb_frontend_ioctl_data {
+ struct list_head list_head;
+ struct dvb_adapter_s *adapter;
+ int (*before_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg);
+ int (*after_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg);
+ void *before_after_data;
+};
+
+
+static LIST_HEAD(frontend_list);
+static LIST_HEAD(frontend_ioctl_list);
+
+static DECLARE_MUTEX(frontend_mutex);
+
+
+static
+inline void ddelay (int ms)
+{
+ current->state=TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*ms)/1000);
+}
+
+
+static
+int dvb_frontend_internal_ioctl (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg)
+{
+ int err = -EOPNOTSUPP;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (frontend->before_ioctl)
+ err = frontend->before_ioctl (frontend, cmd, arg);
+
+ if (err) {
+ err = frontend->ioctl (frontend, cmd, arg);
+
+ if (err && frontend->after_ioctl)
+ err = frontend->after_ioctl (frontend, cmd, arg);
+ }
+
+ return err;
+}
+
+
+/**
+ * if 2 tuners are located side by side you can get interferences when
+ * they try to tune to the same frequency, so both lose sync.
+ * We will slightly mistune in this case. The AFC of the demodulator
+ * should make it still possible to receive the requested transponder
+ * on both tuners...
+ */
+static
+void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive)
+{
+ struct list_head *entry;
+ int stepsize = this_fe->info->frequency_stepsize;
+ int frequency = this_fe->parameters.frequency + this_fe->lnb_drift;
+
+ if (!stepsize || recursive > 10) {
+ printk ("%s: too deep recursion, check frequency_stepsize "
+ "in your frontend code!\n", __FUNCTION__);
+ return;
+ }
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (!recursive) {
+ if (down_interruptible (&frontend_mutex))
+ return;
+ this_fe->bending = 0;
+ }
+
+ list_for_each (entry, &frontend_list) {
+ struct dvb_frontend_data *fe;
+ int f;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ f = fe->parameters.frequency + fe->lnb_drift + fe->bending;
+
+ if (this_fe != fe &&
+ frequency >= f - stepsize && frequency <= f + stepsize)
+ {
+ if (recursive % 2 == 0)
+ this_fe->bending += stepsize;
+ else
+ this_fe->bending = -this_fe->bending;
+ dvb_bend_frequency (this_fe, recursive + 1);
+ goto done;
+ }
+ }
+done:
+ if (!recursive)
+ up (&frontend_mutex);
+}
+
+
+static
+void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe,
+ fe_status_t s)
+{
+ struct list_head *e;
+ struct dvb_fe_notifier_callbacks *c;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if ((fe->status & FE_HAS_LOCK) && !(s & FE_HAS_LOCK))
+ fe->lost_sync_jiffies = jiffies;
+
+ if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK))
+ ddelay (fe->info->notifier_delay);
+
+ fe->status = s;
+
+ if (!(s & FE_HAS_LOCK) && (fe->info->caps & FE_CAN_MUTE_TS))
+ return;
+
+ /**
+ * now tell the Demux about the TS status changes...
+ */
+ list_for_each (e, &fe->notifier_callbacks) {
+ c = list_entry (e, struct dvb_fe_notifier_callbacks, list_head);
+ c->callback (fe->status, c->data);
+ }
+}
+
+
+static
+void dvb_frontend_add_event (struct dvb_frontend_data *fe, fe_status_t status)
+{
+ struct dvb_fe_events *events = &fe->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, &fe->parameters,
+ sizeof (struct dvb_frontend_parameters));
+
+ if (status & FE_HAS_LOCK)
+ dvb_frontend_internal_ioctl (&fe->frontend,
+ FE_GET_FRONTEND,
+ &e->parameters);
+ events->eventw = wp;
+
+ up (&events->sem);
+
+ e->status = status;
+ dvb_call_frontend_notifiers (fe, status);
+
+ wake_up_interruptible (&events->wait_queue);
+}
+
+
+static
+int dvb_frontend_get_event (struct dvb_frontend_data *fe,
+ struct dvb_frontend_event *event, int flags)
+{
+ struct dvb_fe_events *events = &fe->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;
+
+ ret = wait_event_interruptible (events->wait_queue,
+ events->eventw != events->eventr);
+ 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
+struct dvb_frontend_parameters default_param [] = {
+ { /* NTV on Astra */
+ frequency: 12669500-10600000,
+ inversion: INVERSION_OFF,
+ { qpsk: { symbol_rate: 22000000, fec_inner: FEC_AUTO } }
+ },
+ { /* Cable */
+ frequency: 394000000,
+ inversion: INVERSION_OFF,
+ { qam: { symbol_rate: 6900000,
+ fec_inner: FEC_AUTO,
+ modulation: QAM_64
+ }
+ }
+ },
+ { /* DVB-T */
+ frequency: 730000000,
+ inversion: INVERSION_OFF,
+ { ofdm: { bandwidth: BANDWIDTH_8_MHZ,
+ code_rate_HP: FEC_2_3,
+ code_rate_LP: FEC_1_2,
+ constellation: QAM_16,
+ transmission_mode: TRANSMISSION_MODE_2K,
+ guard_interval: GUARD_INTERVAL_1_8,
+ hierarchy_information: HIERARCHY_NONE
+ }
+ }
+ }
+};
+
+
+static
+int dvb_frontend_set_parameters (struct dvb_frontend_data *fe,
+ struct dvb_frontend_parameters *param,
+ int first_trial)
+{
+ struct dvb_frontend *frontend = &fe->frontend;
+ int err;
+
+ dprintk ("%s: f == %i, drift == %i\n",
+ __FUNCTION__, param->frequency, fe->lnb_drift);
+
+ dvb_bend_frequency (fe, 0);
+
+ if (first_trial) {
+ fe->timeout_count = 0;
+ fe->lost_sync_count = 0;
+ fe->lost_sync_jiffies = jiffies;
+ fe->lnb_drift = 0;
+ if (fe->status & ~FE_TIMEDOUT)
+ dvb_frontend_add_event (fe, 0);
+ memcpy (&fe->parameters, param,
+ sizeof (struct dvb_frontend_parameters));
+ }
+
+ param->frequency += fe->lnb_drift + fe->bending;
+ err = dvb_frontend_internal_ioctl (frontend, FE_SET_FRONTEND, param);
+ param->frequency -= fe->lnb_drift + fe->bending;
+
+ wake_up_interruptible (&fe->wait_queue);
+
+ return err;
+}
+
+static
+void dvb_frontend_init (struct dvb_frontend_data *fe)
+{
+ struct dvb_frontend *frontend = &fe->frontend;
+ struct dvb_frontend_parameters *init_param;
+
+ printk ("%s: initialising frontend %i:%i (%s)...\n", __FUNCTION__,
+ frontend->i2c->adapter->num, frontend->i2c->id,
+ fe->info->name);
+
+ dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL);
+
+ if (fe->info->type == FE_QPSK) {
+ dvb_frontend_internal_ioctl (frontend, FE_SET_VOLTAGE,
+ (void*) SEC_VOLTAGE_13);
+ dvb_frontend_internal_ioctl (frontend, FE_SET_TONE,
+ (void*) SEC_TONE_ON);
+ }
+
+ init_param = &default_param[fe->info->type-FE_QPSK];
+
+ dvb_frontend_set_parameters (fe, init_param, 1);
+}
+
+
+static
+void update_delay (int *quality, int *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 = HZ/20 + q2 * HZ / (128*128);
+}
+
+
+#define LNB_DRIFT 1024 /* max. tolerated LNB drift, XXX FIXME: adjust! */
+#define TIMEOUT 2*HZ
+
+/**
+ * here we only come when we have lost the lock bit,
+ * let's try to do something useful...
+ */
+static
+void dvb_frontend_recover (struct dvb_frontend_data *fe)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+#if 0
+ if (fe->timeout_count > 3) {
+ printk ("%s: frontend seems dead, reinitializing...\n",
+ __FUNCTION__);
+ dvb_call_frontend_notifiers (fe, 0);
+ dvb_frontend_internal_ioctl (&fe->frontend, FE_INIT, NULL);
+ dvb_frontend_set_parameters (fe, &fe->parameters, 1);
+ dvb_frontend_add_event (fe, FE_REINIT);
+ fe->lost_sync_jiffies = jiffies;
+ fe->timeout_count = 0;
+ return;
+ }
+#endif
+
+ /**
+ * let's start a zigzag scan to compensate LNB drift...
+ */
+ {
+ int j = fe->lost_sync_count;
+ int stepsize = fe->info->frequency_stepsize;
+
+ if (j % 32 == 0)
+ fe->lnb_drift = 0;
+
+ if (j % 2)
+ fe->lnb_drift += stepsize * ((j+1)/2);
+ else
+ fe->lnb_drift = -fe->lnb_drift;
+
+ dvb_frontend_set_parameters (fe, &fe->parameters, 0);
+ }
+
+ dvb_frontend_internal_ioctl (&fe->frontend, FE_RESET, NULL);
+}
+
+
+
+static
+int dvb_frontend_is_exiting (struct dvb_frontend_data *fe)
+{
+ if (fe->exit)
+ return 1;
+
+ if (fe->dvbdev->users == 0 && dvb_shutdown_timeout)
+ if (jiffies - fe->release_jiffies > dvb_shutdown_timeout * HZ)
+ return 1;
+
+ return 0;
+}
+
+
+static
+int dvb_frontend_thread (void *data)
+{
+ struct dvb_frontend_data *fe = (struct dvb_frontend_data *) data;
+ int quality = 0, delay = 3*HZ;
+ fe_status_t s;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ lock_kernel ();
+ daemonize ();
+ sigfillset (&current->blocked);
+ strncpy (current->comm, "kdvb-fe", sizeof (current->comm));
+ fe->thread = current;
+ unlock_kernel ();
+
+ dvb_call_frontend_notifiers (fe, 0);
+ dvb_frontend_init (fe);
+
+ while (!dvb_frontend_is_exiting (fe)) {
+ up (&fe->sem); /* is locked when we enter the thread... */
+
+ interruptible_sleep_on_timeout (&fe->wait_queue, delay);
+
+ if (down_interruptible (&fe->sem)) {
+ fe->thread = NULL;
+ return -ERESTARTSYS;
+ }
+
+ if (dvb_frontend_is_exiting (fe))
+ break;
+
+ dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s);
+
+ update_delay (&quality, &delay, s & FE_HAS_LOCK);
+
+ s &= ~FE_TIMEDOUT;
+
+ if (s & FE_HAS_LOCK) {
+ fe->timeout_count = 0;
+ fe->lost_sync_count = 0;
+ } else {
+ fe->lost_sync_count++;
+printk ("%s: no sync, lost_sync_count %i\n", __FUNCTION__, fe->lost_sync_count);
+ dvb_frontend_recover (fe);
+ delay = HZ/5;
+ if (jiffies - fe->lost_sync_jiffies > TIMEOUT) {
+ s |= FE_TIMEDOUT;
+ if ((fe->status & FE_TIMEDOUT) == 0)
+ fe->timeout_count++;
+ }
+ }
+
+ if (s != fe->status)
+ dvb_frontend_add_event (fe, s);
+ };
+
+ dvb_frontend_internal_ioctl (&fe->frontend, FE_SLEEP, NULL);
+ up (&fe->sem);
+ fe->thread = NULL;
+ return 0;
+}
+
+
+static
+void dvb_frontend_start (struct dvb_frontend_data *fe)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (!fe->exit && !fe->thread) {
+ if (down_interruptible (&fe->sem))
+ return;
+ kernel_thread (dvb_frontend_thread, fe, 0);
+ }
+}
+
+
+static
+void dvb_frontend_stop (struct dvb_frontend_data *fe)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ fe->exit = 1;
+ wake_up_interruptible (&fe->wait_queue);
+
+ while (fe->thread) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (5);
+ };
+}
+
+
+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_data *fe = dvbdev->priv;
+ int err = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (!fe || !fe->frontend.ioctl || fe->exit)
+ return -ENODEV;
+
+ if (down_interruptible (&fe->sem))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case FE_DISEQC_SEND_MASTER_CMD:
+ case FE_DISEQC_SEND_BURST:
+ case FE_SET_TONE:
+ if (fe->status)
+ dvb_call_frontend_notifiers (fe, 0);
+ dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
+ break;
+ case FE_SET_FRONTEND:
+ err = dvb_frontend_set_parameters (fe, parg, 1);
+ break;
+ case FE_GET_EVENT:
+ err = dvb_frontend_get_event (fe, parg, file->f_flags);
+ break;
+ case FE_GET_FRONTEND:
+ memcpy (parg, &fe->parameters,
+ sizeof (struct dvb_frontend_parameters));
+ /* fall-through... */
+ default:
+ dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg);
+ };
+
+ up (&fe->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_data *fe = dvbdev->priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (fe->events.eventw != fe->events.eventr)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ poll_wait (file, &fe->events.wait_queue, wait);
+
+ if (fe->events.eventw != fe->events.eventr)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ return 0;
+}
+
+
+static
+int dvb_frontend_open (struct inode *inode, struct file *file)
+{
+ dvb_device_t *dvbdev = file->private_data;
+ struct dvb_frontend_data *fe = dvbdev->priv;
+ int ret;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if ((ret = dvb_generic_open (inode, file)) < 0)
+ return ret;
+
+ dvb_frontend_start (fe);
+
+ /* empty event queue */
+ fe->events.eventr = fe->events.eventw;
+
+ return ret;
+}
+
+
+static
+int dvb_frontend_release (struct inode *inode, struct file *file)
+{
+ dvb_device_t *dvbdev = file->private_data;
+ struct dvb_frontend_data *fe = dvbdev->priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ fe->release_jiffies = jiffies;
+
+ return dvb_generic_release (inode, file);
+}
+
+
+
+int
+dvb_add_frontend_ioctls (struct dvb_adapter_s *adapter,
+ int (*before_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ int (*after_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ void *before_after_data)
+{
+ struct dvb_frontend_ioctl_data *ioctl;
+ struct list_head *entry;
+ int frontend_count = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return -ERESTARTSYS;
+
+ ioctl = kmalloc (sizeof(struct dvb_frontend_ioctl_data), GFP_KERNEL);
+
+ if (!ioctl) {
+ up (&frontend_mutex);
+ return -ENOMEM;
+ }
+
+ ioctl->adapter = adapter;
+ ioctl->before_ioctl = before_ioctl;
+ ioctl->after_ioctl = after_ioctl;
+ ioctl->before_after_data = before_after_data;
+
+ list_add_tail (&ioctl->list_head, &frontend_ioctl_list);
+
+ list_for_each (entry, &frontend_list) {
+ struct dvb_frontend_data *fe;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ if (fe->frontend.i2c->adapter == adapter &&
+ fe->frontend.before_ioctl == NULL &&
+ fe->frontend.after_ioctl == NULL)
+ {
+ fe->frontend.before_ioctl = before_ioctl;
+ fe->frontend.after_ioctl = after_ioctl;
+ fe->frontend.before_after_data = before_after_data;
+ dvb_frontend_start (fe);
+ frontend_count++;
+ }
+ }
+
+ up (&frontend_mutex);
+
+ return frontend_count;
+}
+
+
+void
+dvb_remove_frontend_ioctls (struct dvb_adapter_s *adapter,
+ int (*before_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ int (*after_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg))
+{
+ struct list_head *entry;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return;
+
+ list_for_each (entry, &frontend_list) {
+ struct dvb_frontend_data *fe;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ if (fe->frontend.i2c->adapter == adapter &&
+ fe->frontend.before_ioctl == before_ioctl &&
+ fe->frontend.after_ioctl == after_ioctl)
+ {
+ fe->frontend.before_ioctl = NULL;
+ fe->frontend.after_ioctl = NULL;
+
+ }
+ }
+
+ up (&frontend_mutex);
+}
+
+
+int
+dvb_add_frontend_notifier (struct dvb_adapter_s *adapter,
+ void (*callback) (fe_status_t s, void *data),
+ void *data)
+{
+ struct list_head *entry;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return -ERESTARTSYS;
+
+ list_for_each (entry, &frontend_list) {
+ struct dvb_frontend_data *fe;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ if (fe->frontend.i2c->adapter == adapter) {
+ struct dvb_fe_notifier_callbacks *e;
+
+ e = kmalloc (sizeof(struct dvb_fe_notifier_callbacks),
+ GFP_KERNEL);
+
+ if (!e) {
+ up (&frontend_mutex);
+ return -ENOMEM;
+ }
+
+ e->callback = callback;
+ e->data = data;
+ list_add_tail (&e->list_head, &fe->notifier_callbacks);
+
+ up (&frontend_mutex);
+ return 0;
+ }
+ }
+
+ up (&frontend_mutex);
+
+ return -ENODEV;
+}
+
+
+void
+dvb_remove_frontend_notifier (struct dvb_adapter_s *adapter,
+ void (*callback) (fe_status_t s, void *data))
+{
+ struct list_head *entry;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return;
+
+ list_for_each (entry, &frontend_list) {
+ struct dvb_frontend_data *fe;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ if (fe->frontend.i2c->adapter == adapter) {
+ struct list_head *e0, *n0;
+
+ list_for_each_safe (e0, n0, &fe->notifier_callbacks) {
+ struct dvb_fe_notifier_callbacks *e;
+
+ e = list_entry (e0,
+ struct dvb_fe_notifier_callbacks,
+ list_head);
+ list_del (&e->list_head);
+ kfree (e);
+ }
+ }
+ }
+
+ up (&frontend_mutex);
+}
+
+
+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 (int (*ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ struct dvb_i2c_bus *i2c,
+ void *data,
+ struct dvb_frontend_info *info)
+{
+ struct list_head *entry;
+ struct dvb_frontend_data *fe;
+ dvb_device_t dvbdev_template = { users: 1, writers: 1,
+ fops: &dvb_frontend_fops,
+ kernel_ioctl: dvb_frontend_ioctl
+ };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return -ERESTARTSYS;
+
+ if (!(fe = kmalloc (sizeof (struct dvb_frontend_data), GFP_KERNEL))) {
+ up (&frontend_mutex);
+ return -ENOMEM;
+ }
+
+ memset (fe, 0, sizeof (struct dvb_frontend_data));
+
+ init_MUTEX (&fe->sem);
+ init_waitqueue_head (&fe->wait_queue);
+ init_waitqueue_head (&fe->events.wait_queue);
+ init_MUTEX (&fe->events.sem);
+ fe->events.eventw = fe->events.eventr = 0;
+ fe->events.overflow = 0;
+ INIT_LIST_HEAD (&fe->notifier_callbacks);
+
+ fe->frontend.ioctl = ioctl;
+ fe->frontend.i2c = i2c;
+ fe->frontend.data = data;
+ fe->info = info;
+
+ list_for_each (entry, &frontend_ioctl_list) {
+ struct dvb_frontend_ioctl_data *ioctl;
+
+ ioctl = list_entry (entry, struct dvb_frontend_ioctl_data, list_head);
+
+ if (ioctl->adapter == i2c->adapter) {
+ fe->frontend.before_ioctl = ioctl->before_ioctl;
+ fe->frontend.after_ioctl = ioctl->after_ioctl;
+ fe->frontend.before_after_data = ioctl->before_after_data;
+ dvb_frontend_start (fe);
+ break;
+ }
+ }
+
+ list_add_tail (&fe->list_head, &frontend_list);
+
+ dvb_register_device (i2c->adapter, &fe->dvbdev, &dvbdev_template,
+ fe, DVB_DEVICE_FRONTEND);
+
+ up (&frontend_mutex);
+
+ return 0;
+}
+
+
+int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ struct dvb_i2c_bus *i2c)
+{
+ struct list_head *entry, *n;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (down_interruptible (&frontend_mutex))
+ return -ERESTARTSYS;
+
+ list_for_each_safe (entry, n, &frontend_list) {
+ struct dvb_frontend_data *fe;
+
+ fe = list_entry (entry, struct dvb_frontend_data, list_head);
+
+ if (fe->frontend.ioctl == ioctl && fe->frontend.i2c == i2c) {
+ dvb_unregister_device (fe->dvbdev);
+
+ list_del (entry);
+ up (&frontend_mutex);
+
+ dvb_frontend_stop (fe);
+ kfree (fe);
+ return 0;
+ }
+ }
+
+ up (&frontend_mutex);
+ return -EINVAL;
+}
+
+MODULE_PARM(dvb_frontend_debug,"i");
+MODULE_PARM(dvb_shutdown_timeout,"i");
+MODULE_PARM_DESC(dvb_frontend_debug, "enable verbose debug messages");
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
new file mode 100644
index 000000000..9a65459e6
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -0,0 +1,117 @@
+/*
+ * dvb-core.h
+ *
+ * Copyright (C) 2001 Ralph Metzler <ralph@convergence.de>
+ * overhauled by Holger Waechtler <holger@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.
+ *
+ */
+
+#ifndef _DVB_FRONTEND_H_
+#define _DVB_FRONTEND_H_
+
+#include <asm/types.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#include <linux/dvb/frontend.h>
+
+#include "dvb_i2c.h"
+#include "dvbdev.h"
+
+
+
+
+/**
+ * when before_ioctl is registered and returns value 0, ioctl and after_ioctl
+ * are not executed.
+ */
+
+struct dvb_frontend {
+ int (*before_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
+ int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
+ int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg);
+ struct dvb_i2c_bus *i2c;
+ void *before_after_data; /* can be used by hardware module... */
+ void *data; /* can be used by hardware module... */
+};
+
+
+/**
+ * private frontend command ioctl's.
+ * keep them in sync with the public ones defined in linux/dvb/frontend.h
+ */
+#define FE_SLEEP _IO('v', 80)
+#define FE_INIT _IO('v', 81)
+#define FE_RESET _IO('v', 82)
+
+
+extern int
+dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ struct dvb_i2c_bus *i2c,
+ void *data,
+ struct dvb_frontend_info *info);
+
+extern int
+dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ struct dvb_i2c_bus *i2c);
+
+
+/**
+ * Add special ioctl code performed before and after the main ioctl
+ * to all frontend devices on the specified DVB adapter.
+ * This is necessairy because the 22kHz/13V-18V/DiSEqC stuff depends
+ * heavily on the hardware around the frontend, the same tuner can create
+ * these signals on about a million different ways...
+ *
+ * Return value: number of frontends where the ioctl's were applied.
+ */
+extern int
+dvb_add_frontend_ioctls (struct dvb_adapter_s *adapter,
+ int (*before_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ int (*after_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ void *before_after_data);
+
+
+extern void
+dvb_remove_frontend_ioctls (struct dvb_adapter_s *adapter,
+ int (*before_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg),
+ int (*after_ioctl) (struct dvb_frontend *frontend,
+ unsigned int cmd, void *arg));
+
+extern int
+dvb_add_frontend_notifier (struct dvb_adapter_s *adapter,
+ void (*callback) (fe_status_t s, void *data),
+ void *data);
+extern void
+dvb_remove_frontend_notifier (struct dvb_adapter_s *adapter,
+ void (*callback) (fe_status_t s, void *data));
+
+#endif
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_i2c.c b/linux/drivers/media/dvb/dvb-core/dvb_i2c.c
new file mode 100644
index 000000000..7aae8c93d
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_i2c.c
@@ -0,0 +1,301 @@
+/*
+ * dvb_i2c.h: simplified i2c interface for DVB adapters to get rid of i2c-core.c
+ *
+ * Copyright (C) 2002 Holger Waechtler 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/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include "compat.h"
+#include "dvb_i2c.h"
+
+
+struct dvb_i2c_device {
+ struct list_head list_head;
+ struct module *owner;
+ int (*attach) (struct dvb_i2c_bus *i2c);
+ void (*detach) (struct dvb_i2c_bus *i2c);
+};
+
+
+LIST_HEAD(dvb_i2c_buslist);
+LIST_HEAD(dvb_i2c_devicelist);
+
+DECLARE_MUTEX(dvb_i2c_mutex);
+
+
+static
+int register_i2c_client (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
+{
+ struct dvb_i2c_device *client;
+
+ if (!(client = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ client->detach = dev->detach;
+ client->owner = dev->owner;
+
+ INIT_LIST_HEAD(&client->list_head);
+
+ list_add_tail (&client->list_head, &i2c->client_list);
+
+ return 0;
+}
+
+
+static
+void try_attach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
+{
+ if (dev->owner) {
+ if (!MOD_CAN_QUERY(dev->owner))
+ return;
+
+ __MOD_INC_USE_COUNT(dev->owner);
+ }
+
+ if (dev->attach (i2c) == 0) {
+ register_i2c_client (i2c, dev);
+ } else {
+ if (dev->owner)
+ __MOD_DEC_USE_COUNT(dev->owner);
+ }
+}
+
+
+static
+void detach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev)
+{
+ dev->detach (i2c);
+
+ if (dev->owner)
+ __MOD_DEC_USE_COUNT(dev->owner);
+}
+
+
+static
+void unregister_i2c_client_from_bus (struct dvb_i2c_device *dev,
+ struct dvb_i2c_bus *i2c)
+{
+ struct list_head *entry;
+
+ list_for_each (entry, &i2c->client_list) {
+ struct dvb_i2c_device *client;
+
+ client = list_entry (entry, struct dvb_i2c_device, list_head);
+
+ if (client->detach == dev->detach)
+ detach_device (i2c, dev);
+ }
+}
+
+
+static
+void unregister_i2c_client_from_all_busses (struct dvb_i2c_device *dev)
+{
+ struct list_head *entry;
+
+ list_for_each (entry, &dvb_i2c_buslist) {
+ struct dvb_i2c_bus *i2c;
+
+ i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
+
+ unregister_i2c_client_from_bus (dev, i2c);
+ }
+}
+
+
+static
+void unregister_all_clients_from_bus (struct dvb_i2c_bus *i2c)
+{
+ struct list_head *entry, *n;
+
+ list_for_each_safe (entry, n, &(i2c->client_list)) {
+ struct dvb_i2c_device *client;
+
+ client = list_entry (entry, struct dvb_i2c_device, list_head);
+
+ detach_device (i2c, client);
+
+ list_del (entry);
+ }
+}
+
+
+
+static
+void probe_device_on_all_busses (struct dvb_i2c_device *dev)
+{
+ struct list_head *entry;
+
+ list_for_each (entry, &dvb_i2c_buslist) {
+ struct dvb_i2c_bus *i2c;
+
+ i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
+
+ try_attach_device (i2c, dev);
+ }
+}
+
+
+static
+void probe_devices_on_bus (struct dvb_i2c_bus *i2c)
+{
+ struct list_head *entry;
+
+ list_for_each (entry, &dvb_i2c_devicelist) {
+ struct dvb_i2c_device *dev;
+
+ dev = list_entry (entry, struct dvb_i2c_device, list_head);
+
+ try_attach_device (i2c, dev);
+ }
+}
+
+
+struct dvb_i2c_bus*
+dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
+ struct i2c_msg msgs[], int num),
+ void *data,
+ struct dvb_adapter_s *adapter,
+ int id)
+{
+ struct dvb_i2c_bus *i2c;
+
+ if (!(i2c = kmalloc (sizeof (struct dvb_i2c_bus), GFP_KERNEL)))
+ return NULL;
+
+ INIT_LIST_HEAD(&i2c->list_head);
+ INIT_LIST_HEAD(&i2c->client_list);
+
+ i2c->xfer = xfer;
+ i2c->data = data;
+ i2c->adapter = adapter;
+ i2c->id = id;
+
+ probe_devices_on_bus (i2c);
+
+ list_add_tail (&i2c->list_head, &dvb_i2c_buslist);
+
+ return i2c;
+}
+
+
+struct dvb_i2c_bus*
+dvb_find_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
+ struct i2c_msg msgs[], int num),
+ struct dvb_adapter_s *adapter,
+ int id)
+{
+ struct list_head *entry;
+
+ if (down_interruptible (&dvb_i2c_mutex))
+ return NULL;
+
+ list_for_each (entry, &dvb_i2c_buslist) {
+ struct dvb_i2c_bus *i2c;
+
+ i2c = list_entry (entry, struct dvb_i2c_bus, list_head);
+
+ if (i2c->xfer == xfer &&
+ i2c->adapter == adapter &&
+ i2c->id == id)
+ {
+ up (&dvb_i2c_mutex);
+ return i2c;
+ }
+ }
+
+ up (&dvb_i2c_mutex);
+
+ return NULL;
+}
+
+
+
+void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
+ struct i2c_msg msgs[], int num),
+ struct dvb_adapter_s *adapter,
+ int id)
+{
+ struct dvb_i2c_bus *i2c = dvb_find_i2c_bus (xfer, adapter, id);
+
+ if (i2c) {
+ unregister_all_clients_from_bus (i2c);
+ list_del (&i2c->list_head);
+ kfree (i2c);
+ }
+}
+
+
+int dvb_register_i2c_device (struct module *owner,
+ int (*attach) (struct dvb_i2c_bus *i2c),
+ void (*detach) (struct dvb_i2c_bus *i2c))
+{
+ struct dvb_i2c_device *entry;
+
+ if (down_interruptible (&dvb_i2c_mutex))
+ return -ERESTARTSYS;
+
+ if (!(entry = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL)))
+ return -ENOMEM;
+
+ entry->owner = owner;
+ entry->attach = attach;
+ entry->detach = detach;
+
+ INIT_LIST_HEAD(&entry->list_head);
+
+ probe_device_on_all_busses (entry);
+
+ list_add_tail (&entry->list_head, &dvb_i2c_devicelist);
+
+ up (&dvb_i2c_mutex);
+
+ return 0;
+}
+
+
+int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c))
+{
+ struct list_head *entry, *n;
+
+ if (down_interruptible (&dvb_i2c_mutex))
+ return -ERESTARTSYS;
+
+ list_for_each_safe (entry, n, &dvb_i2c_devicelist) {
+ struct dvb_i2c_device *dev;
+
+ dev = list_entry (entry, struct dvb_i2c_device, list_head);
+
+ if (dev->attach == attach) {
+ list_del (entry);
+ unregister_i2c_client_from_all_busses (dev);
+ kfree (entry);
+ up (&dvb_i2c_mutex);
+ return 0;
+ }
+ }
+
+ up (&dvb_i2c_mutex);
+
+ return -EINVAL;
+}
+
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_i2c.h b/linux/drivers/media/dvb/dvb-core/dvb_i2c.h
new file mode 100644
index 000000000..c06e4d1bc
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_i2c.h
@@ -0,0 +1,63 @@
+/*
+ * dvb_i2c.h: i2c interface to get rid of i2c-core.c
+ *
+ * Copyright (C) 2002 Holger Waechtler 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
+ */
+
+#ifndef _DVB_I2C_H_
+#define _DVB_I2C_H_
+
+#include <linux/list.h>
+#include <linux/i2c.h>
+
+#include "dvbdev.h"
+
+
+struct dvb_i2c_bus {
+ struct list_head list_head;
+ int (*xfer) (struct dvb_i2c_bus *i2c, struct i2c_msg msgs[], int num);
+ void *data;
+ struct dvb_adapter_s *adapter;
+ int id;
+ struct list_head client_list;
+};
+
+
+extern
+struct dvb_i2c_bus* dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
+ struct i2c_msg msgs[],
+ int num),
+ void *data,
+ struct dvb_adapter_s *adapter,
+ int id);
+
+extern
+void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c,
+ struct i2c_msg msgs[], int num),
+ struct dvb_adapter_s *adapter,
+ int id);
+
+
+extern int dvb_register_i2c_device (struct module *owner,
+ int (*attach) (struct dvb_i2c_bus *i2c),
+ void (*detach) (struct dvb_i2c_bus *i2c));
+
+extern int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c));
+
+#endif
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c
new file mode 100644
index 000000000..619ec95b6
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c
@@ -0,0 +1,49 @@
+#include <linux/module.h>
+
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_frontend.h"
+#include "dvb_i2c.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+
+EXPORT_SYMBOL(DmxDevInit);
+EXPORT_SYMBOL(DmxDevRelease);
+EXPORT_SYMBOL(DvbDmxInit);
+EXPORT_SYMBOL(DvbDmxRelease);
+EXPORT_SYMBOL(DvbDmxSWFilterPackets);
+
+EXPORT_SYMBOL(dvb_register_frontend);
+EXPORT_SYMBOL(dvb_unregister_frontend);
+EXPORT_SYMBOL(dvb_add_frontend_ioctls);
+EXPORT_SYMBOL(dvb_remove_frontend_ioctls);
+EXPORT_SYMBOL(dvb_add_frontend_notifier);
+EXPORT_SYMBOL(dvb_remove_frontend_notifier);
+
+EXPORT_SYMBOL(dvb_register_i2c_bus);
+EXPORT_SYMBOL(dvb_unregister_i2c_bus);
+EXPORT_SYMBOL(dvb_register_i2c_device);
+EXPORT_SYMBOL(dvb_unregister_i2c_device);
+
+EXPORT_SYMBOL(dvb_net_init);
+EXPORT_SYMBOL(dvb_net_release);
+
+EXPORT_SYMBOL(dvb_register_adapter);
+EXPORT_SYMBOL(dvb_unregister_adapter);
+EXPORT_SYMBOL(dvb_register_device);
+EXPORT_SYMBOL(dvb_unregister_device);
+EXPORT_SYMBOL(dvb_generic_ioctl);
+EXPORT_SYMBOL(dvb_generic_open);
+EXPORT_SYMBOL(dvb_generic_release);
+EXPORT_SYMBOL(generic_usercopy);
+
+EXPORT_SYMBOL(init_ipack);
+EXPORT_SYMBOL(reset_ipack);
+EXPORT_SYMBOL(free_ipack);
+EXPORT_SYMBOL(send_ipack_rest);
+EXPORT_SYMBOL(instant_repack);
+EXPORT_SYMBOL(pes2ts_init);
+EXPORT_SYMBOL(pes2ts);
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c
new file mode 100644
index 000000000..6553682a8
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c
@@ -0,0 +1,507 @@
+/*
+ * dvb_net.c
+ *
+ * Copyright (C) 2001 Convergence integrated media GmbH
+ * Ralph Metzler <ralph@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.
+ *
+ */
+
+#include <linux/dvb/net.h>
+
+#include <asm/uaccess.h>
+#include "demux.h"
+#include "dvb_net.h"
+
+/*
+ * 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>
+ */
+
+unsigned short my_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);
+ eth= skb->mac.ethernet;
+
+ 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);
+}
+
+static void
+dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
+{
+ u8 *eth;
+ struct sk_buff *skb;
+
+ if (pkt_len<13) {
+ printk("%s: IP/MPE packet length = %d too small.\n", dev->name, pkt_len);
+ return;
+ }
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ ((dvb_net_priv_t *)dev->priv)->stats.rx_dropped++;
+ return;
+ }
+ eth=(u8 *) skb_put(skb, pkt_len+2);
+ memcpy(eth+14, (void*)pkt+12, pkt_len-12);
+
+ 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[13]=0x00;
+
+ skb->protocol=my_eth_type_trans(skb,dev);
+ skb->dev=dev;
+
+ ((dvb_net_priv_t *)dev->priv)->stats.rx_packets++;
+ ((dvb_net_priv_t *)dev->priv)->stats.rx_bytes+=skb->len;
+ //sti();
+ netif_rx(skb);
+}
+
+static int
+dvb_net_callback(u8 *buffer1, size_t buffer1_len,
+ u8 *buffer2, size_t buffer2_len,
+ dmx_section_filter_t *filter,
+ dmx_success_t success)
+{
+ struct net_device *dev=(struct net_device *) filter->priv;
+
+ /* FIXME: this only works if exactly one complete section is
+ delivered in buffer1 only */
+ dvb_net_sec(dev, buffer1, buffer1_len);
+ return 0;
+}
+
+static int
+dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ return 0;
+}
+
+#define MASK 0x00;
+
+static int
+dvb_net_filter_set(struct net_device *dev,
+ dmx_section_filter_t **secfilter,
+ unsigned char *mac)
+{
+ dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv;
+ int ret;
+
+ *secfilter=0;
+ 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, 0, DMX_MAX_FILTER_SIZE);
+ memset((*secfilter)->filter_mask , 0, DMX_MAX_FILTER_SIZE);
+
+ (*secfilter)->filter_value[0]=0x3e;
+ (*secfilter)->filter_mask[0]=MASK;
+
+ (*secfilter)->filter_value[3]=mac[5];
+ (*secfilter)->filter_mask[3]=MASK;
+ (*secfilter)->filter_value[4]=mac[4];
+ (*secfilter)->filter_mask[4]=MASK;
+ (*secfilter)->filter_value[8]=mac[3];
+ (*secfilter)->filter_mask[8]=MASK;
+ (*secfilter)->filter_value[9]=mac[2];
+ (*secfilter)->filter_mask[9]=MASK;
+
+ (*secfilter)->filter_value[10]=mac[1];
+ (*secfilter)->filter_mask[10]=MASK;
+ (*secfilter)->filter_value[11]=mac[0];
+ (*secfilter)->filter_mask[11]=MASK;
+
+ printk("%s: filter mac=%02x %02x %02x %02x %02x %02x\n",
+ dev->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ return 0;
+}
+
+static int
+dvb_net_feed_start(struct net_device *dev)
+{
+ int ret, i;
+ dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv;
+ dmx_demux_t *demux=priv->demux;
+ unsigned char *mac=(unsigned char *) dev->dev_addr;
+
+ priv->secfeed=0;
+ priv->secfilter=0;
+
+ ret=demux->allocate_section_feed(demux, &priv->secfeed,
+ dvb_net_callback);
+ if (ret<0) {
+ printk("%s: could not get section feed\n", dev->name);
+ return ret;
+ }
+
+ ret=priv->secfeed->set(priv->secfeed, priv->pid, 32768, 0, 0);
+ if (ret<0) {
+ printk("%s: could not set section feed\n", dev->name);
+ priv->demux->
+ release_section_feed(priv->demux, priv->secfeed);
+ priv->secfeed=0;
+ return ret;
+ }
+ MOD_INC_USE_COUNT;
+
+ dvb_net_filter_set(dev, &priv->secfilter, mac);
+ for (i=0; i<priv->multi_num; i++)
+ dvb_net_filter_set(dev, &priv->secfilter,
+ priv->multi_macs[i]);
+
+ priv->secfeed->start_filtering(priv->secfeed);
+ printk("%s: feed_started\n", dev->name);
+ return 0;
+}
+
+static void
+dvb_net_feed_stop(struct net_device *dev)
+{
+ dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv;
+ int i;
+
+ if (priv->secfeed) {
+ if (priv->secfeed->is_filtering)
+ priv->secfeed->stop_filtering(priv->secfeed);
+ printk("%s: feed_stopped\n", dev->name);
+ if (priv->secfilter)
+ priv->secfeed->
+ release_filter(priv->secfeed,
+ priv->secfilter);
+ priv->secfilter=0;
+
+ for (i=0; i<priv->multi_num; i++) {
+ if (priv->multi_secfilter[i])
+ priv->secfeed->
+ release_filter(priv->secfeed,
+ priv->multi_secfilter[i]);
+ priv->multi_secfilter[i]=0;
+ }
+ priv->demux->
+ release_section_feed(priv->demux, priv->secfeed);
+ priv->secfeed=0;
+ MOD_DEC_USE_COUNT;
+ } else
+ printk("%s: no feed to stop\n", dev->name);
+}
+
+static int
+dvb_set_mc_filter(struct net_device *dev, struct dev_mc_list *mc)
+{
+ dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv;
+
+ if (priv->multi_num==DVB_NET_MULTICAST_MAX)
+ return -ENOMEM;
+
+ printk("%s: set_mc_filter %d: %02x %02x %02x %02x %02x %02x\n",
+ dev->name,
+ priv->multi_num,
+ mc->dmi_addr[0],
+ mc->dmi_addr[1],
+ mc->dmi_addr[2],
+ mc->dmi_addr[3],
+ mc->dmi_addr[4],
+ mc->dmi_addr[5]);
+ memcpy(priv->multi_macs[priv->multi_num], mc->dmi_addr, 6);
+
+ priv->multi_num++;
+ return 0;
+}
+
+static void
+dvb_net_set_multi(struct net_device *dev)
+{
+ dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv;
+
+ printk("%s: set_multi()\n", dev->name);
+ dvb_net_feed_stop(dev);
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Enable promiscuous mode */
+ printk("%s: promiscuous mode\n", dev->name);
+ } else if((dev->flags&IFF_ALLMULTI)) {
+ /* Disable promiscuous mode, use normal mode. */
+ printk("%s: normal mode\n", dev->name);
+ } else if(dev->mc_count) {
+ int mci;
+ struct dev_mc_list *mc;
+
+ printk("%s: set_mc_list, %d entries\n",
+ dev->name, dev->mc_count);
+ 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 int
+dvb_net_set_config(struct net_device *dev, struct ifmap *map)
+{
+ if (netif_running(dev))
+ return -EBUSY;
+ return 0;
+}
+
+static int
+dvb_net_set_mac(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr=p;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ if (netif_running(dev)) {
+ dvb_net_feed_stop(dev);
+ dvb_net_feed_start(dev);
+ }
+ return 0;
+}
+
+
+static int
+dvb_net_open(struct net_device *dev)
+{
+ dvb_net_feed_start(dev);
+ return 0;
+}
+
+static int
+dvb_net_stop(struct net_device *dev)
+{
+ dvb_net_feed_stop(dev);
+ return 0;
+}
+
+static struct net_device_stats *
+dvb_net_get_stats(struct net_device *dev)
+{
+ return &((dvb_net_priv_t *)dev->priv)->stats;
+}
+
+
+static int
+dvb_net_init_dev(struct net_device *dev)
+{
+ printk("dvb_net: dvb_net_init_dev()\n");
+
+ 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_multi;
+ dev->set_config = dvb_net_set_config;
+ dev->set_mac_address = dvb_net_set_mac;
+ dev->mtu = 4096;
+
+ dev->flags |= IFF_NOARP;
+ dev->hard_header_cache = NULL;
+
+ //SET_MODULE_OWNER(dev);
+
+ return 0;
+}
+
+static int
+get_if(dvb_net_t *dvbnet)
+{
+ int i;
+
+ for (i=0; i<dvbnet->dev_num; i++)
+ if (!dvbnet->state[i])
+ break;
+ if (i==dvbnet->dev_num)
+ return -1;
+ dvbnet->state[i]=1;
+ return i;
+}
+
+
+int
+dvb_net_add_if(dvb_net_t *dvbnet, u16 pid)
+{
+ struct net_device *net;
+ dmx_demux_t *demux;
+ int result;
+ int if_num;
+
+ if_num=get_if(dvbnet);
+ if (if_num<0)
+ return -EINVAL;
+
+ net=&dvbnet->device[if_num];
+ demux=dvbnet->demux;
+
+ net->base_addr = 0;
+ net->irq = 0;
+ net->dma = 0;
+ net->mem_start = 0;
+ memcpy(net->name, "dvb0_0", 7);
+ net->name[3]=dvbnet->card_num+0x30;
+ net->name[5]=if_num+0x30;
+ net->next = NULL;
+ net->init = dvb_net_init_dev;
+ net->priv = kmalloc(sizeof(dvb_net_priv_t), GFP_KERNEL);
+ if (net->priv == NULL)
+ return -ENOMEM;
+ memset(net->priv, 0, sizeof(dvb_net_priv_t));
+
+ ((dvb_net_priv_t *)net->priv)->demux=demux;
+ ((dvb_net_priv_t *)net->priv)->pid=pid;
+
+ net->base_addr=pid;
+
+ if ((result = register_netdev(net)) < 0) {
+ return result;
+ }
+ MOD_INC_USE_COUNT;
+ return if_num;
+}
+
+int
+dvb_net_remove_if(dvb_net_t *dvbnet, int num)
+{
+ if (!dvbnet->state[num])
+ return -EINVAL;
+ dvb_net_stop(&dvbnet->device[num]);
+ kfree(dvbnet->device[num].priv);
+ unregister_netdev(&dvbnet->device[num]);
+ dvbnet->state[num]=0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int dvb_net_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *parg)
+{
+ dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
+ dvb_net_t *dvbnet=(dvb_net_t *) 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;
+
+ result=dvb_net_add_if(dvbnet, dvbnetif->pid);
+ if (result<0)
+ return result;
+ dvbnetif->if_num=result;
+ break;
+ }
+ case NET_REMOVE_IF:
+ return dvb_net_remove_if(dvbnet, (int) parg);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct file_operations dvb_net_fops = {
+ owner: THIS_MODULE,
+ read: 0,
+ write: 0,
+ ioctl: dvb_generic_ioctl,
+ open: dvb_generic_open,
+ release: dvb_generic_release,
+ poll: 0,
+};
+
+static dvb_device_t dvbdev_net = {
+ priv: 0,
+ users: 1,
+ writers: 1,
+ fops: &dvb_net_fops,
+ kernel_ioctl: dvb_net_ioctl,
+};
+
+void
+dvb_net_release(dvb_net_t *dvbnet)
+{
+ int i;
+
+ dvb_unregister_device(dvbnet->dvbdev);
+ for (i=0; i<dvbnet->dev_num; i++) {
+ if (!dvbnet->state[i])
+ continue;
+ dvb_net_remove_if(dvbnet, i);
+ }
+}
+
+int
+dvb_net_init(dvb_adapter_t *adap, dvb_net_t *dvbnet, dmx_demux_t *demux)
+{
+ int i;
+
+ dvbnet->demux=demux;
+ dvbnet->dev_num=DVB_NET_DEVICES_MAX;
+ for (i=0; i<dvbnet->dev_num; i++)
+ dvbnet->state[i]=0;
+ dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, dvbnet, DVB_DEVICE_NET);
+ return 0;
+}
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.h b/linux/drivers/media/dvb/dvb-core/dvb_net.h
new file mode 100644
index 000000000..714c09565
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_net.h
@@ -0,0 +1,63 @@
+/*
+ * dvb_net.h
+ *
+ * Copyright (C) 2001 Convergence integrated media GmbH
+ * Ralph Metzler <ralph@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_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
+#define DVB_NET_MULTICAST_MAX 10
+
+typedef struct dvb_net_priv_s {
+ struct net_device_stats stats;
+ char name[6];
+ u16 pid;
+ dmx_demux_t *demux;
+ dmx_section_feed_t *secfeed;
+ dmx_section_filter_t *secfilter;
+ int multi_num;
+ dmx_section_filter_t *multi_secfilter[DVB_NET_MULTICAST_MAX];
+ unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
+} dvb_net_priv_t;
+
+typedef struct dvb_net_s {
+ dvb_device_t *dvbdev;
+
+ int card_num;
+ int dev_num;
+ struct net_device device[DVB_NET_DEVICES_MAX];
+ int state[DVB_NET_DEVICES_MAX];
+ dmx_demux_t *demux;
+} dvb_net_t;
+
+
+void dvb_net_release(dvb_net_t *);
+int dvb_net_init(dvb_adapter_t *, dvb_net_t *, dmx_demux_t *);
+
+#endif
diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c
new file mode 100644
index 000000000..b38cfdc6a
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c
@@ -0,0 +1,434 @@
+/*
+ * 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.
+ *
+ */
+
+/*#define CONFIG_DVB_DEVFS_ONLY 1*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+
+#include "compat.h"
+#include "dvbdev.h"
+
+static int dvbdev_debug = 0;
+#define dprintk if (dvbdev_debug) printk
+
+static devfs_handle_t dvb_devfs_handle;
+static LIST_HEAD(dvb_adapter_list);
+static DECLARE_MUTEX(dvbdev_register_lock);
+
+
+static char *dnames[] = {
+ "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+ "net", "osd"
+};
+
+
+#ifdef CONFIG_DVB_DEVFS_ONLY
+
+ #define DVB_MAX_IDS ~0
+ #define nums2minor(num,type,id) 0
+ #define DVB_DEVFS_FLAGS (DEVFS_FL_DEFAULT|DEVFS_FL_AUTO_DEVNUM)
+
+#else
+
+ #define DVB_MAX_IDS 4
+ #define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
+ #define DVB_DEVFS_FLAGS (DEVFS_FL_DEFAULT)
+
+
+static
+dvb_device_t* dvbdev_find_device (int minor)
+{
+ struct list_head *entry;
+
+ list_for_each (entry, &dvb_adapter_list) {
+ struct list_head *entry0;
+ dvb_adapter_t *adap;
+ adap = list_entry (entry, dvb_adapter_t, list_head);
+ list_for_each (entry0, &adap->device_list) {
+ dvb_device_t *dev;
+ dev = list_entry (entry0, dvb_device_t, 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)
+{
+ dvb_device_t *dvbdev;
+
+ dvbdev = dvbdev_find_device (minor(inode->i_rdev));
+
+ 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,
+ read: NULL,
+ write: NULL,
+ ioctl: NULL,
+ open: dvb_device_open,
+ release: NULL,
+ poll: NULL,
+};
+#endif /* CONFIG_DVB_DEVFS_ONLY */
+
+
+
+int dvb_generic_open(struct inode *inode, struct file *file)
+{
+ dvb_device_t *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->users)
+ return -EBUSY;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ if (!dvbdev->writers)
+ return -EBUSY;
+ dvbdev->writers--;
+ }
+
+ dvbdev->users--;
+ return 0;
+}
+
+
+int dvb_generic_release(struct inode *inode, struct file *file)
+{
+ dvb_device_t *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+ dvbdev->writers++;
+
+ dvbdev->users++;
+ return 0;
+}
+
+
+/*
+ * helper function -- handles userspace copying for ioctl arguments
+ */
+int
+generic_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:
+ 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 *)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 *)arg, parg, _IOC_SIZE(cmd)))
+ err = -EFAULT;
+ break;
+ }
+
+out:
+ if (mbuf)
+ kfree(mbuf);
+
+ return err;
+}
+
+
+int dvb_generic_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ dvb_device_t *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->kernel_ioctl)
+ return -EINVAL;
+
+ return generic_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
+}
+
+
+static
+int dvbdev_get_free_id (struct dvb_adapter_s *adap, int type)
+{
+ u32 id = 0;
+
+ while (id < DVB_MAX_IDS) {
+ struct list_head *entry;
+ list_for_each (entry, &adap->device_list) {
+ dvb_device_t *dev;
+ dev = list_entry (entry, dvb_device_t, list_head);
+ if (dev->type == type && dev->id == id)
+ goto skip;
+ }
+ return id;
+skip:
+ id++;
+ }
+ return -ENFILE;
+}
+
+
+int dvb_register_device(dvb_adapter_t *adap, dvb_device_t **pdvbdev,
+ dvb_device_t *template, void *priv, int type)
+{
+ u32 id;
+ char name [20];
+ dvb_device_t *dvbdev;
+
+ if (down_interruptible (&dvbdev_register_lock))
+ return -ERESTARTSYS;
+
+ if ((id = dvbdev_get_free_id (adap, type)) < 0) {
+ up (&dvbdev_register_lock);
+ *pdvbdev = 0;
+ printk ("%s: could get find free device id...\n", __FUNCTION__);
+ return -ENFILE;
+ }
+
+ *pdvbdev = dvbdev = kmalloc(sizeof(dvb_device_t), GFP_KERNEL);
+
+ if (!dvbdev) {
+ up(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ up (&dvbdev_register_lock);
+
+ memcpy(dvbdev, template, sizeof(dvb_device_t));
+ dvbdev->type = type;
+ dvbdev->id = id;
+ dvbdev->adapter = adap;
+ dvbdev->priv = priv;
+
+ list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+ sprintf(name, "%s%d", dnames[type], id);
+ dvbdev->devfs_handle = devfs_register(adap->devfs_handle, name,
+ DVB_DEVFS_FLAGS,
+ DVB_MAJOR,
+ nums2minor(adap->num, type, id),
+ S_IFCHR | S_IRUSR | S_IWUSR,
+ dvbdev->fops, dvbdev);
+
+ dprintk("%s: register adapter%d/%s @ minor: %i (0x%02x) - dvbdev: %p\n",
+ __FUNCTION__, adap->num, name, nums2minor(adap->num, type, id),
+ nums2minor(adap->num, type, id), dvbdev);
+
+ return 0;
+}
+
+
+void dvb_unregister_device(dvb_device_t *dvbdev)
+{
+ if (!dvbdev)
+ return;
+
+ devfs_unregister(dvbdev->devfs_handle);
+ list_del (&dvbdev->list_head);
+ kfree (dvbdev);
+}
+
+
+static
+int dvbdev_get_free_adapter_num (void)
+{
+ int num = 0;
+
+ while (1) {
+ struct list_head *entry;
+ list_for_each (entry, &dvb_adapter_list) {
+ dvb_adapter_t *adap;
+ adap = list_entry (entry, dvb_adapter_t, list_head);
+ if (adap->num == num)
+ goto skip;
+ }
+ return num;
+skip:
+ num++;
+ }
+
+ return -ENFILE;
+}
+
+
+int dvb_register_adapter(dvb_adapter_t **padap, char *name)
+{
+ char dirname[10];
+ dvb_adapter_t *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(dvb_adapter_t), GFP_KERNEL))) {
+ up(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ memset (adap, 0, sizeof(dvb_adapter_t));
+ INIT_LIST_HEAD (&adap->device_list);
+
+ MOD_INC_USE_COUNT;
+
+ printk ("%s: registering new adapter (%s).\n", __FUNCTION__, name);
+
+ sprintf(dirname, "adapter%d", num);
+ adap->devfs_handle = devfs_mk_dir(dvb_devfs_handle, dirname, NULL);
+ adap->num = num;
+
+ list_add_tail (&adap->list_head, &dvb_adapter_list);
+
+ up (&dvbdev_register_lock);
+
+ return num;
+}
+
+
+int dvb_unregister_adapter(dvb_adapter_t *adap)
+{
+ devfs_unregister (adap->devfs_handle);
+ if (down_interruptible (&dvbdev_register_lock))
+ return -ERESTARTSYS;
+ list_del (&adap->list_head);
+ up (&dvbdev_register_lock);
+ kfree (adap);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static
+int __init init_dvbdev(void)
+{
+ dvb_devfs_handle = devfs_mk_dir (NULL, "dvb", NULL);
+#ifndef CONFIG_DVB_DEVFS_ONLY
+ if(register_chrdev(DVB_MAJOR,"DVB", &dvb_device_fops)) {
+ printk("video_dev: unable to get major %d\n", DVB_MAJOR);
+ return -EIO;
+ }
+#endif
+ return 0;
+}
+
+
+static
+void __exit exit_dvbdev(void)
+{
+#ifndef CONFIG_DVB_DEVFS_ONLY
+ unregister_chrdev(DVB_MAJOR, "DVB");
+#endif
+ devfs_unregister(dvb_devfs_handle);
+}
+
+module_init(init_dvbdev);
+module_exit(exit_dvbdev);
+
+MODULE_DESCRIPTION("DVB Core Driver");
+MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(dvbdev_debug,"i");
+MODULE_PARM_DESC(dvbdev_debug, "enable verbose debug messages");
+
diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.h b/linux/drivers/media/dvb/dvb-core/dvbdev.h
new file mode 100644
index 000000000..52b0f0d09
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h
@@ -0,0 +1,91 @@
+/*
+ * dvbdev.h
+ *
+ * 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 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/version.h>
+#include <linux/poll.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/list.h>
+
+#define DVB_MAJOR 250
+
+#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
+
+
+typedef struct dvb_adapter_s
+{
+ int num;
+ devfs_handle_t devfs_handle;
+ struct list_head list_head;
+ struct list_head device_list;
+} dvb_adapter_t;
+
+
+typedef struct dvb_device
+{
+ struct list_head list_head;
+ struct file_operations *fops;
+ devfs_handle_t devfs_handle;
+ dvb_adapter_t *adapter;
+ int type;
+ u32 id;
+
+ int users;
+ int writers;
+
+ /* don't really need those !? */
+ int (*kernel_ioctl)(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg); // FIXME: use generic_usercopy()
+
+ void *priv;
+} dvb_device_t;
+
+
+int dvb_register_device(dvb_adapter_t *adap, dvb_device_t **pdvbdev,
+ dvb_device_t *template, void *priv, int type);
+void dvb_unregister_device(struct dvb_device *dvbdev);
+
+int dvb_register_adapter(dvb_adapter_t **padap, char *name);
+int dvb_unregister_adapter(dvb_adapter_t *adap);
+
+int dvb_generic_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+int dvb_generic_open(struct inode *inode, struct file *file);
+int dvb_generic_release(struct inode *inode, struct file *file);
+int generic_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 */