!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/net/wan/   drwxr-xr-x
Free 318.37 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     sdla_x25.c (150.75 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*****************************************************************************
* sdla_x25.c    WANPIPE(tm) Multiprotocol WAN Link Driver.  X.25 module.
*
* Author:    Nenad Corbic    <ncorbic@sangoma.com>
*
* Copyright:    (c) 1995-2001 Sangoma Technologies Inc.
*
*        This program is free software; you can redistribute it and/or
*        modify it under the terms of the GNU General Public License
*        as published by the Free Software Foundation; either version
*        2 of the License, or (at your option) any later version.
* ============================================================================
* Apr 03, 2001  Nenad Corbic     o Fixed the rx_skb=NULL bug in x25 in rx_intr().
* Dec 26, 2000  Nenad Corbic     o Added a new polling routine, that uses
*                                  a kernel timer (more efficient).
* Dec 25, 2000  Nenad Corbic     o Updated for 2.4.X kernel
* Jul 26, 2000  Nenad Corbic     o Increased the local packet buffering
*                    for API to 4096+header_size. 
* Jul 17, 2000  Nenad Corbic     o Fixed the x25 startup bug. Enable 
*                    communications only after all interfaces
*                    come up.  HIGH SVC/PVC is used to calculate
*                    the number of channels.
*                                  Enable protocol only after all interfaces
*                                  are enabled.
* Jul 10, 2000    Nenad Corbic     o Fixed the M_BIT bug. 
* Apr 25, 2000  Nenad Corbic     o Pass Modem messages to the API.
*                                  Disable idle timeout in X25 API.
* Apr 14, 2000  Nenad Corbic     o Fixed: Large LCN number support.
*                                  Maximum LCN number is 4095.
*                                  Maximum number of X25 channels is 255.
* Apr 06, 2000  Nenad Corbic     o Added SMP Support.
* Mar 29, 2000  Nenad Corbic     o Added support for S514 PCI Card
* Mar 23, 2000  Nenad Corbic     o Improved task queue, BH handling.
* Mar 14, 2000  Nenad Corbic       o Updated Protocol Violation handling
*                                  routines.  Bug Fix.
* Mar 10, 2000  Nenad Corbic     o Bug Fix: corrupted mbox recovery.
* Mar 09, 2000  Nenad Corbic     o Fixed the auto HDLC bug.
* Mar 08, 2000    Nenad Corbic     o Fixed LAPB HDLC startup problems.
*                                  Application must bring the link up 
*                                  before tx/rx, and bring the 
*                                  link down on close().
* Mar 06, 2000    Nenad Corbic     o Added an option for logging call setup 
*                                  information. 
* Feb 29, 2000  Nenad Corbic      o Added support for LAPB HDLC API
* Feb 25, 2000  Nenad Corbic     o Fixed the modem failure handling.
*                                  No Modem OOB message will be passed 
*                                  to the user.
* Feb 21, 2000  Nenad Corbic      o Added Xpipemon Debug Support
* Dec 30, 1999     Nenad Corbic     o Socket based X25API 
* Sep 17, 1998    Jaspreet Singh     o Updates for 2.2.X  kernel
* Mar 15, 1998    Alan Cox     o 2.1.x porting
* Dec 19, 1997    Jaspreet Singh     o Added multi-channel IPX support
* Nov 27, 1997    Jaspreet Singh     o Added protection against enabling of irqs
*                   when they are disabled.
* Nov 17, 1997  Farhan Thawar    o Added IPX support
*                 o Changed if_send() to now buffer packets when
*                   the board is busy
*                 o Removed queueing of packets via the polling
*                   routing
*                 o Changed if_send() critical flags to properly
*                   handle race conditions
* Nov 06, 1997  Farhan Thawar    o Added support for SVC timeouts
*                 o Changed PVC encapsulation to ETH_P_IP
* Jul 21, 1997  Jaspreet Singh     o Fixed freeing up of buffers using kfree()
*                   when packets are received.
* Mar 11, 1997  Farhan Thawar   Version 3.1.1
*                                o added support for V35
*                                o changed if_send() to return 0 if
*                                  wandev.critical() is true
*                                o free socket buffer in if_send() if
*                                  returning 0
*                                o added support for single '@' address to
*                                  accept all incoming calls
*                                o fixed bug in set_chan_state() to disconnect
* Jan 15, 1997    Gene Kozin    Version 3.1.0
*                 o implemented exec() entry point
* Jan 07, 1997    Gene Kozin    Initial version.
*****************************************************************************/

/*======================================================
 *     Includes 
 *=====================================================*/

#include <linux/module.h>
#include <linux/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/ctype.h>
#include <linux/slab.h>    /* kmalloc(), kfree() */
#include <linux/wanrouter.h>    /* WAN router definitions */
#include <linux/wanpipe.h>    /* WANPIPE common user API definitions */
#include <asm/byteorder.h>    /* htons(), etc. */
#include <asm/atomic.h>
#include <linux/delay.h>    /* Experimental delay */

#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 
 *=====================================================*/


#define    CMD_OK        0        /* normal firmware return code */
#define    CMD_TIMEOUT    0xFF        /* firmware command timed out */
#define    MAX_CMD_RETRY    10        /* max number of firmware retries */

#define    X25_CHAN_MTU    4096        /* unfragmented logical channel MTU */
#define    X25_HRDHDR_SZ    7        /* max encapsulation header size */
#define    X25_CONCT_TMOUT    (90*HZ)        /* link connection timeout */
#define    X25_RECON_TMOUT    (10*HZ)        /* link connection timeout */
#define    CONNECT_TIMEOUT    (90*HZ)        /* link connection timeout */
#define    HOLD_DOWN_TIME    (30*HZ)        /* link hold down time */
#define MAX_BH_BUFF    10
#define M_BIT        0x01    

//#define PRINT_DEBUG 1
#ifdef PRINT_DEBUG
#define DBG_PRINTK(format, a...) printk(format, ## a)
#else
#define DBG_PRINTK(format, a...)
#endif  

#define TMR_INT_ENABLED_POLL_ACTIVE      0x01
#define TMR_INT_ENABLED_POLL_CONNECT_ON  0x02
#define TMR_INT_ENABLED_POLL_CONNECT_OFF 0x04
#define TMR_INT_ENABLED_POLL_DISCONNECT  0x08
#define TMR_INT_ENABLED_CMD_EXEC     0x10
#define TMR_INT_ENABLED_UPDATE         0x20
#define TMR_INT_ENABLED_UDP_PKT         0x40

#define MAX_X25_ADDR_SIZE    16
#define MAX_X25_DATA_SIZE     129
#define MAX_X25_FACL_SIZE    110

#define TRY_CMD_AGAIN    2
#define DELAY_RESULT    1
#define RETURN_RESULT   0

#define DCD(x) (x & 0x03 ? "HIGH" : "LOW")
#define CTS(x) (x & 0x05 ? "HIGH" : "LOW")


/* Driver will not write log messages about 
 * modem status if defined.*/
#define MODEM_NOT_LOG 1

/*==================================================== 
 *     For IPXWAN 
 *===================================================*/

#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))


/*====================================================
 *           MEMORY DEBUGGING FUNCTION
 *====================================================

#define KMEM_SAFETYZONE 8

static void * dbg_kmalloc(unsigned int size, int prio, int line) {
    int i = 0;
    void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio);
    char * c1 = v;    
    c1 += sizeof(unsigned int);
    *((unsigned int *)v) = size;

    for (i = 0; i < KMEM_SAFETYZONE; i++) {
        c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D';
        c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F';
        c1 += 8;
    }
    c1 += size;
    for (i = 0; i < KMEM_SAFETYZONE; i++) {
        c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G';
        c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L';
        c1 += 8;
    }
    v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8;
    printk(KERN_INFO "line %d  kmalloc(%d,%d) = %p\n",line,size,prio,v);
    return v;
}
static void dbg_kfree(void * v, int line) {
    unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8));
    unsigned int size = *sp;
    char * c1 = ((char *)v) - KMEM_SAFETYZONE*8;
    int i = 0;
    for (i = 0; i < KMEM_SAFETYZONE; i++) {
        if (   c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D'
            || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') {
            printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v);
            printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
                            c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
        }
        c1 += 8;
    }
    c1 += size;
    for (i = 0; i < KMEM_SAFETYZONE; i++) {
        if (   c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G'
            || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L'
           ) {
            printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v);
            printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
                            c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
        }
        c1 += 8;
    }
    printk(KERN_INFO "line %d  kfree(%p)\n",line,v);
    v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8);
    kfree(v);
}

#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
#define kfree(x) dbg_kfree(x,__LINE__)

==============================================================*/



/*===============================================
 *     Data Structures 
 *===============================================*/


/*========================================================
 * Name:     x25_channel
 *
 * Purpose:    To hold private informaton for each  
 *              logical channel.
 *        
 * Rationale:      Per-channel debugging is possible if each 
 *              channel has its own private area.
 *    
 * Assumptions:
 *
 * Description:    This is an extention of the 'netdevice_t' 
 *              we create for each network interface to keep 
 *              the rest of X.25 channel-specific data. 
 *
 * Construct:    Typedef
 */
typedef struct x25_channel
{
    wanpipe_common_t common;    /* common area for x25api and socket */
    char name[WAN_IFNAME_SZ+1];    /* interface name, ASCIIZ */
    char addr[WAN_ADDRESS_SZ+1];    /* media address, ASCIIZ */
    unsigned tx_pkt_size;
    unsigned short protocol;    /* ethertype, 0 - multiplexed */
    char drop_sequence;        /* mark sequence for dropping */
    unsigned long state_tick;    /* time of the last state change */
    unsigned idle_timeout;        /* sec, before disconnecting */
    unsigned long i_timeout_sofar;  /* # of sec's we've been idle */
    unsigned hold_timeout;        /* sec, before re-connecting */
    unsigned long tick_counter;    /* counter for transmit time out */
    char devtint;            /* Weather we should dev_tint() */
    struct sk_buff* rx_skb;        /* receive socket buffer */
    struct sk_buff* tx_skb;        /* transmit socket buffer */

    bh_data_t *bh_head;            /* Circular buffer for x25api_bh */
    unsigned long  tq_working;
    volatile int  bh_write;
    volatile int  bh_read;
    atomic_t  bh_buff_used;

    sdla_t* card;            /* -> owner */
    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];            PACKED;/* ASCIIZ destination address */
    char src[17];            PACKED;/* ASCIIZ source address */
    char nuser;            PACKED;/* number of user data bytes */
    unsigned char user[127];    PACKED;/* user data */
    char nfacil;            PACKED;/* number of facilities */
    struct
    {
        unsigned char code;     PACKED;
        unsigned char parm;     PACKED;
    } facil[64];                    /* facilities */
} x25_call_info_t;
#else
typedef struct x25_call_info
{
    char dest[MAX_X25_ADDR_SIZE]        PACKED;/* ASCIIZ destination address */
    char src[MAX_X25_ADDR_SIZE]        PACKED;/* ASCIIZ source address */
    unsigned char nuser            PACKED;
    unsigned char user[MAX_X25_DATA_SIZE]    PACKED;/* user data */
    unsigned char nfacil            PACKED;
    unsigned char facil[MAX_X25_FACL_SIZE]    PACKED;
    unsigned short lcn                     PACKED;
} x25_call_info_t;
#endif


  
/*===============================================
 *    Private Function Prototypes
 *==============================================*/


