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