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