aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/dvb-core
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2012-06-14 16:35:53 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-08-13 23:08:14 -0300
commit3d6c2bc08ac4f75bf3597740357c98f2207ca412 (patch)
treedfe6d7c9e466cef06224ffadd3310d61f41a4678 /drivers/media/dvb-core
parent5bc3cb743bbab408792c1b4ef31adf6268aa4b7e (diff)
[media] dvb: move the dvb core one level up
just like the V4L2 core, move the DVB core to drivers/media, as the intention is to get rid of both "video" and "dvb" directories. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb-core')
-rw-r--r--drivers/media/dvb-core/Kconfig29
-rw-r--r--drivers/media/dvb-core/Makefile11
-rw-r--r--drivers/media/dvb-core/demux.h280
-rw-r--r--drivers/media/dvb-core/dmxdev.c1275
-rw-r--r--drivers/media/dvb-core/dmxdev.h118
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h361
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c1753
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.h136
-rw-r--r--drivers/media/dvb-core/dvb_demux.c1318
-rw-r--r--drivers/media/dvb-core/dvb_demux.h151
-rw-r--r--drivers/media/dvb-core/dvb_filter.c603
-rw-r--r--drivers/media/dvb-core/dvb_filter.h246
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c2553
-rw-r--r--drivers/media/dvb-core/dvb_frontend.h425
-rw-r--r--drivers/media/dvb-core/dvb_math.c145
-rw-r--r--drivers/media/dvb-core/dvb_math.h58
-rw-r--r--drivers/media/dvb-core/dvb_net.c1516
-rw-r--r--drivers/media/dvb-core/dvb_net.h66
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c299
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.h186
-rw-r--r--drivers/media/dvb-core/dvbdev.c507
-rw-r--r--drivers/media/dvb-core/dvbdev.h146
22 files changed, 12182 insertions, 0 deletions
diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig
new file mode 100644
index 000000000000..fa7a2490ed5f
--- /dev/null
+++ b/drivers/media/dvb-core/Kconfig
@@ -0,0 +1,29 @@
+#
+# DVB device configuration
+#
+
+config DVB_MAX_ADAPTERS
+ int "maximum number of DVB/ATSC adapters"
+ depends on DVB_CORE
+ default 8
+ range 1 255
+ help
+ Maximum number of DVB/ATSC adapters. Increasing this number
+ increases the memory consumption of the DVB subsystem even
+ if a much lower number of DVB/ATSC adapters is present.
+ Only values in the range 4-32 are tested.
+
+ If you are unsure about this, use the default value 8
+
+config DVB_DYNAMIC_MINORS
+ bool "Dynamic DVB minor allocation"
+ depends on DVB_CORE
+ default n
+ help
+ If you say Y here, the DVB subsystem will use dynamic minor
+ allocation for any device that uses the DVB major number.
+ This means that you can have more than 4 of a single type
+ of device (like demuxes and frontends) per adapter, but udev
+ will be required to manage the device nodes.
+
+ If you are unsure about this, say N here.
diff --git a/drivers/media/dvb-core/Makefile b/drivers/media/dvb-core/Makefile
new file mode 100644
index 000000000000..8f22bcd7c1f9
--- /dev/null
+++ b/drivers/media/dvb-core/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+dvb-net-$(CONFIG_DVB_NET) := dvb_net.o
+
+dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
+ dvb_ca_en50221.o dvb_frontend.o \
+ $(dvb-net-y) dvb_ringbuffer.o dvb_math.o
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h
new file mode 100644
index 000000000000..eb91fd808c16
--- /dev/null
+++ b/drivers/media/dvb-core/demux.h
@@ -0,0 +1,280 @@
+/*
+ * 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>
+#include <linux/dvb/dmx.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_SECTION_SIZE
+#define DMX_MAX_SECTION_SIZE 4096
+#endif
+#ifndef DMX_MAX_SECFEED_SIZE
+#define DMX_MAX_SECFEED_SIZE (DMX_MAX_SECTION_SIZE + 188)
+#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) */
+#define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to
+ the demux device, not to the dvr device */
+
+/* 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 circular_buffer_size,
+ 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 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 */
+ enum dmx_frontend_source source;
+};
+
+/*--------------------------------------------------------------------------*/
+/* MPEG-2 TS Demux */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Flags OR'ed in the capabilities 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
+
+/*
+ * 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 */
+ void* priv; /* Pointer to private data of the API client */
+ int (*open) (struct dmx_demux* demux);
+ int (*close) (struct dmx_demux* demux);
+ int (*write) (struct dmx_demux* demux, const char __user *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 (*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_caps) (struct dmx_demux* demux, struct dmx_caps *caps);
+
+ int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src);
+
+ int (*get_stc) (struct dmx_demux* demux, unsigned int num,
+ u64 *stc, unsigned int *base);
+};
+
+#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
new file mode 100644
index 000000000000..889c9c16c6df
--- /dev/null
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -0,0 +1,1275 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * 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.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.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 int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
+ const u8 *src, size_t len)
+{
+ ssize_t free;
+
+ if (!len)
+ return 0;
+ if (!buf->data)
+ return 0;
+
+ free = dvb_ringbuffer_free(buf);
+ if (len > free) {
+ dprintk("dmxdev: buffer overflow\n");
+ return -EOVERFLOW;
+ }
+
+ return dvb_ringbuffer_write(buf, src, len);
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+ int non_blocking, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t todo;
+ ssize_t avail;
+ ssize_t ret = 0;
+
+ if (!src->data)
+ return 0;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ return ret;
+ }
+
+ for (todo = count; todo > 0; todo -= ret) {
+ if (non_blocking && dvb_ringbuffer_empty(src)) {
+ ret = -EWOULDBLOCK;
+ break;
+ }
+
+ ret = wait_event_interruptible(src->queue,
+ !dvb_ringbuffer_empty(src) ||
+ (src->error != 0));
+ if (ret < 0)
+ break;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ break;
+ }
+
+ avail = dvb_ringbuffer_avail(src);
+ if (avail > todo)
+ avail = todo;
+
+ ret = dvb_ringbuffer_read_user(src, buf, avail);
+ if (ret < 0)
+ break;
+
+ buf += ret;
+ }
+
+ return (count - todo) ? (count - todo) : ret;
+}
+
+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 int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ struct dmx_frontend *front;
+
+ dprintk("function : %s\n", __func__);
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_RDWR) {
+ if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ void *mem;
+ if (!dvbdev->readers) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EBUSY;
+ }
+ mem = vmalloc(DVR_BUFFER_SIZE);
+ if (!mem) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+ dvbdev->readers--;
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
+
+ if (!dmxdev->demux->write) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+
+ front = get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+ if (!front) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux, front);
+ }
+ dvbdev->users++;
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ mutex_lock(&dmxdev->mutex);
+
+ 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) {
+ dvbdev->readers++;
+ 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);
+ }
+ }
+ /* TODO */
+ dvbdev->users--;
+ if (dvbdev->users == 1 && dmxdev->exit == 1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else
+ mutex_unlock(&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 = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int ret;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+ ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+ mutex_unlock(&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 = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ if (dmxdev->exit)
+ return -ENODEV;
+
+ return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+ void *newmem;
+ void *oldmem;
+
+ dprintk("function : %s\n", __func__);
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdev->lock);
+
+ vfree(oldmem);
+
+ return 0;
+}
+
+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 dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+ void *newmem;
+ void *oldmem;
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ vfree(oldmem);
+
+ 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 = 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 %*ph\n", 6, buffer1);
+ 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) {
+ dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ dmxdevfilter->buffer.error = ret;
+ }
+ 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 = feed->priv;
+ struct dvb_ringbuffer *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
+ || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_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) {
+ dvb_ringbuffer_flush(buffer);
+ buffer->error = ret;
+ }
+ 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)
+{
+ struct dmxdev_feed *feed;
+
+ 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:
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+ feed->ts->stop_filtering(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)
+{
+ struct dmxdev_feed *feed;
+ int ret;
+
+ 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);
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = feed->ts->start_filtering(feed->ts);
+ if (ret < 0) {
+ dvb_dmxdev_feed_stop(filter);
+ return ret;
+ }
+ }
+ 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].params.sec.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)
+{
+ struct dmxdev_feed *feed;
+ struct dmx_demux *demux;
+
+ 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:
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ demux = dmxdevfilter->dev->demux;
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ demux->release_ts_feed(demux, feed->ts);
+ feed->ts = NULL;
+ }
+ break;
+ default:
+ if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
+ return 0;
+ return -EINVAL;
+ }
+
+ dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ return 0;
+}
+
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ /* delete all PIDs */
+ list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+ list_del(&feed->next);
+ kfree(feed);
+ }
+
+ BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ return 0;
+
+ if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+ dvb_dmxdev_delete_pids(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ return 0;
+}
+
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmxdev_feed *feed)
+{
+ struct timespec timeout = { 0 };
+ struct dmx_pes_filter_params *para = &filter->params.pes;
+ dmx_output_t otype;
+ int ret;
+ int ts_type;
+ dmx_pes_type_t ts_pes;
+ struct dmx_ts_feed *tsfeed;
+
+ feed->ts = NULL;
+ otype = para->output;
+
+ 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;
+ else if (otype == DMX_OUT_TSDEMUX_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX;
+ else if (otype == DMX_OUT_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+ ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+ dvb_dmxdev_ts_callback);
+ if (ret < 0)
+ return ret;
+
+ tsfeed = feed->ts;
+ tsfeed->priv = filter;
+
+ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ ret = tsfeed->start_filtering(tsfeed);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev *dmxdev = filter->dev;
+ struct dmxdev_feed *feed;
+ 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 (!filter->buffer.data) {
+ mem = vmalloc(filter->buffer.size);
+ if (!mem)
+ return -ENOMEM;
+ spin_lock_irq(&filter->dev->lock);
+ filter->buffer.data = mem;
+ spin_unlock_irq(&filter->dev->lock);
+ }
+
+ dvb_ringbuffer_flush(&filter->buffer);
+
+ 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].type == DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].params.sec.pid == para->pid) {
+ *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",
+ __func__);
+ return ret;
+ }
+
+ ret = (*secfeed)->set(*secfeed, para->pid, 32768,
+ (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+ if (ret < 0) {
+ printk("DVB (%s): could not set feed\n",
+ __func__);
+ 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:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (ret < 0) {
+ dvb_dmxdev_filter_stop(filter);
+ 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 = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int i;
+ struct dmxdev_filter *dmxdevfilter;
+
+ if (!dmxdev->filter)
+ return -EINVAL;
+
+ if (mutex_lock_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) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EMFILE;
+ }
+
+ dmxdevfilter = &dmxdev->filter[i];
+ mutex_init(&dmxdevfilter->mutex);
+ file->private_data = dmxdevfilter;
+
+ dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ init_timer(&dmxdevfilter->timer);
+
+ dvbdev->users++;
+
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter)
+{
+ mutex_lock(&dmxdev->mutex);
+ mutex_lock(&dmxdevfilter->mutex);
+
+ 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);
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&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_add_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ /* only TS packet filters may have multiple PIDs */
+ if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+ (!list_empty(&filter->feed.ts)))
+ return -EINVAL;
+
+ feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+ if (feed == NULL)
+ return -ENOMEM;
+
+ feed->pid = pid;
+ list_add(&feed->next, &filter->feed.ts);
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+ return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+ if ((feed->pid == pid) && (feed->ts != NULL)) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(filter->dev->demux,
+ feed->ts);
+ list_del(&feed->next);
+ kfree(feed);
+ }
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_sct_filter_params *params)
+{
+ dprintk("function : %s\n", __func__);
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_SEC;
+ 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)
+{
+ int ret;
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
+ return -EINVAL;
+
+ dmxdevfilter->type = DMXDEV_TYPE_PES;
+ memcpy(&dmxdevfilter->params, params,
+ sizeof(struct dmx_pes_filter_params));
+ INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+ dmxdevfilter->params.pes.pid);
+ if (ret < 0)
+ return ret;
+
+ 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 = file->private_data;
+ int ret;
+
+ if (mutex_lock_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);
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ return ret;
+}
+
+static int dvb_demux_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ unsigned long arg = (unsigned long)parg;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_START:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ ret = -EINVAL;
+ else
+ ret = dvb_dmxdev_filter_start(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_STOP:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_stop(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PES_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_BUFFER_SIZE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_PES_PIDS:
+ if (!dmxdev->demux->get_pes_pids) {
+ ret = -EINVAL;
+ break;
+ }
+ dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
+ break;
+
+ case DMX_GET_CAPS:
+ if (!dmxdev->demux->get_caps) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->get_caps(dmxdev->demux, parg);
+ break;
+
+ case DMX_SET_SOURCE:
+ if (!dmxdev->demux->set_source) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->set_source(dmxdev->demux, 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;
+
+ case DMX_ADD_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_REMOVE_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ 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 (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+ return mask;
+}
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+ int ret;
+
+ ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->dvbdev->users--;
+ if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dmxdev->dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return ret;
+}
+
+static const struct file_operations dvb_demux_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_demux_read,
+ .unlocked_ioctl = dvb_demux_ioctl,
+ .open = dvb_demux_open,
+ .release = dvb_demux_release,
+ .poll = dvb_demux_poll,
+ .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_demux = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+ .fops = &dvb_demux_fops
+};
+
+static int dvb_dvr_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned long arg = (unsigned long)parg;
+ int ret;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_SET_BUFFER_SIZE:
+ ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_dvr_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned int mask = 0;
+
+ dprintk("function : %s\n", __func__);
+
+ 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 (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ } else
+ mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+ return mask;
+}
+
+static const struct file_operations dvb_dvr_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_dvr_read,
+ .write = dvb_dvr_write,
+ .unlocked_ioctl = dvb_dvr_ioctl,
+ .open = dvb_dvr_open,
+ .release = dvb_dvr_release,
+ .poll = dvb_dvr_poll,
+ .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_dvr = {
+ .priv = NULL,
+ .readers = 1,
+ .users = 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;
+
+ mutex_init(&dmxdev->mutex);
+ 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);
+ }
+
+ 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_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+ dmxdev->exit=1;
+ if (dmxdev->dvbdev->users > 1) {
+ wait_event(dmxdev->dvbdev->wait_queue,
+ dmxdev->dvbdev->users==1);
+ }
+ if (dmxdev->dvr_dvbdev->users > 1) {
+ wait_event(dmxdev->dvr_dvbdev->wait_queue,
+ dmxdev->dvr_dvbdev->users==1);
+ }
+
+ dvb_unregister_device(dmxdev->dvbdev);
+ dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ dmxdev->demux->close(dmxdev->demux);
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb-core/dmxdev.h b/drivers/media/dvb-core/dmxdev.h
new file mode 100644
index 000000000000..02ebe28f830d
--- /dev/null
+++ b/drivers/media/dvb-core/dmxdev.h
@@ -0,0 +1,118 @@
+/*
+ * 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 <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/dvb/dmx.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_ringbuffer.h"
+
+enum dmxdev_type {
+ 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_feed {
+ u16 pid;
+ struct dmx_ts_feed *ts;
+ struct list_head next;
+};
+
+struct dmxdev_filter {
+ union {
+ struct dmx_section_filter *sec;
+ } filter;
+
+ union {
+ /* list of TS and PES feeds (struct dmxdev_feed) */
+ struct list_head ts;
+ struct dmx_section_feed *sec;
+ } feed;
+
+ union {
+ struct dmx_sct_filter_params sec;
+ struct dmx_pes_filter_params pes;
+ } params;
+
+ enum dmxdev_type type;
+ enum dmxdev_state state;
+ struct dmxdev *dev;
+ struct dvb_ringbuffer buffer;
+
+ struct mutex mutex;
+
+ /* only for sections */
+ struct timer_list timer;
+ int todo;
+ u8 secheader[3];
+};
+
+
+struct dmxdev {
+ struct dvb_device *dvbdev;
+ struct dvb_device *dvr_dvbdev;
+
+ struct dmxdev_filter *filter;
+ struct dmx_demux *demux;
+
+ int filternum;
+ int capabilities;
+
+ unsigned int exit:1;
+#define DMXDEV_CAP_DUPLEX 1
+ struct dmx_frontend *dvr_orig_fe;
+
+ struct dvb_ringbuffer dvr_buffer;
+#define DVR_BUFFER_SIZE (10*188*1024)
+
+ struct mutex 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-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
new file mode 100644
index 000000000000..26c44818a5ab
--- /dev/null
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -0,0 +1,361 @@
+/* dvb-usb-ids.h is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see
+ * dvb-usb-init.c for copyright information.
+ *
+ * a header file containing define's for the USB device supported by the
+ * various drivers.
+ */
+#ifndef _DVB_USB_IDS_H_
+#define _DVB_USB_IDS_H_
+
+/* Vendor IDs */
+#define USB_VID_ADSTECH 0x06e1
+#define USB_VID_AFATECH 0x15a4
+#define USB_VID_ALCOR_MICRO 0x058f
+#define USB_VID_ALINK 0x05e3
+#define USB_VID_AMT 0x1c73
+#define USB_VID_ANCHOR 0x0547
+#define USB_VID_ANSONIC 0x10b9
+#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
+#define USB_VID_ASUS 0x0b05
+#define USB_VID_AVERMEDIA 0x07ca
+#define USB_VID_COMPRO 0x185b
+#define USB_VID_COMPRO_UNK 0x145f
+#define USB_VID_CONEXANT 0x0572
+#define USB_VID_CYPRESS 0x04b4
+#define USB_VID_DIBCOM 0x10b8
+#define USB_VID_DPOSH 0x1498
+#define USB_VID_DVICO 0x0fe9
+#define USB_VID_E3C 0x18b4
+#define USB_VID_ELGATO 0x0fd9
+#define USB_VID_EMPIA 0xeb1a
+#define USB_VID_GENPIX 0x09c0
+#define USB_VID_GRANDTEC 0x5032
+#define USB_VID_GTEK 0x1f4d
+#define USB_VID_HANFTEK 0x15f4
+#define USB_VID_HAUPPAUGE 0x2040
+#define USB_VID_HYPER_PALTEK 0x1025
+#define USB_VID_INTEL 0x8086
+#define USB_VID_ITETECH 0x048d
+#define USB_VID_KWORLD 0xeb2a
+#define USB_VID_KWORLD_2 0x1b80
+#define USB_VID_KYE 0x0458
+#define USB_VID_LEADTEK 0x0413
+#define USB_VID_LITEON 0x04ca
+#define USB_VID_MEDION 0x1660
+#define USB_VID_MIGLIA 0x18f3
+#define USB_VID_MSI 0x0db0
+#define USB_VID_MSI_2 0x1462
+#define USB_VID_OPERA1 0x695c
+#define USB_VID_PINNACLE 0x2304
+#define USB_VID_PCTV 0x2013
+#define USB_VID_PIXELVIEW 0x1554
+#define USB_VID_REALTEK 0x0bda
+#define USB_VID_TECHNOTREND 0x0b48
+#define USB_VID_TERRATEC 0x0ccd
+#define USB_VID_TELESTAR 0x10b9
+#define USB_VID_VISIONPLUS 0x13d3
+#define USB_VID_SONY 0x1415
+#define USB_VID_TWINHAN 0x1822
+#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+#define USB_VID_UNIWILL 0x1584
+#define USB_VID_WIDEVIEW 0x14aa
+#define USB_VID_GIGABYTE 0x1044
+#define USB_VID_YUAN 0x1164
+#define USB_VID_XTENSIONS 0x1ae7
+#define USB_VID_HUMAX_COEX 0x10b9
+#define USB_VID_774 0x7a69
+#define USB_VID_EVOLUTEPC 0x1e59
+#define USB_VID_AZUREWAVE 0x13d3
+#define USB_VID_TECHNISAT 0x14f7
+
+/* Product IDs */
+#define USB_PID_ADSTECH_USB2_COLD 0xa333
+#define USB_PID_ADSTECH_USB2_WARM 0xa334
+#define USB_PID_AFATECH_AF9005 0x9020
+#define USB_PID_AFATECH_AF9015_9015 0x9015
+#define USB_PID_AFATECH_AF9015_9016 0x9016
+#define USB_PID_AFATECH_AF9035_1000 0x1000
+#define USB_PID_AFATECH_AF9035_1001 0x1001
+#define USB_PID_AFATECH_AF9035_1002 0x1002
+#define USB_PID_AFATECH_AF9035_1003 0x1003
+#define USB_PID_AFATECH_AF9035_9035 0x9035
+#define USB_PID_TREKSTOR_DVBT 0x901b
+#define USB_VID_ALINK_DTU 0xf170
+#define USB_PID_ANSONIC_DVBT_USB 0x6000
+#define USB_PID_ANYSEE 0x861f
+#define USB_PID_AZUREWAVE_AD_TU700 0x3237
+#define USB_PID_AZUREWAVE_6007 0x0ccd
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
+#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
+#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
+#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
+#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
+#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80
+#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397
+#define USB_PID_CONEXANT_D680_DMB 0x86d6
+#define USB_PID_CREATIX_CTX1921 0x1921
+#define USB_PID_DELOCK_USB2_DVBT 0xb803
+#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
+#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
+#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
+#define USB_PID_DIBCOM_STK7700P 0x1e14
+#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
+#define USB_PID_DIBCOM_STK7700D 0x1ef0
+#define USB_PID_DIBCOM_STK7700_U7000 0x7001
+#define USB_PID_DIBCOM_STK7070P 0x1ebc
+#define USB_PID_DIBCOM_STK7070PD 0x1ebe
+#define USB_PID_DIBCOM_STK807XP 0x1f90
+#define USB_PID_DIBCOM_STK807XPVR 0x1f98
+#define USB_PID_DIBCOM_STK8096GP 0x1fa0
+#define USB_PID_DIBCOM_NIM8096MD 0x1fa8
+#define USB_PID_DIBCOM_TFE8096P 0x1f9C
+#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
+#define USB_PID_DIBCOM_STK7770P 0x1e80
+#define USB_PID_DIBCOM_NIM7090 0x1bb2
+#define USB_PID_DIBCOM_TFE7090PVR 0x1bb4
+#define USB_PID_DIBCOM_TFE7090E 0x1bb7
+#define USB_PID_DIBCOM_TFE7790E 0x1e6e
+#define USB_PID_DIBCOM_NIM9090M 0x2383
+#define USB_PID_DIBCOM_NIM9090MD 0x2384
+#define USB_PID_DPOSH_M9206_COLD 0x9206
+#define USB_PID_DPOSH_M9206_WARM 0xa090
+#define USB_PID_E3C_EC168 0x1689
+#define USB_PID_E3C_EC168_2 0xfffa
+#define USB_PID_E3C_EC168_3 0xfffb
+#define USB_PID_E3C_EC168_4 0x1001
+#define USB_PID_E3C_EC168_5 0x1002
+#define USB_PID_FREECOM_DVBT 0x0160
+#define USB_PID_FREECOM_DVBT_2 0x0161
+#define USB_PID_UNIWILL_STK7700P 0x6003
+#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
+#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_INTEL_CE9500 0x9500
+#define USB_PID_ITETECH_IT9135 0x9135
+#define USB_PID_ITETECH_IT9135_9005 0x9005
+#define USB_PID_ITETECH_IT9135_9006 0x9006
+#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_399U_2 0xe400
+#define USB_PID_KWORLD_395U 0xe396
+#define USB_PID_KWORLD_395U_2 0xe39b
+#define USB_PID_KWORLD_395U_3 0xe395
+#define USB_PID_KWORLD_395U_4 0xe39a
+#define USB_PID_KWORLD_MC810 0xc810
+#define USB_PID_KWORLD_PC160_2T 0xc160
+#define USB_PID_KWORLD_PC160_T 0xc161
+#define USB_PID_KWORLD_UB383_T 0xe383
+#define USB_PID_KWORLD_UB499_2T_T09 0xe409
+#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
+#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
+#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097
+#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099
+#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9
+#define USB_PID_TWINHAN_VP7041_COLD 0x3201
+#define USB_PID_TWINHAN_VP7041_WARM 0x3202
+#define USB_PID_TWINHAN_VP7020_COLD 0x3203
+#define USB_PID_TWINHAN_VP7020_WARM 0x3204
+#define USB_PID_TWINHAN_VP7045_COLD 0x3205
+#define USB_PID_TWINHAN_VP7045_WARM 0x3206
+#define USB_PID_TWINHAN_VP7021_COLD 0x3207
+#define USB_PID_TWINHAN_VP7021_WARM 0x3208
+#define USB_PID_TINYTWIN 0x3226
+#define USB_PID_TINYTWIN_2 0xe402
+#define USB_PID_TINYTWIN_3 0x9016
+#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
+#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
+#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
+#define USB_PID_ARTEC_T14_COLD 0x810b
+#define USB_PID_ARTEC_T14_WARM 0x810c
+#define USB_PID_ARTEC_T14BR 0x810f
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
+#define USB_PID_DTT200U_COLD 0x0201
+#define USB_PID_DTT200U_WARM 0x0301
+#define USB_PID_WT220U_ZAP250_COLD 0x0220
+#define USB_PID_WT220U_COLD 0x0222
+#define USB_PID_WT220U_WARM 0x0221
+#define USB_PID_WT220U_FC_COLD 0x0225
+#define USB_PID_WT220U_FC_WARM 0x0226
+#define USB_PID_WT220U_ZL0353_COLD 0x022a
+#define USB_PID_WT220U_ZL0353_WARM 0x022b
+#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
+#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
+#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941
+#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
+#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070
+#define USB_PID_HAUPPAUGE_MYTV_T 0x7080
+#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
+#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200
+#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200
+#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210
+#define USB_PID_AVERMEDIA_EXPRESS 0xb568
+#define USB_PID_AVERMEDIA_VOLAR 0xa807
+#define USB_PID_AVERMEDIA_VOLAR_2 0xb808
+#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868
+#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
+#define USB_PID_AVERMEDIA_A309 0xa309
+#define USB_PID_AVERMEDIA_A310 0xa310
+#define USB_PID_AVERMEDIA_A850 0x850a
+#define USB_PID_AVERMEDIA_A850T 0x850b
+#define USB_PID_AVERMEDIA_A805 0xa805
+#define USB_PID_AVERMEDIA_A815M 0x815a
+#define USB_PID_AVERMEDIA_A835 0xa835
+#define USB_PID_AVERMEDIA_B835 0xb835
+#define USB_PID_AVERMEDIA_1867 0x1867
+#define USB_PID_AVERMEDIA_A867 0xa867
+#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
+#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
+#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
+#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
+#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081
+#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
+#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060
+#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
+#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
+#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
+#define USB_PID_TERRATEC_H7 0x10b4
+#define USB_PID_TERRATEC_H7_2 0x10a3
+#define USB_PID_TERRATEC_T3 0x10a0
+#define USB_PID_TERRATEC_T5 0x10a1
+#define USB_PID_NOXON_DAB_STICK 0x00b3
+#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
+#define USB_PID_PINNACLE_PCTV2000E 0x022c
+#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
+#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229
+#define USB_PID_PINNACLE_PCTV71E 0x022b
+#define USB_PID_PINNACLE_PCTV72E 0x0236
+#define USB_PID_PINNACLE_PCTV73E 0x0237
+#define USB_PID_PINNACLE_PCTV310E 0x3211
+#define USB_PID_PINNACLE_PCTV801E 0x023a
+#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
+#define USB_PID_PINNACLE_PCTV340E 0x023d
+#define USB_PID_PINNACLE_PCTV340E_SE 0x023e
+#define USB_PID_PINNACLE_PCTV73A 0x0243
+#define USB_PID_PINNACLE_PCTV73ESE 0x0245
+#define USB_PID_PINNACLE_PCTV74E 0x0246
+#define USB_PID_PINNACLE_PCTV282E 0x0248
+#define USB_PID_PIXELVIEW_SBTVD 0x5010
+#define USB_PID_PCTV_200E 0x020e
+#define USB_PID_PCTV_400E 0x020f
+#define USB_PID_PCTV_450E 0x0222
+#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_REALTEK_RTL2831U 0x2831
+#define USB_PID_REALTEK_RTL2832U 0x2832
+#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
+#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
+#define USB_PID_NEBULA_DIGITV 0x0201
+#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
+#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
+#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501
+#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00
+#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01
+#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10
+#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11
+#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50
+#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
+#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
+#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
+#define USB_PID_MEDION_MD95700 0x0932
+#define USB_PID_MSI_MEGASKY580 0x5580
+#define USB_PID_MSI_MEGASKY580_55801 0x5581
+#define USB_PID_KYE_DVB_T_COLD 0x701e
+#define USB_PID_KYE_DVB_T_WARM 0x701f
+#define USB_PID_LITEON_DVB_T_COLD 0xf000
+#define USB_PID_LITEON_DVB_T_WARM 0xf001
+#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
+#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
+#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
+#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
+#define USB_PID_WINFAST_DTV2000DS 0x6a04
+#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
+#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
+#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
+#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
+#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
+#define USB_PID_GENPIX_8PSK_REV_2 0x0202
+#define USB_PID_GENPIX_SKYWALKER_1 0x0203
+#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204
+#define USB_PID_GENPIX_SKYWALKER_2 0x0206
+#define USB_PID_SIGMATEK_DVB_110 0x6610
+#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
+#define USB_PID_MSI_DIGIVOX_DUO 0x8801
+#define USB_PID_OPERA1_COLD 0x2830
+#define USB_PID_OPERA1_WARM 0x3829
+#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
+#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
+#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_GIGABYTE_U8000 0x7002
+#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3000H 0x1736
+#define USB_PID_ASUS_U3100 0x173f
+#define USB_PID_YUAN_EC372S 0x1edc
+#define USB_PID_YUAN_STK7700PH 0x1f08
+#define USB_PID_YUAN_PD378S 0x2edc
+#define USB_PID_YUAN_MC770 0x0871
+#define USB_PID_YUAN_STK7700D 0x1efc
+#define USB_PID_YUAN_STK7700D_2 0x1e8c
+#define USB_PID_DW2102 0x2102
+#define USB_PID_XTENSIONS_XD_380 0x0381
+#define USB_PID_TELESTAR_STARSTICK_2 0x8000
+#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
+#define USB_PID_SONY_PLAYTV 0x0003
+#define USB_PID_MYGICA_D689 0xd811
+#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
+#define USB_PID_ELGATO_EYETV_DTT 0x0021
+#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
+#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
+#define USB_PID_ELGATO_EYETV_SAT 0x002a
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
+#define USB_PID_FRIIO_WHITE 0x0001
+#define USB_PID_TVWAY_PLUS 0x0002
+#define USB_PID_SVEON_STV20 0xe39d
+#define USB_PID_SVEON_STV22 0xe401
+#define USB_PID_SVEON_STV22_IT9137 0xe411
+#define USB_PID_AZUREWAVE_AZ6027 0x3275
+#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4
+#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac
+#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001
+#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002
+#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004
+#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500
+#endif
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 000000000000..9be65a3b931f
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1753 @@
+/*
+ * 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/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/kthread.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 10
+
+#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;
+
+ /* mutex used for serializing access to one CI slot */
+ struct mutex slot_lock;
+
+ /* 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;
+
+ /* 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 */
+ struct task_struct *thread;
+
+ /* 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 char *findstr(char * haystack, int hlen, char * 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", __func__);
+
+ /* 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", __func__, 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", __func__, 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", __func__);
+
+ /* 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((char *)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((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+ (findstr((char *)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", __func__);
+
+ /* 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", __func__);
+
+ /* check if we have space for a link buf in the rx_buffer */
+ if (ebuf == NULL) {
+ int buf_free;
+
+ if (ca->slot_info[slot].rx_buffer.data == NULL) {
+ status = -EIO;
+ goto exit;
+ }
+ buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+
+ 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) {
+ if (ca->slot_info[slot].rx_buffer.data == NULL) {
+ status = -EIO;
+ goto exit;
+ }
+ dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
+ } 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", __func__);
+
+
+ /* sanity check */
+ if (bytes_write > ca->slot_info[slot].link_buf_size)
+ return -EINVAL;
+
+ /* it is possible we are dealing with a single buffer implementation,
+ thus if there is data available for read or if there is even a read
+ already in progress, we do nothing but awake the kernel thread to
+ process the data if necessary. */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+ goto exitnowrite;
+ if (status & (STATUSREG_DA | STATUSREG_RE)) {
+ if (status & STATUSREG_DA)
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ 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", __func__);
+
+ ca->pub->slot_shutdown(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+
+ /* 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 = 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 = 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 = 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_thread_wakeup(ca);
+ 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", __func__);
+
+ ca->wakeup = 1;
+ mb();
+ wake_up_process(ca->thread);
+}
+
+/**
+ * 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;
+
+ /* Beware of too high polling frequency, because one polling
+ * call might take several hundred milliseconds until timeout!
+ */
+ for (slot = 0; slot < ca->slot_count; slot++) {
+ switch (ca->slot_info[slot].slot_state) {
+ default:
+ case DVB_CA_SLOTSTATE_NONE:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ * 5; /* 5s */
+ break;
+ case DVB_CA_SLOTSTATE_INVALID:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ / 10; /* 100ms */
+ 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; /* 100ms */
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ delay = HZ * 60; /* 60s */
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
+ delay = HZ / 10; /* 100ms */
+ if (ca->open) {
+ if ((!ca->slot_info[slot].da_irq_supported) ||
+ (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
+ delay = HZ / 10; /* 100ms */
+ }
+ 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 = data;
+ int slot;
+ int flags;
+ int status;
+ int pktcount;
+ void *rxbuf;
+
+ dprintk("%s\n", __func__);
+
+ /* choose the correct initial delay */
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ /* main loop */
+ while (!kthread_should_stop()) {
+ /* sleep for a bit */
+ if (!ca->wakeup) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(ca->delay);
+ if (kthread_should_stop())
+ return 0;
+ }
+ ca->wakeup = 0;
+
+ /* go through all the slots processing them */
+ for (slot = 0; slot < ca->slot_count; slot++) {
+
+ mutex_lock(&ca->slot_info[slot].slot_lock);
+
+ // 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) {
+ /* we need this extra check for annoying interfaces like the budget-av */
+ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
+ (ca->pub->poll_slot_status)) {
+ status = ca->pub->poll_slot_status(ca->pub, slot, 0);
+ if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ }
+
+ 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) {
+ /* we need this extra check for annoying interfaces like the budget-av */
+ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
+ (ca->pub->poll_slot_status)) {
+ status = ca->pub->poll_slot_status(ca->pub, slot, 0);
+ if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+ dvb_ca_en50221_thread_update_delay(ca);
+ break;
+ }
+ }
+
+ 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;
+ }
+
+ if (ca->slot_info[slot].rx_buffer.data == NULL) {
+ 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;
+ }
+ dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
+ }
+
+ 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)
+ break;
+
+ // poll slots for data
+ 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;
+ }
+
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
+ }
+ }
+
+ 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 file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err = 0;
+ int slot;
+
+ dprintk("%s\n", __func__);
+
+ switch (cmd) {
+ case CA_RESET:
+ for (slot = 0; slot < ca->slot_count; slot++) {
+ mutex_lock(&ca->slot_info[slot].slot_lock);
+ 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);
+ }
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
+ }
+ ca->next_read_slot = 0;
+ dvb_ca_en50221_thread_wakeup(ca);
+ break;
+
+ case CA_GET_CAP: {
+ struct ca_caps *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 = 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 long dvb_ca_en50221_io_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(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 = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ u8 slot, connection_id;
+ int status;
+ u8 fragbuf[HOST_LINK_BUF_SIZE];
+ int fragpos = 0;
+ int fraglen;
+ unsigned long timeout;
+ int written;
+
+ dprintk("%s\n", __func__);
+
+ /* 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 (fraglen < 0)
+ break;
+ if (fraglen > HOST_LINK_BUF_SIZE - 2)
+ fraglen = HOST_LINK_BUF_SIZE - 2;
+ if ((count - fragpos) < fraglen)
+ fraglen = count - fragpos;
+
+ fragbuf[0] = connection_id;
+ fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+ status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
+ if (status) {
+ status = -EFAULT;
+ 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;
+ }
+
+ mutex_lock(&ca->slot_info[slot].slot_lock);
+ status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+ mutex_unlock(&ca->slot_info[slot].slot_lock);
+ 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;
+ size_t 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;
+
+ if (ca->slot_info[slot].rx_buffer.data == NULL) {
+ 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);
+ 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);
+ }
+
+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 = file->private_data;
+ struct dvb_ca_private *ca = 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", __func__);
+
+ /* 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);
+ 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_user(&ca->slot_info[slot].rx_buffer, idx, 2,
+ buf + pktlen, fraglen)) < 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;
+ status = copy_to_user(buf, hdr, 2);
+ if (status) {
+ status = -EFAULT;
+ goto exit;
+ }
+ status = pktlen;
+
+exit:
+ 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 = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ if (!try_module_get(ca->pub->owner))
+ return -EIO;
+
+ err = dvb_generic_open(inode, file);
+ if (err < 0) {
+ module_put(ca->pub->owner);
+ return err;
+ }
+
+ for (i = 0; i < ca->slot_count; i++) {
+
+ if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ if (ca->slot_info[i].rx_buffer.data != NULL) {
+ /* it is safe to call this here without locks because
+ * ca->open == 0. Data is not read in this case */
+ dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+ }
+ }
+ }
+
+ 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 = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ int err;
+
+ dprintk("%s\n", __func__);
+
+ /* 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 err;
+}
+
+
+/**
+ * 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 = file->private_data;
+ struct dvb_ca_private *ca = dvbdev->priv;
+ unsigned int mask = 0;
+ int slot;
+ int result = 0;
+
+ dprintk("%s\n", __func__);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+ 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) {
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+
+
+static const struct file_operations dvb_ca_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_ca_en50221_io_read,
+ .write = dvb_ca_en50221_io_write,
+ .unlocked_ioctl = dvb_ca_en50221_io_ioctl,
+ .open = dvb_ca_en50221_io_open,
+ .release = dvb_ca_en50221_io_release,
+ .poll = dvb_ca_en50221_io_poll,
+ .llseek = noop_llseek,
+};
+
+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", __func__);
+
+ if (slot_count < 1)
+ return -EINVAL;
+
+ /* initialise the system data */
+ if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ ca->pub = pubca;
+ ca->flags = flags;
+ ca->slot_count = slot_count;
+ if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ init_waitqueue_head(&ca->wait_queue);
+ 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;
+ mutex_init(&ca->slot_info[i].slot_lock);
+ }
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ goto error;
+ }
+ mb();
+
+ /* create a kthread for monitoring this CA device */
+ ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
+ ca->dvbdev->adapter->num, ca->dvbdev->id);
+ if (IS_ERR(ca->thread)) {
+ ret = PTR_ERR(ca->thread);
+ printk("dvb_ca_init: failed to start kernel_thread (%d)\n",
+ ret);
+ goto error;
+ }
+ 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 = pubca->private;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ /* shutdown the thread if there was one */
+ kthread_stop(ca->thread);
+
+ for (i = 0; i < ca->slot_count; i++) {
+ dvb_ca_en50221_slot_shutdown(ca, i);
+ vfree(ca->slot_info[i].rx_buffer.data);
+ }
+ kfree(ca->slot_info);
+ dvb_unregister_device(ca->dvbdev);
+ kfree(ca);
+ pubca->private = NULL;
+}
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h
new file mode 100644
index 000000000000..7df2e141187a
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ca_en50221.h
@@ -0,0 +1,136 @@
+/*
+ * 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 will be
+ * called for different slots concurrently and need to use locks where
+ * and if appropriate. There will be no concurrent access to one slot.
+ */
+
+ /* 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-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
new file mode 100644
index 000000000000..17cb81fd194c
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -0,0 +1,1318 @@
+/*
+ * 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/sched.h>
+#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 <asm/div64.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 int dvb_demux_tscheck;
+module_param(dvb_demux_tscheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_tscheck,
+ "enable transport stream continuity and TEI check");
+
+static int dvb_demux_speedcheck;
+module_param(dvb_demux_speedcheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_speedcheck,
+ "enable transport stream speed check");
+
+static int dvb_demux_feed_err_pkts = 1;
+module_param(dvb_demux_feed_err_pkts, int, 0644);
+MODULE_PARM_DESC(dvb_demux_feed_err_pkts,
+ "when set to 0, drop packets with the TEI bit set (1 by default)");
+
+#define dprintk_tscheck(x...) do { \
+ if (dvb_demux_tscheck && printk_ratelimit()) \
+ printk(x); \
+ } while (0)
+
+/******************************************************************************
+ * 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 = ((feed->cc + 1) & 0x0f) == cc;
+ feed->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_SECTION_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; /* redundant 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);
+ }
+
+ 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 */
+ dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
+ }
+
+ 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_DEMUX)) == TS_PACKET))
+
+static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+{
+ struct dvb_demux_feed *feed;
+ u16 pid = ts_pid(buf);
+ int dvr_done = 0;
+
+ if (dvb_demux_speedcheck) {
+ struct timespec cur_time, delta_time;
+ u64 speed_bytes, speed_timedelta;
+
+ demux->speed_pkts_cnt++;
+
+ /* show speed every SPEED_PKTS_INTERVAL packets */
+ if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
+ cur_time = current_kernel_time();
+
+ if (demux->speed_last_time.tv_sec != 0 &&
+ demux->speed_last_time.tv_nsec != 0) {
+ delta_time = timespec_sub(cur_time,
+ demux->speed_last_time);
+ speed_bytes = (u64)demux->speed_pkts_cnt
+ * 188 * 8;
+ /* convert to 1024 basis */
+ speed_bytes = 1000 * div64_u64(speed_bytes,
+ 1024);
+ speed_timedelta =
+ (u64)timespec_to_ns(&delta_time);
+ speed_timedelta = div64_u64(speed_timedelta,
+ 1000000); /* nsec -> usec */
+ printk(KERN_INFO "TS speed %llu Kbits/sec \n",
+ div64_u64(speed_bytes,
+ speed_timedelta));
+ };
+
+ demux->speed_last_time = cur_time;
+ demux->speed_pkts_cnt = 0;
+ };
+ };
+
+ if (buf[1] & 0x80) {
+ dprintk_tscheck("TEI detected. "
+ "PID=0x%x data1=0x%x\n",
+ pid, buf[1]);
+ /* data in this packet cant be trusted - drop it unless
+ * module option dvb_demux_feed_err_pkts is set */
+ if (!dvb_demux_feed_err_pkts)
+ return;
+ } else /* if TEI bit is set, pid may be wrong- skip pkt counter */
+ if (demux->cnt_storage && dvb_demux_tscheck) {
+ /* check pkt counter */
+ if (pid < MAX_PID) {
+ if ((buf[3] & 0xf) != demux->cnt_storage[pid])
+ dprintk_tscheck("TS packet counter mismatch. "
+ "PID=0x%x expected 0x%x "
+ "got 0x%x\n",
+ pid, demux->cnt_storage[pid],
+ buf[3] & 0xf);
+
+ demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf;
+ };
+ /* end check */
+ };
+
+ list_for_each_entry(feed, &demux->feed_list, 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);
+ else 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);
+
+static inline int find_next_packet(const u8 *buf, int pos, size_t count,
+ const int pktsize)
+{
+ int start = pos, lost;
+
+ while (pos < count) {
+ if (buf[pos] == 0x47 ||
+ (pktsize == 204 && buf[pos] == 0xB8))
+ break;
+ pos++;
+ }
+
+ lost = pos - start;
+ if (lost) {
+ /* This garbage is part of a valid packet? */
+ int backtrack = pos - pktsize;
+ if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
+ (pktsize == 204 && buf[backtrack] == 0xB8)))
+ return backtrack;
+ }
+
+ return pos;
+}
+
+/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
+static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
+ size_t count, const int pktsize)
+{
+ int p = 0, i, j;
+ const u8 *q;
+
+ spin_lock(&demux->lock);
+
+ if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
+ i = demux->tsbufp;
+ j = pktsize - i;
+ if (count < j) {
+ memcpy(&demux->tsbuf[i], buf, count);
+ demux->tsbufp += count;
+ goto bailout;
+ }
+ memcpy(&demux->tsbuf[i], buf, j);
+ if (demux->tsbuf[0] == 0x47) /* double check */
+ dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+ demux->tsbufp = 0;
+ p += j;
+ }
+
+ while (1) {
+ p = find_next_packet(buf, p, count, pktsize);
+ if (p >= count)
+ break;
+ if (count - p < pktsize)
+ break;
+
+ q = &buf[p];
+
+ if (pktsize == 204 && (*q == 0xB8)) {
+ memcpy(demux->tsbuf, q, 188);
+ demux->tsbuf[0] = 0x47;
+ q = demux->tsbuf;
+ }
+ dvb_dmx_swfilter_packet(demux, q);
+ p += pktsize;
+ }
+
+ i = count - p;
+ if (i) {
+ memcpy(demux->tsbuf, &buf[p], i);
+ demux->tsbufp = i;
+ if (pktsize == 204 && demux->tsbuf[0] == 0xB8)
+ demux->tsbuf[0] = 0x47;
+ }
+
+bailout:
+ spin_unlock(&demux->lock);
+}
+
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ _dvb_dmx_swfilter(demux, buf, count, 188);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter);
+
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ _dvb_dmx_swfilter(demux, buf, count, 204);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+
+void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ spin_lock(&demux->lock);
+
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
+
+ spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
+
+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",
+ __func__, 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",
+ __func__, 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 circular_buffer_size, 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 (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (ts_type & TS_DECODER) {
+ if (pes_type >= DMX_TS_PES_OTHER) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (demux->pesfilter[pes_type] &&
+ demux->pesfilter[pes_type] != feed) {
+ mutex_unlock(&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->timeout = timeout;
+ feed->ts_type = ts_type;
+ feed->pes_type = pes_type;
+
+ if (feed->buffer_size) {
+#ifdef NOBUFS
+ feed->buffer = NULL;
+#else
+ feed->buffer = vmalloc(feed->buffer_size);
+ if (!feed->buffer) {
+ mutex_unlock(&demux->mutex);
+ return -ENOMEM;
+ }
+#endif
+ }
+
+ feed->state = DMX_STATE_READY;
+ mutex_unlock(&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 (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->start_feed) {
+ mutex_unlock(&demux->mutex);
+ return -ENODEV;
+ }
+
+ if ((ret = demux->start_feed(feed)) < 0) {
+ mutex_unlock(&demux->mutex);
+ return ret;
+ }
+
+ spin_lock_irq(&demux->lock);
+ ts_feed->is_filtering = 1;
+ feed->state = DMX_STATE_GO;
+ spin_unlock_irq(&demux->lock);
+ mutex_unlock(&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;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state < DMX_STATE_GO) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->stop_feed) {
+ mutex_unlock(&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);
+ mutex_unlock(&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 (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (!(feed = dvb_dmx_feed_alloc(demux))) {
+ mutex_unlock(&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;
+ mutex_unlock(&demux->mutex);
+ return -EBUSY;
+ }
+
+ feed->filter->type = DMX_TYPE_TS;
+ feed->filter->feed = feed;
+ feed->filter->state = DMX_STATE_READY;
+
+ mutex_unlock(&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;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state == DMX_STATE_FREE) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ vfree(feed->buffer);
+ feed->buffer = NULL;
+#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;
+
+ mutex_unlock(&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 (mutex_lock_interruptible(&dvbdemux->mutex))
+ return -ERESTARTSYS;
+
+ dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
+ if (!dvbdmxfilter) {
+ mutex_unlock(&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);
+
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dmx_section_feed_set(struct dmx_section_feed *feed,
+ u16 pid, size_t circular_buffer_size,
+ 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 (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ dvb_demux_feed_add(dvbdmxfeed);
+
+ dvbdmxfeed->pid = pid;
+ dvbdmxfeed->buffer_size = circular_buffer_size;
+ dvbdmxfeed->feed.sec.check_crc = check_crc;
+
+#ifdef NOBUFS
+ dvbdmxfeed->buffer = NULL;
+#else
+ dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
+ if (!dvbdmxfeed->buffer) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -ENOMEM;
+ }
+#endif
+
+ dvbdmxfeed->state = DMX_STATE_READY;
+ mutex_unlock(&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 (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (feed->is_filtering) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ if (!dvbdmxfeed->filter) {
+ mutex_unlock(&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) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -ENODEV;
+ }
+
+ prepare_secfilters(dvbdmxfeed);
+
+ if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ spin_lock_irq(&dvbdmx->lock);
+ feed->is_filtering = 1;
+ dvbdmxfeed->state = DMX_STATE_GO;
+ spin_unlock_irq(&dvbdmx->lock);
+
+ mutex_unlock(&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;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (!dvbdmx->stop_feed) {
+ mutex_unlock(&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);
+
+ mutex_unlock(&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;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfilter->feed != dvbdmxfeed) {
+ mutex_unlock(&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);
+ mutex_unlock(&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 (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+ mutex_unlock(&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;
+
+ mutex_unlock(&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;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfeed->state == DMX_STATE_FREE) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ vfree(dvbdmxfeed->buffer);
+ dvbdmxfeed->buffer = NULL;
+#endif
+ dvbdmxfeed->state = DMX_STATE_FREE;
+
+ dvb_demux_feed_del(dvbdmxfeed);
+
+ dvbdmxfeed->pid = 0xffff;
+
+ mutex_unlock(&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 __user *buf, size_t count)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+ void *p;
+
+ if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+ return -EINVAL;
+
+ p = memdup_user(buf, count);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ if (mutex_lock_interruptible(&dvbdemux->mutex)) {
+ kfree(p);
+ return -ERESTARTSYS;
+ }
+ dvb_dmx_swfilter(dvbdemux, p, count);
+ kfree(p);
+ mutex_unlock(&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;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ demux->frontend = frontend;
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ demux->frontend = NULL;
+ mutex_unlock(&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;
+ struct dmx_demux *dmx = &dvbdemux->dmx;
+
+ dvbdemux->cnt_storage = NULL;
+ 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);
+ dvbdemux->filter = NULL;
+ 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->cnt_storage = vmalloc(MAX_PID + 1);
+ if (!dvbdemux->cnt_storage)
+ printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n");
+
+ INIT_LIST_HEAD(&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->priv = 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->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;
+
+ mutex_init(&dvbdemux->mutex);
+ spin_lock_init(&dvbdemux->lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dvb_dmx_init);
+
+void dvb_dmx_release(struct dvb_demux *dvbdemux)
+{
+ vfree(dvbdemux->cnt_storage);
+ vfree(dvbdemux->filter);
+ vfree(dvbdemux->feed);
+}
+
+EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h
new file mode 100644
index 000000000000..fa7188a253aa
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_demux.h
@@ -0,0 +1,151 @@
+/*
+ * 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 <linux/mutex.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
+
+#define MAX_PID 0x1fff
+
+#define SPEED_PKTS_INTERVAL 50000
+
+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;
+
+ u16 hw_handle;
+ struct timer_list timer;
+};
+
+#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;
+
+ struct timespec timeout;
+ struct dvb_demux_filter *filter;
+
+ 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;
+ unsigned 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 mutex mutex;
+ spinlock_t lock;
+
+ uint8_t *cnt_storage; /* for TS continuity check */
+
+ struct timespec speed_last_time; /* for TS speed check */
+ uint32_t speed_pkts_cnt; /* for TS speed check */
+};
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux);
+void 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);
+void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf,
+ size_t count);
+
+#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb-core/dvb_filter.c b/drivers/media/dvb-core/dvb_filter.c
new file mode 100644
index 000000000000..772003fb1821
--- /dev/null
+++ b/drivers/media/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-core/dvb_filter.h b/drivers/media/dvb-core/dvb_filter.h
new file mode 100644
index 000000000000..375e3be184b1
--- /dev/null
+++ b/drivers/media/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-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
new file mode 100644
index 000000000000..12e5eb1fff76
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -0,0 +1,2553 @@
+/*
+ * 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
+ */
+
+/* Enables DVBv3 compatibility bits at the headers */
+#define __DVB_CORE__
+
+#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/semaphore.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/freezer.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <asm/processor.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include <linux/dvb/version.h>
+
+static int dvb_frontend_debug;
+static int dvb_shutdown_timeout;
+static int dvb_force_auto_inversion;
+static int dvb_override_tune_delay;
+static int dvb_powerdown_on_sleep = 1;
+static int dvb_mfe_wait_time = 5;
+
+module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
+MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
+module_param(dvb_shutdown_timeout, int, 0644);
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
+module_param(dvb_force_auto_inversion, int, 0644);
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+module_param(dvb_override_tune_delay, int, 0644);
+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, 0644);
+MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
+module_param(dvb_mfe_wait_time, int, 0644);
+MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
+
+#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_ERROR 256
+#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)
+
+#define FE_ALGO_HW 1
+/*
+ * 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.
+ */
+
+#define DVB_FE_NO_EXIT 0
+#define DVB_FE_NORMAL_EXIT 1
+#define DVB_FE_DEVICE_REMOVED 2
+
+static DEFINE_MUTEX(frontend_mutex);
+
+struct dvb_frontend_private {
+
+ /* thread/frontend values */
+ struct dvb_device *dvbdev;
+ struct dvb_frontend_parameters parameters_out;
+ struct dvb_fe_events events;
+ struct semaphore sem;
+ struct list_head list_head;
+ wait_queue_head_t wait_queue;
+ struct task_struct *thread;
+ unsigned long release_jiffies;
+ unsigned int exit;
+ unsigned int wakeup;
+ fe_status_t status;
+ unsigned long tune_mode_flags;
+ unsigned int delay;
+ unsigned int reinitialise;
+ int tone;
+ int voltage;
+
+ /* swzigzag values */
+ unsigned int state;
+ unsigned int bending;
+ int lnb_drift;
+ unsigned int inversion;
+ unsigned int auto_step;
+ unsigned int auto_sub_step;
+ unsigned int started_auto_step;
+ unsigned int min_delay;
+ unsigned int max_drift;
+ unsigned int step_size;
+ int quality;
+ unsigned int check_wrapped;
+ enum dvbfe_search algo_status;
+};
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe);
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p_out);
+static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p);
+
+static bool has_get_frontend(struct dvb_frontend *fe)
+{
+ return fe->ops.get_frontend != NULL;
+}
+
+/*
+ * Due to DVBv3 API calls, a delivery system should be mapped into one of
+ * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC),
+ * otherwise, a DVBv3 call will fail.
+ */
+enum dvbv3_emulation_type {
+ DVBV3_UNKNOWN,
+ DVBV3_QPSK,
+ DVBV3_QAM,
+ DVBV3_OFDM,
+ DVBV3_ATSC,
+};
+
+static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
+{
+ switch (delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ return DVBV3_QAM;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ case SYS_DSS:
+ return DVBV3_QPSK;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ return DVBV3_OFDM;
+ case SYS_ATSC:
+ case SYS_ATSCMH:
+ case SYS_DVBC_ANNEX_B:
+ return DVBV3_ATSC;
+ case SYS_UNDEFINED:
+ case SYS_ISDBC:
+ case SYS_DVBH:
+ case SYS_DAB:
+ default:
+ /*
+ * Doesn't know how to emulate those types and/or
+ * there's no frontend driver from this type yet
+ * with some emulation code, so, we're not sure yet how
+ * to handle them, or they're not compatible with a DVBv3 call.
+ */
+ return DVBV3_UNKNOWN;
+ }
+}
+
+static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+ struct dvb_frontend_event *e;
+ int wp;
+
+ dprintk ("%s\n", __func__);
+
+ if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
+ dtv_get_frontend(fe, &fepriv->parameters_out);
+
+ mutex_lock(&events->mtx);
+
+ 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];
+ e->status = status;
+ e->parameters = fepriv->parameters_out;
+
+ events->eventw = wp;
+
+ mutex_unlock(&events->mtx);
+
+ 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 = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ dprintk ("%s\n", __func__);
+
+ 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;
+ }
+
+ mutex_lock(&events->mtx);
+ *event = events->events[events->eventr];
+ events->eventr = (events->eventr + 1) % MAX_EVENT;
+ mutex_unlock(&events->mtx);
+
+ return 0;
+}
+
+static void dvb_frontend_clear_events(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ mutex_lock(&events->mtx);
+ events->eventr = events->eventw;
+ mutex_unlock(&events->mtx);
+}
+
+static void dvb_frontend_init(struct dvb_frontend *fe)
+{
+ dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
+ fe->dvb->num,
+ fe->id,
+ fe->ops.info.name);
+
+ if (fe->ops.init)
+ fe->ops.init(fe);
+ if (fe->ops.tuner_ops.init) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.init(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+}
+
+void dvb_frontend_reinitialise(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ fepriv->reinitialise = 1;
+ dvb_frontend_wakeup(fe);
+}
+EXPORT_SYMBOL(dvb_frontend_reinitialise);
+
+static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)
+{
+ int q2;
+
+ dprintk ("%s\n", __func__);
+
+ if (locked)
+ (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
+ else
+ (fepriv->quality) = (fepriv->quality * 220 + 0) / 256;
+
+ q2 = fepriv->quality - 128;
+ q2 *= q2;
+
+ fepriv->delay = fepriv->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_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped)
+{
+ int autoinversion;
+ int ready = 0;
+ int fe_set_err = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+ int original_inversion = c->inversion;
+ u32 original_frequency = c->frequency;
+
+ /* are we using autoinversion? */
+ autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
+ (c->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",
+ __func__, fepriv->lnb_drift, fepriv->inversion,
+ fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step);
+
+ /* set the frontend itself */
+ c->frequency += fepriv->lnb_drift;
+ if (autoinversion)
+ c->inversion = fepriv->inversion;
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ fe_set_err = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (fe_set_err < 0) {
+ fepriv->state = FESTATE_ERROR;
+ return fe_set_err;
+ }
+
+ c->frequency = original_frequency;
+ c->inversion = original_inversion;
+
+ fepriv->auto_sub_step++;
+ return 0;
+}
+
+static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
+{
+ fe_status_t s = 0;
+ int retval = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+
+ /* if we've got no parameters, just keep idling */
+ if (fepriv->state & FESTATE_IDLE) {
+ fepriv->delay = 3*HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* in SCAN mode, we just set the frontend when asked and leave it alone */
+ if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
+ if (fepriv->state & FESTATE_RETUNE) {
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ retval = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (retval < 0)
+ fepriv->state = FESTATE_ERROR;
+ else
+ fepriv->state = FESTATE_TUNED;
+ }
+ fepriv->delay = 3*HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* 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)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, 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)) &&
+ (c->inversion == INVERSION_AUTO)) {
+ c->inversion = fepriv->inversion;
+ }
+ return;
+ }
+
+ /* if we are tuned already, check we're still locked */
+ if (fepriv->state & FESTATE_TUNED) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* we're tuned, and the lock is still good... */
+ if (s & FE_HAS_LOCK) {
+ return;
+ } else { /* if we _WERE_ tuned, but now don't have a lock */
+ fepriv->state = FESTATE_ZIGZAG_FAST;
+ fepriv->started_auto_step = fepriv->auto_step;
+ fepriv->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)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* 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) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* 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;
+ fepriv->check_wrapped = 0;
+ }
+
+ /* fast zigzag. */
+ if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
+ fepriv->delay = fepriv->min_delay;
+
+ /* perform a tune */
+ retval = dvb_frontend_swzigzag_autotune(fe,
+ fepriv->check_wrapped);
+ if (retval < 0) {
+ return;
+ } else if (retval) {
+ /* 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;
+ return;
+ }
+ fepriv->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) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* Note: don't bother checking for wrapping; we stay in this
+ * state until we get a lock */
+ dvb_frontend_swzigzag_autotune(fe, 0);
+ }
+}
+
+static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fepriv->exit != DVB_FE_NO_EXIT)
+ return 1;
+
+ if (fepriv->dvbdev->writers == 1)
+ if (time_after_eq(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 = 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 = fe->frontend_priv;
+
+ fepriv->wakeup = 1;
+ wake_up_interruptible(&fepriv->wait_queue);
+}
+
+static int dvb_frontend_thread(void *data)
+{
+ struct dvb_frontend *fe = data;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ fe_status_t s;
+ enum dvbfe_algo algo;
+
+ bool re_tune = false;
+
+ dprintk("%s\n", __func__);
+
+ fepriv->check_wrapped = 0;
+ fepriv->quality = 0;
+ fepriv->delay = 3*HZ;
+ fepriv->status = 0;
+ fepriv->wakeup = 0;
+ fepriv->reinitialise = 0;
+
+ dvb_frontend_init(fe);
+
+ set_freezable();
+ while (1) {
+ up(&fepriv->sem); /* is locked when we enter the thread... */
+restart:
+ wait_event_interruptible_timeout(fepriv->wait_queue,
+ dvb_frontend_should_wakeup(fe) || kthread_should_stop()
+ || freezing(current),
+ fepriv->delay);
+
+ if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
+ /* got signal or quitting */
+ fepriv->exit = DVB_FE_NORMAL_EXIT;
+ break;
+ }
+
+ if (try_to_freeze())
+ goto restart;
+
+ if (down_interruptible(&fepriv->sem))
+ break;
+
+ if (fepriv->reinitialise) {
+ dvb_frontend_init(fe);
+ if (fe->ops.set_tone && fepriv->tone != -1)
+ fe->ops.set_tone(fe, fepriv->tone);
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
+ fe->ops.set_voltage(fe, fepriv->voltage);
+ fepriv->reinitialise = 0;
+ }
+
+ /* do an iteration of the tuning loop */
+ if (fe->ops.get_frontend_algo) {
+ algo = fe->ops.get_frontend_algo(fe);
+ switch (algo) {
+ case DVBFE_ALGO_HW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+
+ if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
+ re_tune = true;
+ fepriv->state = FESTATE_TUNED;
+ } else {
+ re_tune = false;
+ }
+
+ if (fe->ops.tune)
+ fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+ dprintk("%s: state changed, adding current state\n", __func__);
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
+ break;
+ case DVBFE_ALGO_SW:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
+ dvb_frontend_swzigzag(fe);
+ break;
+ case DVBFE_ALGO_CUSTOM:
+ dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+ if (fepriv->state & FESTATE_RETUNE) {
+ dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__);
+ fepriv->state = FESTATE_TUNED;
+ }
+ /* Case where we are going to search for a carrier
+ * User asked us to retune again for some reason, possibly
+ * requesting a search with a new set of parameters
+ */
+ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+ if (fe->ops.search) {
+ fepriv->algo_status = fe->ops.search(fe);
+ /* We did do a search as was requested, the flags are
+ * now unset as well and has the flags wrt to search.
+ */
+ } else {
+ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+ }
+ }
+ /* Track the carrier if the search was successful */
+ if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) {
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ fepriv->delay = HZ / 2;
+ }
+ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s); /* update event list */
+ fepriv->status = s;
+ if (!(s & FE_HAS_LOCK)) {
+ fepriv->delay = HZ / 10;
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ } else {
+ fepriv->delay = 60 * HZ;
+ }
+ }
+ break;
+ default:
+ dprintk("%s: UNDEFINED ALGO !\n", __func__);
+ break;
+ }
+ } else {
+ dvb_frontend_swzigzag(fe);
+ }
+ }
+
+ if (dvb_powerdown_on_sleep) {
+ if (fe->ops.set_voltage)
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
+ if (fe->ops.tuner_ops.sleep) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.sleep(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ if (fe->ops.sleep)
+ fe->ops.sleep(fe);
+ }
+
+ fepriv->thread = NULL;
+ if (kthread_should_stop())
+ fepriv->exit = DVB_FE_DEVICE_REMOVED;
+ else
+ fepriv->exit = DVB_FE_NO_EXIT;
+ mb();
+
+ dvb_frontend_wakeup(fe);
+ return 0;
+}
+
+static void dvb_frontend_stop(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dprintk ("%s\n", __func__);
+
+ fepriv->exit = DVB_FE_NORMAL_EXIT;
+ mb();
+
+ if (!fepriv->thread)
+ return;
+
+ kthread_stop(fepriv->thread);
+
+ sema_init(&fepriv->sem, 1);
+ fepriv->state = FESTATE_IDLE;
+
+ /* paranoia check in case a signal arrived */
+ if (fepriv->thread)
+ printk("dvb_frontend_stop: warning: thread %p won't exit\n",
+ fepriv->thread);
+}
+
+s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime)
+{
+ return ((curtime.tv_usec < lasttime.tv_usec) ?
+ 1000000 - lasttime.tv_usec + curtime.tv_usec :
+ curtime.tv_usec - lasttime.tv_usec);
+}
+EXPORT_SYMBOL(timeval_usec_diff);
+
+static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec)
+{
+ curtime->tv_usec += add_usec;
+ if (curtime->tv_usec >= 1000000) {
+ curtime->tv_usec -= 1000000;
+ curtime->tv_sec++;
+ }
+}
+
+/*
+ * Sleep until gettimeofday() > waketime + add_usec
+ * This needs to be as precise as possible, but as the delay is
+ * usually between 2ms and 32ms, it is done using a scheduled msleep
+ * followed by usleep (normally a busy-wait loop) for the remainder
+ */
+void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec)
+{
+ struct timeval lasttime;
+ s32 delta, newdelta;
+
+ timeval_usec_add(waketime, add_usec);
+
+ do_gettimeofday(&lasttime);
+ delta = timeval_usec_diff(lasttime, *waketime);
+ if (delta > 2500) {
+ msleep((delta - 1500) / 1000);
+ do_gettimeofday(&lasttime);
+ newdelta = timeval_usec_diff(lasttime, *waketime);
+ delta = (newdelta > delta) ? 0 : newdelta;
+ }
+ if (delta > 0)
+ udelay(delta);
+}
+EXPORT_SYMBOL(dvb_frontend_sleep_until);
+
+static int dvb_frontend_start(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct task_struct *fe_thread;
+
+ dprintk ("%s\n", __func__);
+
+ if (fepriv->thread) {
+ if (fepriv->exit == DVB_FE_NO_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 = DVB_FE_NO_EXIT;
+ fepriv->thread = NULL;
+ mb();
+
+ fe_thread = kthread_run(dvb_frontend_thread, fe,
+ "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
+ if (IS_ERR(fe_thread)) {
+ ret = PTR_ERR(fe_thread);
+ printk("dvb_frontend_start: failed to start kthread (%d)\n", ret);
+ up(&fepriv->sem);
+ return ret;
+ }
+ fepriv->thread = fe_thread;
+ return 0;
+}
+
+static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
+ u32 *freq_min, u32 *freq_max)
+{
+ *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
+
+ if (fe->ops.info.frequency_max == 0)
+ *freq_max = fe->ops.tuner_ops.info.frequency_max;
+ else if (fe->ops.tuner_ops.info.frequency_max == 0)
+ *freq_max = fe->ops.info.frequency_max;
+ else
+ *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
+
+ if (*freq_min == 0 || *freq_max == 0)
+ printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+ fe->dvb->num,fe->id);
+}
+
+static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq_min;
+ u32 freq_max;
+
+ /* range check: frequency */
+ dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max);
+ if ((freq_min && c->frequency < freq_min) ||
+ (freq_max && c->frequency > freq_max)) {
+ printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->frequency, freq_min, freq_max);
+ return -EINVAL;
+ }
+
+ /* range check: symbol rate */
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if ((fe->ops.info.symbol_rate_min &&
+ c->symbol_rate < fe->ops.info.symbol_rate_min) ||
+ (fe->ops.info.symbol_rate_max &&
+ c->symbol_rate > fe->ops.info.symbol_rate_max)) {
+ printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->symbol_rate,
+ fe->ops.info.symbol_rate_min,
+ fe->ops.info.symbol_rate_max);
+ return -EINVAL;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i;
+ u32 delsys;
+
+ delsys = c->delivery_system;
+ memset(c, 0, sizeof(struct dtv_frontend_properties));
+ c->delivery_system = delsys;
+
+ c->state = DTV_CLEAR;
+
+ dprintk("%s() Clearing cache for delivery system %d\n", __func__,
+ c->delivery_system);
+
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+ c->bandwidth_hz = 0; /* AUTO */
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+ c->hierarchy = HIERARCHY_AUTO;
+ c->symbol_rate = 0;
+ c->code_rate_HP = FEC_AUTO;
+ c->code_rate_LP = FEC_AUTO;
+ c->fec_inner = FEC_AUTO;
+ c->rolloff = ROLLOFF_AUTO;
+ c->voltage = SEC_VOLTAGE_OFF;
+ c->sectone = SEC_TONE_OFF;
+ c->pilot = PILOT_AUTO;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 0;
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+
+ c->isdbs_ts_id = 0;
+ c->dvbt2_plp_id = 0;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ break;
+ case SYS_ATSC:
+ c->modulation = VSB_8;
+ break;
+ default:
+ c->modulation = QAM_AUTO;
+ break;
+ }
+
+ return 0;
+}
+
+#define _DTV_CMD(n, s, b) \
+[n] = { \
+ .name = #n, \
+ .cmd = n, \
+ .set = s,\
+ .buffer = b \
+}
+
+static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
+ _DTV_CMD(DTV_TUNE, 1, 0),
+ _DTV_CMD(DTV_CLEAR, 1, 0),
+
+ /* Set */
+ _DTV_CMD(DTV_FREQUENCY, 1, 0),
+ _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
+ _DTV_CMD(DTV_MODULATION, 1, 0),
+ _DTV_CMD(DTV_INVERSION, 1, 0),
+ _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
+ _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
+ _DTV_CMD(DTV_INNER_FEC, 1, 0),
+ _DTV_CMD(DTV_VOLTAGE, 1, 0),
+ _DTV_CMD(DTV_TONE, 1, 0),
+ _DTV_CMD(DTV_PILOT, 1, 0),
+ _DTV_CMD(DTV_ROLLOFF, 1, 0),
+ _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
+ _DTV_CMD(DTV_HIERARCHY, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
+ _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
+ _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
+ _DTV_CMD(DTV_INTERLEAVING, 1, 0),
+
+ _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
+
+ _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
+ _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0),
+
+ /* Get */
+ _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
+ _DTV_CMD(DTV_API_VERSION, 0, 0),
+ _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
+ _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
+ _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
+ _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
+ _DTV_CMD(DTV_HIERARCHY, 0, 0),
+ _DTV_CMD(DTV_INTERLEAVING, 0, 0),
+
+ _DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
+
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
+
+ _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
+};
+
+static void dtv_property_dump(struct dtv_property *tvp)
+{
+ int i;
+
+ if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
+ printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n",
+ __func__, tvp->cmd);
+ return;
+ }
+
+ dprintk("%s() tvp.cmd = 0x%08x (%s)\n"
+ ,__func__
+ ,tvp->cmd
+ ,dtv_cmds[ tvp->cmd ].name);
+
+ if(dtv_cmds[ tvp->cmd ].buffer) {
+
+ dprintk("%s() tvp.u.buffer.len = 0x%02x\n"
+ ,__func__
+ ,tvp->u.buffer.len);
+
+ for(i = 0; i < tvp->u.buffer.len; i++)
+ dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+ ,__func__
+ ,i
+ ,tvp->u.buffer.data[i]);
+
+ } else
+ dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data);
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+static int dtv_property_cache_sync(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
+ const struct dvb_frontend_parameters *p)
+{
+ c->frequency = p->frequency;
+ c->inversion = p->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ dprintk("%s() Preparing QPSK req\n", __func__);
+ c->symbol_rate = p->u.qpsk.symbol_rate;
+ c->fec_inner = p->u.qpsk.fec_inner;
+ break;
+ case DVBV3_QAM:
+ dprintk("%s() Preparing QAM req\n", __func__);
+ c->symbol_rate = p->u.qam.symbol_rate;
+ c->fec_inner = p->u.qam.fec_inner;
+ c->modulation = p->u.qam.modulation;
+ break;
+ case DVBV3_OFDM:
+ dprintk("%s() Preparing OFDM req\n", __func__);
+ switch (p->u.ofdm.bandwidth) {
+ case BANDWIDTH_10_MHZ:
+ c->bandwidth_hz = 10000000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ c->bandwidth_hz = 8000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ c->bandwidth_hz = 7000000;
+ break;
+ case BANDWIDTH_6_MHZ:
+ c->bandwidth_hz = 6000000;
+ break;
+ case BANDWIDTH_5_MHZ:
+ c->bandwidth_hz = 5000000;
+ break;
+ case BANDWIDTH_1_712_MHZ:
+ c->bandwidth_hz = 1712000;
+ break;
+ case BANDWIDTH_AUTO:
+ c->bandwidth_hz = 0;
+ }
+
+ c->code_rate_HP = p->u.ofdm.code_rate_HP;
+ c->code_rate_LP = p->u.ofdm.code_rate_LP;
+ c->modulation = p->u.ofdm.constellation;
+ c->transmission_mode = p->u.ofdm.transmission_mode;
+ c->guard_interval = p->u.ofdm.guard_interval;
+ c->hierarchy = p->u.ofdm.hierarchy_information;
+ break;
+ case DVBV3_ATSC:
+ dprintk("%s() Preparing ATSC req\n", __func__);
+ c->modulation = p->u.vsb.modulation;
+ if (c->delivery_system == SYS_ATSCMH)
+ break;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ case DVBV3_UNKNOWN:
+ printk(KERN_ERR
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ const struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_UNKNOWN:
+ printk(KERN_ERR
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ case DVBV3_QPSK:
+ dprintk("%s() Preparing QPSK req\n", __func__);
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ break;
+ case DVBV3_QAM:
+ dprintk("%s() Preparing QAM req\n", __func__);
+ p->u.qam.symbol_rate = c->symbol_rate;
+ p->u.qam.fec_inner = c->fec_inner;
+ p->u.qam.modulation = c->modulation;
+ break;
+ case DVBV3_OFDM:
+ dprintk("%s() Preparing OFDM req\n", __func__);
+
+ switch (c->bandwidth_hz) {
+ case 10000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ;
+ break;
+ case 8000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ case 7000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 6000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 5000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ;
+ break;
+ case 1712000:
+ p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ;
+ break;
+ case 0:
+ default:
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ }
+ p->u.ofdm.code_rate_HP = c->code_rate_HP;
+ p->u.ofdm.code_rate_LP = c->code_rate_LP;
+ p->u.ofdm.constellation = c->modulation;
+ p->u.ofdm.transmission_mode = c->transmission_mode;
+ p->u.ofdm.guard_interval = c->guard_interval;
+ p->u.ofdm.hierarchy_information = c->hierarchy;
+ break;
+ case DVBV3_ATSC:
+ dprintk("%s() Preparing VSB req\n", __func__);
+ p->u.vsb.modulation = c->modulation;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * dtv_get_frontend - calls a callback for retrieving DTV parameters
+ * @fe: struct dvb_frontend pointer
+ * @c: struct dtv_frontend_properties pointer (DVBv5 cache)
+ * @p_out struct dvb_frontend_parameters pointer (DVBv3 FE struct)
+ *
+ * This routine calls either the DVBv3 or DVBv5 get_frontend call.
+ * If c is not null, it will update the DVBv5 cache struct pointed by it.
+ * If p_out is not null, it will update the DVBv3 params pointed by it.
+ */
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p_out)
+{
+ int r;
+
+ if (fe->ops.get_frontend) {
+ r = fe->ops.get_frontend(fe);
+ if (unlikely(r < 0))
+ return r;
+ if (p_out)
+ dtv_property_legacy_params_sync(fe, p_out);
+ return 0;
+ }
+
+ /* As everything is in cache, get_frontend fops are always supported */
+ return 0;
+}
+
+static int dvb_frontend_ioctl_legacy(struct file *file,
+ unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct file *file,
+ unsigned int cmd, void *parg);
+
+static int dtv_property_process_get(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dtv_property *tvp,
+ struct file *file)
+{
+ int r, ncaps;
+
+ switch(tvp->cmd) {
+ case DTV_ENUM_DELSYS:
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+ tvp->u.buffer.len = ncaps;
+ break;
+ case DTV_FREQUENCY:
+ tvp->u.data = c->frequency;
+ break;
+ case DTV_MODULATION:
+ tvp->u.data = c->modulation;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ tvp->u.data = c->bandwidth_hz;
+ break;
+ case DTV_INVERSION:
+ tvp->u.data = c->inversion;
+ break;
+ case DTV_SYMBOL_RATE:
+ tvp->u.data = c->symbol_rate;
+ break;
+ case DTV_INNER_FEC:
+ tvp->u.data = c->fec_inner;
+ break;
+ case DTV_PILOT:
+ tvp->u.data = c->pilot;
+ break;
+ case DTV_ROLLOFF:
+ tvp->u.data = c->rolloff;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ tvp->u.data = c->delivery_system;
+ break;
+ case DTV_VOLTAGE:
+ tvp->u.data = c->voltage;
+ break;
+ case DTV_TONE:
+ tvp->u.data = c->sectone;
+ break;
+ case DTV_API_VERSION:
+ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+ break;
+ case DTV_CODE_RATE_HP:
+ tvp->u.data = c->code_rate_HP;
+ break;
+ case DTV_CODE_RATE_LP:
+ tvp->u.data = c->code_rate_LP;
+ break;
+ case DTV_GUARD_INTERVAL:
+ tvp->u.data = c->guard_interval;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ tvp->u.data = c->transmission_mode;
+ break;
+ case DTV_HIERARCHY:
+ tvp->u.data = c->hierarchy;
+ break;
+ case DTV_INTERLEAVING:
+ tvp->u.data = c->interleaving;
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ tvp->u.data = c->isdbt_partial_reception;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ tvp->u.data = c->isdbt_sb_mode;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ tvp->u.data = c->isdbt_sb_subchannel;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ tvp->u.data = c->isdbt_sb_segment_idx;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ tvp->u.data = c->isdbt_sb_segment_count;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ tvp->u.data = c->isdbt_layer_enabled;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ tvp->u.data = c->layer[0].fec;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ tvp->u.data = c->layer[0].modulation;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ tvp->u.data = c->layer[0].segment_count;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[0].interleaving;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ tvp->u.data = c->layer[1].fec;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ tvp->u.data = c->layer[1].modulation;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ tvp->u.data = c->layer[1].segment_count;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[1].interleaving;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ tvp->u.data = c->layer[2].fec;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ tvp->u.data = c->layer[2].modulation;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ tvp->u.data = c->layer[2].segment_count;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[2].interleaving;
+ break;
+ case DTV_ISDBS_TS_ID:
+ tvp->u.data = c->isdbs_ts_id;
+ break;
+ case DTV_DVBT2_PLP_ID:
+ tvp->u.data = c->dvbt2_plp_id;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_FIC_VER:
+ tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
+ break;
+ case DTV_ATSCMH_PARADE_ID:
+ tvp->u.data = fe->dtv_property_cache.atscmh_parade_id;
+ break;
+ case DTV_ATSCMH_NOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_nog;
+ break;
+ case DTV_ATSCMH_TNOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_tnog;
+ break;
+ case DTV_ATSCMH_SGN:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sgn;
+ break;
+ case DTV_ATSCMH_PRC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_prc;
+ break;
+ case DTV_ATSCMH_RS_FRAME_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_PRI:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_SEC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec;
+ break;
+ case DTV_ATSCMH_SCCC_BLOCK_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_A:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_B:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_C:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_D:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Allow the frontend to override outgoing properties */
+ if (fe->ops.get_property) {
+ r = fe->ops.get_property(fe, tvp);
+ if (r < 0)
+ return r;
+ }
+
+ dtv_property_dump(tvp);
+
+ return 0;
+}
+
+static int dtv_set_frontend(struct dvb_frontend *fe);
+
+static bool is_dvbv3_delsys(u32 delsys)
+{
+ bool status;
+
+ status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) ||
+ (delsys == SYS_DVBS) || (delsys == SYS_ATSC);
+
+ return status;
+}
+
+static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
+{
+ int ncaps, i;
+ u32 delsys = SYS_UNDEFINED;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ enum dvbv3_emulation_type type;
+
+ /*
+ * It was reported that some old DVBv5 applications were
+ * filling delivery_system with SYS_UNDEFINED. If this happens,
+ * assume that the application wants to use the first supported
+ * delivery system.
+ */
+ if (c->delivery_system == SYS_UNDEFINED)
+ c->delivery_system = fe->ops.delsys[0];
+
+ if (desired_system == SYS_UNDEFINED) {
+ /*
+ * A DVBv3 call doesn't know what's the desired system.
+ * Also, DVBv3 applications don't know that ops.info->type
+ * could be changed, and they simply dies when it doesn't
+ * match.
+ * So, don't change the current delivery system, as it
+ * may be trying to do the wrong thing, like setting an
+ * ISDB-T frontend as DVB-T. Instead, find the closest
+ * DVBv3 system that matches the delivery system.
+ */
+ if (is_dvbv3_delsys(c->delivery_system)) {
+ dprintk("%s() Using delivery system to %d\n",
+ __func__, c->delivery_system);
+ return 0;
+ }
+ type = dvbv3_type(c->delivery_system);
+ switch (type) {
+ case DVBV3_QPSK:
+ desired_system = SYS_DVBS;
+ break;
+ case DVBV3_QAM:
+ desired_system = SYS_DVBC_ANNEX_A;
+ break;
+ case DVBV3_ATSC:
+ desired_system = SYS_ATSC;
+ break;
+ case DVBV3_OFDM:
+ desired_system = SYS_DVBT;
+ break;
+ default:
+ dprintk("%s(): This frontend doesn't support DVBv3 calls\n",
+ __func__);
+ return -EINVAL;
+ }
+ /*
+ * Get a delivery system that is compatible with DVBv3
+ * NOTE: in order for this to work with softwares like Kaffeine that
+ * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
+ * DVB-S, drivers that support both should put the SYS_DVBS entry
+ * before the SYS_DVBS2, otherwise it won't switch back to DVB-S.
+ * The real fix is that userspace applications should not use DVBv3
+ * and not trust on calling FE_SET_FRONTEND to switch the delivery
+ * system.
+ */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if (fe->ops.delsys[ncaps] == desired_system) {
+ delsys = desired_system;
+ break;
+ }
+ ncaps++;
+ }
+ if (delsys == SYS_UNDEFINED) {
+ dprintk("%s() Couldn't find a delivery system that matches %d\n",
+ __func__, desired_system);
+ }
+ } else {
+ /*
+ * This is a DVBv5 call. So, it likely knows the supported
+ * delivery systems.
+ */
+
+ /* Check if the desired delivery system is supported */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if (fe->ops.delsys[ncaps] == desired_system) {
+ c->delivery_system = desired_system;
+ dprintk("%s() Changing delivery system to %d\n",
+ __func__, desired_system);
+ return 0;
+ }
+ ncaps++;
+ }
+ type = dvbv3_type(desired_system);
+
+ /*
+ * The delivery system is not supported. See if it can be
+ * emulated.
+ * The emulation only works if the desired system is one of the
+ * DVBv3 delivery systems
+ */
+ if (!is_dvbv3_delsys(desired_system)) {
+ dprintk("%s() can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Get the last non-DVBv3 delivery system that has the same type
+ * of the desired system
+ */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) &&
+ !is_dvbv3_delsys(fe->ops.delsys[ncaps]))
+ delsys = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+ /* There's nothing compatible with the desired delivery system */
+ if (delsys == SYS_UNDEFINED) {
+ dprintk("%s() Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ c->delivery_system = delsys;
+
+ /*
+ * The DVBv3 or DVBv5 call is requesting a different system. So,
+ * emulation is needed.
+ *
+ * Emulate newer delivery systems like ISDBT, DVBT and DTMB
+ * for older DVBv5 applications. The emulation will try to use
+ * the auto mode for most things, and will assume that the desired
+ * delivery system is the last one at the ops.delsys[] array
+ */
+ dprintk("%s() Using delivery system %d emulated as if it were a %d\n",
+ __func__, delsys, desired_system);
+
+ /*
+ * For now, handles ISDB-T calls. More code may be needed here for the
+ * other emulated stuff
+ */
+ if (type == DVBV3_OFDM) {
+ if (c->delivery_system == SYS_ISDBT) {
+ dprintk("%s() Using defaults for SYS_ISDBT\n",
+ __func__);
+ if (!c->bandwidth_hz)
+ c->bandwidth_hz = 6000000;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 0;
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+ }
+ }
+ dprintk("change delivery system on cache to %d\n", c->delivery_system);
+
+ return 0;
+}
+
+static int dtv_property_process_set(struct dvb_frontend *fe,
+ struct dtv_property *tvp,
+ struct file *file)
+{
+ int r = 0;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.set_property) {
+ r = fe->ops.set_property(fe, tvp);
+ if (r < 0)
+ return r;
+ }
+
+ switch(tvp->cmd) {
+ case DTV_CLEAR:
+ /*
+ * Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ dvb_frontend_clear_cache(fe);
+ break;
+ case DTV_TUNE:
+ /* interpret the cache of data, build either a traditional frontend
+ * tunerequest so we can pass validation in the FE_SET_FRONTEND
+ * ioctl.
+ */
+ c->state = tvp->cmd;
+ dprintk("%s() Finalised property cache\n", __func__);
+
+ r = dtv_set_frontend(fe);
+ break;
+ case DTV_FREQUENCY:
+ c->frequency = tvp->u.data;
+ break;
+ case DTV_MODULATION:
+ c->modulation = tvp->u.data;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ c->bandwidth_hz = tvp->u.data;
+ break;
+ case DTV_INVERSION:
+ c->inversion = tvp->u.data;
+ break;
+ case DTV_SYMBOL_RATE:
+ c->symbol_rate = tvp->u.data;
+ break;
+ case DTV_INNER_FEC:
+ c->fec_inner = tvp->u.data;
+ break;
+ case DTV_PILOT:
+ c->pilot = tvp->u.data;
+ break;
+ case DTV_ROLLOFF:
+ c->rolloff = tvp->u.data;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ r = set_delivery_system(fe, tvp->u.data);
+ break;
+ case DTV_VOLTAGE:
+ c->voltage = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(file, FE_SET_VOLTAGE,
+ (void *)c->voltage);
+ break;
+ case DTV_TONE:
+ c->sectone = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(file, FE_SET_TONE,
+ (void *)c->sectone);
+ break;
+ case DTV_CODE_RATE_HP:
+ c->code_rate_HP = tvp->u.data;
+ break;
+ case DTV_CODE_RATE_LP:
+ c->code_rate_LP = tvp->u.data;
+ break;
+ case DTV_GUARD_INTERVAL:
+ c->guard_interval = tvp->u.data;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ c->transmission_mode = tvp->u.data;
+ break;
+ case DTV_HIERARCHY:
+ c->hierarchy = tvp->u.data;
+ break;
+ case DTV_INTERLEAVING:
+ c->interleaving = tvp->u.data;
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ c->isdbt_partial_reception = tvp->u.data;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ c->isdbt_sb_mode = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ c->isdbt_sb_subchannel = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ c->isdbt_sb_segment_idx = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ c->isdbt_sb_segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ c->isdbt_layer_enabled = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ c->layer[0].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ c->layer[0].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ c->layer[0].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ c->layer[0].interleaving = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ c->layer[1].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ c->layer[1].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ c->layer[1].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ c->layer[1].interleaving = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ c->layer[2].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ c->layer[2].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ c->layer[2].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ c->layer[2].interleaving = tvp->u.data;
+ break;
+ case DTV_ISDBS_TS_ID:
+ c->isdbs_ts_id = tvp->u.data;
+ break;
+ case DTV_DVBT2_PLP_ID:
+ c->dvbt2_plp_id = tvp->u.data;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_PARADE_ID:
+ fe->dtv_property_cache.atscmh_parade_id = tvp->u.data;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static int dvb_frontend_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err = -EOPNOTSUPP;
+
+ dprintk("%s (%d)\n", __func__, _IOC_NR(cmd));
+
+ if (fepriv->exit != DVB_FE_NO_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;
+
+ if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+ err = dvb_frontend_ioctl_properties(file, cmd, parg);
+ else {
+ c->state = DTV_UNDEFINED;
+ err = dvb_frontend_ioctl_legacy(file, cmd, parg);
+ }
+
+ up(&fepriv->sem);
+ return err;
+}
+
+static int dvb_frontend_ioctl_properties(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 = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int err = 0;
+
+ struct dtv_properties *tvps = NULL;
+ struct dtv_property *tvp = NULL;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ if(cmd == FE_SET_PROPERTY) {
+ tvps = (struct dtv_properties __user *)parg;
+
+ dprintk("%s() properties.num = %d\n", __func__, tvps->num);
+ dprintk("%s() properties.props = %p\n", __func__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_set(fe, tvp + i, file);
+ if (err < 0)
+ goto out;
+ (tvp + i)->result = err;
+ }
+
+ if (c->state == DTV_TUNE)
+ dprintk("%s() Property cache is full, tuning\n", __func__);
+
+ } else
+ if(cmd == FE_GET_PROPERTY) {
+ tvps = (struct dtv_properties __user *)parg;
+
+ dprintk("%s() properties.num = %d\n", __func__, tvps->num);
+ dprintk("%s() properties.props = %p\n", __func__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ /*
+ * Fills the cache out struct with the cache contents, plus
+ * the data retrieved from get_frontend, if the frontend
+ * is not idle. Otherwise, returns the cached content
+ */
+ if (fepriv->state != FESTATE_IDLE) {
+ err = dtv_get_frontend(fe, NULL);
+ if (err < 0)
+ goto out;
+ }
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_get(fe, c, tvp + i, file);
+ if (err < 0)
+ goto out;
+ (tvp + i)->result = err;
+ }
+
+ if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ } else
+ err = -EOPNOTSUPP;
+
+out:
+ kfree(tvp);
+ return err;
+}
+
+static int dtv_set_frontend(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_tune_settings fetunesettings;
+ u32 rolloff = 0;
+
+ if (dvb_frontend_check_parameters(fe) < 0)
+ return -EINVAL;
+
+ /*
+ * Initialize output parameters to match the values given by
+ * the user. FE_SET_FRONTEND triggers an initial frontend event
+ * with status = 0, which copies output parameters to userspace.
+ */
+ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+
+ /*
+ * Be sure that the bandwidth will be filled for all
+ * non-satellite systems, as tuners need to know what
+ * low pass/Nyquist half filter should be applied, in
+ * order to avoid inter-channel noise.
+ *
+ * ISDB-T and DVB-T/T2 already sets bandwidth.
+ * ATSC and DVB-C don't set, so, the core should fill it.
+ *
+ * On DVB-C Annex A and C, the bandwidth is a function of
+ * the roll-off and symbol rate. Annex B defines different
+ * roll-off factors depending on the modulation. Fortunately,
+ * Annex B is only used with 6MHz, so there's no need to
+ * calculate it.
+ *
+ * While not officially supported, a side effect of handling it at
+ * the cache level is that a program could retrieve the bandwidth
+ * via DTV_BANDWIDTH_HZ, which may be useful for test programs.
+ */
+ switch (c->delivery_system) {
+ case SYS_ATSC:
+ case SYS_DVBC_ANNEX_B:
+ c->bandwidth_hz = 6000000;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ rolloff = 115;
+ break;
+ case SYS_DVBC_ANNEX_C:
+ rolloff = 113;
+ break;
+ default:
+ break;
+ }
+ if (rolloff)
+ c->bandwidth_hz = (c->symbol_rate * rolloff) / 100;
+
+ /* force auto frequency inversion if requested */
+ if (dvb_force_auto_inversion)
+ c->inversion = INVERSION_AUTO;
+
+ /*
+ * without hierarchical coding code_rate_LP is irrelevant,
+ * so we tolerate the otherwise invalid FEC_NONE setting
+ */
+ if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE)
+ c->code_rate_LP = FEC_AUTO;
+
+ /* get frontend-specific tuning settings */
+ memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_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 (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_ISDBS:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = c->symbol_rate / 16000;
+ fepriv->max_drift = c->symbol_rate / 2000;
+ break;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ 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;
+ default:
+ /*
+ * FIXME: This sounds wrong! if freqency_stepsize is
+ * defined by the frontend, why not use it???
+ */
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = 0; /* no zigzag */
+ fepriv->max_drift = 0;
+ break;
+ }
+ }
+ if (dvb_override_tune_delay > 0)
+ fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+
+ fepriv->state = FESTATE_RETUNE;
+
+ /* Request the search algorithm to search */
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
+ dvb_frontend_clear_events(fe);
+ dvb_frontend_add_event(fe, 0);
+ dvb_frontend_wakeup(fe);
+ fepriv->status = 0;
+
+ return 0;
+}
+
+
+static int dvb_frontend_ioctl_legacy(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 = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int err = -EOPNOTSUPP;
+
+ switch (cmd) {
+ case FE_GET_INFO: {
+ struct dvb_frontend_info* info = parg;
+
+ memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info));
+ dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max);
+
+ /*
+ * Associate the 4 delivery systems supported by DVBv3
+ * API with their DVBv5 counterpart. For the other standards,
+ * use the closest type, assuming that it would hopefully
+ * work with a DVBv3 application.
+ * It should be noticed that, on multi-frontend devices with
+ * different types (terrestrial and cable, for example),
+ * a pure DVBv3 application won't be able to use all delivery
+ * systems. Yet, changing the DVBv5 cache to the other delivery
+ * system should be enough for making it work.
+ */
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ info->type = FE_QPSK;
+ break;
+ case DVBV3_ATSC:
+ info->type = FE_ATSC;
+ break;
+ case DVBV3_QAM:
+ info->type = FE_QAM;
+ break;
+ case DVBV3_OFDM:
+ info->type = FE_OFDM;
+ break;
+ default:
+ printk(KERN_ERR
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ fe->ops.info.type = FE_OFDM;
+ }
+ dprintk("current delivery system on cache: %d, V3 type: %d\n",
+ c->delivery_system, fe->ops.info.type);
+
+ /* 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: {
+ fe_status_t* status = parg;
+
+ /* if retune was requested but hasn't occurred yet, prevent
+ * that user get signal state from previous tuning */
+ if (fepriv->state == FESTATE_RETUNE ||
+ fepriv->state == FESTATE_ERROR) {
+ err=0;
+ *status = 0;
+ break;
+ }
+
+ if (fe->ops.read_status)
+ err = fe->ops.read_status(fe, status);
+ 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->tone = (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->voltage = (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 long) parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ } else if (fe->ops.set_voltage) {
+ /*
+ * NOTE: This is a fallback condition. Some frontends
+ * (stv0299 for instance) take longer than 8msec to
+ * respond to a set_voltage command. Those switches
+ * need custom routines to switch properly. For all
+ * other frontends, the following should work ok.
+ * Dish network legacy switches (as used by Dish500)
+ * are controlled by sending 9-bit command words
+ * spaced 8msec apart.
+ * the actual command word is switch/port dependent
+ * so it is up to the userspace application to send
+ * the right command.
+ * The command must always start with a '0' after
+ * initialization, so parg is 8 bits and does not
+ * include the initialization or start bit
+ */
+ unsigned long swcmd = ((unsigned long) parg) << 1;
+ struct timeval nexttime;
+ struct timeval tv[10];
+ int i;
+ u8 last = 1;
+ if (dvb_frontend_debug)
+ printk("%s switch command: 0x%04lx\n", __func__, swcmd);
+ do_gettimeofday(&nexttime);
+ if (dvb_frontend_debug)
+ memcpy(&tv[0], &nexttime, sizeof(struct timeval));
+ /* before sending a command, initialize by sending
+ * a 32ms 18V to the switch
+ */
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_18);
+ dvb_frontend_sleep_until(&nexttime, 32000);
+
+ for (i = 0; i < 9; i++) {
+ if (dvb_frontend_debug)
+ do_gettimeofday(&tv[i + 1]);
+ if ((swcmd & 0x01) != last) {
+ /* set voltage to (last ? 13V : 18V) */
+ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
+ last = (last) ? 0 : 1;
+ }
+ swcmd = swcmd >> 1;
+ if (i != 8)
+ dvb_frontend_sleep_until(&nexttime, 8000);
+ }
+ if (dvb_frontend_debug) {
+ printk("%s(%d): switch delay (should be 32k followed by all 8k\n",
+ __func__, fe->dvb->num);
+ for (i = 1; i < 10; i++)
+ printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i]));
+ }
+ err = 0;
+ 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, (long) parg);
+ break;
+
+ case FE_SET_FRONTEND:
+ err = set_delivery_system(fe, SYS_UNDEFINED);
+ if (err)
+ break;
+
+ err = dtv_property_cache_sync(fe, c, parg);
+ if (err)
+ break;
+ err = dtv_set_frontend(fe);
+ break;
+ case FE_GET_EVENT:
+ err = dvb_frontend_get_event (fe, parg, file->f_flags);
+ break;
+
+ case FE_GET_FRONTEND:
+ err = dtv_get_frontend(fe, parg);
+ break;
+
+ case FE_SET_FRONTEND_TUNE_MODE:
+ fepriv->tune_mode_flags = (unsigned long) parg;
+ err = 0;
+ break;
+ };
+
+ 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 = fe->frontend_priv;
+
+ dprintk ("%s\n", __func__);
+
+ 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 = fe->frontend_priv;
+ struct dvb_adapter *adapter = fe->dvb;
+ int ret;
+
+ dprintk ("%s\n", __func__);
+ if (fepriv->exit == DVB_FE_DEVICE_REMOVED)
+ return -ENODEV;
+
+ if (adapter->mfe_shared) {
+ mutex_lock (&adapter->mfe_lock);
+
+ if (adapter->mfe_dvbdev == NULL)
+ adapter->mfe_dvbdev = dvbdev;
+
+ else if (adapter->mfe_dvbdev != dvbdev) {
+ struct dvb_device
+ *mfedev = adapter->mfe_dvbdev;
+ struct dvb_frontend
+ *mfe = mfedev->priv;
+ struct dvb_frontend_private
+ *mfepriv = mfe->frontend_priv;
+ int mferetry = (dvb_mfe_wait_time << 1);
+
+ mutex_unlock (&adapter->mfe_lock);
+ while (mferetry-- && (mfedev->users != -1 ||
+ mfepriv->thread != NULL)) {
+ if(msleep_interruptible(500)) {
+ if(signal_pending(current))
+ return -EINTR;
+ }
+ }
+
+ mutex_lock (&adapter->mfe_lock);
+ if(adapter->mfe_dvbdev != dvbdev) {
+ mfedev = adapter->mfe_dvbdev;
+ mfe = mfedev->priv;
+ mfepriv = mfe->frontend_priv;
+ if (mfedev->users != -1 ||
+ mfepriv->thread != NULL) {
+ mutex_unlock (&adapter->mfe_lock);
+ return -EBUSY;
+ }
+ adapter->mfe_dvbdev = dvbdev;
+ }
+ }
+ }
+
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
+ if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
+ goto err0;
+
+ /* If we took control of the bus, we need to force
+ reinitialization. This is because many ts_bus_ctrl()
+ functions strobe the RESET pin on the demod, and if the
+ frontend thread already exists then the dvb_init() routine
+ won't get called (which is what usually does initial
+ register configuration). */
+ fepriv->reinitialise = 1;
+ }
+
+ if ((ret = dvb_generic_open (inode, file)) < 0)
+ goto err1;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ /* normal tune mode when opened R/W */
+ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT;
+ fepriv->tone = -1;
+ fepriv->voltage = -1;
+
+ ret = dvb_frontend_start (fe);
+ if (ret)
+ goto err2;
+
+ /* empty event queue */
+ fepriv->events.eventr = fepriv->events.eventw = 0;
+ }
+
+ if (adapter->mfe_shared)
+ mutex_unlock (&adapter->mfe_lock);
+ return ret;
+
+err2:
+ dvb_generic_release(inode, file);
+err1:
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+err0:
+ if (adapter->mfe_shared)
+ mutex_unlock (&adapter->mfe_lock);
+ 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 = fe->frontend_priv;
+ int ret;
+
+ dprintk ("%s\n", __func__);
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ fepriv->release_jiffies = jiffies;
+ mb();
+ }
+
+ ret = dvb_generic_release (inode, file);
+
+ if (dvbdev->users == -1) {
+ wake_up(&fepriv->wait_queue);
+ if (fepriv->exit != DVB_FE_NO_EXIT) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ wake_up(&dvbdev->wait_queue);
+ }
+ if (fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+ }
+
+ return ret;
+}
+
+static const struct file_operations dvb_frontend_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dvb_generic_ioctl,
+ .poll = dvb_frontend_poll,
+ .open = dvb_frontend_open,
+ .release = dvb_frontend_release,
+ .llseek = noop_llseek,
+};
+
+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", __func__);
+
+ if (mutex_lock_interruptible(&frontend_mutex))
+ return -ERESTARTSYS;
+
+ fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
+ if (fe->frontend_priv == NULL) {
+ mutex_unlock(&frontend_mutex);
+ return -ENOMEM;
+ }
+ fepriv = fe->frontend_priv;
+
+ sema_init(&fepriv->sem, 1);
+ init_waitqueue_head (&fepriv->wait_queue);
+ init_waitqueue_head (&fepriv->events.wait_queue);
+ mutex_init(&fepriv->events.mtx);
+ fe->dvb = dvb;
+ fepriv->inversion = INVERSION_OFF;
+
+ printk ("DVB: registering adapter %i frontend %i (%s)...\n",
+ fe->dvb->num,
+ fe->id,
+ fe->ops.info.name);
+
+ dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+ fe, DVB_DEVICE_FRONTEND);
+
+ /*
+ * Initialize the cache to the proper values according with the
+ * first supported delivery system (ops->delsys[0])
+ */
+
+ fe->dtv_property_cache.delivery_system = fe->ops.delsys[0];
+ dvb_frontend_clear_cache(fe);
+
+ mutex_unlock(&frontend_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_frontend);
+
+int dvb_unregister_frontend(struct dvb_frontend* fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ dprintk ("%s\n", __func__);
+
+ mutex_lock(&frontend_mutex);
+ dvb_frontend_stop (fe);
+ mutex_unlock(&frontend_mutex);
+
+ if (fepriv->dvbdev->users < -1)
+ wait_event(fepriv->dvbdev->wait_queue,
+ fepriv->dvbdev->users==-1);
+
+ mutex_lock(&frontend_mutex);
+ dvb_unregister_device (fepriv->dvbdev);
+
+ /* fe is invalid now */
+ kfree(fepriv);
+ mutex_unlock(&frontend_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_frontend);
+
+#ifdef CONFIG_MEDIA_ATTACH
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ void *ptr;
+
+ if (fe->ops.release_sec) {
+ fe->ops.release_sec(fe);
+ symbol_put_addr(fe->ops.release_sec);
+ }
+ if (fe->ops.tuner_ops.release) {
+ fe->ops.tuner_ops.release(fe);
+ symbol_put_addr(fe->ops.tuner_ops.release);
+ }
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
+ ptr = (void*)fe->ops.release;
+ if (ptr) {
+ fe->ops.release(fe);
+ symbol_put_addr(ptr);
+ }
+}
+#else
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ if (fe->ops.release_sec)
+ fe->ops.release_sec(fe);
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
+ if (fe->ops.release)
+ fe->ops.release(fe);
+}
+#endif
+EXPORT_SYMBOL(dvb_frontend_detach);
diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h
new file mode 100644
index 000000000000..de410cc94fbb
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_frontend.h
@@ -0,0 +1,425 @@
+/*
+ * 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/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dvbdev.h"
+
+/*
+ * Maximum number of Delivery systems per frontend. It
+ * should be smaller or equal to 32
+ */
+#define MAX_DELSYS 8
+
+struct dvb_frontend_tune_settings {
+ int min_delay_ms;
+ int step_size;
+ int max_drift;
+};
+
+struct dvb_frontend;
+
+struct dvb_tuner_info {
+ char name[128];
+
+ u32 frequency_min;
+ u32 frequency_max;
+ u32 frequency_step;
+
+ u32 bandwidth_min;
+ u32 bandwidth_max;
+ u32 bandwidth_step;
+};
+
+struct analog_parameters {
+ unsigned int frequency;
+ unsigned int mode;
+ unsigned int audmode;
+ u64 std;
+};
+
+enum dvbfe_modcod {
+ DVBFE_MODCOD_DUMMY_PLFRAME = 0,
+ DVBFE_MODCOD_QPSK_1_4,
+ DVBFE_MODCOD_QPSK_1_3,
+ DVBFE_MODCOD_QPSK_2_5,
+ DVBFE_MODCOD_QPSK_1_2,
+ DVBFE_MODCOD_QPSK_3_5,
+ DVBFE_MODCOD_QPSK_2_3,
+ DVBFE_MODCOD_QPSK_3_4,
+ DVBFE_MODCOD_QPSK_4_5,
+ DVBFE_MODCOD_QPSK_5_6,
+ DVBFE_MODCOD_QPSK_8_9,
+ DVBFE_MODCOD_QPSK_9_10,
+ DVBFE_MODCOD_8PSK_3_5,
+ DVBFE_MODCOD_8PSK_2_3,
+ DVBFE_MODCOD_8PSK_3_4,
+ DVBFE_MODCOD_8PSK_5_6,
+ DVBFE_MODCOD_8PSK_8_9,
+ DVBFE_MODCOD_8PSK_9_10,
+ DVBFE_MODCOD_16APSK_2_3,
+ DVBFE_MODCOD_16APSK_3_4,
+ DVBFE_MODCOD_16APSK_4_5,
+ DVBFE_MODCOD_16APSK_5_6,
+ DVBFE_MODCOD_16APSK_8_9,
+ DVBFE_MODCOD_16APSK_9_10,
+ DVBFE_MODCOD_32APSK_3_4,
+ DVBFE_MODCOD_32APSK_4_5,
+ DVBFE_MODCOD_32APSK_5_6,
+ DVBFE_MODCOD_32APSK_8_9,
+ DVBFE_MODCOD_32APSK_9_10,
+ DVBFE_MODCOD_RESERVED_1,
+ DVBFE_MODCOD_BPSK_1_3,
+ DVBFE_MODCOD_BPSK_1_4,
+ DVBFE_MODCOD_RESERVED_2
+};
+
+enum tuner_param {
+ DVBFE_TUNER_FREQUENCY = (1 << 0),
+ DVBFE_TUNER_TUNERSTEP = (1 << 1),
+ DVBFE_TUNER_IFFREQ = (1 << 2),
+ DVBFE_TUNER_BANDWIDTH = (1 << 3),
+ DVBFE_TUNER_REFCLOCK = (1 << 4),
+ DVBFE_TUNER_IQSENSE = (1 << 5),
+ DVBFE_TUNER_DUMMY = (1 << 31)
+};
+
+/*
+ * ALGO_HW: (Hardware Algorithm)
+ * ----------------------------------------------------------------
+ * Devices that support this algorithm do everything in hardware
+ * and no software support is needed to handle them.
+ * Requesting these devices to LOCK is the only thing required,
+ * device is supposed to do everything in the hardware.
+ *
+ * ALGO_SW: (Software Algorithm)
+ * ----------------------------------------------------------------
+ * These are dumb devices, that require software to do everything
+ *
+ * ALGO_CUSTOM: (Customizable Agorithm)
+ * ----------------------------------------------------------------
+ * Devices having this algorithm can be customized to have specific
+ * algorithms in the frontend driver, rather than simply doing a
+ * software zig-zag. In this case the zigzag maybe hardware assisted
+ * or it maybe completely done in hardware. In all cases, usage of
+ * this algorithm, in conjunction with the search and track
+ * callbacks, utilizes the driver specific algorithm.
+ *
+ * ALGO_RECOVERY: (Recovery Algorithm)
+ * ----------------------------------------------------------------
+ * These devices have AUTO recovery capabilities from LOCK failure
+ */
+enum dvbfe_algo {
+ DVBFE_ALGO_HW = (1 << 0),
+ DVBFE_ALGO_SW = (1 << 1),
+ DVBFE_ALGO_CUSTOM = (1 << 2),
+ DVBFE_ALGO_RECOVERY = (1 << 31)
+};
+
+struct tuner_state {
+ u32 frequency;
+ u32 tunerstep;
+ u32 ifreq;
+ u32 bandwidth;
+ u32 iqsense;
+ u32 refclock;
+};
+
+/*
+ * search callback possible return status
+ *
+ * DVBFE_ALGO_SEARCH_SUCCESS
+ * The frontend search algorithm completed and returned successfully
+ *
+ * DVBFE_ALGO_SEARCH_ASLEEP
+ * The frontend search algorithm is sleeping
+ *
+ * DVBFE_ALGO_SEARCH_FAILED
+ * The frontend search for a signal failed
+ *
+ * DVBFE_ALGO_SEARCH_INVALID
+ * The frontend search algorith was probably supplied with invalid
+ * parameters and the search is an invalid one
+ *
+ * DVBFE_ALGO_SEARCH_ERROR
+ * The frontend search algorithm failed due to some error
+ *
+ * DVBFE_ALGO_SEARCH_AGAIN
+ * The frontend search algorithm was requested to search again
+ */
+enum dvbfe_search {
+ DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0),
+ DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1),
+ DVBFE_ALGO_SEARCH_FAILED = (1 << 2),
+ DVBFE_ALGO_SEARCH_INVALID = (1 << 3),
+ DVBFE_ALGO_SEARCH_AGAIN = (1 << 4),
+ DVBFE_ALGO_SEARCH_ERROR = (1 << 31),
+};
+
+
+struct dvb_tuner_ops {
+
+ struct dvb_tuner_info info;
+
+ int (*release)(struct dvb_frontend *fe);
+ int (*init)(struct dvb_frontend *fe);
+ int (*sleep)(struct dvb_frontend *fe);
+
+ /** This is for simple PLLs - set all parameters in one go. */
+ int (*set_params)(struct dvb_frontend *fe);
+ int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p);
+
+ /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
+ int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len);
+
+ /** This is to allow setting tuner-specific configs */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+
+ int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
+ int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency);
+
+#define TUNER_STATUS_LOCKED 1
+#define TUNER_STATUS_STEREO 2
+ int (*get_status)(struct dvb_frontend *fe, u32 *status);
+ int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
+ int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
+
+ /** These are provided separately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter separately. */
+ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+
+ /*
+ * These are provided separately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter separately.
+ */
+ int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+ int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+};
+
+struct analog_demod_info {
+ char *name;
+};
+
+struct analog_demod_ops {
+
+ struct analog_demod_info info;
+
+ void (*set_params)(struct dvb_frontend *fe,
+ struct analog_parameters *params);
+ int (*has_signal)(struct dvb_frontend *fe);
+ int (*get_afc)(struct dvb_frontend *fe);
+ void (*tuner_status)(struct dvb_frontend *fe);
+ void (*standby)(struct dvb_frontend *fe);
+ void (*release)(struct dvb_frontend *fe);
+ int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable);
+
+ /** This is to allow setting tuner-specific configuration */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+};
+
+struct dtv_frontend_properties;
+
+struct dvb_frontend_ops {
+
+ struct dvb_frontend_info info;
+
+ u8 delsys[MAX_DELSYS];
+
+ void (*release)(struct dvb_frontend* fe);
+ void (*release_sec)(struct dvb_frontend* fe);
+
+ int (*init)(struct dvb_frontend* fe);
+ int (*sleep)(struct dvb_frontend* fe);
+
+ int (*write)(struct dvb_frontend* fe, const u8 buf[], int len);
+
+ /* if this is set, it overrides the default swzigzag */
+ int (*tune)(struct dvb_frontend* fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ fe_status_t *status);
+ /* get frontend tuning algorithm from the module */
+ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe);
+
+ /* these two are only used for the swzigzag code */
+ int (*set_frontend)(struct dvb_frontend *fe);
+ int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
+
+ int (*get_frontend)(struct dvb_frontend *fe);
+
+ 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, long arg);
+ int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
+ int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
+ int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
+
+ /* These callbacks are for devices that implement their own
+ * tuning algorithms, rather than a simple swzigzag
+ */
+ enum dvbfe_search (*search)(struct dvb_frontend *fe);
+
+ struct dvb_tuner_ops tuner_ops;
+ struct analog_demod_ops analog_ops;
+
+ int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+ int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+};
+
+#ifdef __DVB_CORE__
+#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 mutex mtx;
+};
+#endif
+
+struct dtv_frontend_properties {
+
+ /* Cache State */
+ u32 state;
+
+ u32 frequency;
+ fe_modulation_t modulation;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t sectone;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec_inner;
+ fe_transmit_mode_t transmission_mode;
+ u32 bandwidth_hz; /* 0 = AUTO */
+ fe_guard_interval_t guard_interval;
+ fe_hierarchy_t hierarchy;
+ u32 symbol_rate;
+ fe_code_rate_t code_rate_HP;
+ fe_code_rate_t code_rate_LP;
+
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ fe_delivery_system_t delivery_system;
+
+ enum fe_interleaving interleaving;
+
+ /* ISDB-T specifics */
+ u8 isdbt_partial_reception;
+ u8 isdbt_sb_mode;
+ u8 isdbt_sb_subchannel;
+ u32 isdbt_sb_segment_idx;
+ u32 isdbt_sb_segment_count;
+ u8 isdbt_layer_enabled;
+ struct {
+ u8 segment_count;
+ fe_code_rate_t fec;
+ fe_modulation_t modulation;
+ u8 interleaving;
+ } layer[3];
+
+ /* ISDB-T specifics */
+ u32 isdbs_ts_id;
+
+ /* DVB-T2 specifics */
+ u32 dvbt2_plp_id;
+
+ /* ATSC-MH specifics */
+ u8 atscmh_fic_ver;
+ u8 atscmh_parade_id;
+ u8 atscmh_nog;
+ u8 atscmh_tnog;
+ u8 atscmh_sgn;
+ u8 atscmh_prc;
+
+ u8 atscmh_rs_frame_mode;
+ u8 atscmh_rs_frame_ensemble;
+ u8 atscmh_rs_code_mode_pri;
+ u8 atscmh_rs_code_mode_sec;
+ u8 atscmh_sccc_block_mode;
+ u8 atscmh_sccc_code_mode_a;
+ u8 atscmh_sccc_code_mode_b;
+ u8 atscmh_sccc_code_mode_c;
+ u8 atscmh_sccc_code_mode_d;
+};
+
+struct dvb_frontend {
+ struct dvb_frontend_ops ops;
+ struct dvb_adapter *dvb;
+ void *demodulator_priv;
+ void *tuner_priv;
+ void *frontend_priv;
+ void *sec_priv;
+ void *analog_demod_priv;
+ struct dtv_frontend_properties dtv_property_cache;
+#define DVB_FRONTEND_COMPONENT_TUNER 0
+#define DVB_FRONTEND_COMPONENT_DEMOD 1
+ int (*callback)(void *adapter_priv, int component, int cmd, int arg);
+ int id;
+};
+
+extern int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe);
+
+extern int dvb_unregister_frontend(struct dvb_frontend *fe);
+
+extern void dvb_frontend_detach(struct dvb_frontend *fe);
+
+extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
+
+extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
+extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime);
+
+#endif
diff --git a/drivers/media/dvb-core/dvb_math.c b/drivers/media/dvb-core/dvb_math.c
new file mode 100644
index 000000000000..beb7c93aa6cb
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_math.c
@@ -0,0 +1,145 @@
+/*
+ * dvb-math provides some complex fixed-point math
+ * operations shared between the dvb related stuff
+ *
+ * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com)
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/bug.h>
+#include "dvb_math.h"
+
+static const unsigned short logtable[256] = {
+ 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
+ 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
+ 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
+ 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
+ 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
+ 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
+ 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
+ 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
+ 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
+ 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
+ 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
+ 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
+ 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
+ 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
+ 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
+ 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
+ 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
+ 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
+ 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
+ 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
+ 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
+ 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
+ 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
+ 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
+ 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
+ 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
+ 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
+ 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
+ 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
+ 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
+ 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
+ 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
+};
+
+unsigned int intlog2(u32 value)
+{
+ /**
+ * returns: log2(value) * 2^24
+ * wrong result if value = 0 (log2(0) is undefined)
+ */
+ unsigned int msb;
+ unsigned int logentry;
+ unsigned int significand;
+ unsigned int interpolation;
+
+ if (unlikely(value == 0)) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ /* first detect the msb (count begins at 0) */
+ msb = fls(value) - 1;
+
+ /**
+ * now we use a logtable after the following method:
+ *
+ * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
+ * where x = msb and therefore 1 <= y < 2
+ * first y is determined by shifting the value left
+ * so that msb is bit 31
+ * 0x00231f56 -> 0x8C7D5800
+ * the result is y * 2^31 -> "significand"
+ * then the highest 9 bits are used for a table lookup
+ * the highest bit is discarded because it's always set
+ * the highest nine bits in our example are 100011000
+ * so we would use the entry 0x18
+ */
+ significand = value << (31 - msb);
+ logentry = (significand >> 23) & 0xff;
+
+ /**
+ * last step we do is interpolation because of the
+ * limitations of the log table the error is that part of
+ * the significand which isn't used for lookup then we
+ * compute the ratio between the error and the next table entry
+ * and interpolate it between the log table entry used and the
+ * next one the biggest error possible is 0x7fffff
+ * (in our example it's 0x7D5800)
+ * needed value for next table entry is 0x800000
+ * so the interpolation is
+ * (error / 0x800000) * (logtable_next - logtable_current)
+ * in the implementation the division is moved to the end for
+ * better accuracy there is also an overflow correction if
+ * logtable_next is 256
+ */
+ interpolation = ((significand & 0x7fffff) *
+ ((logtable[(logentry + 1) & 0xff] -
+ logtable[logentry]) & 0xffff)) >> 15;
+
+ /* now we return the result */
+ return ((msb << 24) + (logtable[logentry] << 8) + interpolation);
+}
+EXPORT_SYMBOL(intlog2);
+
+unsigned int intlog10(u32 value)
+{
+ /**
+ * returns: log10(value) * 2^24
+ * wrong result if value = 0 (log10(0) is undefined)
+ */
+ u64 log;
+
+ if (unlikely(value == 0)) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ log = intlog2(value);
+
+ /**
+ * we use the following method:
+ * log10(x) = log2(x) * log10(2)
+ */
+
+ return (log * 646456993) >> 31;
+}
+EXPORT_SYMBOL(intlog10);
diff --git a/drivers/media/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h
new file mode 100644
index 000000000000..aecc867e9404
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_math.h
@@ -0,0 +1,58 @@
+/*
+ * dvb-math provides some complex fixed-point math
+ * operations shared between the dvb related stuff
+ *
+ * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com)
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __DVB_MATH_H
+#define __DVB_MATH_H
+
+#include <linux/types.h>
+
+/**
+ * computes log2 of a value; the result is shifted left by 24 bits
+ *
+ * to use rational values you can use the following method:
+ * intlog2(value) = intlog2(value * 2^x) - x * 2^24
+ *
+ * example: intlog2(8) will give 3 << 24 = 3 * 2^24
+ * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24
+ * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24
+ *
+ * @param value The value (must be != 0)
+ * @return log2(value) * 2^24
+ */
+extern unsigned int intlog2(u32 value);
+
+/**
+ * computes log10 of a value; the result is shifted left by 24 bits
+ *
+ * to use rational values you can use the following method:
+ * intlog10(value) = intlog10(value * 10^x) - x * 2^24
+ *
+ * example: intlog10(1000) will give 3 << 24 = 3 * 2^24
+ * due to the implementation intlog10(1000) might be not exactly 3 * 2^24
+ *
+ * look at intlog2 for similar examples
+ *
+ * @param value The value (must be != 0)
+ * @return log10(value) * 2^24
+ */
+extern unsigned int intlog10(u32 value);
+
+#endif
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
new file mode 100644
index 000000000000..8766ce8c354d
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -0,0 +1,1516 @@
+/*
+ * 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 RFC 4326.
+ *
+ * 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.
+ * Apr 2006: cp v3: Bugfixes and compliency with RFC 4326 (ULE) by
+ * Christian Praehauser <cpraehaus@cosy.sbg.ac.at>,
+ * Paris Lodron University of Salzburg.
+ */
+
+/*
+ * FIXME / TODO (dvb_net.c):
+ *
+ * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero.
+ *
+ */
+
+#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/mutex.h>
+#include <linux/sched.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 MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"
+#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5]
+
+#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;
+ u16 pid;
+ struct net_device *net;
+ 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. */
+ struct mutex mutex;
+};
+
+
+/**
+ * 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 __be16 dvb_net_eth_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb_reset_mac_header(skb);
+ skb_pull(skb,dev->hard_header_len);
+ eth = eth_hdr(skb);
+
+ 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
+
+#define ULE_OPTEXTHDR_PADDING 0
+
+static int ule_test_sndu( struct dvb_net_priv *p )
+{
+ return -1;
+}
+
+static int ule_bridged_sndu( struct dvb_net_priv *p )
+{
+ struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr;
+ if(ntohs(hdr->h_proto) < 1536) {
+ int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data);
+ /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */
+ if(framelen != ntohs(hdr->h_proto)) {
+ return -1;
+ }
+ }
+ /* Note:
+ * From RFC4326:
+ * "A bridged SNDU is a Mandatory Extension Header of Type 1.
+ * It must be the final (or only) extension header specified in the header chain of a SNDU."
+ * The 'ule_bridged' flag will cause the extension header processing loop to terminate.
+ */
+ p->ule_bridged = 1;
+ return 0;
+}
+
+static int ule_exthdr_padding(struct dvb_net_priv *p)
+{
+ 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 ) =
+ { [0] = ule_exthdr_padding, [1] = 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 );
+ if(ext_len >= 0) {
+ p->ule_next_hdr += ext_len;
+ if (!p->ule_bridged) {
+ p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr);
+ p->ule_next_hdr += 2;
+ } else {
+ p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)));
+ /* This assures the extension handling loop will terminate. */
+ }
+ }
+ // else: extension handler failed or SNDU should be discarded
+ } else
+ ext_len = -1; /* SNDU has to be discarded. */
+ } else {
+ /* Optional extension header. Calculate the length. */
+ ext_len = hlen << 1;
+ /* 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( *(__be16 *)(p->ule_next_hdr-2) );
+ /*
+ * note: the length of the next header type is included in the
+ * length of THIS optional extension header
+ */
+ }
+
+ 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 < 0)
+ return l; /* Stop extension header processing and discard SNDU. */
+ total_ext_len += l;
+#ifdef ULE_DEBUG
+ dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, "
+ "l=%i, total_ext_len=%i\n", p->ule_next_hdr,
+ (int) p->ule_sndu_type, l, total_ext_len);
+#endif
+
+ } 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 = netdev_priv(dev);
+ unsigned long skipped = 0L;
+ const u8 *ts, *ts_end, *from_where = NULL;
+ u8 ts_remain = 0, how_much = 0, new_ts = 1;
+ struct ethhdr *ethh = NULL;
+ bool error = false;
+
+#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;
+#endif
+
+ /* For all TS cells in current buffer.
+ * Appearently, we are called for every single TS cell.
+ */
+ for (ts = buf, ts_end = 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. */
+ dev->stats.rx_errors++;
+ dev->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;
+ }
+ }
+
+ if (new_ts) {
+ /* Check continuity counter. */
+ if ((ts[3] & 0x0F) == priv->tscc)
+ priv->tscc = (priv->tscc + 1) & 0x0F;
+ else {
+ /* TS discontinuity handling: */
+ printk(KERN_WARNING "%lu: TS discontinuity: got %#x, "
+ "expected %#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.
+ dev->stats.rx_errors++;
+ dev->stats.rx_frame_errors++;
+ }
+ reset_ule(priv);
+ /* skip to next PUSI. */
+ priv->need_pusi = 1;
+ 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 < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) {
+ /* 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) {
+ error = true;
+ dev_kfree_skb(priv->ule_skb);
+ }
+
+ if (error || priv->ule_sndu_remain) {
+ dev->stats.rx_errors++;
+ dev->stats.rx_frame_errors++;
+ error = false;
+ }
+
+ reset_ule(priv);
+ priv->need_pusi = 1;
+ 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. */
+ dev->stats.rx_errors++;
+ dev->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;
+ ts += TS_SZ;
+ 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 < 5) {
+ printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+ "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ 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;
+ }
+
+ priv->ule_sndu_remain = priv->ule_sndu_len + 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_remain--;
+ 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_1 = 0;
+ 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);
+ dev->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. */
+ __be16 ulen = htons(priv->ule_sndu_len);
+ __be16 utype = htons(priv->ule_sndu_type);
+ const u8 *tail;
+ struct kvec iov[3] = {
+ { &ulen, sizeof ulen },
+ { &utype, sizeof utype },
+ { priv->ule_skb->data, priv->ule_skb->len - 4 }
+ };
+ u32 ule_crc = ~0L, expected_crc;
+ if (priv->ule_dbit) {
+ /* Set D-bit for CRC32 verification,
+ * if it was set originally. */
+ ulen |= htons(0x8000);
+ }
+
+ ule_crc = iov_crc32(ule_crc, iov, 3);
+ tail = skb_tail_pointer(priv->ule_skb);
+ expected_crc = *(tail - 4) << 24 |
+ *(tail - 3) << 16 |
+ *(tail - 2) << 8 |
+ *(tail - 1);
+ if (ule_crc != expected_crc) {
+ printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, 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
+
+ dev->stats.rx_errors++;
+ dev->stats.rx_crc_errors++;
+ dev_kfree_skb(priv->ule_skb);
+ } else {
+ /* CRC32 verified OK. */
+ u8 dest_addr[ETH_ALEN];
+ static const u8 bc_addr[ETH_ALEN] =
+ { [ 0 ... ETH_ALEN-1] = 0xff };
+
+ /* CRC32 was OK. Remove it from skb. */
+ priv->ule_skb->tail -= 4;
+ priv->ule_skb->len -= 4;
+
+ if (!priv->ule_dbit) {
+ /*
+ * The destination MAC address is the
+ * next data in the skb. It comes
+ * before any extension headers.
+ *
+ * Check if the payload of this SNDU
+ * should be passed up the stack.
+ */
+ register int drop = 0;
+ if (priv->rx_mode != RX_MODE_PROMISC) {
+ if (priv->ule_skb->data[0] & 0x01) {
+ /* multicast or broadcast */
+ if (memcmp(priv->ule_skb->data, bc_addr, ETH_ALEN)) {
+ /* multicast */
+ if (priv->rx_mode == RX_MODE_MULTI) {
+ int i;
+ for(i = 0; i < priv->multi_num && memcmp(priv->ule_skb->data, priv->multi_macs[i], ETH_ALEN); i++)
+ ;
+ if (i == priv->multi_num)
+ drop = 1;
+ } else if (priv->rx_mode != RX_MODE_ALL_MULTI)
+ drop = 1; /* no broadcast; */
+ /* else: all multicast mode: accept all multicast packets */
+ }
+ /* else: broadcast */
+ }
+ else if (memcmp(priv->ule_skb->data, dev->dev_addr, ETH_ALEN))
+ drop = 1;
+ /* else: destination address matches the MAC address of our receiver device */
+ }
+ /* else: promiscuous mode; pass everything up the stack */
+
+ if (drop) {
+#ifdef ULE_DEBUG
+ dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n",
+ MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr));
+#endif
+ dev_kfree_skb(priv->ule_skb);
+ goto sndu_done;
+ }
+ else
+ {
+ skb_copy_from_linear_data(priv->ule_skb,
+ dest_addr,
+ ETH_ALEN);
+ skb_pull(priv->ule_skb, ETH_ALEN);
+ }
+ }
+
+ /* 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);
+ }
+
+ /*
+ * Construct/assure correct ethernet header.
+ * Note: in bridged mode (priv->ule_bridged !=
+ * 0) we already have the (original) ethernet
+ * header at the start of the payload (after
+ * optional dest. address and any extension
+ * headers).
+ */
+
+ if (!priv->ule_bridged) {
+ skb_push(priv->ule_skb, ETH_HLEN);
+ ethh = (struct ethhdr *)priv->ule_skb->data;
+ if (!priv->ule_dbit) {
+ /* dest_addr buffer is only valid if priv->ule_dbit == 0 */
+ memcpy(ethh->h_dest, dest_addr, ETH_ALEN);
+ memset(ethh->h_source, 0, ETH_ALEN);
+ }
+ else /* zeroize source and dest */
+ memset( ethh, 0, ETH_ALEN*2 );
+
+ 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; */
+ dev->stats.rx_packets++;
+ dev->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 = feed->priv;
+
+ if (buffer2)
+ printk(KERN_WARNING "buffer2 not NULL: %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,
+ const u8 *pkt, int pkt_len)
+{
+ u8 *eth;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &dev->stats;
+ int snap = 0;
+
+ /* 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) {
+ /* handle LLC/SNAP, see rfc-1042 */
+ if (pkt_len < 24 || memcmp(&pkt[12], "\xaa\xaa\x03\0\0\0", 6)) {
+ stats->rx_dropped++;
+ return;
+ }
+ snap = 8;
+ }
+ 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, 8 byte LLC/SNAP
+ */
+ if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) {
+ //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 - snap);
+ memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap);
+
+ /* 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;
+
+ if (snap) {
+ eth[12] = pkt[18];
+ eth[13] = pkt[19];
+ } else {
+ /* protocol numbers are from rfc-1700 or
+ * http://www.iana.org/assignments/ethernet-numbers
+ */
+ if (pkt[12] >> 4 == 6) { /* version field from IP header */
+ eth[12] = 0x86; /* IPv6 */
+ eth[13] = 0xdd;
+ } else {
+ eth[12] = 0x08; /* IPv4 */
+ 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 = filter->priv;
+
+ /**
+ * we rely on the DVB API definition where exactly one complete
+ * section is delivered in buffer1
+ */
+ dvb_net_sec (dev, buffer1, buffer1_len);
+ return 0;
+}
+
+static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+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 = netdev_priv(dev);
+ 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=%pM\n", dev->name, mac);
+ dprintk("%s: filter mask=%pM\n", dev->name, mac_mask);
+
+ return 0;
+}
+
+static int dvb_net_feed_start(struct net_device *dev)
+{
+ int ret = 0, i;
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ struct dmx_demux *demux = priv->demux;
+ unsigned char *mac = (unsigned char *) dev->dev_addr;
+
+ dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode);
+ mutex_lock(&priv->mutex);
+ if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
+ printk("%s: BUG %d\n", __func__, __LINE__);
+
+ priv->secfeed=NULL;
+ priv->secfilter=NULL;
+ priv->tsfeed = NULL;
+
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+ dprintk("%s: alloc secfeed\n", __func__);
+ 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);
+ goto error;
+ }
+
+ ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 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;
+ goto error;
+ }
+
+ if (priv->rx_mode != RX_MODE_PROMISC) {
+ dprintk("%s: set secfilter\n", __func__);
+ 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", __func__, 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", __func__);
+ 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", __func__);
+ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
+ break;
+ }
+
+ dprintk("%s: start filtering\n", __func__);
+ priv->secfeed->start_filtering(priv->secfeed);
+ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+ struct timespec timeout = { 0, 10000000 }; // 10 msec
+
+ /* we have payloads encapsulated in TS */
+ dprintk("%s: alloc tsfeed\n", __func__);
+ 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);
+ goto error;
+ }
+
+ /* Set netdevice pointer for ts decaps callback. */
+ priv->tsfeed->priv = (void *)dev;
+ ret = priv->tsfeed->set(priv->tsfeed,
+ priv->pid, /* pid */
+ TS_PACKET, /* type */
+ DMX_TS_PES_OTHER, /* pes type */
+ 32768, /* circular buffer size */
+ timeout /* 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;
+ goto error;
+ }
+
+ dprintk("%s: start filtering\n", __func__);
+ priv->tsfeed->start_filtering(priv->tsfeed);
+ } else
+ ret = -EINVAL;
+
+error:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int dvb_net_feed_stop(struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ int i, ret = 0;
+
+ dprintk("%s\n", __func__);
+ mutex_lock(&priv->mutex);
+ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+ if (priv->secfeed) {
+ if (priv->secfeed->is_filtering) {
+ dprintk("%s: stop secfeed\n", __func__);
+ priv->secfeed->stop_filtering(priv->secfeed);
+ }
+
+ if (priv->secfilter) {
+ dprintk("%s: release secfilter\n", __func__);
+ 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",
+ __func__, 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", __func__);
+ 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
+ ret = -EINVAL;
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+
+static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+
+ if (priv->multi_num == DVB_NET_MULTICAST_MAX)
+ return -ENOMEM;
+
+ memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN);
+
+ priv->multi_num++;
+ return 0;
+}
+
+
+static void wq_set_multicast_list (struct work_struct *work)
+{
+ struct dvb_net_priv *priv =
+ container_of(work, struct dvb_net_priv, set_multicast_list_wq);
+ struct net_device *dev = priv->net;
+
+ dvb_net_feed_stop(dev);
+ priv->rx_mode = RX_MODE_UNI;
+ netif_addr_lock_bh(dev);
+
+ 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 (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ dprintk("%s: set_mc_list, %d entries\n",
+ dev->name, netdev_mc_count(dev));
+
+ priv->rx_mode = RX_MODE_MULTI;
+ priv->multi_num = 0;
+
+ netdev_for_each_mc_addr(ha, dev)
+ dvb_set_mc_filter(dev, ha->addr);
+ }
+
+ netif_addr_unlock_bh(dev);
+ dvb_net_feed_start(dev);
+}
+
+
+static void dvb_net_set_multicast_list (struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+ schedule_work(&priv->set_multicast_list_wq);
+}
+
+
+static void wq_restart_net_feed (struct work_struct *work)
+{
+ struct dvb_net_priv *priv =
+ container_of(work, struct dvb_net_priv, restart_net_feed_wq);
+ struct net_device *dev = priv->net;
+
+ 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 = netdev_priv(dev);
+ 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 = netdev_priv(dev);
+
+ priv->in_use++;
+ dvb_net_feed_start(dev);
+ return 0;
+}
+
+
+static int dvb_net_stop(struct net_device *dev)
+{
+ struct dvb_net_priv *priv = netdev_priv(dev);
+
+ priv->in_use--;
+ return dvb_net_feed_stop(dev);
+}
+
+static const struct header_ops dvb_header_ops = {
+ .create = eth_header,
+ .parse = eth_header_parse,
+ .rebuild = eth_rebuild_header,
+};
+
+
+static const struct net_device_ops dvb_netdev_ops = {
+ .ndo_open = dvb_net_open,
+ .ndo_stop = dvb_net_stop,
+ .ndo_start_xmit = dvb_net_tx,
+ .ndo_set_rx_mode = dvb_net_set_multicast_list,
+ .ndo_set_mac_address = dvb_net_set_mac,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void dvb_net_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+
+ dev->header_ops = &dvb_header_ops;
+ dev->netdev_ops = &dvb_netdev_ops;
+ dev->mtu = 4096;
+
+ 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 = netdev_priv(net);
+ priv->net = net;
+ 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);
+ INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed);
+ mutex_init(&priv->mutex);
+
+ 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 long num)
+{
+ struct net_device *net = dvbnet->device[num];
+ struct dvb_net_priv *priv;
+
+ if (!dvbnet->state[num])
+ return -EINVAL;
+ priv = netdev_priv(net);
+ if (priv->in_use)
+ return -EBUSY;
+
+ dvb_net_stop(net);
+ flush_work_sync(&priv->set_multicast_list_wq);
+ flush_work_sync(&priv->restart_net_feed_wq);
+ 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 file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+
+ if (((file->f_flags&O_ACCMODE)==O_RDONLY))
+ return -EPERM;
+
+ switch (cmd) {
+ case NET_ADD_IF:
+ {
+ struct dvb_net_if *dvbnetif = 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 = parg;
+
+ if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+ !dvbnet->state[dvbnetif->if_num])
+ return -EINVAL;
+
+ netdev = dvbnet->device[dvbnetif->if_num];
+
+ priv_data = netdev_priv(netdev);
+ 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 long) parg >= DVB_NET_DEVICES_MAX)
+ return -EINVAL;
+ ret = dvb_net_remove_if(dvbnet, (unsigned long) parg);
+ if (!ret)
+ module_put(dvbdev->adapter->module);
+ return ret;
+ }
+
+ /* binary compatibility cruft */
+ case __NET_ADD_IF_OLD:
+ {
+ struct __dvb_net_if_old *dvbnetif = 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 = parg;
+
+ if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+ !dvbnet->state[dvbnetif->if_num])
+ return -EINVAL;
+
+ netdev = dvbnet->device[dvbnetif->if_num];
+
+ priv_data = netdev_priv(netdev);
+ dvbnetif->pid=priv_data->pid;
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static long dvb_net_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
+}
+
+static int dvb_net_close(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_net *dvbnet = dvbdev->priv;
+
+ dvb_generic_release(inode, file);
+
+ if(dvbdev->users == 1 && dvbnet->exit == 1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ wake_up(&dvbdev->wait_queue);
+ }
+ return 0;
+}
+
+
+static const struct file_operations dvb_net_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dvb_net_ioctl,
+ .open = dvb_generic_open,
+ .release = dvb_net_close,
+ .llseek = noop_llseek,
+};
+
+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;
+
+ dvbnet->exit = 1;
+ if (dvbnet->dvbdev->users < 1)
+ wait_event(dvbnet->dvbdev->wait_queue,
+ dvbnet->dvbdev->users==1);
+
+ 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;
+
+ return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net,
+ dvbnet, DVB_DEVICE_NET);
+}
+EXPORT_SYMBOL(dvb_net_init);
diff --git a/drivers/media/dvb-core/dvb_net.h b/drivers/media/dvb-core/dvb_net.h
new file mode 100644
index 000000000000..1e53acd50cf4
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_net.h
@@ -0,0 +1,66 @@
+/*
+ * 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
+
+#ifdef CONFIG_DVB_NET
+
+struct dvb_net {
+ struct dvb_device *dvbdev;
+ struct net_device *device[DVB_NET_DEVICES_MAX];
+ int state[DVB_NET_DEVICES_MAX];
+ unsigned int exit:1;
+ struct dmx_demux *demux;
+};
+
+void dvb_net_release(struct dvb_net *);
+int dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
+
+#else
+
+struct dvb_net {
+ struct dvb_device *dvbdev;
+};
+
+static inline void dvb_net_release(struct dvb_net *dvbnet)
+{
+}
+
+static inline int dvb_net_init(struct dvb_adapter *adap,
+ struct dvb_net *dvbnet, struct dmx_demux *dmx)
+{
+ return 0;
+}
+
+#endif /* ifdef CONFIG_DVB_NET */
+
+#endif
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
new file mode 100644
index 000000000000..a5712cd7c65f
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -0,0 +1,299 @@
+/*
+ *
+ * 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.
+ */
+
+
+
+#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;
+ rbuf->error=0;
+
+ 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;
+ rbuf->error = 0;
+}
+EXPORT_SYMBOL(dvb_ringbuffer_flush);
+
+void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
+{
+ rbuf->pread = rbuf->pwrite = 0;
+ rbuf->error = 0;
+}
+
+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_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+ if (split > 0) {
+ if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ rbuf->pread = 0;
+ }
+ if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
+ return -EFAULT;
+
+ rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+
+ return len;
+}
+
+void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
+{
+ size_t todo = len;
+ size_t split;
+
+ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+ if (split > 0) {
+ memcpy(buf, rbuf->data+rbuf->pread, split);
+ buf += split;
+ todo -= split;
+ rbuf->pread = 0;
+ }
+ memcpy(buf, rbuf->data+rbuf->pread, todo);
+
+ rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+}
+
+
+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_user(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8 __user *buf, size_t len)
+{
+ 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 (copy_to_user(buf, rbuf->data+idx, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ if (copy_to_user(buf, rbuf->data+idx, todo))
+ return -EFAULT;
+
+ return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8* buf, size_t len)
+{
+ 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) {
+ memcpy(buf, rbuf->data+idx, split);
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ memcpy(buf, rbuf->data+idx, todo);
+ 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_spinlock_wakeup);
+EXPORT_SYMBOL(dvb_ringbuffer_read_user);
+EXPORT_SYMBOL(dvb_ringbuffer_read);
+EXPORT_SYMBOL(dvb_ringbuffer_write);
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h
new file mode 100644
index 000000000000..41f04dae69b6
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_ringbuffer.h
@@ -0,0 +1,186 @@
+/*
+ *
+ * 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;
+ int error;
+
+ 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));
+** 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.
+** Resetting the buffer counts as a read and write 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);
+
+
+/*
+** Reset the read and write pointers to zero and flush the buffer
+** This counts as a read and write operation
+*/
+extern void dvb_ringbuffer_reset(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_user(struct dvb_ringbuffer *rbuf,
+ u8 __user *buf, size_t len);
+extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf,
+ u8 *buf, size_t len);
+
+
+/* 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_user(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8 __user *buf, size_t len);
+extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8 *buf, size_t len);
+
+/**
+ * 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-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
new file mode 100644
index 000000000000..39eab73b01ae
--- /dev/null
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -0,0 +1,507 @@
+/*
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include "dvbdev.h"
+
+static DEFINE_MUTEX(dvbdev_mutex);
+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 DEFINE_MUTEX(dvbdev_register_lock);
+
+static const char * const dnames[] = {
+ "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+ "net", "osd"
+};
+
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+#define MAX_DVB_MINORS 256
+#define DVB_MAX_IDS MAX_DVB_MINORS
+#else
+#define DVB_MAX_IDS 4
+#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
+#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
+#endif
+
+static struct class *dvb_class;
+
+static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
+static DECLARE_RWSEM(minor_rwsem);
+
+static int dvb_device_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev;
+
+ mutex_lock(&dvbdev_mutex);
+ down_read(&minor_rwsem);
+ dvbdev = dvb_minors[iminor(inode)];
+
+ if (dvbdev && dvbdev->fops) {
+ int err = 0;
+ const 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 == NULL) {
+ file->f_op = old_fops;
+ goto fail;
+ }
+ 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);
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ return err;
+ }
+fail:
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ return -ENODEV;
+}
+
+
+static const struct file_operations dvb_device_fops =
+{
+ .owner = THIS_MODULE,
+ .open = dvb_device_open,
+ .llseek = noop_llseek,
+};
+
+static struct cdev dvb_device_cdev;
+
+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);
+
+
+long dvb_generic_ioctl(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(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 dvb_device *dev;
+ list_for_each_entry(dev, &adap->device_list, 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;
+ struct file_operations *dvbdevfops;
+ struct device *clsdev;
+ int minor;
+ int id;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ if ((id = dvbdev_get_free_id (adap, type)) < 0){
+ mutex_unlock(&dvbdev_register_lock);
+ *pdvbdev = NULL;
+ printk(KERN_ERR "%s: couldn't find free device id\n", __func__);
+ return -ENFILE;
+ }
+
+ *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
+
+ if (!dvbdev){
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
+
+ if (!dvbdevfops){
+ kfree (dvbdev);
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(dvbdev, template, sizeof(struct dvb_device));
+ dvbdev->type = type;
+ dvbdev->id = id;
+ dvbdev->adapter = adap;
+ dvbdev->priv = priv;
+ dvbdev->fops = dvbdevfops;
+ init_waitqueue_head (&dvbdev->wait_queue);
+
+ memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
+ dvbdevfops->owner = adap->module;
+
+ list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+ down_write(&minor_rwsem);
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+ for (minor = 0; minor < MAX_DVB_MINORS; minor++)
+ if (dvb_minors[minor] == NULL)
+ break;
+
+ if (minor == MAX_DVB_MINORS) {
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ up_write(&minor_rwsem);
+ mutex_unlock(&dvbdev_register_lock);
+ return -EINVAL;
+ }
+#else
+ minor = nums2minor(adap->num, type, id);
+#endif
+
+ dvbdev->minor = minor;
+ dvb_minors[minor] = dvbdev;
+ up_write(&minor_rwsem);
+
+ mutex_unlock(&dvbdev_register_lock);
+
+ clsdev = device_create(dvb_class, adap->device,
+ MKDEV(DVB_MAJOR, minor),
+ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
+ if (IS_ERR(clsdev)) {
+ printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
+ __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
+ return PTR_ERR(clsdev);
+ }
+
+ dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
+ adap->num, dnames[type], id, minor, minor);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_device);
+
+
+void dvb_unregister_device(struct dvb_device *dvbdev)
+{
+ if (!dvbdev)
+ return;
+
+ down_write(&minor_rwsem);
+ dvb_minors[dvbdev->minor] = NULL;
+ up_write(&minor_rwsem);
+
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
+
+ list_del (&dvbdev->list_head);
+ kfree (dvbdev->fops);
+ kfree (dvbdev);
+}
+EXPORT_SYMBOL(dvb_unregister_device);
+
+static int dvbdev_check_free_adapter_num(int num)
+{
+ 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)
+ return 0;
+ }
+ return 1;
+}
+
+static int dvbdev_get_free_adapter_num (void)
+{
+ int num = 0;
+
+ while (num < DVB_MAX_ADAPTERS) {
+ if (dvbdev_check_free_adapter_num(num))
+ return num;
+ num++;
+ }
+
+ return -ENFILE;
+}
+
+
+int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+ struct module *module, struct device *device,
+ short *adapter_nums)
+{
+ int i, num;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
+ num = adapter_nums[i];
+ if (num >= 0 && num < DVB_MAX_ADAPTERS) {
+ /* use the one the driver asked for */
+ if (dvbdev_check_free_adapter_num(num))
+ break;
+ } else {
+ num = dvbdev_get_free_adapter_num();
+ break;
+ }
+ num = -1;
+ }
+
+ if (num < 0) {
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENFILE;
+ }
+
+ memset (adap, 0, sizeof(struct dvb_adapter));
+ INIT_LIST_HEAD (&adap->device_list);
+
+ printk(KERN_INFO "DVB: registering new adapter (%s)\n", name);
+
+ adap->num = num;
+ adap->name = name;
+ adap->module = module;
+ adap->device = device;
+ adap->mfe_shared = 0;
+ adap->mfe_dvbdev = NULL;
+ mutex_init (&adap->mfe_lock);
+
+ list_add_tail (&adap->list_head, &dvb_adapter_list);
+
+ mutex_unlock(&dvbdev_register_lock);
+
+ return num;
+}
+EXPORT_SYMBOL(dvb_register_adapter);
+
+
+int dvb_unregister_adapter(struct dvb_adapter *adap)
+{
+ mutex_lock(&dvbdev_register_lock);
+ list_del (&adap->list_head);
+ mutex_unlock(&dvbdev_register_lock);
+ 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 file *file,
+ unsigned int cmd, unsigned long arg,
+ int (*func)(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 */
+ mutex_lock(&dvbdev_mutex);
+ if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
+ err = -EINVAL;
+ mutex_unlock(&dvbdev_mutex);
+
+ 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 dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
+ add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
+ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
+ return 0;
+}
+
+static char *dvb_devnode(struct device *dev, umode_t *mode)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
+ dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
+}
+
+
+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(KERN_ERR "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(KERN_ERR "dvb-core: unable register character device\n");
+ goto error;
+ }
+
+ dvb_class = class_create(THIS_MODULE, "dvb");
+ if (IS_ERR(dvb_class)) {
+ retval = PTR_ERR(dvb_class);
+ goto error;
+ }
+ dvb_class->dev_uevent = dvb_uevent;
+ dvb_class->devnode = dvb_devnode;
+ return 0;
+
+error:
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(dev, MAX_DVB_MINORS);
+ return retval;
+}
+
+
+static void __exit exit_dvbdev(void)
+{
+ class_destroy(dvb_class);
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
+}
+
+subsys_initcall(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-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h
new file mode 100644
index 000000000000..93a9470d3f0c
--- /dev/null
+++ b/drivers/media/dvb-core/dvbdev.h
@@ -0,0 +1,146 @@
+/*
+ * 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>
+
+#define DVB_MAJOR 212
+
+#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0
+ #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS
+#else
+ #define DVB_MAX_ADAPTERS 8
+#endif
+
+#define DVB_UNSET (-1)
+
+#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
+
+#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \
+ static short adapter_nr[] = \
+ {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \
+ module_param_array(adapter_nr, short, NULL, 0444); \
+ MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
+
+struct dvb_frontend;
+
+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 device *device;
+
+ struct module *module;
+
+ int mfe_shared; /* indicates mutually exclusive frontends */
+ struct dvb_device *mfe_dvbdev; /* frontend device in use */
+ struct mutex mfe_lock; /* access lock for thread creation */
+};
+
+
+struct dvb_device {
+ struct list_head list_head;
+ const struct file_operations *fops;
+ struct dvb_adapter *adapter;
+ int type;
+ int minor;
+ u32 id;
+
+ /* in theory, 'users' can vanish now,
+ but I don't want to change too much now... */
+ int readers;
+ int writers;
+ int users;
+
+ wait_queue_head_t wait_queue;
+ /* don't really need those !? -- FIXME: use video_usercopy */
+ int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg);
+
+ void *priv;
+};
+
+
+extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+ struct module *module, struct device *device,
+ short *adapter_nums);
+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 long dvb_generic_ioctl (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 file *file, unsigned int cmd, unsigned long arg,
+ int (*func)(struct file *file, unsigned int cmd, void *arg));
+
+/** generic DVB attach function. */
+#ifdef CONFIG_MEDIA_ATTACH
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ void *__r = NULL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (void *) __a(ARGS); \
+ if (__r == NULL) \
+ symbol_put(FUNCTION); \
+ } else { \
+ printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
+ } \
+ __r; \
+})
+
+#else
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+#endif
+
+#endif /* #ifndef _DVBDEV_H_ */