/*================================================= 
 * WAN link driver entry points. These are 
 * called by the WAN router module.
 */
static int update (wan_device_t* wandev);
static int new_if (wan_device_t* wandev, netdevice_t* dev,
    wanif_conf_t* conf);
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 
 */
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   (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, netdevice_t* dev);
static struct net_device_stats *if_stats (netdevice_t* dev);

#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 *);

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 
 */
static int x25_get_version (sdla_t* card, char* str);
static int x25_configure (sdla_t* card, TX25Config* conf);
static int hdlc_configure (sdla_t* card, TX25Config* conf);
static int set_hdlc_level (sdla_t* card);
static int x25_get_err_stats (sdla_t* card);
static int x25_get_stats (sdla_t* card);
static int x25_set_intr_mode (sdla_t* card, int mode);
static int x25_close_hdlc (sdla_t* card);
static int x25_open_hdlc (sdla_t* card);
static int x25_setup_hdlc (sdla_t* card);
static int x25_set_dtr (sdla_t* card, int dtr);
static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan);
static int x25_place_call (sdla_t* card, x25_channel_t* chan);
static int x25_accept_call (sdla_t* card, int lcn, int qdm);
static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn);
static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf);
static int x25_fetch_events (sdla_t* card);
static int x25_error (sdla_t* card, int err, int cmd, int lcn);

/*=================================================  
 *    X.25 asynchronous event handlers 
 */
static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);


/*=================================================  
 *    Miscellaneous functions 
 */
static int connect (sdla_t* card);
static int disconnect (sdla_t* card);
static 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*, 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 *, unsigned long, unsigned char);
static int handle_IPXWAN(unsigned char *, char *, unsigned char , 
             unsigned long , unsigned short );

extern void disable_irq(unsigned int);
extern void enable_irq(unsigned int);

static void S508_S514_lock(sdla_t *, unsigned long *);
static void S508_S514_unlock(sdla_t *, unsigned long *);


/*=================================================  
 *     Global Variables 
 *=================================================*/



/*================================================= 
 *    Public Functions 
 *=================================================*/




/*===================================================================
 * wpx_init:    X.25 Protocol Initialization routine.
 *
 * Purpose:    To initialize the protocol/firmware.
 * 
 * Rationale:    This function is called by setup() function, in
 *              sdlamain.c, to dynamically setup the x25 protocol.
 *        This is the first protocol specific function, which
 *              executes once on startup.
 *                
 * Description:    This procedure initializes the x25 firmware and
 *            sets up the mailbox, transmit and receive buffer
 *              pointers. It also initializes all debugging structures
 *              and sets up the X25 environment.
 *
 *        Sets up hardware options defined by user in [wanpipe#] 
 *        section of wanpipe#.conf configuration file. 
 *
 *         At this point adapter is completely initialized 
 *          and X.25 firmware is running.
 *          o read firmware version (to make sure it's alive)
 *          o configure adapter
 *          o initialize protocol-specific fields of the 
 *                adapter data space.
 *
 * Called by:    setup() function in sdlamain.c
 *
 * Assumptions:    None
 *
 * Warnings:    None
 *
 * Return:     0    o.k.
 *         < 0    failure.
 */

int wpx_init (sdla_t* card, wandev_conf_t* conf)
{
    union{
        char str[80];
        TX25Config cfg;
    } u;

    /* Verify configuration ID */
    if (conf->config_id != WANCONFIG_X25){
        printk(KERN_INFO "%s: invalid configuration ID %u!\n",
            card->devname, conf->config_id)
        ;
        return -EINVAL;
    }

    /* Initialize protocol-specific fields */
    card->mbox  = (void*)(card->hw.dpmbase + X25_MBOX_OFFS);
    card->rxmb  = (void*)(card->hw.dpmbase + X25_RXMBOX_OFFS);
    card->flags = (void*)(card->hw.dpmbase + X25_STATUS_OFFS);

    /* Initialize for S514 Card */
    if(card->hw.type == SDLA_S514) {
        card->mbox += X25_MB_VECTOR;
        card->flags += X25_MB_VECTOR;
        card->rxmb += X25_MB_VECTOR;
    }


    /* Read firmware version.  Note that when adapter initializes, it
     * clears the mailbox, so it may appear that the first command was
     * executed successfully when in fact it was merely erased. To work
     * around this, we execute the first command twice.
     */
    if (x25_get_version(card, NULL) || x25_get_version(card, u.str))
        return -EIO;


    /* X25 firmware can run ether in X25 or LAPB HDLC mode.
         * Check the user defined option and configure accordingly */
    if (conf->u.x25.LAPB_hdlc_only == WANOPT_YES){
        if (set_hdlc_level(card) != CMD_OK){
            return -EIO;    
        }else{
            printk(KERN_INFO "%s: running LAP_B HDLC firmware v%s\n",
                card->devname, u.str);
        }
        card->u.x.LAPB_hdlc = 1;
    }else{
        printk(KERN_INFO "%s: running X.25 firmware v%s\n",
                card->devname, u.str);
        card->u.x.LAPB_hdlc = 0;
    }

    /* Configure adapter. Here we set resonable defaults, then parse
     * device configuration structure and set configuration options.
     * Most configuration options are verified and corrected (if
     * necessary) since we can't rely on the adapter to do so.
     */
    memset(&u.cfg, 0, sizeof(u.cfg));
    u.cfg.t1        = 3;
    u.cfg.n2        = 10;
    u.cfg.autoHdlc        = 1;        /* automatic HDLC connection */
    u.cfg.hdlcWindow    = 7;
    u.cfg.pktWindow        = 2;
    u.cfg.station        = 1;        /* DTE */
    u.cfg.options        = 0x0090;    /* disable D-bit pragmatics */
    u.cfg.ccittCompat    = 1988;
    u.cfg.t10t20        = 30;
    u.cfg.t11t21        = 30;
    u.cfg.t12t22        = 30;
    u.cfg.t13t23        = 30;
    u.cfg.t16t26        = 30;
    u.cfg.t28        = 30;
    u.cfg.r10r20        = 5;
    u.cfg.r12r22        = 5;
    u.cfg.r13r23        = 5;
    u.cfg.responseOpt    = 1;        /* RR's after every packet */

    if (card->u.x.LAPB_hdlc){
        u.cfg.hdlcMTU = 1027;
    }

    if (conf->u.x25.x25_conf_opt){
        u.cfg.options = conf->u.x25.x25_conf_opt;
    }

    if (conf->clocking != WANOPT_EXTERNAL)
        u.cfg.baudRate = bps_to_speed_code(conf->bps);

    if (conf->station != WANOPT_DTE){
        u.cfg.station = 0;        /* DCE mode */
    }

        if (conf->interface != WANOPT_RS232 ){
            u.cfg.hdlcOptions |= 0x80;      /* V35 mode */
    } 

    /* adjust MTU */
    if (!conf->mtu || (conf->mtu >= 1024))
        card->wandev.mtu = 1024;
    else if (conf->mtu >= 512)
        card->wandev.mtu = 512;
    else if (conf->mtu >= 256)
        card->wandev.mtu = 256;
    else if (conf->mtu >= 128)
        card->wandev.mtu = 128;
    else 
        card->wandev.mtu = 64;

    u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu;

    if (conf->u.x25.hi_pvc){
        card->u.x.hi_pvc = min_t(unsigned int, conf->u.x25.hi_pvc, MAX_LCN_NUM);
        card->u.x.lo_pvc = min_t(unsigned int, conf->u.x25.lo_pvc, card->u.x.hi_pvc);
    }

    if (conf->u.x25.hi_svc){
        card->u.x.hi_svc = min_t(unsigned int, conf->u.x25.hi_svc, MAX_LCN_NUM);
        card->u.x.lo_svc = min_t(unsigned int, conf->u.x25.lo_svc, card->u.x.hi_svc);
    }

    /* Figure out the total number of channels to configure */
    card->u.x.num_of_ch = 0;
    if (card->u.x.hi_svc != 0){
        card->u.x.num_of_ch = (card->u.x.hi_svc - card->u.x.lo_svc) + 1;
    }
    if (card->u.x.hi_pvc != 0){
        card->u.x.num_of_ch += (card->u.x.hi_pvc - card->u.x.lo_pvc) + 1;
    }

    if (card->u.x.num_of_ch == 0){
        printk(KERN_INFO "%s: ERROR, Minimum number of PVC/SVC channels is 1 !\n"
                 "%s: Please set the Lowest/Highest PVC/SVC values !\n",
                 card->devname,card->devname);
        return -ECHRNG;
    }
    
    u.cfg.loPVC = card->u.x.lo_pvc;
    u.cfg.hiPVC = card->u.x.hi_pvc;
    u.cfg.loTwoWaySVC = card->u.x.lo_svc;
    u.cfg.hiTwoWaySVC = card->u.x.hi_svc;

    if (conf->u.x25.hdlc_window)
        u.cfg.hdlcWindow = min_t(unsigned int, conf->u.x25.hdlc_window, 7);
    if (conf->u.x25.pkt_window)
        u.cfg.pktWindow = min_t(unsigned int, conf->u.x25.pkt_window, 7);

    if (conf->u.x25.t1)
        u.cfg.t1 = min_t(unsigned int, conf->u.x25.t1, 30);
    if (conf->u.x25.t2)
        u.cfg.t2 = min_t(unsigned int, conf->u.x25.t2, 29);
    if (conf->u.x25.t4)
        u.cfg.t4 = min_t(unsigned int, conf->u.x25.t4, 240);
    if (conf->u.x25.n2)
        u.cfg.n2 = min_t(unsigned int, conf->u.x25.n2, 30);

    if (conf->u.x25.t10_t20)
        u.cfg.t10t20 = min_t(unsigned int, conf->u.x25.t10_t20,255);
    if (conf->u.x25.t11_t21)
        u.cfg.t11t21 = min_t(unsigned int, conf->u.x25.t11_t21,255);
    if (conf->u.x25.t12_t22)
        u.cfg.t12t22 = min_t(unsigned int, conf->u.x25.t12_t22,255);
    if (conf->u.x25.t13_t23)    
        u.cfg.t13t23 = min_t(unsigned int, conf->u.x25.t13_t23,255);
    if (conf->u.x25.t16_t26)
        u.cfg.t16t26 = min_t(unsigned int, conf->u.x25.t16_t26, 255);
    if (conf->u.x25.t28)
        u.cfg.t28 = min_t(unsigned int, conf->u.x25.t28, 255);

    if (conf->u.x25.r10_r20)
        u.cfg.r10r20 = min_t(unsigned int, conf->u.x25.r10_r20,250);
    if (conf->u.x25.r12_r22)
        u.cfg.r12r22 = min_t(unsigned int, conf->u.x25.r12_r22,250);
    if (conf->u.x25.r13_r23)
        u.cfg.r13r23 = min_t(unsigned int, conf->u.x25.r13_r23,250);


    if (conf->u.x25.ccitt_compat)
        u.cfg.ccittCompat = conf->u.x25.ccitt_compat;

    /* initialize adapter */
    if (card->u.x.LAPB_hdlc){
        if (hdlc_configure(card, &u.cfg) != CMD_OK)
            return -EIO;
    }else{
        if (x25_configure(card, &u.cfg) != CMD_OK)
            return -EIO;
    }

    if ((x25_close_hdlc(card) != CMD_OK) ||        /* close HDLC link */
        (x25_set_dtr(card, 0) != CMD_OK))        /* drop DTR */
        return -EIO;

    /* Initialize protocol-specific fields of adapter data space */
    card->wandev.bps    = conf->bps;
    card->wandev.interface    = conf->interface;
    card->wandev.clocking    = conf->clocking;
    card->wandev.station    = conf->station;
    card->isr        = &wpx_isr;
    card->poll        = NULL; //&wpx_poll;
    card->disable_comm    = &disable_comm;
    card->exec        = &wpx_exec;
    card->wandev.update    = &update;
    card->wandev.new_if    = &new_if;
    card->wandev.del_if    = &del_if;

    /* WARNING: This function cannot exit with an error
     *          after the change of state */
    card->wandev.state    = WAN_DISCONNECTED;
    
    card->wandev.enable_tx_int = 0;
    card->irq_dis_if_send_count = 0;
        card->irq_dis_poll_count = 0;
    card->u.x.tx_dev = NULL;
    card->u.x.no_dev = 0;


    /* Configure for S514 PCI Card */
    if (card->hw.type == SDLA_S514) {
        card->u.x.hdlc_buf_status = 
            (volatile unsigned char *)
                (card->hw.dpmbase + X25_MB_VECTOR+ X25_MISC_HDLC_BITS);
    }else{
        card->u.x.hdlc_buf_status = 
            (volatile unsigned char *)(card->hw.dpmbase + X25_MISC_HDLC_BITS); 
    }

    card->u.x.poll_device=NULL;
    card->wandev.udp_port = conf->udp_port;

    /* Enable or disable call setup logging */
    if (conf->u.x25.logging == WANOPT_YES){
        printk(KERN_INFO "%s: Enabling Call Logging.\n",
            card->devname);
        card->u.x.logging = 1;
    }else{    
        card->u.x.logging = 0;
    }

    /* Enable or disable modem status reporting */
    if (conf->u.x25.oob_on_modem == WANOPT_YES){
        printk(KERN_INFO "%s: Enabling OOB on Modem change.\n",
            card->devname);
        card->u.x.oob_on_modem = 1;
    }else{
        card->u.x.oob_on_modem = 0;
    }
    
    init_global_statistics(card);    

#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;
    
    return 0;
}

