|  | /***************************************************************************** | 
|  | * 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> | 
|  | #include <linux/jiffies.h>	/* time_after() macro */ | 
|  | #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; | 
|  | } | 
|  | if (time_after(jiffies, timeout + 1*HZ)){ | 
|  | 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 *****************************************************************/ |