farsync.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:60k
- /*
- * 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 32
- struct 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_DEBUG
- static 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 void
- fst_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
- */
- static inline void
- fst_cpurelease ( struct fst_card_info *card )
- {
- (void) readb ( card->ctlmem );
- }
- /* Clear the cards interrupt flag
- */
- static inline void
- fst_clear_intr ( struct fst_card_info *card )
- {
- /* Poke the appropriate PLX chip register (same as enabling interrupts)
- */
- outw ( 0x0543, card->pci_conf + 0x4C );
- }
- /* Disable card interrupts
- */
- static inline void
- fst_disable_intr ( struct fst_card_info *card )
- {
- outw ( 0x0000, card->pci_conf + 0x4C );
- }
- /* Issue a Mailbox command for a port.
- * Note we issue them on a fire and forget basis, not expecting to see an
- * error and not waiting for completion.
- */
- static void
- fst_issue_cmd ( struct fst_port_info *port, unsigned short cmd )
- {
- struct fst_card_info *card;
- unsigned short mbval;
- unsigned long flags;
- int safety;
- card = port->card;
- spin_lock_irqsave ( &card->card_lock, flags );
- mbval = FST_RDW ( card, portMailbox[port->index][0]);
- safety = 0;
- /* Wait for any previous command to complete */
- while ( mbval > NAK )
- {
- spin_unlock_irqrestore ( &card->card_lock, flags );
- schedule_timeout ( 1 );
- spin_lock_irqsave ( &card->card_lock, flags );
- if ( ++safety > 1000 )
- {
- printk_err ("Mailbox safety timeoutn");
- break;
- }
- mbval = FST_RDW ( card, portMailbox[port->index][0]);
- }
- if ( safety > 0 )
- {
- dbg ( DBG_CMD,"Mailbox clear after %d jiffiesn", safety );
- }
- if ( mbval == NAK )
- {
- dbg ( DBG_CMD,"issue_cmd: previous command was NAK'dn");
- }
- FST_WRW ( card, portMailbox[port->index][0], cmd );
- if ( cmd == ABORTTX || cmd == STARTPORT )
- {
- port->txpos = 0;
- port->txipos = 0;
- port->txcnt = 0;
- }
- spin_unlock_irqrestore ( &card->card_lock, flags );
- }
- /* Port output signals control
- */
- static inline void
- fst_op_raise ( struct fst_port_info *port, unsigned int outputs )
- {
- outputs |= FST_RDL ( port->card, v24OpSts[port->index]);
- FST_WRL ( port->card, v24OpSts[port->index], outputs );
- if ( port->run )
- fst_issue_cmd ( port, SETV24O );
- }
- static inline void
- fst_op_lower ( struct fst_port_info *port, unsigned int outputs )
- {
- outputs = ~outputs & FST_RDL ( port->card, v24OpSts[port->index]);
- FST_WRL ( port->card, v24OpSts[port->index], outputs );
- if ( port->run )
- fst_issue_cmd ( port, SETV24O );
- }
- /*
- * Setup port Rx buffers
- */
- static void
- fst_rx_config ( struct fst_port_info *port )
- {
- int i;
- int pi;
- unsigned int offset;
- unsigned long flags;
- struct fst_card_info *card;
- pi = port->index;
- card = port->card;
- spin_lock_irqsave ( &card->card_lock, flags );
- for ( i = 0 ; i < NUM_RX_BUFFER ; i++ )
- {
- offset = BUF_OFFSET ( rxBuffer[pi][i][0]);
- FST_WRW ( card, rxDescrRing[pi][i].ladr, (u16) offset );
- FST_WRB ( card, rxDescrRing[pi][i].hadr, (u8)( offset >> 16 ));
- FST_WRW ( card, rxDescrRing[pi][i].bcnt,
- cnv_bcnt ( LEN_RX_BUFFER ));
- FST_WRW ( card, rxDescrRing[pi][i].mcnt, 0 );
- FST_WRB ( card, rxDescrRing[pi][i].bits, DMA_OWN );
- }
- port->rxpos = 0;
- spin_unlock_irqrestore ( &card->card_lock, flags );
- }
- /*
- * Setup port Tx buffers
- */
- static void
- fst_tx_config ( struct fst_port_info *port )
- {
- int i;
- int pi;
- unsigned int offset;
- unsigned long flags;
- struct fst_card_info *card;
- pi = port->index;
- card = port->card;
- spin_lock_irqsave ( &card->card_lock, flags );
- for ( i = 0 ; i < NUM_TX_BUFFER ; i++ )
- {
- offset = BUF_OFFSET ( txBuffer[pi][i][0]);
- FST_WRW ( card, txDescrRing[pi][i].ladr, (u16) offset );
- FST_WRB ( card, txDescrRing[pi][i].hadr, (u8)( offset >> 16 ));
- FST_WRW ( card, txDescrRing[pi][i].bcnt, 0 );
- FST_WRB ( card, txDescrRing[pi][i].bits, 0 );
- }
- port->txpos = 0;
- port->txipos = 0;
- port->txcnt = 0;
- spin_unlock_irqrestore ( &card->card_lock, flags );
- }
- /* Control signal change interrupt event
- */
- static void
- fst_intr_ctlchg ( struct fst_card_info *card, struct fst_port_info *port )
- {
- int signals;
- signals = FST_RDL ( card, v24DebouncedSts[port->index]);
- if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD ))
- {
- if ( ! netif_carrier_ok ( port->dev ))
- {
- dbg ( DBG_INTR,"DCD activen");
- /* Poke sPPP to renegotiate */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- {
- sppp_reopen ( port->dev );
- }
- netif_carrier_on ( port->dev );
- }
- }
- else
- {
- if ( netif_carrier_ok ( port->dev ))
- {
- dbg ( DBG_INTR,"DCD lostn");
- netif_carrier_off ( port->dev );
- }
- }
- }
- /* Rx complete interrupt
- */
- static void
- fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
- {
- unsigned char dmabits;
- int pi;
- int rxp;
- unsigned short len;
- struct sk_buff *skb;
- int i;
- /* Check we have a buffer to process */
- pi = port->index;
- rxp = port->rxpos;
- dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits );
- if ( dmabits & DMA_OWN )
- {
- dbg ( DBG_RX | DBG_INTR,"intr_rx: No buffer port %d pos %dn",
- pi, rxp );
- return;
- }
- /* Get buffer length */
- len = FST_RDW ( card, rxDescrRing[pi][rxp].mcnt );
- /* 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 %dn", pi, rxp, dmabits,
- len );
- if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 )
- {
- port->stats.rx_errors++;
- /* Update error stats and discard buffer */
- if ( dmabits & RX_OFLO )
- {
- port->stats.rx_fifo_errors++;
- }
- if ( dmabits & RX_CRC )
- {
- port->stats.rx_crc_errors++;
- }
- if ( dmabits & RX_FRAM )
- {
- port->stats.rx_frame_errors++;
- }
- if ( dmabits == ( RX_STP | RX_ENP ))
- {
- port->stats.rx_length_errors++;
- }
- /* Discard buffer descriptors until we see the end of packet
- * marker
- */
- i = 0;
- while (( dmabits & ( DMA_OWN | RX_ENP )) == 0 )
- {
- FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
- if ( ++rxp >= NUM_RX_BUFFER )
- rxp = 0;
- if ( ++i > NUM_RX_BUFFER )
- {
- dbg ( DBG_ASS,"intr_rx: Discarding more bufs"
- " than we haven");
- break;
- }
- dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits );
- }
- /* Discard the terminal buffer */
- if ( ! ( dmabits & DMA_OWN ))
- {
- FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
- if ( ++rxp >= NUM_RX_BUFFER )
- rxp = 0;
- }
- port->rxpos = rxp;
- return;
- }
- /* Allocate SKB */
- if (( skb = dev_alloc_skb ( len )) == NULL )
- {
- dbg ( DBG_RX,"intr_rx: can't allocate buffern");
- port->stats.rx_dropped++;
- /* Return descriptor to card */
- FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
- if ( ++rxp >= NUM_RX_BUFFER )
- port->rxpos = 0;
- else
- port->rxpos = rxp;
- return;
- }
- memcpy_fromio ( skb_put ( skb, len ),
- card->mem + BUF_OFFSET ( rxBuffer[pi][rxp][0]),
- len );
- /* Reset buffer descriptor */
- FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
- if ( ++rxp >= NUM_RX_BUFFER )
- port->rxpos = 0;
- else
- port->rxpos = rxp;
- /* Update stats */
- port->stats.rx_packets++;
- port->stats.rx_bytes += len;
- /* Push upstream */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- {
- /* Mark for further processing by sPPP module */
- skb->protocol = htons ( ETH_P_WAN_PPP );
- }
- else
- {
- /* DEC customer specific protocol (since nothing defined for
- * marking raw data), at least one other driver uses this value
- * for this purpose.
- */
- skb->protocol = htons ( ETH_P_CUST );
- skb->pkt_type = PACKET_HOST;
- }
- skb->mac.raw = skb->data;
- skb->dev = port->dev;
- netif_rx ( skb );
- port->dev->last_rx = jiffies;
- }
- /*
- * The interrupt service routine
- * Dev_id is our fst_card_info pointer
- */
- static void
- fst_intr ( int irq, void *dev_id, struct pt_regs *regs )
- {
- struct fst_card_info *card;
- struct fst_port_info *port;
- int rdidx; /* Event buffer indices */
- int wridx;
- int event; /* Actual event for processing */
- int pi;
- if (( card = dev_id ) == NULL )
- {
- dbg ( DBG_INTR,"intr: spurious %dn", irq );
- return;
- }
- dbg ( DBG_INTR,"intr: %d %pn", irq, card );
- spin_lock ( &card->card_lock );
- /* Clear and reprime the interrupt source */
- fst_clear_intr ( card );
- /* Set the software acknowledge */
- FST_WRB ( card, interruptHandshake, 0xEE );
- /* Drain the event queue */
- rdidx = FST_RDB ( card, interruptEvent.rdindex );
- wridx = FST_RDB ( card, interruptEvent.wrindex );
- while ( rdidx != wridx )
- {
- event = FST_RDB ( card, interruptEvent.evntbuff[rdidx]);
- port = &card->ports[event & 0x03];
- dbg ( DBG_INTR,"intr: %xn", event );
- switch ( event )
- {
- case CTLA_CHG:
- case CTLB_CHG:
- case CTLC_CHG:
- case CTLD_CHG:
- if ( port->run && port->dev != NULL )
- fst_intr_ctlchg ( card, port );
- break;
- case ABTA_SENT:
- case ABTB_SENT:
- case ABTC_SENT:
- case ABTD_SENT:
- dbg ( DBG_TX,"Abort complete port %dn", event & 0x03 );
- break;
- case TXA_UNDF:
- case TXB_UNDF:
- case TXC_UNDF:
- case TXD_UNDF:
- /* Difficult to see how we'd get this given that we
- * always load up the entire packet for DMA.
- */
- dbg ( DBG_TX,"Tx underflow port %dn", event & 0x03 );
- port->stats.tx_errors++;
- port->stats.tx_fifo_errors++;
- break;
- case INIT_CPLT:
- dbg ( DBG_INIT,"Card init OK intrn");
- break;
- case INIT_FAIL:
- dbg ( DBG_INIT,"Card init FAILED intrn");
- card->state = FST_IFAILED;
- break;
- default:
- printk_err ("intr: unknown card event code. ignoredn");
- break;
- }
- /* Bump and wrap the index */
- if ( ++rdidx >= MAX_CIRBUFF )
- rdidx = 0;
- }
- FST_WRB ( card, interruptEvent.rdindex, rdidx );
- for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ )
- {
- if ( port->dev == NULL || ! port->run )
- continue;
- /* Check for rx completions */
- while ( ! ( FST_RDB ( card, rxDescrRing[pi][port->rxpos].bits )
- & DMA_OWN ))
- {
- fst_intr_rx ( card, port );
- }
- /* Check for Tx completions */
- while ( port->txcnt > 0 && ! ( FST_RDB ( card,
- txDescrRing[pi][port->txipos].bits ) & DMA_OWN ))
- {
- --port->txcnt;
- if ( ++port->txipos >= NUM_TX_BUFFER )
- port->txipos = 0;
- netif_wake_queue ( port->dev );
- }
- }
- spin_unlock ( &card->card_lock );
- }
- /* Check that the shared memory configuration is one that we can handle
- * and that some basic parameters are correct
- */
- static void
- check_started_ok ( struct fst_card_info *card )
- {
- int i;
- /* Check structure version and end marker */
- if ( FST_RDW ( card, smcVersion ) != SMC_VERSION )
- {
- printk_err ("Bad shared memory version %d expected %dn",
- FST_RDW ( card, smcVersion ), SMC_VERSION );
- card->state = FST_BADVERSION;
- return;
- }
- if ( FST_RDL ( card, endOfSmcSignature ) != END_SIG )
- {
- printk_err ("Missing shared memory signaturen");
- card->state = FST_BADVERSION;
- return;
- }
- /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */
- if (( i = FST_RDB ( card, taskStatus )) == 0x01 )
- {
- card->state = FST_RUNNING;
- }
- else if ( i == 0xFF )
- {
- printk_err ("Firmware initialisation failed. Card haltedn");
- card->state = FST_HALTED;
- return;
- }
- else if ( i != 0x00 )
- {
- printk_err ("Unknown firmware status 0x%xn", i );
- card->state = FST_HALTED;
- return;
- }
- /* Finally check the number of ports reported by firmware against the
- * number we assumed at card detection. Should never happen with
- * existing firmware etc so we just report it for the moment.
- */
- if ( FST_RDL ( card, numberOfPorts ) != card->nports )
- {
- printk_warn ("Port count mismatch."
- " Firmware thinks %d we say %dn",
- FST_RDL ( card, numberOfPorts ), card->nports );
- }
- }
- static int
- fst_change_mtu ( struct net_device *dev, int new_mtu )
- {
- if ( new_mtu < 128 || new_mtu > FST_MAX_MTU )
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
- }
- /* Sooner or later you can't avoid a forward declaration */
- static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd );
- static int
- switch_proto ( struct fst_port_info *port, int new_proto )
- {
- int err;
- int orig_mtu;
- struct net_device *dev;
- dev = port->dev;
- /* Turn off sPPP module ? */
- if (( new_proto != FST_HDLC && new_proto != FST_PPP )
- && ( port->proto == FST_HDLC || port->proto == FST_PPP ))
- {
- sppp_close ( port->pppdev.dev );
- sppp_detach ( port->pppdev.dev );
- /* Reset some fields overwritten by sPPP */
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
- dev->tx_queue_len = FST_TX_QUEUE_LEN;
- dev->type = ARPHRD_MYTYPE;
- dev->addr_len = 0;
- dev->hard_header_len = 0;
- dev->do_ioctl = fst_ioctl;
- dev->change_mtu = fst_change_mtu;
- dev->flags = IFF_POINTOPOINT|IFF_NOARP;
- return 0;
- }
- /* Turn on sPPP ? */
- if (( new_proto == FST_HDLC || new_proto == FST_PPP )
- && ( port->proto != FST_HDLC && port->proto != FST_PPP ))
- {
- orig_mtu = dev->mtu;
- /* Attach to sync PPP module */
- port->pppdev.dev = dev;
- sppp_attach ( &port->pppdev );
- if ( orig_mtu < dev->mtu )
- dev->change_mtu ( dev, orig_mtu );
- /* Claw back the ioctl routine. We promise to be good and call
- * the sync PPP routines once we've eliminated our functions.
- */
- dev->do_ioctl = fst_ioctl;
- /* Set the mode */
- if ( new_proto == FST_HDLC )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL,
- SPPPIOCCISCO );
- }
- else
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL,
- SPPPIOCPPP );
- }
- /* Open the device */
- if ( err == 0 )
- {
- (void) sppp_open ( port->dev );
- }
- return err;
- }
- /* Switch sPPP mode to that desired */
- err = 0;
- if ( new_proto == FST_HDLC && port->pppdev.dev != NULL )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO );
- }
- else if ( new_proto == FST_PPP && port->pppdev.dev != NULL )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP );
- }
- /* Anything else is switching from one raw mode to another which is
- * basically a NOP
- */
- return err;
- }
- static int
- set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port,
- struct fstioc_info *info )
- {
- int err;
- /* Set things according to the user set valid flags */
- err = 0;
- if ( info->valid & FSTVAL_PROTO )
- {
- if ( port->proto != info->proto )
- {
- err = switch_proto ( port, info->proto );
- if ( err == 0 )
- port->proto = info->proto;
- }
- }
- if ( info->valid & FSTVAL_CABLE )
- {
- FST_WRB ( card, portConfig[port->index].lineInterface,
- info->lineInterface );
- port->hwif = info->lineInterface;
- }
- if ( info->valid & FSTVAL_SPEED )
- {
- FST_WRL ( card, portConfig[port->index].lineSpeed,
- info->lineSpeed );
- FST_WRB ( card, portConfig[port->index].internalClock,
- info->internalClock );
- }
- if ( info->valid & FSTVAL_MODE )
- {
- FST_WRW ( card, cardMode, info->cardMode );
- }
- #if FST_DEBUG
- if ( info->valid & FSTVAL_DEBUG )
- {
- fst_debug_mask = info->debug;
- }
- #endif
- return err;
- }
- static void
- gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port,
- struct fstioc_info *info )
- {
- int i;
- memset ( info, 0, sizeof ( struct fstioc_info ));
- i = port->index;
- info->nports = card->nports;
- info->type = card->type;
- info->state = card->state;
- info->proto = port->proto;
- 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
- ;
- 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->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 );
- }
- 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;
- dbg ( DBG_IOCTL,"ioctl: %x, %pn", cmd, ifr->ifr_data );
- port = dev->priv;
- card = port->card;
- if ( !capable ( CAP_NET_ADMIN ))
- return -EPERM;
- switch ( cmd )
- {
- case FSTCPURESET:
- fst_cpureset ( card );
- card->state = FST_RESET;
- return 0;
- case FSTCPURELEASE:
- fst_cpurelease ( card );
- card->state = FST_STARTING;
- return 0;
- case FSTWRITE: /* Code write (download) */
- /* First copy in the header with the length and offset of data
- * to write
- */
- if ( ifr->ifr_data == NULL )
- {
- return -EINVAL;
- }
- if ( copy_from_user ( &wrthdr, ifr->ifr_data,
- sizeof ( struct fstioc_write )))
- {
- return -EFAULT;
- }
- /* Sanity check the parameters. We don't support partial writes
- * when going over the top
- */
- if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE
- || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
- {
- return -ENXIO;
- }
- /* 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
- */
- if ( card->state == FST_RESET )
- {
- card->state = FST_DOWNLOAD;
- }
- return 0;
- case FSTGETCONF:
- /* If card has just been started check the shared memory config
- * version and marker
- */
- if ( card->state == FST_STARTING )
- {
- check_started_ok ( card );
- /* If everything checked out enable card interrupts */
- if ( card->state == FST_RUNNING )
- {
- spin_lock_irqsave ( &card->card_lock, flags );
- fst_clear_intr ( card );
- FST_WRB ( card, interruptHandshake, 0xEE );
- spin_unlock_irqrestore ( &card->card_lock,
- flags );
- }
- }
- if ( ifr->ifr_data == NULL )
- {
- return -EINVAL;
- }
- gather_conf_info ( card, port, &info );
- if ( copy_to_user ( ifr->ifr_data, &info, sizeof ( info )))
- {
- return -EFAULT;
- }
- return 0;
- case FSTSETCONF:
- if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info )))
- {
- return -EFAULT;
- }
- return set_conf_from_info ( card, port, &info );
- default:
- /* Not one of ours. Pass it through to sPPP package */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- return sppp_do_ioctl ( dev, ifr, cmd );
- else
- return -EINVAL;
- }
- }
- static void
- fst_openport ( struct fst_port_info *port )
- {
- int signals;
- /* Only init things if card is actually running. This allows open to
- * succeed for downloads etc.
- */
- if ( port->card->state == FST_RUNNING )
- {
- if ( port->run )
- {
- dbg ( DBG_OPEN,"open: found port already runningn");
- fst_issue_cmd ( port, STOPPORT );
- port->run = 0;
- }
- fst_rx_config ( port );
- fst_tx_config ( port );
- fst_op_raise ( port, OPSTS_RTS | OPSTS_DTR );
- fst_issue_cmd ( port, STARTPORT );
- port->run = 1;
- signals = FST_RDL ( port->card, v24DebouncedSts[port->index]);
- if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE
- : IPSTS_DCD ))
- netif_carrier_on ( port->dev );
- else
- netif_carrier_off ( port->dev );
- }
- }
- 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 );
- }
- else
- {
- dbg ( DBG_OPEN,"close: port not runningn");
- }
- }
- }
- static int
- fst_open ( struct net_device *dev )
- {
- struct fst_card_info *card;
- struct fst_port_info *port;
- int orig_mtu;
- int err;
- MOD_INC_USE_COUNT;
- port = dev->priv;
- card = port->card;
- switch ( port->proto )
- {
- case FST_HDLC:
- case FST_PPP:
- orig_mtu = dev->mtu;
- /* Attach to sync PPP module */
- port->pppdev.dev = dev;
- sppp_attach ( &port->pppdev );
- if ( orig_mtu < dev->mtu )
- dev->change_mtu ( dev, orig_mtu );
- /* Claw back the ioctl routine. We promise to be good and call
- * the sync PPP routines once we've eliminated our functions.
- */
- dev->do_ioctl = fst_ioctl;
- err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC
- ? SPPPIOCCISCO : SPPPIOCPPP );
- if ( err )
- {
- sppp_detach ( dev );
- MOD_DEC_USE_COUNT;
- return err;
- }
- err = sppp_open ( dev );
- if ( err )
- {
- sppp_detach ( dev );
- MOD_DEC_USE_COUNT;
- return err;
- }
- break;
- case FST_MONITOR:
- case FST_RAW:
- break;
- default:
- dbg ( DBG_OPEN,"open: Unknown proto %dn", port->proto );
- break;
- }
- fst_openport ( port );
- netif_wake_queue ( dev );
- return 0;
- }
- static int
- fst_close ( struct net_device *dev )
- {
- struct fst_port_info *port;
- port = dev->priv;
- netif_stop_queue ( dev );
- switch ( port->proto )
- {
- case FST_HDLC:
- case FST_PPP:
- sppp_close ( dev );
- sppp_detach ( dev );
- break;
- case FST_MONITOR:
- case FST_RAW:
- break;
- default:
- dbg ( DBG_OPEN,"close: Unknown proto %dn", port->proto );
- break;
- }
- fst_closeport ( port );
- MOD_DEC_USE_COUNT;
- return 0;
- }
- static void
- fst_tx_timeout ( struct net_device *dev )
- {
- struct fst_port_info *port;
- dbg ( DBG_INTR | DBG_TX,"tx_timeoutn");
- port = dev->priv;
- port->stats.tx_errors++;
- port->stats.tx_aborted_errors++;
- if ( port->txcnt > 0 )
- fst_issue_cmd ( port, ABORTTX );
- dev->trans_start = jiffies;
- netif_wake_queue ( dev );
- }
- static int
- fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
- {
- struct fst_card_info *card;
- struct fst_port_info *port;
- unsigned char dmabits;
- unsigned long flags;
- int pi;
- int txp;
- port = dev->priv;
- card = port->card;
- /* Drop packet if in monitor (rx only) mode */
- if ( port->proto == FST_MONITOR )
- {
- dev_kfree_skb ( skb );
- return 0;
- }
- /* Drop packet with error if we don't have carrier */
- if ( ! netif_carrier_ok ( dev ))
- {
- dev_kfree_skb ( skb );
- port->stats.tx_errors++;
- port->stats.tx_carrier_errors++;
- return 0;
- }
- /* Drop it if it's too big! MTU failure ? */
- if ( skb->len > LEN_TX_BUFFER )
- {
- dbg ( DBG_TX,"Packet too large %d vs %dn", skb->len,
- LEN_TX_BUFFER );
- dev_kfree_skb ( skb );
- port->stats.tx_errors++;
- return 0;
- }
- /* Check we have a buffer */
- pi = port->index;
- spin_lock_irqsave ( &card->card_lock, flags );
- txp = port->txpos;
- dmabits = FST_RDB ( card, txDescrRing[pi][txp].bits );
- if ( dmabits & DMA_OWN )
- {
- spin_unlock_irqrestore ( &card->card_lock, flags );
- dbg ( DBG_TX,"Out of Tx buffersn");
- dev_kfree_skb ( skb );
- port->stats.tx_errors++;
- return 0;
- }
- if ( ++port->txpos >= NUM_TX_BUFFER )
- port->txpos = 0;
- if ( ++port->txcnt >= NUM_TX_BUFFER )
- netif_stop_queue ( dev );
- /* Release the card lock before we copy the data as we now have
- * exclusive access to the buffer.
- */
- spin_unlock_irqrestore ( &card->card_lock, flags );
- /* Enqueue the packet */
- memcpy_toio ( card->mem + BUF_OFFSET ( txBuffer[pi][txp][0]),
- skb->data, skb->len );
- FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len ));
- FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP );
- port->stats.tx_packets++;
- port->stats.tx_bytes += skb->len;
- dev_kfree_skb ( skb );
- dev->trans_start = jiffies;
- return 0;
- }
- static struct net_device_stats *
- fst_get_stats ( struct net_device *dev )
- {
- struct fst_port_info *port;
- if (( port = dev->priv ) != NULL )
- return &port->stats;
- else
- return NULL;
- }
- /*
- * 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[] __devinitdata = {
- "no hardware", /* Should never be seen */
- "FarSync T2P",
- "FarSync T4P"
- };
- static void __devinit
- fst_init_card ( struct fst_card_info *card )
- {
- int i;
- int err;
- struct net_device *dev;
- /* 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.
- */
- for ( i = 0 ; i < card->nports ; i++ )
- {
- card->ports[i].if_ptr = &card->ports[i].pppdev;
- card->ports[i].card = card;
- card->ports[i].index = i;
- card->ports[i].proto = FST_HDLC;
- card->ports[i].run = 0;
- dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL );
- if ( dev == NULL )
- {
- printk_err ("Cannot allocate net_device for port %dn",
- i );
- /* No point going any further */
- card->nports = i;
- break;
- }
- memset ( dev, 0, sizeof ( struct net_device ));
- card->ports[i].dev = dev;
- if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 )
- {
- printk_err ("Cannot allocate i/f name for port %dn",
- i );
- kfree ( dev );
- card->nports = i;
- break;
- }
- /* Fill in remainder of 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->rmem_start = card->phys_mem
- + BUF_OFFSET ( rxBuffer[i][0][0]);
- dev->rmem_end = card->phys_mem
- + BUF_OFFSET ( rxBuffer[i][NUM_RX_BUFFER][0]);
- dev->base_addr = card->pci_conf;
- dev->irq = card->irq;
- dev->get_stats = fst_get_stats;
- dev->mtu = FST_DEF_MTU;
- dev->change_mtu = fst_change_mtu;
- dev->priv = &card->ports[i];
- dev->tx_queue_len = FST_TX_QUEUE_LEN;
- dev->type = ARPHRD_MYTYPE;
- dev->addr_len = 0;
- dev->open = fst_open;
- dev->stop = fst_close;
- dev->hard_start_xmit = fst_start_xmit;
- dev->do_ioctl = fst_ioctl;
- dev->watchdog_timeo = FST_TX_TIMEOUT;
- dev->tx_timeout = fst_tx_timeout;
- dev->flags = IFF_POINTOPOINT|IFF_NOARP;
- if (( err = register_netdev ( dev )) < 0 )
- {
- printk_err ("Cannot register %s (errno %d)n",
- dev->name, -err );
- kfree ( dev );
- card->nports = i;
- break;
- }
- }
- spin_lock_init ( &card->card_lock );
- printk ( KERN_INFO "%s-%s: %s IRQ%d, %d portsn",
- card->ports[0].dev->name,
- card->ports[card->nports-1].dev->name,
- type_strings[card->type], card->irq, card->nports );
- }
- /*
- * Initialise card when detected.
- * Returns 0 to indicate success, or errno otherwise.
- */
- static int __devinit
- fst_add_one ( struct pci_dev *pdev, const struct pci_device_id *ent )
- {
- static int firsttime_done = 0;
- struct fst_card_info *card;
- int err = 0;
- if ( ! firsttime_done )
- {
- printk ( KERN_INFO "FarSync X21 driver " FST_USER_VERSION
- " (c) 2001 FarSite Communications Ltd.n");
- firsttime_done = 1;
- }
- /* Allocate driver private data */
- card = kmalloc ( sizeof ( struct fst_card_info ), GFP_KERNEL);
- if (card == NULL)
- {
- printk_err ("FarSync card found but insufficient memory for"
- " driver storagen");
- return -ENOMEM;
- }
- memset ( card, 0, sizeof ( struct fst_card_info ));
- /* Record info we need*/
- card->irq = pdev->irq;
- card->pci_conf = pci_resource_start ( pdev, 1 );
- card->phys_mem = pci_resource_start ( pdev, 2 );
- card->phys_ctlmem = pci_resource_start ( pdev, 3 );
- card->type = ent->driver_data;
- card->nports = ( ent->driver_data == FST_TYPE_T2P ) ? 2 : 4;
- card->state = FST_UNINIT;
- dbg ( DBG_PCI,"type %d nports %d irq %dn", card->type,
- card->nports, card->irq );
- dbg ( DBG_PCI,"conf %04x mem %08x ctlmem %08xn",
- card->pci_conf, card->phys_mem, card->phys_ctlmem );
- /* Check we can get access to the memory and I/O regions */
- if ( ! request_region ( card->pci_conf, 0x80,"PLX config regs"))
- {
- printk_err ("Unable to get config I/O @ 0x%04Xn",
- card->pci_conf );
- err = -ENODEV;
- goto error_free_card;
- }
- if ( ! request_mem_region ( card->phys_mem, FST_MEMSIZE,"Shared RAM"))
- {
- printk_err ("Unable to get main memory @ 0x%08Xn",
- card->phys_mem );
- err = -ENODEV;
- goto error_release_io;
- }
- if ( ! request_mem_region ( card->phys_ctlmem, 0x10,"Control memory"))
- {
- printk_err ("Unable to get control memory @ 0x%08Xn",
- card->phys_ctlmem );
- err = -ENODEV;
- goto error_release_mem;
- }
- /* Try to enable the device */
- if (( err = pci_enable_device ( pdev )) != 0 )
- {
- printk_err ("Failed to enable card. Err %dn", -err );
- goto error_release_ctlmem;
- }
- /* Get virtual addresses of memory regions */
- if (( card->mem = ioremap ( card->phys_mem, FST_MEMSIZE )) == NULL )
- {
- printk_err ("Physical memory remap failedn");
- err = -ENODEV;
- goto error_release_ctlmem;
- }
- if (( card->ctlmem = ioremap ( card->phys_ctlmem, 0x10 )) == NULL )
- {
- printk_err ("Control memory remap failedn");
- err = -ENODEV;
- goto error_unmap_mem;
- }
- dbg ( DBG_PCI,"kernel mem %p, ctlmem %pn", card->mem, card->ctlmem);
- /* Reset the card's processor */
- fst_cpureset ( card );
- card->state = FST_RESET;
- /* Register the interrupt handler */
- if ( request_irq ( card->irq, fst_intr, SA_SHIRQ, FST_DEV_NAME, card ))
- {
- printk_err ("Unable to register interrupt %dn", card->irq );
- err = -ENODEV;
- goto error_unmap_ctlmem;
- }
- /* Record driver data for later use */
- pci_set_drvdata(pdev, card);
- /* Remainder of card setup */
- fst_init_card ( card );
- return 0; /* Success */
- /* Failure. Release resources */
- error_unmap_ctlmem:
- iounmap ( card->ctlmem );
- error_unmap_mem:
- iounmap ( card->mem );
- error_release_ctlmem:
- release_mem_region ( card->phys_ctlmem, 0x10 );
- error_release_mem:
- release_mem_region ( card->phys_mem, FST_MEMSIZE );
- error_release_io:
- release_region ( card->pci_conf, 0x80 );
- error_free_card:
- kfree ( card );
- return err;
- }
- /*
- * Cleanup and close down a card
- */
- static void __devexit
- fst_remove_one ( struct pci_dev *pdev )
- {
- struct fst_card_info *card;
- int i;
- card = pci_get_drvdata(pdev);
- for ( i = 0 ; i < card->nports ; i++ )
- {
- unregister_netdev ( card->ports[i].dev );
- kfree ( card->ports[i].dev );
- }
- fst_disable_intr ( card );
- free_irq ( card->irq, card );
- iounmap ( card->ctlmem );
- iounmap ( card->mem );
- release_mem_region ( card->phys_ctlmem, 0x10 );
- release_mem_region ( card->phys_mem, FST_MEMSIZE );
- release_region ( card->pci_conf, 0x80 );
- kfree ( card );
- }
- static struct pci_driver fst_driver = {
- name: FST_NAME,
- id_table: fst_pci_dev_id,
- probe: fst_add_one,
- remove: __devexit_p(fst_remove_one),
- suspend: NULL,
- resume: NULL,
- };
- static int __init
- fst_init(void)
- {
- return pci_module_init ( &fst_driver );
- }
- static void __exit
- fst_cleanup_module(void)
- {
- pci_unregister_driver ( &fst_driver );
- }
- module_init ( fst_init );
- module_exit ( fst_cleanup_module );
- MODULE_LICENSE("GPL");