/***************************************************************************** * sdla_x25.c WANPIPE(tm) Multiprotocol WAN Link Driver. X.25 module. * * Author: Nenad Corbic * * 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 #include /* printk(), and other useful stuff */ #include /* offsetof(), etc. */ #include /* return codes */ #include /* inline memset(), etc. */ #include #include /* kmalloc(), kfree() */ #include /* WAN router definitions */ #include /* WANPIPE common user API definitions */ #include #include /* time_after() macro */ #include /* htons(), etc. */ #include #include /* Experimental delay */ #include #include #include #include /* X.25 firmware API definitions */ #include #include /*====================================================== * Defines & Macros *=====================================================*/ #define CMD_OK 0 /* normal firmware return code */ #define CMD_TIMEOUT 0xFF /* firmware command timed out */ #define MAX_CMD_RETRY 10 /* max number of firmware retries */ #define X25_CHAN_MTU 4096 /* unfragmented logical channel MTU */ #define X25_HRDHDR_SZ 7 /* max encapsulation header size */ #define X25_CONCT_TMOUT (90*HZ) /* link connection timeout */ #define X25_RECON_TMOUT (10*HZ) /* link connection timeout */ #define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */ #define HOLD_DOWN_TIME (30*HZ) /* link hold down time */ #define MAX_BH_BUFF 10 #define M_BIT 0x01 //#define PRINT_DEBUG 1 #ifdef PRINT_DEBUG #define DBG_PRINTK(format, a...) printk(format, ## a) #else #define DBG_PRINTK(format, a...) #endif #define TMR_INT_ENABLED_POLL_ACTIVE 0x01 #define TMR_INT_ENABLED_POLL_CONNECT_ON 0x02 #define TMR_INT_ENABLED_POLL_CONNECT_OFF 0x04 #define TMR_INT_ENABLED_POLL_DISCONNECT 0x08 #define TMR_INT_ENABLED_CMD_EXEC 0x10 #define TMR_INT_ENABLED_UPDATE 0x20 #define TMR_INT_ENABLED_UDP_PKT 0x40 #define MAX_X25_ADDR_SIZE 16 #define MAX_X25_DATA_SIZE 129 #define MAX_X25_FACL_SIZE 110 #define TRY_CMD_AGAIN 2 #define DELAY_RESULT 1 #define RETURN_RESULT 0 #define DCD(x) (x & 0x03 ? "HIGH" : "LOW") #define CTS(x) (x & 0x05 ? "HIGH" : "LOW") /* Driver will not write log messages about * modem status if defined.*/ #define MODEM_NOT_LOG 1 /*==================================================== * For IPXWAN *===================================================*/ #define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b))) /*==================================================== * MEMORY DEBUGGING FUNCTION *==================================================== #define KMEM_SAFETYZONE 8 static void * dbg_kmalloc(unsigned int size, int prio, int line) { int i = 0; void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio); char * c1 = v; c1 += sizeof(unsigned int); *((unsigned int *)v) = size; for (i = 0; i < KMEM_SAFETYZONE; i++) { c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D'; c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F'; c1 += 8; } c1 += size; for (i = 0; i < KMEM_SAFETYZONE; i++) { c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G'; c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L'; c1 += 8; } v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8; printk(KERN_INFO "line %d kmalloc(%d,%d) = %p\n",line,size,prio,v); return v; } static void dbg_kfree(void * v, int line) { unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8)); unsigned int size = *sp; char * c1 = ((char *)v) - KMEM_SAFETYZONE*8; int i = 0; for (i = 0; i < KMEM_SAFETYZONE; i++) { if ( c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D' || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') { printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v); printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); } c1 += 8; } c1 += size; for (i = 0; i < KMEM_SAFETYZONE; i++) { if ( c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G' || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L' ) { printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v); printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8, c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] ); } c1 += 8; } printk(KERN_INFO "line %d kfree(%p)\n",line,v); v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8); kfree(v); } #define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__) #define kfree(x) dbg_kfree(x,__LINE__) ==============================================================*/ /*=============================================== * Data Structures *===============================================*/ /*======================================================== * Name: x25_channel * * Purpose: To hold private informaton for each * logical channel. * * Rationale: Per-channel debugging is possible if each * channel has its own private area. * * Assumptions: * * Description: This is an extention of the struct net_device * we create for each network interface to keep * the rest of X.25 channel-specific data. * * Construct: Typedef */ typedef struct x25_channel { wanpipe_common_t common; /* common area for x25api and socket */ char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */ char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */ unsigned tx_pkt_size; unsigned short protocol; /* ethertype, 0 - multiplexed */ char drop_sequence; /* mark sequence for dropping */ unsigned long state_tick; /* time of the last state change */ unsigned idle_timeout; /* sec, before disconnecting */ unsigned long i_timeout_sofar; /* # of sec's we've been idle */ unsigned hold_timeout; /* sec, before re-connecting */ unsigned long tick_counter; /* counter for transmit time out */ char devtint; /* Weather we should dev_tint() */ struct sk_buff* rx_skb; /* receive socket buffer */ struct sk_buff* tx_skb; /* transmit socket buffer */ bh_data_t *bh_head; /* Circular buffer for x25api_bh */ unsigned long tq_working; volatile int bh_write; volatile int bh_read; atomic_t bh_buff_used; sdla_t* card; /* -> owner */ struct net_device *dev; /* -> bound devce */ int ch_idx; unsigned char enable_IPX; unsigned long network_number; struct net_device_stats ifstats; /* interface statistics */ unsigned short transmit_length; unsigned short tx_offset; char transmit_buffer[X25_CHAN_MTU+sizeof(x25api_hdr_t)]; if_send_stat_t if_send_stat; rx_intr_stat_t rx_intr_stat; pipe_mgmt_stat_t pipe_mgmt_stat; unsigned long router_start_time; /* Router start time in seconds */ unsigned long router_up_time; } x25_channel_t; /* FIXME Take this out */ #ifdef NEX_OLD_CALL_INFO typedef struct x25_call_info { char dest[17]; PACKED;/* ASCIIZ destination address */ char src[17]; PACKED;/* ASCIIZ source address */ char nuser; PACKED;/* number of user data bytes */ unsigned char user[127]; PACKED;/* user data */ char nfacil; PACKED;/* number of facilities */ struct { unsigned char code; PACKED; unsigned char parm; PACKED; } facil[64]; /* facilities */ } x25_call_info_t; #else typedef struct x25_call_info { char dest[MAX_X25_ADDR_SIZE] PACKED;/* ASCIIZ destination address */ char src[MAX_X25_ADDR_SIZE] PACKED;/* ASCIIZ source address */ unsigned char nuser PACKED; unsigned char user[MAX_X25_DATA_SIZE] PACKED;/* user data */ unsigned char nfacil PACKED; unsigned char facil[MAX_X25_FACL_SIZE] PACKED; unsigned short lcn PACKED; } x25_call_info_t; #endif /*=============================================== * Private Function Prototypes *==============================================*/ /*================================================= * WAN link driver entry points. These are * called by the WAN router module. */ static int update(struct wan_device* wandev); static int new_if(struct wan_device* wandev, struct net_device* dev, wanif_conf_t* conf); static int del_if(struct wan_device* wandev, struct net_device* dev); static void disable_comm (sdla_t* card); static void disable_comm_shutdown(sdla_t *card); /*================================================= * WANPIPE-specific entry points */ static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data); static void x25api_bh(struct net_device *dev); static int x25api_bh_cleanup(struct net_device *dev); static int bh_enqueue(struct net_device *dev, struct sk_buff *skb); /*================================================= * Network device interface */ static int if_init(struct net_device* dev); static int if_open(struct net_device* dev); static int if_close(struct net_device* dev); static int if_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len); static int if_rebuild_hdr (struct sk_buff* skb); static int if_send(struct sk_buff* skb, struct net_device* dev); static struct net_device_stats *if_stats(struct net_device* dev); static void if_tx_timeout(struct net_device *dev); /*================================================= * Interrupt handlers */ static void wpx_isr (sdla_t *); static void rx_intr (sdla_t *); static void tx_intr (sdla_t *); static void status_intr (sdla_t *); static void event_intr (sdla_t *); static void spur_intr (sdla_t *); static void timer_intr (sdla_t *); static int tx_intr_send(sdla_t *card, struct net_device *dev); static struct net_device *move_dev_to_next(sdla_t *card, struct net_device *dev); /*================================================= * Background polling routines */ static void wpx_poll (sdla_t* card); static void poll_disconnected (sdla_t* card); static void poll_connecting (sdla_t* card); static void poll_active (sdla_t* card); static void trigger_x25_poll(sdla_t *card); static void x25_timer_routine(unsigned long data); /*================================================= * X.25 firmware interface functions */ static int x25_get_version (sdla_t* card, char* str); static int x25_configure (sdla_t* card, TX25Config* conf); static int hdlc_configure (sdla_t* card, TX25Config* conf); static int set_hdlc_level (sdla_t* card); static int x25_get_err_stats (sdla_t* card); static int x25_get_stats (sdla_t* card); static int x25_set_intr_mode (sdla_t* card, int mode); static int x25_close_hdlc (sdla_t* card); static int x25_open_hdlc (sdla_t* card); static int x25_setup_hdlc (sdla_t* card); static int x25_set_dtr (sdla_t* card, int dtr); static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan); static int x25_place_call (sdla_t* card, x25_channel_t* chan); static int x25_accept_call (sdla_t* card, int lcn, int qdm); static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn); static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf); static int x25_fetch_events (sdla_t* card); static int x25_error (sdla_t* card, int err, int cmd, int lcn); /*================================================= * X.25 asynchronous event handlers */ static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb); static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb); static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb); static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb); static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb); /*================================================= * Miscellaneous functions */ static int connect (sdla_t* card); static int disconnect (sdla_t* card); static struct net_device* get_dev_by_lcn(struct wan_device* wandev, unsigned lcn); static int chan_connect(struct net_device* dev); static int chan_disc(struct net_device* dev); static void set_chan_state(struct net_device* dev, int state); static int chan_send(struct net_device *dev, void* buff, unsigned data_len, unsigned char tx_intr); static unsigned char bps_to_speed_code (unsigned long bps); static unsigned int dec_to_uint (unsigned char* str, int len); static unsigned int hex_to_uint (unsigned char*, int); static void parse_call_info (unsigned char*, x25_call_info_t*); static struct net_device *find_channel(sdla_t *card, unsigned lcn); static void bind_lcn_to_dev(sdla_t *card, struct net_device *dev, unsigned lcn); static void setup_for_delayed_transmit(struct net_device *dev, void *buf, unsigned len); /*================================================= * X25 API Functions */ static int wanpipe_pull_data_in_skb(sdla_t *card, struct net_device *dev, struct sk_buff **); static void timer_intr_exec(sdla_t *, unsigned char); static int execute_delayed_cmd(sdla_t *card, struct net_device *dev, mbox_cmd_t *usr_cmd, char bad_cmd); static int api_incoming_call (sdla_t*, TX25Mbox *, int); static int alloc_and_init_skb_buf (sdla_t *,struct sk_buff **, int); static void send_delayed_cmd_result(sdla_t *card, struct net_device *dev, TX25Mbox* mbox); static int clear_confirm_event (sdla_t *, TX25Mbox*); static void send_oob_msg (sdla_t *card, struct net_device *dev, TX25Mbox *mbox); static int timer_intr_cmd_exec(sdla_t *card); static void api_oob_event (sdla_t *card,TX25Mbox *mbox); static int check_bad_command(sdla_t *card, struct net_device *dev); static int channel_disconnect(sdla_t* card, struct net_device *dev); static void hdlc_link_down (sdla_t*); /*================================================= * XPIPEMON Functions */ static int process_udp_mgmt_pkt(sdla_t *); static int udp_pkt_type( struct sk_buff *, sdla_t*); static int reply_udp( unsigned char *, unsigned int); static void init_x25_channel_struct( x25_channel_t *); static void init_global_statistics( sdla_t *); static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t *card, struct net_device *dev, struct sk_buff *skb, int lcn); static unsigned short calc_checksum (char *, int); /*================================================= * IPX functions */ static void switch_net_numbers(unsigned char *, unsigned long, unsigned char); static int handle_IPXWAN(unsigned char *, char *, unsigned char , unsigned long , unsigned short ); extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); static void S508_S514_lock(sdla_t *, unsigned long *); static void S508_S514_unlock(sdla_t *, unsigned long *); /*================================================= * Global Variables *=================================================*/ /*================================================= * Public Functions *=================================================*/ /*=================================================================== * wpx_init: X.25 Protocol Initialization routine. * * Purpose: To initialize the protocol/firmware. * * Rationale: This function is called by setup() function, in * sdlamain.c, to dynamically setup the x25 protocol. * This is the first protocol specific function, which * executes once on startup. * * Description: This procedure initializes the x25 firmware and * sets up the mailbox, transmit and receive buffer * pointers. It also initializes all debugging structures * and sets up the X25 environment. * * Sets up hardware options defined by user in [wanpipe#] * section of wanpipe#.conf configuration file. * * At this point adapter is completely initialized * and X.25 firmware is running. * o read firmware version (to make sure it's alive) * o configure adapter * o initialize protocol-specific fields of the * adapter data space. * * Called by: setup() function in sdlamain.c * * Assumptions: None * * Warnings: None * * Return: 0 o.k. * < 0 failure. */ int wpx_init (sdla_t* card, wandev_conf_t* conf) { union{ char str[80]; TX25Config cfg; } u; /* Verify configuration ID */ if (conf->config_id != WANCONFIG_X25){ printk(KERN_INFO "%s: invalid configuration ID %u!\n", card->devname, conf->config_id) ; return -EINVAL; } /* Initialize protocol-specific fields */ card->mbox = (void*)(card->hw.dpmbase + X25_MBOX_OFFS); card->rxmb = (void*)(card->hw.dpmbase + X25_RXMBOX_OFFS); card->flags = (void*)(card->hw.dpmbase + X25_STATUS_OFFS); /* Initialize for S514 Card */ if(card->hw.type == SDLA_S514) { card->mbox += X25_MB_VECTOR; card->flags += X25_MB_VECTOR; card->rxmb += X25_MB_VECTOR; } /* Read firmware version. Note that when adapter initializes, it * clears the mailbox, so it may appear that the first command was * executed successfully when in fact it was merely erased. To work * around this, we execute the first command twice. */ if (x25_get_version(card, NULL) || x25_get_version(card, u.str)) return -EIO; /* X25 firmware can run ether in X25 or LAPB HDLC mode. * Check the user defined option and configure accordingly */ if (conf->u.x25.LAPB_hdlc_only == WANOPT_YES){ if (set_hdlc_level(card) != CMD_OK){ return -EIO; }else{ printk(KERN_INFO "%s: running LAP_B HDLC firmware v%s\n", card->devname, u.str); } card->u.x.LAPB_hdlc = 1; }else{ printk(KERN_INFO "%s: running X.25 firmware v%s\n", card->devname, u.str); card->u.x.LAPB_hdlc = 0; } /* Configure adapter. Here we set resonable defaults, then parse * device configuration structure and set configuration options. * Most configuration options are verified and corrected (if * necessary) since we can't rely on the adapter to do so. */ memset(&u.cfg, 0, sizeof(u.cfg)); u.cfg.t1 = 3; u.cfg.n2 = 10; u.cfg.autoHdlc = 1; /* automatic HDLC connection */ u.cfg.hdlcWindow = 7; u.cfg.pktWindow = 2; u.cfg.station = 1; /* DTE */ u.cfg.options = 0x0090; /* disable D-bit pragmatics */ u.cfg.ccittCompat = 1988; u.cfg.t10t20 = 30; u.cfg.t11t21 = 30; u.cfg.t12t22 = 30; u.cfg.t13t23 = 30; u.cfg.t16t26 = 30; u.cfg.t28 = 30; u.cfg.r10r20 = 5; u.cfg.r12r22 = 5; u.cfg.r13r23 = 5; u.cfg.responseOpt = 1; /* RR's after every packet */ if (card->u.x.LAPB_hdlc){ u.cfg.hdlcMTU = 1027; } if (conf->u.x25.x25_conf_opt){ u.cfg.options = conf->u.x25.x25_conf_opt; } if (conf->clocking != WANOPT_EXTERNAL) u.cfg.baudRate = bps_to_speed_code(conf->bps); if (conf->station != WANOPT_DTE){ u.cfg.station = 0; /* DCE mode */ } if (conf->interface != WANOPT_RS232 ){ u.cfg.hdlcOptions |= 0x80; /* V35 mode */ } /* adjust MTU */ if (!conf->mtu || (conf->mtu >= 1024)) card->wandev.mtu = 1024; else if (conf->mtu >= 512) card->wandev.mtu = 512; else if (conf->mtu >= 256) card->wandev.mtu = 256; else if (conf->mtu >= 128) card->wandev.mtu = 128; else card->wandev.mtu = 64; u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu; if (conf->u.x25.hi_pvc){ card->u.x.hi_pvc = min_t(unsigned int, conf->u.x25.hi_pvc, MAX_LCN_NUM); card->u.x.lo_pvc = min_t(unsigned int, conf->u.x25.lo_pvc, card->u.x.hi_pvc); } if (conf->u.x25.hi_svc){ card->u.x.hi_svc = min_t(unsigned int, conf->u.x25.hi_svc, MAX_LCN_NUM); card->u.x.lo_svc = min_t(unsigned int, conf->u.x25.lo_svc, card->u.x.hi_svc); } /* Figure out the total number of channels to configure */ card->u.x.num_of_ch = 0; if (card->u.x.hi_svc != 0){ card->u.x.num_of_ch = (card->u.x.hi_svc - card->u.x.lo_svc) + 1; } if (card->u.x.hi_pvc != 0){ card->u.x.num_of_ch += (card->u.x.hi_pvc - card->u.x.lo_pvc) + 1; } if (card->u.x.num_of_ch == 0){ printk(KERN_INFO "%s: ERROR, Minimum number of PVC/SVC channels is 1 !\n" "%s: Please set the Lowest/Highest PVC/SVC values !\n", card->devname,card->devname); return -ECHRNG; } u.cfg.loPVC = card->u.x.lo_pvc; u.cfg.hiPVC = card->u.x.hi_pvc; u.cfg.loTwoWaySVC = card->u.x.lo_svc; u.cfg.hiTwoWaySVC = card->u.x.hi_svc; if (conf->u.x25.hdlc_window) u.cfg.hdlcWindow = min_t(unsigned int, conf->u.x25.hdlc_window, 7); if (conf->u.x25.pkt_window) u.cfg.pktWindow = min_t(unsigned int, conf->u.x25.pkt_window, 7); if (conf->u.x25.t1) u.cfg.t1 = min_t(unsigned int, conf->u.x25.t1, 30); if (conf->u.x25.t2) u.cfg.t2 = min_t(unsigned int, conf->u.x25.t2, 29); if (conf->u.x25.t4) u.cfg.t4 = min_t(unsigned int, conf->u.x25.t4, 240); if (conf->u.x25.n2) u.cfg.n2 = min_t(unsigned int, conf->u.x25.n2, 30); if (conf->u.x25.t10_t20) u.cfg.t10t20 = min_t(unsigned int, conf->u.x25.t10_t20,255); if (conf->u.x25.t11_t21) u.cfg.t11t21 = min_t(unsigned int, conf->u.x25.t11_t21,255); if (conf->u.x25.t12_t22) u.cfg.t12t22 = min_t(unsigned int, conf->u.x25.t12_t22,255); if (conf->u.x25.t13_t23) u.cfg.t13t23 = min_t(unsigned int, conf->u.x25.t13_t23,255); if (conf->u.x25.t16_t26) u.cfg.t16t26 = min_t(unsigned int, conf->u.x25.t16_t26, 255); if (conf->u.x25.t28) u.cfg.t28 = min_t(unsigned int, conf->u.x25.t28, 255); if (conf->u.x25.r10_r20) u.cfg.r10r20 = min_t(unsigned int, conf->u.x25.r10_r20,250); if (conf->u.x25.r12_r22) u.cfg.r12r22 = min_t(unsigned int, conf->u.x25.r12_r22,250); if (conf->u.x25.r13_r23) u.cfg.r13r23 = min_t(unsigned int, conf->u.x25.r13_r23,250); if (conf->u.x25.ccitt_compat) u.cfg.ccittCompat = conf->u.x25.ccitt_compat; /* initialize adapter */ if (card->u.x.LAPB_hdlc){ if (hdlc_configure(card, &u.cfg) != CMD_OK) return -EIO; }else{ if (x25_configure(card, &u.cfg) != CMD_OK) return -EIO; } if ((x25_close_hdlc(card) != CMD_OK) || /* close HDLC link */ (x25_set_dtr(card, 0) != CMD_OK)) /* drop DTR */ return -EIO; /* Initialize protocol-specific fields of adapter data space */ card->wandev.bps = conf->bps; card->wandev.interface = conf->interface; card->wandev.clocking = conf->clocking; card->wandev.station = conf->station; card->isr = &wpx_isr; card->poll = NULL; //&wpx_poll; card->disable_comm = &disable_comm; card->exec = &wpx_exec; card->wandev.update = &update; card->wandev.new_if = &new_if; card->wandev.del_if = &del_if; /* WARNING: This function cannot exit with an error * after the change of state */ card->wandev.state = WAN_DISCONNECTED; card->wandev.enable_tx_int = 0; card->irq_dis_if_send_count = 0; card->irq_dis_poll_count = 0; card->u.x.tx_dev = NULL; card->u.x.no_dev = 0; /* Configure for S514 PCI Card */ if (card->hw.type == SDLA_S514) { card->u.x.hdlc_buf_status = (volatile unsigned char *) (card->hw.dpmbase + X25_MB_VECTOR+ X25_MISC_HDLC_BITS); }else{ card->u.x.hdlc_buf_status = (volatile unsigned char *)(card->hw.dpmbase + X25_MISC_HDLC_BITS); } card->u.x.poll_device=NULL; card->wandev.udp_port = conf->udp_port; /* Enable or disable call setup logging */ if (conf->u.x25.logging == WANOPT_YES){ printk(KERN_INFO "%s: Enabling Call Logging.\n", card->devname); card->u.x.logging = 1; }else{ card->u.x.logging = 0; } /* Enable or disable modem status reporting */ if (conf->u.x25.oob_on_modem == WANOPT_YES){ printk(KERN_INFO "%s: Enabling OOB on Modem change.\n", card->devname); card->u.x.oob_on_modem = 1; }else{ card->u.x.oob_on_modem = 0; } init_global_statistics(card); INIT_WORK(&card->u.x.x25_poll_work, (void *)wpx_poll, card); init_timer(&card->u.x.x25_timer); card->u.x.x25_timer.data = (unsigned long)card; card->u.x.x25_timer.function = x25_timer_routine; return 0; } /*========================================================= * WAN Device Driver Entry Points *========================================================*/ /*============================================================ * Name: update(), Update device status & statistics. * * Purpose: To provide debugging and statitical * information to the /proc file system. * /proc/net/wanrouter/wanpipe# * * Rationale: The /proc file system is used to collect * information about the kernel and drivers. * Using the /proc file system the user * can see exactly what the sangoma drivers are * doing. And in what state they are in. * * Description: Collect all driver statistical information * and pass it to the top laywer. * * Since we have to execute a debugging command, * to obtain firmware statitics, we trigger a * UPDATE function within the timer interrtup. * We wait until the timer update is complete. * Once complete return the appropriate return * code to indicate that the update was successful. * * Called by: device_stat() in wanmain.c * * Assumptions: * * Warnings: This function will degrade the performance * of the router, since it uses the mailbox. * * Return: 0 OK * <0 Failed (or busy). */ static int update(struct wan_device* wandev) { volatile sdla_t* card; TX25Status* status; unsigned long timeout; /* sanity checks */ if ((wandev == NULL) || (wandev->private == NULL)) return -EFAULT; if (wandev->state == WAN_UNCONFIGURED) return -ENODEV; if (test_bit(SEND_CRIT, (void*)&wandev->critical)) return -EAGAIN; if (!wandev->dev) return -ENODEV; card = wandev->private; status = card->flags; card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UPDATE; status->imask |= INTR_ON_TIMER; timeout = jiffies; for (;;){ if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE)){ break; } if (time_after(jiffies, timeout + 1*HZ)){ card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE; return -EAGAIN; } } return 0; } /*=================================================================== * Name: new_if * * Purpose: To allocate and initialize resources for a * new logical channel. * * Rationale: A new channel can be added dynamically via * ioctl call. * * Description: Allocate a private channel structure, x25_channel_t. * Parse the user interface options from wanpipe#.conf * configuration file. * Bind the private are into the network device private * area pointer (dev->priv). * Prepare the network device structure for registration. * * Called by: ROUTER_IFNEW Ioctl call, from wanrouter_ioctl() * (wanmain.c) * * Assumptions: None * * Warnings: None * * Return: 0 Ok * <0 Failed (channel will not be created) */ static int new_if(struct wan_device* wandev, struct net_device* dev, wanif_conf_t* conf) { sdla_t* card = wandev->private; x25_channel_t* chan; int err = 0; if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)){ printk(KERN_INFO "%s: invalid interface name!\n", card->devname); return -EINVAL; } if(card->wandev.new_if_cnt++ > 0 && card->u.x.LAPB_hdlc) { printk(KERN_INFO "%s: Error: Running LAPB HDLC Mode !\n", card->devname); printk(KERN_INFO "%s: Maximum number of network interfaces must be one !\n", card->devname); return -EEXIST; } /* allocate and initialize private data */ chan = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC); if (chan == NULL){ return -ENOMEM; } memset(chan, 0, sizeof(x25_channel_t)); /* Bug Fix: Seg Err on PVC startup * It must be here since bind_lcn_to_dev expects * it bellow */ dev->priv = chan; strcpy(chan->name, conf->name); chan->card = card; chan->dev = dev; chan->common.sk = NULL; chan->common.func = NULL; chan->common.rw_bind = 0; chan->tx_skb = chan->rx_skb = NULL; /* verify media address */ if (conf->addr[0] == '@'){ /* SVC */ chan->common.svc = 1; strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ); /* Set channel timeouts (default if not specified) */ chan->idle_timeout = (conf->idle_timeout) ? conf->idle_timeout : 90; chan->hold_timeout = (conf->hold_timeout) ? conf->hold_timeout : 10; }else if (isdigit(conf->addr[0])){ /* PVC */ int lcn = dec_to_uint(conf->addr, 0); if ((lcn >= card->u.x.lo_pvc) && (lcn <= card->u.x.hi_pvc)){ bind_lcn_to_dev (card, dev, lcn); }else{ printk(KERN_ERR "%s: PVC %u is out of range on interface %s!\n", wandev->name, lcn, chan->name); err = -EINVAL; } }else{ printk(KERN_ERR "%s: invalid media address on interface %s!\n", wandev->name, chan->name); err = -EINVAL; } if(strcmp(conf->usedby, "WANPIPE") == 0){ printk(KERN_INFO "%s: Running in WANPIPE mode %s\n", wandev->name, chan->name); chan->common.usedby = WANPIPE; chan->protocol = htons(ETH_P_IP); }else if(strcmp(conf->usedby, "API") == 0){ chan->common.usedby = API; printk(KERN_INFO "%s: Running in API mode %s\n", wandev->name, chan->name); chan->protocol = htons(X25_PROT); } if (err){ kfree(chan); dev->priv = NULL; return err; } chan->enable_IPX = conf->enable_IPX; if (chan->enable_IPX) chan->protocol = htons(ETH_P_IPX); if (conf->network_number) chan->network_number = conf->network_number; else chan->network_number = 0xDEADBEEF; /* prepare network device data space for registration */ strcpy(dev->name,chan->name); dev->init = &if_init; init_x25_channel_struct(chan); return 0; } /*=================================================================== * Name: del_if(), Remove a logical channel. * * Purpose: To dynamically remove a logical channel. * * Rationale: Each logical channel should be dynamically * removable. This functin is called by an * IOCTL_IFDEL ioctl call or shutdown(). * * Description: Do nothing. * * Called by: IOCTL_IFDEL : wanrouter_ioctl() from wanmain.c * shutdown() from sdlamain.c * * Assumptions: * * Warnings: * * Return: 0 Ok. Void function. */ //FIXME Del IF Should be taken out now. static int del_if(struct wan_device* wandev, struct net_device* dev) { return 0; } /*============================================================ * Name: wpx_exec * * Description: Execute adapter interface command. * This option is currently dissabled. *===========================================================*/ static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data) { return 0; } /*============================================================ * Name: disable_comm * * Description: Disable communications during shutdown. * Dont check return code because there is * nothing we can do about it. * * Warning: Dev and private areas are gone at this point. *===========================================================*/ static void disable_comm(sdla_t* card) { disable_comm_shutdown(card); del_timer(&card->u.x.x25_timer); return; } /*============================================================ * Network Device Interface *===========================================================*/ /*=================================================================== * Name: if_init(), Netowrk Interface Initialization * * Purpose: To initialize a network interface device structure. * * Rationale: During network interface startup, the if_init * is called by the kernel to initialize the * netowrk device structure. Thus a driver * can customze a network device. * * Description: Initialize the netowrk device call back * routines. This is where we tell the kernel * which function to use when it wants to send * via our interface. * Furthermore, we initialize the device flags, * MTU and physical address of the board. * * Called by: Kernel (/usr/src/linux/net/core/dev.c) * (dev->init()) * * Assumptions: None * * Warnings: None * * Return: 0 Ok : Void function. */ static int if_init(struct net_device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; struct wan_device* wandev = &card->wandev; /* Initialize device driver entry points */ dev->open = &if_open; dev->stop = &if_close; dev->hard_header = &if_header; dev->rebuild_header = &if_rebuild_hdr; dev->hard_start_xmit = &if_send; dev->get_stats = &if_stats; dev->tx_timeout = &if_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; /* Initialize media-specific parameters */ dev->type = ARPHRD_PPP; /* ARP h/w type */ dev->flags |= IFF_POINTOPOINT; dev->flags |= IFF_NOARP; if (chan->common.usedby == API){ dev->mtu = X25_CHAN_MTU+sizeof(x25api_hdr_t); }else{ dev->mtu = card->wandev.mtu; } dev->hard_header_len = X25_HRDHDR_SZ; /* media header length */ dev->addr_len = 2; /* hardware address length */ if (!chan->common.svc){ *(unsigned short*)dev->dev_addr = htons(chan->common.lcn); } /* Initialize hardware parameters (just for reference) */ dev->irq = wandev->irq; dev->dma = wandev->dma; dev->base_addr = wandev->ioport; dev->mem_start = (unsigned long)wandev->maddr; dev->mem_end = wandev->maddr + wandev->msize - 1; /* Set transmit buffer queue length */ dev->tx_queue_len = 100; SET_MODULE_OWNER(dev); /* FIXME Why are we doing this */ set_chan_state(dev, WAN_DISCONNECTED); return 0; } /*=================================================================== * Name: if_open(), Open/Bring up the Netowrk Interface * * Purpose: To bring up a network interface. * * Rationale: * * Description: Open network interface. * o prevent module from unloading by incrementing use count * o if link is disconnected then initiate connection * * Called by: Kernel (/usr/src/linux/net/core/dev.c) * (dev->open()) * * Assumptions: None * * Warnings: None * * Return: 0 Ok * <0 Failure: Interface will not come up. */ static int if_open(struct net_device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; struct timeval tv; unsigned long smp_flags; if (netif_running(dev)) return -EBUSY; chan->tq_working = 0; /* Initialize the workqueue */ INIT_WORK(&chan->common.wanpipe_work, (void *)x25api_bh, dev); /* Allocate and initialize BH circular buffer */ /* Add 1 to MAX_BH_BUFF so we don't have test with (MAX_BH_BUFF-1) */ chan->bh_head = kmalloc((sizeof(bh_data_t)*(MAX_BH_BUFF+1)),GFP_ATOMIC); if (chan->bh_head == NULL){ printk(KERN_INFO "%s: ERROR, failed to allocate memory ! BH_BUFFERS !\n", card->devname); return -ENOBUFS; } memset(chan->bh_head,0,(sizeof(bh_data_t)*(MAX_BH_BUFF+1))); atomic_set(&chan->bh_buff_used, 0); /* Increment the number of interfaces */ ++card->u.x.no_dev; wanpipe_open(card); /* LAPB protocol only uses one interface, thus * start the protocol after it comes up. */ if (card->u.x.LAPB_hdlc){ if (card->open_cnt == 1){ TX25Status* status = card->flags; S508_S514_lock(card, &smp_flags); x25_set_intr_mode(card, INTR_ON_TIMER); status->imask &= ~INTR_ON_TIMER; S508_S514_unlock(card, &smp_flags); } }else{ /* X25 can have multiple interfaces thus, start the * protocol once all interfaces are up */ //FIXME: There is a bug here. If interface is //brought down and up, it will try to enable comm. if (card->open_cnt == card->u.x.num_of_ch){ S508_S514_lock(card, &smp_flags); connect(card); S508_S514_unlock(card, &smp_flags); mod_timer(&card->u.x.x25_timer, jiffies + HZ); } } /* Device is not up until the we are in connected state */ do_gettimeofday( &tv ); chan->router_start_time = tv.tv_sec; netif_start_queue(dev); return 0; } /*=================================================================== * Name: if_close(), Close/Bring down the Netowrk Interface * * Purpose: To bring down a network interface. * * Rationale: * * Description: Close network interface. * o decrement use module use count * * Called by: Kernel (/usr/src/linux/net/core/dev.c) * (dev->close()) * ifconfig down: will trigger the kernel * which will call this function. * * Assumptions: None * * Warnings: None * * Return: 0 Ok * <0 Failure: Interface will not exit properly. */ static int if_close(struct net_device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; unsigned long smp_flags; netif_stop_queue(dev); if ((chan->common.state == WAN_CONNECTED) || (chan->common.state == WAN_CONNECTING)){ S508_S514_lock(card, &smp_flags); chan_disc(dev); S508_S514_unlock(card, &smp_flags); } wanpipe_close(card); S508_S514_lock(card, &smp_flags); if (chan->bh_head){ int i; struct sk_buff *skb; for (i=0; i<(MAX_BH_BUFF+1); i++){ skb = ((bh_data_t *)&chan->bh_head[i])->skb; if (skb != NULL){ dev_kfree_skb_any(skb); } } kfree(chan->bh_head); chan->bh_head=NULL; } S508_S514_unlock(card, &smp_flags); /* If this is the last close, disconnect physical link */ if (!card->open_cnt){ S508_S514_lock(card, &smp_flags); disconnect(card); x25_set_intr_mode(card, 0); S508_S514_unlock(card, &smp_flags); } /* Decrement the number of interfaces */ --card->u.x.no_dev; return 0; } /*====================================================================== * Build media header. * o encapsulate packet according to encapsulation type. * * The trick here is to put packet type (Ethertype) into 'protocol' * field of the socket buffer, so that we don't forget it. * If encapsulation fails, set skb->protocol to 0 and discard * packet later. * * Return: media header length. *======================================================================*/ static int if_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len) { x25_channel_t* chan = dev->priv; int hdr_len = dev->hard_header_len; skb->protocol = htons(type); if (!chan->protocol){ hdr_len = wanrouter_encapsulate(skb, dev, type); if (hdr_len < 0){ hdr_len = 0; skb->protocol = htons(0); } } return hdr_len; } /*=============================================================== * Re-build media header. * * Return: 1 physical address resolved. * 0 physical address not resolved *==============================================================*/ static int if_rebuild_hdr (struct sk_buff* skb) { struct net_device *dev = skb->dev; x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n", card->devname, dev->name); return 1; } /*============================================================================ * Handle transmit timeout event from netif watchdog */ static void if_tx_timeout(struct net_device *dev) { x25_channel_t* chan = dev->priv; sdla_t *card = chan->card; /* If our device stays busy for at least 5 seconds then we will * kick start the device by making dev->tbusy = 0. We expect * that our device never stays busy more than 5 seconds. So this * is only used as a last resort. */ ++chan->if_send_stat.if_send_tbusy_timeout; printk (KERN_INFO "%s: Transmit timed out on %s\n", card->devname, dev->name); netif_wake_queue (dev); } /*========================================================================= * Send a packet on a network interface. * o set tbusy flag (marks start of the transmission). * o check link state. If link is not up, then drop the packet. * o check channel status. If it's down then initiate a call. * o pass a packet to corresponding WAN device. * o free socket buffer * * Return: 0 complete (socket buffer must be freed) * non-0 packet may be re-transmitted (tbusy must be set) * * Notes: * 1. This routine is called either by the protocol stack or by the "net * bottom half" (with interrupts enabled). * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. * *========================================================================*/ static int if_send(struct sk_buff* skb, struct net_device* dev) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; TX25Status* status = card->flags; int udp_type; unsigned long smp_flags=0; ++chan->if_send_stat.if_send_entry; netif_stop_queue(dev); /* No need to check frame length, since socket code * will perform the check for us */ chan->tick_counter = jiffies; /* Critical region starts here */ S508_S514_lock(card, &smp_flags); if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)){ printk(KERN_INFO "Hit critical in if_send()! %lx\n",card->wandev.critical); goto if_send_crit_exit; } udp_type = udp_pkt_type(skb, card); if(udp_type != UDP_INVALID_TYPE) { if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, dev, skb, chan->common.lcn)) { status->imask |= INTR_ON_TIMER; if (udp_type == UDP_XPIPE_TYPE){ chan->if_send_stat.if_send_PIPE_request++; } } netif_start_queue(dev); clear_bit(SEND_CRIT,(void*)&card->wandev.critical); S508_S514_unlock(card, &smp_flags); return 0; } if (chan->transmit_length){ //FIXME: This check doesn't make sense any more if (chan->common.state != WAN_CONNECTED){ chan->transmit_length=0; atomic_set(&chan->common.driver_busy,0); }else{ netif_stop_queue(dev); ++card->u.x.tx_interrupts_pending; status->imask |= INTR_ON_TX_FRAME; clear_bit(SEND_CRIT,(void*)&card->wandev.critical); S508_S514_unlock(card, &smp_flags); return 1; } } if (card->wandev.state != WAN_CONNECTED){ ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; ++chan->if_send_stat.if_send_wan_disconnected; }else if ( chan->protocol && (chan->protocol != skb->protocol)){ printk(KERN_INFO "%s: unsupported Ethertype 0x%04X on interface %s!\n", chan->name, htons(skb->protocol), dev->name); printk(KERN_INFO "PROTO %Xn", htons(chan->protocol)); ++chan->ifstats.tx_errors; ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; ++chan->if_send_stat.if_send_protocol_error; }else switch (chan->common.state){ case WAN_DISCONNECTED: /* Try to establish connection. If succeded, then start * transmission, else drop a packet. */ if (chan->common.usedby == API){ ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; break; }else{ if (chan_connect(dev) != 0){ ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; break; } } /* fall through */ case WAN_CONNECTED: if( skb->protocol == htons(ETH_P_IPX)) { if(chan->enable_IPX) { switch_net_numbers( skb->data, chan->network_number, 0); } else { ++card->wandev.stats.tx_dropped; ++chan->ifstats.tx_dropped; ++chan->if_send_stat.if_send_protocol_error; goto if_send_crit_exit; } } /* We never drop here, if cannot send than, copy * a packet into a transmit buffer */ chan_send(dev, skb->data, skb->len, 0); break; default: ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; break; } if_send_crit_exit: dev_kfree_skb_any(skb); netif_start_queue(dev); clear_bit(SEND_CRIT,(void*)&card->wandev.critical); S508_S514_unlock(card, &smp_flags); return 0; } /*============================================================================ * Setup so that a frame can be transmitted on the occurrence of a transmit * interrupt. *===========================================================================*/ static void setup_for_delayed_transmit(struct net_device* dev, void* buf, unsigned len) { x25_channel_t* chan = dev->priv; sdla_t* card = chan->card; TX25Status* status = card->flags; ++chan->if_send_stat.if_send_adptr_bfrs_full; if(chan->transmit_length) { printk(KERN_INFO "%s: Error, transmit length set in delayed transmit!\n", card->devname); return; } if (chan->common.usedby == API){ if (len > X25_CHAN_MTU+sizeof(x25api_hdr_t)) { ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; printk(KERN_INFO "%s: Length is too big for delayed transmit\n", card->devname); return; } }else{ if (len > X25_MAX_DATA) { ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; printk(KERN_INFO "%s: Length is too big for delayed transmit\n", card->devname); return; } } chan->transmit_length = len; atomic_set(&chan->common.driver_busy,1); memcpy(chan->transmit_buffer, buf, len); ++chan->if_send_stat.if_send_tx_int_enabled; /* Enable Transmit Interrupt */ ++card->u.x.tx_interrupts_pending; status->imask |= INTR_ON_TX_FRAME; } /*=============================================================== * net_device_stats * * Get ethernet-style interface statistics. * Return a pointer to struct enet_statistics. * *==============================================================*/ static struct net_device_stats *if_stats(struct net_device* dev) { x25_channel_t *chan = dev->priv; if(chan == NULL) return NULL; return &chan->ifstats; } /* * Interrupt Handlers */ /* * X.25 Interrupt Service Routine. */ static void wpx_isr (sdla_t* card) { TX25Status* status = card->flags; card->in_isr = 1; ++card->statistics.isr_entry; if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){ card->in_isr=0; status->iflags = 0; return; } if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)){ printk(KERN_INFO "%s: wpx_isr: wandev.critical set to 0x%02lx, int type = 0x%02x\n", card->devname, card->wandev.critical, status->iflags); card->in_isr = 0; status->iflags = 0; return; } /* For all interrupts set the critical flag to CRITICAL_RX_INTR. * If the if_send routine is called with this flag set it will set * the enable transmit flag to 1. (for a delayed interrupt) */ switch (status->iflags){ case RX_INTR_PENDING: /* receive interrupt */ rx_intr(card); break; case TX_INTR_PENDING: /* transmit interrupt */ tx_intr(card); break; case MODEM_INTR_PENDING: /* modem status interrupt */ status_intr(card); break; case X25_ASY_TRANS_INTR_PENDING: /* network event interrupt */ event_intr(card); break; case TIMER_INTR_PENDING: timer_intr(card); break; default: /* unwanted interrupt */ spur_intr(card); } card->in_isr = 0; status->iflags = 0; /* clear interrupt condition */ } /* * Receive interrupt handler. * This routine handles fragmented IP packets using M-bit according to the * RFC1356. * o map ligical channel number to network interface. * o allocate socket buffer or append received packet to the existing one. * o if M-bit is reset (i.e. it's the last packet in a sequence) then * decapsulate packet and pass socket buffer to the protocol stack. * * Notes: * 1. When allocating a socket buffer, if M-bit is set then more data is * coming and we have to allocate buffer for the maximum IP packet size * expected on this channel. * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no * socket buffers available) the whole packet sequence must be discarded. */ static void rx_intr (sdla_t* card) { TX25Mbox* rxmb = card->rxmb; unsigned lcn = rxmb->cmd.lcn; struct net_device* dev = find_channel(card,lcn); x25_channel_t* chan; struct sk_buff* skb=NULL; if (dev == NULL){ /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n", card->devname, lcn); return; } chan = dev->priv; chan->i_timeout_sofar = jiffies; /* Copy the data from the board, into an * skb buffer */ if (wanpipe_pull_data_in_skb(card,dev,&skb)){ ++chan->ifstats.rx_dropped; ++card->wandev.stats.rx_dropped; ++chan->rx_intr_stat.rx_intr_no_socket; ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack; return; } dev->last_rx = jiffies; /* timestamp */ /* ------------ API ----------------*/ if (chan->common.usedby == API){ if (bh_enqueue(dev, skb)){ ++chan->ifstats.rx_dropped; ++card->wandev.stats.rx_dropped; ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack; dev_kfree_skb_any(skb); return; } ++chan->ifstats.rx_packets; chan->ifstats.rx_bytes += skb->len; chan->rx_skb = NULL; if (!test_and_set_bit(0, &chan->tq_working)){ wanpipe_queue_work(&chan->common.wanpipe_work); } return; } /* ------------- WANPIPE -------------------*/ /* set rx_skb to NULL so we won't access it later when kernel already owns it */ chan->rx_skb=NULL; /* Decapsulate packet, if necessary */ if (!skb->protocol && !wanrouter_type_trans(skb, dev)){ /* can't decapsulate packet */ dev_kfree_skb_any(skb); ++chan->ifstats.rx_errors; ++chan->ifstats.rx_dropped; ++card->wandev.stats.rx_dropped; ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack; }else{ if( handle_IPXWAN(skb->data, chan->name, chan->enable_IPX, chan->network_number, skb->protocol)){ if( chan->enable_IPX ){ if(chan_send(dev, skb->data, skb->len,0)){ chan->tx_skb = skb; }else{ dev_kfree_skb_any(skb); ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack; } }else{ /* increment IPX packet dropped statistic */ ++chan->ifstats.rx_dropped; ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack; } }else{ skb->mac.raw = skb->data; chan->ifstats.rx_bytes += skb->len; ++chan->ifstats.rx_packets; ++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack; netif_rx(skb); } } return; } static int wanpipe_pull_data_in_skb(sdla_t *card, struct net_device *dev, struct sk_buff **skb) { void *bufptr; TX25Mbox* rxmb = card->rxmb; unsigned len = rxmb->cmd.length; /* packet length */ unsigned qdm = rxmb->cmd.qdm; /* Q,D and M bits */ x25_channel_t *chan = dev->priv; struct sk_buff *new_skb = *skb; if (chan->common.usedby == WANPIPE){ if (chan->drop_sequence){ if (!(qdm & 0x01)){ chan->drop_sequence = 0; } return 1; } new_skb = chan->rx_skb; }else{ /* Add on the API header to the received * data */ len += sizeof(x25api_hdr_t); } if (new_skb == NULL){ int bufsize; if (chan->common.usedby == WANPIPE){ bufsize = (qdm & 0x01) ? dev->mtu : len; }else{ bufsize = len; } /* Allocate new socket buffer */ new_skb = dev_alloc_skb(bufsize + dev->hard_header_len); if (new_skb == NULL){ printk(KERN_INFO "%s: no socket buffers available!\n", card->devname); chan->drop_sequence = 1; /* set flag */ ++chan->ifstats.rx_dropped; return 1; } } if (skb_tailroom(new_skb) < len){ /* No room for the packet. Call off the whole thing! */ dev_kfree_skb_any(new_skb); if (chan->common.usedby == WANPIPE){ chan->rx_skb = NULL; if (qdm & 0x01){ chan->drop_sequence = 1; } } printk(KERN_INFO "%s: unexpectedly long packet sequence " "on interface %s!\n", card->devname, dev->name); ++chan->ifstats.rx_length_errors; return 1; } bufptr = skb_put(new_skb,len); if (chan->common.usedby == API){ /* Fill in the x25api header */ x25api_t * api_data = (x25api_t*)bufptr; api_data->hdr.qdm = rxmb->cmd.qdm; api_data->hdr.cause = rxmb->cmd.cause; api_data->hdr.diagn = rxmb->cmd.diagn; api_data->hdr.length = rxmb->cmd.length; memcpy(api_data->data, rxmb->data, rxmb->cmd.length); }else{ memcpy(bufptr, rxmb->data, len); } new_skb->dev = dev; if (chan->common.usedby == API){ new_skb->mac.raw = new_skb->data; new_skb->protocol = htons(X25_PROT); new_skb->pkt_type = WAN_PACKET_DATA; }else{ new_skb->protocol = chan->protocol; chan->rx_skb = new_skb; } /* If qdm bit is set, more data is coming * thus, exit and wait for more data before * sending the packet up. (Used by router only) */ if ((qdm & 0x01) && (chan->common.usedby == WANPIPE)) return 1; *skb = new_skb; return 0; } /*=============================================================== * tx_intr * * Transmit interrupt handler. * For each dev, check that there is something to send. * If data available, transmit. * *===============================================================*/ static void tx_intr (sdla_t* card) { struct net_device *dev; TX25Status* status = card->flags; unsigned char more_to_tx=0; x25_channel_t *chan=NULL; int i=0; if (card->u.x.tx_dev == NULL){ card->u.x.tx_dev = card->wandev.dev; } dev = card->u.x.tx_dev; for (;;){ chan = dev->priv; if (chan->transmit_length){ /* Device was set to transmit, check if the TX * buffers are available */ if (chan->common.state != WAN_CONNECTED){ chan->transmit_length = 0; atomic_set(&chan->common.driver_busy,0); chan->tx_offset=0; if (netif_queue_stopped(dev)){ if (chan->common.usedby == API){ netif_start_queue(dev); wakeup_sk_bh(dev); }else{ netif_wake_queue(dev); } } dev = move_dev_to_next(card,dev); break; } if ((status->cflags[chan->ch_idx] & 0x40 || card->u.x.LAPB_hdlc) && (*card->u.x.hdlc_buf_status & 0x40) ){ /* Tx buffer available, we can send */ if (tx_intr_send(card, dev)){ more_to_tx=1; } /* If more than one interface present, move the * device pointer to the next interface, so on the * next TX interrupt we will try sending from it. */ dev = move_dev_to_next(card,dev); break; }else{ /* Tx buffers not available, but device set * the TX interrupt. Set more_to_tx and try * to transmit for other devices. */ more_to_tx=1; dev = move_dev_to_next(card,dev); } }else{ /* This device was not set to transmit, * go to next */ dev = move_dev_to_next(card,dev); } if (++i == card->u.x.no_dev){ if (!more_to_tx){ DBG_PRINTK(KERN_INFO "%s: Nothing to Send in TX INTR\n", card->devname); } break; } } //End of FOR card->u.x.tx_dev = dev; if (!more_to_tx){ /* if any other interfaces have transmit interrupts pending, */ /* do not disable the global transmit interrupt */ if (!(--card->u.x.tx_interrupts_pending)){ status->imask &= ~INTR_ON_TX_FRAME; } } return; } /*=============================================================== * move_dev_to_next * * *===============================================================*/ struct net_device *move_dev_to_next(sdla_t *card, struct net_device *dev) { if (card->u.x.no_dev != 1){ if (!*((struct net_device **)dev->priv)) return card->wandev.dev; else return *((struct net_device **)dev->priv); } return dev; } /*=============================================================== * tx_intr_send * * *===============================================================*/ static int tx_intr_send(sdla_t *card, struct net_device *dev) { x25_channel_t* chan = dev->priv; if (chan_send (dev,chan->transmit_buffer,chan->transmit_length,1)){ /* Packet was split up due to its size, do not disable * tx_intr */ return 1; } chan->transmit_length=0; atomic_set(&chan->common.driver_busy,0); chan->tx_offset=0; /* If we are in API mode, wakeup the * sock BH handler, not the NET_BH */ if (netif_queue_stopped(dev)){ if (chan->common.usedby == API){ netif_start_queue(dev); wakeup_sk_bh(dev); }else{ netif_wake_queue(dev); } } return 0; } /*=============================================================== * timer_intr * * Timer interrupt handler. * Check who called the timer interrupt and perform * action accordingly. * *===============================================================*/ static void timer_intr (sdla_t *card) { TX25Status* status = card->flags; if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC){ if (timer_intr_cmd_exec(card) == 0){ card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_CMD_EXEC; } }else if(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UDP_PKT) { if ((*card->u.x.hdlc_buf_status & 0x40) && card->u.x.udp_type == UDP_XPIPE_TYPE){ if(process_udp_mgmt_pkt(card)) { card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UDP_PKT; } } }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_ACTIVE) { struct net_device *dev = card->u.x.poll_device; x25_channel_t *chan = NULL; if (!dev){ card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE; return; } chan = dev->priv; printk(KERN_INFO "%s: Closing down Idle link %s on LCN %d\n", card->devname,chan->name,chan->common.lcn); chan->i_timeout_sofar = jiffies; chan_disc(dev); card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE; card->u.x.poll_device=NULL; }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_ON) { wanpipe_set_state(card, WAN_CONNECTED); if (card->u.x.LAPB_hdlc){ struct net_device *dev = card->wandev.dev; set_chan_state(dev,WAN_CONNECTED); send_delayed_cmd_result(card,dev,card->mbox); } /* 0x8F enable all interrupts */ x25_set_intr_mode(card, INTR_ON_RX_FRAME| INTR_ON_TX_FRAME| INTR_ON_MODEM_STATUS_CHANGE| //INTR_ON_COMMAND_COMPLETE| X25_ASY_TRANS_INTR_PENDING | INTR_ON_TIMER | DIRECT_RX_INTR_USAGE ); status->imask &= ~INTR_ON_TX_FRAME; /* mask Tx interrupts */ card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_ON; }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_OFF) { //printk(KERN_INFO "Poll connect, Turning OFF\n"); disconnect(card); card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_OFF; }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_DISCONNECT) { //printk(KERN_INFO "POll disconnect, trying to connect\n"); connect(card); card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_DISCONNECT; }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE){ if (*card->u.x.hdlc_buf_status & 0x40){ x25_get_err_stats(card); x25_get_stats(card); card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE; } } if(!card->u.x.timer_int_enabled){ //printk(KERN_INFO "Turning Timer Off \n"); status->imask &= ~INTR_ON_TIMER; } } /*==================================================================== * Modem status interrupt handler. *===================================================================*/ static void status_intr (sdla_t* card) { /* Added to avoid Modem status message flooding */ static TX25ModemStatus last_stat; TX25Mbox* mbox = card->mbox; TX25ModemStatus *modem_status; struct net_device *dev; x25_channel_t *chan; int err; memset(&mbox->cmd, 0, sizeof(TX25Cmd)); mbox->cmd.command = X25_READ_MODEM_STATUS; err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT; if (err){ x25_error(card, err, X25_READ_MODEM_STATUS, 0); }else{ modem_status = (TX25ModemStatus*)mbox->data; /* Check if the last status was the same * if it was, do NOT print message again */ if (last_stat.status != modem_status->status){ printk(KERN_INFO "%s: Modem Status Change: DCD=%s, CTS=%s\n", card->devname,DCD(modem_status->status),CTS(modem_status->status)); last_stat.status = modem_status->status; if (card->u.x.oob_on_modem){ mbox->cmd.pktType = mbox->cmd.command; mbox->cmd.result = 0x08; /* Send a OOB to all connected sockets */ for (dev = c