Ralph Metzler wrote: >Holger Kiehl writes: > > On Mon, 31 Oct 2005, Holger Kiehl wrote: > > > > > Hello > > > > > > Removing several dvb net interfaces as follows: > > > > > > /sbin/ifconfig dvb0_4 down > > > /sbin/ifconfig dvb0_3 down > > > /sbin/ifconfig dvb0_2 down > > > /sbin/ifconfig dvb0_1 down > > > /sbin/ifconfig dvb0_0 down > > > > > > Always produces an oops on a 64 bit system. On a 32 bit system this is no > > > problem. Applying the attached patch makes it work on 64 bit systems. > > > > > After booting the system I still get the oops: > > > > Oct 31 14:51:43 sojus kernel: dvb_demux_feed_del: feed not in list (type=1 > > state=0 pid=ffff) > > Oct 31 14:51:43 sojus kernel: Unable to handle kernel paging request at > > 00000000000010f8 RIP: > > Oct 31 14:51:43 sojus kernel: <ffffffff8028dde0>{dvb_net_filter_sec_set+48} > > Oct 31 14:51:43 sojus kernel: PGD cce6a067 PUD 0 > > Oct 31 14:51:43 sojus kernel: Oops: 0000 [1] SMP > > Oct 31 14:51:43 sojus kernel: CPU 1 > > >There are some race conditions in the networking code which only >show up on SMP. >Please try the file below. It adds a mutex around the filter handling >code. I did not test this myself (lacking an SMP system for testing). >I did this blindly based on another bug report but did not hear back >from that person if this suffices to remove the race. So, if you or >anybody else with SMP can test it that would be great. > >The could of course be something else showing up onl on 64 bit. >Does this ooops also happen on 32 bit SMP? > >Ralph > > Ralph- Did you intend on having this committed? If so, please generate a patch, containing a sign-off and short description. We only have until next week if we want to get patches into kernel 2.6.15. Thanks, Michael Krufky > >/* > * dvb_net.c > * > * Copyright (C) 2001 Convergence integrated media GmbH > * Ralph Metzler <ralph@xxxxxxxxxxxxxx> > * Copyright (C) 2002 Ralph Metzler <rjkm@xxxxxxxxxxxxxx> > * > * 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@xxxxxxxxxxxxxx> > * and Wolfram Stering <wstering@xxxxxxxxxxxxxx> > * > * 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. */ > > struct semaphore mutex; > spinlock_t lock; >}; > > >/** > * 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@xxxxxxxxxx> > */ >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 ) >{ > struct dvb_net_priv *priv = dev->priv; > 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) >{ > struct net_device *dev = feed->priv; > > 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); > int snap = 0; > > /* 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) { > /* 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; > } > 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); > * 12 byte MPE header; 4 byte checksum; + 2 byte alignment, 8 byte LLC/SNAP > */ > if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) { > //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 */ > eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14 - snap); > memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap); > > /* 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; > > 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; > } > } > > 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) >{ > struct net_device *dev = filter->priv; > > /** > * 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) >{ > struct dvb_net_priv *priv = dev->priv; > 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=0, i; > struct dvb_net_priv *priv = dev->priv; > struct dmx_demux *demux = priv->demux; > unsigned char *mac = (unsigned char *) dev->dev_addr; > > dprintk("%s: rx_mode %i\n", __FUNCTION__, priv->rx_mode); > down(&priv->mutex); > 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); > goto error; > } > > ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 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; > goto error; > } > > 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); > goto error; > } > > /* 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, > 32768, /* circular buffer size */ > 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; > goto error; > } > > dprintk("%s: start filtering\n", __FUNCTION__); > priv->tsfeed->start_filtering(priv->tsfeed); > } else > ret=-EINVAL; > >error: > up(&priv->mutex); > return ret; >} > >static int dvb_net_feed_stop(struct net_device *dev) >{ > struct dvb_net_priv *priv = dev->priv; > int i, ret=0; > > dprintk("%s\n", __FUNCTION__); > down(&priv->mutex); > 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 > ret=-EINVAL; > up(&priv->mutex); > return ret; >} > > >static int dvb_set_mc_filter (struct net_device *dev, struct dev_mc_list *mc) >{ > struct dvb_net_priv *priv = dev->priv; > > 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; > struct dvb_net_priv *priv = dev->priv; > > > dvb_net_feed_stop(dev); > > priv->rx_mode = RX_MODE_UNI; > > spin_lock_bh(&dev->xmit_lock); > 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); > } > } > spin_unlock_bh(&dev->xmit_lock); > dvb_net_feed_start(dev); >} > > >static void dvb_net_set_multicast_list (struct net_device *dev) >{ > struct dvb_net_priv *priv = dev->priv; > 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) >{ > struct dvb_net_priv *priv = dev->priv; > 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) >{ > struct dvb_net_priv *priv = dev->priv; > > priv->in_use++; > dvb_net_feed_start(dev); > return 0; >} > > >static int dvb_net_stop(struct net_device *dev) >{ > struct dvb_net_priv *priv = dev->priv; > > 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); > sema_init(&priv->mutex, 1); > spin_lock_init(&priv->lock); > > 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) >{ > struct dvb_device *dvbdev = file->private_data; > struct dvb_net *dvbnet = dvbdev->priv; > > if (((file->f_flags&O_ACCMODE)==O_RDONLY)) > return -EPERM; > > switch (cmd) { > case NET_ADD_IF: > { > struct dvb_net_if *dvbnetif = parg; > 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; > struct dvb_net_if *dvbnetif = parg; > > if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || > !dvbnet->state[dvbnetif->if_num]) > return -EINVAL; > > netdev = dvbnet->device[dvbnetif->if_num]; > > priv_data = netdev->priv; > 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: > { > struct __dvb_net_if_old *dvbnetif = parg; > 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; > struct __dvb_net_if_old *dvbnetif = parg; > > if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || > !dvbnet->state[dvbnetif->if_num]) > return -EINVAL; > > netdev = dvbnet->device[dvbnetif->if_num]; > > priv_data = netdev->priv; > 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); > > >_______________________________________________ > >linux-dvb@xxxxxxxxxxx >http://www.linuxtv.org/cgi-bin/mailman/listinfo/linux-dvb > >