/*=========================================================
 *    WAN Device Driver Entry Points 
 *========================================================*/

/*============================================================
 * Name:    update(),  Update device status & statistics.
 *
 * Purpose:    To provide debugging and statitical
 *              information to the /proc file system.
 *              /proc/net/wanrouter/wanpipe#
 *                  
 * Rationale:    The /proc file system is used to collect
 *              information about the kernel and drivers.
 *              Using the /proc file system the user
 *              can see exactly what the sangoma drivers are
 *              doing. And in what state they are in. 
 *                
 * Description: Collect all driver statistical information
 *              and pass it to the top laywer. 
 *        
 *        Since we have to execute a debugging command, 
 *              to obtain firmware statitics, we trigger a 
 *              UPDATE function within the timer interrtup.
 *              We wait until the timer update is complete.
 *              Once complete return the appropriate return
 *              code to indicate that the update was successful.
 *              
 * Called by:    device_stat() in wanmain.c
 *
 * Assumptions:    
 *
 * Warnings:    This function will degrade the performance
 *              of the router, since it uses the mailbox. 
 *
 * Return:     0     OK
 *         <0    Failed (or busy).
 */

static int update (wan_device_t* wandev)
{
    volatile sdla_t* card;
    TX25Status* status;
    unsigned long timeout;

    /* sanity checks */
    if ((wandev == NULL) || (wandev->private == NULL))
        return -EFAULT;

    if (wandev->state == WAN_UNCONFIGURED)
        return -ENODEV;

    if (test_bit(SEND_CRIT, (void*)&wandev->critical))
        return -EAGAIN;

    if (!wandev->dev)
        return -ENODEV;
    
    card = wandev->private;
    status = card->flags;

    card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UPDATE;
    status->imask |= INTR_ON_TIMER;
    timeout = jiffies;    

    for (;;){
        if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE)){    
            break;
        }
        if ((jiffies-timeout) > 1*HZ){
            card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
            return -EAGAIN;
        }
    }
    return 0;
}


/*===================================================================
 * Name:    new_if
 *
 * Purpose:    To allocate and initialize resources for a 
 *              new logical channel.  
 * 
 * Rationale:    A new channel can be added dynamically via
 *              ioctl call.
 *                
 * Description:    Allocate a private channel structure, x25_channel_t.
 *        Parse the user interface options from wanpipe#.conf 
 *        configuration file. 
 *        Bind the private are into the network device private
 *              area pointer (dev->priv).
 *        Prepare the network device structure for registration.
 *
 * Called by:    ROUTER_IFNEW Ioctl call, from wanrouter_ioctl() 
 *              (wanmain.c)
 *
 * Assumptions: None
 *
 * Warnings:    None
 *
 * Return:     0     Ok
 *        <0     Failed (channel will not be created)
 */
static int new_if (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)){
        printk(KERN_INFO "%s: invalid interface name!\n",
            card->devname);
        return -EINVAL;
    }

    if(card->wandev.new_if_cnt++ > 0 && card->u.x.LAPB_hdlc) {
        printk(KERN_INFO "%s: Error: Running LAPB HDLC Mode !\n",
                        card->devname);
        printk(KERN_INFO 
            "%s: Maximum number of network interfaces must be one !\n",
                        card->devname);
        return -EEXIST;
    }

    /* allocate and initialize private data */
    chan = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC);
    if (chan == NULL){
        return -ENOMEM;
    }
    
    memset(chan, 0, sizeof(x25_channel_t));

    /* Bug Fix: Seg Err on PVC startup
     * It must be here since bind_lcn_to_dev expects 
     * it bellow */
    dev->priv = chan;
    
    strcpy(chan->name, conf->name);
    chan->card = card;
    chan->dev = dev;
    chan->common.sk = NULL;
    chan->common.func = NULL;
    chan->common.rw_bind = 0;
    chan->tx_skb = chan->rx_skb = NULL;

    /* verify media address */
    if (conf->addr[0] == '@'){        /* SVC */
        chan->common.svc = 1;
        strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);

        /* Set channel timeouts (default if not specified) */
        chan->idle_timeout = (conf->idle_timeout) ? 
                    conf->idle_timeout : 90;
        chan->hold_timeout = (conf->hold_timeout) ? 
                    conf->hold_timeout : 10;

    }else if (is_digit(conf->addr[0])){    /* PVC */
        int lcn = dec_to_uint(conf->addr, 0);

        if ((lcn >= card->u.x.lo_pvc) && (lcn <= card->u.x.hi_pvc)){
            bind_lcn_to_dev (card, dev, lcn);
        }else{
            printk(KERN_ERR
                "%s: PVC %u is out of range on interface %s!\n",
                wandev->name, lcn, chan->name);
            err = -EINVAL;
        }
    }else{
        printk(KERN_ERR
            "%s: invalid media address on interface %s!\n",
            wandev->name, chan->name);
        err = -EINVAL;
    }

    if(strcmp(conf->usedby, "WANPIPE") == 0){
                printk(KERN_INFO "%s: Running in WANPIPE mode %s\n",
            wandev->name, chan->name);
                chan->common.usedby = WANPIPE;
        chan->protocol = htons(ETH_P_IP);

        }else if(strcmp(conf->usedby, "API") == 0){
        chan->common.usedby = API;
                printk(KERN_INFO "%s: Running in API mode %s\n",
            wandev->name, chan->name);
        chan->protocol = htons(X25_PROT);
    }


    if (err){
        kfree(chan);
        dev->priv = NULL;
        return err;
    }
    
    chan->enable_IPX = conf->enable_IPX;
    
    if (chan->enable_IPX)
        chan->protocol = htons(ETH_P_IPX);
    
    if (conf->network_number)
        chan->network_number = conf->network_number;
    else
        chan->network_number = 0xDEADBEEF;

    /* prepare network device data space for registration */
#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;

    init_x25_channel_struct(chan);

    return 0;
}

/*===================================================================
 * Name:    del_if(),  Remove a logical channel.     
 *
 * Purpose:    To dynamically remove a logical channel.
 * 
 * Rationale:    Each logical channel should be dynamically
 *              removable. This functin is called by an 
 *              IOCTL_IFDEL ioctl call or shutdown(). 
 *                
 * Description: Do nothing.
 *
 * Called by:    IOCTL_IFDEL : wanrouter_ioctl() from wanmain.c
 *              shutdown() from sdlamain.c
 *
 * Assumptions: 
 *
 * Warnings:
 *
 * Return:     0 Ok. Void function.
 */

//FIXME Del IF Should be taken out now.

static int del_if (wan_device_t* wandev, netdevice_t* dev)
{
    return 0;
}


/*============================================================
 * Name:    wpx_exec
 *
 * Description:    Execute adapter interface command.
 *         This option is currently dissabled.
 *===========================================================*/

static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data)
{
        return 0;
}

/*============================================================
 * Name:    disable_comm    
 *
 * Description:    Disable communications during shutdown.
 *              Dont check return code because there is 
 *              nothing we can do about it.  
 *
 * Warning:    Dev and private areas are gone at this point.
 *===========================================================*/

static void disable_comm(sdla_t* card)
{
    disable_comm_shutdown(card);
    del_timer(&card->u.x.x25_timer);
    return;
}


/*============================================================
 *    Network Device Interface 
 *===========================================================*/

/*===================================================================
 * Name:    if_init(),   Netowrk Interface Initialization      
 *
 * Purpose:    To initialize a network interface device structure.
 * 
 * Rationale:    During network interface startup, the if_init
 *              is called by the kernel to initialize the
 *              netowrk device structure.  Thus a driver
 *              can customze a network device. 
 *                
 * Description:    Initialize the netowrk device call back
 *              routines.  This is where we tell the kernel
 *              which function to use when it wants to send
 *              via our interface. 
 *        Furthermore, we initialize the device flags, 
 *              MTU and physical address of the board.
 *
 * Called by:    Kernel (/usr/src/linux/net/core/dev.c)
 *         (dev->init())
 *
 * Assumptions: None
 *    
 * Warnings:    None
 *
 * Return:     0     Ok : Void function.
 */
static int if_init (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;
    dev->stop        = &if_close;
    dev->hard_header    = &if_header;
    dev->rebuild_header    = &if_rebuild_hdr;
    dev->hard_start_xmit    = &if_send;
    dev->get_stats        = &if_stats;

#ifdef LINUX_2_4
    dev->tx_timeout        = &if_tx_timeout;
    dev->watchdog_timeo    = TX_TIMEOUT;
#endif

    /* Initialize media-specific parameters */
#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->common.svc){
        *(unsigned short*)dev->dev_addr = htons(chan->common.lcn);
    }
    
    /* Initialize hardware parameters (just for reference) */
    dev->irq    = wandev->irq;
    dev->dma    = wandev->dma;
    dev->base_addr    = wandev->ioport;
    dev->mem_start    = (unsigned long)wandev->maddr;
    dev->mem_end    = wandev->maddr + wandev->msize - 1;

        /* Set transmit buffer queue length */
        dev->tx_queue_len = 100;

    /* Initialize socket buffers */
#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;
}


