2005-04-17 02:20:36 +04:00
/*
* dvb_net . c
*
* Copyright ( C ) 2001 Convergence integrated media GmbH
* Ralph Metzler < ralph @ convergence . de >
* Copyright ( C ) 2002 Ralph Metzler < rjkm @ metzlerbros . de >
*
* ULE Decapsulation code :
* Copyright ( C ) 2003 , 2004 gcs - Global Communication & Services GmbH .
* and Department of Scientific Computing
* Paris Lodron University of Salzburg .
* Hilmar Linder < hlinder @ cosy . sbg . ac . at >
* and Wolfram Stering < wstering @ cosy . sbg . ac . at >
*
* ULE Decaps according to draft - ietf - ipdvb - ule - 03. txt .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
* Or , point your browser to http : //www.gnu.org/copyleft/gpl.html
*/
/*
* ULE ChangeLog :
* Feb 2004 : hl / ws v1 : Implementing draft - fair - ipdvb - ule - 01. txt
*
* Dec 2004 : hl / ws v2 : Implementing draft - ietf - ipdvb - ule - 03. txt :
* ULE Extension header handling .
* Bugreports by Moritz Vieth and Hanno Tersteegen ,
* Fraunhofer Institute for Open Communication Systems
* Competence Center for Advanced Satellite Communications .
* Bugfixes and robustness improvements .
* Filtering on dest MAC addresses , if present ( D - Bit = 0 )
* ULE_DEBUG compile - time option .
*/
/*
* FIXME / TODO ( dvb_net . c ) :
*
* Unloading does not work for 2.6 .9 kernels : a refcount doesn ' t go to zero .
*
* TS_FEED callback is called once for every single TS cell although it is
* registered ( in dvb_net_feed_start ( ) ) for 100 TS cells ( used for dvb_net_ule ( ) ) .
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/dvb/net.h>
# include <linux/uio.h>
# include <asm/uaccess.h>
# include <linux/crc32.h>
# include <linux/version.h>
# include "dvb_demux.h"
# include "dvb_net.h"
static int dvb_net_debug ;
module_param ( dvb_net_debug , int , 0444 ) ;
MODULE_PARM_DESC ( dvb_net_debug , " enable debug messages " ) ;
# define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)
static inline __u32 iov_crc32 ( __u32 c , struct kvec * iov , unsigned int cnt )
{
unsigned int j ;
for ( j = 0 ; j < cnt ; j + + )
c = crc32_be ( c , iov [ j ] . iov_base , iov [ j ] . iov_len ) ;
return c ;
}
# define DVB_NET_MULTICAST_MAX 10
# undef ULE_DEBUG
# ifdef ULE_DEBUG
# define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
static void hexdump ( const unsigned char * buf , unsigned short len )
{
char str [ 80 ] , octet [ 10 ] ;
int ofs , i , l ;
for ( ofs = 0 ; ofs < len ; ofs + = 16 ) {
sprintf ( str , " %03d: " , ofs ) ;
for ( i = 0 ; i < 16 ; i + + ) {
if ( ( i + ofs ) < len )
sprintf ( octet , " %02x " , buf [ ofs + i ] ) ;
else
strcpy ( octet , " " ) ;
strcat ( str , octet ) ;
}
strcat ( str , " " ) ;
l = strlen ( str ) ;
for ( i = 0 ; ( i < 16 ) & & ( ( i + ofs ) < len ) ; i + + )
str [ l + + ] = isprint ( buf [ ofs + i ] ) ? buf [ ofs + i ] : ' . ' ;
str [ l ] = ' \0 ' ;
printk ( KERN_WARNING " %s \n " , str ) ;
}
}
# endif
struct dvb_net_priv {
int in_use ;
struct net_device_stats stats ;
u16 pid ;
struct dvb_net * host ;
struct dmx_demux * demux ;
struct dmx_section_feed * secfeed ;
struct dmx_section_filter * secfilter ;
struct dmx_ts_feed * tsfeed ;
int multi_num ;
struct dmx_section_filter * multi_secfilter [ DVB_NET_MULTICAST_MAX ] ;
unsigned char multi_macs [ DVB_NET_MULTICAST_MAX ] [ 6 ] ;
int rx_mode ;
# define RX_MODE_UNI 0
# define RX_MODE_MULTI 1
# define RX_MODE_ALL_MULTI 2
# define RX_MODE_PROMISC 3
struct work_struct set_multicast_list_wq ;
struct work_struct restart_net_feed_wq ;
unsigned char feedtype ; /* Either FEED_TYPE_ or FEED_TYPE_ULE */
int need_pusi ; /* Set to 1, if synchronization on PUSI required. */
unsigned char tscc ; /* TS continuity counter after sync on PUSI. */
struct sk_buff * ule_skb ; /* ULE SNDU decodes into this buffer. */
unsigned char * ule_next_hdr ; /* Pointer into skb to next ULE extension header. */
unsigned short ule_sndu_len ; /* ULE SNDU length in bytes, w/o D-Bit. */
unsigned short ule_sndu_type ; /* ULE SNDU type field, complete. */
unsigned char ule_sndu_type_1 ; /* ULE SNDU type field, if split across 2 TS cells. */
unsigned char ule_dbit ; /* Whether the DestMAC address present
* or not ( bit is set ) . */
unsigned char ule_bridged ; /* Whether the ULE_BRIDGED extension header was found. */
int ule_sndu_remain ; /* Nr. of bytes still required for current ULE SNDU. */
unsigned long ts_count ; /* Current ts cell counter. */
} ;
/**
* Determine the packet ' s protocol ID . The rule here is that we
* assume 802.3 if the type field is short enough to be a length .
* This is normal practice and works for any ' now in use ' protocol .
*
* stolen from eth . c out of the linux kernel , hacked for dvb - device
* by Michael Holzt < kju @ debian . org >
*/
static unsigned short dvb_net_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 ) ;
# if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
eth = skb - > mac . ethernet ;
# else
eth = eth_hdr ( skb ) ;
# endif
if ( * eth - > h_dest & 1 ) {
if ( memcmp ( eth - > h_dest , dev - > broadcast , ETH_ALEN ) = = 0 )
skb - > pkt_type = PACKET_BROADCAST ;
else
skb - > pkt_type = PACKET_MULTICAST ;
}
if ( ntohs ( eth - > h_proto ) > = 1536 )
return eth - > h_proto ;
rawp = skb - > data ;
/**
* This is a magic hack to spot IPX packets . Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer . We look for FFFF which isn ' t a used 802.2 SSAP / DSAP . This
* won ' t work for fault tolerant netware but does for the rest .
*/
if ( * ( unsigned short * ) rawp = = 0xFFFF )
return htons ( ETH_P_802_3 ) ;
/**
* Real 802.2 LLC
*/
return htons ( ETH_P_802_2 ) ;
}
# define TS_SZ 188
# define TS_SYNC 0x47
# define TS_TEI 0x80
# define TS_SC 0xC0
# define TS_PUSI 0x40
# define TS_AF_A 0x20
# define TS_AF_D 0x10
/* ULE Extension Header handlers. */
# define ULE_TEST 0
# define ULE_BRIDGED 1
static int ule_test_sndu ( struct dvb_net_priv * p )
{
return - 1 ;
}
static int ule_bridged_sndu ( struct dvb_net_priv * p )
{
/* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt.
* This has to be the last extension header , otherwise it won ' t work .
* Blame the authors !
*/
p - > ule_bridged = 1 ;
return 0 ;
}
/** Handle ULE extension headers.
* Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding .
* Returns : > = 0 : nr . of bytes consumed by next extension header
* - 1 : Mandatory extension header that is not recognized or TEST SNDU ; discard .
*/
static int handle_one_ule_extension ( struct dvb_net_priv * p )
{
/* Table of mandatory extension header handlers. The header type is the index. */
static int ( * ule_mandatory_ext_handlers [ 255 ] ) ( struct dvb_net_priv * p ) =
{ [ 0 ] = ule_test_sndu , [ 1 ] = ule_bridged_sndu , [ 2 ] = NULL , } ;
/* Table of optional extension header handlers. The header type is the index. */
static int ( * ule_optional_ext_handlers [ 255 ] ) ( struct dvb_net_priv * p ) = { NULL , } ;
int ext_len = 0 ;
unsigned char hlen = ( p - > ule_sndu_type & 0x0700 ) > > 8 ;
unsigned char htype = p - > ule_sndu_type & 0x00FF ;
/* Discriminate mandatory and optional extension headers. */
if ( hlen = = 0 ) {
/* Mandatory extension header */
if ( ule_mandatory_ext_handlers [ htype ] ) {
ext_len = ule_mandatory_ext_handlers [ htype ] ( p ) ;
p - > ule_next_hdr + = ext_len ;
if ( ! p - > ule_bridged ) {
p - > ule_sndu_type = ntohs ( * ( unsigned short * ) p - > ule_next_hdr ) ;
p - > ule_next_hdr + = 2 ;
} else {
p - > ule_sndu_type = ntohs ( * ( unsigned short * ) ( p - > ule_next_hdr + ( ( p - > ule_dbit ? 2 : 3 ) * ETH_ALEN ) ) ) ;
/* This assures the extension handling loop will terminate. */
}
} else
ext_len = - 1 ; /* SNDU has to be discarded. */
} else {
/* Optional extension header. Calculate the length. */
ext_len = hlen < < 2 ;
/* Process the optional extension header according to its type. */
if ( ule_optional_ext_handlers [ htype ] )
( void ) ule_optional_ext_handlers [ htype ] ( p ) ;
p - > ule_next_hdr + = ext_len ;
p - > ule_sndu_type = ntohs ( * ( unsigned short * ) p - > ule_next_hdr ) ;
p - > ule_next_hdr + = 2 ;
}
return ext_len ;
}
static int handle_ule_extensions ( struct dvb_net_priv * p )
{
int total_ext_len = 0 , l ;
p - > ule_next_hdr = p - > ule_skb - > data ;
do {
l = handle_one_ule_extension ( p ) ;
if ( l = = - 1 ) return - 1 ; /* Stop extension header processing and discard SNDU. */
total_ext_len + = l ;
} while ( p - > ule_sndu_type < 1536 ) ;
return total_ext_len ;
}
/** Prepare for a new ULE SNDU: reset the decoder state. */
static inline void reset_ule ( struct dvb_net_priv * p )
{
p - > ule_skb = NULL ;
p - > ule_next_hdr = NULL ;
p - > ule_sndu_len = 0 ;
p - > ule_sndu_type = 0 ;
p - > ule_sndu_type_1 = 0 ;
p - > ule_sndu_remain = 0 ;
p - > ule_dbit = 0xFF ;
p - > ule_bridged = 0 ;
}
/**
* Decode ULE SNDUs according to draft - ietf - ipdvb - ule - 03. txt from a sequence of
* TS cells of a single PID .
*/
static void dvb_net_ule ( struct net_device * dev , const u8 * buf , size_t buf_len )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
unsigned long skipped = 0L ;
u8 * ts , * ts_end , * from_where = NULL , ts_remain = 0 , how_much = 0 , new_ts = 1 ;
struct ethhdr * ethh = NULL ;
# ifdef ULE_DEBUG
/* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */
static unsigned char ule_hist [ 100 * TS_SZ ] ;
static unsigned char * ule_where = ule_hist , ule_dump = 0 ;
# endif
if ( dev = = NULL ) {
printk ( KERN_ERR " NO netdev struct! \n " ) ;
return ;
}
/* For all TS cells in current buffer.
* Appearently , we are called for every single TS cell .
*/
for ( ts = ( char * ) buf , ts_end = ( char * ) buf + buf_len ; ts < ts_end ; /* no default incr. */ ) {
if ( new_ts ) {
/* We are about to process a new TS cell. */
# ifdef ULE_DEBUG
if ( ule_where > = & ule_hist [ 100 * TS_SZ ] ) ule_where = ule_hist ;
memcpy ( ule_where , ts , TS_SZ ) ;
if ( ule_dump ) {
hexdump ( ule_where , TS_SZ ) ;
ule_dump = 0 ;
}
ule_where + = TS_SZ ;
# endif
/* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
if ( ( ts [ 0 ] ! = TS_SYNC ) | | ( ts [ 1 ] & TS_TEI ) | | ( ( ts [ 3 ] & TS_SC ) ! = 0 ) ) {
printk ( KERN_WARNING " %lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x. \n " ,
priv - > ts_count , ts [ 0 ] , ts [ 1 ] & TS_TEI > > 7 , ts [ 3 ] & 0xC0 > > 6 ) ;
/* Drop partly decoded SNDU, reset state, resync on PUSI. */
if ( priv - > ule_skb ) {
dev_kfree_skb ( priv - > ule_skb ) ;
/* Prepare for next SNDU. */
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_errors + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_frame_errors + + ;
}
reset_ule ( priv ) ;
priv - > need_pusi = 1 ;
/* Continue with next TS cell. */
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
ts_remain = 184 ;
from_where = ts + 4 ;
}
/* Synchronize on PUSI, if required. */
if ( priv - > need_pusi ) {
if ( ts [ 1 ] & TS_PUSI ) {
/* Find beginning of first ULE SNDU in current TS cell. */
/* Synchronize continuity counter. */
priv - > tscc = ts [ 3 ] & 0x0F ;
/* There is a pointer field here. */
if ( ts [ 4 ] > ts_remain ) {
printk ( KERN_ERR " %lu: Invalid ULE packet "
" (pointer field %d) \n " , priv - > ts_count , ts [ 4 ] ) ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
/* Skip to destination of pointer field. */
from_where = & ts [ 5 ] + ts [ 4 ] ;
ts_remain - = 1 + ts [ 4 ] ;
skipped = 0 ;
} else {
skipped + + ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
}
/* Check continuity counter. */
if ( new_ts ) {
if ( ( ts [ 3 ] & 0x0F ) = = priv - > tscc )
priv - > tscc = ( priv - > tscc + 1 ) & 0x0F ;
else {
/* TS discontinuity handling: */
printk ( KERN_WARNING " %lu: TS discontinuity: got %#x, "
" exptected %#x. \n " , priv - > ts_count , ts [ 3 ] & 0x0F , priv - > tscc ) ;
/* Drop partly decoded SNDU, reset state, resync on PUSI. */
if ( priv - > ule_skb ) {
dev_kfree_skb ( priv - > ule_skb ) ;
/* Prepare for next SNDU. */
// reset_ule(priv); moved to below.
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_errors + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_frame_errors + + ;
}
reset_ule ( priv ) ;
/* skip to next PUSI. */
priv - > need_pusi = 1 ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
/* If we still have an incomplete payload, but PUSI is
* set ; some TS cells are missing .
* This is only possible here , if we missed exactly 16 TS
* cells ( continuity counter wrap ) . */
if ( ts [ 1 ] & TS_PUSI ) {
if ( ! priv - > need_pusi ) {
if ( * from_where > 181 ) {
/* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */
printk ( KERN_WARNING " %lu: Invalid pointer "
" field: %u. \n " , priv - > ts_count , * from_where ) ;
/* Drop partly decoded SNDU, reset state, resync on PUSI. */
if ( priv - > ule_skb ) {
dev_kfree_skb ( priv - > ule_skb ) ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_errors + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_frame_errors + + ;
}
reset_ule ( priv ) ;
priv - > need_pusi = 1 ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
/* Skip pointer field (we're processing a
* packed payload ) . */
from_where + = 1 ;
ts_remain - = 1 ;
} else
priv - > need_pusi = 0 ;
if ( priv - > ule_sndu_remain > 183 ) {
/* Current SNDU lacks more data than there could be available in the
* current TS cell . */
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_errors + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_length_errors + + ;
printk ( KERN_WARNING " %lu: Expected %d more SNDU bytes, but "
" got PUSI (pf %d, ts_remain %d). Flushing incomplete payload. \n " ,
priv - > ts_count , priv - > ule_sndu_remain , ts [ 4 ] , ts_remain ) ;
dev_kfree_skb ( priv - > ule_skb ) ;
/* Prepare for next SNDU. */
reset_ule ( priv ) ;
/* Resync: go to where pointer field points to: start of next ULE SNDU. */
from_where + = ts [ 4 ] ;
ts_remain - = ts [ 4 ] ;
}
}
}
/* Check if new payload needs to be started. */
if ( priv - > ule_skb = = NULL ) {
/* Start a new payload with skb.
* Find ULE header . It is only guaranteed that the
* length field ( 2 bytes ) is contained in the current
* TS .
* Check ts_remain has to be > = 2 here . */
if ( ts_remain < 2 ) {
printk ( KERN_WARNING " Invalid payload packing: only %d "
" bytes left in TS. Resyncing. \n " , ts_remain ) ;
priv - > ule_sndu_len = 0 ;
priv - > need_pusi = 1 ;
continue ;
}
if ( ! priv - > ule_sndu_len ) {
/* Got at least two bytes, thus extrace the SNDU length. */
priv - > ule_sndu_len = from_where [ 0 ] < < 8 | from_where [ 1 ] ;
if ( priv - > ule_sndu_len & 0x8000 ) {
/* D-Bit is set: no dest mac present. */
priv - > ule_sndu_len & = 0x7FFF ;
priv - > ule_dbit = 1 ;
} else
priv - > ule_dbit = 0 ;
if ( priv - > ule_sndu_len > 32763 ) {
printk ( KERN_WARNING " %lu: Invalid ULE SNDU length %u. "
" Resyncing. \n " , priv - > ts_count , priv - > ule_sndu_len ) ;
priv - > ule_sndu_len = 0 ;
priv - > need_pusi = 1 ;
new_ts = 1 ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
}
ts_remain - = 2 ; /* consume the 2 bytes SNDU length. */
from_where + = 2 ;
}
/*
* State of current TS :
* ts_remain ( remaining bytes in the current TS cell )
* 0 ule_type is not available now , we need the next TS cell
* 1 the first byte of the ule_type is present
* > = 2 full ULE header present , maybe some payload data as well .
*/
switch ( ts_remain ) {
case 1 :
priv - > ule_sndu_type = from_where [ 0 ] < < 8 ;
priv - > ule_sndu_type_1 = 1 ; /* first byte of ule_type is set. */
ts_remain - = 1 ; from_where + = 1 ;
/* Continue w/ next TS. */
case 0 :
new_ts = 1 ;
ts + = TS_SZ ;
priv - > ts_count + + ;
continue ;
default : /* complete ULE header is present in current TS. */
/* Extract ULE type field. */
if ( priv - > ule_sndu_type_1 ) {
priv - > ule_sndu_type | = from_where [ 0 ] ;
from_where + = 1 ; /* points to payload start. */
ts_remain - = 1 ;
} else {
/* Complete type is present in new TS. */
priv - > ule_sndu_type = from_where [ 0 ] < < 8 | from_where [ 1 ] ;
from_where + = 2 ; /* points to payload start. */
ts_remain - = 2 ;
}
break ;
}
/* Allocate the skb (decoder target buffer) with the correct size, as follows:
* prepare for the largest case : bridged SNDU with MAC address ( dbit = 0 ) . */
priv - > ule_skb = dev_alloc_skb ( priv - > ule_sndu_len + ETH_HLEN + ETH_ALEN ) ;
if ( priv - > ule_skb = = NULL ) {
printk ( KERN_NOTICE " %s: Memory squeeze, dropping packet. \n " ,
dev - > name ) ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_dropped + + ;
return ;
}
/* This includes the CRC32 _and_ dest mac, if !dbit. */
priv - > ule_sndu_remain = priv - > ule_sndu_len ;
priv - > ule_skb - > dev = dev ;
/* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */
skb_reserve ( priv - > ule_skb , ETH_HLEN + ETH_ALEN ) ;
}
/* Copy data into our current skb. */
how_much = min ( priv - > ule_sndu_remain , ( int ) ts_remain ) ;
memcpy ( skb_put ( priv - > ule_skb , how_much ) , from_where , how_much ) ;
priv - > ule_sndu_remain - = how_much ;
ts_remain - = how_much ;
from_where + = how_much ;
/* Check for complete payload. */
if ( priv - > ule_sndu_remain < = 0 ) {
/* Check CRC32, we've got it in our skb already. */
unsigned short ulen = htons ( priv - > ule_sndu_len ) ;
unsigned short utype = htons ( priv - > ule_sndu_type ) ;
struct kvec iov [ 3 ] = {
{ & ulen , sizeof ulen } ,
{ & utype , sizeof utype } ,
{ priv - > ule_skb - > data , priv - > ule_skb - > len - 4 }
} ;
unsigned long ule_crc = ~ 0L , expected_crc ;
if ( priv - > ule_dbit ) {
/* Set D-bit for CRC32 verification,
* if it was set originally . */
ulen | = 0x0080 ;
}
ule_crc = iov_crc32 ( ule_crc , iov , 3 ) ;
expected_crc = * ( ( u8 * ) priv - > ule_skb - > tail - 4 ) < < 24 |
* ( ( u8 * ) priv - > ule_skb - > tail - 3 ) < < 16 |
* ( ( u8 * ) priv - > ule_skb - > tail - 2 ) < < 8 |
* ( ( u8 * ) priv - > ule_skb - > tail - 1 ) ;
if ( ule_crc ! = expected_crc ) {
printk ( KERN_WARNING " %lu: CRC32 check FAILED: %#lx / %#lx, SNDU len %d type %#x, ts_remain %d, next 2: %x. \n " ,
priv - > ts_count , ule_crc , expected_crc , priv - > ule_sndu_len , priv - > ule_sndu_type , ts_remain , ts_remain > 2 ? * ( unsigned short * ) from_where : 0 ) ;
# ifdef ULE_DEBUG
hexdump ( iov [ 0 ] . iov_base , iov [ 0 ] . iov_len ) ;
hexdump ( iov [ 1 ] . iov_base , iov [ 1 ] . iov_len ) ;
hexdump ( iov [ 2 ] . iov_base , iov [ 2 ] . iov_len ) ;
if ( ule_where = = ule_hist ) {
hexdump ( & ule_hist [ 98 * TS_SZ ] , TS_SZ ) ;
hexdump ( & ule_hist [ 99 * TS_SZ ] , TS_SZ ) ;
} else if ( ule_where = = & ule_hist [ TS_SZ ] ) {
hexdump ( & ule_hist [ 99 * TS_SZ ] , TS_SZ ) ;
hexdump ( ule_hist , TS_SZ ) ;
} else {
hexdump ( ule_where - TS_SZ - TS_SZ , TS_SZ ) ;
hexdump ( ule_where - TS_SZ , TS_SZ ) ;
}
ule_dump = 1 ;
# endif
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_errors + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_crc_errors + + ;
dev_kfree_skb ( priv - > ule_skb ) ;
} else {
/* CRC32 verified OK. */
/* Handle ULE Extension Headers. */
if ( priv - > ule_sndu_type < 1536 ) {
/* There is an extension header. Handle it accordingly. */
int l = handle_ule_extensions ( priv ) ;
if ( l < 0 ) {
/* Mandatory extension header unknown or TEST SNDU. Drop it. */
// printk( KERN_WARNING "Dropping SNDU, extension headers.\n" );
dev_kfree_skb ( priv - > ule_skb ) ;
goto sndu_done ;
}
skb_pull ( priv - > ule_skb , l ) ;
}
/* CRC32 was OK. Remove it from skb. */
priv - > ule_skb - > tail - = 4 ;
priv - > ule_skb - > len - = 4 ;
/* Filter on receiver's destination MAC address, if present. */
if ( ! priv - > ule_dbit ) {
/* The destination MAC address is the next data in the skb. */
if ( memcmp ( priv - > ule_skb - > data , dev - > dev_addr , ETH_ALEN ) ) {
/* MAC addresses don't match. Drop SNDU. */
// printk( KERN_WARNING "Dropping SNDU, MAC address.\n" );
dev_kfree_skb ( priv - > ule_skb ) ;
goto sndu_done ;
}
if ( ! priv - > ule_bridged ) {
skb_push ( priv - > ule_skb , ETH_ALEN + 2 ) ;
ethh = ( struct ethhdr * ) priv - > ule_skb - > data ;
memcpy ( ethh - > h_dest , ethh - > h_source , ETH_ALEN ) ;
memset ( ethh - > h_source , 0 , ETH_ALEN ) ;
ethh - > h_proto = htons ( priv - > ule_sndu_type ) ;
} else {
/* Skip the Receiver destination MAC address. */
skb_pull ( priv - > ule_skb , ETH_ALEN ) ;
}
} else {
if ( ! priv - > ule_bridged ) {
skb_push ( priv - > ule_skb , ETH_HLEN ) ;
ethh = ( struct ethhdr * ) priv - > ule_skb - > data ;
memcpy ( ethh - > h_dest , dev - > dev_addr , ETH_ALEN ) ;
memset ( ethh - > h_source , 0 , ETH_ALEN ) ;
ethh - > h_proto = htons ( priv - > ule_sndu_type ) ;
} else {
/* skb is in correct state; nothing to do. */
}
}
priv - > ule_bridged = 0 ;
/* Stuff into kernel's protocol stack. */
priv - > ule_skb - > protocol = dvb_net_eth_type_trans ( priv - > ule_skb , dev ) ;
/* If D-bit is set (i.e. destination MAC address not present),
* receive the packet anyhow . */
/* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
priv - > ule_skb - > pkt_type = PACKET_HOST ; */
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_packets + + ;
( ( struct dvb_net_priv * ) dev - > priv ) - > stats . rx_bytes + = priv - > ule_skb - > len ;
netif_rx ( priv - > ule_skb ) ;
}
sndu_done :
/* Prepare for next SNDU. */
reset_ule ( priv ) ;
}
/* More data in current TS (look at the bytes following the CRC32)? */
if ( ts_remain > = 2 & & * ( ( unsigned short * ) from_where ) ! = 0xFFFF ) {
/* Next ULE SNDU starts right there. */
new_ts = 0 ;
priv - > ule_skb = NULL ;
priv - > ule_sndu_type_1 = 0 ;
priv - > ule_sndu_len = 0 ;
// printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n",
// *(from_where + 0), *(from_where + 1),
// *(from_where + 2), *(from_where + 3));
// printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0);
// hexdump(ts, 188);
} else {
new_ts = 1 ;
ts + = TS_SZ ;
priv - > ts_count + + ;
if ( priv - > ule_skb = = NULL ) {
priv - > need_pusi = 1 ;
priv - > ule_sndu_type_1 = 0 ;
priv - > ule_sndu_len = 0 ;
}
}
} /* for all available TS cells */
}
static int dvb_net_ts_callback ( const u8 * buffer1 , size_t buffer1_len ,
const u8 * buffer2 , size_t buffer2_len ,
struct dmx_ts_feed * feed , enum dmx_success success )
{
2005-05-17 08:54:24 +04:00
struct net_device * dev = feed - > priv ;
2005-04-17 02:20:36 +04:00
if ( buffer2 ! = 0 )
printk ( KERN_WARNING " buffer2 not 0: %p. \n " , buffer2 ) ;
if ( buffer1_len > 32768 )
printk ( KERN_WARNING " length > 32k: %zu. \n " , buffer1_len ) ;
/* printk("TS callback: %u bytes, %u TS cells @ %p.\n",
buffer1_len , buffer1_len / TS_SZ , buffer1 ) ; */
dvb_net_ule ( dev , buffer1 , buffer1_len ) ;
return 0 ;
}
static void dvb_net_sec ( struct net_device * dev , u8 * pkt , int pkt_len )
{
u8 * eth ;
struct sk_buff * skb ;
struct net_device_stats * stats = & ( ( ( struct dvb_net_priv * ) dev - > priv ) - > stats ) ;
2005-05-17 08:54:25 +04:00
int snap = 0 ;
2005-04-17 02:20:36 +04:00
/* note: pkt_len includes a 32bit checksum */
if ( pkt_len < 16 ) {
printk ( " %s: IP/MPE packet length = %d too small. \n " ,
dev - > name , pkt_len ) ;
stats - > rx_errors + + ;
stats - > rx_length_errors + + ;
return ;
}
/* it seems some ISPs manage to screw up here, so we have to
* relax the error checks . . . */
#if 0
if ( ( pkt [ 5 ] & 0xfd ) ! = 0xc1 ) {
/* drop scrambled or broken packets */
# else
if ( ( pkt [ 5 ] & 0x3c ) ! = 0x00 ) {
/* drop scrambled */
# endif
stats - > rx_errors + + ;
stats - > rx_crc_errors + + ;
return ;
}
if ( pkt [ 5 ] & 0x02 ) {
2005-05-17 08:54:25 +04:00
/* handle LLC/SNAP, see rfc-1042 */
if ( pkt_len < 24 | | memcmp ( & pkt [ 12 ] , " \xaa \xaa \x03 \0 \0 \0 " , 6 ) ) {
stats - > rx_dropped + + ;
return ;
}
snap = 8 ;
2005-04-17 02:20:36 +04:00
}
if ( pkt [ 7 ] ) {
/* FIXME: assemble datagram from multiple sections */
stats - > rx_errors + + ;
stats - > rx_frame_errors + + ;
return ;
}
/* we have 14 byte ethernet header (ip header follows);
2005-05-17 08:54:25 +04:00
* 12 byte MPE header ; 4 byte checksum ; + 2 byte alignment , 8 byte LLC / SNAP
2005-04-17 02:20:36 +04:00
*/
2005-05-17 08:54:25 +04:00
if ( ! ( skb = dev_alloc_skb ( pkt_len - 4 - 12 + 14 + 2 - snap ) ) ) {
2005-04-17 02:20:36 +04:00
//printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
stats - > rx_dropped + + ;
return ;
}
skb_reserve ( skb , 2 ) ; /* longword align L3 header */
skb - > dev = dev ;
/* copy L3 payload */
2005-05-17 08:54:25 +04:00
eth = ( u8 * ) skb_put ( skb , pkt_len - 12 - 4 + 14 - snap ) ;
memcpy ( eth + 14 , pkt + 12 + snap , pkt_len - 12 - 4 - snap ) ;
2005-04-17 02:20:36 +04:00
/* create ethernet header: */
eth [ 0 ] = pkt [ 0x0b ] ;
eth [ 1 ] = pkt [ 0x0a ] ;
eth [ 2 ] = pkt [ 0x09 ] ;
eth [ 3 ] = pkt [ 0x08 ] ;
eth [ 4 ] = pkt [ 0x04 ] ;
eth [ 5 ] = pkt [ 0x03 ] ;
eth [ 6 ] = eth [ 7 ] = eth [ 8 ] = eth [ 9 ] = eth [ 10 ] = eth [ 11 ] = 0 ;
2005-05-17 08:54:25 +04:00
if ( snap ) {
eth [ 12 ] = pkt [ 18 ] ;
eth [ 13 ] = pkt [ 19 ] ;
} else {
/* protocol numbers are from rfc-1700 or
* http : //www.iana.org/assignments/ethernet-numbers
*/
if ( pkt [ 12 ] > > 4 = = 6 ) { /* version field from IP header */
eth [ 12 ] = 0x86 ; /* IPv6 */
eth [ 13 ] = 0xdd ;
} else {
eth [ 12 ] = 0x08 ; /* IPv4 */
eth [ 13 ] = 0x00 ;
}
}
2005-04-17 02:20:36 +04:00
skb - > protocol = dvb_net_eth_type_trans ( skb , dev ) ;
stats - > rx_packets + + ;
stats - > rx_bytes + = skb - > len ;
netif_rx ( skb ) ;
}
static int dvb_net_sec_callback ( const u8 * buffer1 , size_t buffer1_len ,
const u8 * buffer2 , size_t buffer2_len ,
struct dmx_section_filter * filter ,
enum dmx_success success )
{
2005-05-17 08:54:24 +04:00
struct net_device * dev = filter - > priv ;
2005-04-17 02:20:36 +04:00
/**
* we rely on the DVB API definition where exactly one complete
* section is delivered in buffer1
*/
dvb_net_sec ( dev , ( u8 * ) buffer1 , buffer1_len ) ;
return 0 ;
}
static int dvb_net_tx ( struct sk_buff * skb , struct net_device * dev )
{
dev_kfree_skb ( skb ) ;
return 0 ;
}
static u8 mask_normal [ 6 ] = { 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ;
static u8 mask_allmulti [ 6 ] = { 0xff , 0xff , 0xff , 0x00 , 0x00 , 0x00 } ;
static u8 mac_allmulti [ 6 ] = { 0x01 , 0x00 , 0x5e , 0x00 , 0x00 , 0x00 } ;
static u8 mask_promisc [ 6 ] = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } ;
static int dvb_net_filter_sec_set ( struct net_device * dev ,
struct dmx_section_filter * * secfilter ,
u8 * mac , u8 * mac_mask )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
int ret ;
* secfilter = NULL ;
ret = priv - > secfeed - > allocate_filter ( priv - > secfeed , secfilter ) ;
if ( ret < 0 ) {
printk ( " %s: could not get filter \n " , dev - > name ) ;
return ret ;
}
( * secfilter ) - > priv = ( void * ) dev ;
memset ( ( * secfilter ) - > filter_value , 0x00 , DMX_MAX_FILTER_SIZE ) ;
memset ( ( * secfilter ) - > filter_mask , 0x00 , DMX_MAX_FILTER_SIZE ) ;
memset ( ( * secfilter ) - > filter_mode , 0xff , DMX_MAX_FILTER_SIZE ) ;
( * secfilter ) - > filter_value [ 0 ] = 0x3e ;
( * secfilter ) - > filter_value [ 3 ] = mac [ 5 ] ;
( * secfilter ) - > filter_value [ 4 ] = mac [ 4 ] ;
( * secfilter ) - > filter_value [ 8 ] = mac [ 3 ] ;
( * secfilter ) - > filter_value [ 9 ] = mac [ 2 ] ;
( * secfilter ) - > filter_value [ 10 ] = mac [ 1 ] ;
( * secfilter ) - > filter_value [ 11 ] = mac [ 0 ] ;
( * secfilter ) - > filter_mask [ 0 ] = 0xff ;
( * secfilter ) - > filter_mask [ 3 ] = mac_mask [ 5 ] ;
( * secfilter ) - > filter_mask [ 4 ] = mac_mask [ 4 ] ;
( * secfilter ) - > filter_mask [ 8 ] = mac_mask [ 3 ] ;
( * secfilter ) - > filter_mask [ 9 ] = mac_mask [ 2 ] ;
( * secfilter ) - > filter_mask [ 10 ] = mac_mask [ 1 ] ;
( * secfilter ) - > filter_mask [ 11 ] = mac_mask [ 0 ] ;
dprintk ( " %s: filter mac=%02x %02x %02x %02x %02x %02x \n " ,
dev - > name , mac [ 0 ] , mac [ 1 ] , mac [ 2 ] , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ) ;
dprintk ( " %s: filter mask=%02x %02x %02x %02x %02x %02x \n " ,
dev - > name , mac_mask [ 0 ] , mac_mask [ 1 ] , mac_mask [ 2 ] ,
mac_mask [ 3 ] , mac_mask [ 4 ] , mac_mask [ 5 ] ) ;
return 0 ;
}
static int dvb_net_feed_start ( struct net_device * dev )
{
int ret , i ;
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
struct dmx_demux * demux = priv - > demux ;
unsigned char * mac = ( unsigned char * ) dev - > dev_addr ;
dprintk ( " %s: rx_mode %i \n " , __FUNCTION__ , priv - > rx_mode ) ;
if ( priv - > tsfeed | | priv - > secfeed | | priv - > secfilter | | priv - > multi_secfilter [ 0 ] )
printk ( " %s: BUG %d \n " , __FUNCTION__ , __LINE__ ) ;
priv - > secfeed = NULL ;
priv - > secfilter = NULL ;
priv - > tsfeed = NULL ;
if ( priv - > feedtype = = DVB_NET_FEEDTYPE_MPE ) {
dprintk ( " %s: alloc secfeed \n " , __FUNCTION__ ) ;
ret = demux - > allocate_section_feed ( demux , & priv - > secfeed ,
dvb_net_sec_callback ) ;
if ( ret < 0 ) {
printk ( " %s: could not allocate section feed \n " , dev - > name ) ;
return ret ;
}
ret = priv - > secfeed - > set ( priv - > secfeed , priv - > pid , 32768 , 0 , 1 ) ;
if ( ret < 0 ) {
printk ( " %s: could not set section feed \n " , dev - > name ) ;
priv - > demux - > release_section_feed ( priv - > demux , priv - > secfeed ) ;
priv - > secfeed = NULL ;
return ret ;
}
if ( priv - > rx_mode ! = RX_MODE_PROMISC ) {
dprintk ( " %s: set secfilter \n " , __FUNCTION__ ) ;
dvb_net_filter_sec_set ( dev , & priv - > secfilter , mac , mask_normal ) ;
}
switch ( priv - > rx_mode ) {
case RX_MODE_MULTI :
for ( i = 0 ; i < priv - > multi_num ; i + + ) {
dprintk ( " %s: set multi_secfilter[%d] \n " , __FUNCTION__ , i ) ;
dvb_net_filter_sec_set ( dev , & priv - > multi_secfilter [ i ] ,
priv - > multi_macs [ i ] , mask_normal ) ;
}
break ;
case RX_MODE_ALL_MULTI :
priv - > multi_num = 1 ;
dprintk ( " %s: set multi_secfilter[0] \n " , __FUNCTION__ ) ;
dvb_net_filter_sec_set ( dev , & priv - > multi_secfilter [ 0 ] ,
mac_allmulti , mask_allmulti ) ;
break ;
case RX_MODE_PROMISC :
priv - > multi_num = 0 ;
dprintk ( " %s: set secfilter \n " , __FUNCTION__ ) ;
dvb_net_filter_sec_set ( dev , & priv - > secfilter , mac , mask_promisc ) ;
break ;
}
dprintk ( " %s: start filtering \n " , __FUNCTION__ ) ;
priv - > secfeed - > start_filtering ( priv - > secfeed ) ;
} else if ( priv - > feedtype = = DVB_NET_FEEDTYPE_ULE ) {
struct timespec timeout = { 0 , 30000000 } ; // 30 msec
/* we have payloads encapsulated in TS */
dprintk ( " %s: alloc tsfeed \n " , __FUNCTION__ ) ;
ret = demux - > allocate_ts_feed ( demux , & priv - > tsfeed , dvb_net_ts_callback ) ;
if ( ret < 0 ) {
printk ( " %s: could not allocate ts feed \n " , dev - > name ) ;
return ret ;
}
/* Set netdevice pointer for ts decaps callback. */
priv - > tsfeed - > priv = ( void * ) dev ;
ret = priv - > tsfeed - > set ( priv - > tsfeed , priv - > pid ,
TS_PACKET , DMX_TS_PES_OTHER ,
188 * 100 , /* nr. of bytes delivered per callback */
32768 , /* circular buffer size */
0 , /* descramble */
timeout ) ;
if ( ret < 0 ) {
printk ( " %s: could not set ts feed \n " , dev - > name ) ;
priv - > demux - > release_ts_feed ( priv - > demux , priv - > tsfeed ) ;
priv - > tsfeed = NULL ;
return ret ;
}
dprintk ( " %s: start filtering \n " , __FUNCTION__ ) ;
priv - > tsfeed - > start_filtering ( priv - > tsfeed ) ;
} else
return - EINVAL ;
return 0 ;
}
static int dvb_net_feed_stop ( struct net_device * dev )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
int i ;
dprintk ( " %s \n " , __FUNCTION__ ) ;
if ( priv - > feedtype = = DVB_NET_FEEDTYPE_MPE ) {
if ( priv - > secfeed ) {
if ( priv - > secfeed - > is_filtering ) {
dprintk ( " %s: stop secfeed \n " , __FUNCTION__ ) ;
priv - > secfeed - > stop_filtering ( priv - > secfeed ) ;
}
if ( priv - > secfilter ) {
dprintk ( " %s: release secfilter \n " , __FUNCTION__ ) ;
priv - > secfeed - > release_filter ( priv - > secfeed ,
priv - > secfilter ) ;
priv - > secfilter = NULL ;
}
for ( i = 0 ; i < priv - > multi_num ; i + + ) {
if ( priv - > multi_secfilter [ i ] ) {
dprintk ( " %s: release multi_filter[%d] \n " ,
__FUNCTION__ , i ) ;
priv - > secfeed - > release_filter ( priv - > secfeed ,
priv - > multi_secfilter [ i ] ) ;
priv - > multi_secfilter [ i ] = NULL ;
}
}
priv - > demux - > release_section_feed ( priv - > demux , priv - > secfeed ) ;
priv - > secfeed = NULL ;
} else
printk ( " %s: no feed to stop \n " , dev - > name ) ;
} else if ( priv - > feedtype = = DVB_NET_FEEDTYPE_ULE ) {
if ( priv - > tsfeed ) {
if ( priv - > tsfeed - > is_filtering ) {
dprintk ( " %s: stop tsfeed \n " , __FUNCTION__ ) ;
priv - > tsfeed - > stop_filtering ( priv - > tsfeed ) ;
}
priv - > demux - > release_ts_feed ( priv - > demux , priv - > tsfeed ) ;
priv - > tsfeed = NULL ;
}
else
printk ( " %s: no ts feed to stop \n " , dev - > name ) ;
} else
return - EINVAL ;
return 0 ;
}
static int dvb_set_mc_filter ( struct net_device * dev , struct dev_mc_list * mc )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
if ( priv - > multi_num = = DVB_NET_MULTICAST_MAX )
return - ENOMEM ;
memcpy ( priv - > multi_macs [ priv - > multi_num ] , mc - > dmi_addr , 6 ) ;
priv - > multi_num + + ;
return 0 ;
}
static void wq_set_multicast_list ( void * data )
{
struct net_device * dev = data ;
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
dvb_net_feed_stop ( dev ) ;
priv - > rx_mode = RX_MODE_UNI ;
if ( dev - > flags & IFF_PROMISC ) {
dprintk ( " %s: promiscuous mode \n " , dev - > name ) ;
priv - > rx_mode = RX_MODE_PROMISC ;
} else if ( ( dev - > flags & IFF_ALLMULTI ) ) {
dprintk ( " %s: allmulti mode \n " , dev - > name ) ;
priv - > rx_mode = RX_MODE_ALL_MULTI ;
} else if ( dev - > mc_count ) {
int mci ;
struct dev_mc_list * mc ;
dprintk ( " %s: set_mc_list, %d entries \n " ,
dev - > name , dev - > mc_count ) ;
priv - > rx_mode = RX_MODE_MULTI ;
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 void dvb_net_set_multicast_list ( struct net_device * dev )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
schedule_work ( & priv - > set_multicast_list_wq ) ;
}
static void wq_restart_net_feed ( void * data )
{
struct net_device * dev = data ;
if ( netif_running ( dev ) ) {
dvb_net_feed_stop ( dev ) ;
dvb_net_feed_start ( dev ) ;
}
}
static int dvb_net_set_mac ( struct net_device * dev , void * p )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
struct sockaddr * addr = p ;
memcpy ( dev - > dev_addr , addr - > sa_data , dev - > addr_len ) ;
if ( netif_running ( dev ) )
schedule_work ( & priv - > restart_net_feed_wq ) ;
return 0 ;
}
static int dvb_net_open ( struct net_device * dev )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
priv - > in_use + + ;
dvb_net_feed_start ( dev ) ;
return 0 ;
}
static int dvb_net_stop ( struct net_device * dev )
{
2005-05-17 08:54:24 +04:00
struct dvb_net_priv * priv = dev - > priv ;
2005-04-17 02:20:36 +04:00
priv - > in_use - - ;
return dvb_net_feed_stop ( dev ) ;
}
static struct net_device_stats * dvb_net_get_stats ( struct net_device * dev )
{
return & ( ( struct dvb_net_priv * ) dev - > priv ) - > stats ;
}
static void dvb_net_setup ( struct net_device * dev )
{
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_multicast_list ;
dev - > set_mac_address = dvb_net_set_mac ;
dev - > mtu = 4096 ;
dev - > mc_count = 0 ;
dev - > hard_header_cache = NULL ;
dev - > flags | = IFF_NOARP ;
}
static int get_if ( struct dvb_net * dvbnet )
{
int i ;
for ( i = 0 ; i < DVB_NET_DEVICES_MAX ; i + + )
if ( ! dvbnet - > state [ i ] )
break ;
if ( i = = DVB_NET_DEVICES_MAX )
return - 1 ;
dvbnet - > state [ i ] = 1 ;
return i ;
}
static int dvb_net_add_if ( struct dvb_net * dvbnet , u16 pid , u8 feedtype )
{
struct net_device * net ;
struct dvb_net_priv * priv ;
int result ;
int if_num ;
if ( feedtype ! = DVB_NET_FEEDTYPE_MPE & & feedtype ! = DVB_NET_FEEDTYPE_ULE )
return - EINVAL ;
if ( ( if_num = get_if ( dvbnet ) ) < 0 )
return - EINVAL ;
net = alloc_netdev ( sizeof ( struct dvb_net_priv ) , " dvb " , dvb_net_setup ) ;
if ( ! net )
return - ENOMEM ;
if ( dvbnet - > dvbdev - > id )
snprintf ( net - > name , IFNAMSIZ , " dvb%d%u%d " ,
dvbnet - > dvbdev - > adapter - > num , dvbnet - > dvbdev - > id , if_num ) ;
else
/* compatibility fix to keep dvb0_0 format */
snprintf ( net - > name , IFNAMSIZ , " dvb%d_%d " ,
dvbnet - > dvbdev - > adapter - > num , if_num ) ;
net - > addr_len = 6 ;
memcpy ( net - > dev_addr , dvbnet - > dvbdev - > adapter - > proposed_mac , 6 ) ;
dvbnet - > device [ if_num ] = net ;
priv = net - > priv ;
priv - > demux = dvbnet - > demux ;
priv - > pid = pid ;
priv - > rx_mode = RX_MODE_UNI ;
priv - > need_pusi = 1 ;
priv - > tscc = 0 ;
priv - > feedtype = feedtype ;
reset_ule ( priv ) ;
INIT_WORK ( & priv - > set_multicast_list_wq , wq_set_multicast_list , net ) ;
INIT_WORK ( & priv - > restart_net_feed_wq , wq_restart_net_feed , net ) ;
net - > base_addr = pid ;
if ( ( result = register_netdev ( net ) ) < 0 ) {
dvbnet - > device [ if_num ] = NULL ;
free_netdev ( net ) ;
return result ;
}
printk ( " dvb_net: created network interface %s \n " , net - > name ) ;
return if_num ;
}
static int dvb_net_remove_if ( struct dvb_net * dvbnet , unsigned int num )
{
struct net_device * net = dvbnet - > device [ num ] ;
struct dvb_net_priv * priv ;
if ( ! dvbnet - > state [ num ] )
return - EINVAL ;
priv = net - > priv ;
if ( priv - > in_use )
return - EBUSY ;
dvb_net_stop ( net ) ;
flush_scheduled_work ( ) ;
printk ( " dvb_net: removed network interface %s \n " , net - > name ) ;
unregister_netdev ( net ) ;
dvbnet - > state [ num ] = 0 ;
dvbnet - > device [ num ] = NULL ;
free_netdev ( net ) ;
return 0 ;
}
static int dvb_net_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * parg )
{
2005-05-17 08:54:24 +04:00
struct dvb_device * dvbdev = file - > private_data ;
struct dvb_net * dvbnet = dvbdev - > priv ;
2005-04-17 02:20:36 +04:00
if ( ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) )
return - EPERM ;
switch ( cmd ) {
case NET_ADD_IF :
{
2005-05-17 08:54:24 +04:00
struct dvb_net_if * dvbnetif = parg ;
2005-04-17 02:20:36 +04:00
int result ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! try_module_get ( dvbdev - > adapter - > module ) )
return - EPERM ;
result = dvb_net_add_if ( dvbnet , dvbnetif - > pid , dvbnetif - > feedtype ) ;
if ( result < 0 ) {
module_put ( dvbdev - > adapter - > module ) ;
return result ;
}
dvbnetif - > if_num = result ;
break ;
}
case NET_GET_IF :
{
struct net_device * netdev ;
struct dvb_net_priv * priv_data ;
2005-05-17 08:54:24 +04:00
struct dvb_net_if * dvbnetif = parg ;
2005-04-17 02:20:36 +04:00
if ( dvbnetif - > if_num > = DVB_NET_DEVICES_MAX | |
! dvbnet - > state [ dvbnetif - > if_num ] )
return - EINVAL ;
netdev = dvbnet - > device [ dvbnetif - > if_num ] ;
2005-05-17 08:54:24 +04:00
priv_data = netdev - > priv ;
2005-04-17 02:20:36 +04:00
dvbnetif - > pid = priv_data - > pid ;
dvbnetif - > feedtype = priv_data - > feedtype ;
break ;
}
case NET_REMOVE_IF :
{
int ret ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ( unsigned int ) parg > = DVB_NET_DEVICES_MAX )
return - EINVAL ;
ret = dvb_net_remove_if ( dvbnet , ( unsigned int ) parg ) ;
if ( ! ret )
module_put ( dvbdev - > adapter - > module ) ;
return ret ;
}
/* binary compatiblity cruft */
case __NET_ADD_IF_OLD :
{
2005-05-17 08:54:24 +04:00
struct __dvb_net_if_old * dvbnetif = parg ;
2005-04-17 02:20:36 +04:00
int result ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
if ( ! try_module_get ( dvbdev - > adapter - > module ) )
return - EPERM ;
result = dvb_net_add_if ( dvbnet , dvbnetif - > pid , DVB_NET_FEEDTYPE_MPE ) ;
if ( result < 0 ) {
module_put ( dvbdev - > adapter - > module ) ;
return result ;
}
dvbnetif - > if_num = result ;
break ;
}
case __NET_GET_IF_OLD :
{
struct net_device * netdev ;
struct dvb_net_priv * priv_data ;
2005-05-17 08:54:24 +04:00
struct __dvb_net_if_old * dvbnetif = parg ;
2005-04-17 02:20:36 +04:00
if ( dvbnetif - > if_num > = DVB_NET_DEVICES_MAX | |
! dvbnet - > state [ dvbnetif - > if_num ] )
return - EINVAL ;
netdev = dvbnet - > device [ dvbnetif - > if_num ] ;
2005-05-17 08:54:24 +04:00
priv_data = netdev - > priv ;
2005-04-17 02:20:36 +04:00
dvbnetif - > pid = priv_data - > pid ;
break ;
}
default :
return - ENOTTY ;
}
return 0 ;
}
static int dvb_net_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return dvb_usercopy ( inode , file , cmd , arg , dvb_net_do_ioctl ) ;
}
static struct file_operations dvb_net_fops = {
. owner = THIS_MODULE ,
. ioctl = dvb_net_ioctl ,
. open = dvb_generic_open ,
. release = dvb_generic_release ,
} ;
static struct dvb_device dvbdev_net = {
. priv = NULL ,
. users = 1 ,
. writers = 1 ,
. fops = & dvb_net_fops ,
} ;
void dvb_net_release ( struct dvb_net * dvbnet )
{
int i ;
dvb_unregister_device ( dvbnet - > dvbdev ) ;
for ( i = 0 ; i < DVB_NET_DEVICES_MAX ; i + + ) {
if ( ! dvbnet - > state [ i ] )
continue ;
dvb_net_remove_if ( dvbnet , i ) ;
}
}
EXPORT_SYMBOL ( dvb_net_release ) ;
int dvb_net_init ( struct dvb_adapter * adap , struct dvb_net * dvbnet ,
struct dmx_demux * dmx )
{
int i ;
dvbnet - > demux = dmx ;
for ( i = 0 ; i < DVB_NET_DEVICES_MAX ; i + + )
dvbnet - > state [ i ] = 0 ;
dvb_register_device ( adap , & dvbnet - > dvbdev , & dvbdev_net ,
dvbnet , DVB_DEVICE_NET ) ;
return 0 ;
}
EXPORT_SYMBOL ( dvb_net_init ) ;