patch-2.4.4 linux/drivers/net/wan/sdla_x25.c
Next file: linux/drivers/net/wan/sdladrv.c
Previous file: linux/drivers/net/wan/sdla_ppp.c
Back to the patch index
Back to the overall index
- Lines: 6444
- Date:
Fri Apr 20 11:54:22 2001
- Orig file:
v2.4.3/linux/drivers/net/wan/sdla_x25.c
- Orig date:
Tue Mar 6 19:44:36 2001
diff -u --recursive --new-file v2.4.3/linux/drivers/net/wan/sdla_x25.c linux/drivers/net/wan/sdla_x25.c
@@ -1,16 +1,55 @@
/*****************************************************************************
* sdla_x25.c WANPIPE(tm) Multiprotocol WAN Link Driver. X.25 module.
*
-* Author: Gene Kozin <genek@compuserve.com>
+* Author: Nenad Corbic <ncorbic@sangoma.com>
*
-* Copyright: (c) 1995-1997 Sangoma Technologies Inc.
+* 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.
* ============================================================================
-* Mar 15, 1998 Alan Cox o 2.1.x porting
+* 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
@@ -38,21 +77,41 @@
* Jan 07, 1997 Gene Kozin Initial version.
*****************************************************************************/
+/*======================================================
+ * Includes
+ *=====================================================*/
+#include <linux/version.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/slab.h> /* kmalloc(), kfree() */
+#include <linux/ctype.h>
+#include <linux/malloc.h> /* kmalloc(), kfree() */
#include <linux/wanrouter.h> /* WAN router definitions */
#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
#include <asm/byteorder.h> /* htons(), etc. */
-#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h> /* Experimental delay */
-#define _GNUC_
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ #include <asm/uaccess.h>
+#else
+ #include <asm/segment.h>
+ #include <net/route.h>
+#endif
+
+#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
+ *=====================================================*/
-/****** Defines & Macros ****************************************************/
#define CMD_OK 0 /* normal firmware return code */
#define CMD_TIMEOUT 0xFF /* firmware command timed out */
@@ -64,27 +123,141 @@
#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
+ *===================================================*/
-/* 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)))
-/****** Data Structures *****************************************************/
-/* 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.
+/*====================================================
+ * 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 'netdevice_t'
+ * we create for each network interface to keep
+ * the rest of X.25 channel-specific data.
+ *
+ * Construct: Typedef
*/
typedef struct x25_channel
{
- /* This member must be first. */
- struct net_device *slave; /* WAN slave */
-
+ 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 lcn; /* logical channel number */
unsigned tx_pkt_size;
unsigned short protocol; /* ethertype, 0 - multiplexed */
- char svc; /* 0 - permanent, 1 - switched */
- char state; /* channel state */
char drop_sequence; /* mark sequence for dropping */
unsigned long state_tick; /* time of the last state change */
unsigned idle_timeout; /* sec, before disconnecting */
@@ -94,63 +267,144 @@
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 */
+ netdevice_t *dev; /* -> bound devce */
+
int ch_idx;
+ unsigned char enable_IPX;
+ unsigned long network_number;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
struct net_device_stats ifstats; /* interface statistics */
+#else
+ struct enet_statistics ifstats;
+#endif
+ 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]; /* ASCIIZ destination address */
- char src[17]; /* ASCIIZ source address */
- char nuser; /* number of user data bytes */
- unsigned char user[127]; /* user data */
- char nfacil; /* number of facilities */
+ 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;
- unsigned char parm;
- } facil[64]; /* facilities */
+ 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
+
-/****** Function Prototypes *************************************************/
+
+/*===============================================
+ * Private Function Prototypes
+ *==============================================*/
-/* WAN link driver entry points. These are called by the WAN router module. */
+
+/*=================================================
+ * WAN link driver entry points. These are
+ * called by the WAN router module.
+ */
static int update (wan_device_t* wandev);
-static int new_if (wan_device_t* wandev, struct net_device* dev,
+static int new_if (wan_device_t* wandev, netdevice_t* dev,
wanif_conf_t* conf);
-static int del_if (wan_device_t* wandev, struct net_device* dev);
+static int del_if (wan_device_t* wandev, netdevice_t* dev);
+static void disable_comm (sdla_t* card);
+static void disable_comm_shutdown(sdla_t *card);
-/* WANPIPE-specific entry points */
+
+
+/*=================================================
+ * WANPIPE-specific entry points
+ */
static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data);
+static void x25api_bh (netdevice_t *);
+static int x25api_bh_cleanup (netdevice_t *);
+static int bh_enqueue (netdevice_t *, struct sk_buff *);
+
-/* 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,
+/*=================================================
+ * Network device interface
+ */
+static int if_init (netdevice_t* dev);
+static int if_open (netdevice_t* dev);
+static int if_close (netdevice_t* dev);
+static int if_header (struct sk_buff* skb, netdevice_t* 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 int if_send (struct sk_buff* skb, netdevice_t* dev);
+static struct net_device_stats *if_stats (netdevice_t* dev);
-/* Interrupt handlers */
-static void wpx_isr (sdla_t* card);
-static void rx_intr (sdla_t* card);
-static void tx_intr (sdla_t* card);
-static void status_intr (sdla_t* card);
-static void event_intr (sdla_t* card);
-static void spur_intr (sdla_t* card);
+#ifdef LINUX_2_4
+static void if_tx_timeout (netdevice_t *dev);
+#endif
+
+/*=================================================
+ * 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 *);
-/* Background polling routines */
+static int tx_intr_send(sdla_t *, netdevice_t *);
+static netdevice_t * move_dev_to_next (sdla_t *, netdevice_t *);
+
+/*=================================================
+ * 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 */
+/*=================================================
+ * 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);
@@ -166,62 +420,136 @@
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 */
+/*=================================================
+ * 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 */
+
+/*=================================================
+ * Miscellaneous functions
+ */
static int connect (sdla_t* card);
static int disconnect (sdla_t* card);
-static struct net_device* get_dev_by_lcn(wan_device_t* 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, struct sk_buff* skb);
+static netdevice_t* get_dev_by_lcn(wan_device_t* wandev, unsigned lcn);
+static int chan_connect (netdevice_t* dev);
+static int chan_disc (netdevice_t* dev);
+static void set_chan_state (netdevice_t* dev, int state);
+static int chan_send (netdevice_t* , void* , unsigned, unsigned char);
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* str, int len);
-static void parse_call_info (unsigned char* str, x25_call_info_t* info);
+static unsigned int hex_to_uint (unsigned char*, int);
+static void parse_call_info (unsigned char*, x25_call_info_t*);
+static netdevice_t * find_channel(sdla_t *, unsigned);
+static void bind_lcn_to_dev (sdla_t *, netdevice_t *,unsigned);
+static void setup_for_delayed_transmit (netdevice_t*, void*, unsigned);
+
+
+/*=================================================
+ * X25 API Functions
+ */
+static int wanpipe_pull_data_in_skb (sdla_t *, netdevice_t *, struct sk_buff **);
+static void timer_intr_exec(sdla_t *, unsigned char);
+static int execute_delayed_cmd (sdla_t*, netdevice_t *, mbox_cmd_t *,char);
+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 *, netdevice_t *dev, TX25Mbox*);
+static int clear_confirm_event (sdla_t *, TX25Mbox*);
+static void send_oob_msg (sdla_t *, netdevice_t *, TX25Mbox *);
+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 *, netdevice_t *);
+static int channel_disconnect (sdla_t*, netdevice_t *);
+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, char, sdla_t*, netdevice_t *, struct sk_buff *, int);
+static unsigned short calc_checksum (char *, int);
-/* IPX functions */
-static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming);
-static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto);
+
+
+/*=================================================
+ * 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);
-/****** Global Data **********************************************************
- * Note: All data must be explicitly initialized!!!
- */
+static void S508_S514_lock(sdla_t *, unsigned long *);
+static void S508_S514_unlock(sdla_t *, unsigned long *);
-/****** Public Functions ****************************************************/
-/*============================================================================
- * X.25 Protocol Initialization routine.
+/*=================================================
+ * 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.
*
- * This routine is called by the main WANPIPE module during setup. 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.
+ * Sets up hardware options defined by user in [wanpipe#]
+ * section of wanpipe#.conf configuration file.
*
- * Return: 0 o.k.
- * < 0 failure.
+ * 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
- {
+ union{
char str[80];
TX25Config cfg;
} u;
/* Verify configuration ID */
- if (conf->config_id != WANCONFIG_X25)
- {
+ if (conf->config_id != WANCONFIG_X25){
printk(KERN_INFO "%s: invalid configuration ID %u!\n",
card->devname, conf->config_id)
;
@@ -233,23 +561,43 @@
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
- ;
- printk(KERN_INFO "%s: running X.25 firmware v%s\n",
- card->devname, 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 and don't
- * want it to fail either.
+ * necessary) since we can't rely on the adapter to do so.
*/
memset(&u.cfg, 0, sizeof(u.cfg));
u.cfg.t1 = 3;
@@ -258,7 +606,7 @@
u.cfg.hdlcWindow = 7;
u.cfg.pktWindow = 2;
u.cfg.station = 1; /* DTE */
- u.cfg.options = 0x00B0; /* disable D-bit pragmatics */
+ u.cfg.options = 0x0090; /* disable D-bit pragmatics */
u.cfg.ccittCompat = 1988;
u.cfg.t10t20 = 30;
u.cfg.t11t21 = 30;
@@ -271,71 +619,120 @@
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.baudRate = bps_to_speed_code(conf->bps);
+
+ if (conf->station != WANOPT_DTE){
u.cfg.station = 0; /* DCE mode */
}
- if (conf->interface != WANOPT_RS232 ) {
+
+ if (conf->interface != WANOPT_RS232 ){
u.cfg.hdlcOptions |= 0x80; /* V35 mode */
}
+
/* adjust MTU */
if (!conf->mtu || (conf->mtu >= 1024))
- card->wandev.mtu = 1024
- ;
+ card->wandev.mtu = 1024;
else if (conf->mtu >= 512)
- card->wandev.mtu = 512
- ;
+ card->wandev.mtu = 512;
else if (conf->mtu >= 256)
- card->wandev.mtu = 256
- ;
+ card->wandev.mtu = 256;
else if (conf->mtu >= 128)
- card->wandev.mtu = 128
- ;
- else card->wandev.mtu = 64;
+ 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(conf->u.x25.hi_pvc, 4095);
+ if (conf->u.x25.hi_pvc){
+ card->u.x.hi_pvc = min(conf->u.x25.hi_pvc, MAX_LCN_NUM);
card->u.x.lo_pvc = min(conf->u.x25.lo_pvc, card->u.x.hi_pvc);
}
- if (conf->u.x25.hi_svc)
- {
- card->u.x.hi_svc = min(conf->u.x25.hi_svc, 4095);
+
+ if (conf->u.x25.hi_svc){
+ card->u.x.hi_svc = min(conf->u.x25.hi_svc, MAX_LCN_NUM);
card->u.x.lo_svc = min(conf->u.x25.lo_svc, card->u.x.hi_svc);
}
- u.cfg.loPVC = card->u.x.lo_pvc;
- u.cfg.hiPVC = card->u.x.hi_pvc;
+
+ /* 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(conf->u.x25.hdlc_window, 7)
- ;
+ u.cfg.hdlcWindow = min(conf->u.x25.hdlc_window, 7);
if (conf->u.x25.pkt_window)
- u.cfg.pktWindow = min(conf->u.x25.pkt_window, 7)
- ;
+ u.cfg.pktWindow = min(conf->u.x25.pkt_window, 7);
+
if (conf->u.x25.t1)
- u.cfg.t1 = min(conf->u.x25.t1, 30)
- ;
- u.cfg.t2 = min(conf->u.x25.t2, 29);
- u.cfg.t4 = min(conf->u.x25.t4, 240);
+ u.cfg.t1 = min(conf->u.x25.t1, 30);
+ if (conf->u.x25.t2)
+ u.cfg.t2 = min(conf->u.x25.t2, 29);
+ if (conf->u.x25.t4)
+ u.cfg.t4 = min(conf->u.x25.t4, 240);
if (conf->u.x25.n2)
- u.cfg.n2 = min(conf->u.x25.n2, 30)
- ;
+ u.cfg.n2 = min(conf->u.x25.n2, 30);
+
+ if (conf->u.x25.t10_t20)
+ u.cfg.t10t20 = min(conf->u.x25.t10_t20,255);
+ if (conf->u.x25.t11_t21)
+ u.cfg.t11t21 = min(conf->u.x25.t11_t21,255);
+ if (conf->u.x25.t12_t22)
+ u.cfg.t12t22 = min(conf->u.x25.t12_t22,255);
+ if (conf->u.x25.t13_t23)
+ u.cfg.t13t23 = min(conf->u.x25.t13_t23,255);
+ if (conf->u.x25.t16_t26)
+ u.cfg.t16t26 = min(conf->u.x25.t16_t26, 255);
+ if (conf->u.x25.t28)
+ u.cfg.t28 = min(conf->u.x25.t28, 255);
+
+ if (conf->u.x25.r10_r20)
+ u.cfg.r10r20 = min(conf->u.x25.r10_r20,250);
+ if (conf->u.x25.r12_r22)
+ u.cfg.r12r22 = min(conf->u.x25.r12_r22,250);
+ if (conf->u.x25.r13_r23)
+ u.cfg.r13r23 = min(conf->u.x25.r13_r23,250);
+
+
if (conf->u.x25.ccitt_compat)
- u.cfg.ccittCompat = conf->u.x25.ccitt_compat
- ;
+ u.cfg.ccittCompat = conf->u.x25.ccitt_compat;
/* initialize adapter */
- if ((x25_configure(card, &u.cfg) != CMD_OK) ||
- (x25_close_hdlc(card) != CMD_OK) || /* close HDLC link */
+ 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
- ;
+ return -EIO;
/* Initialize protocol-specific fields of adapter data space */
card->wandev.bps = conf->bps;
@@ -343,202 +740,392 @@
card->wandev.clocking = conf->clocking;
card->wandev.station = conf->station;
card->isr = &wpx_isr;
- card->poll = &wpx_poll;
+ 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->wandev.enable_IPX = conf->enable_IPX;
+ 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);
+
+#ifndef LINUX_2_4
+ card->u.x.x25_poll_task.next = NULL;
+#endif
+ card->u.x.x25_poll_task.sync=0;
+ card->u.x.x25_poll_task.routine = (void*)(void*)wpx_poll;
+ card->u.x.x25_poll_task.data = 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;
- if (conf->network_number)
- card->wandev.network_number = conf->network_number;
- else
- card->wandev.network_number = 0xDEADBEEF;
return 0;
}
-/******* WAN Device Driver Entry Points *************************************/
+/*=========================================================
+ * WAN Device Driver Entry Points
+ *========================================================*/
-/*============================================================================
- * Update device status & statistics.
+/*============================================================
+ * 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 (wan_device_t* wandev)
{
- sdla_t* card;
+ 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_and_set_bit(0, (void*)&wandev->critical))
+
+ if (test_bit(SEND_CRIT, (void*)&wandev->critical))
return -EAGAIN;
+
+ if (!wandev->dev)
+ return -ENODEV;
+
card = wandev->private;
+ status = card->flags;
- x25_get_err_stats(card);
- x25_get_stats(card);
- wandev->critical = 0;
+ 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 ((jiffies-timeout) > 1*HZ){
+ card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
+ return -EAGAIN;
+ }
+ }
return 0;
}
-/*============================================================================
- * Create new logical channel.
- * This routine is called by the router when ROUTER_IFNEW IOCTL is being
- * handled.
- * o parse media- and hardware-specific configuration
- * o make sure that a new channel can be created
- * o allocate resources, if necessary
- * o prepare network device structure for registaration.
+
+/*===================================================================
+ * 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
*
- * Return: 0 o.k.
- * < 0 failure (channel will not be created)
+ * Warnings: None
+ *
+ * Return: 0 Ok
+ * <0 Failed (channel will not be created)
*/
-static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* conf)
+static int new_if (wan_device_t* wandev, netdevice_t* 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))
- {
+ if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)){
printk(KERN_INFO "%s: invalid interface name!\n",
- card->devname)
- ;
+ 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_KERNEL);
- if (chan == NULL)
- return -ENOMEM
- ;
+ 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->protocol = ETH_P_IP;
+ 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->svc = 1;
+ 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 */
- {
+ 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))
- {
- chan->lcn = lcn;
- }
- else
- {
+ 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)
- ;
+ wandev->name, lcn, chan->name);
err = -EINVAL;
}
- }
- else
- {
+ }else{
printk(KERN_ERR
"%s: invalid media address on interface %s!\n",
- wandev->name, chan->name)
- ;
+ wandev->name, chan->name);
err = -EINVAL;
}
- if (err)
- {
+
+ 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);
+#ifdef LINUX_2_4
+ strcpy(dev->name,chan->name);
+#else
+ dev->name = (char *)kmalloc(strlen(chan->name) + 2, GFP_KERNEL);
+ if(dev->name == NULL)
+ {
+ kfree(chan);
+ dev->priv = NULL;
+ return -ENOMEM;
+ }
+ sprintf(dev->name, "%s", chan->name);
+#endif
dev->init = &if_init;
- dev->priv = chan;
+
+ init_x25_channel_struct(chan);
+
return 0;
}
-/*============================================================================
- * Delete logical channel.
+/*===================================================================
+ * 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.
*/
-static int del_if (wan_device_t* wandev, struct net_device* dev)
+
+//FIXME Del IF Should be taken out now.
+
+static int del_if (wan_device_t* wandev, netdevice_t* dev)
{
- if (dev->priv)
- {
- kfree(dev->priv);
- dev->priv = NULL;
- }
return 0;
}
-/****** WANPIPE-specific entry points ***************************************/
-/*============================================================================
- * Execute adapter interface command.
- */
+/*============================================================
+ * 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)
{
- TX25Mbox* mbox = card->mbox;
- int retry = MAX_CMD_RETRY;
- int err, len;
- TX25Cmd cmd;
-
- if(copy_from_user((void*)&cmd, u_cmd, sizeof(cmd)))
- return -EFAULT;
-
- /* execute command */
+ return 0;
+}
- do
- {
- memcpy(&mbox->cmd, &cmd, sizeof(cmd));
- if (cmd.length)
- {
- if(copy_from_user((void*)&mbox->data, u_data, cmd.length))
- return-EFAULT;
- }
- if (sdla_exec(mbox))
- err = mbox->cmd.result
- ;
- else return -EIO;
- }
- while (err && retry-- && x25_error(card, err, cmd.command, cmd.lcn));
+/*============================================================
+ * 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.
+ *===========================================================*/
- /* return result */
- if(copy_to_user(u_cmd, (void*)&mbox->cmd, sizeof(TX25Cmd)))
- return -EFAULT;
- len = mbox->cmd.length;
- if (len && u_data && copy_to_user(u_data, (void*)&mbox->data, len))
- return -EFAULT;
- return 0;
+static void disable_comm(sdla_t* card)
+{
+ disable_comm_shutdown(card);
+ del_timer(&card->u.x.x25_timer);
+ return;
}
-/****** Network Device Interface ********************************************/
-/*============================================================================
- * Initialize Linux network interface.
+/*============================================================
+ * Network Device Interface
+ *===========================================================*/
+
+/*===================================================================
+ * Name: if_init(), Netowrk Interface Initialization
*
- * This routine is called only once for each interface, during Linux network
- * interface registration. Returning anything but zero will fail interface
- * registration.
+ * 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)
+static int if_init (netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
wan_device_t* wandev = &card->wandev;
+#ifdef LINUX_2_0
+ int i;
+#endif
/* Initialize device driver entry points */
dev->open = &if_open;
@@ -548,127 +1135,266 @@
dev->hard_start_xmit = &if_send;
dev->get_stats = &if_stats;
+#ifdef LINUX_2_4
+ dev->tx_timeout = &if_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
/* Initialize media-specific parameters */
- dev->type = 30; /* ARP h/w type */
- dev->mtu = X25_CHAN_MTU;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ dev->type = ARPHRD_PPP; /* ARP h/w type */
+#else
+ dev->family = AF_INET; /* address family */
+ dev->type = ARPHRD_PPP; /* no x25 type */
+#endif
+ 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->svc)
- *(unsigned short*)dev->dev_addr = htons(chan->lcn);
-
+
+ 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 = dev->mem_end + wandev->msize - 1;
+ dev->mem_end = wandev->maddr + wandev->msize - 1;
/* Set transmit buffer queue length */
- dev->tx_queue_len = 10;
+ dev->tx_queue_len = 100;
/* Initialize socket buffers */
-
- dev_init_buffers(dev);
+#if !defined(LINUX_2_1) && !defined(LINUX_2_4)
+ for (i = 0; i < DEV_NUMBUFFS; ++i)
+ skb_queue_head_init(&dev->buffs[i]);
+#endif
+ /* FIXME Why are we doing this */
set_chan_state(dev, WAN_DISCONNECTED);
return 0;
}
-/*============================================================================
- * Open network interface.
- * o prevent module from unloading by incrementing use count
- * o if link is disconnected then initiate connection
+
+/*===================================================================
+ * 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())
*
- * Return 0 if O.k. or errno.
+ * Assumptions: None
+ *
+ * Warnings: None
+ *
+ * Return: 0 Ok
+ * <0 Failur: Interface will not come up.
*/
-static int if_open (struct net_device* dev)
+
+static int if_open (netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
+ struct timeval tv;
+ unsigned long smp_flags;
+
+ if (is_dev_running(dev))
+ return -EBUSY;
+
+ chan->tq_working = 0;
+
+ /* Initialize the task queue */
+#ifndef LINUX_2_4
+ chan->common.wanpipe_task.next = NULL;
+#endif
+ chan->common.wanpipe_task.sync = 0;
+ chan->common.wanpipe_task.routine = (void *)(void *)x25api_bh;
+ chan->common.wanpipe_task.data = 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);
- if (dev->start)
- return -EBUSY; /* only one open is allowed */
+ /* Increment the number of interfaces */
+ ++card->u.x.no_dev;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- return -EAGAIN;
+ 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);
+
+ del_timer(&card->u.x.x25_timer);
+ card->u.x.x25_timer.expires=jiffies+HZ;
+ add_timer(&card->u.x.x25_timer);
+ }
+ }
+ /* Device is not up untill the we are in connected state */
+ do_gettimeofday( &tv );
+ chan->router_start_time = tv.tv_sec;
+
+#ifdef LINUX_2_4
+ netif_start_queue(dev);
+#else
dev->interrupt = 0;
dev->tbusy = 0;
dev->start = 1;
- wanpipe_open(card);
-
- /* If this is the first open, initiate physical connection */
- if (card->open_cnt == 1)
- connect(card);
- card->wandev.critical = 0;
+#endif
return 0;
}
-/*============================================================================
- * Close network interface.
- * o reset flags.
- * o if there's no more open channels then disconnect physical link.
+/*===================================================================
+ * 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)
+static int if_close (netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
-
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- return -EAGAIN;
-
- dev->start = 0;
- if ((chan->state == WAN_CONNECTED) || (chan->state == WAN_CONNECTING))
+ unsigned long smp_flags;
+
+ stop_net_queue(dev);
+#ifndef LINUX_2_4
+ dev->start=0;
+#endif
+
+ 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);
- /* If this is the last close, disconnect physical link */
- if (!card->open_cnt)
- disconnect(card);
-
- card->wandev.critical = 0;
+ 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){
+ wan_dev_kfree_skb(skb, FREE_READ);
+ }
+ }
+ 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.
+/*======================================================================
+ * 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.
+ * 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,
+ * Return: media header length.
+ *======================================================================*/
+
+static int if_header (struct sk_buff* skb, netdevice_t* 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 = type;
- if (!chan->protocol)
- {
- hdr_len = wanrouter_encapsulate(skb, dev);
- if (hdr_len < 0)
- {
+
+ skb->protocol = htons(type);
+ if (!chan->protocol){
+ hdr_len = wanrouter_encapsulate(skb, dev, type);
+ if (hdr_len < 0){
hdr_len = 0;
- skb->protocol = 0;
+ skb->protocol = htons(0);
}
}
return hdr_len;
}
-/*============================================================================
- * Re-build media header.
+/*===============================================================
+ * Re-build media header.
*
- * Return: 1 physical address resolved.
- * 0 physical address not resolved
- */
-
+ * 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;
+ netdevice_t *dev = skb->dev;
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
@@ -677,185 +1403,294 @@
return 1;
}
+
+#ifdef LINUX_2_4
/*============================================================================
- * 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
+ * Handle transmit timeout event from netif watchdog
+ */
+static void if_tx_timeout (netdevice_t *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);
+}
+#endif
+
+
+/*=========================================================================
+ * 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)
+ * 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.
- */
+ * 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)
+static int if_send (struct sk_buff* skb, netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
- struct net_device *dev2;
TX25Status* status = card->flags;
- unsigned long host_cpu_flags;
+ int udp_type;
+ unsigned long smp_flags=0;
- if (dev->tbusy)
- {
- ++chan->ifstats.rx_dropped;
- if ((jiffies - chan->tick_counter) < (5*HZ))
- {
- return dev->tbusy;
+ ++chan->if_send_stat.if_send_entry;
+
+#ifdef LINUX_2_4
+ netif_stop_queue(dev);
+#endif
+
+ /* No need to check frame length, since socket code
+ * will perform the check for us */
+
+#ifndef LINUX_2_4
+ if (dev->tbusy){
+ netdevice_t *dev2;
+
+ ++chan->if_send_stat.if_send_tbusy;
+ if ((jiffies - chan->tick_counter) < (5*HZ)){
+ return 1;
}
printk(KERN_INFO "%s: Transmit time out %s!\n",
- card->devname, dev->name);
+ card->devname, dev->name);
+
+ for( dev2 = card->wandev.dev; dev2;
+ dev2 = *((netdevice_t**)dev2->priv)){
- dev2 = card->wandev.dev;
- while (dev2) {
- x25_channel_t *chan2 = dev2->priv;
dev2->tbusy = 0;
- dev2 = chan2->slave;
}
+ ++chan->if_send_stat.if_send_tbusy_timeout;
}
+#endif
+
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);
- disable_irq(card->hw.irq);
- ++card->irq_dis_if_send_count;
+ if(udp_type != UDP_INVALID_TYPE) {
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- {
- printk(KERN_INFO "Hit critical in if_send()!\n");
- if (card->wandev.critical == CRITICAL_IN_ISR)
- {
- card->wandev.enable_tx_int = 1;
- dev->tbusy = 1;
-
- save_flags(host_cpu_flags);
- cli();
- if ((!(--card->irq_dis_if_send_count)) &&
- (!card->irq_dis_poll_count))
- enable_irq(card->hw.irq);
- restore_flags(host_cpu_flags);
-
- return dev->tbusy;
- }
- dev_kfree_skb(skb);
-
- save_flags(host_cpu_flags);
- cli();
- if ((!(--card->irq_dis_if_send_count)) &&
- (!card->irq_dis_poll_count))
- enable_irq(card->hw.irq);
- restore_flags(host_cpu_flags);
+ if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, dev, skb,
+ chan->common.lcn)) {
- return dev->tbusy;
+ status->imask |= INTR_ON_TIMER;
+ if (udp_type == UDP_XPIPE_TYPE){
+ chan->if_send_stat.if_send_PIPE_request++;
+ }
+ }
+ start_net_queue(dev);
+ clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+ S508_S514_unlock(card, &smp_flags);
+ return 0;
}
- /* Below is only until we have per-channel IPX going.... */
- if(!(chan->svc))
- chan->protocol = skb->protocol;
+ 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{
+ stop_net_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)
+ if (card->wandev.state != WAN_CONNECTED){
++chan->ifstats.tx_dropped;
-
- /* Below is only until we have per-channel IPX going.... */
- else if ( (chan->svc) && (chan->protocol && (chan->protocol != skb->protocol)))
- {
+ ++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",
- card->devname, skb->protocol, dev->name);
+ chan->name, htons(skb->protocol), dev->name);
+
+ printk(KERN_INFO "PROTO %Xn", htons(chan->protocol));
++chan->ifstats.tx_errors;
- }
- else switch (chan->state)
- {
+ ++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_connect(dev) != 0)
- {
+ 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 == ETH_P_IPX )
- {
- if(card->wandev.enable_IPX)
- {
+ if( skb->protocol == htons(ETH_P_IPX)) {
+ if(chan->enable_IPX) {
switch_net_numbers( skb->data,
- card->wandev.network_number, 0);
- }
- else
- {
+ chan->network_number, 0);
+ } else {
++card->wandev.stats.tx_dropped;
++chan->ifstats.tx_dropped;
- goto tx_done;
+ ++chan->if_send_stat.if_send_protocol_error;
+ goto if_send_crit_exit;
}
}
- dev->trans_start = jiffies;
- if(chan_send(dev, skb))
- {
- dev->tbusy = 1;
- status->imask |= 0x2;
- }
+ /* 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;
}
-tx_done:
- if (!dev->tbusy)
- dev_kfree_skb(skb);
-
- card->wandev.critical = 0;
- save_flags(host_cpu_flags);
- cli();
- if ((!(--card->irq_dis_if_send_count)) && (!card->irq_dis_poll_count))
- enable_irq(card->hw.irq);
- restore_flags(host_cpu_flags);
- return dev->tbusy;
+
+
+if_send_crit_exit:
+
+ wan_dev_kfree_skb(skb, FREE_WRITE);
+
+ start_net_queue(dev);
+ clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
+ S508_S514_unlock(card, &smp_flags);
+ return 0;
}
/*============================================================================
- * Get Ethernet-style interface statistics.
- * Return a pointer to struct net_device_stats
- */
-
-static struct net_device_stats* if_stats (struct net_device* dev)
+ * Setup so that a frame can be transmitted on the occurence of a transmit
+ * interrupt.
+ *===========================================================================*/
+
+static void setup_for_delayed_transmit (netdevice_t* dev, void* buf,
+ unsigned len)
{
- x25_channel_t* chan = dev->priv;
- if(chan==NULL)
+ 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 (netdevice_t* dev)
+{
+ x25_channel_t *chan = dev->priv;
+
+ if(chan == NULL)
return NULL;
+
return &chan->ifstats;
}
-/****** Interrupt Handlers **************************************************/
-/*============================================================================
+/*
+ * Interrupt Handlers
+ */
+
+/*
* X.25 Interrupt Service Routine.
*/
-
+
static void wpx_isr (sdla_t* card)
{
TX25Status* status = card->flags;
- struct net_device *dev;
- unsigned long host_cpu_flags;
card->in_isr = 1;
- card->buff_int_mode_unbusy = 0;
+ ++card->statistics.isr_entry;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- {
+ 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 "wpx_isr: %s, wandev.critical set to 0x%02X, int type = 0x%02X\n", card->devname, card->wandev.critical, status->iflags);
+ 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;
}
@@ -863,92 +1698,62 @@
* If the if_send routine is called with this flag set it will set
* the enable transmit flag to 1. (for a delayed interrupt)
*/
- card->wandev.critical = CRITICAL_IN_ISR;
+ switch (status->iflags){
- switch (status->iflags)
- {
- case 0x01: /* receive interrupt */
+ case RX_INTR_PENDING: /* receive interrupt */
rx_intr(card);
break;
- case 0x02: /* transmit interrupt */
+ case TX_INTR_PENDING: /* transmit interrupt */
tx_intr(card);
- card->buff_int_mode_unbusy = 1;
- status->imask &= ~0x2;
break;
- case 0x04: /* modem status interrupt */
+ case MODEM_INTR_PENDING: /* modem status interrupt */
status_intr(card);
break;
- case 0x10: /* network event interrupt */
+ 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->wandev.critical = CRITICAL_INTR_HANDLED;
- if( card->wandev.enable_tx_int)
- {
- card->wandev.enable_tx_int = 0;
- status->imask |= 0x2;
- }
- save_flags(host_cpu_flags);
- cli();
+
card->in_isr = 0;
status->iflags = 0; /* clear interrupt condition */
- card->wandev.critical = 0;
- restore_flags(host_cpu_flags);
-
- if(card->buff_int_mode_unbusy)
- {
- x25_channel_t *chan;
-
- dev = card->wandev.dev;
- while (dev) {
- chan = dev->priv;
- if(chan->devtint) {
- mark_bh(NET_BH);
- return;
- }
-
- dev = chan->slave;
- }
- }
}
-/*============================================================================
- * 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.
+/*
+ * 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; /* logical channel number */
- unsigned len = rxmb->cmd.length; /* packet length */
- unsigned qdm = rxmb->cmd.qdm; /* Q,D and M bits */
- wan_device_t* wandev = &card->wandev;
- struct net_device* dev = get_dev_by_lcn(wandev, lcn);
+ unsigned lcn = rxmb->cmd.lcn;
+ netdevice_t* dev = find_channel(card,lcn);
x25_channel_t* chan;
- struct sk_buff* skb;
- void* bufptr;
+ struct sk_buff* skb=NULL;
- if (dev == NULL)
- {
+ if (dev == NULL){
/* Invalid channel, discard packet */
printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
card->devname, lcn);
@@ -957,171 +1762,565 @@
chan = dev->priv;
chan->i_timeout_sofar = jiffies;
- if (chan->drop_sequence)
- {
- if (!(qdm & 0x01)) chan->drop_sequence = 0;
+
+
+ /* 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;
}
- skb = chan->rx_skb;
- if (skb == NULL)
- {
- /* Allocate new socket buffer */
- int bufsize = (qdm & 0x01) ? dev->mtu : len;
+ dev->last_rx = jiffies; /* timestamp */
- skb = dev_alloc_skb(bufsize + dev->hard_header_len);
- if (skb == NULL)
- {
- printk(KERN_INFO "%s: no socket buffers available!\n",
- card->devname);
- chan->drop_sequence = 1; /* set flag */
+
+ /* ------------ 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;
+ wan_dev_kfree_skb(skb, FREE_READ);
return;
- }
- skb->dev = dev;
- skb->protocol = htons(chan->protocol);
- chan->rx_skb = skb;
- }
+ }
- if (skb_tailroom(skb) < len)
- {
- /* No room for the packet. Call off the whole thing! */
- dev_kfree_skb(skb);
- chan->rx_skb = NULL;
- if (qdm & 0x01) chan->drop_sequence = 1;
+ ++chan->ifstats.rx_packets;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ chan->ifstats.rx_bytes += skb->len;
+#endif
+
- printk(KERN_INFO "%s: unexpectedly long packet sequence "
- "on interface %s!\n", card->devname, dev->name);
- ++chan->ifstats.rx_length_errors;
+ chan->rx_skb = NULL;
+ if (!test_and_set_bit(0, &chan->tq_working)){
+ wanpipe_queue_tq(&chan->common.wanpipe_task);
+ wanpipe_mark_bh();
+ }
return;
}
- /* Append packet to the socket buffer */
- bufptr = skb_put(skb, len);
- memcpy(bufptr, rxmb->data, len);
-
- if (qdm & 0x01)
- return; /* more data is coming */
-
- dev->last_rx = jiffies; /* timestamp */
- chan->rx_skb = NULL; /* dequeue packet */
+ /* ------------- 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))
- {
+ if (!skb->protocol && !wanrouter_type_trans(skb, dev)){
/* can't decapsulate packet */
- dev_kfree_skb(skb);
+ wan_dev_kfree_skb(skb, FREE_READ);
++chan->ifstats.rx_errors;
- }
- else
- {
- if( handle_IPXWAN(skb->data, card->devname, card->wandev.enable_IPX, card->wandev.network_number, skb->protocol))
- {
- if( card->wandev.enable_IPX )
- {
- if(chan_send(dev, skb))
- {
+ ++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{
+ wan_dev_kfree_skb(skb, FREE_WRITE);
+ ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
}
- else
- {
- dev_kfree_skb(skb);
- }
+ }else{
+ /* increment IPX packet dropped statistic */
+ ++chan->ifstats.rx_dropped;
+ ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
}
- else
- {
- /* FIXME: increment IPX packet dropped statistic */
- }
- }
- else
- {
- netif_rx(skb);
- ++chan->ifstats.rx_packets;
+ }else{
+ skb->mac.raw = skb->data;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
chan->ifstats.rx_bytes += skb->len;
+#endif
+ ++chan->ifstats.rx_packets;
+ ++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack;
+ netif_rx(skb);
}
}
+
+ return;
}
-/*============================================================================
- * Transmit interrupt handler.
- * o Release socket buffer
- * o Clear 'tbusy' flag
- */
-static void tx_intr (sdla_t* card)
+static int wanpipe_pull_data_in_skb (sdla_t *card, netdevice_t *dev, struct sk_buff **skb)
{
- struct net_device *dev;
- x25_channel_t *chan;
+ 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);
+ }
- /* unbusy all devices and then dev_tint(); */
- dev = card->wandev.dev;
- while (dev) {
- chan->devtint = dev->tbusy;
- dev->tbusy = 0;
+ if (new_skb == NULL){
+ int bufsize;
- dev = chan->slave;
+ 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! */
+ wan_dev_kfree_skb(new_skb, FREE_READ);
+ if (chan->common.usedby == WANPIPE){
+ chan->rx_skb = NULL;
+ if (qdm & 0x01){
+ chan->drop_sequence = 1;
+ }
+ }
-/*============================================================================
- * Modem status interrupt handler.
- */
-static void status_intr (sdla_t* card)
-{
-}
+ printk(KERN_INFO "%s: unexpectedly long packet sequence "
+ "on interface %s!\n", card->devname, dev->name);
+ ++chan->ifstats.rx_length_errors;
+ return 1;
+ }
-/*============================================================================
- * Network event interrupt handler.
- */
-static void event_intr (sdla_t* card)
-{
-}
+ bufptr = skb_put(new_skb,len);
-/*============================================================================
- * Spurious interrupt handler.
- * o print a warning
- * o
- * If number of spurious interrupts exceeded some limit, then ???
- */
-static void spur_intr (sdla_t* card)
-{
- printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
-}
-/****** Background Polling Routines ****************************************/
+ 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;
-/*============================================================================
- * Main polling routine.
- * This routine is repeatedly called by the WANPIPE 'thread' to allow for
- * time-dependent housekeeping work.
+ return 0;
+}
+
+/*===============================================================
+ * tx_intr
+ *
+ * Transmit interrupt handler.
+ * For each dev, check that there is something to send.
+ * If data available, transmit.
*
- * Notes:
- * 1. This routine may be called on interrupt context with all interrupts
- * enabled. Beware!
- */
+ *===============================================================*/
-static void wpx_poll (sdla_t* card)
+static void tx_intr (sdla_t* card)
{
- unsigned long host_cpu_flags;
-
- disable_irq(card->hw.irq);
- ++card->irq_dis_poll_count;
+ netdevice_t *dev;
+ TX25Status* status = card->flags;
+ unsigned char more_to_tx=0;
+ x25_channel_t *chan=NULL;
+ int i=0;
- if (test_and_set_bit(0, (void*)&card->wandev.critical))
- {
- printk(KERN_INFO "%s: critical in polling!\n",card->devname);
- save_flags(host_cpu_flags);
- cli();
- if ((!card->irq_dis_if_send_count) &&
- (!(--card->irq_dis_poll_count)))
- enable_irq(card->hw.irq);
- restore_flags(host_cpu_flags);
- return;
+ if (card->u.x.tx_dev == NULL){
+ card->u.x.tx_dev = card->wandev.dev;
}
- switch(card->wandev.state)
- {
+ 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 (is_queue_stopped(dev)){
+ if (chan->common.usedby == API){
+ start_net_queue(dev);
+ wakeup_sk_bh(dev);
+ }else{
+ wake_net_dev(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
+ *
+ *
+ *===============================================================*/
+
+
+netdevice_t * move_dev_to_next (sdla_t *card, netdevice_t *dev)
+{
+ if (card->u.x.no_dev != 1){
+ if (*((netdevice_t**)dev->priv) == NULL){
+ return card->wandev.dev;
+ }else{
+ return *((netdevice_t**)dev->priv);
+ }
+ }
+ return dev;
+}
+
+/*===============================================================
+ * tx_intr_send
+ *
+ *
+ *===============================================================*/
+
+static int tx_intr_send(sdla_t *card, netdevice_t *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 (is_queue_stopped(dev)){
+ if (chan->common.usedby == API){
+ start_net_queue(dev);
+ wakeup_sk_bh(dev);
+ }else{
+ wake_net_dev(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) {
+
+ netdevice_t *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){
+ netdevice_t *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;
+ netdevice_t *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 = *((netdevice_t**)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 'thead' 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;
@@ -1132,104 +2331,172 @@
case WAN_DISCONNECTED:
poll_disconnected(card);
+ break;
}
- card->wandev.critical = 0;
- save_flags(host_cpu_flags);
- cli();
- if ((!card->irq_dis_if_send_count) && (!(--card->irq_dis_poll_count)))
- enable_irq(card->hw.irq);
- restore_flags(host_cpu_flags);
+
+wpx_poll_exit:
+ clear_bit(POLL_CRIT,&card->wandev.critical);
+ return;
}
-/*============================================================================
- * Handle physical link establishment phase.
- * o if connection timed out, disconnect the link.
- */
+static void trigger_x25_poll(sdla_t *card)
+{
+#ifdef LINUX_2_4
+ schedule_task(&card->u.x.x25_poll_task);
+#else
+ queue_task(&card->u.x.x25_poll_task, &tq_scheduler);
+#endif
+}
+
+/*====================================================================
+ * Handle physical link establishment phase.
+ * o if connection timed out, disconnect the link.
+ *===================================================================*/
+
static void poll_connecting (sdla_t* card)
{
- TX25Status* status = card->flags;
+ 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);
- if (status->gflags & X25_HDLC_ABM)
- {
- wanpipe_set_state(card, WAN_CONNECTED);
- x25_set_intr_mode(card, 0x83); /* enable Rx interrupts */
- status->imask &= ~0x2; /* mask Tx interrupts */
}
- else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT)
- disconnect(card);
}
-/*============================================================================
- * Handle physical link disconnected phase.
- * o if hold-down timeout has expired and there are open interfaces, connect
- * link.
- */
+/*====================================================================
+ * 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)
{
- if (card->open_cnt && ((jiffies - card->state_tick) > HOLD_DOWN_TIME))
- connect(card);
+ netdevice_t *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.
- */
+/*====================================================================
+ * 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;
-
- /* Fetch X.25 asynchronous events */
- x25_fetch_events(card);
+ netdevice_t* dev;
+ TX25Status* status = card->flags;
- dev = card->wandev.dev;
- while (dev) {
+ for (dev = card->wandev.dev; dev; dev = *((netdevice_t**)dev->priv)){
x25_channel_t* chan = dev->priv;
- struct sk_buff* skb = chan->tx_skb;
-
- /* If there is a packet queued for transmission then kick
- * the channel's send routine. When transmission is complete
- * or if error has occurred, release socket buffer and reset
- * 'tbusy' flag.
- */
- if (skb && (chan_send(dev, skb) == 0))
- {
- chan->tx_skb = NULL;
- dev->tbusy = 0;
- dev_kfree_skb(skb);
- }
/* If SVC has been idle long enough, close virtual circuit */
-
- if(( chan->svc )&&( chan->state == WAN_CONNECTED ))
- {
- if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout )
- {
+ 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 */
- printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n",card->devname,chan->name,chan->lcn);
- chan->i_timeout_sofar = jiffies;
- chan_disc(dev);
+ card->u.x.poll_device=dev;
+ timer_intr_exec (card, TMR_INT_ENABLED_POLL_ACTIVE);
}
}
- dev = chan->slave;
+#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;
+ }
}
}
-/****** 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 dealt 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.
- */
+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.
+ *===================================================================*/
-/*============================================================================
- * 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;
@@ -1247,15 +2514,16 @@
if (!err && str)
{
int len = mbox->cmd.length;
+
memcpy(str, mbox->data, len);
str[len] = '\0';
}
return err;
}
-/*============================================================================
- * Configure adapter.
- */
+/*====================================================================
+ * Configure adapter.
+ *===================================================================*/
static int x25_configure (sdla_t* card, TX25Config* conf)
{
@@ -1263,8 +2531,7 @@
int retry = MAX_CMD_RETRY;
int err;
- do
- {
+ do{
memset(&mbox->cmd, 0, sizeof(TX25Cmd));
memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
mbox->cmd.length = sizeof(TX25Config);
@@ -1274,9 +2541,51 @@
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;
@@ -1289,7 +2598,7 @@
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;
@@ -1302,9 +2611,10 @@
return err;
}
-/*============================================================================
- * Get protocol statistics.
- */
+/*====================================================================
+ * Get protocol statistics.
+ *===================================================================*/
+
static int x25_get_stats (sdla_t* card)
{
TX25Mbox* mbox = card->mbox;
@@ -1316,21 +2626,22 @@
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));
+ } 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->rxData;
+ card->wandev.stats.tx_packets = stats->txData;
}
return err;
}
-/*============================================================================
- * Close HDLC link.
- */
+/*====================================================================
+ * Close HDLC link.
+ *===================================================================*/
+
static int x25_close_hdlc (sdla_t* card)
{
TX25Mbox* mbox = card->mbox;
@@ -1343,13 +2654,15 @@
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.
- */
+
+/*====================================================================
+ * Open HDLC link.
+ *===================================================================*/
+
static int x25_open_hdlc (sdla_t* card)
{
TX25Mbox* mbox = card->mbox;
@@ -1362,13 +2675,13 @@
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;
@@ -1385,9 +2698,10 @@
return err;
}
-/*============================================================================
+/*====================================================================
* Set (raise/drop) DTR.
- */
+ *===================================================================*/
+
static int x25_set_dtr (sdla_t* card, int dtr)
{
TX25Mbox* mbox = card->mbox;
@@ -1404,13 +2718,14 @@
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.
- */
+/*====================================================================
+ * Set interrupt mode.
+ *===================================================================*/
+
static int x25_set_intr_mode (sdla_t* card, int mode)
{
TX25Mbox* mbox = card->mbox;
@@ -1421,30 +2736,32 @@
{
memset(&mbox->cmd, 0, sizeof(TX25Cmd));
mbox->data[0] = mode;
- if (card->hw.fwid == SFID_X25_508)
- {
+ if (card->hw.fwid == SFID_X25_508){
mbox->data[1] = card->hw.irq;
- mbox->cmd.length = 2;
+ mbox->data[2] = 2;
+ mbox->cmd.length = 3;
+ }else {
+ mbox->cmd.length = 1;
}
- 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)) ;
+ } while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0));
+
return err;
}
-/*============================================================================
- * Read X.25 channel configuration.
- */
+/*====================================================================
+ * 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->lcn;
+ int lcn = chan->common.lcn;
int err;
- do
- {
+ do{
memset(&mbox->cmd, 0, sizeof(TX25Cmd));
mbox->cmd.lcn = lcn;
mbox->cmd.command = X25_READ_CHANNEL_CONFIG;
@@ -1456,19 +2773,25 @@
TX25Status* status = card->flags;
/* calculate an offset into the array of status bytes */
- if (card->u.x.hi_svc <= 255)
+ if (card->u.x.hi_svc <= X25_MAX_CHAN){
+
chan->ch_idx = lcn - 1;
- else
- {
+
+ }else{
int offset;
- switch (mbox->data[0] && 0x1F)
+ /* 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:
+ case 0x01:
offset = status->pvc_map; break;
- case 0x03:
+ case 0x03:
offset = status->icc_map; break;
- case 0x07:
+ case 0x07:
offset = status->twc_map; break;
case 0x0B:
offset = status->ogc_map; break;
@@ -1481,37 +2804,38 @@
/* get actual transmit packet size on this channel */
switch(mbox->data[1] & 0x38)
{
- case 0x00:
- chan->tx_pkt_size = 16;
+ case 0x00:
+ chan->tx_pkt_size = 16;
break;
- case 0x08:
- chan->tx_pkt_size = 32;
+ case 0x08:
+ chan->tx_pkt_size = 32;
break;
- case 0x10:
- chan->tx_pkt_size = 64;
+ case 0x10:
+ chan->tx_pkt_size = 64;
break;
- case 0x18:
- chan->tx_pkt_size = 128;
+ case 0x18:
+ chan->tx_pkt_size = 128;
break;
- case 0x20:
- chan->tx_pkt_size = 256;
+ case 0x20:
+ chan->tx_pkt_size = 256;
break;
- case 0x28:
- chan->tx_pkt_size = 512;
+ case 0x28:
+ chan->tx_pkt_size = 512;
break;
- case 0x30:
- chan->tx_pkt_size = 1024;
+ case 0x30:
+ chan->tx_pkt_size = 1024;
break;
}
- printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
- card->devname, lcn, chan->tx_pkt_size);
+ 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.
- */
+/*====================================================================
+ * Place X.25 call.
+ *====================================================================*/
static int x25_place_call (sdla_t* card, x25_channel_t* chan)
{
@@ -1520,7 +2844,15 @@
int err;
char str[64];
- sprintf(str, "-d%s -uCC", chan->addr);
+
+ 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));
@@ -1530,17 +2862,15 @@
err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
} while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0));
- if (!err)
- {
- chan->lcn = mbox->cmd.lcn;
- chan->protocol = ETH_P_IP;
+ if (!err){
+ bind_lcn_to_dev (card, chan->dev, mbox->cmd.lcn);
}
return err;
}
-/*============================================================================
- * Accept X.25 call.
- */
+/*====================================================================
+ * Accept X.25 call.
+ *====================================================================*/
static int x25_accept_call (sdla_t* card, int lcn, int qdm)
{
@@ -1556,13 +2886,14 @@
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.
- */
+/*====================================================================
+ * Clear X.25 call.
+ *====================================================================*/
+
static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn)
{
TX25Mbox* mbox = card->mbox;
@@ -1578,35 +2909,59 @@
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.
- */
+/*====================================================================
+ * 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;
- mbox->cmd.qdm = qdm;
- mbox->cmd.command = X25_WRITE;
+
+ 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, X25_WRITE, lcn));
+ } 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.
- */
+/*====================================================================
+ * Fetch X.25 asynchronous events.
+ *===================================================================*/
+
static int x25_fetch_events (sdla_t* card)
{
TX25Status* status = card->flags;
@@ -1618,25 +2973,26 @@
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);
+ 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.
- */
+/*====================================================================
+ * 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;
@@ -1651,138 +3007,212 @@
return 0;
}
memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen);
- switch (err)
- {
- case 0x40: /* X.25 asynchronous packet was received */
- mb->data[dlen] = '\0';
- switch (mb->cmd.pktType & 0x7F)
- {
- case 0x30: /* incoming call */
- retry = incoming_call(card, cmd, lcn, mb);
- break;
-
- case 0x31: /* connected */
- retry = call_accepted(card, cmd, lcn, mb);
- break;
+ switch (err){
- case 0x02: /* call clear request */
- retry = call_cleared(card, cmd, lcn, mb);
- break;
+ case X25RES_ASYNC_PACKET: /* X.25 asynchronous packet was received */
- case 0x04: /* 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);
- break;
+ mb->data[dlen] = '\0';
- case 0x08: /* restart request */
- retry = restart_event(card, cmd, lcn, mb);
- break;
+ switch (mb->cmd.pktType & 0x7F){
- default:
- 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);
- }
+ case ASE_CALL_RQST: /* incoming call */
+ retry = incoming_call(card, cmd, lcn, mb);
break;
- case 0x41: /* X.25 protocol violation indication */
- 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);
+ case ASE_CALL_ACCEPTED: /* connected */
+ retry = call_accepted(card, cmd, lcn, mb);
break;
- case 0x42: /* X.25 timeout */
- retry = timeout_event(card, cmd, lcn, mb);
+ case ASE_CLEAR_RQST: /* call clear request */
+ retry = call_cleared(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);
+ 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 0x08: /* modem failure */
- printk(KERN_INFO "%s: modem failure!\n", card->devname);
+ case ASE_RESTART_RQST: /* restart request */
+ retry = restart_event(card, cmd, lcn, mb);
break;
- case 0x09: /* N2 retry limit */
- printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
- card->devname);
+ 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 handeled 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 0x06: /* unnumbered frame was received while in ABM */
- printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
- card->devname, mb->data[0]);
+ case PVE_RESTART_RQST: /* Restart request */
+ retry = restart_event(card, cmd, lcn, mb);
break;
- case CMD_TIMEOUT:
- printk(KERN_ERR "%s: command 0x%02X timed out!\n",
- card->devname, cmd);
- retry = 0; /* abort command */
+ 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;
- default:
- printk(KERN_INFO "%s: command 0x%02X returned 0x%02X!\n",
- card->devname, cmd, err);
- retry = 0; /* abort command */
+ 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.
- */
+/*====================================================================
+ * 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.
+
+
+/*====================================================================
+ *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.
- */
+ * 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 occured), 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)
{
wan_device_t* wandev = &card->wandev;
int new_lcn = mb->cmd.lcn;
- struct net_device* dev = get_dev_by_lcn(wandev, new_lcn);
+ netdevice_t* 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 */
- 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;
- }
+//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);
@@ -1794,90 +3224,120 @@
x25_clear_call(card, new_lcn, 0, 0);
return 1;
}
+
parse_call_info(mb->data, info);
- printk(KERN_INFO "%s: X.25 incoming call on LCN %d! Call data: %s\n",
- card->devname, new_lcn, mb->data);
+
+ 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 */
- dev = wandev->dev;
- while (dev) {
+ for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
chan = dev->priv;
- if (!chan->svc || (chan->state != WAN_DISCONNECTED))
+ 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;
-
- dev = chan->slave;
}
- if (dev == NULL)
- {
- printk(KERN_INFO "%s: no channels available!\n",
- card->devname);
- x25_clear_call(card, new_lcn, 0, 0);
- }
+ 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){
- /* Check requested protocol encapsulation */
- else if (info->nuser == 0)
- {
printk(KERN_INFO
"%s: no user data in incoming call on LCN %d!\n",
- card->devname, new_lcn);
+ card->devname, new_lcn)
+ ;
x25_clear_call(card, new_lcn, 0, 0);
- }
- else switch (info->user[0])
- {
+
+ }else switch (info->user[0]){
+
case 0: /* multiplexed */
- chan->protocol = 0;
+ chan->protocol = htons(0);
accept = 1;
break;
case NLPID_IP: /* IP datagrams */
- chan->protocol = ETH_P_IP;
accept = 1;
break;
case NLPID_SNAP: /* IPX datagrams */
- chan->protocol = ETH_P_IPX;
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)){
- if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK))
- {
- chan->lcn = new_lcn;
+ 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
+ else
x25_clear_call(card, new_lcn, 0, 0);
}
kfree(info);
return 1;
}
-/*============================================================================
- * Handle accepted call.
- */
+/*====================================================================
+ * 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 = get_dev_by_lcn(&card->wandev, new_lcn);
+ netdevice_t* dev = find_channel(card, new_lcn);
x25_channel_t* chan;
- printk(KERN_INFO "%s: X.25 call accepted on LCN %d!\n",
- card->devname, new_lcn);
- if (dev == NULL)
- {
+ if (dev == NULL){
printk(KERN_INFO
"%s: clearing orphaned connection on LCN %d!\n",
card->devname, new_lcn);
@@ -1885,6 +3345,10 @@
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)
@@ -1892,178 +3356,256 @@
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.
- */
+/*====================================================================
+ * 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 = get_dev_by_lcn(&card->wandev, new_lcn);
+ netdevice_t* dev = find_channel(card, new_lcn);
+ x25_channel_t *chan;
+ unsigned char old_state;
- printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
+ 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)
+ }
+
+ 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.
- */
-
+/*====================================================================
+ * Handle X.25 restart event.
+ *====================================================================*/
+
static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
{
wan_device_t* wandev = &card->wandev;
- struct net_device* dev;
+ netdevice_t* 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 */
- dev = wandev->dev;
- while (dev) {
- x25_channel_t *chan = dev->priv;
+ for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
+ chan=dev->priv;
+ old_state = chan->common.state;
set_chan_state(dev, WAN_DISCONNECTED);
- dev = chan->slave;
- }
+ 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 = get_dev_by_lcn(&card->wandev, new_lcn);
+ netdevice_t* 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)
+
+ if (dev){
+ x25_channel_t *chan = dev->priv;
set_chan_state(dev, WAN_DISCONNECTED);
- }
- else printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
+
+ 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 ******************************************************/
+/*
+ * Miscellaneous
+ */
-/*============================================================================
- * Establish physical connection.
- * o open HDLC and raise DTR
+/*====================================================================
+ * Establish physical connection.
+ * o open HDLC and raise DTR
*
- * Return: 0 connection established
- * 1 connection is in progress
- * <0 error
- */
+ * 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
+/*
+ * Tear down physical connection.
+ * o close HDLC link
+ * o drop DTR
*
- * Return: 0
- * <0 error
+ * Return: 0
+ * <0 error
*/
+
static int disconnect (sdla_t* card)
{
wanpipe_set_state(card, WAN_DISCONNECTED);
- x25_set_intr_mode(card, 0); /* disable interrupt generation */
- x25_close_hdlc(card); /* close HDLC link */
- x25_set_dtr(card, 0); /* drop DTR */
+ 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 (wan_device_t* wandev, unsigned lcn)
+
+static netdevice_t* get_dev_by_lcn (wan_device_t* wandev, unsigned lcn)
{
- struct net_device* dev;
- x25_channel_t *chan;
+ netdevice_t* dev;
- dev = wandev->dev;
- while (dev) {
- if (chan->lcn == lcn)
+ for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv))
+ if (((x25_channel_t*)dev->priv)->common.lcn == lcn)
break;
- dev = chan->slave;
- }
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
+/*
+ * 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)
+
+static int chan_connect (netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
- if (chan->svc)
- {
- if (!chan->addr[0])
+ 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
- {
+ }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
+/*
+ * Disconnect logical channel.
+ * o if SVC then clear X.25 call
*/
-static int chan_disc (struct net_device* dev)
+
+static int chan_disc (netdevice_t* dev)
{
x25_channel_t* chan = dev->priv;
- if (chan->svc)
- x25_clear_call(chan->card, chan->lcn, 0, 0);
+ 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.
+/*
+ * Set logical channel state.
*/
-static void set_chan_state (struct net_device* dev, int state)
+
+static void set_chan_state (netdevice_t* dev, int state)
{
x25_channel_t* chan = dev->priv;
sdla_t* card = chan->card;
@@ -2071,99 +3613,257 @@
save_flags(flags);
cli();
- if (chan->state != state)
+ if (chan->common.state != state)
{
switch (state)
{
case WAN_CONNECTED:
- printk (KERN_INFO "%s: interface %s connected!\n",
- card->devname, dev->name);
- *(unsigned short*)dev->dev_addr = htons(chan->lcn);
+ 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:
- printk (KERN_INFO "%s: interface %s connecting...\n",
- card->devname, dev->name);
+ 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:
- printk (KERN_INFO "%s: interface %s disconnected!\n",
- card->devname, dev->name);
- if (chan->svc)
- {
+ 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;
- chan->lcn = 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 (is_queue_stopped(dev)){
+ wake_net_dev(dev);
+ }
+ }
+ atomic_set(&chan->common.command,0);
break;
- }
- chan->state = state;
- }
+
+ 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.
+/*
+ * 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, struct sk_buff* skb)
+
+static int chan_send (netdevice_t* 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, qdm;
+ 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))
- return 1;
-
- if (skb->len > chan->tx_pkt_size)
- {
- len = chan->tx_pkt_size;
- qdm = 0x01; /* set M-bit (more data) */
+ 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;
+ }
}
- else /* final packet */
- {
- len = skb->len;
- qdm = 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;
}
- switch(x25_send(card, chan->lcn, qdm, len, skb->data))
- {
+
+ /* 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;
- if (qdm)
- {
- skb_pull(skb, len);
- return 1;
+
+#ifdef LINUX_2_4
+ dev->trans_start=jiffies;
+#endif
+
+ 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;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ chan->ifstats.tx_bytes += len;
+#endif
+
+ 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;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ chan->ifstats.tx_bytes += len;
+#endif
+
+ ++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;
+#if defined(LINUX_2_1) || defined(LINUX_2_4)
+ chan->ifstats.tx_bytes += len;
+#endif
+ ++chan->if_send_stat.if_send_bfr_passed_to_adptr;
+ res=0;
}
- ++chan->ifstats.tx_packets;
- chan->ifstats.tx_bytes += skb->len;
break;
case 0x33: /* Tx busy */
- return 1;
+ 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;
-/* return 1; */
+ 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 0;
+ return res;
}
-/*============================================================================
- * Parse X.25 call request data and fill x25_call_info_t structure.
+
+/*
+ * 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)
@@ -2172,54 +3872,45 @@
for (; *str; ++str)
{
int i;
- unsigned ch;
+ 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.
+ */
- if (*str == '-') switch (str[1])
- {
case 'd': /* destination address */
- for (i = 0; i < 16; ++i)
- {
+ for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
ch = str[2+i];
- if (!is_digit(ch))
- break;
+ if (isspace(ch)) break;
info->dest[i] = ch;
}
break;
-
+
case 's': /* source address */
- for (i = 0; i < 16; ++i)
- {
+ for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
ch = str[2+i];
- if (!is_digit(ch))
- break;
+ if (isspace(ch)) break;
info->src[i] = ch;
}
break;
case 'u': /* user data */
- for (i = 0; i < 127; ++i)
- {
- ch = str[2+2*i];
- if (!is_hex_digit(ch))
- break;
- info->user[i] = hex_to_uint(&str[2+2*i], 2);
+ 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 < 64; ++i)
- {
- ch = str[2+4*i];
- if (!is_hex_digit(ch))
- break;
- info->facil[i].code =
- hex_to_uint(&str[2+4*i], 2);
- ch = str[4+4*i];
- if (!is_hex_digit(ch))
- break;
- info->facil[i].parm =
- hex_to_uint(&str[4+4*i], 2);
+ 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;
@@ -2227,14 +3918,15 @@
}
}
-/*============================================================================
- * Convert line speed in bps to a number used by S502 code.
+/*
+ * 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 ;
+ if (bps <= 1200) number = 0x01;
else if (bps <= 2400) number = 0x02;
else if (bps <= 4800) number = 0x03;
else if (bps <= 9600) number = 0x04;
@@ -2251,29 +3943,36 @@
return number;
}
-/*============================================================================
- * Convert decimal string to unsigned integer.
- * If len != 0 then only 'len' characters of the string are converted.
+/*
+ * 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);
+ 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.
+/*
+ * 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);
+ if (!len)
+ len = strlen(str);
+
for (val = 0; len; ++str, --len)
{
ch = *str;
@@ -2281,8 +3980,7 @@
val = (val << 4) + (ch - (unsigned)'0');
else if (is_hex_digit(ch))
val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10);
- else
- break;
+ else break;
}
return val;
}
@@ -2292,21 +3990,21 @@
{
int i;
- if( proto == htons(ETH_P_IPX) ) {
+ 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. */
+ /* It's not IPX so pass it up the stack.*/
return 0;
}
if( sendpacket[16] == 0x90 &&
sendpacket[17] == 0x04)
{
- /* It's IPXWAN */
+ /* It's IPXWAN */
if( sendpacket[2] == 0x02 &&
sendpacket[34] == 0x00)
@@ -2315,19 +4013,24 @@
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 */
+ * 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. */
+ /* We also want to turn off all header compression opt. */
for(; sendpacket[i] == 0x80 ;)
{
sendpacket[i + 1] = 0;
@@ -2364,7 +4067,9 @@
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);
}
@@ -2382,18 +4087,18 @@
return 1;
} else {
- /* If we get here its an IPX-data packet, so it'll get passed up the stack.
- switch the network numbers */
+ /*If we get here its 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
-
-*/
+ * 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)
{
@@ -2402,21 +4107,17 @@
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)
- {
+ 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
- {
+ } else {
/* If the incoming network is 0, make it ours */
- if( pnetwork_number == 0)
- {
+ if( pnetwork_number == 0) {
sendpacket[6] = (unsigned char)(network_number >> 24);
sendpacket[7] = (unsigned char)((network_number &
0x00FF0000) >> 16);
@@ -2431,21 +4132,17 @@
pnetwork_number = (unsigned long)((sendpacket[18] << 24) +
(sendpacket[19] << 16) + (sendpacket[20] << 8) +
sendpacket[21]);
-
- if( !incoming )
- {
+
+
+ if( !incoming ) {
/* If the source network is ours, make it 0 */
- if( pnetwork_number == network_number)
- {
+ if( pnetwork_number == network_number) {
sendpacket[18] = sendpacket[19] = sendpacket[20] =
- sendpacket[21] = 0x00;
+ sendpacket[21] = 0x00;
}
- }
- else
- {
+ } else {
/* If the source network is 0, make it ours */
- if( pnetwork_number == 0 )
- {
+ if( pnetwork_number == 0 ) {
sendpacket[18] = (unsigned char)(network_number >> 24);
sendpacket[19] = (unsigned char)((network_number &
0x00FF0000) >> 16);
@@ -2457,5 +4154,1419 @@
}
} /* 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.
+ *
+ *===============================================================*/
+
+
+netdevice_t * 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, netdevice_t *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 (netdevice_t * 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);
+ wan_dev_kfree_skb(skb, FREE_READ);
+ 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 (netdevice_t *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 (netdevice_t *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)
+{
+ netdevice_t *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, netdevice_t *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 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);
+ wan_dev_kfree_skb(skb, FREE_READ);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*===============================================================
+ * send_delayed_cmd_result
+ *
+ * Wait commands like PLEACE CALL or CLEAR CALL must wait
+ * untill the result arrivers. This function passes
+ * the result to a waiting sock.
+ *
+ *===============================================================*/
+static void send_delayed_cmd_result(sdla_t *card, netdevice_t *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)
+{
+ netdevice_t *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, netdevice_t *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);
+ wan_dev_kfree_skb(skb, FREE_READ);
+ }
+ }
+
+ 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! */
+ wan_dev_kfree_skb(new_skb, FREE_READ);
+ 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)
+{
+ netdevice_t *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, netdevice_t *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, netdevice_t *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;
+ netdevice_t *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,
+ netdevice_t *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){
+ wan_dev_kfree_skb(skb, FREE_WRITE);
+ }else{
+ wan_dev_kfree_skb(skb, FREE_READ);
+ }
+
+ 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);
+
+}
/****** End *****************************************************************/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)