/*===================================================================
 * Name:    if_open(),   Open/Bring up the Netowrk Interface 
 *
 * Purpose:    To bring up a network interface.
 * 
 * Rationale:    
 *                
 * Description:    Open network interface.
 *         o prevent module from unloading by incrementing use count
 *         o if link is disconnected then initiate connection
 *
 * Called by:    Kernel (/usr/src/linux/net/core/dev.c)
 *         (dev->open())
 *
 * Assumptions: None
 *    
 * Warnings:    None
 *
 * Return:     0     Ok
 *         <0     Failur: Interface will not come up.
 */

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);

    /* Increment the number of interfaces */
    ++card->u.x.no_dev;
    
    wanpipe_open(card);

    /* LAPB protocol only uses one interface, thus
     * start the protocol after it comes up. */
    if (card->u.x.LAPB_hdlc){
        if (card->open_cnt == 1){
            TX25Status* status = card->flags;
            S508_S514_lock(card, &smp_flags);
            x25_set_intr_mode(card, INTR_ON_TIMER); 
            status->imask &= ~INTR_ON_TIMER;
            S508_S514_unlock(card, &smp_flags);
        }
    }else{
        /* X25 can have multiple interfaces thus, start the 
         * protocol once all interfaces are up */

        //FIXME: There is a bug here. If interface is
        //brought down and up, it will try to enable comm.
        if (card->open_cnt == card->u.x.num_of_ch){

            S508_S514_lock(card, &smp_flags);
            connect(card);
            S508_S514_unlock(card, &smp_flags);

            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;
#endif
    return 0;
}

/*===================================================================
 * Name:    if_close(),   Close/Bring down the Netowrk Interface 
 *
 * Purpose:    To bring down a network interface.
 * 
 * Rationale:    
 *                
 * Description:    Close network interface.
 *         o decrement use module use count
 *
 * Called by:    Kernel (/usr/src/linux/net/core/dev.c)
 *         (dev->close())
 *        ifconfig <name> down: will trigger the kernel
 *              which will call this function.
 *
 * Assumptions: None
 *    
 * Warnings:    None
 *
 * Return:     0     Ok
 *         <0     Failure: Interface will not exit properly.
 */
static int if_close (netdevice_t* dev)
{
    x25_channel_t* chan = dev->priv;
    sdla_t* card = chan->card;
    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);

    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.
 *
 *     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, 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 = htons(type);
    if (!chan->protocol){
        hdr_len = wanrouter_encapsulate(skb, dev, type);
        if (hdr_len < 0){
            hdr_len = 0;
            skb->protocol = htons(0);
        }
    }
    return hdr_len;
}

/*===============================================================
 *     Re-build media header.
 *
 *     Return:        1    physical address resolved.
 *            0    physical address not resolved
 *==============================================================*/

static int if_rebuild_hdr (struct sk_buff* skb)
{
    netdevice_t *dev = skb->dev; 
    x25_channel_t* chan = dev->priv;
    sdla_t* card = chan->card;

    printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
        card->devname, dev->name);
    return 1;
}


#ifdef LINUX_2_4
/*============================================================================
 * 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)
 *        non-0    packet may be re-transmitted (tbusy must be set)
 *
 *     Notes:
 *     1. This routine is called either by the protocol stack or by the "net
 *        bottom half" (with interrupts enabled).
 *     2. Setting tbusy flag will inhibit further transmit requests from the
 *        protocol stack and can be used for flow control with protocol layer.
 *
 *========================================================================*/

static int if_send (struct sk_buff* skb, netdevice_t* dev)
{
    x25_channel_t* chan = dev->priv;
    sdla_t* card = chan->card;
    TX25Status* status = card->flags;
    int udp_type;
    unsigned long smp_flags=0;

    ++chan->if_send_stat.if_send_entry;

#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);
        
        for( dev2 = card->wandev.dev; dev2; 
             dev2 = *((netdevice_t**)dev2->priv)){

                dev2->tbusy = 0;
        }
        ++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);

        if(udp_type != UDP_INVALID_TYPE) {

                if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, dev, skb,
                        chan->common.lcn)) {

                        status->imask |= INTR_ON_TIMER;
                        if (udp_type == UDP_XPIPE_TYPE){
                                chan->if_send_stat.if_send_PIPE_request++;
            }
                   }
        start_net_queue(dev);
        clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
        S508_S514_unlock(card, &smp_flags);
        return 0;
    }

    if (chan->transmit_length){
        //FIXME: This check doesn't make sense any more
        if (chan->common.state != WAN_CONNECTED){
            chan->transmit_length=0;
            atomic_set(&chan->common.driver_busy,0);
        }else{
            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){
        ++chan->ifstats.tx_dropped;
        ++card->wandev.stats.tx_dropped;
        ++chan->if_send_stat.if_send_wan_disconnected;    
        
    }else if ( chan->protocol && (chan->protocol != skb->protocol)){
        printk(KERN_INFO
            "%s: unsupported Ethertype 0x%04X on interface %s!\n",
            chan->name, htons(skb->protocol), dev->name);
        
        printk(KERN_INFO "PROTO %Xn", htons(chan->protocol));
        ++chan->ifstats.tx_errors;
        ++chan->ifstats.tx_dropped;
        ++card->wandev.stats.tx_dropped;
        ++chan->if_send_stat.if_send_protocol_error;
        
    }else switch (chan->common.state){

        case WAN_DISCONNECTED:
            /* Try to establish connection. If succeded, then start
             * transmission, else drop a packet.
             */
            if (chan->common.usedby == API){
                ++chan->ifstats.tx_dropped;
                ++card->wandev.stats.tx_dropped;
                break;
            }else{
                if (chan_connect(dev) != 0){
                    ++chan->ifstats.tx_dropped;
                    ++card->wandev.stats.tx_dropped;
                    break;
                }
            }
            /* fall through */

        case WAN_CONNECTED:
            if( skb->protocol == htons(ETH_P_IPX)) {
                if(chan->enable_IPX) {
                    switch_net_numbers( skb->data, 
                        chan->network_number, 0);
                } else {
                    ++card->wandev.stats.tx_dropped;
                    ++chan->ifstats.tx_dropped;
                    ++chan->if_send_stat.if_send_protocol_error;
                    goto if_send_crit_exit;
                }
            }
            /* We never drop here, if cannot send than, copy
                     * a packet into a transmit buffer 
                         */
            chan_send(dev, skb->data, skb->len, 0);
            break;

        default:
            ++chan->ifstats.tx_dropped;    
            ++card->wandev.stats.tx_dropped;
            break;
    }


if_send_crit_exit:
    
           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;
}

/*============================================================================
 * 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;
        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 
 */

/*
 * X.25 Interrupt Service Routine.
 */

static void wpx_isr (sdla_t* card)
{
    TX25Status* status = card->flags;

    card->in_isr = 1;
    ++card->statistics.isr_entry;

    if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){
        card->in_isr=0;
        status->iflags = 0;
        return;
    }
    
    if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)){

         printk(KERN_INFO "%s: wpx_isr: wandev.critical set to 0x%02lx, int type = 0x%02x\n", 
            card->devname, card->wandev.critical, status->iflags);
        card->in_isr = 0;
        status->iflags = 0;
        return;
    }

    /* For all interrupts set the critical flag to CRITICAL_RX_INTR.
         * If the if_send routine is called with this flag set it will set
         * the enable transmit flag to 1. (for a delayed interrupt)
         */
    switch (status->iflags){

        case RX_INTR_PENDING:        /* receive interrupt */
            rx_intr(card);
            break;

        case TX_INTR_PENDING:        /* transmit interrupt */
            tx_intr(card);
            break;

        case MODEM_INTR_PENDING:    /* modem status interrupt */
            status_intr(card);
            break;

        case X25_ASY_TRANS_INTR_PENDING:    /* network event interrupt */
            event_intr(card);
            break;

        case TIMER_INTR_PENDING:
            timer_intr(card);
            break;

        default:        /* unwanted interrupt */
            spur_intr(card);
    }

    card->in_isr = 0;
    status->iflags = 0;    /* clear interrupt condition */
}

/*
 *     Receive interrupt handler.
 *     This routine handles fragmented IP packets using M-bit according to the
 *     RFC1356.
 *     o map ligical channel number to network interface.
 *     o allocate socket buffer or append received packet to the existing one.
 *     o if M-bit is reset (i.e. it's the last packet in a sequence) then 
 *       decapsulate packet and pass socket buffer to the protocol stack.
 *
 *     Notes:
 *     1. When allocating a socket buffer, if M-bit is set then more data is
 *        coming and we have to allocate buffer for the maximum IP packet size
 *        expected on this channel.
 *     2. If something goes wrong and X.25 packet has to be dropped (e.g. no
 *        socket buffers available) the whole packet sequence must be discarded.
 */

