📄 farsync.c
字号:
/* * FarSync X21 driver for Linux (2.4.x kernel version) * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * * Copyright (C) 2001 FarSite Communications Ltd. * www.farsite.co.uk * * 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> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/ioport.h>#include <linux/netdevice.h>#include <linux/init.h>#include <linux/if_arp.h>#include <asm/uaccess.h>#include <net/syncppp.h>#include "farsync.h"/* * Module info */MODULE_AUTHOR("R.J.Dunlop <bob.dunlop@farsite.co.uk>");MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd.");EXPORT_NO_SYMBOLS;/* Driver configuration and global parameters * ========================================== *//* Number of ports (per card) supported */#define FST_MAX_PORTS 4/* PCI vendor and device IDs */#define FSC_PCI_VENDOR_ID 0x1619 /* FarSite Communications Ltd */#define T2P_PCI_DEVICE_ID 0x0400 /* T2P X21 2 port card */#define T4P_PCI_DEVICE_ID 0x0440 /* T4P X21 4 port card *//* Default parameters for the link */#define FST_TX_QUEUE_LEN 100 /* At 8Mbps a longer queue length is * useful, the syncppp module forces * this down assuming a slower line I * guess. */#define FST_MAX_MTU 8000 /* Huge but possible */#define FST_DEF_MTU 1500 /* Common sane value */#define FST_TX_TIMEOUT (2*HZ)#ifdef ARPHRD_RAWHDLC#define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */#else#define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */#endif/* Card shared memory layout * ========================= */#pragma pack(1)/* This information is derived in part from the FarSite FarSync Smc.h * file. Unfortunately various name clashes and the non-portability of the * bit field declarations in that file have meant that I have chosen to * recreate the information here. * * The SMC (Shared Memory Configuration) has a version number that is * incremented every time there is a significant change. This number can * be used to check that we have not got out of step with the firmware * contained in the .CDE files. */#define SMC_VERSION 11#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 */#define LEN_TX_BUFFER 8192 /* Size of packet buffers */#define LEN_RX_BUFFER 8192#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/* Interrupt retry time in milliseconds */#define INT_RETRY_TIME 2/* The Am186CH/CC processors support a SmartDMA mode using circular pools * of buffer descriptors. The structure is almost identical to that used * in the LANCE Ethernet controllers. Details available as PDF from the * AMD web site: http://www.amd.com/products/epd/processors/\ * 2.16bitcont/3.am186cxfa/a21914/21914.pdf */struct txdesc { /* Transmit descriptor */ volatile u16 ladr; /* Low order address of packet. This is a * linear address in the Am186 memory space */ volatile u8 hadr; /* High order address. Low 4 bits only, high 4 * bits must be zero */ volatile u8 bits; /* Status and config */ volatile u16 bcnt; /* 2s complement of packet size in low 15 bits. * Transmit terminal count interrupt enable in * top bit. */ u16 unused; /* Not used in Tx */};struct rxdesc { /* Receive descriptor */ volatile u16 ladr; /* Low order address of packet */ volatile u8 hadr; /* High order address */ volatile u8 bits; /* Status and config */ volatile u16 bcnt; /* 2s complement of buffer size in low 15 bits. * Receive terminal count interrupt enable in * top bit. */ volatile u16 mcnt; /* Message byte count (15 bits) */};/* Convert a length into the 15 bit 2's complement *//* #define cnv_bcnt(len) (( ~(len) + 1 ) & 0x7FFF ) *//* Since we need to set the high bit to enable the completion interrupt this * can be made a lot simpler */#define cnv_bcnt(len) (-(len))/* Status and config bits for the above */#define DMA_OWN 0x80 /* SmartDMA owns the descriptor */#define TX_STP 0x02 /* Tx: start of packet */#define TX_ENP 0x01 /* Tx: end of packet */#define RX_ERR 0x40 /* Rx: error (OR of next 4 bits) */#define RX_FRAM 0x20 /* Rx: framing error */#define RX_OFLO 0x10 /* Rx: overflow error */#define RX_CRC 0x08 /* Rx: CRC error */#define RX_HBUF 0x04 /* Rx: buffer error */#define RX_STP 0x02 /* Rx: start of packet */#define RX_ENP 0x01 /* Rx: end of packet *//* Interrupts from the card are caused by various events and these are presented * in a circular buffer as several events may be processed on one physical int */#define MAX_CIRBUFF 32struct cirbuff { u8 rdindex; /* read, then increment and wrap */ u8 wrindex; /* write, then increment and wrap */ u8 evntbuff[MAX_CIRBUFF];};/* Interrupt event codes. * Where appropriate the two low order bits indicate the port number */#define CTLA_CHG 0x18 /* Control signal changed */#define CTLB_CHG 0x19#define CTLC_CHG 0x1A#define CTLD_CHG 0x1B#define INIT_CPLT 0x20 /* Initialisation complete */#define INIT_FAIL 0x21 /* Initialisation failed */#define ABTA_SENT 0x24 /* Abort sent */#define ABTB_SENT 0x25#define ABTC_SENT 0x26#define ABTD_SENT 0x27#define TXA_UNDF 0x28 /* Transmission underflow */#define TXB_UNDF 0x29#define TXC_UNDF 0x2A#define TXD_UNDF 0x2B/* Port physical configuration. See farsync.h for field values */struct port_cfg { u16 lineInterface; /* Physical interface type */ u8 x25op; /* Unused at present */ u8 internalClock; /* 1 => internal clock, 0 => external */ u32 lineSpeed; /* Speed in bps */};/* 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. * The structure starts at offset SMC_BASE. * See farsync.h for some field values. */struct fst_shared { /* DMA descriptor rings */ struct rxdesc rxDescrRing[FST_MAX_PORTS][NUM_RX_BUFFER]; struct txdesc txDescrRing[FST_MAX_PORTS][NUM_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 taskStatus; /* 0x00 => initialising, 0x01 => running, * 0xFF => halted */ u8 interruptHandshake; /* Set to 0x01 by adapter to signal interrupt, * set to 0xEE by host to acknowledge interrupt */ u16 smcVersion; /* Must match SMC_VERSION */ u32 smcFirmwareVersion; /* 0xIIVVRRBB where II = product ID, VV = major * version, RR = revision and BB = build */ u16 txa_done; /* Obsolete completion flags */ u16 rxa_done; u16 txb_done; u16 rxb_done; u16 txc_done; u16 rxc_done; u16 txd_done; u16 rxd_done; u16 mailbox[4]; /* Diagnostics mailbox. Not used */ struct cirbuff interruptEvent; /* 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]; u16 clockStatus[FST_MAX_PORTS]; /* 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 portMailbox[FST_MAX_PORTS][2]; /* command, modifier */ u16 cardMailbox[4]; /* Not used */ /* Number of times that card thinks the host has * missed an interrupt by not acknowledging * within 2mS (I guess NT has problems) */ u32 interruptRetryCount; /* Driver private data used as an ID. We'll not * use this on Linux I'd rather keep such things * in main memory rather than on the PCI bus */ u32 portHandle[FST_MAX_PORTS]; /* Count of Tx underflows for stats */ u32 transmitBufferUnderflow[FST_MAX_PORTS]; /* Debounced V.24 control input status */ u32 v24DebouncedSts[FST_MAX_PORTS]; /* Adapter debounce timers. Don't touch */ u32 ctsTimer[FST_MAX_PORTS]; u32 ctsTimerRun[FST_MAX_PORTS]; u32 dcdTimer[FST_MAX_PORTS]; u32 dcdTimerRun[FST_MAX_PORTS]; u32 numberOfPorts; /* Number of ports detected at startup */ u16 _reserved[64]; u16 cardMode; /* Bit-mask to enable features: * Bit 0: 1 enables LED identify mode */ u16 portScheduleOffset; u32 endOfSmcSignature; /* endOfSmcSignature MUST be the last member of * the structure and marks the end of the shared * memory. Adapter code initializes its value as * END_SIG. */};/* endOfSmcSignature value */#define END_SIG 0x12345678/* Mailbox values. (portMailbox) */#define NOP 0 /* No operation */#define ACK 1 /* Positive acknowledgement to PC driver */#define NAK 2 /* Negative acknowledgement to PC driver */#define STARTPORT 3 /* Start an HDLC port */#define STOPPORT 4 /* Stop an HDLC port */#define ABORTTX 5 /* Abort the transmitter for a port */#define SETV24O 6 /* Set V24 outputs *//* Larger buffers are positioned in memory at offset BFM_BASE */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];};/* Calculate offset of a buffer object within the shared memory window */#define BUF_OFFSET(X) ((unsigned int)&(((struct buf_window *)BFM_BASE)->X))#pragma pack()/* Device driver private information * ================================= *//* Per port (line or channel) information */struct fst_port_info { void *if_ptr; /* Some drivers describe this as a * general purpose pointer. However if * using syncPPP it has a very specific * purpose: it must be the first item in * the structure pointed to by dev->priv * and must in turn point to the * associated ppp_device structure. */ struct fst_card_info *card; /* Card we're associated with */ int index; /* Port index on the card */ int proto; /* Protocol we are running */ int hwif; /* Line hardware (lineInterface copy) */ int run; /* Port is running */ int rxpos; /* Next Rx buffer to use */ int txpos; /* Next Tx buffer to use */ int txipos; /* Next Tx buffer to check for free */ int txcnt; /* Count of Tx buffers in use */ struct net_device *dev; /* Kernel network device entry */ struct net_device_stats stats; /* Standard statistics */ struct ppp_device pppdev; /* Link to syncPPP */};/* Per card information */struct fst_card_info { 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 */ unsigned int nports; /* Number of serial ports */ unsigned int type; /* Type index of card */ unsigned int state; /* State of card */ spinlock_t card_lock; /* Lock for SMP access */ unsigned short pci_conf; /* PCI card config in I/O space */ /* Per port info */ struct fst_port_info ports[ FST_MAX_PORTS ];};/* * 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 */#if FST_DEBUGstatic int fst_debug_mask = { FST_DEBUG };/* Most common debug activity is to print something if the corresponding bit * is set in the debug mask. Note: this uses a non-ANSI extension in GCC to * support variable numbers of macro parameters. The inverted if prevents us * eating someone else's else clause. */#define dbg(F,fmt,A...) if ( ! ( fst_debug_mask & (F))) \ ; \ else \ printk ( KERN_DEBUG FST_NAME ": " fmt, ## A )#else# define dbg(X...) /* NOP */#endif/* Printing short cuts */#define printk_err(fmt,A...) printk ( KERN_ERR FST_NAME ": " fmt, ## A )#define printk_warn(fmt,A...) printk ( KERN_WARNING FST_NAME ": " fmt, ## A )#define printk_info(fmt,A...) printk ( KERN_INFO FST_NAME ": " fmt, ## A )/* * PCI ID lookup table */static struct pci_device_id fst_pci_dev_id[] __devinitdata = { { FSC_PCI_VENDOR_ID, T2P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T2P }, { FSC_PCI_VENDOR_ID, T4P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FST_TYPE_T4P }, { 0, } /* End */};MODULE_DEVICE_TABLE ( pci, fst_pci_dev_id );/* 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 0x50. */static inline voidfst_cpureset ( struct fst_card_info *card ){ unsigned int regval; regval = inl ( card->pci_conf + 0x50 ); outl ( regval | 0x40000000, card->pci_conf + 0x50 ); outl ( regval & ~0x40000000, card->pci_conf + 0x50 );}/* Release the processor from reset */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -