diff options
author | Holger Waechtler <devnull@localhost> | 2002-10-16 16:52:27 +0000 |
---|---|---|
committer | Holger Waechtler <devnull@localhost> | 2002-10-16 16:52:27 +0000 |
commit | 4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7 (patch) | |
tree | 23af98b0e1da37f97558e63953011e23d1e360a5 /linux/drivers/media/dvb/dvb-core | |
download | mediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.gz mediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.bz2 |
the 2.5 tree
Diffstat (limited to 'linux/drivers/media/dvb/dvb-core')
20 files changed, 6679 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/dvb-core/Config.help b/linux/drivers/media/dvb/dvb-core/Config.help new file mode 100644 index 000000000..ad3c23d74 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/Config.help @@ -0,0 +1,13 @@ +CONFIG_DVB_CORE + DVB core utility functions for device handling, software fallbacks etc. + + Say Y when you have a DVB card and want to use it. If unsure say N. + + +CONFIG_DVB_DEVFS_ONLY + Drop support for old major/minor device scheme and support only devfs + systems. This saves some code. + + If unsure say N. + + diff --git a/linux/drivers/media/dvb/dvb-core/Config.in b/linux/drivers/media/dvb/dvb-core/Config.in new file mode 100644 index 000000000..9c43c6498 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/Config.in @@ -0,0 +1,7 @@ + +dep_tristate ' DVB Core Support' CONFIG_DVB_CORE $CONFIG_DVB + +if [ "$CONFIG_DVB_CORE" != "n" ]; then + dep_bool ' devfs only' CONFIG_DVB_DEVFS_ONLY $CONFIG_DVB_CORE $CONFIG_DEVFS_FS +fi + diff --git a/linux/drivers/media/dvb/dvb-core/Makefile b/linux/drivers/media/dvb/dvb-core/Makefile new file mode 100644 index 000000000..a32a5f553 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the kernel DVB device drivers. +# + +export-objs := dvb_ksyms.o + +dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ + dvb_frontend.o dvb_i2c.o dvb_net.o dvb_ksyms.o + +obj-$(CONFIG_DVB_CORE) += dvb-core.o + +include $(TOPDIR)/Rules.make + diff --git a/linux/drivers/media/dvb/dvb-core/compat.h b/linux/drivers/media/dvb/dvb-core/compat.h new file mode 100644 index 000000000..25af8145b --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/compat.h @@ -0,0 +1,24 @@ +#ifndef __CRAP_H +#define __CRAP_H + +/** + * compatibility crap for old kernels. No guarantee for a working driver + * even when everything compiles. + */ + + +#include <linux/module.h> +#include <linux/list.h> + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(x) +#endif + +#ifndef list_for_each_safe +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) +#endif + +#endif + diff --git a/linux/drivers/media/dvb/dvb-core/demux.h b/linux/drivers/media/dvb/dvb-core/demux.h new file mode 100644 index 000000000..ce769ebf1 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/demux.h @@ -0,0 +1,301 @@ +/* demux.h + * + * Copyright (c) 2002 Convergence GmbH + * + * based on code: + * Copyright (c) 2000 Nokia Research Center + * Tampere, FINLAND + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef __DEMUX_H +#define __DEMUX_H + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/time.h> +#include <linux/errno.h> + +/*--------------------------------------------------------------------------*/ +/* Common definitions */ +/*--------------------------------------------------------------------------*/ + +/* + * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter. + */ + +#ifndef DMX_MAX_FILTER_SIZE +#define DMX_MAX_FILTER_SIZE 18 +#endif + +/* + * dmx_success_t: Success codes for the Demux Callback API. + */ + +typedef enum { + DMX_OK = 0, /* Received Ok */ + DMX_LENGTH_ERROR, /* Incorrect length */ + DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */ + DMX_CRC_ERROR, /* Incorrect CRC */ + DMX_FRAME_ERROR, /* Frame alignment error */ + DMX_FIFO_ERROR, /* Receiver FIFO overrun */ + DMX_MISSED_ERROR /* Receiver missed packet */ +} dmx_success_t; + +/*--------------------------------------------------------------------------*/ +/* TS packet reception */ +/*--------------------------------------------------------------------------*/ + +/* TS filter type for set() */ + +#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */ +#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS + payload (<=184 bytes per packet) to callback */ +#define TS_DECODER 4 /* send stream to built-in decoder (if present) */ + +/* PES type for filters which write to built-in decoder */ +/* these should be kept identical to the types in dmx.h */ + +typedef enum +{ /* also send packets to decoder (if it exists) */ + DMX_TS_PES_AUDIO0, + DMX_TS_PES_VIDEO0, + DMX_TS_PES_TELETEXT0, + DMX_TS_PES_SUBTITLE0, + DMX_TS_PES_PCR0, + + DMX_TS_PES_AUDIO1, + DMX_TS_PES_VIDEO1, + DMX_TS_PES_TELETEXT1, + DMX_TS_PES_SUBTITLE1, + DMX_TS_PES_PCR1, + + DMX_TS_PES_AUDIO2, + DMX_TS_PES_VIDEO2, + DMX_TS_PES_TELETEXT2, + DMX_TS_PES_SUBTITLE2, + DMX_TS_PES_PCR2, + + DMX_TS_PES_AUDIO3, + DMX_TS_PES_VIDEO3, + DMX_TS_PES_TELETEXT3, + DMX_TS_PES_SUBTITLE3, + DMX_TS_PES_PCR3, + + DMX_TS_PES_OTHER +} dmx_ts_pes_t; + +#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0 +#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0 +#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0 +#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0 +#define DMX_TS_PES_PCR DMX_TS_PES_PCR0 + + +struct dmx_ts_feed_s { + int is_filtering; /* Set to non-zero when filtering in progress */ + struct dmx_demux_s *parent; /* Back-pointer */ + void *priv; /* Pointer to private data of the API client */ + int (*set) (struct dmx_ts_feed_s *feed, + uint16_t pid, + int type, + dmx_ts_pes_t pes_type, + size_t callback_length, + size_t circular_buffer_size, + int descramble, + struct timespec timeout); + int (*start_filtering) (struct dmx_ts_feed_s* feed); + int (*stop_filtering) (struct dmx_ts_feed_s* feed); +}; + +typedef struct dmx_ts_feed_s dmx_ts_feed_t; + +/*--------------------------------------------------------------------------*/ +/* Section reception */ +/*--------------------------------------------------------------------------*/ + +typedef struct { + __u8 filter_value [DMX_MAX_FILTER_SIZE]; + __u8 filter_mask [DMX_MAX_FILTER_SIZE]; + __u8 filter_mode [DMX_MAX_FILTER_SIZE]; + struct dmx_section_feed_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ +} dmx_section_filter_t; + +struct dmx_section_feed_s { + int is_filtering; /* Set to non-zero when filtering in progress */ + struct dmx_demux_s* parent; /* Back-pointer */ + void* priv; /* Pointer to private data of the API client */ + int (*set) (struct dmx_section_feed_s* feed, + __u16 pid, + size_t circular_buffer_size, + int descramble, + int check_crc); + int (*allocate_filter) (struct dmx_section_feed_s* feed, + dmx_section_filter_t** filter); + int (*release_filter) (struct dmx_section_feed_s* feed, + dmx_section_filter_t* filter); + int (*start_filtering) (struct dmx_section_feed_s* feed); + int (*stop_filtering) (struct dmx_section_feed_s* feed); +}; +typedef struct dmx_section_feed_s dmx_section_feed_t; + +/*--------------------------------------------------------------------------*/ +/* Callback functions */ +/*--------------------------------------------------------------------------*/ + +typedef int (*dmx_ts_cb) ( __u8 * buffer1, + size_t buffer1_length, + __u8 * buffer2, + size_t buffer2_length, + dmx_ts_feed_t* source, + dmx_success_t success); + +typedef int (*dmx_section_cb) ( __u8 * buffer1, + size_t buffer1_len, + __u8 * buffer2, + size_t buffer2_len, + dmx_section_filter_t * source, + dmx_success_t success); + +/*--------------------------------------------------------------------------*/ +/* DVB Front-End */ +/*--------------------------------------------------------------------------*/ + +typedef enum { + DMX_MEMORY_FE, + DMX_FRONTEND_0, + DMX_FRONTEND_1, + DMX_FRONTEND_2, + DMX_FRONTEND_3, + DMX_STREAM_0, /* external stream input, e.g. LVDS */ + DMX_STREAM_1, + DMX_STREAM_2, + DMX_STREAM_3 +} dmx_frontend_source_t; + +typedef struct { + /* The following char* fields point to NULL terminated strings */ + char* id; /* Unique front-end identifier */ + char* vendor; /* Name of the front-end vendor */ + char* model; /* Name of the front-end model */ + struct list_head connectivity_list; /* List of front-ends that can + be connected to a particular + demux */ + void* priv; /* Pointer to private data of the API client */ + dmx_frontend_source_t source; +} dmx_frontend_t; + +/*--------------------------------------------------------------------------*/ +/* MPEG-2 TS Demux */ +/*--------------------------------------------------------------------------*/ + +/* + * Flags OR'ed in the capabilites field of struct dmx_demux_s. + */ + +#define DMX_TS_FILTERING 1 +#define DMX_PES_FILTERING 2 +#define DMX_SECTION_FILTERING 4 +#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */ +#define DMX_CRC_CHECKING 16 +#define DMX_TS_DESCRAMBLING 32 +#define DMX_SECTION_PAYLOAD_DESCRAMBLING 64 +#define DMX_MAC_ADDRESS_DESCRAMBLING 128 + +/* + * Demux resource type identifier. +*/ + +/* + * DMX_FE_ENTRY(): Casts elements in the list of registered + * front-ends from the generic type struct list_head + * to the type * dmx_frontend_t + *. +*/ + +#define DMX_FE_ENTRY(list) list_entry(list, dmx_frontend_t, connectivity_list) + +struct dmx_demux_s { + /* The following char* fields point to NULL terminated strings */ + char* id; /* Unique demux identifier */ + char* vendor; /* Name of the demux vendor */ + char* model; /* Name of the demux model */ + __u32 capabilities; /* Bitfield of capability flags */ + dmx_frontend_t* frontend; /* Front-end connected to the demux */ + struct list_head reg_list; /* List of registered demuxes */ + void* priv; /* Pointer to private data of the API client */ + int users; /* Number of users */ + int (*open) (struct dmx_demux_s* demux); + int (*close) (struct dmx_demux_s* demux); + int (*write) (struct dmx_demux_s* demux, const char* buf, size_t count); + int (*allocate_ts_feed) (struct dmx_demux_s* demux, + dmx_ts_feed_t** feed, + dmx_ts_cb callback); + int (*release_ts_feed) (struct dmx_demux_s* demux, + dmx_ts_feed_t* feed); + int (*allocate_section_feed) (struct dmx_demux_s* demux, + dmx_section_feed_t** feed, + dmx_section_cb callback); + int (*release_section_feed) (struct dmx_demux_s* demux, + dmx_section_feed_t* feed); + int (*descramble_mac_address) (struct dmx_demux_s* demux, + __u8* buffer1, + size_t buffer1_length, + __u8* buffer2, + size_t buffer2_length, + __u16 pid); + int (*descramble_section_payload) (struct dmx_demux_s* demux, + __u8* buffer1, + size_t buffer1_length, + __u8* buffer2, size_t buffer2_length, + __u16 pid); + int (*add_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + int (*remove_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + struct list_head* (*get_frontends) (struct dmx_demux_s* demux); + int (*connect_frontend) (struct dmx_demux_s* demux, + dmx_frontend_t* frontend); + int (*disconnect_frontend) (struct dmx_demux_s* demux); + + int (*get_pes_pids) (struct dmx_demux_s* demux, __u16 *pids); +}; +typedef struct dmx_demux_s dmx_demux_t; + +/*--------------------------------------------------------------------------*/ +/* Demux directory */ +/*--------------------------------------------------------------------------*/ + +/* + * DMX_DIR_ENTRY(): Casts elements in the list of registered + * demuxes from the generic type struct list_head* to the type dmx_demux_t + *. + */ + +#define DMX_DIR_ENTRY(list) list_entry(list, dmx_demux_t, reg_list) + +int dmx_register_demux (dmx_demux_t* demux); +int dmx_unregister_demux (dmx_demux_t* demux); +struct list_head* dmx_get_demuxes (void); + +#endif /* #ifndef __DEMUX_H */ + diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c new file mode 100644 index 000000000..d388a1b62 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -0,0 +1,1113 @@ +/* + * dmxdev.c - DVB demultiplexer device + * + * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> + * & Marcus Metzler <marcus@convergence.de> + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/vmalloc.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <asm/uaccess.h> + +#include "dmxdev.h" + + +//MODULE_DESCRIPTION(""); +//MODULE_AUTHOR("Ralph Metzler, Marcus Metzler"); +//#ifdef MODULE_LICENSE +//MODULE_LICENSE("GPL"); +//#endif +MODULE_PARM(debug,"i"); +static int debug = 0; + +#define dprintk if (debug) printk + +inline dmxdev_filter_t * +DmxDevFile2Filter(struct file *file) +{ + return (dmxdev_filter_t *) file->private_data; +} + +inline dmxdev_dvr_t * +DmxDevFile2DVR(dmxdev_t *dmxdev, struct file *file) +{ + return (dmxdev_dvr_t *) file->private_data; +} + +static inline void +DmxDevBufferInit(dmxdev_buffer_t *buffer) +{ + buffer->data=0; + buffer->size=8192; + buffer->pread=0; + buffer->pwrite=0; + buffer->error=0; + init_waitqueue_head(&buffer->queue); +} + +static inline int +DmxDevBufferWrite(dmxdev_buffer_t *buf, uint8_t *src, int len) +{ + int split; + int free; + int todo; + + if (!len) + return 0; + if (!buf->data) + return 0; + + free=buf->pread-buf->pwrite; + split=0; + if (free<=0) { + free+=buf->size; + split=buf->size-buf->pwrite; + } + if (len>=free) { + dprintk("dmxdev: buffer overflow\n"); + return -1; + } + if (split>=len) + split=0; + todo=len; + if (split) { + memcpy(buf->data + buf->pwrite, src, split); + todo-=split; + buf->pwrite=0; + } + memcpy(buf->data + buf->pwrite, src+split, todo); + buf->pwrite=(buf->pwrite+todo)%buf->size; + return len; +} + +static ssize_t +DmxDevBufferRead(dmxdev_buffer_t *src, int non_blocking, + char *buf, size_t count, loff_t *ppos) +{ + unsigned long todo=count; + int split, avail, error; + + if (!src->data) + return 0; + + if ((error=src->error)) { + src->error=0; + return error; + } + + if (non_blocking && (src->pwrite==src->pread)) + return -EWOULDBLOCK; + + while (todo>0) { + if (non_blocking && (src->pwrite==src->pread)) + return (count-todo) ? (count-todo) : -EWOULDBLOCK; + + if (wait_event_interruptible(src->queue, + (src->pread!=src->pwrite) || + (src->error))<0) + return count-todo; + + if ((error=src->error)) { + src->error=0; + return error; + } + + split=src->size; + avail=src->pwrite - src->pread; + if (avail<0) { + avail+=src->size; + split=src->size - src->pread; + } + if (avail>todo) + avail=todo; + if (split<avail) { + if (copy_to_user(buf, src->data+src->pread, split)) + return -EFAULT; + buf+=split; + src->pread=0; + todo-=split; + avail-=split; + } + if (avail) { + if (copy_to_user(buf, src->data+src->pread, avail)) + return -EFAULT; + src->pread = (src->pread + avail) % src->size; + todo-=avail; + buf+=avail; + } + } + return count; +} + +static dmx_frontend_t * +get_fe(dmx_demux_t *demux, int type) +{ + struct list_head *head, *pos; + + head=demux->get_frontends(demux); + if (!head) + return 0; + list_for_each(pos, head) + if (DMX_FE_ENTRY(pos)->source==type) + return DMX_FE_ENTRY(pos); + + return 0; +} + +static inline void +DmxDevDVRStateSet(dmxdev_dvr_t *dmxdevdvr, int state) +{ + spin_lock_irq(&dmxdevdvr->dev->lock); + dmxdevdvr->state=state; + spin_unlock_irq(&dmxdevdvr->dev->lock); +} + +static int dvb_dvr_open(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + dmx_frontend_t *front; + + dprintk ("function : %s\n", __FUNCTION__); + + if (down_interruptible (&dmxdev->mutex)) + return -ERESTARTSYS; + + if ((file->f_flags&O_ACCMODE)==O_RDWR) { + if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) { + up(&dmxdev->mutex); + return -EOPNOTSUPP; + } + } + + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + DmxDevBufferInit(&dmxdev->dvr_buffer); + dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE; + dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE); + if (!dmxdev->dvr_buffer.data) { + up(&dmxdev->mutex); + return -ENOMEM; + } + } + + if ((file->f_flags&O_ACCMODE)==O_WRONLY) { + dmxdev->dvr_orig_fe=dmxdev->demux->frontend; + + if (!dmxdev->demux->write) { + up(&dmxdev->mutex); + return -EOPNOTSUPP; + } + + front=get_fe(dmxdev->demux, DMX_MEMORY_FE); + + if (!front) { + up(&dmxdev->mutex); + return -EINVAL; + } + dmxdev->demux->disconnect_frontend(dmxdev->demux); + dmxdev->demux->connect_frontend(dmxdev->demux, front); + } + up(&dmxdev->mutex); + return 0; +} + +static int dvb_dvr_release(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + + if (down_interruptible (&dmxdev->mutex)) + return -ERESTARTSYS; + + if ((file->f_flags&O_ACCMODE)==O_WRONLY) { + dmxdev->demux->disconnect_frontend(dmxdev->demux); + dmxdev->demux->connect_frontend(dmxdev->demux, + dmxdev->dvr_orig_fe); + } + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + if (dmxdev->dvr_buffer.data) { + void *mem=dmxdev->dvr_buffer.data; + mb(); + spin_lock_irq(&dmxdev->lock); + dmxdev->dvr_buffer.data=0; + spin_unlock_irq(&dmxdev->lock); + vfree(mem); + } + } + up(&dmxdev->mutex); + return 0; +} + +static ssize_t +dvb_dvr_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + int ret; + + if (!dmxdev->demux->write) + return -EOPNOTSUPP; + if ((file->f_flags&O_ACCMODE)!=O_WRONLY) + return -EINVAL; + if (down_interruptible (&dmxdev->mutex)) + return -ERESTARTSYS; + ret=dmxdev->demux->write(dmxdev->demux, buf, count); + up(&dmxdev->mutex); + return ret; +} + +static ssize_t +dvb_dvr_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + int ret; + + //down(&dmxdev->mutex); + ret= DmxDevBufferRead(&dmxdev->dvr_buffer, + file->f_flags&O_NONBLOCK, + buf, count, ppos); + //up(&dmxdev->mutex); + return ret; +} + +static inline void +DmxDevFilterStateSet(dmxdev_filter_t *dmxdevfilter, int state) +{ + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->state=state; + spin_unlock_irq(&dmxdevfilter->dev->lock); +} + +static int +DmxDevSetBufferSize(dmxdev_filter_t *dmxdevfilter, unsigned long size) +{ + dmxdev_buffer_t *buf=&dmxdevfilter->buffer; + void *mem; + + if (buf->size==size) + return 0; + if (dmxdevfilter->state>=DMXDEV_STATE_GO) + return -EBUSY; + spin_lock_irq(&dmxdevfilter->dev->lock); + mem=buf->data; + buf->data=0; + buf->size=size; + buf->pwrite=buf->pread=0; + spin_unlock_irq(&dmxdevfilter->dev->lock); + if (mem) + vfree(mem); + + if (buf->size) { + mem=vmalloc(dmxdevfilter->buffer.size); + if (!mem) + return -ENOMEM; + spin_lock_irq(&dmxdevfilter->dev->lock); + buf->data=mem; + spin_unlock_irq(&dmxdevfilter->dev->lock); + } + return 0; +} + +static void +DmxDevFilterTimeout(unsigned long data) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *)data; + + dmxdevfilter->buffer.error=-ETIMEDOUT; + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT; + spin_unlock_irq(&dmxdevfilter->dev->lock); + wake_up(&dmxdevfilter->buffer.queue); +} + +static void +DmxDevFilterTimer(dmxdev_filter_t *dmxdevfilter) +{ + struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec; + + del_timer(&dmxdevfilter->timer); + if (para->timeout) { + dmxdevfilter->timer.function=DmxDevFilterTimeout; + dmxdevfilter->timer.data=(unsigned long) dmxdevfilter; + dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000; + add_timer(&dmxdevfilter->timer); + } +} + +static int +DmxDevSectionCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + dmx_section_filter_t *filter, + dmx_success_t success) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) filter->priv; + int ret; + + if (dmxdevfilter->buffer.error) { + wake_up(&dmxdevfilter->buffer.queue); + return 0; + } + spin_lock(&dmxdevfilter->dev->lock); + if (dmxdevfilter->state!=DMXDEV_STATE_GO) { + spin_unlock(&dmxdevfilter->dev->lock); + return 0; + } + del_timer(&dmxdevfilter->timer); + dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n", + buffer1[0], buffer1[1], + buffer1[2], buffer1[3], + buffer1[4], buffer1[5]); + ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer1, buffer1_len); + if (ret==buffer1_len) { + ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer2, buffer2_len); + } + if (ret<0) { + dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread; + dmxdevfilter->buffer.error=-EOVERFLOW; + } + if (dmxdevfilter->params.sec.flags&DMX_ONESHOT) + dmxdevfilter->state=DMXDEV_STATE_DONE; + spin_unlock(&dmxdevfilter->dev->lock); + wake_up(&dmxdevfilter->buffer.queue); + return 0; +} + +static int +DmxDevTSCallback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + dmx_ts_feed_t *feed, + dmx_success_t success) +{ + dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) feed->priv; + dmxdev_buffer_t *buffer; + int ret; + + spin_lock(&dmxdevfilter->dev->lock); + if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) { + spin_unlock(&dmxdevfilter->dev->lock); + return 0; + } + + if (dmxdevfilter->params.pes.output==DMX_OUT_TAP) + buffer=&dmxdevfilter->buffer; + else + buffer=&dmxdevfilter->dev->dvr_buffer; + if (buffer->error) { + spin_unlock(&dmxdevfilter->dev->lock); + wake_up(&buffer->queue); + return 0; + } + ret=DmxDevBufferWrite(buffer, buffer1, buffer1_len); + if (ret==buffer1_len) + ret=DmxDevBufferWrite(buffer, buffer2, buffer2_len); + if (ret<0) { + buffer->pwrite=buffer->pread; + buffer->error=-EOVERFLOW; + } + spin_unlock(&dmxdevfilter->dev->lock); + wake_up(&buffer->queue); + return 0; +} + + +/* stop feed but only mark the specified filter as stopped (state set) */ + +static int +DmxDevFeedStop(dmxdev_filter_t *dmxdevfilter) +{ + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + del_timer(&dmxdevfilter->timer); + dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); + break; + case DMXDEV_TYPE_PES: + dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts); + break; + default: + return -EINVAL; + } + return 0; +} + + +/* start feed associated with the specified filter */ + +static int +DmxDevFeedStart(dmxdev_filter_t *dmxdevfilter) +{ + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO); + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + dmxdevfilter->feed.sec->start_filtering(dmxdevfilter->feed.sec); + break; + case DMXDEV_TYPE_PES: + dmxdevfilter->feed.ts->start_filtering(dmxdevfilter->feed.ts); + break; + default: + return -EINVAL; + } + return 0; +} + + +/* restart section feed if it has filters left associated with it, + otherwise release the feed */ + +static int +DmxDevFeedRestart(dmxdev_filter_t *dmxdevfilter) +{ + int i; + dmxdev_t *dmxdev=dmxdevfilter->dev; + uint16_t pid=dmxdevfilter->params.sec.pid; + + for (i=0; i<dmxdev->filternum; i++) + if (dmxdev->filter[i].state>=DMXDEV_STATE_GO && + dmxdev->filter[i].type==DMXDEV_TYPE_SEC && + dmxdev->filter[i].pid==pid) { + DmxDevFeedStart(&dmxdev->filter[i]); + return 0; + } + + dmxdevfilter->dev->demux-> + release_section_feed(dmxdev->demux, + dmxdevfilter->feed.sec); + + return 0; +} + +static int +DmxDevFilterStop(dmxdev_filter_t *dmxdevfilter) +{ + if (dmxdevfilter->state<DMXDEV_STATE_GO) + return 0; + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + if (!dmxdevfilter->feed.sec) + break; + DmxDevFeedStop(dmxdevfilter); + if (dmxdevfilter->filter.sec) + dmxdevfilter->feed.sec-> + release_filter(dmxdevfilter->feed.sec, + dmxdevfilter->filter.sec); + DmxDevFeedRestart(dmxdevfilter); + dmxdevfilter->feed.sec=0; + break; + case DMXDEV_TYPE_PES: + if (!dmxdevfilter->feed.ts) + break; + DmxDevFeedStop(dmxdevfilter); + dmxdevfilter->dev->demux-> + release_ts_feed(dmxdevfilter->dev->demux, + dmxdevfilter->feed.ts); + dmxdevfilter->feed.ts=0; + break; + default: + if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED) + return 0; + return -EINVAL; + } + dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0; + return 0; +} + +static inline int +DmxDevFilterReset(dmxdev_filter_t *dmxdevfilter) +{ + if (dmxdevfilter->state<DMXDEV_STATE_SET) + return 0; + + dmxdevfilter->type=DMXDEV_TYPE_NONE; + dmxdevfilter->pid=0xffff; + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED); + return 0; +} + +static int +DmxDevFilterStart(dmxdev_filter_t *dmxdevfilter) +{ + dmxdev_t *dmxdev=dmxdevfilter->dev; + void *mem; + int ret, i; + + if (dmxdevfilter->state<DMXDEV_STATE_SET) + return -EINVAL; + if (dmxdevfilter->state>=DMXDEV_STATE_GO) + DmxDevFilterStop(dmxdevfilter); + + mem=dmxdevfilter->buffer.data; + if (!mem) { + mem=vmalloc(dmxdevfilter->buffer.size); + spin_lock_irq(&dmxdevfilter->dev->lock); + dmxdevfilter->buffer.data=mem; + spin_unlock_irq(&dmxdevfilter->dev->lock); + if (!dmxdevfilter->buffer.data) + return -ENOMEM; + } + + switch (dmxdevfilter->type) { + case DMXDEV_TYPE_SEC: + { + struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec; + dmx_section_filter_t **secfilter=&dmxdevfilter->filter.sec; + dmx_section_feed_t **secfeed=&dmxdevfilter->feed.sec; + + *secfilter=0; + *secfeed=0; + + /* find active filter/feed with same PID */ + for (i=0; i<dmxdev->filternum; i++) + if (dmxdev->filter[i].state>=DMXDEV_STATE_GO && + dmxdev->filter[i].pid==para->pid) { + if (dmxdev->filter[i].type!=DMXDEV_TYPE_SEC) + return -EBUSY; + *secfeed=dmxdev->filter[i].feed.sec; + break; + } + + /* if no feed found, try to allocate new one */ + if (!*secfeed) { + ret=dmxdev->demux-> + allocate_section_feed(dmxdev->demux, + secfeed, + DmxDevSectionCallback); + if (ret<0) { + printk ("DVB (%s): could not alloc feed\n", + __FUNCTION__); + return ret; + } + + ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0, + (para->flags & DMX_CHECK_CRC) ? 1 : 0); + + if (ret<0) { + printk ("DVB (%s): could not set feed\n", + __FUNCTION__); + DmxDevFeedRestart(dmxdevfilter); + return ret; + } + } + else + DmxDevFeedStop(dmxdevfilter); + + ret=(*secfeed)->allocate_filter(*secfeed, secfilter); + if (ret<0) { + DmxDevFeedRestart(dmxdevfilter); + dmxdevfilter->feed.sec-> + start_filtering(*secfeed); + dprintk ("could not get filter\n"); + return ret; + } + + (*secfilter)->priv=(void *) dmxdevfilter; + + memcpy(&((*secfilter)->filter_value[3]), + &(para->filter.filter[1]), DMX_FILTER_SIZE-1); + memcpy(&(*secfilter)->filter_mask[3], + ¶->filter.mask[1], DMX_FILTER_SIZE-1); + memcpy(&(*secfilter)->filter_mode[3], + ¶->filter.mode[1], DMX_FILTER_SIZE-1); + + (*secfilter)->filter_value[0]=para->filter.filter[0]; + (*secfilter)->filter_mask[0]=para->filter.mask[0]; + (*secfilter)->filter_mode[0]=para->filter.mode[0]; + (*secfilter)->filter_mask[1]=0; + (*secfilter)->filter_mask[2]=0; + + dmxdevfilter->todo=0; + dmxdevfilter->feed.sec-> + start_filtering(dmxdevfilter->feed.sec); + DmxDevFilterTimer(dmxdevfilter); + break; + } + + case DMXDEV_TYPE_PES: + { + struct timespec timeout = {0 }; + struct dmx_pes_filter_params *para=&dmxdevfilter->params.pes; + dmx_output_t otype; + int ret; + int ts_type; + dmx_ts_pes_t ts_pes; + dmx_ts_feed_t **tsfeed=&dmxdevfilter->feed.ts; + + dmxdevfilter->feed.ts=0; + otype=para->output; + + ts_pes=(dmx_ts_pes_t) para->pes_type; + + if (ts_pes<DMX_PES_OTHER) + ts_type=TS_DECODER; + else + ts_type=0; + + if (otype==DMX_OUT_TS_TAP) + ts_type|=TS_PACKET; + + if (otype==DMX_OUT_TAP) + ts_type|=TS_PAYLOAD_ONLY|TS_PACKET; + + ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux, + tsfeed, + DmxDevTSCallback); + if (ret<0) + return ret; + + (*tsfeed)->priv=(void *) dmxdevfilter; + ret=(*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, 188, 32768, 0, timeout); + if (ret<0) { + dmxdev->demux-> + release_ts_feed(dmxdev->demux, *tsfeed); + return ret; + } + dmxdevfilter->feed.ts-> + start_filtering(dmxdevfilter->feed.ts); + break; + } + default: + return -EINVAL; + } + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO); + return 0; +} + +static int dvb_demux_open(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + int i; + dmxdev_filter_t *dmxdevfilter; + + if (!dmxdev->filter) + return -EINVAL; + if (down_interruptible(&dmxdev->mutex)) + return -ERESTARTSYS; + for (i=0; i<dmxdev->filternum; i++) + if (dmxdev->filter[i].state==DMXDEV_STATE_FREE) + break; + if (i==dmxdev->filternum) { + up(&dmxdev->mutex); + return -EMFILE; + } + dmxdevfilter=&dmxdev->filter[i]; + dmxdevfilter->dvbdev=dmxdev->dvbdev; + file->private_data=dmxdevfilter; + + DmxDevBufferInit(&dmxdevfilter->buffer); + dmxdevfilter->type=DMXDEV_TYPE_NONE; + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED); + dmxdevfilter->feed.ts=0; + init_timer(&dmxdevfilter->timer); + + up(&dmxdev->mutex); + return 0; +} + +int +DmxDevFilterFree(dmxdev_t *dmxdev, dmxdev_filter_t *dmxdevfilter) +{ + if (down_interruptible(&dmxdev->mutex)) + return -ERESTARTSYS; + + DmxDevFilterStop(dmxdevfilter); + DmxDevFilterReset(dmxdevfilter); + + if (dmxdevfilter->buffer.data) { + void *mem=dmxdevfilter->buffer.data; + + spin_lock_irq(&dmxdev->lock); + dmxdevfilter->buffer.data=0; + spin_unlock_irq(&dmxdev->lock); + vfree(mem); + } + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_FREE); + wake_up(&dmxdevfilter->buffer.queue); + up(&dmxdev->mutex); + return 0; +} + +static inline void +invert_mode(dmx_filter_t *filter) +{ + int i; + + for (i=0; i<DMX_FILTER_SIZE; i++) + filter->mode[i]^=0xff; +} + + +static int +DmxDevFilterSet(dmxdev_t *dmxdev, + dmxdev_filter_t *dmxdevfilter, + struct dmx_sct_filter_params *params) +{ + dprintk ("function : %s\n", __FUNCTION__); + + DmxDevFilterStop(dmxdevfilter); + + dmxdevfilter->type=DMXDEV_TYPE_SEC; + dmxdevfilter->pid=params->pid; + memcpy(&dmxdevfilter->params.sec, + params, sizeof(struct dmx_sct_filter_params)); + invert_mode(&dmxdevfilter->params.sec.filter); + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + if (params->flags&DMX_IMMEDIATE_START) + return DmxDevFilterStart(dmxdevfilter); + + return 0; +} + +static int +DmxDevPesFilterSet(dmxdev_t *dmxdev, + dmxdev_filter_t *dmxdevfilter, + struct dmx_pes_filter_params *params) +{ + DmxDevFilterStop(dmxdevfilter); + + if (params->pes_type>DMX_PES_OTHER || params->pes_type<0) + return -EINVAL; + + dmxdevfilter->type=DMXDEV_TYPE_PES; + dmxdevfilter->pid=params->pid; + memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params)); + + DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET); + + if (params->flags&DMX_IMMEDIATE_START) + return DmxDevFilterStart(dmxdevfilter); + + return 0; +} + +static ssize_t +DmxDevReadSec(dmxdev_filter_t *dfil, struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + int result, hcount; + int done=0; + + if (dfil->todo<=0) { + hcount=3+dfil->todo; + if (hcount>count) + hcount=count; + result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK, + buf, hcount, ppos); + if (result<0) { + dfil->todo=0; + return result; + } + if (copy_from_user(dfil->secheader-dfil->todo, buf, result)) + return -EFAULT; + buf+=result; + done=result; + count-=result; + dfil->todo-=result; + if (dfil->todo>-3) + return done; + dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff; + if (!count) + return done; + } + if (count>dfil->todo) + count=dfil->todo; + result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK, + buf, count, ppos); + if (result<0) + return result; + dfil->todo-=result; + return (result+done); +} + + +ssize_t +dvb_demux_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file); + //dmxdev_t *dmxdev=dmxdevfilter->dev; + int ret=0; + + // semaphore should not be necessary (I hope ...) + //down(&dmxdev->mutex); + if (dmxdevfilter->type==DMXDEV_TYPE_SEC) + ret=DmxDevReadSec(dmxdevfilter, file, buf, count, ppos); + else + ret=DmxDevBufferRead(&dmxdevfilter->buffer, + file->f_flags&O_NONBLOCK, + buf, count, ppos); + //up(&dmxdev->mutex); + return ret; +} + + +static int dvb_demux_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file); + dmxdev_t *dmxdev=dmxdevfilter->dev; + unsigned long arg=(unsigned long) parg; + int ret=0; + + if (down_interruptible (&dmxdev->mutex)) + return -ERESTARTSYS; + + switch (cmd) { + case DMX_START: + if (dmxdevfilter->state<DMXDEV_STATE_SET) + ret=-EINVAL; + else + ret=DmxDevFilterStart(dmxdevfilter); + break; + + case DMX_STOP: + ret=DmxDevFilterStop(dmxdevfilter); + break; + + case DMX_SET_FILTER: + ret=DmxDevFilterSet(dmxdev, dmxdevfilter, + (struct dmx_sct_filter_params *)parg); + break; + + case DMX_SET_PES_FILTER: + ret=DmxDevPesFilterSet(dmxdev, dmxdevfilter, + (struct dmx_pes_filter_params *)parg); + break; + + case DMX_SET_BUFFER_SIZE: + ret=DmxDevSetBufferSize(dmxdevfilter, arg); + break; + + case DMX_GET_EVENT: + break; + + case DMX_GET_PES_PIDS: + if (!dmxdev->demux->get_pes_pids) { + ret=-EINVAL; + break; + } + dmxdev->demux->get_pes_pids(dmxdev->demux, (uint16_t *)parg); + break; + + default: + ret=-EINVAL; + } + up(&dmxdev->mutex); + return ret; +} + +static int dvb_demux_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return generic_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl); +} + + +static unsigned int dvb_demux_poll(struct file *file, poll_table *wait) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file); + + if (!dmxdevfilter) + return -EINVAL; + + if (dmxdevfilter->state==DMXDEV_STATE_FREE) + return 0; + + if (dmxdevfilter->buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI | POLLERR); + + if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + if (dmxdevfilter->state!=DMXDEV_STATE_GO) + return 0; + + poll_wait(file, &dmxdevfilter->buffer.queue, wait); + + if (dmxdevfilter->state==DMXDEV_STATE_FREE) + return 0; + + if (dmxdevfilter->buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI | POLLERR); + + if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + return 0; +} + +static int dvb_demux_release(struct inode *inode, struct file *file) +{ + dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file); + dmxdev_t *dmxdev=dmxdevfilter->dev; + + return DmxDevFilterFree(dmxdev, dmxdevfilter); +} + +static struct file_operations dvb_demux_fops = { + owner: THIS_MODULE, + read: dvb_demux_read, + write: 0, + ioctl: dvb_demux_ioctl, + open: dvb_demux_open, + release: dvb_demux_release, + poll: dvb_demux_poll, +}; + +static dvb_device_t dvbdev_demux = { + priv: 0, + users: 1, + writers: 1, + fops: &dvb_demux_fops +}; + +static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + + int ret=0; + + if (down_interruptible (&dmxdev->mutex)) + return -ERESTARTSYS; + + switch (cmd) { + case DMX_SET_BUFFER_SIZE: + // FIXME: implement + ret=0; + break; + + default: + ret=-EINVAL; + } + up(&dmxdev->mutex); + return ret; +} + +static int dvb_dvr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return generic_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl); +} + + +static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv; + + dprintk ("function : %s\n", __FUNCTION__); + + if ((file->f_flags&O_ACCMODE)==O_RDONLY) { + if (dmxdev->dvr_buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI | POLLERR); + + if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + poll_wait(file, &dmxdev->dvr_buffer.queue, wait); + + if (dmxdev->dvr_buffer.error) + return (POLLIN | POLLRDNORM | POLLPRI | POLLERR); + + if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite) + return (POLLIN | POLLRDNORM | POLLPRI); + + return 0; + } else + return (POLLOUT | POLLWRNORM | POLLPRI); +} + +static struct file_operations dvb_dvr_fops = { + owner: THIS_MODULE, + read: dvb_dvr_read, + write: dvb_dvr_write, + ioctl: dvb_dvr_ioctl, + open: dvb_dvr_open, + release: dvb_dvr_release, + poll: dvb_dvr_poll, +}; + +static dvb_device_t dvbdev_dvr = { + priv: 0, + users: 1, + writers: 1, + fops: &dvb_dvr_fops +}; + +int +DmxDevInit(dmxdev_t *dmxdev, dvb_adapter_t *dvb_adapter) +{ + int i; + + if (dmxdev->demux->open(dmxdev->demux)<0) + return -EUSERS; + + dmxdev->filter=vmalloc(dmxdev->filternum*sizeof(dmxdev_filter_t)); + if (!dmxdev->filter) + return -ENOMEM; + + dmxdev->dvr=vmalloc(dmxdev->filternum*sizeof(dmxdev_dvr_t)); + if (!dmxdev->dvr) { + vfree(dmxdev->filter); + dmxdev->filter=0; + return -ENOMEM; + } + sema_init(&dmxdev->mutex, 1); + spin_lock_init(&dmxdev->lock); + for (i=0; i<dmxdev->filternum; i++) { + dmxdev->filter[i].dev=dmxdev; + dmxdev->filter[i].buffer.data=0; + DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE); + dmxdev->dvr[i].dev=dmxdev; + dmxdev->dvr[i].buffer.data=0; + DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE); + DmxDevDVRStateSet(&dmxdev->dvr[i], DMXDEV_STATE_FREE); + } + dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX); + dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR); + + DmxDevBufferInit(&dmxdev->dvr_buffer); + MOD_INC_USE_COUNT; + return 0; +} + +void +DmxDevRelease(dmxdev_t *dmxdev) +{ + dvb_unregister_device(dmxdev->dvbdev); + dvb_unregister_device(dmxdev->dvr_dvbdev); + if (dmxdev->filter) { + vfree(dmxdev->filter); + dmxdev->filter=0; + } + if (dmxdev->dvr) { + vfree(dmxdev->dvr); + dmxdev->dvr=0; + } + dmxdev->demux->close(dmxdev->demux); + MOD_DEC_USE_COUNT; +} + + diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.h b/linux/drivers/media/dvb/dvb-core/dmxdev.h new file mode 100644 index 000000000..c5fa89d51 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.h @@ -0,0 +1,128 @@ +/* + * dmxdev.h + * + * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> + * & Marcus Metzler <marcus@convergence.de> + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DMXDEV_H_ +#define _DMXDEV_H_ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include <linux/dvb/dmx.h> + +#include <linux/version.h> +#include <linux/wait.h> +#include <linux/types.h> +#include <linux/fs.h> + +#include "dvbdev.h" +#include "demux.h" + +typedef enum { + DMXDEV_TYPE_NONE, + DMXDEV_TYPE_SEC, + DMXDEV_TYPE_PES, +} dmxdev_type_t; + +typedef enum { + DMXDEV_STATE_FREE, + DMXDEV_STATE_ALLOCATED, + DMXDEV_STATE_SET, + DMXDEV_STATE_GO, + DMXDEV_STATE_DONE, + DMXDEV_STATE_TIMEDOUT +} dmxdev_state_t; + +typedef struct dmxdev_buffer_s { + uint8_t *data; + uint32_t size; + int32_t pread; + int32_t pwrite; + wait_queue_head_t queue; + int error; +} dmxdev_buffer_t; + + +typedef struct dmxdev_filter_s { + dvb_device_t *dvbdev; + + union { + dmx_section_filter_t *sec; + } filter; + + union { + dmx_ts_feed_t *ts; + dmx_section_feed_t *sec; + } feed; + + union { + struct dmx_sct_filter_params sec; + struct dmx_pes_filter_params pes; + } params; + + int type; + dmxdev_state_t state; + struct dmxdev_s *dev; + dmxdev_buffer_t buffer; + + // only for sections + struct timer_list timer; + int todo; + uint8_t secheader[3]; + + u16 pid; +} dmxdev_filter_t; + + +typedef struct dmxdev_dvr_s { + int state; + struct dmxdev_s *dev; + dmxdev_buffer_t buffer; +} dmxdev_dvr_t; + + +typedef struct dmxdev_s { + dvb_device_t *dvbdev; + dvb_device_t *dvr_dvbdev; + + dmxdev_filter_t *filter; + dmxdev_dvr_t *dvr; + dmx_demux_t *demux; + + int filternum; + int capabilities; +#define DMXDEV_CAP_DUPLEX 1 + dmx_frontend_t *dvr_orig_fe; + + dmxdev_buffer_t dvr_buffer; +#define DVR_BUFFER_SIZE (10*188*1024) + + struct semaphore mutex; + spinlock_t lock; +} dmxdev_t; + + +int DmxDevInit(dmxdev_t *dmxdev, dvb_adapter_t *); +void DmxDevRelease(dmxdev_t *dmxdev); + +#endif /* _DMXDEV_H_ */ diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c new file mode 100644 index 000000000..3a3e3834a --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c @@ -0,0 +1,1179 @@ +/* + * dvb_demux.c - DVB kernel demux API + * + * Copyright (C) 2000-2001 Ralph Metzler <ralph@convergence.de> + * & Marcus Metzler <marcus@convergence.de> + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/vmalloc.h> +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/version.h> +#include <asm/uaccess.h> + +#include "compat.h" +#include "dvb_demux.h" + +#define NOBUFS + +LIST_HEAD(dmx_muxs); + +int dmx_register_demux(dmx_demux_t *demux) +{ + struct list_head *pos, *head=&dmx_muxs; + + if (!(demux->id && demux->vendor && demux->model)) + return -EINVAL; + list_for_each(pos, head) + { + if (!strcmp(DMX_DIR_ENTRY(pos)->id, demux->id)) + return -EEXIST; + } + demux->users=0; + list_add(&(demux->reg_list), head); + MOD_INC_USE_COUNT; + return 0; +} + +int dmx_unregister_demux(dmx_demux_t* demux) +{ + struct list_head *pos, *n, *head=&dmx_muxs; + + list_for_each_safe (pos, n, head) + { + if (DMX_DIR_ENTRY(pos)==demux) + { + if (demux->users>0) + return -EINVAL; + list_del(pos); + MOD_DEC_USE_COUNT; + return 0; + } + } + return -ENODEV; +} + + +struct list_head *dmx_get_demuxes(void) +{ + if (list_empty(&dmx_muxs)) + return NULL; + + return &dmx_muxs; +} + +/****************************************************************************** + * static inlined helper functions + ******************************************************************************/ + +static inline u16 +section_length(const u8 *buf) +{ + return 3+((buf[1]&0x0f)<<8)+buf[2]; +} + +static inline u16 +ts_pid(const u8 *buf) +{ + return ((buf[1]&0x1f)<<8)+buf[2]; +} + +static inline int +payload(const u8 *tsp) +{ + if (!(tsp[3]&0x10)) // no payload? + return 0; + if (tsp[3]&0x20) { // adaptation field? + if (tsp[4]>183) // corrupted data? + return 0; + else + return 184-1-tsp[4]; + } + return 184; +} + + +static u32 +dvb_crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; + +u32 dvb_crc32(u8 *data, int len) +{ + int i; + u32 crc = 0xffffffff; + + for (i=0; i<len; i++) + crc = (crc << 8) ^ dvb_crc_table[((crc >> 24) ^ *data++) & 0xff]; + return crc; +} + +void dvb_set_crc32(u8 *data, int length) +{ + u32 crc; + + crc=dvb_crc32(data,length); + data[length] = (crc>>24)&0xff; + data[length+1] = (crc>>16)&0xff; + data[length+2] = (crc>>8)&0xff; + data[length+3] = (crc)&0xff; +} + + +/****************************************************************************** + * Software filter functions + ******************************************************************************/ + +static inline int +DvbDmxSWFilterPayload(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + int p, count; + //int ccok; + //u8 cc; + + if (!(count=payload(buf))) + return -1; + p=188-count; + /* + cc=buf[3]&0x0f; + ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0; + dvbdmxfeed->cc=cc; + if (!ccok) + printk("missed packet!\n"); + */ + if (buf[1]&0x40) // PUSI ? + dvbdmxfeed->peslen=0xfffa; + dvbdmxfeed->peslen+=count; + + return dvbdmxfeed->cb.ts((u8 *)&buf[p], count, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); +} + + +static int +DvbDmxSWFilterSectionFilter(dvb_demux_feed_t *dvbdmxfeed, + dvb_demux_filter_t *f) +{ + dmx_section_filter_t *filter=&f->filter; + int i; + u8 xor, neq=0; + + for (i=0; i<DVB_DEMUX_MASK_MAX; i++) { + xor=filter->filter_value[i]^dvbdmxfeed->secbuf[i]; + if (f->maskandmode[i]&xor) + return 0; + neq|=f->maskandnotmode[i]&xor; + } + if (f->doneq & !neq) + return 0; + + return dvbdmxfeed->cb.sec(dvbdmxfeed->secbuf, dvbdmxfeed->seclen, + 0, 0, filter, DMX_OK); +} + +static inline int +DvbDmxSWFilterSectionFeed(dvb_demux_feed_t *dvbdmxfeed) +{ + u8 *buf=dvbdmxfeed->secbuf; + dvb_demux_filter_t *f; + + if (dvbdmxfeed->secbufp!=dvbdmxfeed->seclen) + return -1; + if (!dvbdmxfeed->feed.sec.is_filtering) + return 0; + if (!(f=dvbdmxfeed->filter)) + return 0; + do + if (DvbDmxSWFilterSectionFilter(dvbdmxfeed, f)<0) + return -1; + while ((f=f->next) && dvbdmxfeed->feed.sec.is_filtering); + + dvbdmxfeed->secbufp=dvbdmxfeed->seclen=0; + memset(buf, 0, DVB_DEMUX_MASK_MAX); + return 0; +} + +static inline int +DvbDmxSWFilterSectionPacket(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + int p, count; + int ccok, rest; + u8 cc; + + if (!(count=payload(buf))) + return -1; + p=188-count; + + cc=buf[3]&0x0f; + ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0; + dvbdmxfeed->cc=cc; + + if (buf[1]&0x40) { // PUSI set + // offset to start of first section is in buf[p] + if (p+buf[p]>187) // trash if it points beyond packet + return -1; + if (buf[p] && ccok) { // rest of previous section? + // did we have enough data in last packet to calc length? + int tmp=3-dvbdmxfeed->secbufp; + if (tmp>0 && tmp!=3) { + if (p+tmp>=187) + return -1; + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, + buf+p+1, tmp); + dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf); + if (dvbdmxfeed->seclen>4096) + return -1; + } + rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp; + if (rest==buf[p] && dvbdmxfeed->seclen) { + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, + buf+p+1, buf[p]); + dvbdmxfeed->secbufp+=buf[p]; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + } + } + p+=buf[p]+1; // skip rest of last section + count=188-p; + while (count>0) { + if ((count>2) && // enough data to determine sec length? + ((dvbdmxfeed->seclen=section_length(buf+p))<=count)) { + if (dvbdmxfeed->seclen>4096) + return -1; + memcpy(dvbdmxfeed->secbuf, buf+p, + dvbdmxfeed->seclen); + dvbdmxfeed->secbufp=dvbdmxfeed->seclen; + p+=dvbdmxfeed->seclen; + count=188-p; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + + // filling bytes until packet end? + if (count && buf[p]==0xff) + count=0; + } else { // section continues to following TS packet + memcpy(dvbdmxfeed->secbuf, buf+p, count); + dvbdmxfeed->secbufp+=count; + count=0; + } + } + return 0; + } + + // section continued below + if (!ccok) + return -1; + if (!dvbdmxfeed->secbufp) // any data in last ts packet? + return -1; + // did we have enough data in last packet to calc section length? + if (dvbdmxfeed->secbufp<3) { + int tmp=3-dvbdmxfeed->secbufp; + + if (tmp>count) + return -1; + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, tmp); + dvbdmxfeed->seclen=section_length(dvbdmxfeed->secbuf); + if (dvbdmxfeed->seclen>4096) + return -1; + } + rest=dvbdmxfeed->seclen-dvbdmxfeed->secbufp; + if (rest<0) + return -1; + if (rest<=count) { // section completed in this TS packet + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, rest); + dvbdmxfeed->secbufp+=rest; + DvbDmxSWFilterSectionFeed(dvbdmxfeed); + } else { // section continues in following ts packet + memcpy(dvbdmxfeed->secbuf+dvbdmxfeed->secbufp, buf+p, count); + dvbdmxfeed->secbufp+=count; + } + return 0; +} + +static inline void +DvbDmxSWFilterPacketType(dvb_demux_feed_t *dvbdmxfeed, const u8 *buf) +{ + switch(dvbdmxfeed->type) { + case DMX_TYPE_TS: + if (!dvbdmxfeed->feed.ts.is_filtering) + break; + if (dvbdmxfeed->ts_type & TS_PACKET) { + if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) + DvbDmxSWFilterPayload(dvbdmxfeed, buf); + else + dvbdmxfeed->cb.ts((u8 *)buf, 188, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + } + if (dvbdmxfeed->ts_type & TS_DECODER) + if (dvbdmxfeed->demux->write_to_decoder) + dvbdmxfeed->demux-> + write_to_decoder(dvbdmxfeed, (u8 *)buf, 188); + break; + + case DMX_TYPE_SEC: + if (!dvbdmxfeed->feed.sec.is_filtering) + break; + if (DvbDmxSWFilterSectionPacket(dvbdmxfeed, buf)<0) + dvbdmxfeed->seclen=dvbdmxfeed->secbufp=0; + break; + + default: + break; + } +} + +void inline +DvbDmxSWFilterPacket(dvb_demux_t *dvbdmx, const u8 *buf) +{ + dvb_demux_feed_t *dvbdmxfeed; + + if (!(dvbdmxfeed=dvbdmx->pid2feed[ts_pid(buf)])) + return; + DvbDmxSWFilterPacketType(dvbdmxfeed, buf); +} + +void +DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count) +{ + dvb_demux_feed_t *dvbdmxfeed; + + spin_lock(&dvbdmx->lock); + if ((dvbdmxfeed=dvbdmx->pid2feed[0x2000])) + dvbdmxfeed->cb.ts((u8 *)buf, count*188, 0, 0, + &dvbdmxfeed->feed.ts, DMX_OK); + while (count) { + DvbDmxSWFilterPacket(dvbdmx, buf); + count--; + buf+=188; + } + spin_unlock(&dvbdmx->lock); +} + +static inline void +DvbDmxSWFilter(dvb_demux_t *dvbdmx, const u8 *buf, size_t count) +{ + int p=0,i, j; + + if ((i=dvbdmx->tsbufp)) { + if (count<(j=188-i)) { + memcpy(&dvbdmx->tsbuf[i], buf, count); + dvbdmx->tsbufp+=count; + return; + } + memcpy(&dvbdmx->tsbuf[i], buf, j); + DvbDmxSWFilterPacket(dvbdmx, dvbdmx->tsbuf); + dvbdmx->tsbufp=0; + p+=j; + } + + while (p<count) { + if (buf[p]==0x47) { + if (count-p>=188) { + DvbDmxSWFilterPacket(dvbdmx, buf+p); + p+=188; + } else { + i=count-p; + memcpy(dvbdmx->tsbuf, buf+p, i); + dvbdmx->tsbufp=i; + return; + } + } else + p++; + } +} + + +/****************************************************************************** + ****************************************************************************** + * DVB DEMUX API LEVEL FUNCTIONS + ****************************************************************************** + ******************************************************************************/ + +static dvb_demux_filter_t * +DvbDmxFilterAlloc(dvb_demux_t *dvbdmx) +{ + int i; + + for (i=0; i<dvbdmx->filternum; i++) + if (dvbdmx->filter[i].state==DMX_STATE_FREE) + break; + if (i==dvbdmx->filternum) + return 0; + dvbdmx->filter[i].state=DMX_STATE_ALLOCATED; + return &dvbdmx->filter[i]; +} + +static dvb_demux_feed_t * +DvbDmxFeedAlloc(dvb_demux_t *dvbdmx) +{ + int i; + + for (i=0; i<dvbdmx->feednum; i++) + if (dvbdmx->feed[i].state==DMX_STATE_FREE) + break; + if (i==dvbdmx->feednum) + return 0; + dvbdmx->feed[i].state=DMX_STATE_ALLOCATED; + return &dvbdmx->feed[i]; +} + + +/****************************************************************************** + * dmx_ts_feed API calls + ******************************************************************************/ + +static int +dmx_pid_set(u16 pid, dvb_demux_feed_t *dvbdmxfeed) +{ + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + dvb_demux_feed_t **pid2feed=dvbdmx->pid2feed; + + if (pid>DMX_MAX_PID) + return -EINVAL; + if (dvbdmxfeed->pid!=0xffff) { + if (dvbdmxfeed->pid<=DMX_MAX_PID) + pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + if (pid2feed[pid]) { + return -EBUSY; + } + pid2feed[pid]=dvbdmxfeed; + dvbdmxfeed->pid=pid; + return 0; +} + + +static int +dmx_ts_feed_set(struct dmx_ts_feed_s* feed, + u16 pid, + int ts_type, + dmx_ts_pes_t pes_type, + size_t callback_length, + size_t circular_buffer_size, + int descramble, + struct timespec timeout + ) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (ts_type & TS_DECODER) { + if (pes_type >= DMX_TS_PES_OTHER) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (dvbdmx->pesfilter[pes_type] && + (dvbdmx->pesfilter[pes_type]!=dvbdmxfeed)) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if ((pes_type != DMX_TS_PES_PCR0) && + (pes_type != DMX_TS_PES_PCR1) && + (pes_type != DMX_TS_PES_PCR2) && + (pes_type != DMX_TS_PES_PCR3)) { + if ((ret=dmx_pid_set(pid, dvbdmxfeed))<0) { + up(&dvbdmx->mutex); + return ret; + } else + dvbdmxfeed->pid=pid; + } + dvbdmx->pesfilter[pes_type]=dvbdmxfeed; + dvbdmx->pids[pes_type]=dvbdmxfeed->pid; + } else + if ((ret=dmx_pid_set(pid, dvbdmxfeed))<0) { + up(&dvbdmx->mutex); + return ret; + } + + dvbdmxfeed->buffer_size=circular_buffer_size; + dvbdmxfeed->descramble=descramble; + dvbdmxfeed->timeout=timeout; + dvbdmxfeed->cb_length=callback_length; + dvbdmxfeed->ts_type=ts_type; + dvbdmxfeed->pes_type=pes_type; + + if (dvbdmxfeed->descramble) { + up(&dvbdmx->mutex); + return -ENOSYS; + } + + if (dvbdmxfeed->buffer_size) { +#ifdef NOBUFS + dvbdmxfeed->buffer=0; +#else + dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); + if (!dvbdmxfeed->buffer) { + up(&dvbdmx->mutex); + return -ENOMEM; + } +#endif + } + dvbdmxfeed->state=DMX_STATE_READY; + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_ts_feed_start_filtering(struct dmx_ts_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfeed->state!=DMX_STATE_READY || + dvbdmxfeed->type!=DMX_TYPE_TS) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (!dvbdmx->start_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->start_feed(dvbdmxfeed); + if (ret<0) { + up(&dvbdmx->mutex); + return ret; + } + spin_lock_irq(&dvbdmx->lock); + feed->is_filtering=1; + dvbdmxfeed->state=DMX_STATE_GO; + spin_unlock_irq(&dvbdmx->lock); + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_ts_feed_stop_filtering(struct dmx_ts_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfeed->state<DMX_STATE_GO) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (!dvbdmx->stop_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->stop_feed(dvbdmxfeed); + spin_lock_irq(&dvbdmx->lock); + feed->is_filtering=0; + dvbdmxfeed->state=DMX_STATE_ALLOCATED; + + spin_unlock_irq(&dvbdmx->lock); + up(&dvbdmx->mutex); + return ret; +} + +static int dvbdmx_allocate_ts_feed(dmx_demux_t *demux, + dmx_ts_feed_t **feed, + dmx_ts_cb callback) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmxfeed->type=DMX_TYPE_TS; + dvbdmxfeed->cb.ts=callback; + dvbdmxfeed->demux=dvbdmx; + dvbdmxfeed->pid=0xffff; + dvbdmxfeed->peslen=0xfffa; + dvbdmxfeed->buffer=0; + + (*feed)=&dvbdmxfeed->feed.ts; + (*feed)->is_filtering=0; + (*feed)->parent=demux; + (*feed)->priv=0; + (*feed)->set=dmx_ts_feed_set; + (*feed)->start_filtering=dmx_ts_feed_start_filtering; + (*feed)->stop_filtering=dmx_ts_feed_stop_filtering; + + + if (!(dvbdmxfeed->filter=DvbDmxFilterAlloc(dvbdmx))) { + dvbdmxfeed->state=DMX_STATE_FREE; + up(&dvbdmx->mutex); + return -EBUSY; + } + + dvbdmxfeed->filter->type=DMX_TYPE_TS; + dvbdmxfeed->filter->feed=dvbdmxfeed; + dvbdmxfeed->filter->state=DMX_STATE_READY; + + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_release_ts_feed(dmx_demux_t *demux, dmx_ts_feed_t *feed) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfeed->state==DMX_STATE_FREE) { + up(&dvbdmx->mutex); + return -EINVAL; + } +#ifndef NOBUFS + if (dvbdmxfeed->buffer) { + vfree(dvbdmxfeed->buffer); + dvbdmxfeed->buffer=0; + } +#endif + dvbdmxfeed->state=DMX_STATE_FREE; + dvbdmxfeed->filter->state=DMX_STATE_FREE; + if (dvbdmxfeed->pid<=DMX_MAX_PID) { + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + + up(&dvbdmx->mutex); + return 0; +} + + +/****************************************************************************** + * dmx_section_feed API calls + ******************************************************************************/ + +static int +dmx_section_feed_allocate_filter(struct dmx_section_feed_s* feed, + dmx_section_filter_t** filter) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdemux=dvbdmxfeed->demux; + dvb_demux_filter_t *dvbdmxfilter; + + if (down_interruptible (&dvbdemux->mutex)) + return -ERESTARTSYS; + + dvbdmxfilter=DvbDmxFilterAlloc(dvbdemux); + if (!dvbdmxfilter) { + up(&dvbdemux->mutex); + return -ENOSPC; + } + spin_lock_irq(&dvbdemux->lock); + *filter=&dvbdmxfilter->filter; + (*filter)->parent=feed; + (*filter)->priv=0; + dvbdmxfilter->feed=dvbdmxfeed; + dvbdmxfilter->type=DMX_TYPE_SEC; + dvbdmxfilter->state=DMX_STATE_READY; + + dvbdmxfilter->next=dvbdmxfeed->filter; + dvbdmxfeed->filter=dvbdmxfilter; + spin_unlock_irq(&dvbdemux->lock); + up(&dvbdemux->mutex); + return 0; +} + +static int +dmx_section_feed_set(struct dmx_section_feed_s* feed, + u16 pid, size_t circular_buffer_size, + int descramble, int check_crc) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + + if (pid>0x1fff) + return -EINVAL; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfeed->pid!=0xffff) { + dvbdmx->pid2feed[dvbdmxfeed->pid]=0; + dvbdmxfeed->pid=0xffff; + } + if (dvbdmx->pid2feed[pid]) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmx->pid2feed[pid]=dvbdmxfeed; + dvbdmxfeed->pid=pid; + + dvbdmxfeed->buffer_size=circular_buffer_size; + dvbdmxfeed->descramble=descramble; + if (dvbdmxfeed->descramble) { + up(&dvbdmx->mutex); + return -ENOSYS; + } + + dvbdmxfeed->check_crc=check_crc; +#ifdef NOBUFS + dvbdmxfeed->buffer=0; +#else + dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size); + if (!dvbdmxfeed->buffer) { + up(&dvbdmx->mutex); + return -ENOMEM; + } +#endif + dvbdmxfeed->state=DMX_STATE_READY; + up(&dvbdmx->mutex); + return 0; +} + +static void prepare_secfilters(dvb_demux_feed_t *dvbdmxfeed) +{ + int i; + dmx_section_filter_t *sf; + dvb_demux_filter_t *f; + u8 mask, mode, doneq; + + if (!(f=dvbdmxfeed->filter)) + return; + do { + sf=&f->filter; + doneq=0; + for (i=0; i<DVB_DEMUX_MASK_MAX; i++) { + mode=sf->filter_mode[i]; + mask=sf->filter_mask[i]; + f->maskandmode[i]=mask&mode; + doneq|=f->maskandnotmode[i]=mask&~mode; + } + f->doneq=doneq ? 1 : 0; + } while ((f=f->next)); +} + + +static int +dmx_section_feed_start_filtering(dmx_section_feed_t *feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (feed->is_filtering) { + up(&dvbdmx->mutex); + return -EBUSY; + } + if (!dvbdmxfeed->filter) { + up(&dvbdmx->mutex); + return -EINVAL; + } + dvbdmxfeed->secbufp=0; + dvbdmxfeed->seclen=0; + + if (!dvbdmx->start_feed) { + up(&dvbdmx->mutex); + return -1; + } + prepare_secfilters(dvbdmxfeed); + ret=dvbdmx->start_feed(dvbdmxfeed); + if (ret<0) { + up(&dvbdmx->mutex); + return ret; + } + spin_lock_irq(&dvbdmx->lock); + feed->is_filtering=1; + dvbdmxfeed->state=DMX_STATE_GO; + spin_unlock_irq(&dvbdmx->lock); + up(&dvbdmx->mutex); + return 0; +} + +static int +dmx_section_feed_stop_filtering(struct dmx_section_feed_s* feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + int ret; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (!dvbdmx->stop_feed) { + up(&dvbdmx->mutex); + return -1; + } + ret=dvbdmx->stop_feed(dvbdmxfeed); + spin_lock_irq(&dvbdmx->lock); + dvbdmxfeed->state=DMX_STATE_READY; + feed->is_filtering=0; + spin_unlock_irq(&dvbdmx->lock); + up(&dvbdmx->mutex); + return ret; +} + +static int +dmx_section_feed_release_filter(dmx_section_feed_t *feed, + dmx_section_filter_t* filter) +{ + dvb_demux_filter_t *dvbdmxfilter=(dvb_demux_filter_t *) filter, *f; + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=dvbdmxfeed->demux; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfilter->feed!=dvbdmxfeed) { + up(&dvbdmx->mutex); + return -EINVAL; + } + if (feed->is_filtering) + feed->stop_filtering(feed); + + spin_lock_irq(&dvbdmx->lock); + f=dvbdmxfeed->filter; + if (f==dvbdmxfilter) + dvbdmxfeed->filter=dvbdmxfilter->next; + else { + while(f->next!=dvbdmxfilter) + f=f->next; + f->next=f->next->next; + } + dvbdmxfilter->state=DMX_STATE_FREE; + spin_unlock_irq(&dvbdmx->lock); + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_allocate_section_feed(dmx_demux_t *demux, + dmx_section_feed_t **feed, + dmx_section_cb callback) +{ + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + dvb_demux_feed_t *dvbdmxfeed; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (!(dvbdmxfeed=DvbDmxFeedAlloc(dvbdmx))) { + up(&dvbdmx->mutex); + return -EBUSY; + } + dvbdmxfeed->type=DMX_TYPE_SEC; + dvbdmxfeed->cb.sec=callback; + dvbdmxfeed->demux=dvbdmx; + dvbdmxfeed->pid=0xffff; + dvbdmxfeed->secbufp=0; + dvbdmxfeed->filter=0; + dvbdmxfeed->buffer=0; + + (*feed)=&dvbdmxfeed->feed.sec; + (*feed)->is_filtering=0; + (*feed)->parent=demux; + (*feed)->priv=0; + (*feed)->set=dmx_section_feed_set; + (*feed)->allocate_filter=dmx_section_feed_allocate_filter; + (*feed)->release_filter=dmx_section_feed_release_filter; + (*feed)->start_filtering=dmx_section_feed_start_filtering; + (*feed)->stop_filtering=dmx_section_feed_stop_filtering; + + up(&dvbdmx->mutex); + return 0; +} + +static int dvbdmx_release_section_feed(dmx_demux_t *demux, + dmx_section_feed_t *feed) +{ + dvb_demux_feed_t *dvbdmxfeed=(dvb_demux_feed_t *) feed; + dvb_demux_t *dvbdmx=(dvb_demux_t *) demux; + + if (down_interruptible (&dvbdmx->mutex)) + return -ERESTARTSYS; + + if (dvbdmxfeed->state==DMX_STATE_FREE) { + up(&dvbdmx->mutex); + return -EINVAL; + } +#ifndef NOBUFS + if (dvbdmxfeed->buffer) { + vfree(dvbdmxfeed->buffer); + dvbdmxfeed->buffer=0; + } +#endif + dvbdmxfeed->state=DMX_STATE_FREE; + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + if (dvbdmxfeed->pid!=0xffff) + dvbdmxfeed->demux->pid2feed[dvbdmxfeed->pid]=0; + up(&dvbdmx->mutex); + return 0; +} + + +/****************************************************************************** + * dvb_demux kernel data API calls + ******************************************************************************/ + +static int dvbdmx_open(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (dvbdemux->users>=MAX_DVB_DEMUX_USERS) + return -EUSERS; + dvbdemux->users++; + return 0; +} + +static int dvbdmx_close(struct dmx_demux_s *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (dvbdemux->users==0) + return -ENODEV; + dvbdemux->users--; + //FIXME: release any unneeded resources if users==0 + return 0; +} + +static int dvbdmx_write(dmx_demux_t *demux, const char *buf, size_t count) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if ((!demux->frontend) || + (demux->frontend->source!=DMX_MEMORY_FE)) + return -EINVAL; + + if (down_interruptible (&dvbdemux->mutex)) + return -ERESTARTSYS; + + DvbDmxSWFilter(dvbdemux, buf, count); + up(&dvbdemux->mutex); + return count; +} + + +static int dvbdmx_add_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + struct list_head *pos, *head=&dvbdemux->frontend_list; + + if (!(frontend->id && frontend->vendor && frontend->model)) + return -EINVAL; + list_for_each(pos, head) + { + if (!strcmp(DMX_FE_ENTRY(pos)->id, frontend->id)) + return -EEXIST; + } + + list_add(&(frontend->connectivity_list), head); + return 0; +} + +static int +dvbdmx_remove_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + struct list_head *pos, *n, *head=&dvbdemux->frontend_list; + + list_for_each_safe (pos, n, head) + { + if (DMX_FE_ENTRY(pos)==frontend) + { + list_del(pos); + return 0; + } + } + return -ENODEV; +} + +static struct list_head * +dvbdmx_get_frontends(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (list_empty(&dvbdemux->frontend_list)) + return NULL; + return &dvbdemux->frontend_list; +} + +static int dvbdmx_connect_frontend(dmx_demux_t *demux, + dmx_frontend_t *frontend) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (demux->frontend) + return -EINVAL; + + if (down_interruptible (&dvbdemux->mutex)) + return -ERESTARTSYS; + + demux->frontend=frontend; + up(&dvbdemux->mutex); + return 0; +} + +static int dvbdmx_disconnect_frontend(dmx_demux_t *demux) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + if (down_interruptible (&dvbdemux->mutex)) + return -ERESTARTSYS; + + demux->frontend=NULL; + up(&dvbdemux->mutex); + return 0; +} + +static int dvbdmx_get_pes_pids(dmx_demux_t *demux, u16 *pids) +{ + dvb_demux_t *dvbdemux=(dvb_demux_t *) demux; + + memcpy(pids, dvbdemux->pids, 5*sizeof(u16)); + return 0; +} + +int +DvbDmxInit(dvb_demux_t *dvbdemux) +{ + int i; + dmx_demux_t *dmx=&dvbdemux->dmx; + + dvbdemux->users=0; + dvbdemux->filter=vmalloc(dvbdemux->filternum*sizeof(dvb_demux_filter_t)); + if (!dvbdemux->filter) + return -ENOMEM; + + dvbdemux->feed=vmalloc(dvbdemux->feednum*sizeof(dvb_demux_feed_t)); + if (!dvbdemux->feed) { + vfree(dvbdemux->filter); + return -ENOMEM; + } + for (i=0; i<dvbdemux->filternum; i++) { + dvbdemux->filter[i].state=DMX_STATE_FREE; + dvbdemux->filter[i].index=i; + } + for (i=0; i<dvbdemux->feednum; i++) + dvbdemux->feed[i].state=DMX_STATE_FREE; + dvbdemux->frontend_list.next= + dvbdemux->frontend_list.prev= + &dvbdemux->frontend_list; + for (i=0; i<DMX_TS_PES_OTHER; i++) { + dvbdemux->pesfilter[i]=NULL; + dvbdemux->pids[i]=0xffff; + } + dvbdemux->playing=dvbdemux->recording=0; + memset(dvbdemux->pid2feed, 0, (DMX_MAX_PID+1)*sizeof(dvb_demux_feed_t *)); + dvbdemux->tsbufp=0; + + dmx->frontend=0; + dmx->reg_list.next=dmx->reg_list.prev=&dmx->reg_list; + dmx->priv=(void *) dvbdemux; + //dmx->users=0; // reset in dmx_register_demux() + dmx->open=dvbdmx_open; + dmx->close=dvbdmx_close; + dmx->write=dvbdmx_write; + dmx->allocate_ts_feed=dvbdmx_allocate_ts_feed; + dmx->release_ts_feed=dvbdmx_release_ts_feed; + dmx->allocate_section_feed=dvbdmx_allocate_section_feed; + dmx->release_section_feed=dvbdmx_release_section_feed; + + dmx->descramble_mac_address=NULL; + dmx->descramble_section_payload=NULL; + + dmx->add_frontend=dvbdmx_add_frontend; + dmx->remove_frontend=dvbdmx_remove_frontend; + dmx->get_frontends=dvbdmx_get_frontends; + dmx->connect_frontend=dvbdmx_connect_frontend; + dmx->disconnect_frontend=dvbdmx_disconnect_frontend; + dmx->get_pes_pids=dvbdmx_get_pes_pids; + sema_init(&dvbdemux->mutex, 1); + spin_lock_init(&dvbdemux->lock); + + if (dmx_register_demux(dmx)<0) + return -1; + + return 0; +} + +int +DvbDmxRelease(dvb_demux_t *dvbdemux) +{ + dmx_demux_t *dmx=&dvbdemux->dmx; + + dmx_unregister_demux(dmx); + if (dvbdemux->filter) + vfree(dvbdemux->filter); + if (dvbdemux->feed) + vfree(dvbdemux->feed); + return 0; +} + +#if 0 +MODULE_DESCRIPTION("Software MPEG Demultiplexer"); +MODULE_AUTHOR("Ralph Metzler, Markus Metzler"); +MODULE_LICENSE("GPL"); +#endif + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.h b/linux/drivers/media/dvb/dvb-core/dvb_demux.h new file mode 100644 index 000000000..7fb008c4b --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.h @@ -0,0 +1,134 @@ +/* + * dvb_demux.h - DVB kernel demux API + * + * Copyright (C) 2000-2001 Marcus Metzler <marcus@convergence.de> + * & Ralph Metzler <ralph@convergence.de> + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _DVB_DEMUX_H_ +#define _DVB_DEMUX_H_ + +#include "demux.h" + +#define DMX_TYPE_TS 0 +#define DMX_TYPE_SEC 1 +#define DMX_TYPE_PES 2 + +#define DMX_STATE_FREE 0 +#define DMX_STATE_ALLOCATED 1 +#define DMX_STATE_SET 2 +#define DMX_STATE_READY 3 +#define DMX_STATE_GO 4 + +#define DVB_DEMUX_MASK_MAX 18 + +typedef struct dvb_demux_filter_s { + dmx_section_filter_t filter; + u8 maskandmode [DMX_MAX_FILTER_SIZE]; + u8 maskandnotmode [DMX_MAX_FILTER_SIZE]; + int doneq; + + struct dvb_demux_filter_s *next; + struct dvb_demux_feed_s *feed; + int index; + int state; + int type; + int pesto; + + u16 handle; + u16 hw_handle; + struct timer_list timer; + int ts_state; + + //u16 pid; //to be removed +} dvb_demux_filter_t; + +typedef struct dvb_demux_feed_s { + union { + dmx_ts_feed_t ts; + dmx_section_feed_t sec; + } feed; + + union { + dmx_ts_cb ts; + dmx_section_cb sec; + } cb; + + struct dvb_demux_s *demux; + int type; + int state; + u16 pid; + u8 *buffer; + int buffer_size; + int descramble; + int check_crc; + + struct timespec timeout; + dvb_demux_filter_t *filter; + int cb_length; + + int ts_type; + dmx_ts_pes_t pes_type; + + u8 secbuf[4096]; + int secbufp; + int seclen; + int cc; + + u16 peslen; +} dvb_demux_feed_t; + +typedef struct dvb_demux_s { + dmx_demux_t dmx; + void *priv; + int filternum; + int feednum; + int (*start_feed)(dvb_demux_feed_t *); + int (*stop_feed)(dvb_demux_feed_t *); + int (*write_to_decoder)(dvb_demux_feed_t *, u8 *, size_t); + + + int users; +#define MAX_DVB_DEMUX_USERS 10 + dvb_demux_filter_t *filter; + dvb_demux_feed_t *feed; + + struct list_head frontend_list; + + dvb_demux_feed_t *pesfilter[DMX_TS_PES_OTHER]; + u16 pids[DMX_TS_PES_OTHER]; + int playing; + int recording; + +#define DMX_MAX_PID 0x2000 + dvb_demux_feed_t *pid2feed[DMX_MAX_PID+1]; + u8 tsbuf[188]; + int tsbufp; + + struct semaphore mutex; + spinlock_t lock; +} dvb_demux_t; + + +int DvbDmxInit(dvb_demux_t *dvbdemux); +int DvbDmxRelease(dvb_demux_t *dvbdemux); +void DvbDmxSWFilterPackets(dvb_demux_t *dvbdmx, const u8 *buf, int count); + +#endif /* _DVB_DEMUX_H_ */ diff --git a/linux/drivers/media/dvb/dvb-core/dvb_filter.c b/linux/drivers/media/dvb/dvb-core/dvb_filter.c new file mode 100644 index 000000000..4b6ef6d32 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_filter.c @@ -0,0 +1,962 @@ +#include <linux/module.h> +#include <linux/videodev.h> +#include "dvb_filter.h" + +unsigned int bitrates[3][16] = +{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, + {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, + {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}}; + +uint32_t freq[4] = {441, 480, 320, 0}; + +unsigned int ac3_bitrates[32] = + {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, + 0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint32_t ac3_freq[4] = {480, 441, 320, 0}; +uint32_t ac3_frames[3][32] = + {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, + 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, + 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, + 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; + + + +void pes2ts_init(pes2ts_t *p2ts, unsigned short pid, + pes2ts_cb_t *cb, void *priv) +{ + unsigned char *buf=p2ts->buf; + + buf[0]=0x47; + buf[1]=(pid>>8); + buf[2]=pid&0xff; + p2ts->cc=0; + p2ts->cb=cb; + p2ts->priv=priv; +} + +int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len) +{ + unsigned char *buf=p2ts->buf; + int ret=0, rest; + + //len=6+((pes[4]<<8)|pes[5]); + + buf[1]|=0x40; + while (len>=184) { + buf[3]=0x10|((p2ts->cc++)&0x0f); + memcpy(buf+4, pes, 184); + if ((ret=p2ts->cb(p2ts->priv, buf))) + return ret; + len-=184; pes+=184; + buf[1]&=~0x40; + } + if (!len) + return 0; + buf[3]=0x30|((p2ts->cc++)&0x0f); + rest=183-len; + if (rest) { + buf[5]=0x00; + if (rest-1) + memset(buf+6, 0xff, rest-1); + } + buf[4]=rest; + memcpy(buf+5+rest, pes, len); + return p2ts->cb(p2ts->priv, buf); +} + +void reset_ipack(ipack *p) +{ + p->found = 0; + p->cid = 0; + p->plength = 0; + p->flag1 = 0; + p->flag2 = 0; + p->hlength = 0; + p->mpeg = 0; + p->check = 0; + p->which = 0; + p->done = 0; + p->count = 0; +} + +void init_ipack(ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)) +{ + if ( !(p->buf = vmalloc(size*sizeof(u8))) ){ + printk ("Couldn't allocate memory for ipack\n"); + } + p->size = size; + p->func = func; + p->repack_subids = 0; + reset_ipack(p); +} + +void free_ipack(ipack * p) +{ + if (p->buf) vfree(p->buf); +} + +void send_ipack(ipack *p) +{ + int off; + AudioInfo ai; + int ac3_off = 0; + int streamid=0; + int nframes= 0; + int f=0; + + switch ( p->mpeg ){ + case 2: + if (p->count < 10) return; + p->buf[3] = p->cid; + + p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8); + p->buf[5] = (u8)((p->count-6) & 0x00FF); + if (p->repack_subids && p->cid == PRIVATE_STREAM1){ + + off = 9+p->buf[8]; + streamid = p->buf[off]; + if ((streamid & 0xF8) == 0x80){ + ai.off = 0; + ac3_off = ((p->buf[off+2] << 8)| + p->buf[off+3]); + if (ac3_off < p->count) + f=get_ac3info(p->buf+off+3+ac3_off, + p->count-ac3_off, &ai,0); + if ( !f ){ + nframes = (p->count-off-3-ac3_off)/ + ai.framesize + 1; + p->buf[off+2] = (ac3_off >> 8)& 0xFF; + p->buf[off+3] = (ac3_off)& 0xFF; + p->buf[off+1] = nframes; + + ac3_off += nframes * ai.framesize - + p->count; + } + } + } + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x80; + p->buf[7] = 0x00; + p->buf[8] = 0x00; + p->count = 9; + if (p->repack_subids && p->cid == PRIVATE_STREAM1 + && (streamid & 0xF8)==0x80 ){ + p->count += 4; + p->buf[9] = streamid; + p->buf[10] = (ac3_off >> 8)& 0xFF; + p->buf[11] = (ac3_off)& 0xFF; + p->buf[12] = 0; + } + + break; + case 1: + if (p->count < 8) return; + p->buf[3] = p->cid; + + p->buf[4] = (u8)(((p->count-6) & 0xFF00) >> 8); + p->buf[5] = (u8)((p->count-6) & 0x00FF); + p->func(p->buf, p->count, p->data); + + p->buf[6] = 0x0F; + p->count = 7; + break; + } +} + +void send_ipack_rest(ipack *p) +{ + if (p->plength != MMAX_PLENGTH-6 || p->found<=6) + return; + p->plength = p->found-6; + p->found = 0; + send_ipack(p); + reset_ipack(p); +} + +static void write_ipack(ipack *p, u8 *data, int count) +{ + u8 headr[3] = { 0x00, 0x00, 0x01} ; + + if (p->count < 6){ + memcpy(p->buf, headr, 3); + p->count = 6; + } + + if (p->count + count < p->size){ + memcpy(p->buf+p->count, data, count); + p->count += count; + } else { + int rest = p->size - p->count; + memcpy(p->buf+p->count, data, rest); + p->count += rest; + send_ipack(p); + if (count - rest > 0) + write_ipack(p, data+rest, count-rest); + } +} + +int instant_repack(u8 *buf, int count, ipack *p) +{ + int l; + int c=0; + + while (c < count && (p->mpeg == 0 || + (p->mpeg == 1 && p->found < 7) || + (p->mpeg == 2 && p->found < 9)) + && (p->found < 5 || !p->done)){ + switch ( p->found ){ + case 0: + case 1: + if (buf[c] == 0x00) p->found++; + else p->found = 0; + c++; + break; + case 2: + if (buf[c] == 0x01) p->found++; + else if (buf[c] == 0) { + p->found = 2; + } else p->found = 0; + c++; + break; + case 3: + p->cid = 0; + switch (buf[c]){ + case PROG_STREAM_MAP: + case PRIVATE_STREAM2: + case PROG_STREAM_DIR: + case ECM_STREAM : + case EMM_STREAM : + case PADDING_STREAM : + case DSM_CC_STREAM : + case ISO13522_STREAM: + p->done = 1; + case PRIVATE_STREAM1: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + p->found++; + p->cid = buf[c]; + c++; + break; + default: + p->found = 0; + break; + } + break; + + case 4: + if (count-c > 1){ + p->plen[0] = buf[c]; + c++; + p->plen[1] = buf[c]; + c++; + p->found+=2; + p->plength=(p->plen[0]<<8)|p->plen[1]; + } else { + p->plen[0] = buf[c]; + p->found++; + return count; + } + break; + case 5: + p->plen[1] = buf[c]; + c++; + p->found++; + p->plength=(p->plen[0]<<8)|p->plen[1]; + break; + case 6: + if (!p->done){ + p->flag1 = buf[c]; + c++; + p->found++; + if ( (p->flag1 & 0xC0) == 0x80 ) p->mpeg = 2; + else { + p->hlength = 0; + p->which = 0; + p->mpeg = 1; + p->flag2 = 0; + } + } + break; + + case 7: + if ( !p->done && p->mpeg == 2) { + p->flag2 = buf[c]; + c++; + p->found++; + } + break; + + case 8: + if ( !p->done && p->mpeg == 2) { + p->hlength = buf[c]; + c++; + p->found++; + } + break; + + default: + + break; + } + } + + if (c == count) return count; + + if (!p->plength) p->plength = MMAX_PLENGTH-6; + + if ( p->done || ((p->mpeg == 2 && p->found >= 9) || + (p->mpeg == 1 && p->found >= 7)) ){ + switch (p->cid){ + + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case PRIVATE_STREAM1: + + if (p->mpeg == 2 && p->found == 9) { + write_ipack(p, &p->flag1, 1); + write_ipack(p, &p->flag2, 1); + write_ipack(p, &p->hlength, 1); + } + + if (p->mpeg == 1 && p->found == 7) + write_ipack(p, &p->flag1, 1); + + if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && + p->found < 14) { + while (c < count && p->found < 14) { + p->pts[p->found-9] = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + } + if (c == count) return count; + } + + if (p->mpeg == 1 && p->which < 2000) { + + if (p->found == 7) { + p->check = p->flag1; + p->hlength = 1; + } + + while (!p->which && c < count && + p->check == 0xFF){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + } + + if ( c == count) return count; + + if ( (p->check & 0xC0) == 0x40 && !p->which){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + + p->which = 1; + if ( c == count) return count; + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if ( c == count) return count; + } + + if (p->which == 1){ + p->check = buf[c]; + write_ipack(p, buf+c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if ( c == count) return count; + } + + if ( (p->check & 0x30) && p->check != 0xFF){ + p->flag2 = (p->check & 0xF0) << 2; + p->pts[0] = p->check; + p->which = 3; + } + + if ( c == count) return count; + if (p->which > 2){ + if ((p->flag2 & PTS_DTS_FLAGS) + == PTS_ONLY){ + while (c < count && + p->which < 7){ + p->pts[p->which-2] = + buf[c]; + write_ipack(p,buf+c,1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if ( c == count) return count; + } else if ((p->flag2 & PTS_DTS_FLAGS) + == PTS_DTS){ + while (c < count && + p->which< 12){ + if (p->which< 7) + p->pts[p->which + -2] = + buf[c]; + write_ipack(p,buf+c,1); + c++; + p->found++; + p->which++; + p->hlength++; + } + if ( c == count) return count; + } + p->which = 2000; + } + + } + + while (c < count && p->found < p->plength+6){ + l = count -c; + if (l+p->found > p->plength+6) + l = p->plength+6-p->found; + write_ipack(p, buf+c, l); + p->found += l; + c += l; + } + + break; + } + + + if ( p->done ){ + if( p->found + count - c < p->plength+6){ + p->found += count-c; + c = count; + } else { + c += p->plength+6 - p->found; + p->found = p->plength+6; + } + } + + if (p->plength && p->found == p->plength+6) { + send_ipack(p); + reset_ipack(p); + if (c < count) + instant_repack(buf+c, count-c, p); + } + } + return count; +} + + + +void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, + void (*pes_write)(u8 *buf, int count, void *data), + void *priv) +{ + init_ipack(pa, IPACKS, pes_write); + init_ipack(pv, IPACKS, pes_write); + pa->pid = pida; + pv->pid = pidv; + pa->data = priv; + pv->data = priv; +} + +void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188) +{ + u8 off = 0; + + if (!buf || !p ){ + printk("NULL POINTER IDIOT\n"); + return; + } + if (buf[1]&PAY_START) { + if (p->plength == MMAX_PLENGTH-6 && p->found>6){ + p->plength = p->found-6; + p->found = 0; + send_ipack(p); + reset_ipack(p); + } + } + if (buf[3] & ADAPT_FIELD) { // adaptation field? + off = buf[4] + 1; + if (off+4 > 187) return; + } + instant_repack(buf+4+off, TS_SIZE-4-off, p); +} + +/* needs 5 byte input, returns picture coding type*/ +int read_picture_header(uint8_t *headr, mpg_picture *pic, int field, int pr) +{ + uint8_t pct; + + if (pr) printk( "Pic header: "); + pic->temporal_reference[field] = (( headr[0] << 2 ) | + (headr[1] & 0x03) )& 0x03ff; + if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]); + + pct = ( headr[1] >> 2 ) & 0x07; + pic->picture_coding_type[field] = pct; + if (pr) { + switch(pct){ + case I_FRAME: + printk( " I-FRAME"); + break; + case B_FRAME: + printk( " B-FRAME"); + break; + case P_FRAME: + printk( " P-FRAME"); + break; + } + } + + + pic->vinfo.vbv_delay = (( headr[1] >> 5 ) | ( headr[2] << 3) | + ( (headr[3] & 0x1F) << 11) ) & 0xffff; + + if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay); + + pic->picture_header_parameter = ( headr[3] & 0xe0 ) | + ((headr[4] & 0x80) >> 3); + + if ( pct == B_FRAME ){ + pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f; + } + if (pr) printk( " pic head param: 0x%x", + pic->picture_header_parameter); + + return pct; +} + + +/* needs 4 byte input */ +int read_gop_header(uint8_t *headr, mpg_picture *pic, int pr) +{ + if (pr) printk("GOP header: "); + + pic->time_code = (( headr[0] << 17 ) | ( headr[1] << 9) | + ( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff; + + if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F, + ((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F), + ((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F)); + + if ( ( headr[3] & 0x40 ) != 0 ){ + pic->closed_gop = 1; + } else { + pic->closed_gop = 0; + } + if (pr) printk("closed: %d", pic->closed_gop); + + if ( ( headr[3] & 0x20 ) != 0 ){ + pic->broken_link = 1; + } else { + pic->broken_link = 0; + } + if (pr) printk(" broken: %d\n", pic->broken_link); + + return 0; +} + +/* needs 8 byte input */ +int read_sequence_header(uint8_t *headr, VideoInfo *vi, int pr) +{ + int sw; + int form = -1; + + if (pr) printk("Reading sequence header\n"); + + vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4); + vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]); + + sw = (int)((headr[3]&0xF0) >> 4) ; + + switch( sw ){ + case 1: + if (pr) + printk("Videostream: ASPECT: 1:1"); + vi->aspect_ratio = 100; + break; + case 2: + if (pr) + printk("Videostream: ASPECT: 4:3"); + vi->aspect_ratio = 133; + break; + case 3: + if (pr) + printk("Videostream: ASPECT: 16:9"); + vi->aspect_ratio = 177; + break; + case 4: + if (pr) + printk("Videostream: ASPECT: 2.21:1"); + vi->aspect_ratio = 221; + break; + + case 5 ... 15: + if (pr) + printk("Videostream: ASPECT: reserved"); + vi->aspect_ratio = 0; + break; + + default: + vi->aspect_ratio = 0; + return -1; + } + + if (pr) + printk(" Size = %dx%d",vi->horizontal_size,vi->vertical_size); + + sw = (int)(headr[3]&0x0F); + + switch ( sw ) { + case 1: + if (pr) + printk(" FRate: 23.976 fps"); + vi->framerate = 23976; + form = -1; + break; + case 2: + if (pr) + printk(" FRate: 24 fps"); + vi->framerate = 24000; + form = -1; + break; + case 3: + if (pr) + printk(" FRate: 25 fps"); + vi->framerate = 25000; + form = VIDEO_MODE_PAL; + break; + case 4: + if (pr) + printk(" FRate: 29.97 fps"); + vi->framerate = 29970; + form = VIDEO_MODE_NTSC; + break; + case 5: + if (pr) + printk(" FRate: 30 fps"); + vi->framerate = 30000; + form = VIDEO_MODE_NTSC; + break; + case 6: + if (pr) + printk(" FRate: 50 fps"); + vi->framerate = 50000; + form = VIDEO_MODE_PAL; + break; + case 7: + if (pr) + printk(" FRate: 60 fps"); + vi->framerate = 60000; + form = VIDEO_MODE_NTSC; + break; + } + + vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03); + + vi->vbv_buffer_size + = (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5); + + if (pr){ + printk(" BRate: %d Mbit/s",4*(vi->bit_rate)/10000); + printk(" vbvbuffer %d",16*1024*(vi->vbv_buffer_size)); + printk("\n"); + } + + vi->video_format = form; + + return 0; +} + +int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr) +{ + uint8_t *headr; + int found = 0; + int c = 0; + + while (found < 4 && c+4 < count){ + uint8_t *b; + + b = mbuf+c; + if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01 + && b[3] == 0xb3) found = 4; + else { + c++; + } + } + + if (! found) return -1; + c += 4; + if (c+12 >= count) return -1; + headr = mbuf+c; + if (read_sequence_header(headr, vi, pr) < 0) return -1; + vi->off = c-4; + return 0; +} + +int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr) +{ + uint8_t *headr; + int found = 0; + int c = 0; + int fr = 0; + + while (found < 2 && c < count){ + uint8_t b[2]; + memcpy( b, mbuf+c, 2); + + if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8) + found = 2; + else { + c++; + } + } + + if (!found) return -1; + + if (c+3 >= count) return -1; + headr = mbuf+c; + + ai->layer = (headr[1] & 0x06) >> 1; + + if (pr) + printk("Audiostream: Layer: %d", 4-ai->layer); + + + ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000; + + if (pr){ + if (ai->bit_rate == 0) + printk(" Bit rate: free"); + else if (ai->bit_rate == 0xf) + printk(" BRate: reserved"); + else + printk(" BRate: %d kb/s", ai->bit_rate/1000); + } + + fr = (headr[2] & 0x0c ) >> 2; + ai->frequency = freq[fr]*100; + if (pr){ + if (ai->frequency == 3) + printk(" Freq: reserved\n"); + else + printk(" Freq: %d kHz\n",ai->frequency); + + } + ai->off = c; + return 0; +} + +int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr) +{ + uint8_t *headr; + int found = 0; + int c = 0; + uint8_t frame = 0; + int fr = 0; + + while ( !found && c < count){ + uint8_t *b = mbuf+c; + + if ( b[0] == 0x0b && b[1] == 0x77 ) + found = 1; + else { + c++; + } + } + + if (!found) return -1; + if (pr) + printk("Audiostream: AC3"); + + ai->off = c; + if (c+5 >= count) return -1; + + ai->layer = 0; // 0 for AC3 + headr = mbuf+c+2; + + frame = (headr[2]&0x3f); + ai->bit_rate = ac3_bitrates[frame >> 1]*1000; + + if (pr) + printk(" BRate: %d kb/s", ai->bit_rate/1000); + + ai->frequency = (headr[2] & 0xc0 ) >> 6; + fr = (headr[2] & 0xc0 ) >> 6; + ai->frequency = freq[fr]*100; + if (pr) printk (" Freq: %d Hz\n", ai->frequency); + + + ai->framesize = ac3_frames[fr][frame >> 1]; + if ((frame & 1) && (fr == 1)) ai->framesize++; + ai->framesize = ai->framesize << 1; + if (pr) printk (" Framesize %d\n", ai->framesize); + + + return 0; +} + +uint8_t *skip_pes_header(uint8_t **bufp) +{ + uint8_t *inbuf = *bufp; + uint8_t *buf = inbuf; + uint8_t *pts = NULL; + int skip = 0; + +int mpeg1_skip_table[16] = { + 1, 0xffff, 5, 10, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + + + if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */ + if (buf[7] & PTS_ONLY) + pts = buf+9; + else pts = NULL; + buf = inbuf + 9 + inbuf[8]; + } else { /* mpeg1 */ + for (buf = inbuf + 6; *buf == 0xff; buf++) + if (buf == inbuf + 6 + 16) { + break; + } + if ((*buf & 0xc0) == 0x40) + buf += 2; + skip = mpeg1_skip_table [*buf >> 4]; + if (skip == 5 || skip == 10) pts = buf; + else pts = NULL; + + buf += mpeg1_skip_table [*buf >> 4]; + } + + *bufp = buf; + return pts; +} + + +void initialize_quant_matrix( uint32_t *matrix ) +{ + int i; + + matrix[0] = 0x08101013; + matrix[1] = 0x10131616; + matrix[2] = 0x16161616; + matrix[3] = 0x1a181a1b; + matrix[4] = 0x1b1b1a1a; + matrix[5] = 0x1a1a1b1b; + matrix[6] = 0x1b1d1d1d; + matrix[7] = 0x2222221d; + matrix[8] = 0x1d1d1b1b; + matrix[9] = 0x1d1d2020; + matrix[10] = 0x22222526; + matrix[11] = 0x25232322; + matrix[12] = 0x23262628; + matrix[13] = 0x28283030; + matrix[14] = 0x2e2e3838; + matrix[15] = 0x3a454553; + + for ( i = 16 ; i < 32 ; i++ ) + matrix[i] = 0x10101010; +} + +void initialize_mpg_picture(mpg_picture *pic) +{ + int i; + + /* set MPEG1 */ + pic->mpeg1_flag = 1; + pic->profile_and_level = 0x4A ; /* MP@LL */ + pic->progressive_sequence = 1; + pic->low_delay = 0; + + pic->sequence_display_extension_flag = 0; + for ( i = 0 ; i < 4 ; i++ ){ + pic->frame_centre_horizontal_offset[i] = 0; + pic->frame_centre_vertical_offset[i] = 0; + } + pic->last_frame_centre_horizontal_offset = 0; + pic->last_frame_centre_vertical_offset = 0; + + pic->picture_display_extension_flag[0] = 0; + pic->picture_display_extension_flag[1] = 0; + pic->sequence_header_flag = 0; + pic->gop_flag = 0; + pic->sequence_end_flag = 0; +} + + +void mpg_set_picture_parameter( int32_t field_type, mpg_picture *pic ) +{ + int16_t last_h_offset; + int16_t last_v_offset; + + int16_t *p_h_offset; + int16_t *p_v_offset; + + if ( pic->mpeg1_flag ){ + pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE; + pic->top_field_first = 0; + pic->repeat_first_field = 0; + pic->progressive_frame = 1; + pic->picture_coding_parameter = 0x000010; + } + + /* Reset flag */ + pic->picture_display_extension_flag[field_type] = 0; + + last_h_offset = pic->last_frame_centre_horizontal_offset; + last_v_offset = pic->last_frame_centre_vertical_offset; + if ( field_type == FIRST_FIELD ){ + p_h_offset = pic->frame_centre_horizontal_offset; + p_v_offset = pic->frame_centre_vertical_offset; + *p_h_offset = last_h_offset; + *(p_h_offset + 1) = last_h_offset; + *(p_h_offset + 2) = last_h_offset; + *p_v_offset = last_v_offset; + *(p_v_offset + 1) = last_v_offset; + *(p_v_offset + 2) = last_v_offset; + } else { + pic->frame_centre_horizontal_offset[3] = last_h_offset; + pic->frame_centre_vertical_offset[3] = last_v_offset; + } +} + +void init_mpg_picture( mpg_picture *pic, int chan, int32_t field_type) +{ + pic->picture_header = 0; + pic->sequence_header_data + = ( INIT_HORIZONTAL_SIZE << 20 ) + | ( INIT_VERTICAL_SIZE << 8 ) + | ( INIT_ASPECT_RATIO << 4 ) + | ( INIT_FRAME_RATE ); + pic->mpeg1_flag = 0; + pic->vinfo.horizontal_size + = INIT_DISP_HORIZONTAL_SIZE; + pic->vinfo.vertical_size + = INIT_DISP_VERTICAL_SIZE; + pic->picture_display_extension_flag[field_type] + = 0; + pic->pts_flag[field_type] = 0; + + pic->sequence_gop_header = 0; + pic->picture_header = 0; + pic->sequence_header_flag = 0; + pic->gop_flag = 0; + pic->sequence_end_flag = 0; + pic->sequence_display_extension_flag = 0; + pic->last_frame_centre_horizontal_offset = 0; + pic->last_frame_centre_vertical_offset = 0; + pic->channel = chan; +} diff --git a/linux/drivers/media/dvb/dvb-core/dvb_filter.h b/linux/drivers/media/dvb/dvb-core/dvb_filter.h new file mode 100644 index 000000000..062fdcae4 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_filter.h @@ -0,0 +1,249 @@ +#ifndef _DVB_FILTER_H_ +#define _DVB_FILTER_H_ + +#include <linux/slab.h> +#include <linux/vmalloc.h> + +#include "demux.h" + +typedef int (pes2ts_cb_t) (void *, unsigned char *); + +typedef struct pes2ts_s { + unsigned char buf[188]; + unsigned char cc; + pes2ts_cb_t *cb; + void *priv; +} pes2ts_t; + +void pes2ts_init(pes2ts_t *p2ts, unsigned short pid, + pes2ts_cb_t *cb, void *priv); +int pes2ts(pes2ts_t *p2ts, unsigned char *pes, int len); + + +#define PROG_STREAM_MAP 0xBC +#define PRIVATE_STREAM1 0xBD +#define PADDING_STREAM 0xBE +#define PRIVATE_STREAM2 0xBF +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define PICTURE_START 0x00 +#define USER_START 0xb2 +#define SEQUENCE_HEADER 0xb3 +#define SEQUENCE_ERROR 0xb4 +#define EXTENSION_START 0xb5 +#define SEQUENCE_END 0xb7 +#define GOP_START 0xb8 +#define EXCEPT_SLICE 0xb0 + +#define SEQUENCE_EXTENSION 0x01 +#define SEQUENCE_DISPLAY_EXTENSION 0x02 +#define PICTURE_CODING_EXTENSION 0x08 +#define QUANT_MATRIX_EXTENSION 0x03 +#define PICTURE_DISPLAY_EXTENSION 0x07 + +#define I_FRAME 0x01 +#define B_FRAME 0x02 +#define P_FRAME 0x03 + +/* Initialize sequence_data */ +#define INIT_HORIZONTAL_SIZE 720 +#define INIT_VERTICAL_SIZE 576 +#define INIT_ASPECT_RATIO 0x02 +#define INIT_FRAME_RATE 0x03 +#define INIT_DISP_HORIZONTAL_SIZE 540 +#define INIT_DISP_VERTICAL_SIZE 576 + + +//flags2 +#define PTS_DTS_FLAGS 0xC0 +#define ESCR_FLAG 0x20 +#define ES_RATE_FLAG 0x10 +#define DSM_TRICK_FLAG 0x08 +#define ADD_CPY_FLAG 0x04 +#define PES_CRC_FLAG 0x02 +#define PES_EXT_FLAG 0x01 + +//pts_dts flags +#define PTS_ONLY 0x80 +#define PTS_DTS 0xC0 + +#define TS_SIZE 188 +#define TRANS_ERROR 0x80 +#define PAY_START 0x40 +#define TRANS_PRIO 0x20 +#define PID_MASK_HI 0x1F +//flags +#define TRANS_SCRMBL1 0x80 +#define TRANS_SCRMBL2 0x40 +#define ADAPT_FIELD 0x20 +#define PAYLOAD 0x10 +#define COUNT_MASK 0x0F + +// adaptation flags +#define DISCON_IND 0x80 +#define RAND_ACC_IND 0x40 +#define ES_PRI_IND 0x20 +#define PCR_FLAG 0x10 +#define OPCR_FLAG 0x08 +#define SPLICE_FLAG 0x04 +#define TRANS_PRIV 0x02 +#define ADAP_EXT_FLAG 0x01 + +// adaptation extension flags +#define LTW_FLAG 0x80 +#define PIECE_RATE 0x40 +#define SEAM_SPLICE 0x20 + + +#define MAX_PLENGTH 0xFFFF +#define MMAX_PLENGTH (256*MAX_PLENGTH) + +#ifndef IPACKS +#define IPACKS 2048 +#endif + +typedef struct ipack_s { + int size; + int found; + u8 *buf; + u8 cid; + uint32_t plength; + u8 plen[2]; + u8 flag1; + u8 flag2; + u8 hlength; + u8 pts[5]; + u16 *pid; + int mpeg; + u8 check; + int which; + int done; + void *data; + void (*func)(u8 *buf, int size, void *priv); + int count; + int repack_subids; +} ipack; + +typedef struct video_i{ + uint32_t horizontal_size; + uint32_t vertical_size; + uint32_t aspect_ratio; + uint32_t framerate; + uint32_t video_format; + uint32_t bit_rate; + uint32_t comp_bit_rate; + uint32_t vbv_buffer_size; + int16_t vbv_delay; + uint32_t CSPF; + uint32_t off; +} VideoInfo; + + +#define OFF_SIZE 4 +#define FIRST_FIELD 0 +#define SECOND_FIELD 1 +#define VIDEO_FRAME_PICTURE 0x03 + +typedef struct mpg_picture_s{ + int channel; + VideoInfo vinfo; + uint32_t *sequence_gop_header; + uint32_t *picture_header; + int32_t time_code; + int low_delay; + int closed_gop; + int broken_link; + int sequence_header_flag; + int gop_flag; + int sequence_end_flag; + + uint8_t profile_and_level; + int32_t picture_coding_parameter; + uint32_t matrix[32]; + int8_t matrix_change_flag; + + uint8_t picture_header_parameter; + /* bit 0 - 2: bwd f code + bit 3 : fpb vector + bit 4 - 6: fwd f code + bit 7 : fpf vector */ + + int mpeg1_flag; + int progressive_sequence; + int sequence_display_extension_flag; + uint32_t sequence_header_data; + int16_t last_frame_centre_horizontal_offset; + int16_t last_frame_centre_vertical_offset; + + uint32_t pts[2]; /* [0] 1st field, [1] 2nd field */ + int top_field_first; + int repeat_first_field; + int progressive_frame; + int bank; + int forward_bank; + int backward_bank; + int compress; + int16_t frame_centre_horizontal_offset[OFF_SIZE]; + /* [0-2] 1st field, [3] 2nd field */ + int16_t frame_centre_vertical_offset[OFF_SIZE]; + /* [0-2] 1st field, [3] 2nd field */ + int16_t temporal_reference[2]; + /* [0] 1st field, [1] 2nd field */ + + int8_t picture_coding_type[2]; + /* [0] 1st field, [1] 2nd field */ + int8_t picture_structure[2]; + /* [0] 1st field, [1] 2nd field */ + int8_t picture_display_extension_flag[2]; + /* [0] 1st field, [1] 2nd field */ + /* picture_display_extenion() 0:no 1:exit*/ + int8_t pts_flag[2]; + /* [0] 1st field, [1] 2nd field */ +} mpg_picture; + + + + +typedef struct audio_i{ + int layer ; + uint32_t bit_rate ; + uint32_t frequency ; + uint32_t mode ; + uint32_t mode_extension ; + uint32_t emphasis ; + uint32_t framesize; + uint32_t off; +} AudioInfo; + + +void reset_ipack(ipack *p); +int instant_repack(u8 *buf, int count, ipack *p); +void init_ipack(ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)); +void free_ipack(ipack * p); +void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, + void (*pes_write)(u8 *buf, int count, void *data), + void *priv); +void ts_to_pes(ipack *p, u8 *buf); +void send_ipack(ipack *p); +void send_ipack_rest(ipack *p); +int get_ainfo(uint8_t *mbuf, int count, AudioInfo *ai, int pr); +int get_ac3info(uint8_t *mbuf, int count, AudioInfo *ai, int pr); +int get_vinfo(uint8_t *mbuf, int count, VideoInfo *vi, int pr); +uint8_t *skip_pes_header(uint8_t **bufp); +void initialize_quant_matrix( uint32_t *matrix ); +void initialize_mpg_picture(mpg_picture *pic); +void init_mpg_picture( mpg_picture *pic, int chan, int32_t field_type); +void mpg_set_picture_parameter( int32_t field_type, mpg_picture *pic ); +int read_sequence_header(uint8_t *headr, VideoInfo *vi, int pr); +int read_gop_header(uint8_t *headr, mpg_picture *pic, int pr); +int read_picture_header(uint8_t *headr, mpg_picture *pic, int field, int pr); +#endif diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c new file mode 100644 index 000000000..8a6d2b79f --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -0,0 +1,931 @@ +/* + * dvb-core.c: DVB core driver + * + * Copyright (C) 1999-2001 Ralph Metzler + * Marcus Metzler + * Holger Waechtler + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/module.h> +#include <linux/compatmac.h> +#include <linux/list.h> + +#include "compat.h" +#include "dvb_frontend.h" +#include "dvbdev.h" + + +static int dvb_frontend_debug = 0; +static int dvb_shutdown_timeout = 0; + +#define dprintk if (dvb_frontend_debug) printk + +#define MAX_EVENT 8 + +struct dvb_fe_events { + struct dvb_frontend_event events[MAX_EVENT]; + int eventw; + int eventr; + int overflow; + wait_queue_head_t wait_queue; + struct semaphore sem; +}; + + +struct dvb_fe_notifier_callbacks { + struct list_head list_head; + void (*callback) (fe_status_t s, void *data); + void *data; +}; + + +struct dvb_frontend_data { + struct dvb_frontend_info *info; + struct dvb_frontend frontend; + struct dvb_device *dvbdev; + struct list_head notifier_callbacks; + struct dvb_frontend_parameters parameters; + struct dvb_fe_events events; + struct semaphore sem; + struct list_head list_head; + wait_queue_head_t wait_queue; + struct task_struct *thread; + unsigned long release_jiffies; + unsigned long lost_sync_jiffies; + int bending; + int lnb_drift; + int timeout_count; + int lost_sync_count; + int exit; + fe_status_t status; +}; + + +struct dvb_frontend_ioctl_data { + struct list_head list_head; + struct dvb_adapter_s *adapter; + int (*before_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg); + int (*after_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg); + void *before_after_data; +}; + + +static LIST_HEAD(frontend_list); +static LIST_HEAD(frontend_ioctl_list); + +static DECLARE_MUTEX(frontend_mutex); + + +static +inline void ddelay (int ms) +{ + current->state=TASK_INTERRUPTIBLE; + schedule_timeout((HZ*ms)/1000); +} + + +static +int dvb_frontend_internal_ioctl (struct dvb_frontend *frontend, + unsigned int cmd, void *arg) +{ + int err = -EOPNOTSUPP; + + dprintk ("%s\n", __FUNCTION__); + + if (frontend->before_ioctl) + err = frontend->before_ioctl (frontend, cmd, arg); + + if (err) { + err = frontend->ioctl (frontend, cmd, arg); + + if (err && frontend->after_ioctl) + err = frontend->after_ioctl (frontend, cmd, arg); + } + + return err; +} + + +/** + * if 2 tuners are located side by side you can get interferences when + * they try to tune to the same frequency, so both lose sync. + * We will slightly mistune in this case. The AFC of the demodulator + * should make it still possible to receive the requested transponder + * on both tuners... + */ +static +void dvb_bend_frequency (struct dvb_frontend_data *this_fe, int recursive) +{ + struct list_head *entry; + int stepsize = this_fe->info->frequency_stepsize; + int frequency = this_fe->parameters.frequency + this_fe->lnb_drift; + + if (!stepsize || recursive > 10) { + printk ("%s: too deep recursion, check frequency_stepsize " + "in your frontend code!\n", __FUNCTION__); + return; + } + + dprintk ("%s\n", __FUNCTION__); + + if (!recursive) { + if (down_interruptible (&frontend_mutex)) + return; + this_fe->bending = 0; + } + + list_for_each (entry, &frontend_list) { + struct dvb_frontend_data *fe; + int f; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + f = fe->parameters.frequency + fe->lnb_drift + fe->bending; + + if (this_fe != fe && + frequency >= f - stepsize && frequency <= f + stepsize) + { + if (recursive % 2 == 0) + this_fe->bending += stepsize; + else + this_fe->bending = -this_fe->bending; + dvb_bend_frequency (this_fe, recursive + 1); + goto done; + } + } +done: + if (!recursive) + up (&frontend_mutex); +} + + +static +void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe, + fe_status_t s) +{ + struct list_head *e; + struct dvb_fe_notifier_callbacks *c; + + dprintk ("%s\n", __FUNCTION__); + + if ((fe->status & FE_HAS_LOCK) && !(s & FE_HAS_LOCK)) + fe->lost_sync_jiffies = jiffies; + + if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK)) + ddelay (fe->info->notifier_delay); + + fe->status = s; + + if (!(s & FE_HAS_LOCK) && (fe->info->caps & FE_CAN_MUTE_TS)) + return; + + /** + * now tell the Demux about the TS status changes... + */ + list_for_each (e, &fe->notifier_callbacks) { + c = list_entry (e, struct dvb_fe_notifier_callbacks, list_head); + c->callback (fe->status, c->data); + } +} + + +static +void dvb_frontend_add_event (struct dvb_frontend_data *fe, fe_status_t status) +{ + struct dvb_fe_events *events = &fe->events; + struct dvb_frontend_event *e; + int wp; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&events->sem)) + return; + + wp = (events->eventw + 1) % MAX_EVENT; + + if (wp == events->eventr) { + events->overflow = 1; + events->eventr = (events->eventr + 1) % MAX_EVENT; + } + + e = &events->events[events->eventw]; + + memcpy (&e->parameters, &fe->parameters, + sizeof (struct dvb_frontend_parameters)); + + if (status & FE_HAS_LOCK) + dvb_frontend_internal_ioctl (&fe->frontend, + FE_GET_FRONTEND, + &e->parameters); + events->eventw = wp; + + up (&events->sem); + + e->status = status; + dvb_call_frontend_notifiers (fe, status); + + wake_up_interruptible (&events->wait_queue); +} + + +static +int dvb_frontend_get_event (struct dvb_frontend_data *fe, + struct dvb_frontend_event *event, int flags) +{ + struct dvb_fe_events *events = &fe->events; + + dprintk ("%s\n", __FUNCTION__); + + if (events->overflow) { + events->overflow = 0; + return -EOVERFLOW; + } + + if (events->eventw == events->eventr) { + int ret; + + if (flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible (events->wait_queue, + events->eventw != events->eventr); + if (ret < 0) + return ret; + } + + if (down_interruptible (&events->sem)) + return -ERESTARTSYS; + + memcpy (event, &events->events[events->eventr], + sizeof(struct dvb_frontend_event)); + + events->eventr = (events->eventr + 1) % MAX_EVENT; + + up (&events->sem); + + return 0; +} + + +static +struct dvb_frontend_parameters default_param [] = { + { /* NTV on Astra */ + frequency: 12669500-10600000, + inversion: INVERSION_OFF, + { qpsk: { symbol_rate: 22000000, fec_inner: FEC_AUTO } } + }, + { /* Cable */ + frequency: 394000000, + inversion: INVERSION_OFF, + { qam: { symbol_rate: 6900000, + fec_inner: FEC_AUTO, + modulation: QAM_64 + } + } + }, + { /* DVB-T */ + frequency: 730000000, + inversion: INVERSION_OFF, + { ofdm: { bandwidth: BANDWIDTH_8_MHZ, + code_rate_HP: FEC_2_3, + code_rate_LP: FEC_1_2, + constellation: QAM_16, + transmission_mode: TRANSMISSION_MODE_2K, + guard_interval: GUARD_INTERVAL_1_8, + hierarchy_information: HIERARCHY_NONE + } + } + } +}; + + +static +int dvb_frontend_set_parameters (struct dvb_frontend_data *fe, + struct dvb_frontend_parameters *param, + int first_trial) +{ + struct dvb_frontend *frontend = &fe->frontend; + int err; + + dprintk ("%s: f == %i, drift == %i\n", + __FUNCTION__, param->frequency, fe->lnb_drift); + + dvb_bend_frequency (fe, 0); + + if (first_trial) { + fe->timeout_count = 0; + fe->lost_sync_count = 0; + fe->lost_sync_jiffies = jiffies; + fe->lnb_drift = 0; + if (fe->status & ~FE_TIMEDOUT) + dvb_frontend_add_event (fe, 0); + memcpy (&fe->parameters, param, + sizeof (struct dvb_frontend_parameters)); + } + + param->frequency += fe->lnb_drift + fe->bending; + err = dvb_frontend_internal_ioctl (frontend, FE_SET_FRONTEND, param); + param->frequency -= fe->lnb_drift + fe->bending; + + wake_up_interruptible (&fe->wait_queue); + + return err; +} + +static +void dvb_frontend_init (struct dvb_frontend_data *fe) +{ + struct dvb_frontend *frontend = &fe->frontend; + struct dvb_frontend_parameters *init_param; + + printk ("%s: initialising frontend %i:%i (%s)...\n", __FUNCTION__, + frontend->i2c->adapter->num, frontend->i2c->id, + fe->info->name); + + dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL); + + if (fe->info->type == FE_QPSK) { + dvb_frontend_internal_ioctl (frontend, FE_SET_VOLTAGE, + (void*) SEC_VOLTAGE_13); + dvb_frontend_internal_ioctl (frontend, FE_SET_TONE, + (void*) SEC_TONE_ON); + } + + init_param = &default_param[fe->info->type-FE_QPSK]; + + dvb_frontend_set_parameters (fe, init_param, 1); +} + + +static +void update_delay (int *quality, int *delay, int locked) +{ + int q2; + + dprintk ("%s\n", __FUNCTION__); + + if (locked) + (*quality) = (*quality * 220 + 36*256) / 256; + else + (*quality) = (*quality * 220 + 0) / 256; + + q2 = *quality - 128; + q2 *= q2; + + *delay = HZ/20 + q2 * HZ / (128*128); +} + + +#define LNB_DRIFT 1024 /* max. tolerated LNB drift, XXX FIXME: adjust! */ +#define TIMEOUT 2*HZ + +/** + * here we only come when we have lost the lock bit, + * let's try to do something useful... + */ +static +void dvb_frontend_recover (struct dvb_frontend_data *fe) +{ + dprintk ("%s\n", __FUNCTION__); + +#if 0 + if (fe->timeout_count > 3) { + printk ("%s: frontend seems dead, reinitializing...\n", + __FUNCTION__); + dvb_call_frontend_notifiers (fe, 0); + dvb_frontend_internal_ioctl (&fe->frontend, FE_INIT, NULL); + dvb_frontend_set_parameters (fe, &fe->parameters, 1); + dvb_frontend_add_event (fe, FE_REINIT); + fe->lost_sync_jiffies = jiffies; + fe->timeout_count = 0; + return; + } +#endif + + /** + * let's start a zigzag scan to compensate LNB drift... + */ + { + int j = fe->lost_sync_count; + int stepsize = fe->info->frequency_stepsize; + + if (j % 32 == 0) + fe->lnb_drift = 0; + + if (j % 2) + fe->lnb_drift += stepsize * ((j+1)/2); + else + fe->lnb_drift = -fe->lnb_drift; + + dvb_frontend_set_parameters (fe, &fe->parameters, 0); + } + + dvb_frontend_internal_ioctl (&fe->frontend, FE_RESET, NULL); +} + + + +static +int dvb_frontend_is_exiting (struct dvb_frontend_data *fe) +{ + if (fe->exit) + return 1; + + if (fe->dvbdev->users == 0 && dvb_shutdown_timeout) + if (jiffies - fe->release_jiffies > dvb_shutdown_timeout * HZ) + return 1; + + return 0; +} + + +static +int dvb_frontend_thread (void *data) +{ + struct dvb_frontend_data *fe = (struct dvb_frontend_data *) data; + int quality = 0, delay = 3*HZ; + fe_status_t s; + + dprintk ("%s\n", __FUNCTION__); + + lock_kernel (); + daemonize (); + sigfillset (¤t->blocked); + strncpy (current->comm, "kdvb-fe", sizeof (current->comm)); + fe->thread = current; + unlock_kernel (); + + dvb_call_frontend_notifiers (fe, 0); + dvb_frontend_init (fe); + + while (!dvb_frontend_is_exiting (fe)) { + up (&fe->sem); /* is locked when we enter the thread... */ + + interruptible_sleep_on_timeout (&fe->wait_queue, delay); + + if (down_interruptible (&fe->sem)) { + fe->thread = NULL; + return -ERESTARTSYS; + } + + if (dvb_frontend_is_exiting (fe)) + break; + + dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s); + + update_delay (&quality, &delay, s & FE_HAS_LOCK); + + s &= ~FE_TIMEDOUT; + + if (s & FE_HAS_LOCK) { + fe->timeout_count = 0; + fe->lost_sync_count = 0; + } else { + fe->lost_sync_count++; +printk ("%s: no sync, lost_sync_count %i\n", __FUNCTION__, fe->lost_sync_count); + dvb_frontend_recover (fe); + delay = HZ/5; + if (jiffies - fe->lost_sync_jiffies > TIMEOUT) { + s |= FE_TIMEDOUT; + if ((fe->status & FE_TIMEDOUT) == 0) + fe->timeout_count++; + } + } + + if (s != fe->status) + dvb_frontend_add_event (fe, s); + }; + + dvb_frontend_internal_ioctl (&fe->frontend, FE_SLEEP, NULL); + up (&fe->sem); + fe->thread = NULL; + return 0; +} + + +static +void dvb_frontend_start (struct dvb_frontend_data *fe) +{ + dprintk ("%s\n", __FUNCTION__); + + if (!fe->exit && !fe->thread) { + if (down_interruptible (&fe->sem)) + return; + kernel_thread (dvb_frontend_thread, fe, 0); + } +} + + +static +void dvb_frontend_stop (struct dvb_frontend_data *fe) +{ + dprintk ("%s\n", __FUNCTION__); + + fe->exit = 1; + wake_up_interruptible (&fe->wait_queue); + + while (fe->thread) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout (5); + }; +} + + +static +int dvb_frontend_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend_data *fe = dvbdev->priv; + int err = 0; + + dprintk ("%s\n", __FUNCTION__); + + if (!fe || !fe->frontend.ioctl || fe->exit) + return -ENODEV; + + if (down_interruptible (&fe->sem)) + return -ERESTARTSYS; + + switch (cmd) { + case FE_DISEQC_SEND_MASTER_CMD: + case FE_DISEQC_SEND_BURST: + case FE_SET_TONE: + if (fe->status) + dvb_call_frontend_notifiers (fe, 0); + dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg); + break; + case FE_SET_FRONTEND: + err = dvb_frontend_set_parameters (fe, parg, 1); + break; + case FE_GET_EVENT: + err = dvb_frontend_get_event (fe, parg, file->f_flags); + break; + case FE_GET_FRONTEND: + memcpy (parg, &fe->parameters, + sizeof (struct dvb_frontend_parameters)); + /* fall-through... */ + default: + dvb_frontend_internal_ioctl (&fe->frontend, cmd, parg); + }; + + up (&fe->sem); + + return err; +} + + +static +unsigned int dvb_frontend_poll (struct file *file, struct poll_table_struct *wait) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend_data *fe = dvbdev->priv; + + dprintk ("%s\n", __FUNCTION__); + + if (fe->events.eventw != fe->events.eventr) + return (POLLIN | POLLRDNORM | POLLPRI); + + poll_wait (file, &fe->events.wait_queue, wait); + + if (fe->events.eventw != fe->events.eventr) + return (POLLIN | POLLRDNORM | POLLPRI); + + return 0; +} + + +static +int dvb_frontend_open (struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev = file->private_data; + struct dvb_frontend_data *fe = dvbdev->priv; + int ret; + + dprintk ("%s\n", __FUNCTION__); + + if ((ret = dvb_generic_open (inode, file)) < 0) + return ret; + + dvb_frontend_start (fe); + + /* empty event queue */ + fe->events.eventr = fe->events.eventw; + + return ret; +} + + +static +int dvb_frontend_release (struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev = file->private_data; + struct dvb_frontend_data *fe = dvbdev->priv; + + dprintk ("%s\n", __FUNCTION__); + + fe->release_jiffies = jiffies; + + return dvb_generic_release (inode, file); +} + + + +int +dvb_add_frontend_ioctls (struct dvb_adapter_s *adapter, + int (*before_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + int (*after_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + void *before_after_data) +{ + struct dvb_frontend_ioctl_data *ioctl; + struct list_head *entry; + int frontend_count = 0; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return -ERESTARTSYS; + + ioctl = kmalloc (sizeof(struct dvb_frontend_ioctl_data), GFP_KERNEL); + + if (!ioctl) { + up (&frontend_mutex); + return -ENOMEM; + } + + ioctl->adapter = adapter; + ioctl->before_ioctl = before_ioctl; + ioctl->after_ioctl = after_ioctl; + ioctl->before_after_data = before_after_data; + + list_add_tail (&ioctl->list_head, &frontend_ioctl_list); + + list_for_each (entry, &frontend_list) { + struct dvb_frontend_data *fe; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + if (fe->frontend.i2c->adapter == adapter && + fe->frontend.before_ioctl == NULL && + fe->frontend.after_ioctl == NULL) + { + fe->frontend.before_ioctl = before_ioctl; + fe->frontend.after_ioctl = after_ioctl; + fe->frontend.before_after_data = before_after_data; + dvb_frontend_start (fe); + frontend_count++; + } + } + + up (&frontend_mutex); + + return frontend_count; +} + + +void +dvb_remove_frontend_ioctls (struct dvb_adapter_s *adapter, + int (*before_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + int (*after_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg)) +{ + struct list_head *entry; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return; + + list_for_each (entry, &frontend_list) { + struct dvb_frontend_data *fe; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + if (fe->frontend.i2c->adapter == adapter && + fe->frontend.before_ioctl == before_ioctl && + fe->frontend.after_ioctl == after_ioctl) + { + fe->frontend.before_ioctl = NULL; + fe->frontend.after_ioctl = NULL; + + } + } + + up (&frontend_mutex); +} + + +int +dvb_add_frontend_notifier (struct dvb_adapter_s *adapter, + void (*callback) (fe_status_t s, void *data), + void *data) +{ + struct list_head *entry; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return -ERESTARTSYS; + + list_for_each (entry, &frontend_list) { + struct dvb_frontend_data *fe; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + if (fe->frontend.i2c->adapter == adapter) { + struct dvb_fe_notifier_callbacks *e; + + e = kmalloc (sizeof(struct dvb_fe_notifier_callbacks), + GFP_KERNEL); + + if (!e) { + up (&frontend_mutex); + return -ENOMEM; + } + + e->callback = callback; + e->data = data; + list_add_tail (&e->list_head, &fe->notifier_callbacks); + + up (&frontend_mutex); + return 0; + } + } + + up (&frontend_mutex); + + return -ENODEV; +} + + +void +dvb_remove_frontend_notifier (struct dvb_adapter_s *adapter, + void (*callback) (fe_status_t s, void *data)) +{ + struct list_head *entry; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return; + + list_for_each (entry, &frontend_list) { + struct dvb_frontend_data *fe; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + if (fe->frontend.i2c->adapter == adapter) { + struct list_head *e0, *n0; + + list_for_each_safe (e0, n0, &fe->notifier_callbacks) { + struct dvb_fe_notifier_callbacks *e; + + e = list_entry (e0, + struct dvb_fe_notifier_callbacks, + list_head); + list_del (&e->list_head); + kfree (e); + } + } + } + + up (&frontend_mutex); +} + + +static +struct file_operations dvb_frontend_fops = { + owner: THIS_MODULE, + ioctl: dvb_generic_ioctl, + poll: dvb_frontend_poll, + open: dvb_frontend_open, + release: dvb_frontend_release +}; + + + +int +dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + struct dvb_i2c_bus *i2c, + void *data, + struct dvb_frontend_info *info) +{ + struct list_head *entry; + struct dvb_frontend_data *fe; + dvb_device_t dvbdev_template = { users: 1, writers: 1, + fops: &dvb_frontend_fops, + kernel_ioctl: dvb_frontend_ioctl + }; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return -ERESTARTSYS; + + if (!(fe = kmalloc (sizeof (struct dvb_frontend_data), GFP_KERNEL))) { + up (&frontend_mutex); + return -ENOMEM; + } + + memset (fe, 0, sizeof (struct dvb_frontend_data)); + + init_MUTEX (&fe->sem); + init_waitqueue_head (&fe->wait_queue); + init_waitqueue_head (&fe->events.wait_queue); + init_MUTEX (&fe->events.sem); + fe->events.eventw = fe->events.eventr = 0; + fe->events.overflow = 0; + INIT_LIST_HEAD (&fe->notifier_callbacks); + + fe->frontend.ioctl = ioctl; + fe->frontend.i2c = i2c; + fe->frontend.data = data; + fe->info = info; + + list_for_each (entry, &frontend_ioctl_list) { + struct dvb_frontend_ioctl_data *ioctl; + + ioctl = list_entry (entry, struct dvb_frontend_ioctl_data, list_head); + + if (ioctl->adapter == i2c->adapter) { + fe->frontend.before_ioctl = ioctl->before_ioctl; + fe->frontend.after_ioctl = ioctl->after_ioctl; + fe->frontend.before_after_data = ioctl->before_after_data; + dvb_frontend_start (fe); + break; + } + } + + list_add_tail (&fe->list_head, &frontend_list); + + dvb_register_device (i2c->adapter, &fe->dvbdev, &dvbdev_template, + fe, DVB_DEVICE_FRONTEND); + + up (&frontend_mutex); + + return 0; +} + + +int dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + struct dvb_i2c_bus *i2c) +{ + struct list_head *entry, *n; + + dprintk ("%s\n", __FUNCTION__); + + if (down_interruptible (&frontend_mutex)) + return -ERESTARTSYS; + + list_for_each_safe (entry, n, &frontend_list) { + struct dvb_frontend_data *fe; + + fe = list_entry (entry, struct dvb_frontend_data, list_head); + + if (fe->frontend.ioctl == ioctl && fe->frontend.i2c == i2c) { + dvb_unregister_device (fe->dvbdev); + + list_del (entry); + up (&frontend_mutex); + + dvb_frontend_stop (fe); + kfree (fe); + return 0; + } + } + + up (&frontend_mutex); + return -EINVAL; +} + +MODULE_PARM(dvb_frontend_debug,"i"); +MODULE_PARM(dvb_shutdown_timeout,"i"); +MODULE_PARM_DESC(dvb_frontend_debug, "enable verbose debug messages"); +MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware"); diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.h b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h new file mode 100644 index 000000000..9a65459e6 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -0,0 +1,117 @@ +/* + * dvb-core.h + * + * Copyright (C) 2001 Ralph Metzler <ralph@convergence.de> + * overhauled by Holger Waechtler <holger@convergence.de> + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVB_FRONTEND_H_ +#define _DVB_FRONTEND_H_ + +#include <asm/types.h> +#include <linux/sched.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(x) +#endif + +#include <linux/dvb/frontend.h> + +#include "dvb_i2c.h" +#include "dvbdev.h" + + + + +/** + * when before_ioctl is registered and returns value 0, ioctl and after_ioctl + * are not executed. + */ + +struct dvb_frontend { + int (*before_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); + int (*ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); + int (*after_ioctl) (struct dvb_frontend *frontend, unsigned int cmd, void *arg); + struct dvb_i2c_bus *i2c; + void *before_after_data; /* can be used by hardware module... */ + void *data; /* can be used by hardware module... */ +}; + + +/** + * private frontend command ioctl's. + * keep them in sync with the public ones defined in linux/dvb/frontend.h + */ +#define FE_SLEEP _IO('v', 80) +#define FE_INIT _IO('v', 81) +#define FE_RESET _IO('v', 82) + + +extern int +dvb_register_frontend (int (*ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + struct dvb_i2c_bus *i2c, + void *data, + struct dvb_frontend_info *info); + +extern int +dvb_unregister_frontend (int (*ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + struct dvb_i2c_bus *i2c); + + +/** + * Add special ioctl code performed before and after the main ioctl + * to all frontend devices on the specified DVB adapter. + * This is necessairy because the 22kHz/13V-18V/DiSEqC stuff depends + * heavily on the hardware around the frontend, the same tuner can create + * these signals on about a million different ways... + * + * Return value: number of frontends where the ioctl's were applied. + */ +extern int +dvb_add_frontend_ioctls (struct dvb_adapter_s *adapter, + int (*before_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + int (*after_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + void *before_after_data); + + +extern void +dvb_remove_frontend_ioctls (struct dvb_adapter_s *adapter, + int (*before_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg), + int (*after_ioctl) (struct dvb_frontend *frontend, + unsigned int cmd, void *arg)); + +extern int +dvb_add_frontend_notifier (struct dvb_adapter_s *adapter, + void (*callback) (fe_status_t s, void *data), + void *data); +extern void +dvb_remove_frontend_notifier (struct dvb_adapter_s *adapter, + void (*callback) (fe_status_t s, void *data)); + +#endif + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_i2c.c b/linux/drivers/media/dvb/dvb-core/dvb_i2c.c new file mode 100644 index 000000000..7aae8c93d --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_i2c.c @@ -0,0 +1,301 @@ +/* + * dvb_i2c.h: simplified i2c interface for DVB adapters to get rid of i2c-core.c + * + * Copyright (C) 2002 Holger Waechtler for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/module.h> + +#include "compat.h" +#include "dvb_i2c.h" + + +struct dvb_i2c_device { + struct list_head list_head; + struct module *owner; + int (*attach) (struct dvb_i2c_bus *i2c); + void (*detach) (struct dvb_i2c_bus *i2c); +}; + + +LIST_HEAD(dvb_i2c_buslist); +LIST_HEAD(dvb_i2c_devicelist); + +DECLARE_MUTEX(dvb_i2c_mutex); + + +static +int register_i2c_client (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev) +{ + struct dvb_i2c_device *client; + + if (!(client = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL))) + return -ENOMEM; + + client->detach = dev->detach; + client->owner = dev->owner; + + INIT_LIST_HEAD(&client->list_head); + + list_add_tail (&client->list_head, &i2c->client_list); + + return 0; +} + + +static +void try_attach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev) +{ + if (dev->owner) { + if (!MOD_CAN_QUERY(dev->owner)) + return; + + __MOD_INC_USE_COUNT(dev->owner); + } + + if (dev->attach (i2c) == 0) { + register_i2c_client (i2c, dev); + } else { + if (dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); + } +} + + +static +void detach_device (struct dvb_i2c_bus *i2c, struct dvb_i2c_device *dev) +{ + dev->detach (i2c); + + if (dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); +} + + +static +void unregister_i2c_client_from_bus (struct dvb_i2c_device *dev, + struct dvb_i2c_bus *i2c) +{ + struct list_head *entry; + + list_for_each (entry, &i2c->client_list) { + struct dvb_i2c_device *client; + + client = list_entry (entry, struct dvb_i2c_device, list_head); + + if (client->detach == dev->detach) + detach_device (i2c, dev); + } +} + + +static +void unregister_i2c_client_from_all_busses (struct dvb_i2c_device *dev) +{ + struct list_head *entry; + + list_for_each (entry, &dvb_i2c_buslist) { + struct dvb_i2c_bus *i2c; + + i2c = list_entry (entry, struct dvb_i2c_bus, list_head); + + unregister_i2c_client_from_bus (dev, i2c); + } +} + + +static +void unregister_all_clients_from_bus (struct dvb_i2c_bus *i2c) +{ + struct list_head *entry, *n; + + list_for_each_safe (entry, n, &(i2c->client_list)) { + struct dvb_i2c_device *client; + + client = list_entry (entry, struct dvb_i2c_device, list_head); + + detach_device (i2c, client); + + list_del (entry); + } +} + + + +static +void probe_device_on_all_busses (struct dvb_i2c_device *dev) +{ + struct list_head *entry; + + list_for_each (entry, &dvb_i2c_buslist) { + struct dvb_i2c_bus *i2c; + + i2c = list_entry (entry, struct dvb_i2c_bus, list_head); + + try_attach_device (i2c, dev); + } +} + + +static +void probe_devices_on_bus (struct dvb_i2c_bus *i2c) +{ + struct list_head *entry; + + list_for_each (entry, &dvb_i2c_devicelist) { + struct dvb_i2c_device *dev; + + dev = list_entry (entry, struct dvb_i2c_device, list_head); + + try_attach_device (i2c, dev); + } +} + + +struct dvb_i2c_bus* +dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c, + struct i2c_msg msgs[], int num), + void *data, + struct dvb_adapter_s *adapter, + int id) +{ + struct dvb_i2c_bus *i2c; + + if (!(i2c = kmalloc (sizeof (struct dvb_i2c_bus), GFP_KERNEL))) + return NULL; + + INIT_LIST_HEAD(&i2c->list_head); + INIT_LIST_HEAD(&i2c->client_list); + + i2c->xfer = xfer; + i2c->data = data; + i2c->adapter = adapter; + i2c->id = id; + + probe_devices_on_bus (i2c); + + list_add_tail (&i2c->list_head, &dvb_i2c_buslist); + + return i2c; +} + + +struct dvb_i2c_bus* +dvb_find_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c, + struct i2c_msg msgs[], int num), + struct dvb_adapter_s *adapter, + int id) +{ + struct list_head *entry; + + if (down_interruptible (&dvb_i2c_mutex)) + return NULL; + + list_for_each (entry, &dvb_i2c_buslist) { + struct dvb_i2c_bus *i2c; + + i2c = list_entry (entry, struct dvb_i2c_bus, list_head); + + if (i2c->xfer == xfer && + i2c->adapter == adapter && + i2c->id == id) + { + up (&dvb_i2c_mutex); + return i2c; + } + } + + up (&dvb_i2c_mutex); + + return NULL; +} + + + +void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c, + struct i2c_msg msgs[], int num), + struct dvb_adapter_s *adapter, + int id) +{ + struct dvb_i2c_bus *i2c = dvb_find_i2c_bus (xfer, adapter, id); + + if (i2c) { + unregister_all_clients_from_bus (i2c); + list_del (&i2c->list_head); + kfree (i2c); + } +} + + +int dvb_register_i2c_device (struct module *owner, + int (*attach) (struct dvb_i2c_bus *i2c), + void (*detach) (struct dvb_i2c_bus *i2c)) +{ + struct dvb_i2c_device *entry; + + if (down_interruptible (&dvb_i2c_mutex)) + return -ERESTARTSYS; + + if (!(entry = kmalloc (sizeof (struct dvb_i2c_device), GFP_KERNEL))) + return -ENOMEM; + + entry->owner = owner; + entry->attach = attach; + entry->detach = detach; + + INIT_LIST_HEAD(&entry->list_head); + + probe_device_on_all_busses (entry); + + list_add_tail (&entry->list_head, &dvb_i2c_devicelist); + + up (&dvb_i2c_mutex); + + return 0; +} + + +int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c)) +{ + struct list_head *entry, *n; + + if (down_interruptible (&dvb_i2c_mutex)) + return -ERESTARTSYS; + + list_for_each_safe (entry, n, &dvb_i2c_devicelist) { + struct dvb_i2c_device *dev; + + dev = list_entry (entry, struct dvb_i2c_device, list_head); + + if (dev->attach == attach) { + list_del (entry); + unregister_i2c_client_from_all_busses (dev); + kfree (entry); + up (&dvb_i2c_mutex); + return 0; + } + } + + up (&dvb_i2c_mutex); + + return -EINVAL; +} + + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_i2c.h b/linux/drivers/media/dvb/dvb-core/dvb_i2c.h new file mode 100644 index 000000000..c06e4d1bc --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_i2c.h @@ -0,0 +1,63 @@ +/* + * dvb_i2c.h: i2c interface to get rid of i2c-core.c + * + * Copyright (C) 2002 Holger Waechtler for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _DVB_I2C_H_ +#define _DVB_I2C_H_ + +#include <linux/list.h> +#include <linux/i2c.h> + +#include "dvbdev.h" + + +struct dvb_i2c_bus { + struct list_head list_head; + int (*xfer) (struct dvb_i2c_bus *i2c, struct i2c_msg msgs[], int num); + void *data; + struct dvb_adapter_s *adapter; + int id; + struct list_head client_list; +}; + + +extern +struct dvb_i2c_bus* dvb_register_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c, + struct i2c_msg msgs[], + int num), + void *data, + struct dvb_adapter_s *adapter, + int id); + +extern +void dvb_unregister_i2c_bus (int (*xfer) (struct dvb_i2c_bus *i2c, + struct i2c_msg msgs[], int num), + struct dvb_adapter_s *adapter, + int id); + + +extern int dvb_register_i2c_device (struct module *owner, + int (*attach) (struct dvb_i2c_bus *i2c), + void (*detach) (struct dvb_i2c_bus *i2c)); + +extern int dvb_unregister_i2c_device (int (*attach) (struct dvb_i2c_bus *i2c)); + +#endif + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c new file mode 100644 index 000000000..619ec95b6 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c @@ -0,0 +1,49 @@ +#include <linux/module.h> + +#include "dmxdev.h" +#include "dvb_filter.h" +#include "dvb_frontend.h" +#include "dvb_i2c.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_net.h" + + +EXPORT_SYMBOL(DmxDevInit); +EXPORT_SYMBOL(DmxDevRelease); +EXPORT_SYMBOL(DvbDmxInit); +EXPORT_SYMBOL(DvbDmxRelease); +EXPORT_SYMBOL(DvbDmxSWFilterPackets); + +EXPORT_SYMBOL(dvb_register_frontend); +EXPORT_SYMBOL(dvb_unregister_frontend); +EXPORT_SYMBOL(dvb_add_frontend_ioctls); +EXPORT_SYMBOL(dvb_remove_frontend_ioctls); +EXPORT_SYMBOL(dvb_add_frontend_notifier); +EXPORT_SYMBOL(dvb_remove_frontend_notifier); + +EXPORT_SYMBOL(dvb_register_i2c_bus); +EXPORT_SYMBOL(dvb_unregister_i2c_bus); +EXPORT_SYMBOL(dvb_register_i2c_device); +EXPORT_SYMBOL(dvb_unregister_i2c_device); + +EXPORT_SYMBOL(dvb_net_init); +EXPORT_SYMBOL(dvb_net_release); + +EXPORT_SYMBOL(dvb_register_adapter); +EXPORT_SYMBOL(dvb_unregister_adapter); +EXPORT_SYMBOL(dvb_register_device); +EXPORT_SYMBOL(dvb_unregister_device); +EXPORT_SYMBOL(dvb_generic_ioctl); +EXPORT_SYMBOL(dvb_generic_open); +EXPORT_SYMBOL(dvb_generic_release); +EXPORT_SYMBOL(generic_usercopy); + +EXPORT_SYMBOL(init_ipack); +EXPORT_SYMBOL(reset_ipack); +EXPORT_SYMBOL(free_ipack); +EXPORT_SYMBOL(send_ipack_rest); +EXPORT_SYMBOL(instant_repack); +EXPORT_SYMBOL(pes2ts_init); +EXPORT_SYMBOL(pes2ts); + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.c b/linux/drivers/media/dvb/dvb-core/dvb_net.c new file mode 100644 index 000000000..6553682a8 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.c @@ -0,0 +1,507 @@ +/* + * dvb_net.c + * + * Copyright (C) 2001 Convergence integrated media GmbH + * Ralph Metzler <ralph@convergence.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <linux/dvb/net.h> + +#include <asm/uaccess.h> +#include "demux.h" +#include "dvb_net.h" + +/* + * Determine the packet's protocol ID. The rule here is that we + * assume 802.3 if the type field is short enough to be a length. + * This is normal practice and works for any 'now in use' protocol. + * + * stolen from eth.c out of the linux kernel, hacked for dvb-device + * by Michael Holzt <kju@debian.org> + */ + +unsigned short my_eth_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct ethhdr *eth; + unsigned char *rawp; + + skb->mac.raw=skb->data; + skb_pull(skb,dev->hard_header_len); + eth= skb->mac.ethernet; + + if(*eth->h_dest&1) + { + if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_MULTICAST; + } + + if (ntohs(eth->h_proto) >= 1536) + return eth->h_proto; + + rawp = skb->data; + + /* + * This is a magic hack to spot IPX packets. Older Novell breaks + * the protocol design and runs IPX over 802.3 without an 802.2 LLC + * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This + * won't work for fault tolerant netware but does for the rest. + */ + if (*(unsigned short *)rawp == 0xFFFF) + return htons(ETH_P_802_3); + + /* + * Real 802.2 LLC + */ + return htons(ETH_P_802_2); +} + +static void +dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len) +{ + u8 *eth; + struct sk_buff *skb; + + if (pkt_len<13) { + printk("%s: IP/MPE packet length = %d too small.\n", dev->name, pkt_len); + return; + } + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", + dev->name); + ((dvb_net_priv_t *)dev->priv)->stats.rx_dropped++; + return; + } + eth=(u8 *) skb_put(skb, pkt_len+2); + memcpy(eth+14, (void*)pkt+12, pkt_len-12); + + eth[0]=pkt[0x0b]; + eth[1]=pkt[0x0a]; + eth[2]=pkt[0x09]; + eth[3]=pkt[0x08]; + eth[4]=pkt[0x04]; + eth[5]=pkt[0x03]; + eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0; + eth[12]=0x08; eth[13]=0x00; + + skb->protocol=my_eth_type_trans(skb,dev); + skb->dev=dev; + + ((dvb_net_priv_t *)dev->priv)->stats.rx_packets++; + ((dvb_net_priv_t *)dev->priv)->stats.rx_bytes+=skb->len; + //sti(); + netif_rx(skb); +} + +static int +dvb_net_callback(u8 *buffer1, size_t buffer1_len, + u8 *buffer2, size_t buffer2_len, + dmx_section_filter_t *filter, + dmx_success_t success) +{ + struct net_device *dev=(struct net_device *) filter->priv; + + /* FIXME: this only works if exactly one complete section is + delivered in buffer1 only */ + dvb_net_sec(dev, buffer1, buffer1_len); + return 0; +} + +static int +dvb_net_tx(struct sk_buff *skb, struct net_device *dev) +{ + return 0; +} + +#define MASK 0x00; + +static int +dvb_net_filter_set(struct net_device *dev, + dmx_section_filter_t **secfilter, + unsigned char *mac) +{ + dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv; + int ret; + + *secfilter=0; + ret=priv->secfeed->allocate_filter(priv->secfeed, secfilter); + if (ret<0) { + printk("%s: could not get filter\n", dev->name); + return ret; + } + + (*secfilter)->priv=(void *) dev; + + memset((*secfilter)->filter_value, 0, DMX_MAX_FILTER_SIZE); + memset((*secfilter)->filter_mask , 0, DMX_MAX_FILTER_SIZE); + + (*secfilter)->filter_value[0]=0x3e; + (*secfilter)->filter_mask[0]=MASK; + + (*secfilter)->filter_value[3]=mac[5]; + (*secfilter)->filter_mask[3]=MASK; + (*secfilter)->filter_value[4]=mac[4]; + (*secfilter)->filter_mask[4]=MASK; + (*secfilter)->filter_value[8]=mac[3]; + (*secfilter)->filter_mask[8]=MASK; + (*secfilter)->filter_value[9]=mac[2]; + (*secfilter)->filter_mask[9]=MASK; + + (*secfilter)->filter_value[10]=mac[1]; + (*secfilter)->filter_mask[10]=MASK; + (*secfilter)->filter_value[11]=mac[0]; + (*secfilter)->filter_mask[11]=MASK; + + printk("%s: filter mac=%02x %02x %02x %02x %02x %02x\n", + dev->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return 0; +} + +static int +dvb_net_feed_start(struct net_device *dev) +{ + int ret, i; + dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv; + dmx_demux_t *demux=priv->demux; + unsigned char *mac=(unsigned char *) dev->dev_addr; + + priv->secfeed=0; + priv->secfilter=0; + + ret=demux->allocate_section_feed(demux, &priv->secfeed, + dvb_net_callback); + if (ret<0) { + printk("%s: could not get section feed\n", dev->name); + return ret; + } + + ret=priv->secfeed->set(priv->secfeed, priv->pid, 32768, 0, 0); + if (ret<0) { + printk("%s: could not set section feed\n", dev->name); + priv->demux-> + release_section_feed(priv->demux, priv->secfeed); + priv->secfeed=0; + return ret; + } + MOD_INC_USE_COUNT; + + dvb_net_filter_set(dev, &priv->secfilter, mac); + for (i=0; i<priv->multi_num; i++) + dvb_net_filter_set(dev, &priv->secfilter, + priv->multi_macs[i]); + + priv->secfeed->start_filtering(priv->secfeed); + printk("%s: feed_started\n", dev->name); + return 0; +} + +static void +dvb_net_feed_stop(struct net_device *dev) +{ + dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv; + int i; + + if (priv->secfeed) { + if (priv->secfeed->is_filtering) + priv->secfeed->stop_filtering(priv->secfeed); + printk("%s: feed_stopped\n", dev->name); + if (priv->secfilter) + priv->secfeed-> + release_filter(priv->secfeed, + priv->secfilter); + priv->secfilter=0; + + for (i=0; i<priv->multi_num; i++) { + if (priv->multi_secfilter[i]) + priv->secfeed-> + release_filter(priv->secfeed, + priv->multi_secfilter[i]); + priv->multi_secfilter[i]=0; + } + priv->demux-> + release_section_feed(priv->demux, priv->secfeed); + priv->secfeed=0; + MOD_DEC_USE_COUNT; + } else + printk("%s: no feed to stop\n", dev->name); +} + +static int +dvb_set_mc_filter(struct net_device *dev, struct dev_mc_list *mc) +{ + dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv; + + if (priv->multi_num==DVB_NET_MULTICAST_MAX) + return -ENOMEM; + + printk("%s: set_mc_filter %d: %02x %02x %02x %02x %02x %02x\n", + dev->name, + priv->multi_num, + mc->dmi_addr[0], + mc->dmi_addr[1], + mc->dmi_addr[2], + mc->dmi_addr[3], + mc->dmi_addr[4], + mc->dmi_addr[5]); + memcpy(priv->multi_macs[priv->multi_num], mc->dmi_addr, 6); + + priv->multi_num++; + return 0; +} + +static void +dvb_net_set_multi(struct net_device *dev) +{ + dvb_net_priv_t *priv=(dvb_net_priv_t *)dev->priv; + + printk("%s: set_multi()\n", dev->name); + dvb_net_feed_stop(dev); + + if (dev->flags&IFF_PROMISC) { + /* Enable promiscuous mode */ + printk("%s: promiscuous mode\n", dev->name); + } else if((dev->flags&IFF_ALLMULTI)) { + /* Disable promiscuous mode, use normal mode. */ + printk("%s: normal mode\n", dev->name); + } else if(dev->mc_count) { + int mci; + struct dev_mc_list *mc; + + printk("%s: set_mc_list, %d entries\n", + dev->name, dev->mc_count); + priv->multi_num=0; + for (mci=0, mc=dev->mc_list; + mci<dev->mc_count; + mc=mc->next, mci++) { + dvb_set_mc_filter(dev, mc); + } + } + dvb_net_feed_start(dev); +} + +static int +dvb_net_set_config(struct net_device *dev, struct ifmap *map) +{ + if (netif_running(dev)) + return -EBUSY; + return 0; +} + +static int +dvb_net_set_mac(struct net_device *dev, void *p) +{ + struct sockaddr *addr=p; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + if (netif_running(dev)) { + dvb_net_feed_stop(dev); + dvb_net_feed_start(dev); + } + return 0; +} + + +static int +dvb_net_open(struct net_device *dev) +{ + dvb_net_feed_start(dev); + return 0; +} + +static int +dvb_net_stop(struct net_device *dev) +{ + dvb_net_feed_stop(dev); + return 0; +} + +static struct net_device_stats * +dvb_net_get_stats(struct net_device *dev) +{ + return &((dvb_net_priv_t *)dev->priv)->stats; +} + + +static int +dvb_net_init_dev(struct net_device *dev) +{ + printk("dvb_net: dvb_net_init_dev()\n"); + + ether_setup(dev); + + dev->open = dvb_net_open; + dev->stop = dvb_net_stop; + dev->hard_start_xmit = dvb_net_tx; + dev->get_stats = dvb_net_get_stats; + dev->set_multicast_list = dvb_net_set_multi; + dev->set_config = dvb_net_set_config; + dev->set_mac_address = dvb_net_set_mac; + dev->mtu = 4096; + + dev->flags |= IFF_NOARP; + dev->hard_header_cache = NULL; + + //SET_MODULE_OWNER(dev); + + return 0; +} + +static int +get_if(dvb_net_t *dvbnet) +{ + int i; + + for (i=0; i<dvbnet->dev_num; i++) + if (!dvbnet->state[i]) + break; + if (i==dvbnet->dev_num) + return -1; + dvbnet->state[i]=1; + return i; +} + + +int +dvb_net_add_if(dvb_net_t *dvbnet, u16 pid) +{ + struct net_device *net; + dmx_demux_t *demux; + int result; + int if_num; + + if_num=get_if(dvbnet); + if (if_num<0) + return -EINVAL; + + net=&dvbnet->device[if_num]; + demux=dvbnet->demux; + + net->base_addr = 0; + net->irq = 0; + net->dma = 0; + net->mem_start = 0; + memcpy(net->name, "dvb0_0", 7); + net->name[3]=dvbnet->card_num+0x30; + net->name[5]=if_num+0x30; + net->next = NULL; + net->init = dvb_net_init_dev; + net->priv = kmalloc(sizeof(dvb_net_priv_t), GFP_KERNEL); + if (net->priv == NULL) + return -ENOMEM; + memset(net->priv, 0, sizeof(dvb_net_priv_t)); + + ((dvb_net_priv_t *)net->priv)->demux=demux; + ((dvb_net_priv_t *)net->priv)->pid=pid; + + net->base_addr=pid; + + if ((result = register_netdev(net)) < 0) { + return result; + } + MOD_INC_USE_COUNT; + return if_num; +} + +int +dvb_net_remove_if(dvb_net_t *dvbnet, int num) +{ + if (!dvbnet->state[num]) + return -EINVAL; + dvb_net_stop(&dvbnet->device[num]); + kfree(dvbnet->device[num].priv); + unregister_netdev(&dvbnet->device[num]); + dvbnet->state[num]=0; + MOD_DEC_USE_COUNT; + return 0; +} + +int dvb_net_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + dvb_device_t *dvbdev=(dvb_device_t *) file->private_data; + dvb_net_t *dvbnet=(dvb_net_t *) dvbdev->priv; + + if (((file->f_flags&O_ACCMODE)==O_RDONLY)) + return -EPERM; + + switch (cmd) { + case NET_ADD_IF: + { + struct dvb_net_if *dvbnetif=(struct dvb_net_if *)parg; + int result; + + result=dvb_net_add_if(dvbnet, dvbnetif->pid); + if (result<0) + return result; + dvbnetif->if_num=result; + break; + } + case NET_REMOVE_IF: + return dvb_net_remove_if(dvbnet, (int) parg); + default: + return -EINVAL; + } + return 0; +} + +static struct file_operations dvb_net_fops = { + owner: THIS_MODULE, + read: 0, + write: 0, + ioctl: dvb_generic_ioctl, + open: dvb_generic_open, + release: dvb_generic_release, + poll: 0, +}; + +static dvb_device_t dvbdev_net = { + priv: 0, + users: 1, + writers: 1, + fops: &dvb_net_fops, + kernel_ioctl: dvb_net_ioctl, +}; + +void +dvb_net_release(dvb_net_t *dvbnet) +{ + int i; + + dvb_unregister_device(dvbnet->dvbdev); + for (i=0; i<dvbnet->dev_num; i++) { + if (!dvbnet->state[i]) + continue; + dvb_net_remove_if(dvbnet, i); + } +} + +int +dvb_net_init(dvb_adapter_t *adap, dvb_net_t *dvbnet, dmx_demux_t *demux) +{ + int i; + + dvbnet->demux=demux; + dvbnet->dev_num=DVB_NET_DEVICES_MAX; + for (i=0; i<dvbnet->dev_num; i++) + dvbnet->state[i]=0; + dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, dvbnet, DVB_DEVICE_NET); + return 0; +} + diff --git a/linux/drivers/media/dvb/dvb-core/dvb_net.h b/linux/drivers/media/dvb/dvb-core/dvb_net.h new file mode 100644 index 000000000..714c09565 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvb_net.h @@ -0,0 +1,63 @@ +/* + * dvb_net.h + * + * Copyright (C) 2001 Convergence integrated media GmbH + * Ralph Metzler <ralph@convergence.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVB_NET_H_ +#define _DVB_NET_H_ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "dvbdev.h" + +#define DVB_NET_DEVICES_MAX 10 +#define DVB_NET_MULTICAST_MAX 10 + +typedef struct dvb_net_priv_s { + struct net_device_stats stats; + char name[6]; + u16 pid; + dmx_demux_t *demux; + dmx_section_feed_t *secfeed; + dmx_section_filter_t *secfilter; + int multi_num; + dmx_section_filter_t *multi_secfilter[DVB_NET_MULTICAST_MAX]; + unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6]; +} dvb_net_priv_t; + +typedef struct dvb_net_s { + dvb_device_t *dvbdev; + + int card_num; + int dev_num; + struct net_device device[DVB_NET_DEVICES_MAX]; + int state[DVB_NET_DEVICES_MAX]; + dmx_demux_t *demux; +} dvb_net_t; + + +void dvb_net_release(dvb_net_t *); +int dvb_net_init(dvb_adapter_t *, dvb_net_t *, dmx_demux_t *); + +#endif diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c new file mode 100644 index 000000000..b38cfdc6a --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -0,0 +1,434 @@ +/* + * dvbdev.c + * + * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> + * & Marcus Metzler <marcus@convergence.de> + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/*#define CONFIG_DVB_DEVFS_ONLY 1*/ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/kmod.h> +#include <linux/slab.h> + +#include "compat.h" +#include "dvbdev.h" + +static int dvbdev_debug = 0; +#define dprintk if (dvbdev_debug) printk + +static devfs_handle_t dvb_devfs_handle; +static LIST_HEAD(dvb_adapter_list); +static DECLARE_MUTEX(dvbdev_register_lock); + + +static char *dnames[] = { + "video", "audio", "sec", "frontend", "demux", "dvr", "ca", + "net", "osd" +}; + + +#ifdef CONFIG_DVB_DEVFS_ONLY + + #define DVB_MAX_IDS ~0 + #define nums2minor(num,type,id) 0 + #define DVB_DEVFS_FLAGS (DEVFS_FL_DEFAULT|DEVFS_FL_AUTO_DEVNUM) + +#else + + #define DVB_MAX_IDS 4 + #define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) + #define DVB_DEVFS_FLAGS (DEVFS_FL_DEFAULT) + + +static +dvb_device_t* dvbdev_find_device (int minor) +{ + struct list_head *entry; + + list_for_each (entry, &dvb_adapter_list) { + struct list_head *entry0; + dvb_adapter_t *adap; + adap = list_entry (entry, dvb_adapter_t, list_head); + list_for_each (entry0, &adap->device_list) { + dvb_device_t *dev; + dev = list_entry (entry0, dvb_device_t, list_head); + if (nums2minor(adap->num, dev->type, dev->id) == minor) + return dev; + } + } + + return NULL; +} + + +static +int dvb_device_open(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev; + + dvbdev = dvbdev_find_device (minor(inode->i_rdev)); + + if (dvbdev && dvbdev->fops) { + int err = 0; + struct file_operations *old_fops; + + file->private_data = dvbdev; + old_fops = file->f_op; + file->f_op = fops_get(dvbdev->fops); + if(file->f_op->open) + err = file->f_op->open(inode,file); + if (err) { + fops_put(file->f_op); + file->f_op = fops_get(old_fops); + } + fops_put(old_fops); + return err; + } + return -ENODEV; +} + + +static struct file_operations dvb_device_fops = +{ + owner: THIS_MODULE, + read: NULL, + write: NULL, + ioctl: NULL, + open: dvb_device_open, + release: NULL, + poll: NULL, +}; +#endif /* CONFIG_DVB_DEVFS_ONLY */ + + + +int dvb_generic_open(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev = file->private_data; + + if (!dvbdev) + return -ENODEV; + + if (!dvbdev->users) + return -EBUSY; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (!dvbdev->writers) + return -EBUSY; + dvbdev->writers--; + } + + dvbdev->users--; + return 0; +} + + +int dvb_generic_release(struct inode *inode, struct file *file) +{ + dvb_device_t *dvbdev = file->private_data; + + if (!dvbdev) + return -ENODEV; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + dvbdev->writers++; + + dvbdev->users++; + return 0; +} + + +/* + * helper function -- handles userspace copying for ioctl arguments + */ +int +generic_usercopy(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg)) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + parg = (void *)arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + if (mbuf) + kfree(mbuf); + + return err; +} + + +int dvb_generic_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + dvb_device_t *dvbdev = file->private_data; + + if (!dvbdev) + return -ENODEV; + + if (!dvbdev->kernel_ioctl) + return -EINVAL; + + return generic_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl); +} + + +static +int dvbdev_get_free_id (struct dvb_adapter_s *adap, int type) +{ + u32 id = 0; + + while (id < DVB_MAX_IDS) { + struct list_head *entry; + list_for_each (entry, &adap->device_list) { + dvb_device_t *dev; + dev = list_entry (entry, dvb_device_t, list_head); + if (dev->type == type && dev->id == id) + goto skip; + } + return id; +skip: + id++; + } + return -ENFILE; +} + + +int dvb_register_device(dvb_adapter_t *adap, dvb_device_t **pdvbdev, + dvb_device_t *template, void *priv, int type) +{ + u32 id; + char name [20]; + dvb_device_t *dvbdev; + + if (down_interruptible (&dvbdev_register_lock)) + return -ERESTARTSYS; + + if ((id = dvbdev_get_free_id (adap, type)) < 0) { + up (&dvbdev_register_lock); + *pdvbdev = 0; + printk ("%s: could get find free device id...\n", __FUNCTION__); + return -ENFILE; + } + + *pdvbdev = dvbdev = kmalloc(sizeof(dvb_device_t), GFP_KERNEL); + + if (!dvbdev) { + up(&dvbdev_register_lock); + return -ENOMEM; + } + + up (&dvbdev_register_lock); + + memcpy(dvbdev, template, sizeof(dvb_device_t)); + dvbdev->type = type; + dvbdev->id = id; + dvbdev->adapter = adap; + dvbdev->priv = priv; + + list_add_tail (&dvbdev->list_head, &adap->device_list); + + sprintf(name, "%s%d", dnames[type], id); + dvbdev->devfs_handle = devfs_register(adap->devfs_handle, name, + DVB_DEVFS_FLAGS, + DVB_MAJOR, + nums2minor(adap->num, type, id), + S_IFCHR | S_IRUSR | S_IWUSR, + dvbdev->fops, dvbdev); + + dprintk("%s: register adapter%d/%s @ minor: %i (0x%02x) - dvbdev: %p\n", + __FUNCTION__, adap->num, name, nums2minor(adap->num, type, id), + nums2minor(adap->num, type, id), dvbdev); + + return 0; +} + + +void dvb_unregister_device(dvb_device_t *dvbdev) +{ + if (!dvbdev) + return; + + devfs_unregister(dvbdev->devfs_handle); + list_del (&dvbdev->list_head); + kfree (dvbdev); +} + + +static +int dvbdev_get_free_adapter_num (void) +{ + int num = 0; + + while (1) { + struct list_head *entry; + list_for_each (entry, &dvb_adapter_list) { + dvb_adapter_t *adap; + adap = list_entry (entry, dvb_adapter_t, list_head); + if (adap->num == num) + goto skip; + } + return num; +skip: + num++; + } + + return -ENFILE; +} + + +int dvb_register_adapter(dvb_adapter_t **padap, char *name) +{ + char dirname[10]; + dvb_adapter_t *adap; + int num; + + if (down_interruptible (&dvbdev_register_lock)) + return -ERESTARTSYS; + + if ((num = dvbdev_get_free_adapter_num ()) < 0) { + up (&dvbdev_register_lock); + return -ENFILE; + } + + if (!(*padap = adap = kmalloc(sizeof(dvb_adapter_t), GFP_KERNEL))) { + up(&dvbdev_register_lock); + return -ENOMEM; + } + + memset (adap, 0, sizeof(dvb_adapter_t)); + INIT_LIST_HEAD (&adap->device_list); + + MOD_INC_USE_COUNT; + + printk ("%s: registering new adapter (%s).\n", __FUNCTION__, name); + + sprintf(dirname, "adapter%d", num); + adap->devfs_handle = devfs_mk_dir(dvb_devfs_handle, dirname, NULL); + adap->num = num; + + list_add_tail (&adap->list_head, &dvb_adapter_list); + + up (&dvbdev_register_lock); + + return num; +} + + +int dvb_unregister_adapter(dvb_adapter_t *adap) +{ + devfs_unregister (adap->devfs_handle); + if (down_interruptible (&dvbdev_register_lock)) + return -ERESTARTSYS; + list_del (&adap->list_head); + up (&dvbdev_register_lock); + kfree (adap); + MOD_DEC_USE_COUNT; + return 0; +} + + +static +int __init init_dvbdev(void) +{ + dvb_devfs_handle = devfs_mk_dir (NULL, "dvb", NULL); +#ifndef CONFIG_DVB_DEVFS_ONLY + if(register_chrdev(DVB_MAJOR,"DVB", &dvb_device_fops)) { + printk("video_dev: unable to get major %d\n", DVB_MAJOR); + return -EIO; + } +#endif + return 0; +} + + +static +void __exit exit_dvbdev(void) +{ +#ifndef CONFIG_DVB_DEVFS_ONLY + unregister_chrdev(DVB_MAJOR, "DVB"); +#endif + devfs_unregister(dvb_devfs_handle); +} + +module_init(init_dvbdev); +module_exit(exit_dvbdev); + +MODULE_DESCRIPTION("DVB Core Driver"); +MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler"); +MODULE_LICENSE("GPL"); + +MODULE_PARM(dvbdev_debug,"i"); +MODULE_PARM_DESC(dvbdev_debug, "enable verbose debug messages"); + diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.h b/linux/drivers/media/dvb/dvb-core/dvbdev.h new file mode 100644 index 000000000..52b0f0d09 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.h @@ -0,0 +1,91 @@ +/* + * dvbdev.h + * + * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de> + * & Marcus Metzler <marcus@convergence.de> + for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Lesser Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _DVBDEV_H_ +#define _DVBDEV_H_ + +#include <linux/types.h> +#include <linux/version.h> +#include <linux/poll.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/list.h> + +#define DVB_MAJOR 250 + +#define DVB_DEVICE_VIDEO 0 +#define DVB_DEVICE_AUDIO 1 +#define DVB_DEVICE_SEC 2 +#define DVB_DEVICE_FRONTEND 3 +#define DVB_DEVICE_DEMUX 4 +#define DVB_DEVICE_DVR 5 +#define DVB_DEVICE_CA 6 +#define DVB_DEVICE_NET 7 +#define DVB_DEVICE_OSD 8 + + +typedef struct dvb_adapter_s +{ + int num; + devfs_handle_t devfs_handle; + struct list_head list_head; + struct list_head device_list; +} dvb_adapter_t; + + +typedef struct dvb_device +{ + struct list_head list_head; + struct file_operations *fops; + devfs_handle_t devfs_handle; + dvb_adapter_t *adapter; + int type; + u32 id; + + int users; + int writers; + + /* don't really need those !? */ + int (*kernel_ioctl)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); // FIXME: use generic_usercopy() + + void *priv; +} dvb_device_t; + + +int dvb_register_device(dvb_adapter_t *adap, dvb_device_t **pdvbdev, + dvb_device_t *template, void *priv, int type); +void dvb_unregister_device(struct dvb_device *dvbdev); + +int dvb_register_adapter(dvb_adapter_t **padap, char *name); +int dvb_unregister_adapter(dvb_adapter_t *adap); + +int dvb_generic_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +int dvb_generic_open(struct inode *inode, struct file *file); +int dvb_generic_release(struct inode *inode, struct file *file); +int generic_usercopy(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg)); + +#endif /* #ifndef __DVBDEV_H */ |