2005-04-16 15:20:36 -07:00
/*
* dmxdev . c - DVB demultiplexer device
*
* Copyright ( C ) 2000 Ralph Metzler < ralph @ convergence . de >
* & Marcus Metzler < marcus @ convergence . de >
for convergence integrated media GmbH
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation ; either version 2.1
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
*/
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/sched.h>
# include <linux/poll.h>
# include <linux/ioctl.h>
# include <linux/wait.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include "dmxdev.h"
static int debug ;
module_param ( debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " Turn on/off debugging (default:off). " ) ;
# define dprintk if (debug) printk
static inline void dvb_dmxdev_buffer_init ( struct dmxdev_buffer * buffer )
{
buffer - > data = NULL ;
buffer - > size = 8192 ;
buffer - > pread = 0 ;
buffer - > pwrite = 0 ;
buffer - > error = 0 ;
init_waitqueue_head ( & buffer - > queue ) ;
}
static inline int dvb_dmxdev_buffer_write ( struct dmxdev_buffer * buf , const u8 * src , int len )
{
int split ;
int free ;
int todo ;
if ( ! len )
return 0 ;
if ( ! buf - > data )
return 0 ;
free = buf - > pread - buf - > pwrite ;
split = 0 ;
if ( free < = 0 ) {
free + = buf - > size ;
split = buf - > size - buf - > pwrite ;
}
if ( len > = free ) {
dprintk ( " dmxdev: buffer overflow \n " ) ;
return - 1 ;
}
if ( split > = len )
split = 0 ;
todo = len ;
if ( split ) {
memcpy ( buf - > data + buf - > pwrite , src , split ) ;
todo - = split ;
buf - > pwrite = 0 ;
}
memcpy ( buf - > data + buf - > pwrite , src + split , todo ) ;
buf - > pwrite = ( buf - > pwrite + todo ) % buf - > size ;
return len ;
}
static ssize_t dvb_dmxdev_buffer_read ( struct dmxdev_buffer * src ,
int non_blocking , char __user * buf , size_t count , loff_t * ppos )
{
unsigned long todo = count ;
int split , avail , error ;
if ( ! src - > data )
return 0 ;
if ( ( error = src - > error ) ) {
src - > pwrite = src - > pread ;
src - > error = 0 ;
return error ;
}
if ( non_blocking & & ( src - > pwrite = = src - > pread ) )
return - EWOULDBLOCK ;
while ( todo > 0 ) {
if ( non_blocking & & ( src - > pwrite = = src - > pread ) )
return ( count - todo ) ? ( count - todo ) : - EWOULDBLOCK ;
if ( wait_event_interruptible ( src - > queue ,
( src - > pread ! = src - > pwrite ) | |
( src - > error ) ) < 0 )
return count - todo ;
if ( ( error = src - > error ) ) {
src - > pwrite = src - > pread ;
src - > error = 0 ;
return error ;
}
split = src - > size ;
avail = src - > pwrite - src - > pread ;
if ( avail < 0 ) {
avail + = src - > size ;
split = src - > size - src - > pread ;
}
if ( avail > todo )
avail = todo ;
if ( split < avail ) {
if ( copy_to_user ( buf , src - > data + src - > pread , split ) )
return - EFAULT ;
buf + = split ;
src - > pread = 0 ;
todo - = split ;
avail - = split ;
}
if ( avail ) {
if ( copy_to_user ( buf , src - > data + src - > pread , avail ) )
return - EFAULT ;
src - > pread = ( src - > pread + avail ) % src - > size ;
todo - = avail ;
buf + = avail ;
}
}
return count ;
}
static struct dmx_frontend * get_fe ( struct dmx_demux * demux , int type )
{
struct list_head * head , * pos ;
head = demux - > get_frontends ( demux ) ;
if ( ! head )
return NULL ;
list_for_each ( pos , head )
if ( DMX_FE_ENTRY ( pos ) - > source = = type )
return DMX_FE_ENTRY ( pos ) ;
return NULL ;
}
static inline void dvb_dmxdev_dvr_state_set ( struct dmxdev_dvr * dmxdevdvr , int state )
{
spin_lock_irq ( & dmxdevdvr - > dev - > lock ) ;
dmxdevdvr - > state = state ;
spin_unlock_irq ( & dmxdevdvr - > dev - > lock ) ;
}
static int dvb_dvr_open ( struct inode * inode , struct file * file )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
struct dmx_frontend * front ;
dprintk ( " function : %s \n " , __FUNCTION__ ) ;
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDWR ) {
if ( ! ( dmxdev - > capabilities & DMXDEV_CAP_DUPLEX ) ) {
up ( & dmxdev - > mutex ) ;
return - EOPNOTSUPP ;
}
}
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) {
dvb_dmxdev_buffer_init ( & dmxdev - > dvr_buffer ) ;
dmxdev - > dvr_buffer . size = DVR_BUFFER_SIZE ;
dmxdev - > dvr_buffer . data = vmalloc ( DVR_BUFFER_SIZE ) ;
if ( ! dmxdev - > dvr_buffer . data ) {
up ( & dmxdev - > mutex ) ;
return - ENOMEM ;
}
}
if ( ( file - > f_flags & O_ACCMODE ) = = O_WRONLY ) {
dmxdev - > dvr_orig_fe = dmxdev - > demux - > frontend ;
if ( ! dmxdev - > demux - > write ) {
up ( & dmxdev - > mutex ) ;
return - EOPNOTSUPP ;
}
front = get_fe ( dmxdev - > demux , DMX_MEMORY_FE ) ;
if ( ! front ) {
up ( & dmxdev - > mutex ) ;
return - EINVAL ;
}
dmxdev - > demux - > disconnect_frontend ( dmxdev - > demux ) ;
dmxdev - > demux - > connect_frontend ( dmxdev - > demux , front ) ;
}
up ( & dmxdev - > mutex ) ;
return 0 ;
}
static int dvb_dvr_release ( struct inode * inode , struct file * file )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
if ( ( file - > f_flags & O_ACCMODE ) = = O_WRONLY ) {
dmxdev - > demux - > disconnect_frontend ( dmxdev - > demux ) ;
dmxdev - > demux - > connect_frontend ( dmxdev - > demux ,
dmxdev - > dvr_orig_fe ) ;
}
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) {
if ( dmxdev - > dvr_buffer . data ) {
void * mem = dmxdev - > dvr_buffer . data ;
mb ( ) ;
spin_lock_irq ( & dmxdev - > lock ) ;
dmxdev - > dvr_buffer . data = NULL ;
spin_unlock_irq ( & dmxdev - > lock ) ;
vfree ( mem ) ;
}
}
up ( & dmxdev - > mutex ) ;
return 0 ;
}
static ssize_t dvb_dvr_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
int ret ;
if ( ! dmxdev - > demux - > write )
return - EOPNOTSUPP ;
if ( ( file - > f_flags & O_ACCMODE ) ! = O_WRONLY )
return - EINVAL ;
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
ret = dmxdev - > demux - > write ( dmxdev - > demux , buf , count ) ;
up ( & dmxdev - > mutex ) ;
return ret ;
}
static ssize_t dvb_dvr_read ( struct file * file , char __user * buf , size_t count ,
loff_t * ppos )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
int ret ;
//down(&dmxdev->mutex);
ret = dvb_dmxdev_buffer_read ( & dmxdev - > dvr_buffer ,
file - > f_flags & O_NONBLOCK ,
buf , count , ppos ) ;
//up(&dmxdev->mutex);
return ret ;
}
static inline void dvb_dmxdev_filter_state_set ( struct dmxdev_filter * dmxdevfilter , int state )
{
spin_lock_irq ( & dmxdevfilter - > dev - > lock ) ;
dmxdevfilter - > state = state ;
spin_unlock_irq ( & dmxdevfilter - > dev - > lock ) ;
}
static int dvb_dmxdev_set_buffer_size ( struct dmxdev_filter * dmxdevfilter , unsigned long size )
{
struct dmxdev_buffer * buf = & dmxdevfilter - > buffer ;
void * mem ;
if ( buf - > size = = size )
return 0 ;
if ( dmxdevfilter - > state > = DMXDEV_STATE_GO )
return - EBUSY ;
spin_lock_irq ( & dmxdevfilter - > dev - > lock ) ;
mem = buf - > data ;
buf - > data = NULL ;
buf - > size = size ;
buf - > pwrite = buf - > pread = 0 ;
spin_unlock_irq ( & dmxdevfilter - > dev - > lock ) ;
vfree ( mem ) ;
if ( buf - > size ) {
mem = vmalloc ( dmxdevfilter - > buffer . size ) ;
if ( ! mem )
return - ENOMEM ;
spin_lock_irq ( & dmxdevfilter - > dev - > lock ) ;
buf - > data = mem ;
spin_unlock_irq ( & dmxdevfilter - > dev - > lock ) ;
}
return 0 ;
}
static void dvb_dmxdev_filter_timeout ( unsigned long data )
{
struct dmxdev_filter * dmxdevfilter = ( struct dmxdev_filter * ) data ;
dmxdevfilter - > buffer . error = - ETIMEDOUT ;
spin_lock_irq ( & dmxdevfilter - > dev - > lock ) ;
dmxdevfilter - > state = DMXDEV_STATE_TIMEDOUT ;
spin_unlock_irq ( & dmxdevfilter - > dev - > lock ) ;
wake_up ( & dmxdevfilter - > buffer . queue ) ;
}
static void dvb_dmxdev_filter_timer ( struct dmxdev_filter * dmxdevfilter )
{
struct dmx_sct_filter_params * para = & dmxdevfilter - > params . sec ;
del_timer ( & dmxdevfilter - > timer ) ;
if ( para - > timeout ) {
dmxdevfilter - > timer . function = dvb_dmxdev_filter_timeout ;
dmxdevfilter - > timer . data = ( unsigned long ) dmxdevfilter ;
dmxdevfilter - > timer . expires = jiffies + 1 + ( HZ / 2 + HZ * para - > timeout ) / 1000 ;
add_timer ( & dmxdevfilter - > timer ) ;
}
}
static int dvb_dmxdev_section_callback ( const u8 * buffer1 , size_t buffer1_len ,
const u8 * buffer2 , size_t buffer2_len ,
struct dmx_section_filter * filter , enum dmx_success success )
{
2005-05-16 21:54:24 -07:00
struct dmxdev_filter * dmxdevfilter = filter - > priv ;
2005-04-16 15:20:36 -07:00
int ret ;
if ( dmxdevfilter - > buffer . error ) {
wake_up ( & dmxdevfilter - > buffer . queue ) ;
return 0 ;
}
spin_lock ( & dmxdevfilter - > dev - > lock ) ;
if ( dmxdevfilter - > state ! = DMXDEV_STATE_GO ) {
spin_unlock ( & dmxdevfilter - > dev - > lock ) ;
return 0 ;
}
del_timer ( & dmxdevfilter - > timer ) ;
dprintk ( " dmxdev: section callback %02x %02x %02x %02x %02x %02x \n " ,
buffer1 [ 0 ] , buffer1 [ 1 ] ,
buffer1 [ 2 ] , buffer1 [ 3 ] ,
buffer1 [ 4 ] , buffer1 [ 5 ] ) ;
ret = dvb_dmxdev_buffer_write ( & dmxdevfilter - > buffer , buffer1 , buffer1_len ) ;
if ( ret = = buffer1_len ) {
ret = dvb_dmxdev_buffer_write ( & dmxdevfilter - > buffer , buffer2 , buffer2_len ) ;
}
if ( ret < 0 ) {
dmxdevfilter - > buffer . pwrite = dmxdevfilter - > buffer . pread ;
dmxdevfilter - > buffer . error = - EOVERFLOW ;
}
if ( dmxdevfilter - > params . sec . flags & DMX_ONESHOT )
dmxdevfilter - > state = DMXDEV_STATE_DONE ;
spin_unlock ( & dmxdevfilter - > dev - > lock ) ;
wake_up ( & dmxdevfilter - > buffer . queue ) ;
return 0 ;
}
static int dvb_dmxdev_ts_callback ( const u8 * buffer1 , size_t buffer1_len ,
const u8 * buffer2 , size_t buffer2_len ,
struct dmx_ts_feed * feed , enum dmx_success success )
{
2005-05-16 21:54:24 -07:00
struct dmxdev_filter * dmxdevfilter = feed - > priv ;
2005-04-16 15:20:36 -07:00
struct dmxdev_buffer * buffer ;
int ret ;
spin_lock ( & dmxdevfilter - > dev - > lock ) ;
if ( dmxdevfilter - > params . pes . output = = DMX_OUT_DECODER ) {
spin_unlock ( & dmxdevfilter - > dev - > lock ) ;
return 0 ;
}
if ( dmxdevfilter - > params . pes . output = = DMX_OUT_TAP )
buffer = & dmxdevfilter - > buffer ;
else
buffer = & dmxdevfilter - > dev - > dvr_buffer ;
if ( buffer - > error ) {
spin_unlock ( & dmxdevfilter - > dev - > lock ) ;
wake_up ( & buffer - > queue ) ;
return 0 ;
}
ret = dvb_dmxdev_buffer_write ( buffer , buffer1 , buffer1_len ) ;
if ( ret = = buffer1_len )
ret = dvb_dmxdev_buffer_write ( buffer , buffer2 , buffer2_len ) ;
if ( ret < 0 ) {
buffer - > pwrite = buffer - > pread ;
buffer - > error = - EOVERFLOW ;
}
spin_unlock ( & dmxdevfilter - > dev - > lock ) ;
wake_up ( & buffer - > queue ) ;
return 0 ;
}
/* stop feed but only mark the specified filter as stopped (state set) */
static int dvb_dmxdev_feed_stop ( struct dmxdev_filter * dmxdevfilter )
{
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_SET ) ;
switch ( dmxdevfilter - > type ) {
case DMXDEV_TYPE_SEC :
del_timer ( & dmxdevfilter - > timer ) ;
dmxdevfilter - > feed . sec - > stop_filtering ( dmxdevfilter - > feed . sec ) ;
break ;
case DMXDEV_TYPE_PES :
dmxdevfilter - > feed . ts - > stop_filtering ( dmxdevfilter - > feed . ts ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/* start feed associated with the specified filter */
static int dvb_dmxdev_feed_start ( struct dmxdev_filter * filter )
{
dvb_dmxdev_filter_state_set ( filter , DMXDEV_STATE_GO ) ;
switch ( filter - > type ) {
case DMXDEV_TYPE_SEC :
return filter - > feed . sec - > start_filtering ( filter - > feed . sec ) ;
break ;
case DMXDEV_TYPE_PES :
return filter - > feed . ts - > start_filtering ( filter - > feed . ts ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/* restart section feed if it has filters left associated with it,
otherwise release the feed */
static int dvb_dmxdev_feed_restart ( struct dmxdev_filter * filter )
{
int i ;
struct dmxdev * dmxdev = filter - > dev ;
u16 pid = filter - > params . sec . pid ;
for ( i = 0 ; i < dmxdev - > filternum ; i + + )
if ( dmxdev - > filter [ i ] . state > = DMXDEV_STATE_GO & &
dmxdev - > filter [ i ] . type = = DMXDEV_TYPE_SEC & &
dmxdev - > filter [ i ] . pid = = pid ) {
dvb_dmxdev_feed_start ( & dmxdev - > filter [ i ] ) ;
return 0 ;
}
filter - > dev - > demux - > release_section_feed ( dmxdev - > demux , filter - > feed . sec ) ;
return 0 ;
}
static int dvb_dmxdev_filter_stop ( struct dmxdev_filter * dmxdevfilter )
{
if ( dmxdevfilter - > state < DMXDEV_STATE_GO )
return 0 ;
switch ( dmxdevfilter - > type ) {
case DMXDEV_TYPE_SEC :
if ( ! dmxdevfilter - > feed . sec )
break ;
dvb_dmxdev_feed_stop ( dmxdevfilter ) ;
if ( dmxdevfilter - > filter . sec )
dmxdevfilter - > feed . sec - >
release_filter ( dmxdevfilter - > feed . sec ,
dmxdevfilter - > filter . sec ) ;
dvb_dmxdev_feed_restart ( dmxdevfilter ) ;
dmxdevfilter - > feed . sec = NULL ;
break ;
case DMXDEV_TYPE_PES :
if ( ! dmxdevfilter - > feed . ts )
break ;
dvb_dmxdev_feed_stop ( dmxdevfilter ) ;
dmxdevfilter - > dev - > demux - >
release_ts_feed ( dmxdevfilter - > dev - > demux ,
dmxdevfilter - > feed . ts ) ;
dmxdevfilter - > feed . ts = NULL ;
break ;
default :
if ( dmxdevfilter - > state = = DMXDEV_STATE_ALLOCATED )
return 0 ;
return - EINVAL ;
}
dmxdevfilter - > buffer . pwrite = dmxdevfilter - > buffer . pread = 0 ;
return 0 ;
}
static inline int dvb_dmxdev_filter_reset ( struct dmxdev_filter * dmxdevfilter )
{
if ( dmxdevfilter - > state < DMXDEV_STATE_SET )
return 0 ;
dmxdevfilter - > type = DMXDEV_TYPE_NONE ;
dmxdevfilter - > pid = 0xffff ;
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_ALLOCATED ) ;
return 0 ;
}
static int dvb_dmxdev_filter_start ( struct dmxdev_filter * filter )
{
struct dmxdev * dmxdev = filter - > dev ;
void * mem ;
int ret , i ;
if ( filter - > state < DMXDEV_STATE_SET )
return - EINVAL ;
if ( filter - > state > = DMXDEV_STATE_GO )
dvb_dmxdev_filter_stop ( filter ) ;
if ( ! ( mem = filter - > buffer . data ) ) {
mem = vmalloc ( filter - > buffer . size ) ;
spin_lock_irq ( & filter - > dev - > lock ) ;
filter - > buffer . data = mem ;
spin_unlock_irq ( & filter - > dev - > lock ) ;
if ( ! filter - > buffer . data )
return - ENOMEM ;
}
filter - > buffer . pwrite = filter - > buffer . pread = 0 ;
switch ( filter - > type ) {
case DMXDEV_TYPE_SEC :
{
struct dmx_sct_filter_params * para = & filter - > params . sec ;
struct dmx_section_filter * * secfilter = & filter - > filter . sec ;
struct dmx_section_feed * * secfeed = & filter - > feed . sec ;
* secfilter = NULL ;
* secfeed = NULL ;
/* find active filter/feed with same PID */
for ( i = 0 ; i < dmxdev - > filternum ; i + + ) {
if ( dmxdev - > filter [ i ] . state > = DMXDEV_STATE_GO & &
dmxdev - > filter [ i ] . pid = = para - > pid & &
dmxdev - > filter [ i ] . type = = DMXDEV_TYPE_SEC ) {
* secfeed = dmxdev - > filter [ i ] . feed . sec ;
break ;
}
}
/* if no feed found, try to allocate new one */
if ( ! * secfeed ) {
ret = dmxdev - > demux - > allocate_section_feed ( dmxdev - > demux ,
secfeed ,
dvb_dmxdev_section_callback ) ;
if ( ret < 0 ) {
printk ( " DVB (%s): could not alloc feed \n " ,
__FUNCTION__ ) ;
return ret ;
}
ret = ( * secfeed ) - > set ( * secfeed , para - > pid , 32768 , 0 ,
( para - > flags & DMX_CHECK_CRC ) ? 1 : 0 ) ;
if ( ret < 0 ) {
printk ( " DVB (%s): could not set feed \n " ,
__FUNCTION__ ) ;
dvb_dmxdev_feed_restart ( filter ) ;
return ret ;
}
} else {
dvb_dmxdev_feed_stop ( filter ) ;
}
ret = ( * secfeed ) - > allocate_filter ( * secfeed , secfilter ) ;
if ( ret < 0 ) {
dvb_dmxdev_feed_restart ( filter ) ;
filter - > feed . sec - > start_filtering ( * secfeed ) ;
dprintk ( " could not get filter \n " ) ;
return ret ;
}
( * secfilter ) - > priv = filter ;
memcpy ( & ( ( * secfilter ) - > filter_value [ 3 ] ) ,
& ( para - > filter . filter [ 1 ] ) , DMX_FILTER_SIZE - 1 ) ;
memcpy ( & ( * secfilter ) - > filter_mask [ 3 ] ,
& para - > filter . mask [ 1 ] , DMX_FILTER_SIZE - 1 ) ;
memcpy ( & ( * secfilter ) - > filter_mode [ 3 ] ,
& para - > filter . mode [ 1 ] , DMX_FILTER_SIZE - 1 ) ;
( * secfilter ) - > filter_value [ 0 ] = para - > filter . filter [ 0 ] ;
( * secfilter ) - > filter_mask [ 0 ] = para - > filter . mask [ 0 ] ;
( * secfilter ) - > filter_mode [ 0 ] = para - > filter . mode [ 0 ] ;
( * secfilter ) - > filter_mask [ 1 ] = 0 ;
( * secfilter ) - > filter_mask [ 2 ] = 0 ;
filter - > todo = 0 ;
ret = filter - > feed . sec - > start_filtering ( filter - > feed . sec ) ;
if ( ret < 0 )
return ret ;
dvb_dmxdev_filter_timer ( filter ) ;
break ;
}
case DMXDEV_TYPE_PES :
{
struct timespec timeout = { 0 } ;
struct dmx_pes_filter_params * para = & filter - > params . pes ;
dmx_output_t otype ;
int ret ;
int ts_type ;
enum dmx_ts_pes ts_pes ;
struct dmx_ts_feed * * tsfeed = & filter - > feed . ts ;
filter - > feed . ts = NULL ;
otype = para - > output ;
ts_pes = ( enum dmx_ts_pes ) para - > pes_type ;
if ( ts_pes < DMX_PES_OTHER )
ts_type = TS_DECODER ;
else
ts_type = 0 ;
if ( otype = = DMX_OUT_TS_TAP )
ts_type | = TS_PACKET ;
if ( otype = = DMX_OUT_TAP )
ts_type | = TS_PAYLOAD_ONLY | TS_PACKET ;
ret = dmxdev - > demux - > allocate_ts_feed ( dmxdev - > demux ,
tsfeed ,
dvb_dmxdev_ts_callback ) ;
if ( ret < 0 )
return ret ;
( * tsfeed ) - > priv = ( void * ) filter ;
ret = ( * tsfeed ) - > set ( * tsfeed , para - > pid , ts_type , ts_pes ,
188 , 32768 , 0 , timeout ) ;
if ( ret < 0 ) {
dmxdev - > demux - > release_ts_feed ( dmxdev - > demux , * tsfeed ) ;
return ret ;
}
ret = filter - > feed . ts - > start_filtering ( filter - > feed . ts ) ;
2005-07-07 17:57:38 -07:00
if ( ret < 0 ) {
dmxdev - > demux - > release_ts_feed ( dmxdev - > demux , * tsfeed ) ;
2005-04-16 15:20:36 -07:00
return ret ;
2005-07-07 17:57:38 -07:00
}
2005-04-16 15:20:36 -07:00
break ;
}
default :
return - EINVAL ;
}
dvb_dmxdev_filter_state_set ( filter , DMXDEV_STATE_GO ) ;
return 0 ;
}
static int dvb_demux_open ( struct inode * inode , struct file * file )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
int i ;
struct dmxdev_filter * dmxdevfilter ;
if ( ! dmxdev - > filter )
return - EINVAL ;
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
for ( i = 0 ; i < dmxdev - > filternum ; i + + )
if ( dmxdev - > filter [ i ] . state = = DMXDEV_STATE_FREE )
break ;
if ( i = = dmxdev - > filternum ) {
up ( & dmxdev - > mutex ) ;
return - EMFILE ;
}
dmxdevfilter = & dmxdev - > filter [ i ] ;
sema_init ( & dmxdevfilter - > mutex , 1 ) ;
dmxdevfilter - > dvbdev = dmxdev - > dvbdev ;
file - > private_data = dmxdevfilter ;
dvb_dmxdev_buffer_init ( & dmxdevfilter - > buffer ) ;
dmxdevfilter - > type = DMXDEV_TYPE_NONE ;
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_ALLOCATED ) ;
dmxdevfilter - > feed . ts = NULL ;
init_timer ( & dmxdevfilter - > timer ) ;
up ( & dmxdev - > mutex ) ;
return 0 ;
}
static int dvb_dmxdev_filter_free ( struct dmxdev * dmxdev , struct dmxdev_filter * dmxdevfilter )
{
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
dvb_dmxdev_filter_stop ( dmxdevfilter ) ;
dvb_dmxdev_filter_reset ( dmxdevfilter ) ;
if ( dmxdevfilter - > buffer . data ) {
void * mem = dmxdevfilter - > buffer . data ;
spin_lock_irq ( & dmxdev - > lock ) ;
dmxdevfilter - > buffer . data = NULL ;
spin_unlock_irq ( & dmxdev - > lock ) ;
vfree ( mem ) ;
}
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_FREE ) ;
wake_up ( & dmxdevfilter - > buffer . queue ) ;
up ( & dmxdevfilter - > mutex ) ;
up ( & dmxdev - > mutex ) ;
return 0 ;
}
static inline void invert_mode ( dmx_filter_t * filter )
{
int i ;
for ( i = 0 ; i < DMX_FILTER_SIZE ; i + + )
filter - > mode [ i ] ^ = 0xff ;
}
static int dvb_dmxdev_filter_set ( struct dmxdev * dmxdev ,
struct dmxdev_filter * dmxdevfilter ,
struct dmx_sct_filter_params * params )
{
dprintk ( " function : %s \n " , __FUNCTION__ ) ;
dvb_dmxdev_filter_stop ( dmxdevfilter ) ;
dmxdevfilter - > type = DMXDEV_TYPE_SEC ;
dmxdevfilter - > pid = params - > pid ;
memcpy ( & dmxdevfilter - > params . sec ,
params , sizeof ( struct dmx_sct_filter_params ) ) ;
invert_mode ( & dmxdevfilter - > params . sec . filter ) ;
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_SET ) ;
if ( params - > flags & DMX_IMMEDIATE_START )
return dvb_dmxdev_filter_start ( dmxdevfilter ) ;
return 0 ;
}
static int dvb_dmxdev_pes_filter_set ( struct dmxdev * dmxdev ,
struct dmxdev_filter * dmxdevfilter ,
struct dmx_pes_filter_params * params )
{
dvb_dmxdev_filter_stop ( dmxdevfilter ) ;
if ( params - > pes_type > DMX_PES_OTHER | | params - > pes_type < 0 )
return - EINVAL ;
dmxdevfilter - > type = DMXDEV_TYPE_PES ;
dmxdevfilter - > pid = params - > pid ;
memcpy ( & dmxdevfilter - > params , params , sizeof ( struct dmx_pes_filter_params ) ) ;
dvb_dmxdev_filter_state_set ( dmxdevfilter , DMXDEV_STATE_SET ) ;
if ( params - > flags & DMX_IMMEDIATE_START )
return dvb_dmxdev_filter_start ( dmxdevfilter ) ;
return 0 ;
}
static ssize_t dvb_dmxdev_read_sec ( struct dmxdev_filter * dfil ,
struct file * file , char __user * buf , size_t count , loff_t * ppos )
{
int result , hcount ;
int done = 0 ;
if ( dfil - > todo < = 0 ) {
hcount = 3 + dfil - > todo ;
if ( hcount > count )
hcount = count ;
result = dvb_dmxdev_buffer_read ( & dfil - > buffer , file - > f_flags & O_NONBLOCK ,
buf , hcount , ppos ) ;
if ( result < 0 ) {
dfil - > todo = 0 ;
return result ;
}
if ( copy_from_user ( dfil - > secheader - dfil - > todo , buf , result ) )
return - EFAULT ;
buf + = result ;
done = result ;
count - = result ;
dfil - > todo - = result ;
if ( dfil - > todo > - 3 )
return done ;
dfil - > todo = ( ( dfil - > secheader [ 1 ] < < 8 ) | dfil - > secheader [ 2 ] ) & 0xfff ;
if ( ! count )
return done ;
}
if ( count > dfil - > todo )
count = dfil - > todo ;
result = dvb_dmxdev_buffer_read ( & dfil - > buffer , file - > f_flags & O_NONBLOCK ,
buf , count , ppos ) ;
if ( result < 0 )
return result ;
dfil - > todo - = result ;
return ( result + done ) ;
}
static ssize_t
dvb_demux_read ( struct file * file , char __user * buf , size_t count , loff_t * ppos )
{
2005-07-07 17:57:39 -07:00
struct dmxdev_filter * dmxdevfilter = file - > private_data ;
2005-04-16 15:20:36 -07:00
int ret = 0 ;
if ( down_interruptible ( & dmxdevfilter - > mutex ) )
return - ERESTARTSYS ;
if ( dmxdevfilter - > type = = DMXDEV_TYPE_SEC )
ret = dvb_dmxdev_read_sec ( dmxdevfilter , file , buf , count , ppos ) ;
else
ret = dvb_dmxdev_buffer_read ( & dmxdevfilter - > buffer ,
file - > f_flags & O_NONBLOCK ,
buf , count , ppos ) ;
up ( & dmxdevfilter - > mutex ) ;
return ret ;
}
static int dvb_demux_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * parg )
{
2005-07-07 17:57:39 -07:00
struct dmxdev_filter * dmxdevfilter = file - > private_data ;
2005-04-16 15:20:36 -07:00
struct dmxdev * dmxdev = dmxdevfilter - > dev ;
unsigned long arg = ( unsigned long ) parg ;
int ret = 0 ;
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
switch ( cmd ) {
case DMX_START :
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
if ( dmxdevfilter - > state < DMXDEV_STATE_SET )
ret = - EINVAL ;
else
ret = dvb_dmxdev_filter_start ( dmxdevfilter ) ;
up ( & dmxdevfilter - > mutex ) ;
break ;
case DMX_STOP :
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
ret = dvb_dmxdev_filter_stop ( dmxdevfilter ) ;
up ( & dmxdevfilter - > mutex ) ;
break ;
case DMX_SET_FILTER :
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
ret = dvb_dmxdev_filter_set ( dmxdev , dmxdevfilter ,
( struct dmx_sct_filter_params * ) parg ) ;
up ( & dmxdevfilter - > mutex ) ;
break ;
case DMX_SET_PES_FILTER :
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
ret = dvb_dmxdev_pes_filter_set ( dmxdev , dmxdevfilter ,
( struct dmx_pes_filter_params * ) parg ) ;
up ( & dmxdevfilter - > mutex ) ;
break ;
case DMX_SET_BUFFER_SIZE :
if ( down_interruptible ( & dmxdevfilter - > mutex ) ) {
up ( & dmxdev - > mutex ) ;
return - ERESTARTSYS ;
}
ret = dvb_dmxdev_set_buffer_size ( dmxdevfilter , arg ) ;
up ( & dmxdevfilter - > mutex ) ;
break ;
case DMX_GET_EVENT :
break ;
case DMX_GET_PES_PIDS :
if ( ! dmxdev - > demux - > get_pes_pids ) {
ret = - EINVAL ;
break ;
}
dmxdev - > demux - > get_pes_pids ( dmxdev - > demux , ( u16 * ) parg ) ;
break ;
case DMX_GET_STC :
if ( ! dmxdev - > demux - > get_stc ) {
ret = - EINVAL ;
break ;
}
ret = dmxdev - > demux - > get_stc ( dmxdev - > demux ,
( ( struct dmx_stc * ) parg ) - > num ,
& ( ( struct dmx_stc * ) parg ) - > stc ,
& ( ( struct dmx_stc * ) parg ) - > base ) ;
break ;
default :
ret = - EINVAL ;
}
up ( & dmxdev - > mutex ) ;
return ret ;
}
static int dvb_demux_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return dvb_usercopy ( inode , file , cmd , arg , dvb_demux_do_ioctl ) ;
}
static unsigned int dvb_demux_poll ( struct file * file , poll_table * wait )
{
2005-07-07 17:57:39 -07:00
struct dmxdev_filter * dmxdevfilter = file - > private_data ;
2005-04-16 15:20:36 -07:00
unsigned int mask = 0 ;
if ( ! dmxdevfilter )
return - EINVAL ;
poll_wait ( file , & dmxdevfilter - > buffer . queue , wait ) ;
if ( dmxdevfilter - > state ! = DMXDEV_STATE_GO & &
dmxdevfilter - > state ! = DMXDEV_STATE_DONE & &
dmxdevfilter - > state ! = DMXDEV_STATE_TIMEDOUT )
return 0 ;
if ( dmxdevfilter - > buffer . error )
mask | = ( POLLIN | POLLRDNORM | POLLPRI | POLLERR ) ;
if ( dmxdevfilter - > buffer . pread ! = dmxdevfilter - > buffer . pwrite )
mask | = ( POLLIN | POLLRDNORM | POLLPRI ) ;
return mask ;
}
static int dvb_demux_release ( struct inode * inode , struct file * file )
{
2005-07-07 17:57:39 -07:00
struct dmxdev_filter * dmxdevfilter = file - > private_data ;
2005-04-16 15:20:36 -07:00
struct dmxdev * dmxdev = dmxdevfilter - > dev ;
return dvb_dmxdev_filter_free ( dmxdev , dmxdevfilter ) ;
}
static struct file_operations dvb_demux_fops = {
. owner = THIS_MODULE ,
. read = dvb_demux_read ,
. ioctl = dvb_demux_ioctl ,
. open = dvb_demux_open ,
. release = dvb_demux_release ,
. poll = dvb_demux_poll ,
} ;
static struct dvb_device dvbdev_demux = {
. priv = NULL ,
. users = 1 ,
. writers = 1 ,
. fops = & dvb_demux_fops
} ;
static int dvb_dvr_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * parg )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
int ret = 0 ;
if ( down_interruptible ( & dmxdev - > mutex ) )
return - ERESTARTSYS ;
switch ( cmd ) {
case DMX_SET_BUFFER_SIZE :
// FIXME: implement
ret = 0 ;
break ;
default :
ret = - EINVAL ;
}
up ( & dmxdev - > mutex ) ;
return ret ;
}
static int dvb_dvr_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return dvb_usercopy ( inode , file , cmd , arg , dvb_dvr_do_ioctl ) ;
}
static unsigned int dvb_dvr_poll ( struct file * file , poll_table * wait )
{
2005-05-16 21:54:24 -07:00
struct dvb_device * dvbdev = file - > private_data ;
struct dmxdev * dmxdev = dvbdev - > priv ;
2005-04-16 15:20:36 -07:00
unsigned int mask = 0 ;
dprintk ( " function : %s \n " , __FUNCTION__ ) ;
poll_wait ( file , & dmxdev - > dvr_buffer . queue , wait ) ;
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) {
if ( dmxdev - > dvr_buffer . error )
mask | = ( POLLIN | POLLRDNORM | POLLPRI | POLLERR ) ;
if ( dmxdev - > dvr_buffer . pread ! = dmxdev - > dvr_buffer . pwrite )
mask | = ( POLLIN | POLLRDNORM | POLLPRI ) ;
} else
mask | = ( POLLOUT | POLLWRNORM | POLLPRI ) ;
return mask ;
}
static struct file_operations dvb_dvr_fops = {
. owner = THIS_MODULE ,
. read = dvb_dvr_read ,
. write = dvb_dvr_write ,
. ioctl = dvb_dvr_ioctl ,
. open = dvb_dvr_open ,
. release = dvb_dvr_release ,
. poll = dvb_dvr_poll ,
} ;
static struct dvb_device dvbdev_dvr = {
. priv = NULL ,
. users = 1 ,
. writers = 1 ,
. fops = & dvb_dvr_fops
} ;
int
dvb_dmxdev_init ( struct dmxdev * dmxdev , struct dvb_adapter * dvb_adapter )
{
int i ;
if ( dmxdev - > demux - > open ( dmxdev - > demux ) < 0 )
return - EUSERS ;
dmxdev - > filter = vmalloc ( dmxdev - > filternum * sizeof ( struct dmxdev_filter ) ) ;
if ( ! dmxdev - > filter )
return - ENOMEM ;
dmxdev - > dvr = vmalloc ( dmxdev - > filternum * sizeof ( struct dmxdev_dvr ) ) ;
if ( ! dmxdev - > dvr ) {
vfree ( dmxdev - > filter ) ;
dmxdev - > filter = NULL ;
return - ENOMEM ;
}
sema_init ( & dmxdev - > mutex , 1 ) ;
spin_lock_init ( & dmxdev - > lock ) ;
for ( i = 0 ; i < dmxdev - > filternum ; i + + ) {
dmxdev - > filter [ i ] . dev = dmxdev ;
dmxdev - > filter [ i ] . buffer . data = NULL ;
dvb_dmxdev_filter_state_set ( & dmxdev - > filter [ i ] , DMXDEV_STATE_FREE ) ;
dmxdev - > dvr [ i ] . dev = dmxdev ;
dmxdev - > dvr [ i ] . buffer . data = NULL ;
dvb_dmxdev_dvr_state_set ( & dmxdev - > dvr [ i ] , DMXDEV_STATE_FREE ) ;
}
dvb_register_device ( dvb_adapter , & dmxdev - > dvbdev , & dvbdev_demux , dmxdev , DVB_DEVICE_DEMUX ) ;
dvb_register_device ( dvb_adapter , & dmxdev - > dvr_dvbdev , & dvbdev_dvr , dmxdev , DVB_DEVICE_DVR ) ;
dvb_dmxdev_buffer_init ( & dmxdev - > dvr_buffer ) ;
return 0 ;
}
EXPORT_SYMBOL ( dvb_dmxdev_init ) ;
void
dvb_dmxdev_release ( struct dmxdev * dmxdev )
{
dvb_unregister_device ( dmxdev - > dvbdev ) ;
dvb_unregister_device ( dmxdev - > dvr_dvbdev ) ;
vfree ( dmxdev - > filter ) ;
dmxdev - > filter = NULL ;
vfree ( dmxdev - > dvr ) ;
dmxdev - > dvr = NULL ;
dmxdev - > demux - > close ( dmxdev - > demux ) ;
}
EXPORT_SYMBOL ( dvb_dmxdev_release ) ;