|
@@ -1,5497 +0,0 @@
|
|
-/*****************************************************************************
|
|
|
|
-* sdla_x25.c WANPIPE(tm) Multiprotocol WAN Link Driver. X.25 module.
|
|
|
|
-*
|
|
|
|
-* Author: Nenad Corbic <ncorbic@sangoma.com>
|
|
|
|
-*
|
|
|
|
-* Copyright: (c) 1995-2001 Sangoma Technologies Inc.
|
|
|
|
-*
|
|
|
|
-* This program is free software; you can redistribute it and/or
|
|
|
|
-* modify it under the terms of the GNU General Public License
|
|
|
|
-* as published by the Free Software Foundation; either version
|
|
|
|
-* 2 of the License, or (at your option) any later version.
|
|
|
|
-* ============================================================================
|
|
|
|
-* Apr 03, 2001 Nenad Corbic o Fixed the rx_skb=NULL bug in x25 in rx_intr().
|
|
|
|
-* Dec 26, 2000 Nenad Corbic o Added a new polling routine, that uses
|
|
|
|
-* a kernel timer (more efficient).
|
|
|
|
-* Dec 25, 2000 Nenad Corbic o Updated for 2.4.X kernel
|
|
|
|
-* Jul 26, 2000 Nenad Corbic o Increased the local packet buffering
|
|
|
|
-* for API to 4096+header_size.
|
|
|
|
-* Jul 17, 2000 Nenad Corbic o Fixed the x25 startup bug. Enable
|
|
|
|
-* communications only after all interfaces
|
|
|
|
-* come up. HIGH SVC/PVC is used to calculate
|
|
|
|
-* the number of channels.
|
|
|
|
-* Enable protocol only after all interfaces
|
|
|
|
-* are enabled.
|
|
|
|
-* Jul 10, 2000 Nenad Corbic o Fixed the M_BIT bug.
|
|
|
|
-* Apr 25, 2000 Nenad Corbic o Pass Modem messages to the API.
|
|
|
|
-* Disable idle timeout in X25 API.
|
|
|
|
-* Apr 14, 2000 Nenad Corbic o Fixed: Large LCN number support.
|
|
|
|
-* Maximum LCN number is 4095.
|
|
|
|
-* Maximum number of X25 channels is 255.
|
|
|
|
-* Apr 06, 2000 Nenad Corbic o Added SMP Support.
|
|
|
|
-* Mar 29, 2000 Nenad Corbic o Added support for S514 PCI Card
|
|
|
|
-* Mar 23, 2000 Nenad Corbic o Improved task queue, BH handling.
|
|
|
|
-* Mar 14, 2000 Nenad Corbic o Updated Protocol Violation handling
|
|
|
|
-* routines. Bug Fix.
|
|
|
|
-* Mar 10, 2000 Nenad Corbic o Bug Fix: corrupted mbox recovery.
|
|
|
|
-* Mar 09, 2000 Nenad Corbic o Fixed the auto HDLC bug.
|
|
|
|
-* Mar 08, 2000 Nenad Corbic o Fixed LAPB HDLC startup problems.
|
|
|
|
-* Application must bring the link up
|
|
|
|
-* before tx/rx, and bring the
|
|
|
|
-* link down on close().
|
|
|
|
-* Mar 06, 2000 Nenad Corbic o Added an option for logging call setup
|
|
|
|
-* information.
|
|
|
|
-* Feb 29, 2000 Nenad Corbic o Added support for LAPB HDLC API
|
|
|
|
-* Feb 25, 2000 Nenad Corbic o Fixed the modem failure handling.
|
|
|
|
-* No Modem OOB message will be passed
|
|
|
|
-* to the user.
|
|
|
|
-* Feb 21, 2000 Nenad Corbic o Added Xpipemon Debug Support
|
|
|
|
-* Dec 30, 1999 Nenad Corbic o Socket based X25API
|
|
|
|
-* Sep 17, 1998 Jaspreet Singh o Updates for 2.2.X kernel
|
|
|
|
-* Mar 15, 1998 Alan Cox o 2.1.x porting
|
|
|
|
-* Dec 19, 1997 Jaspreet Singh o Added multi-channel IPX support
|
|
|
|
-* Nov 27, 1997 Jaspreet Singh o Added protection against enabling of irqs
|
|
|
|
-* when they are disabled.
|
|
|
|
-* Nov 17, 1997 Farhan Thawar o Added IPX support
|
|
|
|
-* o Changed if_send() to now buffer packets when
|
|
|
|
-* the board is busy
|
|
|
|
-* o Removed queueing of packets via the polling
|
|
|
|
-* routing
|
|
|
|
-* o Changed if_send() critical flags to properly
|
|
|
|
-* handle race conditions
|
|
|
|
-* Nov 06, 1997 Farhan Thawar o Added support for SVC timeouts
|
|
|
|
-* o Changed PVC encapsulation to ETH_P_IP
|
|
|
|
-* Jul 21, 1997 Jaspreet Singh o Fixed freeing up of buffers using kfree()
|
|
|
|
-* when packets are received.
|
|
|
|
-* Mar 11, 1997 Farhan Thawar Version 3.1.1
|
|
|
|
-* o added support for V35
|
|
|
|
-* o changed if_send() to return 0 if
|
|
|
|
-* wandev.critical() is true
|
|
|
|
-* o free socket buffer in if_send() if
|
|
|
|
-* returning 0
|
|
|
|
-* o added support for single '@' address to
|
|
|
|
-* accept all incoming calls
|
|
|
|
-* o fixed bug in set_chan_state() to disconnect
|
|
|
|
-* Jan 15, 1997 Gene Kozin Version 3.1.0
|
|
|
|
-* o implemented exec() entry point
|
|
|
|
-* Jan 07, 1997 Gene Kozin Initial version.
|
|
|
|
-*****************************************************************************/
|
|
|
|
-
|
|
|
|
-/*======================================================
|
|
|
|
- * Includes
|
|
|
|
- *=====================================================*/
|
|
|
|
-
|
|
|
|
-#include <linux/module.h>
|
|
|
|
-#include <linux/kernel.h> /* printk(), and other useful stuff */
|
|
|
|
-#include <linux/stddef.h> /* offsetof(), etc. */
|
|
|
|
-#include <linux/errno.h> /* return codes */
|
|
|
|
-#include <linux/string.h> /* inline memset(), etc. */
|
|
|
|
-#include <linux/ctype.h>
|
|
|
|
-#include <linux/slab.h> /* kmalloc(), kfree() */
|
|
|
|
-#include <linux/wanrouter.h> /* WAN router definitions */
|
|
|
|
-#include <linux/wanpipe.h> /* WANPIPE common user API definitions */
|
|
|
|
-#include <linux/workqueue.h>
|
|
|
|
-#include <linux/jiffies.h> /* time_after() macro */
|
|
|
|
-#include <asm/byteorder.h> /* htons(), etc. */
|
|
|
|
-#include <asm/atomic.h>
|
|
|
|
-#include <linux/delay.h> /* Experimental delay */
|
|
|
|
-
|
|
|
|
-#include <asm/uaccess.h>
|
|
|
|
-
|
|
|
|
-#include <linux/if.h>
|
|
|
|
-#include <linux/if_arp.h>
|
|
|
|
-#include <linux/sdla_x25.h> /* X.25 firmware API definitions */
|
|
|
|
-#include <linux/if_wanpipe_common.h>
|
|
|
|
-#include <linux/if_wanpipe.h>
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*======================================================
|
|
|
|
- * Defines & Macros
|
|
|
|
- *=====================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-#define CMD_OK 0 /* normal firmware return code */
|
|
|
|
-#define CMD_TIMEOUT 0xFF /* firmware command timed out */
|
|
|
|
-#define MAX_CMD_RETRY 10 /* max number of firmware retries */
|
|
|
|
-
|
|
|
|
-#define X25_CHAN_MTU 4096 /* unfragmented logical channel MTU */
|
|
|
|
-#define X25_HRDHDR_SZ 7 /* max encapsulation header size */
|
|
|
|
-#define X25_CONCT_TMOUT (90*HZ) /* link connection timeout */
|
|
|
|
-#define X25_RECON_TMOUT (10*HZ) /* link connection timeout */
|
|
|
|
-#define CONNECT_TIMEOUT (90*HZ) /* link connection timeout */
|
|
|
|
-#define HOLD_DOWN_TIME (30*HZ) /* link hold down time */
|
|
|
|
-#define MAX_BH_BUFF 10
|
|
|
|
-#define M_BIT 0x01
|
|
|
|
-
|
|
|
|
-//#define PRINT_DEBUG 1
|
|
|
|
-#ifdef PRINT_DEBUG
|
|
|
|
-#define DBG_PRINTK(format, a...) printk(format, ## a)
|
|
|
|
-#else
|
|
|
|
-#define DBG_PRINTK(format, a...)
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-#define TMR_INT_ENABLED_POLL_ACTIVE 0x01
|
|
|
|
-#define TMR_INT_ENABLED_POLL_CONNECT_ON 0x02
|
|
|
|
-#define TMR_INT_ENABLED_POLL_CONNECT_OFF 0x04
|
|
|
|
-#define TMR_INT_ENABLED_POLL_DISCONNECT 0x08
|
|
|
|
-#define TMR_INT_ENABLED_CMD_EXEC 0x10
|
|
|
|
-#define TMR_INT_ENABLED_UPDATE 0x20
|
|
|
|
-#define TMR_INT_ENABLED_UDP_PKT 0x40
|
|
|
|
-
|
|
|
|
-#define MAX_X25_ADDR_SIZE 16
|
|
|
|
-#define MAX_X25_DATA_SIZE 129
|
|
|
|
-#define MAX_X25_FACL_SIZE 110
|
|
|
|
-
|
|
|
|
-#define TRY_CMD_AGAIN 2
|
|
|
|
-#define DELAY_RESULT 1
|
|
|
|
-#define RETURN_RESULT 0
|
|
|
|
-
|
|
|
|
-#define DCD(x) (x & 0x03 ? "HIGH" : "LOW")
|
|
|
|
-#define CTS(x) (x & 0x05 ? "HIGH" : "LOW")
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/* Driver will not write log messages about
|
|
|
|
- * modem status if defined.*/
|
|
|
|
-#define MODEM_NOT_LOG 1
|
|
|
|
-
|
|
|
|
-/*====================================================
|
|
|
|
- * For IPXWAN
|
|
|
|
- *===================================================*/
|
|
|
|
-
|
|
|
|
-#define CVHexToAscii(b) (((unsigned char)(b) > (unsigned char)9) ? ((unsigned char)'A' + ((unsigned char)(b) - (unsigned char)10)) : ((unsigned char)'0' + (unsigned char)(b)))
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*====================================================
|
|
|
|
- * MEMORY DEBUGGING FUNCTION
|
|
|
|
- *====================================================
|
|
|
|
-
|
|
|
|
-#define KMEM_SAFETYZONE 8
|
|
|
|
-
|
|
|
|
-static void * dbg_kmalloc(unsigned int size, int prio, int line) {
|
|
|
|
- int i = 0;
|
|
|
|
- void * v = kmalloc(size+sizeof(unsigned int)+2*KMEM_SAFETYZONE*8,prio);
|
|
|
|
- char * c1 = v;
|
|
|
|
- c1 += sizeof(unsigned int);
|
|
|
|
- *((unsigned int *)v) = size;
|
|
|
|
-
|
|
|
|
- for (i = 0; i < KMEM_SAFETYZONE; i++) {
|
|
|
|
- c1[0] = 'D'; c1[1] = 'E'; c1[2] = 'A'; c1[3] = 'D';
|
|
|
|
- c1[4] = 'B'; c1[5] = 'E'; c1[6] = 'E'; c1[7] = 'F';
|
|
|
|
- c1 += 8;
|
|
|
|
- }
|
|
|
|
- c1 += size;
|
|
|
|
- for (i = 0; i < KMEM_SAFETYZONE; i++) {
|
|
|
|
- c1[0] = 'M'; c1[1] = 'U'; c1[2] = 'N'; c1[3] = 'G';
|
|
|
|
- c1[4] = 'W'; c1[5] = 'A'; c1[6] = 'L'; c1[7] = 'L';
|
|
|
|
- c1 += 8;
|
|
|
|
- }
|
|
|
|
- v = ((char *)v) + sizeof(unsigned int) + KMEM_SAFETYZONE*8;
|
|
|
|
- printk(KERN_INFO "line %d kmalloc(%d,%d) = %p\n",line,size,prio,v);
|
|
|
|
- return v;
|
|
|
|
-}
|
|
|
|
-static void dbg_kfree(void * v, int line) {
|
|
|
|
- unsigned int * sp = (unsigned int *)(((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8));
|
|
|
|
- unsigned int size = *sp;
|
|
|
|
- char * c1 = ((char *)v) - KMEM_SAFETYZONE*8;
|
|
|
|
- int i = 0;
|
|
|
|
- for (i = 0; i < KMEM_SAFETYZONE; i++) {
|
|
|
|
- if ( c1[0] != 'D' || c1[1] != 'E' || c1[2] != 'A' || c1[3] != 'D'
|
|
|
|
- || c1[4] != 'B' || c1[5] != 'E' || c1[6] != 'E' || c1[7] != 'F') {
|
|
|
|
- printk(KERN_INFO "kmalloced block at %p has been corrupted (underrun)!\n",v);
|
|
|
|
- printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
|
|
|
|
- c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
|
|
|
|
- }
|
|
|
|
- c1 += 8;
|
|
|
|
- }
|
|
|
|
- c1 += size;
|
|
|
|
- for (i = 0; i < KMEM_SAFETYZONE; i++) {
|
|
|
|
- if ( c1[0] != 'M' || c1[1] != 'U' || c1[2] != 'N' || c1[3] != 'G'
|
|
|
|
- || c1[4] != 'W' || c1[5] != 'A' || c1[6] != 'L' || c1[7] != 'L'
|
|
|
|
- ) {
|
|
|
|
- printk(KERN_INFO "kmalloced block at %p has been corrupted (overrun):\n",v);
|
|
|
|
- printk(KERN_INFO " %4x: %2x %2x %2x %2x %2x %2x %2x %2x\n", i*8,
|
|
|
|
- c1[0],c1[1],c1[2],c1[3],c1[4],c1[5],c1[6],c1[7] );
|
|
|
|
- }
|
|
|
|
- c1 += 8;
|
|
|
|
- }
|
|
|
|
- printk(KERN_INFO "line %d kfree(%p)\n",line,v);
|
|
|
|
- v = ((char *)v) - (sizeof(unsigned int) + KMEM_SAFETYZONE*8);
|
|
|
|
- kfree(v);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#define kmalloc(x,y) dbg_kmalloc(x,y,__LINE__)
|
|
|
|
-#define kfree(x) dbg_kfree(x,__LINE__)
|
|
|
|
-
|
|
|
|
-==============================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================
|
|
|
|
- * Data Structures
|
|
|
|
- *===============================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*========================================================
|
|
|
|
- * Name: x25_channel
|
|
|
|
- *
|
|
|
|
- * Purpose: To hold private informaton for each
|
|
|
|
- * logical channel.
|
|
|
|
- *
|
|
|
|
- * Rationale: Per-channel debugging is possible if each
|
|
|
|
- * channel has its own private area.
|
|
|
|
- *
|
|
|
|
- * Assumptions:
|
|
|
|
- *
|
|
|
|
- * Description: This is an extention of the struct net_device
|
|
|
|
- * we create for each network interface to keep
|
|
|
|
- * the rest of X.25 channel-specific data.
|
|
|
|
- *
|
|
|
|
- * Construct: Typedef
|
|
|
|
- */
|
|
|
|
-typedef struct x25_channel
|
|
|
|
-{
|
|
|
|
- wanpipe_common_t common; /* common area for x25api and socket */
|
|
|
|
- char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */
|
|
|
|
- char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */
|
|
|
|
- unsigned tx_pkt_size;
|
|
|
|
- unsigned short protocol; /* ethertype, 0 - multiplexed */
|
|
|
|
- char drop_sequence; /* mark sequence for dropping */
|
|
|
|
- unsigned long state_tick; /* time of the last state change */
|
|
|
|
- unsigned idle_timeout; /* sec, before disconnecting */
|
|
|
|
- unsigned long i_timeout_sofar; /* # of sec's we've been idle */
|
|
|
|
- unsigned hold_timeout; /* sec, before re-connecting */
|
|
|
|
- unsigned long tick_counter; /* counter for transmit time out */
|
|
|
|
- char devtint; /* Weather we should dev_tint() */
|
|
|
|
- struct sk_buff* rx_skb; /* receive socket buffer */
|
|
|
|
- struct sk_buff* tx_skb; /* transmit socket buffer */
|
|
|
|
-
|
|
|
|
- bh_data_t *bh_head; /* Circular buffer for x25api_bh */
|
|
|
|
- unsigned long tq_working;
|
|
|
|
- volatile int bh_write;
|
|
|
|
- volatile int bh_read;
|
|
|
|
- atomic_t bh_buff_used;
|
|
|
|
-
|
|
|
|
- sdla_t* card; /* -> owner */
|
|
|
|
- struct net_device *dev; /* -> bound devce */
|
|
|
|
-
|
|
|
|
- int ch_idx;
|
|
|
|
- unsigned char enable_IPX;
|
|
|
|
- unsigned long network_number;
|
|
|
|
- struct net_device_stats ifstats; /* interface statistics */
|
|
|
|
- unsigned short transmit_length;
|
|
|
|
- unsigned short tx_offset;
|
|
|
|
- char transmit_buffer[X25_CHAN_MTU+sizeof(x25api_hdr_t)];
|
|
|
|
-
|
|
|
|
- if_send_stat_t if_send_stat;
|
|
|
|
- rx_intr_stat_t rx_intr_stat;
|
|
|
|
- pipe_mgmt_stat_t pipe_mgmt_stat;
|
|
|
|
-
|
|
|
|
- unsigned long router_start_time; /* Router start time in seconds */
|
|
|
|
- unsigned long router_up_time;
|
|
|
|
-
|
|
|
|
-} x25_channel_t;
|
|
|
|
-
|
|
|
|
-/* FIXME Take this out */
|
|
|
|
-
|
|
|
|
-#ifdef NEX_OLD_CALL_INFO
|
|
|
|
-typedef struct x25_call_info
|
|
|
|
-{
|
|
|
|
- char dest[17]; PACKED;/* ASCIIZ destination address */
|
|
|
|
- char src[17]; PACKED;/* ASCIIZ source address */
|
|
|
|
- char nuser; PACKED;/* number of user data bytes */
|
|
|
|
- unsigned char user[127]; PACKED;/* user data */
|
|
|
|
- char nfacil; PACKED;/* number of facilities */
|
|
|
|
- struct
|
|
|
|
- {
|
|
|
|
- unsigned char code; PACKED;
|
|
|
|
- unsigned char parm; PACKED;
|
|
|
|
- } facil[64]; /* facilities */
|
|
|
|
-} x25_call_info_t;
|
|
|
|
-#else
|
|
|
|
-typedef struct x25_call_info
|
|
|
|
-{
|
|
|
|
- char dest[MAX_X25_ADDR_SIZE] PACKED;/* ASCIIZ destination address */
|
|
|
|
- char src[MAX_X25_ADDR_SIZE] PACKED;/* ASCIIZ source address */
|
|
|
|
- unsigned char nuser PACKED;
|
|
|
|
- unsigned char user[MAX_X25_DATA_SIZE] PACKED;/* user data */
|
|
|
|
- unsigned char nfacil PACKED;
|
|
|
|
- unsigned char facil[MAX_X25_FACL_SIZE] PACKED;
|
|
|
|
- unsigned short lcn PACKED;
|
|
|
|
-} x25_call_info_t;
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================
|
|
|
|
- * Private Function Prototypes
|
|
|
|
- *==============================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * WAN link driver entry points. These are
|
|
|
|
- * called by the WAN router module.
|
|
|
|
- */
|
|
|
|
-static int update(struct wan_device* wandev);
|
|
|
|
-static int new_if(struct wan_device* wandev, struct net_device* dev,
|
|
|
|
- wanif_conf_t* conf);
|
|
|
|
-static int del_if(struct wan_device* wandev, struct net_device* dev);
|
|
|
|
-static void disable_comm (sdla_t* card);
|
|
|
|
-static void disable_comm_shutdown(sdla_t *card);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * WANPIPE-specific entry points
|
|
|
|
- */
|
|
|
|
-static int wpx_exec (struct sdla* card, void* u_cmd, void* u_data);
|
|
|
|
-static void x25api_bh(struct net_device *dev);
|
|
|
|
-static int x25api_bh_cleanup(struct net_device *dev);
|
|
|
|
-static int bh_enqueue(struct net_device *dev, struct sk_buff *skb);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Network device interface
|
|
|
|
- */
|
|
|
|
-static int if_init(struct net_device* dev);
|
|
|
|
-static int if_open(struct net_device* dev);
|
|
|
|
-static int if_close(struct net_device* dev);
|
|
|
|
-static int if_header(struct sk_buff* skb, struct net_device* dev,
|
|
|
|
- unsigned short type, void* daddr, void* saddr, unsigned len);
|
|
|
|
-static int if_rebuild_hdr (struct sk_buff* skb);
|
|
|
|
-static int if_send(struct sk_buff* skb, struct net_device* dev);
|
|
|
|
-static struct net_device_stats *if_stats(struct net_device* dev);
|
|
|
|
-
|
|
|
|
-static void if_tx_timeout(struct net_device *dev);
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Interrupt handlers
|
|
|
|
- */
|
|
|
|
-static void wpx_isr (sdla_t *);
|
|
|
|
-static void rx_intr (sdla_t *);
|
|
|
|
-static void tx_intr (sdla_t *);
|
|
|
|
-static void status_intr (sdla_t *);
|
|
|
|
-static void event_intr (sdla_t *);
|
|
|
|
-static void spur_intr (sdla_t *);
|
|
|
|
-static void timer_intr (sdla_t *);
|
|
|
|
-
|
|
|
|
-static int tx_intr_send(sdla_t *card, struct net_device *dev);
|
|
|
|
-static struct net_device *move_dev_to_next(sdla_t *card,
|
|
|
|
- struct net_device *dev);
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Background polling routines
|
|
|
|
- */
|
|
|
|
-static void wpx_poll (sdla_t* card);
|
|
|
|
-static void poll_disconnected (sdla_t* card);
|
|
|
|
-static void poll_connecting (sdla_t* card);
|
|
|
|
-static void poll_active (sdla_t* card);
|
|
|
|
-static void trigger_x25_poll(sdla_t *card);
|
|
|
|
-static void x25_timer_routine(unsigned long data);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * X.25 firmware interface functions
|
|
|
|
- */
|
|
|
|
-static int x25_get_version (sdla_t* card, char* str);
|
|
|
|
-static int x25_configure (sdla_t* card, TX25Config* conf);
|
|
|
|
-static int hdlc_configure (sdla_t* card, TX25Config* conf);
|
|
|
|
-static int set_hdlc_level (sdla_t* card);
|
|
|
|
-static int x25_get_err_stats (sdla_t* card);
|
|
|
|
-static int x25_get_stats (sdla_t* card);
|
|
|
|
-static int x25_set_intr_mode (sdla_t* card, int mode);
|
|
|
|
-static int x25_close_hdlc (sdla_t* card);
|
|
|
|
-static int x25_open_hdlc (sdla_t* card);
|
|
|
|
-static int x25_setup_hdlc (sdla_t* card);
|
|
|
|
-static int x25_set_dtr (sdla_t* card, int dtr);
|
|
|
|
-static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan);
|
|
|
|
-static int x25_place_call (sdla_t* card, x25_channel_t* chan);
|
|
|
|
-static int x25_accept_call (sdla_t* card, int lcn, int qdm);
|
|
|
|
-static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn);
|
|
|
|
-static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf);
|
|
|
|
-static int x25_fetch_events (sdla_t* card);
|
|
|
|
-static int x25_error (sdla_t* card, int err, int cmd, int lcn);
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * X.25 asynchronous event handlers
|
|
|
|
- */
|
|
|
|
-static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
|
|
|
|
-static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
|
|
|
|
-static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
|
|
|
|
-static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
|
|
|
|
-static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Miscellaneous functions
|
|
|
|
- */
|
|
|
|
-static int connect (sdla_t* card);
|
|
|
|
-static int disconnect (sdla_t* card);
|
|
|
|
-static struct net_device* get_dev_by_lcn(struct wan_device* wandev,
|
|
|
|
- unsigned lcn);
|
|
|
|
-static int chan_connect(struct net_device* dev);
|
|
|
|
-static int chan_disc(struct net_device* dev);
|
|
|
|
-static void set_chan_state(struct net_device* dev, int state);
|
|
|
|
-static int chan_send(struct net_device *dev, void* buff, unsigned data_len,
|
|
|
|
- unsigned char tx_intr);
|
|
|
|
-static unsigned char bps_to_speed_code (unsigned long bps);
|
|
|
|
-static unsigned int dec_to_uint (unsigned char* str, int len);
|
|
|
|
-static unsigned int hex_to_uint (unsigned char*, int);
|
|
|
|
-static void parse_call_info (unsigned char*, x25_call_info_t*);
|
|
|
|
-static struct net_device *find_channel(sdla_t *card, unsigned lcn);
|
|
|
|
-static void bind_lcn_to_dev(sdla_t *card, struct net_device *dev, unsigned lcn);
|
|
|
|
-static void setup_for_delayed_transmit(struct net_device *dev,
|
|
|
|
- void *buf, unsigned len);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * X25 API Functions
|
|
|
|
- */
|
|
|
|
-static int wanpipe_pull_data_in_skb(sdla_t *card, struct net_device *dev,
|
|
|
|
- struct sk_buff **);
|
|
|
|
-static void timer_intr_exec(sdla_t *, unsigned char);
|
|
|
|
-static int execute_delayed_cmd(sdla_t *card, struct net_device *dev,
|
|
|
|
- mbox_cmd_t *usr_cmd, char bad_cmd);
|
|
|
|
-static int api_incoming_call (sdla_t*, TX25Mbox *, int);
|
|
|
|
-static int alloc_and_init_skb_buf (sdla_t *,struct sk_buff **, int);
|
|
|
|
-static void send_delayed_cmd_result(sdla_t *card, struct net_device *dev,
|
|
|
|
- TX25Mbox* mbox);
|
|
|
|
-static int clear_confirm_event (sdla_t *, TX25Mbox*);
|
|
|
|
-static void send_oob_msg (sdla_t *card, struct net_device *dev, TX25Mbox *mbox);
|
|
|
|
-static int timer_intr_cmd_exec(sdla_t *card);
|
|
|
|
-static void api_oob_event (sdla_t *card,TX25Mbox *mbox);
|
|
|
|
-static int check_bad_command(sdla_t *card, struct net_device *dev);
|
|
|
|
-static int channel_disconnect(sdla_t* card, struct net_device *dev);
|
|
|
|
-static void hdlc_link_down (sdla_t*);
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * XPIPEMON Functions
|
|
|
|
- */
|
|
|
|
-static int process_udp_mgmt_pkt(sdla_t *);
|
|
|
|
-static int udp_pkt_type( struct sk_buff *, sdla_t*);
|
|
|
|
-static int reply_udp( unsigned char *, unsigned int);
|
|
|
|
-static void init_x25_channel_struct( x25_channel_t *);
|
|
|
|
-static void init_global_statistics( sdla_t *);
|
|
|
|
-static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t *card,
|
|
|
|
- struct net_device *dev,
|
|
|
|
- struct sk_buff *skb, int lcn);
|
|
|
|
-static unsigned short calc_checksum (char *, int);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * IPX functions
|
|
|
|
- */
|
|
|
|
-static void switch_net_numbers(unsigned char *, unsigned long, unsigned char);
|
|
|
|
-static int handle_IPXWAN(unsigned char *, char *, unsigned char ,
|
|
|
|
- unsigned long , unsigned short );
|
|
|
|
-
|
|
|
|
-extern void disable_irq(unsigned int);
|
|
|
|
-extern void enable_irq(unsigned int);
|
|
|
|
-
|
|
|
|
-static void S508_S514_lock(sdla_t *, unsigned long *);
|
|
|
|
-static void S508_S514_unlock(sdla_t *, unsigned long *);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Global Variables
|
|
|
|
- *=================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=================================================
|
|
|
|
- * Public Functions
|
|
|
|
- *=================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===================================================================
|
|
|
|
- * wpx_init: X.25 Protocol Initialization routine.
|
|
|
|
- *
|
|
|
|
- * Purpose: To initialize the protocol/firmware.
|
|
|
|
- *
|
|
|
|
- * Rationale: This function is called by setup() function, in
|
|
|
|
- * sdlamain.c, to dynamically setup the x25 protocol.
|
|
|
|
- * This is the first protocol specific function, which
|
|
|
|
- * executes once on startup.
|
|
|
|
- *
|
|
|
|
- * Description: This procedure initializes the x25 firmware and
|
|
|
|
- * sets up the mailbox, transmit and receive buffer
|
|
|
|
- * pointers. It also initializes all debugging structures
|
|
|
|
- * and sets up the X25 environment.
|
|
|
|
- *
|
|
|
|
- * Sets up hardware options defined by user in [wanpipe#]
|
|
|
|
- * section of wanpipe#.conf configuration file.
|
|
|
|
- *
|
|
|
|
- * At this point adapter is completely initialized
|
|
|
|
- * and X.25 firmware is running.
|
|
|
|
- * o read firmware version (to make sure it's alive)
|
|
|
|
- * o configure adapter
|
|
|
|
- * o initialize protocol-specific fields of the
|
|
|
|
- * adapter data space.
|
|
|
|
- *
|
|
|
|
- * Called by: setup() function in sdlamain.c
|
|
|
|
- *
|
|
|
|
- * Assumptions: None
|
|
|
|
- *
|
|
|
|
- * Warnings: None
|
|
|
|
- *
|
|
|
|
- * Return: 0 o.k.
|
|
|
|
- * < 0 failure.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-int wpx_init (sdla_t* card, wandev_conf_t* conf)
|
|
|
|
-{
|
|
|
|
- union{
|
|
|
|
- char str[80];
|
|
|
|
- TX25Config cfg;
|
|
|
|
- } u;
|
|
|
|
-
|
|
|
|
- /* Verify configuration ID */
|
|
|
|
- if (conf->config_id != WANCONFIG_X25){
|
|
|
|
- printk(KERN_INFO "%s: invalid configuration ID %u!\n",
|
|
|
|
- card->devname, conf->config_id)
|
|
|
|
- ;
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Initialize protocol-specific fields */
|
|
|
|
- card->mbox = (void*)(card->hw.dpmbase + X25_MBOX_OFFS);
|
|
|
|
- card->rxmb = (void*)(card->hw.dpmbase + X25_RXMBOX_OFFS);
|
|
|
|
- card->flags = (void*)(card->hw.dpmbase + X25_STATUS_OFFS);
|
|
|
|
-
|
|
|
|
- /* Initialize for S514 Card */
|
|
|
|
- if(card->hw.type == SDLA_S514) {
|
|
|
|
- card->mbox += X25_MB_VECTOR;
|
|
|
|
- card->flags += X25_MB_VECTOR;
|
|
|
|
- card->rxmb += X25_MB_VECTOR;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Read firmware version. Note that when adapter initializes, it
|
|
|
|
- * clears the mailbox, so it may appear that the first command was
|
|
|
|
- * executed successfully when in fact it was merely erased. To work
|
|
|
|
- * around this, we execute the first command twice.
|
|
|
|
- */
|
|
|
|
- if (x25_get_version(card, NULL) || x25_get_version(card, u.str))
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* X25 firmware can run ether in X25 or LAPB HDLC mode.
|
|
|
|
- * Check the user defined option and configure accordingly */
|
|
|
|
- if (conf->u.x25.LAPB_hdlc_only == WANOPT_YES){
|
|
|
|
- if (set_hdlc_level(card) != CMD_OK){
|
|
|
|
- return -EIO;
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: running LAP_B HDLC firmware v%s\n",
|
|
|
|
- card->devname, u.str);
|
|
|
|
- }
|
|
|
|
- card->u.x.LAPB_hdlc = 1;
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: running X.25 firmware v%s\n",
|
|
|
|
- card->devname, u.str);
|
|
|
|
- card->u.x.LAPB_hdlc = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Configure adapter. Here we set resonable defaults, then parse
|
|
|
|
- * device configuration structure and set configuration options.
|
|
|
|
- * Most configuration options are verified and corrected (if
|
|
|
|
- * necessary) since we can't rely on the adapter to do so.
|
|
|
|
- */
|
|
|
|
- memset(&u.cfg, 0, sizeof(u.cfg));
|
|
|
|
- u.cfg.t1 = 3;
|
|
|
|
- u.cfg.n2 = 10;
|
|
|
|
- u.cfg.autoHdlc = 1; /* automatic HDLC connection */
|
|
|
|
- u.cfg.hdlcWindow = 7;
|
|
|
|
- u.cfg.pktWindow = 2;
|
|
|
|
- u.cfg.station = 1; /* DTE */
|
|
|
|
- u.cfg.options = 0x0090; /* disable D-bit pragmatics */
|
|
|
|
- u.cfg.ccittCompat = 1988;
|
|
|
|
- u.cfg.t10t20 = 30;
|
|
|
|
- u.cfg.t11t21 = 30;
|
|
|
|
- u.cfg.t12t22 = 30;
|
|
|
|
- u.cfg.t13t23 = 30;
|
|
|
|
- u.cfg.t16t26 = 30;
|
|
|
|
- u.cfg.t28 = 30;
|
|
|
|
- u.cfg.r10r20 = 5;
|
|
|
|
- u.cfg.r12r22 = 5;
|
|
|
|
- u.cfg.r13r23 = 5;
|
|
|
|
- u.cfg.responseOpt = 1; /* RR's after every packet */
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- u.cfg.hdlcMTU = 1027;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.x25_conf_opt){
|
|
|
|
- u.cfg.options = conf->u.x25.x25_conf_opt;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (conf->clocking != WANOPT_EXTERNAL)
|
|
|
|
- u.cfg.baudRate = bps_to_speed_code(conf->bps);
|
|
|
|
-
|
|
|
|
- if (conf->station != WANOPT_DTE){
|
|
|
|
- u.cfg.station = 0; /* DCE mode */
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (conf->interface != WANOPT_RS232 ){
|
|
|
|
- u.cfg.hdlcOptions |= 0x80; /* V35 mode */
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* adjust MTU */
|
|
|
|
- if (!conf->mtu || (conf->mtu >= 1024))
|
|
|
|
- card->wandev.mtu = 1024;
|
|
|
|
- else if (conf->mtu >= 512)
|
|
|
|
- card->wandev.mtu = 512;
|
|
|
|
- else if (conf->mtu >= 256)
|
|
|
|
- card->wandev.mtu = 256;
|
|
|
|
- else if (conf->mtu >= 128)
|
|
|
|
- card->wandev.mtu = 128;
|
|
|
|
- else
|
|
|
|
- card->wandev.mtu = 64;
|
|
|
|
-
|
|
|
|
- u.cfg.defPktSize = u.cfg.pktMTU = card->wandev.mtu;
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.hi_pvc){
|
|
|
|
- card->u.x.hi_pvc = min_t(unsigned int, conf->u.x25.hi_pvc, MAX_LCN_NUM);
|
|
|
|
- card->u.x.lo_pvc = min_t(unsigned int, conf->u.x25.lo_pvc, card->u.x.hi_pvc);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.hi_svc){
|
|
|
|
- card->u.x.hi_svc = min_t(unsigned int, conf->u.x25.hi_svc, MAX_LCN_NUM);
|
|
|
|
- card->u.x.lo_svc = min_t(unsigned int, conf->u.x25.lo_svc, card->u.x.hi_svc);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Figure out the total number of channels to configure */
|
|
|
|
- card->u.x.num_of_ch = 0;
|
|
|
|
- if (card->u.x.hi_svc != 0){
|
|
|
|
- card->u.x.num_of_ch = (card->u.x.hi_svc - card->u.x.lo_svc) + 1;
|
|
|
|
- }
|
|
|
|
- if (card->u.x.hi_pvc != 0){
|
|
|
|
- card->u.x.num_of_ch += (card->u.x.hi_pvc - card->u.x.lo_pvc) + 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (card->u.x.num_of_ch == 0){
|
|
|
|
- printk(KERN_INFO "%s: ERROR, Minimum number of PVC/SVC channels is 1 !\n"
|
|
|
|
- "%s: Please set the Lowest/Highest PVC/SVC values !\n",
|
|
|
|
- card->devname,card->devname);
|
|
|
|
- return -ECHRNG;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- u.cfg.loPVC = card->u.x.lo_pvc;
|
|
|
|
- u.cfg.hiPVC = card->u.x.hi_pvc;
|
|
|
|
- u.cfg.loTwoWaySVC = card->u.x.lo_svc;
|
|
|
|
- u.cfg.hiTwoWaySVC = card->u.x.hi_svc;
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.hdlc_window)
|
|
|
|
- u.cfg.hdlcWindow = min_t(unsigned int, conf->u.x25.hdlc_window, 7);
|
|
|
|
- if (conf->u.x25.pkt_window)
|
|
|
|
- u.cfg.pktWindow = min_t(unsigned int, conf->u.x25.pkt_window, 7);
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.t1)
|
|
|
|
- u.cfg.t1 = min_t(unsigned int, conf->u.x25.t1, 30);
|
|
|
|
- if (conf->u.x25.t2)
|
|
|
|
- u.cfg.t2 = min_t(unsigned int, conf->u.x25.t2, 29);
|
|
|
|
- if (conf->u.x25.t4)
|
|
|
|
- u.cfg.t4 = min_t(unsigned int, conf->u.x25.t4, 240);
|
|
|
|
- if (conf->u.x25.n2)
|
|
|
|
- u.cfg.n2 = min_t(unsigned int, conf->u.x25.n2, 30);
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.t10_t20)
|
|
|
|
- u.cfg.t10t20 = min_t(unsigned int, conf->u.x25.t10_t20,255);
|
|
|
|
- if (conf->u.x25.t11_t21)
|
|
|
|
- u.cfg.t11t21 = min_t(unsigned int, conf->u.x25.t11_t21,255);
|
|
|
|
- if (conf->u.x25.t12_t22)
|
|
|
|
- u.cfg.t12t22 = min_t(unsigned int, conf->u.x25.t12_t22,255);
|
|
|
|
- if (conf->u.x25.t13_t23)
|
|
|
|
- u.cfg.t13t23 = min_t(unsigned int, conf->u.x25.t13_t23,255);
|
|
|
|
- if (conf->u.x25.t16_t26)
|
|
|
|
- u.cfg.t16t26 = min_t(unsigned int, conf->u.x25.t16_t26, 255);
|
|
|
|
- if (conf->u.x25.t28)
|
|
|
|
- u.cfg.t28 = min_t(unsigned int, conf->u.x25.t28, 255);
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.r10_r20)
|
|
|
|
- u.cfg.r10r20 = min_t(unsigned int, conf->u.x25.r10_r20,250);
|
|
|
|
- if (conf->u.x25.r12_r22)
|
|
|
|
- u.cfg.r12r22 = min_t(unsigned int, conf->u.x25.r12_r22,250);
|
|
|
|
- if (conf->u.x25.r13_r23)
|
|
|
|
- u.cfg.r13r23 = min_t(unsigned int, conf->u.x25.r13_r23,250);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if (conf->u.x25.ccitt_compat)
|
|
|
|
- u.cfg.ccittCompat = conf->u.x25.ccitt_compat;
|
|
|
|
-
|
|
|
|
- /* initialize adapter */
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- if (hdlc_configure(card, &u.cfg) != CMD_OK)
|
|
|
|
- return -EIO;
|
|
|
|
- }else{
|
|
|
|
- if (x25_configure(card, &u.cfg) != CMD_OK)
|
|
|
|
- return -EIO;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((x25_close_hdlc(card) != CMD_OK) || /* close HDLC link */
|
|
|
|
- (x25_set_dtr(card, 0) != CMD_OK)) /* drop DTR */
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- /* Initialize protocol-specific fields of adapter data space */
|
|
|
|
- card->wandev.bps = conf->bps;
|
|
|
|
- card->wandev.interface = conf->interface;
|
|
|
|
- card->wandev.clocking = conf->clocking;
|
|
|
|
- card->wandev.station = conf->station;
|
|
|
|
- card->isr = &wpx_isr;
|
|
|
|
- card->poll = NULL; //&wpx_poll;
|
|
|
|
- card->disable_comm = &disable_comm;
|
|
|
|
- card->exec = &wpx_exec;
|
|
|
|
- card->wandev.update = &update;
|
|
|
|
- card->wandev.new_if = &new_if;
|
|
|
|
- card->wandev.del_if = &del_if;
|
|
|
|
-
|
|
|
|
- /* WARNING: This function cannot exit with an error
|
|
|
|
- * after the change of state */
|
|
|
|
- card->wandev.state = WAN_DISCONNECTED;
|
|
|
|
-
|
|
|
|
- card->wandev.enable_tx_int = 0;
|
|
|
|
- card->irq_dis_if_send_count = 0;
|
|
|
|
- card->irq_dis_poll_count = 0;
|
|
|
|
- card->u.x.tx_dev = NULL;
|
|
|
|
- card->u.x.no_dev = 0;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Configure for S514 PCI Card */
|
|
|
|
- if (card->hw.type == SDLA_S514) {
|
|
|
|
- card->u.x.hdlc_buf_status =
|
|
|
|
- (volatile unsigned char *)
|
|
|
|
- (card->hw.dpmbase + X25_MB_VECTOR+ X25_MISC_HDLC_BITS);
|
|
|
|
- }else{
|
|
|
|
- card->u.x.hdlc_buf_status =
|
|
|
|
- (volatile unsigned char *)(card->hw.dpmbase + X25_MISC_HDLC_BITS);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- card->u.x.poll_device=NULL;
|
|
|
|
- card->wandev.udp_port = conf->udp_port;
|
|
|
|
-
|
|
|
|
- /* Enable or disable call setup logging */
|
|
|
|
- if (conf->u.x25.logging == WANOPT_YES){
|
|
|
|
- printk(KERN_INFO "%s: Enabling Call Logging.\n",
|
|
|
|
- card->devname);
|
|
|
|
- card->u.x.logging = 1;
|
|
|
|
- }else{
|
|
|
|
- card->u.x.logging = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Enable or disable modem status reporting */
|
|
|
|
- if (conf->u.x25.oob_on_modem == WANOPT_YES){
|
|
|
|
- printk(KERN_INFO "%s: Enabling OOB on Modem change.\n",
|
|
|
|
- card->devname);
|
|
|
|
- card->u.x.oob_on_modem = 1;
|
|
|
|
- }else{
|
|
|
|
- card->u.x.oob_on_modem = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- init_global_statistics(card);
|
|
|
|
-
|
|
|
|
- INIT_WORK(&card->u.x.x25_poll_work, (void *)wpx_poll, card);
|
|
|
|
-
|
|
|
|
- init_timer(&card->u.x.x25_timer);
|
|
|
|
- card->u.x.x25_timer.data = (unsigned long)card;
|
|
|
|
- card->u.x.x25_timer.function = x25_timer_routine;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*=========================================================
|
|
|
|
- * WAN Device Driver Entry Points
|
|
|
|
- *========================================================*/
|
|
|
|
-
|
|
|
|
-/*============================================================
|
|
|
|
- * Name: update(), Update device status & statistics.
|
|
|
|
- *
|
|
|
|
- * Purpose: To provide debugging and statitical
|
|
|
|
- * information to the /proc file system.
|
|
|
|
- * /proc/net/wanrouter/wanpipe#
|
|
|
|
- *
|
|
|
|
- * Rationale: The /proc file system is used to collect
|
|
|
|
- * information about the kernel and drivers.
|
|
|
|
- * Using the /proc file system the user
|
|
|
|
- * can see exactly what the sangoma drivers are
|
|
|
|
- * doing. And in what state they are in.
|
|
|
|
- *
|
|
|
|
- * Description: Collect all driver statistical information
|
|
|
|
- * and pass it to the top laywer.
|
|
|
|
- *
|
|
|
|
- * Since we have to execute a debugging command,
|
|
|
|
- * to obtain firmware statitics, we trigger a
|
|
|
|
- * UPDATE function within the timer interrtup.
|
|
|
|
- * We wait until the timer update is complete.
|
|
|
|
- * Once complete return the appropriate return
|
|
|
|
- * code to indicate that the update was successful.
|
|
|
|
- *
|
|
|
|
- * Called by: device_stat() in wanmain.c
|
|
|
|
- *
|
|
|
|
- * Assumptions:
|
|
|
|
- *
|
|
|
|
- * Warnings: This function will degrade the performance
|
|
|
|
- * of the router, since it uses the mailbox.
|
|
|
|
- *
|
|
|
|
- * Return: 0 OK
|
|
|
|
- * <0 Failed (or busy).
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int update(struct wan_device* wandev)
|
|
|
|
-{
|
|
|
|
- volatile sdla_t* card;
|
|
|
|
- TX25Status* status;
|
|
|
|
- unsigned long timeout;
|
|
|
|
-
|
|
|
|
- /* sanity checks */
|
|
|
|
- if ((wandev == NULL) || (wandev->private == NULL))
|
|
|
|
- return -EFAULT;
|
|
|
|
-
|
|
|
|
- if (wandev->state == WAN_UNCONFIGURED)
|
|
|
|
- return -ENODEV;
|
|
|
|
-
|
|
|
|
- if (test_bit(SEND_CRIT, (void*)&wandev->critical))
|
|
|
|
- return -EAGAIN;
|
|
|
|
-
|
|
|
|
- if (!wandev->dev)
|
|
|
|
- return -ENODEV;
|
|
|
|
-
|
|
|
|
- card = wandev->private;
|
|
|
|
- status = card->flags;
|
|
|
|
-
|
|
|
|
- card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UPDATE;
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
- timeout = jiffies;
|
|
|
|
-
|
|
|
|
- for (;;){
|
|
|
|
- if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE)){
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (time_after(jiffies, timeout + 1*HZ)){
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
|
|
|
|
- return -EAGAIN;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===================================================================
|
|
|
|
- * Name: new_if
|
|
|
|
- *
|
|
|
|
- * Purpose: To allocate and initialize resources for a
|
|
|
|
- * new logical channel.
|
|
|
|
- *
|
|
|
|
- * Rationale: A new channel can be added dynamically via
|
|
|
|
- * ioctl call.
|
|
|
|
- *
|
|
|
|
- * Description: Allocate a private channel structure, x25_channel_t.
|
|
|
|
- * Parse the user interface options from wanpipe#.conf
|
|
|
|
- * configuration file.
|
|
|
|
- * Bind the private are into the network device private
|
|
|
|
- * area pointer (dev->priv).
|
|
|
|
- * Prepare the network device structure for registration.
|
|
|
|
- *
|
|
|
|
- * Called by: ROUTER_IFNEW Ioctl call, from wanrouter_ioctl()
|
|
|
|
- * (wanmain.c)
|
|
|
|
- *
|
|
|
|
- * Assumptions: None
|
|
|
|
- *
|
|
|
|
- * Warnings: None
|
|
|
|
- *
|
|
|
|
- * Return: 0 Ok
|
|
|
|
- * <0 Failed (channel will not be created)
|
|
|
|
- */
|
|
|
|
-static int new_if(struct wan_device* wandev, struct net_device* dev,
|
|
|
|
- wanif_conf_t* conf)
|
|
|
|
-{
|
|
|
|
- sdla_t* card = wandev->private;
|
|
|
|
- x25_channel_t* chan;
|
|
|
|
- int err = 0;
|
|
|
|
-
|
|
|
|
- if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)){
|
|
|
|
- printk(KERN_INFO "%s: invalid interface name!\n",
|
|
|
|
- card->devname);
|
|
|
|
- return -EINVAL;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(card->wandev.new_if_cnt++ > 0 && card->u.x.LAPB_hdlc) {
|
|
|
|
- printk(KERN_INFO "%s: Error: Running LAPB HDLC Mode !\n",
|
|
|
|
- card->devname);
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: Maximum number of network interfaces must be one !\n",
|
|
|
|
- card->devname);
|
|
|
|
- return -EEXIST;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* allocate and initialize private data */
|
|
|
|
- chan = kmalloc(sizeof(x25_channel_t), GFP_ATOMIC);
|
|
|
|
- if (chan == NULL){
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memset(chan, 0, sizeof(x25_channel_t));
|
|
|
|
-
|
|
|
|
- /* Bug Fix: Seg Err on PVC startup
|
|
|
|
- * It must be here since bind_lcn_to_dev expects
|
|
|
|
- * it bellow */
|
|
|
|
- dev->priv = chan;
|
|
|
|
-
|
|
|
|
- strcpy(chan->name, conf->name);
|
|
|
|
- chan->card = card;
|
|
|
|
- chan->dev = dev;
|
|
|
|
- chan->common.sk = NULL;
|
|
|
|
- chan->common.func = NULL;
|
|
|
|
- chan->common.rw_bind = 0;
|
|
|
|
- chan->tx_skb = chan->rx_skb = NULL;
|
|
|
|
-
|
|
|
|
- /* verify media address */
|
|
|
|
- if (conf->addr[0] == '@'){ /* SVC */
|
|
|
|
- chan->common.svc = 1;
|
|
|
|
- strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);
|
|
|
|
-
|
|
|
|
- /* Set channel timeouts (default if not specified) */
|
|
|
|
- chan->idle_timeout = (conf->idle_timeout) ?
|
|
|
|
- conf->idle_timeout : 90;
|
|
|
|
- chan->hold_timeout = (conf->hold_timeout) ?
|
|
|
|
- conf->hold_timeout : 10;
|
|
|
|
-
|
|
|
|
- }else if (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 <name> down: will trigger the kernel
|
|
|
|
- * which will call this function.
|
|
|
|
- *
|
|
|
|
- * Assumptions: None
|
|
|
|
- *
|
|
|
|
- * Warnings: None
|
|
|
|
- *
|
|
|
|
- * Return: 0 Ok
|
|
|
|
- * <0 Failure: Interface will not exit properly.
|
|
|
|
- */
|
|
|
|
-static int if_close(struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- unsigned long smp_flags;
|
|
|
|
-
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
-
|
|
|
|
- if ((chan->common.state == WAN_CONNECTED) ||
|
|
|
|
- (chan->common.state == WAN_CONNECTING)){
|
|
|
|
- S508_S514_lock(card, &smp_flags);
|
|
|
|
- chan_disc(dev);
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- wanpipe_close(card);
|
|
|
|
-
|
|
|
|
- S508_S514_lock(card, &smp_flags);
|
|
|
|
- if (chan->bh_head){
|
|
|
|
- int i;
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
-
|
|
|
|
- for (i=0; i<(MAX_BH_BUFF+1); i++){
|
|
|
|
- skb = ((bh_data_t *)&chan->bh_head[i])->skb;
|
|
|
|
- if (skb != NULL){
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- kfree(chan->bh_head);
|
|
|
|
- chan->bh_head=NULL;
|
|
|
|
- }
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
-
|
|
|
|
- /* If this is the last close, disconnect physical link */
|
|
|
|
- if (!card->open_cnt){
|
|
|
|
- S508_S514_lock(card, &smp_flags);
|
|
|
|
- disconnect(card);
|
|
|
|
- x25_set_intr_mode(card, 0);
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Decrement the number of interfaces */
|
|
|
|
- --card->u.x.no_dev;
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*======================================================================
|
|
|
|
- * Build media header.
|
|
|
|
- * o encapsulate packet according to encapsulation type.
|
|
|
|
- *
|
|
|
|
- * The trick here is to put packet type (Ethertype) into 'protocol'
|
|
|
|
- * field of the socket buffer, so that we don't forget it.
|
|
|
|
- * If encapsulation fails, set skb->protocol to 0 and discard
|
|
|
|
- * packet later.
|
|
|
|
- *
|
|
|
|
- * Return: media header length.
|
|
|
|
- *======================================================================*/
|
|
|
|
-
|
|
|
|
-static int if_header(struct sk_buff* skb, struct net_device* dev,
|
|
|
|
- unsigned short type, void* daddr, void* saddr,
|
|
|
|
- unsigned len)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- int hdr_len = dev->hard_header_len;
|
|
|
|
-
|
|
|
|
- skb->protocol = htons(type);
|
|
|
|
- if (!chan->protocol){
|
|
|
|
- hdr_len = wanrouter_encapsulate(skb, dev, type);
|
|
|
|
- if (hdr_len < 0){
|
|
|
|
- hdr_len = 0;
|
|
|
|
- skb->protocol = htons(0);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return hdr_len;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * Re-build media header.
|
|
|
|
- *
|
|
|
|
- * Return: 1 physical address resolved.
|
|
|
|
- * 0 physical address not resolved
|
|
|
|
- *==============================================================*/
|
|
|
|
-
|
|
|
|
-static int if_rebuild_hdr (struct sk_buff* skb)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev = skb->dev;
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: rebuild_header() called for interface %s!\n",
|
|
|
|
- card->devname, dev->name);
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*============================================================================
|
|
|
|
- * Handle transmit timeout event from netif watchdog
|
|
|
|
- */
|
|
|
|
-static void if_tx_timeout(struct net_device *dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t *card = chan->card;
|
|
|
|
-
|
|
|
|
- /* If our device stays busy for at least 5 seconds then we will
|
|
|
|
- * kick start the device by making dev->tbusy = 0. We expect
|
|
|
|
- * that our device never stays busy more than 5 seconds. So this
|
|
|
|
- * is only used as a last resort.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- ++chan->if_send_stat.if_send_tbusy_timeout;
|
|
|
|
- printk (KERN_INFO "%s: Transmit timed out on %s\n",
|
|
|
|
- card->devname, dev->name);
|
|
|
|
- netif_wake_queue (dev);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=========================================================================
|
|
|
|
- * Send a packet on a network interface.
|
|
|
|
- * o set tbusy flag (marks start of the transmission).
|
|
|
|
- * o check link state. If link is not up, then drop the packet.
|
|
|
|
- * o check channel status. If it's down then initiate a call.
|
|
|
|
- * o pass a packet to corresponding WAN device.
|
|
|
|
- * o free socket buffer
|
|
|
|
- *
|
|
|
|
- * Return: 0 complete (socket buffer must be freed)
|
|
|
|
- * non-0 packet may be re-transmitted (tbusy must be set)
|
|
|
|
- *
|
|
|
|
- * Notes:
|
|
|
|
- * 1. This routine is called either by the protocol stack or by the "net
|
|
|
|
- * bottom half" (with interrupts enabled).
|
|
|
|
- * 2. Setting tbusy flag will inhibit further transmit requests from the
|
|
|
|
- * protocol stack and can be used for flow control with protocol layer.
|
|
|
|
- *
|
|
|
|
- *========================================================================*/
|
|
|
|
-
|
|
|
|
-static int if_send(struct sk_buff* skb, struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
- int udp_type;
|
|
|
|
- unsigned long smp_flags=0;
|
|
|
|
-
|
|
|
|
- ++chan->if_send_stat.if_send_entry;
|
|
|
|
-
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
-
|
|
|
|
- /* No need to check frame length, since socket code
|
|
|
|
- * will perform the check for us */
|
|
|
|
-
|
|
|
|
- chan->tick_counter = jiffies;
|
|
|
|
-
|
|
|
|
- /* Critical region starts here */
|
|
|
|
- S508_S514_lock(card, &smp_flags);
|
|
|
|
-
|
|
|
|
- if (test_and_set_bit(SEND_CRIT, (void*)&card->wandev.critical)){
|
|
|
|
- printk(KERN_INFO "Hit critical in if_send()! %lx\n",card->wandev.critical);
|
|
|
|
- goto if_send_crit_exit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- udp_type = udp_pkt_type(skb, card);
|
|
|
|
-
|
|
|
|
- if(udp_type != UDP_INVALID_TYPE) {
|
|
|
|
-
|
|
|
|
- if(store_udp_mgmt_pkt(udp_type, UDP_PKT_FRM_STACK, card, dev, skb,
|
|
|
|
- chan->common.lcn)) {
|
|
|
|
-
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
- if (udp_type == UDP_XPIPE_TYPE){
|
|
|
|
- chan->if_send_stat.if_send_PIPE_request++;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- netif_start_queue(dev);
|
|
|
|
- clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (chan->transmit_length){
|
|
|
|
- //FIXME: This check doesn't make sense any more
|
|
|
|
- if (chan->common.state != WAN_CONNECTED){
|
|
|
|
- chan->transmit_length=0;
|
|
|
|
- atomic_set(&chan->common.driver_busy,0);
|
|
|
|
- }else{
|
|
|
|
- netif_stop_queue(dev);
|
|
|
|
- ++card->u.x.tx_interrupts_pending;
|
|
|
|
- status->imask |= INTR_ON_TX_FRAME;
|
|
|
|
- clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (card->wandev.state != WAN_CONNECTED){
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->if_send_stat.if_send_wan_disconnected;
|
|
|
|
-
|
|
|
|
- }else if ( chan->protocol && (chan->protocol != skb->protocol)){
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: unsupported Ethertype 0x%04X on interface %s!\n",
|
|
|
|
- chan->name, htons(skb->protocol), dev->name);
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "PROTO %Xn", htons(chan->protocol));
|
|
|
|
- ++chan->ifstats.tx_errors;
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->if_send_stat.if_send_protocol_error;
|
|
|
|
-
|
|
|
|
- }else switch (chan->common.state){
|
|
|
|
-
|
|
|
|
- case WAN_DISCONNECTED:
|
|
|
|
- /* Try to establish connection. If succeded, then start
|
|
|
|
- * transmission, else drop a packet.
|
|
|
|
- */
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- break;
|
|
|
|
- }else{
|
|
|
|
- if (chan_connect(dev) != 0){
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- /* fall through */
|
|
|
|
-
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- if( skb->protocol == htons(ETH_P_IPX)) {
|
|
|
|
- if(chan->enable_IPX) {
|
|
|
|
- switch_net_numbers( skb->data,
|
|
|
|
- chan->network_number, 0);
|
|
|
|
- } else {
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++chan->if_send_stat.if_send_protocol_error;
|
|
|
|
- goto if_send_crit_exit;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- /* We never drop here, if cannot send than, copy
|
|
|
|
- * a packet into a transmit buffer
|
|
|
|
- */
|
|
|
|
- chan_send(dev, skb->data, skb->len, 0);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-if_send_crit_exit:
|
|
|
|
-
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
-
|
|
|
|
- netif_start_queue(dev);
|
|
|
|
- clear_bit(SEND_CRIT,(void*)&card->wandev.critical);
|
|
|
|
- S508_S514_unlock(card, &smp_flags);
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*============================================================================
|
|
|
|
- * Setup so that a frame can be transmitted on the occurrence of a transmit
|
|
|
|
- * interrupt.
|
|
|
|
- *===========================================================================*/
|
|
|
|
-
|
|
|
|
-static void setup_for_delayed_transmit(struct net_device* dev, void* buf,
|
|
|
|
- unsigned len)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- ++chan->if_send_stat.if_send_adptr_bfrs_full;
|
|
|
|
-
|
|
|
|
- if(chan->transmit_length) {
|
|
|
|
- printk(KERN_INFO "%s: Error, transmit length set in delayed transmit!\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- if (len > X25_CHAN_MTU+sizeof(x25api_hdr_t)) {
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- printk(KERN_INFO "%s: Length is too big for delayed transmit\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- if (len > X25_MAX_DATA) {
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- printk(KERN_INFO "%s: Length is too big for delayed transmit\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chan->transmit_length = len;
|
|
|
|
- atomic_set(&chan->common.driver_busy,1);
|
|
|
|
- memcpy(chan->transmit_buffer, buf, len);
|
|
|
|
-
|
|
|
|
- ++chan->if_send_stat.if_send_tx_int_enabled;
|
|
|
|
-
|
|
|
|
- /* Enable Transmit Interrupt */
|
|
|
|
- ++card->u.x.tx_interrupts_pending;
|
|
|
|
- status->imask |= INTR_ON_TX_FRAME;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * net_device_stats
|
|
|
|
- *
|
|
|
|
- * Get ethernet-style interface statistics.
|
|
|
|
- * Return a pointer to struct enet_statistics.
|
|
|
|
- *
|
|
|
|
- *==============================================================*/
|
|
|
|
-static struct net_device_stats *if_stats(struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
-
|
|
|
|
- if(chan == NULL)
|
|
|
|
- return NULL;
|
|
|
|
-
|
|
|
|
- return &chan->ifstats;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Interrupt Handlers
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * X.25 Interrupt Service Routine.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void wpx_isr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- card->in_isr = 1;
|
|
|
|
- ++card->statistics.isr_entry;
|
|
|
|
-
|
|
|
|
- if (test_bit(PERI_CRIT,(void*)&card->wandev.critical)){
|
|
|
|
- card->in_isr=0;
|
|
|
|
- status->iflags = 0;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (test_bit(SEND_CRIT, (void*)&card->wandev.critical)){
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: wpx_isr: wandev.critical set to 0x%02lx, int type = 0x%02x\n",
|
|
|
|
- card->devname, card->wandev.critical, status->iflags);
|
|
|
|
- card->in_isr = 0;
|
|
|
|
- status->iflags = 0;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* For all interrupts set the critical flag to CRITICAL_RX_INTR.
|
|
|
|
- * If the if_send routine is called with this flag set it will set
|
|
|
|
- * the enable transmit flag to 1. (for a delayed interrupt)
|
|
|
|
- */
|
|
|
|
- switch (status->iflags){
|
|
|
|
-
|
|
|
|
- case RX_INTR_PENDING: /* receive interrupt */
|
|
|
|
- rx_intr(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case TX_INTR_PENDING: /* transmit interrupt */
|
|
|
|
- tx_intr(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case MODEM_INTR_PENDING: /* modem status interrupt */
|
|
|
|
- status_intr(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25_ASY_TRANS_INTR_PENDING: /* network event interrupt */
|
|
|
|
- event_intr(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case TIMER_INTR_PENDING:
|
|
|
|
- timer_intr(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default: /* unwanted interrupt */
|
|
|
|
- spur_intr(card);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- card->in_isr = 0;
|
|
|
|
- status->iflags = 0; /* clear interrupt condition */
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Receive interrupt handler.
|
|
|
|
- * This routine handles fragmented IP packets using M-bit according to the
|
|
|
|
- * RFC1356.
|
|
|
|
- * o map ligical channel number to network interface.
|
|
|
|
- * o allocate socket buffer or append received packet to the existing one.
|
|
|
|
- * o if M-bit is reset (i.e. it's the last packet in a sequence) then
|
|
|
|
- * decapsulate packet and pass socket buffer to the protocol stack.
|
|
|
|
- *
|
|
|
|
- * Notes:
|
|
|
|
- * 1. When allocating a socket buffer, if M-bit is set then more data is
|
|
|
|
- * coming and we have to allocate buffer for the maximum IP packet size
|
|
|
|
- * expected on this channel.
|
|
|
|
- * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no
|
|
|
|
- * socket buffers available) the whole packet sequence must be discarded.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void rx_intr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* rxmb = card->rxmb;
|
|
|
|
- unsigned lcn = rxmb->cmd.lcn;
|
|
|
|
- struct net_device* dev = find_channel(card,lcn);
|
|
|
|
- x25_channel_t* chan;
|
|
|
|
- struct sk_buff* skb=NULL;
|
|
|
|
-
|
|
|
|
- if (dev == NULL){
|
|
|
|
- /* Invalid channel, discard packet */
|
|
|
|
- printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
|
|
|
|
- card->devname, lcn);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chan = dev->priv;
|
|
|
|
- chan->i_timeout_sofar = jiffies;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Copy the data from the board, into an
|
|
|
|
- * skb buffer
|
|
|
|
- */
|
|
|
|
- if (wanpipe_pull_data_in_skb(card,dev,&skb)){
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- ++card->wandev.stats.rx_dropped;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_no_socket;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- dev->last_rx = jiffies; /* timestamp */
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* ------------ API ----------------*/
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
-
|
|
|
|
- if (bh_enqueue(dev, skb)){
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- ++card->wandev.stats.rx_dropped;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ++chan->ifstats.rx_packets;
|
|
|
|
- chan->ifstats.rx_bytes += skb->len;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- chan->rx_skb = NULL;
|
|
|
|
- if (!test_and_set_bit(0, &chan->tq_working)){
|
|
|
|
- wanpipe_queue_work(&chan->common.wanpipe_work);
|
|
|
|
- }
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* ------------- WANPIPE -------------------*/
|
|
|
|
-
|
|
|
|
- /* set rx_skb to NULL so we won't access it later when kernel already owns it */
|
|
|
|
- chan->rx_skb=NULL;
|
|
|
|
-
|
|
|
|
- /* Decapsulate packet, if necessary */
|
|
|
|
- if (!skb->protocol && !wanrouter_type_trans(skb, dev)){
|
|
|
|
- /* can't decapsulate packet */
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- ++chan->ifstats.rx_errors;
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- ++card->wandev.stats.rx_dropped;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- if( handle_IPXWAN(skb->data, chan->name,
|
|
|
|
- chan->enable_IPX, chan->network_number,
|
|
|
|
- skb->protocol)){
|
|
|
|
-
|
|
|
|
- if( chan->enable_IPX ){
|
|
|
|
- if(chan_send(dev, skb->data, skb->len,0)){
|
|
|
|
- chan->tx_skb = skb;
|
|
|
|
- }else{
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- /* increment IPX packet dropped statistic */
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- skb->mac.raw = skb->data;
|
|
|
|
- chan->ifstats.rx_bytes += skb->len;
|
|
|
|
- ++chan->ifstats.rx_packets;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack;
|
|
|
|
- netif_rx(skb);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-static int wanpipe_pull_data_in_skb(sdla_t *card, struct net_device *dev,
|
|
|
|
- struct sk_buff **skb)
|
|
|
|
-{
|
|
|
|
- void *bufptr;
|
|
|
|
- TX25Mbox* rxmb = card->rxmb;
|
|
|
|
- unsigned len = rxmb->cmd.length; /* packet length */
|
|
|
|
- unsigned qdm = rxmb->cmd.qdm; /* Q,D and M bits */
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- struct sk_buff *new_skb = *skb;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == WANPIPE){
|
|
|
|
- if (chan->drop_sequence){
|
|
|
|
- if (!(qdm & 0x01)){
|
|
|
|
- chan->drop_sequence = 0;
|
|
|
|
- }
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- new_skb = chan->rx_skb;
|
|
|
|
- }else{
|
|
|
|
- /* Add on the API header to the received
|
|
|
|
- * data
|
|
|
|
- */
|
|
|
|
- len += sizeof(x25api_hdr_t);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (new_skb == NULL){
|
|
|
|
- int bufsize;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == WANPIPE){
|
|
|
|
- bufsize = (qdm & 0x01) ? dev->mtu : len;
|
|
|
|
- }else{
|
|
|
|
- bufsize = len;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Allocate new socket buffer */
|
|
|
|
- new_skb = dev_alloc_skb(bufsize + dev->hard_header_len);
|
|
|
|
- if (new_skb == NULL){
|
|
|
|
- printk(KERN_INFO "%s: no socket buffers available!\n",
|
|
|
|
- card->devname);
|
|
|
|
- chan->drop_sequence = 1; /* set flag */
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (skb_tailroom(new_skb) < len){
|
|
|
|
- /* No room for the packet. Call off the whole thing! */
|
|
|
|
- dev_kfree_skb_any(new_skb);
|
|
|
|
- if (chan->common.usedby == WANPIPE){
|
|
|
|
- chan->rx_skb = NULL;
|
|
|
|
- if (qdm & 0x01){
|
|
|
|
- chan->drop_sequence = 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: unexpectedly long packet sequence "
|
|
|
|
- "on interface %s!\n", card->devname, dev->name);
|
|
|
|
- ++chan->ifstats.rx_length_errors;
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- bufptr = skb_put(new_skb,len);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- /* Fill in the x25api header
|
|
|
|
- */
|
|
|
|
- x25api_t * api_data = (x25api_t*)bufptr;
|
|
|
|
- api_data->hdr.qdm = rxmb->cmd.qdm;
|
|
|
|
- api_data->hdr.cause = rxmb->cmd.cause;
|
|
|
|
- api_data->hdr.diagn = rxmb->cmd.diagn;
|
|
|
|
- api_data->hdr.length = rxmb->cmd.length;
|
|
|
|
- memcpy(api_data->data, rxmb->data, rxmb->cmd.length);
|
|
|
|
- }else{
|
|
|
|
- memcpy(bufptr, rxmb->data, len);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- new_skb->dev = dev;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- new_skb->mac.raw = new_skb->data;
|
|
|
|
- new_skb->protocol = htons(X25_PROT);
|
|
|
|
- new_skb->pkt_type = WAN_PACKET_DATA;
|
|
|
|
- }else{
|
|
|
|
- new_skb->protocol = chan->protocol;
|
|
|
|
- chan->rx_skb = new_skb;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* If qdm bit is set, more data is coming
|
|
|
|
- * thus, exit and wait for more data before
|
|
|
|
- * sending the packet up. (Used by router only)
|
|
|
|
- */
|
|
|
|
- if ((qdm & 0x01) && (chan->common.usedby == WANPIPE))
|
|
|
|
- return 1;
|
|
|
|
-
|
|
|
|
- *skb = new_skb;
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * tx_intr
|
|
|
|
- *
|
|
|
|
- * Transmit interrupt handler.
|
|
|
|
- * For each dev, check that there is something to send.
|
|
|
|
- * If data available, transmit.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static void tx_intr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
- unsigned char more_to_tx=0;
|
|
|
|
- x25_channel_t *chan=NULL;
|
|
|
|
- int i=0;
|
|
|
|
-
|
|
|
|
- if (card->u.x.tx_dev == NULL){
|
|
|
|
- card->u.x.tx_dev = card->wandev.dev;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- dev = card->u.x.tx_dev;
|
|
|
|
-
|
|
|
|
- for (;;){
|
|
|
|
-
|
|
|
|
- chan = dev->priv;
|
|
|
|
- if (chan->transmit_length){
|
|
|
|
- /* Device was set to transmit, check if the TX
|
|
|
|
- * buffers are available
|
|
|
|
- */
|
|
|
|
- if (chan->common.state != WAN_CONNECTED){
|
|
|
|
- chan->transmit_length = 0;
|
|
|
|
- atomic_set(&chan->common.driver_busy,0);
|
|
|
|
- chan->tx_offset=0;
|
|
|
|
- if (netif_queue_stopped(dev)){
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- netif_start_queue(dev);
|
|
|
|
- wakeup_sk_bh(dev);
|
|
|
|
- }else{
|
|
|
|
- netif_wake_queue(dev);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((status->cflags[chan->ch_idx] & 0x40 || card->u.x.LAPB_hdlc) &&
|
|
|
|
- (*card->u.x.hdlc_buf_status & 0x40) ){
|
|
|
|
- /* Tx buffer available, we can send */
|
|
|
|
-
|
|
|
|
- if (tx_intr_send(card, dev)){
|
|
|
|
- more_to_tx=1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* If more than one interface present, move the
|
|
|
|
- * device pointer to the next interface, so on the
|
|
|
|
- * next TX interrupt we will try sending from it.
|
|
|
|
- */
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- break;
|
|
|
|
- }else{
|
|
|
|
- /* Tx buffers not available, but device set
|
|
|
|
- * the TX interrupt. Set more_to_tx and try
|
|
|
|
- * to transmit for other devices.
|
|
|
|
- */
|
|
|
|
- more_to_tx=1;
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- /* This device was not set to transmit,
|
|
|
|
- * go to next
|
|
|
|
- */
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (++i == card->u.x.no_dev){
|
|
|
|
- if (!more_to_tx){
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: Nothing to Send in TX INTR\n",
|
|
|
|
- card->devname);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- } //End of FOR
|
|
|
|
-
|
|
|
|
- card->u.x.tx_dev = dev;
|
|
|
|
-
|
|
|
|
- if (!more_to_tx){
|
|
|
|
- /* if any other interfaces have transmit interrupts pending, */
|
|
|
|
- /* do not disable the global transmit interrupt */
|
|
|
|
- if (!(--card->u.x.tx_interrupts_pending)){
|
|
|
|
- status->imask &= ~INTR_ON_TX_FRAME;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * move_dev_to_next
|
|
|
|
- *
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-struct net_device *move_dev_to_next(sdla_t *card, struct net_device *dev)
|
|
|
|
-{
|
|
|
|
- if (card->u.x.no_dev != 1){
|
|
|
|
- if (!*((struct net_device **)dev->priv))
|
|
|
|
- return card->wandev.dev;
|
|
|
|
- else
|
|
|
|
- return *((struct net_device **)dev->priv);
|
|
|
|
- }
|
|
|
|
- return dev;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * tx_intr_send
|
|
|
|
- *
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int tx_intr_send(sdla_t *card, struct net_device *dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
-
|
|
|
|
- if (chan_send (dev,chan->transmit_buffer,chan->transmit_length,1)){
|
|
|
|
-
|
|
|
|
- /* Packet was split up due to its size, do not disable
|
|
|
|
- * tx_intr
|
|
|
|
- */
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chan->transmit_length=0;
|
|
|
|
- atomic_set(&chan->common.driver_busy,0);
|
|
|
|
- chan->tx_offset=0;
|
|
|
|
-
|
|
|
|
- /* If we are in API mode, wakeup the
|
|
|
|
- * sock BH handler, not the NET_BH */
|
|
|
|
- if (netif_queue_stopped(dev)){
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- netif_start_queue(dev);
|
|
|
|
- wakeup_sk_bh(dev);
|
|
|
|
- }else{
|
|
|
|
- netif_wake_queue(dev);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * timer_intr
|
|
|
|
- *
|
|
|
|
- * Timer interrupt handler.
|
|
|
|
- * Check who called the timer interrupt and perform
|
|
|
|
- * action accordingly.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static void timer_intr (sdla_t *card)
|
|
|
|
-{
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC){
|
|
|
|
-
|
|
|
|
- if (timer_intr_cmd_exec(card) == 0){
|
|
|
|
- card->u.x.timer_int_enabled &=
|
|
|
|
- ~TMR_INT_ENABLED_CMD_EXEC;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }else if(card->u.x.timer_int_enabled & TMR_INT_ENABLED_UDP_PKT) {
|
|
|
|
-
|
|
|
|
- if ((*card->u.x.hdlc_buf_status & 0x40) &&
|
|
|
|
- card->u.x.udp_type == UDP_XPIPE_TYPE){
|
|
|
|
-
|
|
|
|
- if(process_udp_mgmt_pkt(card)) {
|
|
|
|
- card->u.x.timer_int_enabled &=
|
|
|
|
- ~TMR_INT_ENABLED_UDP_PKT;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_ACTIVE) {
|
|
|
|
-
|
|
|
|
- struct net_device *dev = card->u.x.poll_device;
|
|
|
|
- x25_channel_t *chan = NULL;
|
|
|
|
-
|
|
|
|
- if (!dev){
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE;
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- chan = dev->priv;
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: Closing down Idle link %s on LCN %d\n",
|
|
|
|
- card->devname,chan->name,chan->common.lcn);
|
|
|
|
- chan->i_timeout_sofar = jiffies;
|
|
|
|
- chan_disc(dev);
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_ACTIVE;
|
|
|
|
- card->u.x.poll_device=NULL;
|
|
|
|
-
|
|
|
|
- }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_ON) {
|
|
|
|
-
|
|
|
|
- wanpipe_set_state(card, WAN_CONNECTED);
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- struct net_device *dev = card->wandev.dev;
|
|
|
|
- set_chan_state(dev,WAN_CONNECTED);
|
|
|
|
- send_delayed_cmd_result(card,dev,card->mbox);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* 0x8F enable all interrupts */
|
|
|
|
- x25_set_intr_mode(card, INTR_ON_RX_FRAME|
|
|
|
|
- INTR_ON_TX_FRAME|
|
|
|
|
- INTR_ON_MODEM_STATUS_CHANGE|
|
|
|
|
- //INTR_ON_COMMAND_COMPLETE|
|
|
|
|
- X25_ASY_TRANS_INTR_PENDING |
|
|
|
|
- INTR_ON_TIMER |
|
|
|
|
- DIRECT_RX_INTR_USAGE
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- status->imask &= ~INTR_ON_TX_FRAME; /* mask Tx interrupts */
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_ON;
|
|
|
|
-
|
|
|
|
- }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_CONNECT_OFF) {
|
|
|
|
-
|
|
|
|
- //printk(KERN_INFO "Poll connect, Turning OFF\n");
|
|
|
|
- disconnect(card);
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_CONNECT_OFF;
|
|
|
|
-
|
|
|
|
- }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_POLL_DISCONNECT) {
|
|
|
|
-
|
|
|
|
- //printk(KERN_INFO "POll disconnect, trying to connect\n");
|
|
|
|
- connect(card);
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_POLL_DISCONNECT;
|
|
|
|
-
|
|
|
|
- }else if (card->u.x.timer_int_enabled & TMR_INT_ENABLED_UPDATE){
|
|
|
|
-
|
|
|
|
- if (*card->u.x.hdlc_buf_status & 0x40){
|
|
|
|
- x25_get_err_stats(card);
|
|
|
|
- x25_get_stats(card);
|
|
|
|
- card->u.x.timer_int_enabled &= ~TMR_INT_ENABLED_UPDATE;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(!card->u.x.timer_int_enabled){
|
|
|
|
- //printk(KERN_INFO "Turning Timer Off \n");
|
|
|
|
- status->imask &= ~INTR_ON_TIMER;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Modem status interrupt handler.
|
|
|
|
- *===================================================================*/
|
|
|
|
-static void status_intr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
-
|
|
|
|
- /* Added to avoid Modem status message flooding */
|
|
|
|
- static TX25ModemStatus last_stat;
|
|
|
|
-
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- TX25ModemStatus *modem_status;
|
|
|
|
- struct net_device *dev;
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_READ_MODEM_STATUS;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err){
|
|
|
|
- x25_error(card, err, X25_READ_MODEM_STATUS, 0);
|
|
|
|
- }else{
|
|
|
|
-
|
|
|
|
- modem_status = (TX25ModemStatus*)mbox->data;
|
|
|
|
-
|
|
|
|
- /* Check if the last status was the same
|
|
|
|
- * if it was, do NOT print message again */
|
|
|
|
-
|
|
|
|
- if (last_stat.status != modem_status->status){
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: Modem Status Change: DCD=%s, CTS=%s\n",
|
|
|
|
- card->devname,DCD(modem_status->status),CTS(modem_status->status));
|
|
|
|
-
|
|
|
|
- last_stat.status = modem_status->status;
|
|
|
|
-
|
|
|
|
- if (card->u.x.oob_on_modem){
|
|
|
|
-
|
|
|
|
- mbox->cmd.pktType = mbox->cmd.command;
|
|
|
|
- mbox->cmd.result = 0x08;
|
|
|
|
-
|
|
|
|
- /* Send a OOB to all connected sockets */
|
|
|
|
- for (dev = card->wandev.dev; dev;
|
|
|
|
- dev = *((struct net_device**)dev->priv)) {
|
|
|
|
- chan=dev->priv;
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- send_oob_msg(card,dev,mbox);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* The modem OOB message will probably kill the
|
|
|
|
- * the link. If we don't clear the flag here,
|
|
|
|
- * a deadlock could occur */
|
|
|
|
- if (atomic_read(&card->u.x.command_busy)){
|
|
|
|
- atomic_set(&card->u.x.command_busy,0);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_STATUS;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err){
|
|
|
|
- x25_error(card, err, X25_HDLC_LINK_STATUS, 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Network event interrupt handler.
|
|
|
|
- *===================================================================*/
|
|
|
|
-static void event_intr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- x25_fetch_events(card);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Spurious interrupt handler.
|
|
|
|
- * o print a warning
|
|
|
|
- * o
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static void spur_intr (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- printk(KERN_INFO "%s: spurious interrupt!\n", card->devname);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Background Polling Routines
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Main polling routine.
|
|
|
|
- * This routine is repeatedly called by the WANPIPE 'thread' to allow for
|
|
|
|
- * time-dependent housekeeping work.
|
|
|
|
- *
|
|
|
|
- * Notes:
|
|
|
|
- * 1. This routine may be called on interrupt context with all interrupts
|
|
|
|
- * enabled. Beware!
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static void wpx_poll (sdla_t *card)
|
|
|
|
-{
|
|
|
|
- if (!card->wandev.dev){
|
|
|
|
- goto wpx_poll_exit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (card->open_cnt != card->u.x.num_of_ch){
|
|
|
|
- goto wpx_poll_exit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (test_bit(PERI_CRIT,&card->wandev.critical)){
|
|
|
|
- goto wpx_poll_exit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (test_bit(SEND_CRIT,&card->wandev.critical)){
|
|
|
|
- goto wpx_poll_exit;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch(card->wandev.state){
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- poll_active(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WAN_CONNECTING:
|
|
|
|
- poll_connecting(card);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WAN_DISCONNECTED:
|
|
|
|
- poll_disconnected(card);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-wpx_poll_exit:
|
|
|
|
- clear_bit(POLL_CRIT,&card->wandev.critical);
|
|
|
|
- return;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void trigger_x25_poll(sdla_t *card)
|
|
|
|
-{
|
|
|
|
- schedule_work(&card->u.x.x25_poll_work);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle physical link establishment phase.
|
|
|
|
- * o if connection timed out, disconnect the link.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static void poll_connecting (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- volatile TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- if (status->gflags & X25_HDLC_ABM){
|
|
|
|
-
|
|
|
|
- timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_ON);
|
|
|
|
-
|
|
|
|
- }else if ((jiffies - card->state_tick) > CONNECT_TIMEOUT){
|
|
|
|
-
|
|
|
|
- timer_intr_exec (card, TMR_INT_ENABLED_POLL_CONNECT_OFF);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle physical link disconnected phase.
|
|
|
|
- * o if hold-down timeout has expired and there are open interfaces,
|
|
|
|
- * connect link.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static void poll_disconnected (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev;
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- if (!card->u.x.LAPB_hdlc && card->open_cnt &&
|
|
|
|
- ((jiffies - card->state_tick) > HOLD_DOWN_TIME)){
|
|
|
|
- timer_intr_exec(card, TMR_INT_ENABLED_POLL_DISCONNECT);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if ((dev=card->wandev.dev) == NULL)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- if ((chan=dev->priv) == NULL)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API &&
|
|
|
|
- atomic_read(&chan->common.command) &&
|
|
|
|
- card->u.x.LAPB_hdlc){
|
|
|
|
-
|
|
|
|
- if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC))
|
|
|
|
- card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
|
|
|
|
-
|
|
|
|
- if (!(status->imask & INTR_ON_TIMER))
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle active link phase.
|
|
|
|
- * o fetch X.25 asynchronous events.
|
|
|
|
- * o kick off transmission on all interfaces.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static void poll_active (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- struct net_device* dev;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- for (dev = card->wandev.dev; dev;
|
|
|
|
- dev = *((struct net_device **)dev->priv)){
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
-
|
|
|
|
- /* If SVC has been idle long enough, close virtual circuit */
|
|
|
|
- if ( chan->common.svc &&
|
|
|
|
- chan->common.state == WAN_CONNECTED &&
|
|
|
|
- chan->common.usedby == WANPIPE ){
|
|
|
|
-
|
|
|
|
- if( (jiffies - chan->i_timeout_sofar) / HZ > chan->idle_timeout ){
|
|
|
|
- /* Close svc */
|
|
|
|
- card->u.x.poll_device=dev;
|
|
|
|
- timer_intr_exec (card, TMR_INT_ENABLED_POLL_ACTIVE);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-#ifdef PRINT_DEBUG
|
|
|
|
- chan->ifstats.tx_compressed = atomic_read(&chan->common.command);
|
|
|
|
- chan->ifstats.tx_errors = chan->common.state;
|
|
|
|
- chan->ifstats.rx_fifo_errors = atomic_read(&card->u.x.command_busy);
|
|
|
|
- ++chan->ifstats.tx_bytes;
|
|
|
|
-
|
|
|
|
- chan->ifstats.rx_fifo_errors=atomic_read(&chan->common.disconnect);
|
|
|
|
- chan->ifstats.multicast=atomic_read(&chan->bh_buff_used);
|
|
|
|
- chan->ifstats.rx_length_errors=*card->u.x.hdlc_buf_status;
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API &&
|
|
|
|
- atomic_read(&chan->common.command) &&
|
|
|
|
- !card->u.x.LAPB_hdlc){
|
|
|
|
-
|
|
|
|
- if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC))
|
|
|
|
- card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
|
|
|
|
-
|
|
|
|
- if (!(status->imask & INTR_ON_TIMER))
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((chan->common.usedby == API) &&
|
|
|
|
- atomic_read(&chan->common.disconnect)){
|
|
|
|
-
|
|
|
|
- if (chan->common.state == WAN_DISCONNECTED){
|
|
|
|
- atomic_set(&chan->common.disconnect,0);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- atomic_set(&chan->common.command,X25_CLEAR_CALL);
|
|
|
|
- if (!(card->u.x.timer_int_enabled & TMR_INT_ENABLED_CMD_EXEC))
|
|
|
|
- card->u.x.timer_int_enabled |= TMR_INT_ENABLED_CMD_EXEC;
|
|
|
|
-
|
|
|
|
- if (!(status->imask & INTR_ON_TIMER))
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void timer_intr_exec(sdla_t *card, unsigned char TYPE)
|
|
|
|
-{
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
- card->u.x.timer_int_enabled |= TYPE;
|
|
|
|
- if (!(status->imask & INTR_ON_TIMER))
|
|
|
|
- status->imask |= INTR_ON_TIMER;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * SDLA Firmware-Specific Functions
|
|
|
|
- *
|
|
|
|
- * Almost all X.25 commands can unexpetedly fail due to so called 'X.25
|
|
|
|
- * asynchronous events' such as restart, interrupt, incoming call request,
|
|
|
|
- * call clear request, etc. They can't be ignored and have to be delt with
|
|
|
|
- * immediately. To tackle with this problem we execute each interface
|
|
|
|
- * command in a loop until good return code is received or maximum number
|
|
|
|
- * of retries is reached. Each interface command returns non-zero return
|
|
|
|
- * code, an asynchronous event/error handler x25_error() is called.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Read X.25 firmware version.
|
|
|
|
- * Put code version as ASCII string in str.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_get_version (sdla_t* card, char* str)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_READ_CODE_VERSION;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- &&
|
|
|
|
- x25_error(card, err, X25_READ_CODE_VERSION, 0));
|
|
|
|
-
|
|
|
|
- if (!err && str)
|
|
|
|
- {
|
|
|
|
- int len = mbox->cmd.length;
|
|
|
|
-
|
|
|
|
- memcpy(str, mbox->data, len);
|
|
|
|
- str[len] = '\0';
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Configure adapter.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_configure (sdla_t* card, TX25Config* conf)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do{
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
|
|
|
|
- mbox->cmd.length = sizeof(TX25Config);
|
|
|
|
- mbox->cmd.command = X25_SET_CONFIGURATION;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Configure adapter for HDLC only.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int hdlc_configure (sdla_t* card, TX25Config* conf)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do{
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- memcpy(mbox->data, (void*)conf, sizeof(TX25Config));
|
|
|
|
- mbox->cmd.length = sizeof(TX25Config);
|
|
|
|
- mbox->cmd.command = X25_HDLC_SET_CONFIG;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_SET_CONFIGURATION, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int set_hdlc_level (sdla_t* card)
|
|
|
|
-{
|
|
|
|
-
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do{
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = SET_PROTOCOL_LEVEL;
|
|
|
|
- mbox->cmd.length = 1;
|
|
|
|
- mbox->data[0] = HDLC_LEVEL; //| DO_HDLC_LEVEL_ERROR_CHECKING;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, SET_PROTOCOL_LEVEL, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Get communications error statistics.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_get_err_stats (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_HDLC_READ_COMM_ERR;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_HDLC_READ_COMM_ERR, 0));
|
|
|
|
-
|
|
|
|
- if (!err)
|
|
|
|
- {
|
|
|
|
- THdlcCommErr* stats = (void*)mbox->data;
|
|
|
|
-
|
|
|
|
- card->wandev.stats.rx_over_errors = stats->rxOverrun;
|
|
|
|
- card->wandev.stats.rx_crc_errors = stats->rxBadCrc;
|
|
|
|
- card->wandev.stats.rx_missed_errors = stats->rxAborted;
|
|
|
|
- card->wandev.stats.tx_aborted_errors = stats->txAborted;
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Get protocol statistics.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_get_stats (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_READ_STATISTICS;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_READ_STATISTICS, 0)) ;
|
|
|
|
-
|
|
|
|
- if (!err)
|
|
|
|
- {
|
|
|
|
- TX25Stats* stats = (void*)mbox->data;
|
|
|
|
-
|
|
|
|
- card->wandev.stats.rx_packets = stats->rxData;
|
|
|
|
- card->wandev.stats.tx_packets = stats->txData;
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Close HDLC link.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_close_hdlc (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_CLOSE;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_CLOSE, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Open HDLC link.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_open_hdlc (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_OPEN;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_OPEN, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*=====================================================================
|
|
|
|
- * Setup HDLC link.
|
|
|
|
- *====================================================================*/
|
|
|
|
-static int x25_setup_hdlc (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_SETUP;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_SETUP, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Set (raise/drop) DTR.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_set_dtr (sdla_t* card, int dtr)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->data[0] = 0;
|
|
|
|
- mbox->data[2] = 0;
|
|
|
|
- mbox->data[1] = dtr ? 0x02 : 0x01;
|
|
|
|
- mbox->cmd.length = 3;
|
|
|
|
- mbox->cmd.command = X25_SET_GLOBAL_VARS;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_SET_GLOBAL_VARS, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Set interrupt mode.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_set_intr_mode (sdla_t* card, int mode)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->data[0] = mode;
|
|
|
|
- if (card->hw.fwid == SFID_X25_508){
|
|
|
|
- mbox->data[1] = card->hw.irq;
|
|
|
|
- mbox->data[2] = 2;
|
|
|
|
- mbox->cmd.length = 3;
|
|
|
|
- }else {
|
|
|
|
- mbox->cmd.length = 1;
|
|
|
|
- }
|
|
|
|
- mbox->cmd.command = X25_SET_INTERRUPT_MODE;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_SET_INTERRUPT_MODE, 0));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Read X.25 channel configuration.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_get_chan_conf (sdla_t* card, x25_channel_t* chan)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int lcn = chan->common.lcn;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do{
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.lcn = lcn;
|
|
|
|
- mbox->cmd.command = X25_READ_CHANNEL_CONFIG;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_READ_CHANNEL_CONFIG, lcn));
|
|
|
|
-
|
|
|
|
- if (!err)
|
|
|
|
- {
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- /* calculate an offset into the array of status bytes */
|
|
|
|
- if (card->u.x.hi_svc <= X25_MAX_CHAN){
|
|
|
|
-
|
|
|
|
- chan->ch_idx = lcn - 1;
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- int offset;
|
|
|
|
-
|
|
|
|
- /* FIX: Apr 14 2000 : Nenad Corbic
|
|
|
|
- * The data field was being compared to 0x1F using
|
|
|
|
- * '&&' instead of '&'.
|
|
|
|
- * This caused X25API to fail for LCNs greater than 255.
|
|
|
|
- */
|
|
|
|
- switch (mbox->data[0] & 0x1F)
|
|
|
|
- {
|
|
|
|
- case 0x01:
|
|
|
|
- offset = status->pvc_map; break;
|
|
|
|
- case 0x03:
|
|
|
|
- offset = status->icc_map; break;
|
|
|
|
- case 0x07:
|
|
|
|
- offset = status->twc_map; break;
|
|
|
|
- case 0x0B:
|
|
|
|
- offset = status->ogc_map; break;
|
|
|
|
- default:
|
|
|
|
- offset = 0;
|
|
|
|
- }
|
|
|
|
- chan->ch_idx = lcn - 1 - offset;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* get actual transmit packet size on this channel */
|
|
|
|
- switch(mbox->data[1] & 0x38)
|
|
|
|
- {
|
|
|
|
- case 0x00:
|
|
|
|
- chan->tx_pkt_size = 16;
|
|
|
|
- break;
|
|
|
|
- case 0x08:
|
|
|
|
- chan->tx_pkt_size = 32;
|
|
|
|
- break;
|
|
|
|
- case 0x10:
|
|
|
|
- chan->tx_pkt_size = 64;
|
|
|
|
- break;
|
|
|
|
- case 0x18:
|
|
|
|
- chan->tx_pkt_size = 128;
|
|
|
|
- break;
|
|
|
|
- case 0x20:
|
|
|
|
- chan->tx_pkt_size = 256;
|
|
|
|
- break;
|
|
|
|
- case 0x28:
|
|
|
|
- chan->tx_pkt_size = 512;
|
|
|
|
- break;
|
|
|
|
- case 0x30:
|
|
|
|
- chan->tx_pkt_size = 1024;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (card->u.x.logging)
|
|
|
|
- printk(KERN_INFO "%s: X.25 packet size on LCN %d is %d.\n",
|
|
|
|
- card->devname, lcn, chan->tx_pkt_size);
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Place X.25 call.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_place_call (sdla_t* card, x25_channel_t* chan)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
- char str[64];
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if (chan->protocol == htons(ETH_P_IP)){
|
|
|
|
- sprintf(str, "-d%s -uCC", chan->addr);
|
|
|
|
-
|
|
|
|
- }else if (chan->protocol == htons(ETH_P_IPX)){
|
|
|
|
- sprintf(str, "-d%s -u800000008137", chan->addr);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- strcpy(mbox->data, str);
|
|
|
|
- mbox->cmd.length = strlen(str);
|
|
|
|
- mbox->cmd.command = X25_PLACE_CALL;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_PLACE_CALL, 0));
|
|
|
|
-
|
|
|
|
- if (!err){
|
|
|
|
- bind_lcn_to_dev (card, chan->dev, mbox->cmd.lcn);
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Accept X.25 call.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_accept_call (sdla_t* card, int lcn, int qdm)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.lcn = lcn;
|
|
|
|
- mbox->cmd.qdm = qdm;
|
|
|
|
- mbox->cmd.command = X25_ACCEPT_CALL;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_ACCEPT_CALL, lcn));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Clear X.25 call.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_clear_call (sdla_t* card, int lcn, int cause, int diagn)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.lcn = lcn;
|
|
|
|
- mbox->cmd.cause = cause;
|
|
|
|
- mbox->cmd.diagn = diagn;
|
|
|
|
- mbox->cmd.command = X25_CLEAR_CALL;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_CLEAR_CALL, lcn));
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Send X.25 data packet.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_send (sdla_t* card, int lcn, int qdm, int len, void* buf)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = MAX_CMD_RETRY;
|
|
|
|
- int err;
|
|
|
|
- unsigned char cmd;
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc)
|
|
|
|
- cmd = X25_HDLC_WRITE;
|
|
|
|
- else
|
|
|
|
- cmd = X25_WRITE;
|
|
|
|
-
|
|
|
|
- do
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- memcpy(mbox->data, buf, len);
|
|
|
|
- mbox->cmd.length = len;
|
|
|
|
- mbox->cmd.lcn = lcn;
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- mbox->cmd.pf = qdm;
|
|
|
|
- }else{
|
|
|
|
- mbox->cmd.qdm = qdm;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- mbox->cmd.command = cmd;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && retry-- && x25_error(card, err, cmd , lcn));
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* If buffers are busy the return code for LAPB HDLC is
|
|
|
|
- * 1. The above functions are looking for return code
|
|
|
|
- * of X25RES_NOT_READY if busy. */
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc && err == 1){
|
|
|
|
- err = X25RES_NOT_READY;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Fetch X.25 asynchronous events.
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_fetch_events (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int err = 0;
|
|
|
|
-
|
|
|
|
- if (status->gflags & 0x20)
|
|
|
|
- {
|
|
|
|
- memset(&mbox->cmd, 0, sizeof(TX25Cmd));
|
|
|
|
- mbox->cmd.command = X25_IS_DATA_AVAILABLE;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err) x25_error(card, err, X25_IS_DATA_AVAILABLE, 0);
|
|
|
|
- }
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * X.25 asynchronous event/error handler.
|
|
|
|
- * This routine is called each time interface command returns
|
|
|
|
- * non-zero return code to handle X.25 asynchronous events and
|
|
|
|
- * common errors. Return non-zero to repeat command or zero to
|
|
|
|
- * cancel it.
|
|
|
|
- *
|
|
|
|
- * Notes:
|
|
|
|
- * 1. This function may be called recursively, as handling some of the
|
|
|
|
- * asynchronous events (e.g. call request) requires execution of the
|
|
|
|
- * interface command(s) that, in turn, may also return asynchronous
|
|
|
|
- * events. To avoid re-entrancy problems we copy mailbox to dynamically
|
|
|
|
- * allocated memory before processing events.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int x25_error (sdla_t* card, int err, int cmd, int lcn)
|
|
|
|
-{
|
|
|
|
- int retry = 1;
|
|
|
|
- unsigned dlen = ((TX25Mbox*)card->mbox)->cmd.length;
|
|
|
|
- TX25Mbox* mb;
|
|
|
|
-
|
|
|
|
- mb = kmalloc(sizeof(TX25Mbox) + dlen, GFP_ATOMIC);
|
|
|
|
- if (mb == NULL)
|
|
|
|
- {
|
|
|
|
- printk(KERN_ERR "%s: x25_error() out of memory!\n",
|
|
|
|
- card->devname);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- memcpy(mb, card->mbox, sizeof(TX25Mbox) + dlen);
|
|
|
|
- switch (err){
|
|
|
|
-
|
|
|
|
- case X25RES_ASYNC_PACKET: /* X.25 asynchronous packet was received */
|
|
|
|
-
|
|
|
|
- mb->data[dlen] = '\0';
|
|
|
|
-
|
|
|
|
- switch (mb->cmd.pktType & 0x7F){
|
|
|
|
-
|
|
|
|
- case ASE_CALL_RQST: /* incoming call */
|
|
|
|
- retry = incoming_call(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ASE_CALL_ACCEPTED: /* connected */
|
|
|
|
- retry = call_accepted(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ASE_CLEAR_RQST: /* call clear request */
|
|
|
|
- retry = call_cleared(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ASE_RESET_RQST: /* reset request */
|
|
|
|
- printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
|
|
|
|
- "Cause:0x%02X Diagn:0x%02X\n",
|
|
|
|
- card->devname, mb->cmd.lcn, mb->cmd.cause,
|
|
|
|
- mb->cmd.diagn);
|
|
|
|
- api_oob_event (card,mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ASE_RESTART_RQST: /* restart request */
|
|
|
|
- retry = restart_event(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case ASE_CLEAR_CONFRM:
|
|
|
|
- if (clear_confirm_event (card,mb))
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- /* I use the goto statement here so if
|
|
|
|
- * somebody inserts code between the
|
|
|
|
- * case and default, we will not have
|
|
|
|
- * ghost problems */
|
|
|
|
-
|
|
|
|
- goto dflt_1;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
-dflt_1:
|
|
|
|
- printk(KERN_INFO "%s: X.25 event 0x%02X on LCN %d! "
|
|
|
|
- "Cause:0x%02X Diagn:0x%02X\n",
|
|
|
|
- card->devname, mb->cmd.pktType,
|
|
|
|
- mb->cmd.lcn, mb->cmd.cause, mb->cmd.diagn);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25RES_PROTO_VIOLATION: /* X.25 protocol violation indication */
|
|
|
|
-
|
|
|
|
- /* Bug Fix: Mar 14 2000
|
|
|
|
- * The Protocol violation error conditions were
|
|
|
|
- * not handled previously */
|
|
|
|
-
|
|
|
|
- switch (mb->cmd.pktType & 0x7F){
|
|
|
|
-
|
|
|
|
- case PVE_CLEAR_RQST: /* Clear request */
|
|
|
|
- retry = call_cleared(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case PVE_RESET_RQST: /* Reset request */
|
|
|
|
- printk(KERN_INFO "%s: X.25 reset request on LCN %d! "
|
|
|
|
- "Cause:0x%02X Diagn:0x%02X\n",
|
|
|
|
- card->devname, mb->cmd.lcn, mb->cmd.cause,
|
|
|
|
- mb->cmd.diagn);
|
|
|
|
- api_oob_event (card,mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case PVE_RESTART_RQST: /* Restart request */
|
|
|
|
- retry = restart_event(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default :
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: X.25 protocol violation on LCN %d! "
|
|
|
|
- "Packet:0x%02X Cause:0x%02X Diagn:0x%02X\n",
|
|
|
|
- card->devname, mb->cmd.lcn,
|
|
|
|
- mb->cmd.pktType & 0x7F, mb->cmd.cause, mb->cmd.diagn);
|
|
|
|
- api_oob_event(card,mb);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x42: /* X.25 timeout */
|
|
|
|
- retry = timeout_event(card, cmd, lcn, mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x43: /* X.25 retry limit exceeded */
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: exceeded X.25 retry limit on LCN %d! "
|
|
|
|
- "Packet:0x%02X Diagn:0x%02X\n", card->devname,
|
|
|
|
- mb->cmd.lcn, mb->cmd.pktType, mb->cmd.diagn)
|
|
|
|
- ;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x08: /* modem failure */
|
|
|
|
-#ifndef MODEM_NOT_LOG
|
|
|
|
- printk(KERN_INFO "%s: modem failure!\n", card->devname);
|
|
|
|
-#endif /* MODEM_NOT_LOG */
|
|
|
|
- api_oob_event(card,mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x09: /* N2 retry limit */
|
|
|
|
- printk(KERN_INFO "%s: exceeded HDLC retry limit!\n",
|
|
|
|
- card->devname);
|
|
|
|
- api_oob_event(card,mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x06: /* unnumbered frame was received while in ABM */
|
|
|
|
- printk(KERN_INFO "%s: received Unnumbered frame 0x%02X!\n",
|
|
|
|
- card->devname, mb->data[0]);
|
|
|
|
- api_oob_event(card,mb);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case CMD_TIMEOUT:
|
|
|
|
- printk(KERN_ERR "%s: command 0x%02X timed out!\n",
|
|
|
|
- card->devname, cmd)
|
|
|
|
- ;
|
|
|
|
- retry = 0; /* abort command */
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25RES_NOT_READY:
|
|
|
|
- retry = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x01:
|
|
|
|
- if (card->u.x.LAPB_hdlc)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- if (mb->cmd.command == 0x16)
|
|
|
|
- break;
|
|
|
|
- /* I use the goto statement here so if
|
|
|
|
- * somebody inserts code between the
|
|
|
|
- * case and default, we will not have
|
|
|
|
- * ghost problems */
|
|
|
|
- goto dflt_2;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
-dflt_2:
|
|
|
|
- printk(KERN_INFO "%s: command 0x%02X returned 0x%02X! Lcn %i\n",
|
|
|
|
- card->devname, cmd, err, mb->cmd.lcn)
|
|
|
|
- ;
|
|
|
|
- retry = 0; /* abort command */
|
|
|
|
- }
|
|
|
|
- kfree(mb);
|
|
|
|
- return retry;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * X.25 Asynchronous Event Handlers
|
|
|
|
- * These functions are called by the x25_error() and should return 0, if
|
|
|
|
- * the command resulting in the asynchronous event must be aborted.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- *Handle X.25 incoming call request.
|
|
|
|
- * RFC 1356 establishes the following rules:
|
|
|
|
- * 1. The first octet in the Call User Data (CUD) field of the call
|
|
|
|
- * request packet contains NLPID identifying protocol encapsulation
|
|
|
|
- * 2. Calls MUST NOT be accepted unless router supports requested
|
|
|
|
- * protocol encapsulation.
|
|
|
|
- * 3. A diagnostic code 249 defined by ISO/IEC 8208 may be used
|
|
|
|
- * when clearing a call because protocol encapsulation is not
|
|
|
|
- * supported.
|
|
|
|
- * 4. If an incoming call is received while a call request is
|
|
|
|
- * pending (i.e. call collision has occurred), the incoming call
|
|
|
|
- * shall be rejected and call request shall be retried.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int incoming_call (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- struct wan_device* wandev = &card->wandev;
|
|
|
|
- int new_lcn = mb->cmd.lcn;
|
|
|
|
- struct net_device* dev = get_dev_by_lcn(wandev, new_lcn);
|
|
|
|
- x25_channel_t* chan = NULL;
|
|
|
|
- int accept = 0; /* set to '1' if o.k. to accept call */
|
|
|
|
- unsigned int user_data;
|
|
|
|
- x25_call_info_t* info;
|
|
|
|
-
|
|
|
|
- /* Make sure there is no call collision */
|
|
|
|
- if (dev != NULL)
|
|
|
|
- {
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: X.25 incoming call collision on LCN %d!\n",
|
|
|
|
- card->devname, new_lcn);
|
|
|
|
-
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Make sure D bit is not set in call request */
|
|
|
|
-//FIXME: THIS IS NOT TURE !!!! TAKE IT OUT
|
|
|
|
-// if (mb->cmd.qdm & 0x02)
|
|
|
|
-// {
|
|
|
|
-// printk(KERN_INFO
|
|
|
|
-// "%s: X.25 incoming call on LCN %d with D-bit set!\n",
|
|
|
|
-// card->devname, new_lcn);
|
|
|
|
-//
|
|
|
|
-// x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
-// return 1;
|
|
|
|
-// }
|
|
|
|
-
|
|
|
|
- /* Parse call request data */
|
|
|
|
- info = kmalloc(sizeof(x25_call_info_t), GFP_ATOMIC);
|
|
|
|
- if (info == NULL)
|
|
|
|
- {
|
|
|
|
- printk(KERN_ERR
|
|
|
|
- "%s: not enough memory to parse X.25 incoming call "
|
|
|
|
- "on LCN %d!\n", card->devname, new_lcn);
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- parse_call_info(mb->data, info);
|
|
|
|
-
|
|
|
|
- if (card->u.x.logging)
|
|
|
|
- printk(KERN_INFO "\n%s: X.25 incoming call on LCN %d!\n",
|
|
|
|
- card->devname, new_lcn);
|
|
|
|
-
|
|
|
|
- /* Conver the first two ASCII characters into an
|
|
|
|
- * interger. Used to check the incoming protocol
|
|
|
|
- */
|
|
|
|
- user_data = hex_to_uint(info->user,2);
|
|
|
|
-
|
|
|
|
- /* Find available channel */
|
|
|
|
- for (dev = wandev->dev; dev; dev = *((struct net_device **)dev->priv)) {
|
|
|
|
- chan = dev->priv;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- if (!chan->common.svc || (chan->common.state != WAN_DISCONNECTED))
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- if (user_data == NLPID_IP && chan->protocol != htons(ETH_P_IP)){
|
|
|
|
- printk(KERN_INFO "IP packet but configured for IPX : %x, %x\n",
|
|
|
|
- htons(chan->protocol), info->user[0]);
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (user_data == NLPID_SNAP && chan->protocol != htons(ETH_P_IPX)){
|
|
|
|
- printk(KERN_INFO "IPX packet but configured for IP: %x\n",
|
|
|
|
- htons(chan->protocol));
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- if (strcmp(info->src, chan->addr) == 0)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- /* If just an '@' is specified, accept all incoming calls */
|
|
|
|
- if (strcmp(chan->addr, "") == 0)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (dev == NULL){
|
|
|
|
-
|
|
|
|
- /* If the call is not for any WANPIPE interfaces
|
|
|
|
- * check to see if there is an API listening queue
|
|
|
|
- * waiting for data. If there is send the packet
|
|
|
|
- * up the stack.
|
|
|
|
- */
|
|
|
|
- if (card->sk != NULL && card->func != NULL){
|
|
|
|
- if (api_incoming_call(card,mb,new_lcn)){
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- }
|
|
|
|
- accept = 0;
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: no channels available!\n",
|
|
|
|
- card->devname);
|
|
|
|
-
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }else if (info->nuser == 0){
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: no user data in incoming call on LCN %d!\n",
|
|
|
|
- card->devname, new_lcn)
|
|
|
|
- ;
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
-
|
|
|
|
- }else switch (info->user[0]){
|
|
|
|
-
|
|
|
|
- case 0: /* multiplexed */
|
|
|
|
- chan->protocol = htons(0);
|
|
|
|
- accept = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case NLPID_IP: /* IP datagrams */
|
|
|
|
- accept = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case NLPID_SNAP: /* IPX datagrams */
|
|
|
|
- accept = 1;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: unsupported NLPID 0x%02X in incoming call "
|
|
|
|
- "on LCN %d!\n", card->devname, info->user[0], new_lcn);
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 249);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (accept && (x25_accept_call(card, new_lcn, 0) == CMD_OK)){
|
|
|
|
-
|
|
|
|
- bind_lcn_to_dev (card, chan->dev, new_lcn);
|
|
|
|
-
|
|
|
|
- if (x25_get_chan_conf(card, chan) == CMD_OK)
|
|
|
|
- set_chan_state(dev, WAN_CONNECTED);
|
|
|
|
- else
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- }
|
|
|
|
- kfree(info);
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle accepted call.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int call_accepted (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- unsigned new_lcn = mb->cmd.lcn;
|
|
|
|
- struct net_device* dev = find_channel(card, new_lcn);
|
|
|
|
- x25_channel_t* chan;
|
|
|
|
-
|
|
|
|
- if (dev == NULL){
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: clearing orphaned connection on LCN %d!\n",
|
|
|
|
- card->devname, new_lcn);
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (card->u.x.logging)
|
|
|
|
- printk(KERN_INFO "%s: X.25 call accepted on Dev %s and LCN %d!\n",
|
|
|
|
- card->devname, dev->name, new_lcn);
|
|
|
|
-
|
|
|
|
- /* Get channel configuration and notify router */
|
|
|
|
- chan = dev->priv;
|
|
|
|
- if (x25_get_chan_conf(card, chan) != CMD_OK)
|
|
|
|
- {
|
|
|
|
- x25_clear_call(card, new_lcn, 0, 0);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_CONNECTED);
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- send_delayed_cmd_result(card,dev,mb);
|
|
|
|
- bind_lcn_to_dev (card, dev, new_lcn);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle cleared call.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int call_cleared (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- unsigned new_lcn = mb->cmd.lcn;
|
|
|
|
- struct net_device* dev = find_channel(card, new_lcn);
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- unsigned char old_state;
|
|
|
|
-
|
|
|
|
- if (card->u.x.logging){
|
|
|
|
- printk(KERN_INFO "%s: X.25 clear request on LCN %d! Cause:0x%02X "
|
|
|
|
- "Diagn:0x%02X\n",
|
|
|
|
- card->devname, new_lcn, mb->cmd.cause, mb->cmd.diagn);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (dev == NULL){
|
|
|
|
- printk(KERN_INFO "%s: X.25 clear request : No device for clear\n",
|
|
|
|
- card->devname);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chan=dev->priv;
|
|
|
|
-
|
|
|
|
- old_state = chan->common.state;
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
-
|
|
|
|
- switch (old_state){
|
|
|
|
-
|
|
|
|
- case WAN_CONNECTING:
|
|
|
|
- send_delayed_cmd_result(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- send_oob_msg(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return ((cmd == X25_WRITE) && (lcn == new_lcn)) ? 0 : 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle X.25 restart event.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int restart_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- struct wan_device* wandev = &card->wandev;
|
|
|
|
- struct net_device* dev;
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- unsigned char old_state;
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: X.25 restart request! Cause:0x%02X Diagn:0x%02X\n",
|
|
|
|
- card->devname, mb->cmd.cause, mb->cmd.diagn);
|
|
|
|
-
|
|
|
|
- /* down all logical channels */
|
|
|
|
- for (dev = wandev->dev; dev; dev = *((struct net_device **)dev->priv)) {
|
|
|
|
- chan=dev->priv;
|
|
|
|
- old_state = chan->common.state;
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- switch (old_state){
|
|
|
|
-
|
|
|
|
- case WAN_CONNECTING:
|
|
|
|
- send_delayed_cmd_result(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- send_oob_msg(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return (cmd == X25_WRITE) ? 0 : 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Handle timeout event.
|
|
|
|
- *====================================================================*/
|
|
|
|
-
|
|
|
|
-static int timeout_event (sdla_t* card, int cmd, int lcn, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- unsigned new_lcn = mb->cmd.lcn;
|
|
|
|
-
|
|
|
|
- if (mb->cmd.pktType == 0x05) /* call request time out */
|
|
|
|
- {
|
|
|
|
- struct net_device* dev = find_channel(card,new_lcn);
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: X.25 call timed timeout on LCN %d!\n",
|
|
|
|
- card->devname, new_lcn);
|
|
|
|
-
|
|
|
|
- if (dev){
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- send_delayed_cmd_result(card,dev,card->mbox);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: X.25 packet 0x%02X timeout on LCN %d!\n",
|
|
|
|
- card->devname, mb->cmd.pktType, new_lcn);
|
|
|
|
- }
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Miscellaneous
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-/*====================================================================
|
|
|
|
- * Establish physical connection.
|
|
|
|
- * o open HDLC and raise DTR
|
|
|
|
- *
|
|
|
|
- * Return: 0 connection established
|
|
|
|
- * 1 connection is in progress
|
|
|
|
- * <0 error
|
|
|
|
- *===================================================================*/
|
|
|
|
-
|
|
|
|
-static int connect (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- if (x25_open_hdlc(card) || x25_setup_hdlc(card))
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- wanpipe_set_state(card, WAN_CONNECTING);
|
|
|
|
-
|
|
|
|
- x25_set_intr_mode(card, INTR_ON_TIMER);
|
|
|
|
- status->imask &= ~INTR_ON_TIMER;
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Tear down physical connection.
|
|
|
|
- * o close HDLC link
|
|
|
|
- * o drop DTR
|
|
|
|
- *
|
|
|
|
- * Return: 0
|
|
|
|
- * <0 error
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int disconnect (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- wanpipe_set_state(card, WAN_DISCONNECTED);
|
|
|
|
- x25_set_intr_mode(card, INTR_ON_TIMER); /* disable all interrupt except timer */
|
|
|
|
- x25_close_hdlc(card); /* close HDLC link */
|
|
|
|
- x25_set_dtr(card, 0); /* drop DTR */
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Find network device by its channel number.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static struct net_device* get_dev_by_lcn(struct wan_device* wandev,
|
|
|
|
- unsigned lcn)
|
|
|
|
-{
|
|
|
|
- struct net_device* dev;
|
|
|
|
-
|
|
|
|
- for (dev = wandev->dev; dev; dev = *((struct net_device **)dev->priv))
|
|
|
|
- if (((x25_channel_t*)dev->priv)->common.lcn == lcn)
|
|
|
|
- break;
|
|
|
|
- return dev;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Initiate connection on the logical channel.
|
|
|
|
- * o for PVC we just get channel configuration
|
|
|
|
- * o for SVCs place an X.25 call
|
|
|
|
- *
|
|
|
|
- * Return: 0 connected
|
|
|
|
- * >0 connection in progress
|
|
|
|
- * <0 failure
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int chan_connect(struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
-
|
|
|
|
- if (chan->common.svc && chan->common.usedby == WANPIPE){
|
|
|
|
- if (!chan->addr[0]){
|
|
|
|
- printk(KERN_INFO "%s: No Destination Address\n",
|
|
|
|
- card->devname);
|
|
|
|
- return -EINVAL; /* no destination address */
|
|
|
|
- }
|
|
|
|
- printk(KERN_INFO "%s: placing X.25 call to %s ...\n",
|
|
|
|
- card->devname, chan->addr);
|
|
|
|
-
|
|
|
|
- if (x25_place_call(card, chan) != CMD_OK)
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_CONNECTING);
|
|
|
|
- return 1;
|
|
|
|
- }else{
|
|
|
|
- if (x25_get_chan_conf(card, chan) != CMD_OK)
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_CONNECTED);
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Disconnect logical channel.
|
|
|
|
- * o if SVC then clear X.25 call
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int chan_disc(struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
-
|
|
|
|
- if (chan->common.svc){
|
|
|
|
- x25_clear_call(chan->card, chan->common.lcn, 0, 0);
|
|
|
|
-
|
|
|
|
- /* For API we disconnect on clear
|
|
|
|
- * confirmation.
|
|
|
|
- */
|
|
|
|
- if (chan->common.usedby == API)
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Set logical channel state.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void set_chan_state(struct net_device* dev, int state)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- save_flags(flags);
|
|
|
|
- cli();
|
|
|
|
- if (chan->common.state != state)
|
|
|
|
- {
|
|
|
|
- switch (state)
|
|
|
|
- {
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- if (card->u.x.logging){
|
|
|
|
- printk (KERN_INFO
|
|
|
|
- "%s: interface %s connected, lcn %i !\n",
|
|
|
|
- card->devname, dev->name,chan->common.lcn);
|
|
|
|
- }
|
|
|
|
- *(unsigned short*)dev->dev_addr = htons(chan->common.lcn);
|
|
|
|
- chan->i_timeout_sofar = jiffies;
|
|
|
|
-
|
|
|
|
- /* LAPB is PVC Based */
|
|
|
|
- if (card->u.x.LAPB_hdlc)
|
|
|
|
- chan->common.svc=0;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WAN_CONNECTING:
|
|
|
|
- if (card->u.x.logging){
|
|
|
|
- printk (KERN_INFO
|
|
|
|
- "%s: interface %s connecting, lcn %i ...\n",
|
|
|
|
- card->devname, dev->name, chan->common.lcn);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WAN_DISCONNECTED:
|
|
|
|
- if (card->u.x.logging){
|
|
|
|
- printk (KERN_INFO
|
|
|
|
- "%s: interface %s disconnected, lcn %i !\n",
|
|
|
|
- card->devname, dev->name,chan->common.lcn);
|
|
|
|
- }
|
|
|
|
- atomic_set(&chan->common.disconnect,0);
|
|
|
|
-
|
|
|
|
- if (chan->common.svc) {
|
|
|
|
- *(unsigned short*)dev->dev_addr = 0;
|
|
|
|
- card->u.x.svc_to_dev_map[(chan->common.lcn%X25_MAX_CHAN)]=NULL;
|
|
|
|
- chan->common.lcn = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (chan->transmit_length){
|
|
|
|
- chan->transmit_length=0;
|
|
|
|
- atomic_set(&chan->common.driver_busy,0);
|
|
|
|
- chan->tx_offset=0;
|
|
|
|
- if (netif_queue_stopped(dev)){
|
|
|
|
- netif_wake_queue(dev);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case WAN_DISCONNECTING:
|
|
|
|
- if (card->u.x.logging){
|
|
|
|
- printk (KERN_INFO
|
|
|
|
- "\n%s: interface %s disconnecting, lcn %i ...\n",
|
|
|
|
- card->devname, dev->name,chan->common.lcn);
|
|
|
|
- }
|
|
|
|
- atomic_set(&chan->common.disconnect,0);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- chan->common.state = state;
|
|
|
|
- }
|
|
|
|
- chan->state_tick = jiffies;
|
|
|
|
- restore_flags(flags);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Send packet on a logical channel.
|
|
|
|
- * When this function is called, tx_skb field of the channel data
|
|
|
|
- * space points to the transmit socket buffer. When transmission
|
|
|
|
- * is complete, release socket buffer and reset 'tbusy' flag.
|
|
|
|
- *
|
|
|
|
- * Return: 0 - transmission complete
|
|
|
|
- * 1 - busy
|
|
|
|
- *
|
|
|
|
- * Notes:
|
|
|
|
- * 1. If packet length is greater than MTU for this channel, we'll fragment
|
|
|
|
- * the packet into 'complete sequence' using M-bit.
|
|
|
|
- * 2. When transmission is complete, an event notification should be issued
|
|
|
|
- * to the router.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int chan_send(struct net_device* dev, void* buff, unsigned data_len,
|
|
|
|
- unsigned char tx_intr)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
- unsigned len=0, qdm=0, res=0, orig_len = 0;
|
|
|
|
- void *data;
|
|
|
|
-
|
|
|
|
- /* Check to see if channel is ready */
|
|
|
|
- if ((!(status->cflags[chan->ch_idx] & 0x40) && !card->u.x.LAPB_hdlc) ||
|
|
|
|
- !(*card->u.x.hdlc_buf_status & 0x40)){
|
|
|
|
-
|
|
|
|
- if (!tx_intr){
|
|
|
|
- setup_for_delayed_transmit (dev, buff, data_len);
|
|
|
|
- return 0;
|
|
|
|
- }else{
|
|
|
|
- /* By returning 0 to tx_intr the packet will be dropped */
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- printk(KERN_INFO "%s: ERROR, Tx intr could not send, dropping %s:\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
- ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- /* Remove the API Header */
|
|
|
|
- x25api_hdr_t *api_data = (x25api_hdr_t *)buff;
|
|
|
|
-
|
|
|
|
- /* Set the qdm bits from the packet header
|
|
|
|
- * User has the option to set the qdm bits
|
|
|
|
- */
|
|
|
|
- qdm = api_data->qdm;
|
|
|
|
-
|
|
|
|
- orig_len = len = data_len - sizeof(x25api_hdr_t);
|
|
|
|
- data = (unsigned char*)buff + sizeof(x25api_hdr_t);
|
|
|
|
- }else{
|
|
|
|
- data = buff;
|
|
|
|
- orig_len = len = data_len;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (tx_intr){
|
|
|
|
- /* We are in tx_intr, minus the tx_offset from
|
|
|
|
- * the total length. The tx_offset part of the
|
|
|
|
- * data has already been sent. Also, move the
|
|
|
|
- * data pointer to proper offset location.
|
|
|
|
- */
|
|
|
|
- len -= chan->tx_offset;
|
|
|
|
- data = (unsigned char*)data + chan->tx_offset;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Check if the packet length is greater than MTU
|
|
|
|
- * If YES: Cut the len to MTU and set the M bit
|
|
|
|
- */
|
|
|
|
- if (len > chan->tx_pkt_size && !card->u.x.LAPB_hdlc){
|
|
|
|
- len = chan->tx_pkt_size;
|
|
|
|
- qdm |= M_BIT;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Pass only first three bits of the qdm byte to the send
|
|
|
|
- * routine. In case user sets any other bit which might
|
|
|
|
- * cause errors.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- switch(x25_send(card, chan->common.lcn, (qdm&0x07), len, data)){
|
|
|
|
- case 0x00: /* success */
|
|
|
|
- chan->i_timeout_sofar = jiffies;
|
|
|
|
-
|
|
|
|
- dev->trans_start=jiffies;
|
|
|
|
-
|
|
|
|
- if ((qdm & M_BIT) && !card->u.x.LAPB_hdlc){
|
|
|
|
- if (!tx_intr){
|
|
|
|
- /* The M bit was set, which means that part of the
|
|
|
|
- * packet has been sent. Copy the packet into a buffer
|
|
|
|
- * and set the offset to len, so on next tx_inter
|
|
|
|
- * the packet will be sent using the below offset.
|
|
|
|
- */
|
|
|
|
- chan->tx_offset += len;
|
|
|
|
-
|
|
|
|
- ++chan->ifstats.tx_packets;
|
|
|
|
- chan->ifstats.tx_bytes += len;
|
|
|
|
-
|
|
|
|
- if (chan->tx_offset < orig_len){
|
|
|
|
- setup_for_delayed_transmit (dev, buff, data_len);
|
|
|
|
- }
|
|
|
|
- res=0;
|
|
|
|
- }else{
|
|
|
|
- /* We are already in tx_inter, thus data is already
|
|
|
|
- * in the buffer. Update the offset and wait for
|
|
|
|
- * next tx_intr. We add on to the offset, since data can
|
|
|
|
- * be X number of times larger than max data size.
|
|
|
|
- */
|
|
|
|
- ++chan->ifstats.tx_packets;
|
|
|
|
- chan->ifstats.tx_bytes += len;
|
|
|
|
-
|
|
|
|
- ++chan->if_send_stat.if_send_bfr_passed_to_adptr;
|
|
|
|
- chan->tx_offset += len;
|
|
|
|
-
|
|
|
|
- /* The user can set the qdm bit as well.
|
|
|
|
- * If the entire packet was sent and qdm is still
|
|
|
|
- * set, than it's the user who has set the M bit. In that,
|
|
|
|
- * case indicate that the packet was send by returning
|
|
|
|
- * 0 and wait for a new packet. Otherwise, wait for next
|
|
|
|
- * tx interrupt to send the rest of the packet */
|
|
|
|
-
|
|
|
|
- if (chan->tx_offset < orig_len){
|
|
|
|
- res=1;
|
|
|
|
- }else{
|
|
|
|
- res=0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- ++chan->ifstats.tx_packets;
|
|
|
|
- chan->ifstats.tx_bytes += len;
|
|
|
|
- ++chan->if_send_stat.if_send_bfr_passed_to_adptr;
|
|
|
|
- res=0;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 0x33: /* Tx busy */
|
|
|
|
- if (tx_intr){
|
|
|
|
- printk(KERN_INFO "%s: Tx_intr: Big Error dropping packet %s\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
|
|
|
|
- res=0;
|
|
|
|
- }else{
|
|
|
|
- DBG_PRINTK(KERN_INFO
|
|
|
|
- "%s: Send: Big Error should have tx: storring %s\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
- setup_for_delayed_transmit (dev, buff, data_len);
|
|
|
|
- res=1;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default: /* failure */
|
|
|
|
- ++chan->ifstats.tx_errors;
|
|
|
|
- if (tx_intr){
|
|
|
|
- printk(KERN_INFO "%s: Tx_intr: Failure to send, dropping %s\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
- ++chan->ifstats.tx_dropped;
|
|
|
|
- ++card->wandev.stats.tx_dropped;
|
|
|
|
- ++chan->if_send_stat.if_send_bfr_not_passed_to_adptr;
|
|
|
|
- res=0;
|
|
|
|
- }else{
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: Send: Failure to send !!!, storing %s\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
- setup_for_delayed_transmit (dev, buff, data_len);
|
|
|
|
- res=1;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- return res;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Parse X.25 call request data and fill x25_call_info_t structure.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void parse_call_info (unsigned char* str, x25_call_info_t* info)
|
|
|
|
-{
|
|
|
|
- memset(info, 0, sizeof(x25_call_info_t));
|
|
|
|
- for (; *str; ++str)
|
|
|
|
- {
|
|
|
|
- int i;
|
|
|
|
- unsigned char ch;
|
|
|
|
-
|
|
|
|
- if (*str == '-') switch (str[1]) {
|
|
|
|
-
|
|
|
|
- /* Take minus 2 off the maximum size so that
|
|
|
|
- * last byte is 0. This way we can use string
|
|
|
|
- * manipulaton functions on call information.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- case 'd': /* destination address */
|
|
|
|
- for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
|
|
|
|
- ch = str[2+i];
|
|
|
|
- if (isspace(ch)) break;
|
|
|
|
- info->dest[i] = ch;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 's': /* source address */
|
|
|
|
- for (i = 0; i < (MAX_X25_ADDR_SIZE-2); ++i){
|
|
|
|
- ch = str[2+i];
|
|
|
|
- if (isspace(ch)) break;
|
|
|
|
- info->src[i] = ch;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 'u': /* user data */
|
|
|
|
- for (i = 0; i < (MAX_X25_DATA_SIZE-2); ++i){
|
|
|
|
- ch = str[2+i];
|
|
|
|
- if (isspace(ch)) break;
|
|
|
|
- info->user[i] = ch;
|
|
|
|
- }
|
|
|
|
- info->nuser = i;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case 'f': /* facilities */
|
|
|
|
- for (i = 0; i < (MAX_X25_FACL_SIZE-2); ++i){
|
|
|
|
- ch = str[2+i];
|
|
|
|
- if (isspace(ch)) break;
|
|
|
|
- info->facil[i] = ch;
|
|
|
|
- }
|
|
|
|
- info->nfacil = i;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Convert line speed in bps to a number used by S502 code.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static unsigned char bps_to_speed_code (unsigned long bps)
|
|
|
|
-{
|
|
|
|
- unsigned char number;
|
|
|
|
-
|
|
|
|
- if (bps <= 1200) number = 0x01;
|
|
|
|
- else if (bps <= 2400) number = 0x02;
|
|
|
|
- else if (bps <= 4800) number = 0x03;
|
|
|
|
- else if (bps <= 9600) number = 0x04;
|
|
|
|
- else if (bps <= 19200) number = 0x05;
|
|
|
|
- else if (bps <= 38400) number = 0x06;
|
|
|
|
- else if (bps <= 45000) number = 0x07;
|
|
|
|
- else if (bps <= 56000) number = 0x08;
|
|
|
|
- else if (bps <= 64000) number = 0x09;
|
|
|
|
- else if (bps <= 74000) number = 0x0A;
|
|
|
|
- else if (bps <= 112000) number = 0x0B;
|
|
|
|
- else if (bps <= 128000) number = 0x0C;
|
|
|
|
- else number = 0x0D;
|
|
|
|
-
|
|
|
|
- return number;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Convert decimal string to unsigned integer.
|
|
|
|
- * If len != 0 then only 'len' characters of the string are converted.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static unsigned int dec_to_uint (unsigned char* str, int len)
|
|
|
|
-{
|
|
|
|
- unsigned val;
|
|
|
|
-
|
|
|
|
- if (!len)
|
|
|
|
- len = strlen(str);
|
|
|
|
-
|
|
|
|
- for (val = 0; len && isdigit(*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 (isdigit(ch))
|
|
|
|
- val = (val << 4) + (ch - (unsigned)'0');
|
|
|
|
- else if (isxdigit(ch))
|
|
|
|
- val = (val << 4) + ((ch & 0xDF) - (unsigned)'A' + 10);
|
|
|
|
- else break;
|
|
|
|
- }
|
|
|
|
- return val;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-static int handle_IPXWAN(unsigned char *sendpacket, char *devname, unsigned char enable_IPX, unsigned long network_number, unsigned short proto)
|
|
|
|
-{
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- if( proto == ETH_P_IPX) {
|
|
|
|
- /* It's an IPX packet */
|
|
|
|
- if(!enable_IPX) {
|
|
|
|
- /* Return 1 so we don't pass it up the stack. */
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- /* It's not IPX so pass it up the stack.*/
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if( sendpacket[16] == 0x90 &&
|
|
|
|
- sendpacket[17] == 0x04)
|
|
|
|
- {
|
|
|
|
- /* It's IPXWAN */
|
|
|
|
-
|
|
|
|
- if( sendpacket[2] == 0x02 &&
|
|
|
|
- sendpacket[34] == 0x00)
|
|
|
|
- {
|
|
|
|
- /* It's a timer request packet */
|
|
|
|
- printk(KERN_INFO "%s: Received IPXWAN Timer Request packet\n",devname);
|
|
|
|
-
|
|
|
|
- /* Go through the routing options and answer no to every
|
|
|
|
- * option except Unnumbered RIP/SAP
|
|
|
|
- */
|
|
|
|
- for(i = 41; sendpacket[i] == 0x00; i += 5)
|
|
|
|
- {
|
|
|
|
- /* 0x02 is the option for Unnumbered RIP/SAP */
|
|
|
|
- if( sendpacket[i + 4] != 0x02)
|
|
|
|
- {
|
|
|
|
- sendpacket[i + 1] = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Skip over the extended Node ID option */
|
|
|
|
- if( sendpacket[i] == 0x04 )
|
|
|
|
- {
|
|
|
|
- i += 8;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* We also want to turn off all header compression opt. */
|
|
|
|
- for(; sendpacket[i] == 0x80 ;)
|
|
|
|
- {
|
|
|
|
- sendpacket[i + 1] = 0;
|
|
|
|
- i += (sendpacket[i + 2] << 8) + (sendpacket[i + 3]) + 4;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Set the packet type to timer response */
|
|
|
|
- sendpacket[34] = 0x01;
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: Sending IPXWAN Timer Response\n",devname);
|
|
|
|
- }
|
|
|
|
- else if( sendpacket[34] == 0x02 )
|
|
|
|
- {
|
|
|
|
- /* This is an information request packet */
|
|
|
|
- printk(KERN_INFO "%s: Received IPXWAN Information Request packet\n",devname);
|
|
|
|
-
|
|
|
|
- /* Set the packet type to information response */
|
|
|
|
- sendpacket[34] = 0x03;
|
|
|
|
-
|
|
|
|
- /* Set the router name */
|
|
|
|
- sendpacket[51] = 'X';
|
|
|
|
- sendpacket[52] = 'T';
|
|
|
|
- sendpacket[53] = 'P';
|
|
|
|
- sendpacket[54] = 'I';
|
|
|
|
- sendpacket[55] = 'P';
|
|
|
|
- sendpacket[56] = 'E';
|
|
|
|
- sendpacket[57] = '-';
|
|
|
|
- sendpacket[58] = CVHexToAscii(network_number >> 28);
|
|
|
|
- sendpacket[59] = CVHexToAscii((network_number & 0x0F000000)>> 24);
|
|
|
|
- sendpacket[60] = CVHexToAscii((network_number & 0x00F00000)>> 20);
|
|
|
|
- sendpacket[61] = CVHexToAscii((network_number & 0x000F0000)>> 16);
|
|
|
|
- sendpacket[62] = CVHexToAscii((network_number & 0x0000F000)>> 12);
|
|
|
|
- sendpacket[63] = CVHexToAscii((network_number & 0x00000F00)>> 8);
|
|
|
|
- sendpacket[64] = CVHexToAscii((network_number & 0x000000F0)>> 4);
|
|
|
|
- sendpacket[65] = CVHexToAscii(network_number & 0x0000000F);
|
|
|
|
- for(i = 66; i < 99; i+= 1)
|
|
|
|
- {
|
|
|
|
- sendpacket[i] = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- printk(KERN_INFO "%s: Sending IPXWAN Information Response packet\n",devname);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- printk(KERN_INFO "%s: Unknown IPXWAN packet!\n",devname);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Set the WNodeID to our network address */
|
|
|
|
- sendpacket[35] = (unsigned char)(network_number >> 24);
|
|
|
|
- sendpacket[36] = (unsigned char)((network_number & 0x00FF0000) >> 16);
|
|
|
|
- sendpacket[37] = (unsigned char)((network_number & 0x0000FF00) >> 8);
|
|
|
|
- sendpacket[38] = (unsigned char)(network_number & 0x000000FF);
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
- } else {
|
|
|
|
- /*If we get here it's an IPX-data packet, so it'll get passed up the stack.
|
|
|
|
- */
|
|
|
|
- /* switch the network numbers */
|
|
|
|
- switch_net_numbers(sendpacket, network_number, 1);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * If incoming is 0 (outgoing)- if the net numbers is ours make it 0
|
|
|
|
- * if incoming is 1 - if the net number is 0 make it ours
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static void switch_net_numbers(unsigned char *sendpacket, unsigned long network_number, unsigned char incoming)
|
|
|
|
-{
|
|
|
|
- unsigned long pnetwork_number;
|
|
|
|
-
|
|
|
|
- pnetwork_number = (unsigned long)((sendpacket[6] << 24) +
|
|
|
|
- (sendpacket[7] << 16) + (sendpacket[8] << 8) +
|
|
|
|
- sendpacket[9]);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if (!incoming) {
|
|
|
|
- /*If the destination network number is ours, make it 0 */
|
|
|
|
- if( pnetwork_number == network_number) {
|
|
|
|
- sendpacket[6] = sendpacket[7] = sendpacket[8] =
|
|
|
|
- sendpacket[9] = 0x00;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- /* If the incoming network is 0, make it ours */
|
|
|
|
- if( pnetwork_number == 0) {
|
|
|
|
- sendpacket[6] = (unsigned char)(network_number >> 24);
|
|
|
|
- sendpacket[7] = (unsigned char)((network_number &
|
|
|
|
- 0x00FF0000) >> 16);
|
|
|
|
- sendpacket[8] = (unsigned char)((network_number &
|
|
|
|
- 0x0000FF00) >> 8);
|
|
|
|
- sendpacket[9] = (unsigned char)(network_number &
|
|
|
|
- 0x000000FF);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- pnetwork_number = (unsigned long)((sendpacket[18] << 24) +
|
|
|
|
- (sendpacket[19] << 16) + (sendpacket[20] << 8) +
|
|
|
|
- sendpacket[21]);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if( !incoming ) {
|
|
|
|
- /* If the source network is ours, make it 0 */
|
|
|
|
- if( pnetwork_number == network_number) {
|
|
|
|
- sendpacket[18] = sendpacket[19] = sendpacket[20] =
|
|
|
|
- sendpacket[21] = 0x00;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- /* If the source network is 0, make it ours */
|
|
|
|
- if( pnetwork_number == 0 ) {
|
|
|
|
- sendpacket[18] = (unsigned char)(network_number >> 24);
|
|
|
|
- sendpacket[19] = (unsigned char)((network_number &
|
|
|
|
- 0x00FF0000) >> 16);
|
|
|
|
- sendpacket[20] = (unsigned char)((network_number &
|
|
|
|
- 0x0000FF00) >> 8);
|
|
|
|
- sendpacket[21] = (unsigned char)(network_number &
|
|
|
|
- 0x000000FF);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-} /* switch_net_numbers */
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/********************* X25API SPECIFIC FUNCTIONS ****************/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * find_channel
|
|
|
|
- *
|
|
|
|
- * Manages the lcn to device map. It increases performance
|
|
|
|
- * because it eliminates the need to search through the link
|
|
|
|
- * list for a device which is bounded to a specific lcn.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-struct net_device *find_channel(sdla_t *card, unsigned lcn)
|
|
|
|
-{
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
-
|
|
|
|
- return card->wandev.dev;
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- /* We don't know whether the incoming lcn
|
|
|
|
- * is a PVC or an SVC channel. But we do know that
|
|
|
|
- * the lcn cannot be for both the PVC and the SVC
|
|
|
|
- * channel.
|
|
|
|
-
|
|
|
|
- * If the lcn number is greater or equal to 255,
|
|
|
|
- * take the modulo 255 of that number. We only have
|
|
|
|
- * 255 locations, thus higher numbers must be mapped
|
|
|
|
- * to a number between 0 and 245.
|
|
|
|
-
|
|
|
|
- * We must separate pvc's and svc's since two don't
|
|
|
|
- * have to be contiguous. Meaning pvc's can start
|
|
|
|
- * from 1 to 10 and svc's can start from 256 to 266.
|
|
|
|
- * But 256%255 is 1, i.e. CONFLICT.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Highest LCN number must be less or equal to 4096 */
|
|
|
|
- if ((lcn <= MAX_LCN_NUM) && (lcn > 0)){
|
|
|
|
-
|
|
|
|
- if (lcn < X25_MAX_CHAN){
|
|
|
|
- if (card->u.x.svc_to_dev_map[lcn])
|
|
|
|
- return card->u.x.svc_to_dev_map[lcn];
|
|
|
|
-
|
|
|
|
- if (card->u.x.pvc_to_dev_map[lcn])
|
|
|
|
- return card->u.x.pvc_to_dev_map[lcn];
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- int new_lcn = lcn%X25_MAX_CHAN;
|
|
|
|
- if (card->u.x.svc_to_dev_map[new_lcn])
|
|
|
|
- return card->u.x.svc_to_dev_map[new_lcn];
|
|
|
|
-
|
|
|
|
- if (card->u.x.pvc_to_dev_map[new_lcn])
|
|
|
|
- return card->u.x.pvc_to_dev_map[new_lcn];
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return NULL;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void bind_lcn_to_dev(sdla_t *card, struct net_device *dev, unsigned lcn)
|
|
|
|
-{
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
-
|
|
|
|
- /* Modulo the lcn number by X25_MAX_CHAN (255)
|
|
|
|
- * because the lcn number can be greater than 255
|
|
|
|
- *
|
|
|
|
- * We need to split svc and pvc since they don't have
|
|
|
|
- * to be contigous.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- if (chan->common.svc){
|
|
|
|
- card->u.x.svc_to_dev_map[(lcn % X25_MAX_CHAN)] = dev;
|
|
|
|
- }else{
|
|
|
|
- card->u.x.pvc_to_dev_map[(lcn % X25_MAX_CHAN)] = dev;
|
|
|
|
- }
|
|
|
|
- chan->common.lcn = lcn;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * x25api_bh
|
|
|
|
- *
|
|
|
|
- *
|
|
|
|
- *==============================================================*/
|
|
|
|
-
|
|
|
|
-static void x25api_bh(struct net_device* dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t* card = chan->card;
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
-
|
|
|
|
- if (atomic_read(&chan->bh_buff_used) == 0){
|
|
|
|
- printk(KERN_INFO "%s: BH Buffer Empty in BH\n",
|
|
|
|
- card->devname);
|
|
|
|
- clear_bit(0, &chan->tq_working);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- while (atomic_read(&chan->bh_buff_used)){
|
|
|
|
-
|
|
|
|
- /* If the sock is in the process of unlinking the
|
|
|
|
- * driver from the socket, we must get out.
|
|
|
|
- * This never happends but is a sanity check. */
|
|
|
|
- if (test_bit(0,&chan->common.common_critical)){
|
|
|
|
- clear_bit(0, &chan->tq_working);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* If LAPB HDLC, do not drop packets if socket is
|
|
|
|
- * not connected. Let the buffer fill up and
|
|
|
|
- * turn off rx interrupt */
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- if (chan->common.sk == NULL || chan->common.func == NULL){
|
|
|
|
- clear_bit(0, &chan->tq_working);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- skb = ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb;
|
|
|
|
-
|
|
|
|
- if (skb == NULL){
|
|
|
|
- printk(KERN_INFO "%s: BH Skb empty for read %i\n",
|
|
|
|
- card->devname,chan->bh_read);
|
|
|
|
- }else{
|
|
|
|
-
|
|
|
|
- if (chan->common.sk == NULL || chan->common.func == NULL){
|
|
|
|
- printk(KERN_INFO "%s: BH: Socket disconnected, dropping\n",
|
|
|
|
- card->devname);
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- x25api_bh_cleanup(dev);
|
|
|
|
- ++chan->ifstats.rx_dropped;
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_not_passed_to_stack;
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if (chan->common.func(skb,dev,chan->common.sk) != 0){
|
|
|
|
- /* Sock full cannot send, queue us for another
|
|
|
|
- * try
|
|
|
|
- */
|
|
|
|
- printk(KERN_INFO "%s: BH: !!! Packet failed to send !!!!! \n",
|
|
|
|
- card->devname);
|
|
|
|
- atomic_set(&chan->common.receive_block,1);
|
|
|
|
- return;
|
|
|
|
- }else{
|
|
|
|
- x25api_bh_cleanup(dev);
|
|
|
|
- ++chan->rx_intr_stat.rx_intr_bfr_passed_to_stack;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- clear_bit(0, &chan->tq_working);
|
|
|
|
-
|
|
|
|
- return;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * x25api_bh_cleanup
|
|
|
|
- *
|
|
|
|
- *
|
|
|
|
- *==============================================================*/
|
|
|
|
-
|
|
|
|
-static int x25api_bh_cleanup(struct net_device *dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t *card = chan->card;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- ((bh_data_t *)&chan->bh_head[chan->bh_read])->skb = NULL;
|
|
|
|
-
|
|
|
|
- if (chan->bh_read == MAX_BH_BUFF){
|
|
|
|
- chan->bh_read=0;
|
|
|
|
- }else{
|
|
|
|
- ++chan->bh_read;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* If the Receive interrupt was off, it means
|
|
|
|
- * that we filled up our circular buffer. Check
|
|
|
|
- * that we have space in the buffer. If so
|
|
|
|
- * turn the RX interrupt back on.
|
|
|
|
- */
|
|
|
|
- if (!(status->imask & INTR_ON_RX_FRAME)){
|
|
|
|
- if (atomic_read(&chan->bh_buff_used) < (MAX_BH_BUFF+1)){
|
|
|
|
- printk(KERN_INFO "%s: BH: Turning on the interrupt\n",
|
|
|
|
- card->devname);
|
|
|
|
- status->imask |= INTR_ON_RX_FRAME;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- atomic_dec(&chan->bh_buff_used);
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * bh_enqueue
|
|
|
|
- *
|
|
|
|
- *
|
|
|
|
- *==============================================================*/
|
|
|
|
-
|
|
|
|
-static int bh_enqueue(struct net_device *dev, struct sk_buff *skb)
|
|
|
|
-{
|
|
|
|
- x25_channel_t* chan = dev->priv;
|
|
|
|
- sdla_t *card = chan->card;
|
|
|
|
- TX25Status* status = card->flags;
|
|
|
|
-
|
|
|
|
- if (atomic_read(&chan->bh_buff_used) == (MAX_BH_BUFF+1)){
|
|
|
|
- printk(KERN_INFO "%s: Bottom half buffer FULL\n",
|
|
|
|
- card->devname);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ((bh_data_t *)&chan->bh_head[chan->bh_write])->skb = skb;
|
|
|
|
-
|
|
|
|
- if (chan->bh_write == MAX_BH_BUFF){
|
|
|
|
- chan->bh_write=0;
|
|
|
|
- }else{
|
|
|
|
- ++chan->bh_write;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- atomic_inc(&chan->bh_buff_used);
|
|
|
|
-
|
|
|
|
- if (atomic_read(&chan->bh_buff_used) == (MAX_BH_BUFF+1)){
|
|
|
|
- printk(KERN_INFO "%s: Buffer is now full, Turning off RX Intr\n",
|
|
|
|
- card->devname);
|
|
|
|
- status->imask &= ~INTR_ON_RX_FRAME;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * timer_intr_cmd_exec
|
|
|
|
- *
|
|
|
|
- * Called by timer interrupt to execute a command
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int timer_intr_cmd_exec (sdla_t* card)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev;
|
|
|
|
- unsigned char more_to_exec=0;
|
|
|
|
- volatile x25_channel_t *chan=NULL;
|
|
|
|
- int i=0,bad_cmd=0,err=0;
|
|
|
|
-
|
|
|
|
- if (card->u.x.cmd_dev == NULL){
|
|
|
|
- card->u.x.cmd_dev = card->wandev.dev;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- dev = card->u.x.cmd_dev;
|
|
|
|
-
|
|
|
|
- for (;;){
|
|
|
|
-
|
|
|
|
- chan = dev->priv;
|
|
|
|
-
|
|
|
|
- if (atomic_read(&chan->common.command)){
|
|
|
|
-
|
|
|
|
- bad_cmd = check_bad_command(card,dev);
|
|
|
|
-
|
|
|
|
- if ((!chan->common.mbox || atomic_read(&chan->common.disconnect)) &&
|
|
|
|
- !bad_cmd){
|
|
|
|
-
|
|
|
|
- /* Socket has died or exited, We must bring the
|
|
|
|
- * channel down before anybody else tries to
|
|
|
|
- * use it */
|
|
|
|
- err = channel_disconnect(card,dev);
|
|
|
|
- }else{
|
|
|
|
- err = execute_delayed_cmd(card, dev,
|
|
|
|
- (mbox_cmd_t*)chan->common.mbox,
|
|
|
|
- bad_cmd);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch (err){
|
|
|
|
-
|
|
|
|
- case RETURN_RESULT:
|
|
|
|
-
|
|
|
|
- /* Return the result to the socket without
|
|
|
|
- * delay. NO_WAIT Command */
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
- if (atomic_read(&card->u.x.command_busy))
|
|
|
|
- atomic_set(&card->u.x.command_busy,0);
|
|
|
|
-
|
|
|
|
- send_delayed_cmd_result(card,dev,card->mbox);
|
|
|
|
-
|
|
|
|
- more_to_exec=0;
|
|
|
|
- break;
|
|
|
|
- case DELAY_RESULT:
|
|
|
|
-
|
|
|
|
- /* Wait for the remote to respond, before
|
|
|
|
- * sending the result up to the socket.
|
|
|
|
- * WAIT command */
|
|
|
|
- if (atomic_read(&card->u.x.command_busy))
|
|
|
|
- atomic_set(&card->u.x.command_busy,0);
|
|
|
|
-
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
- more_to_exec=0;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
-
|
|
|
|
- /* If command could not be executed for
|
|
|
|
- * some reason (i.e return code 0x33 busy)
|
|
|
|
- * set the more_to_exec bit which will
|
|
|
|
- * indicate that this command must be exectued
|
|
|
|
- * again during next timer interrupt
|
|
|
|
- */
|
|
|
|
- more_to_exec=1;
|
|
|
|
- if (atomic_read(&card->u.x.command_busy) == 0)
|
|
|
|
- atomic_set(&card->u.x.command_busy,1);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- bad_cmd=0;
|
|
|
|
-
|
|
|
|
- /* If flags is set, there are no hdlc buffers,
|
|
|
|
- * thus, wait for the next pass and try the
|
|
|
|
- * same command again. Otherwise, start searching
|
|
|
|
- * from next device on the next pass.
|
|
|
|
- */
|
|
|
|
- if (!more_to_exec){
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- }else{
|
|
|
|
- /* This device has nothing to execute,
|
|
|
|
- * go to next.
|
|
|
|
- */
|
|
|
|
- if (atomic_read(&card->u.x.command_busy))
|
|
|
|
- atomic_set(&card->u.x.command_busy,0);
|
|
|
|
- dev = move_dev_to_next(card,dev);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (++i == card->u.x.no_dev){
|
|
|
|
- if (!more_to_exec){
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: Nothing to execute in Timer\n",
|
|
|
|
- card->devname);
|
|
|
|
- if (atomic_read(&card->u.x.command_busy)){
|
|
|
|
- atomic_set(&card->u.x.command_busy,0);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- } //End of FOR
|
|
|
|
-
|
|
|
|
- card->u.x.cmd_dev = dev;
|
|
|
|
-
|
|
|
|
- if (more_to_exec){
|
|
|
|
- /* If more commands are pending, do not turn off timer
|
|
|
|
- * interrupt */
|
|
|
|
- return 1;
|
|
|
|
- }else{
|
|
|
|
- /* No more commands, turn off timer interrupt */
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * execute_delayed_cmd
|
|
|
|
- *
|
|
|
|
- * Execute an API command which was passed down from the
|
|
|
|
- * sock. Sock is very limited in which commands it can
|
|
|
|
- * execute. Wait and No Wait commands are supported.
|
|
|
|
- * Place Call, Clear Call and Reset wait commands, where
|
|
|
|
- * Accept Call is a no_wait command.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int execute_delayed_cmd(sdla_t* card, struct net_device *dev,
|
|
|
|
- mbox_cmd_t *usr_cmd, char bad_cmd)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int err;
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- int delay=RETURN_RESULT;
|
|
|
|
-
|
|
|
|
- if (!(*card->u.x.hdlc_buf_status & 0x40) && !bad_cmd){
|
|
|
|
- return TRY_CMD_AGAIN;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* This way a command is guaranteed to be executed for
|
|
|
|
- * a specific lcn, the network interface is bound to. */
|
|
|
|
- usr_cmd->cmd.lcn = chan->common.lcn;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* If channel is pvc, instead of place call
|
|
|
|
- * run x25_channel configuration. If running LAPB HDLC
|
|
|
|
- * enable communications.
|
|
|
|
- */
|
|
|
|
- if ((!chan->common.svc) && (usr_cmd->cmd.command == X25_PLACE_CALL)){
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- DBG_PRINTK(KERN_INFO "LAPB: Connecting\n");
|
|
|
|
- connect(card);
|
|
|
|
- set_chan_state(dev,WAN_CONNECTING);
|
|
|
|
- return DELAY_RESULT;
|
|
|
|
- }else{
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: PVC is CONNECTING\n",card->devname);
|
|
|
|
- if (x25_get_chan_conf(card, chan) == CMD_OK){
|
|
|
|
- set_chan_state(dev, WAN_CONNECTED);
|
|
|
|
- }else{
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
- }
|
|
|
|
- return RETURN_RESULT;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Copy the socket mbox command onto the board */
|
|
|
|
-
|
|
|
|
- memcpy(&mbox->cmd, &usr_cmd->cmd, sizeof(TX25Cmd));
|
|
|
|
- if (usr_cmd->cmd.length){
|
|
|
|
- memcpy(mbox->data, usr_cmd->data, usr_cmd->cmd.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Check if command is bad. We need to copy the cmd into
|
|
|
|
- * the buffer regardless since we return the, mbox to
|
|
|
|
- * the user */
|
|
|
|
- if (bad_cmd){
|
|
|
|
- mbox->cmd.result=0x01;
|
|
|
|
- return RETURN_RESULT;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
-
|
|
|
|
- if (err != CMD_OK && err != X25RES_NOT_READY)
|
|
|
|
- x25_error(card, err, usr_cmd->cmd.command, usr_cmd->cmd.lcn);
|
|
|
|
-
|
|
|
|
- if (mbox->cmd.result == X25RES_NOT_READY){
|
|
|
|
- return TRY_CMD_AGAIN;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch (mbox->cmd.command){
|
|
|
|
-
|
|
|
|
- case X25_PLACE_CALL:
|
|
|
|
-
|
|
|
|
- switch (mbox->cmd.result){
|
|
|
|
-
|
|
|
|
- case CMD_OK:
|
|
|
|
-
|
|
|
|
- /* Check if Place call is a wait command or a
|
|
|
|
- * no wait command */
|
|
|
|
- if (atomic_read(&chan->common.command) & 0x80)
|
|
|
|
- delay=RETURN_RESULT;
|
|
|
|
- else
|
|
|
|
- delay=DELAY_RESULT;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- DBG_PRINTK(KERN_INFO "\n%s: PLACE CALL Binding dev %s to lcn %i\n",
|
|
|
|
- card->devname,dev->name, mbox->cmd.lcn);
|
|
|
|
-
|
|
|
|
- bind_lcn_to_dev (card, dev, mbox->cmd.lcn);
|
|
|
|
- set_chan_state(dev, WAN_CONNECTING);
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- delay=RETURN_RESULT;
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25_ACCEPT_CALL:
|
|
|
|
-
|
|
|
|
- switch (mbox->cmd.result){
|
|
|
|
-
|
|
|
|
- case CMD_OK:
|
|
|
|
-
|
|
|
|
- DBG_PRINTK(KERN_INFO "\n%s: ACCEPT Binding dev %s to lcn %i\n",
|
|
|
|
- card->devname,dev->name,mbox->cmd.lcn);
|
|
|
|
-
|
|
|
|
- bind_lcn_to_dev (card, dev, mbox->cmd.lcn);
|
|
|
|
-
|
|
|
|
- if (x25_get_chan_conf(card, chan) == CMD_OK){
|
|
|
|
-
|
|
|
|
- set_chan_state(dev, WAN_CONNECTED);
|
|
|
|
- delay=RETURN_RESULT;
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- if (x25_clear_call(card, usr_cmd->cmd.lcn, 0, 0) == CMD_OK){
|
|
|
|
- /* if clear is successful, wait for clear confirm
|
|
|
|
- */
|
|
|
|
- delay=DELAY_RESULT;
|
|
|
|
- }else{
|
|
|
|
- /* Do not change the state here. If we fail
|
|
|
|
- * the accept the return code is send up
|
|
|
|
- *the stack, which will ether retry
|
|
|
|
- * or clear the call
|
|
|
|
- */
|
|
|
|
- DBG_PRINTK(KERN_INFO
|
|
|
|
- "%s: ACCEPT: STATE MAY BE CURRUPTED 2 !!!!!\n",
|
|
|
|
- card->devname);
|
|
|
|
- delay=RETURN_RESULT;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- case X25RES_ASYNC_PACKET:
|
|
|
|
- delay=TRY_CMD_AGAIN;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: ACCEPT FAILED\n",card->devname);
|
|
|
|
- if (x25_clear_call(card, usr_cmd->cmd.lcn, 0, 0) == CMD_OK){
|
|
|
|
- delay=DELAY_RESULT;
|
|
|
|
- }else{
|
|
|
|
- /* Do not change the state here. If we fail the accept. The
|
|
|
|
- * return code is send up the stack, which will ether retry
|
|
|
|
- * or clear the call */
|
|
|
|
- DBG_PRINTK(KERN_INFO
|
|
|
|
- "%s: ACCEPT: STATE MAY BE CORRUPTED 1 !!!!!\n",
|
|
|
|
- card->devname);
|
|
|
|
- delay=RETURN_RESULT;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25_CLEAR_CALL:
|
|
|
|
-
|
|
|
|
- switch (mbox->cmd.result){
|
|
|
|
-
|
|
|
|
- case CMD_OK:
|
|
|
|
- DBG_PRINTK(KERN_INFO
|
|
|
|
- "CALL CLEAR OK: Dev %s Mbox Lcn %i Chan Lcn %i\n",
|
|
|
|
- dev->name,mbox->cmd.lcn,chan->common.lcn);
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTING);
|
|
|
|
- delay = DELAY_RESULT;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25RES_CHANNEL_IN_USE:
|
|
|
|
- case X25RES_ASYNC_PACKET:
|
|
|
|
- delay = TRY_CMD_AGAIN;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case X25RES_LINK_NOT_IN_ABM:
|
|
|
|
- case X25RES_INVAL_LCN:
|
|
|
|
- case X25RES_INVAL_STATE:
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
- delay = RETURN_RESULT;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default:
|
|
|
|
- /* If command did not execute because of user
|
|
|
|
- * fault, do not change the state. This will
|
|
|
|
- * signal the socket that clear command failed.
|
|
|
|
- * User can retry or close the socket.
|
|
|
|
- * When socket gets killed, it will set the
|
|
|
|
- * chan->disconnect which will signal
|
|
|
|
- * driver to clear the call */
|
|
|
|
- printk(KERN_INFO "%s: Clear Command Failed, Rc %x\n",
|
|
|
|
- card->devname,mbox->cmd.command);
|
|
|
|
- delay = RETURN_RESULT;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return delay;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * api_incoming_call
|
|
|
|
- *
|
|
|
|
- * Pass an incoming call request up the listening
|
|
|
|
- * sock. If the API sock is not listening reject the
|
|
|
|
- * call.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int api_incoming_call (sdla_t* card, TX25Mbox *mbox, int lcn)
|
|
|
|
-{
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
- int len = sizeof(TX25Cmd)+mbox->cmd.length;
|
|
|
|
-
|
|
|
|
- if (alloc_and_init_skb_buf(card, &skb, len)){
|
|
|
|
- printk(KERN_INFO "%s: API incoming call, no memory\n",card->devname);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memcpy(skb_put(skb,len),&mbox->cmd,len);
|
|
|
|
-
|
|
|
|
- skb->mac.raw = skb->data;
|
|
|
|
- skb->protocol = htons(X25_PROT);
|
|
|
|
- skb->pkt_type = WAN_PACKET_ASYNC;
|
|
|
|
-
|
|
|
|
- if (card->func(skb,card->sk) < 0){
|
|
|
|
- printk(KERN_INFO "%s: MAJOR ERROR: Failed to send up place call \n",card->devname);
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * send_delayed_cmd_result
|
|
|
|
- *
|
|
|
|
- * Wait commands like PLEACE CALL or CLEAR CALL must wait
|
|
|
|
- * until the result arrives. This function passes
|
|
|
|
- * the result to a waiting sock.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-static void send_delayed_cmd_result(sdla_t *card, struct net_device *dev,
|
|
|
|
- TX25Mbox* mbox)
|
|
|
|
-{
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- mbox_cmd_t *usr_cmd = (mbox_cmd_t *)chan->common.mbox;
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
- int len=sizeof(unsigned char);
|
|
|
|
-
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
-
|
|
|
|
- /* If the sock is in the process of unlinking the
|
|
|
|
- * driver from the socket, we must get out.
|
|
|
|
- * This never happends but is a sanity check. */
|
|
|
|
- if (test_bit(0,&chan->common.common_critical)){
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!usr_cmd || !chan->common.sk || !chan->common.func){
|
|
|
|
- DBG_PRINTK(KERN_INFO "Delay result: Sock not bounded sk: %u, func: %u, mbox: %u\n",
|
|
|
|
- (unsigned int)chan->common.sk,
|
|
|
|
- (unsigned int)chan->common.func,
|
|
|
|
- (unsigned int)usr_cmd);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memcpy(&usr_cmd->cmd, &mbox->cmd, sizeof(TX25Cmd));
|
|
|
|
- if (mbox->cmd.length > 0){
|
|
|
|
- memcpy(usr_cmd->data, mbox->data, mbox->cmd.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (alloc_and_init_skb_buf(card,&skb,len)){
|
|
|
|
- printk(KERN_INFO "Delay result: No sock buffers\n");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memcpy(skb_put(skb,len),&mbox->cmd.command,len);
|
|
|
|
-
|
|
|
|
- skb->mac.raw = skb->data;
|
|
|
|
- skb->pkt_type = WAN_PACKET_CMD;
|
|
|
|
-
|
|
|
|
- chan->common.func(skb,dev,chan->common.sk);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * clear_confirm_event
|
|
|
|
- *
|
|
|
|
- * Pass the clear confirmation event up the sock. The
|
|
|
|
- * API will disconnect only after the clear confirmation
|
|
|
|
- * has been received.
|
|
|
|
- *
|
|
|
|
- * Depending on the state, clear confirmation could
|
|
|
|
- * be an OOB event, or a result of an API command.
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int clear_confirm_event (sdla_t *card, TX25Mbox* mb)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev;
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- unsigned char old_state;
|
|
|
|
-
|
|
|
|
- dev = find_channel(card,mb->cmd.lcn);
|
|
|
|
- if (!dev){
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: *** GOT CLEAR BUT NO DEV %i\n",
|
|
|
|
- card->devname,mb->cmd.lcn);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- chan=dev->priv;
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: GOT CLEAR CONFIRM %s: Mbox lcn %i Chan lcn %i\n",
|
|
|
|
- card->devname, dev->name, mb->cmd.lcn, chan->common.lcn);
|
|
|
|
-
|
|
|
|
- /* If not API fall through to default.
|
|
|
|
- * If API, send the result to a waiting
|
|
|
|
- * socket.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- old_state = chan->common.state;
|
|
|
|
- set_chan_state(dev, WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API){
|
|
|
|
- switch (old_state) {
|
|
|
|
-
|
|
|
|
- case WAN_DISCONNECTING:
|
|
|
|
- case WAN_CONNECTING:
|
|
|
|
- send_delayed_cmd_result(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- case WAN_CONNECTED:
|
|
|
|
- send_oob_msg(card,dev,mb);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * send_oob_msg
|
|
|
|
- *
|
|
|
|
- * Construct an NEM Message and pass it up the connected
|
|
|
|
- * sock. If the sock is not bounded discard the NEM.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static void send_oob_msg(sdla_t *card, struct net_device *dev, TX25Mbox *mbox)
|
|
|
|
-{
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- mbox_cmd_t *usr_cmd = (mbox_cmd_t *)chan->common.mbox;
|
|
|
|
- struct sk_buff *skb;
|
|
|
|
- int len=sizeof(x25api_hdr_t)+mbox->cmd.length;
|
|
|
|
- x25api_t *api_hdr;
|
|
|
|
-
|
|
|
|
- /* If the sock is in the process of unlinking the
|
|
|
|
- * driver from the socket, we must get out.
|
|
|
|
- * This never happends but is a sanity check. */
|
|
|
|
- if (test_bit(0,&chan->common.common_critical)){
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!usr_cmd || !chan->common.sk || !chan->common.func){
|
|
|
|
- DBG_PRINTK(KERN_INFO "OOB MSG: Sock not bounded\n");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- memcpy(&usr_cmd->cmd, &mbox->cmd, sizeof(TX25Cmd));
|
|
|
|
- if (mbox->cmd.length > 0){
|
|
|
|
- memcpy(usr_cmd->data, mbox->data, mbox->cmd.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (alloc_and_init_skb_buf(card,&skb,len)){
|
|
|
|
- printk(KERN_INFO "%s: OOB MSG: No sock buffers\n",card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- api_hdr = (x25api_t*)skb_put(skb,len);
|
|
|
|
- api_hdr->hdr.pktType = mbox->cmd.pktType & 0x7F;
|
|
|
|
- api_hdr->hdr.qdm = mbox->cmd.qdm;
|
|
|
|
- api_hdr->hdr.cause = mbox->cmd.cause;
|
|
|
|
- api_hdr->hdr.diagn = mbox->cmd.diagn;
|
|
|
|
- api_hdr->hdr.length = mbox->cmd.length;
|
|
|
|
- api_hdr->hdr.result = mbox->cmd.result;
|
|
|
|
- api_hdr->hdr.lcn = mbox->cmd.lcn;
|
|
|
|
-
|
|
|
|
- if (mbox->cmd.length > 0){
|
|
|
|
- memcpy(api_hdr->data,mbox->data,mbox->cmd.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- skb->mac.raw = skb->data;
|
|
|
|
- skb->pkt_type = WAN_PACKET_ERR;
|
|
|
|
-
|
|
|
|
- if (chan->common.func(skb,dev,chan->common.sk) < 0){
|
|
|
|
- if (bh_enqueue(dev,skb)){
|
|
|
|
- printk(KERN_INFO "%s: Dropping OOB MSG\n",card->devname);
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: OOB MSG OK, %s, lcn %i\n",
|
|
|
|
- card->devname, dev->name, mbox->cmd.lcn);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * alloc_and_init_skb_buf
|
|
|
|
- *
|
|
|
|
- * Allocate and initialize an skb buffer.
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static int alloc_and_init_skb_buf (sdla_t *card, struct sk_buff **skb, int len)
|
|
|
|
-{
|
|
|
|
- struct sk_buff *new_skb = *skb;
|
|
|
|
-
|
|
|
|
- new_skb = dev_alloc_skb(len + X25_HRDHDR_SZ);
|
|
|
|
- if (new_skb == NULL){
|
|
|
|
- printk(KERN_INFO "%s: no socket buffers available!\n",
|
|
|
|
- card->devname);
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (skb_tailroom(new_skb) < len){
|
|
|
|
- /* No room for the packet. Call off the whole thing! */
|
|
|
|
- dev_kfree_skb_any(new_skb);
|
|
|
|
- printk(KERN_INFO "%s: Listen: unexpectedly long packet sequence\n"
|
|
|
|
- ,card->devname);
|
|
|
|
- *skb = NULL;
|
|
|
|
- return 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- *skb = new_skb;
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * api_oob_event
|
|
|
|
- *
|
|
|
|
- * Send an OOB event up to the sock
|
|
|
|
- *
|
|
|
|
- *===============================================================*/
|
|
|
|
-
|
|
|
|
-static void api_oob_event (sdla_t *card,TX25Mbox *mbox)
|
|
|
|
-{
|
|
|
|
- struct net_device *dev = find_channel(card, mbox->cmd.lcn);
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
-
|
|
|
|
- if (!dev)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- chan=dev->priv;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API)
|
|
|
|
- send_oob_msg(card,dev,mbox);
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-static int channel_disconnect(sdla_t* card, struct net_device *dev)
|
|
|
|
-{
|
|
|
|
-
|
|
|
|
- int err;
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
-
|
|
|
|
- DBG_PRINTK(KERN_INFO "%s: TIMER: %s, Device down disconnecting\n",
|
|
|
|
- card->devname,dev->name);
|
|
|
|
-
|
|
|
|
- if (chan->common.svc){
|
|
|
|
- err = x25_clear_call(card,chan->common.lcn,0,0);
|
|
|
|
- }else{
|
|
|
|
- /* If channel is PVC or LAPB HDLC, there is no call
|
|
|
|
- * to be cleared, thus drop down to the default
|
|
|
|
- * area
|
|
|
|
- */
|
|
|
|
- err = 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- switch (err){
|
|
|
|
-
|
|
|
|
- case X25RES_CHANNEL_IN_USE:
|
|
|
|
- case X25RES_NOT_READY:
|
|
|
|
- err = TRY_CMD_AGAIN;
|
|
|
|
- break;
|
|
|
|
- case CMD_OK:
|
|
|
|
- DBG_PRINTK(KERN_INFO "CALL CLEAR OK: Dev %s Chan Lcn %i\n",
|
|
|
|
- dev->name,chan->common.lcn);
|
|
|
|
-
|
|
|
|
- set_chan_state(dev,WAN_DISCONNECTING);
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
- err = DELAY_RESULT;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- /* If LAPB HDLC protocol, bring the whole link down
|
|
|
|
- * once the application terminates
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- set_chan_state(dev,WAN_DISCONNECTED);
|
|
|
|
-
|
|
|
|
- if (card->u.x.LAPB_hdlc){
|
|
|
|
- DBG_PRINTK(KERN_INFO "LAPB: Disconnecting Link\n");
|
|
|
|
- hdlc_link_down (card);
|
|
|
|
- }
|
|
|
|
- atomic_set(&chan->common.command,0);
|
|
|
|
- err = RETURN_RESULT;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return err;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static void hdlc_link_down (sdla_t *card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int retry = 5;
|
|
|
|
- int err=0;
|
|
|
|
-
|
|
|
|
- do {
|
|
|
|
- memset(mbox,0,sizeof(TX25Mbox));
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_DISC;
|
|
|
|
- mbox->cmd.length = 1;
|
|
|
|
- mbox->data[0]=0;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
-
|
|
|
|
- } while (err && retry-- && x25_error(card, err, X25_HDLC_LINK_DISC, 0));
|
|
|
|
-
|
|
|
|
- if (err)
|
|
|
|
- printk(KERN_INFO "%s: Hdlc Link Down Failed %x\n",card->devname,err);
|
|
|
|
-
|
|
|
|
- disconnect (card);
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int check_bad_command(sdla_t* card, struct net_device *dev)
|
|
|
|
-{
|
|
|
|
- x25_channel_t *chan = dev->priv;
|
|
|
|
- int bad_cmd = 0;
|
|
|
|
-
|
|
|
|
- switch (atomic_read(&chan->common.command)&0x7F){
|
|
|
|
-
|
|
|
|
- case X25_PLACE_CALL:
|
|
|
|
- if (chan->common.state != WAN_DISCONNECTED)
|
|
|
|
- bad_cmd=1;
|
|
|
|
- break;
|
|
|
|
- case X25_CLEAR_CALL:
|
|
|
|
- if (chan->common.state == WAN_DISCONNECTED)
|
|
|
|
- bad_cmd=1;
|
|
|
|
- break;
|
|
|
|
- case X25_ACCEPT_CALL:
|
|
|
|
- if (chan->common.state != WAN_CONNECTING)
|
|
|
|
- bad_cmd=1;
|
|
|
|
- break;
|
|
|
|
- case X25_RESET:
|
|
|
|
- if (chan->common.state != WAN_CONNECTED)
|
|
|
|
- bad_cmd=1;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- bad_cmd=1;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (bad_cmd){
|
|
|
|
- printk(KERN_INFO "%s: Invalid State, BAD Command %x, dev %s, lcn %i, st %i\n",
|
|
|
|
- card->devname,atomic_read(&chan->common.command),dev->name,
|
|
|
|
- chan->common.lcn, chan->common.state);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return bad_cmd;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*************************** XPIPEMON FUNCTIONS **************************/
|
|
|
|
-
|
|
|
|
-/*==============================================================================
|
|
|
|
- * Process UDP call of type XPIPE
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int process_udp_mgmt_pkt(sdla_t *card)
|
|
|
|
-{
|
|
|
|
- int c_retry = MAX_CMD_RETRY;
|
|
|
|
- unsigned int len;
|
|
|
|
- struct sk_buff *new_skb;
|
|
|
|
- TX25Mbox *mbox = card->mbox;
|
|
|
|
- int err;
|
|
|
|
- int udp_mgmt_req_valid = 1;
|
|
|
|
- struct net_device *dev;
|
|
|
|
- x25_channel_t *chan;
|
|
|
|
- unsigned short lcn;
|
|
|
|
- struct timeval tv;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- x25_udp_pkt_t *x25_udp_pkt;
|
|
|
|
- x25_udp_pkt = (x25_udp_pkt_t *)card->u.x.udp_pkt_data;
|
|
|
|
-
|
|
|
|
- dev = card->u.x.udp_dev;
|
|
|
|
- chan = dev->priv;
|
|
|
|
- lcn = chan->common.lcn;
|
|
|
|
-
|
|
|
|
- switch(x25_udp_pkt->cblock.command) {
|
|
|
|
-
|
|
|
|
- /* XPIPE_ENABLE_TRACE */
|
|
|
|
- case XPIPE_ENABLE_TRACING:
|
|
|
|
-
|
|
|
|
- /* XPIPE_GET_TRACE_INFO */
|
|
|
|
- case XPIPE_GET_TRACE_INFO:
|
|
|
|
-
|
|
|
|
- /* SET FT1 MODE */
|
|
|
|
- case XPIPE_SET_FT1_MODE:
|
|
|
|
-
|
|
|
|
- if(card->u.x.udp_pkt_src == UDP_PKT_FRM_NETWORK) {
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_direction_err;
|
|
|
|
- udp_mgmt_req_valid = 0;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* XPIPE_FT1_READ_STATUS */
|
|
|
|
- case XPIPE_FT1_READ_STATUS:
|
|
|
|
-
|
|
|
|
- /* FT1 MONITOR STATUS */
|
|
|
|
- case XPIPE_FT1_STATUS_CTRL:
|
|
|
|
- if(card->hw.fwid != SFID_X25_508) {
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_type_err;
|
|
|
|
- udp_mgmt_req_valid = 0;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- default:
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(!udp_mgmt_req_valid) {
|
|
|
|
- /* set length to 0 */
|
|
|
|
- x25_udp_pkt->cblock.length = 0;
|
|
|
|
- /* set return code */
|
|
|
|
- x25_udp_pkt->cblock.result = (card->hw.fwid != SFID_X25_508) ? 0x1F : 0xCD;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- switch (x25_udp_pkt->cblock.command) {
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- case XPIPE_FLUSH_DRIVER_STATS:
|
|
|
|
- init_x25_channel_struct(chan);
|
|
|
|
- init_global_statistics(card);
|
|
|
|
- mbox->cmd.length = 0;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- case XPIPE_DRIVER_STAT_IFSEND:
|
|
|
|
- memcpy(x25_udp_pkt->data, &chan->if_send_stat, sizeof(if_send_stat_t));
|
|
|
|
- mbox->cmd.length = sizeof(if_send_stat_t);
|
|
|
|
- x25_udp_pkt->cblock.length = mbox->cmd.length;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case XPIPE_DRIVER_STAT_INTR:
|
|
|
|
- memcpy(&x25_udp_pkt->data[0], &card->statistics, sizeof(global_stats_t));
|
|
|
|
- memcpy(&x25_udp_pkt->data[sizeof(global_stats_t)],
|
|
|
|
- &chan->rx_intr_stat, sizeof(rx_intr_stat_t));
|
|
|
|
-
|
|
|
|
- mbox->cmd.length = sizeof(global_stats_t) +
|
|
|
|
- sizeof(rx_intr_stat_t);
|
|
|
|
- x25_udp_pkt->cblock.length = mbox->cmd.length;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case XPIPE_DRIVER_STAT_GEN:
|
|
|
|
- memcpy(x25_udp_pkt->data,
|
|
|
|
- &chan->pipe_mgmt_stat.UDP_PIPE_mgmt_kmalloc_err,
|
|
|
|
- sizeof(pipe_mgmt_stat_t));
|
|
|
|
-
|
|
|
|
- memcpy(&x25_udp_pkt->data[sizeof(pipe_mgmt_stat_t)],
|
|
|
|
- &card->statistics, sizeof(global_stats_t));
|
|
|
|
-
|
|
|
|
- x25_udp_pkt->cblock.result = 0;
|
|
|
|
- x25_udp_pkt->cblock.length = sizeof(global_stats_t)+
|
|
|
|
- sizeof(rx_intr_stat_t);
|
|
|
|
- mbox->cmd.length = x25_udp_pkt->cblock.length;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- case XPIPE_ROUTER_UP_TIME:
|
|
|
|
- do_gettimeofday(&tv);
|
|
|
|
- chan->router_up_time = tv.tv_sec - chan->router_start_time;
|
|
|
|
- *(unsigned long *)&x25_udp_pkt->data = chan->router_up_time;
|
|
|
|
- x25_udp_pkt->cblock.length = mbox->cmd.length = 4;
|
|
|
|
- x25_udp_pkt->cblock.result = 0;
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- default :
|
|
|
|
-
|
|
|
|
- do {
|
|
|
|
- memcpy(&mbox->cmd, &x25_udp_pkt->cblock.command, sizeof(TX25Cmd));
|
|
|
|
- if(mbox->cmd.length){
|
|
|
|
- memcpy(&mbox->data,
|
|
|
|
- (char *)x25_udp_pkt->data,
|
|
|
|
- mbox->cmd.length);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- } while (err && c_retry-- && x25_error(card, err, mbox->cmd.command, 0));
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if ( err == CMD_OK ||
|
|
|
|
- (err == 1 &&
|
|
|
|
- (mbox->cmd.command == 0x06 ||
|
|
|
|
- mbox->cmd.command == 0x16) ) ){
|
|
|
|
-
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_cmnd_OK;
|
|
|
|
- } else {
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_cmnd_timeout;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* copy the result back to our buffer */
|
|
|
|
- memcpy(&x25_udp_pkt->cblock.command, &mbox->cmd, sizeof(TX25Cmd));
|
|
|
|
-
|
|
|
|
- if(mbox->cmd.length) {
|
|
|
|
- memcpy(&x25_udp_pkt->data, &mbox->data, mbox->cmd.length);
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- } //switch
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Fill UDP TTL */
|
|
|
|
-
|
|
|
|
- x25_udp_pkt->ip_pkt.ttl = card->wandev.ttl;
|
|
|
|
- len = reply_udp(card->u.x.udp_pkt_data, mbox->cmd.length);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if(card->u.x.udp_pkt_src == UDP_PKT_FRM_NETWORK) {
|
|
|
|
-
|
|
|
|
- err = x25_send(card, lcn, 0, len, card->u.x.udp_pkt_data);
|
|
|
|
- if (!err)
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_send_passed;
|
|
|
|
- else
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_adptr_send_failed;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
-
|
|
|
|
- /* Allocate socket buffer */
|
|
|
|
- if((new_skb = dev_alloc_skb(len)) != NULL) {
|
|
|
|
- void *buf;
|
|
|
|
-
|
|
|
|
- /* copy data into new_skb */
|
|
|
|
- buf = skb_put(new_skb, len);
|
|
|
|
- memcpy(buf, card->u.x.udp_pkt_data, len);
|
|
|
|
-
|
|
|
|
- /* Decapsulate packet and pass it up the protocol
|
|
|
|
- stack */
|
|
|
|
- new_skb->dev = dev;
|
|
|
|
-
|
|
|
|
- if (chan->common.usedby == API)
|
|
|
|
- new_skb->protocol = htons(X25_PROT);
|
|
|
|
- else
|
|
|
|
- new_skb->protocol = htons(ETH_P_IP);
|
|
|
|
-
|
|
|
|
- new_skb->mac.raw = new_skb->data;
|
|
|
|
-
|
|
|
|
- netif_rx(new_skb);
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_passed_to_stack;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
- ++chan->pipe_mgmt_stat.UDP_PIPE_mgmt_no_socket;
|
|
|
|
- printk(KERN_INFO
|
|
|
|
- "%s: UDP mgmt cmnd, no socket buffers available!\n",
|
|
|
|
- card->devname);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- card->u.x.udp_pkt_lgth = 0;
|
|
|
|
-
|
|
|
|
- return 1;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*==============================================================================
|
|
|
|
- * Determine what type of UDP call it is. DRVSTATS or XPIPE8ND ?
|
|
|
|
- */
|
|
|
|
-static int udp_pkt_type( struct sk_buff *skb, sdla_t* card )
|
|
|
|
-{
|
|
|
|
- x25_udp_pkt_t *x25_udp_pkt = (x25_udp_pkt_t *)skb->data;
|
|
|
|
-
|
|
|
|
- if((x25_udp_pkt->ip_pkt.protocol == UDPMGMT_UDP_PROTOCOL) &&
|
|
|
|
- (x25_udp_pkt->ip_pkt.ver_inet_hdr_length == 0x45) &&
|
|
|
|
- (x25_udp_pkt->udp_pkt.udp_dst_port == ntohs(card->wandev.udp_port)) &&
|
|
|
|
- (x25_udp_pkt->wp_mgmt.request_reply == UDPMGMT_REQUEST)) {
|
|
|
|
-
|
|
|
|
- if(!strncmp(x25_udp_pkt->wp_mgmt.signature,
|
|
|
|
- UDPMGMT_XPIPE_SIGNATURE, 8)){
|
|
|
|
- return UDP_XPIPE_TYPE;
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: UDP Packet, Failed Signature !\n",
|
|
|
|
- card->devname);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return UDP_INVALID_TYPE;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*============================================================================
|
|
|
|
- * Reply to UDP Management system.
|
|
|
|
- * Return nothing.
|
|
|
|
- */
|
|
|
|
-static int reply_udp( unsigned char *data, unsigned int mbox_len )
|
|
|
|
-{
|
|
|
|
- unsigned short len, udp_length, temp, ip_length;
|
|
|
|
- unsigned long ip_temp;
|
|
|
|
- int even_bound = 0;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- x25_udp_pkt_t *x25_udp_pkt = (x25_udp_pkt_t *)data;
|
|
|
|
-
|
|
|
|
- /* Set length of packet */
|
|
|
|
- len = sizeof(ip_pkt_t)+
|
|
|
|
- sizeof(udp_pkt_t)+
|
|
|
|
- sizeof(wp_mgmt_t)+
|
|
|
|
- sizeof(cblock_t)+
|
|
|
|
- mbox_len;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* fill in UDP reply */
|
|
|
|
- x25_udp_pkt->wp_mgmt.request_reply = UDPMGMT_REPLY;
|
|
|
|
-
|
|
|
|
- /* fill in UDP length */
|
|
|
|
- udp_length = sizeof(udp_pkt_t)+
|
|
|
|
- sizeof(wp_mgmt_t)+
|
|
|
|
- sizeof(cblock_t)+
|
|
|
|
- mbox_len;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* put it on an even boundary */
|
|
|
|
- if ( udp_length & 0x0001 ) {
|
|
|
|
- udp_length += 1;
|
|
|
|
- len += 1;
|
|
|
|
- even_bound = 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- temp = (udp_length<<8)|(udp_length>>8);
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_length = temp;
|
|
|
|
-
|
|
|
|
- /* swap UDP ports */
|
|
|
|
- temp = x25_udp_pkt->udp_pkt.udp_src_port;
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_src_port =
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_dst_port;
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_dst_port = temp;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* add UDP pseudo header */
|
|
|
|
- temp = 0x1100;
|
|
|
|
- *((unsigned short *)
|
|
|
|
- (x25_udp_pkt->data+mbox_len+even_bound)) = temp;
|
|
|
|
- temp = (udp_length<<8)|(udp_length>>8);
|
|
|
|
- *((unsigned short *)
|
|
|
|
- (x25_udp_pkt->data+mbox_len+even_bound+2)) = temp;
|
|
|
|
-
|
|
|
|
- /* calculate UDP checksum */
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_checksum = 0;
|
|
|
|
-
|
|
|
|
- x25_udp_pkt->udp_pkt.udp_checksum =
|
|
|
|
- calc_checksum(&data[UDP_OFFSET], udp_length+UDP_OFFSET);
|
|
|
|
-
|
|
|
|
- /* fill in IP length */
|
|
|
|
- ip_length = len;
|
|
|
|
- temp = (ip_length<<8)|(ip_length>>8);
|
|
|
|
- x25_udp_pkt->ip_pkt.total_length = temp;
|
|
|
|
-
|
|
|
|
- /* swap IP addresses */
|
|
|
|
- ip_temp = x25_udp_pkt->ip_pkt.ip_src_address;
|
|
|
|
- x25_udp_pkt->ip_pkt.ip_src_address =
|
|
|
|
- x25_udp_pkt->ip_pkt.ip_dst_address;
|
|
|
|
- x25_udp_pkt->ip_pkt.ip_dst_address = ip_temp;
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* fill in IP checksum */
|
|
|
|
- x25_udp_pkt->ip_pkt.hdr_checksum = 0;
|
|
|
|
- x25_udp_pkt->ip_pkt.hdr_checksum = calc_checksum(data, sizeof(ip_pkt_t));
|
|
|
|
-
|
|
|
|
- return len;
|
|
|
|
-} /* reply_udp */
|
|
|
|
-
|
|
|
|
-unsigned short calc_checksum (char *data, int len)
|
|
|
|
-{
|
|
|
|
- unsigned short temp;
|
|
|
|
- unsigned long sum=0;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- for( i = 0; i <len; i+=2 ) {
|
|
|
|
- memcpy(&temp,&data[i],2);
|
|
|
|
- sum += (unsigned long)temp;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- while (sum >> 16 ) {
|
|
|
|
- sum = (sum & 0xffffUL) + (sum >> 16);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- temp = (unsigned short)sum;
|
|
|
|
- temp = ~temp;
|
|
|
|
-
|
|
|
|
- if( temp == 0 )
|
|
|
|
- temp = 0xffff;
|
|
|
|
-
|
|
|
|
- return temp;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*=============================================================================
|
|
|
|
- * Store a UDP management packet for later processing.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
-static int store_udp_mgmt_pkt(int udp_type, char udp_pkt_src, sdla_t* card,
|
|
|
|
- struct net_device *dev, struct sk_buff *skb,
|
|
|
|
- int lcn)
|
|
|
|
-{
|
|
|
|
- int udp_pkt_stored = 0;
|
|
|
|
-
|
|
|
|
- if(!card->u.x.udp_pkt_lgth && (skb->len <= MAX_LGTH_UDP_MGNT_PKT)){
|
|
|
|
- card->u.x.udp_pkt_lgth = skb->len;
|
|
|
|
- card->u.x.udp_type = udp_type;
|
|
|
|
- card->u.x.udp_pkt_src = udp_pkt_src;
|
|
|
|
- card->u.x.udp_lcn = lcn;
|
|
|
|
- card->u.x.udp_dev = dev;
|
|
|
|
- memcpy(card->u.x.udp_pkt_data, skb->data, skb->len);
|
|
|
|
- card->u.x.timer_int_enabled |= TMR_INT_ENABLED_UDP_PKT;
|
|
|
|
- udp_pkt_stored = 1;
|
|
|
|
-
|
|
|
|
- }else{
|
|
|
|
- printk(KERN_INFO "%s: ERROR: UDP packet not stored for LCN %d\n",
|
|
|
|
- card->devname,lcn);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(udp_pkt_src == UDP_PKT_FRM_STACK){
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- }else{
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return(udp_pkt_stored);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*=============================================================================
|
|
|
|
- * Initial the ppp_private_area structure.
|
|
|
|
- */
|
|
|
|
-static void init_x25_channel_struct( x25_channel_t *chan )
|
|
|
|
-{
|
|
|
|
- memset(&chan->if_send_stat.if_send_entry,0,sizeof(if_send_stat_t));
|
|
|
|
- memset(&chan->rx_intr_stat.rx_intr_no_socket,0,sizeof(rx_intr_stat_t));
|
|
|
|
- memset(&chan->pipe_mgmt_stat.UDP_PIPE_mgmt_kmalloc_err,0,sizeof(pipe_mgmt_stat_t));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*============================================================================
|
|
|
|
- * Initialize Global Statistics
|
|
|
|
- */
|
|
|
|
-static void init_global_statistics( sdla_t *card )
|
|
|
|
-{
|
|
|
|
- memset(&card->statistics.isr_entry,0,sizeof(global_stats_t));
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * SMP Support
|
|
|
|
- * ==============================================================*/
|
|
|
|
-
|
|
|
|
-static void S508_S514_lock(sdla_t *card, unsigned long *smp_flags)
|
|
|
|
-{
|
|
|
|
- spin_lock_irqsave(&card->wandev.lock, *smp_flags);
|
|
|
|
-}
|
|
|
|
-static void S508_S514_unlock(sdla_t *card, unsigned long *smp_flags)
|
|
|
|
-{
|
|
|
|
- spin_unlock_irqrestore(&card->wandev.lock, *smp_flags);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/*===============================================================
|
|
|
|
- * x25_timer_routine
|
|
|
|
- *
|
|
|
|
- * A more efficient polling routine. Each half a second
|
|
|
|
- * queue a polling task. We want to do the polling in a
|
|
|
|
- * task not timer, because timer runs in interrupt time.
|
|
|
|
- *
|
|
|
|
- * FIXME Polling should be rethinked.
|
|
|
|
- *==============================================================*/
|
|
|
|
-
|
|
|
|
-static void x25_timer_routine(unsigned long data)
|
|
|
|
-{
|
|
|
|
- sdla_t *card = (sdla_t*)data;
|
|
|
|
-
|
|
|
|
- if (!card->wandev.dev){
|
|
|
|
- printk(KERN_INFO "%s: Stopping the X25 Poll Timer: No Dev.\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (card->open_cnt != card->u.x.num_of_ch){
|
|
|
|
- printk(KERN_INFO "%s: Stopping the X25 Poll Timer: Interface down.\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (test_bit(PERI_CRIT,&card->wandev.critical)){
|
|
|
|
- printk(KERN_INFO "%s: Stopping the X25 Poll Timer: Shutting down.\n",
|
|
|
|
- card->devname);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!test_and_set_bit(POLL_CRIT,&card->wandev.critical)){
|
|
|
|
- trigger_x25_poll(card);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- card->u.x.x25_timer.expires=jiffies+(HZ>>1);
|
|
|
|
- add_timer(&card->u.x.x25_timer);
|
|
|
|
- return;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void disable_comm_shutdown(sdla_t *card)
|
|
|
|
-{
|
|
|
|
- TX25Mbox* mbox = card->mbox;
|
|
|
|
- int err;
|
|
|
|
-
|
|
|
|
- /* Turn of interrutps */
|
|
|
|
- mbox->data[0] = 0;
|
|
|
|
- if (card->hw.fwid == SFID_X25_508){
|
|
|
|
- mbox->data[1] = card->hw.irq;
|
|
|
|
- mbox->data[2] = 2;
|
|
|
|
- mbox->cmd.length = 3;
|
|
|
|
- }else {
|
|
|
|
- mbox->cmd.length = 1;
|
|
|
|
- }
|
|
|
|
- mbox->cmd.command = X25_SET_INTERRUPT_MODE;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err)
|
|
|
|
- printk(KERN_INFO "INTERRUPT OFF FAIED %x\n",err);
|
|
|
|
-
|
|
|
|
- /* Bring down HDLC */
|
|
|
|
- mbox->cmd.command = X25_HDLC_LINK_CLOSE;
|
|
|
|
- mbox->cmd.length = 0;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err)
|
|
|
|
- printk(KERN_INFO "LINK CLOSED FAILED %x\n",err);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- /* Brind down DTR */
|
|
|
|
- mbox->data[0] = 0;
|
|
|
|
- mbox->data[2] = 0;
|
|
|
|
- mbox->data[1] = 0x01;
|
|
|
|
- mbox->cmd.length = 3;
|
|
|
|
- mbox->cmd.command = X25_SET_GLOBAL_VARS;
|
|
|
|
- err = sdla_exec(mbox) ? mbox->cmd.result : CMD_TIMEOUT;
|
|
|
|
- if (err)
|
|
|
|
- printk(KERN_INFO "DTR DOWN FAILED %x\n",err);
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-MODULE_LICENSE("GPL");
|
|
|
|
-
|
|
|
|
-/****** End *****************************************************************/
|
|
|