From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kevin Curtis Subject: [PATCH 005/007] WAN Drivers: Update farsync driver and introduce fsflex driver Date: Wed, 18 Sep 2013 11:12:18 +0100 Message-ID: Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 8BIT Cc: "linux-kernel@vger.kernel.org" , "kernel-janitors@vger.kernel.org" , Dermot Smith To: "netdev@vger.kernel.org" Return-path: Content-Language: en-US Sender: linux-kernel-owner@vger.kernel.org List-Id: netdev.vger.kernel.org Farsite Communications FarSync driver update Patch 5 of 7 Note that this patch must be applied with patch 4 (farsync_include_patch) Update the current farsync driver to support all of the PCI and PCI X cards manufactured by Farsite Communications. Add a tty interface so that the ports can also be used by the ppp daemon. Add support for big endian systems (the FarSite cards have a little endian processor on board). Signed-off-by: Kevin Curtis --- diff -uprN -X linux-3.10.1/Documentation/dontdiff linux-3.10.1/drivers/net/wan/farsync.c linux-3.10.1_new/drivers/net/wan/farsync.c --- linux-3.10.1/drivers/net/wan/farsync.c 2013-07-13 19:42:41.000000000 +0100 +++ linux-3.10.1_new/drivers/net/wan/farsync.c 2013-09-16 16:30:06.391104883 +0100 @@ -3,16 +3,16 @@ * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * - * Copyright (C) 2001-2004 FarSite Communications Ltd. - * www.farsite.co.uk + * Copyright (C) 2001-2013 FarSite Communications Ltd. + * www.farsite.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: R.J.Dunlop - * Maintainer: Kevin Curtis + * Author: R.J.Dunlop + * Maintainer: Kevin Curtis */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -20,18 +20,29 @@ #include #include #include -#include +#include #include -#include +#include +#include +#include #include #include -#include +#include #include +#include #include -#include -#include - +#include +#include +/* TTY includes */ +#include +#include +#include +#include +#include +#include +#define FST_BUILD_NO "-k219" #include "farsync.h" +#include "fscmn.h" /* * Module info @@ -44,28 +55,65 @@ MODULE_LICENSE("GPL"); * ========================================== */ +#define FST_PORT_NAME "hdlc" +#define FST_DRIVER_TYPE "WAN" + /* Number of ports (per card) and cards supported */ #define FST_MAX_PORTS 4 #define FST_MAX_CARDS 32 +/* + * 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 + */ +#define FST_MIN_DMA_LEN 64 /* do DMA on transfer > 64 bytes */ + +int fst_txq_low = FST_LOW_WATER_MARK; +int fst_txq_high = FST_HIGH_WATER_MARK; +int fst_min_dma_len = FST_MIN_DMA_LEN; +int fst_dmathr = 0xdd00dd00; +int fst_iocinfo_version = FST_VERSION_CURRENT; +int fst_max_reads = 7; +int fst_excluded_cards; +int fst_excluded_list[FST_MAX_CARDS]; + +module_param(fst_txq_low, int, S_IRUGO); +module_param(fst_txq_high, int, S_IRUGO); +module_param(fst_min_dma_len, int, S_IRUGO); +module_param(fst_dmathr, int, S_IRUGO); +module_param(fst_iocinfo_version, 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); + /* Default parameters for the link */ #define FST_TX_QUEUE_LEN 100 /* At 8Mbps a longer queue length is - * useful */ + * useful, the syncppp module forces + * this down assuming a slower line I + * guess. + */ #define FST_TXQ_DEPTH 16 /* This one is for the buffering - * of frames on the way down to the card - * so that we can keep the card busy - * and maximise throughput + * of frames on the way down to the + * card so that we can keep the card + * busy and maximise throughput + */ +#define FST_MAX_MTU (32*1024) /* Limitation of hdlc controller on + * card */ -#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_DEF_MTU 1500 /* Common sane value */ - -#define FST_TX_TIMEOUT (2*HZ) +#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 (10*HZ) +#define FST_MAX_SEGMENTS (FST_MAX_MTU/128) /* Max tx/rx desriptors/frame */ #ifdef ARPHRD_RAWHDLC #define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */ @@ -73,25 +121,10 @@ MODULE_LICENSE("GPL"); #define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */ #endif -/* - * Modules parameters and associated variables - */ -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); - /* 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 @@ -103,23 +136,33 @@ 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 - * buffers */ + * configuration structure + */ +#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 @@ -189,6 +232,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 @@ -211,54 +262,181 @@ 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 TXB_TBUA 0x49 +#define TXC_TBUA 0x4A +#define TXD_TBUA 0x4B + +#define RXA_RBUA 0x4C +#define RXB_RBUA 0x4D +#define RXC_RBUA 0x4E +#define RXD_RBUA 0x4f + +/* Data Encoding for the T4E+ */ + +#define FSCMN_ENCODING_NRZ 0x80 +#define FSCMN_ENCODING_NRZI 0xa0 +#define FSCMN_ENCODING_FM0 0xc0 +#define FSCMN_ENCODING_FM1 0xd0 +#define FSCMN_ENCODING_MANCHESTER 0xe0 +#define FSCMN_ENCODING_DIFF_MANCHESTER 0xf0 /* Port physical configuration. See farsync.h for field values */ struct port_cfg { - u16 lineInterface; /* Physical interface type */ + u16 line_interface; /* Physical interface type */ u8 x25op; /* Unused at present */ - u8 internalClock; /* 1 => internal clock, 0 => external */ - u8 transparentMode; /* 1 => on, 0 => off */ - u8 invertClock; /* 0 => normal, 1 => inverted */ - u8 padBytes[6]; /* Padding */ - u32 lineSpeed; /* Speed in bps */ + u8 internal_clock; /* 1 => internal clock, 0 => external */ + u8 transparent_mode; /* 1 => on, 0 => off */ + u8 invert_clock; /* 0 => normal, 1 => inverted */ + u8 tx_rx_start; /* 0 = start tx and rx on open */ + /* 1 = start tx only */ + /* 2 = start rx only */ + u8 num_tx_buffers; /* Number of Tx Buffers to use, max 128 */ + u8 num_rx_buffers; /* Number of Rx Buffers to use, max 128 */ + u8 clock_source; /* FS_CLOCK_REFERENCE_xxx */ + u8 padBytes[2]; /* Padding to 16 bytes */ + u32 line_speed; /* Speed in bps */ + /* + * Extras for T4E Mk II + */ + u8 extended_clocking; /* Enabling flag */ + u8 internal_tx_clock; /* only if extended_clocking is TRUE */ + u8 internal_rx_clock; /* only if extended_clocking is TRUE */ + u8 terminal_tx_clock; /* only if extended_clocking is TRUE */ + u8 terminal_rx_clock; /* only if extended_clocking is TRUE */ + u8 dcd_output; /* TRUE => v24opsts.dcd will apply */ + u8 transmit_msb_first; /* default (FALSE) tx lsb first */ + /* tx MSB first is normally only used in */ + /* (some) transparent applications */ + u8 receive_msb_first; /* default (FALSE) rx lsb first */ + /* rx MSB first is normally only used in */ + /* (some) transparent applications */ + u32 estimated_line_speed; /* applies only is linespeed is 0 */ + u8 encoding; /* default NRZ, NRZI, FM0, FM1 or Manchester */ + u8 enable_nrzi_clocking; /* default FALSE, set TRUE for NRZI */ + u8 termination; /* default FALSE no termination */ + u8 immediate_ints; /* set True for low latency mode */ +}; + +struct dsl_config { + u32 data_rate; /* data rate in bps */ + u8 terminal_type; /* central or remote */ + u8 annex_type; /* A (US) or B (EU) */ + u8 test_mode; /* Various loop modes */ + u8 backoff; /* Power backoff in dB */ + u8 b_line_probing_enable; /* set TRUE to probe line */ + u8 snrth; /* Signal to Noise Ratio Threshold in dB */ + /* SNR Margin Defect when signal_quality + * 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 activation_status; + u8 no_common_mode_status; + u8 transceiverStatus1; + u8 transceiverStatus2; + u8 line_loss; /* dB */ + char signal_quality; + u8 near_end_block_error_count; /* wraps after 0xff, s/w can rst */ + char signal_to_noise_ratio; /* dB */ + u8 code_violation_count; + u8 errored_second_count; /* | */ + u8 severely_errored_second_count; /* > reset after download */ + u8 loss_of_sync_word_second_count;/* | */ + u8 unavailable_second_count; + char frequency_deviation; + char negotiated_power_back_off; + u8 negotiated_psd; + u8 negotiated_b_channels; + u8 negotiated_z_bits; + u16 negotiated_sync_word; + u8 negotiated_stuff_bits; + u8 chip_version; + u8 firmware_version; + u8 rom_version; + u16 atm_tx_cell_count; + u16 atm_rx_cell_count; + u16 atm_hec_error_count; + u8 b_link_up; /* TRUE => link is up and ready to pass data */ + /* FALSE => link is down */ + u8 xpld_version; /* 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 utopia_atm_status; /* 1 byte status */ + u8 spare[23]; /* adjust structure to 32 bytes */ +}; + +struct dsl_control { + u32 offset_atm_rx; + u32 offset_atm_tx; + u16 bytes_to_send; /* 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; + u32 data_rate; /* data rate in bps */ + u8 clocking; /* master or slave */ + u8 framing; /* E1, T1 or J1 */ u8 structure; - u8 interface; - u8 coding; - u8 lineBuildOut; - u8 equalizer; - u8 transparentMode; - u8 loopMode; + u8 interface; /* RJ48C or BNC */ + u8 coding; /* HDB3 or B8ZS */ + u8 line_build_out; + u8 equalizer; /* short on long haul settings */ + u8 transparent_mode; /* hdlc (0) or transparent (1) */ + u8 loop_mode; u8 range; - u8 txBufferMode; - u8 rxBufferMode; - u8 startingSlot; - u8 losThreshold; - u8 enableIdleCode; - u8 idleCode; - u8 spare[44]; + u8 tx_buffer_mode; /* 0, 96 bits 1 frame or 2 frames */ + u8 rx_buffer_mode; /* 0, 96 bits 1 frame or 2 frames */ + u8 starting_slot; /* E1 1-31; T1/J1 0-23 */ + u8 los_threshold; /* 0-7 */ + u8 enable_idle_code; /* 0 disabled, 1 enabled */ + u8 idle_code; /* 0x00 to 0xff */ + u8 ext_mode; /* LOBYTE of HIWORD(FS_LINE_MODE_*) */ + u8 spare1[1]; /* adjust to keep structure size 64 bytes */ + u8 ext_sync_clock_enable; /* TE1e: Enable external sync clock */ + u8 ext_sync_clock_offset; /* TE1e: Enable d.c. offset on clock */ + u32 ext_sync_clock_rate; /* TE1e: Sync clock rate */ + u8 pps_enable; /* TE1e: Enable 1PPS sync */ + u8 pps_offset; /* TE1e: 1PPS d.c. offset */ + u8 spare[34]; /* Keep structure 64 bytes */ }; /* TE1 Status */ struct su_status { - u32 receiveBufferDelay; - u32 framingErrorCount; - u32 codeViolationCount; - u32 crcErrorCount; - u32 lineAttenuation; + u32 receive_buffer_delay; /* delay trhough rx buffer (o-63) */ + u32 framing_error_count; /* count of framing errors */ + u32 code_violation_count; /* count of code vilations */ + u32 crc_error_count; /* count of CRC errors */ + u32 line_attenuation; /* in dB */ u8 portStarted; - u8 lossOfSignal; - u8 receiveRemoteAlarm; - u8 alarmIndicationSignal; - u8 spare[40]; + u8 loss_of_signal; /* LOS alarm */ + u8 receive_remote_alarm; /* RRA alarm */ + u8 alarm_indication_signal; /* 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. @@ -267,14 +445,16 @@ 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 rx_descr_ring[FST_MAX_PORTS][MAX_RX_BUFFER]; + struct txdesc tx_descr_ring[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 small_rx_buffer[FST_MAX_PORTS][NUM_SMALL_RX_BUFFER] + [LEN_SMALL_RX_BUFFER]; + u8 small_tx_buffer[FST_MAX_PORTS][NUM_SMALL_TX_BUFFER] + [LEN_SMALL_TX_BUFFER]; - u8 taskStatus; /* 0x00 => initialising, 0x01 => running, + u8 task_status; /* 0x00 => initialising, 0x01 => running, * 0xFF => halted */ @@ -282,12 +462,16 @@ struct fst_shared { * set to 0xEE by host to acknowledge interrupt */ - u16 smcVersion; /* Must match SMC_VERSION */ + u16 smc_version; /* Must match SMC_VERSION */ - u32 smcFirmwareVersion; /* 0xIIVVRRBB where II = product ID, VV = major + u32 smc_firmware_version; /* 0xIIVVRRBB where II = product ID, + * VV = major * version, RR = revision and BB = build */ + u8 async_ability[FST_MAX_PORTS]; /* TRUE => async and sync */ + /* FALSE => sync only */ + u8 synth_ability; /* TRUE => synthesizer present */ u16 txa_done; /* Obsolete completion flags */ u16 rxa_done; u16 txb_done; @@ -299,19 +483,20 @@ struct fst_shared { u16 mailbox[4]; /* Diagnostics mailbox. Not used */ - struct cirbuff interruptEvent; /* interrupt causes */ + struct cirbuff interrupt_event; /* interrupt causes */ u32 v24IpSts[FST_MAX_PORTS]; /* V.24 control input status */ u32 v24OpSts[FST_MAX_PORTS]; /* V.24 control output status */ - struct port_cfg portConfig[FST_MAX_PORTS]; + struct port_cfg port_config[FST_MAX_PORTS]; - u16 clockStatus[FST_MAX_PORTS]; /* lsb: 0=> present, 1=> absent */ + u16 clock_status[FST_MAX_PORTS]; /* lsb: 0=> present, + * 1=> absent + */ + u16 cable_status; /* lsb: 0=> present, 1=> absent */ - u16 cableStatus; /* lsb: 0=> present, 1=> absent */ - - u16 txDescrIndex[FST_MAX_PORTS]; /* transmit descriptor ring index */ - u16 rxDescrIndex[FST_MAX_PORTS]; /* receive descriptor ring index */ + u16 tx_descr_index[FST_MAX_PORTS]; /* transmit descriptor ring index */ + u16 rx_descr_index[FST_MAX_PORTS]; /* receive descriptor ring index */ u16 portMailbox[FST_MAX_PORTS][2]; /* command, modifier */ u16 cardMailbox[4]; /* Not used */ @@ -339,21 +524,61 @@ struct fst_shared { u32 ctsTimerRun[FST_MAX_PORTS]; u32 dcdTimer[FST_MAX_PORTS]; u32 dcdTimerRun[FST_MAX_PORTS]; + u32 ri_timer[FST_MAX_PORTS]; + u32 ri_timer_run[FST_MAX_PORTS]; - u32 numberOfPorts; /* Number of ports detected at startup */ + u32 number_of_ports; /* Number of ports detected at startup */ u16 _reserved[64]; - u16 cardMode; /* Bit-mask to enable features: + u16 card_mode; /* Bit-mask to enable features: * Bit 0: 1 enables LED identify mode */ u16 portScheduleOffset; - struct su_config suConfig; /* TE1 Bits */ - struct su_status suStatus; + struct su_config su_config; /* TE1 Config structure */ + struct su_status su_status; /* TE1 Stats structure */ + struct dsl_config dsl_config; /* DSL Config structure */ + struct dsl_status dsl_status; /* DSL Stats structure */ + struct dsl_control dsl_control; /* DSL FiFos etc */ + +#define NOTIFICATION_FIFO_LEN 64 + char card_notifications[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 ct_bus_primary_mode; /* e.g. FS_CT_BUS_MODE_xxx */ + u32 ct_bus_primary_feed; /* e.g. FS_CLOCK_REFERENCE_xxx */ + u32 ct_bus_backup_mode; /* e.g. FS_CT_BUS_MODE_xxx */ + u32 ct_bus_backup_feed; /* e.g. FS_CLOCK_REFERENCE_xxx */ + u8 cta_clock_present; /* 1 => CTA present, 0 => CTA absent */ + u8 b_primary_fallback; /* 1 => Primary to Backup fallback allowed */ + u8 b_backup_fallback; /* 1 => Backup to Local Osc fallback allowed */ + u8 b_primary_clock_status; /* 0 = out-of-tolerance, 1 = good */ + u8 b_backup_clock_status; /* 0 = out-of-tolerance, 1 = good */ + u8 b_ct_a_status; /* 0 = out-of-tolerance, 1 = good */ + u8 b_ct_b_status; /* 0 = out-of-tolerance, 1 = good */ + + u16 current_status_summary; /* 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 capability_mask; /* T4E+ and newer, T4E+ sets bit 0 true */ + u8 fill; /* Temporary to fix an alignment issue */ + + u32 u_current_config_reference; /* e.g. FS_CONFIG_IN_USE_xxx */ - u32 endOfSmcSignature; /* endOfSmcSignature MUST be the last member of + u32 end_of_smc_signature; /* endOfSmcSignature MUST be the + * last member of * the structure and marks the end of shared * memory. Adapter code initializes it as * END_SIG. @@ -371,11 +596,17 @@ struct fst_shared { #define STOPPORT 4 /* Stop an HDLC port */ #define ABORTTX 5 /* Abort the transmitter for a port */ #define SETV24O 6 /* Set V24 outputs */ +#define RECONFIG 7 /* Reconfigure port (esp async) */ +#define FLUSHTX 8 /* Flush the Tx Fifo (esp async) */ +#define SETXON 9 /* XON command */ +#define SETXOFF 10 /* XOFF command */ +#define RECONFIGLINE 11 /* Reconfigure the port line parameters */ /* 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 */ @@ -402,52 +633,324 @@ struct fst_shared { #define DMADAC1 0xb8 #define DMAMARBR 0xac -#define FST_MIN_DMA_LEN 64 #define FST_RX_DMA_INT 0x01 #define FST_TX_DMA_INT 0x02 #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 tx_buffer[FST_MAX_PORTS][TX_BUFFER_SPACE]; + u8 rx_buffer[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 long)&(((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 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)) + +#define FIFO_RDB(M) readb(M) +#define FIFO_RDW(M) readw(M) +#define FIFO_RDL(M) readl(M) +#define FIFO_WRB(M, B) writeb((B), (M)) +#define FIFO_WRW(M, W) writew((W), (M)) +#define FIFO_WRL(M, L) writel((L), (M)) + +/* 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 + +/* these replace the original portMailbox commands that could result in + * lost commands if the card was slow in processing the previous one. + */ + +#define CMD_FIFO_START_PORT 0x0080 +#define CMD_FIFO_STOP_PORT 0x0100 +#define CMD_FIFO_SET_V24 0x0200 +#define CMD_FIFO_ABORT_PORT 0x0400 + +#define CMD_FIFO_STAT_RESET 0x0800 + +#define CMD_FIFO_CONFIG_CT_BUS 0x1000 + +/* T4E MkII */ +#define CMD_FIFO_RECONFIG_PORT 0x2000 + +#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_RX_DATA_FIFO_HIGH (3*FST_RX_DATA_FIFO_LEN/4) +#define FST_RX_DATA_FIFO_LOW (1*FST_RX_DATA_FIFO_LEN/4) +#define FST_TX_DATA_FIFO_LOW (1*FST_TX_DATA_FIFO_LEN/4) +#define FST_MSG_FIFO_LEN 64 +#define FST_RSP_FIFO_LEN 64 +#define MAX_RX_EVENT_FIFO_LEN 1024 + +#define ASYNC_STAT_VAL 0x80 /* data associated with this status */ +#define ASYNC_STAT_CD 0x40 /* state of Carrier Detect Signal */ +#define ASYNC_STAT_RI 0x20 /* state of Ring Indicate Signal */ +#define ASYNC_STAT_CTS 0x10 /* state of Clear to Send Signal */ +#define ASYNC_STAT_BRK 0x08 /* receiving Break */ +#define ASYNC_STAT_FRM 0x04 /* framing error */ +#define ASYNC_STAT_PAR 0x02 /* parity error */ +#define ASYNC_STAT_OVR 0x01 /* overrun error */ + +#pragma pack(2) +typedef struct async_rx_event { + u8 status_byte; + u8 character; + u32 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 rx_event_fifos[FST_MAX_PORTS][MAX_RX_EVENT_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 long)&(((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 long)&(((struct dsl_tx_fifo *)DT_BASE)->X)) +#define DR_OFFSET(X) ((unsigned long)&(((struct dsl_rx_fifo *)DR_BASE)->X)) + +#define FIFO_LENGTH offsetof(struct fst_fifo, fifo_length) +#define READ_IDX offsetof(struct fst_fifo, read_idx) +#define WRITE_IDX offsetof(struct fst_fifo, write_idx) +#define OVERFLOW_IDX offsetof(struct fst_fifo, overflow_idx) +#define DATA_0 offsetof(struct fst_fifo, data[0]) +#define DATA_1 offsetof(struct fst_fifo, data[1]) +#define DATA_2 offsetof(struct fst_fifo, data[2]) +#define DATA_3 offsetof(struct fst_fifo, data[3]) + +/* 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 * ================================= */ -/* Per port (line or channel) information - */ + +/* Something to count interrupts */ +struct fst_ints { + unsigned long int_186; + unsigned long int_txdma; + unsigned long int_rxdma; + unsigned long int_no_work; +}; + +struct fst_ints fst_int_counter[FST_MAX_CARDS]; + +struct fst_tx_buckets { + unsigned long txb0; + unsigned long txb1; + unsigned long txb2; + unsigned long txb4; + unsigned long txb8; +}; + +struct fst_rx_buckets { + unsigned long rxb0; + unsigned long rxb1; + unsigned long rxb2; + unsigned long rxb4; + unsigned long rxb8; +}; + +struct fst_tx_buckets tx_buckets[FST_MAX_CARDS]; +struct fst_rx_buckets rx_buckets[FST_MAX_CARDS]; + +/* 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 mode; /* HDLC, Transparent, Async */ + int hwif; /* Line hardware (line_interface copy) */ int run; /* Port is running */ - int mode; /* Normal or FarSync raw */ + int ignore_carrier; /* 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 */ - /* - * A sixteen entry transmit queue - */ + 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 */ + struct net_device_stats stats; /* Standard statistics */ + unsigned short vpi; /* ATM VPI for DSL-S1 */ + unsigned short vci; /* ATM VCI for DSL-S1 */ + unsigned char activation_status; /* Current trained status*/ + 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]; + unsigned int rx_latency; /* Not yet supported */ + unsigned char *rx_latency_buffer; /* The fifo */ + unsigned int tx_latency; /* How much to buffer before we start + * transmitting + */ + unsigned char *tx_latency_buffer; /* The fifo */ + unsigned int latency_rate; /* The expected line rate */ + unsigned int latency_reached; /* If we are in latency mode, whether + * we can tx yet + */ + /* 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; + int char_opens; /* The num of opens on the char dev */ + spinlock_t rxf_lock; /* Lock for queue head access */ + struct fst_char_rx_frame *char_inq; + struct fst_char_rx_frame *char_inq_end; + unsigned char char_inq_max_len; /* Only allow this many frames */ + unsigned char char_inq_threshold; /* Generate a notify if */ + wait_queue_head_t pollq; /* poll() support */ + wait_queue_head_t readq; /* Blocking read support (char) */ + wait_queue_head_t writeq; /* Blocking write support (char) */ + int mtu_for_rx_skb; + int minor_dev_no; + char low_latency; + char fstioc_info_ver; + char readv_mode; + unsigned char flow_controlled; + unsigned char exception; + struct fst_fifo *fifo_rxdata; + struct fst_fifo fifo_txdata; + char compat; }; -/* Per card information - */ +/* 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 */ @@ -455,9 +958,18 @@ 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 msg fifo + * response + */ + wait_queue_head_t cmdfifo_waitq;/* A queue to wait for a cmd fifo + * response + */ + int fifo_complete; + int cmdfifo_complete[FST_MAX_PORTS]; 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 */ @@ -476,35 +988,11 @@ struct fst_card_info { int dma_len_tx; int dma_txpos; int dma_rxpos; + int dma_tx_flags; + int last_tx_port; }; -/* 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 - */ +/* Debug support */ #if FST_DEBUG static int fst_debug_mask = { FST_DEBUG }; @@ -514,57 +1002,80 @@ static int fst_debug_mask = { FST_DEBUG * support variable numbers of macro parameters. The inverted if prevents us * eating someone else's else clause. */ -#define dbg(F, fmt, args...) \ +#define fst_dbg(F, fmt, args...) \ do { \ if (fst_debug_mask & (F)) \ printk(KERN_DEBUG pr_fmt(fmt), ##args); \ } while (0) #else -#define dbg(F, fmt, args...) \ +#define fst_dbg(F, fmt, args...) \ do { \ if (0) \ printk(KERN_DEBUG pr_fmt(fmt), ##args); \ } while (0) #endif -/* - * PCI ID lookup table - */ +/* PCI ID lookup table */ static DEFINE_PCI_DEVICE_TABLE(fst_pci_dev_id) = { - {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}, + { +#ifdef FSC_TXP_SUPPORT + 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}, { +#endif - {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1C, PCI_ANY_ID, - PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, - {0,} /* End */ + 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_TE1e}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_DSL_S1, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_DSL_S1}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2U_PMC, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T2U_PMC}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4E, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T4E}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2UE, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T2UE}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4UE, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T4UE}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2Ee, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T2Ee}, { + PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4Ee, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T4Ee}, { + 0,} /* End */ }; MODULE_DEVICE_TABLE(pci, fst_pci_dev_id); -/* - * Device Driver Work Queues +static const char map_interface[14] = { + FSCMN_INTERFACE_AUTO, /* 0 */ + FSCMN_INTERFACE_V24, /* 1 V24 */ + FSCMN_INTERFACE_X21, /* 2 X21 */ + FSCMN_INTERFACE_V35, /* 3 V35 */ + FSCMN_INTERFACE_X21D, /* 4 X21D */ + FSCMN_INTERFACE_NO_CABLE, /* 5 NOCABLE */ + FSCMN_INTERFACE_RS530, /* 6 RS530_449 */ + 0, /* 7 T1 */ + 0, /* 8 E1 */ + 0, /* 9 J1 */ + 0, /* 10 SHDSL */ + FSCMN_INTERFACE_RS485, /* 11 RS485 */ + 0, /* 12 UX35C */ + FSCMN_INTERFACE_RS485_FDX /* 13 RS485_FDX */ +}; + +/* Device Driver Work Queues * - * So that we don't spend too much time processing events in the - * Interrupt Service routine, we will declare a work queue per Card + * 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); @@ -572,250 +1083,3069 @@ static void do_bottom_half_rx(struct fst 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); +DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0); +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; +static void fst_process_tty_work(struct work_struct *work); +static void fst_process_async_tty_work(struct work_struct *work); -static void -fst_q_work_item(u64 * queue, int card_index) -{ - unsigned long flags; - u64 mask; +static struct work_struct fst_rx_work; - /* - * Grab the queue exclusively - */ - spin_lock_irqsave(&fst_work_q_lock, flags); +unsigned int fst_ncards; +struct fst_card_info *fst_cards_list[FST_MAX_CARDS]; +struct fst_port_info *fst_ports_list[FST_MAX_CARDS * FST_MAX_PORTS]; +spinlock_t fst_work_q_lock; +u64 fst_work_rxq; +u64 fst_work_txq; +u64 fst_work_intq; +int last_segment_cnt; + +/* We need to send an idle cell when using mpoa encapsulation */ +char atm_idle_cell[53]; + +static unsigned int fst_minor; +static struct class *farsync_class; + +static unsigned int fst_major; /* The char driver major device number. + * Setting it to 0 will cause us to use + * dynamic allocation during module load + */ - /* - * 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 = (u64)1 << card_index; - *queue |= mask; - spin_unlock_irqrestore(&fst_work_q_lock, flags); +static char *type_strings[] = { + "no hardware", /* Should never be seen */ + "FarSync T2P ", + "FarSync T4P ", + "FarSync T1U ", + "FarSync T2U ", + "FarSync T4U ", + "FarSync TE1 ", + "FarSync DSL-S1 ", + "FarSync T4E ", + "Invalid (flex) ", + "FarSync T4Ue ", + "FarSync T2Ue ", + "FarSync T4E+ ", + "FarSync T2U-PMC", + "FarSync TE1e ", + "FarSync T2Ee ", + "FarSync T4Ee ", + "Invalid (flex2) " +}; + +static char *state_strings[] = { + "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); + +#define XTOA(x) (((x) > (9)) ? (x + 0x37) : (x + 0x30)) +static unsigned char serial[16]; + +void extract_serial(struct fst_card_info *card) +{ + serial[0] = (u8) (FST_RDW(card, _reserved[6]) >> 8); + serial[1] = XTOA((u8) ((FST_RDW(card, _reserved[13]) & 0xf))); + serial[2] = XTOA((u8) (((FST_RDB(card, _reserved[6]) >> 4) & 0xf))); + serial[3] = XTOA((u8) ((FST_RDW(card, _reserved[6]) & 0xf))); + serial[4] = XTOA((u8) (((FST_RDW(card, _reserved[15]) >> 12) & 0xf))); + serial[5] = XTOA((u8) (((FST_RDW(card, _reserved[15]) >> 8) & 0xf))); + serial[6] = XTOA((u8) (((FST_RDW(card, _reserved[15]) >> 4) & 0xf))); + serial[7] = XTOA((u8) ((FST_RDW(card, _reserved[15]) & 0xf))); + serial[8] = '\0'; + serial[9] = '\0'; } -static void -fst_process_tx_work_q(unsigned long /*void **/work_q) +/* 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) (P->dev->stats.S) +#define hdlc_stats(D) (&(D->stats)) + +#define FIFO_ASY_CMD 0 +#define FIFO_ASY_TX 1 +#define FIFO_ASY_MSG 2 +#define FIFO_ASY_RX 3 +#define FIFO_ASY_RSP 4 +#define FIFO_ASY_RXEVNT 5 +#define FIFO_PORT_RX 6 +#define FIFO_PORT_TX 7 + +static char *fifo_strings[] = { + "Command Fifo", + "Transmit Fifo", + "Message Fifo", + "Receive Fifo", + "Response Fifo", + "Rx Event Fifo", + "Port Rx Fifo", + "Port Tx Fifo" +}; + +static void fst_issue_cmd(struct fst_port_info *port, unsigned short cmd); + +/* Converts the struct fstioc_req from native byte order to Little Endian */ +static void fstioc_req_to_le(struct fstioc_req *sys_request) { - unsigned long flags; - u64 work_txq; + cpu_to_le16s(&sys_request->msg_type); + cpu_to_le16s(&sys_request->msg_len); + cpu_to_le16s(&sys_request->ret_code); + cpu_to_le16s(&sys_request->i_reg_idx); + cpu_to_le16s(&sys_request->value); +} + +/* Converts the struct fstioc_req from Little Endian to native byte order */ +static void fstioc_req_to_cpu(struct fstioc_req *sys_request) +{ + le16_to_cpus(&sys_request->msg_type); + le16_to_cpus(&sys_request->msg_len); + le16_to_cpus(&sys_request->ret_code); + le16_to_cpus(&sys_request->i_reg_idx); + le16_to_cpus(&sys_request->value); +} + +static void fifo_init(struct fst_card_info *card) +{ + struct fst_fifo fifo; int i; - /* - * Grab the queue exclusively + /* Initialise async window to zeros */ + fst_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 fifo's to the card first followed by the fifo's from the card */ - 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 + /* must setup the fifo's length + * before writing the header back */ - 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]); + fifo.fifo_length = (u16) (FST_CMD_FIFO_LEN + 1); + 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'; + + for (i = 0; i < FST_MAX_PORTS; i++) { + fst_dbg(DBG_FIFO, "Offset of cmd fifo[%d] is %lx\n", i, + ASY_OFFSET(cmd_fifo[i])); + + FST_A_WRW(card, cmd_fifo[i][FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, cmd_fifo[i][READ_IDX], fifo.read_idx); + FST_A_WRW(card, cmd_fifo[i][WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, cmd_fifo[i][OVERFLOW_IDX], fifo.overflow_idx); + FST_A_WRB(card, cmd_fifo[i][DATA_0], fifo.data[0]); + FST_A_WRB(card, cmd_fifo[i][DATA_1], fifo.data[1]); + FST_A_WRB(card, cmd_fifo[i][DATA_2], fifo.data[2]); + FST_A_WRB(card, cmd_fifo[i][DATA_3], fifo.data[3]); + } + + /* must setup the fifo's length + * before writing the header back + */ + fifo.fifo_length = (u16) (FST_TX_DATA_FIFO_LEN + 1); + fifo.read_idx = (u16) (fifo.fifo_length - 1); + fifo.write_idx = (u16) (fifo.fifo_length - 1); + + for (i = 0; i < FST_MAX_PORTS; i++) { + fst_dbg(DBG_FIFO, "Offset of tx fifo[%d] is %lx\n", i, + ASY_OFFSET(tx_fifo[i])); + FST_A_WRW(card, tx_fifo[i][FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, tx_fifo[i][READ_IDX], fifo.read_idx); + FST_A_WRW(card, tx_fifo[i][WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, tx_fifo[i][OVERFLOW_IDX], fifo.overflow_idx); + FST_A_WRB(card, tx_fifo[i][DATA_0], fifo.data[0]); + FST_A_WRB(card, tx_fifo[i][DATA_1], fifo.data[1]); + FST_A_WRB(card, tx_fifo[i][DATA_2], fifo.data[2]); + FST_A_WRB(card, tx_fifo[i][DATA_3], fifo.data[3]); + } + + /* must setup the fifo's length + * before writing the header back + */ + fifo.fifo_length = (u16) (FST_MSG_FIFO_LEN + 1); + fifo.read_idx = (u16) (fifo.fifo_length - 1); + fifo.write_idx = (u16) (fifo.fifo_length - 1); + fst_dbg(DBG_FIFO, "Offset of msg_fifo is %lx\n", ASY_OFFSET(msg_fifo)); + + FST_A_WRW(card, msg_fifo[FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, msg_fifo[READ_IDX], fifo.read_idx); + FST_A_WRW(card, msg_fifo[WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, msg_fifo[OVERFLOW_IDX], fifo.overflow_idx); + FST_A_WRB(card, msg_fifo[DATA_0], fifo.data[0]); + FST_A_WRB(card, msg_fifo[DATA_1], fifo.data[1]); + FST_A_WRB(card, msg_fifo[DATA_2], fifo.data[2]); + FST_A_WRB(card, msg_fifo[DATA_3], fifo.data[3]); + + fifo.data[0] = 'B'; + fifo.data[1] = 'E'; + fifo.data[2] = 'E'; + fifo.data[3] = 'F'; + + /* must setup the fifo's length + * before writing the header back + */ + fifo.fifo_length = (u16) (FST_RX_DATA_FIFO_LEN + 1); + fifo.read_idx = (u16) (fifo.fifo_length - 1); + fifo.write_idx = (u16) (fifo.fifo_length - 1); + for (i = 0; i < FST_MAX_PORTS; i++) { + fst_dbg(DBG_FIFO, "Offset of rx fifo[%d] is %lx\n", i, + ASY_OFFSET(rx_fifo[i])); + FST_A_WRW(card, rx_fifo[i][FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, rx_fifo[i][READ_IDX], fifo.read_idx); + FST_A_WRW(card, rx_fifo[i][WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, rx_fifo[i][OVERFLOW_IDX], fifo.overflow_idx); + FST_A_WRB(card, rx_fifo[i][DATA_0], fifo.data[0]); + FST_A_WRB(card, rx_fifo[i][DATA_1], fifo.data[1]); + FST_A_WRB(card, rx_fifo[i][DATA_2], fifo.data[2]); + FST_A_WRB(card, rx_fifo[i][DATA_3], fifo.data[3]); + } + + /* must setup the fifo's length + * before writing the header back + */ + fifo.fifo_length = (u16) (MAX_RX_EVENT_FIFO_LEN + 1); + fifo.read_idx = (u16) (fifo.fifo_length - 1); + fifo.write_idx = (u16) (fifo.fifo_length - 1); + + for (i = 0; i < FST_MAX_PORTS; i++) { + fst_dbg(DBG_FIFO, "Offset of rx event fifo[%d] is %lx\n", i, + ASY_OFFSET(rx_event_fifos[i])); + FST_A_WRW(card, rx_event_fifos[i][FIFO_LENGTH], + fifo.fifo_length); + FST_A_WRW(card, rx_event_fifos[i][READ_IDX], fifo.read_idx); + FST_A_WRW(card, rx_event_fifos[i][WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, rx_event_fifos[i][OVERFLOW_IDX], + fifo.overflow_idx); + FST_A_WRB(card, rx_event_fifos[i][DATA_0], fifo.data[0]); + FST_A_WRB(card, rx_event_fifos[i][DATA_1], fifo.data[1]); + FST_A_WRB(card, rx_event_fifos[i][DATA_2], fifo.data[2]); + FST_A_WRB(card, rx_event_fifos[i][DATA_3], fifo.data[3]); + } + + /* must setup the fifo's length + * before writing the header back + */ + fifo.fifo_length = (u16) (FST_RSP_FIFO_LEN + 1); + fifo.read_idx = (u16) (fifo.fifo_length - 1); + fifo.write_idx = (u16) (fifo.fifo_length - 1); + fst_dbg(DBG_FIFO, "Offset of rsp fifo is %lx\n", ASY_OFFSET(rsp_fifo)); + FST_A_WRW(card, rsp_fifo[FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, rsp_fifo[READ_IDX], fifo.read_idx); + FST_A_WRW(card, rsp_fifo[WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, rsp_fifo[OVERFLOW_IDX], fifo.overflow_idx); + FST_A_WRB(card, rsp_fifo[DATA_0], fifo.data[0]); + FST_A_WRB(card, rsp_fifo[DATA_1], fifo.data[1]); + FST_A_WRB(card, rsp_fifo[DATA_2], fifo.data[2]); + FST_A_WRB(card, rsp_fifo[DATA_3], fifo.data[3]); +} + +static void fifo_reset(struct fst_card_info *card, int port_index) +{ + struct fst_fifo fifo; + + /* Flush the tx fifo + * and reset the read_idx to be the same as the write_idx + * on the rx fifo + */ + fst_issue_cmd(card->ports[port_index], FLUSHTX); + fifo.fifo_length = FST_A_RDW(card, rx_fifo[port_index][FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, rx_fifo[port_index][READ_IDX]); + fifo.write_idx = FST_A_RDW(card, rx_fifo[port_index][WRITE_IDX]); + fifo.overflow_idx = FST_A_RDW(card, rx_fifo[port_index][OVERFLOW_IDX]); + + fst_dbg(DBG_FIFO, + "Re-aligning fifo indexes: read index = %d write index = %d\n", + fifo.read_idx, fifo.write_idx); + + fifo.read_idx = fifo.write_idx; + FST_A_WRW(card, rx_fifo[port_index][FIFO_LENGTH], fifo.fifo_length); + FST_A_WRW(card, rx_fifo[port_index][READ_IDX], fifo.read_idx); + FST_A_WRW(card, rx_fifo[port_index][WRITE_IDX], fifo.write_idx); + FST_A_WRW(card, rx_fifo[port_index][OVERFLOW_IDX], fifo.overflow_idx); +} + +static int check_fifo_space(char *actual_fifo, int fifo_select) +{ + int h_size = 0; + struct fst_fifo fifo; + u16 avail_fifo_space = 0; + u16 used_space = 0; + u16 free_space = 0; + u16 next_write_idx = 0; + + h_size = sizeof(struct fst_fifo) - 4; + fst_dbg(DBG_FIFO, + "check_fifo_space [%s]\n", fifo_strings[fifo_select]); + /* Make a local copy of the fifo while we update it */ + fifo.fifo_length = FIFO_RDW(actual_fifo + FIFO_LENGTH); + fifo.read_idx = FIFO_RDW(actual_fifo + READ_IDX); + fifo.write_idx = FIFO_RDW(actual_fifo + WRITE_IDX); + fifo.overflow_idx = FIFO_RDW(actual_fifo + OVERFLOW_IDX); + + next_write_idx = (u16) ((fifo.write_idx + 1) % fifo.fifo_length); + fst_dbg(DBG_FIFO, "next_write_idx = %d\n", next_write_idx); + free_space = (u16) ((fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length); + used_space = (u16) (fifo.fifo_length - free_space - 1); + avail_fifo_space = fifo.fifo_length - used_space; + fst_dbg(DBG_FIFO, + "size = %d, free_space = %d, used_space = %d, avail_fifo_space = %d\n", + fifo.fifo_length, free_space, used_space, avail_fifo_space); + avail_fifo_space = (u16) min_t(u16, avail_fifo_space, + (u16) fifo.fifo_length); + fst_dbg(DBG_FIFO, "Check fifo space [%s] returning %d\n", + fifo_strings[fifo_select], used_space); + return used_space; +} + +static int write_into_fifo(char *actual_fifo, int fifo_select, + char *request, int msg_len) +{ + int retval = -1; + int h_size = 0; + struct fst_fifo fifo; + u16 avail_fifo_space = 0; + u16 used_space = 0; + u16 free_space = 0; + u16 next_write_idx = 0; + u16 first_write_amount = 0; + u16 second_write_amount = 0; + + h_size = sizeof(struct fst_fifo) - 4; + fst_dbg(DBG_FIFO, "write_into_fifo [%s] a message of %d bytes\n", + fifo_strings[fifo_select], msg_len); + /* Make a local copy of the fifo while we update it */ + + fifo.fifo_length = FIFO_RDW(actual_fifo + FIFO_LENGTH); + fifo.read_idx = FIFO_RDW(actual_fifo + READ_IDX); + fifo.write_idx = FIFO_RDW(actual_fifo + WRITE_IDX); + fifo.overflow_idx = FIFO_RDW(actual_fifo + OVERFLOW_IDX); + + next_write_idx = (u16) ((fifo.write_idx + 1) % fifo.fifo_length); + fst_dbg(DBG_FIFO, "next_write_idx = %d\n", next_write_idx); + free_space = (u16) ((fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length); + used_space = (u16) (fifo.fifo_length - free_space - 1); + avail_fifo_space = fifo.fifo_length - used_space; + fst_dbg(DBG_FIFO, + "size = %d, free_space = %d, used_space = %d, avail_fifo_space = %d\n", + fifo.fifo_length, free_space, used_space, avail_fifo_space); + avail_fifo_space = (u16) min_t(u16, avail_fifo_space, + (u16) fifo.fifo_length); + + if (fifo.overflow_idx == -1) { + fst_dbg(DBG_FIFO, "msg_fifo.overflow_idx = -1\n"); + if (avail_fifo_space >= msg_len) { + fst_dbg(DBG_FIFO, + "And there is space to write data\n"); + /* How much of it can we write into the end fragment */ + first_write_amount = min_t(u16, msg_len, + (fifo.fifo_length - + next_write_idx)); + fst_dbg(DBG_FIFO, "First write amount is %d\n", + first_write_amount); + fst_dbg(DBG_FIFO, "Next write index is %d\n", + next_write_idx); + fst_dbg(DBG_FIFO, "Copying to offset %p\n", + actual_fifo + h_size + next_write_idx); + if (first_write_amount > fifo.fifo_length) { + fst_dbg(DBG_FIFO, "Fifo length error %d %d\n", + first_write_amount, fifo.fifo_length); + return retval; + } + memcpy_toio(actual_fifo + h_size + next_write_idx, + request, first_write_amount); + fifo.write_idx = + (u16) ((fifo.write_idx + first_write_amount) + % fifo.fifo_length); + fst_dbg(DBG_FIFO, "Write Index updated to %d\n", + fifo.write_idx); + if (first_write_amount != msg_len) { + fst_dbg(DBG_FIFO, + "We need a second chunk because of wrap\n"); + /* were do we start writing the next chunk + * to + */ + next_write_idx = (u16) ((fifo.write_idx + 1) % + fifo.fifo_length); + + /* write the rest into the starting fragment */ + second_write_amount = (u16) (msg_len - + first_write_amount); + if (second_write_amount > fifo.fifo_length) { + fst_dbg(DBG_FIFO, + "Fifo length error on 2nd write %d %d\n", + second_write_amount, + fifo.fifo_length); + return retval; + } + fst_dbg(DBG_FIFO, + "Second write amount is %d\n", + second_write_amount); + fst_dbg(DBG_FIFO, "Next write index is %d\n", + next_write_idx); + fst_dbg(DBG_FIFO, "Copying to offset %p\n", + actual_fifo + h_size + next_write_idx); + memcpy_toio(actual_fifo + h_size + + next_write_idx, + request + first_write_amount, + second_write_amount); + fifo.write_idx = + (u16) ((fifo.write_idx + + second_write_amount) + % fifo.fifo_length); } + retval = 0; + } else { + fst_dbg(DBG_FIFO, + "%s: No room in fifo (%d) for %d bytes of data\n", + fifo_strings[fifo_select], avail_fifo_space, + msg_len); } - work_txq = work_txq >> 1; + } else { + fst_dbg(DBG_FIFO, "write_into_fifo: [%s] fifo overflow\n", + fifo_strings[fifo_select]); + fifo.overflow_idx = fifo.write_idx; } + + fst_dbg(DBG_FIFO, "Updating the fifo header\n"); + /* Write the local copy back (of write_idx and overflow idx) + * to the fifo + */ + FIFO_WRW(actual_fifo + WRITE_IDX, fifo.write_idx); + FIFO_WRW(actual_fifo + OVERFLOW_IDX, fifo.overflow_idx); + + return retval; } -static void -fst_process_int_work_q(unsigned long /*void **/work_q) +static int read_from_fifo(char *actual_fifo, int fifo_select, + char *data, int msg_len) { - unsigned long flags; - u64 work_intq; - int i; - + int h_size = 0; + struct fst_fifo fifo; + u16 next_read_idx = 0; + u16 amount_to_read = 0; + char *data1 = NULL; + u16 length1; + char *data2 = NULL; + u16 length2; + u16 data_len; + + fst_dbg(DBG_FIFO, "read_from_fifo [%s] into buffer of size %d\n", + fifo_strings[fifo_select], msg_len); + h_size = sizeof(struct fst_fifo) - 4; /* - * Grab the queue exclusively + * Get the fifo header */ - 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 + fifo.fifo_length = FIFO_RDW(actual_fifo + FIFO_LENGTH); + fifo.read_idx = FIFO_RDW(actual_fifo + READ_IDX); + fifo.write_idx = FIFO_RDW(actual_fifo + WRITE_IDX); + fifo.overflow_idx = FIFO_RDW(actual_fifo + OVERFLOW_IDX); + if (fifo.fifo_length > (FST_RX_DATA_FIFO_LEN + 1)) { + fst_dbg(DBG_FIFO, "fifo size error! %d\n", fifo.fifo_length); + return 0; + } + /* Work out the length of data in the fifo */ + data_len = (fifo.write_idx - fifo.read_idx + fifo.fifo_length) + % fifo.fifo_length; + fst_dbg(DBG_FIFO, "Actual amount of data in the fifo is %d\n", + data_len); + /* But we will only take the smaller of the length of data + * in the fifo or the length of the buffer we have been given + * to copy the data into */ - 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]); - } + amount_to_read = (u16) min_t(u16, data_len, (u16) msg_len); + fst_dbg(DBG_FIFO, "amount to read is %d\n", amount_to_read); + if (amount_to_read > 0) { + /* where should we start reading from ? */ + next_read_idx = (fifo.read_idx + 1) % fifo.fifo_length; + fst_dbg(DBG_FIFO, "Next read index = %d\n", next_read_idx); + if (next_read_idx > (FST_RX_DATA_FIFO_LEN + 1)) { + fst_dbg(DBG_FIFO, "read index size error!\n"); + return 0; } - work_intq = work_intq >> 1; + data1 = &actual_fifo[next_read_idx + h_size]; + length1 = (u16) min_t(u16, amount_to_read, + (u16) (fifo.fifo_length - next_read_idx)); + fst_dbg(DBG_FIFO, "length1 = %d\n", length1); + if (amount_to_read > (FST_RX_DATA_FIFO_LEN + 1)) { + fst_dbg(DBG_FIFI, "amount to read size error!\n"); + return 0; + } + data2 = actual_fifo + h_size; + length2 = amount_to_read - length1; + fst_dbg(DBG_FIFO, "length2 = %d\n", length2); + fst_dbg(DBG_FIFO, + "Copying 1st blob from offset %p of length %d\n", + data1, length1); + memcpy_fromio(data, data1, length1); + + if (length2) { + fst_dbg(DBG_ASS, "Copying from offset %p\n", data2); + memcpy_fromio(data + length1, data2, length2); + } + fifo.read_idx = (fifo.read_idx + amount_to_read) % + fifo.fifo_length; + /* Write the fifo header back as we have taken some data */ + FIFO_WRW(actual_fifo + READ_IDX, fifo.read_idx); + fst_dbg(DBG_FIFO, "read_from_fifo: Amount read was %d\n", + length1 + length2); + return amount_to_read; } + return 0; } -/* 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 void fst_reset_rx_fifo(struct fst_port_info *port) +{ + int used_space; + + used_space = ((port->fifo_rxdata->write_idx - + port->fifo_rxdata->read_idx)); + if (used_space < 0) { + fst_dbg(DBG_FIFO, "adjusting used_space %d\n", used_space); + used_space = used_space + FST_RX_DATA_FIFO_LEN; + } + fst_dbg(DBG_FIFO, "%s: Resetting Rx Fifo %d %d\n", port->dev->name, + port->fifo_rxdata->write_idx, port->fifo_rxdata->read_idx); + fst_dbg(DBG_FIFO, "%s: %d bytes left in fifo\n", port->dev->name, + used_space); + port->fifo_rxdata->fifo_length = (u16) (FST_RX_DATA_FIFO_LEN + 1); + port->fifo_rxdata->read_idx = + (u16) (port->fifo_rxdata->fifo_length - 1); + port->fifo_rxdata->write_idx = + (u16) (port->fifo_rxdata->fifo_length - 1); + port->fifo_rxdata->overflow_idx = -1; +} + +static void +fst_intr_async_rx(struct fst_card_info *card, struct fst_port_info *port); + +static void +fst_intr_async_rx_event(struct fst_card_info *card, + struct fst_port_info *port); + +/* Check to see if the TX Fifo is at least half full + * If so then signal to select that another write can be made */ -static inline void -fst_cpureset(struct fst_card_info *card) +static void check_tx_fifos(struct fst_card_info *card) { - unsigned char interrupt_line_register; - unsigned long j = jiffies + 1; - unsigned int regval; + struct fst_fifo fifo; + int length; + int i; - 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"); + /* This is for the port shared memory tx fifo only */ + fst_dbg(DBG_FIFO, "Check tx fifo's\n"); + for (i = 0; i < card->nports; i++) { + if ((card->ports[i]->run) + && (card->ports[i]->mode == FST_MODE_ASYNC)) { + fifo.fifo_length = + FST_A_RDW(card, tx_fifo[i][FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, tx_fifo[i][READ_IDX]); + fifo.write_idx = + FST_A_RDW(card, tx_fifo[i][WRITE_IDX]); + fifo.overflow_idx = + FST_A_RDW(card, tx_fifo[i][OVERFLOW_IDX]); + + memcpy(&card->ports[i]->fifo_txdata, &fifo, + sizeof(struct fst_fifo)); + length = (fifo.write_idx - fifo.read_idx); + if (length < 0) + length += fifo.fifo_length; + if (length < fifo.fifo_length / 2) { + fst_dbg(DBG_ASS, + "%s: Room in the Tx Fifo (%d)\n", + card->ports[i]->dev->name, length); + wake_up_interruptible(&card->ports[i]->pollq); + wake_up_interruptible(&card->ports[i]->writeq); + } + if (length == 0) { + fst_dbg(DBG_FIFO, + "%s: We let the tx fifo stutter!\n", + card->ports[i]->dev->name); + } } - /* - * 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; + } +} + +static void check_rx_fifos(struct fst_card_info *card) +{ + struct fst_fifo fifo; + int i; + + /* This is for the port shared memory rx fifos only */ + fst_dbg(DBG_FIFO, "Check rx fifo's\n"); + for (i = 0; i < card->nports; i++) { + fifo.fifo_length = FST_A_RDW(card, rx_fifo[i][FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, rx_fifo[i][READ_IDX]); + fifo.write_idx = FST_A_RDW(card, rx_fifo[i][WRITE_IDX]); + fifo.overflow_idx = FST_A_RDW(card, rx_fifo[i][OVERFLOW_IDX]); + if (fifo.read_idx != fifo.write_idx) { + fst_dbg(DBG_FIFO, "%s: Data found in Rx Fifo\n", + card->ports[i]->dev->name); + fst_intr_async_rx(card, card->ports[i]); + } + } +} + +static void check_rx_event_fifos(struct fst_card_info *card) +{ + struct fst_fifo fifo; + int i; + + /* This is for the port shared memory rx events fifos only + * This is where errors are reported + */ + fst_dbg(DBG_FIFO, "Check rx event fifo's\n"); + for (i = 0; i < card->nports; i++) { + fifo.fifo_length = + FST_A_RDW(card, rx_event_fifos[i][FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, rx_event_fifos[i][READ_IDX]); + fifo.write_idx = FST_A_RDW(card, rx_event_fifos[i][WRITE_IDX]); + fifo.overflow_idx = + FST_A_RDW(card, rx_event_fifos[i][OVERFLOW_IDX]); + if (fifo.read_idx != fifo.write_idx) { + fst_dbg(DBG_ASS, "%s: Rx Event found\n", + card->ports[i]->dev->name); + fst_intr_async_rx_event(card, card->ports[i]); + port_to_stats(card->ports[i], rx_errors)++; + } + } +} + +static void check_fifo_resp(struct fst_card_info *card) +{ + u16 used_space = 0; + u16 free_space = 0; + u16 next_write_idx = 0; + struct fst_fifo fifo; + + /* This is for the command/response fifo only */ + fst_dbg(DBG_FIFO, "Check fifo response\n"); + fifo.fifo_length = FST_A_RDW(card, rsp_fifo[FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, rsp_fifo[READ_IDX]); + fifo.write_idx = FST_A_RDW(card, rsp_fifo[WRITE_IDX]); + fifo.overflow_idx = FST_A_RDW(card, rsp_fifo[OVERFLOW_IDX]); + next_write_idx = (fifo.write_idx + 1) % fifo.fifo_length; + free_space = (fifo.read_idx - next_write_idx + fifo.fifo_length) % + fifo.fifo_length; + used_space = fifo.fifo_length - free_space - 1; + + if (used_space >= sizeof(struct fstioc_req)) { + /* + * we have a response to process so wake the queue that + * the process should be waiting on + */ + fst_dbg(DBG_FIFO, "Rsp Fifo has data waiting\n"); + card->fifo_complete = 1; + wake_up_interruptible(&card->fifo_waitq); + } +} + +static void check_cmdfifo_done(struct fst_card_info *card) +{ + u16 used_space = 0; + u16 free_space = 0; + u16 next_write_idx = 0; + struct fst_fifo fifo; + int i; + + /* This is for the command/response fifo only */ + fst_dbg(DBG_FIFO, "Check command fifo processed\n"); + + for (i = 0; i < card->nports; i++) { + fifo.fifo_length = FST_A_RDW(card, cmd_fifo[i][FIFO_LENGTH]); + fifo.read_idx = FST_A_RDW(card, cmd_fifo[i][READ_IDX]); + fifo.write_idx = FST_A_RDW(card, cmd_fifo[i][WRITE_IDX]); + fifo.overflow_idx = FST_A_RDW(card, cmd_fifo[i][OVERFLOW_IDX]); + + next_write_idx = (fifo.write_idx + 1) % fifo.fifo_length; + free_space = + (fifo.read_idx - next_write_idx + + fifo.fifo_length) % fifo.fifo_length; + used_space = fifo.fifo_length - free_space - 1; + + if (used_space == 0) { + /* + * The last command we sent has been processed + */ + fst_dbg(DBG_FIFO, "Cmd Fifo now empty\n"); + card->cmdfifo_complete[i] = 1; + wake_up_interruptible(&card->cmdfifo_waitq); + } else { + fst_dbg(DBG_FIFO, "Used space is still %d\n", + used_space); + } + } +} + +static int fst_alloc_rx_fifo(struct fst_port_info *port) +{ + + /* Allocate the data for the fifo */ + port->fifo_rxdata = kmalloc(FST_RX_DATA_FIFO_LEN + + sizeof(struct fst_fifo), GFP_ATOMIC); + if (port->fifo_rxdata == NULL) { + fst_dbg(DBG_FIFO, + "fifo_init: can't allocate %u bytes for Fifo\n", + (unsigned int)(FST_RX_DATA_FIFO_LEN + + sizeof(struct fst_fifo))); + return 1; + } + fst_dbg(DBG_FIFO, "%s: Fifo memory allocated at %p\n", + port->dev->name, port->fifo_rxdata); + /* Now initialise it + * must setup the fifo's length + */ + port->fifo_rxdata->fifo_length = (u16) (FST_RX_DATA_FIFO_LEN + 1); + port->fifo_rxdata->read_idx = + (u16) (port->fifo_rxdata->fifo_length - 1); + port->fifo_rxdata->write_idx = + (u16) (port->fifo_rxdata->fifo_length - 1); + port->fifo_rxdata->overflow_idx = -1; + port->fifo_rxdata->data[0] = 'F'; + port->fifo_rxdata->data[1] = '1'; + port->fifo_rxdata->data[2] = 'D'; + port->fifo_rxdata->data[3] = '0'; + return 0; +} + +struct net_device *get_net_device(int if_index) +{ + struct net_device *dev; + + dev = port_to_dev(fst_ports_list[if_index]); + fst_dbg(DBG_ASS, "get_net_device: returning %s\n", dev->name); + return dev; +} + +/* Sooner or later you can't avoid a forward declaration */ +static int fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +static const speed_t baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, + 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, + 2500000, 3000000, 3500000, 4000000 +}; + +static int n_baud_table = ARRAY_SIZE(baud_table); + +static unsigned int get_baud(tcflag_t c_cflag) +{ + unsigned int baud; + + baud = c_cflag & CBAUD; + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + + if (baud < 1 || baud + 15 > n_baud_table) + c_cflag &= ~CBAUDEX; + else + baud += 15; + } + baud = baud_table[baud]; + fst_dbg(DBG_IOCTL, "Baud rate was %d\n", baud); + return baud; +} + +static unsigned int get_rtscts(tcflag_t c_cflag) +{ + unsigned int rtscts; + + rtscts = c_cflag & CRTSCTS; + fst_dbg(DBG_IOCTL, "rtscts was %x\n", rtscts); + if (rtscts) + return 1; + else + return 0; +} + +static unsigned int get_xonxoff(tcflag_t c_iflag) +{ + unsigned int xon, xoff; + + xon = c_iflag & IXON; + xoff = c_iflag & IXOFF; + fst_dbg(DBG_IOCTL, "xon was %x xoff was %x\n", xon, xoff); + if (xon && xoff) + return 2; + else + return 0; +} + +static unsigned int get_char_size(tcflag_t c_cflag) +{ + unsigned int char_size; + + char_size = c_cflag & CSIZE; + fst_dbg(DBG_IOCTL, "Character size = %d\n", char_size); + switch (char_size) { + case CS5: + fst_dbg(DBG_IOCTL, "Character size set as 5 bits\n"); + return 5; + + case CS6: + fst_dbg(DBG_IOCTL, "Character size set as 6 bits\n"); + return 6; + + case CS7: + fst_dbg(DBG_IOCTL, "Character size set as 7 bits\n"); + return 7; + + case CS8: + default: + fst_dbg(DBG_IOCTL, "Character size set as 8 bits\n"); + return 8; + } +} + +static unsigned int get_stop_bits(tcflag_t c_cflag) +{ + unsigned int stop_bits; + + stop_bits = c_cflag & CSTOPB; + fst_dbg(DBG_IOCTL, "Stop bits = %d\n", stop_bits); + if (stop_bits) { + fst_dbg(DBG_IOCTL, "2 stop bits selected\n"); + return 2; + } + fst_dbg(DBG_IOCTL, "1 stop bit selected\n"); + return 1; +} + +static unsigned int get_parity(tcflag_t c_cflag) +{ + unsigned int parity_enable, parity; + + parity_enable = c_cflag & PARENB; + parity = c_cflag & PARODD; + fst_dbg(DBG_IOCTL, "Parity enable is %d and parity odd is %d\n", + parity_enable, parity); + if (parity_enable) { + if (parity) { + fst_dbg(DBG_IOCTL, "Returning Odd Parity\n"); + return 1; + } else { + fst_dbg(DBG_IOCTL, "Returning Even Parity\n"); + return 2; + } + } else { + fst_dbg(DBG_IOCTL, "Parity not enabled\n"); + return 0; + } +} + +static void +init_async_parameters(struct fst_port_info *port, + unsigned int char_size, unsigned int stop_bits, + unsigned int parity, unsigned int flow_control) +{ + fst_dbg(DBG_IOCTL, "%s: Initialising async parameters\n", + port->dev->name); + FST_A_WRB(port->card, async_config[port->index].flow_control, + flow_control); + FST_A_WRB(port->card, async_config[port->index].xon_char, 0x11); + FST_A_WRB(port->card, async_config[port->index].xoff_char, 0x13); + FST_A_WRB(port->card, async_config[port->index].word_length, + char_size); + FST_A_WRB(port->card, async_config[port->index].stop_bits, stop_bits); + FST_A_WRB(port->card, async_config[port->index].parity, parity); + fst_dbg(DBG_IOCTL, "%s: Reconfiguring Port\n", port->dev->name); + fst_issue_cmd(port, RECONFIG); +} + +static void +configure_async_parameters(struct fst_port_info *port, unsigned int baud, + unsigned int char_size, unsigned int stop_bits, + unsigned int parity, unsigned int flow_control) +{ + unsigned char fc; + + fst_dbg(DBG_IOCTL, "%s: Setting async parameters\n", port->dev->name); + fc = COM_FLOW_CONTROL_NONE; + if ((flow_control == 1) || (flow_control == 3)) + fc = COM_FLOW_CONTROL_RTSCTS; + if (flow_control == 2) + fc = COM_FLOW_CONTROL_XONXOFF; + fst_dbg(DBG_IOCTL, "Flow control chosen as %d\n", fc); + FST_A_WRB(port->card, async_config[port->index].flow_control, fc); + FST_A_WRB(port->card, async_config[port->index].xon_char, 0x11); + FST_A_WRB(port->card, async_config[port->index].xoff_char, 0x13); + FST_A_WRW(port->card, async_config[port->index].rx_fifo_size, + FST_RX_DATA_FIFO_LEN); + FST_WRL(port->card, port_config[port->index].line_speed, baud); + FST_WRB(port->card, port_config[port->index].internal_clock, 1); + FST_A_WRB(port->card, async_config[port->index].word_length, + char_size); + if (stop_bits == 2) { + FST_A_WRB(port->card, async_config[port->index].stop_bits, + COM_STOP_BITS_2); + } else { + FST_A_WRB(port->card, async_config[port->index].stop_bits, + COM_STOP_BITS_1); + } + + if (parity) { + if (parity == 1) + FST_A_WRB(port->card, async_config[port->index].parity, + COM_ODD_PARITY); + else + FST_A_WRB(port->card, async_config[port->index].parity, + COM_EVEN_PARITY); + } else { + FST_A_WRB(port->card, async_config[port->index].parity, + COM_NO_PARITY); + } + fst_dbg(DBG_IOCTL, "%s: Reconfiguring Port\n", port->dev->name); + fst_issue_cmd(port, RECONFIG); +} + +/* Char tty interface for async PPP */ +#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_CLOSED 0 /* Initial state */ +#define FST_TTY_ST_OPEN 1 /* Has at least one open */ +#define FST_TTY_ST_DATA 2 /* Open and used for data */ +#define FST_TTY_ST_DISC 3 /* Open but line has dropped */ + +/* 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); +static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count); +static int fst_tty_write_room(struct tty_struct *tty); +static int fst_tty_chars_in_buffer(struct tty_struct *tty); +int fst_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int); +int fst_tty_tiocmget(struct tty_struct *); +static void fst_tty_flush_buffer(struct tty_struct *tty); +static void fst_tty_hangup(struct tty_struct *tty); +static void fst_tty_set_termios(struct tty_struct *tty, struct ktermios *old); +static struct tty_driver *serial_drv; + +typedef struct _fst_tty_area { + struct tty_port tty_port; + 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; + struct sk_buff *skb; + int len; /* async */ + char *data; /* async */ + struct work_struct fst_tty_work; + struct work_struct fst_async_tty_work; + +} st_fst_tty_area; + +static const struct tty_operations fst_tty_ops = { + .open = fst_tty_open, + .close = fst_tty_close, + .write = fst_tty_write, + .write_room = fst_tty_write_room, + .chars_in_buffer = fst_tty_chars_in_buffer, + .tiocmset = fst_tty_tiocmset, + .tiocmget = fst_tty_tiocmget, + .set_termios = fst_tty_set_termios, + .flush_buffer = fst_tty_flush_buffer, + .hangup = fst_tty_hangup, +}; + +st_fst_tty_area fst_tty_area[FST_TTY_NPORTS]; +int fst_tty_refcount; +int fst_tty_unreg_flag; +int fst_tty_cnt; + +static int check_tx_fifo_threshold(struct fst_port_info *port) +{ + u16 used_space = 0; + + /* This specifically for the async tty interface to start tx's + * again when it had been flow controlled by retruning a zero + * to tty_write() + */ + + used_space = + (u16) check_fifo_space((char *)port->fifo_rxdata, FIFO_ASY_TX); + if (used_space < (u16) FST_TX_DATA_FIFO_LOW) { + if (port->start) { + st_fst_tty_area *fst_tty; + fst_tty = &fst_tty_area[port->minor_dev_no]; + if (fst_tty->tty) { + fst_dbg(DBG_TTY, + "%s: Starting tty layer transmits again\n", + fst_tty->name); + tty_wakeup(fst_tty->tty); + } + port->start = 0; + } + } + return 0; +} + +void fst_tty_uninit(void) +{ + /* Unload the tty driver + */ + int res; + + pr_info("Unregister the tty driver\n"); + res = tty_unregister_driver(serial_drv); + if (res) { + fst_dbg(DBG_ASS, + "ERROR ->unregister the tty driver error=%d\n", + res); + } + put_tty_driver(serial_drv); +} + +int fst_tty_init_body(void) +{ + /* initialize tty driver struct */ + serial_drv = alloc_tty_driver(FST_TTY_NPORTS); + if (serial_drv == NULL) + return 0; + serial_drv->magic = TTY_DRIVER_MAGIC; + serial_drv->driver_name = "farsync_tty"; + serial_drv->name = "tty_hdlc"; + serial_drv->major = 0; + 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 | TTY_DRIVER_DYNAMIC_DEV; + + /* interface routines from the upper tty layer to the tty driver */ + tty_set_operations(serial_drv, &fst_tty_ops); + + /* register the TTY driver */ + if (tty_register_driver(serial_drv)) { + fst_dbg(DBG_ASS, "fsc: Failed to register serial driver!\n"); + put_tty_driver(serial_drv); + return 0; + } + return 1; /* OK */ +} + +int fst_tty_init(void) +{ + /* Load the tty driver */ + int retval = 0; + + pr_info("Initialising tty driver\n"); + retval = fst_tty_init_body(); + if (retval) + memset((void *)fst_tty_area, 0, + sizeof(st_fst_tty_area) * FST_TTY_NPORTS); + return retval; +} + +static int fst_tty_open(struct tty_struct *tty, struct file *file) +{ + /* Open a farsync port + * It must not already be open by the network stack + */ + int port_num; + st_fst_tty_area *fst_tty; + struct net_device *dev; + struct fst_port_info *port; + char dev_name[16]; + + fst_dbg(DBG_ASS, "fst_tty_open\n"); + if (!tty) + return -ENODEV; + + port_num = tty->index; + if ((port_num < 0) || (port_num >= FST_TTY_NPORTS)) { + fst_dbg(DBG_ASS, "fst_tty: open invalid minor %i\n", port_num); + return -ENODEV; + } + + fst_tty = &fst_tty_area[port_num]; + + INIT_WORK(&fst_tty->fst_tty_work, fst_process_tty_work); + INIT_WORK(&fst_tty->fst_async_tty_work, fst_process_async_tty_work); + + if (fst_tty->num_open == 0) { + /* + * We need to find the dev device and see if it is open + */ + dev = get_net_device(port_num); + if (dev == NULL) { + fst_dbg(DBG_ASS, + "Cannot open char device %s because net device not found\n", + dev->name); + return -ENXIO; + } + port = dev_to_port(dev); + if (port == NULL) { + fst_dbg(DBG_ASS, + "Cannot open char device %s because port is not created\n", + dev->name); + return -EACCES; + } + if (port->run) { + /* We could be a DSL port, and by this time the port + * will have already be opened and therefore be running + */ + if (port->card->type != FST_TYPE_DSL_S1) { + /* + * It is likely that the network device is + * active so refuse this open + */ + return -EBUSY; + } + } + /* first open of this tty */ + fst_tty->state = FST_TTY_ST_OPEN; + fst_tty->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; + + strcpy(dev_name, "tty_"); + strcat(dev_name, dev->name); + strcpy(fst_tty->name, dev->name); + fst_dbg(DBG_ASS, + "%s: Initializing TTY Sync Driver, tty major#%d minor#%i\n", + fst_tty->name, FST_TTY_MAJOR, fst_tty->tty_minor); + /* Set a flag to indicate we are now using the char + * interface for data read and write + */ + port->char_file = file; + /* + * Open the physical port if not already open + */ + if (!port->port_mode) + fst_openport(port); + fst_tty->port = port; + try_module_get(THIS_MODULE); + fst_tty_cnt++; + pr_info("TTY driver port %s is now open\n", fst_tty->name); + } + fst_tty->num_open++; + return 0; +} + +static void fst_tty_close(struct tty_struct *tty, struct file *flip) +{ + /* + * Close a farsync port + */ + st_fst_tty_area *fst_tty; + unsigned long flags; + + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, "fst_tty: no TTY in close\n"); + return; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) { + fst_dbg(DBG_ASS, "%s: TTY is not opened\n", fst_tty->name); + return; + } + + if (!fst_tty->num_open) { + fst_dbg(DBG_ASS, "%s: TTY is closed\n", fst_tty->name); + return; + } + + if (--fst_tty->num_open > 0) { + fst_dbg(DBG_ASS, "%s: TTY closing the second open\n", + fst_tty->name); + return; + } + + fst_tty_cnt--; + if (fst_tty->state != FST_TTY_ST_CLOSED) { + spin_lock_irqsave(&fst_tty->port->card->card_lock, flags); + fst_tty->tty = NULL; + fst_tty->state = FST_TTY_ST_CLOSED; + spin_unlock_irqrestore(&fst_tty->port->card->card_lock, flags); + fst_closeport(fst_tty->port); + fst_tty->port->run = 0; + fst_tty->port->char_file = NULL; + } + + module_put(THIS_MODULE); + pr_info("TTY driver port %s is now closed\n", fst_tty->name); + return; +} + +static int fst_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + /* Send data to the farsync port */ + st_fst_tty_area *fst_tty; + struct sk_buff *skb; + int space_used = 0; + int retval; + int from_user = 0; + + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, "Could not find TTY device in write\n"); + return -ENODEV; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) { + fst_dbg(DBG_ASS, "%s: TTY device is not opened\n", + fst_tty->name); + return -ENODEV; + } + + if (count > FST_MAX_MTU) { + fst_dbg(DBG_ASS, "%s: Size of write is too large\n", + fst_tty->name); + return -EINVAL; /* frame too big */ + } + + if (fst_tty->state == FST_TTY_ST_CLOSED) { + fst_dbg(DBG_TTY, + "%s: discarding %d bytes of write data on a disconnected port\n", + fst_tty->name, count); + return -ENODEV; + } + fst_dbg(DBG_TTY, "%s: fst_tty_write %s data len=%i\n", fst_tty->name, + (from_user) ? "from user" : "from kernel", count); + /* If we are in async mode, check that there is space in the + * tx fifo + */ + if (fst_tty->port->mode == FST_MODE_ASYNC) { + char *fifo_ptr; + + fifo_ptr = + fst_tty->port->card->mem + + ASY_OFFSET(tx_fifo[fst_tty->port->index]); + space_used = check_fifo_space(fifo_ptr, FIFO_ASY_TX); + if ((FST_TX_DATA_FIFO_LEN - space_used) < count) { + fst_dbg(DBG_FIFO, + "Couldn't accept write of %d bytes, no room in fifo %d\n", + count, FST_TX_DATA_FIFO_LEN - space_used); + fst_tty->port->start = 1; + return 0; + } + fst_tty->port->card->dma_port_tx = fst_tty->port; + fst_dbg(DBG_TX, "%s: Sending %d bytes of async data %p\n", + fst_tty->port->dev->name, count, + (char *)(fifo_ptr - fst_tty->port->card->mem)); + write_into_fifo(fifo_ptr, FIFO_ASY_TX, (char *)buf, count); + port_to_stats(fst_tty->port, tx_packets)++; + port_to_stats(fst_tty->port, tx_bytes) += count; + return count; + } + skb = dev_alloc_skb(count); + if (skb == NULL) { + fst_dbg(DBG_ASS, "fst_tty_write: can't allocate skb\n"); + return -EFAULT; + } + + /* Read it in */ + if (from_user) { + fst_dbg(DBG_TTY, "Value of from user is %d\n", from_user); + if (__copy_from_user(skb_put(skb, count), buf, count)) { + dev_kfree_skb(skb); + return -EFAULT; + } + } else { + memcpy(skb_put(skb, count), buf, count); + } + fst_tty->state = FST_TTY_ST_DATA; + retval = fst_start_xmit(skb, port_to_dev(fst_tty->port)); + if (retval != count) { + fst_dbg(DBG_TTY, + "tty_write: Asked to transmit %d, transmitted %d\n", + count, retval); + } + return retval; +} + +static int fst_tty_write_room(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + int room = FST_MAX_MTU; + + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, "Could not find TTY device to write room\n"); + return -ENODEV; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) { + fst_dbg(DBG_ASS, "%s: TTY device is not opened\n", + fst_tty->name); + return -ENODEV; + } + + fst_dbg(DBG_ASS, "%s: write room\n", fst_tty->name); + + if (fst_tty->port->start) + room = 0; + fst_dbg(DBG_TTY, "%s: write room: returning %d\n", + fst_tty->name, room); + + return room; +} + +static int fst_tty_chars_in_buffer(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, + "Could not find TTY device for chars in buffer\n"); + return -ENODEV; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if ((fst_tty->tty != tty) || (fst_tty->state < FST_TTY_ST_OPEN)) { + fst_dbg(DBG_ASS, "%s: TTY device is not opened\n", + fst_tty->name); + return -ENODEV; + } + fst_dbg(DBG_TTY, "%s: chars in buffer:returning 0\n", fst_tty->name); + return 0; +} + +static void fst_tty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + st_fst_tty_area *fst_tty; + unsigned int baud; + unsigned int stop_bits; + unsigned int char_size; + unsigned int parity; + unsigned int rtscts; + unsigned int xonxoff; + + fst_dbg(DBG_TTY, "tty_set_termios\n"); + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, "Could not find TTY for tiocmset\n"); + return; + } + fst_tty = (st_fst_tty_area *) tty->driver_data; + old = &fst_tty->tty->termios; + fst_dbg(DBG_TTY, "%s: Setting %x\n", fst_tty->name, old->c_cflag); + baud = get_baud(old->c_cflag); + char_size = get_char_size(old->c_cflag); + stop_bits = get_stop_bits(old->c_cflag); + parity = get_parity(old->c_cflag); + rtscts = get_rtscts(old->c_cflag); + xonxoff = get_xonxoff(old->c_iflag); + /* If we are in async mode we need to configure the async parameters + */ + if (fst_tty->port->mode == FST_MODE_ASYNC) { + configure_async_parameters(fst_tty->port, baud, char_size, + stop_bits, parity, + rtscts | xonxoff); + } +} + +int fst_tty_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear) +{ + st_fst_tty_area *fst_tty; + unsigned long outputs; + + fst_dbg(DBG_TTY, "%s: set:%x clear:%x\n", __func__, set, clear); + + if (!tty || !tty->driver_data) { + fst_dbg(DBG_ASS, "Could not find TTY for tiocmset\n"); + return -ENODEV; + } + + fst_tty = (st_fst_tty_area *) tty->driver_data; + + if (set & TIOCM_RTS) { + fst_dbg(DBG_TTY, "Set RTS\n"); + outputs = OPSTS_RTS | FST_RDL(fst_tty->port->card, + v24OpSts[fst_tty->port->index]); + FST_WRL(fst_tty->port->card, v24OpSts[fst_tty->port->index], + outputs); + if (fst_tty->port->run) + fst_issue_cmd(fst_tty->port, SETV24O); + } + + if (set & TIOCM_DTR) { + fst_dbg(DBG_TTY, "Set DTR\n"); + outputs = OPSTS_DTR | FST_RDL(fst_tty->port->card, + v24OpSts[fst_tty->port->index]); + FST_WRL(fst_tty->port->card, v24OpSts[fst_tty->port->index], + outputs); + if (fst_tty->port->run) + fst_issue_cmd(fst_tty->port, SETV24O); + } + + if (clear & TIOCM_RTS) { + fst_dbg(DBG_TTY, "Clear RTS\n"); + outputs = OPSTS_RTS ^ FST_RDL(fst_tty->port->card, + v24OpSts[fst_tty->port->index]); + FST_WRL(fst_tty->port->card, v24OpSts[fst_tty->port->index], + outputs); + if (fst_tty->port->run) + fst_issue_cmd(fst_tty->port, SETV24O); + } + if (clear & TIOCM_DTR) { + fst_dbg(DBG_TTY, "Clear DTR\n"); + outputs = OPSTS_DTR ^ FST_RDL(fst_tty->port->card, + v24OpSts[fst_tty->port->index]); + FST_WRL(fst_tty->port->card, v24OpSts[fst_tty->port->index], + outputs); + if (fst_tty->port->run) + fst_issue_cmd(fst_tty->port, SETV24O); + } + return 0; +} + +int fst_tty_tiocmget(struct tty_struct *tty) +{ + unsigned int signals = 0; + unsigned long out = 0; + + st_fst_tty_area *fst_tty = (st_fst_tty_area *) tty->driver_data; + fst_tty = (st_fst_tty_area *) tty->driver_data; + + fst_dbg(DBG_TTY, "%s: tiocmget\n", fst_tty->name); + + signals = FST_RDL(fst_tty->port->card, v24OpSts[fst_tty->tty_minor]); + if (signals & OPSTS_RTS) + out |= TIOCM_RTS; + if (signals & OPSTS_DTR) + out |= TIOCM_DTR; + signals = FST_RDL(fst_tty->port->card, v24IpSts[fst_tty->tty_minor]); + if (signals & IPSTS_CTS) + out |= TIOCM_CTS; + if (signals & IPSTS_DSR) + out |= TIOCM_DSR; + if (signals & IPSTS_DCD) + out |= TIOCM_CD; + fst_dbg(DBG_TTY, "Returning signals as %lx\n", out); + return out; +} + +static void fst_tty_flush_buffer(struct tty_struct *tty) +{ + st_fst_tty_area *fst_tty; + + if (!tty || !tty->driver_data) { + fst_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)) { + fst_dbg(DBG_ASS, "%s: TTY device is not opened\n", + fst_tty->name); + return; + } + + fst_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) { + fst_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)) { + fst_dbg(DBG_ASS, "%s: TTY device is not opened\n", + fst_tty->name); + return; + } + fst_dbg(DBG_ASS, "fst_tty_hangup\n"); +} + +static void fst_process_async_tty_work(struct work_struct *work) +{ + struct fst_port_info *port; + char *data; + int len; + st_fst_tty_area *fst_tty; + struct tty_ldisc *ld; + + fst_tty = container_of(work, st_fst_tty_area, fst_async_tty_work); + port = fst_tty->port; + len = fst_tty->len; + data = fst_tty->data; + + if (fst_tty_cnt == 0) { + kfree(data); + return; + } + /* ToDo: Note that the third parameter to the ldisc receive_buf + * function is a pointer to an array of status bytes which is + * the same length as the data. When we move on to getting the + * status bytes from the async interface we can implement this. + * For the moment it is a NULL pointer + */ + + fst_tty->state = FST_TTY_ST_DATA; + ld = tty_ldisc_ref(fst_tty->tty); + if (ld) { + if (fst_tty->tty && (ld->ops->receive_buf)) { + fst_dbg(DBG_TTY, + "%s: call line disc. receive_buf of length %d first byte is %x\n", + fst_tty->name, len, data[0]); + ld->ops->receive_buf(fst_tty->tty, data, NULL, len); + tty_ldisc_deref(ld); + port_to_stats(port, rx_packets)++; + port_to_stats(port, rx_bytes) += len; + } else { + fst_dbg(DBG_ASS, + "%s: frame dropped. receive_buf of length %d\n", + fst_tty->name, len); + port_to_stats(port, rx_dropped)++; + } + } + kfree(data); + return; +} + +static void fst_process_tty_work(struct work_struct *work) +{ + struct fst_port_info *port; + st_fst_tty_area *fst_tty; + struct sk_buff *skb; + struct tty_ldisc *ld; + if (fst_tty_cnt == 0) + return; + + fst_tty = container_of(work, st_fst_tty_area, fst_tty_work); + port = fst_tty->port; + skb = fst_tty->skb; + + ld = tty_ldisc_ref(fst_tty->tty); + if (ld) { + if ((fst_tty->tty) && (ld->ops->receive_buf)) { + fst_dbg(DBG_ASS, + "%s: call line disc. receive_buf of length %d\n", + fst_tty->name, skb->len); + + ld->ops->receive_buf(fst_tty->tty, skb->data, + NULL, skb->len); + } else { + fst_dbg(DBG_ASS, + "%s: frame dropped. receive_buf of length %d\n", + fst_tty->name, skb->len); + } + tty_ldisc_deref(ld); + } + dev_kfree_skb(skb); +} + +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 = (u64) 1 << card_index; + *queue |= mask; + spin_unlock_irqrestore(&fst_work_q_lock, flags); +} + +static void fst_process_rx_work_q(struct work_struct *work_q) +{ + unsigned long flags; + u64 work_rxq; + int i; + + /* Grab the queue exclusively */ + fst_dbg(DBG_TX, "fst_process_rx_work_q\n"); + spin_lock_irqsave(&fst_work_q_lock, flags); + work_rxq = fst_work_rxq; + fst_work_rxq = 0; + spin_unlock_irqrestore(&fst_work_q_lock, flags); + + /* Call the bottom half for each card with work waiting + */ + for (i = 0; i < FST_MAX_CARDS; i++) { + if (work_rxq & 0x01) { + if (fst_cards_list[i] != NULL) { + fst_dbg(DBG_ASS, "Calling rx bh for card %d\n", + i); + do_bottom_half_rx(fst_cards_list[i]); + } + } + work_rxq = work_rxq >> 1; + } +} + +static void fst_process_tx_work_q(unsigned long work_q) +{ + unsigned long flags; + u64 work_txq; + int i; + + /* Grab the queue exclusively */ + fst_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 < FST_MAX_CARDS; i++) { + if (work_txq & 0x01) { + if (fst_cards_list[i] != NULL) { + fst_dbg(DBG_TX, "Calling tx bh for card %d\n", + i); + do_bottom_half_tx(fst_cards_list[i]); + } + } + work_txq = work_txq >> 1; + } +} + +static void fst_process_int_work_q(unsigned long work_q) +{ + unsigned long flags; + u64 work_intq; + int i; + + /* Grab the queue exclusively */ + fst_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 < FST_MAX_CARDS; i++) { + if (work_intq & 0x01) { + if (fst_cards_list[i] != NULL) { + if (fst_cards_list[i]->type != + FST_TYPE_DSL_S1) { + fst_dbg(DBG_INTR, + "Calling rx & tx bh for card %d\n", + i); + do_bottom_half_rx(fst_cards_list[i]); + } else { + /* If it is a DSL card we need to bet + * out of Interrupt mode + * so we need to schedule some work + */ + fst_dbg(DBG_ASS, + "Scheduling work for DSL rx\n"); + fst_q_work_item(&fst_work_rxq, i); + schedule_work(&fst_rx_work); + } + /* Calling the tx BH directly could mean that + * it runs twice concurrently. So we have to + * schedule it + */ + fst_q_work_item(&fst_work_txq, i); + tasklet_schedule(&fst_tx_task); + } + } + work_intq = work_intq >> 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)) { + fst_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 */ ; + /* Do nothing */; outw(0x240f, card->pci_conf + CNTRL_9054 + 2); - /* - * We are delaying here to allow the 9054 to reload its eeprom + /* We are delaying here to allow the 9054 to reload its eeprom */ j = jiffies + 1; while (jiffies < j) - /* Do nothing */ ; + /* Do nothing */; + outw(0x040f, card->pci_conf + CNTRL_9054 + 2); + + if (pci_write_config_byte + (card->device, PCIILR, interrupt_line_register)) { + fst_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 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); +} + +/* Enable 186 interrupts + */ +static inline void fst_enable_186_intr(struct fst_card_info *card) +{ + if (card->family == FST_FAMILY_TXU) + outl(0x0f0c0900, card->pci_conf + INTCSR_9054); +} + +/* Disable 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); +} + +/* Disable 186 interrupts + */ +static inline void fst_disable_186_intr(struct fst_card_info *card) +{ + if (card->family == FST_FAMILY_TXU) + outl(0x0f0c0100, card->pci_conf + INTCSR_9054); +} + +static int fst_proc_info(struct seq_file *m, void *v) +{ + struct seq_file *buffer = m; + struct fst_card_info *card; + int c; + int port_count = 0; + + seq_printf(buffer, + "FarSync %s Driver version %s - Patch Level %s - Build %s\n", + FST_DRIVER_TYPE, FST_USER_VERSION, FST_PATCH_LEVEL, + FST_ADDITIONAL); + seq_printf(buffer, "%d Cards found\n", fst_ncards); + for (c = 0; c < fst_ncards; c++) { + card = fst_cards_list[c]; + if (card->state == FST_RUNNING) + extract_serial(card); + else + strcpy(serial, "UNKNOWN "); + seq_printf(buffer, + "\t%s-%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, + serial, + type_strings[card->type], + card->irq, card->nports, state_strings[card->state]); + port_count += card->nports; + } + seq_printf(buffer, "Total number of ports = %d\n", port_count); + seq_printf(buffer, "Total number of async connects = %d\n", + fst_tty_cnt); + return 0; +} + +/* Process the result of trying to pass a recieved frame up the stack + */ +static void fst_process_rx_status(int rx_status, char *name) +{ + switch (rx_status) { + case NET_RX_SUCCESS: + { + /* + * Nothing to do here + */ + break; + } + + case NET_RX_DROP: + { + pr_err("%s: Received packet dropped\n", name); + break; + } + } +} + +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 = NET_RX_SUCCESS; + struct fstioc_mon header; + + /* Allocate SKB. Length of frame to monitor + length of the + * header we prepend. + */ + mon_skb = dev_alloc_skb(skb->len + sizeof(struct fstioc_mon)); + if (mon_skb == NULL) { + fst_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 */ + fst_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); + fst_dbg(DBG_INTR, "Monitor packet length is %d\n", mon_skb->len); + if (port->run) + rx_status = netif_rx(mon_skb); + else { + dev_kfree_skb(mon_skb); + fst_dbg(DBG_ASS, "Discarding monitor frame, port closed\n"); + } + fst_process_rx_status(rx_status, port_to_dev(port)->name); + if (rx_status == NET_RX_DROP) { + fst_dbg(DBG_ASS, "Could not deleiver monitor message"); + return 1; + } + return 0; +} + +#define SDMAPOLL 0x17 +static void fst_check_send(struct fst_port_info *port) +{ + struct fst_card_info *card; + unsigned long flags; + + fst_dbg(DBG_CMD, "In fst_check_send %p\n", port); + if (port->low_latency & LOW_LATENCY_TX) { + card = port->card; + spin_lock_irqsave(&card->card_lock, flags); + FST_WRW(card, mailbox[1], port->index); + FST_WRW(card, mailbox[0], SDMAPOLL); + spin_unlock_irqrestore(&card->card_lock, flags); + } else { + fst_dbg(DBG_CMD, "Low Latency Tx not enabled\n"); + } +} + +static void fst_issue_cmd(struct fst_port_info *port, unsigned short cmd) +{ + u16 command; + char *fifo_ptr; + int status; + + fifo_ptr = port->card->mem + ASY_OFFSET(cmd_fifo[port->index]); + port->card->cmdfifo_complete[port->index] = 0; + switch (cmd) { + case SETV24O: + fst_dbg(DBG_CMD, "%s: Setting V24 Output Signals\n", + port->dev->name); + command = cpu_to_le16(CMD_FIFO_SET_V24); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case STARTPORT: + fst_dbg(DBG_CMD, "%s: Sending Start Port\n", port->dev->name); + command = cpu_to_le16(CMD_FIFO_START_PORT); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case STOPPORT: + fst_dbg(DBG_CMD, "%s: Sending Stop Port\n", port->dev->name); + command = cpu_to_le16(CMD_FIFO_STOP_PORT); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case ABORTTX: + fst_dbg(DBG_CMD, "%s: Sending Abort Tx\n", port->dev->name); + command = cpu_to_le16(CMD_FIFO_ABORT_PORT); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case RECONFIG: + fst_dbg(DBG_CMD, "%s: Sending Reconfig async port\n", + port->dev->name); + command = cpu_to_le16(FST_CMD_RECONFIG); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case FLUSHTX: + fst_dbg(DBG_CMD, "%s: Sending flush async tx fifo\n", + port->dev->name); + command = cpu_to_le16(FST_CMD_FIFO_FLUSH_TX); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case SETXON: + fst_dbg(DBG_CMD, "%s: Sending XON command\n", port->dev->name); + command = cpu_to_le16(FST_CMD_FIFO_XON); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case SETXOFF: + fst_dbg(DBG_CMD, "%s: Sending XOFF command\n", port->dev->name); + command = cpu_to_le16(FST_CMD_FIFO_XOFF); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + case RECONFIGLINE: + fst_dbg(DBG_CMD, "%s: Sending Reconfig line parameters\n", + port->dev->name); + command = cpu_to_le16(CMD_FIFO_RECONFIG_PORT); + write_into_fifo(fifo_ptr, FIFO_ASY_CMD, (char *)&command, 2); + break; + + default: + fst_dbg(DBG_ASS, "%s: Issue ASY Command: Command %d not implemented\n", + port_to_dev(port)->name, cmd); + } + /* wait for a reply */ + if (port->card->state == FST_RUNNING) { + fst_dbg(DBG_CMD, "Waiting for cmd fifo to be processed\n"); + status = + wait_event_interruptible_timeout(port->card->cmdfifo_waitq, + (port-> + card->cmdfifo_complete + [port->index]), 500); + if (status == -ERESTARTSYS) + pr_info("Fifo command interrupted by signal\n"); + if (status == 0) + pr_err("Timeout in processing fifo cmd %d\n", cmd); + fst_dbg(DBG_CMD, "Reply received (%d) or interrupts (%x)\n", + port->card->cmdfifo_complete[port->index], status); + } else { + fst_dbg(DBG_ASS, + "Command %d issued when card not yet running\n", cmd); + } +} + +/* 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); + /* If we are in ignore carrier mode, then now is the + * time to tell the network layer that the carrier is on + */ + if (port->ignore_carrier) { + if (!netif_carrier_ok(port_to_dev(port))) { + fst_dbg(DBG_OPEN, "Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); + } + } +} + +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); + /* If we are in ignore carrier mode, then now is the + * time to tell the network layer that carrier is off + */ + if (port->ignore_carrier) + netif_carrier_off(port_to_dev(port)); +} + +/* Setup port Rx buffers + */ +static void 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, port_config[pi].num_rx_buffers, + (u8) port->num_rx_buffers); + for (i = 0; i < port->num_rx_buffers; i++) { + offset = BUF_OFFSET(rx_buffer[pi][0]) + + i * port->rx_buffer_size; + + FST_WRW(card, rx_descr_ring[pi][i].ladr, (u16) offset); + FST_WRB(card, rx_descr_ring[pi][i].hadr, (u8) (offset >> 16)); + FST_WRW(card, rx_descr_ring[pi][i].bcnt, + cnv_bcnt(port->rx_buffer_size)); + FST_WRW(card, rx_descr_ring[pi][i].mcnt, FST_MAX_MTU - 2); + FST_WRB(card, rx_descr_ring[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, port_config[pi].num_tx_buffers, + (u8) port->num_tx_buffers); + for (i = 0; i < port->num_tx_buffers; i++) { + offset = BUF_OFFSET(tx_buffer[pi][0]) + + i * port->tx_buffer_size; + + FST_WRW(card, tx_descr_ring[pi][i].ladr, (u16) offset); + FST_WRB(card, tx_descr_ring[pi][i].hadr, (u8) (offset >> 16)); + FST_WRW(card, tx_descr_ring[pi][i].bcnt, 0); + FST_WRB(card, tx_descr_ring[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, smc_version) != SMC_VERSION) { + pr_err("Bad shared memory version %d expected %d\n", + FST_RDW(card, smc_version), SMC_VERSION); + card->state = FST_BADVERSION; + return; + } + if (FST_RDL(card, end_of_smc_signature) != END_SIG) { + pr_err("Missing shared memory signature %x\n", + FST_RDL(card, end_of_smc_signature)); + pr_err("size of shared memory structure is %u\n", + (unsigned int)sizeof(struct fst_shared)); + card->state = FST_BADVERSION; + return; + } + /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ + i = FST_RDB(card, task_status); + if (i == 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, dsl_config.snrth, 5); + FST_WRB(card, dsl_config.lpath, 30); + } + if (card->type == FST_TYPE_T4E) { + /* + * Check to see if we really are a T4E+ + */ + if (FST_RDB(card, capability_mask) == 0x01) { + /* + * Yes, we are a T4E+ + */ + card->type = FST_TYPE_T4Ep; + } + } + } else if (i == 0xFF) { + pr_err("Firmware initialisation failed. Card halted\n"); + card->state = FST_HALTED; + return; + } else if (i != 0x00) { + pr_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, number_of_ports) != card->nports) { + pr_warn("Port count mismatch on card %d.", card->card_no); + pr_warn(" Firmware thinks %d we say %d\n", + FST_RDL(card, number_of_ports), card->nports); + } +} + +/* ATM Fifo functions + */ + +static void atm_fifo_init(struct fst_card_info *card) +{ + struct fst_fifo fifo; - if (pci_write_config_byte - (card->device, PCI_INTERRUPT_LINE, interrupt_line_register)) { - dbg(DBG_ASS, - "Error in writing interrupt line register\n"); - } + /* Initialise the dsl fifo's to zeros */ + fst_dbg(DBG_INIT, "Zeroing %x bytes of dsl tx fifo\n", + (unsigned int)sizeof(struct dsl_tx_fifo)); + memset_io(card->mem + DT_BASE, 0, sizeof(struct dsl_tx_fifo)); + fst_dbg(DBG_INIT, "Zeroing %x bytes of dsl rx fifo\n", + (unsigned int)sizeof(struct dsl_rx_fifo)); + memset_io(card->mem + DR_BASE, 0, sizeof(struct dsl_rx_fifo)); - } else { - regval = inl(card->pci_conf + CNTRL_9052); + /* 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'; + + FST_DT_WRW(card, tx_fifo[FIFO_LENGTH], fifo.fifo_length); + FST_DT_WRW(card, tx_fifo[READ_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); + FST_DT_WRB(card, tx_fifo[DATA_0], fifo.data[0]); + FST_DT_WRB(card, tx_fifo[DATA_1], fifo.data[1]); + FST_DT_WRB(card, tx_fifo[DATA_2], fifo.data[2]); + FST_DT_WRB(card, tx_fifo[DATA_3], fifo.data[3]); + + fifo.data[0] = 'B'; + fifo.data[1] = 'E'; + fifo.data[2] = 'E'; + fifo.data[3] = 'F'; + + FST_DR_WRW(card, rx_fifo[FIFO_LENGTH], fifo.fifo_length); + FST_DR_WRW(card, rx_fifo[READ_IDX], fifo.read_idx); + FST_DR_WRW(card, rx_fifo[WRITE_IDX], fifo.write_idx); + FST_DR_WRW(card, rx_fifo[OVERFLOW_IDX], fifo.overflow_idx); + FST_DR_WRB(card, rx_fifo[DATA_0], fifo.data[0]); + FST_DR_WRB(card, rx_fifo[DATA_1], fifo.data[1]); + FST_DR_WRB(card, rx_fifo[DATA_2], fifo.data[2]); + FST_DR_WRB(card, rx_fifo[DATA_3], fifo.data[3]); +} + +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; + + fst_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; + fst_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); + fst_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_t(u16, avail_fifo_space, + (unsigned short)temp); + + if (fifo.overflow_idx == -1) { + fst_dbg(DBG_IOCTL, "tx_fifo.overflow_idx = -1\n"); + if (free_space > data_len) { + fst_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_t(u16, (unsigned short)data_len, + (unsigned + short)(fifo.fifo_length - + next_write_idx)); + fst_dbg(DBG_IOCTL, "First write amount is %d\n", + first_write_amount); + fst_dbg(DBG_IOCTL, "Next write index is %d\n", + next_write_idx); + fst_dbg(DBG_IOCTL, "Copying to offset %lx\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); + fst_dbg(DBG_IOCTL, "Write Index updated to %d\n", + fifo.write_idx); + if (first_write_amount != data_len) { + fst_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); + fst_dbg(DBG_IOCTL, + "Second write amount is %d\n", + second_write_amount); + fst_dbg(DBG_IOCTL, "Next write index is %d\n", + next_write_idx); + fst_dbg(DBG_IOCTL, "Copying to offset %lx\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); + } + fst_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) { + pr_err("Error in atm_write_into_fifo\n"); + pr_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; + fst_dbg(DBG_ASS, + "atm_fifo_write: Not enough room for next pdu %d\n", + data_len); + fst_dbg(DBG_ASS, + "free space = %d used space = %d available = %d\n", + free_space, used_space, avail_fifo_space); + fst_dbg(DBG_ASS, + "Fifo header is: w_idx = %d r_idx = %d\n", + fifo.write_idx, fifo.read_idx); + FST_WRB(card, dsl_control.spare[0], 0xff); + } - outl(regval | 0x40000000, card->pci_conf + CNTRL_9052); - outl(regval & ~0x40000000, card->pci_conf + CNTRL_9052); } + fst_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; } -/* Release the processor from reset - */ -static inline void -fst_cpurelease(struct fst_card_info *card) +static void atm_send_idle_cell(struct fst_card_info *card) { - if (card->family == FST_FAMILY_TXU) { - /* - * Force posted writes to complete - */ - (void) readb(card->mem); + fst_dbg(DBG_ATM, "Sending idle cell\n"); + atm_write_into_fifo(card, atm_idle_cell, 53); + FST_WRW(card, dsl_control.bytes_to_send, 53); + fst_intr_card(card); +} - /* - * 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); +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 + */ + + fst_dbg(DBG_IOCTL, "%d: atm_check_read_length\n", card->card_no); + h_size = sizeof(struct fst_fifo) - 4; + + fifo.fifo_length = FST_DR_RDW(card, rx_fifo[FIFO_LENGTH]); + fifo.read_idx = FST_DR_RDW(card, rx_fifo[READ_IDX]); + fifo.write_idx = FST_DR_RDW(card, rx_fifo[WRITE_IDX]); + fifo.overflow_idx = FST_DR_RDW(card, rx_fifo[OVERFLOW_IDX]); + + amount_to_read = (unsigned short)((fifo.write_idx - fifo.read_idx + + fifo.fifo_length) % + fifo.fifo_length); + fst_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)) { + fst_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; } -/* Clear the cards interrupt flag - */ -static inline void -fst_clear_intr(struct fst_card_info *card) +static void atm_empty_rx_fifo(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) + int h_size = 0; + struct fst_fifo fifo; + + /* Set the rx fifo to empty */ + + fst_dbg(DBG_IOCTL, "atm_empty_rx_fifo\n"); + h_size = sizeof(struct fst_fifo) - 4; + + fifo.fifo_length = FST_DR_RDW(card, rx_fifo[FIFO_LENGTH]); + fifo.read_idx = FST_DR_RDW(card, rx_fifo[READ_IDX]); + fifo.write_idx = FST_DR_RDW(card, rx_fifo[WRITE_IDX]); + fifo.overflow_idx = FST_DR_RDW(card, rx_fifo[OVERFLOW_IDX]); + + fifo.read_idx = fifo.write_idx; + FST_DR_WRW(card, rx_fifo[READ_IDX], fifo.read_idx); + fst_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 + */ + fst_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); + fst_dbg(DBG_IOCTL, "Next read index = %d\n", next_read_idx); + data1 = card->mem + DR_OFFSET(rx_fifo) + h_size + + next_read_idx; + length1 = (u16)min_t(u16, (u16)amount_to_read, + (unsigned short)(fifo.fifo_length + - + next_read_idx)); + fst_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) { + fst_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); + fst_dbg(DBG_IOCTL, "Finished read from fifo\n"); + /* if the accumulated length + this cell exceeds max then + * return error */ - outw(0x0543, card->pci_conf + INTCSR_9052); + if ((accumulated_len + 53) > FST_MAX_ATM_MTU) { + pr_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; + } + pr_err("Invalid last cell header "); + pr_err("length so far = %d len in fifo = %d\n", + accumulated_len, saved_len); + pr_err("Header is %x %x %x %x\n", data[0], data[1], + data[2], data[3]); + pr_err("Write Index = %d Read Index = %d\n", + fifo.write_idx, fifo.read_idx); + FST_RDB(card, end_of_smc_signature); + fst_dbg(DBG_ATM, "Triggering analyser %lx\n", + WIN_OFFSET(end_of_smc_signature)); + return -1; + } else { + /* + * Check header + */ + if (memcmp(data, card->ports[0]->atm_cell_header, 4) == + 0) { + return 0; + } + pr_err("Invalid cell header "); + pr_err("length so far = %d len in fifo = %d\n", + accumulated_len, saved_len); + pr_err("Header is %x %x %x %x\n", data[0], data[1], + data[2], data[3]); + pr_err("Write Index = %d Read Index = %d\n", + fifo.write_idx, fifo.read_idx); + FST_RDB(card, end_of_smc_signature); + fst_dbg(DBG_ATM, "Triggering analyser %lx\n", + WIN_OFFSET(end_of_smc_signature)); + return -1; + } } + return 0; } -/* Enable card interrupts - */ -static inline void -fst_enable_intr(struct fst_card_info *card) +/* 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 +}; + +unsigned int calc_crc(char *mem, int len, unsigned initial) { - if (card->family == FST_FAMILY_TXU) { - outl(0x0f0c0900, card->pci_conf + INTCSR_9054); - } else { - outw(0x0543, card->pci_conf + INTCSR_9052); + unsigned crc; + crc = initial; + + fst_dbg(DBG_ATM, "Calc CRC starting at %x\n", initial); + for (; len; mem++, len--) + crc = CRC32(*mem, crc); + return crc; +} + +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; + + fst_dbg(DBG_ATM, "Generate CRC\n"); + crc = ~crc32(crc, skb->data, total_length - 4); + fst_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); + fst_dbg(DBG_ATM, "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]); + fst_dbg(DBG_ATM, "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); + fst_dbg(DBG_ATM, + "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; + + fst_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; + fst_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; + fst_dbg(DBG_ATM, "No of cells required is %d\n", no_of_cells); + pdu_ptr = dev_alloc_skb(padded_pdu_len + (no_of_cells * 5)); + if (!pdu_ptr) { + fst_dbg(DBG_ASS, "Could not allocate skb for atm cells\n"); + return NULL; + } + cell_ptr = dev_alloc_skb(padded_pdu_len + (no_of_cells * 5)); + if (!cell_ptr) { + fst_dbg(DBG_ASS, "Could not allocate skb for atm cells\n"); + return NULL; } + fst_dbg(DBG_ATM, "Allocated Cells at %p\n", cell_ptr->data); + /* + * Copy the mpoa header in first if required + */ + fst_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); + } + fst_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 */ + fst_dbg(DBG_ATM, "Zero the pad bytes\n"); + pad = skb_put(pdu_ptr, pdu_len); + fst_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)); + fst_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 */ + fst_dbg(DBG_ATM, "Setting the trailer bytes\n"); + trailer = pdu_ptr->data + padded_pdu_len - 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) { + fst_dbg(DBG_ATM, "Formatting atm final header\n"); + memcpy(skb_put(cell_ptr, 5), port->last_atm_cell_header, + 5); + } else { + fst_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 */ + fst_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; } -/* Disable card interrupts +/* Rx Functions */ -static inline void -fst_disable_intr(struct fst_card_info *card) +static int validate_crc(char *pdu_ptr, int padded_len, unsigned int rcv_crc) { - if (card->family == FST_FAMILY_TXU) { - outl(0x00000000, card->pci_conf + INTCSR_9054); - } else { - outw(0x0000, card->pci_conf + INTCSR_9052); + /* 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; + + fst_dbg(DBG_ATM, "Validate CRC\n"); + crc = ~crc32(crc, pdu_ptr, padded_len - 4); + fst_dbg(DBG_ATM, "CRC recalculated as %x on %d bytes\n", crc, + padded_len - 4); + if (rcv_crc != crc) { + pr_err("CRC anomaly, received = %x, generated = %x\n", + rcv_crc, crc); + return 1; } + return 0; } -/* 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 struct sk_buff *process_rx_pdu(struct fst_port_info *port, + struct sk_buff *frame_ptr) { - switch (rx_status) { - case NET_RX_SUCCESS: - { + /* 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; + + fst_dbg(DBG_ATM, "Process RX PDU of length %d\n", frame_ptr->len); + /* Check for minimum length + */ + if (frame_ptr->len < 53) { + pr_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; + fst_dbg(DBG_ATM, "No of atm cells = %d\n", no_of_cells); + pdu_ptr = dev_alloc_skb(frame_len); + if (!pdu_ptr) { + fst_dbg(DBG_ATM, "Could not allocate skb for atm cells\n"); + return NULL; + } + + fst_dbg(DBG_ATM, "Allocated PDU at %p\n", pdu_ptr->data); + for (i = 0; i < no_of_cells; i++) { + fst_dbg(DBG_ATM, "Evaluating Cell Header\n"); + if (i == no_of_cells - 1) { /* - * Nothing to do here + * Last cell header comparison */ - break; + if (memcmp + (port->last_atm_cell_header, frame_ptr->data, + 4) != 0) { + fst_dbg(DBG_ASS, + "Error in last cell header comparison\n"); + fst_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 { + fst_dbg(DBG_ATM, "Last cell header OK\n"); + fst_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) { + fst_dbg(DBG_ASS, + "Error in cell header comparison\n"); + fst_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 { + fst_dbg(DBG_ATM, "Cell header OK\n"); + fst_dbg(DBG_ATM, "%x %x %x %x\n", + frame_ptr->data[0], frame_ptr->data[1], + frame_ptr->data[2], frame_ptr->data[3]); + + } } - case NET_RX_DROP: - { - dbg(DBG_ASS, "%s: Received packet dropped\n", name); - break; + fst_dbg(DBG_ATM, "Extracting cell header for cell %d\n", i); + skb_pull(frame_ptr, 5); + fst_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); + fst_dbg(DBG_ATM, "Received CRC is %x\n", crc); + fst_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, dsl_control.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) { + pr_err("Error in mpoa header comparison\n"); + pr_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]); + pr_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 { + fst_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) { + pr_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) +static inline void fst_init_dma(struct fst_card_info *card) { - /* - * This is only required for the PLX 9054 - */ + unsigned short pci_cr; + + /* This is only required for the PLX 9054 */ if (card->family == FST_FAMILY_TXU) { - pci_set_master(card->device); + fst_dbg(DBG_INTR, "Initialising DMA\n"); + pci_read_config_word(card->device, PCICR, &pci_cr); + pci_cr |= 0x0004; + /* Enable DMA Bus mastering */ + pci_write_config_word(card->device, PCICR, pci_cr); outl(0x00020441, card->pci_conf + DMAMODE0); outl(0x00020441, card->pci_conf + DMAMODE1); - outl(0x0, card->pci_conf + DMATHR); + outl(fst_dmathr, card->pci_conf + DMATHR); } } @@ -823,30 +4153,20 @@ fst_init_dma(struct fst_card_info *card) */ static void fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, - int len, int txpos) + int len, int txpos, int flags) { - struct net_device *dev = port_to_dev(port); - + fst_dbg(DBG_TX, "fst_tx_dma_complete\n"); /* * Everything is now set, just tell the card to go */ - dbg(DBG_TX, "fst_tx_dma_complete\n"); - FST_WRB(card, txDescrRing[port->index][txpos].bits, - DMA_OWN | TX_STP | TX_ENP); - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; - dev->trans_start = jiffies; -} - -/* - * 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 (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->mode == FST_MODE_TRANSPARENT) + flags = flags & 0xfe; + FST_WRB(card, tx_descr_ring[port->index][txpos].bits, flags); + fst_check_send(port); } /* Rx dma complete interrupt @@ -855,219 +4175,237 @@ 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); int pi; - int rx_status; + int rx_status = NET_RX_SUCCESS; + int space_left; - dbg(DBG_TX, "fst_rx_dma_complete\n"); + fst_dbg(DBG_RX, "fst_rx_dma_complete\n"); pi = port->index; - memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); - + if (port->mtu_for_rx_skb != port_to_dev(port)->mtu) + fst_dbg(DBG_ASS, "MTU changed after dma started\n"); + space_left = (port->mtu_for_rx_skb + MAX_PPP_HEADER) - + port->rxq.frame->len; + if (len <= space_left) + memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); + else { + fst_dbg(DBG_ASS, + "This dma skb copy would have exceeded max mtu %d %d %d\n", + port->rxq.frame->len, len, port->rxq.segment_cnt); + fst_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); + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); /* Update stats */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; + fst_dbg(DBG_RX, "Updating stats by %d bytes\n", len); + port_to_stats(port, rx_bytes) += len; - /* 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) - dev->stats.rx_dropped++; + 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) { + pr_err("Error in received atm pdu\n"); + return; + } + } + fst_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 (port->char_file) { + port->rxq.frame->protocol = + htons(ETH_P_WAN_PPP); + } else { + 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 { + new_skb->protocol = + hdlc_type_trans(new_skb, + new_skb->dev); + } + } + } + } + if ((port->char_file) || (port->port_mode)) { + st_fst_tty_area *fst_tty; + fst_tty = &fst_tty_area[port->minor_dev_no]; + fst_tty->skb = skb; + schedule_work(&(fst_tty->fst_tty_work)); + } else { + if (port->run) + rx_status = netif_rx(new_skb); + else { + dev_kfree_skb(new_skb); + fst_dbg(DBG_ASS, + "dma_comp: Discarding data, port closed\n"); + } + } + 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; + port->rxq.segment_cnt = 0; + } } -/* - * Receive a frame through the DMA - */ +/* Receive a frame through the DMA */ static inline void -fst_rx_dma(struct fst_card_info *card, dma_addr_t skb, - dma_addr_t mem, int len) +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 - */ + /* This routine will setup the DMA and start it */ - dbg(DBG_RX, "In fst_rx_dma %lx %lx %d\n", - (unsigned long) skb, (unsigned long) mem, len); + fst_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"); + fst_dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n"); } + /* Copy to here */ + outl((unsigned long)skb, card->pci_conf + DMAPADR0); + /* from here */ + outl((unsigned long)mem, card->pci_conf + DMALADR0); + /* for this length */ + outl(len, card->pci_conf + DMASIZ0); + /* In this direction */ + outl(0x00000000c, card->pci_conf + DMADPR0); - outl(skb, card->pci_conf + DMAPADR0); /* Copy to here */ - outl(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 + /* 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 - */ +/* 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"); - } + /* This routine will setup the DMA and start it. */ - 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 */ + fst_dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len); + if (card->dmatx_in_progress) + fst_dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n"); + /* Copy from here */ + outl((unsigned long)skb, card->pci_conf + DMAPADR1); + /* to here */ + outl((unsigned long)mem, card->pci_conf + DMALADR1); + /* for this length */ + outl(len, card->pci_conf + DMASIZ1); + /* In this direction */ + outl(0x000000004, card->pci_conf + DMADPR1); - /* - * We use the dmatx_in_progress to flag the channel as busy - */ + /* 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 */ } -/* 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_uninterruptible(1); - spin_lock_irqsave(&card->card_lock, flags); - - if (++safety > 2000) { - pr_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 - */ +/* Receive a DSL cell through the DMA */ static void -fst_rx_config(struct fst_port_info *port) +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; - int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; + static unsigned int largest_rx_time; - 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]); + card->dmarx_in_progress = 1; + fst_dbg(DBG_RX, "In fst_rx_dsl_dma %p %p %d\n", skb, mem, len); + /* Copy to here */ + outl((unsigned long)skb, card->pci_conf + DMAPADR0); + /* from here */ + outl((unsigned long)mem, card->pci_conf + DMALADR0); + /* for this length */ + outl(len, card->pci_conf + DMASIZ0); + /* In this direction */ + outl(0x00000000c, card->pci_conf + DMADPR0); - 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); + /* 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) { + fst_dbg(DBG_ASS, + "Time to transfer rxbuffer(%d) = %d microseconds\n", + len, i); + largest_rx_time = i; } - port->rxpos = 0; - spin_unlock_irqrestore(&card->card_lock, flags); } -/* - * Setup port Tx buffers - */ +/* Send a DSL PDU through the DMA */ static void -fst_tx_config(struct fst_port_info *port) +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; - int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; + static unsigned int largest_tx_time; - 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]); + card->dmatx_in_progress = 1; + fst_dbg(DBG_TX, "In fst_tx_dsl_dma %p %p %d\n", skb, mem, len); + /* Copy from here */ + outl((unsigned long)skb, card->pci_conf + DMAPADR1); + /* to here */ + outl((unsigned long)mem, card->pci_conf + DMALADR1); + /* for this length */ + outl(len, card->pci_conf + DMASIZ1); + /* In this direction */ + outl(0x000000004, card->pci_conf + DMADPR1); + + /* We use the dma_done flag to know when the transfer is complete + */ - 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); + 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) { + fst_dbg(DBG_TX, + "Time to transfer txbuffer(%d) = %d microseconds\n", + len, i); + largest_tx_time = i; } - port->txpos = 0; - port->txipos = 0; - port->start = 0; - spin_unlock_irqrestore(&card->card_lock, flags); } -/* TE1 Alarm change interrupt event - */ +/* TE1 Alarm change interrupt event */ static void fst_intr_te1_alarm(struct fst_card_info *card, struct fst_port_info *port) { @@ -1075,16 +4413,17 @@ fst_intr_te1_alarm(struct fst_card_info u8 rra; u8 ais; - los = FST_RDB(card, suStatus.lossOfSignal); - rra = FST_RDB(card, suStatus.receiveRemoteAlarm); - ais = FST_RDB(card, suStatus.alarmIndicationSignal); + los = FST_RDB(card, su_status.loss_of_signal); + rra = FST_RDB(card, su_status.receive_remote_alarm); + ais = FST_RDB(card, su_status.alarm_indication_signal); if (los) { /* * Lost the link */ - if (netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "Net carrier off\n"); + if ((netif_carrier_ok(port_to_dev(port))) + && (!port->ignore_carrier)) { + fst_dbg(DBG_INTR, "Net carrier off\n"); netif_carrier_off(port_to_dev(port)); } } else { @@ -1092,24 +4431,76 @@ fst_intr_te1_alarm(struct fst_card_info * Link available */ if (!netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "Net carrier on\n"); + fst_dbg(DBG_INTR, "Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); } } if (los) - dbg(DBG_INTR, "Assert LOS Alarm\n"); + fst_dbg(DBG_INTR, "Assert LOS Alarm\n"); else - dbg(DBG_INTR, "De-assert LOS Alarm\n"); + fst_dbg(DBG_INTR, "De-assert LOS Alarm\n"); if (rra) - dbg(DBG_INTR, "Assert RRA Alarm\n"); + fst_dbg(DBG_INTR, "Assert RRA Alarm\n"); else - dbg(DBG_INTR, "De-assert RRA Alarm\n"); + fst_dbg(DBG_INTR, "De-assert RRA Alarm\n"); if (ais) - dbg(DBG_INTR, "Assert AIS Alarm\n"); + fst_dbg(DBG_INTR, "Assert AIS Alarm\n"); else - dbg(DBG_INTR, "De-assert AIS Alarm\n"); + fst_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, dsl_status.activation_status); + if (port->activation_status == port->last_act_status) { + fst_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))) { + pr_info("%s SHDSL sync complete OK\n", + port_to_dev(port)->name); + fst_dbg(DBG_INTR, "Net carrier on\n"); + netif_carrier_on(port_to_dev(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))) { + pr_info("%s SHDSL sync lost %d\n", + port_to_dev(port)->name, + port->activation_status); + fst_dbg(DBG_INTR, "Net carrier off\n"); + netif_carrier_off(port_to_dev(port)); + } + } + + /* One more possibility for loopback testing */ + if ((port->activation_status >= 5) && + (FST_RDB(card, dsl_config.test_mode))) { + pr_info("SHDSL sync in TestMode\n"); + fst_dbg(DBG_INTR, "Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); + } } /* Control signal change interrupt event @@ -1121,15 +4512,19 @@ fst_intr_ctlchg(struct fst_card_info *ca 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"); + if (signals & + (((FST_RDW(card, port_config[port->index].line_interface) == X21) + || (FST_RDW(card, port_config[port->index].line_interface) == X21D)) + ? IPSTS_INDICATE : IPSTS_DCD)) { + if ((!netif_carrier_ok(port_to_dev(port))) + && (!port->ignore_carrier)) { + fst_dbg(DBG_INTR, "%s: DCD active\n", port->dev->name); netif_carrier_on(port_to_dev(port)); } } else { - if (netif_carrier_ok(port_to_dev(port))) { - dbg(DBG_INTR, "DCD lost\n"); + if ((netif_carrier_ok(port_to_dev(port))) + && (!port->ignore_carrier)) { + fst_dbg(DBG_INTR, "%s: DCD lost\n", port->dev->name); netif_carrier_off(port_to_dev(port)); } } @@ -1141,31 +4536,34 @@ 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); - /* * Increment the appropriate error counter */ - dev->stats.rx_errors++; + port_to_stats(port, rx_errors)++; if (dmabits & RX_OFLO) { - dev->stats.rx_fifo_errors++; - dbg(DBG_ASS, "Rx fifo error on card %d port %d buffer %d\n", - card->card_no, port->index, rxp); + port_to_stats(port, rx_fifo_errors)++; + printk_ratelimited("Rx fifo (%x) error on card %d port %d buffer %d len %d\n", + dmabits, card->card_no, port->index, rxp, len); } if (dmabits & RX_CRC) { - dev->stats.rx_crc_errors++; - dbg(DBG_ASS, "Rx crc error on card %d port %d\n", - card->card_no, port->index); + port_to_stats(port, rx_crc_errors)++; + printk_ratelimited("Rx crc error on card %d port %d buffer %d\n", + card->card_no, port->index, rxp); } if (dmabits & RX_FRAM) { - dev->stats.rx_frame_errors++; - dbg(DBG_ASS, "Rx frame error on card %d port %d\n", - card->card_no, port->index); + port_to_stats(port, rx_frame_errors)++; + printk_ratelimited("Rx frame error on card %d port %d buffer %d\n", + card->card_no, port->index, rxp); + } + if (dmabits & RX_HBUF) { + port_to_stats(port, rx_length_errors)++; + printk_ratelimited("Rx hardware error on card %d port %d buffer %d\n", + card->card_no, port->index, rxp); } if (dmabits == (RX_STP | RX_ENP)) { - dev->stats.rx_length_errors++; - dbg(DBG_ASS, "Rx length error (%d) on card %d port %d\n", - len, card->card_no, port->index); + port_to_stats(port, rx_length_errors)++; + printk_ratelimited("Rx length error (%d) on card %d port %d buffer %d\n", + len, card->card_no, port->index, rxp); } } @@ -1179,276 +4577,1061 @@ fst_recover_rx_error(struct fst_card_inf int pi; pi = port->index; - /* - * Discard buffer descriptors until we see the start of the + /* + * Discard the skb if we have one + */ + if (port->rxq.frame != NULL) { + dev_kfree_skb(port->rxq.frame); + fst_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. + * 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); - rxp = (rxp+1) % NUM_RX_BUFFER; - if (++i > NUM_RX_BUFFER) { - dbg(DBG_ASS, "intr_rx: Discarding more bufs" - " than we have\n"); + while (dmabits & RX_ERR) { + dmabits = FST_RDB(card, rx_descr_ring[pi][rxp].bits); + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + rxp = 0; + if (++i > port->num_rx_buffers) { + fst_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); + dmabits = FST_RDB(card, rx_descr_ring[pi][rxp].bits); + fst_dbg(DBG_ASS, "DMA Bits of next buffer was %x\n", dmabits); } - dbg(DBG_ASS, "There were %d subsequent buffers in error\n", i); + fst_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); - rxp = (rxp+1) % NUM_RX_BUFFER; + FST_WRB(card, rx_descr_ring[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 +/* 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 = NET_RX_SUCCESS; + struct sk_buff *new_skb; + int saved_len; + + /* Receive a pdu through the rx fifo + */ + if (card->dmarx_in_progress) { + pr_err("Trying to start rx dma when already in progress\n"); + return; + } + len = atm_check_read_length(card); + if (len >= 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. + */ + port->rxq.frame = + dev_alloc_skb(FST_MAX_ATM_MTU + 53); + if (port->rxq.frame == NULL) { + fst_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; + fst_dbg(DBG_RX, + "Received PDU of %d bytes, %d cells\n", + port->rxq.count + 53, + port->rxq.segment_cnt + 1); + fst_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; + fst_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 (port->char_file) { + port->rxq. + frame->protocol = + htons + (ETH_P_WAN_PPP); + } else { + 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 { + new_skb->protocol + = + hdlc_type_trans + (new_skb, + new_skb->dev); + } + } + } + } + if ((port->char_file) + || (port->port_mode)) { + st_fst_tty_area *fst_tty; + fst_tty = + &fst_tty_area + [port->minor_dev_no]; + fst_tty->skb = new_skb; + schedule_work(& + (fst_tty->fst_tty_work)); + } else { + if (port->run) + rx_status = + netif_rx(new_skb); + else { + dev_kfree_skb(new_skb); + fst_dbg(DBG_ASS, + "inline: Discarding data, port closed\n"); + } + } + 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++; + fst_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 + */ + pr_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 */ + } +} + +/* Async Rx event interrupt + */ +static void +fst_intr_async_rx_event(struct fst_card_info *card, struct fst_port_info *port) +{ + char *event_buff = NULL; + int len; + static int largest_rx; + int i; + + fst_dbg(DBG_RX, "%s: fst_intr_async_rx_event\n", port->dev->name); + event_buff = kmalloc(MAX_RX_EVENT_FIFO_LEN, GFP_ATOMIC); + if (event_buff == NULL) { + fst_dbg(DBG_ASS, "%s: Cannot allocate memory for rx event\n", + port->dev->name); + return; + } + len = read_from_fifo(card->mem + + ASY_OFFSET(rx_event_fifos[port->index]), + FIFO_ASY_RXEVNT, + event_buff, MAX_RX_EVENT_FIFO_LEN); + if (len > largest_rx) { + fst_dbg(DBG_RX, "Largest read from asy rx event fifo is %d\n", + len); + largest_rx = len; + } + fst_dbg(DBG_RX, "%s: Read %d bytes from rx event fifo\n", + port->dev->name, len); + + /* On the current implementation the events are single bytes + * and are a bit map of events + */ + for (i = 0; i < len; i++) { + fst_dbg(DBG_ASS, "Event was %x\n", event_buff[i]); + if (event_buff[i] & ASYNC_STAT_OVR) { + port_to_stats(port, rx_over_errors)++; + pr_info("%s: Async - Overrun error\n", + port_to_dev(port)->name); + } + if (event_buff[i] & ASYNC_STAT_PAR) { + port_to_stats(port, rx_crc_errors)++; + pr_info("%s: Async - Parity error\n", + port_to_dev(port)->name); + } + if (event_buff[i] & ASYNC_STAT_FRM) { + port_to_stats(port, rx_frame_errors)++; + pr_info("%s: Async - Framing error\n", + port_to_dev(port)->name); + } + if (event_buff[i] & ASYNC_STAT_BRK) { + port_to_stats(port, rx_length_errors)++; + pr_info("%s: Async - Recieved Break\n", + port_to_dev(port)->name); + } + } + kfree(event_buff); +} + +/* Async Rx complete interrupt */ static void -fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) +fst_intr_async_rx(struct fst_card_info *card, struct fst_port_info *port) +{ + st_fst_tty_area *fst_tty; + char *rx_buff = NULL; + int len; + static int largest_rx; + + fst_dbg(DBG_RX, "%s: fst_intr_async_rx\n", port->dev->name); + rx_buff = kmalloc(FST_RX_DATA_FIFO_LEN, GFP_ATOMIC); + if (rx_buff == NULL) { + fst_dbg(DBG_ASS, "%s: Cannot allocate memory for rx buffer\n", + port->dev->name); + return; + } + card->dma_port_rx = port; + len = read_from_fifo(card->mem + ASY_OFFSET(rx_fifo[port->index]), + FIFO_ASY_RX, rx_buff, FST_RX_DATA_FIFO_LEN); + if (len > largest_rx) { + fst_dbg(DBG_RX, "Largest read from asy rx fifo is %d\n", len); + largest_rx = len; + } + fst_dbg(DBG_RX, "%s: Read %d bytes from rx fifo\n", + port->dev->name, len); + fst_tty = &fst_tty_area[port->minor_dev_no]; + fst_tty->len = len; + fst_tty->data = rx_buff; + schedule_work(&(fst_tty->fst_async_tty_work)); +} + +/* Rx complete interrupt normal cards + */ +static void fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) { unsigned char dmabits; int pi; int rxp; - int rx_status; - unsigned short len; - struct sk_buff *skb; - struct net_device *dev = port_to_dev(port); + int rx_status = NET_RX_SUCCESS; + unsigned short len = 0; /* Check we have a buffer to process */ pi = port->index; rxp = port->rxpos; - dmabits = FST_RDB(card, rxDescrRing[pi][rxp].bits); + dmabits = FST_RDB(card, rx_descr_ring[pi][rxp].bits); if (dmabits & DMA_OWN) { - dbg(DBG_RX | DBG_INTR, "intr_rx: No buffer port %d pos %d\n", - pi, rxp); + fst_dbg(DBG_RX | DBG_INTR, + "intr_rx: No buffer port %d pos %d\n", pi, rxp); return; } if (card->dmarx_in_progress) { return; } - /* Get buffer length */ - len = FST_RDW(card, rxDescrRing[pi][rxp].mcnt); - /* Discard the CRC */ - len -= 2; - if (len == 0) { + /* If a multi segment message, we need to assume the frame length + */ + if ((dmabits == 0) || (dmabits == RX_STP)) { /* - * This seems to happen on the TE1 interface sometimes - * so throw the frame away and log the event. + * start or middle frame, assume length is RX_LEN_BUFFER */ - pr_err("Frame received with 0 length. Card %d Port %d\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; + len = port->rx_buffer_size; + } else { + /* ... unless it is the last or only buffer + * In this case Get buffer length + */ + len = FST_RDW(card, rx_descr_ring[pi][rxp].mcnt); + if (port->mode == FST_MODE_HDLC) { + /* + * Is the CRC in this buffer or the previous one? + */ + if ((len % (port->rx_buffer_size)) == 2) { + /* + * Both crc bytes are here + * The decrement below will get rid of them + */ + } + if ((len % (port->rx_buffer_size)) == 1) { + /* We have 1 crc byte here and 1 as the + * last byte of the previous buffer. + * Decrement the overall length by 1 + */ + port->rxq.frame->len--; + port_to_stats(port, rx_bytes)--; + } + /* Discard the CRC */ + len -= 2; + } } - - /* 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 - */ - 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_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; } - /* Allocate SKB */ - if ((skb = dev_alloc_skb(len)) == NULL) { - dbg(DBG_RX, "intr_rx: can't allocate buffer\n"); + /* Are we in error recovery mode ? */ + if (port->rxq.error_recovery) { + if (dmabits & RX_STP) { + fst_dbg(DBG_ASS, "Error recovery complete\n"); + port->rxq.error_recovery = 0; + } else { + /* + * Still waiting for the next start of packet + * to complete error recovery + * So discard this descriptor + */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + } - dev->stats.rx_dropped++; + /* There were no errors. */ + fst_dbg(DBG_RX, "segments so far = %d\n", port->rxq.segment_cnt); + fst_dbg(DBG_RX, "Rx with no errors\n"); + if (port->mode == FST_MODE_TRANSPARENT) + dmabits |= RX_STP | RX_ENP; + if (dmabits & RX_STP) { + fst_dbg(DBG_RX, "Rx first segment len = %d\n", len); + /* First segment of a frame so allocate SKB */ + port->mtu_for_rx_skb = port_to_dev(port)->mtu; + port->rxq.frame = + dev_alloc_skb(port->mtu_for_rx_skb + MAX_PPP_HEADER); + if (port->rxq.frame == NULL) { + fst_dbg(DBG_ASS, + "intr_rx: can't allocate buffer of %d\n", + port->mtu_for_rx_skb + MAX_PPP_HEADER); + + port_to_stats(port, rx_dropped)++; + port->rxq.error_recovery++; + + /* Return descriptor to card */ + FST_WRB(card, rx_descr_ring[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; + } - /* Return descriptor to card */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + if (dmabits & RX_ENP) { + if (!(port->rxq.flags & RX_STP)) { + fst_dbg(DBG_ASS, "End frame without a start\n"); + /* Reset buffer descriptor */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + /* 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; + fst_dbg(DBG_RX, "Segment count up to %d\n", + last_segment_cnt); + } + port_to_stats(port, rx_packets)++; + if (port->rxq.segment_cnt > 1) { + int temp; + /* More than one segment so adjustment needed + */ + fst_dbg(DBG_RX, + "Adjusting length %d for %d segments\n", + len, port->rxq.segment_cnt); + temp = + len - + ((port->rxq.segment_cnt - + 1) * (port->rx_buffer_size)); + if (temp < 0) { + fst_dbg(DBG_TX, + "CRC split across segments %d!!!\n", + temp); + temp = 0; + } + len = temp; + } + fst_dbg(DBG_RX, "Rx last segment len = %d\n", len); + } - rxp = (rxp+1) % NUM_RX_BUFFER; - port->rxpos = rxp; + if (dmabits == 0) { + if (!(port->rxq.flags & RX_STP)) { + fst_dbg(DBG_ASS, "Middle frame without a start\n"); + /* Reset buffer descriptor */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + + fst_dbg(DBG_RX, "Rx Middle segment len = %d\n", len); + port->rxq.count += len; + port->rxq.segment_cnt++; + } + + /* Finally, some belt and braces checks */ + if (len == 0 && (port->rxq.segment_cnt == 1)) { + pr_err("Zero length hdlc frame received\n"); + port_to_stats(port, rx_length_errors)++; + /* Reset buffer descriptor */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + if (port->rxq.segment_cnt > FST_MAX_SEGMENTS) { + pr_err("Too many segments received %d\n", + port->rxq.segment_cnt); + /* Reset buffer descriptor */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; return; } - /* - * We know the length we need to receive, len. + if (port->rxq.frame == NULL) { + fst_dbg(DBG_ASS, + "Unexpected NULL skb. flags = %x dmabits = %x\n", + port->rxq.flags, dmabits); + /* Reset buffer descriptor */ + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; + return; + } + + /* 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 + * fst_min_dma_len */ - 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); + if ((len < fst_min_dma_len) || (card->family == FST_FAMILY_TXP)) { + int space_left; + space_left = (port->mtu_for_rx_skb + MAX_PPP_HEADER) - + port->rxq.frame->len; + if (len <= space_left) { + memcpy_fromio(skb_put(port->rxq.frame, len), + card->mem + BUF_OFFSET(rx_buffer[pi][0]) + + rxp * port->rx_buffer_size, len); + } else { + fst_dbg(DBG_ASS, + "This inline skb copy would have exceeded max mtu %d %d %d\n", + port->rxq.frame->len, len, + port->rxq.segment_cnt); + fst_dbg(DBG_ASS, "Copying %d bytes\n", space_left); + memcpy_fromio(skb_put(port->rxq.frame, space_left), + card->mem + BUF_OFFSET(rx_buffer[pi][0]) + + rxp*port->rx_buffer_size, space_left); + } /* Reset buffer descriptor */ - FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + FST_WRB(card, rx_descr_ring[pi][rxp].bits, DMA_OWN); /* Update stats */ - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; + fst_dbg(DBG_RX, "Updating stats by %d bytes\n", len); + port_to_stats(port, 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) - dev->stats.rx_dropped++; + if (port->rxq.flags & RX_ENP) { + struct sk_buff *new_skb; + fst_dbg(DBG_RX, "%s: Pushing frame up the stack\n", + port_to_dev(port)->name); + 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) { + pr_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 (port->char_file) { + port->rxq.frame->protocol = + htons(ETH_P_WAN_PPP); + } else { + 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 { + new_skb->protocol = + hdlc_type_trans + (new_skb, + new_skb->dev); + } + } + } + } + if ((port->char_file) || (port->port_mode)) { + st_fst_tty_area *fst_tty; + fst_tty = &fst_tty_area[port->minor_dev_no]; + fst_tty->skb = new_skb; + schedule_work(&(fst_tty->fst_tty_work)); + } else { + if (port->run) + rx_status = netif_rx(new_skb); + else { + dev_kfree_skb(new_skb); + fst_dbg(DBG_ASS, + "inline2: Discarding data, port closed\n"); + } + } + 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; + port->rxq.segment_cnt = 0; + } } else { - card->dma_skb_rx = skb; + 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, card->rx_dma_handle_card, - BUF_OFFSET(rxBuffer[pi][rxp][0]), len); + fst_rx_dma(card, (char *)card->rx_dma_handle_card, + (char *)BUF_OFFSET(rx_buffer[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); + fst_dbg(DBG_ASS, "About to increment rxpos by more than 1\n"); + fst_dbg(DBG_ASS, "rxp = %d rxpos = %d\n", rxp, port->rxpos); } - rxp = (rxp+1) % NUM_RX_BUFFER; - port->rxpos = rxp; + if (++rxp >= port->num_rx_buffers) + port->rxpos = 0; + else + port->rxpos = rxp; } -/* - * The bottom halfs to the ISR - * - */ +/* The bottom halfs to the ISR */ -static void -do_bottom_half_tx(struct fst_card_info *card) +static void do_bottom_half_dsl_tx(struct fst_card_info *card) { struct fst_port_info *port; - int pi; + int bytes_to_send = 0; int txq_length; struct sk_buff *skb; unsigned long flags; - struct net_device *dev; - /* - * Find a free buffer for the transmit - * Step through each port on this card + port = card->ports[0]; + fst_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, dsl_control.bytes_to_send); + if (bytes_to_send != 0) { + fst_dbg(DBG_ASS, + "do_bottom_half_dsl_tx: bytes_to_send 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; + } + /* 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) { + fst_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; + } - dbg(DBG_TX, "do_bottom_half_tx\n"); - for (pi = 0, port = card->ports; pi < card->nports; pi++, port++) { - if (!port->run) - continue; - - dev = port_to_dev(port); - 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 + /* We can go ahead with a transmission + * So get the next pdu off the tx queue + */ + spin_lock_irqsave(&card->card_lock, flags); + txq_length = port->txqe - port->txqs; + if (txq_length < 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 */ + 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, dsl_control.bytes_to_send, 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) { + fst_dbg(DBG_TX, + "Card %d Port %d: Tx queue Now empty\n", + card->card_no, port->index); + } + 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) { + fst_dbg(DBG_ASS, + "%s: Start the network layer\n", + port->dev->name); + netif_wake_queue(port_to_dev(port)); + port->start = 0; + } + } + dev_kfree_skb(skb); + } else { + /* 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); + fst_dbg(DBG_TX, + "No Tx: txqs is now %d txqe is now %d\n", + port->txqs, port->txqe); + return; + } + } +} + +static void do_bottom_half_tx(struct fst_card_info *card) +{ + struct fst_port_info *port; + int pi, pc; + int txq_length[FST_MAX_PORTS]; + int txq_selected = 0; + struct sk_buff *skb; + unsigned long flags; + int tx_length; + + /* Find a free buffer for the transmit + * Step through each port on this card + */ + + fst_dbg(DBG_TX, "do_bottom_half_tx\n"); + /* If DSL card we need to use the fifo */ + if (card->type == FST_TYPE_DSL_S1) { + do_bottom_half_dsl_tx(card); + return; + } + + /* Select a port for transmit */ + txq_selected = card->last_tx_port; + fst_dbg(DBG_TX, "Start tx algorithm for %d ports\n", card->nports); + for (pc = 0; pc < card->nports; pc++) { + pi = (txq_selected + pc) % card->nports; + port = card->ports[pi]; + fst_dbg(DBG_TX, "Trying port %d\n", pi); + if (port_to_dev(port) == NULL || !port->run) { + fst_dbg(DBG_TX, "%s is not running\n", + port_to_dev(port)->name); + continue; + } + fst_dbg(DBG_TX, "Port is running\n"); + while (! + (FST_RDB(card, tx_descr_ring[pi][port->txpos].bits) & + DMA_OWN) +&& !(card->dmatx_in_progress)) { 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[pi] = port->txqe - port->txqs; + if (txq_length[pi] < 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[pi] = txq_length[pi] + FST_TXQ_DEPTH; } spin_unlock_irqrestore(&card->card_lock, flags); - if (txq_length > 0) { - /* - * There is something to send + if (txq_length[pi] > 0) { + /* There is something to send */ + fst_dbg(DBG_TX, + "%s: There is something to send port %d len %d\n", + port_to_dev(port)->name, pi, + txq_length[pi]); + skb = port->txq[port->txqs].frame; + card->last_tx_port = pi + 1; + if (card->last_tx_port >= card->nports) + card->last_tx_port = 0; + + /* Decrement the segment count. It tells us + * how many more bits to the frame there are + * left to do */ - spin_lock_irqsave(&card->card_lock, flags); - skb = port->txq[port->txqs]; - port->txqs++; - if (port->txqs == FST_TXQ_DEPTH) { - port->txqs = 0; - } - spin_unlock_irqrestore(&card->card_lock, flags); - /* - * copy the data and set the required indicators on the - * card. + port->txq[port->txqs].segment_cnt--; + /* Is this the first, last or only segment of + * the frame */ - 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); - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - dev->trans_start = jiffies; + if (port->txq[port->txqs].segment_cnt == 0) { + if (port->txq[port->txqs].current_seg == + 0) { + fst_dbg(DBG_TX, + "Tx first and last\n"); + port->txq[port->txqs].flags = + DMA_OWN | TX_STP | TX_ENP; + tx_length = skb->len; + } else { + fst_dbg(DBG_TX, "Tx last\n"); + port->txq[port->txqs].flags = + DMA_OWN | TX_ENP; + tx_length = skb->len; + } } else { - /* Or do it through dma */ - memcpy(card->tx_dma_handle_host, - skb->data, skb->len); - card->dma_port_tx = port; - card->dma_len_tx = skb->len; - 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); + if (port->txq[port->txqs].current_seg == + 0) { + fst_dbg(DBG_TX, "Tx first\n"); + port->txq[port->txqs].flags = + DMA_OWN | TX_STP; + tx_length = + port->tx_buffer_size; + } else { + fst_dbg(DBG_TX, "Tx Middle\n"); + port->txq[port->txqs].flags = + DMA_OWN; + tx_length = + port->tx_buffer_size; + } } - if (++port->txpos >= NUM_TX_BUFFER) - port->txpos = 0; - /* - * If we have flow control on, can we now release it? + port->txq[port->txqs].current_seg++; + fst_dbg(DBG_TX, "Setting flags to %x\n", + port->txq[port->txqs].flags); + fst_dbg(DBG_TX, "Setting length to %d\n", + tx_length); + + if (port->mode == FST_MODE_ASYNC) { + char *fifo_ptr; + + fifo_ptr = port->card->mem + + ASY_OFFSET(tx_fifo[port->index]); + fst_dbg(DBG_TX, "%s: Sending %d bytes of async data %p\n", + port_to_dev(port)->name, tx_length, + (char *)fifo_ptr); + write_into_fifo(fifo_ptr, FIFO_ASY_TX, + skb->data, tx_length); + } else { + /* copy the data and set the required + * indicators on the card. + */ + FST_WRW(card, + tx_descr_ring[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(tx_buffer[pi][0]) + + port->txpos * + port->tx_buffer_size, + skb->data, + tx_length); + if (port->mode == + FST_MODE_TRANSPARENT) { + flags = flags & 0xfe; + } + FST_WRB(card, + tx_descr_ring[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; + fst_check_send(port); + } else { + /* Or do it through dma */ + memcpy(card->tx_dma_handle_host, + skb->data, tx_length); + card->dma_port_tx = port; + card->dma_len_tx = tx_length; + card->dma_txpos = port->txpos; + card->dma_tx_flags = + port->txq[port->txqs].flags; + fst_tx_dma(card, + (char *) + card->tx_dma_handle_card, + (char *) + BUF_OFFSET(tx_buffer + [pi][0]) + + port->txpos * + port->tx_buffer_size, + tx_length); + } + } + /* 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->start) { - if (txq_length < fst_txq_low) { - netif_wake_queue(port_to_dev - (port)); + wake_up_interruptible(&port->writeq); + if (port->txq[port->txqs].flags & TX_ENP) { + spin_lock_irqsave(&card->card_lock, + flags); + port->txq[port->txqs].frame = NULL; + port->txqs++; + if (port->txqs == FST_TXQ_DEPTH) + port->txqs = 0; + if (port->txqs == port->txqe) { + fst_dbg(DBG_TX, + "Card %d Port %d: Tx queue Now empty\n", + card->card_no, + port->index); + wake_up_interruptible + (&port->pollq); + } + spin_unlock_irqrestore(&card->card_lock, + flags); + /* If we have flow control on, can we + * now release it? + */ + if ((txq_length[pi] < fst_txq_low) + && (port->start)) { + fst_dbg(DBG_ASS, + "%s: Start the network layer\n", + port_to_dev + (port)->name); + if (!port->char_file) { + netif_wake_queue + (port_to_dev(port)); + wake_up_interruptible + (&port->pollq); + } else { + st_fst_tty_area + *fst_tty; + fst_tty = + &fst_tty_area + [port->minor_dev_no]; + if (fst_tty->tty) { + fst_dbg(DBG_TTY, + "%s: Waking up tty transmits\n", + fst_tty->name); + tty_wakeup + (fst_tty->tty); + } + } port->start = 0; } + dev_kfree_skb(skb); } - dev_kfree_skb(skb); } else { - /* - * Nothing to send so break out of the while loop + /* Nothing to send so break out of the while + * loop */ + fst_dbg(DBG_TX, "%s: Nothing to send\n", + port_to_dev(port)->name); break; } + if (++port->txpos >= port->num_tx_buffers) + port->txpos = 0; } } + if ((!card->dmatx_in_progress) && (!card->dmarx_in_progress)) + fst_enable_186_intr(card); } -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; /* 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) + fst_dbg(DBG_RX, "do_bottom_half_rx\n"); + for (pi = 0; pi < card->nports; pi++) { + port = card->ports[pi]; + if (port_to_dev(port) == NULL || !port->run) continue; - - while (!(FST_RDB(card, rxDescrRing[pi][port->rxpos].bits) + 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, rx_descr_ring[pi][port->rxpos].bits) & DMA_OWN) && !(card->dmarx_in_progress)) { if (rx_count > fst_max_reads) { /* @@ -1463,16 +5646,16 @@ do_bottom_half_rx(struct fst_card_info * rx_count++; } } + if ((!card->dmarx_in_progress) && (!card->dmatx_in_progress)) + fst_enable_186_intr(card); } -/* - * The interrupt service routine +/* The interrupt service routine * Dev_id is our fst_card_info pointer */ -static irqreturn_t -fst_intr(int dummy, void *dev_id) +irqreturn_t fst_intr(int irq, void *dev_id) { - struct fst_card_info *card = dev_id; + struct fst_card_info *card; struct fst_port_info *port; int rdidx; /* Event buffer indices */ int wridx; @@ -1480,107 +5663,159 @@ fst_intr(int dummy, void *dev_id) unsigned int dma_intcsr = 0; unsigned int do_card_interrupt; unsigned int int_retry_count; + int i; - /* - * Check to see if the interrupt was for this card + card = dev_id; + if (card == NULL) { + fst_dbg(DBG_INTR, "intr: spurious %d\n", irq); + fst_int_counter[card->card_no].int_no_work++; + return IRQ_NONE; + } + + /* Check to see if the interrupt was for this card * return if not * Note that the call to clear the interrupt is important */ - dbg(DBG_INTR, "intr: %d %p\n", card->irq, card); + fst_dbg(DBG_INTR, "intr: %d %p %d\n", irq, card, card->card_no); if (card->state != FST_RUNNING) { - pr_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_dbg(DBG_INTR, + "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_int_counter[card->card_no].int_no_work++; return IRQ_HANDLED; } /* Clear and reprime the interrupt source */ fst_clear_intr(card); - /* - * Is the interrupt for this card (handshake == 1) + /* Is the interrupt for this card (handshake == 1) */ do_card_interrupt = 0; if (FST_RDB(card, interruptHandshake) == 1) { + fst_disable_186_intr(card); do_card_interrupt += FST_CARD_INT; /* Set the software acknowledge */ FST_WRB(card, interruptHandshake, 0xEE); + fst_int_counter[card->card_no].int_186++; } + if (card->family == FST_FAMILY_TXU) { /* * Is it a DMA Interrupt */ dma_intcsr = inl(card->pci_conf + INTCSR_9054); if (dma_intcsr & 0x00200000) { - /* - * DMA Channel 0 (Rx transfer complete) + /* DMA Channel 0 (Rx transfer complete) */ - dbg(DBG_RX, "DMA Rx xfer complete\n"); + fst_int_counter[card->card_no].int_rxdma++; + fst_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); + /* If non DSL card, complete interrupt processing + */ + if ((card->type != FST_TYPE_DSL_S1) && + (card->dma_port_rx->mode != FST_MODE_ASYNC)) { + 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) { - /* - * DMA Channel 1 (Tx transfer complete) + /* DMA Channel 1 (Tx transfer complete) */ - dbg(DBG_TX, "DMA Tx xfer complete\n"); + fst_int_counter[card->card_no].int_txdma++; + fst_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); + /* If non DSL card, complete interruot processing + */ + if ((card->type != FST_TYPE_DSL_S1) && + (card->dma_port_tx->mode != FST_MODE_ASYNC)) { + 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; } } - /* - * Have we been missing Interrupts + /* Have we been missing Interrupts */ 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_dbg(DBG_ASS, "Card %d int_retry_count is %d\n", + card->card_no, int_retry_count); FST_WRL(card, interruptRetryCount, 0); } if (!do_card_interrupt) { + fst_int_counter[card->card_no].int_no_work++; return IRQ_HANDLED; } - - /* Scehdule the bottom half of the ISR */ - fst_q_work_item(&fst_work_intq, card->card_no); - tasklet_schedule(&fst_int_task); - + if ((!card->dmatx_in_progress) || + (!card->dmarx_in_progress) || + (do_card_interrupt & FST_CARD_INT)) { + /* Scehdule the bottom half of the ISR */ + fst_q_work_item(&fst_work_intq, card->card_no); + tasklet_schedule(&fst_int_task); + } /* Drain the event queue */ - rdidx = FST_RDB(card, interruptEvent.rdindex) & 0x1f; - wridx = FST_RDB(card, interruptEvent.wrindex) & 0x1f; + rdidx = FST_RDB(card, interrupt_event.rdindex) & 0x1f; + wridx = FST_RDB(card, interrupt_event.wrindex) & 0x1f; while (rdidx != wridx) { - event = FST_RDB(card, interruptEvent.evntbuff[rdidx]); - port = &card->ports[event & 0x03]; + event = FST_RDB(card, interrupt_event.evntbuff[rdidx]); + port = card->ports[event & 0x03]; - dbg(DBG_INTR, "Processing Interrupt event: %x\n", event); + fst_dbg(DBG_INTR, "Processing Interrupt event: %x\n", event); switch (event) { case TE1_ALMA: - dbg(DBG_INTR, "TE1 Alarm intr\n"); - if (port->run) + fst_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: + fst_dbg(DBG_INTR, + "%s: DSL Activation status change %d\n", + port_to_dev(port)->name, + FST_RDB(card, dsl_status.activation_status)); + 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) + if ((port->run) && (port_to_dev(port) != NULL)) fst_intr_ctlchg(card, port); break; @@ -1588,7 +5823,8 @@ fst_intr(int dummy, void *dev_id) case ABTB_SENT: case ABTC_SENT: case ABTD_SENT: - dbg(DBG_TX, "Abort complete port %d\n", port->index); + fst_dbg(DBG_TX, "Abort complete port %d\n", + port->index); break; case TXA_UNDF: @@ -1598,24 +5834,44 @@ fst_intr(int dummy, void *dev_id) /* 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_dev(port)->stats.tx_errors++; - port_to_dev(port)->stats.tx_fifo_errors++; - dbg(DBG_ASS, "Tx underflow on card %d port %d\n", - card->card_no, port->index); + port_to_stats(port, tx_errors)++; + port_to_stats(port, tx_window_errors)++; + printk_ratelimited("Tx underflow on card %d port %d\n", + card->card_no, port->index); + break; + + case TXA_TBUA: + case TXB_TBUA: + case TXC_TBUA: + case TXD_TBUA: + port_to_stats(port, tx_errors)++; + port_to_stats(port, tx_window_errors)++; + printk_ratelimited("Tx buffer unavailable on card %d port %d\n", + card->card_no, port->index); + break; + + case RXA_RBUA: + case RXB_RBUA: + case RXC_RBUA: + case RXD_RBUA: + port_to_stats(port, rx_errors)++; + port_to_stats(port, rx_over_errors)++; + printk_ratelimited("Rx buffer unavailable on card %d port %d\n", + card->card_no, port->index); break; case INIT_CPLT: - dbg(DBG_INIT, "Card init OK intr\n"); + fst_dbg(DBG_INIT, "Card init OK intr\n"); break; case INIT_FAIL: - dbg(DBG_INIT, "Card init FAILED intr\n"); + fst_dbg(DBG_INIT, "Card init FAILED intr\n"); card->state = FST_IFAILED; break; default: - pr_err("intr: unknown card event %d. ignored\n", event); + pr_err("intr: card %d: Unknown event = 0x%x\n", + card->card_no, event); break; } @@ -1623,52 +5879,214 @@ fst_intr(int dummy, void *dev_id) if (++rdidx >= MAX_CIRBUFF) rdidx = 0; } - FST_WRB(card, interruptEvent.rdindex, rdidx); - return IRQ_HANDLED; + FST_WRB(card, interrupt_event.rdindex, rdidx); + /* + * Fifo processing + */ + if (!card->fifo_complete) + check_fifo_resp(card); + check_cmdfifo_done(card); + check_rx_fifos(card); + check_tx_fifos(card); + check_rx_event_fifos(card); + for (i = 0; i < card->nports; i++) { + if (card->ports[i]->mode == FST_MODE_ASYNC) + check_tx_fifo_threshold(card->ports[i]); + } + return IRQ_HANDLED; } -/* 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 check_combination(int num, int size) { - int i; - - /* Check structure version and end marker */ - if (FST_RDW(card, smcVersion) != SMC_VERSION) { - pr_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) { - pr_err("Missing shared memory signature\n"); - card->state = FST_BADVERSION; - return; + /* 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: + 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 16: + { + if (size > 4 * 1024) + err++; + break; + } + case 32: + { + if (size > 2 * 1024) + err++; + break; + } + case 64: + { + if (size > 1 * 1024) + err++; + break; + } + case 128: + { + if (size > 1024 / 2) + err++; + break; + } + default: + { + pr_err("Number of buffers must be 1,2,4,8,16,32,64, "); + pr_err("or 128 : %d\n", num); + err++; + } } - /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ - if ((i = FST_RDB(card, taskStatus)) == 0x01) { - card->state = FST_RUNNING; - } else if (i == 0xFF) { - pr_err("Firmware initialisation failed. Card halted\n"); - card->state = FST_HALTED; - return; - } else if (i != 0x00) { - pr_err("Unknown firmware status 0x%x\n", i); - card->state = FST_HALTED; - return; + if (err) + return 1; + return 0; +} + +static int check_te1_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 32K + * 8 32K + * 16 16K + * 32 8K + * 64 4K + * 128 2K + */ + int err = 0; + switch (num) { + case 1: + case 2: + case 4: + case 8: + { + if (size > 32 * 1024) + err++; + break; + } + + case 16: + { + if (size > 16 * 1024) + err++; + break; + } + case 32: + { + if (size > 8 * 1024) + err++; + break; + } + case 64: + { + if (size > 4 * 1024) + err++; + break; + } + case 128: + { + if (size > 2 * 1024) + err++; + break; + } + default: + { + pr_err("Number of buffers must be 1,2,4,8,16,32,64, "); + pr_err("or 128 : %d\n", num); + err++; + } } + if (err) + return 1; + return 0; +} - /* 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. +static int +validate_buffer_config(struct fstioc_info *info, struct fst_card_info *card) +{ + /* We need some checks on buffer configurations because the + * card itself doesn't do any */ - if (FST_RDL(card, numberOfPorts) != card->nports) { - pr_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->num_tx_buffers == 0) || (info->num_rx_buffers == 0)) { + pr_err("Num Tx Buffers or Nun Rx Buffers is zero\n"); + return 1; + } + if ((info->num_tx_buffers > 128) || (info->num_rx_buffers > 128)) { + pr_err("Num Tx Buffers or Nun Rx Buffers > 128\n"); + return 1; + } + if ((info->tx_buffer_size == 0) || (info->rx_buffer_size == 0)) { + pr_err("Tx Buffer Size or Rx Buffer Size is zero\n"); + return 1; + } + if ((card->type == FST_TYPE_TE1) || (card->type == FST_TYPE_TE1e)) { + if ((info->tx_buffer_size > 4 * 32 * 1024) + || (info->rx_buffer_size > 4 * 32 * 1024)) { + pr_err + ("Tx Buffer Size or Rx Buffer Size > 32*1024\n"); + return 1; + } + if (check_te1_combination + (info->num_tx_buffers, info->tx_buffer_size)) { + pr_err + ("Invalid num/size combination on Tx Buffers\n"); + return 1; + } + if (check_te1_combination + (info->num_rx_buffers, info->rx_buffer_size)) { + pr_err + ("Invalid num/size combination on Rx Buffers\n"); + return 1; + } + } else { + if ((info->tx_buffer_size > 32 * 1024) + || (info->rx_buffer_size > 32 * 1024)) { + pr_err + ("Tx Buffer Size or Rx Buffer Size > 32*1024\n"); + return 1; + } + if (check_combination(info->num_tx_buffers, + info->tx_buffer_size)) { + pr_err + ("Invalid num/size combination on Tx Buffers\n"); + return 1; + } + if (check_combination(info->num_rx_buffers, + info->rx_buffer_size)) { + pr_err + ("Invalid num/size combination on Rx Buffers\n"); + return 1; + } } + return 0; } static int @@ -1676,34 +6094,183 @@ set_conf_from_info(struct fst_card_info 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 (info->proto == FST_RAW) - port->mode = FST_RAW; - else - port->mode = FST_GEN_HDLC; + if (info->proto == FST_RAW) { + struct ifreq ifr; + int cmd; + + cmd = SIOCWANDEV; + ifr.ifr_settings.type = IF_PROTO_PPP; + hdlc_ioctl(port_to_dev(port), &ifr, cmd); + port->proto = FST_RAW; + } else { + port->proto = FST_GEN_HDLC; + } } + if (info->transparent_mode == FST_MODE_TRANSPARENT) + FST_WRB(card, port_config[port->index].transparent_mode, 1); + else + FST_WRB(card, port_config[port->index].transparent_mode, 0); + port->mode = info->transparent_mode; + if (port->mode == FST_MODE_ASYNC) { + if (FST_RDB(card, async_ability[port->index])) { + fst_dbg(DBG_ASY, + "%s: Setting the async_mode field in shared memory\n", + port->dev->name); + FST_A_WRB(port->card, async_mode[port->index], 1); + } else { + fst_dbg(DBG_ASS, + "%s: Request to set async mode without async capability\n", + port_to_dev(port)->name); + return -ENOTTY /*EMEDIUMTYPE*/; + } + } else { + fst_dbg(DBG_ASY, + "%s: Clearing the async_mode field in shared memory\n", + port->dev->name); + FST_A_WRB(port->card, async_mode[port->index], 0); + } + if (info->valid & FSTVAL_CABLE) { + if (info->line_interface > RS485_FDX) { + pr_info("Can't set line interface to %d\n", + info->line_interface); + info->line_interface = 0; + } + port->hwif = info->line_interface; + if (port->hwif == UX35C) + info->line_interface = X21; + + if (port->hwif == RS530_449) { + if ((card->type == FST_TYPE_T1U) || + (card->type == FST_TYPE_T2U) || + (card->type == FST_TYPE_T2UE) || + (card->type == FST_TYPE_T4U) || + (card->type == FST_TYPE_T4UE)) + info->line_interface = X21; + } - if (info->valid & FSTVAL_CABLE) - err = -EINVAL; - - if (info->valid & FSTVAL_SPEED) - err = -EINVAL; - + FST_WRB(card, port_config[port->index].line_interface, + map_interface[info->line_interface]); + } + port->ignore_carrier = info->ignore_carrier; + FST_WRB(card, port_config[port->index].termination, info->termination); + if (info->valid & FSTVAL_CARD) { + if (port->fstioc_info_ver > FST_VERSION_OLD) { + fst_dbg(DBG_IOCTL, "Setting low latency to %d\n", + info->low_latency); + if (info->low_latency & LOW_LATENCY_RX) + FST_WRB(card, + port_config[port->index].immediate_ints, + info->low_latency); + port->low_latency = info->low_latency; + } + } + if ((info->valid & FSTVAL_SPEED || (info->valid & FSTVAL_T4E))) { + if (((card->type == FST_TYPE_T4E) || + (card->type == FST_TYPE_T4Ep) || + (card->type == FST_TYPE_T2U_PMC) || + (card->type == FST_TYPE_T2Ee) || + (card->type == FST_TYPE_T4Ee)) + && (info->extended_clocking)) { + /* T4E now has special clocking arrangements + */ + FST_WRB(card, + port_config[port->index].internal_clock, 0); + FST_WRB(card, + port_config[port->index].extended_clocking, + 1); + if (info->extended_clocking & 1) { + FST_WRB(card, + port_config[port->index]. + internal_tx_clock, 1); + fst_dbg(DBG_IOCTL, + "Port %d set internal_tx_clock\n", + port->index); + } else + FST_WRB(card, + port_config[port->index]. + internal_tx_clock, 0); + if (info->extended_clocking & 2) { + FST_WRB(card, + port_config[port->index]. + internal_rx_clock, 1); + fst_dbg(DBG_IOCTL, + "Port %d set internal_rx_clock\n", + port->index); + } else + FST_WRB(card, + port_config[port->index]. + internal_rx_clock, 0); + if (info->extended_clocking & 4) { + FST_WRB(card, + port_config[port->index]. + terminal_tx_clock, 1); + fst_dbg(DBG_IOCTL, + "Port %d set terminal_tx_clock\n", + port->index); + } else + FST_WRB(card, + port_config[port->index]. + terminal_tx_clock, 0); + if (info->extended_clocking & 8) { + FST_WRB(card, + port_config[port->index]. + terminal_rx_clock, 1); + fst_dbg(DBG_IOCTL, + "Port %d set terminal_rx_clock\n", + port->index); + } else + FST_WRB(card, + port_config[port->index]. + terminal_rx_clock, 0); + fst_dbg(DBG_IOCTL, "T4E Clocking is %x\n", + info->extended_clocking); + } else { + FST_WRB(card, port_config[port->index].internal_clock, + info->internal_clock); + FST_WRB(card, + port_config[port->index].extended_clocking, 0); + } + if (port->fstioc_info_ver > FST_VERSION_OLD) + FST_WRB(card, + port_config[port->index].enable_nrzi_clocking, + info->enable_nrzi_clocking); + FST_WRL(card, port_config[port->index].line_speed, + info->line_speed); + FST_WRB(card, port_config[port->index].transmit_msb_first, + info->transmit_msb_first); + FST_WRB(card, port_config[port->index].receive_msb_first, + info->receive_msb_first); + FST_WRB(card, port_config[port->index].tx_rx_start, + info->tx_rx_start); + } if (info->valid & FSTVAL_PHASE) - FST_WRB(card, portConfig[port->index].invertClock, - info->invertClock); + FST_WRB(card, port_config[port->index].invert_clock, + info->invert_clock); 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); + FST_WRW(card, card_mode, info->card_mode); + if (info->valid & FSTVAL_BUFFERS) { + if (validate_buffer_config(info, card)) + return -ENOSPC; + port->num_tx_buffers = info->num_tx_buffers; + port->num_rx_buffers = info->num_rx_buffers; + port->tx_buffer_size = info->tx_buffer_size; + port->rx_buffer_size = info->rx_buffer_size; + fst_dbg(DBG_IOCTL, "txb %d rxb %d txs %d rxs %d\n", + info->num_tx_buffers, info->num_rx_buffers, + info->tx_buffer_size, info->rx_buffer_size); + } + if ((info->valid & FSTVAL_TE1) || + (card->type == FST_TYPE_T2U_PMC) || + (card->type == FST_TYPE_T2Ee) || (card->type == FST_TYPE_T4Ee)) { + unsigned char encoding; + + FST_WRL(card, su_config.data_rate, info->line_speed); + FST_WRB(card, su_config.clocking, info->clock_source); my_framing = FRAMING_E1; if (info->framing == E1) my_framing = FRAMING_E1; @@ -1711,150 +6278,743 @@ 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); - if (info->idleCode) - FST_WRB(card, suConfig.enableIdleCode, 1); + FST_WRB(card, su_config.framing, my_framing); + FST_WRB(card, su_config.structure, info->structure); + FST_WRB(card, su_config.interface, info->interface); + FST_WRB(card, su_config.coding, info->coding); + + switch (info->coding) { + case CODING_NRZI: + encoding = FSCMN_ENCODING_NRZI; + break; + case CODING_FM0: + encoding = FSCMN_ENCODING_FM0; + break; + case CODING_FM1: + encoding = FSCMN_ENCODING_FM1; + break; + case CODING_MANCHESTER: + encoding = FSCMN_ENCODING_MANCHESTER; + break; + case CODING_DIFF_MANCHESTER: + encoding = FSCMN_ENCODING_DIFF_MANCHESTER; + break; + default: + encoding = FSCMN_ENCODING_NRZ; + } + FST_WRB(card, port_config[port->index].encoding, encoding); + FST_WRB(card, su_config.line_build_out, info->line_build_out); + FST_WRB(card, su_config.equalizer, info->equalizer); + FST_WRB(card, su_config.transparent_mode, + info->transparent_mode); + /* Note that we have hi-jacked this field to mean + * transparent, hdlc or async + */ + port->mode = info->transparent_mode; + FST_WRB(card, su_config.loop_mode, info->loop_mode); + FST_WRB(card, su_config.range, info->range); + FST_WRB(card, su_config.tx_buffer_mode, info->tx_buffer_mode); + FST_WRB(card, su_config.rx_buffer_mode, info->rx_buffer_mode); + FST_WRB(card, su_config.starting_slot, info->starting_slot); + FST_WRB(card, su_config.los_threshold, info->los_threshold); + if (info->idle_code) + FST_WRB(card, su_config.enable_idle_code, 1); else - FST_WRB(card, suConfig.enableIdleCode, 0); - FST_WRB(card, suConfig.idleCode, info->idleCode); -#if FST_DEBUG - if (info->valid & FSTVAL_TE1) { - printk("Setting TE1 data\n"); - printk("Line Speed = %d\n", info->lineSpeed); - printk("Start slot = %d\n", info->startingSlot); - printk("Clock source = %d\n", info->clockSource); - printk("Framing = %d\n", my_framing); - printk("Structure = %d\n", info->structure); - printk("interface = %d\n", info->interface); - 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("Loop mode = %d\n", info->loopMode); - printk("Range = %d\n", info->range); - printk("Tx Buffer mode = %d\n", info->txBufferMode); - printk("Rx Buffer mode = %d\n", info->rxBufferMode); - printk("LOS Threshold = %d\n", info->losThreshold); - printk("Idle Code = %d\n", info->idleCode); + FST_WRB(card, su_config.enable_idle_code, 0); + FST_WRB(card, su_config.idle_code, info->idle_code); + + if (card->type == FST_TYPE_TE1e) { + FST_WRB(card, su_config.ext_sync_clock_enable, + info->ext_sync_clock_enable); + FST_WRB(card, su_config.ext_sync_clock_offset, + info->ext_sync_clock_offset); + FST_WRL(card, su_config.ext_sync_clock_rate, + info->ext_sync_clock_rate); + FST_WRB(card, su_config.pps_enable, info->pps_enable); + FST_WRB(card, su_config.pps_offset, info->pps_offset); + } + if ((card->type == FST_TYPE_TE1) + || (card->type == FST_TYPE_TE1e)) { + fst_dbg(DBG_OPEN, "Setting TE1 data\n"); + fst_dbg(DBG_OPEN, "Line Speed = %d\n", + info->line_speed); + fst_dbg(DBG_OPEN, "Start slot = %d\n", + info->starting_slot); + fst_dbg(DBG_OPEN, "Clock source = %d\n", + info->clock_source); + fst_dbg(DBG_OPEN, "Framing = %d\n", my_framing); + fst_dbg(DBG_OPEN, "Structure = %d\n", info->structure); + fst_dbg(DBG_OPEN, "interface = %d\n", info->interface); + fst_dbg(DBG_OPEN, "Coding = %d\n", info->coding); + fst_dbg(DBG_OPEN, "Line build out = %d\n", + info->line_build_out); + fst_dbg(DBG_OPEN, "Equaliser = %d\n", info->equalizer); + fst_dbg(DBG_OPEN, "Transparent mode = %d\n", + info->transparent_mode); + fst_dbg(DBG_OPEN, "Loop mode = %d\n", info->loop_mode); + fst_dbg(DBG_OPEN, "Range = %d\n", info->range); + fst_dbg(DBG_OPEN, "Tx Buffer mode = %d\n", + info->tx_buffer_mode); + fst_dbg(DBG_OPEN, "Rx Buffer mode = %d\n", + info->rx_buffer_mode); + fst_dbg(DBG_OPEN, "LOS Threshold = %d\n", + info->los_threshold); + fst_dbg(DBG_OPEN, "Idle Code = %d\n", info->idle_code); + + if (card->type == FST_TYPE_TE1e) { + fst_dbg(DBG_OPEN, "Ext Sync Clock = %d\n", + info->ext_sync_clock_enable); + fst_dbg(DBG_OPEN, + "Ext Sync Clock d.c. offset = %d\n", + info->ext_sync_clock_offset); + fst_dbg(DBG_OPEN, "Ext Sync Clock rate = %d\n", + info->ext_sync_clock_rate); + fst_dbg(DBG_OPEN, "1PPS sync = %d\n", + info->pps_enable); + fst_dbg(DBG_OPEN, + "1PPS sync d.c. offset = %d\n", + info->pps_offset); + } + } + } + if (info->valid & FSTVAL_DSL_S1) { + if (port->proto != FST_GEN_HDLC) { + if (info->encap == ENCAP_MPOA) { + pr_err + ("Can Only have ATM encapsulation MPOA "); + pr_err("with HDLC proctocol %d\n", + port->proto); + err++; + return -EINVAL; + } + } + port->encap = info->encap; + FST_WRL(card, dsl_config.data_rate, info->line_speed); + FST_WRB(card, dsl_config.terminal_type, info->terminal_type); + FST_WRB(card, dsl_config.annex_type, info->annex_type); + FST_WRB(card, dsl_config.test_mode, info->test_mode); + FST_WRB(card, dsl_config.backoff, info->backoff); + FST_WRB(card, dsl_config.b_line_probing_enable, + info->b_line_probing_enable); + FST_WRB(card, dsl_config.snrth, info->snrth); + FST_WRB(card, dsl_config.lpath, info->lpath); + port->vpi = info->vpi; + port->vci = info->vci; + set_atm_header(port, info->vpi, info->vci); + if (card->type == FST_TYPE_DSL_S1) { + fst_dbg(DBG_OPEN, "Setting DSL-S1 Parameters\n"); + fst_dbg(DBG_OPEN, " Speed = %d\n", info->line_speed); + fst_dbg(DBG_OPEN, " Terminal Type = %d\n", + info->terminal_type); + fst_dbg(DBG_OPEN, " Annex Type = %d\n", + info->annex_type); + fst_dbg(DBG_OPEN, " Test Mode = %d\n", + info->test_mode); + fst_dbg(DBG_OPEN, " Backoff = %d\n", info->backoff); + fst_dbg(DBG_OPEN, " Snrth = %d\n", info->snrth); + fst_dbg(DBG_OPEN, " Lpath = %d\n", info->lpath); + fst_dbg(DBG_OPEN, " VPI set to %x\n", port->vpi); + fst_dbg(DBG_OPEN, " VCI set to %x\n", port->vci); } -#endif } #if FST_DEBUG if (info->valid & FSTVAL_DEBUG) { fst_debug_mask = info->debug; } -#endif +#endif + if ((info->valid & FSTVAL_ASYNC) && + (port->fstioc_info_ver > FST_VERSION_V1)) { + FST_A_WRB(port->card, async_config[port->index].flow_control, + info->async_conf.flow_control); + FST_A_WRB(port->card, async_config[port->index].xon_char, + info->async_conf.xon_char); + FST_A_WRB(port->card, async_config[port->index].xoff_char, + info->async_conf.xoff_char); + FST_A_WRB(port->card, async_config[port->index].word_length, + info->async_conf.word_length); + FST_A_WRB(port->card, async_config[port->index].stop_bits, + info->async_conf.stop_bits); + FST_A_WRB(port->card, async_config[port->index].parity, + info->async_conf.parity); + } + return err; +} + +static int +gather_conf_info(struct fst_card_info *card, struct fst_port_info *port, + struct fstioc_info *info) +{ + int i; + int j; + + /* We can zero the whole structure as info is always the current + * size. + */ + memset(info, 0, sizeof(struct fstioc_info)); + i = port->index; + info->kernel_version = 3.10; + info->nports = card->nports; + info->iocinfo_version = port->fstioc_info_ver; + info->type = card->type; + info->state = card->state; + info->proto = FST_GEN_HDLC; + info->index = i; +#if FST_DEBUG + info->debug = fst_debug_mask; +#endif + + /* 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) +#if FST_DEBUG + | FSTVAL_DEBUG +#endif + ; + if (port->hwif) + info->line_interface = port->hwif; + else + info->line_interface = port->hwif = + FST_RDW(card, port_config[i].line_interface); + info->internal_clock = FST_RDB(card, port_config[i].internal_clock); + info->extended_clocking = + FST_RDB(card, port_config[i].extended_clocking); + info->termination = FST_RDB(card, port_config[i].termination); + + if (info->extended_clocking) { + info->extended_clocking = 0x80; + if (FST_RDB(card, port_config[i].internal_tx_clock)) + info->extended_clocking += 0x01; + if (FST_RDB(card, port_config[i].internal_rx_clock)) + info->extended_clocking += 0x02; + if (FST_RDB(card, port_config[i].terminal_tx_clock)) + info->extended_clocking += 0x04; + if (FST_RDB(card, port_config[i].terminal_rx_clock)) + info->extended_clocking += 0x08; + fst_dbg(DBG_IOCTL, "Returning extended_clocking as %x\n", + info->extended_clocking); + } else + info->extended_clocking = 0; + info->line_speed = FST_RDL(card, port_config[i].line_speed); + info->est_line_speed = FST_RDL(card, + port_config[i].estimated_line_speed); + info->invert_clock = FST_RDB(card, port_config[i].invert_clock); + info->synth_ability = FST_RDB(card, synth_ability); + info->async_ability = FST_RDB(card, async_ability[i]); + info->transmit_msb_first = + FST_RDB(card, port_config[i].transmit_msb_first); + info->receive_msb_first = + FST_RDB(card, port_config[i].receive_msb_first); + info->tx_rx_start = FST_RDB(card, port_config[i].tx_rx_start); + info->v24IpSts = FST_RDL(card, v24IpSts[i]); + info->v24OpSts = FST_RDL(card, v24OpSts[i]); + info->clock_status = FST_RDW(card, clock_status[i]); + info->cable_status = FST_RDW(card, cable_status); + info->card_mode = FST_RDW(card, card_mode); + info->smc_firmware_version = FST_RDL(card, smc_firmware_version); + /* Return the reserved area of the shared memory window + */ + for (j = 0; j < 64; j++) + info->_reserved[j] = FST_RDW(card, _reserved[j]); + info->card_rev_major = ((info->_reserved[0] & 0xf000) >> 12); + info->card_rev_minor = ((info->_reserved[0] & 0x0f00) >> 8); + info->card_rev_build = info->_reserved[11] & 0xff; + info->ignore_carrier = port->ignore_carrier; + info->num_rx_buffers = port->num_rx_buffers; + info->num_tx_buffers = port->num_tx_buffers; + info->rx_buffer_size = port->rx_buffer_size; + info->tx_buffer_size = port->tx_buffer_size; + info->transparent_mode = port->mode; + info->low_latency = port->low_latency; + fst_dbg(DBG_IOCTL, "Returning low latency value of %d\n", + info->low_latency); + /* The T2U can report cable presence for both A or B + * in bits 0 and 1 of cable_status. See which port we are and + * do the mapping. + */ + if (card->family == FST_FAMILY_TXU) { + if (port->index == 0) { + /* Port A */ + info->cable_status = info->cable_status & 1; + } else { + /* Port B */ + info->cable_status = info->cable_status >> 1; + info->cable_status = info->cable_status & 1; + } + } + /* Some additional bits if we are TE1 or TE1e + */ + if ((card->type == FST_TYPE_TE1) || (card->type == FST_TYPE_TE1e)) { + info->line_speed = FST_RDL(card, su_config.data_rate); + info->clock_source = FST_RDB(card, su_config.clocking); + info->framing = FST_RDB(card, su_config.framing); + info->structure = FST_RDB(card, su_config.structure); + info->interface = FST_RDB(card, su_config.interface); + info->coding = FST_RDB(card, su_config.coding); + info->line_build_out = FST_RDB(card, su_config.line_build_out); + info->equalizer = FST_RDB(card, su_config.equalizer); + info->loop_mode = FST_RDB(card, su_config.loop_mode); + info->range = FST_RDB(card, su_config.range); + info->tx_buffer_mode = FST_RDB(card, su_config.tx_buffer_mode); + info->rx_buffer_mode = FST_RDB(card, su_config.rx_buffer_mode); + info->starting_slot = FST_RDB(card, su_config.starting_slot); + if (FST_RDB(card, su_config.enable_idle_code)) + info->idle_code = FST_RDB(card, su_config.idle_code); + else + info->idle_code = 0; + info->los_threshold = FST_RDB(card, su_config.los_threshold); + info->receive_buffer_delay = + FST_RDL(card, su_status.receive_buffer_delay); + info->framing_error_count = + FST_RDL(card, su_status.framing_error_count); + info->code_violation_count = + FST_RDL(card, su_status.code_violation_count); + info->crc_error_count = FST_RDL(card, + su_status.crc_error_count); + info->line_attenuation = FST_RDL(card, + su_status.line_attenuation); + info->loss_of_signal = FST_RDB(card, su_status.loss_of_signal); + info->receive_remote_alarm = + FST_RDB(card, su_status.receive_remote_alarm); + info->alarm_indication_signal = + FST_RDB(card, su_status.alarm_indication_signal); + + if (card->type == FST_TYPE_TE1e) { + info->ext_sync_clock_enable = + FST_RDB(card, su_config.ext_sync_clock_enable); + info->ext_sync_clock_offset = + FST_RDB(card, su_config.ext_sync_clock_offset); + info->ext_sync_clock_rate = + FST_RDL(card, su_config.ext_sync_clock_rate); + info->pps_enable = FST_RDB(card, su_config.pps_enable); + info->pps_offset = FST_RDB(card, su_config.pps_offset); + } + } + + if ((card->type == FST_TYPE_T4Ep) || + (card->type == FST_TYPE_T2U_PMC) || + (card->type == FST_TYPE_T2Ee) || (card->type == FST_TYPE_T4Ee)) { + unsigned char encoding; + + encoding = FST_RDB(card, port_config[port->index].encoding); + switch (encoding) { + case FSCMN_ENCODING_NRZI: + info->coding = CODING_NRZI; + break; + case FSCMN_ENCODING_FM0: + info->coding = CODING_FM0; + break; + case FSCMN_ENCODING_FM1: + info->coding = CODING_FM1; + break; + case FSCMN_ENCODING_MANCHESTER: + info->coding = CODING_MANCHESTER; + break; + case FSCMN_ENCODING_DIFF_MANCHESTER: + info->coding = CODING_DIFF_MANCHESTER; + break; + default: + info->coding = CODING_NRZ; + } + info->enable_nrzi_clocking = + FST_RDB(card, port_config[port->index].enable_nrzi_clocking); + + } + + if (card->type == FST_TYPE_DSL_S1) { + info->line_interface = SHDSL; + info->vpi = port->vpi; + info->vci = port->vci; + info->snrth = FST_RDB(card, dsl_config.snrth); + info->lpath = FST_RDB(card, dsl_config.lpath); + info->line_speed = FST_RDL(card, dsl_config.data_rate); + info->terminal_type = FST_RDB(card, dsl_config.terminal_type); + info->annex_type = FST_RDB(card, dsl_config.annex_type); + info->test_mode = FST_RDB(card, dsl_config.test_mode); + info->backoff = FST_RDB(card, dsl_config.backoff); + info->activation_status = + FST_RDB(card, dsl_status.activation_status); + info->no_common_mode_status = + FST_RDB(card, dsl_status.no_common_mode_status); + info->transceiverStatus1 = + FST_RDB(card, dsl_status.transceiverStatus1); + info->transceiverStatus2 = + FST_RDB(card, dsl_status.transceiverStatus2); + info->line_loss = FST_RDB(card, dsl_status.line_loss); + info->signal_quality = FST_RDB(card, + dsl_status.signal_quality); + info->near_end_block_error_count = + FST_RDB(card, dsl_status.near_end_block_error_count); + info->signal_to_noise_ratio = + FST_RDB(card, dsl_status.signal_to_noise_ratio); + info->code_violation_count = + FST_RDB(card, dsl_status.code_violation_count); + info->errored_second_count = + FST_RDB(card, dsl_status.errored_second_count); + info->severely_errored_second_count = + FST_RDB(card, dsl_status.severely_errored_second_count); + info->loss_of_sync_word_second_count = + FST_RDB(card, dsl_status.loss_of_sync_word_second_count); + info->unavailable_second_count = + FST_RDB(card, dsl_status.unavailable_second_count); + info->frequency_deviation = + FST_RDB(card, dsl_status.frequency_deviation); + info->negotiated_power_back_off = + FST_RDB(card, dsl_status.negotiated_power_back_off); + info->negotiated_psd = FST_RDB(card, + dsl_status.negotiated_psd); + info->negotiated_b_channels = + FST_RDB(card, dsl_status.negotiated_b_channels); + info->negotiated_z_bits = + FST_RDB(card, dsl_status.negotiated_z_bits); + info->negotiated_sync_word = + FST_RDW(card, dsl_status.negotiated_sync_word); + info->negotiated_stuff_bits = + FST_RDB(card, dsl_status.negotiated_stuff_bits); + info->chip_version = FST_RDB(card, dsl_status.chip_version); + info->firmware_version = + FST_RDB(card, dsl_status.firmware_version); + info->rom_version = FST_RDB(card, dsl_status.rom_version); + info->atm_tx_cell_count = FST_RDW(card, + dsl_status.atm_tx_cell_count); + info->atm_rx_cell_count = FST_RDW(card, + dsl_status.atm_rx_cell_count); + info->atm_hec_error_count = + FST_RDW(card, dsl_status.atm_hec_error_count); + info->atm_cells_dropped = port->atm_cells_dropped; + info->encap = port->encap; + info->xpld_version = FST_RDB(card, dsl_status.xpld_version); + info->farEndCountryCode[0] = + FST_RDB(card, dsl_status.farEndCountryCode[0]); + info->farEndCountryCode[1] = + FST_RDB(card, dsl_status.farEndCountryCode[1]); + info->farEndProviderCode[0] = + FST_RDB(card, dsl_status.farEndProviderCode[0]); + info->farEndProviderCode[1] = + FST_RDB(card, dsl_status.farEndProviderCode[1]); + info->farEndProviderCode[2] = + FST_RDB(card, dsl_status.farEndProviderCode[2]); + info->farEndProviderCode[3] = + FST_RDB(card, dsl_status.farEndProviderCode[3]); + info->farEndVendorInfo[0] = + FST_RDB(card, dsl_status.farEndVendorInfo[0]); + info->farEndVendorInfo[1] = + FST_RDB(card, dsl_status.farEndVendorInfo[1]); + info->utopia_atm_status = + FST_RDB(card, dsl_status.utopia_atm_status); + } + /* Update the stats + */ + memcpy(&info->stats, hdlc_stats(port->dev), + sizeof(struct fst_device_stats)); + /* Copy in the async config + */ + info->async_conf.flow_control = FST_A_RDB(port->card, + async_config[port->index].flow_control); + info->async_conf.stop_bits = + FST_A_RDB(port->card, async_config[port->index].stop_bits); + info->async_conf.parity = + FST_A_RDB(port->card, async_config[port->index].parity); + info->async_conf.word_length = + FST_A_RDB(port->card, async_config[port->index].word_length); + info->async_conf.xon_char = + FST_A_RDB(port->card, async_config[port->index].xon_char); + info->async_conf.xoff_char = + FST_A_RDB(port->card, async_config[port->index].xoff_char); + /* and now return the size of the structure according + * to the version set + */ + switch (port->fstioc_info_ver) { + case FST_VERSION_CURRENT: + return fstioc_info_sz_current; + + case FST_VERSION_V1: + return fstioc_info_sz_ver1; + + case FST_VERSION_V2: + return fstioc_info_sz_ver2; + + case FST_VERSION_V3: + return fstioc_info_sz_ver3; + + default: + return fstioc_info_sz_old; + } +} + +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 + */ + fst_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 purge_tx_queue(struct fst_card_info *card, + struct fst_port_info *port) +{ + int txq_length; + int i; + int start; + unsigned long flags; + + /* Purge all frames on the tx queue + */ + + fst_dbg(DBG_TX, "Purging all frame from the tx queue\n"); + /* Is there anything there? + */ + spin_lock_irqsave(&card->card_lock, flags); + start = port->txqs; + txq_length = port->txqe - port->txqs; + if (txq_length < 0) { + /* This is the case where the next free has wrapped but the + * last used hasn't + */ + txq_length = txq_length + FST_TXQ_DEPTH; + } + port->txqs = port->txqe; + spin_unlock_irqrestore(&card->card_lock, flags); + if (txq_length == 0) + return 0; /* Nothing there */ + /* de-queue the buffer's and deallocate the resources + */ + pr_info("%s: Purging %d frames from the transmit queue\n", + port_to_dev(port)->name, txq_length); + for (i = 0; i < txq_length; i++) { + if (port->txq[start].frame) { + fst_dbg(DBG_TX, "Purging buffer %d at %p\n", start, + port->txq[start].frame); + dev_kfree_skb(port->txq[start].frame); + port->txq[start].frame = NULL; + } else { + pr_info("txq purge found NULL pointer\n"); + } + start++; + if (start == FST_TXQ_DEPTH) + start = 0; + } + return 0; +} + +static int parse_custom_clock_rate_structure(struct fst_port_info *port, + struct fstioc_custom_rate_config + *custom_clock_rate) +{ + /* Check that the supplied buffer looks like it may contain a valid + * set of values + */ + if (custom_clock_rate->version != FST_CUSTOM_RATE_CONFIG_VERSION) { + pr_info("Custom clock rate structure is not the version "); + pr_info("expected %d %d\n", FST_CUSTOM_RATE_CONFIG_VERSION, + custom_clock_rate->version); + return 1; + } + if (!((custom_clock_rate->multiplier == + (FST_CUSTOM_CLOCK_MULTIPLIER_1)) + || (custom_clock_rate->multiplier == + (FST_CUSTOM_CLOCK_MULTIPLIER_16)))) { + pr_info("Custom clock rate multiplier is not expected value"); + pr_info(" (1 or 16) %d\n", + custom_clock_rate->multiplier); + return 2; + } + if (custom_clock_rate->clock_type > FST_CUSTOM_RATE_CLOCK_LOW_MASTER) { + pr_info("Custom clock type is not in the rage expected "); + pr_info("(0 - 2) %d\n", custom_clock_rate->clock_type); + return 3; + } + if (strlen(custom_clock_rate->rate_info) != + FST_CUSTOM_RATE_CONFIG_LENGTH - 1) { + pr_info("Custom clock rate info is not the length expected "); + pr_info("%d %d\n", (int)strlen(custom_clock_rate->rate_info), + FST_CUSTOM_RATE_CONFIG_LENGTH - 1); + return 4; + } + /* Validation passed + */ + return 0; +} - return err; +unsigned int to_int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + return -1; +} + +static int strtohex(unsigned char *source, unsigned char *dest, int size) +{ + int count = 0; + int loops = 0; + + loops = (int)strlen(source) / 2; + fst_dbg(DBG_IOCTL, "strtohex of length %d\n", loops); + for (count = 0; count < loops; count++) { + dest[count] = + 16 * to_int(source[2 * count]) + + to_int(source[2 * count + 1]); + } + dest[count] = 0xff; + return 0; } -static void -gather_conf_info(struct fst_card_info *card, struct fst_port_info *port, - struct fstioc_info *info) +static int +set_custom_clock_rate(struct fst_port_info *port, + struct fstioc_custom_rate_config *custom_clock_rate) { + struct fstioc_req sys_request; + char sz_rate_info[(FST_CUSTOM_RATE_CONFIG_LENGTH * 2) + 1]; + unsigned char buf[FST_CUSTOM_RATE_CONFIG_LENGTH]; + unsigned char checksum[16]; + struct crypto_hash *tfm; + struct hash_desc desc; + struct scatterlist sg; + char sx_checksum[3]; + char *psz_clock_type = NULL; int i; + int retval; + unsigned long flags; + int status; - 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; - info->proto = FST_GEN_HDLC; - info->index = i; -#if FST_DEBUG - info->debug = fst_debug_mask; -#endif +#define MD5_SUM_LENGTH 23 - /* Only mark information as valid if card is running. - * Copy the data anyway in case it is useful for diagnostics + /* Prepare the string and construct the usb command and send it */ - info->valid = ((card->state == FST_RUNNING) ? FSTVAL_ALL : FSTVAL_CARD) -#if FST_DEBUG - | FSTVAL_DEBUG -#endif - ; + memset(&sys_request, 0, sizeof(struct fstioc_req)); + memset(buf, 0x00, sizeof(buf)); + sys_request.msg_type = custom_clock_rate->multiplier; + sys_request.msg_len = sizeof(struct fstioc_req); + sys_request.i_reg_idx = port->index; + + fst_dbg(DBG_IOCTL, "Supplied rate = %d and rate info is %s\n", + custom_clock_rate->rate, custom_clock_rate->rate_info); + + switch (custom_clock_rate->clock_type) { + case FST_CUSTOM_RATE_CLOCK_LOW_SLAVE: + psz_clock_type = "0000"; + break; + case FST_CUSTOM_RATE_CLOCK_LOW_MASTER: + psz_clock_type = "0101"; + break; + case FST_CUSTOM_RATE_CLOCK_SLAVE: + default: + psz_clock_type = "0100"; + break; + } - 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); + sprintf(sz_rate_info, "%08X", ntohl(custom_clock_rate->rate)); + strcat(sz_rate_info, "0"); + strcat(sz_rate_info, custom_clock_rate->rate_info); + strcat(sz_rate_info, psz_clock_type); + fst_dbg(DBG_IOCTL, "Temp1 ascii sz_rate_info=%s\n", sz_rate_info); + fst_dbg(DBG_IOCTL, "Length of string to convert is %d\n", + (int)strlen(sz_rate_info)); + strtohex(sz_rate_info, buf, sizeof(buf) - 1); + fst_dbg(DBG_IOCTL, "Temp2 ascii sz_rate_info=%s\n", sz_rate_info); + fst_dbg(DBG_IOCTL, "Temp hex buf: "); + for (i = 0; i < sizeof(buf); i++) + fst_dbg(DBG_IOCTL, "%02x ", buf[i]); + fst_dbg(DBG_IOCTL, "\n"); - /* - * 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. + fst_dbg(DBG_IOCTL, "Temp3 ascii sz_rate_info=%s\n", sz_rate_info); + + /* Use the Kernel Crypto API to calculate the MD5Sum of the + * updated rate string */ - if (card->family == FST_FAMILY_TXU) { - if (port->index == 0) { - /* - * Port A - */ - info->cableStatus = info->cableStatus & 1; - } else { - /* - * Port B - */ - info->cableStatus = info->cableStatus >> 1; - info->cableStatus = info->cableStatus & 1; - } - } - /* - * Some additional bits if we are TE1 + tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); + desc.tfm = tfm; + desc.flags = 0; + crypto_hash_init(&desc); + sg_init_one(&sg, buf, MD5_SUM_LENGTH); + crypto_hash_update(&desc, &sg, MD5_SUM_LENGTH); + memset(checksum, 0, sizeof(checksum)); + crypto_hash_final(&desc, checksum); + fst_dbg(DBG_IOCTL, "Checksum: "); + for (i = 0; i < sizeof(checksum); i++) + fst_dbg(DBG_IOCTL, "%02x ", checksum[i]); + fst_dbg(DBG_IOCTL, "\n"); + + sprintf(sx_checksum, "%02X", checksum[0]); + strcat(sz_rate_info, sx_checksum); + fst_dbg(DBG_IOCTL, "Temp7 ascii sz_rate_info=%s\n", sz_rate_info); + strtohex(sz_rate_info, sys_request.u_msg, + FST_CUSTOM_RATE_CONFIG_LENGTH); + crypto_free_hash(tfm); + + /* Now we can send the request to the card + * Use a spin lock to ensure only one request at a time */ - 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); + fst_dbg(DBG_ASS, "Sending the card the request\n"); + fstioc_req_to_le(&sys_request); + spin_lock_irqsave(&port->card->fifo_lock, flags); + if (write_into_fifo + (port->card->mem + ASY_OFFSET(msg_fifo), FIFO_ASY_MSG, + (char *)&sys_request, sizeof(sys_request))) { + spin_unlock_irqrestore(&port->card->fifo_lock, flags); + fstioc_req_to_cpu(&sys_request); + fst_dbg(DBG_ASS, "Not enough room in fifo\n"); + return -ENOSPC; + } + spin_unlock_irqrestore(&port->card->fifo_lock, flags); + retval = 0; + /* We are always going to wait for a reply + */ + fst_dbg(DBG_ASS, "Waiting for a reply\n"); + port->card->fifo_complete = 0; + status = wait_event_interruptible_timeout(port->card->fifo_waitq, + (port->card->fifo_complete), + 500); + if (status == -ERESTARTSYS) + retval = -EINTR; + if (status == 0) { + pr_err("Timeout in processing SYSREQ %x\n", + sys_request.msg_type); + retval = -ETIME; + } + fst_dbg(DBG_IOCTL, "Reply received (%d) or interrupts (%x)\n", + port->card->fifo_complete, status); + /* Copy the buffer back and we are complete + */ + spin_lock_irqsave(&port->card->fifo_lock, flags); + read_from_fifo(port->card->mem + ASY_OFFSET(rsp_fifo), FIFO_ASY_RSP, + (char *)&sys_request, sys_request.msg_len); + spin_unlock_irqrestore(&port->card->fifo_lock, flags); + fstioc_req_to_cpu(&sys_request); + + if (sys_request.ret_code) { + fst_dbg(DBG_ASS, "Set Custom Clock Rate: Retval is %d\n", + sys_request.ret_code); + return retval; } + fst_dbg(DBG_IOCTL, "Returning from set_custom_clock_rate\n"); + return 0; } static int @@ -1864,12 +7024,10 @@ fst_set_iface(struct fst_card_info *card 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))) { + (&sync, ifr->ifr_settings.ifs_ifsu.sync, sizeof(sync))) { return -EFAULT; } @@ -1880,34 +7038,71 @@ fst_set_iface(struct fst_card_info *card switch (ifr->ifr_settings.type) { case IF_IFACE_V35: - FST_WRW(card, portConfig[i].lineInterface, V35); + FST_WRW(card, port_config[i].line_interface, V35); port->hwif = V35; break; case IF_IFACE_V24: - FST_WRW(card, portConfig[i].lineInterface, V24); + FST_WRW(card, port_config[i].line_interface, V24); port->hwif = V24; break; case IF_IFACE_X21: - FST_WRW(card, portConfig[i].lineInterface, X21); + FST_WRW(card, port_config[i].line_interface, X21); port->hwif = X21; break; case IF_IFACE_X21D: - FST_WRW(card, portConfig[i].lineInterface, X21D); + FST_WRW(card, port_config[i].line_interface, X21D); port->hwif = X21D; break; case IF_IFACE_T1: - FST_WRW(card, portConfig[i].lineInterface, T1); + FST_WRW(card, port_config[i].line_interface, T1); port->hwif = T1; break; case IF_IFACE_E1: - FST_WRW(card, portConfig[i].lineInterface, E1); + FST_WRW(card, port_config[i].line_interface, E1); port->hwif = E1; break; +#if 0 + case IF_IFACE_J1: + FST_WRW(card, port_config[i].line_interface, J1); + port->hwif = J1; + break; +#endif + case IF_IFACE_SHDSL: + FST_WRW(card, port_config[i].line_interface, SHDSL); + port->hwif = SHDSL; + break; + + case IF_IFACE_RS530_449: + if ((card->type == FST_TYPE_T1U) || + (card->type == FST_TYPE_T2U) || + (card->type == FST_TYPE_T2UE) || + (card->type == FST_TYPE_T4U) || + (card->type == FST_TYPE_T4UE)) + FST_WRW(card, port_config[i].line_interface, X21); + else + FST_WRW(card, port_config[i].line_interface, RS530_449); + port->hwif = RS530_449; + break; + + case IF_IFACE_RS485: + FST_WRW(card, port_config[i].line_interface, RS485); + port->hwif = RS485; + break; + + case IF_IFACE_RS485_FDX: + FST_WRW(card, port_config[i].line_interface, RS485_FDX); + port->hwif = RS485_FDX; + break; + + case IF_IFACE_UX35C: + FST_WRW(card, port_config[i].line_interface, X21); + port->hwif = UX35C; + break; case IF_IFACE_SYNC_SERIAL: break; @@ -1918,17 +7113,17 @@ fst_set_iface(struct fst_card_info *card switch (sync.clock_type) { case CLOCK_EXT: - FST_WRB(card, portConfig[i].internalClock, EXTCLK); + FST_WRB(card, port_config[i].internal_clock, EXTCLK); break; case CLOCK_INT: - FST_WRB(card, portConfig[i].internalClock, INTCLK); + FST_WRB(card, port_config[i].internal_clock, INTCLK); break; default: return -EINVAL; } - FST_WRL(card, portConfig[i].lineSpeed, sync.clock_rate); + FST_WRL(card, port_config[i].line_speed, sync.clock_rate); return 0; } @@ -1940,25 +7135,52 @@ fst_get_iface(struct fst_card_info *card 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) { - case E1: - ifr->ifr_settings.type = IF_IFACE_E1; - break; - case T1: - ifr->ifr_settings.type = IF_IFACE_T1; + case V24: + ifr->ifr_settings.type = IF_IFACE_V24; break; + case V35: ifr->ifr_settings.type = IF_IFACE_V35; break; - case V24: - ifr->ifr_settings.type = IF_IFACE_V24; - break; + case X21D: ifr->ifr_settings.type = IF_IFACE_X21D; break; + + case RS530_449: + ifr->ifr_settings.type = IF_IFACE_RS530_449; + break; + + case T1: + ifr->ifr_settings.type = IF_IFACE_T1; + break; + + case E1: + ifr->ifr_settings.type = IF_IFACE_E1; + break; +#if 0 + case J1: + ifr->ifr_settings.type = IF_IFACE_J1; + break; +#endif + case SHDSL: + ifr->ifr_settings.type = IF_IFACE_SHDSL; + break; + + case RS485: + ifr->ifr_settings.type = IF_IFACE_RS485; + break; + + case UX35C: + ifr->ifr_settings.type = IF_IFACE_UX35C; + break; + + case RS485_FDX: + ifr->ifr_settings.type = IF_IFACE_RS485_FDX; + break; + case X21: default: ifr->ifr_settings.type = IF_IFACE_X21; @@ -1967,37 +7189,49 @@ fst_get_iface(struct fst_card_info *card 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, port_config[i].line_speed); /* Lucky card and linux use same encoding here */ - sync.clock_type = FST_RDB(card, portConfig[i].internalClock) == + sync.clock_type = FST_RDB(card, port_config[i].internal_clock) == 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) +static int 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; - void *buf; - - dbg(DBG_IOCTL, "ioctl: %x, %p\n", cmd, ifr->ifr_data); + struct fstioc_status state; + struct fstioc_req sys_request; + struct fstioc_custom_rate_config custom_clock_rate; + int retval = 0; + int len; + int new_mode; + int info_size; + char fstioc_info_ver; + char readv_mode; + unsigned char signals; + struct fstioc_cmd my_cmd; + struct fstioc_char_data char_data; + struct fstioc_latency_data latency_data; + unsigned long copied = 0; + int status; + int speed; + fst_dbg(DBG_IOCTL, "ioctl: %x, %p\n", cmd, ifr->ifr_data); port = dev_to_port(dev); card = port->card; @@ -2024,27 +7258,27 @@ fst_ioctl(struct net_device *dev, struct return -EINVAL; } if (copy_from_user(&wrthdr, ifr->ifr_data, - sizeof (struct fstioc_write))) { + 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; } - /* Now copy the data to the card. */ - - buf = memdup_user(ifr->ifr_data + sizeof(struct fstioc_write), - wrthdr.size); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - memcpy_toio(card->mem + wrthdr.offset, buf, wrthdr.size); - kfree(buf); + /* Now copy the data to the card. + * 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)) { + return -EFAULT; + } /* Writes to the memory of a card in the reset state constitute * a download @@ -2054,6 +7288,19 @@ fst_ioctl(struct net_device *dev, struct } return 0; + case FSTGETSHELL: + + if (ifr->ifr_data == NULL) + return -EINVAL; + + info_size = gather_conf_info(card, port, &info); + + if (port->compat == 1) + memcpy(ifr->ifr_data, &info, info_size); + else if (copy_to_user(ifr->ifr_data, &info, info_size)) + return -EFAULT; + return 0; + case FSTGETCONF: /* If card has just been started check the shared memory config @@ -2064,41 +7311,192 @@ fst_ioctl(struct net_device *dev, struct /* 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 (ifr->ifr_data == NULL) { + if (card->state != FST_RUNNING) + return -ENODEV; + + if (ifr->ifr_data == NULL) return -EINVAL; + if (!access_ok(VERIFY_WRITE, ifr->ifr_data, sizeof(info))) { + fst_dbg(DBG_ASS, + "%s: fstgetconf: Can't access address %p\n", + dev->name, ifr->ifr_data); + return -EFAULT; + } + memset(&info, 0, sizeof(struct fstioc_info)); + info_size = gather_conf_info(card, port, &info); + if (port->compat == 1) { + memcpy(ifr->ifr_data, &info, info_size); + } else { + if (copy_to_user(ifr->ifr_data, &info, info_size)) + return -EFAULT; } + return 0; - gather_conf_info(card, port, &info); + case FSTSETCONF: - if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) { + if (card->state != FST_RUNNING) { + pr_err("Attempt to configure card %d ", + card->card_no); + pr_err("in non-running state (%d)\n", card->state); + return -EIO; + } + if (!access_ok(VERIFY_READ, ifr->ifr_data, sizeof(info))) { + fst_dbg(DBG_ASS, + "%s: fstsetconf: Can't access address %p\n", + dev->name, ifr->ifr_data); return -EFAULT; } + memset(&info, 0, sizeof(struct fstioc_info)); + if (port->fstioc_info_ver == FST_VERSION_CURRENT) { + if (port->compat == 1) { + retval = + __copy_from_user(&info, ifr->ifr_data, + sizeof(info)); + } else { + retval = copy_from_user(&info, ifr->ifr_data, + sizeof(info)); + } + } + if (retval) + 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; + fst_dbg(DBG_IOCTL, "%s: Setting Notify mode to %d\n", + port->dev->name, port->notify_mode); return 0; - case FSTSETCONF: + 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; + fst_dbg(DBG_IOCTL, + "Card %d port %d: Setting Monitor mode to %d\n", + card->card_no, port->index, port->monitor_mode); + return 0; - /* - * Most of the settings have been moved to the generic ioctls - * this just covers debug and board ident now + 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))) { + fst_dbg(DBG_ASS, + "Error in getting data, set port state\n"); + return -EFAULT; + } + fst_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 (card->state != FST_RUNNING) { - pr_err("Attempt to configure card %d in non-running state (%d)\n", - card->card_no, card->state); - return -EIO; + 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 + */ + state.txq_length = port->txqe - port->txqs; + if (state.txq_length < 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; + } + state.rxq_length = 0; + if (port->notify_mode == FST_NOTIFY_EXTENDED) { + /* Update the stats + */ + + memcpy(&state.stats, hdlc_stats(port->dev), + sizeof(struct fst_device_stats)); + len = sizeof(struct fstioc_status); + } else { + len = FST_NOTIFY_BASIC_SIZE; } - if (copy_from_user(&info, ifr->ifr_data, sizeof (info))) { + if (port->compat == 1) + memcpy(ifr->ifr_data, &state, len); + else if (copy_to_user(ifr->ifr_data, &state, len)) + return -EFAULT; + return 0; + + case FSTSYSREQ: + /* Pass the command through to the card. If the command + * generates a response, wait for it. The application + * suspends untill the command completes. + */ + fst_dbg(DBG_IOCTL, "FSTSYSREQ received\n"); + if (copy_from_user + (&sys_request, ifr->ifr_data, sizeof(sys_request))) { + fst_dbg(DBG_ASS, + "Could not copy sysrequest from user\n"); return -EFAULT; } + fst_dbg(DBG_IOCTL, "The request is as follows:\n"); + fst_dbg(DBG_IOCTL, "msg_type = %x\n", sys_request.msg_type); + /* Use a spin lock to ensure only one request at a time + */ + fst_dbg(DBG_IOCTL, "Sending the card the request\n"); + fstioc_req_to_le(&sys_request); + spin_lock_irqsave(&card->fifo_lock, flags); + if (write_into_fifo + (card->mem + ASY_OFFSET(msg_fifo), FIFO_ASY_MSG, + (char *)&sys_request, sizeof(sys_request))) { + spin_unlock_irqrestore(&card->fifo_lock, flags); + fstioc_req_to_cpu(&sys_request); + fst_dbg(DBG_IOCTL, "Not enough room in fifo\n"); + return -ENOSPC; + } + spin_unlock_irqrestore(&card->fifo_lock, flags); + retval = 0; + /* We Are always going to wait for a reply + */ - return set_conf_from_info(card, port, &info); + fst_dbg(DBG_IOCTL, "Waiting for a reply\n"); + card->fifo_complete = 0; + status = wait_event_interruptible_timeout(card->fifo_waitq, + (card->fifo_complete), + 500); + if (status == -ERESTARTSYS) + retval = -EINTR; + if (status == 0) { + pr_err("Timeout in processing SYSREQ %x\n", + sys_request.msg_type); + retval = -ETIME; + } + fst_dbg(DBG_IOCTL, "Reply received (%d) or interrupts (%x)\n", + card->fifo_complete, status); + /* Copy the buffer back and we are complete + */ + spin_lock_irqsave(&card->fifo_lock, flags); + read_from_fifo(card->mem + ASY_OFFSET(rsp_fifo), FIFO_ASY_RSP, + (char *)&sys_request, sys_request.msg_len); + spin_unlock_irqrestore(&card->fifo_lock, flags); + fstioc_req_to_cpu(&sys_request); + if (copy_to_user(ifr->ifr_data, &sys_request, + sizeof(sys_request))) { + fst_dbg(DBG_IOCTL, + "Error copying data back to user\n"); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, "Returning from FSTSYSREQ\n"); + return retval; case SIOCWANDEV: switch (ifr->ifr_settings.type) { @@ -2112,24 +7510,499 @@ fst_ioctl(struct net_device *dev, struct case IF_IFACE_X21D: case IF_IFACE_T1: case IF_IFACE_E1: + case IF_IFACE_SHDSL: + case IF_IFACE_RS530_449: + case IF_IFACE_RS485: + case IF_IFACE_RS485_FDX: + case IF_IFACE_UX35C: 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->proto == FST_RAW) { + ifr->ifr_settings.type = IF_PROTO_RAW; + return 0; + } + 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->proto = FST_GEN_HDLC; + fst_dbg(DBG_IOCTL, "Passing this type to hdlc %x\n", + ifr->ifr_settings.type); + port->hdlc_proto = ifr->ifr_settings.type; + return hdlc_ioctl(dev, ifr, cmd); + } + + case FSTCMD: + /* A general purpose command/response mechanism + * First, get it. + */ + if (port->compat == 1) + memcpy(&my_cmd, ifr->ifr_data, sizeof(my_cmd)); + else if (copy_from_user(&my_cmd, ifr->ifr_data, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: Could not access user buffers\n"); + return -EFAULT; + } + + if (my_cmd.version != 1) { + fst_dbg(DBG_ASS, "fstcmd: bad version %d\n", + my_cmd.version); + return -EINVAL; + } + switch (my_cmd.command) { + case FSTCMD_GET_SERIAL: + extract_serial(card); + + if (copy_to_user + (my_cmd.data_ptr, serial, strlen(serial))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy serial back\n"); + return -EFAULT; + } + my_cmd.output_data_len = strlen(serial); + /* And invert the status field to show we have + * understood and completed the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, "Returning serial number %s", + serial); + return 0; + + case FSTCMD_SET_V24O: + if (my_cmd.input_data_len < 1) { + fst_dbg(DBG_ASS, + "(%s) Cannot set V24 signals , no data provided %d\n", + port->dev->name, + my_cmd.input_data_len); + return -EFAULT; + } + if (copy_from_user(&signals, my_cmd.data_ptr, 1)) { + fst_dbg(DBG_ASS, + "fstcmd: could not set v24 signals\n"); + return -EFAULT; + } + + if (signals > + (OPSTS_RTS + OPSTS_DTR + OPSTS_DSRS + OPSTS_SS + + OPSTS_LL + OPSTS_DCD + OPSTS_RL)) { + fst_dbg(DBG_ASS, + "fstcmd: v24 signals not valid %d\n", + signals); + return -EFAULT; + } + FST_WRL(port->card, v24OpSts[port->index], signals); + fst_issue_cmd(port, SETV24O); + my_cmd.output_data_len = 0; + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + return 0; + + case FSTCMD_GET_VERSION: + if (copy_to_user + (my_cmd.data_ptr, &port->fstioc_info_ver, 1)) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy fstioc_info version back\n"); + return -EFAULT; + } + my_cmd.output_data_len = sizeof(fstioc_info_ver); + /* And invert the status field to show we have + * Understood and complete the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + return 0; + + case FSTCMD_SET_VERSION: + if (my_cmd.input_data_len > 0) { + if (copy_from_user(&fstioc_info_ver, + my_cmd.data_ptr, 1)) { + fst_dbg(DBG_ASS, + "fstcmd: could not get fstioc_info version\n"); + return -EFAULT; + } + if ((fstioc_info_ver < FST_VERSION_OLD) || + (fstioc_info_ver > FST_VERSION_CURRENT)) { + fst_dbg(DBG_ASS, + "fstcmd: setting an invalid fstioc_info version %d\n", + fstioc_info_ver); + return -EFAULT; + } + my_cmd.output_data_len = 0; + /* And invert the status field to show we + * have done the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, + sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + port->fstioc_info_ver = fstioc_info_ver; + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy fstioc_info version, data buffer too small %d %d\n", + port->dev->name, 1, + my_cmd.input_data_len); + return -EFAULT; + } + return 0; + + case FSTCMD_GET_INTS: + if (port->compat == 1) + memcpy(my_cmd.data_ptr, + &fst_int_counter[port->card->card_no], + sizeof(struct fst_ints)); + else + copied = copy_to_user(my_cmd.data_ptr, + &fst_int_counter + [port->card->card_no], + sizeof(struct fst_ints)); + if (copied) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy all the int stats back %lu\n", + copied); + return -EFAULT; + } + my_cmd.output_data_len = sizeof(struct fst_ints); + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + return 0; + + case FSTCMD_RESET_INTS: + memset(&fst_int_counter[port->card->card_no], 0, + sizeof(struct fst_ints)); + my_cmd.output_data_len = 0; + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user(ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + return 0; + + case FSTCMD_RESET_STATS: + memset(hdlc_stats(port->dev), 0, + sizeof(struct fst_device_stats)); + my_cmd.output_data_len = 0; + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user(ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not get char data\n"); + return -EFAULT; + } + return 0; + + case FSTCMD_SET_READV: + if (my_cmd.input_data_len > 0) { + if (copy_from_user(&readv_mode, + my_cmd.data_ptr, 1)) { + fst_dbg(DBG_ASS, + "fstcmd: could not get readv_mode\n"); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, + "Setting readv mode to %d\n", + readv_mode); + if ((readv_mode < FST_READV_NORMAL) || + (readv_mode > FST_READV_SYNC2)) { + fst_dbg(DBG_ASS, + "fstcmd: setting an invalid readv mode %d\n", + readv_mode); + return -EFAULT; + } + my_cmd.output_data_len = 0; + /* And invert the status field to show we + * have done the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, + sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + port->readv_mode = readv_mode; + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy readv_mode, data buffer too small %d %d\n", + port->dev->name, 1, + my_cmd.input_data_len); + return -EFAULT; + } + return 0; + + case FSTCMD_SET_CHAR: + if (my_cmd.input_data_len <= + sizeof(struct fstioc_char_data)) { + if (copy_from_user + (&char_data, my_cmd.data_ptr, + sizeof(struct fstioc_char_data))) { + fst_dbg(DBG_ASS, + "fstcmd: could not get char data\n"); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, + "Setting char data to %d %d\n", + char_data.queue_len, + char_data.threshold); + port->char_inq_max_len = char_data.queue_len; + port->char_inq_threshold = char_data.threshold; + my_cmd.output_data_len = 0; + /* And invert the status field to show we + * have done the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, + sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy char_data, data buffer too small %d %d\n", + port->dev->name, 1, + my_cmd.input_data_len); + return -EFAULT; + } + return 0; + + case FSTCMD_SET_LATENCY: + if (my_cmd.input_data_len >= + sizeof(struct fstioc_latency_data)) { + if (copy_from_user + (&latency_data, my_cmd.data_ptr, + sizeof(struct fstioc_latency_data))) { + fst_dbg(DBG_ASS, + "fstcmd: could not get latency data\n"); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, + "Setting latency data to %d %d %d\n", + latency_data.tx_size, + latency_data.rx_size, + latency_data.rate); + if (latency_data.rx_size != 0) { + fst_dbg(DBG_ASS, + "fstcmd: rx latency not yet supported\n"); + return -EINVAL; + + } + if (latency_data.rate == 0) { + fst_dbg(DBG_ASS, + "fstcmd: latency rate must be greater than zero\n"); + return -EINVAL; + + } + /* Save the values for when the port is openend + * Which is when we will calculate the size + * of the required buffer. + */ + port->tx_latency = latency_data.tx_size; + port->rx_latency = latency_data.rx_size; + port->latency_rate = latency_data.rate; + port->latency_reached = 0; + port->rx_latency_buffer = NULL; + port->tx_latency_buffer = NULL; + my_cmd.output_data_len = 0; + /* And invert the status field to show we + * have done the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, + sizeof(my_cmd)); + else if (copy_to_user + (ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy char_data, data buffer too small %d %d\n", + port->dev->name, 1, + my_cmd.input_data_len); + return -EFAULT; + } + return 0; + + case FSTCMD_UPDATE_CLOCK: + if (my_cmd.input_data_len > 0) { + if (copy_from_user(&speed, + my_cmd.data_ptr, + sizeof(int))) { + fst_dbg(DBG_ASS, + "fstcmd: could not get speed setting\n"); + return -EFAULT; + } + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy speed setting, data buffer too small %d %d\n", + port->dev->name, 1, + my_cmd.input_data_len); + return -EFAULT; + } + fst_dbg(DBG_IOCTL, "%s: Update clock speed to %d\n", + port->dev->name, speed); + FST_WRL(port->card, + port_config[port->index].line_speed, + speed); + fst_issue_cmd(port, RECONFIGLINE); + my_cmd.output_data_len = 0; + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user(ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } return 0; - case IF_GET_PROTO: - if (port->mode == FST_RAW) { - ifr->ifr_settings.type = IF_PROTO_RAW; - return 0; + case FSTCMD_SET_CUSTOM_RATE: + if (!FST_RDB(card, synth_ability)) { + fst_dbg(DBG_ASS, + "%s: custom clock rates not supported on this port\n", + port->dev->name); + return -EIO; } - return hdlc_ioctl(dev, ifr, cmd); + if (my_cmd.input_data_len >= + (unsigned int)sizeof(struct + fstioc_custom_rate_config)) { + if (copy_from_user(&custom_clock_rate, + my_cmd.data_ptr, + sizeof(struct fstioc_custom_rate_config))) { + fst_dbg(DBG_ASS, + "fstcmd: could not get custom rate config structure\n"); + return -EFAULT; + } + } else { + fst_dbg(DBG_ASS, + "(%s) Cannot copy custom_rate_config, data buffer too small %lu %u\n", + port->dev->name, + sizeof(struct fstioc_custom_rate_config), + my_cmd.input_data_len); + + return -EFAULT; + } + retval = + parse_custom_clock_rate_structure(port, + &custom_clock_rate); + if (retval) { + fst_dbg(DBG_ASS, + "%s: Invalid custom_rate_config struct, %d\n", + port->dev->name, retval); + return -EINVAL; + } + retval = + set_custom_clock_rate(port, &custom_clock_rate); + if (retval) { + fst_dbg(DBG_ASS, + "%s: Error in setting custom clock rate, %d\n", + port->dev->name, retval); + return -EINVAL; + } + /* Everything worked + */ + my_cmd.output_data_len = 0; + /* And invert the status field to show we have done + * the command + */ + my_cmd.status = ~my_cmd.status; + if (port->compat == 1) + memcpy(ifr->ifr_data, &my_cmd, sizeof(my_cmd)); + else if (copy_to_user(ifr->ifr_data, &my_cmd, + sizeof(my_cmd))) { + fst_dbg(DBG_ASS, + "fstcmd: could not copy status back\n"); + return -EFAULT; + } + return 0; default: - port->mode = FST_GEN_HDLC; - dbg(DBG_IOCTL, "Passing this type to hdlc %x\n", - ifr->ifr_settings.type); - return hdlc_ioctl(dev, ifr, cmd); + fst_dbg(DBG_ASS, "Unrecognised FST Command %d\n", + my_cmd.command); + return -EINVAL; } default: @@ -2138,8 +8011,7 @@ fst_ioctl(struct net_device *dev, struct } } -static void -fst_openport(struct fst_port_info *port) +static void fst_openport(struct fst_port_info *port) { int signals; int txq_length; @@ -2147,290 +8019,540 @@ fst_openport(struct fst_port_info *port) /* Only init things if card is actually running. This allows open to * succeed for downloads etc. */ + fst_dbg(DBG_OPEN, "Opening port %s\n", port->dev->name); + fst_dbg(DBG_OPEN, "Tx buffers = %d of size %d\n", port->num_tx_buffers, + port->tx_buffer_size); + fst_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, dsl_control.bytes_to_send, 0); + FST_WRL(port->card, dsl_control.offset_atm_rx, DR_BASE); + FST_WRL(port->card, dsl_control.offset_atm_tx, 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); - port->run = 0; + /* This is probably the case where the open is as a + * result of the network device. We shouldn't need + * to do it again + */ + fst_dbg(DBG_OPEN, + "open: found port already running\n"); + } else { + fst_rx_config(port); + fst_tx_config(port); + port->run = 1; + 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); + if (!port->ignore_carrier) { + signals = + FST_RDL(port->card, + v24DebouncedSts[port->index]); + if (signals & + (((FST_RDW + (port->card, + port_config[port-> + index].line_interface) == + X21) + || + (FST_RDW + (port->card, + port_config[port-> + index].line_interface) == + X21D)) + ? IPSTS_INDICATE : IPSTS_DCD)) + netif_carrier_on(port->dev); + else + netif_carrier_off(port->dev); + } + 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; } - - fst_rx_config(port); - fst_tx_config(port); - fst_op_raise(port, OPSTS_RTS | OPSTS_DTR); - - 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)); - else - netif_carrier_off(port_to_dev(port)); - - txq_length = port->txqe - port->txqs; - port->txqe = 0; - port->txqs = 0; } - } -static void -fst_closeport(struct fst_port_info *port) +static void fst_closeport(struct fst_port_info *port) { if (port->card->state == FST_RUNNING) { if (port->run) { - port->run = 0; fst_op_lower(port, OPSTS_RTS | OPSTS_DTR); fst_issue_cmd(port, STOPPORT); + port->run = 0; + if (port->mode == FST_MODE_ASYNC) { + fst_reset_rx_fifo(port); + fifo_reset(port->card, port->index); + } } else { - dbg(DBG_OPEN, "close: port not running\n"); + fst_dbg(DBG_OPEN, "close: port not running\n"); } + purge_tx_queue(port->card, port); } } -static int -fst_open(struct net_device *dev) +static int 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) { + fst_dbg(DBG_ASS, + "%s: Cannot open port if card is not loaded\n", + dev->name); + return -ENODEV; + } + if ((port->run) && (port->char_file)) { + if (!port->monitor_mode) { + fst_dbg(DBG_ASS, "%s: Port is already open\n", + dev->name); + return -EBUSY; + } else { + fst_dbg(DBG_ASS, + "%s: Opening network port to monitor char interface\n", + dev->name); + } + } + if (!try_module_get(THIS_MODULE)) - return -EBUSY; + return -EBUSY; - if (port->mode != FST_RAW) { + if (port->proto != FST_RAW) { err = hdlc_open(dev); - if (err) { - module_put(THIS_MODULE); + if (err) return err; - } } - - fst_openport(port); + if (!port->char_file) + fst_openport(port); netif_wake_queue(dev); return 0; } -static int -fst_close(struct net_device *dev) +static int fst_close(struct net_device *dev) { struct fst_port_info *port; struct fst_card_info *card; unsigned char tx_dma_done; unsigned char rx_dma_done; + unsigned long flags; port = dev_to_port(dev); card = port->card; + netif_stop_queue(dev); + spin_lock_irqsave(&card->card_lock, flags); + if (!port->char_file) { + while (port->txqe != port->txqs) { + fst_dbg(DBG_OPEN, + "%s: Closing port with data in tx queue\n", + dev->name); + dev_kfree_skb(port->txq[port->txqs].frame); + port->txq[port->txqs].frame = NULL; + port->txqs++; + if (port->txqs == FST_TXQ_DEPTH) + port->txqs = 0; + } + } + spin_unlock_irqrestore(&card->card_lock, flags); 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); + fst_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); + + if (!port->char_file) { + /* Only close the port if really not in use */ + 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 + /*Setting currently fixed in FarSync card so we check and forget */ if (encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT) return -EINVAL; return 0; } -static void -fst_tx_timeout(struct net_device *dev) +static void 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); card = port->card; - dev->stats.tx_errors++; - dev->stats.tx_aborted_errors++; - dbg(DBG_ASS, "Tx timeout card %d port %d\n", - card->card_no, port->index); - fst_issue_cmd(port, ABORTTX); - + stats->tx_errors++; + stats->tx_aborted_errors++; + fst_dbg(DBG_ASS, "Tx timeout card %d port %d\n", + card->card_no, port->index); + purge_tx_queue(card, port); dev->trans_start = jiffies; netif_wake_queue(dev); port->start = 0; } -static netdev_tx_t -fst_start_xmit(struct sk_buff *skb, struct net_device *dev) +static int 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; unsigned long flags; int txq_length; + struct sk_buff *new_skb; + int count = 0; port = dev_to_port(dev); card = port->card; - dbg(DBG_TX, "fst_start_xmit: length = %d\n", skb->len); + fst_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)) { + if ((!netif_carrier_ok(dev)) && (!port->ignore_carrier)) { dev_kfree_skb(skb); - dev->stats.tx_errors++; - dev->stats.tx_carrier_errors++; - dbg(DBG_ASS, - "Tried to transmit but no carrier on card %d port %d\n", - card->card_no, port->index); - return NETDEV_TX_OK; + stats->tx_errors++; + stats->tx_carrier_errors++; + fst_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); + if (skb->len > FST_MAX_MTU) { + pr_err("Tx Packet too large %d vs %d\n", skb->len, + FST_MAX_MTU); dev_kfree_skb(skb); - dev->stats.tx_errors++; - return NETDEV_TX_OK; + stats->tx_errors++; + stats->tx_dropped++; + return 0; } - /* - * We are always going to queue the packet + /* Drop it if it's bigger than the tx_buffer_size */ + if (skb->len > port->tx_buffer_size) { + pr_err("Tx Packet too large %d vs buffer %d\n", skb->len, + port->tx_buffer_size); + dev_kfree_skb(skb); + port->stats.tx_errors++; + port->stats.tx_dropped++; + return 0; + } + + /* Drop it if port not running */ + if (!port->run) { + pr_err("Tx When port closed\n"); + dev_kfree_skb(skb); + port->stats.tx_errors++; + port->stats.tx_dropped++; + return 0; + } + + count = skb->len; + /* Drop it if it is too small */ + if (skb->len <= 0) { + fst_dbg(DBG_ASS, "fst_start_xmit: packet too small %d\n", + skb->len); + dev_kfree_skb(skb); + stats->tx_errors++; + stats->tx_dropped++; + return 0; + } + if (skb->len == 0) + pr_info("Transmitting a zero length frame\n"); + + /* We are always going to queue the packet * 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) { - /* - * This is the case where the next free has wrapped but the + /* 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) { - /* - * We have got enough buffers in the pipeline. Ask the network + 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); - port->start = 1; /* I'm using this to signal stop sent up */ + port->start = 1; /* I'm using this to signal stop sent up */ + if (!port->char_file) { + fst_dbg(DBG_ASS, "%s: Stop the network layer\n", + dev->name); + netif_stop_queue(dev); + } else { + /* If the char interface say we can't do it at all + */ + dev_kfree_skb(skb); + return 0; + } } - if (txq_length == FST_TXQ_DEPTH - 1) { - /* - * This shouldn't have happened but such is life + /* This shouldn't have happened but such is life */ dev_kfree_skb(skb); - dev->stats.tx_errors++; - dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", - card->card_no, port->index); - return NETDEV_TX_OK; + stats->tx_errors++; + stats->tx_dropped++; + fst_dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", + card->card_no, port->index); + return 0; } /* * queue the buffer */ + 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) { + fst_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] = skb; + 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); + /* Note that after this point we could get an interrupt and the + * skb we are currenyl post processing could have been taken + * off the queue and dealt with. So don't refer to it anymore + */ /* 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 NETDEV_TX_OK; + if (port->char_file) + return count; + else + return 0; } -/* - * Card setup having checked hardware resources. +static const struct net_device_ops fst_ops = { + .ndo_open = fst_open, + .ndo_stop = fst_close, + .ndo_change_mtu = hdlc_change_mtu, + .ndo_start_xmit = hdlc_start_xmit, + .ndo_do_ioctl = fst_ioctl, + .ndo_tx_timeout = fst_tx_timeout, +}; + +/* Card setup having checked hardware resources. * Should be pretty bizarre if we get an error here (kernel memory * exhaustion is one possibility). If we do see a problem we report it * via a printk and leave the corresponding interface and all that follow * disabled. */ -static char *type_strings[] = { - "no hardware", /* Should never be seen */ - "FarSync T2P", - "FarSync T4P", - "FarSync T1U", - "FarSync T2U", - "FarSync T4U", - "FarSync TE1" -}; -static void -fst_init_card(struct fst_card_info *card) +static void fst_init_card(struct fst_card_info *card) { - int i; + int i, j; int err; + struct fst_port_info *port; + char char_dev_name[16]; + struct device *tty_device = NULL; /* We're working on a number of ports based on the card ID. If the * firmware detects something different later (should never happen) * we'll have to revise it in some way then. */ + extract_serial(card); + init_waitqueue_head(&card->cmdfifo_waitq); for (i = 0; i < card->nports; i++) { - err = register_hdlc_device(card->ports[i].dev); - if (err < 0) { + struct net_device *dev; + hdlc_device *hdlc; + + /* Assign the minor device number */ + for (j = 0; j < FST_MAX_CARDS * FST_MAX_PORTS; j++) { + if (fst_ports_list[j] == NULL) { + fst_minor = j; + break; + } + } + card->ports[i] = kmalloc(sizeof(struct fst_port_info), + GFP_KERNEL); + if (card->ports[i] == NULL) { + pr_err("FarSync card found but insufficient memory "); + pr_err("for driver device storage\n"); + return; + } + port = card->ports[i]; + memset(port, 0, sizeof(struct fst_port_info)); + dev = alloc_hdlcdev(port); + + dev->netdev_ops = &fst_ops; + SET_NETDEV_DEV(dev, &card->device->dev); + err = register_hdlc_device(dev); + if (err < 0) { int j; - pr_err("Cannot register HDLC device for port %d (errno %d)\n", - i, -err); + pr_err("Cannot register HDLC device for port %d", i); + pr_err(" (errno %d)\n", -err); for (j = i; j < card->nports; j++) { - free_netdev(card->ports[j].dev); - card->ports[j].dev = NULL; + free_netdev(card->ports[j]->dev); + kfree(card->ports[j]); + card->ports[j]->dev = NULL; } - card->nports = i; - break; - } + card->nports = i; + break; + } + port->dev = dev; + port->card = card; + port->index = i; + port->run = 0; + port->proto = FST_GEN_HDLC; + port->num_tx_buffers = REQUIRED_TX_BUFFERS; + port->num_rx_buffers = REQUIRED_RX_BUFFERS; + port->tx_buffer_size = REQUIRED_TX_BUFFER_SIZE; + port->rx_buffer_size = REQUIRED_RX_BUFFER_SIZE; + port->fstioc_info_ver = fst_iocinfo_version; + port->char_file = NULL; + port->char_inq = NULL; + port->char_inq_end = NULL; + port->minor_dev_no = fst_minor; + spin_lock_init(&port->rxf_lock); + init_waitqueue_head(&port->pollq); + init_waitqueue_head(&port->readq); + init_waitqueue_head(&port->writeq); + fst_ports_list[fst_minor] = port; + hdlc = dev_to_hdlc(dev); + if (fst_alloc_rx_fifo(port)) { + pr_err("%s: Cannot allocate rx fifo\n", + port->dev->name); + kfree(dev); + card->nports = i; + break; + } + /* 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(tx_buffer[i][0]); + dev->mem_end = card->phys_mem + + BUF_OFFSET(tx_buffer[i][0]) + + (port->num_tx_buffers * port->tx_buffer_size); + dev->base_addr = card->pci_conf; + dev->irq = card->irq; + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->addr_len = 8; + strcpy(dev->dev_addr, serial); + dev->watchdog_timeo = FST_TX_TIMEOUT; + hdlc->attach = fst_attach; + hdlc->xmit = fst_start_xmit; + + sscanf(&port_to_dev(port)->name[4], "%d", &fst_minor); + fst_dbg(DBG_ASS, "fst_major is %x: fst_minor is %d\n", + fst_major, fst_minor); + fst_dbg(DBG_INIT, "Register the tty device %d\n", fst_minor); + tty_port_init(&fst_tty_area[fst_minor].tty_port); + tty_device = + tty_port_register_device(&fst_tty_area[fst_minor].tty_port, + serial_drv, fst_minor, NULL); + + fst_tty_area[fst_minor].tty_port.tty = + fst_tty_area[fst_minor].tty; + strcpy(char_dev_name, ""); + strcat(char_dev_name, port_to_dev(port)->name); + + if (device_create(farsync_class, NULL, + MKDEV(fst_major, fst_minor), NULL, + char_dev_name, fst_minor) < 0) { + pr_err("Cannot create udev entry for %s\n", + port_to_dev(port)->name); + } + /* Set the basic async parameters + */ + init_async_parameters(port, 8, COM_STOP_BITS_1, + COM_NO_PARITY, COM_FLOW_CONTROL_NONE); } - pr_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); -} + spin_lock_init(&card->card_lock); + spin_lock_init(&card->fifo_lock); + init_waitqueue_head(&card->fifo_waitq); + + pr_info("%s-%s: (%s) %s IRQ%d, %d ports\n", + port_to_dev(card->ports[0])->name, + port_to_dev(card->ports[card->nports - 1])->name, + serial, type_strings[card->type], card->irq, card->nports); -static const struct net_device_ops fst_ops = { - .ndo_open = fst_open, - .ndo_stop = fst_close, - .ndo_change_mtu = hdlc_change_mtu, - .ndo_start_xmit = hdlc_start_xmit, - .ndo_do_ioctl = fst_ioctl, - .ndo_tx_timeout = fst_tx_timeout, -}; + 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) { + pr_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) { + pr_err("Could not allocate tx dma buffer\n"); + return; + } + } +} -/* - * Initialise card when detected. +/* Initialise card when detected. * Returns 0 to indicate success, or errno otherwise. */ -static int -fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) +static int fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int no_of_cards_added = 0; + static int firsttime_done; struct fst_card_info *card; int err = 0; int i; - printk_once(KERN_INFO - pr_fmt("FarSync WAN driver " FST_USER_VERSION - " (c) 2001-2004 FarSite Communications Ltd.\n")); + if (!firsttime_done) { + pr_info("FarSync WAN driver " FST_USER_VERSION + "-" FST_PATCH_LEVEL "-" FST_PLATFORM FST_ADDITIONAL + " (c) 2001-2012 FarSite Communications Ltd.\n"); + firsttime_done = 1; #if FST_DEBUG - dbg(DBG_ASS, "The value of debug mask is %x\n", fst_debug_mask); + fst_dbg(DBG_ASS, "The value of debug mask is %x\n", + fst_debug_mask); #endif - /* - * We are going to be clever and allow certain cards not to be + } + + /* 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) { @@ -2440,7 +8562,8 @@ fst_add_one(struct pci_dev *pdev, const */ for (i = 0; i < fst_excluded_cards; i++) { if ((pdev->devfn) >> 3 == fst_excluded_list[i]) { - pr_info("FarSync PCI device %d not assigned\n", + fst_dbg(DBG_ASS, + "FarSync PCI device %d not assigned\n", (pdev->devfn) >> 3); return -EBUSY; } @@ -2448,119 +8571,96 @@ fst_add_one(struct pci_dev *pdev, const } /* Allocate driver private data */ - card = kzalloc(sizeof(struct fst_card_info), GFP_KERNEL); - if (card == NULL) + card = kmalloc(sizeof(struct fst_card_info), GFP_KERNEL); + if (card == NULL) { + pr_err("FarSync card found but insufficient memory for"); + pr_err(" driver storage\n"); return -ENOMEM; + } + memset(card, 0, sizeof(struct fst_card_info)); /* Try to enable the device */ if ((err = pci_enable_device(pdev)) != 0) { pr_err("Failed to enable card. Err %d\n", -err); - kfree(card); - return err; - } - - if ((err = pci_request_regions(pdev, "FarSync")) !=0) { - pr_err("Failed to allocate regions. Err %d\n", -err); - pci_disable_device(pdev); - kfree(card); - return err; + goto error_free_card; } - /* Get virtual addresses of memory regions */ + /* 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); - if ((card->mem = ioremap(card->phys_mem, FST_MEMSIZE)) == NULL) { - pr_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) { - pr_err("Control memory remap failed\n"); - pci_release_regions(pdev); - pci_disable_device(pdev); - iounmap(card->mem); - 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)) { - pr_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; - } - /* Record info we need */ - card->irq = pdev->irq; card->type = ent->driver_data; 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)) + (ent->driver_data == FST_TYPE_TE1) || + (ent->driver_data == FST_TYPE_TE1e) || + (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; + (ent->driver_data == FST_TYPE_T2U) || + (ent->driver_data == FST_TYPE_T2U_PMC) || + (ent->driver_data == FST_TYPE_T2Ee) || + (ent->driver_data == FST_TYPE_T2UE)) ? 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); - pr_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; - - hdlc = dev_to_hdlc(dev); + card->device = pdev; - /* 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; + fst_dbg(DBG_PCI, "type %d nports %d irq %d\n", card->type, + card->nports, card->irq); + fst_dbg(DBG_PCI, "conf %04x mem %08x ctlmem %08x\n", + card->pci_conf, card->phys_mem, card->phys_ctlmem); - dev->netdev_ops = &fst_ops; - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->watchdog_timeo = 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")) { + pr_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")) { + pr_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")) { + pr_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")) { + pr_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 */ + card->mem = ioremap(card->phys_mem, FST_MEMSIZE); + if (card->mem == NULL) { + pr_err("Physical memory remap failed\n"); + err = -ENODEV; + goto error_release_ctlmem; + } + card->ctlmem = ioremap(card->phys_ctlmem, 0x10); + if (card->ctlmem == NULL) { + pr_err("Control memory remap failed\n"); + err = -ENODEV; + goto error_unmap_mem; + } + fst_dbg(DBG_PCI, "kernel mem %p, ctlmem %p\n", + card->mem, card->ctlmem); /* Reset the card's processor */ fst_cpureset(card); @@ -2569,108 +8669,179 @@ fst_add_one(struct pci_dev *pdev, const /* Initialise DMA (if required) */ fst_init_dma(card); + /* Register the interrupt handler */ + if (request_irq(card->irq, fst_intr, IRQF_SHARED, FST_DEV_NAME, + card)) { + pr_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)) + pr_info("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 */ + 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); - 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) { - pr_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) { - pr_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; - } - } + 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 +/* Cleanup and close down a card */ -static void -fst_remove_one(struct pci_dev *pdev) +static void fst_remove_one(struct pci_dev *pdev) { struct fst_card_info *card; int i; card = pci_get_drvdata(pdev); + fst_disable_intr(card); + free_irq(card->irq, card); for (i = 0; i < card->nports; i++) { - struct net_device *dev = port_to_dev(&card->ports[i]); + struct net_device *dev = port_to_dev(card->ports[i]); + sscanf(&port_to_dev(card->ports[i])->name[4], "%d", + &fst_minor); + fst_tty_area[fst_minor].state = FST_TTY_ST_DISC; + fst_dbg(DBG_ASS, "Calling tty unregister device for port %d\n", + fst_minor); + tty_port_destroy(&fst_tty_area[fst_minor].tty_port); + tty_unregister_device(serial_drv, fst_minor); + if (card->ports[i]->fifo_rxdata) { + /* + * Deallocate the rx fifo + */ + u16 used_space; + used_space = + (u16) ((card->ports[i]->fifo_rxdata->write_idx - + card->ports[i]->fifo_rxdata->read_idx) + % card->ports[i]->fifo_rxdata->fifo_length); + fst_dbg(DBG_ASS, "%s: Releasing Fifo memory at %p\n", + card->ports[i]->dev->name, + card->ports[i]->fifo_rxdata); + fst_dbg(DBG_ASS, "%s: %d bytes left in fifo\n", + card->ports[i]->dev->name, used_space); + kfree(card->ports[i]->fifo_rxdata); + card->ports[i]->fifo_rxdata = NULL; + } unregister_hdlc_device(dev); + fst_ports_list[card->ports[i]->minor_dev_no] = NULL; + device_destroy(farsync_class, MKDEV(fst_major, fst_minor)); + kfree(card->ports[i]); } - fst_disable_intr(card); - free_irq(card->irq, card); - iounmap(card->ctlmem); iounmap(card->mem); - pci_release_regions(pdev); + + release_mem_region(card->phys_ctlmem, 0x10); + release_mem_region(card->phys_mem, FST_MEMSIZE); if (card->family == FST_FAMILY_TXU) { - /* - * Free dma buffers + release_region(card->pci_conf, 0x100); + } else { + release_region(card->pci_conf, 0x80); + } + + 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); + kfree(card); } static struct pci_driver fst_driver = { - .name = FST_NAME, - .id_table = fst_pci_dev_id, - .probe = fst_add_one, - .remove = fst_remove_one, - .suspend = NULL, - .resume = NULL, +name: FST_NAME, +id_table : fst_pci_dev_id, +probe : fst_add_one, +remove : fst_remove_one, +suspend : NULL, +resume : NULL, +}; + +static int farsync_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, fst_proc_info, NULL); +} + +static const struct file_operations farsync_proc_fops = { + .open = farsync_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, }; -static int __init -fst_init(void) +static int __init fst_init(void) { int i; + INIT_WORK(&fst_rx_work, fst_process_rx_work_q); for (i = 0; i < FST_MAX_CARDS; i++) - fst_card_array[i] = NULL; + fst_cards_list[i] = NULL; + for (i = 0; i < FST_MAX_CARDS * FST_MAX_PORTS; i++) + fst_ports_list[i] = NULL; spin_lock_init(&fst_work_q_lock); - return pci_register_driver(&fst_driver); + fst_dbg(DBG_ASS, "Creating the device class for farsync\n"); + farsync_class = class_create(THIS_MODULE, "farsync"); + /* Register the char driver */ + fst_tty_init(); + fst_major = FST_TTY_MAJOR; + /* + * Create the /proc entry for /proc/farsync + */ + i = pci_register_driver(&fst_driver); + if (i == -ENODEV) + pr_err("No farsync devices found\n"); + /* Always stay loaded even if no devices + */ + proc_create("farsync", 0, NULL, &farsync_proc_fops); + pr_info("fst_min_dma_len set to %d\n", fst_min_dma_len); + pr_info("fst_dmathr set to %x\n", fst_dmathr); + pr_info("fst_iocinfo_version set to %d\n", fst_iocinfo_version); + return 0; } -static void __exit -fst_cleanup_module(void) +static void __exit fst_cleanup_module(void) { - pr_info("FarSync WAN driver unloading\n"); pci_unregister_driver(&fst_driver); + /* Remove the char device driver entry */ + fst_tty_uninit(); + remove_proc_entry("farsync", NULL); + class_destroy(farsync_class); + pr_info("Unloading module farsync\n"); } module_init(fst_init);