2005-04-17 02:20:36 +04:00
/*
* 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 .
*
*/
2009-10-04 16:11:37 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/module.h>
# include <linux/poll.h>
# include <linux/string.h>
# include <linux/crc32.h>
# include <asm/uaccess.h>
2009-11-02 00:46:53 +03:00
# include <asm/div64.h>
2005-04-17 02:20:36 +04:00
# include "dvb_demux.h"
# define NOBUFS
/*
* * # define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
*/
// #define DVB_DEMUX_SECTION_LOSS_LOG
2009-06-06 16:31:34 +04:00
static int dvb_demux_tscheck ;
module_param ( dvb_demux_tscheck , int , 0644 ) ;
MODULE_PARM_DESC ( dvb_demux_tscheck ,
" enable transport stream continuity and TEI check " ) ;
2009-11-02 00:46:53 +03:00
static int dvb_demux_speedcheck ;
module_param ( dvb_demux_speedcheck , int , 0644 ) ;
MODULE_PARM_DESC ( dvb_demux_speedcheck ,
" enable transport stream speed check " ) ;
2009-06-06 16:31:34 +04:00
# define dprintk_tscheck(x...) do { \
if ( dvb_demux_tscheck & & printk_ratelimit ( ) ) \
printk ( x ) ; \
} while ( 0 )
2005-04-17 02:20:36 +04:00
/******************************************************************************
* static inlined helper functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static inline u16 section_length ( const u8 * buf )
{
2005-09-10 00:02:26 +04:00
return 3 + ( ( buf [ 1 ] & 0x0f ) < < 8 ) + buf [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
static inline u16 ts_pid ( const u8 * buf )
{
2005-09-10 00:02:26 +04:00
return ( ( buf [ 1 ] & 0x1f ) < < 8 ) + buf [ 2 ] ;
2005-04-17 02:20:36 +04:00
}
static inline u8 payload ( const u8 * tsp )
{
2005-09-10 00:02:26 +04:00
if ( ! ( tsp [ 3 ] & 0x10 ) ) // no payload?
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-10 00:02:26 +04:00
if ( tsp [ 3 ] & 0x20 ) { // adaptation field?
if ( tsp [ 4 ] > 183 ) // corrupted data?
2005-04-17 02:20:36 +04:00
return 0 ;
else
2005-09-10 00:02:26 +04:00
return 184 - 1 - tsp [ 4 ] ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
2005-04-17 02:20:36 +04:00
return 184 ;
}
2005-09-10 00:02:26 +04:00
static u32 dvb_dmx_crc32 ( struct dvb_demux_feed * f , const u8 * src , size_t len )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
return ( f - > feed . sec . crc_val = crc32_be ( f - > feed . sec . crc_val , src , len ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
static void dvb_dmx_memcopy ( struct dvb_demux_feed * f , u8 * d , const u8 * s ,
size_t len )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
memcpy ( d , s , len ) ;
2005-04-17 02:20:36 +04:00
}
/******************************************************************************
* Software filter functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-10 00:02:26 +04:00
static inline int dvb_dmx_swfilter_payload ( struct dvb_demux_feed * feed ,
const u8 * buf )
2005-04-17 02:20:36 +04:00
{
int count = payload ( buf ) ;
int p ;
//int ccok;
//u8 cc;
if ( count = = 0 )
return - 1 ;
2005-09-10 00:02:26 +04:00
p = 188 - count ;
2005-04-17 02:20:36 +04:00
/*
2005-09-10 00:02:26 +04:00
cc = buf [ 3 ] & 0x0f ;
ccok = ( ( feed - > cc + 1 ) & 0x0f ) = = cc ;
feed - > cc = cc ;
2005-04-17 02:20:36 +04:00
if ( ! ccok )
printk ( " missed packet! \n " ) ;
*/
2005-09-10 00:02:26 +04:00
if ( buf [ 1 ] & 0x40 ) // PUSI ?
2005-04-17 02:20:36 +04:00
feed - > peslen = 0xfffa ;
feed - > peslen + = count ;
2005-09-10 00:02:26 +04:00
return feed - > cb . ts ( & buf [ p ] , count , NULL , 0 , & feed - > feed . ts , DMX_OK ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
static int dvb_dmx_swfilter_sectionfilter ( struct dvb_demux_feed * feed ,
struct dvb_demux_filter * f )
2005-04-17 02:20:36 +04:00
{
u8 neq = 0 ;
int i ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < DVB_DEMUX_MASK_MAX ; i + + ) {
2005-04-17 02:20:36 +04:00
u8 xor = f - > filter . filter_value [ i ] ^ feed - > feed . sec . secbuf [ i ] ;
if ( f - > maskandmode [ i ] & xor )
return 0 ;
neq | = f - > maskandnotmode [ i ] & xor ;
}
if ( f - > doneq & & ! neq )
return 0 ;
2005-09-10 00:02:26 +04:00
return feed - > cb . sec ( feed - > feed . sec . secbuf , feed - > feed . sec . seclen ,
NULL , 0 , & f - > filter , DMX_OK ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
static inline int dvb_dmx_swfilter_section_feed ( struct dvb_demux_feed * feed )
2005-04-17 02:20:36 +04:00
{
struct dvb_demux * demux = feed - > demux ;
struct dvb_demux_filter * f = feed - > filter ;
struct dmx_section_feed * sec = & feed - > feed . sec ;
int section_syntax_indicator ;
if ( ! sec - > is_filtering )
return 0 ;
if ( ! f )
return 0 ;
if ( sec - > check_crc ) {
section_syntax_indicator = ( ( sec - > secbuf [ 1 ] & 0x80 ) ! = 0 ) ;
if ( section_syntax_indicator & &
demux - > check_crc32 ( feed , sec - > secbuf , sec - > seclen ) )
return - 1 ;
}
do {
if ( dvb_dmx_swfilter_sectionfilter ( feed , f ) < 0 )
return - 1 ;
} while ( ( f = f - > next ) & & sec - > is_filtering ) ;
sec - > seclen = 0 ;
return 0 ;
}
static void dvb_dmx_swfilter_section_new ( struct dvb_demux_feed * feed )
{
struct dmx_section_feed * sec = & feed - > feed . sec ;
# ifdef DVB_DEMUX_SECTION_LOSS_LOG
2005-09-10 00:02:26 +04:00
if ( sec - > secbufp < sec - > tsfeedp ) {
2005-04-17 02:20:36 +04:00
int i , n = sec - > tsfeedp - sec - > secbufp ;
2005-09-10 00:02:26 +04:00
/*
* Section padding is done with 0xff bytes entirely .
* Due to speed reasons , we won ' t check all of them
* but just first and last .
*/
if ( sec - > secbuf [ 0 ] ! = 0xff | | sec - > secbuf [ n - 1 ] ! = 0xff ) {
2005-04-17 02:20:36 +04:00
printk ( " dvb_demux.c section ts padding loss: %d/%d \n " ,
n , sec - > tsfeedp ) ;
printk ( " dvb_demux.c pad data: " ) ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < n ; i + + )
2005-04-17 02:20:36 +04:00
printk ( " %02x " , sec - > secbuf [ i ] ) ;
printk ( " \n " ) ;
}
}
# endif
sec - > tsfeedp = sec - > secbufp = sec - > seclen = 0 ;
sec - > secbuf = sec - > secbuf_base ;
}
/*
2005-09-10 00:02:26 +04:00
* Losless Section Demux 1.4 .1 by Emard
* Valsecchi Patrick :
* - middle of section A ( no PUSI )
* - end of section A and start of section B
* ( with PUSI pointing to the start of the second section )
*
* In this case , without feed - > pusi_seen you ' ll receive a garbage section
* consisting of the end of section A . Basically because tsfeedp
* is incemented and the use = 0 condition is not raised
* when the second packet arrives .
*
* Fix :
* when demux is started , let feed - > pusi_seen = 0 to
* prevent initial feeding of garbage from the end of
* previous section . When you for the first time see PUSI = 1
* then set feed - > pusi_seen = 1
*/
static int dvb_dmx_swfilter_section_copy_dump ( struct dvb_demux_feed * feed ,
const u8 * buf , u8 len )
2005-04-17 02:20:36 +04:00
{
struct dvb_demux * demux = feed - > demux ;
struct dmx_section_feed * sec = & feed - > feed . sec ;
u16 limit , seclen , n ;
2005-09-10 00:02:26 +04:00
if ( sec - > tsfeedp > = DMX_MAX_SECFEED_SIZE )
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-10 00:02:26 +04:00
if ( sec - > tsfeedp + len > DMX_MAX_SECFEED_SIZE ) {
2005-04-17 02:20:36 +04:00
# ifdef DVB_DEMUX_SECTION_LOSS_LOG
printk ( " dvb_demux.c section buffer full loss: %d/%d \n " ,
2005-09-10 00:02:26 +04:00
sec - > tsfeedp + len - DMX_MAX_SECFEED_SIZE ,
DMX_MAX_SECFEED_SIZE ) ;
2005-04-17 02:20:36 +04:00
# endif
len = DMX_MAX_SECFEED_SIZE - sec - > tsfeedp ;
}
2005-09-10 00:02:26 +04:00
if ( len < = 0 )
2005-04-17 02:20:36 +04:00
return 0 ;
demux - > memcopy ( feed , sec - > secbuf_base + sec - > tsfeedp , buf , len ) ;
sec - > tsfeedp + = len ;
2005-09-10 00:02:26 +04:00
/*
* Dump all the sections we can find in the data ( Emard )
*/
2005-04-17 02:20:36 +04:00
limit = sec - > tsfeedp ;
2005-09-10 00:02:26 +04:00
if ( limit > DMX_MAX_SECFEED_SIZE )
return - 1 ; /* internal error should never happen */
2005-04-17 02:20:36 +04:00
/* to be sure always set secbuf */
sec - > secbuf = sec - > secbuf_base + sec - > secbufp ;
2005-09-10 00:02:26 +04:00
for ( n = 0 ; sec - > secbufp + 2 < limit ; n + + ) {
2005-04-17 02:20:36 +04:00
seclen = section_length ( sec - > secbuf ) ;
2005-11-09 08:35:50 +03:00
if ( seclen < = 0 | | seclen > DMX_MAX_SECTION_SIZE
2005-09-10 00:02:26 +04:00
| | seclen + sec - > secbufp > limit )
2005-04-17 02:20:36 +04:00
return 0 ;
sec - > seclen = seclen ;
sec - > crc_val = ~ 0 ;
/* dump [secbuf .. secbuf+seclen) */
2005-09-10 00:02:26 +04:00
if ( feed - > pusi_seen )
2005-04-17 02:20:36 +04:00
dvb_dmx_swfilter_section_feed ( feed ) ;
# ifdef DVB_DEMUX_SECTION_LOSS_LOG
else
printk ( " dvb_demux.c pusi not seen, discarding section data \n " ) ;
# endif
2005-09-10 00:02:26 +04:00
sec - > secbufp + = seclen ; /* secbufp and secbuf moving together is */
sec - > secbuf + = seclen ; /* redundant but saves pointer arithmetic */
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dvb_dmx_swfilter_section_packet ( struct dvb_demux_feed * feed ,
const u8 * buf )
2005-04-17 02:20:36 +04:00
{
u8 p , count ;
int ccok , dc_i = 0 ;
u8 cc ;
count = payload ( buf ) ;
2005-09-10 00:02:26 +04:00
if ( count = = 0 ) /* count == 0 if no payload or out of range */
2005-04-17 02:20:36 +04:00
return - 1 ;
2005-09-10 00:02:26 +04:00
p = 188 - count ; /* payload start */
2005-04-17 02:20:36 +04:00
cc = buf [ 3 ] & 0x0f ;
ccok = ( ( feed - > cc + 1 ) & 0x0f ) = = cc ;
feed - > cc = cc ;
if ( buf [ 3 ] & 0x20 ) {
/* adaption field present, check for discontinuity_indicator */
if ( ( buf [ 4 ] > 0 ) & & ( buf [ 5 ] & 0x80 ) )
dc_i = 1 ;
}
if ( ! ccok | | dc_i ) {
# ifdef DVB_DEMUX_SECTION_LOSS_LOG
2005-09-10 00:02:26 +04:00
printk ( " dvb_demux.c discontinuity detected %d bytes lost \n " ,
count ) ;
/*
* those bytes under sume circumstances will again be reported
* in the following dvb_dmx_swfilter_section_new
*/
2005-04-17 02:20:36 +04:00
# endif
2005-09-10 00:02:26 +04:00
/*
* Discontinuity detected . Reset pusi_seen = 0 to
* stop feeding of suspicious data until next PUSI = 1 arrives
*/
2005-04-17 02:20:36 +04:00
feed - > pusi_seen = 0 ;
dvb_dmx_swfilter_section_new ( feed ) ;
}
if ( buf [ 1 ] & 0x40 ) {
2005-09-10 00:02:26 +04:00
/* PUSI=1 (is set), section boundary is here */
2005-04-17 02:20:36 +04:00
if ( count > 1 & & buf [ p ] < count ) {
2005-09-10 00:02:26 +04:00
const u8 * before = & buf [ p + 1 ] ;
2005-04-17 02:20:36 +04:00
u8 before_len = buf [ p ] ;
2005-09-10 00:02:26 +04:00
const u8 * after = & before [ before_len ] ;
u8 after_len = count - 1 - before_len ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:02:26 +04:00
dvb_dmx_swfilter_section_copy_dump ( feed , before ,
before_len ) ;
2005-04-17 02:20:36 +04:00
/* before start of new section, set pusi_seen = 1 */
feed - > pusi_seen = 1 ;
dvb_dmx_swfilter_section_new ( feed ) ;
2005-09-10 00:02:26 +04:00
dvb_dmx_swfilter_section_copy_dump ( feed , after ,
after_len ) ;
2005-04-17 02:20:36 +04:00
}
# ifdef DVB_DEMUX_SECTION_LOSS_LOG
2005-09-10 00:02:26 +04:00
else if ( count > 0 )
printk ( " dvb_demux.c PUSI=1 but %d bytes lost \n " , count ) ;
2005-04-17 02:20:36 +04:00
# endif
} else {
2005-09-10 00:02:26 +04:00
/* PUSI=0 (is not set), no section boundary */
dvb_dmx_swfilter_section_copy_dump ( feed , & buf [ p ] , count ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static inline void dvb_dmx_swfilter_packet_type ( struct dvb_demux_feed * feed ,
const u8 * buf )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
switch ( feed - > type ) {
2005-04-17 02:20:36 +04:00
case DMX_TYPE_TS :
if ( ! feed - > feed . ts . is_filtering )
break ;
if ( feed - > ts_type & TS_PACKET ) {
if ( feed - > ts_type & TS_PAYLOAD_ONLY )
dvb_dmx_swfilter_payload ( feed , buf ) ;
else
2005-09-10 00:02:26 +04:00
feed - > cb . ts ( buf , 188 , NULL , 0 , & feed - > feed . ts ,
DMX_OK ) ;
2005-04-17 02:20:36 +04:00
}
if ( feed - > ts_type & TS_DECODER )
if ( feed - > demux - > write_to_decoder )
feed - > demux - > write_to_decoder ( feed , buf , 188 ) ;
break ;
case DMX_TYPE_SEC :
if ( ! feed - > feed . sec . is_filtering )
break ;
if ( dvb_dmx_swfilter_section_packet ( feed , buf ) < 0 )
2005-09-10 00:02:26 +04:00
feed - > feed . sec . seclen = feed - > feed . sec . secbufp = 0 ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
}
# define DVR_FEED(f) \
( ( ( f ) - > type = = DMX_TYPE_TS ) & & \
( ( f ) - > feed . ts . is_filtering ) & & \
2008-04-22 21:45:47 +04:00
( ( ( f ) - > ts_type & ( TS_PACKET | TS_DEMUX ) ) = = TS_PACKET ) )
2005-04-17 02:20:36 +04:00
static void dvb_dmx_swfilter_packet ( struct dvb_demux * demux , const u8 * buf )
{
struct dvb_demux_feed * feed ;
u16 pid = ts_pid ( buf ) ;
int dvr_done = 0 ;
2009-11-02 00:46:53 +03:00
if ( dvb_demux_speedcheck ) {
struct timespec cur_time , delta_time ;
u64 speed_bytes , speed_timedelta ;
demux - > speed_pkts_cnt + + ;
/* show speed every SPEED_PKTS_INTERVAL packets */
if ( ! ( demux - > speed_pkts_cnt % SPEED_PKTS_INTERVAL ) ) {
cur_time = current_kernel_time ( ) ;
if ( demux - > speed_last_time . tv_sec ! = 0 & &
demux - > speed_last_time . tv_nsec ! = 0 ) {
delta_time = timespec_sub ( cur_time ,
demux - > speed_last_time ) ;
speed_bytes = ( u64 ) demux - > speed_pkts_cnt
* 188 * 8 ;
/* convert to 1024 basis */
speed_bytes = 1000 * div64_u64 ( speed_bytes ,
1024 ) ;
speed_timedelta =
( u64 ) timespec_to_ns ( & delta_time ) ;
speed_timedelta = div64_u64 ( speed_timedelta ,
1000000 ) ; /* nsec -> usec */
printk ( KERN_INFO " TS speed %llu Kbits/sec \n " ,
div64_u64 ( speed_bytes ,
speed_timedelta ) ) ;
} ;
demux - > speed_last_time = cur_time ;
demux - > speed_pkts_cnt = 0 ;
} ;
} ;
2010-03-06 20:58:01 +03:00
if ( demux - > cnt_storage & & dvb_demux_tscheck ) {
2009-06-06 16:31:34 +04:00
/* check pkt counter */
if ( pid < MAX_PID ) {
if ( buf [ 1 ] & 0x80 )
dprintk_tscheck ( " TEI detected. "
" PID=0x%x data1=0x%x \n " ,
pid , buf [ 1 ] ) ;
if ( ( buf [ 3 ] & 0xf ) ! = demux - > cnt_storage [ pid ] )
dprintk_tscheck ( " TS packet counter mismatch. "
" PID=0x%x expected 0x%x "
" got 0x%x \n " ,
pid , demux - > cnt_storage [ pid ] ,
buf [ 3 ] & 0xf ) ;
demux - > cnt_storage [ pid ] = ( ( buf [ 3 ] & 0xf ) + 1 ) & 0xf ;
} ;
/* end check */
} ;
2007-10-10 12:37:39 +04:00
list_for_each_entry ( feed , & demux - > feed_list , list_head ) {
2005-04-17 02:20:36 +04:00
if ( ( feed - > pid ! = pid ) & & ( feed - > pid ! = 0x2000 ) )
continue ;
/* copy each packet only once to the dvr device, even
* if a PID is in multiple filters ( e . g . video + PCR ) */
if ( ( DVR_FEED ( feed ) ) & & ( dvr_done + + ) )
continue ;
2009-07-15 03:48:37 +04:00
if ( feed - > pid = = pid )
2005-04-17 02:20:36 +04:00
dvb_dmx_swfilter_packet_type ( feed , buf ) ;
2009-07-15 03:48:37 +04:00
else if ( feed - > pid = = 0x2000 )
2005-04-17 02:20:36 +04:00
feed - > cb . ts ( buf , 188 , NULL , 0 , & feed - > feed . ts , DMX_OK ) ;
}
}
2005-09-10 00:02:26 +04:00
void dvb_dmx_swfilter_packets ( struct dvb_demux * demux , const u8 * buf ,
size_t count )
2005-04-17 02:20:36 +04:00
{
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-16 21:27:44 +03:00
spin_lock ( & demux - > lock ) ;
2005-04-17 02:20:36 +04:00
while ( count - - ) {
2005-09-10 00:02:26 +04:00
if ( buf [ 0 ] = = 0x47 )
dvb_dmx_swfilter_packet ( demux , buf ) ;
2005-04-17 02:20:36 +04:00
buf + = 188 ;
}
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-16 21:27:44 +03:00
spin_unlock ( & demux - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
EXPORT_SYMBOL ( dvb_dmx_swfilter_packets ) ;
2005-04-17 02:20:36 +04:00
2011-04-08 19:40:51 +04:00
static inline int find_next_packet ( const u8 * buf , int pos , size_t count ,
const int pktsize )
2005-04-17 02:20:36 +04:00
{
2011-04-08 19:40:51 +04:00
int start = pos , lost ;
2005-04-17 02:20:36 +04:00
2011-04-08 19:40:51 +04:00
while ( pos < count ) {
if ( buf [ pos ] = = 0x47 | |
( pktsize = = 204 & & buf [ pos ] = = 0xB8 ) )
break ;
pos + + ;
2005-04-17 02:20:36 +04:00
}
2011-04-08 19:40:51 +04:00
lost = pos - start ;
if ( lost ) {
/* This garbage is part of a valid packet? */
int backtrack = pos - pktsize ;
if ( backtrack > = 0 & & ( buf [ backtrack ] = = 0x47 | |
( pktsize = = 204 & & buf [ backtrack ] = = 0xB8 ) ) )
return backtrack ;
2005-04-17 02:20:36 +04:00
}
2011-04-08 19:40:51 +04:00
return pos ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:02:26 +04:00
2011-04-08 19:40:51 +04:00
/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
static inline void _dvb_dmx_swfilter ( struct dvb_demux * demux , const u8 * buf ,
size_t count , const int pktsize )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
int p = 0 , i , j ;
2011-04-08 19:40:51 +04:00
const u8 * q ;
2005-09-10 00:02:26 +04:00
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-16 21:27:44 +03:00
spin_lock ( & demux - > lock ) ;
2005-04-17 02:20:36 +04:00
2011-04-08 19:40:51 +04:00
if ( demux - > tsbufp ) { /* tsbuf[0] is now 0x47. */
2005-09-10 00:02:26 +04:00
i = demux - > tsbufp ;
2011-04-08 19:40:51 +04:00
j = pktsize - i ;
2005-09-10 00:02:26 +04:00
if ( count < j ) {
2005-04-17 02:20:36 +04:00
memcpy ( & demux - > tsbuf [ i ] , buf , count ) ;
demux - > tsbufp + = count ;
goto bailout ;
}
memcpy ( & demux - > tsbuf [ i ] , buf , j ) ;
2011-04-08 19:40:51 +04:00
if ( demux - > tsbuf [ 0 ] = = 0x47 ) /* double check */
dvb_dmx_swfilter_packet ( demux , demux - > tsbuf ) ;
2005-04-17 02:20:36 +04:00
demux - > tsbufp = 0 ;
p + = j ;
}
2011-04-08 19:40:51 +04:00
while ( 1 ) {
p = find_next_packet ( buf , p , count , pktsize ) ;
if ( p > = count )
break ;
if ( count - p < pktsize )
break ;
q = & buf [ p ] ;
if ( pktsize = = 204 & & ( * q = = 0xB8 ) ) {
memcpy ( demux - > tsbuf , q , 188 ) ;
demux - > tsbuf [ 0 ] = 0x47 ;
q = demux - > tsbuf ;
2005-04-17 02:20:36 +04:00
}
2011-04-08 19:40:51 +04:00
dvb_dmx_swfilter_packet ( demux , q ) ;
p + = pktsize ;
}
i = count - p ;
if ( i ) {
memcpy ( demux - > tsbuf , & buf [ p ] , i ) ;
demux - > tsbufp = i ;
if ( pktsize = = 204 & & demux - > tsbuf [ 0 ] = = 0xB8 )
demux - > tsbuf [ 0 ] = 0x47 ;
2005-04-17 02:20:36 +04:00
}
bailout :
V4L/DVB (10572): Revert commit dda06a8e4610757def753ee3a541a0b1a1feb36b
On Mon, 02 Feb 2009, Hartmut wrote:
This change set is wrong. The affected functions cannot be called from
an interrupt context, because they may process large buffers. In this
case, interrupts are disabled for a long time. Functions, like
dvb_dmx_swfilter_packets(), could be called only from a tasklet.
This change set does hide some strong design bugs in dm1105.c and
au0828-dvb.c.
Please revert this change set and do fix the bugs in dm1105.c and
au0828-dvb.c (and other files).
On Sun, 15 Feb 2009, Oliver Endriss wrote:
This changeset _must_ be reverted! It breaks all kernels since 2.6.27
for applications which use DVB and require a low interrupt latency.
It is a very bad idea to call the demuxer to process data buffers with
interrupts disabled!
On Mon, 16 Feb 2009, Trent Piepho wrote:
I agree, this is bad. The demuxer is far too much work to be done with
IRQs off. IMHO, even doing it under a spin-lock is excessive. It should
be a mutex. Drivers should use a work-queue to feed the demuxer.
Thank you for testing this changeset and discovering the issues on it.
Cc: Trent Piepho <xyzzy@speakeasy.org>
Cc: Hartmut <e9hack@googlemail.com>
Cc: Oliver Endriss <o.endriss@gmx.de>
Cc: Andreas Oberritter <obi@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2009-02-16 21:27:44 +03:00
spin_unlock ( & demux - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2011-04-08 19:40:51 +04:00
void dvb_dmx_swfilter ( struct dvb_demux * demux , const u8 * buf , size_t count )
{
_dvb_dmx_swfilter ( demux , buf , count , 188 ) ;
}
EXPORT_SYMBOL ( dvb_dmx_swfilter ) ;
void dvb_dmx_swfilter_204 ( struct dvb_demux * demux , const u8 * buf , size_t count )
{
_dvb_dmx_swfilter ( demux , buf , count , 204 ) ;
}
2005-09-10 00:02:26 +04:00
EXPORT_SYMBOL ( dvb_dmx_swfilter_204 ) ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:02:26 +04:00
static struct dvb_demux_filter * dvb_dmx_filter_alloc ( struct dvb_demux * demux )
2005-04-17 02:20:36 +04:00
{
int i ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < demux - > filternum ; i + + )
2005-04-17 02:20:36 +04:00
if ( demux - > filter [ i ] . state = = DMX_STATE_FREE )
break ;
if ( i = = demux - > filternum )
return NULL ;
demux - > filter [ i ] . state = DMX_STATE_ALLOCATED ;
return & demux - > filter [ i ] ;
}
2005-09-10 00:02:26 +04:00
static struct dvb_demux_feed * dvb_dmx_feed_alloc ( struct dvb_demux * demux )
2005-04-17 02:20:36 +04:00
{
int i ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < demux - > feednum ; i + + )
2005-04-17 02:20:36 +04:00
if ( demux - > feed [ i ] . state = = DMX_STATE_FREE )
break ;
if ( i = = demux - > feednum )
return NULL ;
demux - > feed [ i ] . state = DMX_STATE_ALLOCATED ;
return & demux - > feed [ i ] ;
}
static int dvb_demux_feed_find ( struct dvb_demux_feed * feed )
{
struct dvb_demux_feed * entry ;
list_for_each_entry ( entry , & feed - > demux - > feed_list , list_head )
if ( entry = = feed )
return 1 ;
return 0 ;
}
static void dvb_demux_feed_add ( struct dvb_demux_feed * feed )
{
spin_lock_irq ( & feed - > demux - > lock ) ;
if ( dvb_demux_feed_find ( feed ) ) {
printk ( KERN_ERR " %s: feed already in list (type=%x state=%x pid=%x) \n " ,
2008-04-09 06:20:00 +04:00
__func__ , feed - > type , feed - > state , feed - > pid ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
list_add ( & feed - > list_head , & feed - > demux - > feed_list ) ;
out :
spin_unlock_irq ( & feed - > demux - > lock ) ;
}
static void dvb_demux_feed_del ( struct dvb_demux_feed * feed )
{
spin_lock_irq ( & feed - > demux - > lock ) ;
if ( ! ( dvb_demux_feed_find ( feed ) ) ) {
printk ( KERN_ERR " %s: feed not in list (type=%x state=%x pid=%x) \n " ,
2008-04-09 06:20:00 +04:00
__func__ , feed - > type , feed - > state , feed - > pid ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
list_del ( & feed - > list_head ) ;
out :
spin_unlock_irq ( & feed - > demux - > lock ) ;
}
2005-09-10 00:02:26 +04:00
static int dmx_ts_feed_set ( struct dmx_ts_feed * ts_feed , u16 pid , int ts_type ,
enum dmx_ts_pes pes_type ,
size_t circular_buffer_size , struct timespec timeout )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * feed = ( struct dvb_demux_feed * ) ts_feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * demux = feed - > demux ;
if ( pid > DMX_MAX_PID )
return - EINVAL ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & demux - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( ts_type & TS_DECODER ) {
if ( pes_type > = DMX_TS_PES_OTHER ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( demux - > pesfilter [ pes_type ] & &
demux - > pesfilter [ pes_type ] ! = feed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
demux - > pesfilter [ pes_type ] = feed ;
demux - > pids [ pes_type ] = pid ;
}
dvb_demux_feed_add ( feed ) ;
feed - > pid = pid ;
feed - > buffer_size = circular_buffer_size ;
feed - > timeout = timeout ;
feed - > ts_type = ts_type ;
feed - > pes_type = pes_type ;
if ( feed - > buffer_size ) {
# ifdef NOBUFS
2005-09-10 00:02:26 +04:00
feed - > buffer = NULL ;
2005-04-17 02:20:36 +04:00
# else
feed - > buffer = vmalloc ( feed - > buffer_size ) ;
if ( ! feed - > buffer ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
# endif
}
feed - > state = DMX_STATE_READY ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dmx_ts_feed_start_filtering ( struct dmx_ts_feed * ts_feed )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * feed = ( struct dvb_demux_feed * ) ts_feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * demux = feed - > demux ;
int ret ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & demux - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( feed - > state ! = DMX_STATE_READY | | feed - > type ! = DMX_TYPE_TS ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( ! demux - > start_feed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
if ( ( ret = demux - > start_feed ( feed ) ) < 0 ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
spin_lock_irq ( & demux - > lock ) ;
ts_feed - > is_filtering = 1 ;
feed - > state = DMX_STATE_GO ;
spin_unlock_irq ( & demux - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dmx_ts_feed_stop_filtering ( struct dmx_ts_feed * ts_feed )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * feed = ( struct dvb_demux_feed * ) ts_feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * demux = feed - > demux ;
int ret ;
2007-03-10 12:21:25 +03:00
mutex_lock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( feed - > state < DMX_STATE_GO ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
if ( ! demux - > stop_feed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
ret = demux - > stop_feed ( feed ) ;
spin_lock_irq ( & demux - > lock ) ;
ts_feed - > is_filtering = 0 ;
feed - > state = DMX_STATE_ALLOCATED ;
spin_unlock_irq ( & demux - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_allocate_ts_feed ( struct dmx_demux * dmx ,
struct dmx_ts_feed * * ts_feed ,
dmx_ts_cb callback )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * demux = ( struct dvb_demux * ) dmx ;
2005-04-17 02:20:36 +04:00
struct dvb_demux_feed * feed ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & demux - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( ! ( feed = dvb_dmx_feed_alloc ( demux ) ) ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
feed - > type = DMX_TYPE_TS ;
feed - > cb . ts = callback ;
feed - > demux = demux ;
feed - > pid = 0xffff ;
feed - > peslen = 0xfffa ;
feed - > buffer = NULL ;
( * ts_feed ) = & feed - > feed . ts ;
( * ts_feed ) - > parent = dmx ;
( * ts_feed ) - > priv = NULL ;
( * ts_feed ) - > is_filtering = 0 ;
( * ts_feed ) - > start_filtering = dmx_ts_feed_start_filtering ;
( * ts_feed ) - > stop_filtering = dmx_ts_feed_stop_filtering ;
( * ts_feed ) - > set = dmx_ts_feed_set ;
if ( ! ( feed - > filter = dvb_dmx_filter_alloc ( demux ) ) ) {
feed - > state = DMX_STATE_FREE ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
feed - > filter - > type = DMX_TYPE_TS ;
feed - > filter - > feed = feed ;
feed - > filter - > state = DMX_STATE_READY ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_release_ts_feed ( struct dmx_demux * dmx ,
struct dmx_ts_feed * ts_feed )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * demux = ( struct dvb_demux * ) dmx ;
struct dvb_demux_feed * feed = ( struct dvb_demux_feed * ) ts_feed ;
2005-04-17 02:20:36 +04:00
2007-03-10 12:21:25 +03:00
mutex_lock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( feed - > state = = DMX_STATE_FREE ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
# ifndef NOBUFS
vfree ( feed - > buffer ) ;
2005-09-10 00:02:26 +04:00
feed - > buffer = NULL ;
2005-04-17 02:20:36 +04:00
# endif
feed - > state = DMX_STATE_FREE ;
feed - > filter - > state = DMX_STATE_FREE ;
dvb_demux_feed_del ( feed ) ;
feed - > pid = 0xffff ;
if ( feed - > ts_type & TS_DECODER & & feed - > pes_type < DMX_TS_PES_OTHER )
demux - > pesfilter [ feed - > pes_type ] = NULL ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & demux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/******************************************************************************
* dmx_section_feed API calls
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-10 00:02:26 +04:00
static int dmx_section_feed_allocate_filter ( struct dmx_section_feed * feed ,
struct dmx_section_filter * * filter )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * dvbdemux = dvbdmxfeed - > demux ;
struct dvb_demux_filter * dvbdmxfilter ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & dvbdemux - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
dvbdmxfilter = dvb_dmx_filter_alloc ( dvbdemux ) ;
if ( ! dvbdmxfilter ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
spin_lock_irq ( & dvbdemux - > lock ) ;
* filter = & dvbdmxfilter - > filter ;
( * filter ) - > parent = feed ;
( * filter ) - > priv = NULL ;
dvbdmxfilter - > feed = dvbdmxfeed ;
dvbdmxfilter - > type = DMX_TYPE_SEC ;
dvbdmxfilter - > state = DMX_STATE_READY ;
dvbdmxfilter - > next = dvbdmxfeed - > filter ;
dvbdmxfeed - > filter = dvbdmxfilter ;
spin_unlock_irq ( & dvbdemux - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dmx_section_feed_set ( struct dmx_section_feed * feed ,
u16 pid , size_t circular_buffer_size ,
int check_crc )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * dvbdmx = dvbdmxfeed - > demux ;
if ( pid > 0x1fff )
return - EINVAL ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & dvbdmx - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
dvb_demux_feed_add ( dvbdmxfeed ) ;
dvbdmxfeed - > pid = pid ;
dvbdmxfeed - > buffer_size = circular_buffer_size ;
dvbdmxfeed - > feed . sec . check_crc = check_crc ;
# ifdef NOBUFS
dvbdmxfeed - > buffer = NULL ;
# else
2005-09-10 00:02:26 +04:00
dvbdmxfeed - > buffer = vmalloc ( dvbdmxfeed - > buffer_size ) ;
2005-04-17 02:20:36 +04:00
if ( ! dvbdmxfeed - > buffer ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
# endif
dvbdmxfeed - > state = DMX_STATE_READY ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void prepare_secfilters ( struct dvb_demux_feed * dvbdmxfeed )
{
int i ;
struct dvb_demux_filter * f ;
struct dmx_section_filter * sf ;
u8 mask , mode , doneq ;
2005-09-10 00:02:26 +04:00
if ( ! ( f = dvbdmxfeed - > filter ) )
2005-04-17 02:20:36 +04:00
return ;
do {
sf = & f - > filter ;
doneq = 0 ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < DVB_DEMUX_MASK_MAX ; i + + ) {
2005-04-17 02:20:36 +04:00
mode = sf - > filter_mode [ i ] ;
mask = sf - > filter_mask [ i ] ;
f - > maskandmode [ i ] = mask & mode ;
doneq | = f - > maskandnotmode [ i ] = mask & ~ mode ;
}
f - > doneq = doneq ? 1 : 0 ;
} while ( ( f = f - > next ) ) ;
}
static int dmx_section_feed_start_filtering ( struct dmx_section_feed * feed )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * dvbdmx = dvbdmxfeed - > demux ;
int ret ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & dvbdmx - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( feed - > is_filtering ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
if ( ! dvbdmxfeed - > filter ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
dvbdmxfeed - > feed . sec . tsfeedp = 0 ;
dvbdmxfeed - > feed . sec . secbuf = dvbdmxfeed - > feed . sec . secbuf_base ;
dvbdmxfeed - > feed . sec . secbufp = 0 ;
dvbdmxfeed - > feed . sec . seclen = 0 ;
if ( ! dvbdmx - > start_feed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
prepare_secfilters ( dvbdmxfeed ) ;
if ( ( ret = dvbdmx - > start_feed ( dvbdmxfeed ) ) < 0 ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
spin_lock_irq ( & dvbdmx - > lock ) ;
feed - > is_filtering = 1 ;
dvbdmxfeed - > state = DMX_STATE_GO ;
spin_unlock_irq ( & dvbdmx - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dmx_section_feed_stop_filtering ( struct dmx_section_feed * feed )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * dvbdmx = dvbdmxfeed - > demux ;
int ret ;
2007-03-10 12:21:25 +03:00
mutex_lock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ! dvbdmx - > stop_feed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
ret = dvbdmx - > stop_feed ( dvbdmxfeed ) ;
spin_lock_irq ( & dvbdmx - > lock ) ;
dvbdmxfeed - > state = DMX_STATE_READY ;
feed - > is_filtering = 0 ;
spin_unlock_irq ( & dvbdmx - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static int dmx_section_feed_release_filter ( struct dmx_section_feed * feed ,
2005-09-10 00:02:26 +04:00
struct dmx_section_filter * filter )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_filter * dvbdmxfilter = ( struct dvb_demux_filter * ) filter , * f ;
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
2005-04-17 02:20:36 +04:00
struct dvb_demux * dvbdmx = dvbdmxfeed - > demux ;
2007-03-10 12:21:25 +03:00
mutex_lock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( dvbdmxfilter - > feed ! = dvbdmxfeed ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
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 {
2005-09-10 00:02:26 +04:00
while ( f - > next ! = dvbdmxfilter )
2005-04-17 02:20:36 +04:00
f = f - > next ;
f - > next = f - > next - > next ;
}
dvbdmxfilter - > state = DMX_STATE_FREE ;
spin_unlock_irq ( & dvbdmx - > lock ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int dvbdmx_allocate_section_feed ( struct dmx_demux * demux ,
struct dmx_section_feed * * feed ,
dmx_section_cb callback )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdmx = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
struct dvb_demux_feed * dvbdmxfeed ;
2006-02-07 11:49:14 +03:00
if ( mutex_lock_interruptible ( & dvbdmx - > mutex ) )
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
if ( ! ( dvbdmxfeed = dvb_dmx_feed_alloc ( dvbdmx ) ) ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
dvbdmxfeed - > type = DMX_TYPE_SEC ;
dvbdmxfeed - > cb . sec = callback ;
dvbdmxfeed - > demux = dvbdmx ;
dvbdmxfeed - > pid = 0xffff ;
dvbdmxfeed - > feed . sec . secbuf = dvbdmxfeed - > feed . sec . secbuf_base ;
dvbdmxfeed - > feed . sec . secbufp = dvbdmxfeed - > feed . sec . seclen = 0 ;
dvbdmxfeed - > feed . sec . tsfeedp = 0 ;
dvbdmxfeed - > filter = NULL ;
dvbdmxfeed - > buffer = NULL ;
2005-09-10 00:02:26 +04:00
( * feed ) = & dvbdmxfeed - > feed . sec ;
2005-04-17 02:20:36 +04:00
( * feed ) - > is_filtering = 0 ;
( * feed ) - > parent = demux ;
( * feed ) - > priv = NULL ;
( * feed ) - > set = dmx_section_feed_set ;
( * feed ) - > allocate_filter = dmx_section_feed_allocate_filter ;
( * feed ) - > start_filtering = dmx_section_feed_start_filtering ;
( * feed ) - > stop_filtering = dmx_section_feed_stop_filtering ;
( * feed ) - > release_filter = dmx_section_feed_release_filter ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int dvbdmx_release_section_feed ( struct dmx_demux * demux ,
struct dmx_section_feed * feed )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux_feed * dvbdmxfeed = ( struct dvb_demux_feed * ) feed ;
struct dvb_demux * dvbdmx = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
2007-03-10 12:21:25 +03:00
mutex_lock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:02:26 +04:00
if ( dvbdmxfeed - > state = = DMX_STATE_FREE ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
# ifndef NOBUFS
vfree ( dvbdmxfeed - > buffer ) ;
2005-09-10 00:02:26 +04:00
dvbdmxfeed - > buffer = NULL ;
2005-04-17 02:20:36 +04:00
# endif
2005-09-10 00:02:26 +04:00
dvbdmxfeed - > state = DMX_STATE_FREE ;
2005-04-17 02:20:36 +04:00
dvb_demux_feed_del ( dvbdmxfeed ) ;
dvbdmxfeed - > pid = 0xffff ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdmx - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/******************************************************************************
* dvb_demux kernel data API calls
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int dvbdmx_open ( struct dmx_demux * demux )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
if ( dvbdemux - > users > = MAX_DVB_DEMUX_USERS )
return - EUSERS ;
dvbdemux - > users + + ;
return 0 ;
}
static int dvbdmx_close ( struct dmx_demux * demux )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
if ( dvbdemux - > users = = 0 )
return - ENODEV ;
dvbdemux - > users - - ;
//FIXME: release any unneeded resources if users==0
return 0 ;
}
2008-06-22 21:20:19 +04:00
static int dvbdmx_write ( struct dmx_demux * demux , const char __user * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2008-06-22 21:20:19 +04:00
void * p ;
2005-04-17 02:20:36 +04:00
if ( ( ! demux - > frontend ) | | ( demux - > frontend - > source ! = DMX_MEMORY_FE ) )
return - EINVAL ;
2010-05-22 12:21:02 +04:00
p = memdup_user ( buf , count ) ;
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
2008-06-22 21:20:19 +04:00
if ( mutex_lock_interruptible ( & dvbdemux - > mutex ) ) {
kfree ( p ) ;
2005-04-17 02:20:36 +04:00
return - ERESTARTSYS ;
2008-06-22 21:20:19 +04:00
}
dvb_dmx_swfilter ( dvbdemux , p , count ) ;
kfree ( p ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) )
return - EINTR ;
return count ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_add_frontend ( struct dmx_demux * demux ,
struct dmx_frontend * frontend )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
struct list_head * head = & dvbdemux - > frontend_list ;
list_add ( & ( frontend - > connectivity_list ) , head ) ;
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_remove_frontend ( struct dmx_demux * demux ,
struct dmx_frontend * frontend )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
struct list_head * pos , * n , * head = & dvbdemux - > frontend_list ;
2005-09-10 00:02:26 +04:00
list_for_each_safe ( pos , n , head ) {
2005-04-17 02:20:36 +04:00
if ( DMX_FE_ENTRY ( pos ) = = frontend ) {
list_del ( pos ) ;
return 0 ;
}
}
return - ENODEV ;
}
2005-09-10 00:02:26 +04:00
static struct list_head * dvbdmx_get_frontends ( struct dmx_demux * demux )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
if ( list_empty ( & dvbdemux - > frontend_list ) )
return NULL ;
2005-09-10 00:02:26 +04:00
2005-04-17 02:20:36 +04:00
return & dvbdemux - > frontend_list ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_connect_frontend ( struct dmx_demux * demux ,
struct dmx_frontend * frontend )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
if ( demux - > frontend )
return - EINVAL ;
2007-03-10 12:21:25 +03:00
mutex_lock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
demux - > frontend = frontend ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int dvbdmx_disconnect_frontend ( struct dmx_demux * demux )
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
2007-03-10 12:21:25 +03:00
mutex_lock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
demux - > frontend = NULL ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-09-10 00:02:26 +04:00
static int dvbdmx_get_pes_pids ( struct dmx_demux * demux , u16 * pids )
2005-04-17 02:20:36 +04:00
{
2005-09-10 00:02:26 +04:00
struct dvb_demux * dvbdemux = ( struct dvb_demux * ) demux ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:02:26 +04:00
memcpy ( pids , dvbdemux - > pids , 5 * sizeof ( u16 ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
int dvb_dmx_init ( struct dvb_demux * dvbdemux )
{
2005-09-10 00:02:23 +04:00
int i ;
2005-04-17 02:20:36 +04:00
struct dmx_demux * dmx = & dvbdemux - > dmx ;
2009-06-06 16:31:34 +04:00
dvbdemux - > cnt_storage = NULL ;
2005-04-17 02:20:36 +04:00
dvbdemux - > users = 0 ;
2005-09-10 00:02:26 +04:00
dvbdemux - > filter = vmalloc ( dvbdemux - > filternum * sizeof ( struct dvb_demux_filter ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! dvbdemux - > filter )
return - ENOMEM ;
2005-09-10 00:02:26 +04:00
dvbdemux - > feed = vmalloc ( dvbdemux - > feednum * sizeof ( struct dvb_demux_feed ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! dvbdemux - > feed ) {
vfree ( dvbdemux - > filter ) ;
2010-02-01 16:35:22 +03:00
dvbdemux - > filter = NULL ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < dvbdemux - > filternum ; i + + ) {
2005-04-17 02:20:36 +04:00
dvbdemux - > filter [ i ] . state = DMX_STATE_FREE ;
dvbdemux - > filter [ i ] . index = i ;
}
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < dvbdemux - > feednum ; i + + ) {
2005-04-17 02:20:36 +04:00
dvbdemux - > feed [ i ] . state = DMX_STATE_FREE ;
dvbdemux - > feed [ i ] . index = i ;
}
2005-09-10 00:02:26 +04:00
2010-03-06 20:58:01 +03:00
dvbdemux - > cnt_storage = vmalloc ( MAX_PID + 1 ) ;
if ( ! dvbdemux - > cnt_storage )
printk ( KERN_WARNING " Couldn't allocate memory for TS/TEI check. Disabling it \n " ) ;
2010-02-01 17:50:42 +03:00
2005-09-10 00:02:26 +04:00
INIT_LIST_HEAD ( & dvbdemux - > frontend_list ) ;
2005-09-10 00:02:26 +04:00
for ( i = 0 ; i < DMX_TS_PES_OTHER ; i + + ) {
2005-04-17 02:20:36 +04:00
dvbdemux - > pesfilter [ i ] = NULL ;
dvbdemux - > pids [ i ] = 0xffff ;
}
INIT_LIST_HEAD ( & dvbdemux - > feed_list ) ;
dvbdemux - > playing = 0 ;
dvbdemux - > recording = 0 ;
dvbdemux - > tsbufp = 0 ;
if ( ! dvbdemux - > check_crc32 )
dvbdemux - > check_crc32 = dvb_dmx_crc32 ;
2005-09-10 00:02:26 +04:00
if ( ! dvbdemux - > memcopy )
dvbdemux - > memcopy = dvb_dmx_memcopy ;
2005-04-17 02:20:36 +04:00
dmx - > frontend = NULL ;
2005-09-10 00:02:26 +04:00
dmx - > priv = dvbdemux ;
2005-04-17 02:20:36 +04:00
dmx - > open = dvbdmx_open ;
dmx - > close = dvbdmx_close ;
dmx - > write = dvbdmx_write ;
dmx - > allocate_ts_feed = dvbdmx_allocate_ts_feed ;
dmx - > release_ts_feed = dvbdmx_release_ts_feed ;
dmx - > allocate_section_feed = dvbdmx_allocate_section_feed ;
dmx - > release_section_feed = dvbdmx_release_section_feed ;
dmx - > add_frontend = dvbdmx_add_frontend ;
dmx - > remove_frontend = dvbdmx_remove_frontend ;
dmx - > get_frontends = dvbdmx_get_frontends ;
dmx - > connect_frontend = dvbdmx_connect_frontend ;
dmx - > disconnect_frontend = dvbdmx_disconnect_frontend ;
dmx - > get_pes_pids = dvbdmx_get_pes_pids ;
2006-02-07 11:49:14 +03:00
mutex_init ( & dvbdemux - > mutex ) ;
2005-04-17 02:20:36 +04:00
spin_lock_init ( & dvbdemux - > lock ) ;
return 0 ;
}
2005-09-10 00:02:26 +04:00
EXPORT_SYMBOL ( dvb_dmx_init ) ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:02:23 +04:00
void dvb_dmx_release ( struct dvb_demux * dvbdemux )
2005-04-17 02:20:36 +04:00
{
2009-06-06 16:31:34 +04:00
vfree ( dvbdemux - > cnt_storage ) ;
2005-04-17 02:20:36 +04:00
vfree ( dvbdemux - > filter ) ;
vfree ( dvbdemux - > feed ) ;
}
2005-09-10 00:02:26 +04:00
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( dvb_dmx_release ) ;