static void rx_intr (sdla_t* card)
{
    TX25Mbox* rxmb = card->rxmb;
    unsigned lcn = rxmb->cmd.lcn;
    netdevice_t* dev = find_channel(card,lcn);
    x25_channel_t* chan;
    struct sk_buff* skb=NULL;

    if (dev == NULL){
        /* Invalid channel, discard packet */
        printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
            card->devname, lcn);
        return;
    }

    chan = dev->priv;
    chan->i_timeout_sofar = jiffies;


    /* Copy the data from the board, into an
         * skb buffer 
     */
    if (wanpipe_pull_data_in_skb(card,dev,&skb)){
        ++chan->ifstats.rx_dropped;
        ++card->wandev.stats.rx_dropped;
        ++chan->rx_intr_stat.rx_intr_no_socket;
        ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
        return;
    }

    dev->last_rx = jiffies;        /* timestamp */


    /* ------------ API ----------------*/

    if (chan->common.usedby == API){

        if (bh_enqueue(dev, skb)){
            ++chan->ifstats.rx_dropped;
            ++card->wandev.stats.rx_dropped;
            ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
            wan_dev_kfree_skb(skb, FREE_READ);
            return;
        }        

        ++chan->ifstats.rx_packets;
#if defined(LINUX_2_1) || defined(LINUX_2_4)
        chan->ifstats.rx_bytes += skb->len;
#endif
        

        chan->rx_skb = NULL;
        if (!test_and_set_bit(0, &chan->tq_working)){
            wanpipe_queue_tq(&chan->common.wanpipe_task);
            wanpipe_mark_bh();
        }
        return;
    }


    /* ------------- WANPIPE -------------------*/
    
    /* set rx_skb to NULL so we won't access it later when kernel already owns it */
    chan->rx_skb=NULL;
    
    /* Decapsulate packet, if necessary */
    if (!skb->protocol && !wanrouter_type_trans(skb, dev)){
        /* can't decapsulate packet */
                wan_dev_kfree_skb(skb, FREE_READ);
        ++chan->ifstats.rx_errors;
        ++chan->ifstats.rx_dropped;
        ++card->wandev.stats.rx_dropped;
        ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;

    }else{
        if( handle_IPXWAN(skb->data, chan->name, 
                  chan->enable_IPX, chan->network_number, 
                  skb->protocol)){

            if( chan->enable_IPX ){
                if(chan_send(dev, skb->data, skb->len,0)){
                    chan->tx_skb = skb;
                }else{
                                        wan_dev_kfree_skb(skb, FREE_WRITE);
                    ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
                }
            }else{
                /* increment IPX packet dropped statistic */
                ++chan->ifstats.rx_dropped;
                ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
            }
        }else{
            skb->mac.raw = skb->data;
#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;
}


static int wanpipe_pull_data_in_skb (sdla_t *card, netdevice_t *dev, struct sk_buff **skb)
{
    void *bufptr;
    TX25Mbox* rxmb = card->rxmb;
    unsigned len = rxmb->cmd.length;    /* packet length */
    unsigned qdm = rxmb->cmd.qdm;        /* Q,D and M bits */
    x25_channel_t *chan = dev->priv;
    struct sk_buff *new_skb = *skb;

    if (chan->common.usedby == WANPIPE){
        if (chan->drop_sequence){
            if (!(qdm & 0x01)){ 
                chan->drop_sequence = 0;
            }
            return 1;
        }
        new_skb = chan->rx_skb;
    }else{
        /* Add on the API header to the received
                 * data 
         */
        len += sizeof(x25api_hdr_t);
    }

    if (new_skb == NULL){
        int bufsize;

        if (chan->common.usedby == WANPIPE){
            bufsize = (qdm & 0x01) ? dev->mtu : len;
        }else{
            bufsize = len;
        }

        /* Allocate new socket buffer */
        new_skb = dev_alloc_skb(bufsize + dev->hard_header_len);
        if (new_skb == NULL){
            printk(KERN_INFO "%s: no socket buffers available!\n",
                card->devname);
            chan->drop_sequence = 1;    /* set flag */
            ++chan->ifstats.rx_dropped;
            return 1;
        }
    }

    if (skb_tailroom(new_skb) < len){
        /* No room for the packet. Call off the whole thing! */
                wan_dev_kfree_skb(new_skb, FREE_READ);
        if (chan->common.usedby == WANPIPE){
            chan->rx_skb = NULL;
            if (qdm & 0x01){ 
                chan->drop_sequence = 1;
            }
        }

        printk(KERN_INFO "%s: unexpectedly long packet sequence "
            "on interface %s!\n", card->devname, dev->name);
        ++chan->ifstats.rx_length_errors;
        return 1;
    }

    bufptr = skb_put(new_skb,len);


    if (chan->common.usedby == API){
        /* Fill in the x25api header 
         */
        x25api_t * api_data = (x25api_t*)bufptr;
        api_data->hdr.qdm = rxmb->cmd.qdm;
        api_data->hdr.cause = rxmb->cmd.cause;
        api_data->hdr.diagn = rxmb->cmd.diagn;
        api_data->hdr.length = rxmb->cmd.length;
        memcpy(api_data->data, rxmb->data, rxmb->cmd.length);
    }else{
        memcpy(bufptr, rxmb->data, len);
    }

    new_skb->dev = dev;

    if (chan->common.usedby == API){
        new_skb->mac.raw = new_skb->data;
        new_skb->protocol = htons(X25_PROT);
        new_skb->pkt_type = WAN_PACKET_DATA;
    }else{
        new_skb->protocol = chan->protocol;
        chan->rx_skb = new_skb;
    }

    /* If qdm bit is set, more data is coming 
         * thus, exit and wait for more data before
         * sending the packet up. (Used by router only) 
     */
    if ((qdm & 0x01) && (chan->common.usedby == WANPIPE)) 
        return 1;    

    *skb = new_skb; 

    return 0;
}

/*===============================================================
 * tx_intr
 *  
 *     Transmit interrupt handler.
 *    For each dev, check that there is something to send.
 *    If data available, transmit.     
 *
 *===============================================================*/

static void tx_intr (sdla_t* card)
{
    netdevice_t *dev;
    TX25Status* status = card->flags;
    unsigned char more_to_tx=0;
    x25_channel_t *chan=NULL;
    int i=0;    

    if (card->u.x.tx_dev == NULL){
        card->u.x.tx_dev = card->wandev.dev;
    }

    dev = card->u.x.tx_dev;

    for (;;){

        chan = dev->priv;
        if (chan->transmit_length){
            /* Device was set to transmit, check if the TX
                         * buffers are available 
             */        
            if (chan->common.state != WAN_CONNECTED){
                chan->transmit_length = 0;
                atomic_set(&chan->common.driver_busy,0);
                chan->tx_offset=0;
                if (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;

        case WAN_CONNECTING:
            poll_connecting(card);
            break;

        case WAN_DISCONNECTED:
            poll_disconnected(card);
            break;
    }

wpx_poll_exit:
    clear_bit(POLL_CRIT,&card->wandev.critical);
    return;
}

static void trigger_x25_poll(sdla_t *card)
{
#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)
{
    volatile TX25Status* status = card->flags;

    if (status->gflags & X25_HDLC_ABM){

        timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_ON);

    }else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT){

        timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_OFF);

    }
}

/*====================================================================
 *     Handle physical link disconnected phase.
 *     o if hold-down timeout has expired and there are open interfaces, 
 *    connect link.
 *===================================================================*/

static void poll_disconnected (sdla_t* card)
{
    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.
 *===================================================================*/

static void poll_active (sdla_t* card)
{
    netdevice_t* dev;
    TX25Status* status = card->flags;

    for (dev = card->wandev.dev; dev; dev = *((netdevice_t**)dev->priv)){
        x25_channel_t* chan = dev->priv;

        /* If SVC has been idle long enough, close virtual circuit */
        if ( chan->common.svc && 
             chan->common.state == WAN_CONNECTED &&
             chan->common.usedby == WANPIPE ){
        
            if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout ){
                /* Close svc */
                card->u.x.poll_device=dev;
                timer_intr_exec    (card, TMR_INT_ENABLED_POLL_ACTIVE);
            }
        }

#ifdef PRINT_DEBUG
        chan->ifstats.tx_compressed = atomic_read(&chan->common.command);
        chan->ifstats.tx_errors = chan->common.state;
        chan->ifstats.rx_fifo_errors = atomic_read(&card->u.x.command_busy);
        ++chan->ifstats.tx_bytes;

        chan->ifstats.rx_fifo_errors=atomic_read(&chan->common.disconnect);
        chan->ifstats.multicast=atomic_read(&chan->bh_buff_used);
        chan->ifstats.rx_length_errors=*card->u.x.hdlc_buf_status;
#endif    

        if (chan->common.usedby == API && 
            atomic_read(&chan->common.command) && 
                !card->u.x.LAPB_hdlc){

            if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC)) 
                card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;

            if (!(status->imask & INTR_ON_TIMER))
                status->imask |= INTR_ON_TIMER;
        }    

        if ((chan->common.usedby == API) && 
             atomic_read(&chan->common.disconnect)){

            if (chan->common.state == WAN_DISCONNECTED){
                atomic_set(&chan->common.disconnect,0);
                return;
            }

            atomic_set(&chan->common.command,X25_CLEAR_CALL);
            if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC)) 
                card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;

            if (!(status->imask & INTR_ON_TIMER))
                status->imask |= INTR_ON_TIMER;
        }
    }
}

static void timer_intr_exec(sdla_t *card, unsigned char TYPE)
{
    TX25Status* status = card->flags;
    card->u.x.timer_int_enabled |= TYPE;
    if (!(status->imask & INTR_ON_TIMER))
        status->imask |= INTR_ON_TIMER;
}


/*==================================================================== 
 * SDLA Firmware-Specific Functions 
 *
 *  Almost all X.25 commands can unexpetedly fail due to so called 'X.25
 *  asynchronous events' such as restart, interrupt, incoming call request,
 *  call clear request, etc.  They can't be ignored and have to be delt with
 *  immediately.  To tackle with this problem we execute each interface 
 *  command in a loop until good return code is received or maximum number 
 *  of retries is reached.  Each interface command returns non-zero return 
 *  code, an asynchronous event/error handler x25_error() is called.
 *====================================================================*/

/*====================================================================
 *     Read X.25 firmware version.
 *        Put code version as ASCII string in str. 
 *===================================================================*/

static int x25_get_version (sdla_t* card, char* str)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_READ_CODE_VERSION;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- &&
         x25_error(card, err, X25_READ_CODE_VERSION, 0));

    if (!err && str)
    {
        int len = mbox->cmd.length;

        memcpy(str, mbox->data, len);
        str[len] = '\0';
    }
    return err;
}

/*====================================================================
 *     Configure adapter.
 *===================================================================*/

static int x25_configure (sdla_t* card, TX25Config* conf)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do{
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
        mbox->cmd.length  = sizeof(TX25Config);
        mbox->cmd.command = X25_SET_CONFIGURATION;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));
    return err;
}

/*====================================================================
 *     Configure adapter for HDLC only.
 *===================================================================*/

static int hdlc_configure (sdla_t* card, TX25Config* conf)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do{
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
        mbox->cmd.length  = sizeof(TX25Config);
        mbox->cmd.command = X25_HDLC_SET_CONFIG;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));

    return err;
}

static int set_hdlc_level (sdla_t* card)
{

    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do{
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = SET_PROTOCOL_LEVEL;
        mbox->cmd.length = 1;
        mbox->data[0] = HDLC_LEVEL; //| DO_HDLC_LEVEL_ERROR_CHECKING;     
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, SET_PROTOCOL_LEVEL, 0));

    return err;
}



/*====================================================================
 * Get communications error statistics.
 *====================================================================*/

static int x25_get_err_stats (sdla_t* card)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0));
    
    if (!err)
    {
        THdlcCommErr* stats = (void*)mbox->data;

        card->wandev.stats.rx_over_errors    = stats->rxOverrun;
        card->wandev.stats.rx_crc_errors     = stats->rxBadCrc;
        card->wandev.stats.rx_missed_errors  = stats->rxAborted;
        card->wandev.stats.tx_aborted_errors = stats->txAborted;
    }
    return err;
}

/*====================================================================
 *     Get protocol statistics.
 *===================================================================*/

static int x25_get_stats (sdla_t* card)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_READ_STATISTICS;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0)) ;
    
    if (!err)
    {
        TX25Stats* stats = (void*)mbox->data;

        card->wandev.stats.rx_packets = stats->rxData;
        card->wandev.stats.tx_packets = stats->txData;
    }
    return err;
}

/*====================================================================
 *     Close HDLC link.
 *===================================================================*/

static int x25_close_hdlc (sdla_t* card)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_HDLC_LINK_CLOSE;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_CLOSE, 0));
    
    return err;
}


/*====================================================================
 *     Open HDLC link.
 *===================================================================*/

static int x25_open_hdlc (sdla_t* card)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_HDLC_LINK_OPEN;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_OPEN, 0));

    return err;
}

/*=====================================================================
 * Setup HDLC link.
 *====================================================================*/
static int x25_setup_hdlc (sdla_t* card)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_HDLC_LINK_SETUP;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_SETUP, 0));
    
    return err;
}

/*====================================================================
 * Set (raise/drop) DTR.
 *===================================================================*/

static int x25_set_dtr (sdla_t* card, int dtr)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->data[0] = 0;
        mbox->data[2] = 0;
        mbox->data[1] = dtr ? 0x02 : 0x01;
        mbox->cmd.length  = 3;
        mbox->cmd.command = X25_SET_GLOBAL_VARS;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_SET_GLOBAL_VARS, 0));
    
    return err;
}

/*====================================================================
 *     Set interrupt mode.
 *===================================================================*/

static int x25_set_intr_mode (sdla_t* card, int mode)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->data[0] = mode;
        if (card->hw.fwid == SFID_X25_508){
            mbox->data[1] = card->hw.irq;
            mbox->data[2] = 2;
            mbox->cmd.length = 3;
        }else {
             mbox->cmd.length  = 1;
        }
        mbox->cmd.command = X25_SET_INTERRUPT_MODE;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0));
    
    return err;
}

