2005-04-16 15:20:36 -07:00
/*****************************************************************************
* sdla_x25 . c WANPIPE ( tm ) Multiprotocol WAN Link Driver . X .25 module .
*
* Author : Nenad Corbic < ncorbic @ sangoma . com >
*
* Copyright : ( c ) 1995 - 2001 Sangoma Technologies Inc .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* Apr 03 , 2001 Nenad Corbic o Fixed the rx_skb = NULL bug in x25 in rx_intr ( ) .
* Dec 26 , 2000 Nenad Corbic o Added a new polling routine , that uses
* a kernel timer ( more efficient ) .
* Dec 25 , 2000 Nenad Corbic o Updated for 2.4 . X kernel
* Jul 26 , 2000 Nenad Corbic o Increased the local packet buffering
* for API to 4096 + header_size .
* Jul 17 , 2000 Nenad Corbic o Fixed the x25 startup bug . Enable
* communications only after all interfaces
* come up . HIGH SVC / PVC is used to calculate
* the number of channels .
* Enable protocol only after all interfaces
* are enabled .
* Jul 10 , 2000 Nenad Corbic o Fixed the M_BIT bug .
* Apr 25 , 2000 Nenad Corbic o Pass Modem messages to the API .
* Disable idle timeout in X25 API .
* Apr 14 , 2000 Nenad Corbic o Fixed : Large LCN number support .
* Maximum LCN number is 4095.
* Maximum number of X25 channels is 255.
* Apr 06 , 2000 Nenad Corbic o Added SMP Support .
* Mar 29 , 2000 Nenad Corbic o Added support for S514 PCI Card
* Mar 23 , 2000 Nenad Corbic o Improved task queue , BH handling .
* Mar 14 , 2000 Nenad Corbic o Updated Protocol Violation handling
* routines . Bug Fix .
* Mar 10 , 2000 Nenad Corbic o Bug Fix : corrupted mbox recovery .
* Mar 09 , 2000 Nenad Corbic o Fixed the auto HDLC bug .
* Mar 08 , 2000 Nenad Corbic o Fixed LAPB HDLC startup problems .
* Application must bring the link up
* before tx / rx , and bring the
* link down on close ( ) .
* Mar 06 , 2000 Nenad Corbic o Added an option for logging call setup
* information .
* Feb 29 , 2000 Nenad Corbic o Added support for LAPB HDLC API
* Feb 25 , 2000 Nenad Corbic o Fixed the modem failure handling .
* No Modem OOB message will be passed
* to the user .
* Feb 21 , 2000 Nenad Corbic o Added Xpipemon Debug Support
* Dec 30 , 1999 Nenad Corbic o Socket based X25API
* Sep 17 , 1998 Jaspreet Singh o Updates for 2.2 . X kernel
* Mar 15 , 1998 Alan Cox o 2.1 . x porting
* Dec 19 , 1997 Jaspreet Singh o Added multi - channel IPX support
* Nov 27 , 1997 Jaspreet Singh o Added protection against enabling of irqs
* when they are disabled .
* Nov 17 , 1997 Farhan Thawar o Added IPX support
* o Changed if_send ( ) to now buffer packets when
* the board is busy
* o Removed queueing of packets via the polling
* routing
* o Changed if_send ( ) critical flags to properly
* handle race conditions
* Nov 06 , 1997 Farhan Thawar o Added support for SVC timeouts
* o Changed PVC encapsulation to ETH_P_IP
* Jul 21 , 1997 Jaspreet Singh o Fixed freeing up of buffers using kfree ( )
* when packets are received .
* Mar 11 , 1997 Farhan Thawar Version 3.1 .1
* o added support for V35
* o changed if_send ( ) to return 0 if
* wandev . critical ( ) is true
* o free socket buffer in if_send ( ) if
* returning 0
* o added support for single ' @ ' address to
* accept all incoming calls
* o fixed bug in set_chan_state ( ) to disconnect
* Jan 15 , 1997 Gene Kozin Version 3.1 .0
* o implemented exec ( ) entry point
* Jan 07 , 1997 Gene Kozin Initial version .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*======================================================
* Includes
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# include <linux/module.h>
# include <linux/kernel.h> /* printk(), and other useful stuff */
# include <linux/stddef.h> /* offsetof(), etc. */
# include <linux/errno.h> /* return codes */
# include <linux/string.h> /* inline memset(), etc. */
# include <linux/ctype.h>
# include <linux/slab.h> /* kmalloc(), kfree() */
# include <linux/wanrouter.h> /* WAN router definitions */
# include <linux/wanpipe.h> /* WANPIPE common user API definitions */
# include <linux/workqueue.h>
2005-07-15 09:59:26 -07:00
# include <linux/jiffies.h> /* time_after() macro */
2005-04-16 15:20:36 -07:00
# include <asm/byteorder.h> /* htons(), etc. */
# include <asm/atomic.h>
# include <linux/delay.h> /* Experimental delay */
# include <asm/uaccess.h>
# include <linux/if.h>
# include <linux/if_arp.h>
# include <linux/sdla_x25.h> /* X.25 firmware API definitions */
# include <linux/if_wanpipe_common.h>
# include <linux/if_wanpipe.h>
/*======================================================
* Defines & Macros
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# define CMD_OK 0 /* normal firmware return code */
# define CMD_TIMEOUT 0xFF /* firmware command timed out */
# define MAX_CMD_RETRY 10 /* max number of firmware retries */
# define X25_CHAN_MTU 4096 /* unfragmented logical channel MTU */
# define X25_HRDHDR_SZ 7 /* max encapsulation header size */
# define X25_CONCT_TMOUT (90*HZ) /* link connection timeout */
# define X25_RECON_TMOUT (10*HZ) /* link connection timeout */
# define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */
# define HOLD_DOWN_TIME (30*HZ) /* link hold down time */
# define MAX_BH_BUFF 10
# define M_BIT 0x01
//#define PRINT_DEBUG 1
# ifdef PRINT_DEBUG
# define DBG_PRINTK(format, a...) printk(format, ## a)
# else
# define DBG_PRINTK(format, a...)
# endif
# define TMR_INT_ENABLED_POLL_ACTIVE 0x01
# define TMR_INT_ENABLED_POLL_CONNECT_ON 0x02
# define TMR_INT_ENABLED_POLL_CONNECT_OFF 0x04
# define TMR_INT_ENABLED_POLL_DISCONNECT 0x08
# define TMR_INT_ENABLED_CMD_EXEC 0x10
# define TMR_INT_ENABLED_UPDATE 0x20
# define TMR_INT_ENABLED_UDP_PKT 0x40
# define MAX_X25_ADDR_SIZE 16
# define MAX_X25_DATA_SIZE 129
# define MAX_X25_FACL_SIZE 110
# define TRY_CMD_AGAIN 2
# define DELAY_RESULT 1
# define RETURN_RESULT 0
# define DCD(x) (x & 0x03 ? "HIGH" : "LOW")
# define CTS(x) (x & 0x05 ? "HIGH" : "LOW")
/* Driver will not write log messages about
* modem status if defined . */
# define MODEM_NOT_LOG 1
/*====================================================
* For IPXWAN
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
/*====================================================
* MEMORY DEBUGGING FUNCTION
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
# define KMEM_SAFETYZONE 8
static void * dbg_kmalloc ( unsigned int size , int prio , int line ) {
int i = 0 ;
void * v = kmalloc ( size + sizeof ( unsigned int ) + 2 * KMEM_SAFETYZONE * 8 , prio ) ;
char * c1 = v ;
c1 + = sizeof ( unsigned int ) ;
* ( ( unsigned int * ) v ) = size ;
for ( i = 0 ; i < KMEM_SAFETYZONE ; i + + ) {
c1 [ 0 ] = ' D ' ; c1 [ 1 ] = ' E ' ; c1 [ 2 ] = ' A ' ; c1 [ 3 ] = ' D ' ;
c1 [ 4 ] = ' B ' ; c1 [ 5 ] = ' E ' ; c1 [ 6 ] = ' E ' ; c1 [ 7 ] = ' F ' ;
c1 + = 8 ;
}
c1 + = size ;
for ( i = 0 ; i < KMEM_SAFETYZONE ; i + + ) {
c1 [ 0 ] = ' M ' ; c1 [ 1 ] = ' U ' ; c1 [ 2 ] = ' N ' ; c1 [ 3 ] = ' G ' ;
c1 [ 4 ] = ' W ' ; c1 [ 5 ] = ' A ' ; c1 [ 6 ] = ' L ' ; c1 [ 7 ] = ' L ' ;
c1 + = 8 ;
}
v = ( ( char * ) v ) + sizeof ( unsigned int ) + KMEM_SAFETYZONE * 8 ;
printk ( KERN_INFO " line %d kmalloc(%d,%d) = %p \n " , line , size , prio , v ) ;
return v ;
}
static void dbg_kfree ( void * v , int line ) {
unsigned int * sp = ( unsigned int * ) ( ( ( char * ) v ) - ( sizeof ( unsigned int ) + KMEM_SAFETYZONE * 8 ) ) ;
unsigned int size = * sp ;
char * c1 = ( ( char * ) v ) - KMEM_SAFETYZONE * 8 ;
int i = 0 ;
for ( i = 0 ; i < KMEM_SAFETYZONE ; i + + ) {
if ( c1 [ 0 ] ! = ' D ' | | c1 [ 1 ] ! = ' E ' | | c1 [ 2 ] ! = ' A ' | | c1 [ 3 ] ! = ' D '
| | c1 [ 4 ] ! = ' B ' | | c1 [ 5 ] ! = ' E ' | | c1 [ 6 ] ! = ' E ' | | c1 [ 7 ] ! = ' F ' ) {
printk ( KERN_INFO " kmalloced block at %p has been corrupted (underrun)! \n " , v ) ;
printk ( KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x \n " , i * 8 ,
c1 [ 0 ] , c1 [ 1 ] , c1 [ 2 ] , c1 [ 3 ] , c1 [ 4 ] , c1 [ 5 ] , c1 [ 6 ] , c1 [ 7 ] ) ;
}
c1 + = 8 ;
}
c1 + = size ;
for ( i = 0 ; i < KMEM_SAFETYZONE ; i + + ) {
if ( c1 [ 0 ] ! = ' M ' | | c1 [ 1 ] ! = ' U ' | | c1 [ 2 ] ! = ' N ' | | c1 [ 3 ] ! = ' G '
| | c1 [ 4 ] ! = ' W ' | | c1 [ 5 ] ! = ' A ' | | c1 [ 6 ] ! = ' L ' | | c1 [ 7 ] ! = ' L '
) {
printk ( KERN_INFO " kmalloced block at %p has been corrupted (overrun): \n " , v ) ;
printk ( KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x \n " , i * 8 ,
c1 [ 0 ] , c1 [ 1 ] , c1 [ 2 ] , c1 [ 3 ] , c1 [ 4 ] , c1 [ 5 ] , c1 [ 6 ] , c1 [ 7 ] ) ;
}
c1 + = 8 ;
}
printk ( KERN_INFO " line %d kfree(%p) \n " , line , v ) ;
v = ( ( char * ) v ) - ( sizeof ( unsigned int ) + KMEM_SAFETYZONE * 8 ) ;
kfree ( v ) ;
}
# define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
# define kfree(x) dbg_kfree(x,__LINE__)
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*===============================================
* Data Structures
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*========================================================
* Name : x25_channel
*
* Purpose : To hold private informaton for each
* logical channel .
*
* Rationale : Per - channel debugging is possible if each
* channel has its own private area .
*
* Assumptions :
*
* Description : This is an extention of the struct net_device
* we create for each network interface to keep
* the rest of X .25 channel - specific data .
*
* Construct : Typedef
*/
typedef struct x25_channel
{
wanpipe_common_t common ; /* common area for x25api and socket */
char name [ WAN_IFNAME_SZ + 1 ] ; /* interface name, ASCIIZ */
char addr [ WAN_ADDRESS_SZ + 1 ] ; /* media address, ASCIIZ */
unsigned tx_pkt_size ;
unsigned short protocol ; /* ethertype, 0 - multiplexed */
char drop_sequence ; /* mark sequence for dropping */
unsigned long state_tick ; /* time of the last state change */
unsigned idle_timeout ; /* sec, before disconnecting */
unsigned long i_timeout_sofar ; /* # of sec's we've been idle */
unsigned hold_timeout ; /* sec, before re-connecting */
unsigned long tick_counter ; /* counter for transmit time out */
char devtint ; /* Weather we should dev_tint() */
struct sk_buff * rx_skb ; /* receive socket buffer */
struct sk_buff * tx_skb ; /* transmit socket buffer */
bh_data_t * bh_head ; /* Circular buffer for x25api_bh */
unsigned long tq_working ;
volatile int bh_write ;
volatile int bh_read ;
atomic_t bh_buff_used ;
sdla_t * card ; /* -> owner */
struct net_device * dev ; /* -> bound devce */
int ch_idx ;
unsigned char enable_IPX ;
unsigned long network_number ;
struct net_device_stats ifstats ; /* interface statistics */
unsigned short transmit_length ;
unsigned short tx_offset ;
char transmit_buffer [ X25_CHAN_MTU + sizeof ( x25api_hdr_t ) ] ;
if_send_stat_t if_send_stat ;
rx_intr_stat_t rx_intr_stat ;
pipe_mgmt_stat_t pipe_mgmt_stat ;
unsigned long router_start_time ; /* Router start time in seconds */
unsigned long router_up_time ;
} x25_channel_t ;
/* FIXME Take this out */
# ifdef NEX_OLD_CALL_INFO
typedef struct x25_call_info
{
char dest [ 17 ] ; PACKED ; /* ASCIIZ destination address */
char src [ 17 ] ; PACKED ; /* ASCIIZ source address */
char nuser ; PACKED ; /* number of user data bytes */
unsigned char user [ 127 ] ; PACKED ; /* user data */
char nfacil ; PACKED ; /* number of facilities */
struct
{
unsigned char code ; PACKED ;
unsigned char parm ; PACKED ;
} facil [ 64 ] ; /* facilities */
} x25_call_info_t ;
# else
typedef struct x25_call_info
{
char dest [ MAX_X25_ADDR_SIZE ] PACKED ; /* ASCIIZ destination address */
char src [ MAX_X25_ADDR_SIZE ] PACKED ; /* ASCIIZ source address */
unsigned char nuser PACKED ;
unsigned char user [ MAX_X25_DATA_SIZE ] PACKED ; /* user data */
unsigned char nfacil PACKED ;
unsigned char facil [ MAX_X25_FACL_SIZE ] PACKED ;
unsigned short lcn PACKED ;
} x25_call_info_t ;
# endif
/*===============================================
* Private Function Prototypes
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*=================================================
* WAN link driver entry points . These are
* called by the WAN router module .
*/
static int update ( struct wan_device * wandev ) ;
static int new_if ( struct wan_device * wandev , struct net_device * dev ,
wanif_conf_t * conf ) ;
static int del_if ( struct wan_device * wandev , struct net_device * dev ) ;
static void disable_comm ( sdla_t * card ) ;
static void disable_comm_shutdown ( sdla_t * card ) ;
/*=================================================
* WANPIPE - specific entry points
*/
static int wpx_exec ( struct sdla * card , void * u_cmd , void * u_data ) ;
static void x25api_bh ( struct net_device * dev ) ;
static int x25api_bh_cleanup ( struct net_device * dev ) ;
static int bh_enqueue ( struct net_device * dev , struct sk_buff * skb ) ;
/*=================================================
* Network device interface
*/
static int if_init ( struct net_device * dev ) ;
static int if_open ( struct net_device * dev ) ;
static int if_close ( struct net_device * dev ) ;
static int if_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , void * daddr , void * saddr , unsigned len ) ;
static int if_rebuild_hdr ( struct sk_buff * skb ) ;
static int if_send ( struct sk_buff * skb , struct net_device * dev ) ;
static struct net_device_stats * if_stats ( struct net_device * dev ) ;
static void if_tx_timeout ( struct net_device * dev ) ;
/*=================================================
* Interrupt handlers
*/
static void wpx_isr ( sdla_t * ) ;
static void rx_intr ( sdla_t * ) ;
static void tx_intr ( sdla_t * ) ;
static void status_intr ( sdla_t * ) ;
static void event_intr ( sdla_t * ) ;
static void spur_intr ( sdla_t * ) ;
static void timer_intr ( sdla_t * ) ;
static int tx_intr_send ( sdla_t * card , struct net_device * dev ) ;
static struct net_device * move_dev_to_next ( sdla_t * card ,
struct net_device * dev ) ;
/*=================================================
* Background polling routines
*/
static void wpx_poll ( sdla_t * card ) ;
static void poll_disconnected ( sdla_t * card ) ;
static void poll_connecting ( sdla_t * card ) ;
static void poll_active ( sdla_t * card ) ;
static void trigger_x25_poll ( sdla_t * card ) ;
static void x25_timer_routine ( unsigned long data ) ;
/*=================================================
* X .25 firmware interface functions
*/
static int x25_get_version ( sdla_t * card , char * str ) ;
static int x25_configure ( sdla_t * card , TX25Config * conf ) ;
static int hdlc_configure ( sdla_t * card , TX25Config * conf ) ;
static int set_hdlc_level ( sdla_t * card ) ;
static int x25_get_err_stats ( sdla_t * card ) ;
static int x25_get_stats ( sdla_t * card ) ;
static int x25_set_intr_mode ( sdla_t * card , int mode ) ;
static int x25_close_hdlc ( sdla_t * card ) ;
static int x25_open_hdlc ( sdla_t * card ) ;
static int x25_setup_hdlc ( sdla_t * card ) ;
static int x25_set_dtr ( sdla_t * card , int dtr ) ;
static int x25_get_chan_conf ( sdla_t * card , x25_channel_t * chan ) ;
static int x25_place_call ( sdla_t * card , x25_channel_t * chan ) ;
static int x25_accept_call ( sdla_t * card , int lcn , int qdm ) ;
static int x25_clear_call ( sdla_t * card , int lcn , int cause , int diagn ) ;
static int x25_send ( sdla_t * card , int lcn , int qdm , int len , void * buf ) ;
static int x25_fetch_events ( sdla_t * card ) ;
static int x25_error ( sdla_t * card , int err , int cmd , int lcn ) ;
/*=================================================
* X .25 asynchronous event handlers
*/
static int incoming_call ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb ) ;
static int call_accepted ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb ) ;
static int call_cleared ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb ) ;
static int timeout_event ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb ) ;
static int restart_event ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb ) ;
/*=================================================
* Miscellaneous functions
*/
static int connect ( sdla_t * card ) ;
static int disconnect ( sdla_t * card ) ;
static struct net_device * get_dev_by_lcn ( struct wan_device * wandev ,
unsigned lcn ) ;
static int chan_connect ( struct net_device * dev ) ;
static int chan_disc ( struct net_device * dev ) ;
static void set_chan_state ( struct net_device * dev , int state ) ;
static int chan_send ( struct net_device * dev , void * buff , unsigned data_len ,
unsigned char tx_intr ) ;
static unsigned char bps_to_speed_code ( unsigned long bps ) ;
static unsigned int dec_to_uint ( unsigned char * str , int len ) ;
static unsigned int hex_to_uint ( unsigned char * , int ) ;
static void parse_call_info ( unsigned char * , x25_call_info_t * ) ;
static struct net_device * find_channel ( sdla_t * card , unsigned lcn ) ;
static void bind_lcn_to_dev ( sdla_t * card , struct net_device * dev , unsigned lcn ) ;
static void setup_for_delayed_transmit ( struct net_device * dev ,
void * buf , unsigned len ) ;
/*=================================================
* X25 API Functions
*/
static int wanpipe_pull_data_in_skb ( sdla_t * card , struct net_device * dev ,
struct sk_buff * * ) ;
static void timer_intr_exec ( sdla_t * , unsigned char ) ;
static int execute_delayed_cmd ( sdla_t * card , struct net_device * dev ,
mbox_cmd_t * usr_cmd , char bad_cmd ) ;
static int api_incoming_call ( sdla_t * , TX25Mbox * , int ) ;
static int alloc_and_init_skb_buf ( sdla_t * , struct sk_buff * * , int ) ;
static void send_delayed_cmd_result ( sdla_t * card , struct net_device * dev ,
TX25Mbox * mbox ) ;
static int clear_confirm_event ( sdla_t * , TX25Mbox * ) ;
static void send_oob_msg ( sdla_t * card , struct net_device * dev , TX25Mbox * mbox ) ;
static int timer_intr_cmd_exec ( sdla_t * card ) ;
static void api_oob_event ( sdla_t * card , TX25Mbox * mbox ) ;
static int check_bad_command ( sdla_t * card , struct net_device * dev ) ;
static int channel_disconnect ( sdla_t * card , struct net_device * dev ) ;
static void hdlc_link_down ( sdla_t * ) ;
/*=================================================
* XPIPEMON Functions
*/
static int process_udp_mgmt_pkt ( sdla_t * ) ;
static int udp_pkt_type ( struct sk_buff * , sdla_t * ) ;
static int reply_udp ( unsigned char * , unsigned int ) ;
static void init_x25_channel_struct ( x25_channel_t * ) ;
static void init_global_statistics ( sdla_t * ) ;
static int store_udp_mgmt_pkt ( int udp_type , char udp_pkt_src , sdla_t * card ,
struct net_device * dev ,
struct sk_buff * skb , int lcn ) ;
static unsigned short calc_checksum ( char * , int ) ;
/*=================================================
* IPX functions
*/
static void switch_net_numbers ( unsigned char * , unsigned long , unsigned char ) ;
static int handle_IPXWAN ( unsigned char * , char * , unsigned char ,
unsigned long , unsigned short ) ;
extern void disable_irq ( unsigned int ) ;
extern void enable_irq ( unsigned int ) ;
static void S508_S514_lock ( sdla_t * , unsigned long * ) ;
static void S508_S514_unlock ( sdla_t * , unsigned long * ) ;
/*=================================================
* Global Variables
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*=================================================
* Public Functions
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*===================================================================
* wpx_init : X .25 Protocol Initialization routine .
*
* Purpose : To initialize the protocol / firmware .
*
* Rationale : This function is called by setup ( ) function , in
* sdlamain . c , to dynamically setup the x25 protocol .
* This is the first protocol specific function , which
* executes once on startup .
*
* Description : This procedure initializes the x25 firmware and
* sets up the mailbox , transmit and receive buffer
* pointers . It also initializes all debugging structures
* and sets up the X25 environment .
*
* Sets up hardware options defined by user in [ wanpipe # ]
* section of wanpipe # . conf configuration file .
*
* At this point adapter is completely initialized
* and X .25 firmware is running .
* o read firmware version ( to make sure it ' s alive )
* o configure adapter
* o initialize protocol - specific fields of the
* adapter data space .
*
* Called by : setup ( ) function in sdlamain . c
*
* Assumptions : None
*
* Warnings : None
*
* Return : 0 o . k .
* < 0 failure .
*/
int wpx_init ( sdla_t * card , wandev_conf_t * conf )
{
union {
char str [ 80 ] ;
TX25Config cfg ;
} u ;
/* Verify configuration ID */
if ( conf - > config_id ! = WANCONFIG_X25 ) {
printk ( KERN_INFO " %s: invalid configuration ID %u! \n " ,
card - > devname , conf - > config_id )
;
return - EINVAL ;
}
/* Initialize protocol-specific fields */
card - > mbox = ( void * ) ( card - > hw . dpmbase + X25_MBOX_OFFS ) ;
card - > rxmb = ( void * ) ( card - > hw . dpmbase + X25_RXMBOX_OFFS ) ;
card - > flags = ( void * ) ( card - > hw . dpmbase + X25_STATUS_OFFS ) ;
/* Initialize for S514 Card */
if ( card - > hw . type = = SDLA_S514 ) {
card - > mbox + = X25_MB_VECTOR ;
card - > flags + = X25_MB_VECTOR ;
card - > rxmb + = X25_MB_VECTOR ;
}
/* Read firmware version. Note that when adapter initializes, it
* clears the mailbox , so it may appear that the first command was
* executed successfully when in fact it was merely erased . To work
* around this , we execute the first command twice .
*/
if ( x25_get_version ( card , NULL ) | | x25_get_version ( card , u . str ) )
return - EIO ;
/* X25 firmware can run ether in X25 or LAPB HDLC mode.
* Check the user defined option and configure accordingly */
if ( conf - > u . x25 . LAPB_hdlc_only = = WANOPT_YES ) {
if ( set_hdlc_level ( card ) ! = CMD_OK ) {
return - EIO ;
} else {
printk ( KERN_INFO " %s: running LAP_B HDLC firmware v%s \n " ,
card - > devname , u . str ) ;
}
card - > u . x . LAPB_hdlc = 1 ;
} else {
printk ( KERN_INFO " %s: running X.25 firmware v%s \n " ,
card - > devname , u . str ) ;
card - > u . x . LAPB_hdlc = 0 ;
}
/* Configure adapter. Here we set resonable defaults, then parse
* device configuration structure and set configuration options .
* Most configuration options are verified and corrected ( if
* necessary ) since we can ' t rely on the adapter to do so .
*/
memset ( & u . cfg , 0 , sizeof ( u . cfg ) ) ;
u . cfg . t1 = 3 ;
u . cfg . n2 = 10 ;
u . cfg . autoHdlc = 1 ; /* automatic HDLC connection */
u . cfg . hdlcWindow = 7 ;
u . cfg . pktWindow = 2 ;
u . cfg . station = 1 ; /* DTE */
u . cfg . options = 0x0090 ; /* disable D-bit pragmatics */
u . cfg . ccittCompat = 1988 ;
u . cfg . t10t20 = 30 ;
u . cfg . t11t21 = 30 ;
u . cfg . t12t22 = 30 ;
u . cfg . t13t23 = 30 ;
u . cfg . t16t26 = 30 ;
u . cfg . t28 = 30 ;
u . cfg . r10r20 = 5 ;
u . cfg . r12r22 = 5 ;
u . cfg . r13r23 = 5 ;
u . cfg . responseOpt = 1 ; /* RR's after every packet */
if ( card - > u . x . LAPB_hdlc ) {
u . cfg . hdlcMTU = 1027 ;
}
if ( conf - > u . x25 . x25_conf_opt ) {
u . cfg . options = conf - > u . x25 . x25_conf_opt ;
}
if ( conf - > clocking ! = WANOPT_EXTERNAL )
u . cfg . baudRate = bps_to_speed_code ( conf - > bps ) ;
if ( conf - > station ! = WANOPT_DTE ) {
u . cfg . station = 0 ; /* DCE mode */
}
if ( conf - > interface ! = WANOPT_RS232 ) {
u . cfg . hdlcOptions | = 0x80 ; /* V35 mode */
}
/* adjust MTU */
if ( ! conf - > mtu | | ( conf - > mtu > = 1024 ) )
card - > wandev . mtu = 1024 ;
else if ( conf - > mtu > = 512 )
card - > wandev . mtu = 512 ;
else if ( conf - > mtu > = 256 )
card - > wandev . mtu = 256 ;
else if ( conf - > mtu > = 128 )
card - > wandev . mtu = 128 ;
else
card - > wandev . mtu = 64 ;
u . cfg . defPktSize = u . cfg . pktMTU = card - > wandev . mtu ;
if ( conf - > u . x25 . hi_pvc ) {
card - > u . x . hi_pvc = min_t ( unsigned int , conf - > u . x25 . hi_pvc , MAX_LCN_NUM ) ;
card - > u . x . lo_pvc = min_t ( unsigned int , conf - > u . x25 . lo_pvc , card - > u . x . hi_pvc ) ;
}
if ( conf - > u . x25 . hi_svc ) {
card - > u . x . hi_svc = min_t ( unsigned int , conf - > u . x25 . hi_svc , MAX_LCN_NUM ) ;
card - > u . x . lo_svc = min_t ( unsigned int , conf - > u . x25 . lo_svc , card - > u . x . hi_svc ) ;
}
/* Figure out the total number of channels to configure */
card - > u . x . num_of_ch = 0 ;
if ( card - > u . x . hi_svc ! = 0 ) {
card - > u . x . num_of_ch = ( card - > u . x . hi_svc - card - > u . x . lo_svc ) + 1 ;
}
if ( card - > u . x . hi_pvc ! = 0 ) {
card - > u . x . num_of_ch + = ( card - > u . x . hi_pvc - card - > u . x . lo_pvc ) + 1 ;
}
if ( card - > u . x . num_of_ch = = 0 ) {
printk ( KERN_INFO " %s: ERROR, Minimum number of PVC/SVC channels is 1 ! \n "
" %s: Please set the Lowest/Highest PVC/SVC values ! \n " ,
card - > devname , card - > devname ) ;
return - ECHRNG ;
}
u . cfg . loPVC = card - > u . x . lo_pvc ;
u . cfg . hiPVC = card - > u . x . hi_pvc ;
u . cfg . loTwoWaySVC = card - > u . x . lo_svc ;
u . cfg . hiTwoWaySVC = card - > u . x . hi_svc ;
if ( conf - > u . x25 . hdlc_window )
u . cfg . hdlcWindow = min_t ( unsigned int , conf - > u . x25 . hdlc_window , 7 ) ;
if ( conf - > u . x25 . pkt_window )
u . cfg . pktWindow = min_t ( unsigned int , conf - > u . x25 . pkt_window , 7 ) ;
if ( conf - > u . x25 . t1 )
u . cfg . t1 = min_t ( unsigned int , conf - > u . x25 . t1 , 30 ) ;
if ( conf - > u . x25 . t2 )
u . cfg . t2 = min_t ( unsigned int , conf - > u . x25 . t2 , 29 ) ;
if ( conf - > u . x25 . t4 )
u . cfg . t4 = min_t ( unsigned int , conf - > u . x25 . t4 , 240 ) ;
if ( conf - > u . x25 . n2 )
u . cfg . n2 = min_t ( unsigned int , conf - > u . x25 . n2 , 30 ) ;
if ( conf - > u . x25 . t10_t20 )
u . cfg . t10t20 = min_t ( unsigned int , conf - > u . x25 . t10_t20 , 255 ) ;
if ( conf - > u . x25 . t11_t21 )
u . cfg . t11t21 = min_t ( unsigned int , conf - > u . x25 . t11_t21 , 255 ) ;
if ( conf - > u . x25 . t12_t22 )
u . cfg . t12t22 = min_t ( unsigned int , conf - > u . x25 . t12_t22 , 255 ) ;
if ( conf - > u . x25 . t13_t23 )
u . cfg . t13t23 = min_t ( unsigned int , conf - > u . x25 . t13_t23 , 255 ) ;
if ( conf - > u . x25 . t16_t26 )
u . cfg . t16t26 = min_t ( unsigned int , conf - > u . x25 . t16_t26 , 255 ) ;
if ( conf - > u . x25 . t28 )
u . cfg . t28 = min_t ( unsigned int , conf - > u . x25 . t28 , 255 ) ;
if ( conf - > u . x25 . r10_r20 )
u . cfg . r10r20 = min_t ( unsigned int , conf - > u . x25 . r10_r20 , 250 ) ;
if ( conf - > u . x25 . r12_r22 )
u . cfg . r12r22 = min_t ( unsigned int , conf - > u . x25 . r12_r22 , 250 ) ;
if ( conf - > u . x25 . r13_r23 )
u . cfg . r13r23 = min_t ( unsigned int , conf - > u . x25 . r13_r23 , 250 ) ;
if ( conf - > u . x25 . ccitt_compat )
u . cfg . ccittCompat = conf - > u . x25 . ccitt_compat ;
/* initialize adapter */
if ( card - > u . x . LAPB_hdlc ) {
if ( hdlc_configure ( card , & u . cfg ) ! = CMD_OK )
return - EIO ;
} else {
if ( x25_configure ( card , & u . cfg ) ! = CMD_OK )
return - EIO ;
}
if ( ( x25_close_hdlc ( card ) ! = CMD_OK ) | | /* close HDLC link */
( x25_set_dtr ( card , 0 ) ! = CMD_OK ) ) /* drop DTR */
return - EIO ;
/* Initialize protocol-specific fields of adapter data space */
card - > wandev . bps = conf - > bps ;
card - > wandev . interface = conf - > interface ;
card - > wandev . clocking = conf - > clocking ;
card - > wandev . station = conf - > station ;
card - > isr = & wpx_isr ;
card - > poll = NULL ; //&wpx_poll;
card - > disable_comm = & disable_comm ;
card - > exec = & wpx_exec ;
card - > wandev . update = & update ;
card - > wandev . new_if = & new_if ;
card - > wandev . del_if = & del_if ;
/* WARNING: This function cannot exit with an error
* after the change of state */
card - > wandev . state = WAN_DISCONNECTED ;
card - > wandev . enable_tx_int = 0 ;
card - > irq_dis_if_send_count = 0 ;
card - > irq_dis_poll_count = 0 ;
card - > u . x . tx_dev = NULL ;
card - > u . x . no_dev = 0 ;
/* Configure for S514 PCI Card */
if ( card - > hw . type = = SDLA_S514 ) {
card - > u . x . hdlc_buf_status =
( volatile unsigned char * )
( card - > hw . dpmbase + X25_MB_VECTOR + X25_MISC_HDLC_BITS ) ;
} else {
card - > u . x . hdlc_buf_status =
( volatile unsigned char * ) ( card - > hw . dpmbase + X25_MISC_HDLC_BITS ) ;
}
card - > u . x . poll_device = NULL ;
card - > wandev . udp_port = conf - > udp_port ;
/* Enable or disable call setup logging */
if ( conf - > u . x25 . logging = = WANOPT_YES ) {
printk ( KERN_INFO " %s: Enabling Call Logging. \n " ,
card - > devname ) ;
card - > u . x . logging = 1 ;
} else {
card - > u . x . logging = 0 ;
}
/* Enable or disable modem status reporting */
if ( conf - > u . x25 . oob_on_modem = = WANOPT_YES ) {
printk ( KERN_INFO " %s: Enabling OOB on Modem change. \n " ,
card - > devname ) ;
card - > u . x . oob_on_modem = 1 ;
} else {
card - > u . x . oob_on_modem = 0 ;
}
init_global_statistics ( card ) ;
INIT_WORK ( & card - > u . x . x25_poll_work , ( void * ) wpx_poll , card ) ;
init_timer ( & card - > u . x . x25_timer ) ;
card - > u . x . x25_timer . data = ( unsigned long ) card ;
card - > u . x . x25_timer . function = x25_timer_routine ;
return 0 ;
}
/*=========================================================
* WAN Device Driver Entry Points
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*============================================================
* Name : update ( ) , Update device status & statistics .
*
* Purpose : To provide debugging and statitical
* information to the / proc file system .
* / proc / net / wanrouter / wanpipe #
*
* Rationale : The / proc file system is used to collect
* information about the kernel and drivers .
* Using the / proc file system the user
* can see exactly what the sangoma drivers are
* doing . And in what state they are in .
*
* Description : Collect all driver statistical information
* and pass it to the top laywer .
*
* Since we have to execute a debugging command ,
* to obtain firmware statitics , we trigger a
* UPDATE function within the timer interrtup .
* We wait until the timer update is complete .
* Once complete return the appropriate return
* code to indicate that the update was successful .
*
* Called by : device_stat ( ) in wanmain . c
*
* Assumptions :
*
* Warnings : This function will degrade the performance
* of the router , since it uses the mailbox .
*
* Return : 0 OK
* < 0 Failed ( or busy ) .
*/
static int update ( struct wan_device * wandev )
{
volatile sdla_t * card ;
TX25Status * status ;
unsigned long timeout ;
/* sanity checks */
if ( ( wandev = = NULL ) | | ( wandev - > private = = NULL ) )
return - EFAULT ;
if ( wandev - > state = = WAN_UNCONFIGURED )
return - ENODEV ;
if ( test_bit ( SEND_CRIT , ( void * ) & wandev - > critical ) )
return - EAGAIN ;
if ( ! wandev - > dev )
return - ENODEV ;
card = wandev - > private ;
status = card - > flags ;
card - > u . x . timer_int_enabled | = TMR_INT_ENABLED_UPDATE ;
status - > imask | = INTR_ON_TIMER ;
timeout = jiffies ;
for ( ; ; ) {
if ( ! ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_UPDATE ) ) {
break ;
}
2005-07-15 09:59:26 -07:00
if ( time_after ( jiffies , timeout + 1 * HZ ) ) {
2005-04-16 15:20:36 -07:00
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_UPDATE ;
return - EAGAIN ;
}
}
return 0 ;
}
/*===================================================================
* Name : new_if
*
* Purpose : To allocate and initialize resources for a
* new logical channel .
*
* Rationale : A new channel can be added dynamically via
* ioctl call .
*
* Description : Allocate a private channel structure , x25_channel_t .
* Parse the user interface options from wanpipe # . conf
* configuration file .
* Bind the private are into the network device private
* area pointer ( dev - > priv ) .
* Prepare the network device structure for registration .
*
* Called by : ROUTER_IFNEW Ioctl call , from wanrouter_ioctl ( )
* ( wanmain . c )
*
* Assumptions : None
*
* Warnings : None
*
* Return : 0 Ok
* < 0 Failed ( channel will not be created )
*/
static int new_if ( struct wan_device * wandev , struct net_device * dev ,
wanif_conf_t * conf )
{
sdla_t * card = wandev - > private ;
x25_channel_t * chan ;
int err = 0 ;
if ( ( conf - > name [ 0 ] = = ' \0 ' ) | | ( strlen ( conf - > name ) > WAN_IFNAME_SZ ) ) {
printk ( KERN_INFO " %s: invalid interface name! \n " ,
card - > devname ) ;
return - EINVAL ;
}
if ( card - > wandev . new_if_cnt + + > 0 & & card - > u . x . LAPB_hdlc ) {
printk ( KERN_INFO " %s: Error: Running LAPB HDLC Mode ! \n " ,
card - > devname ) ;
printk ( KERN_INFO
" %s: Maximum number of network interfaces must be one ! \n " ,
card - > devname ) ;
return - EEXIST ;
}
/* allocate and initialize private data */
chan = kmalloc ( sizeof ( x25_channel_t ) , GFP_ATOMIC ) ;
if ( chan = = NULL ) {
return - ENOMEM ;
}
memset ( chan , 0 , sizeof ( x25_channel_t ) ) ;
/* Bug Fix: Seg Err on PVC startup
* It must be here since bind_lcn_to_dev expects
* it bellow */
dev - > priv = chan ;
strcpy ( chan - > name , conf - > name ) ;
chan - > card = card ;
chan - > dev = dev ;
chan - > common . sk = NULL ;
chan - > common . func = NULL ;
chan - > common . rw_bind = 0 ;
chan - > tx_skb = chan - > rx_skb = NULL ;
/* verify media address */
if ( conf - > addr [ 0 ] = = ' @ ' ) { /* SVC */
chan - > common . svc = 1 ;
strncpy ( chan - > addr , & conf - > addr [ 1 ] , WAN_ADDRESS_SZ ) ;
/* Set channel timeouts (default if not specified) */
chan - > idle_timeout = ( conf - > idle_timeout ) ?
conf - > idle_timeout : 90 ;
chan - > hold_timeout = ( conf - > hold_timeout ) ?
conf - > hold_timeout : 10 ;
} else if ( is_digit ( conf - > addr [ 0 ] ) ) { /* PVC */
int lcn = dec_to_uint ( conf - > addr , 0 ) ;
if ( ( lcn > = card - > u . x . lo_pvc ) & & ( lcn < = card - > u . x . hi_pvc ) ) {
bind_lcn_to_dev ( card , dev , lcn ) ;
} else {
printk ( KERN_ERR
" %s: PVC %u is out of range on interface %s! \n " ,
wandev - > name , lcn , chan - > name ) ;
err = - EINVAL ;
}
} else {
printk ( KERN_ERR
" %s: invalid media address on interface %s! \n " ,
wandev - > name , chan - > name ) ;
err = - EINVAL ;
}
if ( strcmp ( conf - > usedby , " WANPIPE " ) = = 0 ) {
printk ( KERN_INFO " %s: Running in WANPIPE mode %s \n " ,
wandev - > name , chan - > name ) ;
chan - > common . usedby = WANPIPE ;
chan - > protocol = htons ( ETH_P_IP ) ;
} else if ( strcmp ( conf - > usedby , " API " ) = = 0 ) {
chan - > common . usedby = API ;
printk ( KERN_INFO " %s: Running in API mode %s \n " ,
wandev - > name , chan - > name ) ;
chan - > protocol = htons ( X25_PROT ) ;
}
if ( err ) {
kfree ( chan ) ;
dev - > priv = NULL ;
return err ;
}
chan - > enable_IPX = conf - > enable_IPX ;
if ( chan - > enable_IPX )
chan - > protocol = htons ( ETH_P_IPX ) ;
if ( conf - > network_number )
chan - > network_number = conf - > network_number ;
else
chan - > network_number = 0xDEADBEEF ;
/* prepare network device data space for registration */
strcpy ( dev - > name , chan - > name ) ;
dev - > init = & if_init ;
init_x25_channel_struct ( chan ) ;
return 0 ;
}
/*===================================================================
* Name : del_if ( ) , Remove a logical channel .
*
* Purpose : To dynamically remove a logical channel .
*
* Rationale : Each logical channel should be dynamically
* removable . This functin is called by an
* IOCTL_IFDEL ioctl call or shutdown ( ) .
*
* Description : Do nothing .
*
* Called by : IOCTL_IFDEL : wanrouter_ioctl ( ) from wanmain . c
* shutdown ( ) from sdlamain . c
*
* Assumptions :
*
* Warnings :
*
* Return : 0 Ok . Void function .
*/
//FIXME Del IF Should be taken out now.
static int del_if ( struct wan_device * wandev , struct net_device * dev )
{
return 0 ;
}
/*============================================================
* Name : wpx_exec
*
* Description : Execute adapter interface command .
* This option is currently dissabled .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int wpx_exec ( struct sdla * card , void * u_cmd , void * u_data )
{
return 0 ;
}
/*============================================================
* Name : disable_comm
*
* Description : Disable communications during shutdown .
* Dont check return code because there is
* nothing we can do about it .
*
* Warning : Dev and private areas are gone at this point .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void disable_comm ( sdla_t * card )
{
disable_comm_shutdown ( card ) ;
del_timer ( & card - > u . x . x25_timer ) ;
return ;
}
/*============================================================
* Network Device Interface
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*===================================================================
* Name : if_init ( ) , Netowrk Interface Initialization
*
* Purpose : To initialize a network interface device structure .
*
* Rationale : During network interface startup , the if_init
* is called by the kernel to initialize the
* netowrk device structure . Thus a driver
* can customze a network device .
*
* Description : Initialize the netowrk device call back
* routines . This is where we tell the kernel
* which function to use when it wants to send
* via our interface .
* Furthermore , we initialize the device flags ,
* MTU and physical address of the board .
*
* Called by : Kernel ( / usr / src / linux / net / core / dev . c )
* ( dev - > init ( ) )
*
* Assumptions : None
*
* Warnings : None
*
* Return : 0 Ok : Void function .
*/
static int if_init ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
struct wan_device * wandev = & card - > wandev ;
/* Initialize device driver entry points */
dev - > open = & if_open ;
dev - > stop = & if_close ;
dev - > hard_header = & if_header ;
dev - > rebuild_header = & if_rebuild_hdr ;
dev - > hard_start_xmit = & if_send ;
dev - > get_stats = & if_stats ;
dev - > tx_timeout = & if_tx_timeout ;
dev - > watchdog_timeo = TX_TIMEOUT ;
/* Initialize media-specific parameters */
dev - > type = ARPHRD_PPP ; /* ARP h/w type */
dev - > flags | = IFF_POINTOPOINT ;
dev - > flags | = IFF_NOARP ;
if ( chan - > common . usedby = = API ) {
dev - > mtu = X25_CHAN_MTU + sizeof ( x25api_hdr_t ) ;
} else {
dev - > mtu = card - > wandev . mtu ;
}
dev - > hard_header_len = X25_HRDHDR_SZ ; /* media header length */
dev - > addr_len = 2 ; /* hardware address length */
if ( ! chan - > common . svc ) {
* ( unsigned short * ) dev - > dev_addr = htons ( chan - > common . lcn ) ;
}
/* Initialize hardware parameters (just for reference) */
dev - > irq = wandev - > irq ;
dev - > dma = wandev - > dma ;
dev - > base_addr = wandev - > ioport ;
dev - > mem_start = ( unsigned long ) wandev - > maddr ;
dev - > mem_end = wandev - > maddr + wandev - > msize - 1 ;
/* Set transmit buffer queue length */
dev - > tx_queue_len = 100 ;
SET_MODULE_OWNER ( dev ) ;
/* FIXME Why are we doing this */
set_chan_state ( dev , WAN_DISCONNECTED ) ;
return 0 ;
}
/*===================================================================
* Name : if_open ( ) , Open / Bring up the Netowrk Interface
*
* Purpose : To bring up a network interface .
*
* Rationale :
*
* Description : Open network interface .
* o prevent module from unloading by incrementing use count
* o if link is disconnected then initiate connection
*
* Called by : Kernel ( / usr / src / linux / net / core / dev . c )
* ( dev - > open ( ) )
*
* Assumptions : None
*
* Warnings : None
*
* Return : 0 Ok
* < 0 Failure : Interface will not come up .
*/
static int if_open ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
struct timeval tv ;
unsigned long smp_flags ;
if ( netif_running ( dev ) )
return - EBUSY ;
chan - > tq_working = 0 ;
/* Initialize the workqueue */
INIT_WORK ( & chan - > common . wanpipe_work , ( void * ) x25api_bh , dev ) ;
/* Allocate and initialize BH circular buffer */
/* Add 1 to MAX_BH_BUFF so we don't have test with (MAX_BH_BUFF-1) */
chan - > bh_head = kmalloc ( ( sizeof ( bh_data_t ) * ( MAX_BH_BUFF + 1 ) ) , GFP_ATOMIC ) ;
if ( chan - > bh_head = = NULL ) {
printk ( KERN_INFO " %s: ERROR, failed to allocate memory ! BH_BUFFERS ! \n " ,
card - > devname ) ;
return - ENOBUFS ;
}
memset ( chan - > bh_head , 0 , ( sizeof ( bh_data_t ) * ( MAX_BH_BUFF + 1 ) ) ) ;
atomic_set ( & chan - > bh_buff_used , 0 ) ;
/* Increment the number of interfaces */
+ + card - > u . x . no_dev ;
wanpipe_open ( card ) ;
/* LAPB protocol only uses one interface, thus
* start the protocol after it comes up . */
if ( card - > u . x . LAPB_hdlc ) {
if ( card - > open_cnt = = 1 ) {
TX25Status * status = card - > flags ;
S508_S514_lock ( card , & smp_flags ) ;
x25_set_intr_mode ( card , INTR_ON_TIMER ) ;
status - > imask & = ~ INTR_ON_TIMER ;
S508_S514_unlock ( card , & smp_flags ) ;
}
} else {
/* X25 can have multiple interfaces thus, start the
* protocol once all interfaces are up */
//FIXME: There is a bug here. If interface is
//brought down and up, it will try to enable comm.
if ( card - > open_cnt = = card - > u . x . num_of_ch ) {
S508_S514_lock ( card , & smp_flags ) ;
connect ( card ) ;
S508_S514_unlock ( card , & smp_flags ) ;
mod_timer ( & card - > u . x . x25_timer , jiffies + HZ ) ;
}
}
/* Device is not up until the we are in connected state */
do_gettimeofday ( & tv ) ;
chan - > router_start_time = tv . tv_sec ;
netif_start_queue ( dev ) ;
return 0 ;
}
/*===================================================================
* Name : if_close ( ) , Close / Bring down the Netowrk Interface
*
* Purpose : To bring down a network interface .
*
* Rationale :
*
* Description : Close network interface .
* o decrement use module use count
*
* Called by : Kernel ( / usr / src / linux / net / core / dev . c )
* ( dev - > close ( ) )
* ifconfig < name > down : will trigger the kernel
* which will call this function .
*
* Assumptions : None
*
* Warnings : None
*
* Return : 0 Ok
* < 0 Failure : Interface will not exit properly .
*/
static int if_close ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
unsigned long smp_flags ;
netif_stop_queue ( dev ) ;
if ( ( chan - > common . state = = WAN_CONNECTED ) | |
( chan - > common . state = = WAN_CONNECTING ) ) {
S508_S514_lock ( card , & smp_flags ) ;
chan_disc ( dev ) ;
S508_S514_unlock ( card , & smp_flags ) ;
}
wanpipe_close ( card ) ;
S508_S514_lock ( card , & smp_flags ) ;
if ( chan - > bh_head ) {
int i ;
struct sk_buff * skb ;
for ( i = 0 ; i < ( MAX_BH_BUFF + 1 ) ; i + + ) {
skb = ( ( bh_data_t * ) & chan - > bh_head [ i ] ) - > skb ;
if ( skb ! = NULL ) {
dev_kfree_skb_any ( skb ) ;
}
}
kfree ( chan - > bh_head ) ;
chan - > bh_head = NULL ;
}
S508_S514_unlock ( card , & smp_flags ) ;
/* If this is the last close, disconnect physical link */
if ( ! card - > open_cnt ) {
S508_S514_lock ( card , & smp_flags ) ;
disconnect ( card ) ;
x25_set_intr_mode ( card , 0 ) ;
S508_S514_unlock ( card , & smp_flags ) ;
}
/* Decrement the number of interfaces */
- - card - > u . x . no_dev ;
return 0 ;
}
/*======================================================================
* Build media header .
* o encapsulate packet according to encapsulation type .
*
* The trick here is to put packet type ( Ethertype ) into ' protocol '
* field of the socket buffer , so that we don ' t forget it .
* If encapsulation fails , set skb - > protocol to 0 and discard
* packet later .
*
* Return : media header length .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int if_header ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , void * daddr , void * saddr ,
unsigned len )
{
x25_channel_t * chan = dev - > priv ;
int hdr_len = dev - > hard_header_len ;
skb - > protocol = htons ( type ) ;
if ( ! chan - > protocol ) {
hdr_len = wanrouter_encapsulate ( skb , dev , type ) ;
if ( hdr_len < 0 ) {
hdr_len = 0 ;
skb - > protocol = htons ( 0 ) ;
}
}
return hdr_len ;
}
/*===============================================================
* Re - build media header .
*
* Return : 1 physical address resolved .
* 0 physical address not resolved
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int if_rebuild_hdr ( struct sk_buff * skb )
{
struct net_device * dev = skb - > dev ;
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
printk ( KERN_INFO " %s: rebuild_header() called for interface %s! \n " ,
card - > devname , dev - > name ) ;
return 1 ;
}
/*============================================================================
* Handle transmit timeout event from netif watchdog
*/
static void if_tx_timeout ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
/* If our device stays busy for at least 5 seconds then we will
* kick start the device by making dev - > tbusy = 0. We expect
* that our device never stays busy more than 5 seconds . So this
* is only used as a last resort .
*/
+ + chan - > if_send_stat . if_send_tbusy_timeout ;
printk ( KERN_INFO " %s: Transmit timed out on %s \n " ,
card - > devname , dev - > name ) ;
netif_wake_queue ( dev ) ;
}
/*=========================================================================
* Send a packet on a network interface .
* o set tbusy flag ( marks start of the transmission ) .
* o check link state . If link is not up , then drop the packet .
* o check channel status . If it ' s down then initiate a call .
* o pass a packet to corresponding WAN device .
* o free socket buffer
*
* Return : 0 complete ( socket buffer must be freed )
* non - 0 packet may be re - transmitted ( tbusy must be set )
*
* Notes :
* 1. This routine is called either by the protocol stack or by the " net
* bottom half " (with interrupts enabled).
* 2. Setting tbusy flag will inhibit further transmit requests from the
* protocol stack and can be used for flow control with protocol layer .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int if_send ( struct sk_buff * skb , struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
TX25Status * status = card - > flags ;
int udp_type ;
unsigned long smp_flags = 0 ;
+ + chan - > if_send_stat . if_send_entry ;
netif_stop_queue ( dev ) ;
/* No need to check frame length, since socket code
* will perform the check for us */
chan - > tick_counter = jiffies ;
/* Critical region starts here */
S508_S514_lock ( card , & smp_flags ) ;
if ( test_and_set_bit ( SEND_CRIT , ( void * ) & card - > wandev . critical ) ) {
printk ( KERN_INFO " Hit critical in if_send()! %lx \n " , card - > wandev . critical ) ;
goto if_send_crit_exit ;
}
udp_type = udp_pkt_type ( skb , card ) ;
if ( udp_type ! = UDP_INVALID_TYPE ) {
if ( store_udp_mgmt_pkt ( udp_type , UDP_PKT_FRM_STACK , card , dev , skb ,
chan - > common . lcn ) ) {
status - > imask | = INTR_ON_TIMER ;
if ( udp_type = = UDP_XPIPE_TYPE ) {
chan - > if_send_stat . if_send_PIPE_request + + ;
}
}
netif_start_queue ( dev ) ;
clear_bit ( SEND_CRIT , ( void * ) & card - > wandev . critical ) ;
S508_S514_unlock ( card , & smp_flags ) ;
return 0 ;
}
if ( chan - > transmit_length ) {
//FIXME: This check doesn't make sense any more
if ( chan - > common . state ! = WAN_CONNECTED ) {
chan - > transmit_length = 0 ;
atomic_set ( & chan - > common . driver_busy , 0 ) ;
} else {
netif_stop_queue ( dev ) ;
+ + card - > u . x . tx_interrupts_pending ;
status - > imask | = INTR_ON_TX_FRAME ;
clear_bit ( SEND_CRIT , ( void * ) & card - > wandev . critical ) ;
S508_S514_unlock ( card , & smp_flags ) ;
return 1 ;
}
}
if ( card - > wandev . state ! = WAN_CONNECTED ) {
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > if_send_stat . if_send_wan_disconnected ;
} else if ( chan - > protocol & & ( chan - > protocol ! = skb - > protocol ) ) {
printk ( KERN_INFO
" %s: unsupported Ethertype 0x%04X on interface %s! \n " ,
chan - > name , htons ( skb - > protocol ) , dev - > name ) ;
printk ( KERN_INFO " PROTO %Xn " , htons ( chan - > protocol ) ) ;
+ + chan - > ifstats . tx_errors ;
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > if_send_stat . if_send_protocol_error ;
} else switch ( chan - > common . state ) {
case WAN_DISCONNECTED :
/* Try to establish connection. If succeded, then start
* transmission , else drop a packet .
*/
if ( chan - > common . usedby = = API ) {
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
break ;
} else {
if ( chan_connect ( dev ) ! = 0 ) {
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
break ;
}
}
/* fall through */
case WAN_CONNECTED :
if ( skb - > protocol = = htons ( ETH_P_IPX ) ) {
if ( chan - > enable_IPX ) {
switch_net_numbers ( skb - > data ,
chan - > network_number , 0 ) ;
} else {
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > ifstats . tx_dropped ;
+ + chan - > if_send_stat . if_send_protocol_error ;
goto if_send_crit_exit ;
}
}
/* We never drop here, if cannot send than, copy
* a packet into a transmit buffer
*/
chan_send ( dev , skb - > data , skb - > len , 0 ) ;
break ;
default :
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
break ;
}
if_send_crit_exit :
dev_kfree_skb_any ( skb ) ;
netif_start_queue ( dev ) ;
clear_bit ( SEND_CRIT , ( void * ) & card - > wandev . critical ) ;
S508_S514_unlock ( card , & smp_flags ) ;
return 0 ;
}
/*============================================================================
* Setup so that a frame can be transmitted on the occurrence of a transmit
* interrupt .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void setup_for_delayed_transmit ( struct net_device * dev , void * buf ,
unsigned len )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
TX25Status * status = card - > flags ;
+ + chan - > if_send_stat . if_send_adptr_bfrs_full ;
if ( chan - > transmit_length ) {
printk ( KERN_INFO " %s: Error, transmit length set in delayed transmit! \n " ,
card - > devname ) ;
return ;
}
if ( chan - > common . usedby = = API ) {
if ( len > X25_CHAN_MTU + sizeof ( x25api_hdr_t ) ) {
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
printk ( KERN_INFO " %s: Length is too big for delayed transmit \n " ,
card - > devname ) ;
return ;
}
} else {
if ( len > X25_MAX_DATA ) {
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
printk ( KERN_INFO " %s: Length is too big for delayed transmit \n " ,
card - > devname ) ;
return ;
}
}
chan - > transmit_length = len ;
atomic_set ( & chan - > common . driver_busy , 1 ) ;
memcpy ( chan - > transmit_buffer , buf , len ) ;
+ + chan - > if_send_stat . if_send_tx_int_enabled ;
/* Enable Transmit Interrupt */
+ + card - > u . x . tx_interrupts_pending ;
status - > imask | = INTR_ON_TX_FRAME ;
}
/*===============================================================
* net_device_stats
*
* Get ethernet - style interface statistics .
* Return a pointer to struct enet_statistics .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static struct net_device_stats * if_stats ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
if ( chan = = NULL )
return NULL ;
return & chan - > ifstats ;
}
/*
* Interrupt Handlers
*/
/*
* X .25 Interrupt Service Routine .
*/
static void wpx_isr ( sdla_t * card )
{
TX25Status * status = card - > flags ;
card - > in_isr = 1 ;
+ + card - > statistics . isr_entry ;
if ( test_bit ( PERI_CRIT , ( void * ) & card - > wandev . critical ) ) {
card - > in_isr = 0 ;
status - > iflags = 0 ;
return ;
}
if ( test_bit ( SEND_CRIT , ( void * ) & card - > wandev . critical ) ) {
printk ( KERN_INFO " %s: wpx_isr: wandev.critical set to 0x%02lx, int type = 0x%02x \n " ,
card - > devname , card - > wandev . critical , status - > iflags ) ;
card - > in_isr = 0 ;
status - > iflags = 0 ;
return ;
}
/* For all interrupts set the critical flag to CRITICAL_RX_INTR.
* If the if_send routine is called with this flag set it will set
* the enable transmit flag to 1. ( for a delayed interrupt )
*/
switch ( status - > iflags ) {
case RX_INTR_PENDING : /* receive interrupt */
rx_intr ( card ) ;
break ;
case TX_INTR_PENDING : /* transmit interrupt */
tx_intr ( card ) ;
break ;
case MODEM_INTR_PENDING : /* modem status interrupt */
status_intr ( card ) ;
break ;
case X25_ASY_TRANS_INTR_PENDING : /* network event interrupt */
event_intr ( card ) ;
break ;
case TIMER_INTR_PENDING :
timer_intr ( card ) ;
break ;
default : /* unwanted interrupt */
spur_intr ( card ) ;
}
card - > in_isr = 0 ;
status - > iflags = 0 ; /* clear interrupt condition */
}
/*
* Receive interrupt handler .
* This routine handles fragmented IP packets using M - bit according to the
* RFC1356 .
* o map ligical channel number to network interface .
* o allocate socket buffer or append received packet to the existing one .
* o if M - bit is reset ( i . e . it ' s the last packet in a sequence ) then
* decapsulate packet and pass socket buffer to the protocol stack .
*
* Notes :
* 1. When allocating a socket buffer , if M - bit is set then more data is
* coming and we have to allocate buffer for the maximum IP packet size
* expected on this channel .
* 2. If something goes wrong and X .25 packet has to be dropped ( e . g . no
* socket buffers available ) the whole packet sequence must be discarded .
*/
static void rx_intr ( sdla_t * card )
{
TX25Mbox * rxmb = card - > rxmb ;
unsigned lcn = rxmb - > cmd . lcn ;
struct net_device * dev = find_channel ( card , lcn ) ;
x25_channel_t * chan ;
struct sk_buff * skb = NULL ;
if ( dev = = NULL ) {
/* Invalid channel, discard packet */
printk ( KERN_INFO " %s: receiving on orphaned LCN %d! \n " ,
card - > devname , lcn ) ;
return ;
}
chan = dev - > priv ;
chan - > i_timeout_sofar = jiffies ;
/* Copy the data from the board, into an
* skb buffer
*/
if ( wanpipe_pull_data_in_skb ( card , dev , & skb ) ) {
+ + chan - > ifstats . rx_dropped ;
+ + card - > wandev . stats . rx_dropped ;
+ + chan - > rx_intr_stat . rx_intr_no_socket ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
return ;
}
dev - > last_rx = jiffies ; /* timestamp */
/* ------------ API ----------------*/
if ( chan - > common . usedby = = API ) {
if ( bh_enqueue ( dev , skb ) ) {
+ + chan - > ifstats . rx_dropped ;
+ + card - > wandev . stats . rx_dropped ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
dev_kfree_skb_any ( skb ) ;
return ;
}
+ + chan - > ifstats . rx_packets ;
chan - > ifstats . rx_bytes + = skb - > len ;
chan - > rx_skb = NULL ;
if ( ! test_and_set_bit ( 0 , & chan - > tq_working ) ) {
wanpipe_queue_work ( & chan - > common . wanpipe_work ) ;
}
return ;
}
/* ------------- WANPIPE -------------------*/
/* set rx_skb to NULL so we won't access it later when kernel already owns it */
chan - > rx_skb = NULL ;
/* Decapsulate packet, if necessary */
if ( ! skb - > protocol & & ! wanrouter_type_trans ( skb , dev ) ) {
/* can't decapsulate packet */
dev_kfree_skb_any ( skb ) ;
+ + chan - > ifstats . rx_errors ;
+ + chan - > ifstats . rx_dropped ;
+ + card - > wandev . stats . rx_dropped ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
} else {
if ( handle_IPXWAN ( skb - > data , chan - > name ,
chan - > enable_IPX , chan - > network_number ,
skb - > protocol ) ) {
if ( chan - > enable_IPX ) {
if ( chan_send ( dev , skb - > data , skb - > len , 0 ) ) {
chan - > tx_skb = skb ;
} else {
dev_kfree_skb_any ( skb ) ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
}
} else {
/* increment IPX packet dropped statistic */
+ + chan - > ifstats . rx_dropped ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
}
} else {
skb - > mac . raw = skb - > data ;
chan - > ifstats . rx_bytes + = skb - > len ;
+ + chan - > ifstats . rx_packets ;
+ + chan - > rx_intr_stat . rx_intr_bfr_passed_to_stack ;
netif_rx ( skb ) ;
}
}
return ;
}
static int wanpipe_pull_data_in_skb ( sdla_t * card , struct net_device * dev ,
struct sk_buff * * skb )
{
void * bufptr ;
TX25Mbox * rxmb = card - > rxmb ;
unsigned len = rxmb - > cmd . length ; /* packet length */
unsigned qdm = rxmb - > cmd . qdm ; /* Q,D and M bits */
x25_channel_t * chan = dev - > priv ;
struct sk_buff * new_skb = * skb ;
if ( chan - > common . usedby = = WANPIPE ) {
if ( chan - > drop_sequence ) {
if ( ! ( qdm & 0x01 ) ) {
chan - > drop_sequence = 0 ;
}
return 1 ;
}
new_skb = chan - > rx_skb ;
} else {
/* Add on the API header to the received
* data
*/
len + = sizeof ( x25api_hdr_t ) ;
}
if ( new_skb = = NULL ) {
int bufsize ;
if ( chan - > common . usedby = = WANPIPE ) {
bufsize = ( qdm & 0x01 ) ? dev - > mtu : len ;
} else {
bufsize = len ;
}
/* Allocate new socket buffer */
new_skb = dev_alloc_skb ( bufsize + dev - > hard_header_len ) ;
if ( new_skb = = NULL ) {
printk ( KERN_INFO " %s: no socket buffers available! \n " ,
card - > devname ) ;
chan - > drop_sequence = 1 ; /* set flag */
+ + chan - > ifstats . rx_dropped ;
return 1 ;
}
}
if ( skb_tailroom ( new_skb ) < len ) {
/* No room for the packet. Call off the whole thing! */
dev_kfree_skb_any ( new_skb ) ;
if ( chan - > common . usedby = = WANPIPE ) {
chan - > rx_skb = NULL ;
if ( qdm & 0x01 ) {
chan - > drop_sequence = 1 ;
}
}
printk ( KERN_INFO " %s: unexpectedly long packet sequence "
" on interface %s! \n " , card - > devname , dev - > name ) ;
+ + chan - > ifstats . rx_length_errors ;
return 1 ;
}
bufptr = skb_put ( new_skb , len ) ;
if ( chan - > common . usedby = = API ) {
/* Fill in the x25api header
*/
x25api_t * api_data = ( x25api_t * ) bufptr ;
api_data - > hdr . qdm = rxmb - > cmd . qdm ;
api_data - > hdr . cause = rxmb - > cmd . cause ;
api_data - > hdr . diagn = rxmb - > cmd . diagn ;
api_data - > hdr . length = rxmb - > cmd . length ;
memcpy ( api_data - > data , rxmb - > data , rxmb - > cmd . length ) ;
} else {
memcpy ( bufptr , rxmb - > data , len ) ;
}
new_skb - > dev = dev ;
if ( chan - > common . usedby = = API ) {
new_skb - > mac . raw = new_skb - > data ;
new_skb - > protocol = htons ( X25_PROT ) ;
new_skb - > pkt_type = WAN_PACKET_DATA ;
} else {
new_skb - > protocol = chan - > protocol ;
chan - > rx_skb = new_skb ;
}
/* If qdm bit is set, more data is coming
* thus , exit and wait for more data before
* sending the packet up . ( Used by router only )
*/
if ( ( qdm & 0x01 ) & & ( chan - > common . usedby = = WANPIPE ) )
return 1 ;
* skb = new_skb ;
return 0 ;
}
/*===============================================================
* tx_intr
*
* Transmit interrupt handler .
* For each dev , check that there is something to send .
* If data available , transmit .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void tx_intr ( sdla_t * card )
{
struct net_device * dev ;
TX25Status * status = card - > flags ;
unsigned char more_to_tx = 0 ;
x25_channel_t * chan = NULL ;
int i = 0 ;
if ( card - > u . x . tx_dev = = NULL ) {
card - > u . x . tx_dev = card - > wandev . dev ;
}
dev = card - > u . x . tx_dev ;
for ( ; ; ) {
chan = dev - > priv ;
if ( chan - > transmit_length ) {
/* Device was set to transmit, check if the TX
* buffers are available
*/
if ( chan - > common . state ! = WAN_CONNECTED ) {
chan - > transmit_length = 0 ;
atomic_set ( & chan - > common . driver_busy , 0 ) ;
chan - > tx_offset = 0 ;
if ( netif_queue_stopped ( dev ) ) {
if ( chan - > common . usedby = = API ) {
netif_start_queue ( dev ) ;
wakeup_sk_bh ( dev ) ;
} else {
netif_wake_queue ( dev ) ;
}
}
dev = move_dev_to_next ( card , dev ) ;
break ;
}
if ( ( status - > cflags [ chan - > ch_idx ] & 0x40 | | card - > u . x . LAPB_hdlc ) & &
( * card - > u . x . hdlc_buf_status & 0x40 ) ) {
/* Tx buffer available, we can send */
if ( tx_intr_send ( card , dev ) ) {
more_to_tx = 1 ;
}
/* If more than one interface present, move the
* device pointer to the next interface , so on the
* next TX interrupt we will try sending from it .
*/
dev = move_dev_to_next ( card , dev ) ;
break ;
} else {
/* Tx buffers not available, but device set
* the TX interrupt . Set more_to_tx and try
* to transmit for other devices .
*/
more_to_tx = 1 ;
dev = move_dev_to_next ( card , dev ) ;
}
} else {
/* This device was not set to transmit,
* go to next
*/
dev = move_dev_to_next ( card , dev ) ;
}
if ( + + i = = card - > u . x . no_dev ) {
if ( ! more_to_tx ) {
DBG_PRINTK ( KERN_INFO " %s: Nothing to Send in TX INTR \n " ,
card - > devname ) ;
}
break ;
}
} //End of FOR
card - > u . x . tx_dev = dev ;
if ( ! more_to_tx ) {
/* if any other interfaces have transmit interrupts pending, */
/* do not disable the global transmit interrupt */
if ( ! ( - - card - > u . x . tx_interrupts_pending ) ) {
status - > imask & = ~ INTR_ON_TX_FRAME ;
}
}
return ;
}
/*===============================================================
* move_dev_to_next
*
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct net_device * move_dev_to_next ( sdla_t * card , struct net_device * dev )
{
if ( card - > u . x . no_dev ! = 1 ) {
if ( ! * ( ( struct net_device * * ) dev - > priv ) )
return card - > wandev . dev ;
else
return * ( ( struct net_device * * ) dev - > priv ) ;
}
return dev ;
}
/*===============================================================
* tx_intr_send
*
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int tx_intr_send ( sdla_t * card , struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
if ( chan_send ( dev , chan - > transmit_buffer , chan - > transmit_length , 1 ) ) {
/* Packet was split up due to its size, do not disable
* tx_intr
*/
return 1 ;
}
chan - > transmit_length = 0 ;
atomic_set ( & chan - > common . driver_busy , 0 ) ;
chan - > tx_offset = 0 ;
/* If we are in API mode, wakeup the
* sock BH handler , not the NET_BH */
if ( netif_queue_stopped ( dev ) ) {
if ( chan - > common . usedby = = API ) {
netif_start_queue ( dev ) ;
wakeup_sk_bh ( dev ) ;
} else {
netif_wake_queue ( dev ) ;
}
}
return 0 ;
}
/*===============================================================
* timer_intr
*
* Timer interrupt handler .
* Check who called the timer interrupt and perform
* action accordingly .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void timer_intr ( sdla_t * card )
{
TX25Status * status = card - > flags ;
if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC ) {
if ( timer_intr_cmd_exec ( card ) = = 0 ) {
card - > u . x . timer_int_enabled & =
~ TMR_INT_ENABLED_CMD_EXEC ;
}
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_UDP_PKT ) {
if ( ( * card - > u . x . hdlc_buf_status & 0x40 ) & &
card - > u . x . udp_type = = UDP_XPIPE_TYPE ) {
if ( process_udp_mgmt_pkt ( card ) ) {
card - > u . x . timer_int_enabled & =
~ TMR_INT_ENABLED_UDP_PKT ;
}
}
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_POLL_ACTIVE ) {
struct net_device * dev = card - > u . x . poll_device ;
x25_channel_t * chan = NULL ;
if ( ! dev ) {
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_POLL_ACTIVE ;
return ;
}
chan = dev - > priv ;
printk ( KERN_INFO
" %s: Closing down Idle link %s on LCN %d \n " ,
card - > devname , chan - > name , chan - > common . lcn ) ;
chan - > i_timeout_sofar = jiffies ;
chan_disc ( dev ) ;
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_POLL_ACTIVE ;
card - > u . x . poll_device = NULL ;
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_ON ) {
wanpipe_set_state ( card , WAN_CONNECTED ) ;
if ( card - > u . x . LAPB_hdlc ) {
struct net_device * dev = card - > wandev . dev ;
set_chan_state ( dev , WAN_CONNECTED ) ;
send_delayed_cmd_result ( card , dev , card - > mbox ) ;
}
/* 0x8F enable all interrupts */
x25_set_intr_mode ( card , INTR_ON_RX_FRAME |
INTR_ON_TX_FRAME |
INTR_ON_MODEM_STATUS_CHANGE |
//INTR_ON_COMMAND_COMPLETE|
X25_ASY_TRANS_INTR_PENDING |
INTR_ON_TIMER |
DIRECT_RX_INTR_USAGE
) ;
status - > imask & = ~ INTR_ON_TX_FRAME ; /* mask Tx interrupts */
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_POLL_CONNECT_ON ;
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_OFF ) {
//printk(KERN_INFO "Poll connect, Turning OFF\n");
disconnect ( card ) ;
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_POLL_CONNECT_OFF ;
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_POLL_DISCONNECT ) {
//printk(KERN_INFO "POll disconnect, trying to connect\n");
connect ( card ) ;
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_POLL_DISCONNECT ;
} else if ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_UPDATE ) {
if ( * card - > u . x . hdlc_buf_status & 0x40 ) {
x25_get_err_stats ( card ) ;
x25_get_stats ( card ) ;
card - > u . x . timer_int_enabled & = ~ TMR_INT_ENABLED_UPDATE ;
}
}
if ( ! card - > u . x . timer_int_enabled ) {
//printk(KERN_INFO "Turning Timer Off \n");
status - > imask & = ~ INTR_ON_TIMER ;
}
}
/*====================================================================
* Modem status interrupt handler .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void status_intr ( sdla_t * card )
{
/* Added to avoid Modem status message flooding */
static TX25ModemStatus last_stat ;
TX25Mbox * mbox = card - > mbox ;
TX25ModemStatus * modem_status ;
struct net_device * dev ;
x25_channel_t * chan ;
int err ;
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_READ_MODEM_STATUS ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err ) {
x25_error ( card , err , X25_READ_MODEM_STATUS , 0 ) ;
} else {
modem_status = ( TX25ModemStatus * ) mbox - > data ;
/* Check if the last status was the same
* if it was , do NOT print message again */
if ( last_stat . status ! = modem_status - > status ) {
printk ( KERN_INFO " %s: Modem Status Change: DCD=%s, CTS=%s \n " ,
card - > devname , DCD ( modem_status - > status ) , CTS ( modem_status - > status ) ) ;
last_stat . status = modem_status - > status ;
if ( card - > u . x . oob_on_modem ) {
mbox - > cmd . pktType = mbox - > cmd . command ;
mbox - > cmd . result = 0x08 ;
/* Send a OOB to all connected sockets */
for ( dev = card - > wandev . dev ; dev ;
dev = * ( ( struct net_device * * ) dev - > priv ) ) {
chan = dev - > priv ;
if ( chan - > common . usedby = = API ) {
send_oob_msg ( card , dev , mbox ) ;
}
}
/* The modem OOB message will probably kill the
* the link . If we don ' t clear the flag here ,
* a deadlock could occur */
if ( atomic_read ( & card - > u . x . command_busy ) ) {
atomic_set ( & card - > u . x . command_busy , 0 ) ;
}
}
}
}
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_HDLC_LINK_STATUS ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err ) {
x25_error ( card , err , X25_HDLC_LINK_STATUS , 0 ) ;
}
}
/*====================================================================
* Network event interrupt handler .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void event_intr ( sdla_t * card )
{
x25_fetch_events ( card ) ;
}
/*====================================================================
* Spurious interrupt handler .
* o print a warning
* o
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void spur_intr ( sdla_t * card )
{
printk ( KERN_INFO " %s: spurious interrupt! \n " , card - > devname ) ;
}
/*
* Background Polling Routines
*/
/*====================================================================
* Main polling routine .
* This routine is repeatedly called by the WANPIPE ' thread ' to allow for
* time - dependent housekeeping work .
*
* Notes :
* 1. This routine may be called on interrupt context with all interrupts
* enabled . Beware !
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void wpx_poll ( sdla_t * card )
{
if ( ! card - > wandev . dev ) {
goto wpx_poll_exit ;
}
if ( card - > open_cnt ! = card - > u . x . num_of_ch ) {
goto wpx_poll_exit ;
}
if ( test_bit ( PERI_CRIT , & card - > wandev . critical ) ) {
goto wpx_poll_exit ;
}
if ( test_bit ( SEND_CRIT , & card - > wandev . critical ) ) {
goto wpx_poll_exit ;
}
switch ( card - > wandev . state ) {
case WAN_CONNECTED :
poll_active ( card ) ;
break ;
case WAN_CONNECTING :
poll_connecting ( card ) ;
break ;
case WAN_DISCONNECTED :
poll_disconnected ( card ) ;
break ;
}
wpx_poll_exit :
clear_bit ( POLL_CRIT , & card - > wandev . critical ) ;
return ;
}
static void trigger_x25_poll ( sdla_t * card )
{
schedule_work ( & card - > u . x . x25_poll_work ) ;
}
/*====================================================================
* Handle physical link establishment phase .
* o if connection timed out , disconnect the link .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void poll_connecting ( sdla_t * card )
{
volatile TX25Status * status = card - > flags ;
if ( status - > gflags & X25_HDLC_ABM ) {
timer_intr_exec ( card , TMR_INT_ENABLED_POLL_CONNECT_ON ) ;
} else if ( ( jiffies - card - > state_tick ) > CONNECT_TIMEOUT ) {
timer_intr_exec ( card , TMR_INT_ENABLED_POLL_CONNECT_OFF ) ;
}
}
/*====================================================================
* Handle physical link disconnected phase .
* o if hold - down timeout has expired and there are open interfaces ,
* connect link .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void poll_disconnected ( sdla_t * card )
{
struct net_device * dev ;
x25_channel_t * chan ;
TX25Status * status = card - > flags ;
if ( ! card - > u . x . LAPB_hdlc & & card - > open_cnt & &
( ( jiffies - card - > state_tick ) > HOLD_DOWN_TIME ) ) {
timer_intr_exec ( card , TMR_INT_ENABLED_POLL_DISCONNECT ) ;
}
if ( ( dev = card - > wandev . dev ) = = NULL )
return ;
if ( ( chan = dev - > priv ) = = NULL )
return ;
if ( chan - > common . usedby = = API & &
atomic_read ( & chan - > common . command ) & &
card - > u . x . LAPB_hdlc ) {
if ( ! ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC ) )
card - > u . x . timer_int_enabled | = TMR_INT_ENABLED_CMD_EXEC ;
if ( ! ( status - > imask & INTR_ON_TIMER ) )
status - > imask | = INTR_ON_TIMER ;
}
}
/*====================================================================
* Handle active link phase .
* o fetch X .25 asynchronous events .
* o kick off transmission on all interfaces .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void poll_active ( sdla_t * card )
{
struct net_device * dev ;
TX25Status * status = card - > flags ;
for ( dev = card - > wandev . dev ; dev ;
dev = * ( ( struct net_device * * ) dev - > priv ) ) {
x25_channel_t * chan = dev - > priv ;
/* If SVC has been idle long enough, close virtual circuit */
if ( chan - > common . svc & &
chan - > common . state = = WAN_CONNECTED & &
chan - > common . usedby = = WANPIPE ) {
if ( ( jiffies - chan - > i_timeout_sofar ) / HZ > chan - > idle_timeout ) {
/* Close svc */
card - > u . x . poll_device = dev ;
timer_intr_exec ( card , TMR_INT_ENABLED_POLL_ACTIVE ) ;
}
}
# ifdef PRINT_DEBUG
chan - > ifstats . tx_compressed = atomic_read ( & chan - > common . command ) ;
chan - > ifstats . tx_errors = chan - > common . state ;
chan - > ifstats . rx_fifo_errors = atomic_read ( & card - > u . x . command_busy ) ;
+ + chan - > ifstats . tx_bytes ;
chan - > ifstats . rx_fifo_errors = atomic_read ( & chan - > common . disconnect ) ;
chan - > ifstats . multicast = atomic_read ( & chan - > bh_buff_used ) ;
chan - > ifstats . rx_length_errors = * card - > u . x . hdlc_buf_status ;
# endif
if ( chan - > common . usedby = = API & &
atomic_read ( & chan - > common . command ) & &
! card - > u . x . LAPB_hdlc ) {
if ( ! ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC ) )
card - > u . x . timer_int_enabled | = TMR_INT_ENABLED_CMD_EXEC ;
if ( ! ( status - > imask & INTR_ON_TIMER ) )
status - > imask | = INTR_ON_TIMER ;
}
if ( ( chan - > common . usedby = = API ) & &
atomic_read ( & chan - > common . disconnect ) ) {
if ( chan - > common . state = = WAN_DISCONNECTED ) {
atomic_set ( & chan - > common . disconnect , 0 ) ;
return ;
}
atomic_set ( & chan - > common . command , X25_CLEAR_CALL ) ;
if ( ! ( card - > u . x . timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC ) )
card - > u . x . timer_int_enabled | = TMR_INT_ENABLED_CMD_EXEC ;
if ( ! ( status - > imask & INTR_ON_TIMER ) )
status - > imask | = INTR_ON_TIMER ;
}
}
}
static void timer_intr_exec ( sdla_t * card , unsigned char TYPE )
{
TX25Status * status = card - > flags ;
card - > u . x . timer_int_enabled | = TYPE ;
if ( ! ( status - > imask & INTR_ON_TIMER ) )
status - > imask | = INTR_ON_TIMER ;
}
/*====================================================================
* SDLA Firmware - Specific Functions
*
* Almost all X .25 commands can unexpetedly fail due to so called ' X .25
* asynchronous events ' such as restart , interrupt , incoming call request ,
* call clear request , etc . They can ' t be ignored and have to be delt with
* immediately . To tackle with this problem we execute each interface
* command in a loop until good return code is received or maximum number
* of retries is reached . Each interface command returns non - zero return
* code , an asynchronous event / error handler x25_error ( ) is called .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*====================================================================
* Read X .25 firmware version .
* Put code version as ASCII string in str .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_get_version ( sdla_t * card , char * str )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_READ_CODE_VERSION ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & &
x25_error ( card , err , X25_READ_CODE_VERSION , 0 ) ) ;
if ( ! err & & str )
{
int len = mbox - > cmd . length ;
memcpy ( str , mbox - > data , len ) ;
str [ len ] = ' \0 ' ;
}
return err ;
}
/*====================================================================
* Configure adapter .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_configure ( sdla_t * card , TX25Config * conf )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do {
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
memcpy ( mbox - > data , ( void * ) conf , sizeof ( TX25Config ) ) ;
mbox - > cmd . length = sizeof ( TX25Config ) ;
mbox - > cmd . command = X25_SET_CONFIGURATION ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_SET_CONFIGURATION , 0 ) ) ;
return err ;
}
/*====================================================================
* Configure adapter for HDLC only .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int hdlc_configure ( sdla_t * card , TX25Config * conf )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do {
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
memcpy ( mbox - > data , ( void * ) conf , sizeof ( TX25Config ) ) ;
mbox - > cmd . length = sizeof ( TX25Config ) ;
mbox - > cmd . command = X25_HDLC_SET_CONFIG ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_SET_CONFIGURATION , 0 ) ) ;
return err ;
}
static int set_hdlc_level ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do {
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = SET_PROTOCOL_LEVEL ;
mbox - > cmd . length = 1 ;
mbox - > data [ 0 ] = HDLC_LEVEL ; //| DO_HDLC_LEVEL_ERROR_CHECKING;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , SET_PROTOCOL_LEVEL , 0 ) ) ;
return err ;
}
/*====================================================================
* Get communications error statistics .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_get_err_stats ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_HDLC_READ_COMM_ERR ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_HDLC_READ_COMM_ERR , 0 ) ) ;
if ( ! err )
{
THdlcCommErr * stats = ( void * ) mbox - > data ;
card - > wandev . stats . rx_over_errors = stats - > rxOverrun ;
card - > wandev . stats . rx_crc_errors = stats - > rxBadCrc ;
card - > wandev . stats . rx_missed_errors = stats - > rxAborted ;
card - > wandev . stats . tx_aborted_errors = stats - > txAborted ;
}
return err ;
}
/*====================================================================
* Get protocol statistics .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_get_stats ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_READ_STATISTICS ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_READ_STATISTICS , 0 ) ) ;
if ( ! err )
{
TX25Stats * stats = ( void * ) mbox - > data ;
card - > wandev . stats . rx_packets = stats - > rxData ;
card - > wandev . stats . tx_packets = stats - > txData ;
}
return err ;
}
/*====================================================================
* Close HDLC link .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_close_hdlc ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_HDLC_LINK_CLOSE ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_HDLC_LINK_CLOSE , 0 ) ) ;
return err ;
}
/*====================================================================
* Open HDLC link .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_open_hdlc ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_HDLC_LINK_OPEN ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_HDLC_LINK_OPEN , 0 ) ) ;
return err ;
}
/*=====================================================================
* Setup HDLC link .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_setup_hdlc ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_HDLC_LINK_SETUP ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_HDLC_LINK_SETUP , 0 ) ) ;
return err ;
}
/*====================================================================
* Set ( raise / drop ) DTR .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_set_dtr ( sdla_t * card , int dtr )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > data [ 0 ] = 0 ;
mbox - > data [ 2 ] = 0 ;
mbox - > data [ 1 ] = dtr ? 0x02 : 0x01 ;
mbox - > cmd . length = 3 ;
mbox - > cmd . command = X25_SET_GLOBAL_VARS ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_SET_GLOBAL_VARS , 0 ) ) ;
return err ;
}
/*====================================================================
* Set interrupt mode .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_set_intr_mode ( sdla_t * card , int mode )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > data [ 0 ] = mode ;
if ( card - > hw . fwid = = SFID_X25_508 ) {
mbox - > data [ 1 ] = card - > hw . irq ;
mbox - > data [ 2 ] = 2 ;
mbox - > cmd . length = 3 ;
} else {
mbox - > cmd . length = 1 ;
}
mbox - > cmd . command = X25_SET_INTERRUPT_MODE ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_SET_INTERRUPT_MODE , 0 ) ) ;
return err ;
}
/*====================================================================
* Read X .25 channel configuration .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_get_chan_conf ( sdla_t * card , x25_channel_t * chan )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int lcn = chan - > common . lcn ;
int err ;
do {
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . lcn = lcn ;
mbox - > cmd . command = X25_READ_CHANNEL_CONFIG ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_READ_CHANNEL_CONFIG , lcn ) ) ;
if ( ! err )
{
TX25Status * status = card - > flags ;
/* calculate an offset into the array of status bytes */
if ( card - > u . x . hi_svc < = X25_MAX_CHAN ) {
chan - > ch_idx = lcn - 1 ;
} else {
int offset ;
/* FIX: Apr 14 2000 : Nenad Corbic
* The data field was being compared to 0x1F using
* ' & & ' instead of ' & ' .
* This caused X25API to fail for LCNs greater than 255.
*/
switch ( mbox - > data [ 0 ] & 0x1F )
{
case 0x01 :
offset = status - > pvc_map ; break ;
case 0x03 :
offset = status - > icc_map ; break ;
case 0x07 :
offset = status - > twc_map ; break ;
case 0x0B :
offset = status - > ogc_map ; break ;
default :
offset = 0 ;
}
chan - > ch_idx = lcn - 1 - offset ;
}
/* get actual transmit packet size on this channel */
switch ( mbox - > data [ 1 ] & 0x38 )
{
case 0x00 :
chan - > tx_pkt_size = 16 ;
break ;
case 0x08 :
chan - > tx_pkt_size = 32 ;
break ;
case 0x10 :
chan - > tx_pkt_size = 64 ;
break ;
case 0x18 :
chan - > tx_pkt_size = 128 ;
break ;
case 0x20 :
chan - > tx_pkt_size = 256 ;
break ;
case 0x28 :
chan - > tx_pkt_size = 512 ;
break ;
case 0x30 :
chan - > tx_pkt_size = 1024 ;
break ;
}
if ( card - > u . x . logging )
printk ( KERN_INFO " %s: X.25 packet size on LCN %d is %d. \n " ,
card - > devname , lcn , chan - > tx_pkt_size ) ;
}
return err ;
}
/*====================================================================
* Place X .25 call .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_place_call ( sdla_t * card , x25_channel_t * chan )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
char str [ 64 ] ;
if ( chan - > protocol = = htons ( ETH_P_IP ) ) {
sprintf ( str , " -d%s -uCC " , chan - > addr ) ;
} else if ( chan - > protocol = = htons ( ETH_P_IPX ) ) {
sprintf ( str , " -d%s -u800000008137 " , chan - > addr ) ;
}
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
strcpy ( mbox - > data , str ) ;
mbox - > cmd . length = strlen ( str ) ;
mbox - > cmd . command = X25_PLACE_CALL ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_PLACE_CALL , 0 ) ) ;
if ( ! err ) {
bind_lcn_to_dev ( card , chan - > dev , mbox - > cmd . lcn ) ;
}
return err ;
}
/*====================================================================
* Accept X .25 call .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_accept_call ( sdla_t * card , int lcn , int qdm )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . lcn = lcn ;
mbox - > cmd . qdm = qdm ;
mbox - > cmd . command = X25_ACCEPT_CALL ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_ACCEPT_CALL , lcn ) ) ;
return err ;
}
/*====================================================================
* Clear X .25 call .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_clear_call ( sdla_t * card , int lcn , int cause , int diagn )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . lcn = lcn ;
mbox - > cmd . cause = cause ;
mbox - > cmd . diagn = diagn ;
mbox - > cmd . command = X25_CLEAR_CALL ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_CLEAR_CALL , lcn ) ) ;
return err ;
}
/*====================================================================
* Send X .25 data packet .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_send ( sdla_t * card , int lcn , int qdm , int len , void * buf )
{
TX25Mbox * mbox = card - > mbox ;
int retry = MAX_CMD_RETRY ;
int err ;
unsigned char cmd ;
if ( card - > u . x . LAPB_hdlc )
cmd = X25_HDLC_WRITE ;
else
cmd = X25_WRITE ;
do
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
memcpy ( mbox - > data , buf , len ) ;
mbox - > cmd . length = len ;
mbox - > cmd . lcn = lcn ;
if ( card - > u . x . LAPB_hdlc ) {
mbox - > cmd . pf = qdm ;
} else {
mbox - > cmd . qdm = qdm ;
}
mbox - > cmd . command = cmd ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , cmd , lcn ) ) ;
/* If buffers are busy the return code for LAPB HDLC is
* 1. The above functions are looking for return code
* of X25RES_NOT_READY if busy . */
if ( card - > u . x . LAPB_hdlc & & err = = 1 ) {
err = X25RES_NOT_READY ;
}
return err ;
}
/*====================================================================
* Fetch X .25 asynchronous events .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_fetch_events ( sdla_t * card )
{
TX25Status * status = card - > flags ;
TX25Mbox * mbox = card - > mbox ;
int err = 0 ;
if ( status - > gflags & 0x20 )
{
memset ( & mbox - > cmd , 0 , sizeof ( TX25Cmd ) ) ;
mbox - > cmd . command = X25_IS_DATA_AVAILABLE ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err ) x25_error ( card , err , X25_IS_DATA_AVAILABLE , 0 ) ;
}
return err ;
}
/*====================================================================
* X .25 asynchronous event / error handler .
* This routine is called each time interface command returns
* non - zero return code to handle X .25 asynchronous events and
* common errors . Return non - zero to repeat command or zero to
* cancel it .
*
* Notes :
* 1. This function may be called recursively , as handling some of the
* asynchronous events ( e . g . call request ) requires execution of the
* interface command ( s ) that , in turn , may also return asynchronous
* events . To avoid re - entrancy problems we copy mailbox to dynamically
* allocated memory before processing events .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25_error ( sdla_t * card , int err , int cmd , int lcn )
{
int retry = 1 ;
unsigned dlen = ( ( TX25Mbox * ) card - > mbox ) - > cmd . length ;
TX25Mbox * mb ;
mb = kmalloc ( sizeof ( TX25Mbox ) + dlen , GFP_ATOMIC ) ;
if ( mb = = NULL )
{
printk ( KERN_ERR " %s: x25_error() out of memory! \n " ,
card - > devname ) ;
return 0 ;
}
memcpy ( mb , card - > mbox , sizeof ( TX25Mbox ) + dlen ) ;
switch ( err ) {
case X25RES_ASYNC_PACKET : /* X.25 asynchronous packet was received */
mb - > data [ dlen ] = ' \0 ' ;
switch ( mb - > cmd . pktType & 0x7F ) {
case ASE_CALL_RQST : /* incoming call */
retry = incoming_call ( card , cmd , lcn , mb ) ;
break ;
case ASE_CALL_ACCEPTED : /* connected */
retry = call_accepted ( card , cmd , lcn , mb ) ;
break ;
case ASE_CLEAR_RQST : /* call clear request */
retry = call_cleared ( card , cmd , lcn , mb ) ;
break ;
case ASE_RESET_RQST : /* reset request */
printk ( KERN_INFO " %s: X.25 reset request on LCN %d! "
" Cause:0x%02X Diagn:0x%02X \n " ,
card - > devname , mb - > cmd . lcn , mb - > cmd . cause ,
mb - > cmd . diagn ) ;
api_oob_event ( card , mb ) ;
break ;
case ASE_RESTART_RQST : /* restart request */
retry = restart_event ( card , cmd , lcn , mb ) ;
break ;
case ASE_CLEAR_CONFRM :
if ( clear_confirm_event ( card , mb ) )
break ;
/* I use the goto statement here so if
* somebody inserts code between the
* case and default , we will not have
* ghost problems */
goto dflt_1 ;
default :
dflt_1 :
printk ( KERN_INFO " %s: X.25 event 0x%02X on LCN %d! "
" Cause:0x%02X Diagn:0x%02X \n " ,
card - > devname , mb - > cmd . pktType ,
mb - > cmd . lcn , mb - > cmd . cause , mb - > cmd . diagn ) ;
}
break ;
case X25RES_PROTO_VIOLATION : /* X.25 protocol violation indication */
/* Bug Fix: Mar 14 2000
* The Protocol violation error conditions were
* not handled previously */
switch ( mb - > cmd . pktType & 0x7F ) {
case PVE_CLEAR_RQST : /* Clear request */
retry = call_cleared ( card , cmd , lcn , mb ) ;
break ;
case PVE_RESET_RQST : /* Reset request */
printk ( KERN_INFO " %s: X.25 reset request on LCN %d! "
" Cause:0x%02X Diagn:0x%02X \n " ,
card - > devname , mb - > cmd . lcn , mb - > cmd . cause ,
mb - > cmd . diagn ) ;
api_oob_event ( card , mb ) ;
break ;
case PVE_RESTART_RQST : /* Restart request */
retry = restart_event ( card , cmd , lcn , mb ) ;
break ;
default :
printk ( KERN_INFO
" %s: X.25 protocol violation on LCN %d! "
" Packet:0x%02X Cause:0x%02X Diagn:0x%02X \n " ,
card - > devname , mb - > cmd . lcn ,
mb - > cmd . pktType & 0x7F , mb - > cmd . cause , mb - > cmd . diagn ) ;
api_oob_event ( card , mb ) ;
}
break ;
case 0x42 : /* X.25 timeout */
retry = timeout_event ( card , cmd , lcn , mb ) ;
break ;
case 0x43 : /* X.25 retry limit exceeded */
printk ( KERN_INFO
" %s: exceeded X.25 retry limit on LCN %d! "
" Packet:0x%02X Diagn:0x%02X \n " , card - > devname ,
mb - > cmd . lcn , mb - > cmd . pktType , mb - > cmd . diagn )
;
break ;
case 0x08 : /* modem failure */
# ifndef MODEM_NOT_LOG
printk ( KERN_INFO " %s: modem failure! \n " , card - > devname ) ;
# endif /* MODEM_NOT_LOG */
api_oob_event ( card , mb ) ;
break ;
case 0x09 : /* N2 retry limit */
printk ( KERN_INFO " %s: exceeded HDLC retry limit! \n " ,
card - > devname ) ;
api_oob_event ( card , mb ) ;
break ;
case 0x06 : /* unnumbered frame was received while in ABM */
printk ( KERN_INFO " %s: received Unnumbered frame 0x%02X! \n " ,
card - > devname , mb - > data [ 0 ] ) ;
api_oob_event ( card , mb ) ;
break ;
case CMD_TIMEOUT :
printk ( KERN_ERR " %s: command 0x%02X timed out! \n " ,
card - > devname , cmd )
;
retry = 0 ; /* abort command */
break ;
case X25RES_NOT_READY :
retry = 1 ;
break ;
case 0x01 :
if ( card - > u . x . LAPB_hdlc )
break ;
if ( mb - > cmd . command = = 0x16 )
break ;
/* I use the goto statement here so if
* somebody inserts code between the
* case and default , we will not have
* ghost problems */
goto dflt_2 ;
default :
dflt_2 :
printk ( KERN_INFO " %s: command 0x%02X returned 0x%02X! Lcn %i \n " ,
card - > devname , cmd , err , mb - > cmd . lcn )
;
retry = 0 ; /* abort command */
}
kfree ( mb ) ;
return retry ;
}
/*====================================================================
* X .25 Asynchronous Event Handlers
* These functions are called by the x25_error ( ) and should return 0 , if
* the command resulting in the asynchronous event must be aborted .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/*====================================================================
* Handle X .25 incoming call request .
* RFC 1356 establishes the following rules :
* 1. The first octet in the Call User Data ( CUD ) field of the call
* request packet contains NLPID identifying protocol encapsulation
* 2. Calls MUST NOT be accepted unless router supports requested
* protocol encapsulation .
* 3. A diagnostic code 249 defined by ISO / IEC 8208 may be used
* when clearing a call because protocol encapsulation is not
* supported .
* 4. If an incoming call is received while a call request is
* pending ( i . e . call collision has occurred ) , the incoming call
* shall be rejected and call request shall be retried .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int incoming_call ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb )
{
struct wan_device * wandev = & card - > wandev ;
int new_lcn = mb - > cmd . lcn ;
struct net_device * dev = get_dev_by_lcn ( wandev , new_lcn ) ;
x25_channel_t * chan = NULL ;
int accept = 0 ; /* set to '1' if o.k. to accept call */
unsigned int user_data ;
x25_call_info_t * info ;
/* Make sure there is no call collision */
if ( dev ! = NULL )
{
printk ( KERN_INFO
" %s: X.25 incoming call collision on LCN %d! \n " ,
card - > devname , new_lcn ) ;
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
return 1 ;
}
/* Make sure D bit is not set in call request */
//FIXME: THIS IS NOT TURE !!!! TAKE IT OUT
// if (mb->cmd.qdm & 0x02)
// {
// printk(KERN_INFO
// "%s: X.25 incoming call on LCN %d with D-bit set!\n",
// card->devname, new_lcn);
//
// x25_clear_call(card, new_lcn, 0, 0);
// return 1;
// }
/* Parse call request data */
info = kmalloc ( sizeof ( x25_call_info_t ) , GFP_ATOMIC ) ;
if ( info = = NULL )
{
printk ( KERN_ERR
" %s: not enough memory to parse X.25 incoming call "
" on LCN %d! \n " , card - > devname , new_lcn ) ;
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
return 1 ;
}
parse_call_info ( mb - > data , info ) ;
if ( card - > u . x . logging )
printk ( KERN_INFO " \n %s: X.25 incoming call on LCN %d! \n " ,
card - > devname , new_lcn ) ;
/* Conver the first two ASCII characters into an
* interger . Used to check the incoming protocol
*/
user_data = hex_to_uint ( info - > user , 2 ) ;
/* Find available channel */
for ( dev = wandev - > dev ; dev ; dev = * ( ( struct net_device * * ) dev - > priv ) ) {
chan = dev - > priv ;
if ( chan - > common . usedby = = API )
continue ;
if ( ! chan - > common . svc | | ( chan - > common . state ! = WAN_DISCONNECTED ) )
continue ;
if ( user_data = = NLPID_IP & & chan - > protocol ! = htons ( ETH_P_IP ) ) {
printk ( KERN_INFO " IP packet but configured for IPX : %x, %x \n " ,
htons ( chan - > protocol ) , info - > user [ 0 ] ) ;
continue ;
}
if ( user_data = = NLPID_SNAP & & chan - > protocol ! = htons ( ETH_P_IPX ) ) {
printk ( KERN_INFO " IPX packet but configured for IP: %x \n " ,
htons ( chan - > protocol ) ) ;
continue ;
}
if ( strcmp ( info - > src , chan - > addr ) = = 0 )
break ;
/* If just an '@' is specified, accept all incoming calls */
if ( strcmp ( chan - > addr , " " ) = = 0 )
break ;
}
if ( dev = = NULL ) {
/* If the call is not for any WANPIPE interfaces
* check to see if there is an API listening queue
* waiting for data . If there is send the packet
* up the stack .
*/
if ( card - > sk ! = NULL & & card - > func ! = NULL ) {
if ( api_incoming_call ( card , mb , new_lcn ) ) {
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
}
accept = 0 ;
} else {
printk ( KERN_INFO " %s: no channels available! \n " ,
card - > devname ) ;
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
}
} else if ( info - > nuser = = 0 ) {
printk ( KERN_INFO
" %s: no user data in incoming call on LCN %d! \n " ,
card - > devname , new_lcn )
;
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
} else switch ( info - > user [ 0 ] ) {
case 0 : /* multiplexed */
chan - > protocol = htons ( 0 ) ;
accept = 1 ;
break ;
case NLPID_IP : /* IP datagrams */
accept = 1 ;
break ;
case NLPID_SNAP : /* IPX datagrams */
accept = 1 ;
break ;
default :
printk ( KERN_INFO
" %s: unsupported NLPID 0x%02X in incoming call "
" on LCN %d! \n " , card - > devname , info - > user [ 0 ] , new_lcn ) ;
x25_clear_call ( card , new_lcn , 0 , 249 ) ;
}
if ( accept & & ( x25_accept_call ( card , new_lcn , 0 ) = = CMD_OK ) ) {
bind_lcn_to_dev ( card , chan - > dev , new_lcn ) ;
if ( x25_get_chan_conf ( card , chan ) = = CMD_OK )
set_chan_state ( dev , WAN_CONNECTED ) ;
else
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
}
kfree ( info ) ;
return 1 ;
}
/*====================================================================
* Handle accepted call .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int call_accepted ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb )
{
unsigned new_lcn = mb - > cmd . lcn ;
struct net_device * dev = find_channel ( card , new_lcn ) ;
x25_channel_t * chan ;
if ( dev = = NULL ) {
printk ( KERN_INFO
" %s: clearing orphaned connection on LCN %d! \n " ,
card - > devname , new_lcn ) ;
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
return 1 ;
}
if ( card - > u . x . logging )
printk ( KERN_INFO " %s: X.25 call accepted on Dev %s and LCN %d! \n " ,
card - > devname , dev - > name , new_lcn ) ;
/* Get channel configuration and notify router */
chan = dev - > priv ;
if ( x25_get_chan_conf ( card , chan ) ! = CMD_OK )
{
x25_clear_call ( card , new_lcn , 0 , 0 ) ;
return 1 ;
}
set_chan_state ( dev , WAN_CONNECTED ) ;
if ( chan - > common . usedby = = API ) {
send_delayed_cmd_result ( card , dev , mb ) ;
bind_lcn_to_dev ( card , dev , new_lcn ) ;
}
return 1 ;
}
/*====================================================================
* Handle cleared call .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int call_cleared ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb )
{
unsigned new_lcn = mb - > cmd . lcn ;
struct net_device * dev = find_channel ( card , new_lcn ) ;
x25_channel_t * chan ;
unsigned char old_state ;
if ( card - > u . x . logging ) {
printk ( KERN_INFO " %s: X.25 clear request on LCN %d! Cause:0x%02X "
" Diagn:0x%02X \n " ,
card - > devname , new_lcn , mb - > cmd . cause , mb - > cmd . diagn ) ;
}
if ( dev = = NULL ) {
printk ( KERN_INFO " %s: X.25 clear request : No device for clear \n " ,
card - > devname ) ;
return 1 ;
}
chan = dev - > priv ;
old_state = chan - > common . state ;
set_chan_state ( dev , WAN_DISCONNECTED ) ;
if ( chan - > common . usedby = = API ) {
switch ( old_state ) {
case WAN_CONNECTING :
send_delayed_cmd_result ( card , dev , mb ) ;
break ;
case WAN_CONNECTED :
send_oob_msg ( card , dev , mb ) ;
break ;
}
}
return ( ( cmd = = X25_WRITE ) & & ( lcn = = new_lcn ) ) ? 0 : 1 ;
}
/*====================================================================
* Handle X .25 restart event .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int restart_event ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb )
{
struct wan_device * wandev = & card - > wandev ;
struct net_device * dev ;
x25_channel_t * chan ;
unsigned char old_state ;
printk ( KERN_INFO
" %s: X.25 restart request! Cause:0x%02X Diagn:0x%02X \n " ,
card - > devname , mb - > cmd . cause , mb - > cmd . diagn ) ;
/* down all logical channels */
for ( dev = wandev - > dev ; dev ; dev = * ( ( struct net_device * * ) dev - > priv ) ) {
chan = dev - > priv ;
old_state = chan - > common . state ;
set_chan_state ( dev , WAN_DISCONNECTED ) ;
if ( chan - > common . usedby = = API ) {
switch ( old_state ) {
case WAN_CONNECTING :
send_delayed_cmd_result ( card , dev , mb ) ;
break ;
case WAN_CONNECTED :
send_oob_msg ( card , dev , mb ) ;
break ;
}
}
}
return ( cmd = = X25_WRITE ) ? 0 : 1 ;
}
/*====================================================================
* Handle timeout event .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int timeout_event ( sdla_t * card , int cmd , int lcn , TX25Mbox * mb )
{
unsigned new_lcn = mb - > cmd . lcn ;
if ( mb - > cmd . pktType = = 0x05 ) /* call request time out */
{
struct net_device * dev = find_channel ( card , new_lcn ) ;
printk ( KERN_INFO " %s: X.25 call timed timeout on LCN %d! \n " ,
card - > devname , new_lcn ) ;
if ( dev ) {
x25_channel_t * chan = dev - > priv ;
set_chan_state ( dev , WAN_DISCONNECTED ) ;
if ( chan - > common . usedby = = API ) {
send_delayed_cmd_result ( card , dev , card - > mbox ) ;
}
}
} else {
printk ( KERN_INFO " %s: X.25 packet 0x%02X timeout on LCN %d! \n " ,
card - > devname , mb - > cmd . pktType , new_lcn ) ;
}
return 1 ;
}
/*
* Miscellaneous
*/
/*====================================================================
* Establish physical connection .
* o open HDLC and raise DTR
*
* Return : 0 connection established
* 1 connection is in progress
* < 0 error
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int connect ( sdla_t * card )
{
TX25Status * status = card - > flags ;
if ( x25_open_hdlc ( card ) | | x25_setup_hdlc ( card ) )
return - EIO ;
wanpipe_set_state ( card , WAN_CONNECTING ) ;
x25_set_intr_mode ( card , INTR_ON_TIMER ) ;
status - > imask & = ~ INTR_ON_TIMER ;
return 1 ;
}
/*
* Tear down physical connection .
* o close HDLC link
* o drop DTR
*
* Return : 0
* < 0 error
*/
static int disconnect ( sdla_t * card )
{
wanpipe_set_state ( card , WAN_DISCONNECTED ) ;
x25_set_intr_mode ( card , INTR_ON_TIMER ) ; /* disable all interrupt except timer */
x25_close_hdlc ( card ) ; /* close HDLC link */
x25_set_dtr ( card , 0 ) ; /* drop DTR */
return 0 ;
}
/*
* Find network device by its channel number .
*/
static struct net_device * get_dev_by_lcn ( struct wan_device * wandev ,
unsigned lcn )
{
struct net_device * dev ;
for ( dev = wandev - > dev ; dev ; dev = * ( ( struct net_device * * ) dev - > priv ) )
if ( ( ( x25_channel_t * ) dev - > priv ) - > common . lcn = = lcn )
break ;
return dev ;
}
/*
* Initiate connection on the logical channel .
* o for PVC we just get channel configuration
* o for SVCs place an X .25 call
*
* Return : 0 connected
* > 0 connection in progress
* < 0 failure
*/
static int chan_connect ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
if ( chan - > common . svc & & chan - > common . usedby = = WANPIPE ) {
if ( ! chan - > addr [ 0 ] ) {
printk ( KERN_INFO " %s: No Destination Address \n " ,
card - > devname ) ;
return - EINVAL ; /* no destination address */
}
printk ( KERN_INFO " %s: placing X.25 call to %s ... \n " ,
card - > devname , chan - > addr ) ;
if ( x25_place_call ( card , chan ) ! = CMD_OK )
return - EIO ;
set_chan_state ( dev , WAN_CONNECTING ) ;
return 1 ;
} else {
if ( x25_get_chan_conf ( card , chan ) ! = CMD_OK )
return - EIO ;
set_chan_state ( dev , WAN_CONNECTED ) ;
}
return 0 ;
}
/*
* Disconnect logical channel .
* o if SVC then clear X .25 call
*/
static int chan_disc ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
if ( chan - > common . svc ) {
x25_clear_call ( chan - > card , chan - > common . lcn , 0 , 0 ) ;
/* For API we disconnect on clear
* confirmation .
*/
if ( chan - > common . usedby = = API )
return 0 ;
}
set_chan_state ( dev , WAN_DISCONNECTED ) ;
return 0 ;
}
/*
* Set logical channel state .
*/
static void set_chan_state ( struct net_device * dev , int state )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
unsigned long flags ;
save_flags ( flags ) ;
cli ( ) ;
if ( chan - > common . state ! = state )
{
switch ( state )
{
case WAN_CONNECTED :
if ( card - > u . x . logging ) {
printk ( KERN_INFO
" %s: interface %s connected, lcn %i ! \n " ,
card - > devname , dev - > name , chan - > common . lcn ) ;
}
* ( unsigned short * ) dev - > dev_addr = htons ( chan - > common . lcn ) ;
chan - > i_timeout_sofar = jiffies ;
/* LAPB is PVC Based */
if ( card - > u . x . LAPB_hdlc )
chan - > common . svc = 0 ;
break ;
case WAN_CONNECTING :
if ( card - > u . x . logging ) {
printk ( KERN_INFO
" %s: interface %s connecting, lcn %i ... \n " ,
card - > devname , dev - > name , chan - > common . lcn ) ;
}
break ;
case WAN_DISCONNECTED :
if ( card - > u . x . logging ) {
printk ( KERN_INFO
" %s: interface %s disconnected, lcn %i ! \n " ,
card - > devname , dev - > name , chan - > common . lcn ) ;
}
atomic_set ( & chan - > common . disconnect , 0 ) ;
if ( chan - > common . svc ) {
* ( unsigned short * ) dev - > dev_addr = 0 ;
card - > u . x . svc_to_dev_map [ ( chan - > common . lcn % X25_MAX_CHAN ) ] = NULL ;
chan - > common . lcn = 0 ;
}
if ( chan - > transmit_length ) {
chan - > transmit_length = 0 ;
atomic_set ( & chan - > common . driver_busy , 0 ) ;
chan - > tx_offset = 0 ;
if ( netif_queue_stopped ( dev ) ) {
netif_wake_queue ( dev ) ;
}
}
atomic_set ( & chan - > common . command , 0 ) ;
break ;
case WAN_DISCONNECTING :
if ( card - > u . x . logging ) {
printk ( KERN_INFO
" \n %s: interface %s disconnecting, lcn %i ... \n " ,
card - > devname , dev - > name , chan - > common . lcn ) ;
}
atomic_set ( & chan - > common . disconnect , 0 ) ;
break ;
}
chan - > common . state = state ;
}
chan - > state_tick = jiffies ;
restore_flags ( flags ) ;
}
/*
* Send packet on a logical channel .
* When this function is called , tx_skb field of the channel data
* space points to the transmit socket buffer . When transmission
* is complete , release socket buffer and reset ' tbusy ' flag .
*
* Return : 0 - transmission complete
* 1 - busy
*
* Notes :
* 1. If packet length is greater than MTU for this channel , we ' ll fragment
* the packet into ' complete sequence ' using M - bit .
* 2. When transmission is complete , an event notification should be issued
* to the router .
*/
static int chan_send ( struct net_device * dev , void * buff , unsigned data_len ,
unsigned char tx_intr )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
TX25Status * status = card - > flags ;
unsigned len = 0 , qdm = 0 , res = 0 , orig_len = 0 ;
void * data ;
/* Check to see if channel is ready */
if ( ( ! ( status - > cflags [ chan - > ch_idx ] & 0x40 ) & & ! card - > u . x . LAPB_hdlc ) | |
! ( * card - > u . x . hdlc_buf_status & 0x40 ) ) {
if ( ! tx_intr ) {
setup_for_delayed_transmit ( dev , buff , data_len ) ;
return 0 ;
} else {
/* By returning 0 to tx_intr the packet will be dropped */
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > ifstats . tx_dropped ;
printk ( KERN_INFO " %s: ERROR, Tx intr could not send, dropping %s: \n " ,
card - > devname , dev - > name ) ;
+ + chan - > if_send_stat . if_send_bfr_not_passed_to_adptr ;
return 0 ;
}
}
if ( chan - > common . usedby = = API ) {
/* Remove the API Header */
x25api_hdr_t * api_data = ( x25api_hdr_t * ) buff ;
/* Set the qdm bits from the packet header
* User has the option to set the qdm bits
*/
qdm = api_data - > qdm ;
orig_len = len = data_len - sizeof ( x25api_hdr_t ) ;
data = ( unsigned char * ) buff + sizeof ( x25api_hdr_t ) ;
} else {
data = buff ;
orig_len = len = data_len ;
}
if ( tx_intr ) {
/* We are in tx_intr, minus the tx_offset from
* the total length . The tx_offset part of the
* data has already been sent . Also , move the
* data pointer to proper offset location .
*/
len - = chan - > tx_offset ;
data = ( unsigned char * ) data + chan - > tx_offset ;
}
/* Check if the packet length is greater than MTU
* If YES : Cut the len to MTU and set the M bit
*/
if ( len > chan - > tx_pkt_size & & ! card - > u . x . LAPB_hdlc ) {
len = chan - > tx_pkt_size ;
qdm | = M_BIT ;
}
/* Pass only first three bits of the qdm byte to the send
* routine . In case user sets any other bit which might
* cause errors .
*/
switch ( x25_send ( card , chan - > common . lcn , ( qdm & 0x07 ) , len , data ) ) {
case 0x00 : /* success */
chan - > i_timeout_sofar = jiffies ;
dev - > trans_start = jiffies ;
if ( ( qdm & M_BIT ) & & ! card - > u . x . LAPB_hdlc ) {
if ( ! tx_intr ) {
/* The M bit was set, which means that part of the
* packet has been sent . Copy the packet into a buffer
* and set the offset to len , so on next tx_inter
* the packet will be sent using the below offset .
*/
chan - > tx_offset + = len ;
+ + chan - > ifstats . tx_packets ;
chan - > ifstats . tx_bytes + = len ;
if ( chan - > tx_offset < orig_len ) {
setup_for_delayed_transmit ( dev , buff , data_len ) ;
}
res = 0 ;
} else {
/* We are already in tx_inter, thus data is already
* in the buffer . Update the offset and wait for
* next tx_intr . We add on to the offset , since data can
* be X number of times larger than max data size .
*/
+ + chan - > ifstats . tx_packets ;
chan - > ifstats . tx_bytes + = len ;
+ + chan - > if_send_stat . if_send_bfr_passed_to_adptr ;
chan - > tx_offset + = len ;
/* The user can set the qdm bit as well.
* If the entire packet was sent and qdm is still
* set , than it ' s the user who has set the M bit . In that ,
* case indicate that the packet was send by returning
* 0 and wait for a new packet . Otherwise , wait for next
* tx interrupt to send the rest of the packet */
if ( chan - > tx_offset < orig_len ) {
res = 1 ;
} else {
res = 0 ;
}
}
} else {
+ + chan - > ifstats . tx_packets ;
chan - > ifstats . tx_bytes + = len ;
+ + chan - > if_send_stat . if_send_bfr_passed_to_adptr ;
res = 0 ;
}
break ;
case 0x33 : /* Tx busy */
if ( tx_intr ) {
printk ( KERN_INFO " %s: Tx_intr: Big Error dropping packet %s \n " ,
card - > devname , dev - > name ) ;
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > if_send_stat . if_send_bfr_not_passed_to_adptr ;
res = 0 ;
} else {
DBG_PRINTK ( KERN_INFO
" %s: Send: Big Error should have tx: storring %s \n " ,
card - > devname , dev - > name ) ;
setup_for_delayed_transmit ( dev , buff , data_len ) ;
res = 1 ;
}
break ;
default : /* failure */
+ + chan - > ifstats . tx_errors ;
if ( tx_intr ) {
printk ( KERN_INFO " %s: Tx_intr: Failure to send, dropping %s \n " ,
card - > devname , dev - > name ) ;
+ + chan - > ifstats . tx_dropped ;
+ + card - > wandev . stats . tx_dropped ;
+ + chan - > if_send_stat . if_send_bfr_not_passed_to_adptr ;
res = 0 ;
} else {
DBG_PRINTK ( KERN_INFO " %s: Send: Failure to send !!!, storing %s \n " ,
card - > devname , dev - > name ) ;
setup_for_delayed_transmit ( dev , buff , data_len ) ;
res = 1 ;
}
break ;
}
return res ;
}
/*
* Parse X .25 call request data and fill x25_call_info_t structure .
*/
static void parse_call_info ( unsigned char * str , x25_call_info_t * info )
{
memset ( info , 0 , sizeof ( x25_call_info_t ) ) ;
for ( ; * str ; + + str )
{
int i ;
unsigned char ch ;
if ( * str = = ' - ' ) switch ( str [ 1 ] ) {
/* Take minus 2 off the maximum size so that
* last byte is 0. This way we can use string
* manipulaton functions on call information .
*/
case ' d ' : /* destination address */
for ( i = 0 ; i < ( MAX_X25_ADDR_SIZE - 2 ) ; + + i ) {
ch = str [ 2 + i ] ;
if ( isspace ( ch ) ) break ;
info - > dest [ i ] = ch ;
}
break ;
case ' s ' : /* source address */
for ( i = 0 ; i < ( MAX_X25_ADDR_SIZE - 2 ) ; + + i ) {
ch = str [ 2 + i ] ;
if ( isspace ( ch ) ) break ;
info - > src [ i ] = ch ;
}
break ;
case ' u ' : /* user data */
for ( i = 0 ; i < ( MAX_X25_DATA_SIZE - 2 ) ; + + i ) {
ch = str [ 2 + i ] ;
if ( isspace ( ch ) ) break ;
info - > user [ i ] = ch ;
}
info - > nuser = i ;
break ;
case ' f ' : /* facilities */
for ( i = 0 ; i < ( MAX_X25_FACL_SIZE - 2 ) ; + + i ) {
ch = str [ 2 + i ] ;
if ( isspace ( ch ) ) break ;
info - > facil [ i ] = ch ;
}
info - > nfacil = i ;
break ;
}
}
}
/*
* Convert line speed in bps to a number used by S502 code .
*/
static unsigned char bps_to_speed_code ( unsigned long bps )
{
unsigned char number ;
if ( bps < = 1200 ) number = 0x01 ;
else if ( bps < = 2400 ) number = 0x02 ;
else if ( bps < = 4800 ) number = 0x03 ;
else if ( bps < = 9600 ) number = 0x04 ;
else if ( bps < = 19200 ) number = 0x05 ;
else if ( bps < = 38400 ) number = 0x06 ;
else if ( bps < = 45000 ) number = 0x07 ;
else if ( bps < = 56000 ) number = 0x08 ;
else if ( bps < = 64000 ) number = 0x09 ;
else if ( bps < = 74000 ) number = 0x0A ;
else if ( bps < = 112000 ) number = 0x0B ;
else if ( bps < = 128000 ) number = 0x0C ;
else number = 0x0D ;
return number ;
}
/*
* Convert decimal string to unsigned integer .
* If len ! = 0 then only ' len ' characters of the string are converted .
*/
static unsigned int dec_to_uint ( unsigned char * str , int len )
{
unsigned val ;
if ( ! len )
len = strlen ( str ) ;
for ( val = 0 ; len & & is_digit ( * str ) ; + + str , - - len )
val = ( val * 10 ) + ( * str - ( unsigned ) ' 0 ' ) ;
return val ;
}
/*
* Convert hex string to unsigned integer .
* If len ! = 0 then only ' len ' characters of the string are conferted .
*/
static unsigned int hex_to_uint ( unsigned char * str , int len )
{
unsigned val , ch ;
if ( ! len )
len = strlen ( str ) ;
for ( val = 0 ; len ; + + str , - - len )
{
ch = * str ;
if ( is_digit ( ch ) )
val = ( val < < 4 ) + ( ch - ( unsigned ) ' 0 ' ) ;
else if ( is_hex_digit ( ch ) )
val = ( val < < 4 ) + ( ( ch & 0xDF ) - ( unsigned ) ' A ' + 10 ) ;
else break ;
}
return val ;
}
static int handle_IPXWAN ( unsigned char * sendpacket , char * devname , unsigned char enable_IPX , unsigned long network_number , unsigned short proto )
{
int i ;
if ( proto = = ETH_P_IPX ) {
/* It's an IPX packet */
if ( ! enable_IPX ) {
/* Return 1 so we don't pass it up the stack. */
return 1 ;
}
} else {
/* It's not IPX so pass it up the stack.*/
return 0 ;
}
if ( sendpacket [ 16 ] = = 0x90 & &
sendpacket [ 17 ] = = 0x04 )
{
/* It's IPXWAN */
if ( sendpacket [ 2 ] = = 0x02 & &
sendpacket [ 34 ] = = 0x00 )
{
/* It's a timer request packet */
printk ( KERN_INFO " %s: Received IPXWAN Timer Request packet \n " , devname ) ;
/* Go through the routing options and answer no to every
* option except Unnumbered RIP / SAP
*/
for ( i = 41 ; sendpacket [ i ] = = 0x00 ; i + = 5 )
{
/* 0x02 is the option for Unnumbered RIP/SAP */
if ( sendpacket [ i + 4 ] ! = 0x02 )
{
sendpacket [ i + 1 ] = 0 ;
}
}
/* Skip over the extended Node ID option */
if ( sendpacket [ i ] = = 0x04 )
{
i + = 8 ;
}
/* We also want to turn off all header compression opt. */
for ( ; sendpacket [ i ] = = 0x80 ; )
{
sendpacket [ i + 1 ] = 0 ;
i + = ( sendpacket [ i + 2 ] < < 8 ) + ( sendpacket [ i + 3 ] ) + 4 ;
}
/* Set the packet type to timer response */
sendpacket [ 34 ] = 0x01 ;
printk ( KERN_INFO " %s: Sending IPXWAN Timer Response \n " , devname ) ;
}
else if ( sendpacket [ 34 ] = = 0x02 )
{
/* This is an information request packet */
printk ( KERN_INFO " %s: Received IPXWAN Information Request packet \n " , devname ) ;
/* Set the packet type to information response */
sendpacket [ 34 ] = 0x03 ;
/* Set the router name */
sendpacket [ 51 ] = ' X ' ;
sendpacket [ 52 ] = ' T ' ;
sendpacket [ 53 ] = ' P ' ;
sendpacket [ 54 ] = ' I ' ;
sendpacket [ 55 ] = ' P ' ;
sendpacket [ 56 ] = ' E ' ;
sendpacket [ 57 ] = ' - ' ;
sendpacket [ 58 ] = CVHexToAscii ( network_number > > 28 ) ;
sendpacket [ 59 ] = CVHexToAscii ( ( network_number & 0x0F000000 ) > > 24 ) ;
sendpacket [ 60 ] = CVHexToAscii ( ( network_number & 0x00F00000 ) > > 20 ) ;
sendpacket [ 61 ] = CVHexToAscii ( ( network_number & 0x000F0000 ) > > 16 ) ;
sendpacket [ 62 ] = CVHexToAscii ( ( network_number & 0x0000F000 ) > > 12 ) ;
sendpacket [ 63 ] = CVHexToAscii ( ( network_number & 0x00000F00 ) > > 8 ) ;
sendpacket [ 64 ] = CVHexToAscii ( ( network_number & 0x000000F0 ) > > 4 ) ;
sendpacket [ 65 ] = CVHexToAscii ( network_number & 0x0000000F ) ;
for ( i = 66 ; i < 99 ; i + = 1 )
{
sendpacket [ i ] = 0 ;
}
printk ( KERN_INFO " %s: Sending IPXWAN Information Response packet \n " , devname ) ;
}
else
{
printk ( KERN_INFO " %s: Unknown IPXWAN packet! \n " , devname ) ;
return 0 ;
}
/* Set the WNodeID to our network address */
sendpacket [ 35 ] = ( unsigned char ) ( network_number > > 24 ) ;
sendpacket [ 36 ] = ( unsigned char ) ( ( network_number & 0x00FF0000 ) > > 16 ) ;
sendpacket [ 37 ] = ( unsigned char ) ( ( network_number & 0x0000FF00 ) > > 8 ) ;
sendpacket [ 38 ] = ( unsigned char ) ( network_number & 0x000000FF ) ;
return 1 ;
} else {
/*If we get here it's an IPX-data packet, so it'll get passed up the stack.
*/
/* switch the network numbers */
switch_net_numbers ( sendpacket , network_number , 1 ) ;
return 0 ;
}
}
/*
* If incoming is 0 ( outgoing ) - if the net numbers is ours make it 0
* if incoming is 1 - if the net number is 0 make it ours
*/
static void switch_net_numbers ( unsigned char * sendpacket , unsigned long network_number , unsigned char incoming )
{
unsigned long pnetwork_number ;
pnetwork_number = ( unsigned long ) ( ( sendpacket [ 6 ] < < 24 ) +
( sendpacket [ 7 ] < < 16 ) + ( sendpacket [ 8 ] < < 8 ) +
sendpacket [ 9 ] ) ;
if ( ! incoming ) {
/*If the destination network number is ours, make it 0 */
if ( pnetwork_number = = network_number ) {
sendpacket [ 6 ] = sendpacket [ 7 ] = sendpacket [ 8 ] =
sendpacket [ 9 ] = 0x00 ;
}
} else {
/* If the incoming network is 0, make it ours */
if ( pnetwork_number = = 0 ) {
sendpacket [ 6 ] = ( unsigned char ) ( network_number > > 24 ) ;
sendpacket [ 7 ] = ( unsigned char ) ( ( network_number &
0x00FF0000 ) > > 16 ) ;
sendpacket [ 8 ] = ( unsigned char ) ( ( network_number &
0x0000FF00 ) > > 8 ) ;
sendpacket [ 9 ] = ( unsigned char ) ( network_number &
0x000000FF ) ;
}
}
pnetwork_number = ( unsigned long ) ( ( sendpacket [ 18 ] < < 24 ) +
( sendpacket [ 19 ] < < 16 ) + ( sendpacket [ 20 ] < < 8 ) +
sendpacket [ 21 ] ) ;
if ( ! incoming ) {
/* If the source network is ours, make it 0 */
if ( pnetwork_number = = network_number ) {
sendpacket [ 18 ] = sendpacket [ 19 ] = sendpacket [ 20 ] =
sendpacket [ 21 ] = 0x00 ;
}
} else {
/* If the source network is 0, make it ours */
if ( pnetwork_number = = 0 ) {
sendpacket [ 18 ] = ( unsigned char ) ( network_number > > 24 ) ;
sendpacket [ 19 ] = ( unsigned char ) ( ( network_number &
0x00FF0000 ) > > 16 ) ;
sendpacket [ 20 ] = ( unsigned char ) ( ( network_number &
0x0000FF00 ) > > 8 ) ;
sendpacket [ 21 ] = ( unsigned char ) ( network_number &
0x000000FF ) ;
}
}
} /* switch_net_numbers */
/********************* X25API SPECIFIC FUNCTIONS ****************/
/*===============================================================
* find_channel
*
* Manages the lcn to device map . It increases performance
* because it eliminates the need to search through the link
* list for a device which is bounded to a specific lcn .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
struct net_device * find_channel ( sdla_t * card , unsigned lcn )
{
if ( card - > u . x . LAPB_hdlc ) {
return card - > wandev . dev ;
} else {
/* We don't know whether the incoming lcn
* is a PVC or an SVC channel . But we do know that
* the lcn cannot be for both the PVC and the SVC
* channel .
* If the lcn number is greater or equal to 255 ,
* take the modulo 255 of that number . We only have
* 255 locations , thus higher numbers must be mapped
* to a number between 0 and 245.
* We must separate pvc ' s and svc ' s since two don ' t
* have to be contiguous . Meaning pvc ' s can start
* from 1 to 10 and svc ' s can start from 256 to 266.
* But 256 % 255 is 1 , i . e . CONFLICT .
*/
/* Highest LCN number must be less or equal to 4096 */
if ( ( lcn < = MAX_LCN_NUM ) & & ( lcn > 0 ) ) {
if ( lcn < X25_MAX_CHAN ) {
if ( card - > u . x . svc_to_dev_map [ lcn ] )
return card - > u . x . svc_to_dev_map [ lcn ] ;
if ( card - > u . x . pvc_to_dev_map [ lcn ] )
return card - > u . x . pvc_to_dev_map [ lcn ] ;
} else {
int new_lcn = lcn % X25_MAX_CHAN ;
if ( card - > u . x . svc_to_dev_map [ new_lcn ] )
return card - > u . x . svc_to_dev_map [ new_lcn ] ;
if ( card - > u . x . pvc_to_dev_map [ new_lcn ] )
return card - > u . x . pvc_to_dev_map [ new_lcn ] ;
}
}
return NULL ;
}
}
void bind_lcn_to_dev ( sdla_t * card , struct net_device * dev , unsigned lcn )
{
x25_channel_t * chan = dev - > priv ;
/* Modulo the lcn number by X25_MAX_CHAN (255)
* because the lcn number can be greater than 255
*
* We need to split svc and pvc since they don ' t have
* to be contigous .
*/
if ( chan - > common . svc ) {
card - > u . x . svc_to_dev_map [ ( lcn % X25_MAX_CHAN ) ] = dev ;
} else {
card - > u . x . pvc_to_dev_map [ ( lcn % X25_MAX_CHAN ) ] = dev ;
}
chan - > common . lcn = lcn ;
}
/*===============================================================
* x25api_bh
*
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void x25api_bh ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
struct sk_buff * skb ;
if ( atomic_read ( & chan - > bh_buff_used ) = = 0 ) {
printk ( KERN_INFO " %s: BH Buffer Empty in BH \n " ,
card - > devname ) ;
clear_bit ( 0 , & chan - > tq_working ) ;
return ;
}
while ( atomic_read ( & chan - > bh_buff_used ) ) {
/* If the sock is in the process of unlinking the
* driver from the socket , we must get out .
* This never happends but is a sanity check . */
if ( test_bit ( 0 , & chan - > common . common_critical ) ) {
clear_bit ( 0 , & chan - > tq_working ) ;
return ;
}
/* If LAPB HDLC, do not drop packets if socket is
* not connected . Let the buffer fill up and
* turn off rx interrupt */
if ( card - > u . x . LAPB_hdlc ) {
if ( chan - > common . sk = = NULL | | chan - > common . func = = NULL ) {
clear_bit ( 0 , & chan - > tq_working ) ;
return ;
}
}
skb = ( ( bh_data_t * ) & chan - > bh_head [ chan - > bh_read ] ) - > skb ;
if ( skb = = NULL ) {
printk ( KERN_INFO " %s: BH Skb empty for read %i \n " ,
card - > devname , chan - > bh_read ) ;
} else {
if ( chan - > common . sk = = NULL | | chan - > common . func = = NULL ) {
printk ( KERN_INFO " %s: BH: Socket disconnected, dropping \n " ,
card - > devname ) ;
dev_kfree_skb_any ( skb ) ;
x25api_bh_cleanup ( dev ) ;
+ + chan - > ifstats . rx_dropped ;
+ + chan - > rx_intr_stat . rx_intr_bfr_not_passed_to_stack ;
continue ;
}
if ( chan - > common . func ( skb , dev , chan - > common . sk ) ! = 0 ) {
/* Sock full cannot send, queue us for another
* try
*/
printk ( KERN_INFO " %s: BH: !!! Packet failed to send !!!!! \n " ,
card - > devname ) ;
atomic_set ( & chan - > common . receive_block , 1 ) ;
return ;
} else {
x25api_bh_cleanup ( dev ) ;
+ + chan - > rx_intr_stat . rx_intr_bfr_passed_to_stack ;
}
}
}
clear_bit ( 0 , & chan - > tq_working ) ;
return ;
}
/*===============================================================
* x25api_bh_cleanup
*
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int x25api_bh_cleanup ( struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
TX25Status * status = card - > flags ;
( ( bh_data_t * ) & chan - > bh_head [ chan - > bh_read ] ) - > skb = NULL ;
if ( chan - > bh_read = = MAX_BH_BUFF ) {
chan - > bh_read = 0 ;
} else {
+ + chan - > bh_read ;
}
/* If the Receive interrupt was off, it means
* that we filled up our circular buffer . Check
* that we have space in the buffer . If so
* turn the RX interrupt back on .
*/
if ( ! ( status - > imask & INTR_ON_RX_FRAME ) ) {
if ( atomic_read ( & chan - > bh_buff_used ) < ( MAX_BH_BUFF + 1 ) ) {
printk ( KERN_INFO " %s: BH: Turning on the interrupt \n " ,
card - > devname ) ;
status - > imask | = INTR_ON_RX_FRAME ;
}
}
atomic_dec ( & chan - > bh_buff_used ) ;
return 0 ;
}
/*===============================================================
* bh_enqueue
*
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int bh_enqueue ( struct net_device * dev , struct sk_buff * skb )
{
x25_channel_t * chan = dev - > priv ;
sdla_t * card = chan - > card ;
TX25Status * status = card - > flags ;
if ( atomic_read ( & chan - > bh_buff_used ) = = ( MAX_BH_BUFF + 1 ) ) {
printk ( KERN_INFO " %s: Bottom half buffer FULL \n " ,
card - > devname ) ;
return 1 ;
}
( ( bh_data_t * ) & chan - > bh_head [ chan - > bh_write ] ) - > skb = skb ;
if ( chan - > bh_write = = MAX_BH_BUFF ) {
chan - > bh_write = 0 ;
} else {
+ + chan - > bh_write ;
}
atomic_inc ( & chan - > bh_buff_used ) ;
if ( atomic_read ( & chan - > bh_buff_used ) = = ( MAX_BH_BUFF + 1 ) ) {
printk ( KERN_INFO " %s: Buffer is now full, Turning off RX Intr \n " ,
card - > devname ) ;
status - > imask & = ~ INTR_ON_RX_FRAME ;
}
return 0 ;
}
/*===============================================================
* timer_intr_cmd_exec
*
* Called by timer interrupt to execute a command
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int timer_intr_cmd_exec ( sdla_t * card )
{
struct net_device * dev ;
unsigned char more_to_exec = 0 ;
volatile x25_channel_t * chan = NULL ;
int i = 0 , bad_cmd = 0 , err = 0 ;
if ( card - > u . x . cmd_dev = = NULL ) {
card - > u . x . cmd_dev = card - > wandev . dev ;
}
dev = card - > u . x . cmd_dev ;
for ( ; ; ) {
chan = dev - > priv ;
if ( atomic_read ( & chan - > common . command ) ) {
bad_cmd = check_bad_command ( card , dev ) ;
if ( ( ! chan - > common . mbox | | atomic_read ( & chan - > common . disconnect ) ) & &
! bad_cmd ) {
/* Socket has died or exited, We must bring the
* channel down before anybody else tries to
* use it */
err = channel_disconnect ( card , dev ) ;
} else {
err = execute_delayed_cmd ( card , dev ,
( mbox_cmd_t * ) chan - > common . mbox ,
bad_cmd ) ;
}
switch ( err ) {
case RETURN_RESULT :
/* Return the result to the socket without
* delay . NO_WAIT Command */
atomic_set ( & chan - > common . command , 0 ) ;
if ( atomic_read ( & card - > u . x . command_busy ) )
atomic_set ( & card - > u . x . command_busy , 0 ) ;
send_delayed_cmd_result ( card , dev , card - > mbox ) ;
more_to_exec = 0 ;
break ;
case DELAY_RESULT :
/* Wait for the remote to respond, before
* sending the result up to the socket .
* WAIT command */
if ( atomic_read ( & card - > u . x . command_busy ) )
atomic_set ( & card - > u . x . command_busy , 0 ) ;
atomic_set ( & chan - > common . command , 0 ) ;
more_to_exec = 0 ;
break ;
default :
/* If command could not be executed for
* some reason ( i . e return code 0x33 busy )
* set the more_to_exec bit which will
* indicate that this command must be exectued
* again during next timer interrupt
*/
more_to_exec = 1 ;
if ( atomic_read ( & card - > u . x . command_busy ) = = 0 )
atomic_set ( & card - > u . x . command_busy , 1 ) ;
break ;
}
bad_cmd = 0 ;
/* If flags is set, there are no hdlc buffers,
* thus , wait for the next pass and try the
* same command again . Otherwise , start searching
* from next device on the next pass .
*/
if ( ! more_to_exec ) {
dev = move_dev_to_next ( card , dev ) ;
}
break ;
} else {
/* This device has nothing to execute,
* go to next .
*/
if ( atomic_read ( & card - > u . x . command_busy ) )
atomic_set ( & card - > u . x . command_busy , 0 ) ;
dev = move_dev_to_next ( card , dev ) ;
}
if ( + + i = = card - > u . x . no_dev ) {
if ( ! more_to_exec ) {
DBG_PRINTK ( KERN_INFO " %s: Nothing to execute in Timer \n " ,
card - > devname ) ;
if ( atomic_read ( & card - > u . x . command_busy ) ) {
atomic_set ( & card - > u . x . command_busy , 0 ) ;
}
}
break ;
}
} //End of FOR
card - > u . x . cmd_dev = dev ;
if ( more_to_exec ) {
/* If more commands are pending, do not turn off timer
* interrupt */
return 1 ;
} else {
/* No more commands, turn off timer interrupt */
return 0 ;
}
}
/*===============================================================
* execute_delayed_cmd
*
* Execute an API command which was passed down from the
* sock . Sock is very limited in which commands it can
* execute . Wait and No Wait commands are supported .
* Place Call , Clear Call and Reset wait commands , where
* Accept Call is a no_wait command .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int execute_delayed_cmd ( sdla_t * card , struct net_device * dev ,
mbox_cmd_t * usr_cmd , char bad_cmd )
{
TX25Mbox * mbox = card - > mbox ;
int err ;
x25_channel_t * chan = dev - > priv ;
int delay = RETURN_RESULT ;
if ( ! ( * card - > u . x . hdlc_buf_status & 0x40 ) & & ! bad_cmd ) {
return TRY_CMD_AGAIN ;
}
/* This way a command is guaranteed to be executed for
* a specific lcn , the network interface is bound to . */
usr_cmd - > cmd . lcn = chan - > common . lcn ;
/* If channel is pvc, instead of place call
* run x25_channel configuration . If running LAPB HDLC
* enable communications .
*/
if ( ( ! chan - > common . svc ) & & ( usr_cmd - > cmd . command = = X25_PLACE_CALL ) ) {
if ( card - > u . x . LAPB_hdlc ) {
DBG_PRINTK ( KERN_INFO " LAPB: Connecting \n " ) ;
connect ( card ) ;
set_chan_state ( dev , WAN_CONNECTING ) ;
return DELAY_RESULT ;
} else {
DBG_PRINTK ( KERN_INFO " %s: PVC is CONNECTING \n " , card - > devname ) ;
if ( x25_get_chan_conf ( card , chan ) = = CMD_OK ) {
set_chan_state ( dev , WAN_CONNECTED ) ;
} else {
set_chan_state ( dev , WAN_DISCONNECTED ) ;
}
return RETURN_RESULT ;
}
}
/* Copy the socket mbox command onto the board */
memcpy ( & mbox - > cmd , & usr_cmd - > cmd , sizeof ( TX25Cmd ) ) ;
if ( usr_cmd - > cmd . length ) {
memcpy ( mbox - > data , usr_cmd - > data , usr_cmd - > cmd . length ) ;
}
/* Check if command is bad. We need to copy the cmd into
* the buffer regardless since we return the , mbox to
* the user */
if ( bad_cmd ) {
mbox - > cmd . result = 0x01 ;
return RETURN_RESULT ;
}
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err ! = CMD_OK & & err ! = X25RES_NOT_READY )
x25_error ( card , err , usr_cmd - > cmd . command , usr_cmd - > cmd . lcn ) ;
if ( mbox - > cmd . result = = X25RES_NOT_READY ) {
return TRY_CMD_AGAIN ;
}
switch ( mbox - > cmd . command ) {
case X25_PLACE_CALL :
switch ( mbox - > cmd . result ) {
case CMD_OK :
/* Check if Place call is a wait command or a
* no wait command */
if ( atomic_read ( & chan - > common . command ) & 0x80 )
delay = RETURN_RESULT ;
else
delay = DELAY_RESULT ;
DBG_PRINTK ( KERN_INFO " \n %s: PLACE CALL Binding dev %s to lcn %i \n " ,
card - > devname , dev - > name , mbox - > cmd . lcn ) ;
bind_lcn_to_dev ( card , dev , mbox - > cmd . lcn ) ;
set_chan_state ( dev , WAN_CONNECTING ) ;
break ;
default :
delay = RETURN_RESULT ;
set_chan_state ( dev , WAN_DISCONNECTED ) ;
break ;
}
break ;
case X25_ACCEPT_CALL :
switch ( mbox - > cmd . result ) {
case CMD_OK :
DBG_PRINTK ( KERN_INFO " \n %s: ACCEPT Binding dev %s to lcn %i \n " ,
card - > devname , dev - > name , mbox - > cmd . lcn ) ;
bind_lcn_to_dev ( card , dev , mbox - > cmd . lcn ) ;
if ( x25_get_chan_conf ( card , chan ) = = CMD_OK ) {
set_chan_state ( dev , WAN_CONNECTED ) ;
delay = RETURN_RESULT ;
} else {
if ( x25_clear_call ( card , usr_cmd - > cmd . lcn , 0 , 0 ) = = CMD_OK ) {
/* if clear is successful, wait for clear confirm
*/
delay = DELAY_RESULT ;
} else {
/* Do not change the state here. If we fail
* the accept the return code is send up
* the stack , which will ether retry
* or clear the call
*/
DBG_PRINTK ( KERN_INFO
" %s: ACCEPT: STATE MAY BE CURRUPTED 2 !!!!! \n " ,
card - > devname ) ;
delay = RETURN_RESULT ;
}
}
break ;
case X25RES_ASYNC_PACKET :
delay = TRY_CMD_AGAIN ;
break ;
default :
DBG_PRINTK ( KERN_INFO " %s: ACCEPT FAILED \n " , card - > devname ) ;
if ( x25_clear_call ( card , usr_cmd - > cmd . lcn , 0 , 0 ) = = CMD_OK ) {
delay = DELAY_RESULT ;
} else {
/* Do not change the state here. If we fail the accept. The
* return code is send up the stack , which will ether retry
* or clear the call */
DBG_PRINTK ( KERN_INFO
" %s: ACCEPT: STATE MAY BE CORRUPTED 1 !!!!! \n " ,
card - > devname ) ;
delay = RETURN_RESULT ;
}
}
break ;
case X25_CLEAR_CALL :
switch ( mbox - > cmd . result ) {
case CMD_OK :
DBG_PRINTK ( KERN_INFO
" CALL CLEAR OK: Dev %s Mbox Lcn %i Chan Lcn %i \n " ,
dev - > name , mbox - > cmd . lcn , chan - > common . lcn ) ;
set_chan_state ( dev , WAN_DISCONNECTING ) ;
delay = DELAY_RESULT ;
break ;
case X25RES_CHANNEL_IN_USE :
case X25RES_ASYNC_PACKET :
delay = TRY_CMD_AGAIN ;
break ;
case X25RES_LINK_NOT_IN_ABM :
case X25RES_INVAL_LCN :
case X25RES_INVAL_STATE :
set_chan_state ( dev , WAN_DISCONNECTED ) ;
delay = RETURN_RESULT ;
break ;
default :
/* If command did not execute because of user
* fault , do not change the state . This will
* signal the socket that clear command failed .
* User can retry or close the socket .
* When socket gets killed , it will set the
* chan - > disconnect which will signal
* driver to clear the call */
printk ( KERN_INFO " %s: Clear Command Failed, Rc %x \n " ,
card - > devname , mbox - > cmd . command ) ;
delay = RETURN_RESULT ;
}
break ;
}
return delay ;
}
/*===============================================================
* api_incoming_call
*
* Pass an incoming call request up the listening
* sock . If the API sock is not listening reject the
* call .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int api_incoming_call ( sdla_t * card , TX25Mbox * mbox , int lcn )
{
struct sk_buff * skb ;
int len = sizeof ( TX25Cmd ) + mbox - > cmd . length ;
if ( alloc_and_init_skb_buf ( card , & skb , len ) ) {
printk ( KERN_INFO " %s: API incoming call, no memory \n " , card - > devname ) ;
return 1 ;
}
memcpy ( skb_put ( skb , len ) , & mbox - > cmd , len ) ;
skb - > mac . raw = skb - > data ;
skb - > protocol = htons ( X25_PROT ) ;
skb - > pkt_type = WAN_PACKET_ASYNC ;
if ( card - > func ( skb , card - > sk ) < 0 ) {
printk ( KERN_INFO " %s: MAJOR ERROR: Failed to send up place call \n " , card - > devname ) ;
dev_kfree_skb_any ( skb ) ;
return 1 ;
}
return 0 ;
}
/*===============================================================
* send_delayed_cmd_result
*
* Wait commands like PLEACE CALL or CLEAR CALL must wait
* until the result arrives . This function passes
* the result to a waiting sock .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void send_delayed_cmd_result ( sdla_t * card , struct net_device * dev ,
TX25Mbox * mbox )
{
x25_channel_t * chan = dev - > priv ;
mbox_cmd_t * usr_cmd = ( mbox_cmd_t * ) chan - > common . mbox ;
struct sk_buff * skb ;
int len = sizeof ( unsigned char ) ;
atomic_set ( & chan - > common . command , 0 ) ;
/* If the sock is in the process of unlinking the
* driver from the socket , we must get out .
* This never happends but is a sanity check . */
if ( test_bit ( 0 , & chan - > common . common_critical ) ) {
return ;
}
if ( ! usr_cmd | | ! chan - > common . sk | | ! chan - > common . func ) {
DBG_PRINTK ( KERN_INFO " Delay result: Sock not bounded sk: %u, func: %u, mbox: %u \n " ,
( unsigned int ) chan - > common . sk ,
( unsigned int ) chan - > common . func ,
( unsigned int ) usr_cmd ) ;
return ;
}
memcpy ( & usr_cmd - > cmd , & mbox - > cmd , sizeof ( TX25Cmd ) ) ;
if ( mbox - > cmd . length > 0 ) {
memcpy ( usr_cmd - > data , mbox - > data , mbox - > cmd . length ) ;
}
if ( alloc_and_init_skb_buf ( card , & skb , len ) ) {
printk ( KERN_INFO " Delay result: No sock buffers \n " ) ;
return ;
}
memcpy ( skb_put ( skb , len ) , & mbox - > cmd . command , len ) ;
skb - > mac . raw = skb - > data ;
skb - > pkt_type = WAN_PACKET_CMD ;
chan - > common . func ( skb , dev , chan - > common . sk ) ;
}
/*===============================================================
* clear_confirm_event
*
* Pass the clear confirmation event up the sock . The
* API will disconnect only after the clear confirmation
* has been received .
*
* Depending on the state , clear confirmation could
* be an OOB event , or a result of an API command .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int clear_confirm_event ( sdla_t * card , TX25Mbox * mb )
{
struct net_device * dev ;
x25_channel_t * chan ;
unsigned char old_state ;
dev = find_channel ( card , mb - > cmd . lcn ) ;
if ( ! dev ) {
DBG_PRINTK ( KERN_INFO " %s: *** GOT CLEAR BUT NO DEV %i \n " ,
card - > devname , mb - > cmd . lcn ) ;
return 0 ;
}
chan = dev - > priv ;
DBG_PRINTK ( KERN_INFO " %s: GOT CLEAR CONFIRM %s: Mbox lcn %i Chan lcn %i \n " ,
card - > devname , dev - > name , mb - > cmd . lcn , chan - > common . lcn ) ;
/* If not API fall through to default.
* If API , send the result to a waiting
* socket .
*/
old_state = chan - > common . state ;
set_chan_state ( dev , WAN_DISCONNECTED ) ;
if ( chan - > common . usedby = = API ) {
switch ( old_state ) {
case WAN_DISCONNECTING :
case WAN_CONNECTING :
send_delayed_cmd_result ( card , dev , mb ) ;
break ;
case WAN_CONNECTED :
send_oob_msg ( card , dev , mb ) ;
break ;
}
return 1 ;
}
return 0 ;
}
/*===============================================================
* send_oob_msg
*
* Construct an NEM Message and pass it up the connected
* sock . If the sock is not bounded discard the NEM .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void send_oob_msg ( sdla_t * card , struct net_device * dev , TX25Mbox * mbox )
{
x25_channel_t * chan = dev - > priv ;
mbox_cmd_t * usr_cmd = ( mbox_cmd_t * ) chan - > common . mbox ;
struct sk_buff * skb ;
int len = sizeof ( x25api_hdr_t ) + mbox - > cmd . length ;
x25api_t * api_hdr ;
/* If the sock is in the process of unlinking the
* driver from the socket , we must get out .
* This never happends but is a sanity check . */
if ( test_bit ( 0 , & chan - > common . common_critical ) ) {
return ;
}
if ( ! usr_cmd | | ! chan - > common . sk | | ! chan - > common . func ) {
DBG_PRINTK ( KERN_INFO " OOB MSG: Sock not bounded \n " ) ;
return ;
}
memcpy ( & usr_cmd - > cmd , & mbox - > cmd , sizeof ( TX25Cmd ) ) ;
if ( mbox - > cmd . length > 0 ) {
memcpy ( usr_cmd - > data , mbox - > data , mbox - > cmd . length ) ;
}
if ( alloc_and_init_skb_buf ( card , & skb , len ) ) {
printk ( KERN_INFO " %s: OOB MSG: No sock buffers \n " , card - > devname ) ;
return ;
}
api_hdr = ( x25api_t * ) skb_put ( skb , len ) ;
api_hdr - > hdr . pktType = mbox - > cmd . pktType & 0x7F ;
api_hdr - > hdr . qdm = mbox - > cmd . qdm ;
api_hdr - > hdr . cause = mbox - > cmd . cause ;
api_hdr - > hdr . diagn = mbox - > cmd . diagn ;
api_hdr - > hdr . length = mbox - > cmd . length ;
api_hdr - > hdr . result = mbox - > cmd . result ;
api_hdr - > hdr . lcn = mbox - > cmd . lcn ;
if ( mbox - > cmd . length > 0 ) {
memcpy ( api_hdr - > data , mbox - > data , mbox - > cmd . length ) ;
}
skb - > mac . raw = skb - > data ;
skb - > pkt_type = WAN_PACKET_ERR ;
if ( chan - > common . func ( skb , dev , chan - > common . sk ) < 0 ) {
if ( bh_enqueue ( dev , skb ) ) {
printk ( KERN_INFO " %s: Dropping OOB MSG \n " , card - > devname ) ;
dev_kfree_skb_any ( skb ) ;
}
}
DBG_PRINTK ( KERN_INFO " %s: OOB MSG OK, %s, lcn %i \n " ,
card - > devname , dev - > name , mbox - > cmd . lcn ) ;
}
/*===============================================================
* alloc_and_init_skb_buf
*
* Allocate and initialize an skb buffer .
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static int alloc_and_init_skb_buf ( sdla_t * card , struct sk_buff * * skb , int len )
{
struct sk_buff * new_skb = * skb ;
new_skb = dev_alloc_skb ( len + X25_HRDHDR_SZ ) ;
if ( new_skb = = NULL ) {
printk ( KERN_INFO " %s: no socket buffers available! \n " ,
card - > devname ) ;
return 1 ;
}
if ( skb_tailroom ( new_skb ) < len ) {
/* No room for the packet. Call off the whole thing! */
dev_kfree_skb_any ( new_skb ) ;
printk ( KERN_INFO " %s: Listen: unexpectedly long packet sequence \n "
, card - > devname ) ;
* skb = NULL ;
return 1 ;
}
* skb = new_skb ;
return 0 ;
}
/*===============================================================
* api_oob_event
*
* Send an OOB event up to the sock
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void api_oob_event ( sdla_t * card , TX25Mbox * mbox )
{
struct net_device * dev = find_channel ( card , mbox - > cmd . lcn ) ;
x25_channel_t * chan ;
if ( ! dev )
return ;
chan = dev - > priv ;
if ( chan - > common . usedby = = API )
send_oob_msg ( card , dev , mbox ) ;
}
static int channel_disconnect ( sdla_t * card , struct net_device * dev )
{
int err ;
x25_channel_t * chan = dev - > priv ;
DBG_PRINTK ( KERN_INFO " %s: TIMER: %s, Device down disconnecting \n " ,
card - > devname , dev - > name ) ;
if ( chan - > common . svc ) {
err = x25_clear_call ( card , chan - > common . lcn , 0 , 0 ) ;
} else {
/* If channel is PVC or LAPB HDLC, there is no call
* to be cleared , thus drop down to the default
* area
*/
err = 1 ;
}
switch ( err ) {
case X25RES_CHANNEL_IN_USE :
case X25RES_NOT_READY :
err = TRY_CMD_AGAIN ;
break ;
case CMD_OK :
DBG_PRINTK ( KERN_INFO " CALL CLEAR OK: Dev %s Chan Lcn %i \n " ,
dev - > name , chan - > common . lcn ) ;
set_chan_state ( dev , WAN_DISCONNECTING ) ;
atomic_set ( & chan - > common . command , 0 ) ;
err = DELAY_RESULT ;
break ;
default :
/* If LAPB HDLC protocol, bring the whole link down
* once the application terminates
*/
set_chan_state ( dev , WAN_DISCONNECTED ) ;
if ( card - > u . x . LAPB_hdlc ) {
DBG_PRINTK ( KERN_INFO " LAPB: Disconnecting Link \n " ) ;
hdlc_link_down ( card ) ;
}
atomic_set ( & chan - > common . command , 0 ) ;
err = RETURN_RESULT ;
break ;
}
return err ;
}
static void hdlc_link_down ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int retry = 5 ;
int err = 0 ;
do {
memset ( mbox , 0 , sizeof ( TX25Mbox ) ) ;
mbox - > cmd . command = X25_HDLC_LINK_DISC ;
mbox - > cmd . length = 1 ;
mbox - > data [ 0 ] = 0 ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & retry - - & & x25_error ( card , err , X25_HDLC_LINK_DISC , 0 ) ) ;
if ( err )
printk ( KERN_INFO " %s: Hdlc Link Down Failed %x \n " , card - > devname , err ) ;
disconnect ( card ) ;
}
static int check_bad_command ( sdla_t * card , struct net_device * dev )
{
x25_channel_t * chan = dev - > priv ;
int bad_cmd = 0 ;
switch ( atomic_read ( & chan - > common . command ) & 0x7F ) {
case X25_PLACE_CALL :
if ( chan - > common . state ! = WAN_DISCONNECTED )
bad_cmd = 1 ;
break ;
case X25_CLEAR_CALL :
if ( chan - > common . state = = WAN_DISCONNECTED )
bad_cmd = 1 ;
break ;
case X25_ACCEPT_CALL :
if ( chan - > common . state ! = WAN_CONNECTING )
bad_cmd = 1 ;
break ;
case X25_RESET :
if ( chan - > common . state ! = WAN_CONNECTED )
bad_cmd = 1 ;
break ;
default :
bad_cmd = 1 ;
break ;
}
if ( bad_cmd ) {
printk ( KERN_INFO " %s: Invalid State, BAD Command %x, dev %s, lcn %i, st %i \n " ,
card - > devname , atomic_read ( & chan - > common . command ) , dev - > name ,
chan - > common . lcn , chan - > common . state ) ;
}
return bad_cmd ;
}
/*************************** XPIPEMON FUNCTIONS **************************/
/*==============================================================================
* Process UDP call of type XPIPE
*/
static int process_udp_mgmt_pkt ( sdla_t * card )
{
int c_retry = MAX_CMD_RETRY ;
unsigned int len ;
struct sk_buff * new_skb ;
TX25Mbox * mbox = card - > mbox ;
int err ;
int udp_mgmt_req_valid = 1 ;
struct net_device * dev ;
x25_channel_t * chan ;
unsigned short lcn ;
struct timeval tv ;
x25_udp_pkt_t * x25_udp_pkt ;
x25_udp_pkt = ( x25_udp_pkt_t * ) card - > u . x . udp_pkt_data ;
dev = card - > u . x . udp_dev ;
chan = dev - > priv ;
lcn = chan - > common . lcn ;
switch ( x25_udp_pkt - > cblock . command ) {
/* XPIPE_ENABLE_TRACE */
case XPIPE_ENABLE_TRACING :
/* XPIPE_GET_TRACE_INFO */
case XPIPE_GET_TRACE_INFO :
/* SET FT1 MODE */
case XPIPE_SET_FT1_MODE :
if ( card - > u . x . udp_pkt_src = = UDP_PKT_FRM_NETWORK ) {
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_direction_err ;
udp_mgmt_req_valid = 0 ;
break ;
}
/* XPIPE_FT1_READ_STATUS */
case XPIPE_FT1_READ_STATUS :
/* FT1 MONITOR STATUS */
case XPIPE_FT1_STATUS_CTRL :
if ( card - > hw . fwid ! = SFID_X25_508 ) {
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_adptr_type_err ;
udp_mgmt_req_valid = 0 ;
break ;
}
default :
break ;
}
if ( ! udp_mgmt_req_valid ) {
/* set length to 0 */
x25_udp_pkt - > cblock . length = 0 ;
/* set return code */
x25_udp_pkt - > cblock . result = ( card - > hw . fwid ! = SFID_X25_508 ) ? 0x1F : 0xCD ;
} else {
switch ( x25_udp_pkt - > cblock . command ) {
case XPIPE_FLUSH_DRIVER_STATS :
init_x25_channel_struct ( chan ) ;
init_global_statistics ( card ) ;
mbox - > cmd . length = 0 ;
break ;
case XPIPE_DRIVER_STAT_IFSEND :
memcpy ( x25_udp_pkt - > data , & chan - > if_send_stat , sizeof ( if_send_stat_t ) ) ;
mbox - > cmd . length = sizeof ( if_send_stat_t ) ;
x25_udp_pkt - > cblock . length = mbox - > cmd . length ;
break ;
case XPIPE_DRIVER_STAT_INTR :
memcpy ( & x25_udp_pkt - > data [ 0 ] , & card - > statistics , sizeof ( global_stats_t ) ) ;
memcpy ( & x25_udp_pkt - > data [ sizeof ( global_stats_t ) ] ,
& chan - > rx_intr_stat , sizeof ( rx_intr_stat_t ) ) ;
mbox - > cmd . length = sizeof ( global_stats_t ) +
sizeof ( rx_intr_stat_t ) ;
x25_udp_pkt - > cblock . length = mbox - > cmd . length ;
break ;
case XPIPE_DRIVER_STAT_GEN :
memcpy ( x25_udp_pkt - > data ,
& chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_kmalloc_err ,
sizeof ( pipe_mgmt_stat_t ) ) ;
memcpy ( & x25_udp_pkt - > data [ sizeof ( pipe_mgmt_stat_t ) ] ,
& card - > statistics , sizeof ( global_stats_t ) ) ;
x25_udp_pkt - > cblock . result = 0 ;
x25_udp_pkt - > cblock . length = sizeof ( global_stats_t ) +
sizeof ( rx_intr_stat_t ) ;
mbox - > cmd . length = x25_udp_pkt - > cblock . length ;
break ;
case XPIPE_ROUTER_UP_TIME :
do_gettimeofday ( & tv ) ;
chan - > router_up_time = tv . tv_sec - chan - > router_start_time ;
* ( unsigned long * ) & x25_udp_pkt - > data = chan - > router_up_time ;
x25_udp_pkt - > cblock . length = mbox - > cmd . length = 4 ;
x25_udp_pkt - > cblock . result = 0 ;
break ;
default :
do {
memcpy ( & mbox - > cmd , & x25_udp_pkt - > cblock . command , sizeof ( TX25Cmd ) ) ;
if ( mbox - > cmd . length ) {
memcpy ( & mbox - > data ,
( char * ) x25_udp_pkt - > data ,
mbox - > cmd . length ) ;
}
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
} while ( err & & c_retry - - & & x25_error ( card , err , mbox - > cmd . command , 0 ) ) ;
if ( err = = CMD_OK | |
( err = = 1 & &
( mbox - > cmd . command = = 0x06 | |
mbox - > cmd . command = = 0x16 ) ) ) {
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_adptr_cmnd_OK ;
} else {
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_adptr_cmnd_timeout ;
}
/* copy the result back to our buffer */
memcpy ( & x25_udp_pkt - > cblock . command , & mbox - > cmd , sizeof ( TX25Cmd ) ) ;
if ( mbox - > cmd . length ) {
memcpy ( & x25_udp_pkt - > data , & mbox - > data , mbox - > cmd . length ) ;
}
break ;
} //switch
}
/* Fill UDP TTL */
x25_udp_pkt - > ip_pkt . ttl = card - > wandev . ttl ;
len = reply_udp ( card - > u . x . udp_pkt_data , mbox - > cmd . length ) ;
if ( card - > u . x . udp_pkt_src = = UDP_PKT_FRM_NETWORK ) {
err = x25_send ( card , lcn , 0 , len , card - > u . x . udp_pkt_data ) ;
if ( ! err )
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_adptr_send_passed ;
else
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_adptr_send_failed ;
} else {
/* Allocate socket buffer */
if ( ( new_skb = dev_alloc_skb ( len ) ) ! = NULL ) {
void * buf ;
/* copy data into new_skb */
buf = skb_put ( new_skb , len ) ;
memcpy ( buf , card - > u . x . udp_pkt_data , len ) ;
/* Decapsulate packet and pass it up the protocol
stack */
new_skb - > dev = dev ;
if ( chan - > common . usedby = = API )
new_skb - > protocol = htons ( X25_PROT ) ;
else
new_skb - > protocol = htons ( ETH_P_IP ) ;
new_skb - > mac . raw = new_skb - > data ;
netif_rx ( new_skb ) ;
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_passed_to_stack ;
} else {
+ + chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_no_socket ;
printk ( KERN_INFO
" %s: UDP mgmt cmnd, no socket buffers available! \n " ,
card - > devname ) ;
}
}
card - > u . x . udp_pkt_lgth = 0 ;
return 1 ;
}
/*==============================================================================
* Determine what type of UDP call it is . DRVSTATS or XPIPE8ND ?
*/
static int udp_pkt_type ( struct sk_buff * skb , sdla_t * card )
{
x25_udp_pkt_t * x25_udp_pkt = ( x25_udp_pkt_t * ) skb - > data ;
if ( ( x25_udp_pkt - > ip_pkt . protocol = = UDPMGMT_UDP_PROTOCOL ) & &
( x25_udp_pkt - > ip_pkt . ver_inet_hdr_length = = 0x45 ) & &
( x25_udp_pkt - > udp_pkt . udp_dst_port = = ntohs ( card - > wandev . udp_port ) ) & &
( x25_udp_pkt - > wp_mgmt . request_reply = = UDPMGMT_REQUEST ) ) {
if ( ! strncmp ( x25_udp_pkt - > wp_mgmt . signature ,
UDPMGMT_XPIPE_SIGNATURE , 8 ) ) {
return UDP_XPIPE_TYPE ;
} else {
printk ( KERN_INFO " %s: UDP Packet, Failed Signature ! \n " ,
card - > devname ) ;
}
}
return UDP_INVALID_TYPE ;
}
/*============================================================================
* Reply to UDP Management system .
* Return nothing .
*/
static int reply_udp ( unsigned char * data , unsigned int mbox_len )
{
unsigned short len , udp_length , temp , ip_length ;
unsigned long ip_temp ;
int even_bound = 0 ;
x25_udp_pkt_t * x25_udp_pkt = ( x25_udp_pkt_t * ) data ;
/* Set length of packet */
len = sizeof ( ip_pkt_t ) +
sizeof ( udp_pkt_t ) +
sizeof ( wp_mgmt_t ) +
sizeof ( cblock_t ) +
mbox_len ;
/* fill in UDP reply */
x25_udp_pkt - > wp_mgmt . request_reply = UDPMGMT_REPLY ;
/* fill in UDP length */
udp_length = sizeof ( udp_pkt_t ) +
sizeof ( wp_mgmt_t ) +
sizeof ( cblock_t ) +
mbox_len ;
/* put it on an even boundary */
if ( udp_length & 0x0001 ) {
udp_length + = 1 ;
len + = 1 ;
even_bound = 1 ;
}
temp = ( udp_length < < 8 ) | ( udp_length > > 8 ) ;
x25_udp_pkt - > udp_pkt . udp_length = temp ;
/* swap UDP ports */
temp = x25_udp_pkt - > udp_pkt . udp_src_port ;
x25_udp_pkt - > udp_pkt . udp_src_port =
x25_udp_pkt - > udp_pkt . udp_dst_port ;
x25_udp_pkt - > udp_pkt . udp_dst_port = temp ;
/* add UDP pseudo header */
temp = 0x1100 ;
* ( ( unsigned short * )
( x25_udp_pkt - > data + mbox_len + even_bound ) ) = temp ;
temp = ( udp_length < < 8 ) | ( udp_length > > 8 ) ;
* ( ( unsigned short * )
( x25_udp_pkt - > data + mbox_len + even_bound + 2 ) ) = temp ;
/* calculate UDP checksum */
x25_udp_pkt - > udp_pkt . udp_checksum = 0 ;
x25_udp_pkt - > udp_pkt . udp_checksum =
calc_checksum ( & data [ UDP_OFFSET ] , udp_length + UDP_OFFSET ) ;
/* fill in IP length */
ip_length = len ;
temp = ( ip_length < < 8 ) | ( ip_length > > 8 ) ;
x25_udp_pkt - > ip_pkt . total_length = temp ;
/* swap IP addresses */
ip_temp = x25_udp_pkt - > ip_pkt . ip_src_address ;
x25_udp_pkt - > ip_pkt . ip_src_address =
x25_udp_pkt - > ip_pkt . ip_dst_address ;
x25_udp_pkt - > ip_pkt . ip_dst_address = ip_temp ;
/* fill in IP checksum */
x25_udp_pkt - > ip_pkt . hdr_checksum = 0 ;
x25_udp_pkt - > ip_pkt . hdr_checksum = calc_checksum ( data , sizeof ( ip_pkt_t ) ) ;
return len ;
} /* reply_udp */
unsigned short calc_checksum ( char * data , int len )
{
unsigned short temp ;
unsigned long sum = 0 ;
int i ;
for ( i = 0 ; i < len ; i + = 2 ) {
memcpy ( & temp , & data [ i ] , 2 ) ;
sum + = ( unsigned long ) temp ;
}
while ( sum > > 16 ) {
sum = ( sum & 0xffffUL ) + ( sum > > 16 ) ;
}
temp = ( unsigned short ) sum ;
temp = ~ temp ;
if ( temp = = 0 )
temp = 0xffff ;
return temp ;
}
/*=============================================================================
* Store a UDP management packet for later processing .
*/
static int store_udp_mgmt_pkt ( int udp_type , char udp_pkt_src , sdla_t * card ,
struct net_device * dev , struct sk_buff * skb ,
int lcn )
{
int udp_pkt_stored = 0 ;
if ( ! card - > u . x . udp_pkt_lgth & & ( skb - > len < = MAX_LGTH_UDP_MGNT_PKT ) ) {
card - > u . x . udp_pkt_lgth = skb - > len ;
card - > u . x . udp_type = udp_type ;
card - > u . x . udp_pkt_src = udp_pkt_src ;
card - > u . x . udp_lcn = lcn ;
card - > u . x . udp_dev = dev ;
memcpy ( card - > u . x . udp_pkt_data , skb - > data , skb - > len ) ;
card - > u . x . timer_int_enabled | = TMR_INT_ENABLED_UDP_PKT ;
udp_pkt_stored = 1 ;
} else {
printk ( KERN_INFO " %s: ERROR: UDP packet not stored for LCN %d \n " ,
card - > devname , lcn ) ;
}
if ( udp_pkt_src = = UDP_PKT_FRM_STACK ) {
dev_kfree_skb_any ( skb ) ;
} else {
dev_kfree_skb_any ( skb ) ;
}
return ( udp_pkt_stored ) ;
}
/*=============================================================================
* Initial the ppp_private_area structure .
*/
static void init_x25_channel_struct ( x25_channel_t * chan )
{
memset ( & chan - > if_send_stat . if_send_entry , 0 , sizeof ( if_send_stat_t ) ) ;
memset ( & chan - > rx_intr_stat . rx_intr_no_socket , 0 , sizeof ( rx_intr_stat_t ) ) ;
memset ( & chan - > pipe_mgmt_stat . UDP_PIPE_mgmt_kmalloc_err , 0 , sizeof ( pipe_mgmt_stat_t ) ) ;
}
/*============================================================================
* Initialize Global Statistics
*/
static void init_global_statistics ( sdla_t * card )
{
memset ( & card - > statistics . isr_entry , 0 , sizeof ( global_stats_t ) ) ;
}
/*===============================================================
* SMP Support
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void S508_S514_lock ( sdla_t * card , unsigned long * smp_flags )
{
spin_lock_irqsave ( & card - > wandev . lock , * smp_flags ) ;
}
static void S508_S514_unlock ( sdla_t * card , unsigned long * smp_flags )
{
spin_unlock_irqrestore ( & card - > wandev . lock , * smp_flags ) ;
}
/*===============================================================
* x25_timer_routine
*
* A more efficient polling routine . Each half a second
* queue a polling task . We want to do the polling in a
* task not timer , because timer runs in interrupt time .
*
* FIXME Polling should be rethinked .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
static void x25_timer_routine ( unsigned long data )
{
sdla_t * card = ( sdla_t * ) data ;
if ( ! card - > wandev . dev ) {
printk ( KERN_INFO " %s: Stopping the X25 Poll Timer: No Dev. \n " ,
card - > devname ) ;
return ;
}
if ( card - > open_cnt ! = card - > u . x . num_of_ch ) {
printk ( KERN_INFO " %s: Stopping the X25 Poll Timer: Interface down. \n " ,
card - > devname ) ;
return ;
}
if ( test_bit ( PERI_CRIT , & card - > wandev . critical ) ) {
printk ( KERN_INFO " %s: Stopping the X25 Poll Timer: Shutting down. \n " ,
card - > devname ) ;
return ;
}
if ( ! test_and_set_bit ( POLL_CRIT , & card - > wandev . critical ) ) {
trigger_x25_poll ( card ) ;
}
card - > u . x . x25_timer . expires = jiffies + ( HZ > > 1 ) ;
add_timer ( & card - > u . x . x25_timer ) ;
return ;
}
void disable_comm_shutdown ( sdla_t * card )
{
TX25Mbox * mbox = card - > mbox ;
int err ;
/* Turn of interrutps */
mbox - > data [ 0 ] = 0 ;
if ( card - > hw . fwid = = SFID_X25_508 ) {
mbox - > data [ 1 ] = card - > hw . irq ;
mbox - > data [ 2 ] = 2 ;
mbox - > cmd . length = 3 ;
} else {
mbox - > cmd . length = 1 ;
}
mbox - > cmd . command = X25_SET_INTERRUPT_MODE ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err )
printk ( KERN_INFO " INTERRUPT OFF FAIED %x \n " , err ) ;
/* Bring down HDLC */
mbox - > cmd . command = X25_HDLC_LINK_CLOSE ;
mbox - > cmd . length = 0 ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err )
printk ( KERN_INFO " LINK CLOSED FAILED %x \n " , err ) ;
/* Brind down DTR */
mbox - > data [ 0 ] = 0 ;
mbox - > data [ 2 ] = 0 ;
mbox - > data [ 1 ] = 0x01 ;
mbox - > cmd . length = 3 ;
mbox - > cmd . command = X25_SET_GLOBAL_VARS ;
err = sdla_exec ( mbox ) ? mbox - > cmd . result : CMD_TIMEOUT ;
if ( err )
printk ( KERN_INFO " DTR DOWN FAILED %x \n " , err ) ;
}
MODULE_LICENSE ( " GPL " ) ;
/****** End *****************************************************************/