lkml.org 
[lkml]   [2013]   [Sep]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Date
Subject[PATCH 005/007] WAN Drivers: Update farsync driver and introduce fsflex driver
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 <kevin.curtis@farsite.com>

---
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 <bob.dunlop@farsite.co.uk>
- * Maintainer: Kevin Curtis <kevin.curtis@farsite.co.uk>
+ * Author: R.J.Dunlop <bob.dunlop@farsite.com>
+ * Maintainer: Kevin Curtis <kevin.curtis@farsite.com>
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -20,18 +20,29 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
-#include <linux/pci.h>
+#include <linux/fs.h>
#include <linux/sched.h>
-#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
#include <linux/ioport.h>
#include <linux/init.h>
-#include <linux/interrupt.h>
+#include <linux/uaccess.h>
#include <linux/if.h>
+#include <linux/interrupt.h>
#include <linux/hdlc.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
+#include <linux/delay.h>
+#include <linux/delay.h>
+/* TTY includes */
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <linux/scatterlist.h>
+#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);


\
 
 \ /
  Last update: 2013-09-18 12:41    [W:6.075 / U:0.424 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site