Commit 0a727c0d authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] Digital TV framework

DVB is very different in its need to analogue video. This merges the
cleaned up version of the DVB framework from Convergence, which has been
around for a couple of years.
parent f232856f
......@@ -8,6 +8,7 @@ tristate 'Video For Linux' CONFIG_VIDEO_DEV
if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
source drivers/media/video/Config.in
source drivers/media/radio/Config.in
source drivers/media/dvb/Config.in
fi
endmenu
......@@ -2,6 +2,6 @@
# Makefile for the kernel multimedia device drivers.
#
obj-y := video/ radio/
obj-y := video/ radio/ dvb/
include $(TOPDIR)/Rules.make
CONFIG_DVB
Support Digital Video Broadcasting hardware. Enable this if you
own a DVB adapter and want to use it or if you compile Linux for
a digital SetTopBox.
API specs and user tools and are available for example from
<http://www.linuxtv.org/>.
Please report problems regarding this driver to the LinuxDVB
mailing list.
You might want add the following lines to your /etc/modules.conf:
alias char-major-250 dvb
alias dvb dvb-ttpci
below dvb-ttpci alps_bsru6 alps_bsrv2 \
grundig_29504-401 grundig_29504-491 \
ves1820
If unsure say N.
#
# Multimedia device configuration
#
mainmenu_option next_comment
comment 'Digital Video Broadcasting Devices'
bool 'DVB For Linux' CONFIG_DVB
if [ "$CONFIG_DVB" != "n" ]; then
source drivers/media/dvb/dvb-core/Config.in
source drivers/media/dvb/frontends/Config.in
comment 'Supported DVB Adapters'
source drivers/media/dvb/av7110/Config.in
fi
endmenu
#
# Makefile for the kernel multimedia device drivers.
#
obj-y := dvb-core/ frontends/ av7110/
include $(TOPDIR)/Rules.make
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.
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
#
# 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
#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
/* 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 */
/*
* dmxdev.c - DVB demultiplexer device
*
* Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
* & Marcus Metzler <marcus@convergence.de>
for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include "dmxdev.h"
//MODULE_DESCRIPTION("");
//MODULE_AUTHOR("Ralph Metzler, Marcus Metzler");
//#ifdef MODULE_LICENSE
//MODULE_LICENSE("GPL");
//#endif
MODULE_PARM(debug,"i");
static int debug = 0;
#define dprintk if (debug) printk
inline dmxdev_filter_t *
DmxDevFile2Filter(struct file *file)
{
return (dmxdev_filter_t *) file->private_data;
}
inline dmxdev_dvr_t *
DmxDevFile2DVR(dmxdev_t *dmxdev, struct file *file)
{
return (dmxdev_dvr_t *) file->private_data;
}
static inline void
DmxDevBufferInit(dmxdev_buffer_t *buffer)
{
buffer->data=0;
buffer->size=8192;
buffer->pread=0;
buffer->pwrite=0;
buffer->error=0;
init_waitqueue_head(&buffer->queue);
}
static inline int
DmxDevBufferWrite(dmxdev_buffer_t *buf, uint8_t *src, int len)
{
int split;
int free;
int todo;
if (!len)
return 0;
if (!buf->data)
return 0;
free=buf->pread-buf->pwrite;
split=0;
if (free<=0) {
free+=buf->size;
split=buf->size-buf->pwrite;
}
if (len>=free) {
dprintk("dmxdev: buffer overflow\n");
return -1;
}
if (split>=len)
split=0;
todo=len;
if (split) {
memcpy(buf->data + buf->pwrite, src, split);
todo-=split;
buf->pwrite=0;
}
memcpy(buf->data + buf->pwrite, src+split, todo);
buf->pwrite=(buf->pwrite+todo)%buf->size;
return len;
}
static ssize_t
DmxDevBufferRead(dmxdev_buffer_t *src, int non_blocking,
char *buf, size_t count, loff_t *ppos)
{
unsigned long todo=count;
int split, avail, error;
if (!src->data)
return 0;
if ((error=src->error)) {
src->error=0;
return error;
}
if (non_blocking && (src->pwrite==src->pread))
return -EWOULDBLOCK;
while (todo>0) {
if (non_blocking && (src->pwrite==src->pread))
return (count-todo) ? (count-todo) : -EWOULDBLOCK;
if (wait_event_interruptible(src->queue,
(src->pread!=src->pwrite) ||
(src->error))<0)
return count-todo;
if ((error=src->error)) {
src->error=0;
return error;
}
split=src->size;
avail=src->pwrite - src->pread;
if (avail<0) {
avail+=src->size;
split=src->size - src->pread;
}
if (avail>todo)
avail=todo;
if (split<avail) {
if (copy_to_user(buf, src->data+src->pread, split))
return -EFAULT;
buf+=split;
src->pread=0;
todo-=split;
avail-=split;
}
if (avail) {
if (copy_to_user(buf, src->data+src->pread, avail))
return -EFAULT;
src->pread = (src->pread + avail) % src->size;
todo-=avail;
buf+=avail;
}
}
return count;
}
static dmx_frontend_t *
get_fe(dmx_demux_t *demux, int type)
{
struct list_head *head, *pos;
head=demux->get_frontends(demux);
if (!head)
return 0;
list_for_each(pos, head)
if (DMX_FE_ENTRY(pos)->source==type)
return DMX_FE_ENTRY(pos);
return 0;
}
static inline void
DmxDevDVRStateSet(dmxdev_dvr_t *dmxdevdvr, int state)
{
spin_lock_irq(&dmxdevdvr->dev->lock);
dmxdevdvr->state=state;
spin_unlock_irq(&dmxdevdvr->dev->lock);
}
static int dvb_dvr_open(struct inode *inode, struct file *file)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
dmx_frontend_t *front;
dprintk ("function : %s\n", __FUNCTION__);
if (down_interruptible (&dmxdev->mutex))
return -ERESTARTSYS;
if ((file->f_flags&O_ACCMODE)==O_RDWR) {
if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) {
up(&dmxdev->mutex);
return -EOPNOTSUPP;
}
}
if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
DmxDevBufferInit(&dmxdev->dvr_buffer);
dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE;
dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE);
if (!dmxdev->dvr_buffer.data) {
up(&dmxdev->mutex);
return -ENOMEM;
}
}
if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
dmxdev->dvr_orig_fe=dmxdev->demux->frontend;
if (!dmxdev->demux->write) {
up(&dmxdev->mutex);
return -EOPNOTSUPP;
}
front=get_fe(dmxdev->demux, DMX_MEMORY_FE);
if (!front) {
up(&dmxdev->mutex);
return -EINVAL;
}
dmxdev->demux->disconnect_frontend(dmxdev->demux);
dmxdev->demux->connect_frontend(dmxdev->demux, front);
}
up(&dmxdev->mutex);
return 0;
}
static int dvb_dvr_release(struct inode *inode, struct file *file)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
if (down_interruptible (&dmxdev->mutex))
return -ERESTARTSYS;
if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
dmxdev->demux->disconnect_frontend(dmxdev->demux);
dmxdev->demux->connect_frontend(dmxdev->demux,
dmxdev->dvr_orig_fe);
}
if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
if (dmxdev->dvr_buffer.data) {
void *mem=dmxdev->dvr_buffer.data;
mb();
spin_lock_irq(&dmxdev->lock);
dmxdev->dvr_buffer.data=0;
spin_unlock_irq(&dmxdev->lock);
vfree(mem);
}
}
up(&dmxdev->mutex);
return 0;
}
static ssize_t
dvb_dvr_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
int ret;
if (!dmxdev->demux->write)
return -EOPNOTSUPP;
if ((file->f_flags&O_ACCMODE)!=O_WRONLY)
return -EINVAL;
if (down_interruptible (&dmxdev->mutex))
return -ERESTARTSYS;
ret=dmxdev->demux->write(dmxdev->demux, buf, count);
up(&dmxdev->mutex);
return ret;
}
static ssize_t
dvb_dvr_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
int ret;
//down(&dmxdev->mutex);
ret= DmxDevBufferRead(&dmxdev->dvr_buffer,
file->f_flags&O_NONBLOCK,
buf, count, ppos);
//up(&dmxdev->mutex);
return ret;
}
static inline void
DmxDevFilterStateSet(dmxdev_filter_t *dmxdevfilter, int state)
{
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->state=state;
spin_unlock_irq(&dmxdevfilter->dev->lock);
}
static int
DmxDevSetBufferSize(dmxdev_filter_t *dmxdevfilter, unsigned long size)
{
dmxdev_buffer_t *buf=&dmxdevfilter->buffer;
void *mem;
if (buf->size==size)
return 0;
if (dmxdevfilter->state>=DMXDEV_STATE_GO)
return -EBUSY;
spin_lock_irq(&dmxdevfilter->dev->lock);
mem=buf->data;
buf->data=0;
buf->size=size;
buf->pwrite=buf->pread=0;
spin_unlock_irq(&dmxdevfilter->dev->lock);
if (mem)
vfree(mem);
if (buf->size) {
mem=vmalloc(dmxdevfilter->buffer.size);
if (!mem)
return -ENOMEM;
spin_lock_irq(&dmxdevfilter->dev->lock);
buf->data=mem;
spin_unlock_irq(&dmxdevfilter->dev->lock);
}
return 0;
}
static void
DmxDevFilterTimeout(unsigned long data)
{
dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *)data;
dmxdevfilter->buffer.error=-ETIMEDOUT;
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT;
spin_unlock_irq(&dmxdevfilter->dev->lock);
wake_up(&dmxdevfilter->buffer.queue);
}
static void
DmxDevFilterTimer(dmxdev_filter_t *dmxdevfilter)
{
struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
del_timer(&dmxdevfilter->timer);
if (para->timeout) {
dmxdevfilter->timer.function=DmxDevFilterTimeout;
dmxdevfilter->timer.data=(unsigned long) dmxdevfilter;
dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000;
add_timer(&dmxdevfilter->timer);
}
}
static int
DmxDevSectionCallback(u8 *buffer1, size_t buffer1_len,
u8 *buffer2, size_t buffer2_len,
dmx_section_filter_t *filter,
dmx_success_t success)
{
dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) filter->priv;
int ret;
if (dmxdevfilter->buffer.error) {
wake_up(&dmxdevfilter->buffer.queue);
return 0;
}
spin_lock(&dmxdevfilter->dev->lock);
if (dmxdevfilter->state!=DMXDEV_STATE_GO) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
del_timer(&dmxdevfilter->timer);
dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
buffer1[0], buffer1[1],
buffer1[2], buffer1[3],
buffer1[4], buffer1[5]);
ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer1, buffer1_len);
if (ret==buffer1_len) {
ret=DmxDevBufferWrite(&dmxdevfilter->buffer, buffer2, buffer2_len);
}
if (ret<0) {
dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread;
dmxdevfilter->buffer.error=-EOVERFLOW;
}
if (dmxdevfilter->params.sec.flags&DMX_ONESHOT)
dmxdevfilter->state=DMXDEV_STATE_DONE;
spin_unlock(&dmxdevfilter->dev->lock);
wake_up(&dmxdevfilter->buffer.queue);
return 0;
}
static int
DmxDevTSCallback(u8 *buffer1, size_t buffer1_len,
u8 *buffer2, size_t buffer2_len,
dmx_ts_feed_t *feed,
dmx_success_t success)
{
dmxdev_filter_t *dmxdevfilter=(dmxdev_filter_t *) feed->priv;
dmxdev_buffer_t *buffer;
int ret;
spin_lock(&dmxdevfilter->dev->lock);
if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) {
spin_unlock(&dmxdevfilter->dev->lock);
return 0;
}
if (dmxdevfilter->params.pes.output==DMX_OUT_TAP)
buffer=&dmxdevfilter->buffer;
else
buffer=&dmxdevfilter->dev->dvr_buffer;
if (buffer->error) {
spin_unlock(&dmxdevfilter->dev->lock);
wake_up(&buffer->queue);
return 0;
}
ret=DmxDevBufferWrite(buffer, buffer1, buffer1_len);
if (ret==buffer1_len)
ret=DmxDevBufferWrite(buffer, buffer2, buffer2_len);
if (ret<0) {
buffer->pwrite=buffer->pread;
buffer->error=-EOVERFLOW;
}
spin_unlock(&dmxdevfilter->dev->lock);
wake_up(&buffer->queue);
return 0;
}
/* stop feed but only mark the specified filter as stopped (state set) */
static int
DmxDevFeedStop(dmxdev_filter_t *dmxdevfilter)
{
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET);
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
del_timer(&dmxdevfilter->timer);
dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
break;
case DMXDEV_TYPE_PES:
dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
break;
default:
return -EINVAL;
}
return 0;
}
/* start feed associated with the specified filter */
static int
DmxDevFeedStart(dmxdev_filter_t *dmxdevfilter)
{
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO);
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
dmxdevfilter->feed.sec->start_filtering(dmxdevfilter->feed.sec);
break;
case DMXDEV_TYPE_PES:
dmxdevfilter->feed.ts->start_filtering(dmxdevfilter->feed.ts);
break;
default:
return -EINVAL;
}
return 0;
}
/* restart section feed if it has filters left associated with it,
otherwise release the feed */
static int
DmxDevFeedRestart(dmxdev_filter_t *dmxdevfilter)
{
int i;
dmxdev_t *dmxdev=dmxdevfilter->dev;
uint16_t pid=dmxdevfilter->params.sec.pid;
for (i=0; i<dmxdev->filternum; i++)
if (dmxdev->filter[i].state>=DMXDEV_STATE_GO &&
dmxdev->filter[i].type==DMXDEV_TYPE_SEC &&
dmxdev->filter[i].pid==pid) {
DmxDevFeedStart(&dmxdev->filter[i]);
return 0;
}
dmxdevfilter->dev->demux->
release_section_feed(dmxdev->demux,
dmxdevfilter->feed.sec);
return 0;
}
static int
DmxDevFilterStop(dmxdev_filter_t *dmxdevfilter)
{
if (dmxdevfilter->state<DMXDEV_STATE_GO)
return 0;
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
if (!dmxdevfilter->feed.sec)
break;
DmxDevFeedStop(dmxdevfilter);
if (dmxdevfilter->filter.sec)
dmxdevfilter->feed.sec->
release_filter(dmxdevfilter->feed.sec,
dmxdevfilter->filter.sec);
DmxDevFeedRestart(dmxdevfilter);
dmxdevfilter->feed.sec=0;
break;
case DMXDEV_TYPE_PES:
if (!dmxdevfilter->feed.ts)
break;
DmxDevFeedStop(dmxdevfilter);
dmxdevfilter->dev->demux->
release_ts_feed(dmxdevfilter->dev->demux,
dmxdevfilter->feed.ts);
dmxdevfilter->feed.ts=0;
break;
default:
if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED)
return 0;
return -EINVAL;
}
dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0;
return 0;
}
static inline int
DmxDevFilterReset(dmxdev_filter_t *dmxdevfilter)
{
if (dmxdevfilter->state<DMXDEV_STATE_SET)
return 0;
dmxdevfilter->type=DMXDEV_TYPE_NONE;
dmxdevfilter->pid=0xffff;
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
return 0;
}
static int
DmxDevFilterStart(dmxdev_filter_t *dmxdevfilter)
{
dmxdev_t *dmxdev=dmxdevfilter->dev;
void *mem;
int ret, i;
if (dmxdevfilter->state<DMXDEV_STATE_SET)
return -EINVAL;
if (dmxdevfilter->state>=DMXDEV_STATE_GO)
DmxDevFilterStop(dmxdevfilter);
mem=dmxdevfilter->buffer.data;
if (!mem) {
mem=vmalloc(dmxdevfilter->buffer.size);
spin_lock_irq(&dmxdevfilter->dev->lock);
dmxdevfilter->buffer.data=mem;
spin_unlock_irq(&dmxdevfilter->dev->lock);
if (!dmxdevfilter->buffer.data)
return -ENOMEM;
}
switch (dmxdevfilter->type) {
case DMXDEV_TYPE_SEC:
{
struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
dmx_section_filter_t **secfilter=&dmxdevfilter->filter.sec;
dmx_section_feed_t **secfeed=&dmxdevfilter->feed.sec;
*secfilter=0;
*secfeed=0;
/* find active filter/feed with same PID */
for (i=0; i<dmxdev->filternum; i++)
if (dmxdev->filter[i].state>=DMXDEV_STATE_GO &&
dmxdev->filter[i].pid==para->pid) {
if (dmxdev->filter[i].type!=DMXDEV_TYPE_SEC)
return -EBUSY;
*secfeed=dmxdev->filter[i].feed.sec;
break;
}
/* if no feed found, try to allocate new one */
if (!*secfeed) {
ret=dmxdev->demux->
allocate_section_feed(dmxdev->demux,
secfeed,
DmxDevSectionCallback);
if (ret<0) {
printk ("DVB (%s): could not alloc feed\n",
__FUNCTION__);
return ret;
}
ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0,
(para->flags & DMX_CHECK_CRC) ? 1 : 0);
if (ret<0) {
printk ("DVB (%s): could not set feed\n",
__FUNCTION__);
DmxDevFeedRestart(dmxdevfilter);
return ret;
}
}
else
DmxDevFeedStop(dmxdevfilter);
ret=(*secfeed)->allocate_filter(*secfeed, secfilter);
if (ret<0) {
DmxDevFeedRestart(dmxdevfilter);
dmxdevfilter->feed.sec->
start_filtering(*secfeed);
dprintk ("could not get filter\n");
return ret;
}
(*secfilter)->priv=(void *) dmxdevfilter;
memcpy(&((*secfilter)->filter_value[3]),
&(para->filter.filter[1]), DMX_FILTER_SIZE-1);
memcpy(&(*secfilter)->filter_mask[3],
&para->filter.mask[1], DMX_FILTER_SIZE-1);
memcpy(&(*secfilter)->filter_mode[3],
&para->filter.mode[1], DMX_FILTER_SIZE-1);
(*secfilter)->filter_value[0]=para->filter.filter[0];
(*secfilter)->filter_mask[0]=para->filter.mask[0];
(*secfilter)->filter_mode[0]=para->filter.mode[0];
(*secfilter)->filter_mask[1]=0;
(*secfilter)->filter_mask[2]=0;
dmxdevfilter->todo=0;
dmxdevfilter->feed.sec->
start_filtering(dmxdevfilter->feed.sec);
DmxDevFilterTimer(dmxdevfilter);
break;
}
case DMXDEV_TYPE_PES:
{
struct timespec timeout = {0 };
struct dmx_pes_filter_params *para=&dmxdevfilter->params.pes;
dmx_output_t otype;
int ret;
int ts_type;
dmx_ts_pes_t ts_pes;
dmx_ts_feed_t **tsfeed=&dmxdevfilter->feed.ts;
dmxdevfilter->feed.ts=0;
otype=para->output;
ts_pes=(dmx_ts_pes_t) para->pes_type;
if (ts_pes<DMX_PES_OTHER)
ts_type=TS_DECODER;
else
ts_type=0;
if (otype==DMX_OUT_TS_TAP)
ts_type|=TS_PACKET;
if (otype==DMX_OUT_TAP)
ts_type|=TS_PAYLOAD_ONLY|TS_PACKET;
ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux,
tsfeed,
DmxDevTSCallback);
if (ret<0)
return ret;
(*tsfeed)->priv=(void *) dmxdevfilter;
ret=(*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes, 188, 32768, 0, timeout);
if (ret<0) {
dmxdev->demux->
release_ts_feed(dmxdev->demux, *tsfeed);
return ret;
}
dmxdevfilter->feed.ts->
start_filtering(dmxdevfilter->feed.ts);
break;
}
default:
return -EINVAL;
}
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_GO);
return 0;
}
static int dvb_demux_open(struct inode *inode, struct file *file)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
int i;
dmxdev_filter_t *dmxdevfilter;
if (!dmxdev->filter)
return -EINVAL;
if (down_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
for (i=0; i<dmxdev->filternum; i++)
if (dmxdev->filter[i].state==DMXDEV_STATE_FREE)
break;
if (i==dmxdev->filternum) {
up(&dmxdev->mutex);
return -EMFILE;
}
dmxdevfilter=&dmxdev->filter[i];
dmxdevfilter->dvbdev=dmxdev->dvbdev;
file->private_data=dmxdevfilter;
DmxDevBufferInit(&dmxdevfilter->buffer);
dmxdevfilter->type=DMXDEV_TYPE_NONE;
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
dmxdevfilter->feed.ts=0;
init_timer(&dmxdevfilter->timer);
up(&dmxdev->mutex);
return 0;
}
int
DmxDevFilterFree(dmxdev_t *dmxdev, dmxdev_filter_t *dmxdevfilter)
{
if (down_interruptible(&dmxdev->mutex))
return -ERESTARTSYS;
DmxDevFilterStop(dmxdevfilter);
DmxDevFilterReset(dmxdevfilter);
if (dmxdevfilter->buffer.data) {
void *mem=dmxdevfilter->buffer.data;
spin_lock_irq(&dmxdev->lock);
dmxdevfilter->buffer.data=0;
spin_unlock_irq(&dmxdev->lock);
vfree(mem);
}
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_FREE);
wake_up(&dmxdevfilter->buffer.queue);
up(&dmxdev->mutex);
return 0;
}
static inline void
invert_mode(dmx_filter_t *filter)
{
int i;
for (i=0; i<DMX_FILTER_SIZE; i++)
filter->mode[i]^=0xff;
}
static int
DmxDevFilterSet(dmxdev_t *dmxdev,
dmxdev_filter_t *dmxdevfilter,
struct dmx_sct_filter_params *params)
{
dprintk ("function : %s\n", __FUNCTION__);
DmxDevFilterStop(dmxdevfilter);
dmxdevfilter->type=DMXDEV_TYPE_SEC;
dmxdevfilter->pid=params->pid;
memcpy(&dmxdevfilter->params.sec,
params, sizeof(struct dmx_sct_filter_params));
invert_mode(&dmxdevfilter->params.sec.filter);
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET);
if (params->flags&DMX_IMMEDIATE_START)
return DmxDevFilterStart(dmxdevfilter);
return 0;
}
static int
DmxDevPesFilterSet(dmxdev_t *dmxdev,
dmxdev_filter_t *dmxdevfilter,
struct dmx_pes_filter_params *params)
{
DmxDevFilterStop(dmxdevfilter);
if (params->pes_type>DMX_PES_OTHER || params->pes_type<0)
return -EINVAL;
dmxdevfilter->type=DMXDEV_TYPE_PES;
dmxdevfilter->pid=params->pid;
memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params));
DmxDevFilterStateSet(dmxdevfilter, DMXDEV_STATE_SET);
if (params->flags&DMX_IMMEDIATE_START)
return DmxDevFilterStart(dmxdevfilter);
return 0;
}
static ssize_t
DmxDevReadSec(dmxdev_filter_t *dfil, struct file *file,
char *buf, size_t count, loff_t *ppos)
{
int result, hcount;
int done=0;
if (dfil->todo<=0) {
hcount=3+dfil->todo;
if (hcount>count)
hcount=count;
result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK,
buf, hcount, ppos);
if (result<0) {
dfil->todo=0;
return result;
}
if (copy_from_user(dfil->secheader-dfil->todo, buf, result))
return -EFAULT;
buf+=result;
done=result;
count-=result;
dfil->todo-=result;
if (dfil->todo>-3)
return done;
dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff;
if (!count)
return done;
}
if (count>dfil->todo)
count=dfil->todo;
result=DmxDevBufferRead(&dfil->buffer, file->f_flags&O_NONBLOCK,
buf, count, ppos);
if (result<0)
return result;
dfil->todo-=result;
return (result+done);
}
ssize_t
dvb_demux_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
//dmxdev_t *dmxdev=dmxdevfilter->dev;
int ret=0;
// semaphore should not be necessary (I hope ...)
//down(&dmxdev->mutex);
if (dmxdevfilter->type==DMXDEV_TYPE_SEC)
ret=DmxDevReadSec(dmxdevfilter, file, buf, count, ppos);
else
ret=DmxDevBufferRead(&dmxdevfilter->buffer,
file->f_flags&O_NONBLOCK,
buf, count, ppos);
//up(&dmxdev->mutex);
return ret;
}
static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *parg)
{
dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
dmxdev_t *dmxdev=dmxdevfilter->dev;
unsigned long arg=(unsigned long) parg;
int ret=0;
if (down_interruptible (&dmxdev->mutex))
return -ERESTARTSYS;
switch (cmd) {
case DMX_START:
if (dmxdevfilter->state<DMXDEV_STATE_SET)
ret=-EINVAL;
else
ret=DmxDevFilterStart(dmxdevfilter);
break;
case DMX_STOP:
ret=DmxDevFilterStop(dmxdevfilter);
break;
case DMX_SET_FILTER:
ret=DmxDevFilterSet(dmxdev, dmxdevfilter,
(struct dmx_sct_filter_params *)parg);
break;
case DMX_SET_PES_FILTER:
ret=DmxDevPesFilterSet(dmxdev, dmxdevfilter,
(struct dmx_pes_filter_params *)parg);
break;
case DMX_SET_BUFFER_SIZE:
ret=DmxDevSetBufferSize(dmxdevfilter, arg);
break;
case DMX_GET_EVENT:
break;
case DMX_GET_PES_PIDS:
if (!dmxdev->demux->get_pes_pids) {
ret=-EINVAL;
break;
}
dmxdev->demux->get_pes_pids(dmxdev->demux, (uint16_t *)parg);
break;
default:
ret=-EINVAL;
}
up(&dmxdev->mutex);
return ret;
}
static int dvb_demux_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return generic_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
}
static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
{
dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
if (!dmxdevfilter)
return -EINVAL;
if (dmxdevfilter->state==DMXDEV_STATE_FREE)
return 0;
if (dmxdevfilter->buffer.error)
return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite)
return (POLLIN | POLLRDNORM | POLLPRI);
if (dmxdevfilter->state!=DMXDEV_STATE_GO)
return 0;
poll_wait(file, &dmxdevfilter->buffer.queue, wait);
if (dmxdevfilter->state==DMXDEV_STATE_FREE)
return 0;
if (dmxdevfilter->buffer.error)
return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (dmxdevfilter->buffer.pread!=dmxdevfilter->buffer.pwrite)
return (POLLIN | POLLRDNORM | POLLPRI);
return 0;
}
static int dvb_demux_release(struct inode *inode, struct file *file)
{
dmxdev_filter_t *dmxdevfilter=DmxDevFile2Filter(file);
dmxdev_t *dmxdev=dmxdevfilter->dev;
return DmxDevFilterFree(dmxdev, dmxdevfilter);
}
static struct file_operations dvb_demux_fops = {
owner: THIS_MODULE,
read: dvb_demux_read,
write: 0,
ioctl: dvb_demux_ioctl,
open: dvb_demux_open,
release: dvb_demux_release,
poll: dvb_demux_poll,
};
static dvb_device_t dvbdev_demux = {
priv: 0,
users: 1,
writers: 1,
fops: &dvb_demux_fops
};
static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *parg)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
int ret=0;
if (down_interruptible (&dmxdev->mutex))
return -ERESTARTSYS;
switch (cmd) {
case DMX_SET_BUFFER_SIZE:
// FIXME: implement
ret=0;
break;
default:
ret=-EINVAL;
}
up(&dmxdev->mutex);
return ret;
}
static int dvb_dvr_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return generic_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
}
static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
{
dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
dmxdev_t *dmxdev=(dmxdev_t *) dvbdev->priv;
dprintk ("function : %s\n", __FUNCTION__);
if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
if (dmxdev->dvr_buffer.error)
return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
return (POLLIN | POLLRDNORM | POLLPRI);
poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
if (dmxdev->dvr_buffer.error)
return (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
return (POLLIN | POLLRDNORM | POLLPRI);
return 0;
} else
return (POLLOUT | POLLWRNORM | POLLPRI);
}
static struct file_operations dvb_dvr_fops = {
owner: THIS_MODULE,
read: dvb_dvr_read,
write: dvb_dvr_write,
ioctl: dvb_dvr_ioctl,
open: dvb_dvr_open,
release: dvb_dvr_release,
poll: dvb_dvr_poll,
};
static dvb_device_t dvbdev_dvr = {
priv: 0,
users: 1,
writers: 1,
fops: &dvb_dvr_fops
};
int
DmxDevInit(dmxdev_t *dmxdev, dvb_adapter_t *dvb_adapter)
{
int i;
if (dmxdev->demux->open(dmxdev->demux)<0)
return -EUSERS;
dmxdev->filter=vmalloc(dmxdev->filternum*sizeof(dmxdev_filter_t));
if (!dmxdev->filter)
return -ENOMEM;
dmxdev->dvr=vmalloc(dmxdev->filternum*sizeof(dmxdev_dvr_t));
if (!dmxdev->dvr) {
vfree(dmxdev->filter);
dmxdev->filter=0;
return -ENOMEM;
}
sema_init(&dmxdev->mutex, 1);
spin_lock_init(&dmxdev->lock);
for (i=0; i<dmxdev->filternum; i++) {
dmxdev->filter[i].dev=dmxdev;
dmxdev->filter[i].buffer.data=0;
DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE);
dmxdev->dvr[i].dev=dmxdev;
dmxdev->dvr[i].buffer.data=0;
DmxDevFilterStateSet(&dmxdev->filter[i], DMXDEV_STATE_FREE);
DmxDevDVRStateSet(&dmxdev->dvr[i], DMXDEV_STATE_FREE);
}
dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX);
dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR);
DmxDevBufferInit(&dmxdev->dvr_buffer);
MOD_INC_USE_COUNT;
return 0;
}
void
DmxDevRelease(dmxdev_t *dmxdev)
{
dvb_unregister_device(dmxdev->dvbdev);
dvb_unregister_device(dmxdev->dvr_dvbdev);
if (dmxdev->filter) {
vfree(dmxdev->filter);
dmxdev->filter=0;
}
if (dmxdev->dvr) {
vfree(dmxdev->dvr);
dmxdev->dvr=0;
}
dmxdev->demux->close(dmxdev->demux);
MOD_DEC_USE_COUNT;
}
/*
* 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_ */
/*
* 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
/*
* 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_ */
#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;
}
#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
/*
* 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;
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;
f += fe->lnb_drift;
f += fe->bending;
frequency = this_fe->parameters.frequency;
frequency += this_fe->lnb_drift;
frequency += this_fe->bending;
if (this_fe != fe &&
frequency > f - stepsize && frequency < f + stepsize)
{
if (recursive % 2)
this_fe->bending += stepsize;
else
this_fe->bending = -this_fe->bending;
dvb_bend_frequency (this_fe, recursive + 1);
goto done;
}
}
done:
if (!recursive)
up (&frontend_mutex);
}
static
void dvb_call_frontend_notifiers (struct dvb_frontend_data *fe,
fe_status_t s)
{
struct list_head *e;
struct dvb_fe_notifier_callbacks *c;
dprintk ("%s\n", __FUNCTION__);
if ((fe->status & FE_HAS_LOCK) && !(s & FE_HAS_LOCK))
fe->lost_sync_jiffies = jiffies;
if (((s ^ fe->status) & FE_HAS_LOCK) && (s & FE_HAS_LOCK))
ddelay (fe->info->notifier_delay);
fe->status = s;
if (!(s & FE_HAS_LOCK) && (fe->info->caps & FE_CAN_MUTE_TS))
return;
/**
* now tell the Demux about the TS status changes...
*/
list_for_each (e, &fe->notifier_callbacks) {
c = list_entry (e, struct dvb_fe_notifier_callbacks, list_head);
c->callback (fe->status, c->data);
}
}
static
void dvb_frontend_add_event (struct dvb_frontend_data *fe, fe_status_t status)
{
struct dvb_fe_events *events = &fe->events;
struct dvb_frontend_event *e;
int wp;
dprintk ("%s\n", __FUNCTION__);
if (down_interruptible (&events->sem))
return;
wp = (events->eventw + 1) % MAX_EVENT;
if (wp == events->eventr) {
events->overflow = 1;
events->eventr = (events->eventr + 1) % MAX_EVENT;
}
e = &events->events[events->eventw];
memcpy (&e->parameters, &fe->parameters,
sizeof (struct dvb_frontend_parameters));
if (status & FE_HAS_LOCK)
dvb_frontend_internal_ioctl (&fe->frontend,
FE_GET_FRONTEND,
&e->parameters);
events->eventw = wp;
up (&events->sem);
e->status = status;
dvb_call_frontend_notifiers (fe, status);
wake_up_interruptible (&events->wait_queue);
}
static
int dvb_frontend_get_event (struct dvb_frontend_data *fe,
struct dvb_frontend_event *event, int flags)
{
struct dvb_fe_events *events = &fe->events;
dprintk ("%s\n", __FUNCTION__);
if (events->overflow) {
events->overflow = 0;
return -EOVERFLOW;
}
if (events->eventw == events->eventr) {
int ret;
if (flags & O_NONBLOCK)
return -EWOULDBLOCK;
ret = wait_event_interruptible (events->wait_queue,
events->eventw != events->eventr);
if (ret < 0)
return ret;
}
if (down_interruptible (&events->sem))
return -ERESTARTSYS;
memcpy (event, &events->events[events->eventr],
sizeof(struct dvb_frontend_event));
events->eventr = (events->eventr + 1) % MAX_EVENT;
up (&events->sem);
return 0;
}
static
struct dvb_frontend_parameters default_param [] = {
{ /* NTV on Astra */
frequency: 12669500-10600000,
inversion: INVERSION_OFF,
{ qpsk: { symbol_rate: 22000000, fec_inner: FEC_AUTO } }
},
{ /* Cable */
frequency: 394000000,
inversion: INVERSION_OFF,
{ qam: { symbol_rate: 6900000,
fec_inner: FEC_AUTO,
modulation: QAM_64
}
}
},
{ /* DVB-T */
frequency: 730000000,
inversion: INVERSION_OFF,
{ ofdm: { bandwidth: BANDWIDTH_8_MHZ,
code_rate_HP: FEC_2_3,
code_rate_LP: FEC_1_2,
constellation: QAM_16,
transmission_mode: TRANSMISSION_MODE_2K,
guard_interval: GUARD_INTERVAL_1_8,
hierarchy_information: HIERARCHY_NONE
}
}
}
};
static
int dvb_frontend_set_parameters (struct dvb_frontend_data *fe,
struct dvb_frontend_parameters *param,
int first_trial)
{
struct dvb_frontend *frontend = &fe->frontend;
int err;
dprintk ("%s: f == %i, drift == %i\n",
__FUNCTION__, param->frequency, fe->lnb_drift);
dvb_bend_frequency (fe, 0);
if (first_trial) {
fe->timeout_count = 0;
fe->lost_sync_count = 0;
fe->lost_sync_jiffies = jiffies;
fe->lnb_drift = 0;
if (fe->status & ~FE_TIMEDOUT)
dvb_frontend_add_event (fe, 0);
memcpy (&fe->parameters, param,
sizeof (struct dvb_frontend_parameters));
}
param->frequency += fe->lnb_drift + fe->bending;
err = dvb_frontend_internal_ioctl (frontend, FE_SET_FRONTEND, param);
param->frequency -= fe->lnb_drift + fe->bending;
wake_up_interruptible (&fe->wait_queue);
return err;
}
static
void dvb_frontend_init (struct dvb_frontend_data *fe)
{
struct dvb_frontend *frontend = &fe->frontend;
struct dvb_frontend_parameters *init_param;
printk ("%s: initialising frontend %i:%i (%s)...\n", __FUNCTION__,
frontend->i2c->adapter->num, frontend->i2c->id,
fe->info->name);
dvb_frontend_internal_ioctl (frontend, FE_INIT, NULL);
if (fe->info->type == FE_QPSK) {
dvb_frontend_internal_ioctl (frontend, FE_SET_VOLTAGE,
(void*) SEC_VOLTAGE_13);
dvb_frontend_internal_ioctl (frontend, FE_SET_TONE,
(void*) SEC_TONE_ON);
}
init_param = &default_param[fe->info->type-FE_QPSK];
dvb_frontend_set_parameters (fe, init_param, 1);
}
static
void update_delay (int *quality, int *delay, int locked)
{
int q2;
dprintk ("%s\n", __FUNCTION__);
if (locked)
(*quality) = (*quality * 220 + 36*256) / 256;
else
(*quality) = (*quality * 220 + 0) / 256;
q2 = *quality - 128;
q2 *= q2;
*delay = HZ/20 + q2 * HZ / (128*128);
}
#define LNB_DRIFT 1024 /* max. tolerated LNB drift, XXX FIXME: adjust! */
#define TIMEOUT 2*HZ
/**
* here we only come when we have lost the lock bit,
* let's try to do something useful...
*/
static
void dvb_frontend_recover (struct dvb_frontend_data *fe)
{
dprintk ("%s\n", __FUNCTION__);
#if 0
if (fe->timeout_count > 3) {
printk ("%s: frontend seems dead, reinitializing...\n",
__FUNCTION__);
dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_internal_ioctl (&fe->frontend, FE_INIT, NULL);
dvb_frontend_set_parameters (fe, &fe->parameters, 1);
dvb_frontend_add_event (fe, FE_REINIT);
fe->lost_sync_jiffies = jiffies;
fe->timeout_count = 0;
return;
}
#endif
/**
* let's start a zigzag scan to compensate LNB drift...
*/
{
int j = fe->lost_sync_count;
int stepsize = fe->info->frequency_stepsize;
if (j % 32 == 0)
fe->lnb_drift = 0;
if (j % 2)
fe->lnb_drift += stepsize * ((j+1)/2);
else
fe->lnb_drift = -fe->lnb_drift;
dvb_frontend_set_parameters (fe, &fe->parameters, 0);
}
dvb_frontend_internal_ioctl (&fe->frontend, FE_RESET, NULL);
}
static
int dvb_frontend_is_exiting (struct dvb_frontend_data *fe)
{
if (fe->exit)
return 1;
if (fe->dvbdev->users == 0 && dvb_shutdown_timeout)
if (jiffies - fe->release_jiffies > dvb_shutdown_timeout * HZ)
return 1;
return 0;
}
static
int dvb_frontend_thread (void *data)
{
struct dvb_frontend_data *fe = (struct dvb_frontend_data *) data;
int quality = 0, delay = 3*HZ;
fe_status_t s;
dprintk ("%s\n", __FUNCTION__);
lock_kernel ();
daemonize ();
sigfillset (&current->blocked);
strncpy (current->comm, "kdvb-fe", sizeof (current->comm));
fe->thread = current;
unlock_kernel ();
dvb_call_frontend_notifiers (fe, 0);
dvb_frontend_init (fe);
while (!dvb_frontend_is_exiting (fe)) {
up (&fe->sem); /* is locked when we enter the thread... */
interruptible_sleep_on_timeout (&fe->wait_queue, delay);
if (down_interruptible (&fe->sem)) {
fe->thread = NULL;
return -ERESTARTSYS;
}
if (dvb_frontend_is_exiting (fe))
break;
dvb_frontend_internal_ioctl (&fe->frontend, FE_READ_STATUS, &s);
update_delay (&quality, &delay, s & FE_HAS_LOCK);
s &= ~FE_TIMEDOUT;
if (s & FE_HAS_LOCK) {
fe->timeout_count = 0;
fe->lost_sync_count = 0;
} else {
fe->lost_sync_count++;
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");
/*
* 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
/*
* 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;
}
/*
* 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
#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);
/*
* 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;
}
/*
* 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
/*
* 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");
/*
* 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 */
/*
* audio.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 _DVBAUDIO_H_
#define _DVBAUDIO_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
typedef enum {
AUDIO_SOURCE_DEMUX, /* Select the demux as the main source */
AUDIO_SOURCE_MEMORY /* Select internal memory as the main source */
} audio_stream_source_t;
typedef enum {
AUDIO_STOPPED, /* Device is stopped */
AUDIO_PLAYING, /* Device is currently playing */
AUDIO_PAUSED /* Device is paused */
} audio_play_state_t;
typedef enum {
AUDIO_STEREO,
AUDIO_MONO_LEFT,
AUDIO_MONO_RIGHT,
} audio_channel_select_t;
typedef struct audio_status {
int AV_sync_state; /* sync audio and video? */
int mute_state; /* audio is muted */
audio_play_state_t play_state; /* current playback state */
audio_stream_source_t stream_source; /* current stream source */
audio_channel_select_t channel_select; /* currently selected channel */
int bypass_mode; /* pass on audio data to */
} audio_status_t; /* separate decoder hardware */
typedef struct audio_mixer {
unsigned int volume_left;
unsigned int volume_right;
// what else do we need? bass, pass-through, ...
} audio_mixer_t;
typedef
struct audio_karaoke{ /* if Vocal1 or Vocal2 are non-zero, they get mixed */
int vocal1; /* into left and right t at 70% each */
int vocal2; /* if both, Vocal1 and Vocal2 are non-zero, Vocal1 gets*/
int melody; /* mixed into the left channel and */
/* Vocal2 into the right channel at 100% each. */
/* if Melody is non-zero, the melody channel gets mixed*/
} audio_karaoke_t; /* into left and right */
typedef uint16_t audio_attributes_t;
/* bits: descr. */
/* 15-13 audio coding mode (0=ac3, 2=mpeg1, 3=mpeg2ext, 4=LPCM, 6=DTS, */
/* 12 multichannel extension */
/* 11-10 audio type (0=not spec, 1=language included) */
/* 9- 8 audio application mode (0=not spec, 1=karaoke, 2=surround) */
/* 7- 6 Quantization / DRC (mpeg audio: 1=DRC exists)(lpcm: 0=16bit, */
/* 5- 4 Sample frequency fs (0=48kHz, 1=96kHz) */
/* 2- 0 number of audio channels (n+1 channels) */
/* for GET_CAPABILITIES and SET_FORMAT, the latter should only set one bit */
#define AUDIO_CAP_DTS 1
#define AUDIO_CAP_LPCM 2
#define AUDIO_CAP_MP1 4
#define AUDIO_CAP_MP2 8
#define AUDIO_CAP_MP3 16
#define AUDIO_CAP_AAC 32
#define AUDIO_CAP_OGG 64
#define AUDIO_CAP_SDDS 128
#define AUDIO_CAP_AC3 256
#define AUDIO_STOP _IO('o', 1)
#define AUDIO_PLAY _IO('o', 2)
#define AUDIO_PAUSE _IO('o', 3)
#define AUDIO_CONTINUE _IO('o', 4)
#define AUDIO_SELECT_SOURCE _IO('o', 5)
#define AUDIO_SET_MUTE _IO('o', 6)
#define AUDIO_SET_AV_SYNC _IO('o', 7)
#define AUDIO_SET_BYPASS_MODE _IO('o', 8)
#define AUDIO_CHANNEL_SELECT _IO('o', 9)
#define AUDIO_GET_STATUS _IOR('o', 10, audio_status_t)
#define AUDIO_GET_CAPABILITIES _IOR('o', 11, unsigned int)
#define AUDIO_CLEAR_BUFFER _IO('o', 12)
#define AUDIO_SET_ID _IO('o', 13)
#define AUDIO_SET_MIXER _IOW('o', 14, audio_mixer_t)
#define AUDIO_SET_STREAMTYPE _IO('o', 15)
#define AUDIO_SET_EXT_ID _IO('o', 16)
#define AUDIO_SET_ATTRIBUTES _IOW('o', 17, audio_attributes_t)
#define AUDIO_SET_KARAOKE _IOW('o', 18, audio_karaoke_t)
#endif /* _DVBAUDIO_H_ */
/*
* ca.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 _CA_H_
#define _CA_H_
/* slot interface types and info */
typedef struct ca_slot_info_s {
int num; /* slot number */
int type; /* CA interface this slot supports */
#define CA_CI 1 /* CI high level interface */
#define CA_CI_LINK 2 /* CI link layer level interface */
#define CA_CI_PHYS 4 /* CI physical layer level interface */
#define CA_SC 128 /* simple smart card interface */
unsigned int flags;
#define CA_CI_MODULE_PRESENT 1 /* module (or card) inserted */
#define CA_CI_MODULE_READY 2
} ca_slot_info_t;
/* descrambler types and info */
typedef struct ca_descr_info_s {
unsigned int num; /* number of available descramblers (keys) */
unsigned int type; /* type of supported scrambling system */
#define CA_ECD 1
#define CA_NDS 2
#define CA_DSS 4
} ca_descr_info_t;
typedef struct ca_cap_s {
unsigned int slot_num; /* total number of CA card and module slots */
unsigned int slot_type; /* OR of all supported types */
unsigned int descr_num; /* total number of descrambler slots (keys) */
unsigned int descr_type; /* OR of all supported types */
} ca_cap_t;
/* a message to/from a CI-CAM */
typedef struct ca_msg_s {
unsigned int index;
unsigned int type;
unsigned int length;
unsigned char msg[256];
} ca_msg_t;
typedef struct ca_descr_s {
unsigned int index;
unsigned int parity;
unsigned char cw[8];
} ca_descr_t;
#define CA_RESET _IO('o', 128)
#define CA_GET_CAP _IOR('o', 129, ca_cap_t)
#define CA_GET_SLOT_INFO _IOR('o', 130, ca_slot_info_t)
#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t)
#define CA_GET_MSG _IOR('o', 132, ca_msg_t)
#define CA_SEND_MSG _IOW('o', 133, ca_msg_t)
#define CA_SET_DESCR _IOW('o', 134, ca_descr_t)
#endif
/*
* dmx.h
*
* Copyright (C) 2000 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 _DMX_H_
#define _DMX_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#define DMX_FILTER_SIZE 16
typedef enum
{
DMX_OUT_DECODER, /* Streaming directly to decoder. */
DMX_OUT_TAP, /* Output going to a memory buffer */
/* (to be retrieved via the read command).*/
DMX_OUT_TS_TAP /* Output multiplexed into a new TS */
/* (to be retrieved by reading from the */
/* logical DVR device). */
} dmx_output_t;
typedef enum
{
DMX_IN_FRONTEND, /* Input from a front-end device. */
DMX_IN_DVR /* Input from the logical DVR device. */
} dmx_input_t;
typedef enum
{
DMX_PES_AUDIO0,
DMX_PES_VIDEO0,
DMX_PES_TELETEXT0,
DMX_PES_SUBTITLE0,
DMX_PES_PCR0,
DMX_PES_AUDIO1,
DMX_PES_VIDEO1,
DMX_PES_TELETEXT1,
DMX_PES_SUBTITLE1,
DMX_PES_PCR1,
DMX_PES_AUDIO2,
DMX_PES_VIDEO2,
DMX_PES_TELETEXT2,
DMX_PES_SUBTITLE2,
DMX_PES_PCR2,
DMX_PES_AUDIO3,
DMX_PES_VIDEO3,
DMX_PES_TELETEXT3,
DMX_PES_SUBTITLE3,
DMX_PES_PCR3,
DMX_PES_OTHER
} dmx_pes_type_t;
#define DMX_PES_AUDIO DMX_PES_AUDIO0
#define DMX_PES_VIDEO DMX_PES_VIDEO0
#define DMX_PES_TELETEXT DMX_PES_TELETEXT0
#define DMX_PES_SUBTITLE DMX_PES_SUBTITLE0
#define DMX_PES_PCR DMX_PES_PCR0
typedef enum
{
DMX_SCRAMBLING_EV,
DMX_FRONTEND_EV
} dmx_event_t;
typedef enum
{
DMX_SCRAMBLING_OFF,
DMX_SCRAMBLING_ON
} dmx_scrambling_status_t;
typedef struct dmx_filter
{
uint8_t filter[DMX_FILTER_SIZE];
uint8_t mask[DMX_FILTER_SIZE];
uint8_t mode[DMX_FILTER_SIZE];
} dmx_filter_t;
struct dmx_sct_filter_params
{
uint16_t pid;
dmx_filter_t filter;
uint32_t timeout;
uint32_t flags;
#define DMX_CHECK_CRC 1
#define DMX_ONESHOT 2
#define DMX_IMMEDIATE_START 4
#define DMX_KERNEL_CLIENT 0x8000
};
struct dmx_pes_filter_params
{
uint16_t pid;
dmx_input_t input;
dmx_output_t output;
dmx_pes_type_t pes_type;
uint32_t flags;
};
struct dmx_event
{
dmx_event_t event;
time_t timeStamp;
union
{
dmx_scrambling_status_t scrambling;
} u;
};
typedef struct dmx_caps {
uint32_t caps;
int num_decoders;
} dmx_caps_t;
typedef enum {
DMX_SOURCE_FRONT0 = 0,
DMX_SOURCE_FRONT1,
DMX_SOURCE_FRONT2,
DMX_SOURCE_FRONT3,
DMX_SOURCE_DVR0 = 16,
DMX_SOURCE_DVR1,
DMX_SOURCE_DVR2,
DMX_SOURCE_DVR3,
} dmx_source_t;
#define DMX_START _IO('o',41)
#define DMX_STOP _IO('o',42)
#define DMX_SET_FILTER _IOW('o',43,struct dmx_sct_filter_params)
#define DMX_SET_PES_FILTER _IOW('o',44,struct dmx_pes_filter_params)
#define DMX_SET_BUFFER_SIZE _IO('o',45)
#define DMX_GET_EVENT _IOR('o',46,struct dmx_event)
#define DMX_GET_PES_PIDS _IOR('o',47,uint16_t)
#define DMX_GET_CAPS _IOR('o',48,dmx_caps_t)
#define DMX_SET_SOURCE _IOW('o',49,dmx_source_t)
#endif /*_DMX_H_*/
/*
* frontend.h
*
* Copyright (C) 2000 Marcus Metzler <marcus@convergence.de>
* Ralph Metzler <ralph@convergence.de>
* Holger Waechtler <holger@convergence.de>
* Andre Draszik <ad@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 _FRONTEND_H_
#define _FRONTEND_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
typedef enum {
FE_QPSK,
FE_QAM,
FE_OFDM
} fe_type_t;
typedef enum {
FE_IS_STUPID = 0,
FE_CAN_INVERSION_AUTO = 0x1,
FE_CAN_FEC_1_2 = 0x2,
FE_CAN_FEC_2_3 = 0x4,
FE_CAN_FEC_3_4 = 0x8,
FE_CAN_FEC_4_5 = 0x10,
FE_CAN_FEC_5_6 = 0x20,
FE_CAN_FEC_6_7 = 0x40,
FE_CAN_FEC_7_8 = 0x80,
FE_CAN_FEC_8_9 = 0x100,
FE_CAN_FEC_AUTO = 0x200,
FE_CAN_QPSK = 0x400,
FE_CAN_QAM_16 = 0x800,
FE_CAN_QAM_32 = 0x1000,
FE_CAN_QAM_64 = 0x2000,
FE_CAN_QAM_128 = 0x4000,
FE_CAN_QAM_256 = 0x8000,
FE_CAN_QAM_AUTO = 0x10000,
FE_CAN_TRANSMISSION_MODE_AUTO = 0x20000,
FE_CAN_BANDWIDTH_AUTO = 0x40000,
FE_CAN_GUARD_INTERVAL_AUTO = 0x80000,
FE_CAN_HIERARCHY_AUTO = 0x100000,
FE_CAN_MUTE_TS = 0x80000000
} fe_caps_t;
struct dvb_frontend_info {
char name[128];
fe_type_t type;
uint32_t frequency_min;
uint32_t frequency_max;
uint32_t frequency_stepsize;
uint32_t frequency_tolerance;
uint32_t symbol_rate_min;
uint32_t symbol_rate_max;
uint32_t symbol_rate_tolerance; /* ppm */
uint32_t notifier_delay; /* ms */
fe_caps_t caps;
};
/**
* Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for
* the meaning of this struct...
*/
struct dvb_diseqc_master_cmd {
uint8_t msg [6]; /* { framing, address, command, data [3] } */
uint8_t msg_len; /* valid values are 3...6 */
};
struct dvb_diseqc_slave_reply {
uint8_t msg [4]; /* { framing, data [3] } */
uint8_t msg_len; /* valid values are 0...4, 0 means no msg */
int timeout; /* return from ioctl after timeout ms with */
}; /* errorcode when no message was received */
typedef enum {
SEC_VOLTAGE_13,
SEC_VOLTAGE_18
} fe_sec_voltage_t;
typedef enum {
SEC_TONE_ON,
SEC_TONE_OFF
} fe_sec_tone_mode_t;
typedef enum {
SEC_MINI_A,
SEC_MINI_B
} fe_sec_mini_cmd_t;
typedef enum {
FE_HAS_SIGNAL = 0x01, /* found something above the noise level */
FE_HAS_CARRIER = 0x02, /* found a DVB signal */
FE_HAS_VITERBI = 0x04, /* FEC is stable */
FE_HAS_SYNC = 0x08, /* found sync bytes */
FE_HAS_LOCK = 0x10, /* everything's working... */
FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */
FE_REINIT = 0x40 /* frontend was reinitialized, */
} fe_status_t; /* application is recommned to reset */
/* DiSEqC, tone and parameters */
typedef enum {
INVERSION_OFF,
INVERSION_ON,
INVERSION_AUTO
} fe_spectral_inversion_t;
typedef enum {
FEC_NONE = 0,
FEC_1_2,
FEC_2_3,
FEC_3_4,
FEC_4_5,
FEC_5_6,
FEC_6_7,
FEC_7_8,
FEC_8_9,
FEC_AUTO
} fe_code_rate_t;
typedef enum {
QPSK,
QAM_16,
QAM_32,
QAM_64,
QAM_128,
QAM_256,
QAM_AUTO
} fe_modulation_t;
typedef enum {
TRANSMISSION_MODE_2K,
TRANSMISSION_MODE_8K,
TRANSMISSION_MODE_AUTO
} fe_transmit_mode_t;
typedef enum {
BANDWIDTH_8_MHZ,
BANDWIDTH_7_MHZ,
BANDWIDTH_6_MHZ,
BANDWIDTH_AUTO
} fe_bandwidth_t;
typedef enum {
GUARD_INTERVAL_1_32,
GUARD_INTERVAL_1_16,
GUARD_INTERVAL_1_8,
GUARD_INTERVAL_1_4,
GUARD_INTERVAL_AUTO
} fe_guard_interval_t;
typedef enum {
HIERARCHY_NONE,
HIERARCHY_1,
HIERARCHY_2,
HIERARCHY_4,
HIERARCHY_AUTO
} fe_hierarchy_t;
struct dvb_qpsk_parameters {
uint32_t symbol_rate; /* symbol rate in Symbols per second */
fe_code_rate_t fec_inner; /* forward error correction (see above) */
};
struct dvb_qam_parameters {
uint32_t symbol_rate; /* symbol rate in Symbols per second */
fe_code_rate_t fec_inner; /* forward error correction (see above) */
fe_modulation_t modulation; /* modulation type (see above) */
};
struct dvb_ofdm_parameters {
fe_bandwidth_t bandwidth;
fe_code_rate_t code_rate_HP; /* high priority stream code rate */
fe_code_rate_t code_rate_LP; /* low priority stream code rate */
fe_modulation_t constellation; /* modulation type (see above) */
fe_transmit_mode_t transmission_mode;
fe_guard_interval_t guard_interval;
fe_hierarchy_t hierarchy_information;
};
struct dvb_frontend_parameters {
uint32_t frequency; /* (absolute) frequency in Hz for QAM/OFDM */
/* intermediate frequency in kHz for QPSK */
fe_spectral_inversion_t inversion;
union {
struct dvb_qpsk_parameters qpsk;
struct dvb_qam_parameters qam;
struct dvb_ofdm_parameters ofdm;
} u;
};
struct dvb_frontend_event {
fe_status_t status;
struct dvb_frontend_parameters parameters;
};
#define FE_GET_INFO _IOR('o', 61, struct dvb_frontend_info)
#define FE_DISEQC_RESET_OVERLOAD _IO('o', 62)
#define FE_DISEQC_SEND_MASTER_CMD _IOW('o', 63, struct dvb_diseqc_master_cmd)
#define FE_DISEQC_RECV_SLAVE_REPLY _IOR('o', 64, struct dvb_diseqc_slave_reply)
#define FE_DISEQC_SEND_BURST _IO('o', 65) /* fe_sec_mini_cmd_t */
#define FE_SET_TONE _IO('o', 66) /* fe_sec_tone_mode_t */
#define FE_SET_VOLTAGE _IO('o', 67) /* fe_sec_voltage_t */
#define FE_ENABLE_HIGH_LNB_VOLTAGE _IO('o', 68) /* int */
#define FE_READ_STATUS _IOR('o', 69, fe_status_t)
#define FE_READ_BER _IOR('o', 70, uint32_t)
#define FE_READ_SIGNAL_STRENGTH _IOR('o', 71, uint16_t)
#define FE_READ_SNR _IOR('o', 72, uint16_t)
#define FE_READ_UNCORRECTED_BLOCKS _IOR('o', 73, uint32_t)
#define FE_SET_FRONTEND _IOW('o', 76, struct dvb_frontend_parameters)
#define FE_GET_FRONTEND _IOR('o', 77, struct dvb_frontend_parameters)
#define FE_GET_EVENT _IOR('o', 78, struct dvb_frontend_event)
#endif /*_FRONTEND_H_*/
/*
* net.h
*
* Copyright (C) 2000 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 _DVBNET_H_
#define _DVBNET_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
struct dvb_net_if {
uint16_t pid;
uint16_t if_num;
};
#define NET_ADD_IF _IOWR('o', 52, struct dvb_net_if)
#define NET_REMOVE_IF _IO('o', 53)
#endif /*_DVBNET_H_*/
/*
* osd.h
*
* Copyright (C) 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 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 _DVBOSD_H_
#define _DVBOSD_H_
typedef enum {
// All functions return -2 on "not open"
OSD_Close = 1, // ()
// Disables OSD and releases the buffers
// returns 0 on success
OSD_Open, // (x0,y0,x1,y1,BitPerPixel[2/4/8](color&0x0F),mix[0..15](color&0xF0))
// Opens OSD with this size and bit depth
// returns 0 on success, -1 on DRAM allocation error, -2 on "already open"
OSD_Show, // ()
// enables OSD mode
// returns 0 on success
OSD_Hide, // ()
// disables OSD mode
// returns 0 on success
OSD_Clear, // ()
// Sets all pixel to color 0
// returns 0 on success
OSD_Fill, // (color)
// Sets all pixel to color <col>
// returns 0 on success
OSD_SetColor, // (color,R{x0},G{y0},B{x1},opacity{y1})
// set palette entry <num> to <r,g,b>, <mix> and <trans> apply
// R,G,B: 0..255
// R=Red, G=Green, B=Blue
// opacity=0: pixel opacity 0% (only video pixel shows)
// opacity=1..254: pixel opacity as specified in header
// opacity=255: pixel opacity 100% (only OSD pixel shows)
// returns 0 on success, -1 on error
OSD_SetPalette, // (firstcolor{color},lastcolor{x0},data)
// Set a number of entries in the palette
// sets the entries "firstcolor" through "lastcolor" from the array "data"
// data has 4 byte for each color:
// R,G,B, and a opacity value: 0->transparent, 1..254->mix, 255->pixel
OSD_SetTrans, // (transparency{color})
// Sets transparency of mixed pixel (0..15)
// returns 0 on success
OSD_SetPixel, // (x0,y0,color)
// sets pixel <x>,<y> to color number <col>
// returns 0 on success, -1 on error
OSD_GetPixel, // (x0,y0)
// returns color number of pixel <x>,<y>, or -1
OSD_SetRow, // (x0,y0,x1,data)
// fills pixels x0,y through x1,y with the content of data[]
// returns 0 on success, -1 on clipping all pixel (no pixel drawn)
OSD_SetBlock, // (x0,y0,x1,y1,increment{color},data)
// fills pixels x0,y0 through x1,y1 with the content of data[]
// inc contains the width of one line in the data block,
// inc<=0 uses blockwidth as linewidth
// returns 0 on success, -1 on clipping all pixel
OSD_FillRow, // (x0,y0,x1,color)
// fills pixels x0,y through x1,y with the color <col>
// returns 0 on success, -1 on clipping all pixel
OSD_FillBlock, // (x0,y0,x1,y1,color)
// fills pixels x0,y0 through x1,y1 with the color <col>
// returns 0 on success, -1 on clipping all pixel
OSD_Line, // (x0,y0,x1,y1,color)
// draw a line from x0,y0 to x1,y1 with the color <col>
// returns 0 on success
OSD_Query, // (x0,y0,x1,y1,xasp{color}}), yasp=11
// fills parameters with the picture dimensions and the pixel aspect ratio
// returns 0 on success
OSD_Test, // ()
// draws a test picture. for debugging purposes only
// returns 0 on success
// TODO: remove "test" in final version
OSD_Text, // (x0,y0,size,color,text)
OSD_SetWindow, // (x0) set window with number 0<x0<8 as current
OSD_MoveWindow, // move current window to (x0, y0)
} OSD_Command;
typedef struct osd_cmd_s {
OSD_Command cmd;
int x0;
int y0;
int x1;
int y1;
int color;
void *data;
} osd_cmd_t;
#define OSD_SEND_CMD _IOW('o', 160, osd_cmd_t)
#endif
/*
* video.h
*
* Copyright (C) 2000 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 _DVBVIDEO_H_
#define _DVBVIDEO_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
typedef enum {
VIDEO_FORMAT_4_3, /* Select 4:3 format */
VIDEO_FORMAT_16_9 /* Select 16:9 format. */
} video_format_t;
typedef enum {
VIDEO_SYSTEM_PAL,
VIDEO_SYSTEM_NTSC,
VIDEO_SYSTEM_PALN,
VIDEO_SYSTEM_PALNc,
VIDEO_SYSTEM_PALM,
VIDEO_SYSTEM_NTSC60,
VIDEO_SYSTEM_PAL60,
VIDEO_SYSTEM_PALM60
} video_system_t;
typedef enum {
VIDEO_PAN_SCAN, /* use pan and scan format */
VIDEO_LETTER_BOX, /* use letterbox format */
VIDEO_CENTER_CUT_OUT /* use center cut out format */
} video_displayformat_t;
typedef enum {
VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */
VIDEO_SOURCE_MEMORY /* If this source is selected, the stream
comes from the user through the write
system call */
} video_stream_source_t;
typedef enum {
VIDEO_STOPPED, /* Video is stopped */
VIDEO_PLAYING, /* Video is currently playing */
VIDEO_FREEZED /* Video is freezed */
} video_play_state_t;
struct video_event {
int32_t type;
time_t timestamp;
union {
video_format_t video_format;
} u;
};
struct video_status {
int video_blank; /* blank video on freeze? */
video_play_state_t play_state; /* current state of playback */
video_stream_source_t stream_source; /* current source (demux/memory) */
video_format_t video_format; /* current aspect ratio of stream*/
video_displayformat_t display_format;/* selected cropping mode */
};
struct video_still_picture {
char *iFrame; /* pointer to a single iframe in memory */
int32_t size;
};
typedef
struct video_highlight {
int active; /* 1=show highlight, 0=hide highlight */
uint8_t contrast1; /* 7- 4 Pattern pixel contrast */
/* 3- 0 Background pixel contrast */
uint8_t contrast2; /* 7- 4 Emphasis pixel-2 contrast */
/* 3- 0 Emphasis pixel-1 contrast */
uint8_t color1; /* 7- 4 Pattern pixel color */
/* 3- 0 Background pixel color */
uint8_t color2; /* 7- 4 Emphasis pixel-2 color */
/* 3- 0 Emphasis pixel-1 color */
uint32_t ypos; /* 23-22 auto action mode */
/* 21-12 start y */
/* 9- 0 end y */
uint32_t xpos; /* 23-22 button color number */
/* 21-12 start x */
/* 9- 0 end x */
} video_highlight_t;
typedef struct video_spu {
int active;
int stream_id;
} video_spu_t;
typedef struct video_spu_palette { /* SPU Palette information */
int length;
uint8_t *palette;
} video_spu_palette_t;
typedef struct video_navi_pack {
int length; /* 0 ... 1024 */
uint8_t data[1024];
} video_navi_pack_t;
typedef uint16_t video_attributes_t;
/* bits: descr. */
/* 15-14 Video compression mode (0=MPEG-1, 1=MPEG-2) */
/* 13-12 TV system (0=525/60, 1=625/50) */
/* 11-10 Aspect ratio (0=4:3, 3=16:9) */
/* 9- 8 permitted display mode on 4:3 monitor (0=both, 1=only pan-sca */
/* 7 line 21-1 data present in GOP (1=yes, 0=no) */
/* 6 line 21-2 data present in GOP (1=yes, 0=no) */
/* 5- 3 source resolution (0=720x480/576, 1=704x480/576, 2=352x480/57 */
/* 2 source letterboxed (1=yes, 0=no) */
/* 0 film/camera mode (0=camera, 1=film (625/50 only)) */
/* bit definitions for capabilities: */
/* can the hardware decode MPEG1 and/or MPEG2? */
#define VIDEO_CAP_MPEG1 1
#define VIDEO_CAP_MPEG2 2
/* can you send a system and/or program stream to video device?
(you still have to open the video and the audio device but only
send the stream to the video device) */
#define VIDEO_CAP_SYS 4
#define VIDEO_CAP_PROG 8
/* can the driver also handle SPU, NAVI and CSS encoded data?
(CSS API is not present yet) */
#define VIDEO_CAP_SPU 16
#define VIDEO_CAP_NAVI 32
#define VIDEO_CAP_CSS 64
#define VIDEO_STOP _IO('o', 21)
#define VIDEO_PLAY _IO('o', 22)
#define VIDEO_FREEZE _IO('o', 23)
#define VIDEO_CONTINUE _IO('o', 24)
#define VIDEO_SELECT_SOURCE _IO('o', 25)
#define VIDEO_SET_BLANK _IO('o', 26)
#define VIDEO_GET_STATUS _IOR('o', 27, struct video_status)
#define VIDEO_GET_EVENT _IOR('o', 28, struct video_event)
#define VIDEO_SET_DISPLAY_FORMAT _IO('o', 29)
#define VIDEO_STILLPICTURE _IOW('o', 30, struct video_still_picture)
#define VIDEO_FAST_FORWARD _IO('o', 31)
#define VIDEO_SLOWMOTION _IO('o', 32)
#define VIDEO_GET_CAPABILITIES _IOR('o', 33, unsigned int)
#define VIDEO_CLEAR_BUFFER _IO('o', 34)
#define VIDEO_SET_ID _IO('o', 35)
#define VIDEO_SET_STREAMTYPE _IO('o', 36)
#define VIDEO_SET_FORMAT _IO('o', 37)
#define VIDEO_SET_SYSTEM _IO('o', 38)
#define VIDEO_SET_HIGHLIGHT _IOW('o', 39, video_highlight_t)
#define VIDEO_SET_SPU _IOW('o', 50, video_spu_t)
#define VIDEO_SET_SPU_PALETTE _IOW('o', 51, video_spu_palette_t)
#define VIDEO_GET_NAVI _IOR('o', 52, video_navi_pack_t)
#define VIDEO_SET_ATTRIBUTES _IO('o', 53)
#endif /*_DVBVIDEO_H_*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment