2005-04-16 15:20:36 -07:00
/*
* Linux ARCnet driver - RFC1201 ( standard ) packet encapsulation
*
* Written 1994 - 1999 by Avery Pennarun .
* Derived from skeleton . c by Donald Becker .
*
* Special thanks to Contemporary Controls , Inc . ( www . ccontrols . com )
* for sponsoring the further development of this driver .
*
* * * * * * * * * * * * * * * * * * * * * * *
*
* The original copyright of skeleton . c was as follows :
*
* skeleton . c Written 1993 by Donald Becker .
* Copyright 1993 United States Government as represented by the
* Director , National Security Agency . This software may only be used
* and distributed according to the terms of the GNU General Public License as
* modified by SRC , incorporated herein by reference .
*
* * * * * * * * * * * * * * * * * * * * * * *
*
* For more details , see drivers / net / arcnet . c
*
* * * * * * * * * * * * * * * * * * * * * * *
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/if_arp.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <linux/arcdevice.h>
MODULE_LICENSE ( " GPL " ) ;
# define VERSION "arcnet: RFC1201 \"standard\" (`a') encapsulation support loaded.\n"
2007-08-22 22:59:11 -04:00
static __be16 type_trans ( struct sk_buff * skb , struct net_device * dev ) ;
2005-04-16 15:20:36 -07:00
static void rx ( struct net_device * dev , int bufnum ,
struct archdr * pkthdr , int length ) ;
static int build_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , uint8_t daddr ) ;
static int prepare_tx ( struct net_device * dev , struct archdr * pkt , int length ,
int bufnum ) ;
static int continue_tx ( struct net_device * dev , int bufnum ) ;
2006-01-14 03:10:22 +01:00
static struct ArcProto rfc1201_proto =
2005-04-16 15:20:36 -07:00
{
. suffix = ' a ' ,
. mtu = 1500 , /* could be more, but some receivers can't handle it... */
. is_ip = 1 , /* This is for sending IP and ARP packages */
. rx = rx ,
. build_header = build_header ,
. prepare_tx = prepare_tx ,
. continue_tx = continue_tx ,
. ack_tx = NULL
} ;
static int __init arcnet_rfc1201_init ( void )
{
printk ( VERSION ) ;
arc_proto_map [ ARC_P_IP ]
= arc_proto_map [ ARC_P_IPV6 ]
= arc_proto_map [ ARC_P_ARP ]
= arc_proto_map [ ARC_P_RARP ]
= arc_proto_map [ ARC_P_IPX ]
= arc_proto_map [ ARC_P_NOVELL_EC ]
= & rfc1201_proto ;
/* if someone else already owns the broadcast, we won't take it */
if ( arc_bcast_proto = = arc_proto_default )
arc_bcast_proto = & rfc1201_proto ;
return 0 ;
}
static void __exit arcnet_rfc1201_exit ( void )
{
arcnet_unregister_proto ( & rfc1201_proto ) ;
}
module_init ( arcnet_rfc1201_init ) ;
module_exit ( arcnet_rfc1201_exit ) ;
/*
* Determine a packet ' s protocol ID .
*
* With ARCnet we have to convert everything to Ethernet - style stuff .
*/
2007-08-22 22:59:11 -04:00
static __be16 type_trans ( struct sk_buff * skb , struct net_device * dev )
2005-04-16 15:20:36 -07:00
{
struct archdr * pkt = ( struct archdr * ) skb - > data ;
struct arc_rfc1201 * soft = & pkt - > soft . rfc1201 ;
struct arcnet_local * lp = dev - > priv ;
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE ;
/* Pull off the arcnet header. */
2007-03-19 15:30:44 -07:00
skb_reset_mac_header ( skb ) ;
2005-04-16 15:20:36 -07:00
skb_pull ( skb , hdr_size ) ;
if ( pkt - > hard . dest = = 0 )
skb - > pkt_type = PACKET_BROADCAST ;
else if ( dev - > flags & IFF_PROMISC ) {
/* if we're not sending to ourselves :) */
if ( pkt - > hard . dest ! = dev - > dev_addr [ 0 ] )
skb - > pkt_type = PACKET_OTHERHOST ;
}
/* now return the protocol number */
switch ( soft - > proto ) {
case ARC_P_IP :
return htons ( ETH_P_IP ) ;
case ARC_P_IPV6 :
return htons ( ETH_P_IPV6 ) ;
case ARC_P_ARP :
return htons ( ETH_P_ARP ) ;
case ARC_P_RARP :
return htons ( ETH_P_RARP ) ;
case ARC_P_IPX :
case ARC_P_NOVELL_EC :
return htons ( ETH_P_802_3 ) ;
default :
lp - > stats . rx_errors + + ;
lp - > stats . rx_crc_errors + + ;
return 0 ;
}
return htons ( ETH_P_IP ) ;
}
/* packet receiver */
static void rx ( struct net_device * dev , int bufnum ,
struct archdr * pkthdr , int length )
{
struct arcnet_local * lp = dev - > priv ;
struct sk_buff * skb ;
struct archdr * pkt = pkthdr ;
struct arc_rfc1201 * soft = & pkthdr - > soft . rfc1201 ;
int saddr = pkt - > hard . source , ofs ;
struct Incoming * in = & lp - > rfc1201 . incoming [ saddr ] ;
BUGMSG ( D_DURING , " it's an RFC1201 packet (length=%d) \n " , length ) ;
if ( length > = MinTU )
ofs = 512 - length ;
else
ofs = 256 - length ;
if ( soft - > split_flag = = 0xFF ) { /* Exception Packet */
if ( length > = 4 + RFC1201_HDR_SIZE )
BUGMSG ( D_DURING , " compensating for exception packet \n " ) ;
else {
BUGMSG ( D_EXTRA , " short RFC1201 exception packet from %02Xh " ,
saddr ) ;
return ;
}
/* skip over 4-byte junkola */
length - = 4 ;
ofs + = 4 ;
lp - > hw . copy_from_card ( dev , bufnum , 512 - length ,
soft , sizeof ( pkt - > soft ) ) ;
}
if ( ! soft - > split_flag ) { /* not split */
BUGMSG ( D_RX , " incoming is not split (splitflag=%d) \n " ,
soft - > split_flag ) ;
if ( in - > skb ) { /* already assembling one! */
BUGMSG ( D_EXTRA , " aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d) \n " ,
in - > sequence , soft - > split_flag , soft - > sequence ) ;
lp - > rfc1201 . aborted_seq = soft - > sequence ;
dev_kfree_skb_irq ( in - > skb ) ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_missed_errors + + ;
in - > skb = NULL ;
}
in - > sequence = soft - > sequence ;
skb = alloc_skb ( length + ARC_HDR_SIZE , GFP_ATOMIC ) ;
if ( skb = = NULL ) {
BUGMSG ( D_NORMAL , " Memory squeeze, dropping packet. \n " ) ;
lp - > stats . rx_dropped + + ;
return ;
}
skb_put ( skb , length + ARC_HDR_SIZE ) ;
skb - > dev = dev ;
pkt = ( struct archdr * ) skb - > data ;
soft = & pkt - > soft . rfc1201 ;
/* up to sizeof(pkt->soft) has already been copied from the card */
memcpy ( pkt , pkthdr , sizeof ( struct archdr ) ) ;
if ( length > sizeof ( pkt - > soft ) )
lp - > hw . copy_from_card ( dev , bufnum , ofs + sizeof ( pkt - > soft ) ,
pkt - > soft . raw + sizeof ( pkt - > soft ) ,
length - sizeof ( pkt - > soft ) ) ;
/*
* ARP packets have problems when sent from some DOS systems : the
* source address is always 0 ! So we take the hardware source addr
* ( which is impossible to fumble ) and insert it ourselves .
*/
if ( soft - > proto = = ARC_P_ARP ) {
struct arphdr * arp = ( struct arphdr * ) soft - > payload ;
/* make sure addresses are the right length */
if ( arp - > ar_hln = = 1 & & arp - > ar_pln = = 4 ) {
uint8_t * cptr = ( uint8_t * ) arp + sizeof ( struct arphdr ) ;
if ( ! * cptr ) { /* is saddr = 00? */
BUGMSG ( D_EXTRA ,
" ARP source address was 00h, set to %02Xh. \n " ,
saddr ) ;
lp - > stats . rx_crc_errors + + ;
* cptr = saddr ;
} else {
BUGMSG ( D_DURING , " ARP source address (%Xh) is fine. \n " ,
* cptr ) ;
}
} else {
BUGMSG ( D_NORMAL , " funny-shaped ARP packet. (%Xh, %Xh) \n " ,
arp - > ar_hln , arp - > ar_pln ) ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_crc_errors + + ;
}
}
BUGLVL ( D_SKB ) arcnet_dump_skb ( dev , skb , " rx " ) ;
skb - > protocol = type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
} else { /* split packet */
/*
* NOTE : MSDOS ARP packet correction should only need to apply to
* unsplit packets , since ARP packets are so short .
*
* My interpretation of the RFC1201 document is that if a packet is
* received out of order , the entire assembly process should be
* aborted .
*
* The RFC also mentions " it is possible for successfully received
* packets to be retransmitted . " As of 0.40 all previously received
* packets are allowed , not just the most recent one .
*
* We allow multiple assembly processes , one for each ARCnet card
* possible on the network . Seems rather like a waste of memory ,
* but there ' s no other way to be reliable .
*/
BUGMSG ( D_RX , " packet is split (splitflag=%d, seq=%d) \n " ,
soft - > split_flag , in - > sequence ) ;
if ( in - > skb & & in - > sequence ! = soft - > sequence ) {
BUGMSG ( D_EXTRA , " wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d) \n " ,
saddr , in - > sequence , soft - > sequence ,
soft - > split_flag ) ;
dev_kfree_skb_irq ( in - > skb ) ;
in - > skb = NULL ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_missed_errors + + ;
in - > lastpacket = in - > numpackets = 0 ;
}
if ( soft - > split_flag & 1 ) { /* first packet in split */
BUGMSG ( D_RX , " brand new splitpacket (splitflag=%d) \n " ,
soft - > split_flag ) ;
if ( in - > skb ) { /* already assembling one! */
BUGMSG ( D_EXTRA , " aborting previous (seq=%d) assembly "
" (splitflag=%d, seq=%d) \n " ,
in - > sequence , soft - > split_flag ,
soft - > sequence ) ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_missed_errors + + ;
dev_kfree_skb_irq ( in - > skb ) ;
}
in - > sequence = soft - > sequence ;
in - > numpackets = ( ( unsigned ) soft - > split_flag > > 1 ) + 2 ;
in - > lastpacket = 1 ;
if ( in - > numpackets > 16 ) {
BUGMSG ( D_EXTRA , " incoming packet more than 16 segments; dropping. (splitflag=%d) \n " ,
soft - > split_flag ) ;
lp - > rfc1201 . aborted_seq = soft - > sequence ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_length_errors + + ;
return ;
}
in - > skb = skb = alloc_skb ( 508 * in - > numpackets + ARC_HDR_SIZE ,
GFP_ATOMIC ) ;
if ( skb = = NULL ) {
BUGMSG ( D_NORMAL , " (split) memory squeeze, dropping packet. \n " ) ;
lp - > rfc1201 . aborted_seq = soft - > sequence ;
lp - > stats . rx_dropped + + ;
return ;
}
skb - > dev = dev ;
pkt = ( struct archdr * ) skb - > data ;
soft = & pkt - > soft . rfc1201 ;
memcpy ( pkt , pkthdr , ARC_HDR_SIZE + RFC1201_HDR_SIZE ) ;
skb_put ( skb , ARC_HDR_SIZE + RFC1201_HDR_SIZE ) ;
soft - > split_flag = 0 ; /* end result won't be split */
} else { /* not first packet */
int packetnum = ( ( unsigned ) soft - > split_flag > > 1 ) + 1 ;
/*
* if we ' re not assembling , there ' s no point trying to
* continue .
*/
if ( ! in - > skb ) {
if ( lp - > rfc1201 . aborted_seq ! = soft - > sequence ) {
BUGMSG ( D_EXTRA , " can't continue split without starting "
" first! (splitflag=%d, seq=%d, aborted=%d) \n " ,
soft - > split_flag , soft - > sequence ,
lp - > rfc1201 . aborted_seq ) ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_missed_errors + + ;
}
return ;
}
in - > lastpacket + + ;
if ( packetnum ! = in - > lastpacket ) { /* not the right flag! */
/* harmless duplicate? ignore. */
if ( packetnum < = in - > lastpacket - 1 ) {
BUGMSG ( D_EXTRA , " duplicate splitpacket ignored! (splitflag=%d) \n " ,
soft - > split_flag ) ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_frame_errors + + ;
return ;
}
/* "bad" duplicate, kill reassembly */
BUGMSG ( D_EXTRA , " out-of-order splitpacket, reassembly "
" (seq=%d) aborted (splitflag=%d, seq=%d) \n " ,
in - > sequence , soft - > split_flag , soft - > sequence ) ;
lp - > rfc1201 . aborted_seq = soft - > sequence ;
dev_kfree_skb_irq ( in - > skb ) ;
in - > skb = NULL ;
lp - > stats . rx_errors + + ;
lp - > stats . rx_missed_errors + + ;
in - > lastpacket = in - > numpackets = 0 ;
return ;
}
pkt = ( struct archdr * ) in - > skb - > data ;
soft = & pkt - > soft . rfc1201 ;
}
skb = in - > skb ;
lp - > hw . copy_from_card ( dev , bufnum , ofs + RFC1201_HDR_SIZE ,
skb - > data + skb - > len ,
length - RFC1201_HDR_SIZE ) ;
skb_put ( skb , length - RFC1201_HDR_SIZE ) ;
/* are we done? */
if ( in - > lastpacket = = in - > numpackets ) {
in - > skb = NULL ;
in - > lastpacket = in - > numpackets = 0 ;
BUGMSG ( D_SKB_SIZE , " skb: received %d bytes from %02X (unsplit) \n " ,
skb - > len , pkt - > hard . source ) ;
BUGMSG ( D_SKB_SIZE , " skb: received %d bytes from %02X (split) \n " ,
skb - > len , pkt - > hard . source ) ;
BUGLVL ( D_SKB ) arcnet_dump_skb ( dev , skb , " rx " ) ;
skb - > protocol = type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
}
}
}
/* Create the ARCnet hard/soft headers for RFC1201. */
static int build_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , uint8_t daddr )
{
struct arcnet_local * lp = dev - > priv ;
int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE ;
struct archdr * pkt = ( struct archdr * ) skb_push ( skb , hdr_size ) ;
struct arc_rfc1201 * soft = & pkt - > soft . rfc1201 ;
/* set the protocol ID according to RFC1201 */
switch ( type ) {
case ETH_P_IP :
soft - > proto = ARC_P_IP ;
break ;
case ETH_P_IPV6 :
soft - > proto = ARC_P_IPV6 ;
break ;
case ETH_P_ARP :
soft - > proto = ARC_P_ARP ;
break ;
case ETH_P_RARP :
soft - > proto = ARC_P_RARP ;
break ;
case ETH_P_IPX :
case ETH_P_802_3 :
case ETH_P_802_2 :
soft - > proto = ARC_P_IPX ;
break ;
case ETH_P_ATALK :
soft - > proto = ARC_P_ATALK ;
break ;
default :
BUGMSG ( D_NORMAL , " RFC1201: I don't understand protocol %d (%Xh) \n " ,
type , type ) ;
lp - > stats . tx_errors + + ;
lp - > stats . tx_aborted_errors + + ;
return 0 ;
}
/*
* Set the source hardware address .
*
* This is pretty pointless for most purposes , but it can help in
* debugging . ARCnet does not allow us to change the source address in
* the actual packet sent )
*/
pkt - > hard . source = * dev - > dev_addr ;
soft - > sequence = htons ( lp - > rfc1201 . sequence + + ) ;
soft - > split_flag = 0 ; /* split packets are done elsewhere */
/* see linux/net/ethernet/eth.c to see where I got the following */
if ( dev - > flags & ( IFF_LOOPBACK | IFF_NOARP ) ) {
/*
* FIXME : fill in the last byte of the dest ipaddr here to better
* comply with RFC1051 in " noarp " mode . For now , always broadcasting
* will probably at least get packets sent out : )
*/
pkt - > hard . dest = 0 ;
return hdr_size ;
}
/* otherwise, drop in the dest address */
pkt - > hard . dest = daddr ;
return hdr_size ;
}
static void load_pkt ( struct net_device * dev , struct arc_hardware * hard ,
struct arc_rfc1201 * soft , int softlen , int bufnum )
{
struct arcnet_local * lp = dev - > priv ;
int ofs ;
/* assume length <= XMTU: someone should have handled that by now. */
if ( softlen > MinTU ) {
hard - > offset [ 0 ] = 0 ;
hard - > offset [ 1 ] = ofs = 512 - softlen ;
} else if ( softlen > MTU ) { /* exception packet - add an extra header */
struct arc_rfc1201 excsoft ;
excsoft . proto = soft - > proto ;
excsoft . split_flag = 0xff ;
2007-08-22 22:59:11 -04:00
excsoft . sequence = htons ( 0xffff ) ;
2005-04-16 15:20:36 -07:00
hard - > offset [ 0 ] = 0 ;
ofs = 512 - softlen ;
hard - > offset [ 1 ] = ofs - RFC1201_HDR_SIZE ;
lp - > hw . copy_to_card ( dev , bufnum , ofs - RFC1201_HDR_SIZE ,
& excsoft , RFC1201_HDR_SIZE ) ;
} else
hard - > offset [ 0 ] = ofs = 256 - softlen ;
lp - > hw . copy_to_card ( dev , bufnum , 0 , hard , ARC_HDR_SIZE ) ;
lp - > hw . copy_to_card ( dev , bufnum , ofs , soft , softlen ) ;
lp - > lastload_dest = hard - > dest ;
}
static int prepare_tx ( struct net_device * dev , struct archdr * pkt , int length ,
int bufnum )
{
struct arcnet_local * lp = dev - > priv ;
const int maxsegsize = XMTU - RFC1201_HDR_SIZE ;
struct Outgoing * out ;
BUGMSG ( D_DURING , " prepare_tx: txbufs=%d/%d/%d \n " ,
lp - > next_tx , lp - > cur_tx , bufnum ) ;
length - = ARC_HDR_SIZE ; /* hard header is not included in packet length */
pkt - > soft . rfc1201 . split_flag = 0 ;
/* need to do a split packet? */
if ( length > XMTU ) {
out = & lp - > outgoing ;
out - > length = length - RFC1201_HDR_SIZE ;
out - > dataleft = lp - > outgoing . length ;
out - > numsegs = ( out - > dataleft + maxsegsize - 1 ) / maxsegsize ;
out - > segnum = 0 ;
BUGMSG ( D_DURING , " rfc1201 prep_tx: ready for %d-segment split "
" (%d bytes, seq=%d) \n " , out - > numsegs , out - > length ,
pkt - > soft . rfc1201 . sequence ) ;
return 0 ; /* not done */
}
/* just load the packet into the buffers and send it off */
load_pkt ( dev , & pkt - > hard , & pkt - > soft . rfc1201 , length , bufnum ) ;
return 1 ; /* done */
}
static int continue_tx ( struct net_device * dev , int bufnum )
{
struct arcnet_local * lp = dev - > priv ;
struct Outgoing * out = & lp - > outgoing ;
struct arc_hardware * hard = & out - > pkt - > hard ;
struct arc_rfc1201 * soft = & out - > pkt - > soft . rfc1201 , * newsoft ;
int maxsegsize = XMTU - RFC1201_HDR_SIZE ;
int seglen ;
BUGMSG ( D_DURING ,
" rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d) \n " ,
out - > segnum , out - > numsegs , soft - > sequence ) ;
/* the "new" soft header comes right before the data chunk */
newsoft = ( struct arc_rfc1201 * )
( out - > pkt - > soft . raw + out - > length - out - > dataleft ) ;
if ( ! out - > segnum ) /* first packet; newsoft == soft */
newsoft - > split_flag = ( ( out - > numsegs - 2 ) < < 1 ) | 1 ;
else {
newsoft - > split_flag = out - > segnum < < 1 ;
newsoft - > proto = soft - > proto ;
newsoft - > sequence = soft - > sequence ;
}
seglen = maxsegsize ;
if ( seglen > out - > dataleft )
seglen = out - > dataleft ;
out - > dataleft - = seglen ;
load_pkt ( dev , hard , newsoft , seglen + RFC1201_HDR_SIZE , bufnum ) ;
out - > segnum + + ;
if ( out - > segnum > = out - > numsegs )
return 1 ;
else
return 0 ;
}