/*====================================================================
 *     Read X.25 channel configuration.
 *===================================================================*/

static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int lcn = chan->common.lcn;
    int err;

    do{
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.lcn     = lcn;
        mbox->cmd.command = X25_READ_CHANNEL_CONFIG;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn));

    if (!err)
    {
        TX25Status* status = card->flags;

        /* calculate an offset into the array of status bytes */
        if (card->u.x.hi_svc <= X25_MAX_CHAN){ 

            chan->ch_idx = lcn - 1;

        }else{
            int offset;

            /* FIX: Apr 14 2000 : Nenad Corbic
             * The data field was being compared to 0x1F using
                         * '&&' instead of '&'. 
             * This caused X25API to fail for LCNs greater than 255.
             */
            switch (mbox->data[0] & 0x1F)
            {
                case 0x01: 
                    offset = status->pvc_map; break;
                case 0x03: 
                    offset = status->icc_map; break;
                case 0x07: 
                    offset = status->twc_map; break;
                case 0x0B: 
                    offset = status->ogc_map; break;
                default: 
                    offset = 0;
            }
            chan->ch_idx = lcn - 1 - offset;
        }

        /* get actual transmit packet size on this channel */
        switch(mbox->data[1] & 0x38)
        {
            case 0x00: 
                chan->tx_pkt_size = 16; 
                break;
            case 0x08: 
                chan->tx_pkt_size = 32; 
                break;
            case 0x10: 
                chan->tx_pkt_size = 64; 
                break;
            case 0x18: 
                chan->tx_pkt_size = 128; 
                break;
            case 0x20: 
                chan->tx_pkt_size = 256; 
                break;
            case 0x28: 
                chan->tx_pkt_size = 512; 
                break;
            case 0x30: 
                chan->tx_pkt_size = 1024; 
                break;
        }
        if (card->u.x.logging)
            printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
                card->devname, lcn, chan->tx_pkt_size);
    }
    return err;
}

/*====================================================================
 *     Place X.25 call.
 *====================================================================*/

static int x25_place_call (sdla_t* card, x25_channel_t* chan)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;
    char str[64];


    if (chan->protocol == htons(ETH_P_IP)){
        sprintf(str, "-d%s -uCC", chan->addr);
    
    }else if (chan->protocol == htons(ETH_P_IPX)){
        sprintf(str, "-d%s -u800000008137", chan->addr);
    
    }
    
    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        strcpy(mbox->data, str);
        mbox->cmd.length  = strlen(str);
        mbox->cmd.command = X25_PLACE_CALL;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0));

    if (!err){
        bind_lcn_to_dev (card, chan->dev, mbox->cmd.lcn);
    }
    return err;
}

/*====================================================================
 *     Accept X.25 call.
 *====================================================================*/

static int x25_accept_call (sdla_t* card, int lcn, int qdm)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.lcn     = lcn;
        mbox->cmd.qdm     = qdm;
        mbox->cmd.command = X25_ACCEPT_CALL;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_ACCEPT_CALL, lcn));
    
    return err;
}

/*====================================================================
 *     Clear X.25 call.
 *====================================================================*/

static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.lcn     = lcn;
        mbox->cmd.cause   = cause;
        mbox->cmd.diagn   = diagn;
        mbox->cmd.command = X25_CLEAR_CALL;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, X25_CLEAR_CALL, lcn));
    
    return err;
}

/*====================================================================
 *     Send X.25 data packet.
 *====================================================================*/

static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf)
{
    TX25Mbox* mbox = card->mbox;
      int retry = MAX_CMD_RETRY;
    int err;
    unsigned char cmd;
        
    if (card->u.x.LAPB_hdlc)
        cmd = X25_HDLC_WRITE;
    else
        cmd = X25_WRITE;

    do
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        memcpy(mbox->data, buf, len);
        mbox->cmd.length  = len;
        mbox->cmd.lcn     = lcn;

        if (card->u.x.LAPB_hdlc){
            mbox->cmd.pf = qdm;
        }else{            
            mbox->cmd.qdm = qdm;
        }

        mbox->cmd.command = cmd;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
    } while (err && retry-- && x25_error(card, err, cmd , lcn));


    /* If buffers are busy the return code for LAPB HDLC is
         * 1. The above functions are looking for return code
         * of X25RES_NOT_READY if busy. */

    if (card->u.x.LAPB_hdlc && err == 1){
        err = X25RES_NOT_READY;
    }

    return err;
}

/*====================================================================
 *     Fetch X.25 asynchronous events.
 *===================================================================*/

static int x25_fetch_events (sdla_t* card)
{
    TX25Status* status = card->flags;
    TX25Mbox* mbox = card->mbox;
    int err = 0;

    if (status->gflags & 0x20)
    {
        memset(&mbox->cmd, 0, sizeof(TX25Cmd));
        mbox->cmd.command = X25_IS_DATA_AVAILABLE;
        err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
         if (err) x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
    }
    return err;
}

/*====================================================================
 *     X.25 asynchronous event/error handler.
 *        This routine is called each time interface command returns 
 *        non-zero return code to handle X.25 asynchronous events and 
 *        common errors. Return non-zero to repeat command or zero to 
 *        cancel it.
 *
 *     Notes:
 *     1. This function may be called recursively, as handling some of the
 *        asynchronous events (e.g. call request) requires execution of the
 *        interface command(s) that, in turn, may also return asynchronous
 *        events.  To avoid re-entrancy problems we copy mailbox to dynamically
 *        allocated memory before processing events.
 *====================================================================*/

static int x25_error (sdla_t* card, int err, int cmd, int lcn)
{
    int retry = 1;
    unsigned dlen = ((TX25Mbox*)card->mbox)->cmd.length;
    TX25Mbox* mb;

    mb = kmalloc(sizeof(TX25Mbox) + dlen, GFP_ATOMIC);
    if (mb == NULL)
    {
        printk(KERN_ERR "%s: x25_error() out of memory!\n",
            card->devname);
        return 0;
    }
    memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen);
    switch (err){

    case X25RES_ASYNC_PACKET:    /* X.25 asynchronous packet was received */

        mb->data[dlen] = '\0';

        switch (mb->cmd.pktType & 0x7F){

        case ASE_CALL_RQST:        /* incoming call */
            retry = incoming_call(card, cmd, lcn, mb);
            break;

        case ASE_CALL_ACCEPTED:        /* connected */
            retry = call_accepted(card, cmd, lcn, mb);
            break;

        case ASE_CLEAR_RQST:        /* call clear request */
            retry = call_cleared(card, cmd, lcn, mb);
            break;

        case ASE_RESET_RQST:        /* reset request */
            printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
                "Cause:0x%02X Diagn:0x%02X\n",
                card->devname, mb->cmd.lcn, mb->cmd.cause,
                mb->cmd.diagn);
            api_oob_event (card,mb);
            break;

        case ASE_RESTART_RQST:        /* restart request */
            retry = restart_event(card, cmd, lcn, mb);
            break;

        case ASE_CLEAR_CONFRM:
            if (clear_confirm_event (card,mb))
                break;

            /* I use the goto statement here so if 
                     * somebody inserts code between the
                     * case and default, we will not have
                     * ghost problems */

            goto dflt_1;

        default:
dflt_1:
            printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
                "Cause:0x%02X Diagn:0x%02X\n",
                card->devname, mb->cmd.pktType,
                mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn);
        }
        break;

    case X25RES_PROTO_VIOLATION:    /* X.25 protocol violation indication */

        /* Bug Fix: Mar 14 2000
                 * The Protocol violation error conditions were  
                 * not 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 PVE_RESTART_RQST:    /* Restart request */
            retry = restart_event(card, cmd, lcn, mb);
            break;

        default :
            printk(KERN_INFO
                "%s: X.25 protocol violation on LCN %d! "
                "Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
                card->devname, mb->cmd.lcn,
                mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn);
            api_oob_event(card,mb);
        }
        break;

    case 0x42:    /* X.25 timeout */
        retry = timeout_event(card, cmd, lcn, mb);
        break;

    case 0x43:    /* X.25 retry limit exceeded */
        printk(KERN_INFO
            "%s: exceeded X.25 retry limit on LCN %d! "
            "Packet:0x%02X Diagn:0x%02X\n", card->devname,
            mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn)
        ;
        break;

    case 0x08:    /* modem failure */
#ifndef MODEM_NOT_LOG
        printk(KERN_INFO "%s: modem failure!\n", card->devname);
#endif /* MODEM_NOT_LOG */
        api_oob_event(card,mb);
        break;

    case 0x09:    /* N2 retry limit */
        printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
            card->devname);
        api_oob_event(card,mb);
        break;

    case 0x06:    /* unnumbered frame was received while in ABM */
        printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
            card->devname, mb->data[0]);
        api_oob_event(card,mb);
        break;

    case CMD_TIMEOUT:
        printk(KERN_ERR "%s: command 0x%02X timed out!\n",
            card->devname, cmd)
        ;
        retry = 0;    /* abort command */
        break;

    case X25RES_NOT_READY:
        retry = 1;
        break;

    case 0x01:
        if (card->u.x.LAPB_hdlc)
            break;

        if (mb->cmd.command == 0x16)
            break;
        /* I use the goto statement here so if 
                 * somebody inserts code between the
                 * case and default, we will not have
                 * ghost problems */
        goto dflt_2;

    default:
dflt_2:
        printk(KERN_INFO "%s: command 0x%02X returned 0x%02X! Lcn %i\n",
            card->devname, cmd, err, mb->cmd.lcn)
        ;
        retry = 0;    /* abort command */
    }
    kfree(mb);
    return retry;
}

/*==================================================================== 
 *    X.25 Asynchronous Event Handlers
 *     These functions are called by the x25_error() and should return 0, if
 *     the command resulting in the asynchronous event must be aborted.
 *====================================================================*/



/*====================================================================
 *Handle X.25 incoming call request.
 *    RFC 1356 establishes the following rules:
 *    1. The first octet in the Call User Data (CUD) field of the call
 *            request packet contains NLPID identifying protocol encapsulation
 *     2. Calls MUST NOT be accepted unless router supports requested
 *          protocol encapsulation.
 *    3. A diagnostic code 249 defined by ISO/IEC 8208 may be used 
 *       when clearing a call because protocol encapsulation is not 
 *       supported.
 *    4. If an incoming call is received while a call request is 
 *       pending (i.e. call collision has 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;
    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 */
