2005-04-17 02:20:36 +04:00
/*
* Copyright 1996 The Board of Trustees of The Leland Stanford
* Junior University . All Rights Reserved .
*
* Permission to use , copy , modify , and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted , provided that the above copyright
* notice appear in all copies . Stanford University
* makes no representations about the suitability of this
* software for any purpose . It is provided " as is " without
* express or implied warranty .
*
* strip . c This module implements Starmode Radio IP ( STRIP )
* for kernel - based devices like TTY . It interfaces between a
* raw TTY , and the kernel ' s INET protocol layers ( via DDI ) .
*
* Version : @ ( # ) strip . c 1.3 July 1997
*
* Author : Stuart Cheshire < cheshire @ cs . stanford . edu >
*
* Fixes : v0 .9 12 th Feb 1996 ( SC )
* New byte stuffing ( 2 + 6 run - length encoding )
* New watchdog timer task
* New Protocol key ( SIP0 )
*
* v0 .9 .1 3 rd March 1996 ( SC )
* Changed to dynamic device allocation - - no more compile
* time ( or boot time ) limit on the number of STRIP devices .
*
* v0 .9 .2 13 th March 1996 ( SC )
* Uses arp cache lookups ( but doesn ' t send arp packets yet )
*
* v0 .9 .3 17 th April 1996 ( SC )
* Fixed bug where STR_ERROR flag was getting set unneccessarily
* ( causing otherwise good packets to be unneccessarily dropped )
*
* v0 .9 .4 27 th April 1996 ( SC )
* First attempt at using " &COMMAND " Starmode AT commands
*
* v0 .9 .5 29 th May 1996 ( SC )
* First attempt at sending ( unicast ) ARP packets
*
* v0 .9 .6 5 th June 1996 ( Elliot )
* Put " message level " tags in every " printk " statement
*
* v0 .9 .7 13 th June 1996 ( laik )
* Added support for the / proc fs
*
* v0 .9 .8 July 1996 ( Mema )
* Added packet logging
*
* v1 .0 November 1996 ( SC )
* Fixed ( severe ) memory leaks in the / proc fs code
* Fixed race conditions in the logging code
*
* v1 .1 January 1997 ( SC )
* Deleted packet logging ( use tcpdump instead )
* Added support for Metricom Firmware v204 features
* ( like message checksums )
*
* v1 .2 January 1997 ( SC )
* Put portables list back in
*
* v1 .3 July 1997 ( SC )
* Made STRIP driver set the radio ' s baud rate automatically .
* It is no longer necessarily to manually set the radio ' s
* rate permanently to 115200 - - the driver handles setting
* the rate automatically .
*/
# ifdef MODULE
static const char StripVersion [ ] = " 1.3A-STUART.CHESHIRE-MODULAR " ;
# else
static const char StripVersion [ ] = " 1.3A-STUART.CHESHIRE " ;
# endif
# define TICKLE_TIMERS 0
# define EXT_COUNTERS 1
/************************************************************************/
/* Header files */
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/bitops.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <linux / ctype.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/interrupt.h>
# include <linux/in.h>
# include <linux/tty.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/inetdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/if_arp.h>
# include <linux/if_strip.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/serial.h>
# include <linux/serialP.h>
# include <linux/rcupdate.h>
# include <net/arp.h>
# include <linux/ip.h>
# include <linux/tcp.h>
# include <linux/time.h>
/************************************************************************/
/* Useful structures and definitions */
/*
* A MetricomKey identifies the protocol being carried inside a Metricom
* Starmode packet .
*/
typedef union {
__u8 c [ 4 ] ;
__u32 l ;
} MetricomKey ;
/*
* An IP address can be viewed as four bytes in memory ( which is what it is ) or as
* a single 32 - bit long ( which is convenient for assignment , equality testing etc . )
*/
typedef union {
__u8 b [ 4 ] ;
__u32 l ;
} IPaddr ;
/*
* A MetricomAddressString is used to hold a printable representation of
* a Metricom address .
*/
typedef struct {
__u8 c [ 24 ] ;
} MetricomAddressString ;
/* Encapsulation can expand packet of size x to 65/64x + 1
* Sent packet looks like " <CR>*<address>*<key><encaps payload><CR> "
* 1 1 1 - 18 1 4 ? 1
* eg . < CR > * 0000 - 1234 * SIP0 < encaps payload > < CR >
* We allow 31 bytes for the stars , the key , the address and the < CR > s
*/
# define STRIP_ENCAP_SIZE(X) (32 + (X)*65L / 64L)
/*
* A STRIP_Header is never really sent over the radio , but making a dummy
* header for internal use within the kernel that looks like an Ethernet
* header makes certain other software happier . For example , tcpdump
* already understands Ethernet headers .
*/
typedef struct {
MetricomAddress dst_addr ; /* Destination address, e.g. "0000-1234" */
MetricomAddress src_addr ; /* Source address, e.g. "0000-5678" */
unsigned short protocol ; /* The protocol type, using Ethernet codes */
} STRIP_Header ;
typedef struct {
char c [ 60 ] ;
} MetricomNode ;
# define NODE_TABLE_SIZE 32
typedef struct {
struct timeval timestamp ;
int num_nodes ;
MetricomNode node [ NODE_TABLE_SIZE ] ;
} MetricomNodeTable ;
enum { FALSE = 0 , TRUE = 1 } ;
/*
* Holds the radio ' s firmware version .
*/
typedef struct {
char c [ 50 ] ;
} FirmwareVersion ;
/*
* Holds the radio ' s serial number .
*/
typedef struct {
char c [ 18 ] ;
} SerialNumber ;
/*
* Holds the radio ' s battery voltage .
*/
typedef struct {
char c [ 11 ] ;
} BatteryVoltage ;
typedef struct {
char c [ 8 ] ;
} char8 ;
enum {
NoStructure = 0 , /* Really old firmware */
StructuredMessages = 1 , /* Parsable AT response msgs */
ChecksummedMessages = 2 /* Parsable AT response msgs with checksums */
2005-05-13 06:54:16 +04:00
} ;
2005-04-17 02:20:36 +04:00
struct strip {
int magic ;
/*
* These are pointers to the malloc ( ) ed frame buffers .
*/
unsigned char * rx_buff ; /* buffer for received IP packet */
unsigned char * sx_buff ; /* buffer for received serial data */
int sx_count ; /* received serial data counter */
int sx_size ; /* Serial buffer size */
unsigned char * tx_buff ; /* transmitter buffer */
unsigned char * tx_head ; /* pointer to next byte to XMIT */
int tx_left ; /* bytes left in XMIT queue */
int tx_size ; /* Serial buffer size */
/*
* STRIP interface statistics .
*/
unsigned long rx_packets ; /* inbound frames counter */
unsigned long tx_packets ; /* outbound frames counter */
unsigned long rx_errors ; /* Parity, etc. errors */
unsigned long tx_errors ; /* Planned stuff */
unsigned long rx_dropped ; /* No memory for skb */
unsigned long tx_dropped ; /* When MTU change */
unsigned long rx_over_errors ; /* Frame bigger then STRIP buf. */
unsigned long pps_timer ; /* Timer to determine pps */
unsigned long rx_pps_count ; /* Counter to determine pps */
unsigned long tx_pps_count ; /* Counter to determine pps */
unsigned long sx_pps_count ; /* Counter to determine pps */
unsigned long rx_average_pps ; /* rx packets per second * 8 */
unsigned long tx_average_pps ; /* tx packets per second * 8 */
unsigned long sx_average_pps ; /* sent packets per second * 8 */
# ifdef EXT_COUNTERS
unsigned long rx_bytes ; /* total received bytes */
unsigned long tx_bytes ; /* total received bytes */
unsigned long rx_rbytes ; /* bytes thru radio i/f */
unsigned long tx_rbytes ; /* bytes thru radio i/f */
unsigned long rx_sbytes ; /* tot bytes thru serial i/f */
unsigned long tx_sbytes ; /* tot bytes thru serial i/f */
unsigned long rx_ebytes ; /* tot stat/err bytes */
unsigned long tx_ebytes ; /* tot stat/err bytes */
# endif
/*
* Internal variables .
*/
struct list_head list ; /* Linked list of devices */
int discard ; /* Set if serial error */
int working ; /* Is radio working correctly? */
int firmware_level ; /* Message structuring level */
int next_command ; /* Next periodic command */
unsigned int user_baud ; /* The user-selected baud rate */
int mtu ; /* Our mtu (to spot changes!) */
long watchdog_doprobe ; /* Next time to test the radio */
long watchdog_doreset ; /* Time to do next reset */
long gratuitous_arp ; /* Time to send next ARP refresh */
long arp_interval ; /* Next ARP interval */
struct timer_list idle_timer ; /* For periodic wakeup calls */
MetricomAddress true_dev_addr ; /* True address of radio */
int manual_dev_addr ; /* Hack: See note below */
FirmwareVersion firmware_version ; /* The radio's firmware version */
SerialNumber serial_number ; /* The radio's serial number */
BatteryVoltage battery_voltage ; /* The radio's battery voltage */
/*
* Other useful structures .
*/
struct tty_struct * tty ; /* ptr to TTY structure */
struct net_device * dev ; /* Our device structure */
/*
* Neighbour radio records
*/
MetricomNodeTable portables ;
MetricomNodeTable poletops ;
} ;
/*
* Note : manual_dev_addr hack
*
* It is not possible to change the hardware address of a Metricom radio ,
* or to send packets with a user - specified hardware source address , thus
* trying to manually set a hardware source address is a questionable
* thing to do . However , if the user * does * manually set the hardware
* source address of a STRIP interface , then the kernel will believe it ,
* and use it in certain places . For example , the hardware address listed
* by ifconfig will be the manual address , not the true one .
* ( Both addresses are listed in / proc / net / strip . )
* Also , ARP packets will be sent out giving the user - specified address as
* the source address , not the real address . This is dangerous , because
* it means you won ' t receive any replies - - the ARP replies will go to
* the specified address , which will be some other radio . The case where
* this is useful is when that other radio is also connected to the same
* machine . This allows you to connect a pair of radios to one machine ,
* and to use one exclusively for inbound traffic , and the other
* exclusively for outbound traffic . Pretty neat , huh ?
*
* Here ' s the full procedure to set this up :
*
* 1. " slattach " two interfaces , e . g . st0 for outgoing packets ,
* and st1 for incoming packets
*
* 2. " ifconfig " st0 ( outbound radio ) to have the hardware address
* which is the real hardware address of st1 ( inbound radio ) .
* Now when it sends out packets , it will masquerade as st1 , and
* replies will be sent to that radio , which is exactly what we want .
*
* 3. Set the route table entry ( " route add default ... " or
* " route add -net ... " , as appropriate ) to send packets via the st0
* interface ( outbound radio ) . Do not add any route which sends packets
* out via the st1 interface - - that radio is for inbound traffic only .
*
* 4. " ifconfig " st1 ( inbound radio ) to have hardware address zero .
* This tells the STRIP driver to " shut down " that interface and not
* send any packets through it . In particular , it stops sending the
* periodic gratuitous ARP packets that a STRIP interface normally sends .
* Also , when packets arrive on that interface , it will search the
* interface list to see if there is another interface who ' s manual
* hardware address matches its own real address ( i . e . st0 in this
* example ) and if so it will transfer ownership of the skbuff to
* that interface , so that it looks to the kernel as if the packet
* arrived on that interface . This is necessary because when the
* kernel sends an ARP packet on st0 , it expects to get a reply on
* st0 , and if it sees the reply come from st1 then it will ignore
* it ( to be accurate , it puts the entry in the ARP table , but
* labelled in such a way that st0 can ' t use it ) .
*
* Thanks to Petros Maniatis for coming up with the idea of splitting
* inbound and outbound traffic between two interfaces , which turned
* out to be really easy to implement , even if it is a bit of a hack .
*
* Having set a manual address on an interface , you can restore it
* to automatic operation ( where the address is automatically kept
* consistent with the real address of the radio ) by setting a manual
* address of all ones , e . g . " ifconfig st0 hw strip FFFFFFFFFFFF "
* This ' turns off ' manual override mode for the device address .
*
* Note : The IEEE 802 headers reported in tcpdump will show the * real *
* radio addresses the packets were sent and received from , so that you
* can see what is really going on with packets , and which interfaces
* they are really going through .
*/
/************************************************************************/
/* Constants */
/*
* CommandString1 works on all radios
* Other CommandStrings are only used with firmware that provides structured responses .
*
* ats319 = 1 Enables Info message for node additions and deletions
* ats319 = 2 Enables Info message for a new best node
* ats319 = 4 Enables checksums
* ats319 = 8 Enables ACK messages
*/
static const int MaxCommandStringLength = 32 ;
static const int CompatibilityCommand = 1 ;
static const char CommandString0 [ ] = " *&COMMAND*ATS319=7 " ; /* Turn on checksums & info messages */
static const char CommandString1 [ ] = " *&COMMAND*ATS305? " ; /* Query radio name */
static const char CommandString2 [ ] = " *&COMMAND*ATS325? " ; /* Query battery voltage */
static const char CommandString3 [ ] = " *&COMMAND*ATS300? " ; /* Query version information */
static const char CommandString4 [ ] = " *&COMMAND*ATS311? " ; /* Query poletop list */
static const char CommandString5 [ ] = " *&COMMAND*AT~LA " ; /* Query portables list */
typedef struct {
const char * string ;
long length ;
} StringDescriptor ;
static const StringDescriptor CommandString [ ] = {
{ CommandString0 , sizeof ( CommandString0 ) - 1 } ,
{ CommandString1 , sizeof ( CommandString1 ) - 1 } ,
{ CommandString2 , sizeof ( CommandString2 ) - 1 } ,
{ CommandString3 , sizeof ( CommandString3 ) - 1 } ,
{ CommandString4 , sizeof ( CommandString4 ) - 1 } ,
{ CommandString5 , sizeof ( CommandString5 ) - 1 }
} ;
# define GOT_ALL_RADIO_INFO(S) \
( ( S ) - > firmware_version . c [ 0 ] & & \
( S ) - > battery_voltage . c [ 0 ] & & \
memcmp ( & ( S ) - > true_dev_addr , zero_address . c , sizeof ( zero_address ) ) )
static const char hextable [ 16 ] = " 0123456789ABCDEF " ;
static const MetricomAddress zero_address ;
static const MetricomAddress broadcast_address =
{ { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } } ;
static const MetricomKey SIP0Key = { " SIP0 " } ;
static const MetricomKey ARP0Key = { " ARP0 " } ;
static const MetricomKey ATR_Key = { " ATR " } ;
static const MetricomKey ACK_Key = { " ACK_ " } ;
static const MetricomKey INF_Key = { " INF_ " } ;
static const MetricomKey ERR_Key = { " ERR_ " } ;
static const long MaxARPInterval = 60 * HZ ; /* One minute */
/*
* Maximum Starmode packet length is 1183 bytes . Allowing 4 bytes for
* protocol key , 4 bytes for checksum , one byte for CR , and 65 / 64 expansion
* for STRIP encoding , that translates to a maximum payload MTU of 1155.
* Note : A standard NFS 1 K data packet is a total of 0x480 ( 1152 ) bytes
* long , including IP header , UDP header , and NFS header . Setting the STRIP
* MTU to 1152 allows us to send default sized NFS packets without fragmentation .
*/
static const unsigned short MAX_SEND_MTU = 1152 ;
static const unsigned short MAX_RECV_MTU = 1500 ; /* Hoping for Ethernet sized packets in the future! */
static const unsigned short DEFAULT_STRIP_MTU = 1152 ;
static const int STRIP_MAGIC = 0x5303 ;
static const long LongTime = 0x7FFFFFFF ;
/************************************************************************/
/* Global variables */
static LIST_HEAD ( strip_list ) ;
static DEFINE_SPINLOCK ( strip_lock ) ;
/************************************************************************/
/* Macros */
/* Returns TRUE if text T begins with prefix P */
# define has_prefix(T,L,P) (((L) >= sizeof(P)-1) && !strncmp((T), (P), sizeof(P)-1))
/* Returns TRUE if text T of length L is equal to string S */
# define text_equal(T,L,S) (((L) == sizeof(S)-1) && !strncmp((T), (S), sizeof(S)-1))
# define READHEX(X) ((X)>='0' && (X)<='9' ? (X)-'0' : \
( X ) > = ' a ' & & ( X ) < = ' f ' ? ( X ) - ' a ' + 10 : \
( X ) > = ' A ' & & ( X ) < = ' F ' ? ( X ) - ' A ' + 10 : 0 )
# define READHEX16(X) ((__u16)(READHEX(X)))
# define READDEC(X) ((X)>='0' && (X)<='9' ? (X)-'0' : 0)
# define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)]))
# define JIFFIE_TO_SEC(X) ((X) / HZ)
/************************************************************************/
/* Utility routines */
static int arp_query ( unsigned char * haddr , u32 paddr ,
struct net_device * dev )
{
struct neighbour * neighbor_entry ;
neighbor_entry = neigh_lookup ( & arp_tbl , & paddr , dev ) ;
if ( neighbor_entry ! = NULL ) {
neighbor_entry - > used = jiffies ;
if ( neighbor_entry - > nud_state & NUD_VALID ) {
memcpy ( haddr , neighbor_entry - > ha , dev - > addr_len ) ;
return 1 ;
}
}
return 0 ;
}
static void DumpData ( char * msg , struct strip * strip_info , __u8 * ptr ,
__u8 * end )
{
static const int MAX_DumpData = 80 ;
__u8 pkt_text [ MAX_DumpData ] , * p = pkt_text ;
* p + + = ' \" ' ;
while ( ptr < end & & p < & pkt_text [ MAX_DumpData - 4 ] ) {
if ( * ptr = = ' \\ ' ) {
* p + + = ' \\ ' ;
* p + + = ' \\ ' ;
} else {
if ( * ptr > = 32 & & * ptr < = 126 ) {
* p + + = * ptr ;
} else {
sprintf ( p , " \\ %02X " , * ptr ) ;
p + = 3 ;
}
}
ptr + + ;
}
if ( ptr = = end )
* p + + = ' \" ' ;
* p + + = 0 ;
printk ( KERN_INFO " %s: %-13s%s \n " , strip_info - > dev - > name , msg , pkt_text ) ;
}
/************************************************************************/
/* Byte stuffing/unstuffing routines */
/* Stuffing scheme:
* 00 Unused ( reserved character )
* 01 - 3F Run of 2 - 64 different characters
* 40 - 7F Run of 1 - 64 different characters plus a single zero at the end
* 80 - BF Run of 1 - 64 of the same character
* C0 - FF Run of 1 - 64 zeroes ( ASCII 0 )
*/
typedef enum {
Stuff_Diff = 0x00 ,
Stuff_DiffZero = 0x40 ,
Stuff_Same = 0x80 ,
Stuff_Zero = 0xC0 ,
Stuff_NoCode = 0xFF , /* Special code, meaning no code selected */
Stuff_CodeMask = 0xC0 ,
Stuff_CountMask = 0x3F ,
Stuff_MaxCount = 0x3F ,
Stuff_Magic = 0x0D /* The value we are eliminating */
} StuffingCode ;
/* StuffData encodes the data starting at "src" for "length" bytes.
* It writes it to the buffer pointed to by " dst " ( which must be at least
* as long as 1 + 65 / 64 of the input length ) . The output may be up to 1.6 %
* larger than the input for pathological input , but will usually be smaller .
* StuffData returns the new value of the dst pointer as its result .
* " code_ptr_ptr " points to a " __u8 * " which is used to hold encoding state
* between calls , allowing an encoded packet to be incrementally built up
* from small parts . On the first call , the " __u8 * " pointed to should be
* initialized to NULL ; between subsequent calls the calling routine should
* leave the value alone and simply pass it back unchanged so that the
* encoder can recover its current state .
*/
# define StuffData_FinishBlock(X) \
( * code_ptr = ( X ) ^ Stuff_Magic , code = Stuff_NoCode )
static __u8 * StuffData ( __u8 * src , __u32 length , __u8 * dst ,
__u8 * * code_ptr_ptr )
{
__u8 * end = src + length ;
__u8 * code_ptr = * code_ptr_ptr ;
__u8 code = Stuff_NoCode , count = 0 ;
if ( ! length )
return ( dst ) ;
if ( code_ptr ) {
/*
* Recover state from last call , if applicable
*/
code = ( * code_ptr ^ Stuff_Magic ) & Stuff_CodeMask ;
count = ( * code_ptr ^ Stuff_Magic ) & Stuff_CountMask ;
}
while ( src < end ) {
switch ( code ) {
/* Stuff_NoCode: If no current code, select one */
case Stuff_NoCode :
/* Record where we're going to put this code */
code_ptr = dst + + ;
count = 0 ; /* Reset the count (zero means one instance) */
/* Tentatively start a new block */
if ( * src = = 0 ) {
code = Stuff_Zero ;
src + + ;
} else {
code = Stuff_Same ;
* dst + + = * src + + ^ Stuff_Magic ;
}
/* Note: We optimistically assume run of same -- */
/* which will be fixed later in Stuff_Same */
/* if it turns out not to be true. */
break ;
/* Stuff_Zero: We already have at least one zero encoded */
case Stuff_Zero :
/* If another zero, count it, else finish this code block */
if ( * src = = 0 ) {
count + + ;
src + + ;
} else {
StuffData_FinishBlock ( Stuff_Zero + count ) ;
}
break ;
/* Stuff_Same: We already have at least one byte encoded */
case Stuff_Same :
/* If another one the same, count it */
if ( ( * src ^ Stuff_Magic ) = = code_ptr [ 1 ] ) {
count + + ;
src + + ;
break ;
}
/* else, this byte does not match this block. */
/* If we already have two or more bytes encoded, finish this code block */
if ( count ) {
StuffData_FinishBlock ( Stuff_Same + count ) ;
break ;
}
/* else, we only have one so far, so switch to Stuff_Diff code */
code = Stuff_Diff ;
/* and fall through to Stuff_Diff case below
* Note cunning cleverness here : case Stuff_Diff compares
* the current character with the previous two to see if it
* has a run of three the same . Won ' t this be an error if
* there aren ' t two previous characters stored to compare with ?
* No . Because we know the current character is * not * the same
* as the previous one , the first test below will necessarily
* fail and the send half of the " if " won ' t be executed .
*/
/* Stuff_Diff: We have at least two *different* bytes encoded */
case Stuff_Diff :
/* If this is a zero, must encode a Stuff_DiffZero, and begin a new block */
if ( * src = = 0 ) {
StuffData_FinishBlock ( Stuff_DiffZero +
count ) ;
}
/* else, if we have three in a row, it is worth starting a Stuff_Same block */
else if ( ( * src ^ Stuff_Magic ) = = dst [ - 1 ]
& & dst [ - 1 ] = = dst [ - 2 ] ) {
/* Back off the last two characters we encoded */
code + = count - 2 ;
/* Note: "Stuff_Diff + 0" is an illegal code */
if ( code = = Stuff_Diff + 0 ) {
code = Stuff_Same + 0 ;
}
StuffData_FinishBlock ( code ) ;
code_ptr = dst - 2 ;
/* dst[-1] already holds the correct value */
count = 2 ; /* 2 means three bytes encoded */
code = Stuff_Same ;
}
/* else, another different byte, so add it to the block */
else {
* dst + + = * src ^ Stuff_Magic ;
count + + ;
}
src + + ; /* Consume the byte */
break ;
}
if ( count = = Stuff_MaxCount ) {
StuffData_FinishBlock ( code + count ) ;
}
}
if ( code = = Stuff_NoCode ) {
* code_ptr_ptr = NULL ;
} else {
* code_ptr_ptr = code_ptr ;
StuffData_FinishBlock ( code + count ) ;
}
return ( dst ) ;
}
/*
* UnStuffData decodes the data at " src " , up to ( but not including ) " end " .
* It writes the decoded data into the buffer pointed to by " dst " , up to a
* maximum of " dst_length " , and returns the new value of " src " so that a
* follow - on call can read more data , continuing from where the first left off .
*
* There are three types of results :
* 1. The source data runs out before extracting " dst_length " bytes :
* UnStuffData returns NULL to indicate failure .
* 2. The source data produces exactly " dst_length " bytes :
* UnStuffData returns new_src = end to indicate that all bytes were consumed .
* 3. " dst_length " bytes are extracted , with more remaining .
* UnStuffData returns new_src < end to indicate that there are more bytes
* to be read .
*
* Note : The decoding may be destructive , in that it may alter the source
* data in the process of decoding it ( this is necessary to allow a follow - on
* call to resume correctly ) .
*/
static __u8 * UnStuffData ( __u8 * src , __u8 * end , __u8 * dst ,
__u32 dst_length )
{
__u8 * dst_end = dst + dst_length ;
/* Sanity check */
if ( ! src | | ! end | | ! dst | | ! dst_length )
return ( NULL ) ;
while ( src < end & & dst < dst_end ) {
int count = ( * src ^ Stuff_Magic ) & Stuff_CountMask ;
switch ( ( * src ^ Stuff_Magic ) & Stuff_CodeMask ) {
case Stuff_Diff :
if ( src + 1 + count > = end )
return ( NULL ) ;
do {
* dst + + = * + + src ^ Stuff_Magic ;
}
while ( - - count > = 0 & & dst < dst_end ) ;
if ( count < 0 )
src + = 1 ;
else {
if ( count = = 0 )
* src = Stuff_Same ^ Stuff_Magic ;
else
* src =
( Stuff_Diff +
count ) ^ Stuff_Magic ;
}
break ;
case Stuff_DiffZero :
if ( src + 1 + count > = end )
return ( NULL ) ;
do {
* dst + + = * + + src ^ Stuff_Magic ;
}
while ( - - count > = 0 & & dst < dst_end ) ;
if ( count < 0 )
* src = Stuff_Zero ^ Stuff_Magic ;
else
* src =
( Stuff_DiffZero + count ) ^ Stuff_Magic ;
break ;
case Stuff_Same :
if ( src + 1 > = end )
return ( NULL ) ;
do {
* dst + + = src [ 1 ] ^ Stuff_Magic ;
}
while ( - - count > = 0 & & dst < dst_end ) ;
if ( count < 0 )
src + = 2 ;
else
* src = ( Stuff_Same + count ) ^ Stuff_Magic ;
break ;
case Stuff_Zero :
do {
* dst + + = 0 ;
}
while ( - - count > = 0 & & dst < dst_end ) ;
if ( count < 0 )
src + = 1 ;
else
* src = ( Stuff_Zero + count ) ^ Stuff_Magic ;
break ;
}
}
if ( dst < dst_end )
return ( NULL ) ;
else
return ( src ) ;
}
/************************************************************************/
/* General routines for STRIP */
/*
* get_baud returns the current baud rate , as one of the constants defined in
* termbits . h
* If the user has issued a baud rate override using the ' setserial ' command
* and the logical current rate is set to 38.4 , then the true baud rate
* currently in effect ( 57.6 or 115.2 ) is returned .
*/
static unsigned int get_baud ( struct tty_struct * tty )
{
if ( ! tty | | ! tty - > termios )
return ( 0 ) ;
if ( ( tty - > termios - > c_cflag & CBAUD ) = = B38400 & & tty - > driver_data ) {
struct async_struct * info =
( struct async_struct * ) tty - > driver_data ;
if ( ( info - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_HI )
return ( B57600 ) ;
if ( ( info - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_VHI )
return ( B115200 ) ;
}
return ( tty - > termios - > c_cflag & CBAUD ) ;
}
/*
* set_baud sets the baud rate to the rate defined by baudcode
* Note : The rate B38400 should be avoided , because the user may have
* issued a ' setserial ' speed override to map that to a different speed .
* We could achieve a true rate of 38400 if we needed to by cancelling
* any user speed override that is in place , but that might annoy the
* user , so it is simplest to just avoid using 38400.
*/
static void set_baud ( struct tty_struct * tty , unsigned int baudcode )
{
struct termios old_termios = * ( tty - > termios ) ;
tty - > termios - > c_cflag & = ~ CBAUD ; /* Clear the old baud setting */
tty - > termios - > c_cflag | = baudcode ; /* Set the new baud setting */
tty - > driver - > set_termios ( tty , & old_termios ) ;
}
/*
* Convert a string to a Metricom Address .
*/
# define IS_RADIO_ADDRESS(p) ( \
isdigit ( ( p ) [ 0 ] ) & & isdigit ( ( p ) [ 1 ] ) & & isdigit ( ( p ) [ 2 ] ) & & isdigit ( ( p ) [ 3 ] ) & & \
( p ) [ 4 ] = = ' - ' & & \
isdigit ( ( p ) [ 5 ] ) & & isdigit ( ( p ) [ 6 ] ) & & isdigit ( ( p ) [ 7 ] ) & & isdigit ( ( p ) [ 8 ] ) )
static int string_to_radio_address ( MetricomAddress * addr , __u8 * p )
{
if ( ! IS_RADIO_ADDRESS ( p ) )
return ( 1 ) ;
addr - > c [ 0 ] = 0 ;
addr - > c [ 1 ] = 0 ;
addr - > c [ 2 ] = READHEX ( p [ 0 ] ) < < 4 | READHEX ( p [ 1 ] ) ;
addr - > c [ 3 ] = READHEX ( p [ 2 ] ) < < 4 | READHEX ( p [ 3 ] ) ;
addr - > c [ 4 ] = READHEX ( p [ 5 ] ) < < 4 | READHEX ( p [ 6 ] ) ;
addr - > c [ 5 ] = READHEX ( p [ 7 ] ) < < 4 | READHEX ( p [ 8 ] ) ;
return ( 0 ) ;
}
/*
* Convert a Metricom Address to a string .
*/
static __u8 * radio_address_to_string ( const MetricomAddress * addr ,
MetricomAddressString * p )
{
sprintf ( p - > c , " %02X%02X-%02X%02X " , addr - > c [ 2 ] , addr - > c [ 3 ] ,
addr - > c [ 4 ] , addr - > c [ 5 ] ) ;
return ( p - > c ) ;
}
/*
* Note : Must make sure sx_size is big enough to receive a stuffed
* MAX_RECV_MTU packet . Additionally , we also want to ensure that it ' s
* big enough to receive a large radio neighbour list ( currently 4 K ) .
*/
static int allocate_buffers ( struct strip * strip_info , int mtu )
{
struct net_device * dev = strip_info - > dev ;
int sx_size = max_t ( int , STRIP_ENCAP_SIZE ( MAX_RECV_MTU ) , 4096 ) ;
int tx_size = STRIP_ENCAP_SIZE ( mtu ) + MaxCommandStringLength ;
__u8 * r = kmalloc ( MAX_RECV_MTU , GFP_ATOMIC ) ;
__u8 * s = kmalloc ( sx_size , GFP_ATOMIC ) ;
__u8 * t = kmalloc ( tx_size , GFP_ATOMIC ) ;
if ( r & & s & & t ) {
strip_info - > rx_buff = r ;
strip_info - > sx_buff = s ;
strip_info - > tx_buff = t ;
strip_info - > sx_size = sx_size ;
strip_info - > tx_size = tx_size ;
strip_info - > mtu = dev - > mtu = mtu ;
return ( 1 ) ;
}
2005-10-29 00:53:13 +04:00
kfree ( r ) ;
kfree ( s ) ;
kfree ( t ) ;
2005-04-17 02:20:36 +04:00
return ( 0 ) ;
}
/*
* MTU has been changed by the IP layer .
* We could be in
* an upcall from the tty driver , or in an ip packet queue .
*/
static int strip_change_mtu ( struct net_device * dev , int new_mtu )
{
struct strip * strip_info = netdev_priv ( dev ) ;
int old_mtu = strip_info - > mtu ;
unsigned char * orbuff = strip_info - > rx_buff ;
unsigned char * osbuff = strip_info - > sx_buff ;
unsigned char * otbuff = strip_info - > tx_buff ;
if ( new_mtu > MAX_SEND_MTU ) {
printk ( KERN_ERR
" %s: MTU exceeds maximum allowable (%d), MTU change cancelled. \n " ,
strip_info - > dev - > name , MAX_SEND_MTU ) ;
return - EINVAL ;
}
spin_lock_bh ( & strip_lock ) ;
if ( ! allocate_buffers ( strip_info , new_mtu ) ) {
printk ( KERN_ERR " %s: unable to grow strip buffers, MTU change cancelled. \n " ,
strip_info - > dev - > name ) ;
spin_unlock_bh ( & strip_lock ) ;
return - ENOMEM ;
}
if ( strip_info - > sx_count ) {
if ( strip_info - > sx_count < = strip_info - > sx_size )
memcpy ( strip_info - > sx_buff , osbuff ,
strip_info - > sx_count ) ;
else {
strip_info - > discard = strip_info - > sx_count ;
strip_info - > rx_over_errors + + ;
}
}
if ( strip_info - > tx_left ) {
if ( strip_info - > tx_left < = strip_info - > tx_size )
memcpy ( strip_info - > tx_buff , strip_info - > tx_head ,
strip_info - > tx_left ) ;
else {
strip_info - > tx_left = 0 ;
strip_info - > tx_dropped + + ;
}
}
strip_info - > tx_head = strip_info - > tx_buff ;
spin_unlock_bh ( & strip_lock ) ;
printk ( KERN_NOTICE " %s: strip MTU changed fom %d to %d. \n " ,
strip_info - > dev - > name , old_mtu , strip_info - > mtu ) ;
2005-10-29 00:53:13 +04:00
kfree ( orbuff ) ;
kfree ( osbuff ) ;
kfree ( otbuff ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void strip_unlock ( struct strip * strip_info )
{
/*
* Set the timer to go off in one second .
*/
strip_info - > idle_timer . expires = jiffies + 1 * HZ ;
add_timer ( & strip_info - > idle_timer ) ;
netif_wake_queue ( strip_info - > dev ) ;
}
/*
* If the time is in the near future , time_delta prints the number of
* seconds to go into the buffer and returns the address of the buffer .
* If the time is not in the near future , it returns the address of the
* string " Not scheduled " The buffer must be long enough to contain the
* ascii representation of the number plus 9 charactes for the " seconds "
* and the null character .
*/
# ifdef CONFIG_PROC_FS
static char * time_delta ( char buffer [ ] , long time )
{
time - = jiffies ;
if ( time > LongTime / 2 )
return ( " Not scheduled " ) ;
if ( time < 0 )
time = 0 ; /* Don't print negative times */
sprintf ( buffer , " %ld seconds " , time / HZ ) ;
return ( buffer ) ;
}
/* get Nth element of the linked list */
static struct strip * strip_get_idx ( loff_t pos )
{
struct list_head * l ;
int i = 0 ;
list_for_each_rcu ( l , & strip_list ) {
if ( pos = = i )
return list_entry ( l , struct strip , list ) ;
+ + i ;
}
return NULL ;
}
static void * strip_seq_start ( struct seq_file * seq , loff_t * pos )
{
rcu_read_lock ( ) ;
return * pos ? strip_get_idx ( * pos - 1 ) : SEQ_START_TOKEN ;
}
static void * strip_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct list_head * l ;
struct strip * s ;
+ + * pos ;
if ( v = = SEQ_START_TOKEN )
return strip_get_idx ( 1 ) ;
s = v ;
l = & s - > list ;
list_for_each_continue_rcu ( l , & strip_list ) {
return list_entry ( l , struct strip , list ) ;
}
return NULL ;
}
static void strip_seq_stop ( struct seq_file * seq , void * v )
{
rcu_read_unlock ( ) ;
}
static void strip_seq_neighbours ( struct seq_file * seq ,
const MetricomNodeTable * table ,
const char * title )
{
/* We wrap this in a do/while loop, so if the table changes */
/* while we're reading it, we just go around and try again. */
struct timeval t ;
do {
int i ;
t = table - > timestamp ;
if ( table - > num_nodes )
seq_printf ( seq , " \n %s \n " , title ) ;
for ( i = 0 ; i < table - > num_nodes ; i + + ) {
MetricomNode node ;
spin_lock_bh ( & strip_lock ) ;
node = table - > node [ i ] ;
spin_unlock_bh ( & strip_lock ) ;
seq_printf ( seq , " %s \n " , node . c ) ;
}
} while ( table - > timestamp . tv_sec ! = t . tv_sec
| | table - > timestamp . tv_usec ! = t . tv_usec ) ;
}
/*
* This function prints radio status information via the seq_file
* interface . The interface takes care of buffer size and over
* run issues .
*
* The buffer in seq_file is PAGESIZE ( 4 K )
* so this routine should never print more or it will get truncated .
* With the maximum of 32 portables and 32 poletops
* reported , the routine outputs 3107 bytes into the buffer .
*/
static void strip_seq_status_info ( struct seq_file * seq ,
const struct strip * strip_info )
{
char temp [ 32 ] ;
MetricomAddressString addr_string ;
/* First, we must copy all of our data to a safe place, */
/* in case a serial interrupt comes in and changes it. */
int tx_left = strip_info - > tx_left ;
unsigned long rx_average_pps = strip_info - > rx_average_pps ;
unsigned long tx_average_pps = strip_info - > tx_average_pps ;
unsigned long sx_average_pps = strip_info - > sx_average_pps ;
int working = strip_info - > working ;
int firmware_level = strip_info - > firmware_level ;
long watchdog_doprobe = strip_info - > watchdog_doprobe ;
long watchdog_doreset = strip_info - > watchdog_doreset ;
long gratuitous_arp = strip_info - > gratuitous_arp ;
long arp_interval = strip_info - > arp_interval ;
FirmwareVersion firmware_version = strip_info - > firmware_version ;
SerialNumber serial_number = strip_info - > serial_number ;
BatteryVoltage battery_voltage = strip_info - > battery_voltage ;
char * if_name = strip_info - > dev - > name ;
MetricomAddress true_dev_addr = strip_info - > true_dev_addr ;
MetricomAddress dev_dev_addr =
* ( MetricomAddress * ) strip_info - > dev - > dev_addr ;
int manual_dev_addr = strip_info - > manual_dev_addr ;
# ifdef EXT_COUNTERS
unsigned long rx_bytes = strip_info - > rx_bytes ;
unsigned long tx_bytes = strip_info - > tx_bytes ;
unsigned long rx_rbytes = strip_info - > rx_rbytes ;
unsigned long tx_rbytes = strip_info - > tx_rbytes ;
unsigned long rx_sbytes = strip_info - > rx_sbytes ;
unsigned long tx_sbytes = strip_info - > tx_sbytes ;
unsigned long rx_ebytes = strip_info - > rx_ebytes ;
unsigned long tx_ebytes = strip_info - > tx_ebytes ;
# endif
seq_printf ( seq , " \n Interface name \t \t %s \n " , if_name ) ;
seq_printf ( seq , " Radio working: \t \t %s \n " , working ? " Yes " : " No " ) ;
radio_address_to_string ( & true_dev_addr , & addr_string ) ;
seq_printf ( seq , " Radio address: \t \t %s \n " , addr_string . c ) ;
if ( manual_dev_addr ) {
radio_address_to_string ( & dev_dev_addr , & addr_string ) ;
seq_printf ( seq , " Device address: \t %s \n " , addr_string . c ) ;
}
seq_printf ( seq , " Firmware version: \t %s " , ! working ? " Unknown " :
! firmware_level ? " Should be upgraded " :
firmware_version . c ) ;
if ( firmware_level > = ChecksummedMessages )
seq_printf ( seq , " (Checksums Enabled) " ) ;
seq_printf ( seq , " \n " ) ;
seq_printf ( seq , " Serial number: \t \t %s \n " , serial_number . c ) ;
seq_printf ( seq , " Battery voltage: \t %s \n " , battery_voltage . c ) ;
seq_printf ( seq , " Transmit queue (bytes):%d \n " , tx_left ) ;
seq_printf ( seq , " Receive packet rate: %ld packets per second \n " ,
rx_average_pps / 8 ) ;
seq_printf ( seq , " Transmit packet rate: %ld packets per second \n " ,
tx_average_pps / 8 ) ;
seq_printf ( seq , " Sent packet rate: %ld packets per second \n " ,
sx_average_pps / 8 ) ;
seq_printf ( seq , " Next watchdog probe: \t %s \n " ,
time_delta ( temp , watchdog_doprobe ) ) ;
seq_printf ( seq , " Next watchdog reset: \t %s \n " ,
time_delta ( temp , watchdog_doreset ) ) ;
seq_printf ( seq , " Next gratuitous ARP: \t " ) ;
if ( ! memcmp
( strip_info - > dev - > dev_addr , zero_address . c ,
sizeof ( zero_address ) ) )
seq_printf ( seq , " Disabled \n " ) ;
else {
seq_printf ( seq , " %s \n " , time_delta ( temp , gratuitous_arp ) ) ;
seq_printf ( seq , " Next ARP interval: \t %ld seconds \n " ,
JIFFIE_TO_SEC ( arp_interval ) ) ;
}
if ( working ) {
# ifdef EXT_COUNTERS
seq_printf ( seq , " \n " ) ;
seq_printf ( seq ,
" Total bytes: \t rx: \t %lu \t tx: \t %lu \n " ,
rx_bytes , tx_bytes ) ;
seq_printf ( seq ,
" thru radio: \t rx: \t %lu \t tx: \t %lu \n " ,
rx_rbytes , tx_rbytes ) ;
seq_printf ( seq ,
" thru serial port: \t rx: \t %lu \t tx: \t %lu \n " ,
rx_sbytes , tx_sbytes ) ;
seq_printf ( seq ,
" Total stat/err bytes: \t rx: \t %lu \t tx: \t %lu \n " ,
rx_ebytes , tx_ebytes ) ;
# endif
strip_seq_neighbours ( seq , & strip_info - > poletops ,
" Poletops: " ) ;
strip_seq_neighbours ( seq , & strip_info - > portables ,
" Portables: " ) ;
}
}
/*
* This function is exports status information from the STRIP driver through
* the / proc file system .
*/
static int strip_seq_show ( struct seq_file * seq , void * v )
{
if ( v = = SEQ_START_TOKEN )
seq_printf ( seq , " strip_version: %s \n " , StripVersion ) ;
else
strip_seq_status_info ( seq , ( const struct strip * ) v ) ;
return 0 ;
}
static struct seq_operations strip_seq_ops = {
. start = strip_seq_start ,
. next = strip_seq_next ,
. stop = strip_seq_stop ,
. show = strip_seq_show ,
} ;
static int strip_seq_open ( struct inode * inode , struct file * file )
{
return seq_open ( file , & strip_seq_ops ) ;
}
static struct file_operations strip_seq_fops = {
. owner = THIS_MODULE ,
. open = strip_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
# endif
/************************************************************************/
/* Sending routines */
static void ResetRadio ( struct strip * strip_info )
{
struct tty_struct * tty = strip_info - > tty ;
static const char init [ ] = " ate0q1dt**starmode \r ** " ;
StringDescriptor s = { init , sizeof ( init ) - 1 } ;
/*
* If the radio isn ' t working anymore ,
* we should clear the old status information .
*/
if ( strip_info - > working ) {
printk ( KERN_INFO " %s: No response: Resetting radio. \n " ,
strip_info - > dev - > name ) ;
strip_info - > firmware_version . c [ 0 ] = ' \0 ' ;
strip_info - > serial_number . c [ 0 ] = ' \0 ' ;
strip_info - > battery_voltage . c [ 0 ] = ' \0 ' ;
strip_info - > portables . num_nodes = 0 ;
do_gettimeofday ( & strip_info - > portables . timestamp ) ;
strip_info - > poletops . num_nodes = 0 ;
do_gettimeofday ( & strip_info - > poletops . timestamp ) ;
}
strip_info - > pps_timer = jiffies ;
strip_info - > rx_pps_count = 0 ;
strip_info - > tx_pps_count = 0 ;
strip_info - > sx_pps_count = 0 ;
strip_info - > rx_average_pps = 0 ;
strip_info - > tx_average_pps = 0 ;
strip_info - > sx_average_pps = 0 ;
/* Mark radio address as unknown */
* ( MetricomAddress * ) & strip_info - > true_dev_addr = zero_address ;
if ( ! strip_info - > manual_dev_addr )
* ( MetricomAddress * ) strip_info - > dev - > dev_addr =
zero_address ;
strip_info - > working = FALSE ;
strip_info - > firmware_level = NoStructure ;
strip_info - > next_command = CompatibilityCommand ;
strip_info - > watchdog_doprobe = jiffies + 10 * HZ ;
strip_info - > watchdog_doreset = jiffies + 1 * HZ ;
/* If the user has selected a baud rate above 38.4 see what magic we have to do */
if ( strip_info - > user_baud > B38400 ) {
/*
* Subtle stuff : Pay attention : - )
* If the serial port is currently at the user ' s selected ( > 38.4 ) rate ,
* then we temporarily switch to 19.2 and issue the ATS304 command
* to tell the radio to switch to the user ' s selected rate .
* If the serial port is not currently at that rate , that means we just
* issued the ATS304 command last time through , so this time we restore
* the user ' s selected rate and issue the normal starmode reset string .
*/
if ( strip_info - > user_baud = = get_baud ( tty ) ) {
static const char b0 [ ] = " ate0q1s304=57600 \r " ;
static const char b1 [ ] = " ate0q1s304=115200 \r " ;
static const StringDescriptor baudstring [ 2 ] =
{ { b0 , sizeof ( b0 ) - 1 }
, { b1 , sizeof ( b1 ) - 1 }
} ;
set_baud ( tty , B19200 ) ;
if ( strip_info - > user_baud = = B57600 )
s = baudstring [ 0 ] ;
else if ( strip_info - > user_baud = = B115200 )
s = baudstring [ 1 ] ;
else
s = baudstring [ 1 ] ; /* For now */
} else
set_baud ( tty , strip_info - > user_baud ) ;
}
tty - > driver - > write ( tty , s . string , s . length ) ;
# ifdef EXT_COUNTERS
strip_info - > tx_ebytes + = s . length ;
# endif
}
/*
* Called by the driver when there ' s room for more data . If we have
* more packets to send , we send them here .
*/
static void strip_write_some_more ( struct tty_struct * tty )
{
struct strip * strip_info = ( struct strip * ) tty - > disc_data ;
/* First make sure we're connected. */
if ( ! strip_info | | strip_info - > magic ! = STRIP_MAGIC | |
! netif_running ( strip_info - > dev ) )
return ;
if ( strip_info - > tx_left > 0 ) {
int num_written =
tty - > driver - > write ( tty , strip_info - > tx_head ,
strip_info - > tx_left ) ;
strip_info - > tx_left - = num_written ;
strip_info - > tx_head + = num_written ;
# ifdef EXT_COUNTERS
strip_info - > tx_sbytes + = num_written ;
# endif
} else { /* Else start transmission of another packet */
tty - > flags & = ~ ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
strip_unlock ( strip_info ) ;
}
}
static __u8 * add_checksum ( __u8 * buffer , __u8 * end )
{
__u16 sum = 0 ;
__u8 * p = buffer ;
while ( p < end )
sum + = * p + + ;
end [ 3 ] = hextable [ sum & 0xF ] ;
sum > > = 4 ;
end [ 2 ] = hextable [ sum & 0xF ] ;
sum > > = 4 ;
end [ 1 ] = hextable [ sum & 0xF ] ;
sum > > = 4 ;
end [ 0 ] = hextable [ sum & 0xF ] ;
return ( end + 4 ) ;
}
static unsigned char * strip_make_packet ( unsigned char * buffer ,
struct strip * strip_info ,
struct sk_buff * skb )
{
__u8 * ptr = buffer ;
__u8 * stuffstate = NULL ;
STRIP_Header * header = ( STRIP_Header * ) skb - > data ;
MetricomAddress haddr = header - > dst_addr ;
int len = skb - > len - sizeof ( STRIP_Header ) ;
MetricomKey key ;
/*HexDump("strip_make_packet", strip_info, skb->data, skb->data + skb->len); */
if ( header - > protocol = = htons ( ETH_P_IP ) )
key = SIP0Key ;
else if ( header - > protocol = = htons ( ETH_P_ARP ) )
key = ARP0Key ;
else {
printk ( KERN_ERR
" %s: strip_make_packet: Unknown packet type 0x%04X \n " ,
strip_info - > dev - > name , ntohs ( header - > protocol ) ) ;
return ( NULL ) ;
}
if ( len > strip_info - > mtu ) {
printk ( KERN_ERR
" %s: Dropping oversized transmit packet: %d bytes \n " ,
strip_info - > dev - > name , len ) ;
return ( NULL ) ;
}
/*
* If we ' re sending to ourselves , discard the packet .
* ( Metricom radios choke if they try to send a packet to their own address . )
*/
if ( ! memcmp ( haddr . c , strip_info - > true_dev_addr . c , sizeof ( haddr ) ) ) {
printk ( KERN_ERR " %s: Dropping packet addressed to self \n " ,
strip_info - > dev - > name ) ;
return ( NULL ) ;
}
/*
* If this is a broadcast packet , send it to our designated Metricom
* ' broadcast hub ' radio ( First byte of address being 0xFF means broadcast )
*/
if ( haddr . c [ 0 ] = = 0xFF ) {
u32 brd = 0 ;
struct in_device * in_dev ;
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( strip_info - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev = = NULL ) {
rcu_read_unlock ( ) ;
return NULL ;
}
if ( in_dev - > ifa_list )
brd = in_dev - > ifa_list - > ifa_broadcast ;
rcu_read_unlock ( ) ;
/* arp_query returns 1 if it succeeds in looking up the address, 0 if it fails */
if ( ! arp_query ( haddr . c , brd , strip_info - > dev ) ) {
printk ( KERN_ERR
" %s: Unable to send packet (no broadcast hub configured) \n " ,
strip_info - > dev - > name ) ;
return ( NULL ) ;
}
/*
* If we are the broadcast hub , don ' t bother sending to ourselves .
* ( Metricom radios choke if they try to send a packet to their own address . )
*/
if ( ! memcmp
( haddr . c , strip_info - > true_dev_addr . c , sizeof ( haddr ) ) )
return ( NULL ) ;
}
* ptr + + = 0x0D ;
* ptr + + = ' * ' ;
* ptr + + = hextable [ haddr . c [ 2 ] > > 4 ] ;
* ptr + + = hextable [ haddr . c [ 2 ] & 0xF ] ;
* ptr + + = hextable [ haddr . c [ 3 ] > > 4 ] ;
* ptr + + = hextable [ haddr . c [ 3 ] & 0xF ] ;
* ptr + + = ' - ' ;
* ptr + + = hextable [ haddr . c [ 4 ] > > 4 ] ;
* ptr + + = hextable [ haddr . c [ 4 ] & 0xF ] ;
* ptr + + = hextable [ haddr . c [ 5 ] > > 4 ] ;
* ptr + + = hextable [ haddr . c [ 5 ] & 0xF ] ;
* ptr + + = ' * ' ;
* ptr + + = key . c [ 0 ] ;
* ptr + + = key . c [ 1 ] ;
* ptr + + = key . c [ 2 ] ;
* ptr + + = key . c [ 3 ] ;
ptr =
StuffData ( skb - > data + sizeof ( STRIP_Header ) , len , ptr ,
& stuffstate ) ;
if ( strip_info - > firmware_level > = ChecksummedMessages )
ptr = add_checksum ( buffer + 1 , ptr ) ;
* ptr + + = 0x0D ;
return ( ptr ) ;
}
static void strip_send ( struct strip * strip_info , struct sk_buff * skb )
{
MetricomAddress haddr ;
unsigned char * ptr = strip_info - > tx_buff ;
int doreset = ( long ) jiffies - strip_info - > watchdog_doreset > = 0 ;
int doprobe = ( long ) jiffies - strip_info - > watchdog_doprobe > = 0
& & ! doreset ;
u32 addr , brd ;
/*
* 1. If we have a packet , encapsulate it and put it in the buffer
*/
if ( skb ) {
char * newptr = strip_make_packet ( ptr , strip_info , skb ) ;
strip_info - > tx_pps_count + + ;
if ( ! newptr )
strip_info - > tx_dropped + + ;
else {
ptr = newptr ;
strip_info - > sx_pps_count + + ;
strip_info - > tx_packets + + ; /* Count another successful packet */
# ifdef EXT_COUNTERS
strip_info - > tx_bytes + = skb - > len ;
strip_info - > tx_rbytes + = ptr - strip_info - > tx_buff ;
# endif
/*DumpData("Sending:", strip_info, strip_info->tx_buff, ptr); */
/*HexDump("Sending", strip_info, strip_info->tx_buff, ptr); */
}
}
/*
* 2. If it is time for another tickle , tack it on , after the packet
*/
if ( doprobe ) {
StringDescriptor ts = CommandString [ strip_info - > next_command ] ;
# if TICKLE_TIMERS
{
struct timeval tv ;
do_gettimeofday ( & tv ) ;
printk ( KERN_INFO " **** Sending tickle string %d at %02d.%06d \n " ,
strip_info - > next_command , tv . tv_sec % 100 ,
tv . tv_usec ) ;
}
# endif
if ( ptr = = strip_info - > tx_buff )
* ptr + + = 0x0D ;
* ptr + + = ' * ' ; /* First send "**" to provoke an error message */
* ptr + + = ' * ' ;
/* Then add the command */
memcpy ( ptr , ts . string , ts . length ) ;
/* Add a checksum ? */
if ( strip_info - > firmware_level < ChecksummedMessages )
ptr + = ts . length ;
else
ptr = add_checksum ( ptr , ptr + ts . length ) ;
* ptr + + = 0x0D ; /* Terminate the command with a <CR> */
/* Cycle to next periodic command? */
if ( strip_info - > firmware_level > = StructuredMessages )
if ( + + strip_info - > next_command > =
ARRAY_SIZE ( CommandString ) )
strip_info - > next_command = 0 ;
# ifdef EXT_COUNTERS
strip_info - > tx_ebytes + = ts . length ;
# endif
strip_info - > watchdog_doprobe = jiffies + 10 * HZ ;
strip_info - > watchdog_doreset = jiffies + 1 * HZ ;
/*printk(KERN_INFO "%s: Routine radio test.\n", strip_info->dev->name); */
}
/*
* 3. Set up the strip_info ready to send the data ( if any ) .
*/
strip_info - > tx_head = strip_info - > tx_buff ;
strip_info - > tx_left = ptr - strip_info - > tx_buff ;
strip_info - > tty - > flags | = ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
/*
* 4. Debugging check to make sure we ' re not overflowing the buffer .
*/
if ( strip_info - > tx_size - strip_info - > tx_left < 20 )
printk ( KERN_ERR " %s: Sending%5d bytes;%5d bytes free. \n " ,
strip_info - > dev - > name , strip_info - > tx_left ,
strip_info - > tx_size - strip_info - > tx_left ) ;
/*
* 5. If watchdog has expired , reset the radio . Note : if there ' s data waiting in
* the buffer , strip_write_some_more will send it after the reset has finished
*/
if ( doreset ) {
ResetRadio ( strip_info ) ;
return ;
}
if ( 1 ) {
struct in_device * in_dev ;
brd = addr = 0 ;
rcu_read_lock ( ) ;
2005-10-04 01:35:55 +04:00
in_dev = __in_dev_get_rcu ( strip_info - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( in_dev ) {
if ( in_dev - > ifa_list ) {
brd = in_dev - > ifa_list - > ifa_broadcast ;
addr = in_dev - > ifa_list - > ifa_local ;
}
}
rcu_read_unlock ( ) ;
}
/*
* 6. If it is time for a periodic ARP , queue one up to be sent .
* We only do this if :
* 1. The radio is working
* 2. It ' s time to send another periodic ARP
* 3. We really know what our address is ( and it is not manually set to zero )
* 4. We have a designated broadcast address configured
* If we queue up an ARP packet when we don ' t have a designated broadcast
* address configured , then the packet will just have to be discarded in
* strip_make_packet . This is not fatal , but it causes misleading information
* to be displayed in tcpdump . tcpdump will report that periodic APRs are
* being sent , when in fact they are not , because they are all being dropped
* in the strip_make_packet routine .
*/
if ( strip_info - > working
& & ( long ) jiffies - strip_info - > gratuitous_arp > = 0
& & memcmp ( strip_info - > dev - > dev_addr , zero_address . c ,
sizeof ( zero_address ) )
& & arp_query ( haddr . c , brd , strip_info - > dev ) ) {
/*printk(KERN_INFO "%s: Sending gratuitous ARP with interval %ld\n",
strip_info - > dev - > name , strip_info - > arp_interval / HZ ) ; */
strip_info - > gratuitous_arp =
jiffies + strip_info - > arp_interval ;
strip_info - > arp_interval * = 2 ;
if ( strip_info - > arp_interval > MaxARPInterval )
strip_info - > arp_interval = MaxARPInterval ;
if ( addr )
arp_send ( ARPOP_REPLY , ETH_P_ARP , addr , /* Target address of ARP packet is our address */
strip_info - > dev , /* Device to send packet on */
addr , /* Source IP address this ARP packet comes from */
NULL , /* Destination HW address is NULL (broadcast it) */
strip_info - > dev - > dev_addr , /* Source HW address is our HW address */
strip_info - > dev - > dev_addr ) ; /* Target HW address is our HW address (redundant) */
}
/*
* 7. All ready . Start the transmission
*/
strip_write_some_more ( strip_info - > tty ) ;
}
/* Encapsulate a datagram and kick it into a TTY queue. */
static int strip_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct strip * strip_info = netdev_priv ( dev ) ;
if ( ! netif_running ( dev ) ) {
printk ( KERN_ERR " %s: xmit call when iface is down \n " ,
dev - > name ) ;
return ( 1 ) ;
}
netif_stop_queue ( dev ) ;
del_timer ( & strip_info - > idle_timer ) ;
if ( jiffies - strip_info - > pps_timer > HZ ) {
unsigned long t = jiffies - strip_info - > pps_timer ;
unsigned long rx_pps_count = ( strip_info - > rx_pps_count * HZ * 8 + t / 2 ) / t ;
unsigned long tx_pps_count = ( strip_info - > tx_pps_count * HZ * 8 + t / 2 ) / t ;
unsigned long sx_pps_count = ( strip_info - > sx_pps_count * HZ * 8 + t / 2 ) / t ;
strip_info - > pps_timer = jiffies ;
strip_info - > rx_pps_count = 0 ;
strip_info - > tx_pps_count = 0 ;
strip_info - > sx_pps_count = 0 ;
strip_info - > rx_average_pps = ( strip_info - > rx_average_pps + rx_pps_count + 1 ) / 2 ;
strip_info - > tx_average_pps = ( strip_info - > tx_average_pps + tx_pps_count + 1 ) / 2 ;
strip_info - > sx_average_pps = ( strip_info - > sx_average_pps + sx_pps_count + 1 ) / 2 ;
if ( rx_pps_count / 8 > = 10 )
printk ( KERN_INFO " %s: WARNING: Receiving %ld packets per second. \n " ,
strip_info - > dev - > name , rx_pps_count / 8 ) ;
if ( tx_pps_count / 8 > = 10 )
printk ( KERN_INFO " %s: WARNING: Tx %ld packets per second. \n " ,
strip_info - > dev - > name , tx_pps_count / 8 ) ;
if ( sx_pps_count / 8 > = 10 )
printk ( KERN_INFO " %s: WARNING: Sending %ld packets per second. \n " ,
strip_info - > dev - > name , sx_pps_count / 8 ) ;
}
spin_lock_bh ( & strip_lock ) ;
strip_send ( strip_info , skb ) ;
spin_unlock_bh ( & strip_lock ) ;
if ( skb )
dev_kfree_skb ( skb ) ;
return 0 ;
}
/*
* IdleTask periodically calls strip_xmit , so even when we have no IP packets
* to send for an extended period of time , the watchdog processing still gets
* done to ensure that the radio stays in Starmode
*/
static void strip_IdleTask ( unsigned long parameter )
{
strip_xmit ( NULL , ( struct net_device * ) parameter ) ;
}
/*
* Create the MAC header for an arbitrary protocol layer
*
* saddr ! = NULL means use this specific address ( n / a for Metricom )
* saddr = = NULL means use default device source address
* daddr ! = NULL means use this destination address
* daddr = = NULL means leave destination address alone
* ( e . g . unresolved arp - - kernel will call
* rebuild_header later to fill in the address )
*/
static int strip_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , void * daddr , void * saddr ,
unsigned len )
{
struct strip * strip_info = netdev_priv ( dev ) ;
STRIP_Header * header = ( STRIP_Header * ) skb_push ( skb , sizeof ( STRIP_Header ) ) ;
/*printk(KERN_INFO "%s: strip_header 0x%04X %s\n", dev->name, type,
type = = ETH_P_IP ? " IP " : type = = ETH_P_ARP ? " ARP " : " " ) ; */
header - > src_addr = strip_info - > true_dev_addr ;
header - > protocol = htons ( type ) ;
/*HexDump("strip_header", netdev_priv(dev), skb->data, skb->data + skb->len); */
if ( ! daddr )
return ( - dev - > hard_header_len ) ;
header - > dst_addr = * ( MetricomAddress * ) daddr ;
return ( dev - > hard_header_len ) ;
}
/*
* Rebuild the MAC header . This is called after an ARP
* ( or in future other address resolution ) has completed on this
* sk_buff . We now let ARP fill in the other fields .
* I think this should return zero if packet is ready to send ,
* or non - zero if it needs more time to do an address lookup
*/
static int strip_rebuild_header ( struct sk_buff * skb )
{
# ifdef CONFIG_INET
STRIP_Header * header = ( STRIP_Header * ) skb - > data ;
/* Arp find returns zero if if knows the address, */
/* or if it doesn't know the address it sends an ARP packet and returns non-zero */
return arp_find ( header - > dst_addr . c , skb ) ? 1 : 0 ;
# else
return 0 ;
# endif
}
/************************************************************************/
/* Receiving routines */
static int strip_receive_room ( struct tty_struct * tty )
{
return 0x10000 ; /* We can handle an infinite amount of data. :-) */
}
/*
* This function parses the response to the ATS300 ? command ,
* extracting the radio version and serial number .
*/
static void get_radio_version ( struct strip * strip_info , __u8 * ptr , __u8 * end )
{
__u8 * p , * value_begin , * value_end ;
int len ;
/* Determine the beginning of the second line of the payload */
p = ptr ;
while ( p < end & & * p ! = 10 )
p + + ;
if ( p > = end )
return ;
p + + ;
value_begin = p ;
/* Determine the end of line */
while ( p < end & & * p ! = 10 )
p + + ;
if ( p > = end )
return ;
value_end = p ;
p + + ;
len = value_end - value_begin ;
len = min_t ( int , len , sizeof ( FirmwareVersion ) - 1 ) ;
if ( strip_info - > firmware_version . c [ 0 ] = = 0 )
printk ( KERN_INFO " %s: Radio Firmware: %.*s \n " ,
strip_info - > dev - > name , len , value_begin ) ;
sprintf ( strip_info - > firmware_version . c , " %.*s " , len , value_begin ) ;
/* Look for the first colon */
while ( p < end & & * p ! = ' : ' )
p + + ;
if ( p > = end )
return ;
/* Skip over the space */
p + = 2 ;
len = sizeof ( SerialNumber ) - 1 ;
if ( p + len < = end ) {
sprintf ( strip_info - > serial_number . c , " %.*s " , len , p ) ;
} else {
printk ( KERN_DEBUG
" STRIP: radio serial number shorter (%zd) than expected (%d) \n " ,
end - p , len ) ;
}
}
/*
* This function parses the response to the ATS325 ? command ,
* extracting the radio battery voltage .
*/
static void get_radio_voltage ( struct strip * strip_info , __u8 * ptr , __u8 * end )
{
int len ;
len = sizeof ( BatteryVoltage ) - 1 ;
if ( ptr + len < = end ) {
sprintf ( strip_info - > battery_voltage . c , " %.*s " , len , ptr ) ;
} else {
printk ( KERN_DEBUG
" STRIP: radio voltage string shorter (%zd) than expected (%d) \n " ,
end - ptr , len ) ;
}
}
/*
* This function parses the responses to the AT ~ LA and ATS311 commands ,
* which list the radio ' s neighbours .
*/
static void get_radio_neighbours ( MetricomNodeTable * table , __u8 * ptr , __u8 * end )
{
table - > num_nodes = 0 ;
while ( ptr < end & & table - > num_nodes < NODE_TABLE_SIZE ) {
MetricomNode * node = & table - > node [ table - > num_nodes + + ] ;
char * dst = node - > c , * limit = dst + sizeof ( * node ) - 1 ;
while ( ptr < end & & * ptr < = 32 )
ptr + + ;
while ( ptr < end & & dst < limit & & * ptr ! = 10 )
* dst + + = * ptr + + ;
* dst + + = 0 ;
while ( ptr < end & & ptr [ - 1 ] ! = 10 )
ptr + + ;
}
do_gettimeofday ( & table - > timestamp ) ;
}
static int get_radio_address ( struct strip * strip_info , __u8 * p )
{
MetricomAddress addr ;
if ( string_to_radio_address ( & addr , p ) )
return ( 1 ) ;
/* See if our radio address has changed */
if ( memcmp ( strip_info - > true_dev_addr . c , addr . c , sizeof ( addr ) ) ) {
MetricomAddressString addr_string ;
radio_address_to_string ( & addr , & addr_string ) ;
printk ( KERN_INFO " %s: Radio address = %s \n " ,
strip_info - > dev - > name , addr_string . c ) ;
strip_info - > true_dev_addr = addr ;
if ( ! strip_info - > manual_dev_addr )
* ( MetricomAddress * ) strip_info - > dev - > dev_addr =
addr ;
/* Give the radio a few seconds to get its head straight, then send an arp */
strip_info - > gratuitous_arp = jiffies + 15 * HZ ;
strip_info - > arp_interval = 1 * HZ ;
}
return ( 0 ) ;
}
static int verify_checksum ( struct strip * strip_info )
{
__u8 * p = strip_info - > sx_buff ;
__u8 * end = strip_info - > sx_buff + strip_info - > sx_count - 4 ;
u_short sum =
( READHEX16 ( end [ 0 ] ) < < 12 ) | ( READHEX16 ( end [ 1 ] ) < < 8 ) |
( READHEX16 ( end [ 2 ] ) < < 4 ) | ( READHEX16 ( end [ 3 ] ) ) ;
while ( p < end )
sum - = * p + + ;
if ( sum = = 0 & & strip_info - > firmware_level = = StructuredMessages ) {
strip_info - > firmware_level = ChecksummedMessages ;
printk ( KERN_INFO " %s: Radio provides message checksums \n " ,
strip_info - > dev - > name ) ;
}
return ( sum = = 0 ) ;
}
static void RecvErr ( char * msg , struct strip * strip_info )
{
__u8 * ptr = strip_info - > sx_buff ;
__u8 * end = strip_info - > sx_buff + strip_info - > sx_count ;
DumpData ( msg , strip_info , ptr , end ) ;
strip_info - > rx_errors + + ;
}
static void RecvErr_Message ( struct strip * strip_info , __u8 * sendername ,
const __u8 * msg , u_long len )
{
if ( has_prefix ( msg , len , " 001 " ) ) { /* Not in StarMode! */
RecvErr ( " Error Msg: " , strip_info ) ;
printk ( KERN_INFO " %s: Radio %s is not in StarMode \n " ,
strip_info - > dev - > name , sendername ) ;
}
else if ( has_prefix ( msg , len , " 002 " ) ) { /* Remap handle */
/* We ignore "Remap handle" messages for now */
}
else if ( has_prefix ( msg , len , " 003 " ) ) { /* Can't resolve name */
RecvErr ( " Error Msg: " , strip_info ) ;
printk ( KERN_INFO " %s: Destination radio name is unknown \n " ,
strip_info - > dev - > name ) ;
}
else if ( has_prefix ( msg , len , " 004 " ) ) { /* Name too small or missing */
strip_info - > watchdog_doreset = jiffies + LongTime ;
# if TICKLE_TIMERS
{
struct timeval tv ;
do_gettimeofday ( & tv ) ;
printk ( KERN_INFO
" **** Got ERR_004 response at %02d.%06d \n " ,
tv . tv_sec % 100 , tv . tv_usec ) ;
}
# endif
if ( ! strip_info - > working ) {
strip_info - > working = TRUE ;
printk ( KERN_INFO " %s: Radio now in starmode \n " ,
strip_info - > dev - > name ) ;
/*
* If the radio has just entered a working state , we should do our first
* probe ASAP , so that we find out our radio address etc . without delay .
*/
strip_info - > watchdog_doprobe = jiffies ;
}
if ( strip_info - > firmware_level = = NoStructure & & sendername ) {
strip_info - > firmware_level = StructuredMessages ;
strip_info - > next_command = 0 ; /* Try to enable checksums ASAP */
printk ( KERN_INFO
" %s: Radio provides structured messages \n " ,
strip_info - > dev - > name ) ;
}
if ( strip_info - > firmware_level > = StructuredMessages ) {
/*
* If this message has a valid checksum on the end , then the call to verify_checksum
* will elevate the firmware_level to ChecksummedMessages for us . ( The actual return
* code from verify_checksum is ignored here . )
*/
verify_checksum ( strip_info ) ;
/*
* If the radio has structured messages but we don ' t yet have all our information about it ,
* we should do probes without delay , until we have gathered all the information
*/
if ( ! GOT_ALL_RADIO_INFO ( strip_info ) )
strip_info - > watchdog_doprobe = jiffies ;
}
}
else if ( has_prefix ( msg , len , " 005 " ) ) /* Bad count specification */
RecvErr ( " Error Msg: " , strip_info ) ;
else if ( has_prefix ( msg , len , " 006 " ) ) /* Header too big */
RecvErr ( " Error Msg: " , strip_info ) ;
else if ( has_prefix ( msg , len , " 007 " ) ) { /* Body too big */
RecvErr ( " Error Msg: " , strip_info ) ;
printk ( KERN_ERR
" %s: Error! Packet size too big for radio. \n " ,
strip_info - > dev - > name ) ;
}
else if ( has_prefix ( msg , len , " 008 " ) ) { /* Bad character in name */
RecvErr ( " Error Msg: " , strip_info ) ;
printk ( KERN_ERR
" %s: Radio name contains illegal character \n " ,
strip_info - > dev - > name ) ;
}
else if ( has_prefix ( msg , len , " 009 " ) ) /* No count or line terminator */
RecvErr ( " Error Msg: " , strip_info ) ;
else if ( has_prefix ( msg , len , " 010 " ) ) /* Invalid checksum */
RecvErr ( " Error Msg: " , strip_info ) ;
else if ( has_prefix ( msg , len , " 011 " ) ) /* Checksum didn't match */
RecvErr ( " Error Msg: " , strip_info ) ;
else if ( has_prefix ( msg , len , " 012 " ) ) /* Failed to transmit packet */
RecvErr ( " Error Msg: " , strip_info ) ;
else
RecvErr ( " Error Msg: " , strip_info ) ;
}
static void process_AT_response ( struct strip * strip_info , __u8 * ptr ,
__u8 * end )
{
u_long len ;
__u8 * p = ptr ;
while ( p < end & & p [ - 1 ] ! = 10 )
p + + ; /* Skip past first newline character */
/* Now ptr points to the AT command, and p points to the text of the response. */
len = p - ptr ;
# if TICKLE_TIMERS
{
struct timeval tv ;
do_gettimeofday ( & tv ) ;
printk ( KERN_INFO " **** Got AT response %.7s at %02d.%06d \n " ,
ptr , tv . tv_sec % 100 , tv . tv_usec ) ;
}
# endif
if ( has_prefix ( ptr , len , " ATS300? " ) )
get_radio_version ( strip_info , p , end ) ;
else if ( has_prefix ( ptr , len , " ATS305? " ) )
get_radio_address ( strip_info , p ) ;
else if ( has_prefix ( ptr , len , " ATS311? " ) )
get_radio_neighbours ( & strip_info - > poletops , p , end ) ;
else if ( has_prefix ( ptr , len , " ATS319=7 " ) )
verify_checksum ( strip_info ) ;
else if ( has_prefix ( ptr , len , " ATS325? " ) )
get_radio_voltage ( strip_info , p , end ) ;
else if ( has_prefix ( ptr , len , " AT~LA " ) )
get_radio_neighbours ( & strip_info - > portables , p , end ) ;
else
RecvErr ( " Unknown AT Response: " , strip_info ) ;
}
static void process_ACK ( struct strip * strip_info , __u8 * ptr , __u8 * end )
{
/* Currently we don't do anything with ACKs from the radio */
}
static void process_Info ( struct strip * strip_info , __u8 * ptr , __u8 * end )
{
if ( ptr + 16 > end )
RecvErr ( " Bad Info Msg: " , strip_info ) ;
}
static struct net_device * get_strip_dev ( struct strip * strip_info )
{
/* If our hardware address is *manually set* to zero, and we know our */
/* real radio hardware address, try to find another strip device that has been */
/* manually set to that address that we can 'transfer ownership' of this packet to */
if ( strip_info - > manual_dev_addr & &
! memcmp ( strip_info - > dev - > dev_addr , zero_address . c ,
sizeof ( zero_address ) )
& & memcmp ( & strip_info - > true_dev_addr , zero_address . c ,
sizeof ( zero_address ) ) ) {
struct net_device * dev ;
read_lock_bh ( & dev_base_lock ) ;
dev = dev_base ;
while ( dev ) {
if ( dev - > type = = strip_info - > dev - > type & &
! memcmp ( dev - > dev_addr ,
& strip_info - > true_dev_addr ,
sizeof ( MetricomAddress ) ) ) {
printk ( KERN_INFO
" %s: Transferred packet ownership to %s. \n " ,
strip_info - > dev - > name , dev - > name ) ;
read_unlock_bh ( & dev_base_lock ) ;
return ( dev ) ;
}
dev = dev - > next ;
}
read_unlock_bh ( & dev_base_lock ) ;
}
return ( strip_info - > dev ) ;
}
/*
* Send one completely decapsulated datagram to the next layer .
*/
static void deliver_packet ( struct strip * strip_info , STRIP_Header * header ,
__u16 packetlen )
{
struct sk_buff * skb = dev_alloc_skb ( sizeof ( STRIP_Header ) + packetlen ) ;
if ( ! skb ) {
printk ( KERN_ERR " %s: memory squeeze, dropping packet. \n " ,
strip_info - > dev - > name ) ;
strip_info - > rx_dropped + + ;
} else {
memcpy ( skb_put ( skb , sizeof ( STRIP_Header ) ) , header ,
sizeof ( STRIP_Header ) ) ;
memcpy ( skb_put ( skb , packetlen ) , strip_info - > rx_buff ,
packetlen ) ;
skb - > dev = get_strip_dev ( strip_info ) ;
skb - > protocol = header - > protocol ;
skb - > mac . raw = skb - > data ;
/* Having put a fake header on the front of the sk_buff for the */
/* benefit of tools like tcpdump, skb_pull now 'consumes' that */
/* fake header before we hand the packet up to the next layer. */
skb_pull ( skb , sizeof ( STRIP_Header ) ) ;
/* Finally, hand the packet up to the next layer (e.g. IP or ARP, etc.) */
strip_info - > rx_packets + + ;
strip_info - > rx_pps_count + + ;
# ifdef EXT_COUNTERS
strip_info - > rx_bytes + = packetlen ;
# endif
skb - > dev - > last_rx = jiffies ;
netif_rx ( skb ) ;
}
}
static void process_IP_packet ( struct strip * strip_info ,
STRIP_Header * header , __u8 * ptr ,
__u8 * end )
{
__u16 packetlen ;
/* Decode start of the IP packet header */
ptr = UnStuffData ( ptr , end , strip_info - > rx_buff , 4 ) ;
if ( ! ptr ) {
RecvErr ( " IP Packet too short " , strip_info ) ;
return ;
}
packetlen = ( ( __u16 ) strip_info - > rx_buff [ 2 ] < < 8 ) | strip_info - > rx_buff [ 3 ] ;
if ( packetlen > MAX_RECV_MTU ) {
printk ( KERN_INFO " %s: Dropping oversized received IP packet: %d bytes \n " ,
strip_info - > dev - > name , packetlen ) ;
strip_info - > rx_dropped + + ;
return ;
}
/*printk(KERN_INFO "%s: Got %d byte IP packet\n", strip_info->dev->name, packetlen); */
/* Decode remainder of the IP packet */
ptr =
UnStuffData ( ptr , end , strip_info - > rx_buff + 4 , packetlen - 4 ) ;
if ( ! ptr ) {
RecvErr ( " IP Packet too short " , strip_info ) ;
return ;
}
if ( ptr < end ) {
RecvErr ( " IP Packet too long " , strip_info ) ;
return ;
}
header - > protocol = htons ( ETH_P_IP ) ;
deliver_packet ( strip_info , header , packetlen ) ;
}
static void process_ARP_packet ( struct strip * strip_info ,
STRIP_Header * header , __u8 * ptr ,
__u8 * end )
{
__u16 packetlen ;
struct arphdr * arphdr = ( struct arphdr * ) strip_info - > rx_buff ;
/* Decode start of the ARP packet */
ptr = UnStuffData ( ptr , end , strip_info - > rx_buff , 8 ) ;
if ( ! ptr ) {
RecvErr ( " ARP Packet too short " , strip_info ) ;
return ;
}
packetlen = 8 + ( arphdr - > ar_hln + arphdr - > ar_pln ) * 2 ;
if ( packetlen > MAX_RECV_MTU ) {
printk ( KERN_INFO
" %s: Dropping oversized received ARP packet: %d bytes \n " ,
strip_info - > dev - > name , packetlen ) ;
strip_info - > rx_dropped + + ;
return ;
}
/*printk(KERN_INFO "%s: Got %d byte ARP %s\n",
strip_info - > dev - > name , packetlen ,
ntohs ( arphdr - > ar_op ) = = ARPOP_REQUEST ? " request " : " reply " ) ; */
/* Decode remainder of the ARP packet */
ptr =
UnStuffData ( ptr , end , strip_info - > rx_buff + 8 , packetlen - 8 ) ;
if ( ! ptr ) {
RecvErr ( " ARP Packet too short " , strip_info ) ;
return ;
}
if ( ptr < end ) {
RecvErr ( " ARP Packet too long " , strip_info ) ;
return ;
}
header - > protocol = htons ( ETH_P_ARP ) ;
deliver_packet ( strip_info , header , packetlen ) ;
}
/*
* process_text_message processes a < CR > - terminated block of data received
* from the radio that doesn ' t begin with a ' * ' character . All normal
* Starmode communication messages with the radio begin with a ' * ' ,
* so any text that does not indicates a serial port error , a radio that
* is in Hayes command mode instead of Starmode , or a radio with really
* old firmware that doesn ' t frame its Starmode responses properly .
*/
static void process_text_message ( struct strip * strip_info )
{
__u8 * msg = strip_info - > sx_buff ;
int len = strip_info - > sx_count ;
/* Check for anything that looks like it might be our radio name */
/* (This is here for backwards compatibility with old firmware) */
if ( len = = 9 & & get_radio_address ( strip_info , msg ) = = 0 )
return ;
if ( text_equal ( msg , len , " OK " ) )
return ; /* Ignore 'OK' responses from prior commands */
if ( text_equal ( msg , len , " ERROR " ) )
return ; /* Ignore 'ERROR' messages */
if ( has_prefix ( msg , len , " ate0q1 " ) )
return ; /* Ignore character echo back from the radio */
/* Catch other error messages */
/* (This is here for backwards compatibility with old firmware) */
if ( has_prefix ( msg , len , " ERR_ " ) ) {
RecvErr_Message ( strip_info , NULL , & msg [ 4 ] , len - 4 ) ;
return ;
}
RecvErr ( " No initial * " , strip_info ) ;
}
/*
* process_message processes a < CR > - terminated block of data received
* from the radio . If the radio is not in Starmode or has old firmware ,
* it may be a line of text in response to an AT command . Ideally , with
* a current radio that ' s properly in Starmode , all data received should
* be properly framed and checksummed radio message blocks , containing
* either a starmode packet , or a other communication from the radio
* firmware , like " INF_ " Info messages and & COMMAND responses .
*/
static void process_message ( struct strip * strip_info )
{
STRIP_Header header = { zero_address , zero_address , 0 } ;
__u8 * ptr = strip_info - > sx_buff ;
__u8 * end = strip_info - > sx_buff + strip_info - > sx_count ;
__u8 sendername [ 32 ] , * sptr = sendername ;
MetricomKey key ;
/*HexDump("Receiving", strip_info, ptr, end); */
/* Check for start of address marker, and then skip over it */
if ( * ptr = = ' * ' )
ptr + + ;
else {
process_text_message ( strip_info ) ;
return ;
}
/* Copy out the return address */
while ( ptr < end & & * ptr ! = ' * '
& & sptr < ARRAY_END ( sendername ) - 1 )
* sptr + + = * ptr + + ;
* sptr = 0 ; /* Null terminate the sender name */
/* Check for end of address marker, and skip over it */
if ( ptr > = end | | * ptr ! = ' * ' ) {
RecvErr ( " No second * " , strip_info ) ;
return ;
}
ptr + + ; /* Skip the second '*' */
/* If the sender name is "&COMMAND", ignore this 'packet' */
/* (This is here for backwards compatibility with old firmware) */
if ( ! strcmp ( sendername , " &COMMAND " ) ) {
strip_info - > firmware_level = NoStructure ;
strip_info - > next_command = CompatibilityCommand ;
return ;
}
if ( ptr + 4 > end ) {
RecvErr ( " No proto key " , strip_info ) ;
return ;
}
/* Get the protocol key out of the buffer */
key . c [ 0 ] = * ptr + + ;
key . c [ 1 ] = * ptr + + ;
key . c [ 2 ] = * ptr + + ;
key . c [ 3 ] = * ptr + + ;
/* If we're using checksums, verify the checksum at the end of the packet */
if ( strip_info - > firmware_level > = ChecksummedMessages ) {
end - = 4 ; /* Chop the last four bytes off the packet (they're the checksum) */
if ( ptr > end ) {
RecvErr ( " Missing Checksum " , strip_info ) ;
return ;
}
if ( ! verify_checksum ( strip_info ) ) {
RecvErr ( " Bad Checksum " , strip_info ) ;
return ;
}
}
/*printk(KERN_INFO "%s: Got packet from \"%s\".\n", strip_info->dev->name, sendername); */
/*
* Fill in ( pseudo ) source and destination addresses in the packet .
* We assume that the destination address was our address ( the radio does not
* tell us this ) . If the radio supplies a source address , then we use it .
*/
header . dst_addr = strip_info - > true_dev_addr ;
string_to_radio_address ( & header . src_addr , sendername ) ;
# ifdef EXT_COUNTERS
if ( key . l = = SIP0Key . l ) {
strip_info - > rx_rbytes + = ( end - ptr ) ;
process_IP_packet ( strip_info , & header , ptr , end ) ;
} else if ( key . l = = ARP0Key . l ) {
strip_info - > rx_rbytes + = ( end - ptr ) ;
process_ARP_packet ( strip_info , & header , ptr , end ) ;
} else if ( key . l = = ATR_Key . l ) {
strip_info - > rx_ebytes + = ( end - ptr ) ;
process_AT_response ( strip_info , ptr , end ) ;
} else if ( key . l = = ACK_Key . l ) {
strip_info - > rx_ebytes + = ( end - ptr ) ;
process_ACK ( strip_info , ptr , end ) ;
} else if ( key . l = = INF_Key . l ) {
strip_info - > rx_ebytes + = ( end - ptr ) ;
process_Info ( strip_info , ptr , end ) ;
} else if ( key . l = = ERR_Key . l ) {
strip_info - > rx_ebytes + = ( end - ptr ) ;
RecvErr_Message ( strip_info , sendername , ptr , end - ptr ) ;
} else
RecvErr ( " Unrecognized protocol key " , strip_info ) ;
# else
if ( key . l = = SIP0Key . l )
process_IP_packet ( strip_info , & header , ptr , end ) ;
else if ( key . l = = ARP0Key . l )
process_ARP_packet ( strip_info , & header , ptr , end ) ;
else if ( key . l = = ATR_Key . l )
process_AT_response ( strip_info , ptr , end ) ;
else if ( key . l = = ACK_Key . l )
process_ACK ( strip_info , ptr , end ) ;
else if ( key . l = = INF_Key . l )
process_Info ( strip_info , ptr , end ) ;
else if ( key . l = = ERR_Key . l )
RecvErr_Message ( strip_info , sendername , ptr , end - ptr ) ;
else
RecvErr ( " Unrecognized protocol key " , strip_info ) ;
# endif
}
# define TTYERROR(X) ((X) == TTY_BREAK ? "Break" : \
( X ) = = TTY_FRAME ? " Framing Error " : \
( X ) = = TTY_PARITY ? " Parity Error " : \
( X ) = = TTY_OVERRUN ? " Hardware Overrun " : " Unknown Error " )
/*
* Handle the ' receiver data ready ' interrupt .
* This function is called by the ' tty_io ' module in the kernel when
* a block of STRIP data has been received , which can now be decapsulated
* and sent on to some IP layer for further processing .
*/
static void strip_receive_buf ( struct tty_struct * tty , const unsigned char * cp ,
char * fp , int count )
{
struct strip * strip_info = ( struct strip * ) tty - > disc_data ;
const unsigned char * end = cp + count ;
if ( ! strip_info | | strip_info - > magic ! = STRIP_MAGIC
| | ! netif_running ( strip_info - > dev ) )
return ;
spin_lock_bh ( & strip_lock ) ;
#if 0
{
struct timeval tv ;
do_gettimeofday ( & tv ) ;
printk ( KERN_INFO
" **** strip_receive_buf: %3d bytes at %02d.%06d \n " ,
count , tv . tv_sec % 100 , tv . tv_usec ) ;
}
# endif
# ifdef EXT_COUNTERS
strip_info - > rx_sbytes + = count ;
# endif
/* Read the characters out of the buffer */
while ( cp < end ) {
if ( fp & & * fp )
printk ( KERN_INFO " %s: %s on serial port \n " ,
strip_info - > dev - > name , TTYERROR ( * fp ) ) ;
if ( fp & & * fp + + & & ! strip_info - > discard ) { /* If there's a serial error, record it */
/* If we have some characters in the buffer, discard them */
strip_info - > discard = strip_info - > sx_count ;
strip_info - > rx_errors + + ;
}
/* Leading control characters (CR, NL, Tab, etc.) are ignored */
if ( strip_info - > sx_count > 0 | | * cp > = ' ' ) {
if ( * cp = = 0x0D ) { /* If end of packet, decide what to do with it */
if ( strip_info - > sx_count > 3000 )
printk ( KERN_INFO
" %s: Cut a %d byte packet (%zd bytes remaining)%s \n " ,
strip_info - > dev - > name ,
strip_info - > sx_count ,
end - cp - 1 ,
strip_info - >
discard ? " (discarded) " :
" " ) ;
if ( strip_info - > sx_count >
strip_info - > sx_size ) {
strip_info - > rx_over_errors + + ;
printk ( KERN_INFO
" %s: sx_buff overflow (%d bytes total) \n " ,
strip_info - > dev - > name ,
strip_info - > sx_count ) ;
} else if ( strip_info - > discard )
printk ( KERN_INFO
" %s: Discarding bad packet (%d/%d) \n " ,
strip_info - > dev - > name ,
strip_info - > discard ,
strip_info - > sx_count ) ;
else
process_message ( strip_info ) ;
strip_info - > discard = 0 ;
strip_info - > sx_count = 0 ;
} else {
/* Make sure we have space in the buffer */
if ( strip_info - > sx_count <
strip_info - > sx_size )
strip_info - > sx_buff [ strip_info - >
sx_count ] =
* cp ;
strip_info - > sx_count + + ;
}
}
cp + + ;
}
spin_unlock_bh ( & strip_lock ) ;
}
/************************************************************************/
/* General control routines */
static int set_mac_address ( struct strip * strip_info ,
MetricomAddress * addr )
{
/*
* We ' re using a manually specified address if the address is set
* to anything other than all ones . Setting the address to all ones
* disables manual mode and goes back to automatic address determination
* ( tracking the true address that the radio has ) .
*/
strip_info - > manual_dev_addr =
memcmp ( addr - > c , broadcast_address . c ,
sizeof ( broadcast_address ) ) ;
if ( strip_info - > manual_dev_addr )
* ( MetricomAddress * ) strip_info - > dev - > dev_addr = * addr ;
else
* ( MetricomAddress * ) strip_info - > dev - > dev_addr =
strip_info - > true_dev_addr ;
return 0 ;
}
static int strip_set_mac_address ( struct net_device * dev , void * addr )
{
struct strip * strip_info = netdev_priv ( dev ) ;
struct sockaddr * sa = addr ;
printk ( KERN_INFO " %s: strip_set_dev_mac_address called \n " , dev - > name ) ;
set_mac_address ( strip_info , ( MetricomAddress * ) sa - > sa_data ) ;
return 0 ;
}
static struct net_device_stats * strip_get_stats ( struct net_device * dev )
{
struct strip * strip_info = netdev_priv ( dev ) ;
static struct net_device_stats stats ;
memset ( & stats , 0 , sizeof ( struct net_device_stats ) ) ;
stats . rx_packets = strip_info - > rx_packets ;
stats . tx_packets = strip_info - > tx_packets ;
stats . rx_dropped = strip_info - > rx_dropped ;
stats . tx_dropped = strip_info - > tx_dropped ;
stats . tx_errors = strip_info - > tx_errors ;
stats . rx_errors = strip_info - > rx_errors ;
stats . rx_over_errors = strip_info - > rx_over_errors ;
return ( & stats ) ;
}
/************************************************************************/
/* Opening and closing */
/*
* Here ' s the order things happen :
* When the user runs " slattach -p strip ... "
* 1. The TTY module calls strip_open
* 2. strip_open calls strip_alloc
* 3. strip_alloc calls register_netdev
* 4. register_netdev calls strip_dev_init
* 5. then strip_open finishes setting up the strip_info
*
* When the user runs " ifconfig st<x> up address netmask ... "
* 6. strip_open_low gets called
*
* When the user runs " ifconfig st<x> down "
* 7. strip_close_low gets called
*
* When the user kills the slattach process
* 8. strip_close gets called
* 9. strip_close calls dev_close
* 10. if the device is still up , then dev_close calls strip_close_low
* 11. strip_close calls strip_free
*/
/* Open the low-level part of the STRIP channel. Easy! */
static int strip_open_low ( struct net_device * dev )
{
struct strip * strip_info = netdev_priv ( dev ) ;
if ( strip_info - > tty = = NULL )
return ( - ENODEV ) ;
if ( ! allocate_buffers ( strip_info , dev - > mtu ) )
return ( - ENOMEM ) ;
strip_info - > sx_count = 0 ;
strip_info - > tx_left = 0 ;
strip_info - > discard = 0 ;
strip_info - > working = FALSE ;
strip_info - > firmware_level = NoStructure ;
strip_info - > next_command = CompatibilityCommand ;
strip_info - > user_baud = get_baud ( strip_info - > tty ) ;
printk ( KERN_INFO " %s: Initializing Radio. \n " ,
strip_info - > dev - > name ) ;
ResetRadio ( strip_info ) ;
strip_info - > idle_timer . expires = jiffies + 1 * HZ ;
add_timer ( & strip_info - > idle_timer ) ;
netif_wake_queue ( dev ) ;
return ( 0 ) ;
}
/*
* Close the low - level part of the STRIP channel . Easy !
*/
static int strip_close_low ( struct net_device * dev )
{
struct strip * strip_info = netdev_priv ( dev ) ;
if ( strip_info - > tty = = NULL )
return - EBUSY ;
strip_info - > tty - > flags & = ~ ( 1 < < TTY_DO_WRITE_WAKEUP ) ;
netif_stop_queue ( dev ) ;
/*
* Free all STRIP frame buffers .
*/
2005-10-29 00:53:13 +04:00
kfree ( strip_info - > rx_buff ) ;
strip_info - > rx_buff = NULL ;
kfree ( strip_info - > sx_buff ) ;
strip_info - > sx_buff = NULL ;
kfree ( strip_info - > tx_buff ) ;
strip_info - > tx_buff = NULL ;
2005-04-17 02:20:36 +04:00
del_timer ( & strip_info - > idle_timer ) ;
return 0 ;
}
/*
* This routine is called by DDI when the
* ( dynamically assigned ) device is registered
*/
static void strip_dev_setup ( struct net_device * dev )
{
/*
* Finish setting up the DEVICE info .
*/
SET_MODULE_OWNER ( dev ) ;
dev - > trans_start = 0 ;
dev - > last_rx = 0 ;
dev - > tx_queue_len = 30 ; /* Drop after 30 frames queued */
dev - > flags = 0 ;
dev - > mtu = DEFAULT_STRIP_MTU ;
dev - > type = ARPHRD_METRICOM ; /* dtang */
dev - > hard_header_len = sizeof ( STRIP_Header ) ;
/*
* dev - > priv Already holds a pointer to our struct strip
*/
* ( MetricomAddress * ) & dev - > broadcast = broadcast_address ;
dev - > dev_addr [ 0 ] = 0 ;
dev - > addr_len = sizeof ( MetricomAddress ) ;
/*
* Pointers to interface service routines .
*/
dev - > open = strip_open_low ;
dev - > stop = strip_close_low ;
dev - > hard_start_xmit = strip_xmit ;
dev - > hard_header = strip_header ;
dev - > rebuild_header = strip_rebuild_header ;
dev - > set_mac_address = strip_set_mac_address ;
dev - > get_stats = strip_get_stats ;
dev - > change_mtu = strip_change_mtu ;
}
/*
* Free a STRIP channel .
*/
static void strip_free ( struct strip * strip_info )
{
spin_lock_bh ( & strip_lock ) ;
list_del_rcu ( & strip_info - > list ) ;
spin_unlock_bh ( & strip_lock ) ;
strip_info - > magic = 0 ;
free_netdev ( strip_info - > dev ) ;
}
/*
* Allocate a new free STRIP channel
*/
static struct strip * strip_alloc ( void )
{
struct list_head * n ;
struct net_device * dev ;
struct strip * strip_info ;
dev = alloc_netdev ( sizeof ( struct strip ) , " st%d " ,
strip_dev_setup ) ;
if ( ! dev )
return NULL ; /* If no more memory, return */
strip_info = dev - > priv ;
strip_info - > dev = dev ;
strip_info - > magic = STRIP_MAGIC ;
strip_info - > tty = NULL ;
strip_info - > gratuitous_arp = jiffies + LongTime ;
strip_info - > arp_interval = 0 ;
init_timer ( & strip_info - > idle_timer ) ;
strip_info - > idle_timer . data = ( long ) dev ;
strip_info - > idle_timer . function = strip_IdleTask ;
spin_lock_bh ( & strip_lock ) ;
rescan :
/*
* Search the list to find where to put our new entry
* ( and in the process decide what channel number it is
* going to be )
*/
list_for_each ( n , & strip_list ) {
struct strip * s = hlist_entry ( n , struct strip , list ) ;
if ( s - > dev - > base_addr = = dev - > base_addr ) {
+ + dev - > base_addr ;
goto rescan ;
}
}
sprintf ( dev - > name , " st%ld " , dev - > base_addr ) ;
list_add_tail_rcu ( & strip_info - > list , & strip_list ) ;
spin_unlock_bh ( & strip_lock ) ;
return strip_info ;
}
/*
* Open the high - level part of the STRIP channel .
* This function is called by the TTY module when the
* STRIP line discipline is called for . Because we are
* sure the tty line exists , we only have to link it to
* a free STRIP channel . . .
*/
static int strip_open ( struct tty_struct * tty )
{
struct strip * strip_info = ( struct strip * ) tty - > disc_data ;
/*
* First make sure we ' re not already connected .
*/
if ( strip_info & & strip_info - > magic = = STRIP_MAGIC )
return - EEXIST ;
/*
* OK . Find a free STRIP channel to use .
*/
if ( ( strip_info = strip_alloc ( ) ) = = NULL )
return - ENFILE ;
/*
* Register our newly created device so it can be ifconfig ' d
* strip_dev_init ( ) will be called as a side - effect
*/
if ( register_netdev ( strip_info - > dev ) ! = 0 ) {
printk ( KERN_ERR " strip: register_netdev() failed. \n " ) ;
strip_free ( strip_info ) ;
return - ENFILE ;
}
strip_info - > tty = tty ;
tty - > disc_data = strip_info ;
if ( tty - > driver - > flush_buffer )
tty - > driver - > flush_buffer ( tty ) ;
/*
* Restore default settings
*/
strip_info - > dev - > type = ARPHRD_METRICOM ; /* dtang */
/*
* Set tty options
*/
tty - > termios - > c_iflag | = IGNBRK | IGNPAR ; /* Ignore breaks and parity errors. */
tty - > termios - > c_cflag | = CLOCAL ; /* Ignore modem control signals. */
tty - > termios - > c_cflag & = ~ HUPCL ; /* Don't close on hup */
printk ( KERN_INFO " STRIP: device \" %s \" activated \n " ,
strip_info - > dev - > name ) ;
/*
* Done . We have linked the TTY line to a channel .
*/
return ( strip_info - > dev - > base_addr ) ;
}
/*
* Close down a STRIP channel .
* This means flushing out any pending queues , and then restoring the
* TTY line discipline to what it was before it got hooked to STRIP
* ( which usually is TTY again ) .
*/
static void strip_close ( struct tty_struct * tty )
{
struct strip * strip_info = ( struct strip * ) tty - > disc_data ;
/*
* First make sure we ' re connected .
*/
if ( ! strip_info | | strip_info - > magic ! = STRIP_MAGIC )
return ;
unregister_netdev ( strip_info - > dev ) ;
tty - > disc_data = NULL ;
strip_info - > tty = NULL ;
printk ( KERN_INFO " STRIP: device \" %s \" closed down \n " ,
strip_info - > dev - > name ) ;
strip_free ( strip_info ) ;
tty - > disc_data = NULL ;
}
/************************************************************************/
/* Perform I/O control calls on an active STRIP channel. */
static int strip_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
{
struct strip * strip_info = ( struct strip * ) tty - > disc_data ;
/*
* First make sure we ' re connected .
*/
if ( ! strip_info | | strip_info - > magic ! = STRIP_MAGIC )
return - EINVAL ;
switch ( cmd ) {
case SIOCGIFNAME :
if ( copy_to_user ( ( void __user * ) arg , strip_info - > dev - > name , strlen ( strip_info - > dev - > name ) + 1 ) )
return - EFAULT ;
break ;
case SIOCSIFHWADDR :
{
MetricomAddress addr ;
//printk(KERN_INFO "%s: SIOCSIFHWADDR\n", strip_info->dev->name);
if ( copy_from_user ( & addr , ( void __user * ) arg , sizeof ( MetricomAddress ) ) )
return - EFAULT ;
return set_mac_address ( strip_info , & addr ) ;
}
/*
* Allow stty to read , but not set , the serial port
*/
case TCGETS :
case TCGETA :
return n_tty_ioctl ( tty , file , cmd , arg ) ;
break ;
default :
return - ENOIOCTLCMD ;
break ;
}
return 0 ;
}
/************************************************************************/
/* Initialization */
static struct tty_ldisc strip_ldisc = {
. magic = TTY_LDISC_MAGIC ,
. name = " strip " ,
. owner = THIS_MODULE ,
. open = strip_open ,
. close = strip_close ,
. ioctl = strip_ioctl ,
. receive_buf = strip_receive_buf ,
. receive_room = strip_receive_room ,
. write_wakeup = strip_write_some_more ,
} ;
/*
* Initialize the STRIP driver .
* This routine is called at boot time , to bootstrap the multi - channel
* STRIP driver
*/
static char signon [ ] __initdata =
KERN_INFO " STRIP: Version %s (unlimited channels) \n " ;
static int __init strip_init_driver ( void )
{
int status ;
printk ( signon , StripVersion ) ;
/*
* Fill in our line protocol discipline , and register it
*/
if ( ( status = tty_register_ldisc ( N_STRIP , & strip_ldisc ) ) )
printk ( KERN_ERR " STRIP: can't register line discipline (err = %d) \n " ,
status ) ;
/*
* Register the status file with / proc
*/
proc_net_fops_create ( " strip " , S_IFREG | S_IRUGO , & strip_seq_fops ) ;
return status ;
}
module_init ( strip_init_driver ) ;
static const char signoff [ ] __exitdata =
KERN_INFO " STRIP: Module Unloaded \n " ;
static void __exit strip_exit_driver ( void )
{
int i ;
struct list_head * p , * n ;
/* module ref count rules assure that all entries are unregistered */
list_for_each_safe ( p , n , & strip_list ) {
struct strip * s = list_entry ( p , struct strip , list ) ;
strip_free ( s ) ;
}
/* Unregister with the /proc/net file here. */
proc_net_remove ( " strip " ) ;
2005-06-23 11:10:33 +04:00
if ( ( i = tty_unregister_ldisc ( N_STRIP ) ) )
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " STRIP: can't unregister line discipline (err = %d) \n " , i ) ;
printk ( signoff ) ;
}
module_exit ( strip_exit_driver ) ;
MODULE_AUTHOR ( " Stuart Cheshire <cheshire@cs.stanford.edu> " ) ;
MODULE_DESCRIPTION ( " Starmode Radio IP (STRIP) Device Driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " Starmode Radio IP (STRIP) modem " ) ;