--- linux-2.6.22.14/drivers/net/wan/farsync.c 2008-01-22 14:02:50.000000000 +0200 +++ linux/drivers/net/wan/farsync.c 2008-01-15 11:03:50.000000000 +0200 @@ -3,7 +3,7 @@ * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * - * Copyright (C) 2001-2004 FarSite Communications Ltd. + * Copyright (C) 2001-2002 FarSite Communications Ltd. * www.farsite.co.uk * * This program is free software; you can redistribute it and/or @@ -14,35 +14,126 @@ * Author: R.J.Dunlop * Maintainer: Kevin Curtis */ +/* Revision History + * $Log: farsync.c,v $ + * Revision 1.3 2004/02/03 10:21:20 kevinc + * Added support for idlecode + * + * Revision 1.2 2003/12/17 14:21:03 kevinc + * Removed 4 card limit + * Tidied up TE1 debug + * + * Revision 1.1 2003/11/14 16:50:47 kevinc + * New module for Kernel 2.6 + * + */ -#include #include +#include #include +#include +#include #include +#include #include #include +#include #include #include -#include -#include +#include +#include +/* TTY includes */ +#include +#include +#include #include "farsync.h" +#define FST_PORT_NAME "hdlc" +#define FST_TTY_CHAR 1 +#define IS_GEN_HDLC 1 + +/* Number of ports (per card) and cards supported + */ +#define FST_MAX_PORTS 4 +#define FST_MAX_CARDS 32 + /* * Module info */ MODULE_AUTHOR("R.J.Dunlop "); MODULE_DESCRIPTION("FarSync T-Series WAN driver. FarSite Communications Ltd."); +/* + * Modules parameters and associated varaibles + */ +#define FST_HIGH_WATER_MARK 12 /* Point at which we flow control + * network layer */ +#define FST_LOW_WATER_MARK 8 /* Point at which we remove flow + * control from network layer */ + +int fst_txq_low=FST_LOW_WATER_MARK; +int fst_txq_high=FST_HIGH_WATER_MARK; +int fst_max_reads=7; +int fst_excluded_cards=0; +int fst_excluded_list[FST_MAX_CARDS]; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) +MODULE_PARM (fst_txq_low, "i"); +MODULE_PARM (fst_txq_high, "i"); +MODULE_PARM (fst_max_reads, "i"); +MODULE_PARM (fst_excluded_cards, "i"); +MODULE_PARM (fst_excluded_list, "0-32i"); +#else +module_param (fst_txq_low,int,S_IRUGO); +module_param (fst_txq_high,int,S_IRUGO); +module_param (fst_max_reads,int,S_IRUGO); +module_param (fst_excluded_cards, int, S_IRUGO); +module_param_array (fst_excluded_list, int, NULL, S_IRUGO); +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,9) MODULE_LICENSE("GPL"); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +EXPORT_NO_SYMBOLS; +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,9) +/* + * min()/max() macros that also do + * strict type-checking.. See the + * "unnecessary" pointer comparison. + */ +#define min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) +#endif + /* Driver configuration and global parameters * ========================================== */ -/* Number of ports (per card) and cards supported +/* PCI vendor and device IDs */ -#define FST_MAX_PORTS 4 -#define FST_MAX_CARDS 32 +#define FSC_PCI_VENDOR_ID 0x1619 /* FarSite Communications Ltd */ +#define T2P_PCI_DEVICE_ID 0x0400 /* T2P X21 2 port card */ +#define T4P_PCI_DEVICE_ID 0x0440 /* T4P X21 4 port card */ +#define T1U_PCI_DEVICE_ID 0x0610 /* T1U X21 1 port card */ +#define T2U_PCI_DEVICE_ID 0x0620 /* T2U X21 2 port card */ +#define T4U_PCI_DEVICE_ID 0x0640 /* T4U X21 4 port card */ +#define TE1_PCI_DEVICE_ID 0x1610 /* TE1 X21 1 port card */ +#define TE1C_PCI_DEVICE_ID 0x1612 /* TE1 X21 1 port channelised */ +#define DSL_S1_PCI_DEVICE_ID 0x2610 /* DSL-S1 */ +#define T4E_PCI_DEVICE_ID 0x3640 /* T4E Mk II */ /* Default parameters for the link */ @@ -56,14 +147,14 @@ MODULE_LICENSE("GPL"); * so that we can keep the card busy * and maximise throughput */ -#define FST_HIGH_WATER_MARK 12 /* Point at which we flow control - * network layer */ -#define FST_LOW_WATER_MARK 8 /* Point at which we remove flow - * control from network layer */ -#define FST_MAX_MTU 8000 /* Huge but possible */ +#define FST_MAX_MTU 32*1024 /* Limitation of hdlc controller on + * card */ #define FST_DEF_MTU 1500 /* Common sane value */ - +#define FST_MAX_ATM_MTU (32*53) /* 1500 bytes should fit in 32 cells */ +#define MAX_PPP_HEADER 10 /* Allowance for PPP overhead when + allocating skbs or checking lengths*/ #define FST_TX_TIMEOUT (2*HZ) +#define FST_MAX_SEGMENTS FST_MAX_MTU/128 /* Maximum tx/rx desriptors per frame */ #ifdef ARPHRD_RAWHDLC #define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */ @@ -71,25 +162,14 @@ MODULE_LICENSE("GPL"); #define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */ #endif -/* - * Modules parameters and associated varaibles - */ -static int fst_txq_low = FST_LOW_WATER_MARK; -static int fst_txq_high = FST_HIGH_WATER_MARK; -static int fst_max_reads = 7; -static int fst_excluded_cards = 0; -static int fst_excluded_list[FST_MAX_CARDS]; - -module_param(fst_txq_low, int, 0); -module_param(fst_txq_high, int, 0); -module_param(fst_max_reads, int, 0); -module_param(fst_excluded_cards, int, 0); -module_param_array(fst_excluded_list, int, NULL, 0); + +static int tx_re_entry=0; + /* Card shared memory layout * ========================= */ -#pragma pack(1) +#pragma pack(2) /* This information is derived in part from the FarSite FarSync Smc.h * file. Unfortunately various name clashes and the non-portability of the @@ -101,27 +181,35 @@ module_param_array(fst_excluded_list, in * be used to check that we have not got out of step with the firmware * contained in the .CDE files. */ -#define SMC_VERSION 24 +#define SMC_VERSION 47 #define FST_MEMSIZE 0x100000 /* Size of card memory (1Mb) */ #define SMC_BASE 0x00002000L /* Base offset of the shared memory window main * configuration structure */ -#define BFM_BASE 0x00010000L /* Base offset of the shared memory window DMA +#define BFM_BASE 0x00020000L /* Base offset of the shared memory window DMA * buffers */ +#define AW_BASE 0x000a0000L /* Base offset of the shared memory window async + * context structure */ +#define DT_BASE 0x000afff8L /* DSL Transmit Fifo shared memory base */ +#define DR_BASE 0x000cfff8L /* DSL Receive FIFO shared memory base */ -#define LEN_TX_BUFFER 8192 /* Size of packet buffers */ -#define LEN_RX_BUFFER 8192 +#define MAX_LEN_TX_BUFFER 32*1024 /* Max Size of packet buffers */ +#define MAX_LEN_RX_BUFFER 32*1024 #define LEN_SMALL_TX_BUFFER 256 /* Size of obsolete buffs used for DOS diags */ #define LEN_SMALL_RX_BUFFER 256 -#define NUM_TX_BUFFER 2 /* Must be power of 2. Fixed by firmware */ -#define NUM_RX_BUFFER 8 +#define NUM_SMALL_TX_BUFFER 2 /* Must be power of 2. Fixed by firmware */ +#define NUM_SMALL_RX_BUFFER 8 + +#define MAX_TX_BUFFER 128 /* Must be power of 2. */ +#define MAX_RX_BUFFER 128 /* configurable to this value */ /* Interrupt retry time in milliseconds */ #define INT_RETRY_TIME 2 + /* The Am186CH/CC processors support a SmartDMA mode using circular pools * of buffer descriptors. The structure is almost identical to that used * in the LANCE Ethernet controllers. Details available as PDF from the @@ -173,6 +261,7 @@ struct rxdesc { /* Receive descriptor #define RX_STP 0x02 /* Rx: start of packet */ #define RX_ENP 0x01 /* Rx: end of packet */ + /* Interrupts from the card are caused by various events which are presented * in a circular buffer as several events may be processed on one physical int */ @@ -187,6 +276,14 @@ struct cirbuff { /* Interrupt event codes. * Where appropriate the two low order bits indicate the port number */ +#define TXA_CMPL 0x10 /* Transmit complete events */ +#define TXB_CMPL 0x11 +#define TXC_CMPL 0x12 +#define TXD_CMPL 0x13 +#define RXA_CMPL 0x14 +#define RXB_CMPL 0x15 +#define RXC_CMPL 0x16 +#define RXD_CMPL 0x17 #define CTLA_CHG 0x18 /* Control signal changed */ #define CTLB_CHG 0x19 #define CTLC_CHG 0x1A @@ -209,6 +306,17 @@ struct cirbuff { #define M32_INT 0x2D #define TE1_ALMA 0x30 +#define DSL_FAIL 0x34 +#define DSL_ACST 0x38 +#define DSL_SENT 0x3c +#define DSL_RCVD 0x40 +#define DSL_LINK 0x44 + +#define TXA_TBUA 0x48 +#define TXA_TBUB 0x49 +#define TXA_TBUC 0x4A +#define TXA_TBUD 0x4B + /* Port physical configuration. See farsync.h for field values */ struct port_cfg { @@ -217,46 +325,140 @@ struct port_cfg { u8 internalClock; /* 1 => internal clock, 0 => external */ u8 transparentMode; /* 1 => on, 0 => off */ u8 invertClock; /* 0 => normal, 1 => inverted */ - u8 padBytes[6]; /* Padding */ + u8 txRxStart; /* 0 = start tx and rx on open */ + /* 1 = start tx only */ + /* 2 = start rx only */ + u8 numTxBuffers; /* Number of Tx Buffers to use, max 128 */ + u8 numRxBuffers; /* Number of Rx Buffers to use, max 128 */ + u8 clockSource; /* FS_CLOCK_REFERENCE_xxx */ + u8 padBytes[2]; /* Padding to 16 bytes */ u32 lineSpeed; /* Speed in bps */ + /* + * Extras for T4E Mk II + */ + u8 extendedClocking; /* Enabling flag */ + u8 internalTxClcok; /* only if extendedClocking is TRUE */ + u8 internalRxClock; /* only if extendedClocking is TRUE */ + u8 terminalTxClock; /* only if extendedClocking is TRUE */ + u8 terminalRxClock; /* only if extendedClocking is TRUE */ + u8 dcdOutput; /* TRUE => v24opsts.dcd will apply */ + u8 transmitMSBFirst; /* default (FALSE) tx lsb first */ + /* tx MSB first is normally only used in */ + /* (some) transparent applications */ + u8 receiveMSBFirst; /* default (FALSE) rx lsb first */ + /* rx MSB first is normally only used in */ + /* (some) transparent applications */ + u32 estimatedLineSpeed; /* applies only is linespeed is 0 */ + u8 morePadBytes[4]; /* adjust to keep structure 32 bytes */ +}; + +struct dsl_config +{ + u32 dataRate; // data rate in bps + u8 terminalType; // central or remote + u8 annexType; // A (US) or B (EU) + u8 testMode; // Various loop modes + u8 backoff; // Power backoff in dB + u8 bLineProbingEnable; // set TRUE to probe line + u8 snrth; // Signal to Noise Ratio Threshold in dB + // SNR Margin Defect when signalQuality + // falls below this value + u8 lpath; // Loop Attenuation Threshold in dB + // LA Defect when LineLoss exceeds this + // value + u8 spare[21]; // adjust to keep structure size 32 bytes +}; + +struct dsl_status // some signed, some unsigned TBA +{ + u8 activationStatus; + u8 noCommonModeStatus; + u8 transceiverStatus1; // + u8 transceiverStatus2; // + u8 lineLoss; // dB + char signalQuality; // + u8 nearEndBlockErrorCount; // wraps after 0xff, s/w can rst + char signalToNoiseRatio; // dB + u8 codeViolationCount; // + u8 erroredSecondCount; // | + u8 severelyErroredSecondCount; // > reset after firmware download + u8 lossOfSyncWordSecondCount; // | + u8 unavailableSecondCount; // + char frequencyDeviation; + char negotiatedPowerBackOff; + u8 negotiatedPSD; + u8 negotiatedBChannels; + u8 negotiatedZBits; + u16 negotiatedSyncWord; + u8 negotiatedStuffBits; + u8 chipVersion; + u8 firmwareVersion; + u8 romVersion; + u16 atmTxCellCount; + u16 atmRxCellCount; + u16 atmHecErrorCount; + u8 bLinkUp; // TRUE => link is up and ready to pass data + // FALSE => link is down + u8 xpldVersion; // 8-bit XPLD version number + u8 farEndCountryCode[2]; // 2-byte T.35 Country Code + u8 farEndProviderCode[4]; // 4-byte Provider code (ascii) + u8 farEndVendorInfo[2]; // 2-byte Vendor Information + u8 spare[24]; // adjust structure to 32 bytes +}; + +struct dsl_control +{ + u32 offsetAtmRx; // + u32 offsetAtmTx; // + u16 bytesToSend; // set by writer, reset by reader + u8 spare[6]; // adjuest to keep struct size 16 bytes }; /* TE1 port physical configuration */ struct su_config { - u32 dataRate; - u8 clocking; - u8 framing; - u8 structure; - u8 interface; - u8 coding; - u8 lineBuildOut; - u8 equalizer; - u8 transparentMode; - u8 loopMode; - u8 range; - u8 txBufferMode; - u8 rxBufferMode; - u8 startingSlot; - u8 losThreshold; - u8 enableIdleCode; - u8 idleCode; - u8 spare[44]; + u32 dataRate; // data rate in bps + u8 clocking; // master or slave + u8 framing; // E1, T1 or J1 + u8 structure; // + u8 interface; // RJ48C or BNC + u8 coding; // HDB3 or B8ZS + u8 lineBuildOut; // + u8 equalizer; // short on long haul settings + u8 transparentMode; // hdlc (0) or transparent (1) + u8 loopMode; // + u8 range; // + u8 txBufferMode; // 0, 96 bits 1 frame or 2 frames + u8 rxBufferMode; // 0, 96 bits 1 frame or 2 frames + u8 startingSlot; // E1 1-31; T1/J1 0-23 + u8 losThreshold; // 0-7 + u8 enableIdleCode; // 0 disabled, 1 enabled + u8 idleCode; // 0x00 to 0xff + u8 spare[44]; // Keep structure 64 bytes }; /* TE1 Status */ struct su_status { - u32 receiveBufferDelay; - u32 framingErrorCount; - u32 codeViolationCount; - u32 crcErrorCount; - u32 lineAttenuation; + u32 receiveBufferDelay; // delay trhough rx buffer (o-63) + u32 framingErrorCount; // count of framing errors + u32 codeViolationCount; // count of code vilations + u32 crcErrorCount; // count of CRC errors + u32 lineAttenuation; // in dB u8 portStarted; - u8 lossOfSignal; - u8 receiveRemoteAlarm; - u8 alarmIndicationSignal; - u8 spare[40]; + u8 lossOfSignal; // LOS alarm + u8 receiveRemoteAlarm; // RRA alarm + u8 alarmIndicationSignal; // AIS alarm + u8 spare[40]; // keep structure 64 bytes }; +typedef struct fst_fifo { + u16 fifo_length; + u16 read_idx; + u16 write_idx; + short overflow_idx; + u8 data[4]; +} FIFO, *PFIFO; + + /* Finally sling all the above together into the shared memory structure. * Sorry it's a hodge podge of arrays, structures and unused bits, it's been * evolving under NT for some time so I guess we're stuck with it. @@ -265,12 +467,12 @@ struct su_status { */ struct fst_shared { /* DMA descriptor rings */ - struct rxdesc rxDescrRing[FST_MAX_PORTS][NUM_RX_BUFFER]; - struct txdesc txDescrRing[FST_MAX_PORTS][NUM_TX_BUFFER]; + struct rxdesc rxDescrRing[FST_MAX_PORTS][MAX_RX_BUFFER]; + struct txdesc txDescrRing[FST_MAX_PORTS][MAX_TX_BUFFER]; /* Obsolete small buffers */ - u8 smallRxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_SMALL_RX_BUFFER]; - u8 smallTxBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_SMALL_TX_BUFFER]; + u8 smallRxBuffer[FST_MAX_PORTS][NUM_SMALL_RX_BUFFER][LEN_SMALL_RX_BUFFER]; + u8 smallTxBuffer[FST_MAX_PORTS][NUM_SMALL_TX_BUFFER][LEN_SMALL_TX_BUFFER]; u8 taskStatus; /* 0x00 => initialising, 0x01 => running, * 0xFF => halted @@ -286,6 +488,9 @@ struct fst_shared { * version, RR = revision and BB = build */ + u8 asyncAbility[FST_MAX_PORTS]; /* TRUE => async and sync */ + /* FALSE => sync only */ + u8 synthAbility; /* TRUE => synthesizer present */ u16 txa_done; /* Obsolete completion flags */ u16 rxa_done; u16 txb_done; @@ -337,6 +542,8 @@ struct fst_shared { u32 ctsTimerRun[FST_MAX_PORTS]; u32 dcdTimer[FST_MAX_PORTS]; u32 dcdTimerRun[FST_MAX_PORTS]; + u32 riTimer[FST_MAX_PORTS]; + u32 riTimerRun[FST_MAX_PORTS]; u32 numberOfPorts; /* Number of ports detected at startup */ @@ -348,8 +555,43 @@ struct fst_shared { u16 portScheduleOffset; - struct su_config suConfig; /* TE1 Bits */ - struct su_status suStatus; + struct su_config suConfig; /* TE1 Config structure */ + struct su_status suStatus; /* TE1 Stats structure */ + struct dsl_config dslConfig; /* DSL Config structure */ + struct dsl_status dslStatus; /* DSL Stats structure */ + struct dsl_control dslControl; /* DSL FiFos etc */ + +#define NOTIFICATION_FIFO_LEN 64 + char CardNotifications[NOTIFICATION_FIFO_LEN+sizeof(FIFO)]; + + // The following configuration values are processed by the CDE when + // it receives a CMD_FIFO_CONFIG_CT_BUS request + // This request can be issued at anytime regardless of whether + // any ports are opened or not T4E MkII + u32 CTBusPrimaryMode; // e.g. FS_CT_BUS_MODE_xxx + u32 CTBusPrimaryFeed; // e.g. FS_CLOCK_REFERENCE_xxx + u32 CTBusBackupMode; // e.g. FS_CT_BUS_MODE_xxx + u32 CTBusBackupFeed; // e.g. FS_CLOCK_REFERENCE_xxx + u8 ctaClockPresent; // 1 => CTA present, 0 => CTA absent + u8 bPrimaryFallback; // 1 => Primary to Backup fallback allowed + u8 bBackupFallback; // 1 => Backup to Local Oscillator fallback allowed + u8 bPrimaryClockStatus; // 0 = out-of-tolerance, 1 = good + u8 bBackupClockStatus; // 0 = out-of-tolerance, 1 = good + u8 bCTAStatus; // 0 = out-of-tolerance, 1 = good + u8 bCTBStatus; // 0 = out-of-tolerance, 1 = good + + u16 currentStatusSummary; // Bit-mapped field indicating: + // D15-D9 n.u, default 0's + // D8 Slave/Master, 0=SLAVE, 1=MASTER + // D7 CTA/CTB, 0=A, 1=B + // D6-D4 A/B/C/D/LO (Master only) + // 000=A, 001=B, 010=C, 011=D, 100=LO + // D3-D0 Port A-D ClockSource CT/LO, + // 0=CT, 1=LO + + u8 fill[2]; // Temporary to fix an alignment issue + + u32 uCurrentConfigReference;// e.g. FS_CONFIG_IN_USE_xxx u32 endOfSmcSignature; /* endOfSmcSignature MUST be the last member of * the structure and marks the end of shared @@ -361,6 +603,7 @@ struct fst_shared { /* endOfSmcSignature value */ #define END_SIG 0x12345678 + /* Mailbox values. (portMailbox) */ #define NOP 0 /* No operation */ #define ACK 1 /* Positive acknowledgement to PC driver */ @@ -373,7 +616,8 @@ struct fst_shared { /* PLX Chip Register Offsets */ #define CNTRL_9052 0x50 /* Control Register */ #define CNTRL_9054 0x6c /* Control Register */ - +#define PCIILR 0x3c /* Interrupt Line Register */ +#define PCICR 0x04 /* Interrupt Control Register */ #define INTCSR_9052 0x4c /* Interrupt control/status register */ #define INTCSR_9054 0x68 /* Interrupt control/status register */ @@ -406,46 +650,245 @@ struct fst_shared { #define FST_CARD_INT 0x04 /* Larger buffers are positioned in memory at offset BFM_BASE */ +#define TX_BUFFER_SPACE 0x10000 +#define RX_BUFFER_SPACE 0x10000 +#define REQUIRED_TX_BUFFERS 8 +#define REQUIRED_RX_BUFFERS 8 +#define REQUIRED_TX_BUFFER_SIZE 8*1024 +#define REQUIRED_RX_BUFFER_SIZE 8*1024 struct buf_window { - u8 txBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_TX_BUFFER]; - u8 rxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_RX_BUFFER]; + u8 txBuffer[FST_MAX_PORTS][TX_BUFFER_SPACE]; + u8 rxBuffer[FST_MAX_PORTS][RX_BUFFER_SPACE]; }; /* Calculate offset of a buffer object within the shared memory window */ -#define BUF_OFFSET(X) (BFM_BASE + offsetof(struct buf_window, X)) +#define BUF_OFFSET(X) ((unsigned int)&(((struct buf_window *)BFM_BASE)->X)) + +#pragma pack() + +/* + * Shared memory window access macros + * + * We have a nice memory based structure above, which could be directly + * mapped on i386 but might not work on other architectures unless we use + * the readb,w,l and writeb,w,l macros. Unfortunately these macros take + * physical offsets so we have to convert. The only saving grace is that + * this should all collapse back to a simple indirection eventually. + */ +#define WIN_OFFSET(X) ((unsigned int)&(((struct fst_shared *)SMC_BASE)->X)) + +#define FST_RDB(C,E) readb ((C)->mem + WIN_OFFSET(E)) +#define FST_RDW(C,E) readw ((C)->mem + WIN_OFFSET(E)) +#define FST_RDL(C,E) readl ((C)->mem + WIN_OFFSET(E)) + +#define FST_WRB(C,E,B) writeb ((B), (C)->mem + WIN_OFFSET(E)) +#define FST_WRW(C,E,W) writew ((W), (C)->mem + WIN_OFFSET(E)) +#define FST_WRL(C,E,L) writel ((L), (C)->mem + WIN_OFFSET(E)) + + +/* + * Async memory window definition + */ +#define FST_CMD_RECONFIG 0x0001 +#define FST_CMD_FIFO_FLUSH_TX 0x0002 +#define FST_CMD_FIFO_BREAK_ON 0x0004 +#define FST_CMD_FIFO_BREAK_OFF 0x0008 +#define FST_CMD_FIFO_XON 0x0010 +#define FST_CMD_FIFO_XOFF 0x0020 +#define FST_CMD_FIFO_RESET_TS 0x0040 + +#define FST_CMD_FIFO_LEN 16 +#define FST_TX_DATA_FIFO_LEN 1024 +#define FST_RX_DATA_FIFO_LEN (FST_TX_DATA_FIFO_LEN * sizeof(ASYNC_RX_EVENT)) +#define FST_MSG_FIFO_LEN 64 +#define FST_RSP_FIFO_LEN 64 + +#pragma pack(2) +typedef struct async_rx_event { + u8 status_byte; + u8 character; + unsigned long timestamp; +} ASYNC_RX_EVENT, *PASYNC_RX_EVENT; + +typedef struct fst_async_config { + u8 flow_control; + u8 stop_bits; + u8 parity; + u8 word_length; + u8 xon_char; + u8 xoff_char; + u16 rx_fifo_size; +} ASYNC_CONFIG, *PASYNC_CONFIG; + +struct fst_async_window { + u8 async_mode[FST_MAX_PORTS]; + ASYNC_CONFIG async_config[FST_MAX_PORTS]; + char cmd_fifo[FST_MAX_PORTS] [FST_CMD_FIFO_LEN + sizeof(FIFO)]; + char tx_fifo[FST_MAX_PORTS] [FST_TX_DATA_FIFO_LEN + sizeof(FIFO)]; + char rx_fifo[FST_MAX_PORTS] [FST_RX_DATA_FIFO_LEN + sizeof(FIFO)]; + char msg_fifo[FST_MSG_FIFO_LEN + sizeof(FIFO)]; + char rsp_fifo[FST_RSP_FIFO_LEN + sizeof(FIFO)]; +}; +#pragma pack() + +/* + * Async memory window access macros + * + * We have a nice memory based structure above, which could be directly + * mapped on i386 but might not work on other architectures unless we use + * the readb,w,l and writeb,w,l macros. Unfortunately these macros take + * physical offsets so we have to convert. The only saving grace is that + * this should all collapse back to a simple indirection eventually. + */ +#define ASY_OFFSET(X) ((unsigned int)&(((struct fst_async_window *)AW_BASE)->X)) + +#define FST_A_RDB(C,E) readb ((C)->mem + ASY_OFFSET(E)) +#define FST_A_RDW(C,E) readw ((C)->mem + ASY_OFFSET(E)) +#define FST_A_RDL(C,E) readl ((C)->mem + ASY_OFFSET(E)) + +#define FST_A_WRB(C,E,B) writeb ((B), (C)->mem + ASY_OFFSET(E)) +#define FST_A_WRW(C,E,W) writew ((W), (C)->mem + ASY_OFFSET(E)) +#define FST_A_WRL(C,E,L) writel ((L), (C)->mem + ASY_OFFSET(E)) + +/* + * DSL Fifo's + */ +#define MAX_DSL_TX_BUFFER 32*1024 +#define MAX_DSL_RX_BUFFER 32*1024 + +#pragma pack(2) + +struct dsl_tx_fifo { + char tx_fifo[MAX_DSL_TX_BUFFER + sizeof(FIFO)]; +}; + +struct dsl_rx_fifo { + char rx_fifo[MAX_DSL_RX_BUFFER + sizeof(FIFO)]; +}; #pragma pack() +/* + * DSL memory window access macros + * + * We have a nice memory based structure above, which could be directly + * mapped on i386 but might not work on other architectures unless we use + * the readb,w,l and writeb,w,l macros. Unfortunately these macros take + * physical offsets so we have to convert. The only saving grace is that + * this should all collapse back to a simple indirection eventually. + */ +#define DT_OFFSET(X) ((unsigned int)&(((struct dsl_tx_fifo *)DT_BASE)->X)) +#define DR_OFFSET(X) ((unsigned int)&(((struct dsl_rx_fifo *)DR_BASE)->X)) + +#define FIFO_LENGTH 0 +#define READ_IDX 2 +#define WRITE_IDX 4 +#define OVERFLOW_IDX 6 + +/* + * Transmit Fifo macros + */ +#define FST_DT_RDB(C,E) readb ((C)->mem + DT_OFFSET(E)) +#define FST_DT_RDW(C,E) readw ((C)->mem + DT_OFFSET(E)) +#define FST_DT_RDL(C,E) readl ((C)->mem + DT_OFFSET(E)) + +#define FST_DT_WRB(C,E,B) writeb ((B), (C)->mem + DT_OFFSET(E)) +#define FST_DT_WRW(C,E,W) writew ((W), (C)->mem + DT_OFFSET(E)) +#define FST_DT_WRL(C,E,L) writel ((L), (C)->mem + DT_OFFSET(E)) + +/* + * Receive Fifo macros + */ +#define FST_DR_RDB(C,E) readb ((C)->mem + DR_OFFSET(E)) +#define FST_DR_RDW(C,E) readw ((C)->mem + DR_OFFSET(E)) +#define FST_DR_RDL(C,E) readl ((C)->mem + DR_OFFSET(E)) + +#define FST_DR_WRB(C,E,B) writeb ((B), (C)->mem + DR_OFFSET(E)) +#define FST_DR_WRW(C,E,W) writew ((W), (C)->mem + DR_OFFSET(E)) +#define FST_DR_WRL(C,E,L) writel ((L), (C)->mem + DR_OFFSET(E)) + /* Device driver private information * ================================= */ + +/* + * A structure to queue received frame at the new char interface + */ +struct fst_char_rx_frame { + struct sk_buff *frame; + struct fst_char_rx_frame *next; + struct fst_char_rx_frame *prev; +}; + +/* + * A multi-part tx/rx queue structure + * For long frame support a frame can be upto 32*1024 - 2 bytes in length + * This would occupy 4 tx/rx desriptors (each of 8k) + */ +struct fst_queue_info { + int count; /* Total size of frame */ + int segment_cnt; /* Number of descriptors + * required/used */ + int current_seg; /* Which segment we are currently doing */ + unsigned char flags; /* Start and End frame flags */ + unsigned char error_recovery; + struct sk_buff *frame; /* The complete skb */ +}; + + /* Per port (line or channel) information */ struct fst_port_info { - struct net_device *dev; /* Device struct - must be first */ + struct net_device *dev; /* device struct - must be first */ struct fst_card_info *card; /* Card we're associated with */ int index; /* Port index on the card */ int hwif; /* Line hardware (lineInterface copy) */ + int transparent_mode; /* Are we transparent or not */ int run; /* Port is running */ - int mode; /* Normal or FarSync raw */ + int ignoreCarrier; /* Allow tx regardless of carrier state */ + int proto; /* Normal or FarSync raw */ + int hdlc_proto; /* The proto as hdlc understands it */ + int num_tx_buffers; /* No of tx buffers in card window */ + int num_rx_buffers; /* No of rx buffers in card window */ + int tx_buffer_size; /* Size of tx buffers in card window */ + int rx_buffer_size; /* Size of rx buffers in card window */ int rxpos; /* Next Rx buffer to use */ int txpos; /* Next Tx buffer to use */ int txipos; /* Next Tx buffer to check for free */ int start; /* Indication of start/stop to network */ + int notify_mode; /* Application has selected the notify event */ + int monitor_mode; /* Application has selected monitor */ + unsigned int sequence; /* Monitor sequence no */ + int port_mode; /* DSL normal or active */ + unsigned int atm_cells_dropped; /* Cells discarded by driver */ + unsigned short vpi; /* ATM VPI for DSL-S1 */ + unsigned short vci; /* ATM VCI for DSL-S1 */ + unsigned char activation_status; /* Current trained status of line */ + unsigned char encap; /* type of atm encap ppp or mpoa */ + unsigned char last_act_status; /* Last trained status of line */ + unsigned char atm_cell_header[5]; /* Pre calculated atm header */ + unsigned char last_atm_cell_header[5]; /* Pre cal last atm header */ + unsigned char mpoa_header[MPOA_HEADER_LEN]; /* * A sixteen entry transmit queue */ int txqs; /* index to get next buffer to tx */ int txqe; /* index to queue next packet */ - struct sk_buff *txq[FST_TXQ_DEPTH]; /* The queue */ - int rxqdepth; + struct fst_queue_info txq[FST_TXQ_DEPTH]; /* The sent segments info */ + struct fst_queue_info rxq; /* The received segments info */ + struct file *char_file; + spinlock_t rxf_lock; /* Lock for queue head access */ + struct fst_char_rx_frame *char_inq; + struct fst_char_rx_frame *char_inq_end; + wait_queue_head_t pollq; /* poll() support */ }; + /* Per card information */ struct fst_card_info { - char __iomem *mem; /* Card memory mapped to kernel space */ - char __iomem *ctlmem; /* Control memory for PCI cards */ + char *mem; /* Card memory mapped to kernel space */ + char *ctlmem; /* Control memory for PCI cards */ unsigned int phys_mem; /* Physical memory window address */ unsigned int phys_ctlmem; /* Physical control memory address */ unsigned int irq; /* Interrupt request line number */ @@ -453,9 +896,12 @@ struct fst_card_info { unsigned int type; /* Type index of card */ unsigned int state; /* State of card */ spinlock_t card_lock; /* Lock for SMP access */ + spinlock_t fifo_lock; + wait_queue_head_t fifo_waitq; /* A queue to wait for a fifo response */ + int fifo_complete; unsigned short pci_conf; /* PCI card config in I/O space */ /* Per port info */ - struct fst_port_info ports[FST_MAX_PORTS]; + struct fst_port_info ports[ FST_MAX_PORTS ]; struct pci_dev *device; /* Information about the pci device */ int card_no; /* Inst of the card on the system */ int family; /* TxP or TxU */ @@ -474,32 +920,9 @@ struct fst_card_info { int dma_len_tx; int dma_txpos; int dma_rxpos; + int dma_tx_flags; }; -/* Convert an HDLC device pointer into a port info pointer and similar */ -#define dev_to_port(D) (dev_to_hdlc(D)->priv) -#define port_to_dev(P) ((P)->dev) - - -/* - * Shared memory window access macros - * - * We have a nice memory based structure above, which could be directly - * mapped on i386 but might not work on other architectures unless we use - * the readb,w,l and writeb,w,l macros. Unfortunately these macros take - * physical offsets so we have to convert. The only saving grace is that - * this should all collapse back to a simple indirection eventually. - */ -#define WIN_OFFSET(X) ((long)&(((struct fst_shared *)SMC_BASE)->X)) - -#define FST_RDB(C,E) readb ((C)->mem + WIN_OFFSET(E)) -#define FST_RDW(C,E) readw ((C)->mem + WIN_OFFSET(E)) -#define FST_RDL(C,E) readl ((C)->mem + WIN_OFFSET(E)) - -#define FST_WRB(C,E,B) writeb ((B), (C)->mem + WIN_OFFSET(E)) -#define FST_WRW(C,E,W) writew ((W), (C)->mem + WIN_OFFSET(E)) -#define FST_WRL(C,E,L) writel ((L), (C)->mem + WIN_OFFSET(E)) - /* * Debug support */ @@ -521,40 +944,41 @@ static int fst_debug_mask = { FST_DEBUG #define dbg(X...) /* NOP */ #endif + /* Printing short cuts */ #define printk_err(fmt,A...) printk ( KERN_ERR FST_NAME ": " fmt, ## A ) #define printk_warn(fmt,A...) printk ( KERN_WARNING FST_NAME ": " fmt, ## A ) #define printk_info(fmt,A...) printk ( KERN_INFO FST_NAME ": " fmt, ## A ) + /* * PCI ID lookup table */ static struct pci_device_id fst_pci_dev_id[] __devinitdata = { - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2P, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T2P}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4P, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T4P}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T1U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T1U}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T2U}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4U, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_T4U}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, - - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1C, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, - {0,} /* End */ + { FSC_PCI_VENDOR_ID, T2P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T2P }, + { FSC_PCI_VENDOR_ID, T4P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T4P }, + { FSC_PCI_VENDOR_ID, T1U_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T1U }, + { FSC_PCI_VENDOR_ID, T2U_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T2U }, + { FSC_PCI_VENDOR_ID, T4U_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T4U }, + { FSC_PCI_VENDOR_ID, T4E_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_T4E }, + { FSC_PCI_VENDOR_ID, TE1_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_TE1 }, + { FSC_PCI_VENDOR_ID, TE1C_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_TE1 }, + { FSC_PCI_VENDOR_ID, DSL_S1_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + FST_TYPE_DSL_S1 }, + { 0, } /* End */ }; -MODULE_DEVICE_TABLE(pci, fst_pci_dev_id); +MODULE_DEVICE_TABLE ( pci, fst_pci_dev_id ); + /* * Device Driver Work Queues @@ -562,956 +986,4080 @@ MODULE_DEVICE_TABLE(pci, fst_pci_dev_id) * So that we don't spend too much time processing events in the * Interrupt Service routine, we will declare a work queue per Card * and make the ISR schedule a task in the queue for later execution. - * In the 2.4 Kernel we used to use the immediate queue for BH's - * Now that they are gone, tasklets seem to be much better than work - * queues. */ -static void do_bottom_half_tx(struct fst_card_info *card); -static void do_bottom_half_rx(struct fst_card_info *card); +static void do_bottom_half_tx (struct fst_card_info *card); +static void do_bottom_half_rx (struct fst_card_info *card); static void fst_process_tx_work_q(unsigned long work_q); static void fst_process_int_work_q(unsigned long work_q); -static DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0); -static DECLARE_TASKLET(fst_int_task, fst_process_int_work_q, 0); - -static struct fst_card_info *fst_card_array[FST_MAX_CARDS]; -static spinlock_t fst_work_q_lock; -static u64 fst_work_txq; -static u64 fst_work_intq; +DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0); +DECLARE_TASKLET(fst_int_task, fst_process_int_work_q, 0); -static void -fst_q_work_item(u64 * queue, int card_index) -{ - unsigned long flags; - u64 mask; +unsigned int fst_ncards = 0; +struct fst_card_info *fst_cards_list[FST_MAX_CARDS]; +spinlock_t fst_work_q_lock; +u64 fst_work_txq; +u64 fst_work_intq; +int last_segment_cnt =0; - /* - * Grab the queue exclusively +/* + * We need to send an idle cell when using mpoa + * encapsulation */ - spin_lock_irqsave(&fst_work_q_lock, flags); +char atm_idle_cell [53]; - /* - * Making an entry in the queue is simply a matter of setting - * a bit for the card indicating that there is work to do in the - * bottom half for the card. Note the limitation of 64 cards. - * That ought to be enough +#ifndef FST_TTY_CHAR +static unsigned int fst_major = 0; /* The char driver major device number. + * Setting it to 0 will cause us to use + * dynamic allocation during module load */ - mask = 1 << card_index; - *queue |= mask; - spin_unlock_irqrestore(&fst_work_q_lock, flags); -} +#endif +extern struct file_operations fst_sync_fops; -static void -fst_process_tx_work_q(unsigned long /*void **/work_q) + +static char *type_strings[] __devinitdata = { + "no hardware", /* Should never be seen */ + "FarSync T2P ", + "FarSync T4P ", + "FarSync T1U ", + "FarSync T2U ", + "FarSync T4U ", + "FarSync TE1 ", + "FarSync DSL-S1", + "FarSync T4E " +}; + +static char *state_strings[] __devinitdata = { + "Uninitialised", /* Should never be seen */ + "Reset", + "Downloading", + "Starting", + "Running", + "Bad Version", + "Halted", + "I Failed" +}; + +static void fst_openport( struct fst_port_info *port); +static void fst_closeport ( struct fst_port_info *port ); +static int fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ); +static void fst_tx_dsl_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len); +static void fst_rx_dsl_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len); + +/* Convert an HDLC device pointer into a port info pointer and similar */ +#define dev_to_port(D) (dev_to_hdlc(D)->priv) +#define port_to_dev(P) ((P)->dev) +#define port_to_stats(P,S) (hdlc_stats(P->dev)->S) + +#define FST_PORT_NAME "hdlc" + + +static void fifo_init (struct fst_card_info *card) { - unsigned long flags; - u64 work_txq; - int i; + struct fst_fifo fifo; /* - * Grab the queue exclusively - */ - dbg(DBG_TX, "fst_process_tx_work_q\n"); - spin_lock_irqsave(&fst_work_q_lock, flags); - work_txq = fst_work_txq; - fst_work_txq = 0; - spin_unlock_irqrestore(&fst_work_q_lock, flags); + * Initialise async window to zeros + */ + dbg(DBG_ASS, "Zeroing %x bytes of async window\n", 0x9000); + memset_io(card->mem+AW_BASE, 0, 0x9000); + + /* + * Initiliase the local copy of the fifo header and then + * Copy it to shared memeory. + * Do the command fifo first followed by the response + */ + fifo.fifo_length = (u16)(FST_MSG_FIFO_LEN+1); /* must setup the fifo's length + before flushing the fifo */ + fifo.read_idx = (u16)(fifo.fifo_length-1); + fifo.write_idx = (u16)(fifo.fifo_length-1); + fifo.overflow_idx = -1; + fifo.data[0] = 'D'; + fifo.data[1] = 'E'; + fifo.data[2] = 'A'; + fifo.data[3] = 'D'; + + dbg(DBG_ASS, "Offset of msg fifo is %x\n", ASY_OFFSET(msg_fifo)); + memcpy_toio(card->mem+ASY_OFFSET(msg_fifo), &fifo, sizeof(struct fst_fifo)); + fifo.data[0] = 'B'; + fifo.data[1] = 'E'; + fifo.data[2] = 'E'; + fifo.data[3] = 'F'; + dbg(DBG_ASS, "Offset of msg fifo is %x\n", ASY_OFFSET(rsp_fifo)); + memcpy_toio(card->mem+ASY_OFFSET(rsp_fifo), &fifo, sizeof(struct fst_fifo)); +} + +static int write_into_fifo( struct fst_card_info *card, struct fstioc_req *request) +{ + int retval = -1; + int h_size = 0; + struct fst_fifo fifo; + unsigned short avail_fifo_space = 0; + unsigned short used_space = 0; + unsigned short free_space = 0; + unsigned short next_write_idx = 0; + unsigned short first_write_amount = 0; + unsigned short second_write_amount = 0; + char *p_request; + + h_size = sizeof(struct fst_fifo) - 4; + p_request = (char *) request; + dbg(DBG_IOCTL, "write_into_fifo for message type %x\n", request->msg_type); + memcpy_fromio ( &fifo, card->mem + ASY_OFFSET ( msg_fifo), h_size); + next_write_idx = (unsigned short) ((fifo.write_idx+1) % fifo.fifo_length); + dbg(DBG_IOCTL, "next_write_idx = %d\n", next_write_idx); + free_space = (unsigned short) ((fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length); + used_space = (unsigned short) (fifo.fifo_length - free_space - 1); + avail_fifo_space = (unsigned short) (FST_MSG_FIFO_LEN - used_space); + avail_fifo_space = (unsigned short) min(avail_fifo_space, + (unsigned short)FST_MSG_FIFO_LEN); - /* - * Call the bottom half for each card with work waiting - */ - for (i = 0; i < FST_MAX_CARDS; i++) { - if (work_txq & 0x01) { - if (fst_card_array[i] != NULL) { - dbg(DBG_TX, "Calling tx bh for card %d\n", i); - do_bottom_half_tx(fst_card_array[i]); + if (fifo.overflow_idx == -1) + { + dbg(DBG_IOCTL, "msg_fifo.overflow_idx = -1\n"); + if (avail_fifo_space >= request->msg_len) + { + dbg(DBG_IOCTL, "And there is space to write data\n"); + /* How much of it can we write into the end fragment */ + first_write_amount = (unsigned short) min(request->msg_len, + (unsigned short) (fifo.fifo_length - next_write_idx)); + dbg(DBG_IOCTL, "First write amount is %d\n", first_write_amount); + dbg(DBG_IOCTL, "Next write index is %d\n", next_write_idx); + dbg(DBG_IOCTL, "Copying to offset %x\n", + ASY_OFFSET(msg_fifo)+h_size+next_write_idx); + memcpy_toio (card->mem+ASY_OFFSET(msg_fifo)+h_size+next_write_idx, request, + first_write_amount); + fifo.write_idx = (unsigned short) ((fifo.write_idx + first_write_amount) + % fifo.fifo_length); + dbg(DBG_IOCTL, "Write Index updated to %d\n", fifo.write_idx); + if (first_write_amount != request->msg_len) + { + dbg(DBG_IOCTL, "We need a second write chunk because of wrap\n"); + /* were do we start writing the next chunk to */ + next_write_idx = (unsigned short) ((fifo.write_idx + 1) % + fifo.fifo_length); + + /* write the rest into the starting fragment */ + second_write_amount = (unsigned short) (request->msg_len - + first_write_amount); + dbg(DBG_IOCTL, "Second write amount is %d\n", second_write_amount); + dbg(DBG_IOCTL, "Next write index is %d\n", next_write_idx); + dbg(DBG_IOCTL, "Copying to offset %x\n", + ASY_OFFSET(msg_fifo)+h_size+next_write_idx); + + memcpy_toio (card->mem+ASY_OFFSET(msg_fifo)+h_size+next_write_idx, + p_request+first_write_amount, + second_write_amount); + fifo.write_idx = (unsigned short) ((fifo.write_idx + + second_write_amount) + % fifo.fifo_length); } + retval = 0; + } + else + { + fifo.overflow_idx = fifo.write_idx; } - work_txq = work_txq >> 1; + } + dbg(DBG_IOCTL, "Updating the fifo header\n"); + memcpy_toio ( card->mem + ASY_OFFSET ( msg_fifo), &fifo, h_size); + return retval; } -static void -fst_process_int_work_q(unsigned long /*void **/work_q) +static int read_from_fifo( struct fst_card_info *card, struct fstioc_req *response) { - unsigned long flags; - u64 work_intq; + int h_size = 0; + struct fst_fifo fifo; + char fifo_data[FST_RSP_FIFO_LEN+10]; + unsigned short next_read_idx = 0; + unsigned short amount_to_read = 0; + char *data1 = NULL; + unsigned short length1; + char *data2 = NULL; + unsigned short length2; + char *c_p_response; int i; + dbg(DBG_IOCTL, "read from fifo\n"); + h_size = sizeof(struct fst_fifo) - 4; + c_p_response = (char*) (response); + for (i=0; i< sizeof(struct fstioc_req); i++); + memcpy_fromio(&fifo, card->mem + ASY_OFFSET(rsp_fifo), h_size); + memcpy_fromio(&fifo_data, card->mem + ASY_OFFSET(rsp_fifo) + h_size, + FST_RSP_FIFO_LEN+1); + amount_to_read = (unsigned short) min((unsigned short)((fifo.write_idx - fifo.read_idx + fifo.fifo_length) % fifo.fifo_length), + (unsigned short)sizeof(struct fstioc_req)); + if (amount_to_read > 0) + { + /* where should we start reading from ? */ + next_read_idx = (unsigned short) ((fifo.read_idx + 1) % + fifo.fifo_length); + dbg(DBG_ASS, "Next read index = %d\n", next_read_idx); + data1 = &fifo_data[next_read_idx]; + length1 = (unsigned short) min((unsigned short) amount_to_read, + (unsigned short)(fifo.fifo_length - next_read_idx)); + dbg(DBG_IOCTL, "length1 = %d\n", length1); + + data2 = &fifo_data[0]; + length2 = (unsigned short) (amount_to_read - length1); + dbg(DBG_IOCTL, "length2 = %d\n", length2); + memcpy(c_p_response, data1, length1); + if (length2) + memcpy(&c_p_response[length1], data2, length2); + fifo.read_idx = (unsigned short) ((fifo.read_idx + + amount_to_read) % + fifo.fifo_length); + memcpy_toio ( card->mem + ASY_OFFSET ( rsp_fifo), &fifo, h_size); + dbg(DBG_IOCTL, "Finished read from fifo\n"); + + return amount_to_read; + } + return 0; +} + + +static void check_fifo_resp(struct fst_card_info *card) +{ + unsigned short used_space = 0; + unsigned short free_space = 0; + unsigned short next_write_idx = 0; + struct fst_fifo fifo; + + dbg(DBG_IOCTL, "Check fifo response\n"); + memcpy_fromio ( &fifo, card->mem + ASY_OFFSET (rsp_fifo), + sizeof(struct fst_fifo)); + next_write_idx = (unsigned short) ((fifo.write_idx+1) % fifo.fifo_length); + free_space = (unsigned short) ((fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length); + used_space = (unsigned short) (fifo.fifo_length - free_space - 1); + + if (used_space >= sizeof(struct fstioc_req)) + { /* - * Grab the queue exclusively + * we have a response to process so wake the queue that + * the process should be waiting on */ - dbg(DBG_INTR, "fst_process_int_work_q\n"); - spin_lock_irqsave(&fst_work_q_lock, flags); - work_intq = fst_work_intq; - fst_work_intq = 0; - spin_unlock_irqrestore(&fst_work_q_lock, flags); + dbg(DBG_IOCTL, "Rsp Fifo has data waiting\n"); + card->fifo_complete = 1; + wake_up_interruptible(&card->fifo_waitq); + } +} +struct net_device *get_net_device( int if_index ) +{ + char devname[16]; + char devnum[16]; + struct net_device *dev; + strcpy(devname, FST_PORT_NAME); + sprintf(devnum,"%d", if_index); + strcat(devname, devnum); + if ((dev = dev_get_by_name(devname)) == NULL) + { /* - * Call the bottom half for each card with work waiting + * No device. Error */ - for (i = 0; i < FST_MAX_CARDS; i++) { - if (work_intq & 0x01) { - if (fst_card_array[i] != NULL) { - dbg(DBG_INTR, - "Calling rx & tx bh for card %d\n", i); - do_bottom_half_rx(fst_card_array[i]); - do_bottom_half_tx(fst_card_array[i]); - } - } - work_intq = work_intq >> 1; + return NULL; } + dev_put(dev); + return dev; } -/* Card control functions - * ====================== +#ifdef FST_TTY_CHAR +/* + * Char tty interface for async PPP */ -/* Place the processor in reset state - * - * Used to be a simple write to card control space but a glitch in the latest - * AMD Am186CH processor means that we now have to do it by asserting and de- - * asserting the PLX chip PCI Adapter Software Reset. Bit 30 in CNTRL register - * at offset 9052_CNTRL. Note the updates for the TXU. - */ -static inline void -fst_cpureset(struct fst_card_info *card) -{ - unsigned char interrupt_line_register; - unsigned long j = jiffies + 1; - unsigned int regval; - - if (card->family == FST_FAMILY_TXU) { - if (pci_read_config_byte - (card->device, PCI_INTERRUPT_LINE, &interrupt_line_register)) { - dbg(DBG_ASS, - "Error in reading interrupt line register\n"); - } - /* - * Assert PLX software reset and Am186 hardware reset - * and then deassert the PLX software reset but 186 still in reset - */ - outw(0x440f, card->pci_conf + CNTRL_9054 + 2); - outw(0x040f, card->pci_conf + CNTRL_9054 + 2); - /* - * We are delaying here to allow the 9054 to reset itself - */ - j = jiffies + 1; - while (jiffies < j) - /* Do nothing */ ; - outw(0x240f, card->pci_conf + CNTRL_9054 + 2); - /* - * We are delaying here to allow the 9054 to reload its eeprom - */ - j = jiffies + 1; - while (jiffies < j) - /* Do nothing */ ; - outw(0x040f, card->pci_conf + CNTRL_9054 + 2); +#define FST_TTY_NPORTS FST_MAX_CARDS*FST_MAX_PORTS +#define FST_TTY_MAJOR 190 +#define FST_TTY_MINOR_START 0 +/* tty interface state */ +#define FST_TTY_ST_IDLE 0 +#define FST_TTY_ST_INIT 1 +#define FST_TTY_ST_OPEN 2 + +/* TTY functions prototype */ +static int fst_tty_open(struct tty_struct *tty, struct file *flip); +static void fst_tty_close(struct tty_struct *tty, struct file *flip); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) +static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count); +#else +static int fst_tty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count); +#endif +static int fst_tty_write_room(struct tty_struct *tty); +static int fst_tty_chars_in_buffer(struct tty_struct *tty); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +int fst_tty_tiocmset(struct tty_struct *, struct file *, + unsigned int, unsigned int); +int fst_tty_tiocmget(struct tty_struct *, struct file *); +#else +static int fst_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg); +#endif +static void fst_tty_flush_buffer(struct tty_struct *tty); +static void fst_tty_hangup(struct tty_struct *tty); - if (pci_write_config_byte - (card->device, PCI_INTERRUPT_LINE, interrupt_line_register)) { - dbg(DBG_ASS, - "Error in writing interrupt line register\n"); - } +static struct tty_driver serial_drv; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static struct tty_driver callout_drv; +static struct tty_struct *fst_tty_serial_table[FST_TTY_NPORTS]; +static struct termios *fst_tty_serial_termios[FST_TTY_NPORTS]; +static struct termios *fst_tty_serial_termios_locked[FST_TTY_NPORTS]; +#endif - } else { - regval = inl(card->pci_conf + CNTRL_9052); +typedef struct _fst_tty_area { + int state; /* state of the TTY interface */ + int num_open; + unsigned int tty_minor; /* minor this interface */ + struct fst_port_info *port; /* ptr. to port info */ + unsigned char name[20]; /* interf. name + "-tty" */ + struct tty_struct *tty; +} st_fst_tty_area; - outl(regval | 0x40000000, card->pci_conf + CNTRL_9052); - outl(regval & ~0x40000000, card->pci_conf + CNTRL_9052); - } -} +st_fst_tty_area fst_tty_area[FST_TTY_NPORTS]; +int fst_tty_refcount; +int fst_tty_unreg_flag = 0; +int fst_tty_cnt = 0; -/* Release the processor from reset - */ -static inline void -fst_cpurelease(struct fst_card_info *card) +void fst_tty_uninit (void) { - if (card->family == FST_FAMILY_TXU) { /* - * Force posted writes to complete + * Unload the tty driver */ - (void) readb(card->mem); + int res; - /* - * Release LRESET DO = 1 - * Then release Local Hold, DO = 1 - */ - outw(0x040e, card->pci_conf + CNTRL_9054 + 2); - outw(0x040f, card->pci_conf + CNTRL_9054 + 2); - } else { - (void) readb(card->ctlmem); + printk_info("Unregister the tty driver\n"); + if ((res=tty_unregister_driver(&serial_drv))) + { + dbg(DBG_ASS, "ERROR ->unregister the tty driver error=%d\n", + res); + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if ((res=tty_unregister_driver(&callout_drv))) + { + dbg(DBG_ASS, "ERROR ->unregister the tty driver error=%d\n", + res); } +#endif } -/* Clear the cards interrupt flag - */ -static inline void -fst_clear_intr(struct fst_card_info *card) +void fst_tty_init (void) { - if (card->family == FST_FAMILY_TXU) { - (void) readb(card->ctlmem); - } else { - /* Poke the appropriate PLX chip register (same as enabling interrupts) + /* + * Load the tty driver */ - outw(0x0543, card->pci_conf + INTCSR_9052); - } -} + printk_info("Initialising tty driver\n"); + /* initialize tty driver struct */ + memset(&serial_drv,0,sizeof(struct tty_driver)); + serial_drv.magic = TTY_DRIVER_MAGIC; + serial_drv.driver_name = "farsync_tty"; + serial_drv.name = "farsync"; + serial_drv.major = FST_TTY_MAJOR ; + serial_drv.minor_start = 0; /* Use the whole of the minor range */ + serial_drv.num = FST_TTY_NPORTS; + serial_drv.type = TTY_DRIVER_TYPE_SERIAL; + serial_drv.subtype = SERIAL_TYPE_NORMAL; + + serial_drv.init_termios = tty_std_termios; + serial_drv.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + serial_drv.flags = TTY_DRIVER_REAL_RAW; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + serial_drv.refcount = &fst_tty_refcount; + /* tty data structures */ + serial_drv.table = fst_tty_serial_table; + serial_drv.termios = fst_tty_serial_termios; + serial_drv.termios_locked = fst_tty_serial_termios_locked; +#endif -/* Enable card interrupts - */ -static inline void -fst_enable_intr(struct fst_card_info *card) -{ - if (card->family == FST_FAMILY_TXU) { - outl(0x0f0c0900, card->pci_conf + INTCSR_9054); - } else { - outw(0x0543, card->pci_conf + INTCSR_9052); + /* interface routines from the upper tty layer to the tty driver */ + serial_drv.open = fst_tty_open; + serial_drv.close = fst_tty_close; + serial_drv.write = fst_tty_write; + serial_drv.write_room = fst_tty_write_room; + serial_drv.chars_in_buffer = fst_tty_chars_in_buffer; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + serial_drv.tiocmset = fst_tty_tiocmset; + serial_drv.tiocmget = fst_tty_tiocmget; +#else + serial_drv.ioctl = fst_tty_ioctl; +#endif + serial_drv.flush_buffer = fst_tty_flush_buffer; + serial_drv.hangup = fst_tty_hangup; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + /* the callout device is just like normal device except for major */ + /* number and the subtype code */ + callout_drv = serial_drv; + callout_drv.name = "cuFS"; + callout_drv.major = FST_TTY_MAJOR + 1; + callout_drv.subtype = SERIAL_TYPE_CALLOUT; + callout_drv.read_proc = 0; + callout_drv.proc_entry = 0; +#endif + + /* register the TTY driver */ + if (tty_register_driver(&serial_drv)) + { + printk("fsc: Failed to register serial driver!\n"); + return; } -} -/* Disable card interrupts - */ -static inline void -fst_disable_intr(struct fst_card_info *card) -{ - if (card->family == FST_FAMILY_TXU) { - outl(0x00000000, card->pci_conf + INTCSR_9054); - } else { - outw(0x0000, card->pci_conf + INTCSR_9052); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + if (tty_register_driver(&callout_drv)) + { + printk("fst_tty: Failed to register callout driver!\n"); + return; } +#endif + memset((void *)fst_tty_area, 0, + sizeof(st_fst_tty_area) * FST_TTY_NPORTS); + return; } -/* Process the result of trying to pass a received frame up the stack - */ -static void -fst_process_rx_status(int rx_status, char *name) +static int fst_tty_open(struct tty_struct *tty, struct file *file) { - switch (rx_status) { - case NET_RX_SUCCESS: - { /* - * Nothing to do here + * Open a farsync port + * It must not already be open by the network stack */ - break; - } + int port_num; + st_fst_tty_area *fst_tty; + struct net_device *dev; + struct fst_port_info *port; + char dev_name[16]; - case NET_RX_CN_LOW: + dbg(DBG_ASS, "fst_tty_open\n"); + if (!tty) { - dbg(DBG_ASS, "%s: Receive Low Congestion\n", name); - break; + return -ENODEV; } - case NET_RX_CN_MOD: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + port_num = tty->index; +#else + port_num = MINOR(tty->device) - tty->driver.minor_start; +#endif + + if ((port_num < 0) || (port_num >= FST_TTY_NPORTS)) { - dbg(DBG_ASS, "%s: Receive Moderate Congestion\n", name); - break; + dbg(DBG_ASS, "fst_tty: open invalid minor %i\n", port_num); + return -ENODEV; } - case NET_RX_CN_HIGH: + fst_tty = &fst_tty_area[port_num]; + if (fst_tty->num_open == 0) { - dbg(DBG_ASS, "%s: Receive High Congestion\n", name); - break; + /* first open of this tty */ + fst_tty_area[port_num].state = FST_TTY_ST_OPEN; + fst_tty_area[port_num].tty = tty; + tty->driver_data = &fst_tty_area[port_num]; + fst_tty->num_open= 0; + fst_tty->tty_minor = port_num + FST_TTY_MINOR_START; + + /* + * We need to find the dev device and see if it is open + */ + if ((dev = get_net_device(port_num)) == NULL) + { + dbg(DBG_ASS, "Cannot open char device %s because net device not found\n", dev->name); + return -ENXIO; } + strcpy(dev_name, "tty_"); + strcat(dev_name, dev->name); + strcpy(fst_tty->name, dev->name); + dbg(DBG_ASS, "%s: Initializing TTY Sync Driver, tty major#%d minor#%i\n", + fst_tty->name,FST_TTY_MAJOR,fst_tty->tty_minor); - case NET_RX_DROP: + port = dev_to_port(dev); + if (port == NULL) { - dbg(DBG_ASS, "%s: Received packet dropped\n", name); - break; + dbg(DBG_ASS, "Cannot open char device %s because port is not created\n", dev->name); + return -EACCES; } +#if 0 + /* + * We do not want the port to be in use by the net interface + * So if already open, decline this open request + */ + if (port->run) + { + dbg(DBG_ASS, "Cannot open char device %s because port (%p) already open\n", dev->name, port); + return -EACCES; } -} - -/* Initilaise DMA for PLX 9054 +#endif + /* + * Set a flag to indicate we are now using the char interface for + * data read and write */ -static inline void -fst_init_dma(struct fst_card_info *card) -{ + port->char_file = file; /* - * This is only required for the PLX 9054 + * Open the physical port if not already open */ - if (card->family == FST_FAMILY_TXU) { - pci_set_master(card->device); - outl(0x00020441, card->pci_conf + DMAMODE0); - outl(0x00020441, card->pci_conf + DMAMODE1); - outl(0x0, card->pci_conf + DMATHR); + if (!port->port_mode) + fst_openport(port); + fst_tty->port = port; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + try_module_get(THIS_MODULE); +#else + MOD_INC_USE_COUNT; +#endif + fst_tty_cnt++; + printk_info("TTY driver port %s is now open\n", fst_tty->name); } + fst_tty->num_open++; + return 0; } -/* Tx dma complete interrupt - */ -static void -fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, - int len, int txpos) +static void fst_tty_close(struct tty_struct *tty, struct file *flip) { - struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); - /* - * Everything is now set, just tell the card to go + * Close a farsync port */ - dbg(DBG_TX, "fst_tx_dma_complete\n"); - FST_WRB(card, txDescrRing[port->index][txpos].bits, - DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += len; - dev->trans_start = jiffies; -} + st_fst_tty_area *fst_tty; + unsigned long flags; -/* - * Mark it for our own raw sockets interface - */ -static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev) -{ - skb->dev = dev; - skb_reset_mac_header(skb); - skb->pkt_type = PACKET_HOST; - return htons(ETH_P_CUST); -} + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "fst_tty: no TTY in close \n"); + return; + } -/* Rx dma complete interrupt - */ -static void -fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, - int len, struct sk_buff *skb, int rxp) -{ - struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); - int pi; - int rx_status; + fst_tty = (st_fst_tty_area *) tty->driver_data; - dbg(DBG_TX, "fst_rx_dma_complete\n"); - pi = port->index; - memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); + if ((fst_tty->tty != tty)|| (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY is not opened\n",fst_tty->name); + return; + } - /* Reset buffer descriptor */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + if (!fst_tty->num_open) + { + dbg(DBG_ASS, "%s: TTY is closed\n",fst_tty->name); + return; + } - /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; + if (--fst_tty->num_open > 0) + { + dbg(DBG_ASS, "%s: TTY closing the second open\n", + fst_tty->name); + return; + } - /* Push upstream */ - dbg(DBG_RX, "Pushing the frame up the stack\n"); - if (port->mode == FST_RAW) - skb->protocol = farsync_type_trans(skb, dev); - else - skb->protocol = hdlc_type_trans(skb, dev); - rx_status = netif_rx(skb); - fst_process_rx_status(rx_status, port_to_dev(port)->name); - if (rx_status == NET_RX_DROP) - stats->rx_dropped++; - dev->last_rx = jiffies; + fst_tty_cnt--; + spin_lock_irqsave(&fst_tty->port->card->card_lock, flags); + fst_tty->tty = NULL; + spin_unlock_irqrestore(&fst_tty->port->card->card_lock, flags); + fst_closeport(fst_tty->port); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + printk_info("TTY driver port %s is now closed\n",fst_tty->name); + fst_tty->port->char_file = NULL; + return; } -/* - * Receive a frame through the DMA - */ -static inline void -fst_rx_dma(struct fst_card_info *card, unsigned char *skb, - unsigned char *mem, int len) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) +static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +#else +static int fst_tty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +#endif { /* - * This routine will setup the DMA and start it + * Send data to the farsync port */ + st_fst_tty_area *fst_tty; + struct sk_buff *skb; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) + int from_user = 1; +#endif - dbg(DBG_RX, "In fst_rx_dma %p %p %d\n", skb, mem, len); - if (card->dmarx_in_progress) { - dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n"); + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "Could not find TTY device in write\n"); + return -ENODEV; } - outl((unsigned long) skb, card->pci_conf + DMAPADR0); /* Copy to here */ - outl((unsigned long) mem, card->pci_conf + DMALADR0); /* from here */ - outl(len, card->pci_conf + DMASIZ0); /* for this length */ - outl(0x00000000c, card->pci_conf + DMADPR0); /* In this direction */ - - /* - * We use the dmarx_in_progress flag to flag the channel as busy - */ - card->dmarx_in_progress = 1; - outb(0x03, card->pci_conf + DMACSR0); /* Start the transfer */ -} + fst_tty = (st_fst_tty_area *) tty->driver_data; -/* - * Send a frame through the DMA - */ -static inline void -fst_tx_dma(struct fst_card_info *card, unsigned char *skb, - unsigned char *mem, int len) -{ - /* - * This routine will setup the DMA and start it. - */ + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n", fst_tty->name); + return -ENODEV; + } - dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len); - if (card->dmatx_in_progress) { - dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n"); + if (count > FST_MAX_MTU) + { + dbg(DBG_ASS, "%s: Size of write is too large\n",fst_tty->name); + return -EINVAL; /* frame too big */ } - outl((unsigned long) skb, card->pci_conf + DMAPADR1); /* Copy from here */ - outl((unsigned long) mem, card->pci_conf + DMALADR1); /* to here */ - outl(len, card->pci_conf + DMASIZ1); /* for this length */ - outl(0x000000004, card->pci_conf + DMADPR1); /* In this direction */ + dbg(DBG_TTY, "%s: fst_tty_write %s data len=%i\n",fst_tty->name, + (from_user)?"from user" : "from kernel",count); + if ( (skb = dev_alloc_skb ( count )) == NULL ) + { + dbg ( DBG_ASS,"fst_tty_write: can't allocate skb\n"); + return -EFAULT; + } - /* - * We use the dmatx_in_progress to flag the channel as busy - */ - card->dmatx_in_progress = 1; - outb(0x03, card->pci_conf + DMACSR1); /* Start the transfer */ + /* Read it in */ + if (from_user) + { + dbg(DBG_TTY, "Value of from user is %d \n", from_user); + __copy_from_user ( skb_put(skb, count), buf, count ); + } + else + { + memcpy(skb_put(skb, count), buf, count); + } + fst_start_xmit( skb, port_to_dev(fst_tty->port)); + return count; } -/* Issue a Mailbox command for a port. - * Note we issue them on a fire and forget basis, not expecting to see an - * error and not waiting for completion. - */ -static void -fst_issue_cmd(struct fst_port_info *port, unsigned short cmd) +static int fst_tty_write_room(struct tty_struct *tty) { - struct fst_card_info *card; - unsigned short mbval; - unsigned long flags; - int safety; + st_fst_tty_area *fst_tty; - card = port->card; - spin_lock_irqsave(&card->card_lock, flags); - mbval = FST_RDW(card, portMailbox[port->index][0]); + if (!tty || !tty->driver_data ) { + dbg(DBG_ASS, "Could not find TTY device to write room\n"); + return -ENODEV; + } - safety = 0; - /* Wait for any previous command to complete */ - while (mbval > NAK) { - spin_unlock_irqrestore(&card->card_lock, flags); - schedule_timeout_uninterruptible(1); - spin_lock_irqsave(&card->card_lock, flags); + fst_tty = (st_fst_tty_area *) tty->driver_data; - if (++safety > 2000) { - printk_err("Mailbox safety timeout\n"); - break; + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n",fst_tty->name); + return -ENODEV; } - mbval = FST_RDW(card, portMailbox[port->index][0]); - } - if (safety > 0) { - dbg(DBG_CMD, "Mailbox clear after %d jiffies\n", safety); - } - if (mbval == NAK) { - dbg(DBG_CMD, "issue_cmd: previous command was NAK'd\n"); + dbg(DBG_ASS, "%s: write room\n",fst_tty->name); + + return FST_MAX_MTU; +} + +static int fst_tty_chars_in_buffer(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "Could not find TTY device for chars in buffer\n"); + return -ENODEV; } - FST_WRW(card, portMailbox[port->index][0], cmd); + fst_tty = (st_fst_tty_area *) tty->driver_data; - if (cmd == ABORTTX || cmd == STARTPORT) { - port->txpos = 0; - port->txipos = 0; - port->start = 0; + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n",fst_tty->name); + return -ENODEV; } - spin_unlock_irqrestore(&card->card_lock, flags); + return(0); } -/* Port output signals control - */ -static inline void -fst_op_raise(struct fst_port_info *port, unsigned int outputs) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) +int fst_tty_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) { - outputs |= FST_RDL(port->card, v24OpSts[port->index]); - FST_WRL(port->card, v24OpSts[port->index], outputs); + st_fst_tty_area *fst_tty; - if (port->run) - fst_issue_cmd(port, SETV24O); -} + dbg(DBG_TTY, "%s: set:%x clear:%x\n", __FUNCTION__, set, clear); -static inline void -fst_op_lower(struct fst_port_info *port, unsigned int outputs) -{ - outputs = ~outputs & FST_RDL(port->card, v24OpSts[port->index]); - FST_WRL(port->card, v24OpSts[port->index], outputs); + if (!tty || !tty->driver_data ) { + dbg(DBG_ASS, "Could not find TTY for tiocmset\n"); + return -ENODEV; + } - if (port->run) - fst_issue_cmd(port, SETV24O); -} + fst_tty = (st_fst_tty_area *) tty->driver_data; -/* + if (set & TIOCM_RTS) + { + dbg(DBG_TTY, "Set RTS\n"); + } + if (set & TIOCM_DTR) + { + dbg(DBG_TTY, "Set DTR\n"); + } + if (clear & TIOCM_RTS) + { + dbg(DBG_TTY, "Clear RTS\n"); + } + if (clear & TIOCM_DTR) + { + dbg(DBG_TTY, "Clear DTR\n"); + } + + return 0; +} + +int fst_tty_tiocmget(struct tty_struct *tty, struct file *file) +{ + unsigned int result; + + st_fst_tty_area *fst_tty = (st_fst_tty_area *) tty->driver_data; + fst_tty = (st_fst_tty_area *) tty->driver_data; + + dbg(DBG_TTY, "%s-tty: tiocmget\n",fst_tty->name); + + result = 0; + + return result; +} +#else +static int fst_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "Could not find TTY device for ioctl\n"); + return -ENODEV; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n",fst_tty->name); + return -ENODEV; + } + + dbg(DBG_ASS, "%s: IOCTL cmd %x\n",fst_tty->name,cmd); + + switch (cmd) { + case TIOCMBIS : /* set DTR */ + dbg(DBG_ASS, "Set DTR\n");; + break; + + case TIOCMBIC: /* clear DTR */ + dbg(DBG_ASS, "Set DTR\n");; + break; + default : + dbg(DBG_ASS, "Other\n");; + return -ENOIOCTLCMD; + } + return 0; +} +#endif + +static void fst_tty_flush_buffer(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "Could not find TTY to flush buffer\n"); + return; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n",fst_tty->name); + return; + } + + dbg(DBG_TTY, "%s: call wake_up_interruptible\n",fst_tty->name); + + wake_up_interruptible(&tty->write_wait); + + return; +} + +static void fst_tty_hangup(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data ) + { + dbg(DBG_ASS, "Could not find TTY device to hangup\n"); + return ; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state != FST_TTY_ST_OPEN)) + { + dbg(DBG_ASS, "%s: TTY device is not opened\n",fst_tty->name); + return ; + } + dbg(DBG_ASS, "fst_tty_hangup\n"); +} + +static int +tty_signal_rx_frame ( struct fst_port_info *port, struct sk_buff *skb) +{ + int port_num; + st_fst_tty_area *fst_tty; + char flags=0; + + if (fst_tty_cnt == 0) return -1; + + sscanf(&(port_to_dev(port)->name[4]), "%d", &port_num); + fst_tty = &fst_tty_area[port_num]; + if (fst_tty->tty && (fst_tty->tty->ldisc.receive_buf)) + { + dbg(DBG_TTY, "%s: call line disc. receive_buf of length %d\n", + fst_tty->name, skb->len); + fst_tty->tty->ldisc.receive_buf(fst_tty->tty, skb->data, + &flags, skb->len); + } + else + { + dbg(DBG_ASS, "%s: frame dropped. receive_buf of length %d\n", + fst_tty->name, skb->len); + dbg(DBG_ASS, "tty address = %p, ldisc routine = %p\n", + fst_tty->tty, fst_tty->tty->ldisc.receive_buf); + } + dev_kfree_skb(skb); + return 0; +} + +#else +/* + * Char interface for RAW API + */ +#define UNIT(K) (MINOR(K) & 0x0F) /* Card number */ +#define MTYPE(K) (MINOR(K) & 0xF0) /* Access type */ + +static int +fst_sync_open ( struct inode *inode, struct file *file ) +{ + int unit; + struct net_device *dev; + struct fst_port_info *port; + + unit = UNIT ( inode->i_rdev ); + dbg ( DBG_OPEN,"fst_sync_open: %d on device %d\n", + inode->i_rdev, unit ); + /* + * We need to find the dev device and see if it is open + */ + if ((dev = get_net_device(unit)) == NULL) + { + dbg(DBG_ASS, "Cannot open char device sync%d because net device not found\n", unit); + return -ENXIO; + } + + port = dev_to_port(dev); + if (port->run) + { + dbg(DBG_ASS, "Cannot open char device sync%d because net device is open\n", unit); + return -EACCES; + } + /* + * Set a flag to indicate we are now using the char interface for + * data read and write + */ + port->char_file = file; + file->private_data = port; + port_to_dev(port)->mtu = port->rx_buffer_size; + /* + * Open the physical port if not already open + */ + if (!port->port_mode) + fst_openport(port); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + try_module_get(THIS_MODULE); +#else + MOD_INC_USE_COUNT; +#endif + return 0; /* OK */ +} + +/* Simple close just decrements the module count + */ +static int +fst_sync_release ( struct inode *inode, struct file *file ) +{ + int unit; + struct fst_port_info *port; + + unit = UNIT ( inode->i_rdev ); + dbg ( DBG_OPEN,"fst_sync_release: %d on device %d\n", + inode->i_rdev, unit ); + port = file->private_data; + port->char_file = NULL; + wake_up_interruptible (&port->pollq); + fst_closeport(port); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + module_put(THIS_MODULE); +#else + MOD_DEC_USE_COUNT; +#endif + return 0; /* OK */ +} + +static ssize_t +fst_sync_read ( struct file *file, char *buf, size_t count, loff_t *offp ) +{ + struct fst_port_info *port; + struct fst_char_rx_frame *rx_frame; + unsigned long flags; + int len; + + port = file->private_data; + dbg ( DBG_RX,">>fst_sync_read: %s %d\n", port->dev->name, count ); + /* + * Any data yet + */ + if (port->char_inq == NULL) + { + dbg(DBG_RX, "<dev->name, port->char_inq); + return -EAGAIN; /* Nothing ready */ + } + /* + * Take the next item off the rx queue + */ + dbg(DBG_RX, "Taking an rx buffer off the read queue %p\n", + port->char_inq); + spin_lock_irqsave(&port->rxf_lock, flags); + rx_frame = port->char_inq; + port->char_inq = rx_frame->next; + spin_unlock_irqrestore(&port->rxf_lock, flags); + len = rx_frame->frame->len; + /* + * Copy any data to user space if there is any + */ + if (len == 0) + { + /* + * must be a status change message + */ + dev_kfree_skb(rx_frame->frame); + kfree(rx_frame); + return 0; + } + else + { + if ( copy_to_user ( buf, rx_frame->frame->data, len) < 0 ) + { + dbg ( DBG_ASS,"fst_sync_read: Couldn't copy data to user\n"); + dev_kfree_skb(rx_frame->frame); + kfree(rx_frame); + return -EFAULT; + } + } + *offp += sizeof ( len ); + dev_kfree_skb(rx_frame->frame); + kfree(rx_frame); + return (len); +} + +static ssize_t +fst_sync_write ( struct file *file, const char *buf, size_t count, loff_t *offp ) +{ + struct fst_port_info *port; + struct sk_buff *skb; + + port = file->private_data; + dbg ( DBG_TX,"fst_sync_write: %s %d\n", port->dev->name, count ); + + /* Make sure we can both read and write it */ + if ( ! access_ok ( VERIFY_READ, buf, count )) + { + dbg(DBG_ASS, "fst_sync_write: Can't access write buffer\n"); + return -EFAULT; + } + if ( (skb = dev_alloc_skb ( count )) == NULL ) + { + dbg ( DBG_INTR,"fst_sync_write: can't allocate skb\n"); + return -EFAULT; + } + + /* Read it in */ + __copy_from_user ( skb_put(skb, count), buf, count ); + fst_start_xmit( skb, port->dev); + return count; +} + +/* Controls to reset and release the cards CPU + */ +static int +fst_sync_ioctl ( struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg ) +{ + dbg ( DBG_IOCTL,"fst_sync_ioctl: %x, %lx\n", cmd, arg ); + return 0; +} + +int +fst_sync_async ( int fd, struct file *file, int on ) +{ + + dbg ( DBG_ASS,"fst_sync_async: %d\n", on ); + + return 0; +} + +unsigned int +fst_sync_poll ( struct file *file, poll_table *poll_table ) +{ + int poll_mask = 0; + struct fst_port_info *port; + + port = file->private_data; + dbg(DBG_INTR, ">>fst_sync_poll: %s\n", port->dev->name); + poll_wait( file, &port->pollq, poll_table ); + /* + * Is there something to read? + */ + if (port->char_inq) + poll_mask |= POLLIN | POLLRDNORM; + /* + * Is the txq empty + */ + if (port->txqe == port->txqs) + { + poll_mask |= POLLOUT | POLLWRNORM; + } + dbg ( DBG_INTR, "<dev->name, (unsigned int)poll_table, poll_mask ); + + return poll_mask; +} + +static int +char_queue_rx_frame ( struct fst_port_info *port, struct sk_buff *skb) +{ + struct fst_char_rx_frame *rx_frame; + unsigned long flags; + + /* + * Allocate a queue structure and queue the skb on the ports char interface + * Note that the flags are GFP_ATOMIC as this could be called from an + * interrupt. + */ + dbg(DBG_RX, "char_queue_rx_frame: %s\n", port->dev->name); + rx_frame = kmalloc(sizeof(struct fst_char_rx_frame), GFP_ATOMIC); + if (rx_frame == NULL) + { + dbg(DBG_ASS,"Could not allocate an fst_char_rx_structure\n"); + return NET_RX_DROP; + } + rx_frame->frame = skb; + spin_lock_irqsave(&port->rxf_lock, flags); + if (port->char_inq == NULL) + { + /* + * Nothing on the queue yet + */ + dbg(DBG_RX, "Queue was empty\n"); + port->char_inq = rx_frame; + } + else + { + dbg(DBG_RX, "Queued at end\n"); + port->char_inq_end->next = rx_frame; + } + port->char_inq_end = rx_frame; + rx_frame->next = NULL; + rx_frame->prev= NULL; + spin_unlock_irqrestore(&port->rxf_lock, flags); + wake_up_interruptible (&port->pollq); + dbg(DBG_RX, "Placed skb on read queue and stimmed the poll function\n"); + return 0; +} + +/* Card memory image access */ +static struct file_operations fst_mem_fops = { + read: fst_sync_read, + write: fst_sync_write, + ioctl: fst_sync_ioctl, + open: fst_sync_open, + release: fst_sync_release, + fasync: fst_sync_async, + poll: fst_sync_poll, + +}; + +#endif /* end ifdef FST_TTY_CHAR */ +static void +fst_q_work_item (u64 *queue, int card_index) +{ + unsigned long flags; + u64 mask; + + /* + * Grab the queue exclusively + */ + spin_lock_irqsave(&fst_work_q_lock, flags); + + /* + * Making an entry in the queue is simply a matter of setting + * a bit for the card indicating that there is work to do in the + * bottom half for the card. Note the limitation of 64 cards. + * That ought to be enough + */ + mask = 1 << card_index; + *queue |= mask; + spin_unlock_irqrestore(&fst_work_q_lock, flags); +} + + +static void +fst_process_tx_work_q ( unsigned long work_q) +{ + unsigned long flags; + u64 work_txq; + int i; + + /* + * Grab the queue exclusively + */ + dbg(DBG_TX, "fst_process_tx_work_q\n"); + spin_lock_irqsave(&fst_work_q_lock, flags); + work_txq = fst_work_txq; + fst_work_txq = 0; + spin_unlock_irqrestore(&fst_work_q_lock, flags); + + /* + * Call the bottom half for each card with work waiting + */ + for (i=0; i>1; + } +} + +static void +fst_process_int_work_q ( unsigned long work_q) +{ + unsigned long flags; + u64 work_intq; + int i; + + /* + * Grab the queue exclusively + */ + dbg(DBG_INTR, "fst_process_int_work_q\n"); + spin_lock_irqsave(&fst_work_q_lock, flags); + work_intq = fst_work_intq; + fst_work_intq = 0; + spin_unlock_irqrestore(&fst_work_q_lock, flags); + + /* + * Call the bottom half for each card with work waiting + */ + for (i=0; i>1; + } +} +/* Card control functions + * ====================== + */ +/* Place the processor in reset state + * + * Used to be a simple write to card control space but a glitch in the latest + * AMD Am186CH processor means that we now have to do it by asserting and de- + * asserting the PLX chip PCI Adapter Software Reset. Bit 30 in CNTRL register + * at offset 9052_CNTRL. Note the updates for the TXU. + */ +static inline void +fst_cpureset ( struct fst_card_info *card ) +{ + unsigned char interrupt_line_register; + unsigned long j = jiffies + 1; + unsigned int regval; + + if (card->family == FST_FAMILY_TXU) + { + if (pci_read_config_byte(card->device, PCIILR, &interrupt_line_register)) + { + dbg (DBG_ASS, "Error in reading interrupt line register\n"); + } + /* + * Assert PLX software reset and Am186 hardware reset + * and then deassert the PLX software reset but 186 still in reset + */ + outw ( 0x440f, card->pci_conf + CNTRL_9054 + 2 ); + outw ( 0x040f, card->pci_conf + CNTRL_9054 + 2 ); + /* + * We are delaying here to allow the 9054 to reset itself + */ + j = jiffies + 1; + while (jiffies < j) + /* Do nothing */; + outw ( 0x240f, card->pci_conf + CNTRL_9054 + 2 ); + /* + * We are delaying here to allow the 9054 to reload its eeprom + */ + j = jiffies + 1; + while (jiffies < j) + /* Do nothing */; + outw ( 0x040f, card->pci_conf + CNTRL_9054 + 2 ); + + if (pci_write_config_byte(card->device, PCIILR, interrupt_line_register)) + { + dbg (DBG_ASS, "Error in writing interrupt line register\n"); + } + + } + else + { + regval = inl ( card->pci_conf + CNTRL_9052 ); + + outl ( regval | 0x40000000, card->pci_conf + CNTRL_9052 ); + outl ( regval & ~0x40000000, card->pci_conf + CNTRL_9052 ); + } +} + +/* Release the processor from reset + */ +static inline void +fst_cpurelease ( struct fst_card_info *card ) +{ + if (card->family == FST_FAMILY_TXU) + { + /* + * Force posted writes to complete + */ + (void) readb (card->mem); + + /* + * Release LRESET DO = 1 + * Then release Local Hold, DO = 1 + */ + outw ( 0x040e, card->pci_conf + CNTRL_9054 + 2 ); + outw ( 0x040f, card->pci_conf + CNTRL_9054 + 2 ); + } + else + { + (void) readb ( card->ctlmem ); + } +} + +/* Interrupt the card + */ +static inline void +fst_intr_card ( struct fst_card_info *card ) +{ + if (card->family == FST_FAMILY_TXU) + { + (void) writeb ( 0xff, card->ctlmem ); + } + else + { + /* Not sure what to do here) + */ + outw ( 0x0543, card->pci_conf + INTCSR_9052 ); + } +} + +/* Clear the cards interrupt flag + */ +static inline void +fst_clear_intr ( struct fst_card_info *card ) +{ + if (card->family == FST_FAMILY_TXU) + { + (void) readb ( card->ctlmem ); + } + else + { + /* Poke the appropriate PLX chip register (same as enabling interrupts) + */ + outw ( 0x0543, card->pci_conf + INTCSR_9052 ); + } +} + +/* Enable card interrupts + */ +static inline void +fst_enable_intr ( struct fst_card_info *card ) +{ + if (card->family == FST_FAMILY_TXU) + { + outl ( 0x0f0c0900, card->pci_conf + INTCSR_9054 ); + } + else + { + outw ( 0x0543, card->pci_conf + INTCSR_9052 ); + } +} + +/* Disable card interrupts + */ +static inline void +fst_disable_intr ( struct fst_card_info *card ) +{ + if (card->family == FST_FAMILY_TXU) + { + outl ( 0x00000000, card->pci_conf + INTCSR_9054 ); + } + else + { + outw ( 0x0000, card->pci_conf + INTCSR_9052 ); + } +} +static int +fst_proc_info ( char *buffer, char **start, off_t offset, int length ) +{ + int len=0; + struct fst_card_info *card; + int c; + int port_count=0; + + len = sprintf ( buffer,"FarSync WAN Driver version %s-%s\n", + FST_USER_VERSION, FST_PATCH_LEVEL ); + len += sprintf ( buffer + len,"%d Cards found\n", + fst_ncards ); + for ( c = 0 ; c < fst_ncards ; c++ ) { + card = fst_cards_list[c]; + len += sprintf ( buffer + len, + "\t%s-%s: %s IRQ%d,\t%d ports, State: %s \n", + port_to_dev(&card->ports[0])->name, + port_to_dev(&card->ports[card->nports-1])->name, + type_strings[card->type], + card->irq, card->nports, + state_strings[card->state] ); + port_count += card->nports; + } + len += sprintf ( buffer + len, "Total number of ports = %d\n", + port_count); +#ifdef FST_TTY_CHAR + len += sprintf ( buffer + len, "Total number of async connects = %d\n", + fst_tty_cnt); +#endif + return len; +} + +/* Process the result of trying to pass a recieved frame up the stack + */ +static void fst_process_rx_status ( int rx_status , char *name) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) + switch (rx_status) + { + case NET_RX_SUCCESS: + { + /* + * Nothing to do here + */ + break; + } + + case NET_RX_CN_LOW: + { + printk_warn("%s: Receive Low Congestion\n", name); + break; + } + + case NET_RX_CN_MOD: + { + printk_warn("%s: Receive Moderate Congestion\n", name); + break; + } + + case NET_RX_CN_HIGH: + { + printk_warn("%s: Receive High Congestion\n", name); + break; + } + + case NET_RX_DROP: + { + printk_err("%s: Received packet dropped\n", name); + break; + } + } +#endif +} + + +static int gen_mon_packet(struct sk_buff *skb, struct fst_port_info *port, + int tx_rx_ind) +{ + /* + * We need to duplicate the skb and send it up to + * the monitor application + */ + struct sk_buff *mon_skb; + int rx_status = 0; + struct fstioc_mon header; + + /* + * Allocate SKB. Length of frame to monitor + length of the + * header we prepend. + */ + if (( mon_skb = dev_alloc_skb ( skb->len+sizeof(struct fstioc_mon) )) == NULL ) + { + dbg ( DBG_INTR,"gen_mon_packet: can't allocate skb\n"); + return 1; + } + + /* + * Setup the header + */ + header.version = FSTIOC_MON_VERSION; + header.tx_rx_ind = tx_rx_ind; + header.sequence = port->sequence++; + header.timestamp = jiffies*1000/HZ; + header.length = skb->len; + + memcpy(skb_put (mon_skb, sizeof(struct fstioc_mon)), &header, + sizeof(struct fstioc_mon)); + + /* Push upstream */ + dbg(DBG_INTR, "Pushing monitor frame up the stack\n"); + mon_skb->protocol = htons ( ETH_P_DIAG ); + mon_skb->pkt_type = PACKET_HOST; + skb_reset_mac_header(mon_skb); + mon_skb->dev = port_to_dev(port); + memcpy(skb_put(mon_skb, skb->len), skb->data, skb->len); + dbg(DBG_INTR, "Monitor packet length is %d\n", mon_skb->len); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) + rx_status = netif_rx ( mon_skb ); + fst_process_rx_status( rx_status, port_to_dev(port)->name ); + if (rx_status == NET_RX_DROP) + { + dbg(DBG_ASS, "Could not deleiver monitor message"); + return 1; + } +#else + netif_rx(mon_skb); +#endif + return 0; +} + +static void +fst_notify_state_change ( struct fst_card_info *card, struct fst_port_info *port ) +{ + struct sk_buff *skb; + int rx_status = 0; + + dbg(DBG_INTR, "fst_notify_state_change: %s\n", port_to_dev(port)->name); + if (port->notify_mode) + { + /* Allocate SKB */ + if (( skb = dev_alloc_skb ( 0 )) == NULL ) + { + dbg ( DBG_INTR,"notify_state_change: can't allocate skb\n"); + } + + /* + * We know the length we need to receive, = 0 + */ + skb_put( skb, 0 ); + + /* Push upstream */ + dbg(DBG_INTR, "Pushing frame up the stack\n"); + if ( port->proto == FST_HDLC || port->proto == FST_PPP ) + { + /* Mark for further processing by sPPP module */ + skb->protocol = htons ( ETH_P_WAN_PPP ); + } + else + { + /* DEC customer specific protocol (since nothing defined for + * marking raw data), at least one other driver uses this value + * for this purpose. + */ + skb->protocol = htons ( ETH_P_CUST ); + skb->pkt_type = PACKET_HOST; + } + skb_reset_mac_header(skb); + skb->dev = port_to_dev(port); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0) + if (port->char_file) +#ifdef FST_TTY_CHAR + rx_status = tty_signal_rx_frame(port, skb); +#else + rx_status = char_queue_rx_frame(port, skb); +#endif + else + { + rx_status = netif_rx ( skb ); + fst_process_rx_status( rx_status, port_to_dev(port)->name ); + } + if (rx_status == NET_RX_DROP) + { + dbg(DBG_ASS, "Could not deliver state change message"); + } +#else + netif_rx(skb); +#endif + } + else + { + dbg(DBG_INTR, "Notify mode not on\n"); + } + +} +/* Issue a Mailbox command for a port. + * Note we issue them on a fire and forget basis, not expecting to see an + * error and not waiting for completion. + */ +static void +fst_issue_cmd ( struct fst_port_info *port, unsigned short cmd ) +{ + struct fst_card_info *card; + unsigned short mbval; + unsigned long flags; + int safety; + + card = port->card; + spin_lock_irqsave ( &card->card_lock, flags ); + mbval = FST_RDW ( card, portMailbox[port->index][0]); + + safety = 0; + /* Wait for any previous command to complete */ + while ( mbval > NAK ) + { + spin_unlock_irqrestore ( &card->card_lock, flags ); + schedule_timeout ( 1 ); + spin_lock_irqsave ( &card->card_lock, flags ); + + if ( ++safety > 2000 ) + { + printk_err ("Mailbox safety timeout\n"); + break; + } + + mbval = FST_RDW ( card, portMailbox[port->index][0]); + } + if ( safety > 0 ) + { + dbg ( DBG_CMD,"Mailbox clear after %d jiffies\n", safety ); + } + if ( mbval == NAK ) + { + dbg ( DBG_CMD,"issue_cmd: previous command was NAK'd\n"); + } + + FST_WRW ( card, portMailbox[port->index][0], cmd ); + + if ( cmd == ABORTTX || cmd == STARTPORT ) + { + port->txpos = 0; + port->txipos = 0; + port->start = 0; + } + + spin_unlock_irqrestore ( &card->card_lock, flags ); +} + + +/* Port output signals control + */ +static inline void +fst_op_raise ( struct fst_port_info *port, unsigned int outputs ) +{ + outputs |= FST_RDL ( port->card, v24OpSts[port->index]); + FST_WRL ( port->card, v24OpSts[port->index], outputs ); + + if ( port->run ) + fst_issue_cmd ( port, SETV24O ); +} + +static inline void +fst_op_lower ( struct fst_port_info *port, unsigned int outputs ) +{ + outputs = ~outputs & FST_RDL ( port->card, v24OpSts[port->index]); + FST_WRL ( port->card, v24OpSts[port->index], outputs ); + + if ( port->run ) + fst_issue_cmd ( port, SETV24O ); +} + + +/* * Setup port Rx buffers */ static void -fst_rx_config(struct fst_port_info *port) +fst_rx_config ( struct fst_port_info *port ) +{ + int i; + int pi; + unsigned int offset; + unsigned long flags; + struct fst_card_info *card; + + pi = port->index; + card = port->card; + spin_lock_irqsave ( &card->card_lock, flags ); + FST_WRB ( card, portConfig[pi].numRxBuffers, (u8) port->num_rx_buffers ); + for ( i = 0 ; i < port->num_rx_buffers ; i++ ) + { + offset = BUF_OFFSET ( rxBuffer[pi][0]) + i*port->rx_buffer_size; + + FST_WRW ( card, rxDescrRing[pi][i].ladr, (u16) offset ); + FST_WRB ( card, rxDescrRing[pi][i].hadr, (u8)( offset >> 16 )); + FST_WRW ( card, rxDescrRing[pi][i].bcnt, + cnv_bcnt ( port->rx_buffer_size )); + FST_WRW ( card, rxDescrRing[pi][i].mcnt, FST_MAX_MTU-2 ); + FST_WRB ( card, rxDescrRing[pi][i].bits, DMA_OWN ); + } + port->rxpos = 0; + spin_unlock_irqrestore ( &card->card_lock, flags ); +} + + +/* + * Setup port Tx buffers + */ +static void +fst_tx_config ( struct fst_port_info *port ) +{ + int i; + int pi; + unsigned int offset; + unsigned long flags; + struct fst_card_info *card; + + pi = port->index; + card = port->card; + spin_lock_irqsave ( &card->card_lock, flags ); + FST_WRB ( card, portConfig[pi].numTxBuffers, (u8) port->num_tx_buffers ); + for ( i = 0 ; i < port->num_tx_buffers ; i++ ) + { + offset = BUF_OFFSET ( txBuffer[pi][0]) + i*port->tx_buffer_size; + + FST_WRW ( card, txDescrRing[pi][i].ladr, (u16) offset ); + FST_WRB ( card, txDescrRing[pi][i].hadr, (u8)( offset >> 16 )); + FST_WRW ( card, txDescrRing[pi][i].bcnt, 0 ); + FST_WRB ( card, txDescrRing[pi][i].bits, 0 ); + } + port->txpos = 0; + port->txipos = 0; + port->start = 0; + spin_unlock_irqrestore ( &card->card_lock, flags ); +} + +/* Check that the shared memory configuration is one that we can handle + * and that some basic parameters are correct + */ +static void +check_started_ok ( struct fst_card_info *card ) +{ + int i; + + /* Check structure version and end marker */ + if ( FST_RDW ( card, smcVersion ) != SMC_VERSION ) + { + printk_err ("Bad shared memory version %d expected %d\n", + FST_RDW ( card, smcVersion ), SMC_VERSION ); + card->state = FST_BADVERSION; + return; + } + if ( FST_RDL ( card, endOfSmcSignature ) != END_SIG ) + { + printk_err ("Missing shared memory signature %x\n", + FST_RDL ( card, endOfSmcSignature )); + printk_err ("size of shared memory structure is %x\n", + sizeof(struct fst_shared)); + card->state = FST_BADVERSION; + return; + } + /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ + if (( i = FST_RDB ( card, taskStatus )) == 0x01 ) + { + card->state = FST_RUNNING; + /* + * If there is any card specific initialisation required + * this is the place to do it. After the loader has + * zero'd the memory + */ + if (card->type == FST_TYPE_DSL_S1) + { + FST_WRB ( card, dslConfig.snrth, 5 ); + FST_WRB ( card, dslConfig.lpath, 30 ); + } + } + else if ( i == 0xFF ) + { + printk_err ("Firmware initialisation failed. Card halted\n"); + card->state = FST_HALTED; + return; + } + else if ( i != 0x00 ) + { + printk_err ("Unknown firmware status 0x%x\n", i ); + card->state = FST_HALTED; + return; + } + + /* Finally check the number of ports reported by firmware against the + * number we assumed at card detection. Should never happen with + * existing firmware etc so we just report it for the moment. + */ + if ( FST_RDL ( card, numberOfPorts ) != card->nports ) + { + printk_warn ("Port count mismatch on card %d." + " Firmware thinks %d we say %d\n", card->card_no, + FST_RDL ( card, numberOfPorts ), card->nports ); + } +} + +/* + * ATM Fifo functions + */ + +static void atm_fifo_init (struct fst_card_info *card) +{ + struct fst_fifo fifo; + + /* + * Initialise the dsl fifo's to zeros + */ + dbg(DBG_ASS, "Zeroing %x bytes of dsl tx fifo\n", + sizeof(struct dsl_tx_fifo)); + memset_io(card->mem+DT_BASE, 0, sizeof(struct dsl_tx_fifo)); + dbg(DBG_ASS, "Zeroing %x bytes of dsl rx fifo\n", + sizeof(struct dsl_rx_fifo)); + memset_io(card->mem+DR_BASE, 0, sizeof(struct dsl_rx_fifo)); + + /* + * Initiliase the local copy of the fifo header and then + * Copy it to shared memeory. + * Do the command fifo first followed by the response + */ + fifo.fifo_length = (u16)MAX_DSL_TX_BUFFER; + fifo.read_idx = (u16)fifo.fifo_length-1; + fifo.write_idx = (u16)fifo.fifo_length-1; + fifo.overflow_idx = -1; + fifo.data[0] = 'D'; + fifo.data[1] = 'E'; + fifo.data[2] = 'A'; + fifo.data[3] = 'D'; + memcpy_toio(card->mem+DT_OFFSET(tx_fifo), &fifo, sizeof(struct fst_fifo)); + + fifo.data[0] = 'B'; + fifo.data[1] = 'E'; + fifo.data[2] = 'E'; + fifo.data[3] = 'F'; + memcpy_toio(card->mem+DR_OFFSET(rx_fifo), &fifo, sizeof(struct fst_fifo)); +} + +static int atm_write_into_fifo( struct fst_card_info *card, char * data, + int data_len) +{ + int retval = -1; + int h_size = 0; + struct fst_fifo fifo; + unsigned short avail_fifo_space = 0; + unsigned short used_space = 0; + unsigned short free_space = 0; + unsigned short next_write_idx = 0; + unsigned short first_write_amount = 0; + unsigned short second_write_amount = 0; + int temp = MAX_DSL_TX_BUFFER; /* Needed to fix pesky warning on min macro */ + + dbg(DBG_IOCTL, "atm_write_into_fifo\n"); + /* + * It's OK to read the fifo header in one go, but don't fall into + * the trap of writing it all back. The fields that the card updates + * must not be touched. Therefore we must write back the fields + * individually. + */ + h_size = sizeof(struct fst_fifo) - 4; + dbg(DBG_IOCTL, "%d: atm_write_into_fifo of length %d\n", card->card_no, + data_len); + memcpy_fromio ( &fifo, card->mem + DT_OFFSET ( tx_fifo), h_size); + next_write_idx = (unsigned short) ((fifo.write_idx+1) % fifo.fifo_length); + dbg(DBG_IOCTL, "next_write_idx = %d\n", next_write_idx); + free_space = (unsigned short) ((fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length); + used_space = (unsigned short) (fifo.fifo_length - free_space - 1); + avail_fifo_space = (unsigned short) (MAX_DSL_TX_BUFFER - used_space); + avail_fifo_space = (unsigned short) min(avail_fifo_space, + (unsigned short)temp); + + if (fifo.overflow_idx == -1) + { + dbg(DBG_IOCTL, "tx_fifo.overflow_idx = -1\n"); + if (free_space > data_len) + { + dbg(DBG_IOCTL, "And there is space to write data %d\n", + avail_fifo_space); + /* How much of it can we write into the end fragment */ + first_write_amount = (unsigned short) min((unsigned short) data_len, + (unsigned short) (fifo.fifo_length - next_write_idx)); + dbg(DBG_IOCTL, "First write amount is %d\n", first_write_amount); + dbg(DBG_IOCTL, "Next write index is %d\n", next_write_idx); + dbg(DBG_IOCTL, "Copying to offset %x\n", + DT_OFFSET(tx_fifo)+h_size+next_write_idx); + if (first_write_amount > FST_MIN_DMA_LEN) + { + memcpy(card->tx_dma_handle_host, data, data_len); + fst_tx_dsl_dma(card, + (char *)card->tx_dma_handle_card, + (char *)DT_OFFSET(tx_fifo)+h_size+next_write_idx, + first_write_amount); + } + else + { + memcpy_toio (card->mem+DT_OFFSET(tx_fifo)+h_size+next_write_idx, + data, first_write_amount); + } + fifo.write_idx = (unsigned short) ((fifo.write_idx + first_write_amount) + % fifo.fifo_length); + dbg(DBG_IOCTL, "Write Index updated to %d\n", fifo.write_idx); + if (first_write_amount != data_len) + { + dbg(DBG_IOCTL, "We need a second write chunk because of wrap\n"); + /* were do we start writing the next chunk to */ + next_write_idx = (unsigned short) ((fifo.write_idx + 1) % + fifo.fifo_length); + + /* write the rest into the starting fragment */ + second_write_amount = (unsigned short) (data_len - first_write_amount); + dbg(DBG_IOCTL, "Second write amount is %d\n", second_write_amount); + dbg(DBG_IOCTL, "Next write index is %d\n", next_write_idx); + dbg(DBG_IOCTL, "Copying to offset %x\n", + DT_OFFSET(tx_fifo)+h_size+next_write_idx); + if (first_write_amount > FST_MIN_DMA_LEN) + { + fst_tx_dsl_dma(card, + (char *)card->tx_dma_handle_card+first_write_amount, + (char *)DT_OFFSET(tx_fifo)+h_size+next_write_idx, + second_write_amount); + } + else + { + memcpy_toio (card->mem+DT_OFFSET(tx_fifo)+h_size+next_write_idx, + data+first_write_amount, second_write_amount); + } + fifo.write_idx = (unsigned short) ((fifo.write_idx + + second_write_amount) + % fifo.fifo_length); + } + dbg(DBG_IOCTL, "atm_fifo_write: returned %d bytes written\n", + data_len); + retval = data_len; + if (data_len != first_write_amount + second_write_amount) + { + printk_err("Error in atm_write)into_fifio\n"); + printk_err("data_len = %d fwa=%d swa = %d\n", data_len, + first_write_amount, second_write_amount); + } + } + else + { + /* + * Setting the overflow condition seems to upset the card + * at the moment, so don't do it + */ + //fifo.overflow_idx = fifo.write_idx; + retval = 0; + dbg(DBG_ASS, "atm_fifo_write: Not enough room for next pdu %d\n", + data_len); + dbg(DBG_ASS, "free space = %d used space = %d available = %d\n", + free_space, used_space, avail_fifo_space); + dbg(DBG_ASS, "Fifo header is: w_idx = %d r_idx = %d\n", + fifo.write_idx, fifo.read_idx); + FST_WRB ( card, dslControl.spare[0], 0xff ); + } + + } + dbg(DBG_IOCTL, "Updating the fifo header w_idx=%x r_idx=%x\n", + fifo.write_idx, fifo.read_idx); + FST_DT_WRW(card, tx_fifo[WRITE_IDX], fifo.write_idx); + FST_DT_WRW(card, tx_fifo[OVERFLOW_IDX], fifo.overflow_idx); + return retval; +} + +static void atm_send_idle_cell ( struct fst_card_info *card) +{ + dbg(DBG_ATM, "Sending idle cell\n"); + atm_write_into_fifo(card, atm_idle_cell, 53); + FST_WRW(card, dslControl.bytesToSend, 53); + fst_intr_card(card); +} + +static int atm_check_read_length(struct fst_card_info *card) +{ + int h_size = 0; + struct fst_fifo fifo; + unsigned short amount_to_read = 0; + + /* + * Return the amount of data in the rx fifo + */ + + dbg(DBG_IOCTL, "%d: atm_check_read_length\n", card->card_no); + h_size = sizeof(struct fst_fifo) - 4; + memcpy_fromio(&fifo, card->mem + DR_OFFSET(rx_fifo), h_size); + amount_to_read = (unsigned short) ((fifo.write_idx - fifo.read_idx + + fifo.fifo_length) % fifo.fifo_length); + dbg(DBG_IOCTL, "Found %d bytes of data (%d -%d) in fifo of length %d\n", + amount_to_read, fifo.write_idx, fifo.read_idx, fifo.fifo_length); + /* + * If amount to read is less than 1 cell and the overflow index is not + * 0xffff then reset overflow index to 0xffff + */ + if ((amount_to_read < 53) && (fifo.overflow_idx != -1)) + { + dbg(DBG_ASS, "Resetting overflow index to -1\n"); + fifo.overflow_idx = 0xffff; + FST_DR_WRW(card, rx_fifo[OVERFLOW_IDX], fifo.overflow_idx); + } + return amount_to_read; +} + +static void atm_empty_rx_fifo(struct fst_card_info *card) +{ + int h_size = 0; + struct fst_fifo fifo; + + /* + * Set the rx fifo to empty + */ + + dbg(DBG_IOCTL, "atm_empty_rx_fifo\n"); + h_size = sizeof(struct fst_fifo) - 4; + memcpy_fromio(&fifo, card->mem + DR_OFFSET(rx_fifo), h_size); + fifo.read_idx = fifo.write_idx; + FST_DR_WRW(card, rx_fifo[READ_IDX], fifo.read_idx); + dbg(DBG_IOCTL, "atm_empty_rx_fifo: Setting read_idx to %x\n", fifo.read_idx); + return; +} + +static int atm_read_from_fifo(struct fst_card_info *card, char * data, + int accumulated_len) +{ + int h_size = 0; + struct fst_fifo fifo; + unsigned short next_read_idx = 0; + unsigned short amount_to_read = 0; + char *data1 = NULL; + unsigned short length1; + char *data2 = NULL; + unsigned short length2; + int saved_len; + + /* + * This function will read one cell at a time + * We need to check each cell to see if it the last in a PDU + * Return 0 if not last cell + * Return 1 if last cell + */ + dbg(DBG_IOCTL, "%d: atm_read_from_fifo\n", card->card_no); + h_size = sizeof(struct fst_fifo) - 4; + memcpy_fromio(&fifo, card->mem + DR_OFFSET(rx_fifo), h_size); + amount_to_read = (unsigned short) ((fifo.write_idx - fifo.read_idx + + fifo.fifo_length) % fifo.fifo_length); + saved_len = amount_to_read; + if (amount_to_read >= 53) + { + amount_to_read = 53; + /* where should we start reading from ? */ + next_read_idx = (unsigned short) ((fifo.read_idx + 1) % + fifo.fifo_length); + dbg(DBG_IOCTL, "Next read index = %d\n", next_read_idx); + data1 = card->mem + DR_OFFSET(rx_fifo) + h_size + next_read_idx; + length1 = (unsigned short) min((unsigned short) amount_to_read, + (unsigned short)(fifo.fifo_length - next_read_idx)); + dbg(DBG_IOCTL, "length1 = %d\n", length1); + + data2 = card->mem + DR_OFFSET(rx_fifo) + h_size; + length2 = (unsigned short) (amount_to_read - length1); + if (length1 == 53) + { + fst_rx_dsl_dma(card, + (char *)card->rx_dma_handle_card, + (char *)DR_OFFSET(rx_fifo) + h_size + next_read_idx, + length1); + memcpy(data, card->rx_dma_handle_host, length1); + } + else + { + memcpy_fromio(data, data1, length1); + } + if (length2) + { + dbg(DBG_IOCTL, "length2 = %d\n", length2); + if (length1 == 53) + { + fst_rx_dsl_dma(card, + (char *)card->rx_dma_handle_card, + (char *)DR_OFFSET(rx_fifo) + h_size, + length2); + memcpy(data+length1, card->rx_dma_handle_host, length2); + } + else + { + memcpy_fromio(&data[length1], data2, length2); + } + } + fifo.read_idx = (unsigned short) ((fifo.read_idx + + amount_to_read) % + fifo.fifo_length); + FST_DR_WRW(card, rx_fifo[READ_IDX], fifo.read_idx); + dbg(DBG_IOCTL, "Finished read from fifo\n"); + /* + * if the accumulated length + this cell exceeds max then + * return error + */ + if ((accumulated_len + 53) > FST_MAX_ATM_MTU) + { + printk_err("atm_read_from_fifo: max mtu size exceeded %d\n", + FST_MAX_ATM_MTU); + return -1; + } + /* + * Check the cell PTI for last cell + */ + if (data[3] & 0x2) + { + /* + * PTI set so last frame + * Check header + */ + if (memcmp(data, card->ports[0].last_atm_cell_header, 4) == 0) + { + return 1; + } + printk_err("Invalid last cell header, length so far = %d len in fifo = %d\n", + accumulated_len, saved_len); + printk_err("Header is %x %x %x %x \n", data[0], data[1], data[2], + data[3]); + printk_err("Write Index = %d Read Index = %d\n", fifo.write_idx, + fifo.read_idx); + FST_RDB ( card, endOfSmcSignature); + dbg(DBG_ATM, "Triggering analyser %x\n", WIN_OFFSET(endOfSmcSignature)); + return -1; + } + else + { + /* + * Check header + */ + if (memcmp(data, card->ports[0].atm_cell_header, 4) == 0) + { + return 0; + } + printk_err("Invalid cell header, length so far = %d len in fifo = %d\n", + accumulated_len, saved_len); + printk_err("Header is %x %x %x %x \n", data[0], data[1], data[2], + data[3]); + printk_err("Write Index = %d Read Index = %d\n", fifo.write_idx, + fifo.read_idx); + FST_RDB ( card, endOfSmcSignature); + dbg(DBG_ATM, "Triggering analyser %x\n", WIN_OFFSET(endOfSmcSignature)); + return -1; + } + } + return 0; +} +/* + * ATM Interface functions + */ + +/* + * CRC Routines from net/wan/sbni.c) + * table generated by Rocksoft^tm Model CRC Algorithm Table Generation Program V1.0 + */ +#define crc32( crc, mem, len) calc_crc(mem, len, crc); +#define CRC32_REMAINDER CBF43926 +#define CRC32_INITIAL 0xffffffff +#define CRC32(c,crc) (crc32tab[((size_t)(crc>>24) ^ (c)) & 0xff] ^ (((crc) << 8))) +unsigned long crc32tab[256] = +{ + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +#ifdef CRCASM +unsigned int calc_crc(char *mem, int len, unsigned initial) +{ + unsigned crc, dummy_len; + __asm__ ( + "xorl %%eax,%%eax\n\t" + "1:\n\t" + "movl %%edx,%%eax\n\t" + "shrl $16,%%eax\n\t" + "lodsb\n\t" + "xorb %%ah,%%al\n\t" + "andl $255,%%eax\n\t" + "shll $8,%%edx\n\t" + "xorl (%%edi,%%eax,4),%%edx\n\t" + "loop 1b" + : "=d" (crc), "=c" (dummy_len) + : "S" (mem), "D" (&crc32tab[0]), "1" (len), "0" (initial) + : "eax" + ); + return crc; +} + +#else + +unsigned int calc_crc(char *mem, int len, unsigned initial) +{ + unsigned crc; + crc = initial; + + dbg(DBG_ATM, "Calc CRC starting at %x\n", initial); + for(;len;mem++,len--) + { + crc = CRC32(*mem, crc); + } + return(crc); +} +#endif + +static int generate_crc (char * trailer, struct sk_buff *skb, int total_length) +{ + /* + * This function is called by generate_pdu to take a pdu and + * generate the CRC bytes for it. The CRC is placed in the correct + * position of the pdu. + */ + unsigned int crc = CRC32_INITIAL; + + dbg(DBG_ATM, "Generate CRC\n"); + crc = ~crc32(crc, skb->data, total_length-4); + dbg(DBG_ATM, "CRC calculated as %x on %d bytes\n", crc, total_length-4); + *trailer++ = (unsigned char) (crc >> 24); + *trailer++ = (unsigned char) (crc >> 16); + *trailer++ = (unsigned char) (crc >> 8); + *trailer++ = (unsigned char) (crc & 0xff); + return 0; +} + +#define ATM_HDR_GFC_SHIFT 28 +#define ATM_HDR_VPI_SHIFT 20 +#define ATM_HDR_VCI_SHIFT 4 +#define ATM_HDR_PTI_SHIFT 1 + +/* + * Tx functions + */ +static void set_atm_header(struct fst_port_info *port, unsigned short vpi, + unsigned short vci) +{ + /* + * We use a fixed structure for the atm cells, with a special one + * for the last cell. This function updates them when the vpi/vci + * has been configured + */ + + int i; + unsigned long header; + unsigned char gfc = 0; + unsigned char pti = 0x0; + unsigned long last_pti = 0x1; + unsigned char mpoa_header[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00}; + + header = 0; + header = ((unsigned long)gfc << ATM_HDR_GFC_SHIFT) + | ((unsigned long)vpi << ATM_HDR_VPI_SHIFT) + | ((unsigned long)vci << ATM_HDR_VCI_SHIFT) + | ((unsigned long)pti << ATM_HDR_PTI_SHIFT) ; + header = htonl(header); + memcpy(&port->atm_cell_header, &header, 4); + header = 0; + header = ((unsigned long)gfc << ATM_HDR_GFC_SHIFT) + | ((unsigned long)vpi << ATM_HDR_VPI_SHIFT) + | ((unsigned long)vci << ATM_HDR_VCI_SHIFT) + | ((unsigned long)last_pti << ATM_HDR_PTI_SHIFT) ; + header = htonl(header); + memcpy(&port->last_atm_cell_header, &header, 4); + dbg(DBG_ASS, "ATM Header calculated as %x %x %x %x\n", port->atm_cell_header[0], + port->atm_cell_header[1], port->atm_cell_header[2], + port->atm_cell_header[3]); + dbg(DBG_ASS, "Last ATM Header calculated as %x %x %x %x\n", + port->last_atm_cell_header[0], port->last_atm_cell_header[1], + port->last_atm_cell_header[2], port->last_atm_cell_header[3]); + memcpy(&port->mpoa_header, &mpoa_header, MPOA_HEADER_LEN); + dbg(DBG_ASS, "MPOA Header set to 0xaa 0xaa 0x03 0x00 0x00 0x00 0x08 0x00\n"); + /* + * Lastly, format an idle cell + */ + atm_idle_cell[0] = 0; + atm_idle_cell[1] = 0; + atm_idle_cell[2] = 0; + atm_idle_cell[3] = 1; + atm_idle_cell[4] = 0x52; + for (i = 0; i< 48; i++) + { + atm_idle_cell[i+5] = 0x6a; + } +} + +static struct sk_buff *generate_pdu (struct fst_port_info *port, + struct sk_buff *frame_ptr) +{ + /* + * This function is called from the start_hard_xmit function when it is + * determined that the frame requires atm encapsulation. The function will + * copy the pdu payload (yes, sorry) into the payload parts of a number of + * atm cells. The last cell is updated with the pdu trailer (including CRC) + * and then the block of cells is handed off to the driver tx_bottom_half + */ + + struct sk_buff *pdu_ptr; + struct sk_buff *cell_ptr; + int pdu_len; + int padded_pdu_len; + unsigned char *pad; + unsigned char *trailer; + int no_of_cells; + char *source; + int i, len_to_copy; + int mpoa_header_len; + + dbg(DBG_ATM, "Generate PDU of length %d\n", frame_ptr->len); + pdu_len = frame_ptr->len; + /* + * Take into account the mpoa header if required + */ + mpoa_header_len = 0; + if (port->encap == ENCAP_MPOA) + { + mpoa_header_len = MPOA_HEADER_LEN; + } + padded_pdu_len = ((frame_ptr->len + mpoa_header_len + 8 + 47)/ 48) * 48; + dbg(DBG_ATM, "Length with atm padding is %d\n", padded_pdu_len); + + /* + * Calculate the number of cells required + */ + no_of_cells = padded_pdu_len / 48; + dbg(DBG_ATM, "No of cells required is %d\n", no_of_cells); + if (!(pdu_ptr = dev_alloc_skb(padded_pdu_len + (no_of_cells*5)))) + { + dbg(DBG_ASS, "Could not allocate skb for atm cells\n"); + return NULL; + } + if (!(cell_ptr = dev_alloc_skb(padded_pdu_len + (no_of_cells*5)))) + { + dbg(DBG_ASS, "Could not allocate skb for atm cells\n"); + return NULL; + } + dbg(DBG_ATM, "Allocated Cells at %p\n", cell_ptr->data); + /* + * Copy the mpoa header in first if required + */ + dbg(DBG_ATM, "length of pdu so far id %d\n", pdu_ptr->len); + if (port->encap == ENCAP_MPOA) + { + memcpy(pdu_ptr->data, port->mpoa_header, MPOA_HEADER_LEN); + pad = skb_put(pdu_ptr, MPOA_HEADER_LEN); + } + dbg(DBG_ATM, "length of pdu so far id %d\n", pdu_ptr->len); + /* + * Copy the frame data into the pdu_ptr structure and add the trailer + */ + memcpy(pdu_ptr->data+mpoa_header_len, frame_ptr->data, frame_ptr->len); + /* + * Lets now zero the pad bytes + */ + dbg(DBG_ATM, "Zero the pad bytes\n"); + pad = skb_put(pdu_ptr, pdu_len); + dbg(DBG_ATM, "length of pdu so far id %d\n", pdu_ptr->len); + pad = skb_put(pdu_ptr, padded_pdu_len - (pdu_len + mpoa_header_len)); + dbg(DBG_ATM, "length of pdu so far id %d\n", pdu_ptr->len); + memset (pad, 0, padded_pdu_len - (pdu_len + mpoa_header_len)); + + /* + * Set the trailer fields + */ + dbg(DBG_ATM, "Setting the trailer bytes\n"); + trailer = pdu_ptr->tail - 8; + pdu_len += mpoa_header_len; + *trailer++ = (unsigned char) 0; /* UU = 0 */ + *trailer++ = (unsigned char) 0; /* CPI = 0 */ + *trailer++ = (unsigned char) (pdu_len >> 8); + *trailer++ = (unsigned char) (pdu_len & 0xff); + /* + * and CRC32 + */ + generate_crc(trailer, pdu_ptr, padded_pdu_len); + + /* + * Now fragment the PDU into atm cells + */ + source = pdu_ptr->data; + for (i = 0; i < no_of_cells; i++) + { + if (i == no_of_cells-1) + { + dbg(DBG_ATM, "Formatting atm final header\n"); + memcpy(skb_put(cell_ptr, 5), port->last_atm_cell_header, 5); + } + else + { + dbg(DBG_ATM, "Formatting atm header for cell %d\n", i); + memcpy(skb_put(cell_ptr, 5), port->atm_cell_header, 5); + } + len_to_copy = 48; /* length of data */ + dbg(DBG_ATM, "Copying %d bytes of data to %p\n", len_to_copy, + cell_ptr->data); + memcpy(skb_put(cell_ptr, len_to_copy), source, len_to_copy); + source += 48; + } + /* + * Free the pdu structure + */ + kfree_skb(pdu_ptr); + return cell_ptr; +} + +/* + * Rx Functions + */ +static int validate_crc ( char *pdu_ptr, int padded_len, unsigned int rcv_crc) +{ + /* + * This function is called by the process_rx_pdu function to check that + * the pdu crc bytes are correct. By this time the pdu has all the cell + * headers stripped, but will include any padding? + */ + + unsigned int crc = CRC32_INITIAL; + + dbg(DBG_ATM, "Validate CRC\n"); + crc = ~crc32(crc, pdu_ptr, padded_len-4); + dbg(DBG_ATM, "CRC recalculated as %x on %d bytes\n", crc, padded_len-4); + if (rcv_crc!= crc) + { + printk_err("CRC anomaly, received = %x, generated = %x\n", + rcv_crc, crc); + return 1; + } + return 0; +} + +static struct sk_buff *process_rx_pdu (struct fst_port_info *port, + struct sk_buff *frame_ptr) +{ + /* + * This is called when a port that has been tagged as supporting + * atm encapsulation receives a frame. At this stage of the implementation + * we know that the frame will contain a single PDU in a number of atm cells. + * We need to get the pdu as a continuous string of bytes (maybe we can + * change this later), verify the CRC is correct and then hand the PDU up + * the network stack + */ + int pdu_len; + int frame_len; + int padded_len; + int no_of_cells; + int i; + struct sk_buff *pdu_ptr; + unsigned int crc; + unsigned char *trailer; + + dbg(DBG_ATM, "Process RX PDU of length %d\n", frame_ptr->len); + /* + * Check for minimum length + */ + if (frame_ptr->len < 53) + { + printk_err("Invalid cell length received %d\n", frame_ptr->len); + return NULL; + } + padded_len = 0; + frame_len = frame_ptr->len; + /* + * How many ATM cells is this? + */ + no_of_cells = frame_len/53; + dbg(DBG_ATM, "No of atm cells = %d\n", no_of_cells); + if (!(pdu_ptr = dev_alloc_skb(frame_len))) + { + dbg(DBG_ATM, "Could not allocate skb for atm cells\n"); + return NULL; + } + + dbg(DBG_ATM, "Allocated PDU at %p\n", pdu_ptr->data); + for (i = 0; i < no_of_cells; i++) + { + dbg(DBG_ATM, "Evaluating Cell Header\n"); + if (i == no_of_cells -1) + { + /* + * Last cell header comparison + */ + if (memcmp(port->last_atm_cell_header, frame_ptr->data, 4) != 0) + { + dbg(DBG_ASS, "Error in last cell header comparison\n"); + dbg(DBG_ASS, "%x %x %x %x\n", frame_ptr->data[0], + frame_ptr->data[1], frame_ptr->data[2], frame_ptr->data[3]); + dev_kfree_skb(pdu_ptr); + return NULL; + } + else + { + dbg(DBG_ATM, "Last cell header OK\n"); + dbg(DBG_ATM, "%x %x %x %x\n", frame_ptr->data[0], frame_ptr->data[1], + frame_ptr->data[2], frame_ptr->data[3]); + } + } + else + { + /* + * Any but last cell header comparison + */ + if (memcmp(port->atm_cell_header, frame_ptr->data, 4) != 0) + { + dbg(DBG_ASS, "Error in cell header comparison\n"); + dbg(DBG_ASS, "%x %x %x %x\n", frame_ptr->data[0], + frame_ptr->data[1], frame_ptr->data[2], frame_ptr->data[3]); + dev_kfree_skb(pdu_ptr); + return NULL; + } + else + { + dbg(DBG_ATM, "Cell header OK\n"); + dbg(DBG_ATM, "%x %x %x %x\n", frame_ptr->data[0], frame_ptr->data[1], + frame_ptr->data[2], frame_ptr->data[3]); + + } + } + dbg(DBG_ATM, "Extracting cell header for cell %d\n", i); + skb_pull(frame_ptr, 5); + dbg(DBG_ATM, "Copying %d bytes of data to %p\n", 48, pdu_ptr->data); + memcpy(skb_put(pdu_ptr, 48), frame_ptr->data, 48); + skb_pull(frame_ptr, 48); + padded_len += 48; + } + trailer = pdu_ptr->data + padded_len - 8; + memcpy(&crc, &trailer[4], 4); + memcpy(&pdu_len, &trailer[2], 2); + crc = htonl(crc); + pdu_len = htons(pdu_len); + dbg(DBG_ATM, "Received CRC is %x\n", crc); + dbg(DBG_ATM, "Received length is %d\n", pdu_len); + /* + * By now we should have the PDU padded to an integral number of cells + */ + if (validate_crc(pdu_ptr->data, padded_len, crc)) + { + /* + * PDU CRC error detected, abort frame + */ + dev_kfree_skb(pdu_ptr); + FST_WRB(port->card, dslControl.spare[2], 0xff); + return NULL; + } + /* + * Remove any mpoa header that is there + */ + if (port->encap == ENCAP_MPOA) + { + /* + * We should see an mpoa header, so check it + */ + if (memcmp(pdu_ptr->data, port->mpoa_header, MPOA_HEADER_LEN) != 0) + { + printk_err("Error in mpoa header comparison\n"); + printk_err("Recvd: %x %x %x %x %x %x %x %x\n", pdu_ptr->data[0], + pdu_ptr->data[1], pdu_ptr->data[2], pdu_ptr->data[3], + pdu_ptr->data[4], pdu_ptr->data[5], pdu_ptr->data[6], + pdu_ptr->data[7]); + printk_err("Expctd: %x %x %x %x %x %x %x %x\n", port->mpoa_header[0], + port->mpoa_header[1], port->mpoa_header[2], + port->mpoa_header[3], port->mpoa_header[4], + port->mpoa_header[5], port->mpoa_header[6], + port->mpoa_header[7]); + dev_kfree_skb(pdu_ptr); + return NULL; + } + else + { + dbg(DBG_ATM, "MPOA Header OK\n"); + skb_pull(pdu_ptr, MPOA_HEADER_LEN); + pdu_len -= MPOA_HEADER_LEN; + } + } + /* + * Validate the length of the pdu against the total length + * received, in case the pdu length is corrupted + */ + if (pdu_len > padded_len) + { + printk_err("Invalid pdu length %d %d\n", pdu_len, padded_len); + dev_kfree_skb(pdu_ptr); + return NULL; + } + /* + * Set the real length of the frame + */ + pdu_ptr->len = pdu_len; + return pdu_ptr; +} +/* Initilaise DMA for PLX 9054 + */ +static inline void +fst_init_dma ( struct fst_card_info *card ) +{ + unsigned short pci_cr; + + /* + * This is only required for the PLX 9054 + */ + if (card->family == FST_FAMILY_TXU) + { + dbg(DBG_INTR, "Initialising DMA\n"); + pci_read_config_word (card->device, PCICR, &pci_cr); + pci_cr |= 0x0004; + pci_write_config_word (card->device, PCICR, pci_cr); /* Enable DMA Bus mastering */ + outl ( 0x00020441, card->pci_conf + DMAMODE0 ); + outl ( 0x00020441, card->pci_conf + DMAMODE1 ); + outl ( 0x0, card->pci_conf + DMATHR ); + } +} + + +/* Tx dma complete interrupt + */ +static void +fst_tx_dma_complete ( struct fst_card_info *card, struct fst_port_info *port, + int len, int txpos, int flags) +{ + dbg(DBG_TX, "fst_tx_dma_complete\n"); + /* + * Everything is now set, just tell the card to go + */ + if (flags & TX_ENP) + port_to_stats(port,tx_packets)++; + port_to_stats(port,tx_bytes) += len; + port_to_dev(port)->trans_start = jiffies; + if (port->transparent_mode) + { + flags = flags & 0xfe; + } + FST_WRB ( card, txDescrRing[port->index][txpos].bits, flags); +} + +/* Rx dma complete interrupt + */ +static void +fst_rx_dma_complete ( struct fst_card_info *card, struct fst_port_info *port, + int len, struct sk_buff *skb, int rxp) +{ + int pi; + int rx_status; + int space_left; + + dbg(DBG_RX, "fst_rx_dma_complete\n"); + pi = port->index; + space_left = (port_to_dev(port)->mtu + MAX_PPP_HEADER) - + port->rxq.frame->len; + if (len <= space_left) + { + memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); + } + else + { + dbg(DBG_ASS, "This skb copy would have exceeded max mtu %d %d %d\n", + port->rxq.frame->len, len, port->rxq.segment_cnt); + dbg(DBG_ASS, "Copying %d bytes\n", space_left); + memcpy(skb_put(skb, space_left), card->rx_dma_handle_host, + space_left); + } + + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + + /* Update stats */ + port_to_stats(port,rx_bytes) += len; + + if (port->rxq.flags & RX_ENP) + { + struct sk_buff *new_skb; + + if (port->monitor_mode) + gen_mon_packet(skb, port, FST_MON_RX); + if ((port->vpi == 0) && (port->vci == 0)) + { + /* + * There was no atm encapsualtion to remove + */ + new_skb = skb; + } + else + { + new_skb = process_rx_pdu( port, skb ); + kfree_skb(skb); + if (new_skb == NULL) + { + printk_err("Error in received atm pdu\n"); + return; + } + } + dbg(DBG_RX, "Pushing the frame up the stack\n"); + /* Push upstream */ + skb_reset_mac_header(new_skb); + new_skb->dev = port_to_dev(port); + if (port->proto == FST_RAW) + { + /* + * Mark it for our own raw sockets interface + */ + new_skb->protocol = htons (ETH_P_CUST); + new_skb->pkt_type = PACKET_HOST; + } + else + { +#if IS_GEN_HDLC + if (port->hdlc_proto == IF_PROTO_HDLC) + { + new_skb->protocol = htons ( ETH_P_IP ); + } + else + { + if (port->hdlc_proto == IF_PROTO_HDLC_ETH) + { + new_skb->protocol = htons ( ETH_P_8021Q ); + } + else + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,23) + new_skb->protocol = hdlc_type_trans (new_skb, + new_skb->dev); +#else + new_skb->protocol = htons (ETH_P_HDLC); +#endif + } + } +#else + new_skb->protocol = htons (ETH_P_WAN_PPP); +#endif + } + if ((port->char_file) || (port->port_mode)) +#ifdef FST_TTY_CHAR + rx_status = tty_signal_rx_frame(port, new_skb); +#else + rx_status = char_queue_rx_frame(port, new_skb); +#endif + else + { + rx_status = netif_rx ( new_skb ); + } + fst_process_rx_status(rx_status, port_to_dev(port)->name); + if (rx_status == NET_RX_DROP) + port_to_stats(port,rx_dropped)++; + port_to_dev(port)->last_rx = jiffies; + port->rxq.frame = NULL; + } +} + +/* + * Receive a frame through the DMA + */ +static inline void +fst_rx_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA and start it + */ + + dbg(DBG_RX, "In fst_rx_dma %p %p %d\n", skb, mem, len); + if (card->dmarx_in_progress) + { + dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n"); + } + + outl ( (unsigned long)skb, card->pci_conf + DMAPADR0 ); /* Copy to here */ + outl ( (unsigned long)mem, card->pci_conf + DMALADR0 ); /* from here */ + outl ( len, card->pci_conf + DMASIZ0 ); /* for this length */ + outl ( 0x00000000c, card->pci_conf + DMADPR0); /* In this direction */ + + /* + * We use the dmarx_in_progress flag to flag the channel as busy + */ + card->dmarx_in_progress = 1; + outb ( 0x03, card->pci_conf + DMACSR0 ); /* Start the transfer */ +} + + +/* + * Send a frame through the DMA + */ +static inline void +fst_tx_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA and start it. + */ + + dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len); + if (card->dmatx_in_progress) + { + dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n"); + } + + outl ( (unsigned long)skb, card->pci_conf + DMAPADR1 ); /* Copy from here */ + outl ( (unsigned long)mem, card->pci_conf + DMALADR1 ); /* to here */ + outl ( len, card->pci_conf + DMASIZ1 ); /* for this length */ + outl ( 0x000000004, card->pci_conf + DMADPR1); /* In this direction */ + + /* + * We use the dmatx_in_progress to flag the channel as busy + */ + card->dmatx_in_progress = 1; + outb ( 0x03, card->pci_conf + DMACSR1 ); /* Start the transfer */ +} + +/* + * Receive a DSL cell through the DMA + */ +static void +fst_rx_dsl_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA, start it, wait for it to finsih and + * then return to where we were called from. + * This is not optimal, but I'm interested to see if it performs well + * enough + */ + int i; + static unsigned int largest_rx_time = 0; + + card->dmarx_in_progress = 1; + dbg(DBG_RX, "In fst_rx_dsl_dma %p %p %d\n", skb, mem, len); + outl ( (unsigned long)skb, card->pci_conf + DMAPADR0 ); /* Copy to here */ + outl ( (unsigned long)mem, card->pci_conf + DMALADR0 ); /* from here */ + outl ( len, card->pci_conf + DMASIZ0 ); /* for this length */ + outl ( 0x00000000c, card->pci_conf + DMADPR0); /* In this direction */ + + /* + * We use the dma_done flag to know when the transfer is complete + */ + + outb ( 0x03, card->pci_conf + DMACSR0 ); /* Start the transfer */ + for (i=0; i< 10000; i++) + { + udelay(10); + if (!card->dmarx_in_progress) + { + break; + } + } + if (i > largest_rx_time) + { + dbg(DBG_ASS, "Time to transfer rxbuffer(%d) = %d microseconds\n", len, i); + largest_rx_time = i; + } +} + +/* + * Send a DSL PDU through the DMA + */ +static void +fst_tx_dsl_dma (struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA, start it, wait for it to finsih and + * then return to where we were called from. + * This is not optimal, but I'm interested to see if it performs well + * enough + */ + int i; + static unsigned int largest_tx_time = 0; + + card->dmatx_in_progress = 1; + dbg(DBG_TX, "In fst_tx_dsl_dma %p %p %d\n", skb, mem, len); + outl ( (unsigned long)skb, card->pci_conf + DMAPADR1 ); /* Copy from here */ + outl ( (unsigned long)mem, card->pci_conf + DMALADR1 ); /* to here */ + outl ( len, card->pci_conf + DMASIZ1 ); /* for this length */ + outl ( 0x000000004, card->pci_conf + DMADPR1); /* In this direction */ + + /* + * We use the dma_done flag to know when the transfer is complete + */ + + outb ( 0x03, card->pci_conf + DMACSR1 ); /* Start the transfer */ + for (i=0; i< 10000; i++) + { + udelay(10); + if (!card->dmatx_in_progress) + { + break; + } + } + if (i > largest_tx_time) + { + dbg(DBG_TX, "Time to transfer txbuffer(%d) = %d microseconds\n", len, i); + largest_tx_time = i ; + } +} +//#fsinclude common/gen2.c +/* TE1 Alarm change interrupt event + */ +static void +fst_intr_te1_alarm ( struct fst_card_info *card, struct fst_port_info *port ) +{ + u8 los; + u8 rra; + u8 ais; + + los = FST_RDB ( card, suStatus.lossOfSignal ); + rra = FST_RDB ( card, suStatus.receiveRemoteAlarm ); + ais = FST_RDB ( card, suStatus.alarmIndicationSignal ); + + if ( los ) + { + /* + * Lost the link + */ + if ( netif_carrier_ok ( port_to_dev(port) )) + { + dbg ( DBG_INTR,"Net carrier off\n"); + netif_carrier_off ( port_to_dev(port) ); + fst_notify_state_change (card, port); + } + } + else + { + /* + * Link available + */ + if ( ! netif_carrier_ok (port_to_dev(port) )) + { + dbg ( DBG_INTR,"Net carrier on\n"); + + /* Poke sPPP to renegotiate */ + if ( port->proto == FST_HDLC || port->proto == FST_PPP ) + { + sppp_reopen ( port_to_dev(port) ); + } + + netif_carrier_on ( port_to_dev(port) ); + fst_notify_state_change (card, port); + } + } + + if (los) + dbg (DBG_INTR, "Assert LOS Alarm\n"); + else + dbg (DBG_INTR, "De-assert LOS Alarm\n"); + if (rra) + dbg (DBG_INTR, "Assert RRA Alarm\n"); + else + dbg (DBG_INTR, "De-assert RRA Alarm\n"); + + if (ais) + dbg (DBG_INTR, "Assert AIS Alarm\n"); + else + dbg (DBG_INTR, "De-assert AIS Alarm\n"); +} + +/* DSL Activation Status change + */ +static void +fst_dsl_acstchg ( struct fst_card_info *card, struct fst_port_info *port ) +{ + /* + * This alarm is generated each time the activation status byte changes + * When it changes we have to read it and process the byte accordingly. + * We only need to know if we are in data state or not so that we can + * assert or deaasert the carrier signal indication + */ + + port->activation_status = FST_RDB ( card, dslStatus.activationStatus ); + if (port->activation_status == port->last_act_status) + { + dbg(DBG_ASS, "%s State change interrupt when state hasn't changed\n", + port_to_dev(port)->name); + return; + } + port->last_act_status = port->activation_status; + if ( port->activation_status >= 64) + { + if ( ! netif_carrier_ok(port_to_dev(port))) + { + printk_info("%s SHDSL sync complete OK\n", port_to_dev(port)->name); + dbg ( DBG_INTR,"Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); + fst_notify_state_change (card, port); + /* + * temp fix: Make sure the rx fifo is empty to dispose + * of bad cell that seems to be there sometimes + */ + atm_empty_rx_fifo(card); + atm_send_idle_cell(card); /* allow rx */ + } + } + else + { + if ( netif_carrier_ok ( port_to_dev(port) )) + { + printk_info("%s SHDSL sync lost %d\n", + port_to_dev(port)->name, port->activation_status); + dbg ( DBG_INTR,"Net carrier off\n"); + netif_carrier_off ( port_to_dev(port) ); + fst_notify_state_change (card, port); + } + } + + /* + * One more possibility for loopback testing + */ + if ( (port->activation_status >= 5) && + (FST_RDB ( card, dslConfig.testMode))) + { + printk_info("SHDSL sync in TestMode\n"); + dbg ( DBG_INTR,"Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); + fst_notify_state_change (card, port); + } +} + +/* Control signal change interrupt event + */ +static void +fst_intr_ctlchg ( struct fst_card_info *card, struct fst_port_info *port ) +{ + int signals; + + signals = FST_RDL ( card, v24DebouncedSts[port->index]); + + if ( signals & (((port->hwif == X21) || (port->hwif == X21D)) + ? IPSTS_INDICATE : IPSTS_DCD )) + { + if ( ! netif_carrier_ok ( port_to_dev(port) )) + { + dbg ( DBG_INTR,"DCD active\n"); + + /* Poke sPPP to renegotiate */ + if ( port->proto == FST_HDLC || port->proto == FST_PPP ) + { + sppp_reopen ( port_to_dev(port) ); + } + + netif_carrier_on ( port_to_dev(port) ); + fst_notify_state_change (card, port); + } + } + else + { + if ( netif_carrier_ok ( port_to_dev(port) )) + { + dbg ( DBG_INTR,"DCD lost\n"); + netif_carrier_off ( port_to_dev(port) ); + fst_notify_state_change (card, port); + } + } +} + +/* Log Rx Errors + */ +static void +fst_log_rx_error ( struct fst_card_info *card, struct fst_port_info *port, + unsigned char dmabits, int rxp, unsigned short len) +{ + /* + * Increment the appropriate error counter + */ + port_to_stats(port,rx_errors)++; + if ( dmabits & RX_OFLO ) + { + port_to_stats(port,rx_fifo_errors)++; + printk_err("Rx fifo error on card %d port %d buffer %d\n", + card->card_no, port->index, rxp); + } + if ( dmabits & RX_CRC ) + { + port_to_stats(port,rx_crc_errors)++; + printk_err("Rx crc error on card %d port %d\n", + card->card_no, port->index); + } + if ( dmabits & RX_FRAM ) + { + port_to_stats(port,rx_frame_errors)++; + printk_err("Rx frame error on card %d port %d\n", + card->card_no, port->index); + } + if ( dmabits & RX_HBUF ) + { + port_to_stats(port,rx_length_errors)++; + printk_err("Rx hardware error on card %d port %d\n", + card->card_no, port->index); + } + if ( dmabits == ( RX_STP | RX_ENP )) + { + port_to_stats(port,rx_length_errors)++; + printk_err("Rx length error (%d) on card %d port %d\n", + len,card->card_no, port->index); + } +} + +/* Rx Error Recovery + */ +static void +fst_recover_rx_error ( struct fst_card_info *card, struct fst_port_info *port, + unsigned char dmabits, int rxp, unsigned short len) { int i; int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; pi = port->index; - card = port->card; - spin_lock_irqsave(&card->card_lock, flags); - for (i = 0; i < NUM_RX_BUFFER; i++) { - offset = BUF_OFFSET(rxBuffer[pi][i][0]); - - FST_WRW(card, rxDescrRing[pi][i].ladr, (u16) offset); - FST_WRB(card, rxDescrRing[pi][i].hadr, (u8) (offset >> 16)); - FST_WRW(card, rxDescrRing[pi][i].bcnt, cnv_bcnt(LEN_RX_BUFFER)); - FST_WRW(card, rxDescrRing[pi][i].mcnt, LEN_RX_BUFFER); - FST_WRB(card, rxDescrRing[pi][i].bits, DMA_OWN); + /* + * Discard the skb if we have one + */ + if (port->rxq.frame != NULL) + { + dev_kfree_skb ( port->rxq.frame ); + dbg(DBG_ASS, "Free'd an skb at %p, card %d port %d\n", + port->rxq.frame, + card->card_no, port->index); + port->rxq.frame = NULL; + } + /* + * Discard buffer descriptors until we see the start of the + * next frame. Note that for long frames this could be in + * a subsequent interrupt. + */ + port->rxq.error_recovery++; + i = 0; + while (( dmabits & ( DMA_OWN | RX_STP )) == 0 ) + { + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + rxp = 0; + if ( ++i > port->num_rx_buffers ) + { + dbg ( DBG_ASS,"intr_rx: Discarding more bufs" + " than we have\n"); + break; + } + dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits ); + dbg(DBG_ASS, "DMA Bits of next buffer was %x\n", dmabits); + } + dbg(DBG_ASS, "There were %d subsequent buffers in error\n", i); + + /* Discard the terminal buffer */ + if ( ! ( dmabits & DMA_OWN )) + { + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + rxp = 0; + } + else + { + /* + * Found start of packet so error recovery + * complete + */ + port->rxq.error_recovery = 0; + } + port->rxpos = rxp; + return; + +} + + + +/* Rx complete interrupt dsl cards + */ +static void +fst_intr_rx_fifo ( struct fst_card_info *card, struct fst_port_info *port ) +{ + int len; + int cell_state; + char cell[53]; + int rx_status; + struct sk_buff *new_skb; + int saved_len; + + /* + * Receive a pdu through the rx fifo + */ + if (card->dmarx_in_progress) + { + printk_err("Trying to start rx dma when already in progress\n"); + return; + } + + if ((len = atm_check_read_length(card)) >= 53) + { + saved_len = len; + /* + * We only want to read complete cells at a time + */ + len = (len/53)*53; + while (len > 0) + { + /* + * We have to have at least one cell to get but have we + * allocated a buffer for it yet? + */ + if (port->rxq.frame == NULL) + { + /* + * We are not currently assembling a cell. This is the first. + */ + if ((port->rxq.frame = dev_alloc_skb(FST_MAX_ATM_MTU+53)) == NULL) + { + dbg ( DBG_ASS,"intr_rx: can't allocate buffer\n"); + port_to_stats(port,rx_dropped)++; + port->rxq.error_recovery++; + + /* Empty the fifo */ + atm_empty_rx_fifo(card); + return; + } + port->rxq.flags = RX_STP; + port->rxq.count = 0; + port->rxq.segment_cnt = 0; + } + /* + * Now try and get the next cell + */ + cell_state = atm_read_from_fifo(card, cell, port->rxq.count); + switch (cell_state) + { + case 1: + { + /* + * This was the last cell of a pdu so + * Update stats + */ + memcpy(skb_put(port->rxq.frame, 53), cell, 53); + port_to_stats(port,rx_packets)++; + port_to_stats(port,rx_bytes) = port->rxq.count + 53; + dbg(DBG_RX, "Received PDU of %d bytes, %d cells\n", + port->rxq.count+53, port->rxq.segment_cnt+1); + dbg(DBG_RX, "Pushing frame up the stack\n"); + if (port->monitor_mode) + gen_mon_packet(port->rxq.frame, port, FST_MON_RX); + if ((port->vpi == 0) && (port->vci == 0)) + { + /* + * There was no atm encapsualtion to remove + */ + new_skb = port->rxq.frame; + } + else + { + new_skb = process_rx_pdu( port, port->rxq.frame ); + kfree_skb(port->rxq.frame); + if (new_skb == NULL) + { + port->rxq.frame = NULL; + dbg(DBG_ASS, "Error in processing rx atm pdu\n"); + port->atm_cells_dropped++; + return; + } + } + /* Push upstream */ + skb_reset_mac_header(new_skb); + new_skb->dev = port_to_dev(port); + if ( port->proto == FST_RAW ) + { + /* DEC customer specific protocol + */ + new_skb->protocol = htons (ETH_P_CUST); + new_skb->pkt_type = PACKET_HOST; + } + else + { +#if IS_GEN_HDLC + if (port->hdlc_proto == IF_PROTO_HDLC) + { + new_skb->protocol = htons ( ETH_P_IP ); + } + else + { + if (port->hdlc_proto == IF_PROTO_HDLC_ETH) + { + new_skb->protocol = htons ( ETH_P_8021Q ); + } + else + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,23) + new_skb->protocol = hdlc_type_trans (new_skb, + new_skb->dev); +#else + new_skb->protocol = htons (ETH_P_HDLC); +#endif + } + } +#else + new_skb->protocol = htons (ETH_P_WAN_PPP); +#endif + } + if ((port->char_file) || (port->port_mode)) +#ifdef FST_TTY_CHAR + rx_status = tty_signal_rx_frame(port, new_skb); +#else + rx_status = char_queue_rx_frame(port, new_skb); +#endif + else + { + rx_status = netif_rx ( new_skb ); + } + fst_process_rx_status( rx_status, port_to_dev(port)->name ); + if (rx_status == NET_RX_DROP) + { + port_to_stats(port,rx_dropped)++; + } + port_to_dev(port)->last_rx = jiffies; + port->rxq.frame = NULL; + break; + } + case 0: + { + /* + * Not the last cell of a pdu + */ + memcpy(skb_put(port->rxq.frame, 53), cell, 53); + port->rxq.count += 53; + port->rxq.segment_cnt++; + dbg(DBG_RX, "rx cell %d, length now %d\n", + port->rxq.segment_cnt, port->rxq.count); + break; + } + default: + { + /* + * An error with the cell format + * free skb and start again + */ + printk_err("Error in rx cell, original length of fifo was %d\n", + saved_len); + kfree_skb(port->rxq.frame); + port->rxq.frame = NULL; + port->atm_cells_dropped++; + } + } /* End case */ + len -= 53; + } /* End while */ } - port->rxpos = 0; - spin_unlock_irqrestore(&card->card_lock, flags); } -/* - * Setup port Tx buffers +/* Rx complete interrupt normal cards */ static void -fst_tx_config(struct fst_port_info *port) +fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port ) { - int i; + unsigned char dmabits; int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; + int rxp; + int rx_status; + unsigned short len; + /* Check we have a buffer to process */ pi = port->index; - card = port->card; - spin_lock_irqsave(&card->card_lock, flags); - for (i = 0; i < NUM_TX_BUFFER; i++) { - offset = BUF_OFFSET(txBuffer[pi][i][0]); - - FST_WRW(card, txDescrRing[pi][i].ladr, (u16) offset); - FST_WRB(card, txDescrRing[pi][i].hadr, (u8) (offset >> 16)); - FST_WRW(card, txDescrRing[pi][i].bcnt, 0); - FST_WRB(card, txDescrRing[pi][i].bits, 0); + rxp = port->rxpos; + dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits ); + if ( dmabits & DMA_OWN ) + { + dbg(DBG_RX | DBG_INTR,"intr_rx: No buffer port %d pos %d\n", + pi, rxp ); + return; + } + if (card->dmarx_in_progress) + { + return; } - port->txpos = 0; - port->txipos = 0; - port->start = 0; - spin_unlock_irqrestore(&card->card_lock, flags); -} - -/* TE1 Alarm change interrupt event - */ -static void -fst_intr_te1_alarm(struct fst_card_info *card, struct fst_port_info *port) -{ - u8 los; - u8 rra; - u8 ais; - los = FST_RDB(card, suStatus.lossOfSignal); - rra = FST_RDB(card, suStatus.receiveRemoteAlarm); - ais = FST_RDB(card, suStatus.alarmIndicationSignal); - if (los) { /* - * Lost the link + * If a multi segment message, we need to assume the frame length ... + */ + if ((dmabits == 0) || (dmabits == RX_STP)) + { + /* + * start or middle frame, assume length is RX_LEN_BUFFER */ - if (netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "Net carrier off\n"); - netif_carrier_off(port_to_dev(port)); + len = port->rx_buffer_size; } - } else { + else + { /* - * Link available + * ... unless it is the last or only buffer + * In this case Get buffer length */ - if (!netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "Net carrier on\n"); - netif_carrier_on(port_to_dev(port)); + len = FST_RDW ( card, rxDescrRing[pi][rxp].mcnt ); + if (!port->transparent_mode) + { + /* Discard the CRC */ + len -= 2; } } - if (los) - dbg(DBG_INTR, "Assert LOS Alarm\n"); - else - dbg(DBG_INTR, "De-assert LOS Alarm\n"); - if (rra) - dbg(DBG_INTR, "Assert RRA Alarm\n"); - else - dbg(DBG_INTR, "De-assert RRA Alarm\n"); + dbg ( DBG_RX,"intr_rx: %d,%d: flags %x len %d\n", pi, rxp, dmabits, len ); + if ( dmabits > ( RX_STP | RX_ENP ) || len > FST_MAX_MTU ) + { + fst_log_rx_error(card, port, dmabits, rxp, len); + fst_recover_rx_error(card, port, dmabits, rxp, len); + return; + } - if (ais) - dbg(DBG_INTR, "Assert AIS Alarm\n"); + /* + * Are we in error recovery mode ? + */ + if (port->rxq.error_recovery) + { + if (dmabits & RX_STP) + { + dbg(DBG_ASS, "Error recovery complete\n"); + port->rxq.error_recovery = 0; + } else - dbg(DBG_INTR, "De-assert AIS Alarm\n"); -} + { + /* + * Still waiting for the next start of packet + * to complete error recovery + * So discard this descriptor + */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + } -/* Control signal change interrupt event + /* + * There were no errors. */ -static void -fst_intr_ctlchg(struct fst_card_info *card, struct fst_port_info *port) -{ - int signals; + dbg(DBG_RX, "Rx with no errors\n"); + if (port->transparent_mode) + { + dmabits |= RX_STP | RX_ENP; + } + if (dmabits & RX_STP) + { + dbg(DBG_RX, "Rx first segment len = %d\n", len); + /* First segment of a frame so allocate SKB */ + if (( port->rxq.frame = dev_alloc_skb ( port_to_dev(port)->mtu + MAX_PPP_HEADER)) == NULL ) + { + dbg ( DBG_ASS,"intr_rx: can't allocate buffer\n"); - signals = FST_RDL(card, v24DebouncedSts[port->index]); + port_to_stats(port,rx_dropped)++; + port->rxq.error_recovery++; - if (signals & (((port->hwif == X21) || (port->hwif == X21D)) - ? IPSTS_INDICATE : IPSTS_DCD)) { - if (!netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "DCD active\n"); - netif_carrier_on(port_to_dev(port)); - } - } else { - if (netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "DCD lost\n"); - netif_carrier_off(port_to_dev(port)); + /* Return descriptor to card */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; } + port->rxq.flags = RX_STP; + port->rxq.count = len; + port->rxq.segment_cnt = 1; } -} - -/* Log Rx Errors - */ -static void -fst_log_rx_error(struct fst_card_info *card, struct fst_port_info *port, - unsigned char dmabits, int rxp, unsigned short len) -{ - struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); + if (dmabits & RX_ENP) + { + if (!(port->rxq.flags & RX_STP)) + { + dbg(DBG_ASS, "End frame without a start\n"); + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } /* - * Increment the appropriate error counter + * Last segment. The length we read into len is the total + * length across all segments. So now we need to ajust it + * to get the bytes in this segment. + */ + port->rxq.flags |= RX_ENP; + port->rxq.count = len; + if (!(dmabits & RX_STP)) + port->rxq.segment_cnt++; + if (port->rxq.segment_cnt > last_segment_cnt) + { + last_segment_cnt = port->rxq.segment_cnt; + dbg(DBG_ASS, "Segment count up to %d\n", last_segment_cnt); + } + port_to_stats(port,rx_packets)++; + if (port->rxq.segment_cnt > 1) + { + /* + * More than one segment so adjustment needed */ - stats->rx_errors++; - if (dmabits & RX_OFLO) { - stats->rx_fifo_errors++; - dbg(DBG_ASS, "Rx fifo error on card %d port %d buffer %d\n", - card->card_no, port->index, rxp); + dbg(DBG_RX, "Adjusting length %d for %d segments\n", len, + port->rxq.segment_cnt); + len -= (port->rxq.segment_cnt-1)*(port->rx_buffer_size); } - if (dmabits & RX_CRC) { - stats->rx_crc_errors++; - dbg(DBG_ASS, "Rx crc error on card %d port %d\n", - card->card_no, port->index); + dbg(DBG_RX, "Rx last segment len = %d\n", len); } - if (dmabits & RX_FRAM) { - stats->rx_frame_errors++; - dbg(DBG_ASS, "Rx frame error on card %d port %d\n", - card->card_no, port->index); + + if (dmabits == 0) + { + if (!(port->rxq.flags & RX_STP)) + { + dbg(DBG_ASS, "Middle frame without a start\n"); + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; } - if (dmabits == (RX_STP | RX_ENP)) { - stats->rx_length_errors++; - dbg(DBG_ASS, "Rx length error (%d) on card %d port %d\n", - len, card->card_no, port->index); + + dbg(DBG_RX, "Rx Middle segment len = %d\n", len); + port->rxq.count += len; + port->rxq.segment_cnt++; } -} -/* Rx Error Recovery + /* + * Finally, some belt and braces checks */ -static void -fst_recover_rx_error(struct fst_card_info *card, struct fst_port_info *port, - unsigned char dmabits, int rxp, unsigned short len) -{ - int i; - int pi; + if (port->rxq.segment_cnt > FST_MAX_SEGMENTS) + { + printk_err("Too many segments received %d\n", port->rxq.segment_cnt); + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + + if (port->rxq.frame == NULL) + { + dbg(DBG_ASS, "Unexpected NULL skb. flags = %x dmabits = %x\n", + port->rxq.flags, dmabits); + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } - pi = port->index; /* - * Discard buffer descriptors until we see the start of the - * next frame. Note that for long frames this could be in - * a subsequent interrupt. + * We know the length we need to receive, len. + * It's not worth using the DMA for reads of less than + * FST_MIN_DMA_LEN */ - i = 0; - while ((dmabits & (DMA_OWN | RX_STP)) == 0) { - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); - rxp = (rxp+1) % NUM_RX_BUFFER; - if (++i > NUM_RX_BUFFER) { - dbg(DBG_ASS, "intr_rx: Discarding more bufs" - " than we have\n"); - break; + + if ((len < FST_MIN_DMA_LEN) || (card->family == FST_FAMILY_TXP)) + { + int space_left; + + space_left = (port_to_dev(port)->mtu + MAX_PPP_HEADER) - + port->rxq.frame->len; + if (len <= space_left) + { + memcpy_fromio ( skb_put ( port->rxq.frame, len ), + card->mem + BUF_OFFSET ( rxBuffer[pi][0]) + + rxp*port->rx_buffer_size, + len ); } - dmabits = FST_RDB(card, rxDescrRing[pi][rxp].bits); - dbg(DBG_ASS, "DMA Bits of next buffer was %x\n", dmabits); + else + { + dbg(DBG_ASS, "This skb copy would have exceeded max mtu %d %d %d\n", + port->rxq.frame->len, len, port->rxq.segment_cnt); + dbg(DBG_ASS, "Copying %d bytes\n", space_left); + memcpy_fromio ( skb_put ( port->rxq.frame, space_left), + card->mem + BUF_OFFSET ( rxBuffer[pi][0]) + + rxp*port->rx_buffer_size, + space_left); } - dbg(DBG_ASS, "There were %d subsequent buffers in error\n", i); + /* Reset buffer descriptor */ + FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); - /* Discard the terminal buffer */ - if (!(dmabits & DMA_OWN)) { - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); - rxp = (rxp+1) % NUM_RX_BUFFER; + /* Update stats */ + port_to_stats(port,rx_bytes) += len; + + if (port->rxq.flags & RX_ENP) + { + struct sk_buff *new_skb; + dbg(DBG_RX, "Pushing frame up the stack\n"); + if (port->monitor_mode) + gen_mon_packet(port->rxq.frame, port, FST_MON_RX); + if ((port->vpi == 0) && (port->vci == 0)) + { + /* + * There was no atm encapsualtion to remove + */ + new_skb = port->rxq.frame; } + else + { + new_skb = process_rx_pdu( port, port->rxq.frame ); + kfree_skb(port->rxq.frame); + if (new_skb == NULL) + { + printk_err("Error in processing rx atm pdu\n"); + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else port->rxpos = rxp; return; - + } + } + /* Push upstream */ + skb_reset_mac_header(new_skb); + new_skb->dev = port_to_dev(port); + if ( port->proto == FST_RAW ) + { + /* DEC customer specific protocol + */ + new_skb->protocol = htons (ETH_P_CUST); + new_skb->pkt_type = PACKET_HOST; + } + else + { +#if IS_GEN_HDLC + if (port->hdlc_proto == IF_PROTO_HDLC) + { + new_skb->protocol = htons ( ETH_P_IP ); + } + else + { + if (port->hdlc_proto == IF_PROTO_HDLC_ETH) + { + new_skb->protocol = htons ( ETH_P_8021Q ); + } + else + { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,23) + new_skb->protocol = hdlc_type_trans (new_skb, + new_skb->dev); +#else + new_skb->protocol = htons (ETH_P_HDLC); +#endif + } + } +#else + new_skb->protocol = htons (ETH_P_WAN_PPP); +#endif + } + if ((port->char_file) || (port->port_mode)) +#ifdef FST_TTY_CHAR + rx_status = tty_signal_rx_frame(port, new_skb); +#else + rx_status = char_queue_rx_frame(port, new_skb); +#endif + else + { + rx_status = netif_rx ( new_skb ); + } + fst_process_rx_status( rx_status, port_to_dev(port)->name ); + if (rx_status == NET_RX_DROP) + { + port_to_stats(port,rx_dropped)++; + } + port_to_dev(port)->last_rx = jiffies; + port->rxq.frame = NULL; + } + } + else + { + card->dma_skb_rx = port->rxq.frame; + card->dma_port_rx = port; + card->dma_len_rx = len; + card->dma_rxpos = rxp; + fst_rx_dma(card, (char*)card->rx_dma_handle_card, + (char*)BUF_OFFSET(rxBuffer[pi][0]) + + rxp*port->rx_buffer_size, + len); + } + if (rxp != port->rxpos) + { + dbg(DBG_ASS, "About to increment rxpos by more than 1\n"); + dbg(DBG_ASS, "rxp = %d rxpos = %d\n", rxp, port->rxpos); + } + if ( ++rxp >= port->num_rx_buffers ) + port->rxpos = 0; + else + port->rxpos = rxp; } -/* Rx complete interrupt + +/* + * The bottom halfs to the ISR + * */ -static void -fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) + +static void do_bottom_half_dsl_tx (struct fst_card_info *card) { - unsigned char dmabits; - int pi; - int rxp; - int rx_status; - unsigned short len; + struct fst_port_info *port; + int bytes_to_send = 0; + int txq_length; struct sk_buff *skb; - struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); + unsigned long flags; - /* Check we have a buffer to process */ - pi = port->index; - rxp = port->rxpos; - dmabits = FST_RDB(card, rxDescrRing[pi][rxp].bits); - if (dmabits & DMA_OWN) { - dbg(DBG_RX | DBG_INTR, "intr_rx: No buffer port %d pos %d\n", - pi, rxp); + port = &card->ports[0]; + dbg(DBG_TX, "do_bottom_half_dsl_tx\n"); + /* + * We can't do anything if the fifo is not ready + * Check bytes_to_send for zero + */ + bytes_to_send = FST_RDW(card, dslControl.bytesToSend); + if (bytes_to_send !=0) + { + dbg(DBG_ASS, "do_bottom_half_dsl_tx: bytesToSend not zero (%d)\n", + bytes_to_send); + /* + * Couldn't do anything on this visit + * reschedule another event + */ + fst_q_work_item(&fst_work_txq, card->card_no); + tasklet_schedule(&fst_tx_task); return; } - if (card->dmarx_in_progress) { + /* + * We might be using the dma for the transfer of data into the fifo + * so if a transfer is already in progress then defer this write + * request until later + */ + if (card->dmatx_in_progress) + { + dbg(DBG_ASS, "do_bottom_half_dsl_tx: dma already in progress\n"); + /* + * Couldn't do anything on this visit + * reschedule another event + */ + fst_q_work_item(&fst_work_txq, card->card_no); + tasklet_schedule(&fst_tx_task); return; } - /* Get buffer length */ - len = FST_RDW(card, rxDescrRing[pi][rxp].mcnt); - /* Discard the CRC */ - len -= 2; - if (len == 0) { /* - * This seems to happen on the TE1 interface sometimes - * so throw the frame away and log the event. + * We can go ahead with a transmission + * So get the next pdu off the tx queue + */ + spin_lock_irqsave ( &card->card_lock, flags ); + if ((txq_length = port->txqe - port->txqs) < 0) + { + /* + * This is the case where one has wrapped and the + * maths gives us a negative number + */ + txq_length = txq_length + FST_TXQ_DEPTH ; + } + spin_unlock_irqrestore ( &card->card_lock, flags ); + if (txq_length > 0) + { + /* + * There is something to send + */ + skb = port->txq[port->txqs].frame; + + /* + * Now send it and update the stats */ - printk_err("Frame received with 0 length. Card %d Port %d\n", + if (atm_write_into_fifo(card, skb->data, skb->len)) + { + port_to_stats(port,tx_packets)++; + port_to_stats(port,tx_bytes) += skb->len; + port_to_dev(port)->trans_start = jiffies; + FST_WRW(card, dslControl.bytesToSend, skb->len); + fst_intr_card(card); + /* + * Update the queue pointer + */ + spin_lock_irqsave ( &card->card_lock, flags ); + port->txqs++; + if (port->txqs == FST_TXQ_DEPTH) + { + port->txqs = 0; + } + if (port->txqs == port->txqe) + { + dbg(DBG_TX, "Card %d Port %d: Tx queue Now empty\n", card->card_no, port->index); - /* Return descriptor to card */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); - - rxp = (rxp+1) % NUM_RX_BUFFER; - port->rxpos = rxp; - return; + fst_notify_state_change (card, port); } - - /* Check buffer length and for other errors. We insist on one packet - * in one buffer. This simplifies things greatly and since we've - * allocated 8K it shouldn't be a real world limitation + spin_unlock_irqrestore ( &card->card_lock, flags ); + /* + * If we have flow control on, can we now release it? */ - dbg(DBG_RX, "intr_rx: %d,%d: flags %x len %d\n", pi, rxp, dmabits, len); - if (dmabits != (RX_STP | RX_ENP) || len > LEN_RX_BUFFER - 2) { - fst_log_rx_error(card, port, dmabits, rxp, len); - fst_recover_rx_error(card, port, dmabits, rxp, len); - return; + if (1) + { + if (txq_length < fst_txq_low) + { + netif_wake_queue ( port_to_dev(port) ); + port->start = 0; } - - /* Allocate SKB */ - if ((skb = dev_alloc_skb(len)) == NULL) { - dbg(DBG_RX, "intr_rx: can't allocate buffer\n"); - - stats->rx_dropped++; - - /* Return descriptor to card */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); - - rxp = (rxp+1) % NUM_RX_BUFFER; - port->rxpos = rxp; - return; } - + dev_kfree_skb ( skb ); + } + else + { /* - * We know the length we need to receive, len. - * It's not worth using the DMA for reads of less than - * FST_MIN_DMA_LEN + * Couldn't do anything on this visit + * reschedule another event */ - - if ((len < FST_MIN_DMA_LEN) || (card->family == FST_FAMILY_TXP)) { - memcpy_fromio(skb_put(skb, len), - card->mem + BUF_OFFSET(rxBuffer[pi][rxp][0]), - len); - - /* Reset buffer descriptor */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); - - /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; - - /* Push upstream */ - dbg(DBG_RX, "Pushing frame up the stack\n"); - if (port->mode == FST_RAW) - skb->protocol = farsync_type_trans(skb, dev); - else - skb->protocol = hdlc_type_trans(skb, dev); - rx_status = netif_rx(skb); - fst_process_rx_status(rx_status, port_to_dev(port)->name); - if (rx_status == NET_RX_DROP) { - stats->rx_dropped++; - } - dev->last_rx = jiffies; - } else { - card->dma_skb_rx = skb; - card->dma_port_rx = port; - card->dma_len_rx = len; - card->dma_rxpos = rxp; - fst_rx_dma(card, (char *) card->rx_dma_handle_card, - (char *) BUF_OFFSET(rxBuffer[pi][rxp][0]), len); + fst_q_work_item(&fst_work_txq, card->card_no); + tasklet_schedule(&fst_tx_task); + dbg(DBG_TX, "No Tx: txqs is now %d txqe is now %d\n", + port->txqs, port->txqe); + return; } - if (rxp != port->rxpos) { - dbg(DBG_ASS, "About to increment rxpos by more than 1\n"); - dbg(DBG_ASS, "rxp = %d rxpos = %d\n", rxp, port->rxpos); } - rxp = (rxp+1) % NUM_RX_BUFFER; - port->rxpos = rxp; } -/* - * The bottom halfs to the ISR - * - */ - -static void -do_bottom_half_tx(struct fst_card_info *card) +static void do_bottom_half_tx (struct fst_card_info *card) { struct fst_port_info *port; int pi; int txq_length; struct sk_buff *skb; unsigned long flags; - struct net_device *dev; - struct net_device_stats *stats; + int tx_length; + if (tx_re_entry) + { + dbg(DBG_ASS, "Re-Entry into do_bottom_half_tx\n"); + return; + } + tx_re_entry++; /* * Find a free buffer for the transmit * Step through each port on this card */ dbg(DBG_TX, "do_bottom_half_tx\n"); - for (pi = 0, port = card->ports; pi < card->nports; pi++, port++) { - if (!port->run) + /* + * If DSL card we need to use the fifo + */ + if (card->type == FST_TYPE_DSL_S1) + { + do_bottom_half_dsl_tx(card); + tx_re_entry = 0; + return; + } + + for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ ) + { + if ( port_to_dev(port) == NULL || ! port->run ) continue; - dev = port_to_dev(port); - stats = hdlc_stats(dev); - while (! - (FST_RDB(card, txDescrRing[pi][port->txpos].bits) & - DMA_OWN) - && !(card->dmatx_in_progress)) { + while (!(FST_RDB ( card, txDescrRing[pi][port->txpos].bits ) & DMA_OWN) + && !(card->dmatx_in_progress)) + { /* * There doesn't seem to be a txdone event per-se * We seem to have to deduce it, by checking the DMA_OWN * bit on the next buffer we think we can use */ - spin_lock_irqsave(&card->card_lock, flags); - if ((txq_length = port->txqe - port->txqs) < 0) { + spin_lock_irqsave ( &card->card_lock, flags ); + if ((txq_length = port->txqe - port->txqs) < 0) + { /* * This is the case where one has wrapped and the * maths gives us a negative number */ - txq_length = txq_length + FST_TXQ_DEPTH; + txq_length = txq_length + FST_TXQ_DEPTH ; } - spin_unlock_irqrestore(&card->card_lock, flags); - if (txq_length > 0) { + spin_unlock_irqrestore ( &card->card_lock, flags ); + if (txq_length > 0) + { /* * There is something to send */ - spin_lock_irqsave(&card->card_lock, flags); - skb = port->txq[port->txqs]; - port->txqs++; - if (port->txqs == FST_TXQ_DEPTH) { - port->txqs = 0; + skb = port->txq[port->txqs].frame; + + /* + * Decrement the segment count. It tells us how many more bits + * to the frame there are left to do + */ + port->txq[port->txqs].segment_cnt--; + /* + * Is this the first, last or only segment of the frame + */ + if (port->txq[port->txqs].segment_cnt == 0) + { + if (port->txq[port->txqs].current_seg == 0) + { + dbg(DBG_TX, "Tx first and last\n"); + port->txq[port->txqs].flags = DMA_OWN | TX_STP | TX_ENP; + tx_length = skb->len; + } + else + { + dbg(DBG_TX, "Tx last\n"); + port->txq[port->txqs].flags = DMA_OWN | TX_ENP; + tx_length = skb->len; + } + } + else + { + if (port->txq[port->txqs].current_seg == 0) + { + dbg(DBG_TX, "Tx first\n"); + port->txq[port->txqs].flags = DMA_OWN | TX_STP; + tx_length = port->tx_buffer_size; + } + else + { + dbg(DBG_TX, "Tx Middle\n"); + port->txq[port->txqs].flags = DMA_OWN; + tx_length = port->tx_buffer_size; } - spin_unlock_irqrestore(&card->card_lock, flags); + } + port->txq[port->txqs].current_seg++; + dbg(DBG_TX, "Setting flags to %x\n", port->txq[port->txqs].flags); + dbg(DBG_TX, "Setting length to %d\n", tx_length); + /* * copy the data and set the required indicators on the * card. */ - FST_WRW(card, txDescrRing[pi][port->txpos].bcnt, - cnv_bcnt(skb->len)); - if ((skb->len < FST_MIN_DMA_LEN) - || (card->family == FST_FAMILY_TXP)) { - /* Enqueue the packet with normal io */ - memcpy_toio(card->mem + - BUF_OFFSET(txBuffer[pi] - [port-> - txpos][0]), - skb->data, skb->len); - FST_WRB(card, - txDescrRing[pi][port->txpos]. - bits, - DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += skb->len; - dev->trans_start = jiffies; - } else { + FST_WRW (card, txDescrRing[pi][port->txpos].bcnt, cnv_bcnt(tx_length)); + if ((tx_length < FST_MIN_DMA_LEN) || (card->family == FST_FAMILY_TXP)) + { + int flags = port->txq[port->txqs].flags; + /* Enqueue the packet with normal io*/ + memcpy_toio ( card->mem + BUF_OFFSET ( txBuffer[pi][0]) + + port->txpos*port->tx_buffer_size, + skb->data, tx_length ); + if (port->transparent_mode) + { + flags = flags & 0xfe; + } + FST_WRB ( card, txDescrRing[pi][port->txpos].bits, flags); + if (port->txq[port->txqs].flags & TX_ENP) + port_to_stats(port,tx_packets)++; + port_to_stats(port,tx_bytes) += tx_length; + port_to_dev(port)->trans_start = jiffies; + } + else + { /* Or do it through dma */ - memcpy(card->tx_dma_handle_host, - skb->data, skb->len); + memcpy(card->tx_dma_handle_host, skb->data, tx_length); card->dma_port_tx = port; - card->dma_len_tx = skb->len; + card->dma_len_tx = tx_length; card->dma_txpos = port->txpos; - fst_tx_dma(card, - (char *) card-> - tx_dma_handle_card, - (char *) - BUF_OFFSET(txBuffer[pi] - [port->txpos][0]), - skb->len); + card->dma_tx_flags = port->txq[port->txqs].flags; + fst_tx_dma(card, (char*)card->tx_dma_handle_card, + (char*)BUF_OFFSET(txBuffer[pi][0]) + + port->txpos*port->tx_buffer_size, + tx_length); } - if (++port->txpos >= NUM_TX_BUFFER) - port->txpos = 0; + + /* + * If this was a first or middle segment we have to adjust skb + */ + if ((port->txq[port->txqs].flags == DMA_OWN) || + (port->txq[port->txqs].flags == (DMA_OWN | TX_STP))) + { + skb_pull(port->txq[port->txqs].frame, tx_length); + } + + /* + * If this was the last segment we have some housekeeping to do + */ + if (port->txq[port->txqs].flags & TX_ENP) + { + spin_lock_irqsave ( &card->card_lock, flags ); + port->txqs++; + if (port->txqs == FST_TXQ_DEPTH) + { + port->txqs = 0; + } + if (port->txqs == port->txqe) + { + dbg(DBG_TX, "Card %d Port %d: Tx queue Now empty\n", + card->card_no, port->index); + fst_notify_state_change (card, port); + } + spin_unlock_irqrestore ( &card->card_lock, flags ); /* * If we have flow control on, can we now release it? */ - if (port->start) { - if (txq_length < fst_txq_low) { - netif_wake_queue(port_to_dev - (port)); + if (1) + { + if (txq_length < fst_txq_low) + { + netif_wake_queue ( port_to_dev(port) ); port->start = 0; } } - dev_kfree_skb(skb); - } else { + dev_kfree_skb ( skb ); + } + } + else + { /* * Nothing to send so break out of the while loop */ break; } + if ( ++port->txpos >= port->num_tx_buffers ) + port->txpos = 0; } } + tx_re_entry = 0; } -static void -do_bottom_half_rx(struct fst_card_info *card) +static void do_bottom_half_rx (struct fst_card_info *card) { struct fst_port_info *port; int pi; - int rx_count = 0; + int rx_count=0; /* Check for rx completions on all ports on this card */ dbg(DBG_RX, "do_bottom_half_rx\n"); - for (pi = 0, port = card->ports; pi < card->nports; pi++, port++) { - if (!port->run) + for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ ) + { + //rx_count = 0; + if ( port_to_dev(port) == NULL || ! port->run ) continue; - - while (!(FST_RDB(card, rxDescrRing[pi][port->rxpos].bits) - & DMA_OWN) && !(card->dmarx_in_progress)) { - if (rx_count > fst_max_reads) { + if (card->type == FST_TYPE_DSL_S1) + { + /* + * Only process cells if we are in data state + */ + if ( port->activation_status >= 64) + fst_intr_rx_fifo(card, port); + } + while (!( FST_RDB ( card, rxDescrRing[pi][port->rxpos].bits ) + & DMA_OWN ) && !(card->dmarx_in_progress)) + { + if (rx_count > fst_max_reads) + { /* * Don't spend forever in receive processing * Schedule another event */ fst_q_work_item(&fst_work_intq, card->card_no); tasklet_schedule(&fst_int_task); + //pi = card->nports; break; /* Leave the loop */ } - fst_intr_rx(card, port); + fst_intr_rx ( card, port ); rx_count++; } } } + /* * The interrupt service routine * Dev_id is our fst_card_info pointer */ -static irqreturn_t -fst_intr(int irq, void *dev_id) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) +irqreturn_t +#else +static void +#endif +fst_intr ( int irq, void *dev_id ) { struct fst_card_info *card; struct fst_port_info *port; int rdidx; /* Event buffer indices */ int wridx; int event; /* Actual event for processing */ - unsigned int dma_intcsr = 0; + unsigned int dma_intcsr=0; unsigned int do_card_interrupt; unsigned int int_retry_count; - if ((card = dev_id) == NULL) { - dbg(DBG_INTR, "intr: spurious %d\n", irq); + if (( card = dev_id ) == NULL ) + { + dbg ( DBG_INTR,"intr: spurious %d\n", irq ); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) return IRQ_NONE; +#else + return; +#endif } /* @@ -1519,58 +5067,76 @@ fst_intr(int irq, void *dev_id) * return if not * Note that the call to clear the interrupt is important */ - dbg(DBG_INTR, "intr: %d %p\n", irq, card); - if (card->state != FST_RUNNING) { - printk_err - ("Interrupt received for card %d in a non running state (%d)\n", - card->card_no, card->state); + dbg ( DBG_INTR,"intr: %d %p %d\n", irq, card, card->card_no ); + if (card->state != FST_RUNNING) + { + printk_err("Interrupt received for card %d in a non running state (%d)\n", card->card_no, card->state); /* * It is possible to really be running, i.e. we have re-loaded * a running card * Clear and reprime the interrupt source */ - fst_clear_intr(card); + fst_clear_intr ( card ); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) return IRQ_HANDLED; +#else + return; +#endif } /* Clear and reprime the interrupt source */ - fst_clear_intr(card); + fst_clear_intr ( card ); /* * Is the interrupt for this card (handshake == 1) */ do_card_interrupt = 0; - if (FST_RDB(card, interruptHandshake) == 1) { + if (FST_RDB( card, interruptHandshake) == 1) + { do_card_interrupt += FST_CARD_INT; /* Set the software acknowledge */ - FST_WRB(card, interruptHandshake, 0xEE); + FST_WRB ( card, interruptHandshake, 0xEE ); } - if (card->family == FST_FAMILY_TXU) { + if (card->family == FST_FAMILY_TXU) + { /* * Is it a DMA Interrupt */ - dma_intcsr = inl(card->pci_conf + INTCSR_9054); - if (dma_intcsr & 0x00200000) { + dma_intcsr = inl (card->pci_conf + INTCSR_9054); + if (dma_intcsr & 0x00200000) + { /* * DMA Channel 0 (Rx transfer complete) */ dbg(DBG_RX, "DMA Rx xfer complete\n"); - outb(0x8, card->pci_conf + DMACSR0); - fst_rx_dma_complete(card, card->dma_port_rx, - card->dma_len_rx, card->dma_skb_rx, - card->dma_rxpos); + outb( 0x8, card->pci_conf + DMACSR0); + /* + * If non DSL card, complete interrupt processing + */ + if (card->type != FST_TYPE_DSL_S1) + { + fst_rx_dma_complete (card, card->dma_port_rx, card->dma_len_rx, + card->dma_skb_rx, card->dma_rxpos); + } card->dmarx_in_progress = 0; do_card_interrupt += FST_RX_DMA_INT; } - if (dma_intcsr & 0x00400000) { + if (dma_intcsr & 0x00400000) + { /* * DMA Channel 1 (Tx transfer complete) */ dbg(DBG_TX, "DMA Tx xfer complete\n"); - outb(0x8, card->pci_conf + DMACSR1); - fst_tx_dma_complete(card, card->dma_port_tx, - card->dma_len_tx, card->dma_txpos); + outb( 0x8, card->pci_conf + DMACSR1); + /* + * If non DSL card, complete interruot processing + */ + if (card->type != FST_TYPE_DSL_S1) + { + fst_tx_dma_complete (card, card->dma_port_tx, card->dma_len_tx, + card->dma_txpos, card->dma_tx_flags); + } card->dmatx_in_progress = 0; do_card_interrupt += FST_TX_DMA_INT; } @@ -1579,15 +5145,21 @@ fst_intr(int irq, void *dev_id) /* * Have we been missing Interrupts */ - int_retry_count = FST_RDL(card, interruptRetryCount); - if (int_retry_count) { + int_retry_count = FST_RDL( card, interruptRetryCount); + if (int_retry_count) + { dbg(DBG_ASS, "Card %d int_retry_count is %d\n", card->card_no, int_retry_count); - FST_WRL(card, interruptRetryCount, 0); + FST_WRL( card, interruptRetryCount, 0); } - if (!do_card_interrupt) { + if (!do_card_interrupt) + { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) return IRQ_HANDLED; +#else + return; +#endif } /* Scehdule the bottom half of the ISR */ @@ -1595,150 +5167,323 @@ fst_intr(int irq, void *dev_id) tasklet_schedule(&fst_int_task); /* Drain the event queue */ - rdidx = FST_RDB(card, interruptEvent.rdindex) & 0x1f; - wridx = FST_RDB(card, interruptEvent.wrindex) & 0x1f; - while (rdidx != wridx) { - event = FST_RDB(card, interruptEvent.evntbuff[rdidx]); + rdidx = FST_RDB ( card, interruptEvent.rdindex ) & 0x1f; + wridx = FST_RDB ( card, interruptEvent.wrindex ) & 0x1f; + while ( rdidx != wridx ) + { + event = FST_RDB ( card, interruptEvent.evntbuff[rdidx]); port = &card->ports[event & 0x03]; - dbg(DBG_INTR, "Processing Interrupt event: %x\n", event); + dbg ( DBG_INTR,"Processing Interrupt event: %x\n", event ); - switch (event) { + switch ( event ) + { case TE1_ALMA: - dbg(DBG_INTR, "TE1 Alarm intr\n"); - if (port->run) - fst_intr_te1_alarm(card, port); + dbg ( DBG_INTR,"%s: TE1 Alarm intr\n", + port_to_dev(port)->name); + if ( port->run && port_to_dev(port) != NULL ) + fst_intr_te1_alarm (card, port); + break; + + case DSL_ACST: + break; + + case DSL_LINK: + dbg ( DBG_INTR,"%s: DSL Activation status change %d\n", + port_to_dev(port)->name, + FST_RDB(card, dslStatus.activationStatus)); + if ( port->run && port_to_dev(port) != NULL ) + fst_dsl_acstchg (card, port); + break; + + case TXA_CMPL: + case TXB_CMPL: + case TXC_CMPL: + case TXD_CMPL: + /* + * TX complete for dsl fifo management + */ + break; + + case RXA_CMPL: + case RXB_CMPL: + case RXC_CMPL: + case RXD_CMPL: + /* + * RX complete for dsl fifo management + */ break; case CTLA_CHG: case CTLB_CHG: case CTLC_CHG: case CTLD_CHG: - if (port->run) - fst_intr_ctlchg(card, port); + if ( port->run && port_to_dev(port) != NULL ) + fst_intr_ctlchg ( card, port ); + break; + + case ABTA_SENT: + case ABTB_SENT: + case ABTC_SENT: + case ABTD_SENT: + dbg (DBG_TX,"Abort complete port %d\n", port->index ); + break; + + case TXA_UNDF: + case TXB_UNDF: + case TXC_UNDF: + case TXD_UNDF: + /* Difficult to see how we'd get this given that we + * always load up the entire packet for DMA. + */ + dbg (DBG_TX,"Tx underflow port %d\n", port->index ); + + port_to_stats(port,tx_errors)++; + port_to_stats(port,tx_fifo_errors)++; + dbg(DBG_ASS, "Tx underflow on card %d port %d\n", + card->card_no, port->index); + break; + + case INIT_CPLT: + dbg ( DBG_INIT,"Card init OK intr\n"); + break; + + case INIT_FAIL: + dbg ( DBG_INIT,"Card init FAILED intr\n"); + card->state = FST_IFAILED; + break; + + default: + dbg (DBG_ASS, "intr: card %d: Unknown event = 0x%x\n", + card->card_no, event); + break; + } + + /* Bump and wrap the index */ + if ( ++rdidx >= MAX_CIRBUFF ) + rdidx = 0; + } + FST_WRB ( card, interruptEvent.rdindex, rdidx ); + /* + * Fifo response? + */ + if (!card->fifo_complete) + check_fifo_resp(card); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + return IRQ_HANDLED; +#else + return; +#endif +} +static int +check_combination ( int num, int size) +{ + /* + * Check that the number and size do not exceed the following table + * + * num of buffs max size + * 1 32K + * 2 32K + * 4 16K + * 8 8K + * 16 4K + * 32 2K + * 64 1K + * 128 0.5K + */ + int err = 0; + switch(num) + { + case 1: + { + if (size > 32*1024) + err++; + break; + } + case 2: + { + if (size > 32*1024) + err++; + break; + } + case 4: + { + if (size > 16*1024) + err++; + break; + } + case 8: + { + if (size > 8*1024) + err++; break; - - case ABTA_SENT: - case ABTB_SENT: - case ABTC_SENT: - case ABTD_SENT: - dbg(DBG_TX, "Abort complete port %d\n", port->index); + } + case 16: + { + if (size > 4*1024) + err++; break; - - case TXA_UNDF: - case TXB_UNDF: - case TXC_UNDF: - case TXD_UNDF: - /* Difficult to see how we'd get this given that we - * always load up the entire packet for DMA. - */ - dbg(DBG_TX, "Tx underflow port %d\n", port->index); - hdlc_stats(port_to_dev(port))->tx_errors++; - hdlc_stats(port_to_dev(port))->tx_fifo_errors++; - dbg(DBG_ASS, "Tx underflow on card %d port %d\n", - card->card_no, port->index); + } + case 32: + { + if (size > 2*1024) + err++; break; - - case INIT_CPLT: - dbg(DBG_INIT, "Card init OK intr\n"); + } + case 64: + { + if (size > 1*1024) + err++; break; - - case INIT_FAIL: - dbg(DBG_INIT, "Card init FAILED intr\n"); - card->state = FST_IFAILED; + } + case 128: + { + if (size > 1024/2) + err++; break; - + } default: - printk_err("intr: unknown card event %d. ignored\n", - event); - break; + { + printk_err("Number of buffers must be 1,2,4,8,16,32,64, or 128 : %d\n", + num); + err++; } - - /* Bump and wrap the index */ - if (++rdidx >= MAX_CIRBUFF) - rdidx = 0; } - FST_WRB(card, interruptEvent.rdindex, rdidx); - return IRQ_HANDLED; + if (err) + { + return 1; + } + return 0; } -/* Check that the shared memory configuration is one that we can handle - * and that some basic parameters are correct - */ -static void -check_started_ok(struct fst_card_info *card) +static int +validate_buffer_config (struct fstioc_info *info) { - int i; - - /* Check structure version and end marker */ - if (FST_RDW(card, smcVersion) != SMC_VERSION) { - printk_err("Bad shared memory version %d expected %d\n", - FST_RDW(card, smcVersion), SMC_VERSION); - card->state = FST_BADVERSION; - return; + /* + * We need some checks on buffer configurations because the + * card itself doesn't do any + */ + if ( (info->numTxBuffers == 0) || (info->numRxBuffers == 0) ) + { + printk_err("Num Tx Buffers or Nun Rx Buffers is zero\n"); + return 1; } - if (FST_RDL(card, endOfSmcSignature) != END_SIG) { - printk_err("Missing shared memory signature\n"); - card->state = FST_BADVERSION; - return; + if ( (info->numTxBuffers > 128) || (info->numRxBuffers > 128) ) + { + printk_err("Num Tx Buffers or Nun Rx Buffers > 128\n"); + return 1; } - /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ - if ((i = FST_RDB(card, taskStatus)) == 0x01) { - card->state = FST_RUNNING; - } else if (i == 0xFF) { - printk_err("Firmware initialisation failed. Card halted\n"); - card->state = FST_HALTED; - return; - } else if (i != 0x00) { - printk_err("Unknown firmware status 0x%x\n", i); - card->state = FST_HALTED; - return; + if ( (info->txBufferSize == 0) || (info->rxBufferSize == 0) ) + { + printk_err("Tx Buffer Size or Rx Buffer Size is zero\n"); + return 1; } - - /* Finally check the number of ports reported by firmware against the - * number we assumed at card detection. Should never happen with - * existing firmware etc so we just report it for the moment. - */ - if (FST_RDL(card, numberOfPorts) != card->nports) { - printk_warn("Port count mismatch on card %d." - " Firmware thinks %d we say %d\n", card->card_no, - FST_RDL(card, numberOfPorts), card->nports); + if ( (info->txBufferSize > 32*1024) || (info->rxBufferSize > 32*1024) ) + { + printk_err("Tx Buffer Size or Rx Buffer Size > 32*1024\n"); + return 1; + } + if ( check_combination(info->numTxBuffers, info->txBufferSize) ) + { + printk_err("Invalid num/size combination on Tx Buffers\n"); + return 1; } + if ( check_combination(info->numRxBuffers, info->rxBufferSize) ) + { + printk_err("Invalid num/size combination on Rx Buffers\n"); + return 1; + } + return 0; } - static int -set_conf_from_info(struct fst_card_info *card, struct fst_port_info *port, - struct fstioc_info *info) +set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port, + struct fstioc_info *info ) { int err; - unsigned char my_framing; + int my_framing; - /* Set things according to the user set valid flags - * Several of the old options have been invalidated/replaced by the - * generic hdlc package. - */ + /* Set things according to the user set valid flags */ err = 0; - if (info->valid & FSTVAL_PROTO) { +#if IS_GEN_HDLC + if ( info->valid & FSTVAL_PROTO ) + { if (info->proto == FST_RAW) - port->mode = FST_RAW; + { + struct ifreq ifr; + int cmd; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,20) + cmd = SIOCWANDEV; +#else + cmd = SIOCDEVICE; +#endif + ifr.ifr_settings.type = IF_PROTO_PPP; + hdlc_ioctl(port_to_dev(port), &ifr, cmd); + port->proto = FST_RAW; + } else - port->mode = FST_GEN_HDLC; + { + port->proto = FST_GEN_HDLC; } - - if (info->valid & FSTVAL_CABLE) - err = -EINVAL; - - if (info->valid & FSTVAL_SPEED) - err = -EINVAL; - - if (info->valid & FSTVAL_PHASE) - FST_WRB(card, portConfig[port->index].invertClock, - info->invertClock); - if (info->valid & FSTVAL_MODE) - FST_WRW(card, cardMode, info->cardMode); - if (info->valid & FSTVAL_TE1) { - FST_WRL(card, suConfig.dataRate, info->lineSpeed); - FST_WRB(card, suConfig.clocking, info->clockSource); + } +#else + if ( info->valid & FSTVAL_PROTO ) + { + if ( port->proto != info->proto ) + { + err = switch_proto ( port, info->proto ); + if ( err == 0 ) + port->proto = info->proto; + } + } +#endif + if ( info->valid & FSTVAL_CABLE ) + { + FST_WRB ( card, portConfig[port->index].lineInterface, + info->lineInterface ); + port->hwif = info->lineInterface; + port->ignoreCarrier = info->ignoreCarrier; + } + if ( info->valid & FSTVAL_SPEED ) + { + FST_WRL ( card, portConfig[port->index].lineSpeed, + info->lineSpeed ); + FST_WRB ( card, portConfig[port->index].internalClock, + info->internalClock ); + FST_WRB ( card, portConfig[port->index].transparentMode, + info->transparentMode ); + port->transparent_mode = info->transparentMode; + FST_WRB ( card, portConfig[port->index].transmitMSBFirst, + info->transmitMSBFirst ); + FST_WRB ( card, portConfig[port->index].receiveMSBFirst, + info->receiveMSBFirst ); + } + if ( info->valid & FSTVAL_PHASE ) + { + FST_WRB ( card, portConfig[port->index].invertClock, + info->invertClock ); + } + if ( info->valid & FSTVAL_MODE ) + { + FST_WRW ( card, cardMode, info->cardMode ); + } + if ( info->valid & FSTVAL_BUFFERS ) + { + if (validate_buffer_config( info )) + { + return -ENOSPC; + } + port->num_tx_buffers = info->numTxBuffers; + port->num_rx_buffers = info->numRxBuffers; + port->tx_buffer_size = info->txBufferSize; + port->rx_buffer_size = info->rxBufferSize; + dbg(DBG_ASS, "txb %d rxb %d txs %d rxs %d\n", info->numTxBuffers, + info->numRxBuffers, info->txBufferSize, info->rxBufferSize); + } + if ( info->valid & FSTVAL_TE1 ) + { + FST_WRL ( card, suConfig.dataRate, info->lineSpeed ); + FST_WRB ( card, suConfig.clocking, info->clockSource ); my_framing = FRAMING_E1; if (info->framing == E1) my_framing = FRAMING_E1; @@ -1746,26 +5491,27 @@ set_conf_from_info(struct fst_card_info my_framing = FRAMING_T1; if (info->framing == J1) my_framing = FRAMING_J1; - FST_WRB(card, suConfig.framing, my_framing); - FST_WRB(card, suConfig.structure, info->structure); - FST_WRB(card, suConfig.interface, info->interface); - FST_WRB(card, suConfig.coding, info->coding); - FST_WRB(card, suConfig.lineBuildOut, info->lineBuildOut); - FST_WRB(card, suConfig.equalizer, info->equalizer); - FST_WRB(card, suConfig.transparentMode, info->transparentMode); - FST_WRB(card, suConfig.loopMode, info->loopMode); - FST_WRB(card, suConfig.range, info->range); - FST_WRB(card, suConfig.txBufferMode, info->txBufferMode); - FST_WRB(card, suConfig.rxBufferMode, info->rxBufferMode); - FST_WRB(card, suConfig.startingSlot, info->startingSlot); - FST_WRB(card, suConfig.losThreshold, info->losThreshold); + FST_WRB ( card, suConfig.framing, my_framing ); + FST_WRB ( card, suConfig.structure, info->structure ); + FST_WRB ( card, suConfig.interface, info->interface ); + FST_WRB ( card, suConfig.coding, info->coding ); + FST_WRB ( card, suConfig.lineBuildOut, info->lineBuildOut ); + FST_WRB ( card, suConfig.equalizer, info->equalizer ); + FST_WRB ( card, suConfig.transparentMode, info->transparentMode ); + FST_WRB ( card, suConfig.loopMode, info->loopMode ); + FST_WRB ( card, suConfig.range, info->range ); + FST_WRB ( card, suConfig.txBufferMode, info->txBufferMode ); + FST_WRB ( card, suConfig.rxBufferMode, info->rxBufferMode ); + FST_WRB ( card, suConfig.startingSlot, info->startingSlot ); + FST_WRB ( card, suConfig.losThreshold, info->losThreshold ); if (info->idleCode) - FST_WRB(card, suConfig.enableIdleCode, 1); + FST_WRB ( card, suConfig.enableIdleCode, 1 ); else - FST_WRB(card, suConfig.enableIdleCode, 0); - FST_WRB(card, suConfig.idleCode, info->idleCode); + FST_WRB ( card, suConfig.enableIdleCode, 0 ); + FST_WRB ( card, suConfig.idleCode, info->idleCode ); #if FST_DEBUG - if (info->valid & FSTVAL_TE1) { + if (card->type == FST_TYPE_TE1) + { printk("Setting TE1 data\n"); printk("Line Speed = %d\n", info->lineSpeed); printk("Start slot = %d\n", info->startingSlot); @@ -1776,8 +5522,7 @@ set_conf_from_info(struct fst_card_info printk("Coding = %d\n", info->coding); printk("Line build out = %d\n", info->lineBuildOut); printk("Equaliser = %d\n", info->equalizer); - printk("Transparent mode = %d\n", - info->transparentMode); + printk("Transparent mode = %d\n", info->transparentMode); printk("Loop mode = %d\n", info->loopMode); printk("Range = %d\n", info->range); printk("Tx Buffer mode = %d\n", info->txBufferMode); @@ -1787,8 +5532,57 @@ set_conf_from_info(struct fst_card_info } #endif } + if ( info->valid & FSTVAL_DSL_S1 ) + { +#if IS_GEN_HDLC + if ( port->proto != FST_GEN_HDLC) + { + if (info->encap == ENCAP_MPOA) + { + printk_err("Can Only have ATM encapsulation MPOA with HDLC proctocol %d\n", port->proto); + err++; + return -EINVAL; + } + } +#else + if ( info->encap != ENCAP_PPP) + { + printk_err("Can only configure ATM encapsulation as PPP\n"); + err++; + } +#endif + port->encap = info->encap; + FST_WRL ( card, dslConfig.dataRate, info->lineSpeed ); + FST_WRB ( card, dslConfig.terminalType, info->terminalType ); + FST_WRB ( card, dslConfig.annexType, info->annexType ); + FST_WRB ( card, dslConfig.testMode, info->testMode ); + FST_WRB ( card, dslConfig.backoff, info->backoff ); + FST_WRB ( card, dslConfig.bLineProbingEnable, + info->bLineProbingEnable ); + FST_WRB ( card, dslConfig.snrth, info->snrth ); + FST_WRB ( card, dslConfig.lpath, info->lpath ); + port->vpi = info->vpi; + port->vci = info->vci; + set_atm_header(port, info->vpi, info->vci); +#if FST_DEBUG + if (card->type == FST_TYPE_DSL_S1) + { + printk("Setting DSL-S1 Parameters\n"); + printk(" Speed = %d\n", info->lineSpeed); + printk(" Terminal Type = %d\n", info->terminalType); + printk(" Annex Type = %d\n", info->annexType); + printk(" Test Mode = %d\n", info->testMode); + printk(" Backoff = %d\n", info->backoff); + printk(" Snrth = %d\n", info->snrth); + printk(" Lpath = %d\n", info->lpath); + printk(" VPI set to %x\n", port->vpi); + printk(" VCI set to %x\n", port->vci); + } +#endif + } #if FST_DEBUG - if (info->valid & FSTVAL_DEBUG) { + if ( info->valid & FSTVAL_DEBUG ) + { fst_debug_mask = info->debug; } #endif @@ -1797,19 +5591,24 @@ set_conf_from_info(struct fst_card_info } static void -gather_conf_info(struct fst_card_info *card, struct fst_port_info *port, - struct fstioc_info *info) +gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port, + struct fstioc_info *info ) { int i; + int j; - memset(info, 0, sizeof (struct fstioc_info)); + memset ( info, 0, sizeof ( struct fstioc_info )); i = port->index; info->kernelVersion = LINUX_VERSION_CODE; info->nports = card->nports; info->type = card->type; info->state = card->state; +#if IS_GEN_HDLC info->proto = FST_GEN_HDLC; +#else + info->proto = port->proto; +#endif info->index = i; #if FST_DEBUG info->debug = fst_debug_mask; @@ -1818,35 +5617,58 @@ gather_conf_info(struct fst_card_info *c /* Only mark information as valid if card is running. * Copy the data anyway in case it is useful for diagnostics */ - info->valid = ((card->state == FST_RUNNING) ? FSTVAL_ALL : FSTVAL_CARD) + info->valid + = (( card->state == FST_RUNNING ) ? FSTVAL_ALL : FSTVAL_CARD ) #if FST_DEBUG | FSTVAL_DEBUG #endif ; - info->lineInterface = FST_RDW(card, portConfig[i].lineInterface); - info->internalClock = FST_RDB(card, portConfig[i].internalClock); - info->lineSpeed = FST_RDL(card, portConfig[i].lineSpeed); - info->invertClock = FST_RDB(card, portConfig[i].invertClock); - info->v24IpSts = FST_RDL(card, v24IpSts[i]); - info->v24OpSts = FST_RDL(card, v24OpSts[i]); - info->clockStatus = FST_RDW(card, clockStatus[i]); - info->cableStatus = FST_RDW(card, cableStatus); - info->cardMode = FST_RDW(card, cardMode); - info->smcFirmwareVersion = FST_RDL(card, smcFirmwareVersion); + info->lineInterface = FST_RDW ( card, portConfig[i].lineInterface ); + info->internalClock = FST_RDB ( card, portConfig[i].internalClock ); + info->lineSpeed = FST_RDL ( card, portConfig[i].lineSpeed ); + info->estLineSpeed = FST_RDL ( card, portConfig[i].estimatedLineSpeed ); + info->invertClock = FST_RDB ( card, portConfig[i].invertClock ); + info->synthAbility = FST_RDB ( card, synthAbility); + info->asyncAbility = FST_RDB ( card, asyncAbility[i]); + info->transparentMode = FST_RDB ( card, portConfig[i].transparentMode); + info->transmitMSBFirst = FST_RDB ( card, portConfig[i].transmitMSBFirst); + info->receiveMSBFirst = FST_RDB ( card, portConfig[i].receiveMSBFirst); + info->v24IpSts = FST_RDL ( card, v24IpSts[i] ); + info->v24OpSts = FST_RDL ( card, v24OpSts[i] ); + info->clockStatus = FST_RDW ( card, clockStatus[i] ); + info->cableStatus = FST_RDW ( card, cableStatus ); + info->cardMode = FST_RDW ( card, cardMode ); + info->smcFirmwareVersion = FST_RDL ( card, smcFirmwareVersion ); + /* + * Return the reserved area of the shared memory window + */ + for (j=0; j< 64; j++) + { + info->_reserved[j] = FST_RDW ( card, _reserved[j] ); + } + info->ignoreCarrier = port->ignoreCarrier; + info->numRxBuffers = port->num_rx_buffers; + info->numTxBuffers = port->num_tx_buffers; + info->rxBufferSize = port->rx_buffer_size; + info->txBufferSize = port->tx_buffer_size; /* * The T2U can report cable presence for both A or B * in bits 0 and 1 of cableStatus. See which port we are and * do the mapping. */ - if (card->family == FST_FAMILY_TXU) { - if (port->index == 0) { + if (card->family == FST_FAMILY_TXU) + { + if (port->index == 0) + { /* * Port A */ info->cableStatus = info->cableStatus & 1; - } else { + } + else + { /* * Port B */ @@ -1857,92 +5679,186 @@ gather_conf_info(struct fst_card_info *c /* * Some additional bits if we are TE1 */ - if (card->type == FST_TYPE_TE1) { - info->lineSpeed = FST_RDL(card, suConfig.dataRate); - info->clockSource = FST_RDB(card, suConfig.clocking); - info->framing = FST_RDB(card, suConfig.framing); - info->structure = FST_RDB(card, suConfig.structure); - info->interface = FST_RDB(card, suConfig.interface); - info->coding = FST_RDB(card, suConfig.coding); - info->lineBuildOut = FST_RDB(card, suConfig.lineBuildOut); - info->equalizer = FST_RDB(card, suConfig.equalizer); - info->loopMode = FST_RDB(card, suConfig.loopMode); - info->range = FST_RDB(card, suConfig.range); - info->txBufferMode = FST_RDB(card, suConfig.txBufferMode); - info->rxBufferMode = FST_RDB(card, suConfig.rxBufferMode); - info->startingSlot = FST_RDB(card, suConfig.startingSlot); - info->losThreshold = FST_RDB(card, suConfig.losThreshold); - if (FST_RDB(card, suConfig.enableIdleCode)) - info->idleCode = FST_RDB(card, suConfig.idleCode); - else - info->idleCode = 0; - info->receiveBufferDelay = - FST_RDL(card, suStatus.receiveBufferDelay); - info->framingErrorCount = - FST_RDL(card, suStatus.framingErrorCount); - info->codeViolationCount = - FST_RDL(card, suStatus.codeViolationCount); - info->crcErrorCount = FST_RDL(card, suStatus.crcErrorCount); - info->lineAttenuation = FST_RDL(card, suStatus.lineAttenuation); - info->lossOfSignal = FST_RDB(card, suStatus.lossOfSignal); - info->receiveRemoteAlarm = - FST_RDB(card, suStatus.receiveRemoteAlarm); - info->alarmIndicationSignal = - FST_RDB(card, suStatus.alarmIndicationSignal); + if (card->type == FST_TYPE_TE1) + { + info->lineSpeed = FST_RDL ( card, suConfig.dataRate ); + info->clockSource = FST_RDB ( card, suConfig.clocking ); + info->framing = FST_RDB ( card, suConfig.framing ); + info->structure = FST_RDB ( card, suConfig.structure ); + info->interface = FST_RDB ( card, suConfig.interface ); + info->coding = FST_RDB ( card, suConfig.coding ); + info->lineBuildOut = FST_RDB ( card, suConfig.lineBuildOut ); + info->equalizer = FST_RDB ( card, suConfig.equalizer ); + info->loopMode = FST_RDB ( card, suConfig.loopMode ); + info->range = FST_RDB ( card, suConfig.range ); + info->txBufferMode = FST_RDB ( card, suConfig.txBufferMode ); + info->rxBufferMode = FST_RDB ( card, suConfig.rxBufferMode ); + info->startingSlot = FST_RDB ( card, suConfig.startingSlot ); + if (FST_RDB (card, suConfig.enableIdleCode )) + info->idleCode = FST_RDB ( card, suConfig.idleCode ); + else + info->idleCode = 0 ; + info->losThreshold = FST_RDB ( card, suConfig.losThreshold ); + info->receiveBufferDelay = FST_RDL ( card, suStatus.receiveBufferDelay ); + info->framingErrorCount = FST_RDL ( card, suStatus.framingErrorCount ); + info->codeViolationCount = FST_RDL ( card, suStatus.codeViolationCount ); + info->crcErrorCount = FST_RDL ( card, suStatus.crcErrorCount ); + info->lineAttenuation = FST_RDL ( card, suStatus.lineAttenuation ); + info->lossOfSignal = FST_RDB ( card, suStatus.lossOfSignal ); + info->receiveRemoteAlarm = FST_RDB ( card, suStatus.receiveRemoteAlarm ); + info->alarmIndicationSignal = FST_RDB ( card, suStatus.alarmIndicationSignal ); + } + + if (card->type == FST_TYPE_DSL_S1) + { + info->lineInterface = SHDSL; + info->vpi = port->vpi; + info->vci = port->vci; + info->snrth = FST_RDB ( card, dslConfig.snrth ); + info->lpath = FST_RDB ( card, dslConfig.lpath ); + info->lineSpeed = FST_RDL ( card, dslConfig.dataRate ); + info->terminalType = FST_RDB ( card, dslConfig.terminalType ); + info->annexType = FST_RDB ( card, dslConfig.annexType ); + info->testMode = FST_RDB ( card, dslConfig.testMode ); + info->backoff = FST_RDB ( card, dslConfig.backoff ); + info->activationStatus = FST_RDB ( card, dslStatus.activationStatus ); + info->noCommonModeStatus = FST_RDB ( card, dslStatus.noCommonModeStatus ); + info->transceiverStatus1 = FST_RDB ( card, dslStatus.transceiverStatus1 ); + info->transceiverStatus2 = FST_RDB ( card, dslStatus.transceiverStatus2 ); + info->lineLoss = FST_RDB ( card, dslStatus.lineLoss ); + info->signalQuality = FST_RDB ( card, dslStatus.signalQuality ); + info->nearEndBlockErrorCount = FST_RDB ( card, dslStatus.nearEndBlockErrorCount ); + info->signalToNoiseRatio = FST_RDB ( card, dslStatus.signalToNoiseRatio ); + info->codeViolationCount = FST_RDB ( card, dslStatus.codeViolationCount ); + info->erroredSecondCount = FST_RDB ( card, dslStatus.erroredSecondCount ); + info->severelyErroredSecondCount = FST_RDB ( card, dslStatus.severelyErroredSecondCount ); + info->lossOfSyncWordSecondCount = FST_RDB ( card, dslStatus.lossOfSyncWordSecondCount ); + info->unavailableSecondCount = FST_RDB ( card, dslStatus.unavailableSecondCount ); + info->frequencyDeviation = FST_RDB ( card, dslStatus.frequencyDeviation ); + info->negotiatedPowerBackOff = FST_RDB ( card, dslStatus.negotiatedPowerBackOff ); + info->negotiatedPSD = FST_RDB ( card, dslStatus.negotiatedPSD ); + info->negotiatedBChannels = FST_RDB ( card, dslStatus.negotiatedBChannels ); + info->negotiatedZBits = FST_RDB ( card, dslStatus.negotiatedZBits ); + info->negotiatedSyncWord = FST_RDW ( card, dslStatus.negotiatedSyncWord ); + info->negotiatedStuffBits = FST_RDB ( card, dslStatus.negotiatedStuffBits ); + info->chipVersion = FST_RDB ( card, dslStatus.chipVersion ); + info->firmwareVersion = FST_RDB ( card, dslStatus.firmwareVersion ); + info->romVersion = FST_RDB ( card, dslStatus.romVersion ); + info->atmTxCellCount = FST_RDW (card, dslStatus.atmTxCellCount); + info->atmRxCellCount = FST_RDW (card, dslStatus.atmRxCellCount); + info->atmHecErrorCount = FST_RDW (card, dslStatus.atmHecErrorCount); + info->atmCellsDropped = port->atm_cells_dropped; + info->encap = port->encap; + info ->xpldVersion = FST_RDB ( card, dslStatus.xpldVersion); + info ->farEndCountryCode[0] = FST_RDB ( card, dslStatus.farEndCountryCode[0]); + info ->farEndCountryCode[1] = FST_RDB ( card, dslStatus.farEndCountryCode[1]); + info ->farEndProviderCode[0] = FST_RDB ( card, dslStatus.farEndProviderCode[0]); + info ->farEndProviderCode[1] = FST_RDB ( card, dslStatus.farEndProviderCode[1]); + info ->farEndProviderCode[2] = FST_RDB ( card, dslStatus.farEndProviderCode[2]); + info ->farEndProviderCode[3] = FST_RDB ( card, dslStatus.farEndProviderCode[3]); + info ->farEndVendorInfo[0] = FST_RDB ( card, dslStatus.farEndVendorInfo[0]); + info ->farEndVendorInfo[1] = FST_RDB ( card, dslStatus.farEndVendorInfo[1]); + } +} + +static int +set_dsl_port ( struct fst_port_info *port, int mode) +{ + /* + * This function is called from processing an ioctl to startup + * the dsl port independantly. It has been introduced specifically + * for the tty interface so that the dsl line can be controlled + * (up or down) independently of network opens (ifconfig up/down, + * or tty_open/close). + * Values of mode are normal (off if on) + * active (on it off and not already in use) + * + * Return values are 0 is OK + * 1 is error + */ + + if ( mode == FST_DSL_PORT_ACTIVE) + { + if ((port->run) || (port->port_mode)) + { + /* + * port is already open either by tty or network interface + * or port state has already been set + */ + dbg(DBG_ASS, "Port is busy, run = %d mode = %d\n", + port->run, port->port_mode); + return 1; + } + fst_openport(port); + return 0; } + else + fst_closeport(port); + return 0; } static int -fst_set_iface(struct fst_card_info *card, struct fst_port_info *port, - struct ifreq *ifr) +fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) { sync_serial_settings sync; int i; - if (ifr->ifr_settings.size != sizeof (sync)) { + if ( ifr->ifr_settings.size != sizeof ( sync )) + { return -ENOMEM; } - - if (copy_from_user - (&sync, ifr->ifr_settings.ifs_ifsu.sync, sizeof (sync))) { + if ( copy_from_user ( &sync, ifr->ifr_settings.ifs_ifsu.sync, sizeof ( sync ))) + { return -EFAULT; } - if (sync.loopback) + if ( sync.loopback ) return -EINVAL; i = port->index; - switch (ifr->ifr_settings.type) { + switch ( ifr->ifr_settings.type ) + { case IF_IFACE_V35: - FST_WRW(card, portConfig[i].lineInterface, V35); + FST_WRW ( card, portConfig[i].lineInterface, V35 ); port->hwif = V35; break; case IF_IFACE_V24: - FST_WRW(card, portConfig[i].lineInterface, V24); + FST_WRW ( card, portConfig[i].lineInterface, V24 ); port->hwif = V24; break; case IF_IFACE_X21: - FST_WRW(card, portConfig[i].lineInterface, X21); + FST_WRW ( card, portConfig[i].lineInterface, X21 ); port->hwif = X21; break; case IF_IFACE_X21D: - FST_WRW(card, portConfig[i].lineInterface, X21D); + FST_WRW ( card, portConfig[i].lineInterface, X21D ); port->hwif = X21D; break; case IF_IFACE_T1: - FST_WRW(card, portConfig[i].lineInterface, T1); + FST_WRW ( card, portConfig[i].lineInterface, T1 ); port->hwif = T1; break; case IF_IFACE_E1: - FST_WRW(card, portConfig[i].lineInterface, E1); + FST_WRW ( card, portConfig[i].lineInterface, E1 ); port->hwif = E1; break; +#if 0 + case IF_IFACE_J1: + FST_WRW ( card, portConfig[i].lineInterface, J1 ); + port->hwif = J1; + break; +#endif + case IF_IFACE_SHDSL: + FST_WRW ( card, portConfig[i].lineInterface, SHDSL); + port->hwif = SHDSL; + break; case IF_IFACE_SYNC_SERIAL: break; @@ -1951,34 +5867,42 @@ fst_set_iface(struct fst_card_info *card return -EINVAL; } - switch (sync.clock_type) { + switch ( sync.clock_type ) + { case CLOCK_EXT: - FST_WRB(card, portConfig[i].internalClock, EXTCLK); + FST_WRB ( card, portConfig[i].internalClock, EXTCLK ); break; case CLOCK_INT: - FST_WRB(card, portConfig[i].internalClock, INTCLK); + FST_WRB ( card, portConfig[i].internalClock, INTCLK ); break; default: return -EINVAL; } - FST_WRL(card, portConfig[i].lineSpeed, sync.clock_rate); + FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate ); return 0; } static int -fst_get_iface(struct fst_card_info *card, struct fst_port_info *port, - struct ifreq *ifr) +fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr ) { sync_serial_settings sync; int i; /* First check what line type is set, we'll default to reporting X.21 - * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be - * changed */ - switch (port->hwif) { + switch ( port->hwif ) + { + case SHDSL: + ifr->ifr_settings.type = IF_IFACE_SHDSL; + break; +#if 0 + case J1: + ifr->ifr_settings.type = IF_IFACE_J1; + break; +#endif case E1: ifr->ifr_settings.type = IF_IFACE_E1; break; @@ -1999,53 +5923,61 @@ fst_get_iface(struct fst_card_info *card ifr->ifr_settings.type = IF_IFACE_X21; break; } - if (ifr->ifr_settings.size == 0) { + if ( ifr->ifr_settings.size == 0 ) + { return 0; /* only type requested */ } - if (ifr->ifr_settings.size < sizeof (sync)) { + if ( ifr->ifr_settings.size < sizeof ( sync )) + { return -ENOMEM; } i = port->index; - sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed); + sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed ); /* Lucky card and linux use same encoding here */ - sync.clock_type = FST_RDB(card, portConfig[i].internalClock) == + sync.clock_type = FST_RDB ( card, portConfig[i].internalClock ) == INTCLK ? CLOCK_INT : CLOCK_EXT; sync.loopback = 0; - if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &sync, sizeof (sync))) { + if ( copy_to_user ( ifr->ifr_settings.ifs_ifsu.sync, &sync, sizeof ( sync ))) + { return -EFAULT; } - ifr->ifr_settings.size = sizeof (sync); + ifr->ifr_settings.size = sizeof ( sync ); return 0; } + static int -fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) { struct fst_card_info *card; struct fst_port_info *port; struct fstioc_write wrthdr; struct fstioc_info info; unsigned long flags; + struct fstioc_status state; + struct fstioc_req sys_request; + int retval = 0; + int new_mode; - dbg(DBG_IOCTL, "ioctl: %x, %p\n", cmd, ifr->ifr_data); - - port = dev_to_port(dev); + dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data ); + port = dev_to_port ( dev ); card = port->card; - if (!capable(CAP_NET_ADMIN)) + if ( !capable ( CAP_NET_ADMIN )) return -EPERM; - switch (cmd) { + switch ( cmd ) + { case FSTCPURESET: - fst_cpureset(card); + fst_cpureset ( card ); card->state = FST_RESET; return 0; case FSTCPURELEASE: - fst_cpurelease(card); + fst_cpurelease ( card ); card->state = FST_STARTING; return 0; @@ -2054,19 +5986,22 @@ fst_ioctl(struct net_device *dev, struct /* First copy in the header with the length and offset of data * to write */ - if (ifr->ifr_data == NULL) { + if ( ifr->ifr_data == NULL ) + { return -EINVAL; } - if (copy_from_user(&wrthdr, ifr->ifr_data, - sizeof (struct fstioc_write))) { + if ( copy_from_user ( &wrthdr, ifr->ifr_data, + sizeof ( struct fstioc_write ))) + { return -EFAULT; } /* Sanity check the parameters. We don't support partial writes * when going over the top */ - if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE - || wrthdr.size + wrthdr.offset > FST_MEMSIZE) { + if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE + || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) + { return -ENXIO; } @@ -2074,71 +6009,228 @@ fst_ioctl(struct net_device *dev, struct * This will probably break on some architectures. * I'll fix it when I have something to test on. */ - if (copy_from_user(card->mem + wrthdr.offset, - ifr->ifr_data + sizeof (struct fstioc_write), - wrthdr.size)) { + if ( copy_from_user ( card->mem + wrthdr.offset, + ifr->ifr_data + sizeof ( struct fstioc_write ), + wrthdr.size )) + { return -EFAULT; } /* Writes to the memory of a card in the reset state constitute * a download */ - if (card->state == FST_RESET) { + if ( card->state == FST_RESET ) + { card->state = FST_DOWNLOAD; } return 0; + case FSTGETSHELL: + + if ( ifr->ifr_data == NULL ) + { + return -EINVAL; + } + + gather_conf_info ( card, port, &info ); + + if ( copy_to_user ( ifr->ifr_data, &info, sizeof ( info ))) + { + return -EFAULT; + } + return 0; + case FSTGETCONF: /* If card has just been started check the shared memory config * version and marker */ - if (card->state == FST_STARTING) { - check_started_ok(card); + if ( card->state == FST_STARTING ) + { + check_started_ok ( card ); /* If everything checked out enable card interrupts */ - if (card->state == FST_RUNNING) { - spin_lock_irqsave(&card->card_lock, flags); - fst_enable_intr(card); - FST_WRB(card, interruptHandshake, 0xEE); - spin_unlock_irqrestore(&card->card_lock, flags); + if ( card->state == FST_RUNNING ) + { + spin_lock_irqsave ( &card->card_lock, flags ); + fst_enable_intr ( card ); + FST_WRB ( card, interruptHandshake, 0xEE ); + spin_unlock_irqrestore ( &card->card_lock, + flags ); } } - if (ifr->ifr_data == NULL) { + if ( card->state != FST_RUNNING ) + { + return -ENODEV; + } + + if ( ifr->ifr_data == NULL ) + { return -EINVAL; } - gather_conf_info(card, port, &info); + gather_conf_info ( card, port, &info ); + + if ( copy_to_user ( ifr->ifr_data, &info, sizeof ( info ))) + { + return -EFAULT; + } + return 0; + + case FSTSETCONF: + + /* + * Most of the settings have been moved to the generic ioctls + * this just covers debug and board ident now + */ + + if(card->state != FST_RUNNING) + { + printk_err("Attempt to configure card %d in non-running state (%d)\n", card->card_no, card->state); + return -EIO; + } + if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info ))) + { + return -EFAULT; + } + + return set_conf_from_info ( card, port, &info ); + + case FSTSNOTIFY: + /* + * The application layer above wants to toggle the state notify + * mechanism. + */ + if (copy_from_user(&port->notify_mode, ifr->ifr_data, sizeof( int ))) + { + return -EFAULT; + } + dbg(DBG_IOCTL, "Card %d port %d: Setting Notify mode to %d\n", + card->card_no, port->index, port->notify_mode); + return 0; + + case FSTSETMON: + /* + * The application layer above wants to monitor tx and rx data + * mechanism. + */ + if (copy_from_user(&port->monitor_mode, ifr->ifr_data, sizeof( int ))) + { + return -EFAULT; + } + dbg(DBG_IOCTL, "Card %d port %d: Setting Monitor mode to %d\n", + card->card_no, port->index, port->monitor_mode); + return 0; + + case FSTSETPORT: + /* + * An alternative way to activate the dsl port so that the + * line is already up when needed by pppd + */ + if (copy_from_user(&new_mode, ifr->ifr_data, sizeof( int ))) + { + dbg(DBG_ASS, "Error in getting data, set port state\n"); + return -EFAULT; + } + dbg(DBG_IOCTL, "Card %d port %d: Setting port mode to %d\n", + card->card_no, port->index, new_mode); + if(set_dsl_port (port, new_mode)) + return -EBUSY; + port->port_mode = new_mode; + return 0; - if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) { + case FSTGSTATE: + /* + * The application layer above wants the carrier state + */ + state.carrier_state = netif_carrier_ok ( port_to_dev(port) ); + /* + * and the txq is a simple length + */ + if ((state.txq_length = port->txqe - port->txqs) < 0) + { + /* + * This is the case where the next free has wrapped but the + * last used hasn't + */ + state.txq_length = state.txq_length + FST_TXQ_DEPTH; + } + if ( copy_to_user ( ifr->ifr_data, &state, + sizeof ( struct fstioc_status))) + { return -EFAULT; } return 0; - case FSTSETCONF: - + case FSTSYSREQ: /* - * Most of the settings have been moved to the generic ioctls - * this just covers debug and board ident now + * Pass the command through to the card. If the command generates a + * response, wait for it. The application suspends untill the command + * completes. */ - - if (card->state != FST_RUNNING) { - printk_err - ("Attempt to configure card %d in non-running state (%d)\n", - card->card_no, card->state); - return -EIO; + dbg(DBG_IOCTL, "FSTSYSREQ received\n"); + if ( copy_from_user(&sys_request, ifr->ifr_data, sizeof(sys_request))) + { + dbg(DBG_ASS, "Could not copy sysrequest from user\n"); + return -EFAULT; } - if (copy_from_user(&info, ifr->ifr_data, sizeof (info))) { + dbg(DBG_IOCTL, "The request is as follows:\n"); + dbg(DBG_IOCTL, "msg_type = %x\n", sys_request.msg_type); + dbg(DBG_IOCTL, "msg_len = %d\n", sys_request.msg_len); + dbg(DBG_IOCTL, "i_reg_idx = %d\n", sys_request.i_reg_idx); + /* + * Use a spin lock to ensure only one request at a time + */ + dbg(DBG_IOCTL, "Sending the card the request\n"); + spin_lock_irqsave(&card->fifo_lock, flags); + if ( write_into_fifo(card, &sys_request)) + { + dbg(DBG_IOCTL, "Not enough room in fifo\n"); + return -ENOSPC; + } + retval = 0; + /* + * Are we going to wait for a reply + */ + if(sys_request.msg_type == MSG_FIFO_EEPROM_RD) + { + int status; + + dbg(DBG_IOCTL, "Waiting for a reply\n"); + card->fifo_complete = 0; + status = wait_event_interruptible(card->fifo_waitq, + (card->fifo_complete)); + if (status == -ERESTARTSYS) + retval = -EINTR; + dbg(DBG_IOCTL, "Reply received (%d) or interrupts (%x)\n", + card->fifo_complete, status); + /* + * Copy the buffer back and we are complete + */ + read_from_fifo(card, &sys_request); + dbg(DBG_IOCTL, "The reply is as follows:\n"); + dbg(DBG_IOCTL, "msg_type = %x\n", sys_request.msg_type); + dbg(DBG_IOCTL, "msg_len = %d\n", sys_request.msg_len); + dbg(DBG_IOCTL, "i_reg_idx = %d\n", sys_request.i_reg_idx); + + if ( copy_to_user ( ifr->ifr_data, &sys_request, + sizeof ( sys_request ))) + { + dbg(DBG_IOCTL, "Error copying data back to user\n"); return -EFAULT; } - return set_conf_from_info(card, port, &info); + } + spin_unlock_irqrestore(&card->fifo_lock, flags); + dbg(DBG_IOCTL, "Returning from FSTSYSREQ\n"); + return retval; case SIOCWANDEV: - switch (ifr->ifr_settings.type) { + switch ( ifr->ifr_settings.type ) + { case IF_GET_IFACE: - return fst_get_iface(card, port, ifr); + return fst_get_iface ( card, port, ifr ); case IF_IFACE_SYNC_SERIAL: case IF_IFACE_V35: @@ -2147,34 +6239,48 @@ fst_ioctl(struct net_device *dev, struct case IF_IFACE_X21D: case IF_IFACE_T1: case IF_IFACE_E1: - return fst_set_iface(card, port, ifr); + case IF_IFACE_SHDSL: + return fst_set_iface ( card, port, ifr ); case IF_PROTO_RAW: - port->mode = FST_RAW; + port->proto = FST_RAW; return 0; case IF_GET_PROTO: - if (port->mode == FST_RAW) { + if (port->proto == FST_RAW) + { ifr->ifr_settings.type = IF_PROTO_RAW; return 0; } - return hdlc_ioctl(dev, ifr, cmd); + retval = hdlc_ioctl ( dev, ifr, cmd ); + if (retval == -EINVAL) + { + /* + * Protocol not set yet + */ + port->proto = FST_GEN_HDLC; + ifr->ifr_settings.type = FST_GEN_HDLC; + retval = 0; + } + return retval; default: - port->mode = FST_GEN_HDLC; + port->proto = FST_GEN_HDLC; dbg(DBG_IOCTL, "Passing this type to hdlc %x\n", ifr->ifr_settings.type); - return hdlc_ioctl(dev, ifr, cmd); + port->hdlc_proto = ifr->ifr_settings.type; + return hdlc_ioctl ( dev, ifr, cmd ); } default: /* Not one of ours. Pass through to HDLC package */ - return hdlc_ioctl(dev, ifr, cmd); + return hdlc_ioctl ( dev, ifr, cmd ); } } + static void -fst_openport(struct fst_port_info *port) +fst_openport ( struct fst_port_info *port ) { int signals; int txq_length; @@ -2182,73 +6288,114 @@ fst_openport(struct fst_port_info *port) /* Only init things if card is actually running. This allows open to * succeed for downloads etc. */ - if (port->card->state == FST_RUNNING) { - if (port->run) { - dbg(DBG_OPEN, "open: found port already running\n"); + dbg(DBG_OPEN, "Opening port %s\n", port->dev->name); + dbg(DBG_OPEN, "Tx buffers = %d of size %d\n", port->num_tx_buffers, + port->tx_buffer_size); + dbg(DBG_OPEN, "Rx buffers = %d of size %d\n", port->num_rx_buffers, + port->rx_buffer_size); + + last_segment_cnt = 0; + /* + * The control information for the tx and rx fifo is in the normal + * shared memory window. Initialise this structure next. + */ + FST_WRW (port->card, dslControl.bytesToSend, 0); + FST_WRL (port->card, dslControl.offsetAtmRx, DR_BASE); + FST_WRL (port->card, dslControl.offsetAtmTx, DT_BASE); + if ( port->card->state == FST_RUNNING ) + { + if ( port->run ) + { + dbg ( DBG_OPEN,"open: found port already running\n"); - fst_issue_cmd(port, STOPPORT); + fst_issue_cmd ( port, STOPPORT ); port->run = 0; } - fst_rx_config(port); - fst_tx_config(port); - fst_op_raise(port, OPSTS_RTS | OPSTS_DTR); - - fst_issue_cmd(port, STARTPORT); + fst_rx_config ( port ); + fst_tx_config ( port ); + fst_op_raise ( port, OPSTS_RTS | OPSTS_DTR ); + if (port->card->type == FST_TYPE_DSL_S1) + atm_fifo_init(port->card); + fst_issue_cmd ( port, STARTPORT ); port->run = 1; - signals = FST_RDL(port->card, v24DebouncedSts[port->index]); - if (signals & (((port->hwif == X21) || (port->hwif == X21D)) - ? IPSTS_INDICATE : IPSTS_DCD)) - netif_carrier_on(port_to_dev(port)); + if (1/*!port->char_file*/) + { + signals = FST_RDL ( port->card, v24DebouncedSts[port->index]); + if ( signals & (((port->hwif == X21) || (port->hwif == X21D)) + ? IPSTS_INDICATE : IPSTS_DCD )) + netif_carrier_on ( port_to_dev(port) ); else - netif_carrier_off(port_to_dev(port)); - + netif_carrier_off ( port_to_dev(port) ); + } txq_length = port->txqe - port->txqs; port->txqe = 0; port->txqs = 0; + port->rxq.error_recovery = 0; + port->char_inq = NULL; + port->char_inq_end = NULL; } - } static void -fst_closeport(struct fst_port_info *port) +fst_closeport ( struct fst_port_info *port ) { - if (port->card->state == FST_RUNNING) { - if (port->run) { + if ( port->card->state == FST_RUNNING ) + { + if ( port->run ) + { port->run = 0; - fst_op_lower(port, OPSTS_RTS | OPSTS_DTR); + fst_op_lower ( port, OPSTS_RTS | OPSTS_DTR ); - fst_issue_cmd(port, STOPPORT); - } else { - dbg(DBG_OPEN, "close: port not running\n"); + fst_issue_cmd ( port, STOPPORT ); + } + else + { + dbg ( DBG_OPEN,"close: port not running\n"); } } } + static int -fst_open(struct net_device *dev) +fst_open ( struct net_device *dev ) { int err; struct fst_port_info *port; + struct fst_card_info *card; port = dev_to_port(dev); + card = port->card; + + if (card->state != FST_RUNNING) + { + dbg(DBG_ASS, "%s: Cannot open port if card is not loaded\n", dev->name); + return -ENODEV; + } + if (port->run) + { + dbg(DBG_ASS, "%s: Port is already open\n", dev->name); + return -EBUSY; + } + if (!try_module_get(THIS_MODULE)) return -EBUSY; - if (port->mode != FST_RAW) { - err = hdlc_open(dev); - if (err) + if (port->proto != FST_RAW) + { + err = hdlc_open ( dev ); + if ( err ) return err; } - fst_openport(port); - netif_wake_queue(dev); + fst_openport ( port ); + netif_wake_queue ( dev ); return 0; } static int -fst_close(struct net_device *dev) +fst_close ( struct net_device *dev ) { struct fst_port_info *port; struct fst_card_info *card; @@ -2258,82 +6405,90 @@ fst_close(struct net_device *dev) port = dev_to_port(dev); card = port->card; - tx_dma_done = inb(card->pci_conf + DMACSR1); - rx_dma_done = inb(card->pci_conf + DMACSR0); - dbg(DBG_OPEN, - "Port Close: tx_dma_in_progress = %d (%x) rx_dma_in_progress = %d (%x)\n", - card->dmatx_in_progress, tx_dma_done, card->dmarx_in_progress, - rx_dma_done); - - netif_stop_queue(dev); - fst_closeport(dev_to_port(dev)); - if (port->mode != FST_RAW) { - hdlc_close(dev); + tx_dma_done = inb ( card->pci_conf + DMACSR1 ); + rx_dma_done = inb ( card->pci_conf + DMACSR0 ); + dbg(DBG_OPEN, "Port Close: tx_dma_in_progress = %d (%x) rx_dma_in_progress = %d (%x)\n", + card->dmatx_in_progress, tx_dma_done, + card->dmarx_in_progress, rx_dma_done); + + netif_stop_queue ( dev ); + fst_closeport ( dev_to_port (dev) ); + if (port->proto != FST_RAW) + { + hdlc_close ( dev ); } module_put(THIS_MODULE); return 0; } static int -fst_attach(struct net_device *dev, unsigned short encoding, unsigned short parity) +fst_attach ( struct net_device *dev, unsigned short encoding, unsigned short parity) { /* * Setting currently fixed in FarSync card so we check and forget */ - if (encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT) + if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT ) return -EINVAL; return 0; } static void -fst_tx_timeout(struct net_device *dev) +fst_tx_timeout ( struct net_device *dev ) { struct fst_port_info *port; struct fst_card_info *card; struct net_device_stats *stats = hdlc_stats(dev); - port = dev_to_port(dev); + port = dev_to_port (dev); card = port->card; + stats->tx_errors++; stats->tx_aborted_errors++; dbg(DBG_ASS, "Tx timeout card %d port %d\n", card->card_no, port->index); - fst_issue_cmd(port, ABORTTX); + fst_issue_cmd ( port, ABORTTX ); dev->trans_start = jiffies; - netif_wake_queue(dev); + netif_wake_queue ( dev ); port->start = 0; } + static int -fst_start_xmit(struct sk_buff *skb, struct net_device *dev) +fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) { + struct net_device_stats *stats = hdlc_stats(dev); struct fst_card_info *card; struct fst_port_info *port; - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; int txq_length; + struct sk_buff *new_skb; - port = dev_to_port(dev); + port = dev_to_port ( dev ); card = port->card; - dbg(DBG_TX, "fst_start_xmit: length = %d\n", skb->len); + dbg(DBG_TX,"fst_start_xmit: length = %d\n", skb->len); /* Drop packet with error if we don't have carrier */ - if (!netif_carrier_ok(dev)) { - dev_kfree_skb(skb); + if ( ! netif_carrier_ok ( dev )) + { + dev_kfree_skb ( skb ); stats->tx_errors++; stats->tx_carrier_errors++; - dbg(DBG_ASS, - "Tried to transmit but no carrier on card %d port %d\n", - card->card_no, port->index); + dbg(DBG_ASS, "%s: Tried to transmit but no carrier on card %d port %d\n", + port_to_dev(port)->name, card->card_no, port->index); + /* + * Double check that the line is still down + */ + fst_intr_ctlchg( card, port); return 0; } /* Drop it if it's too big! MTU failure ? */ - if (skb->len > LEN_TX_BUFFER) { - dbg(DBG_ASS, "Packet too large %d vs %d\n", skb->len, - LEN_TX_BUFFER); - dev_kfree_skb(skb); + if ( skb->len > FST_MAX_MTU ) + { + printk_err("Tx Packet too large %d vs %d\n", skb->len, + FST_MAX_MTU ); + dev_kfree_skb ( skb ); stats->tx_errors++; return 0; } @@ -2343,29 +6498,32 @@ fst_start_xmit(struct sk_buff *skb, stru * so that the bottom half is the only place we tx from * Check there is room in the port txq */ - spin_lock_irqsave(&card->card_lock, flags); - if ((txq_length = port->txqe - port->txqs) < 0) { + spin_lock_irqsave ( &card->card_lock, flags ); + if ((txq_length = port->txqe - port->txqs) < 0) + { /* * This is the case where the next free has wrapped but the * last used hasn't */ txq_length = txq_length + FST_TXQ_DEPTH; } - spin_unlock_irqrestore(&card->card_lock, flags); - if (txq_length > fst_txq_high) { + spin_unlock_irqrestore ( &card->card_lock, flags ); + if (txq_length > fst_txq_high) + { /* * We have got enough buffers in the pipeline. Ask the network * layer to stop sending frames down */ - netif_stop_queue(dev); + netif_stop_queue ( dev ); port->start = 1; /* I'm using this to signal stop sent up */ } - if (txq_length == FST_TXQ_DEPTH - 1) { + if (txq_length == FST_TXQ_DEPTH - 1) + { /* * This shouldn't have happened but such is life */ - dev_kfree_skb(skb); + dev_kfree_skb ( skb ); stats->tx_errors++; dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", card->card_no, port->index); @@ -2375,20 +6533,47 @@ fst_start_xmit(struct sk_buff *skb, stru /* * queue the buffer */ - spin_lock_irqsave(&card->card_lock, flags); - port->txq[port->txqe] = skb; + if ((port->vpi == 0) && (port->vci == 0)) + { + /* + * No ATM encapsulation required so no new skb is + * allocated. Just update the pointer of new_skb + */ + new_skb = skb; + } + else + { + new_skb = generate_pdu(port, skb); + kfree_skb(skb); + } + if (new_skb == NULL) + { + dbg(DBG_ASS, "Error in queuing atm pdu\n"); + return 0; + } + if (port->monitor_mode) + { + gen_mon_packet(new_skb, port, FST_MON_TX); + } + spin_lock_irqsave ( &card->card_lock, flags ); + port->txq[port->txqe].frame = new_skb; + port->txq[port->txqe].count = new_skb->len; + port->txq[port->txqe].segment_cnt = + ((new_skb->len-1)/port->tx_buffer_size) + 1 ; + port->txq[port->txqe].current_seg = 0; + port->txq[port->txqe].flags = 0; port->txqe++; if (port->txqe == FST_TXQ_DEPTH) port->txqe = 0; - spin_unlock_irqrestore(&card->card_lock, flags); + spin_unlock_irqrestore ( &card->card_lock, flags ); /* Scehdule the bottom half which now does transmit processing */ fst_q_work_item(&fst_work_txq, card->card_no); tasklet_schedule(&fst_tx_task); - return 0; } + /* * Card setup having checked hardware resources. * Should be pretty bizarre if we get an error here (kernel memory @@ -2396,18 +6581,9 @@ fst_start_xmit(struct sk_buff *skb, stru * via a printk and leave the corresponding interface and all that follow * disabled. */ -static char *type_strings[] __devinitdata = { - "no hardware", /* Should never be seen */ - "FarSync T2P", - "FarSync T4P", - "FarSync T1U", - "FarSync T2U", - "FarSync T4U", - "FarSync TE1" -}; static void __devinit -fst_init_card(struct fst_card_info *card) +fst_init_card ( struct fst_card_info *card ) { int i; int err; @@ -2416,8 +6592,13 @@ fst_init_card(struct fst_card_info *card * firmware detects something different later (should never happen) * we'll have to revise it in some way then. */ - for (i = 0; i < card->nports; i++) { - err = register_hdlc_device(card->ports[i].dev); + for ( i = 0 ; i < card->nports ; i++ ) + { + struct net_device *dev = alloc_hdlcdev(&card->ports[i], "fars%d"); + hdlc_device *hdlc; + + card->ports[i].dev = dev; + err = register_hdlc_device(dev); if (err < 0) { int j; printk_err ("Cannot register HDLC device for port %d" @@ -2429,30 +6610,98 @@ fst_init_card(struct fst_card_info *card card->nports = i; break; } + card->ports[i].card = card; + card->ports[i].index = i; + card->ports[i].run = 0; + card->ports[i].proto = FST_GEN_HDLC; + card->ports[i].num_tx_buffers = REQUIRED_TX_BUFFERS; + card->ports[i].num_rx_buffers = REQUIRED_RX_BUFFERS; + card->ports[i].tx_buffer_size = REQUIRED_TX_BUFFER_SIZE; + card->ports[i].rx_buffer_size = REQUIRED_RX_BUFFER_SIZE; + card->ports[i].char_file = NULL; + card->ports[i].char_inq = NULL; + card->ports[i].char_inq_end = NULL; + spin_lock_init ( &card->ports[i].rxf_lock ); + init_waitqueue_head ( &card->ports[i].pollq ); + + hdlc = dev_to_hdlc(dev); + + /* Fill in the net device info + * Since this is a PCI setup this is purely + * informational. Give them the buffer addresses + * and basic card I/O. + */ + dev->mem_start = card->phys_mem + + BUF_OFFSET ( txBuffer[i][0]); + dev->mem_end = card->phys_mem + + BUF_OFFSET ( txBuffer[i][0]) + + (card->ports[i].num_tx_buffers * + card->ports[i].tx_buffer_size); + dev->base_addr = card->pci_conf; + dev->irq = card->irq; + + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->open = fst_open; + dev->stop = fst_close; + dev->do_ioctl = fst_ioctl; + dev->watchdog_timeo = FST_TX_TIMEOUT; + dev->tx_timeout = fst_tx_timeout; + hdlc->attach = fst_attach; + hdlc->xmit = fst_start_xmit; + } - printk_info("%s-%s: %s IRQ%d, %d ports\n", + spin_lock_init ( &card->card_lock ); + spin_lock_init ( &card->fifo_lock); + init_waitqueue_head (&card->fifo_waitq); + + printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n", port_to_dev(&card->ports[0])->name, - port_to_dev(&card->ports[card->nports - 1])->name, - type_strings[card->type], card->irq, card->nports); + port_to_dev(&card->ports[card->nports-1])->name, + type_strings[card->type], card->irq, card->nports ); + + if (card->family == FST_FAMILY_TXU) + { + /* + * Allocate a dma buffer for transmit and receives + */ + card->rx_dma_handle_host = + pci_alloc_consistent(card->device, MAX_LEN_RX_BUFFER, + &card->rx_dma_handle_card); + if (card->rx_dma_handle_host == NULL) + { + printk_err("Could not allocate rx dma buffer\n"); + return; + } + card->tx_dma_handle_host = + pci_alloc_consistent(card->device, MAX_LEN_TX_BUFFER, + &card->tx_dma_handle_card); + if (card->tx_dma_handle_host == NULL) + { + printk_err("Could not allocate tx dma buffer\n"); + return; + } + } } + /* * Initialise card when detected. * Returns 0 to indicate success, or errno otherwise. */ static int __devinit -fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) +fst_add_one ( struct pci_dev *pdev, const struct pci_device_id *ent ) { static int firsttime_done = 0; - static int no_of_cards_added = 0; struct fst_card_info *card; int err = 0; int i; - if (!firsttime_done) { - printk_info("FarSync WAN driver " FST_USER_VERSION - " (c) 2001-2004 FarSite Communications Ltd.\n"); + if ( ! firsttime_done ) + { + printk ( KERN_INFO "FarSync WAN driver " FST_USER_VERSION + "-" FST_PATCH_LEVEL "-" FST_PLATFORM FST_ADDITIONAL + " (c) 2001-2005 FarSite Communications Ltd.\n"); firsttime_done = 1; dbg(DBG_ASS, "The value of debug mask is %x\n", fst_debug_mask); } @@ -2461,232 +6710,232 @@ fst_add_one(struct pci_dev *pdev, const * We are going to be clever and allow certain cards not to be * configured. An exclude list can be provided in /etc/modules.conf */ - if (fst_excluded_cards != 0) { + if (fst_excluded_cards != 0) + { /* * There are cards to exclude * */ - for (i = 0; i < fst_excluded_cards; i++) { - if ((pdev->devfn) >> 3 == fst_excluded_list[i]) { - printk_info("FarSync PCI device %d not assigned\n", - (pdev->devfn) >> 3); + for (i=0; i< fst_excluded_cards; i++) + { + if ((pdev->devfn)>>3 == fst_excluded_list[i]) + { + printk("FarSync PCI device %d not assigned\n", (pdev->devfn)>>3); return -EBUSY; } } } /* Allocate driver private data */ - card = kmalloc(sizeof (struct fst_card_info), GFP_KERNEL); - if (card == NULL) { - printk_err("FarSync card found but insufficient memory for" + card = kmalloc ( sizeof ( struct fst_card_info ), GFP_KERNEL); + if (card == NULL) + { + printk_err ("FarSync card found but insufficient memory for" " driver storage\n"); return -ENOMEM; } - memset(card, 0, sizeof (struct fst_card_info)); + memset ( card, 0, sizeof ( struct fst_card_info )); /* Try to enable the device */ - if ((err = pci_enable_device(pdev)) != 0) { - printk_err("Failed to enable card. Err %d\n", -err); - kfree(card); - return err; - } - - if ((err = pci_request_regions(pdev, "FarSync")) !=0) { - printk_err("Failed to allocate regions. Err %d\n", -err); - pci_disable_device(pdev); - kfree(card); - return err; - } - - /* Get virtual addresses of memory regions */ - card->pci_conf = pci_resource_start(pdev, 1); - card->phys_mem = pci_resource_start(pdev, 2); - card->phys_ctlmem = pci_resource_start(pdev, 3); - if ((card->mem = ioremap(card->phys_mem, FST_MEMSIZE)) == NULL) { - printk_err("Physical memory remap failed\n"); - pci_release_regions(pdev); - pci_disable_device(pdev); - kfree(card); - return -ENODEV; - } - if ((card->ctlmem = ioremap(card->phys_ctlmem, 0x10)) == NULL) { - printk_err("Control memory remap failed\n"); - pci_release_regions(pdev); - pci_disable_device(pdev); - kfree(card); - return -ENODEV; - } - dbg(DBG_PCI, "kernel mem %p, ctlmem %p\n", card->mem, card->ctlmem); - - /* Register the interrupt handler */ - if (request_irq(pdev->irq, fst_intr, IRQF_SHARED, FST_DEV_NAME, card)) { - printk_err("Unable to register interrupt %d\n", card->irq); - pci_release_regions(pdev); - pci_disable_device(pdev); - iounmap(card->ctlmem); - iounmap(card->mem); - kfree(card); - return -ENODEV; + if (( err = pci_enable_device ( pdev )) != 0 ) + { + printk_err ("Failed to enable card. Err %d\n", -err ); + goto error_free_card; } - /* Record info we need */ + /* Record info we need*/ card->irq = pdev->irq; + card->pci_conf = pci_resource_start ( pdev, 1 ); + card->phys_mem = pci_resource_start ( pdev, 2 ); + card->phys_ctlmem = pci_resource_start ( pdev, 3 ); + card->type = ent->driver_data; - card->family = ((ent->driver_data == FST_TYPE_T2P) || - (ent->driver_data == FST_TYPE_T4P)) + card->family = (( ent->driver_data == FST_TYPE_T2P ) || + ( ent->driver_data == FST_TYPE_T4P ) ) ? FST_FAMILY_TXP : FST_FAMILY_TXU; - if ((ent->driver_data == FST_TYPE_T1U) || - (ent->driver_data == FST_TYPE_TE1)) + if ( ( ent->driver_data == FST_TYPE_T1U ) || + ( ent->driver_data == FST_TYPE_TE1 ) || + ( ent->driver_data == FST_TYPE_DSL_S1 ) ) card->nports = 1; else - card->nports = ((ent->driver_data == FST_TYPE_T2P) || - (ent->driver_data == FST_TYPE_T2U)) ? 2 : 4; + card->nports = (( ent->driver_data == FST_TYPE_T2P ) || + ( ent->driver_data == FST_TYPE_T2U) ) ? 2 : 4; card->state = FST_UNINIT; - spin_lock_init ( &card->card_lock ); - - for ( i = 0 ; i < card->nports ; i++ ) { - struct net_device *dev = alloc_hdlcdev(&card->ports[i]); - hdlc_device *hdlc; - if (!dev) { - while (i--) - free_netdev(card->ports[i].dev); - printk_err ("FarSync: out of memory\n"); - free_irq(card->irq, card); - pci_release_regions(pdev); - pci_disable_device(pdev); - iounmap(card->ctlmem); - iounmap(card->mem); - kfree(card); - return -ENODEV; - } - card->ports[i].dev = dev; - card->ports[i].card = card; - card->ports[i].index = i; - card->ports[i].run = 0; + card->device = pdev; - hdlc = dev_to_hdlc(dev); + dbg ( DBG_PCI,"type %d nports %d irq %d\n", card->type, + card->nports, card->irq ); + dbg ( DBG_PCI,"conf %04x mem %08x ctlmem %08x\n", + card->pci_conf, card->phys_mem, card->phys_ctlmem ); - /* Fill in the net device info */ - /* Since this is a PCI setup this is purely - * informational. Give them the buffer addresses - * and basic card I/O. - */ - dev->mem_start = card->phys_mem - + BUF_OFFSET ( txBuffer[i][0][0]); - dev->mem_end = card->phys_mem - + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]); - dev->base_addr = card->pci_conf; - dev->irq = card->irq; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->open = fst_open; - dev->stop = fst_close; - dev->do_ioctl = fst_ioctl; - dev->watchdog_timeo = FST_TX_TIMEOUT; - dev->tx_timeout = fst_tx_timeout; - hdlc->attach = fst_attach; - hdlc->xmit = fst_start_xmit; + /* Check we can get access to the memory and I/O regions */ + if (card->family == FST_FAMILY_TXU) + { + if ( ! request_region ( card->pci_conf, 0x100,"PLX 9054 config regs")) + { + printk_err ("Unable to get config I/O @ 0x%04X\n", + card->pci_conf ); + err = -ENODEV; + goto error_free_card; + } + } + else + { + if ( ! request_region ( card->pci_conf, 0x80,"PLX 9050/2 config regs")) + { + printk_err ("Unable to get config I/O @ 0x%04X\n", + card->pci_conf ); + err = -ENODEV; + goto error_free_card; + } + } + if ( ! request_mem_region ( card->phys_mem, FST_MEMSIZE,"Shared RAM")) + { + printk_err ("Unable to get main memory @ 0x%08X\n", + card->phys_mem ); + err = -ENODEV; + goto error_release_io; + } + if ( ! request_mem_region ( card->phys_ctlmem, 0x10,"Control memory")) + { + printk_err ("Unable to get control memory @ 0x%08X\n", + card->phys_ctlmem ); + err = -ENODEV; + goto error_release_mem; } - card->device = pdev; - dbg(DBG_PCI, "type %d nports %d irq %d\n", card->type, - card->nports, card->irq); - dbg(DBG_PCI, "conf %04x mem %08x ctlmem %08x\n", - card->pci_conf, card->phys_mem, card->phys_ctlmem); + /* Get virtual addresses of memory regions */ + if (( card->mem = ioremap ( card->phys_mem, FST_MEMSIZE )) == NULL ) + { + printk_err ("Physical memory remap failed\n"); + err = -ENODEV; + goto error_release_ctlmem; + } + if (( card->ctlmem = ioremap ( card->phys_ctlmem, 0x10 )) == NULL ) + { + printk_err ("Control memory remap failed\n"); + err = -ENODEV; + goto error_unmap_mem; + } + dbg ( DBG_PCI,"kernel mem %p, ctlmem %p\n", card->mem, card->ctlmem); /* Reset the card's processor */ - fst_cpureset(card); + fst_cpureset ( card ); card->state = FST_RESET; /* Initialise DMA (if required) */ - fst_init_dma(card); + fst_init_dma ( card ); + + /* Register the interrupt handler */ + if ( request_irq ( card->irq, fst_intr, SA_SHIRQ, FST_DEV_NAME, card )) + { + + printk_err ("Unable to register interrupt %d\n", card->irq ); + err = -ENODEV; + goto error_unmap_ctlmem; + } /* Record driver data for later use */ pci_set_drvdata(pdev, card); + if (!pci_dma_supported(pdev, 0xffffffff)) + { + printk("Can't do DMA on this device\n"); + } /* Remainder of card setup */ - fst_card_array[no_of_cards_added] = card; - card->card_no = no_of_cards_added++; /* Record instance and bump it */ - fst_init_card(card); - if (card->family == FST_FAMILY_TXU) { - /* - * Allocate a dma buffer for transmit and receives - */ - card->rx_dma_handle_host = - pci_alloc_consistent(card->device, FST_MAX_MTU, - &card->rx_dma_handle_card); - if (card->rx_dma_handle_host == NULL) { - printk_err("Could not allocate rx dma buffer\n"); - fst_disable_intr(card); - pci_release_regions(pdev); - pci_disable_device(pdev); - iounmap(card->ctlmem); - iounmap(card->mem); - kfree(card); - return -ENOMEM; - } - card->tx_dma_handle_host = - pci_alloc_consistent(card->device, FST_MAX_MTU, - &card->tx_dma_handle_card); - if (card->tx_dma_handle_host == NULL) { - printk_err("Could not allocate tx dma buffer\n"); - fst_disable_intr(card); - pci_release_regions(pdev); - pci_disable_device(pdev); - iounmap(card->ctlmem); - iounmap(card->mem); - kfree(card); - return -ENOMEM; - } - } + fifo_init(card); + if (card->type == FST_TYPE_DSL_S1) + atm_fifo_init(card); + fst_cards_list[fst_ncards] = card; + card->card_no = fst_ncards++; /* Record instance and bump it */ + card->fifo_complete = 1; + fst_init_card ( card ); + return 0; /* Success */ + + + /* Failure. Release resources */ +error_unmap_ctlmem: + iounmap ( card->ctlmem ); + +error_unmap_mem: + iounmap ( card->mem ); + +error_release_ctlmem: + release_mem_region ( card->phys_ctlmem, 0x10 ); + +error_release_mem: + release_mem_region ( card->phys_mem, FST_MEMSIZE ); + +error_release_io: + release_region ( card->pci_conf, 0x80 ); + +error_free_card: + kfree ( card ); + return err; } + /* * Cleanup and close down a card */ static void __devexit -fst_remove_one(struct pci_dev *pdev) +fst_remove_one ( struct pci_dev *pdev ) { struct fst_card_info *card; int i; card = pci_get_drvdata(pdev); - for (i = 0; i < card->nports; i++) { + for ( i = 0 ; i < card->nports ; i++ ) + { struct net_device *dev = port_to_dev(&card->ports[i]); - unregister_hdlc_device(dev); + unregister_hdlc_device ( dev ); } - fst_disable_intr(card); - free_irq(card->irq, card); + fst_disable_intr ( card ); + free_irq ( card->irq, card ); + + iounmap ( card->ctlmem ); + iounmap ( card->mem ); + + release_mem_region ( card->phys_ctlmem, 0x10 ); + release_mem_region ( card->phys_mem, FST_MEMSIZE ); + if (card->family == FST_FAMILY_TXU) + { + release_region ( card->pci_conf, 0x100 ); + } + else + { + release_region ( card->pci_conf, 0x80 ); + } - iounmap(card->ctlmem); - iounmap(card->mem); - pci_release_regions(pdev); - if (card->family == FST_FAMILY_TXU) { + if ( card->family == FST_FAMILY_TXU) + { /* * Free dma buffers */ - pci_free_consistent(card->device, FST_MAX_MTU, + pci_free_consistent(card->device, MAX_LEN_RX_BUFFER, card->rx_dma_handle_host, card->rx_dma_handle_card); - pci_free_consistent(card->device, FST_MAX_MTU, + pci_free_consistent(card->device, MAX_LEN_TX_BUFFER, card->tx_dma_handle_host, card->tx_dma_handle_card); } - fst_card_array[card->card_no] = NULL; + fst_cards_list[card->card_no] = NULL; + fst_cpureset(card); } static struct pci_driver fst_driver = { - .name = FST_NAME, - .id_table = fst_pci_dev_id, - .probe = fst_add_one, - .remove = __devexit_p(fst_remove_one), - .suspend = NULL, - .resume = NULL, + name: FST_NAME, + id_table: fst_pci_dev_id, + probe: fst_add_one, + remove: __devexit_p(fst_remove_one), + suspend: NULL, + resume: NULL, }; static int __init @@ -2694,18 +6943,53 @@ fst_init(void) { int i; - for (i = 0; i < FST_MAX_CARDS; i++) - fst_card_array[i] = NULL; - spin_lock_init(&fst_work_q_lock); - return pci_register_driver(&fst_driver); + for (i=0; i