//FIXME: THIS IS NOT TURE !!!! TAKE IT OUT
//    if (mb->cmd.qdm & 0x02)
//    {
//        printk(KERN_INFO
//            "%s: X.25 incoming call on LCN %d with D-bit set!\n",
//            card->devname, new_lcn);
//
//        x25_clear_call(card, new_lcn, 0, 0);
//        return 1;
//    }

    /* Parse call request data */
    info = kmalloc(sizeof(x25_call_info_t), GFP_ATOMIC);
    if (info == NULL)
    {
        printk(KERN_ERR
            "%s: not enough memory to parse X.25 incoming call "
            "on LCN %d!\n", card->devname, new_lcn);
        x25_clear_call(card, new_lcn, 0, 0);
        return 1;
    }
 
    parse_call_info(mb->data, info);

    if (card->u.x.logging)
        printk(KERN_INFO "\n%s: X.25 incoming call on LCN %d!\n",
            card->devname, new_lcn);

    /* Conver the first two ASCII characters into an
         * interger. Used to check the incoming protocol 
         */
    user_data = hex_to_uint(info->user,2);

    /* Find available channel */
    for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
        chan = dev->priv;

        if (chan->common.usedby == API)
            continue;

        if (!chan->common.svc || (chan->common.state != WAN_DISCONNECTED))
            continue;

        if (user_data == NLPID_IP && chan->protocol != htons(ETH_P_IP)){
            printk(KERN_INFO "IP packet but configured for IPX : %x, %x\n",
                       htons(chan->protocol), info->user[0]);
            continue;
        }
    
        if (user_data == NLPID_SNAP && chan->protocol != htons(ETH_P_IPX)){
            printk(KERN_INFO "IPX packet but configured for IP: %x\n",
                       htons(chan->protocol));
            continue;
        }
        if (strcmp(info->src, chan->addr) == 0)
            break;

            /* If just an '@' is specified, accept all incoming calls */
            if (strcmp(chan->addr, "") == 0)
                    break;
    }

    if (dev == NULL){

        /* If the call is not for any WANPIPE interfaces
                 * check to see if there is an API listening queue
                 * waiting for data. If there is send the packet
                 * up the stack.
                 */
        if (card->sk != NULL && card->func != NULL){
            if (api_incoming_call(card,mb,new_lcn)){
                x25_clear_call(card, new_lcn, 0, 0);
            }
            accept = 0;
        }else{
            printk(KERN_INFO "%s: no channels available!\n",
                card->devname);
            
            x25_clear_call(card, new_lcn, 0, 0);
        }

    }else if (info->nuser == 0){

        printk(KERN_INFO
            "%s: no user data in incoming call on LCN %d!\n",
            card->devname, new_lcn)
        ;
        x25_clear_call(card, new_lcn, 0, 0);

    }else switch (info->user[0]){

        case 0:        /* multiplexed */
            chan->protocol = htons(0);
            accept = 1;
            break;

        case NLPID_IP:    /* IP datagrams */
            accept = 1;
            break;

        case NLPID_SNAP: /* IPX datagrams */
            accept = 1;
            break;

        default:
            printk(KERN_INFO
                "%s: unsupported NLPID 0x%02X in incoming call "
                "on LCN %d!\n", card->devname, info->user[0], new_lcn);
            x25_clear_call(card, new_lcn, 0, 249);
    }
    
    if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK)){

        bind_lcn_to_dev (card, chan->dev, new_lcn);
        
        if (x25_get_chan_conf(card, chan) == CMD_OK)
            set_chan_state(dev, WAN_CONNECTED);
        else 
            x25_clear_call(card, new_lcn, 0, 0);
    }
    kfree(info);
    return 1;
}

/*====================================================================
 *     Handle accepted call.
 *====================================================================*/

static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
{
    unsigned new_lcn = mb->cmd.lcn;
    netdevice_t* dev = find_channel(card, new_lcn);
    x25_channel_t* chan;

    if (dev == NULL){
        printk(KERN_INFO
            "%s: clearing orphaned connection on LCN %d!\n",
            card->devname, new_lcn);
        x25_clear_call(card, new_lcn, 0, 0);
        return 1;
    }

    if (card->u.x.logging)    
        printk(KERN_INFO "%s: X.25 call accepted on Dev %s and LCN %d!\n",
            card->devname, dev->name, new_lcn);

    /* Get channel configuration and notify router */
    chan = dev->priv;
    if (x25_get_chan_conf(card, chan) != CMD_OK)
    {
        x25_clear_call(card, new_lcn, 0, 0);
        return 1;
    }

    set_chan_state(dev, WAN_CONNECTED);

    if (chan->common.usedby == API){
        send_delayed_cmd_result(card,dev,mb);
        bind_lcn_to_dev (card, dev, new_lcn);
    }

    return 1;
}

/*====================================================================
 *     Handle cleared call.
 *====================================================================*/

static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
{
    unsigned new_lcn = mb->cmd.lcn;
    netdevice_t* dev = find_channel(card, new_lcn);
    x25_channel_t *chan;
    unsigned char old_state;

    if (card->u.x.logging){
        printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
        "Diagn:0x%02X\n",
        card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn);
    }

    if (dev == NULL){ 
        printk(KERN_INFO "%s: X.25 clear request : No device for clear\n",
                card->devname);
        return 1;
    }

    chan=dev->priv;

    old_state = chan->common.state;

    set_chan_state(dev, WAN_DISCONNECTED);

    if (chan->common.usedby == API){

        switch (old_state){
        
        case WAN_CONNECTING:
            send_delayed_cmd_result(card,dev,mb);
            break;
        case WAN_CONNECTED:
            send_oob_msg(card,dev,mb);                
            break;
        }
    }
    
    return ((cmd == X25_WRITE) && (lcn == new_lcn)) ? 0 : 1;
}

/*====================================================================
 *     Handle X.25 restart event.
 *====================================================================*/

static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
{
    wan_device_t* wandev = &card->wandev;
    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 */
    for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv)){
        chan=dev->priv;
        old_state = chan->common.state;

        set_chan_state(dev, WAN_DISCONNECTED);

        if (chan->common.usedby == API){
            switch (old_state){
        
            case WAN_CONNECTING:
                send_delayed_cmd_result(card,dev,mb);
                break;
            case WAN_CONNECTED:
                send_oob_msg(card,dev,mb);                
                break;
            }
        }
    }
    return (cmd == X25_WRITE) ? 0 : 1;
}

/*====================================================================
 * Handle timeout event.
 *====================================================================*/

static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
{
    unsigned new_lcn = mb->cmd.lcn;

    if (mb->cmd.pktType == 0x05)    /* call request time out */
    {
        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){
            x25_channel_t *chan = dev->priv;
            set_chan_state(dev, WAN_DISCONNECTED);

            if (chan->common.usedby == API){
                send_delayed_cmd_result(card,dev,card->mbox);
            }
        }
    }else{ 
        printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
        card->devname, mb->cmd.pktType, new_lcn);
    }
    return 1;
}

/* 
 *    Miscellaneous 
 */

/*====================================================================
 *     Establish physical connection.
 *     o open HDLC and raise DTR
 *
 *     Return:        0    connection established
 *            1    connection is in progress
 *            <0    error
 *===================================================================*/

static int connect (sdla_t* card)
{
    TX25Status* status = card->flags;

    if (x25_open_hdlc(card) || x25_setup_hdlc(card))
        return -EIO;

    wanpipe_set_state(card, WAN_CONNECTING);

    x25_set_intr_mode(card, INTR_ON_TIMER); 
    status->imask &= ~INTR_ON_TIMER;

    return 1;
}

/*
 *     Tear down physical connection.
 *     o close HDLC link
 *     o drop DTR
 *
 *     Return:        0
 *            <0    error
 */

static int disconnect (sdla_t* card)
{
    wanpipe_set_state(card, WAN_DISCONNECTED);
    x25_set_intr_mode(card, INTR_ON_TIMER);    /* disable all interrupt except timer */
    x25_close_hdlc(card);            /* close HDLC link */
    x25_set_dtr(card, 0);            /* drop DTR */
    return 0;
}

/*
 * Find network device by its channel number.
 */

static netdevice_t* get_dev_by_lcn (wan_device_t* wandev, unsigned lcn)
{
    netdevice_t* dev;

    for (dev = wandev->dev; dev; dev = *((netdevice_t**)dev->priv))
        if (((x25_channel_t*)dev->priv)->common.lcn == lcn) 
            break;
    return dev;
}

/*
 *     Initiate connection on the logical channel.
 *     o for PVC we just get channel configuration
 *     o for SVCs place an X.25 call
 *
 *     Return:        0    connected
 *            >0    connection in progress
 *            <0    failure
 */

static int chan_connect (netdevice_t* dev)
{
    x25_channel_t* chan = dev->priv;
    sdla_t* card = chan->card;

    if (chan->common.svc && chan->common.usedby == WANPIPE){
        if (!chan->addr[0]){
            printk(KERN_INFO "%s: No Destination Address\n",
                    card->devname);
            return -EINVAL;    /* no destination address */
        }
        printk(KERN_INFO "%s: placing X.25 call to %s ...\n",
            card->devname, chan->addr);

        if (x25_place_call(card, chan) != CMD_OK)
            return -EIO;

        set_chan_state(dev, WAN_CONNECTING);
        return 1;
    }else{
        if (x25_get_chan_conf(card, chan) != CMD_OK)
            return -EIO;

        set_chan_state(dev, WAN_CONNECTED);
    }
    return 0;
}

/*
 *     Disconnect logical channel.
 *     o if SVC then clear X.25 call
 */

static int chan_disc (netdevice_t* dev)
{
    x25_channel_t* chan = dev->priv;

    if (chan->common.svc){ 
        x25_clear_call(chan->card, chan->common.lcn, 0, 0);

        /* For API we disconnect on clear
                 * confirmation. 
                 */
        if (chan->common.usedby == API)
            return 0;
    }

    set_chan_state(dev, WAN_DISCONNECTED);
    
    return 0;
}

/*
 *     Set logical channel state.
 */

static void set_chan_state (netdevice_t* dev, int state)
{
    x25_channel_t* chan = dev->priv;
    sdla_t* card = chan->card;
    unsigned long flags;

    save_flags(flags);
    cli();
    if (chan->common.state != state)
    {
        switch (state)
        {
            case WAN_CONNECTED:
                if (card->u.x.logging){
                    printk (KERN_INFO 
                        "%s: interface %s connected, lcn %i !\n", 
                        card->devname, dev->name,chan->common.lcn);
                }
                *(unsigned short*)dev->dev_addr = htons(chan->common.lcn);
                chan->i_timeout_sofar = jiffies;

                /* LAPB is PVC Based */
                if (card->u.x.LAPB_hdlc)
                    chan->common.svc=0;
                break;

            case WAN_CONNECTING:
                if (card->u.x.logging){
                    printk (KERN_INFO 
                        "%s: interface %s connecting, lcn %i ...\n", 
                        card->devname, dev->name, chan->common.lcn);
                }
                break;

            case WAN_DISCONNECTED:
                if (card->u.x.logging){
                    printk (KERN_INFO 
                        "%s: interface %s disconnected, lcn %i !\n", 
                        card->devname, dev->name,chan->common.lcn);
                }
                atomic_set(&chan->common.disconnect,0);
                
                if (chan->common.svc) {
                    *(unsigned short*)dev->dev_addr = 0;
                    card->u.x.svc_to_dev_map[(chan->common.lcn%X25_MAX_CHAN)]=NULL;
                            chan->common.lcn = 0;
                }

                if (chan->transmit_length){
                    chan->transmit_length=0;
                    atomic_set(&chan->common.driver_busy,0);
                    chan->tx_offset=0;
                    if (is_queue_stopped(dev)){
                        wake_net_dev(dev);
                    }
                }
                atomic_set(&chan->common.command,0);
                break;

            case WAN_DISCONNECTING:
                if (card->u.x.logging){
                    printk (KERN_INFO 
                    "\n%s: interface %s disconnecting, lcn %i ...\n", 
                    card->devname, dev->name,chan->common.lcn);
                }
                atomic_set(&chan->common.disconnect,0);
                break;
        }
        chan->common.state = state;
    }
    chan->state_tick = jiffies;
    restore_flags(flags);
}

/*
 *     Send packet on a logical channel.
 *        When this function is called, tx_skb field of the channel data 
 *        space points to the transmit socket buffer.  When transmission 
 *        is complete, release socket buffer and reset 'tbusy' flag.
 *
 *     Return:        0    - transmission complete
 *            1    - busy
 *
 *     Notes:
 *     1. If packet length is greater than MTU for this channel, we'll fragment
 *        the packet into 'complete sequence' using M-bit.
 *     2. When transmission is complete, an event notification should be issued
 *        to the router.
 */

static int chan_send (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=0, qdm=0, res=0, orig_len = 0;
    void *data;

    /* Check to see if channel is ready */
    if ((!(status->cflags[chan->ch_idx] & 0x40) && !card->u.x.LAPB_hdlc)  || 
             !(*card->u.x.hdlc_buf_status & 0x40)){ 
            
        if (!tx_intr){
            setup_for_delayed_transmit (dev, buff, data_len);
            return 0;
        }else{
            /* By returning 0 to tx_intr the packet will be dropped */
            ++card->wandev.stats.tx_dropped;
            ++chan->ifstats.tx_dropped;
            printk(KERN_INFO "%s: ERROR, Tx intr could not send, dropping %s:\n", 
                card->devname,dev->name);
            ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
            return 0;
        }
    }

    if (chan->common.usedby == API){
        /* Remove the API Header */
        x25api_hdr_t *api_data = (x25api_hdr_t *)buff;

        /* Set the qdm bits from the packet header 
                 * User has the option to set the qdm bits
                 */
        qdm = api_data->qdm;

        orig_len = len = data_len - sizeof(x25api_hdr_t);
        data = (unsigned char*)buff + sizeof(x25api_hdr_t);
    }else{
        data = buff;
        orig_len = len = data_len;
    }    

    if (tx_intr){
        /* We are in tx_intr, minus the tx_offset from 
                 * the total length. The tx_offset part of the
         * data has already been sent. Also, move the 
         * data pointer to proper offset location.
                 */
        len -= chan->tx_offset;
        data = (unsigned char*)data + chan->tx_offset;
    }
        
    /* Check if the packet length is greater than MTU
         * If YES: Cut the len to MTU and set the M bit 
         */
    if (len > chan->tx_pkt_size && !card->u.x.LAPB_hdlc){
        len = chan->tx_pkt_size;
        qdm |= M_BIT;        
    } 


    /* Pass only first three bits of the qdm byte to the send
         * routine. In case user sets any other bit which might
         * cause errors. 
         */

    switch(x25_send(card, chan->common.lcn, (qdm&0x07), len, data)){
        case 0x00:    /* success */
            chan->i_timeout_sofar = jiffies;

#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;
            }
            break;

        case 0x33:    /* Tx busy */
            if (tx_intr){
                printk(KERN_INFO "%s: Tx_intr: Big Error dropping packet %s\n",
                        card->devname,dev->name);
                ++chan->ifstats.tx_dropped;
                ++card->wandev.stats.tx_dropped;
                ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
                res=0;
            }else{
                DBG_PRINTK(KERN_INFO 
                    "%s: Send: Big Error should have tx: storring %s\n",
                        card->devname,dev->name);
                setup_for_delayed_transmit (dev, buff, data_len);    
                res=1;
            }
            break;

        default:    /* failure */
            ++chan->ifstats.tx_errors;
            if (tx_intr){
                printk(KERN_INFO "%s: Tx_intr: Failure to send, dropping %s\n",
                    card->devname,dev->name);
                ++chan->ifstats.tx_dropped;
                ++card->wandev.stats.tx_dropped;
                ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
                res=0;
            }else{
                DBG_PRINTK(KERN_INFO "%s: Send: Failure to send !!!, storing %s\n",
                    card->devname,dev->name);            
                setup_for_delayed_transmit (dev, buff, data_len);
                res=1;
            }
            break;    
    }
    return res;
}


/*
 *     Parse X.25 call request data and fill x25_call_info_t structure.
 */

static void parse_call_info (unsigned char* str, x25_call_info_t* info)
{
    memset(info, 0, sizeof(x25_call_info_t));
    for (; *str; ++str)
    {
        int i;
        unsigned char ch;

        if (*str == '-') switch (str[1]) {

            /* Take minus 2 off the maximum size so that 
                         * last byte is 0. This way we can use string
                         * manipulaton functions on call information.
                         */

            case 'd':    /* destination address */
                for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
                    ch = str[2+i];
                    if (isspace(ch)) break;
                    info->dest[i] = ch;
                }
                break;

            case 's':    /* source address */
                for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
                    ch = str[2+i];
                    if (isspace(ch)) break;
                    info->src[i] = ch;
                }
                break;

            case 'u':    /* user data */
                for (i = 0; i < (MAX_X25_DATA_SIZE-2); ++i){
                    ch = str[2+i];
                    if (isspace(ch)) break;
                    info->user[i] = ch; 
                }
                info->nuser = i;
                break;

            case 'f':    /* facilities */
                for (i = 0; i < (MAX_X25_FACL_SIZE-2); ++i){
                    ch = str[2+i];
                    if (isspace(ch)) break;
                    info->facil[i] = ch;
                }
                info->nfacil = i;
                break;
        }
    }
}

/*
 *     Convert line speed in bps to a number used by S502 code.
 */

static unsigned char bps_to_speed_code (unsigned long bps)
{
    unsigned char    number;

    if (bps <= 1200)        number = 0x01;
    else if (bps <= 2400)   number = 0x02;
    else if (bps <= 4800)   number = 0x03;
    else if (bps <= 9600)   number = 0x04;
    else if (bps <= 19200)  number = 0x05;
    else if (bps <= 38400)  number = 0x06;
    else if (bps <= 45000)  number = 0x07;
    else if (bps <= 56000)  number = 0x08;
    else if (bps <= 64000)  number = 0x09;
    else if (bps <= 74000)  number = 0x0A;
    else if (bps <= 112000) number = 0x0B;
    else if (bps <= 128000) number = 0x0C;
    else number = 0x0D;

    return number;
}

/*
 *     Convert decimal string to unsigned integer.
 *     If len != 0 then only 'len' characters of the string are converted.
 */

static unsigned int dec_to_uint (unsigned char* str, int len)
{
    unsigned val;

    if (!len) 
        len = strlen(str);

    for (val = 0; len && is_digit(*str); ++str, --len)
        val = (val * 10) + (*str - (unsigned)'0');
    
    return val;
}

/*
 *     Convert hex string to unsigned integer.
 *     If len != 0 then only 'len' characters of the string are conferted.
 */

static unsigned int hex_to_uint (unsigned char* str, int len)
{
    unsigned val, ch;

    if (!len) 
        len = strlen(str);

    for (val = 0; len; ++str, --len)
    {
        ch = *str;
        if (is_digit(ch))
            val = (val << 4) + (ch - (unsigned)'0');
        else if (is_hex_digit(ch))
            val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10);
        else break;
    }
    return val;
}


static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto)
{
    int i;

    if( proto == ETH_P_IPX) {
        /* It's an IPX packet */
        if(!enable_IPX) {
            /* Return 1 so we don't pass it up the stack. */
            return 1;
        }
    } else {
        /* It's not IPX so pass it up the stack.*/ 
        return 0;
    }

    if( sendpacket[16] == 0x90 &&
        sendpacket[17] == 0x04)
    {
        /* It's IPXWAN  */

        if( sendpacket[2] == 0x02 &&
            sendpacket[34] == 0x00)
        {
            /* It's a timer request packet */
            printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);

            /* Go through the routing options and answer no to every
             * option except Unnumbered RIP/SAP
             */
            for(i = 41; sendpacket[i] == 0x00; i += 5)
            {
                /* 0x02 is the option for Unnumbered RIP/SAP */
                if( sendpacket[i + 4] != 0x02)
                {
                    sendpacket[i + 1] = 0;
                }
            }

            /* Skip over the extended Node ID option */
            if( sendpacket[i] == 0x04 )
            {
                i += 8;
            }

            /* We also want to turn off all header compression opt.              */ 
            for(; sendpacket[i] == 0x80 ;)
            {
                sendpacket[i + 1] = 0;
                i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
            }

            /* Set the packet type to timer response */
            sendpacket[34] = 0x01;

            printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
        }
        else if( sendpacket[34] == 0x02 )
        {
            /* This is an information request packet */
            printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);

            /* Set the packet type to information response */
            sendpacket[34] = 0x03;

            /* Set the router name */
            sendpacket[51] = 'X';
            sendpacket[52] = 'T';
            sendpacket[53] = 'P';
            sendpacket[54] = 'I';
            sendpacket[55] = 'P';
            sendpacket[56] = 'E';
            sendpacket[57] = '-';
            sendpacket[58] = CVHexToAscii(network_number >> 28);
            sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24);
            sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20);
            sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16);
            sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12);
            sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8);
            sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
            sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
            for(i = 66; i < 99; i+= 1)
            {
                sendpacket[i] = 0;
            }

            printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
        }
        else
        {
            printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
            return 0;
        }

        /* Set the WNodeID to our network address */
        sendpacket[35] = (unsigned char)(network_number >> 24);
        sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16);
        sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8);
        sendpacket[38] = (unsigned char)(network_number & 0x000000FF);

        return 1;
    } else {
        /*If we get here 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 
 */

static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
{
    unsigned long pnetwork_number;

    pnetwork_number = (unsigned long)((sendpacket[6] << 24) + 
              (sendpacket[7] << 16) + (sendpacket[8] << 8) + 
              sendpacket[9]);
    

    if (!incoming) {
        /*If the destination network number is ours, make it 0 */
        if( pnetwork_number == network_number) {
            sendpacket[6] = sendpacket[7] = sendpacket[8] = 
                     sendpacket[9] = 0x00;
        }
    } else {
        /* If the incoming network is 0, make it ours */
        if( pnetwork_number == 0) {
            sendpacket[6] = (unsigned char)(network_number >> 24);
            sendpacket[7] = (unsigned char)((network_number & 
                     0x00FF0000) >> 16);
            sendpacket[8] = (unsigned char)((network_number & 
                     0x0000FF00) >> 8);
            sendpacket[9] = (unsigned char)(network_number & 
                     0x000000FF);
        }
    }


    pnetwork_number = (unsigned long)((sendpacket[18] << 24) + 
              (sendpacket[19] << 16) + (sendpacket[20] << 8) + 
              sendpacket[21]);
    
    
    if( !incoming ) {
        /* If the source network is ours, make it 0 */
        if( pnetwork_number == network_number) {
            sendpacket[18] = sendpacket[19] = sendpacket[20] = 
                 sendpacket[21] = 0x00;
        }
    } else {
        /* If the source network is 0, make it ours */
        if( pnetwork_number == 0 ) {
            sendpacket[18] = (unsigned char)(network_number >> 24);
            sendpacket[19] = (unsigned char)((network_number & 
                     0x00FF0000) >> 16);
            sendpacket[20] = (unsigned char)((network_number & 
                     0x0000FF00) >> 8);
            sendpacket[21] = (unsigned char)(network_number & 
                     0x000000FF);
        }
    }
} /* switch_net_numbers */




/********************* X25API SPECIFIC FUNCTIONS ****************/


/*===============================================================
 *  find_channel
 *
 *    Manages the lcn to device map. It increases performance
 *      because it eliminates the need to search through the link  
 *      list for a device which is bounded to a specific lcn.
 *
 *===============================================================*/


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);

}

MODULE_LICENSE("GPL");

/****** End *****************************************************************/

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0144 ]--