]> git.neil.brown.name Git - history.git/commitdiff
Import 2.2.11pre7 2.2.11pre7
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:19:20 +0000 (15:19 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:19:20 +0000 (15:19 -0500)
24 files changed:
arch/alpha/kernel/setup.c
drivers/net/Config.in
drivers/net/Makefile
drivers/net/Space.c
drivers/net/sis900.c [new file with mode: 0644]
drivers/sbus/audio/audio.c
drivers/sbus/audio/cs4231.c
drivers/sbus/audio/cs4231.h
include/linux/in.h
include/linux/in6.h
include/linux/socket.h
include/linux/sysctl.h
include/net/sock.h
include/net/tcp.h
kernel/sysctl.c
net/ipv4/af_inet.c
net/ipv4/route.c
net/ipv4/syncookies.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv6/tcp_ipv6.c
net/netsyms.c

index 68726afa57b06793041571cc12febf1314f3740c..15715f469dd6ccddb939f879699ef7d14fd917a0 100644 (file)
@@ -228,7 +228,7 @@ setup_arch(char **cmdline_p, unsigned long * memory_start_p,
 #ifdef CONFIG_ALPHA_GENERIC
               "Booting GENERIC",
 #else
-              "Booting"
+              "Booting",
 #endif
               type_name, (*var_name ? " variation " : ""),
               var_name, alpha_mv.vector_name,
index ad123ec16f5663589b5e86b937eabfda932c78ae..d9b0a339b67d8d526e996124f7d21bd82e0aa03d 100644 (file)
@@ -93,6 +93,7 @@ if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
   fi
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
+      tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900
       tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
index 6f71f8d3470bb08cbe90dc300d631f1e82747416..9cc1e736109916aefb6f1cb6df5a42c4e0f50aed 100644 (file)
@@ -566,6 +566,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_SIS900),y)
+L_OBJS += sis900.o
+else
+  ifeq ($(CONFIG_SIS900),m)
+  M_OBJS += sis900.o
+  endif
+endif
+
 ifeq ($(CONFIG_YELLOWFIN),y)
 L_OBJS += yellowfin.o
 else
index aa014d9cf7776cd9431ce91f6bbe02d473485cff..303c6d1965d752b1703905c52548b2e75e3dc9fa 100644 (file)
@@ -111,6 +111,7 @@ extern int etherh_probe (struct device *dev);
 extern int am79c961_probe(struct device *dev);
 extern int epic100_probe(struct device *dev);
 extern int rtl8139_probe(struct device *dev);
+extern int sis900_probe(struct device *dev);
 extern int hplance_probe(struct device *dev);
 extern int bagetlance_probe(struct device *);
 extern int dec_lance_probe(struct device *);
@@ -205,6 +206,9 @@ struct devprobe pci_probes[] __initdata = {
 #ifdef CONFIG_RTL8139
        {rtl8139_probe, 0},
 #endif
+#ifdef CONFIG_SIS900
+       {sis900_probe, 0},
+#endif
 #ifdef CONFIG_YELLOWFIN
        {yellowfin_probe, 0},
 #endif
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
new file mode 100644 (file)
index 0000000..eddfd47
--- /dev/null
@@ -0,0 +1,1925 @@
+/*****************************************************************************/
+/*      sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux.              */
+/*                                                                           */
+/*                Silicon Integrated System Corporation                      */ 
+/*                Revision:    1.05    Aug 7 1999                           */
+/*                                                                           */
+/*****************************************************************************/
+
+/*                                                                            
+      Modified from the driver which is originally written by Donald Becker. 
+
+      This software may be used and distributed according to the terms
+      of the GNU Public License (GPL), incorporated herein by reference.
+      Drivers based on this skeleton fall under the GPL and must retain
+      the authorship (implicit copyright) notice.
+
+      The author may be reached as becker@tidalwave.net, or
+      Donald Becker
+      312 Severn Ave. #W302
+      Annapolis MD 21403
+
+      Support and updates [to the original skeleton] available at
+      http://www.tidalwave.net/~becker/pci-skeleton.html
+*/
+
+static const char *version =
+"sis900.c:v1.05  8/07/99\n";
+
+static int max_interrupt_work = 20;
+#define sis900_debug debug
+static int sis900_debug = 0;
+
+static int multicast_filter_limit = 128;
+
+#define MAX_UNITS 8             /* More are supported, limit only on options */
+
+#define TX_BUF_SIZE     1536
+#define RX_BUF_SIZE     1536
+
+#define TX_DMA_BURST    0
+#define RX_DMA_BURST    0
+#define TX_FIFO_THRESH  16
+#define TxDRNT_100      (1536>>5)
+#define TxDRNT_10       16 //(1536>>5)
+#define RxDRNT_100      8
+#define RxDRNT_10       8 //(1536>>5)
+#define TRUE            1
+#define FALSE           0
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (4*HZ)
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <asm/processor.h>      /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#define RUN_AT(x) (jiffies + (x))
+
+#include <linux/delay.h>
+
+#if LINUX_VERSION_CODE < 0x20123
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+#if LINUX_VERSION_CODE <= 0x20139
+#define net_device_stats enet_statistics
+#else
+#define NETSTATS_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20155  ||  defined(CARDBUS)
+/* Grrrr, the PCI code changed, but did not consider CardBus... */
+#include <linux/bios32.h>
+#define PCI_SUPPORT_VER1
+#else
+#define PCI_SUPPORT_VER2
+#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
+#else
+#define dev_free_skb(skb) dev_kfree_skb(skb);
+#endif
+
+/* The I/O extent. */
+#define SIS900_TOTAL_SIZE 0x100
+
+/* This table drives the PCI probe routines.  It's mostly boilerplate in all
+   of the drivers, and will likely be provided by some future kernel.
+   Note the matching code -- the first table entry matchs all 56** cards but
+   second only the 1234 card.
+*/
+
+enum pci_flags_bit {
+        PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
+};
+
+struct pci_id_info {
+        const char *name;
+        u16     vendor_id, device_id, device_id_mask, flags;
+        int io_size;
+        struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev,
+                         long ioaddr, int irq, int chip_idx, int fnd_cnt);
+};
+
+static struct device * sis900_probe1(int pci_bus, int pci_devfn,
+                                  struct device *dev, long ioaddr,
+                                  int irq, int chp_idx, int fnd_cnt);
+
+static struct pci_id_info pci_tbl[] =
+{{ "SiS 900 PCI Fast Ethernet",
+   0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
+  { "SiS 7016 PCI Fast Ethernet",
+   0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1},
+ {0,},                                          /* 0 terminated list. */
+};
+
+/* The capability table matches the chip table above. */
+enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04};
+static int sis_cap_tbl[] = {
+        HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
+        HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG,
+};
+
+/* The rest of these values should never change. */
+#define NUM_TX_DESC     16      /* Number of Tx descriptor registers. */
+#define NUM_RX_DESC     8       /* Number of Rx descriptor registers. */
+
+/* Symbolic offsets to registers. */
+enum SIS900_registers {
+        cr=0x0,                 //Command Register
+        cfg=0x4,                //Configuration Register
+        mear=0x8,               //EEPROM Access Register
+        ptscr=0xc,              //PCI Test Control Register
+        isr=0x10,               //Interrupt Status Register
+        imr=0x14,               //Interrupt Mask Register
+        ier=0x18,               //Interrupt Enable Register
+        epar=0x18,              //Enhanced PHY Access Register
+        txdp=0x20,              //Transmit Descriptor Pointer Register
+        txcfg=0x24,             //Transmit Configuration Register
+        rxdp=0x30,              //Receive Descriptor Pointer Register
+        rxcfg=0x34,             //Receive Configuration Register
+        flctrl=0x38,            //Flow Control Register
+        rxlen=0x3c,             //Receive Packet Length Register
+        rfcr=0x48,              //Receive Filter Control Register
+        rfdr=0x4C,              //Receive Filter Data Register
+        pmctrl=0xB0,            //Power Management Control Register
+        pmer=0xB4               //Power Management Wake-up Event Register
+};
+
+#define RESET           0x00000100
+#define SWI             0x00000080
+#define RxRESET         0x00000020
+#define TxRESET         0x00000010
+#define RxDIS           0x00000008
+#define RxENA           0x00000004
+#define TxDIS           0x00000002
+#define TxENA           0x00000001
+
+#define BISE            0x80000000
+#define EUPHCOM         0x00000100
+#define REQALG          0x00000080
+#define SB              0x00000040
+#define POW             0x00000020
+#define EXD             0x00000010
+#define PESEL           0x00000008
+#define LPM             0x00000004
+#define BEM             0x00000001
+
+/* Interrupt register bits, using my own meaningful names. */
+#define WKEVT           0x10000000
+#define TxPAUSEEND      0x08000000
+#define TxPAUSE         0x04000000
+#define TxRCMP          0x02000000
+#define RxRCMP          0x01000000
+#define DPERR           0x00800000
+#define SSERR           0x00400000
+#define RMABT           0x00200000
+#define RTABT           0x00100000
+#define RxSOVR          0x00010000
+#define HIBERR          0x00008000
+#define SWINT           0x00001000
+#define MIBINT          0x00000800
+#define TxURN           0x00000400
+#define TxIDLE          0x00000200
+#define TxERR           0x00000100
+#define TxDESC          0x00000080
+#define TxOK            0x00000040
+#define RxORN           0x00000020
+#define RxIDLE          0x00000010
+#define RxEARLY         0x00000008
+#define RxERR           0x00000004
+#define RxDESC          0x00000002
+#define RxOK            0x00000001
+
+#define IE              0x00000001
+
+#define TxCSI           0x80000000
+#define TxHBI           0x40000000
+#define TxMLB           0x20000000
+#define TxATP           0x10000000
+#define TxIFG           0x0C000000
+#define TxMXF           0x03800000
+#define TxMXF_shift     0x23
+#define TxMXDMA         0x00700000
+#define TxMXDMA_shift   20
+#define TxRTCNT         0x000F0000
+#define TxRTCNT_shift   16
+#define TxFILLT         0x00007F00
+#define TxFILLT_shift   8
+#define TxDRNT          0x0000007F
+
+#define RxAEP           0x80000000
+#define RxARP           0x40000000
+#define RxATP           0x10000000
+#define RxAJAB          0x08000000
+#define RxMXF           0x03800000
+#define RxMXF_shift     23
+#define RxMXDMA         0x00700000
+#define RxMXDMA_shift   20
+#define RxDRNT          0x0000007F
+
+#define RFEN            0x80000000
+#define RFAAB           0x40000000
+#define RFAAM           0x20000000
+#define RFAAP           0x10000000
+#define RFPromiscuous   (RFAAB|RFAAM|RFAAP)
+#define RFAA_shift      28
+#define RFEP            0x00070000
+#define RFEP_shift      16
+
+#define RFDAT           0x0000FFFF
+
+#define OWN             0x80000000
+#define MORE            0x40000000
+#define INTR            0x20000000
+#define OK              0x08000000
+#define DSIZE           0x00000FFF
+
+#define SUPCRC          0x10000000
+#define ABORT           0x04000000
+#define UNDERRUN        0x02000000
+#define NOCARRIER       0x01000000
+#define DEFERD          0x00800000
+#define EXCDEFER        0x00400000
+#define OWCOLL          0x00200000
+#define EXCCOLL         0x00100000
+#define COLCNT          0x000F0000
+
+#define INCCRC          0x10000000
+//      ABORT           0x04000000
+#define OVERRUN         0x02000000
+#define DEST            0x01800000
+#define BCAST           0x01800000
+#define MCAST           0x01000000
+#define UNIMATCH        0x00800000
+#define TOOLONG         0x00400000
+#define RUNT            0x00200000
+#define RXISERR         0x00100000
+#define CRCERR          0x00080000
+#define FAERR           0x00040000
+#define LOOPBK          0x00020000
+#define RXCOL           0x00010000
+
+#define EuphLiteEEMACAddr               0x08
+#define EuphLiteEEVendorID              0x02
+#define EuphLiteEEDeviceID              0x03
+#define EuphLiteEECardTypeRev           0x0b
+#define EuphLiteEEPlexusRev             0x0c
+#define EuphLiteEEChecksum              0x0f
+
+#define RXSTS_shift     18
+#define OWN             0x80000000
+#define MORE            0x40000000
+#define INTR            0x20000000
+#define OK              0x08000000
+#define DSIZE           0x00000FFF
+/* MII register offsets */
+#define MII_CONTROL             0x0000
+#define MII_STATUS              0x0001
+#define MII_PHY_ID0             0x0002
+#define MII_PHY_ID1             0x0003
+#define MII_ANAR                0x0004
+#define MII_ANLPAR              0x0005
+#define MII_ANER                0x0006
+/* MII Control register bit definitions. */
+#define MIICNTL_FDX             0x0100
+#define MIICNTL_RST_AUTO        0x0200
+#define MIICNTL_ISOLATE         0x0400
+#define MIICNTL_PWRDWN          0x0800
+#define MIICNTL_AUTO            0x1000
+#define MIICNTL_SPEED           0x2000
+#define MIICNTL_LPBK            0x4000
+#define MIICNTL_RESET           0x8000
+/* MII Status register bit significance. */
+#define MIISTAT_EXT             0x0001
+#define MIISTAT_JAB             0x0002
+#define MIISTAT_LINK            0x0004
+#define MIISTAT_CAN_AUTO        0x0008
+#define MIISTAT_FAULT           0x0010
+#define MIISTAT_AUTO_DONE       0x0020
+#define MIISTAT_CAN_T           0x0800
+#define MIISTAT_CAN_T_FDX       0x1000
+#define MIISTAT_CAN_TX          0x2000
+#define MIISTAT_CAN_TX_FDX      0x4000
+#define MIISTAT_CAN_T4          0x8000
+/* MII NWAY Register Bits ...
+** valid for the ANAR (Auto-Negotiation Advertisement) and
+** ANLPAR (Auto-Negotiation Link Partner) registers */
+#define MII_NWAY_NODE_SEL       0x001f
+#define MII_NWAY_CSMA_CD        0x0001
+#define MII_NWAY_T              0x0020
+#define MII_NWAY_T_FDX          0x0040
+#define MII_NWAY_TX             0x0080
+#define MII_NWAY_TX_FDX         0x0100
+#define MII_NWAY_T4             0x0200
+#define MII_NWAY_RF             0x2000
+#define MII_NWAY_ACK            0x4000
+#define MII_NWAY_NP             0x8000
+
+/* MII Auto-Negotiation Expansion Register Bits */
+#define MII_ANER_PDF            0x0010
+#define MII_ANER_LP_NP_ABLE     0x0008
+#define MII_ANER_NP_ABLE        0x0004
+#define MII_ANER_RX_PAGE        0x0002
+#define MII_ANER_LP_AN_ABLE     0x0001
+#define HALF_DUPLEX                     1
+#define FDX_CAPABLE_DUPLEX_UNKNOWN      2
+#define FDX_CAPABLE_HALF_SELECTED       3
+#define FDX_CAPABLE_FULL_SELECTED       4
+#define HW_SPEED_UNCONFIG       0
+#define HW_SPEED_10_MBPS        10
+#define HW_SPEED_100_MBPS       100
+#define HW_SPEED_DEFAULT        (HW_SPEED_10_MBPS)
+
+#define ACCEPT_ALL_PHYS         0x01
+#define ACCEPT_ALL_MCASTS       0x02
+#define ACCEPT_ALL_BCASTS       0x04
+#define ACCEPT_ALL_ERRORS       0x08
+#define ACCEPT_CAM_QUALIFIED    0x10
+#define MAC_LOOPBACK            0x20
+//#define FDX_CAPABLE_FULL_SELECTED     4
+#define CRC_SIZE                4
+#define MAC_HEADER_SIZE         14
+
+typedef struct _EuphLiteDesc {
+        u32     llink;
+        unsigned char*  buf;
+        u32     physAddr;
+        /* Hardware sees the physical address of descriptor */
+        u32     plink;
+        u32     cmdsts;
+        u32     bufPhys;
+} EuphLiteDesc;
+
+struct sis900_private {
+        char devname[8];                /* Used only for kernel debugging. */
+        const char *product_name;
+        struct device *next_module;
+        int chip_id;
+        int chip_revision;
+        unsigned char pci_bus, pci_devfn;
+#if LINUX_VERSION_CODE > 0x20139
+        struct net_device_stats stats;
+#else
+        struct enet_statistics stats;
+#endif
+        struct timer_list timer;        /* Media selection timer. */
+        unsigned int cur_rx;    /* Index into the Rx buffer of next Rx pkt. */
+        unsigned int cur_tx, dirty_tx, tx_flag;
+
+        /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+        struct sk_buff* tx_skbuff[NUM_TX_DESC];
+        EuphLiteDesc tx_buf[NUM_TX_DESC];       /* Tx bounce buffers */
+        EuphLiteDesc rx_buf[NUM_RX_DESC];
+        unsigned char *rx_bufs;
+        unsigned char *tx_bufs;                 /* Tx bounce buffer region. */
+        char phys[4];                           /* MII device addresses. */
+        int phy_idx;
+        u16 pmd_status;
+        unsigned int tx_full;                   /* The Tx queue is full. */
+        u16 full_duplex;        /* FullHalf-duplex. */
+        u16 hunmbps;            /* 10010 Mbps. */
+        u16 LinkOn;
+        u16 LinkChange;
+};
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20115
+MODULE_AUTHOR("Silicon Integrated Systems Corporation");
+MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(multicast_filter_limit, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(debug, "i");
+#endif
+#endif
+
+static int sis900_open(struct device *dev);
+static u16 read_eeprom(long ioaddr, int location);
+static int mdio_read(struct device *dev, int phy_id, int location);
+static void mdio_write(struct device *dev, int phy_id, int location, int val);
+static void sis900_timer(unsigned long data);
+static void sis900_tx_timeout(struct device *dev);
+static void sis900_init_ring(struct device *dev);
+static int sis900_start_xmit(struct sk_buff *skb, struct device *dev);
+static int sis900_rx(struct device *dev);
+static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int sis900_close(struct device *dev);
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *sis900_get_stats(struct device *dev);
+static void set_rx_mode(struct device *dev);
+static void sis900_reset(struct device *dev);
+static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed);
+static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex);
+static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value);
+static void elSetMediaType(struct device *dev, int speed, int duplex);
+
+/* A list of all installed SiS900 devices, for removing the driver module. */
+static struct device *root_sis900_dev = NULL;
+
+/* Ideally we would detect all network cards in slot order.  That would
+   be best done a central PCI probe dispatch, which wouldn't work
+   well when dynamically adding drivers.  So instead we detect just the
+   SiS 900 cards in slot order. */
+
+int sis900_probe(struct device *dev)
+{
+        int cards_found = 0;
+        int pci_index = 0;
+        unsigned char pci_bus, pci_device_fn;
+
+        if ( ! pcibios_present())
+                return -ENODEV;
+
+        for (;pci_index < 0xff; pci_index++) {
+                u16 vendor, device, pci_command, new_command;
+                int chip_idx, irq;
+                long ioaddr;
+
+                if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
+                                                pci_index,
+                                                &pci_bus, &pci_device_fn)
+                                != PCIBIOS_SUCCESSFUL) {
+                        break;
+                }
+                pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
+                                                        &vendor);
+                pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
+                                                        &device);
+
+                for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++)
+                        if (vendor == pci_tbl[chip_idx].vendor_id &&
+                                (device & pci_tbl[chip_idx].device_id_mask) ==
+                                pci_tbl[chip_idx].device_id)
+                                break;
+                if (pci_tbl[chip_idx].vendor_id == 0)   /* Compiled out! */
+                        continue;
+
+        {
+#if defined(PCI_SUPPORT_VER2)
+                struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+                ioaddr = pdev->base_address[0] & ~3;
+                irq = pdev->irq;
+#else
+                u32 pci_ioaddr;
+                u8 pci_irq_line;
+                pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                         PCI_INTERRUPT_LINE, &pci_irq_line);
+                pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                         PCI_BASE_ADDRESS_0, &pci_ioaddr);
+                ioaddr = pci_ioaddr & ~3;
+                irq = pci_irq_line;
+#endif
+        }
+
+                if ((pci_tbl[chip_idx].flags & PCI_USES_IO) &&
+                        check_region(ioaddr, pci_tbl[chip_idx].io_size))
+                        continue;
+
+                /* Activate the card: fix for brain-damaged Win98 BIOSes. */
+                pcibios_read_config_word(pci_bus, pci_device_fn,
+                                         PCI_COMMAND, &pci_command);
+                new_command = pci_command | (pci_tbl[chip_idx].flags & 7);
+                if (pci_command != new_command) {
+                        printk(KERN_INFO "  The PCI BIOS has not enabled the"
+                                   " device at %d/%d!"
+                                   "Updating PCI command %4.4x->%4.4x.\n",
+                                        pci_bus, pci_device_fn,
+                                        pci_command, new_command);
+
+                        pcibios_write_config_word(pci_bus, pci_device_fn,
+                                                  PCI_COMMAND, new_command);
+                }
+
+                dev = pci_tbl[chip_idx].probe1(pci_bus,
+                                                pci_device_fn,
+                                                dev,
+                                                ioaddr,
+                                                irq,
+                                                chip_idx,
+                                                cards_found);
+
+                if (dev  && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) {
+                        u8 pci_latency;
+
+                        pcibios_read_config_byte(pci_bus, pci_device_fn,
+                                        PCI_LATENCY_TIMER, &pci_latency);
+
+                        if (pci_latency < 32) {
+                           printk(KERN_NOTICE "  PCI latency timer (CFLT) is "
+                           "unreasonably low at %d.  Setting to 64 clocks.\n",
+                                           pci_latency);
+                           pcibios_write_config_byte(pci_bus, pci_device_fn,
+                                          PCI_LATENCY_TIMER, 64);
+                        }
+                }
+                dev = 0;
+                cards_found++;
+        }
+        return cards_found ? 0 : -ENODEV;
+}
+
+static struct device * sis900_probe1(   int pci_bus,
+                                        int pci_devfn,
+                                        struct device *dev,
+                                        long ioaddr,
+                                        int irq,
+                                        int chip_idx,
+                                        int found_cnt)
+{
+        static int did_version = 0;     /* Already printed version info. */
+        struct sis900_private *tp;
+        int     duplex=0;
+        int     speed=0;
+        int i;
+
+        if (did_version++ == 0)
+                printk(KERN_INFO "%s", version);
+
+        dev = init_etherdev(dev, 0);
+
+        printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ",
+                   dev->name, pci_tbl[chip_idx].name, ioaddr, irq);
+
+        if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) {
+                for (i = 0; i < 3; i++)
+                        ((u16 *)(dev->dev_addr))[i] =
+                                        read_eeprom(ioaddr,i+EuphLiteEEMACAddr);
+                for (i = 0; i < 5; i++)
+                        printk("%2.2x:", (u8)dev->dev_addr[i]);
+                printk("%2.2x.\n", dev->dev_addr[i]);
+        } else
+                printk(KERN_INFO "Error EEPROM read\n");
+
+        /* We do a request_region() to register /proc/ioports info. */
+        request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
+
+        dev->base_addr = ioaddr;
+        dev->irq = irq;
+
+        /* Some data structures must be quadword aligned. */
+        tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
+        memset(tp, 0, sizeof(*tp));
+        dev->priv = tp;
+
+        tp->next_module = root_sis900_dev;
+        root_sis900_dev = dev;
+
+        tp->chip_id = chip_idx;
+        tp->pci_bus = pci_bus;
+        tp->pci_devfn = pci_devfn;
+
+        /* Find the connected MII xcvrs.
+           Doing this in open() would allow detecting external xcvrs later, but
+           takes too much time. */
+        if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) {
+                int phy, phy_idx;
+                for (phy = 0, phy_idx = 0;
+                        phy < 32 && phy_idx < sizeof(tp->phys); phy++)
+                {
+                        int mii_status = mdio_read(dev, phy, MII_STATUS);
+                        /*
+                        {
+                           int p;
+                           int l;
+                           for (p = 0 ; p < 4 ; p ++) {
+                                for (l=0 ; l<16 ; l++) {
+                                  int status = mdio_read(dev, phy, l);
+                                  status = mdio_read(dev, phy, l);
+                                  printk(KERN_INFO "MII info addr[%d][%d]=%x\n",
+                                        p, l, status);
+                                }
+                           }
+                        }
+                        */
+
+                        if (mii_status != 0xffff && mii_status != 0x0000) {
+                                tp->phy_idx = phy_idx;
+                                tp->phys[phy_idx++] = phy;
+                                tp->pmd_status=mdio_read(dev, phy, MII_STATUS);
+                                printk(KERN_INFO "%s: MII transceiver found "
+                                                 "at address %d.\n",
+                                                 dev->name, phy);
+                                break;
+                        }
+                }
+
+                if (phy_idx == 0) {
+                        printk(KERN_INFO "%s: No MII transceivers found!",
+                                        dev->name);
+                        printk(KERN_INFO "Assuming SYM transceiver.\n");
+                        tp->phys[0] = -1;
+                }
+        } else {
+                        tp->phys[0] = 32;
+        }
+
+        if (tp->pmd_status > 0) {
+                tp->pmd_status=
+                   elAutoNegotiate(dev, tp->phys[tp->phy_idx], &duplex, &speed);
+                if (tp->pmd_status & MIISTAT_LINK) {
+                        if (duplex == FDX_CAPABLE_FULL_SELECTED)
+                                tp->full_duplex=1;
+                        if (speed == HW_SPEED_100_MBPS)
+                                tp->hunmbps=1;
+                        tp->LinkOn = TRUE;
+                } else {
+                        tp->LinkOn = FALSE;
+                }
+                tp->LinkChange = FALSE;
+        }
+
+        /*
+        if (found_cnt < MAX_UNITS  &&  full_duplex[found_cnt] > 0)
+                tp->full_duplex = full_duplex[found_cnt];
+
+        if (tp->full_duplex) {
+                printk(KERN_INFO "%s: Media type is Full Duplex.\n", dev->name);
+        } else {
+                printk(KERN_INFO "%s: Media type is Half Duplex.\n", dev->name);
+        }
+        if (tp->hunmbps) {
+                printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name);
+        } else {
+                printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name);
+        }
+        */
+
+        /* The SiS900-specific entries in the device structure. */
+        dev->open = &sis900_open;
+        dev->hard_start_xmit = &sis900_start_xmit;
+        dev->stop = &sis900_close;
+        dev->get_stats = &sis900_get_stats;
+        dev->set_multicast_list = &set_rx_mode;
+        dev->do_ioctl = &mii_ioctl;
+
+        return dev;
+}
+
+/* Serial EEPROM section. */
+
+/*  EEPROM_Ctrl bits. */
+#define EECLK           0x00000004      /* EEPROM shift clock. */
+#define EECS            0x00000008      /* EEPROM chip select. */
+#define EEDO            0x00000002      /* EEPROM chip data out. */
+#define EEDI            0x00000001      /* EEPROM chip data in. */
+
+/* Delay between EEPROM clock transitions.
+   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
+ */
+
+#define eeprom_delay()  inl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EEread          0x0180
+#define EEwrite         0x0140
+#define EEerase         0x01C0
+#define EEwriteEnable   0x0130
+#define EEwriteDisable  0x0100
+#define EEeraseAll      0x0120
+#define EEwriteAll      0x0110
+#define EEaddrMask      0x013F
+#define EEcmdShift      16
+
+static u16 read_eeprom(long ioaddr, int location)
+{
+        int i;
+        u16 retval = 0;
+        long ee_addr = ioaddr + mear;
+        u32 read_cmd = location | EEread;
+
+        outl(0, ee_addr);
+        eeprom_delay();
+        outl(EECLK, ee_addr);
+        eeprom_delay();
+
+        /* Shift the read command bits out. */
+        for (i = 8; i >= 0; i--) {
+                u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS;
+                outl(dataval, ee_addr);
+                eeprom_delay();
+                outl(dataval | EECLK, ee_addr);
+                eeprom_delay();
+        }
+        outb(EECS, ee_addr);
+        eeprom_delay();
+
+        for (i = 16; i > 0; i--) {
+                outl(EECS, ee_addr);
+                eeprom_delay();
+                outl(EECS | EECLK, ee_addr);
+                eeprom_delay();
+                retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0);
+                eeprom_delay();
+        }
+
+        /* Terminate the EEPROM access. */
+        outl(0, ee_addr);
+        eeprom_delay();
+        outl(EECLK, ee_addr);
+        return (retval);
+}
+
+/* MII serial management: mostly bogus for now. */
+/* Read and write the MII management registers using software-generated
+   serial MDIO protocol.
+   The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
+   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+   "overclocking" issues. */
+
+#define mdio_delay()    inl(mdio_addr)
+
+#define MIIread         0x6000
+#define MIIwrite        0x6002
+#define MIIpmdMask      0x0F80
+#define MIIpmdShift     7
+#define MIIregMask      0x007C
+#define MIIregShift     2
+#define MIIturnaroundBits       2
+#define MIIcmdLen       16
+#define MIIcmdShift     16
+#define MIIreset        0xFFFFFFFF
+#define MIIwrLen        32
+
+#define MDC             0x00000040
+#define MDDIR           0x00000020
+#define MDIO            0x00000010
+
+static void mdio_idle(long mdio_addr)
+{
+        outl(MDIO | MDDIR, mdio_addr);
+        mdio_delay();
+        outl(MDIO | MDDIR | MDC, mdio_addr);
+}
+
+/* Syncronize the MII management interface by shifting 32 one bits out. */
+static void mdio_reset(long mdio_addr)
+{
+        int i;
+
+        for (i = 31; i >= 0; i--) {
+                outl(MDDIR | MDIO, mdio_addr);
+                mdio_delay();
+                outl(MDDIR | MDIO | MDC, mdio_addr);
+                mdio_delay();
+        }
+        return;
+}
+
+static int mdio_read(struct device *dev, int phy_id, int location)
+{
+        long mdio_addr = dev->base_addr + mear;
+        int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+        int retval = 0;
+        int i;
+
+        mdio_reset(mdio_addr);
+        mdio_idle(mdio_addr);
+
+        for (i = 15; i >= 0; i--) {
+                int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+                outl(dataval, mdio_addr);
+                outl(dataval | MDC, mdio_addr);
+        }
+
+        /* Read the two transition, 16 data, and wire-idle bits. */
+        for (i = 16; i > 0; i--) {
+                outl(0, mdio_addr);
+                //mdio_delay();
+                retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0);
+                outl(MDC, mdio_addr);
+                mdio_delay();
+        }
+        return retval;
+}
+
+static void mdio_write(struct device *dev, int phy_id, int location, int value)
+{
+        long mdio_addr = dev->base_addr + mear;
+        int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
+        int i;
+
+        mdio_reset(mdio_addr);
+        mdio_idle(mdio_addr);
+
+        /* Shift the command bits out. */
+        for (i = 31; i >= 0; i--) {
+                int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR;
+                outb(dataval, mdio_addr);
+                mdio_delay();
+                outb(dataval | MDC, mdio_addr);
+                mdio_delay();
+        }
+        mdio_delay();
+        /* Clear out extra bits. */
+        for (i = 2; i > 0; i--) {
+                outb(0, mdio_addr);
+                mdio_delay();
+                outb(MDC, mdio_addr);
+                mdio_delay();
+        }
+        return;
+}
+
+static int
+sis900_open(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr;
+
+        if (sis900_debug > 0)
+                printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n",
+                                dev->name, (unsigned int)ioaddr, dev->irq);
+
+        /* Soft reset the chip. */
+        outl(0, ioaddr + imr);
+        outl(0, ioaddr + ier);
+        outl(0, ioaddr + rfcr);
+        outl(RESET | RxRESET | TxRESET, ioaddr + cr);
+
+        if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev))
+        {
+                return -EAGAIN;
+        }
+
+        MOD_INC_USE_COUNT;
+
+        tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL);
+        tp->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL);
+        if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) {
+                if (tp->tx_bufs)
+                        kfree(tp->tx_bufs);
+                if (sis900_debug > 0)
+                        printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n",
+                                   dev->name, TX_BUF_SIZE * NUM_TX_DESC);
+                return -ENOMEM;
+        }
+
+        {
+                u32 rfcrSave;
+                u32 w;
+                u32 i;
+
+                rfcrSave = inl(rfcr);
+                outl(rfcrSave & ~RFEN, rfcr);
+                for (i=0 ; i<3 ; i++) {
+                        w = (u16)*((u16*)(dev->dev_addr)+i);
+                        outl((((u32) i) << RFEP_shift), ioaddr + rfcr);
+                        outl((u32)w, ioaddr + rfdr);
+                        if (sis900_debug > 4) {
+                                printk(KERN_INFO "Filter Addr[%d]=%x\n",
+                                        i, inl(ioaddr + rfdr));
+                        }
+                }
+                /*
+                for (i=0 ; i<3 ; i++) {
+                        outl((((u32) i) << RFEP_shift), ioaddr + rfcr);
+                        if (sis900_debug > 4) {
+                                printk(KERN_INFO "Read Filter Addr[%d]=%x\n",
+                                        i, inl(ioaddr + rfdr));
+                        }
+                }
+                */
+                outl(rfcrSave, rfcr);
+        }
+
+        sis900_init_ring(dev);
+        outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp);
+        outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp);
+
+        if (sis900_debug > 4)
+                printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp));
+
+        /* Check that the chip has finished the reset. */
+        {
+                u32 status;
+                int j=0;
+                status = TxRCMP | RxRCMP;
+                while (status && (j++ < 30000)) {
+                        status ^= (inl(isr) & status);
+                }
+        }
+
+        outl(PESEL, ioaddr + cfg);
+
+        /* Must enable Tx/Rx before setting transfer thresholds! */
+        /*
+                #define TX_DMA_BURST    0
+                #define RX_DMA_BURST    0
+                #define TX_FIFO_THRESH  16
+                #define TxDRNT_100      (1536>>5)
+                #define TxDRNT_10       (1536>>5)
+                #define RxDRNT_100      (1536>>5)
+                #define RxDRNT_10       (1536>>5)
+        */
+        //outl(RxENA | TxENA, ioaddr + cr);
+        outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg);
+        outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10,
+                                                ioaddr + txcfg);
+        //tp->full_duplex = tp->duplex_lock;
+        if (tp->phys[tp->phy_idx] >= 0  ||
+                        (sis_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) {
+                if (sis900_debug > 1)
+                        if (tp->LinkOn) {
+                          printk(KERN_INFO"%s: Setting %s%s-duplex.\n",
+                                dev->name,
+                                tp->hunmbps ? "100mbps " : "10mbps ",
+                                tp->full_duplex ? "full" : "half");
+                        } else {
+                          printk(KERN_INFO"%s: Media Link Off\n", dev->name);
+                        }
+        }
+        set_rx_mode(dev);
+
+        dev->tbusy = 0;
+        dev->interrupt = 0;
+        dev->start = 1;
+
+        /* Enable all known interrupts by setting the interrupt mask. */
+        outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
+        outl(RxENA, ioaddr + cr);
+        outl(IE, ioaddr + ier);
+
+        if (sis900_debug > 1)
+                printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n",
+                           dev->name, ioaddr, dev->irq);
+
+        /* Set the timer to switch to check for link beat and perhaps switch
+           to an alternate media type. */
+        init_timer(&tp->timer);
+        tp->timer.expires = RUN_AT((24*HZ)/10);         /* 2.4 sec. */
+        tp->timer.data = (unsigned long)dev;
+        tp->timer.function = &sis900_timer;             /* timer handler */
+        add_timer(&tp->timer);
+
+        return 0;
+}
+
+static void sis900_timer(unsigned long data)
+{
+        struct device *dev = (struct device *)data;
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr;
+        int next_tick = 0;
+        int duplex, full_duplex=0;
+        int speed, hunmbps=0;
+        u16 status;
+
+//      printk(KERN_INFO "%s: SiS900 timer\n", dev->name);
+        elMIIpollBit(dev, tp->phys[tp->phy_idx], MII_STATUS, MIISTAT_LINK, TRUE, &status);
+        if (status & MIISTAT_LINK) {
+//              printk(KERN_INFO "%s: SiS900 timer link\n", dev->name);
+                /*
+                if (!tp->LinkOn) {
+                        printk(KERN_INFO "%s: AutoNegotiate ...\n", dev->name);
+                        tp->LinkChange=TRUE;
+                        tp->pmd_status=
+                          elAutoNegotiate(dev, tp->phys[tp->phy_idx],
+                                                &duplex, &speed);
+                }
+                 else {
+                        printk(KERN_INFO "%s: Link Still On.\n", dev->name);
+                        elPMDreadMode(dev, tp->phys[tp->phy_idx],
+                                                &speed, &duplex);
+                }
+                */
+                elPMDreadMode(dev, tp->phys[tp->phy_idx],
+                                                &speed, &duplex);
+
+
+                if (duplex == FDX_CAPABLE_FULL_SELECTED) full_duplex=1;
+                if (speed == HW_SPEED_100_MBPS) hunmbps=1;
+                if (full_duplex != tp->full_duplex || hunmbps != tp->hunmbps)
+                        tp->LinkChange = TRUE;
+                if (tp->LinkChange) {
+                        tp->full_duplex=full_duplex;
+                        tp->hunmbps=hunmbps;
+                        //elSetMediaType(dev, speed, duplex);
+                        printk(KERN_INFO "%s: Setting %s%s-duplex based on MII "
+                                   "#%d link partner ability.\n",
+                                   dev->name,
+                                   tp->hunmbps ? "100mbps " : "10mbps ",
+                                   tp->full_duplex ? "full" : "half",
+                                   tp->phys[0]);
+                }
+                tp->LinkOn=TRUE;
+                tp->LinkChange = FALSE;
+        } else {
+                if (tp->LinkOn) {
+                        tp->LinkChange = TRUE;
+                        tp->pmd_status=
+                          elAutoNegotiate(dev, tp->phys[tp->phy_idx],
+                                                &duplex, &speed);
+                        if (tp->pmd_status & MIISTAT_LINK) {
+                                if (duplex == FDX_CAPABLE_FULL_SELECTED)
+                                        tp->full_duplex=1;
+                                if (speed == HW_SPEED_100_MBPS)
+                                        tp->hunmbps=1;
+                        } else {
+                                tp->LinkOn = FALSE;
+                                printk(KERN_INFO "%s: Link Off\n", dev->name);
+                        }
+                }
+//              printk(KERN_INFO "%s: Link Off\n", dev->name);
+        }
+        next_tick = 2*HZ;
+
+        if (sis900_debug > 3) {
+                printk(KERN_INFO "%s:  Other registers are IntMask "
+                                "%4.4x IntStatus %4.4x"
+                                " RxStatus %4.4x.\n",
+                                dev->name,
+                                inw(ioaddr + imr),
+                                inw(ioaddr + isr),
+                                inl(ioaddr + rxcfg));
+        }
+
+        if (next_tick) {
+                tp->timer.expires = RUN_AT(next_tick);
+                add_timer(&tp->timer);
+        }
+}
+
+static void sis900_tx_timeout(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr;
+        int i;
+
+        if (sis900_debug > 0)
+                printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n",
+                           dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
+
+        /* Disable interrupts by clearing the interrupt mask. */
+        outl(0x0000, ioaddr + imr);
+        /* Emit info to figure out what went wrong. */
+        printk(KERN_INFO "%s: Tx queue start entry %d  dirty entry %d.\n",
+                   dev->name, tp->cur_tx, tp->dirty_tx);
+        for (i = 0; i < NUM_TX_DESC; i++)
+                printk(KERN_INFO "%s:  Tx descriptor %d is %8.8x.%s\n",
+                           dev->name, i, (unsigned int)&tp->tx_buf[i],
+                           i == tp->dirty_tx % NUM_TX_DESC ?
+                           " (queue head)" : "");
+        /*
+        printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]);
+        for (mii_reg = 0; mii_reg < 8; mii_reg++)
+                printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg));
+        printk(".\n");
+        */
+
+        /* Soft reset the chip. */
+
+        tp->cur_rx = 0; //??????
+        /* Must enable Tx/Rx before setting transfer thresholds! */
+        /*
+        set_rx_mode(dev);
+        */
+        {       /* Save the unsent Tx packets. */
+                struct sk_buff *saved_skb[NUM_TX_DESC], *skb;
+                int j;
+                for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++)
+                        saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC];
+                tp->dirty_tx = tp->cur_tx = 0;
+
+                for (i = 0; i < j; i++) {
+                        skb = tp->tx_skbuff[i] = saved_skb[i];
+                        /* Always alignment */
+                        memcpy((unsigned char*)(tp->tx_buf[i].buf),
+                                                skb->data, skb->len);
+                        tp->tx_buf[i].cmdsts = OWN | skb->len;
+                        /* Note: the chip doesn't have auto-pad! */
+                        /*
+                        outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN),
+                                 ioaddr + TxStatus0 + i*4);
+                        */
+                }
+                outl(TxENA, ioaddr + cr);
+                tp->cur_tx = i;
+                while (i < NUM_TX_DESC)
+                        tp->tx_skbuff[i++] = 0;
+                if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
+                        dev->tbusy = 0;
+                        tp->tx_full = 0;
+                } else {
+                        tp->tx_full = 1;
+                }
+        }
+
+        dev->trans_start = jiffies;
+        tp->stats.tx_errors++;
+        /* Enable all known interrupts by setting the interrupt mask. */
+        outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr);
+        return;
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+sis900_init_ring(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        int i;
+
+        tp->tx_full = 0;
+        tp->cur_rx = 0;
+        tp->dirty_tx = tp->cur_tx = 0;
+
+        /* Tx Buffer */
+        for (i = 0; i < NUM_TX_DESC; i++) {
+                tp->tx_skbuff[i] = 0;
+                tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE];
+                tp->tx_buf[i].bufPhys =
+                                virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]);
+                /*
+                printk(KERN_INFO "tp->tx_buf[%d].bufPhys=%8.8x\n",
+                                i, tp->tx_buf[i].bufPhys);
+                */
+        }
+
+        /* Tx Descriptor */
+        for (i = 0; i< NUM_TX_DESC; i++) {
+                tp->tx_buf[i].llink = (u32)
+                        &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]);
+                tp->tx_buf[i].plink = (u32)
+                        virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ?
+                                (i+1) : 0].plink));
+                tp->tx_buf[i].physAddr=
+                                virt_to_bus(&(tp->tx_buf[i].plink));
+                tp->tx_buf[i].cmdsts=0;
+
+                /*
+                printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n",
+                                i, tp->tx_buf[i].physAddr);
+                */
+        }
+
+        /* Rx Buffer */
+        for (i = 0; i < NUM_RX_DESC; i++) {
+                tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE];
+                tp->rx_buf[i].bufPhys =
+                                virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]);
+                /*
+                printk(KERN_INFO "tp->rx_buf[%d].bufPhys=%8.8x\n",
+                                i, tp->rx_buf[i].bufPhys);
+                */
+        }
+
+        /* Rx Descriptor */
+        for (i = 0; i< NUM_RX_DESC; i++) {
+                tp->rx_buf[i].llink = (u32)
+                        &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]);
+                tp->rx_buf[i].plink = (u32)
+                        virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ?
+                                (i+1) : 0].plink));
+                tp->rx_buf[i].physAddr=
+                                virt_to_bus(&(tp->rx_buf[i].plink));
+                tp->rx_buf[i].cmdsts=RX_BUF_SIZE;
+                /*
+                printk(KERN_INFO "tp->tx_buf[%d].PhysAddr=%8.8x\n",
+                                i, tp->tx_buf[i].physAddr);
+                */
+        }
+}
+
+static int
+sis900_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr;
+        int entry;
+
+        /* Block a timer-based transmit from overlapping.  This could better be
+           done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+        if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+                if (jiffies - dev->trans_start < TX_TIMEOUT)
+                        return 1;
+                sis900_tx_timeout(dev);
+                return 1;
+        }
+
+        /* Calculate the next Tx descriptor entry. ????? */
+        entry = tp->cur_tx % NUM_TX_DESC;
+
+        tp->tx_skbuff[entry] = skb;
+
+        if (sis900_debug > 5) {
+                int i;
+                printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)",
+                                                dev->name,skb->len);
+
+                for (i = 0; i < skb->len; i++) {
+                        printk("%2.2x ",
+                        (u8)skb->data[i]);
+                }
+                printk(".\n");
+        }
+
+        memcpy(tp->tx_buf[entry].buf,
+                                skb->data, skb->len);
+
+        tp->tx_buf[entry].cmdsts=(OWN | skb->len);
+
+        outl(TxENA, ioaddr + cr);
+        if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */
+                clear_bit(0, (void*)&dev->tbusy);
+        } else {
+                tp->tx_full = 1;
+        }
+
+        /* Note: the chip doesn't have auto-pad! */
+
+        dev->trans_start = jiffies;
+        if (sis900_debug > 4)
+                printk(KERN_INFO "%s: Queued Tx packet at "
+                                "%p size %d to slot %d.\n",
+                           dev->name, skb->data, (int)skb->len, entry);
+
+        return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+        struct device *dev = (struct device *)dev_instance;
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        int boguscnt = max_interrupt_work;
+        int status;
+        long ioaddr = dev->base_addr;
+
+#if defined(__i386__)
+        /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
+        if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+                printk(KERN_INFO "%s: SMP simultaneous entry of "
+                                "an interrupt handler.\n", dev->name);
+                dev->interrupt = 0;     /* Avoid halting machine. */
+                return;
+        }
+#else
+        if (dev->interrupt) {
+                printk(KERN_INFO "%s: Re-entering the "
+                                "interrupt handler.\n", dev->name);
+                return;
+        }
+        dev->interrupt = 1;
+#endif
+
+        do {
+                status = inl(ioaddr + isr);
+                /* Acknowledge all of the current interrupt sources ASAP. */
+                outl(status, ioaddr + isr); // ?????
+
+                if (sis900_debug > 4)
+                        printk(KERN_INFO "%s: interrupt  status=%#4.4x "
+                                "new intstat=%#4.4x.\n",
+                                dev->name, status, inl(ioaddr + isr));
+
+                if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) {
+                        break;
+                }
+
+                if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */
+                        sis900_rx(dev);
+
+                if (status & (TxOK | TxERR)) {
+                        unsigned int dirty_tx;
+
+                        if (sis900_debug > 5) {
+                                printk(KERN_INFO "TxOK:tp->cur_tx:%d,"
+                                                "tp->dirty_tx:%x\n",
+                                        tp->cur_tx, tp->dirty_tx);
+                        }
+                        for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx;
+                                dirty_tx++)
+                        {
+                                int i;
+                                int entry = dirty_tx % NUM_TX_DESC;
+                                int txstatus = tp->tx_buf[entry].cmdsts;
+
+                                if (sis900_debug > 4) {
+                                        printk(KERN_INFO "%s:     Tx Frame contents:"
+                                                "(len=%d)",
+                                                dev->name, (txstatus & DSIZE));
+
+                                        for (i = 0; i < (txstatus & DSIZE) ;
+                                                                        i++) {
+                                                printk("%2.2x ",
+                                                (u8)(tp->tx_buf[entry].buf[i]));
+                                        }
+                                        printk(".\n");
+                                }
+                                if ( ! (txstatus & (OK | UNDERRUN)))
+                                {
+                                        if (sis900_debug > 1)
+                                                printk(KERN_INFO "Tx NOT (OK,"
+                                                        "UnderRun)\n");
+                                        break;  /* It still hasn't been Txed */
+                                }
+
+                                /* Note: TxCarrierLost is always asserted
+                                                at 100mbps.                 */
+                                if (txstatus & (OWCOLL | ABORT)) {
+                                        /* There was an major error, log it. */
+                                        if (sis900_debug > 1)
+                                                printk(KERN_INFO "Tx Out of "
+                                                        " Window,Abort\n");
+#ifndef final_version
+                                        if (sis900_debug > 1)
+                                                printk(KERN_INFO "%s: Transmit "
+                                                    "error, Tx status %8.8x.\n",
+                                                           dev->name, txstatus);
+#endif
+                                        tp->stats.tx_errors++;
+                                        if (txstatus & ABORT) {
+                                                tp->stats.tx_aborted_errors++;
+                                                /*
+                                                outl((TX_DMA_BURST<<8)|
+                                                        0x03000001,
+                                                        ioaddr + TxConfig);
+                                                */
+                                        }
+                                        if (txstatus & NOCARRIER)
+                                                tp->stats.tx_carrier_errors++;
+                                        if (txstatus & OWCOLL)
+                                                tp->stats.tx_window_errors++;
+#ifdef ETHER_STATS
+                                        if ((txstatus & COLCNT)==COLCNT)
+                                                tp->stats.collisions16++;
+#endif
+                                } else {
+#ifdef ETHER_STATS
+                                        /* No count for tp->stats.tx_deferred */
+#endif
+                                        if (txstatus & UNDERRUN) {
+                                           if (sis900_debug > 1)
+                                             printk(KERN_INFO "Tx UnderRun\n");
+                                        /* Add 64 to the Tx FIFO threshold. */
+                                        /*
+                                                if (tp->tx_flag <  0x00300000)
+                                                        tp->tx_flag+=0x00020000;
+                                                tp->stats.tx_fifo_errors++;
+                                        */
+                                        }
+                                        tp->stats.collisions +=
+                                                        (txstatus >> 16) & 0xF;
+#if LINUX_VERSION_CODE > 0x20119
+                                        tp->stats.tx_bytes += txstatus & DSIZE;
+#endif
+                                        if (sis900_debug > 1)
+                                           printk(KERN_INFO "Tx Transmit OK\n");
+                                        tp->stats.tx_packets++;
+                                }
+
+                                /* Free the original skb. */
+                                if (sis900_debug > 1)
+                                        printk(KERN_INFO "Free original skb\n");
+                                dev_free_skb(tp->tx_skbuff[entry]);
+                                tp->tx_skbuff[entry] = 0;
+                        } // for dirty
+
+#ifndef final_version
+                        if (tp->cur_tx - dirty_tx > NUM_TX_DESC) {
+                                printk(KERN_INFO"%s: Out-of-sync dirty pointer,"
+                                                " %d vs. %d, full=%d.\n",
+                                                dev->name, dirty_tx,
+                                                tp->cur_tx, tp->tx_full);
+                                dirty_tx += NUM_TX_DESC;
+                        }
+#endif
+
+                        if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) {
+                                /* The ring is no longer full, clear tbusy. */
+                                //printk(KERN_INFO "Tx Ring NO LONGER Full\n");
+                                tp->tx_full = 0;
+                                dev->tbusy = 0;
+                                mark_bh(NET_BH);
+                        }
+
+                        tp->dirty_tx = dirty_tx;
+                        if (sis900_debug > 1)
+                                printk(KERN_INFO "TxOK release, tp->cur_tx:%d, tp->dirty:%d\n",
+                                                tp->cur_tx, tp->dirty_tx);
+                } // if (TxOK | TxERR)
+
+                /* Check uncommon events with one test. */
+                if (status & (RxORN | TxERR | RxERR)) {
+                        if (sis900_debug > 2)
+                                printk(KERN_INFO "%s: Abnormal interrupt,"
+                                        "status %8.8x.\n", dev->name, status);
+
+                        if (status == 0xffffffff)
+                                break;
+
+                        if (status & (RxORN | RxERR))
+                                tp->stats.rx_errors++;
+
+                        if (status & RxORN) {
+                                tp->stats.rx_over_errors++;
+                                /*
+                                tp->cur_rx =
+                                        inw(ioaddr + RxBufAddr) % RX_BUF_LEN;
+                                outw(tp->cur_rx - 16, ioaddr + RxBufPtr);
+                                */
+                        }
+                }
+                if (--boguscnt < 0) {
+                        printk(KERN_INFO "%s: Too much work at interrupt, "
+                                   "IntrStatus=0x%4.4x.\n",
+                                   dev->name, status);
+                        /* Clear all interrupt sources. */
+                        //outw(0xffff, ioaddr + IntrStatus);
+                        break;
+                }
+        } while (1);
+
+        if (sis900_debug > 3)
+                printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n",
+                           dev->name, inl(ioaddr + isr));
+
+#if defined(__i386__)
+        clear_bit(0, (void*)&dev->interrupt);
+#else
+        dev->interrupt = 0;
+#endif
+        return;
+}
+
+/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the
+   field alignments and semantics. */
+static int sis900_rx(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = dev->base_addr;
+        u16 cur_rx = tp->cur_rx % NUM_RX_DESC;
+        int rx_status=tp->rx_buf[cur_rx].cmdsts;
+
+        if (sis900_debug > 4)
+                printk(KERN_INFO "%s: sis900_rx, current %4.4x,"
+                                " rx status=%8.8x\n",
+                                dev->name, cur_rx,
+                                rx_status);
+
+        while (rx_status & OWN) {
+                int rx_size = rx_status & DSIZE;
+                rx_size -= CRC_SIZE;
+
+                //printk(KERN_INFO "Rx OWN\n");
+                if (sis900_debug > 4) {
+                        int i;
+                        printk(KERN_INFO "%s:  sis900_rx, rx status %8.8x,"
+                                        " size %4.4x, cur %4.4x.\n",
+                                   dev->name, rx_status, rx_size, cur_rx);
+                        printk(KERN_INFO "%s: Rx Frame contents:", dev->name);
+
+                        for (i = 0; i < rx_size; i++) {
+                                printk("%2.2x ",
+                                (u8)(tp->rx_buf[cur_rx].buf[i]));
+                        }
+
+                        printk(".\n");
+                }
+                if (rx_status & TOOLONG) {
+                        if (sis900_debug > 1)
+                                printk(KERN_INFO "%s: Oversized Ethernet frame,"
+                                                " status %4.4x!\n",
+                                           dev->name, rx_status);
+                        tp->stats.rx_length_errors++;
+                } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) {
+                        if (sis900_debug > 1)
+                                printk(KERN_INFO"%s: Ethernet frame had errors,"
+                                        " status %4.4x.\n",
+                                        dev->name, rx_status);
+                        tp->stats.rx_errors++;
+                        if (rx_status & (RXISERR | FAERR))
+                                tp->stats.rx_frame_errors++;
+                        if (rx_status & (RUNT | TOOLONG))
+                                tp->stats.rx_length_errors++;
+                        if (rx_status & CRCERR) tp->stats.rx_crc_errors++;
+                        /* Reset the receiver,
+                                based on RealTek recommendation. (Bug?) */
+                        /*
+                        tp->cur_rx = 0;
+                        outl(TxENA, ioaddr + cr);
+                        outl(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
+                        outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) |
+                                 (RX_DMA_BURST<<8), ioaddr + RxConfig);
+                        */
+                } else {
+                        /* Malloc up new buffer, compatible with net-2e. */
+                        /* Omit the four octet CRC from the length. */
+                        struct sk_buff *skb;
+
+                        //printk(KERN_INFO "Rx OK\n");
+                        skb = dev_alloc_skb(rx_size + 2);
+                        if (skb == NULL) {
+                                printk(KERN_INFO "%s: Memory squeeze,"
+                                                "deferring packet.\n",
+                                                dev->name);
+                                /* We should check that some rx space is free.
+                                   If not,
+                                   free one and mark stats->rx_dropped++. */
+                                tp->stats.rx_dropped++;
+                                tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
+                                break;
+                        }
+                        skb->dev = dev;
+                        skb_reserve(skb, 2); /* 16 byte align the IP fields. */
+                        if (rx_size+CRC_SIZE > RX_BUF_SIZE) {
+                                /*
+                                int semi_count = RX_BUF_LEN - ring_offset - 4;
+                                memcpy(skb_put(skb, semi_count),
+                                        &rx_bufs[ring_offset + 4], semi_count);
+                                memcpy(skb_put(skb, rx_size-semi_count),
+                                        rx_bufs, rx_size - semi_count);
+                                if (sis900_debug > 4) {
+                                        int i;
+                                        printk(KERN_DEBUG"%s:  Frame wrap @%d",
+                                                   dev->name, semi_count);
+                                        for (i = 0; i < 16; i++)
+                                                printk(" %2.2x", rx_bufs[i]);
+                                        printk(".\n");
+                                        memset(rx_bufs, 0xcc, 16);
+                                }
+                                */
+                                //printk(KERN_INFO "Frame Wrap....\n");
+                        } else {
+#if 0  /* USE_IP_COPYSUM */
+                                eth_copy_and_sum(skb,
+                                   tp->rx_buf[cur_rx].buf, rx_size, 0);
+                                skb_put(skb, rx_size);
+#else
+                                memcpy(skb_put(skb, rx_size),
+                                        tp->rx_buf[cur_rx].buf, rx_size);
+#endif
+                        }
+                        skb->protocol = eth_type_trans(skb, dev);
+                        netif_rx(skb);
+#if LINUX_VERSION_CODE > 0x20119
+                        tp->stats.rx_bytes += rx_size;
+#endif
+                        tp->stats.rx_packets++;
+                }
+                tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE;
+
+                cur_rx = ((cur_rx+1) % NUM_RX_DESC);
+                rx_status = tp->rx_buf[cur_rx].cmdsts;
+                //outw(cur_rx - 16, ioaddr + RxBufPtr);
+        } // while
+        if (sis900_debug > 4)
+                printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x "
+                                "Cmd %2.2x.\n",
+                           dev->name, cur_rx,
+                           inb(ioaddr + cr));
+        tp->cur_rx = cur_rx;
+        return 0;
+}
+
+static int
+sis900_close(struct device *dev)
+{
+        long ioaddr = dev->base_addr;
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        int i;
+
+        dev->start = 0;
+        dev->tbusy = 1;
+
+        if (sis900_debug > 1)
+                printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n",
+                           dev->name, inl(ioaddr + isr));
+
+        /* Disable interrupts by clearing the interrupt mask. */
+        outl(0x0000, ioaddr + imr);
+
+        /* Stop the chip's Tx and Rx DMA processes. */
+        outl(0x00, ioaddr + cr);
+
+        del_timer(&tp->timer);
+
+        free_irq(dev->irq, dev);
+
+        for (i = 0; i < NUM_TX_DESC; i++) {
+                if (tp->tx_skbuff[i])
+                        dev_free_skb(tp->tx_skbuff[i]);
+                tp->tx_skbuff[i] = 0;
+        }
+        kfree(tp->rx_bufs);
+        kfree(tp->tx_bufs);
+
+        /* Green! Put the chip in low-power mode. */
+
+        MOD_DEC_USE_COUNT;
+
+        return 0;
+}
+
+static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        u16 *data = (u16 *)&rq->ifr_data;
+
+        switch(cmd) {
+        case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+                data[0] = tp->phys[0] & 0x3f;
+                /* Fall Through */
+        case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+                data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+                return 0;
+        case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+                if (!suser())
+                        return -EPERM;
+                mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+                return 0;
+        default:
+                return -EOPNOTSUPP;
+        }
+}
+
+static struct enet_statistics *
+sis900_get_stats(struct device *dev)
+{
+        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   This routine is not state sensitive and need not be SMP locked. */
+
+static u16 elComputeHashTableIndex(u8 *addr)
+{
+#define POLYNOMIAL 0x04C11DB6L
+    u32      crc = 0xffffffff, msb;
+    int      i, j;
+    u8       byte;
+
+    for( i=0; i<6; i++ ) {
+        byte = *addr++;
+        for( j=0; j<8; j++ ) {
+            msb = crc >> 31;
+            crc <<= 1;
+            if( msb ^ ( byte & 1 )) {
+                crc ^= POLYNOMIAL;
+                crc |= 1;
+            }
+            byte >>= 1;
+        }
+    }
+    // 7 bit crc for 128 bit hash table
+    return( (int)(crc >> 25) );
+}
+
+static u16 elMIIpollBit(struct device *dev,
+                         int phy_id,
+                         int location,
+                         u16 mask,
+                         u16 polarity,
+                         u16 *value)
+{
+        u32 i;
+        i=0;
+        while (1) {
+                *value = mdio_read(dev, phy_id, location);
+                if (polarity) {
+                        if (mask & *value) return(TRUE);
+                } else {
+                        if (mask & ~(*value)) return(TRUE);
+                }
+                if (++i == 1200) break;
+        }
+        return(FALSE);
+}
+
+static u16 elPMDreadMode(struct device *dev,
+                         int phy_id,
+                         int *speed,
+                         int *duplex)
+{
+        u16 status;
+
+        *speed = HW_SPEED_10_MBPS;
+        *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+        status = mdio_read(dev, phy_id, MII_ANLPAR);
+
+        if ( !( status &
+                (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) {
+//              printk(KERN_INFO "%s: Link Partner not detected.\n", dev->name);
+                while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ;
+                while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ;
+                if (status & 0x80)
+                        *speed = HW_SPEED_100_MBPS;
+                if (status & 0x40)
+                        *duplex = FDX_CAPABLE_FULL_SELECTED;
+                if (sis900_debug > 3) {
+                        printk(KERN_INFO"%s: Setting %s%s-duplex.\n",
+                                dev->name,
+                                *speed == HW_SPEED_100_MBPS ?
+                                        "100mbps " : "10mbps ",
+                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
+                                        "full" : "half");
+                }
+        } else {
+//              printk(KERN_INFO "%s: Link Partner detected\n", dev->name);
+                if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) {
+                        *duplex = FDX_CAPABLE_FULL_SELECTED;
+                }
+                if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) {
+                        *speed = HW_SPEED_100_MBPS;
+                }
+                if (sis900_debug > 3) {
+                        printk(KERN_INFO"%s: Setting %s%s-duplex based on"
+                                " auto-negotiated partner ability.\n",
+                                dev->name,
+                                *speed == HW_SPEED_100_MBPS ?
+                                        "100mbps " : "10mbps ",
+                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
+                                        "full" : "half");
+                }
+        }
+        return (status);
+}
+
+static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed)
+{
+        u16 status;
+
+        mdio_write(dev, phy_id, MII_CONTROL, 0);
+        mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO |
+                                                        MIICNTL_RST_AUTO);
+        elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status);
+        elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status);
+        elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status);
+        if (status & MIISTAT_LINK) {
+                elPMDreadMode(dev, phy_id, speed, duplex);
+                elSetMediaType(dev, *speed, *duplex);
+        }
+        return(status);
+}
+
+static void elSetMediaType(struct device *dev, int speed, int duplex)
+{
+        long ioaddr = dev->base_addr;
+        u32     txCfgOn = 0, txCfgOff = TxDRNT;
+        u32     rxCfgOn = 0, rxCfgOff = 0;
+
+        if (speed == HW_SPEED_100_MBPS) {
+                txCfgOn |= (TxDRNT_100 | TxHBI);
+        } else {
+                txCfgOn |= TxDRNT_10;
+        }
+
+        if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+                txCfgOn |= (TxCSI | TxHBI);
+                rxCfgOn |= RxATP;
+        } else {
+                txCfgOff |= (TxCSI | TxHBI);
+                rxCfgOff |= RxATP;
+        }
+        outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg);
+        outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg);
+}
+
+static void set_rx_mode(struct device *dev)
+{
+        long ioaddr = dev->base_addr;
+        u16 mc_filter[8];
+        int i;
+        int rx_mode;
+        u32 rxCfgOn = 0, rxCfgOff = 0;
+        u32 txCfgOn = 0, txCfgOff = 0;
+
+        if (sis900_debug > 3)
+                printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--"
+                                "RxCfg %8.8x.\n",
+                                dev->name, dev->flags, inl(ioaddr + rxcfg));
+
+        /* Note: do not reorder, GCC is clever about common statements. */
+        if (dev->flags & IFF_PROMISC) {
+                printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
+                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
+                                ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS;
+                for (i=0 ; i<8 ; i++)
+                        mc_filter[i]=0xffff;
+        } else if ((dev->mc_count > multicast_filter_limit)
+                           ||  (dev->flags & IFF_ALLMULTI)) {
+                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
+                                ACCEPT_CAM_QUALIFIED;
+                for (i=0 ; i<8 ; i++)
+                        mc_filter[i]=0xffff;
+        } else {
+                struct dev_mc_list *mclist;
+                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
+                                ACCEPT_CAM_QUALIFIED;
+                for (i=0 ; i<8 ; i++)
+                        mc_filter[i]=0;
+                for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                         i++, mclist = mclist->next)
+                        set_bit(elComputeHashTableIndex(mclist->dmi_addr),
+                                                mc_filter);
+        }
+
+        for (i=0 ; i<8 ; i++) {
+                outl((u32)(0x00000004+i) << 16, ioaddr + rfcr);
+                outl(mc_filter[i], ioaddr + rfdr);
+        }
+        /* We can safely update without stopping the chip. */
+        //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS;
+        //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS;
+        outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS |
+                          ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr);
+
+        if (rx_mode & ACCEPT_ALL_ERRORS) {
+                rxCfgOn = RxAEP | RxARP | RxAJAB;
+        } else {
+                rxCfgOff = RxAEP | RxARP | RxAJAB;
+        }
+        if (rx_mode & MAC_LOOPBACK) {
+                rxCfgOn |= RxATP;
+                txCfgOn |= TxMLB;
+        } else {
+                if (!(( (struct sis900_private *)(dev->priv) )->full_duplex))
+                        rxCfgOff |= RxATP;
+                txCfgOff |= TxMLB;
+        }
+
+        if (sis900_debug > 2) {
+                printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
+                printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
+        }
+
+        outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg);
+        outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg);
+
+        if (sis900_debug > 2) {
+                printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
+                printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
+                printk(KERN_INFO "Receive Filter Register:%8.8x\n",
+                                                        inl(ioaddr + rfcr));
+        }
+        return;
+}
+
+static void sis900_reset(struct device *dev)
+{
+        long ioaddr = dev->base_addr;
+
+        outl(0, ioaddr + ier);
+        outl(0, ioaddr + imr);
+        outl(0, ioaddr + rfcr);
+
+        outl(RxRESET | TxRESET | RESET, ioaddr + cr);
+        outl(PESEL, ioaddr + cfg);
+
+        set_rx_mode(dev);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+        return sis900_probe(0);
+}
+
+void
+cleanup_module(void)
+{
+        struct device *next_dev;
+
+        /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+        while (root_sis900_dev) {
+                struct sis900_private *tp =
+                        (struct sis900_private *)root_sis900_dev->priv;
+                next_dev = tp->next_module;
+                unregister_netdev(root_sis900_dev);
+                release_region(root_sis900_dev->base_addr,
+                                           pci_tbl[tp->chip_id].io_size);
+                kfree(tp);
+                kfree(root_sis900_dev);
+                root_sis900_dev = next_dev;
+        }
+}
+
+#endif  /* MODULE */
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index b021019416db43f53d32a22641064d1b8cf60aea..e6da60429b71e93bcbc751f96cc6c3783a535576 100644 (file)
@@ -117,6 +117,13 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex)
          * TODO: Make number of input/output buffers tunable parameters
          */
 
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE > 0x202ff
+        init_waitqueue_head(&drv->open_wait);
+        init_waitqueue_head(&drv->output_write_wait);
+        init_waitqueue_head(&drv->output_drain_wait);
+        init_waitqueue_head(&drv->input_read_wait);
+#endif
+
         drv->num_output_buffers = 8;
        drv->output_buffer_size = (4096 * 2);
        drv->playing_count = 0;
@@ -264,6 +271,7 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
    * If status & 2, a buffer was claimed for DMA and is still in use.
    *
    * The playing_count for non-DMA hardware should never be non-zero.
+   * Value of status for non-DMA hardware should always be 1.
    */
   if (status & 1) {
     if (drv->playing_count) 
@@ -300,8 +308,9 @@ void sparcaudio_output_done(struct sparcaudio_driver * drv, int status)
 
   /* If we got back a buffer, see if anyone wants to write to it */
   if ((status & 1) || ((drv->output_count + drv->playing_count) 
-                 < drv->num_output_buffers)) 
+                       < drv->num_output_buffers)) {
     wake_up_interruptible(&drv->output_write_wait);
+  }
 
   /* If the output queue is empty, shut down the driver. */
   if ((drv->output_count < 1) && (drv->playing_count < 1)) {
@@ -664,7 +673,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
   case SOUND_MIXER_WRITE_CD:
   case SOUND_MIXER_WRITE_LINE:
   case SOUND_MIXER_WRITE_IMIX:
-    if(get_user(k, arg))
+    if(COPY_IN(arg, k))
       return -EFAULT;
     tprintk(("setting input volume (0x%x)", k));
     if (drv->ops->get_input_channels)
@@ -695,7 +704,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
   case SOUND_MIXER_WRITE_PCM:
   case SOUND_MIXER_WRITE_VOLUME:
   case SOUND_MIXER_WRITE_SPEAKER:
-    if(get_user(k, arg))
+    if(COPY_IN(arg, k))
        return -EFAULT;
     tprintk(("setting output volume (0x%x)", k));
     if (drv->ops->get_output_channels)
@@ -736,7 +745,7 @@ static int sparcaudio_mixer_ioctl(struct inode * inode, struct file * file,
   case SOUND_MIXER_WRITE_RECSRC: 
     if (!drv->ops->set_input_port)
       return -EINVAL;
-    if(get_user(k, arg))
+    if(COPY_IN(arg, k))
       return -EFAULT;
     /* only one should ever be selected */
     if (k & SOUND_MASK_IMIX) j = AUDIO_ANALOG_LOOPBACK;
index ab6b64562815044fe1d835d02e38671566bd7fa4..a1c700205f620435a8ec9451a4ef24ac9b2a2233 100644 (file)
@@ -1,7 +1,9 @@
 /*
  * drivers/sbus/audio/cs4231.c
  *
- * Copyright 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
+ * Copyright 1996, 1997, 1998, 1999 Derrick J Brashear (shadow@andrew.cmu.edu)
+ * The 4231/ebus support was written by David Miller, who didn't bother
+ * crediting himself here, so I will.
  *
  * Based on the AMD7930 driver:
  * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
@@ -11,7 +13,7 @@
  * 
  * This was culled from the Crystal docs on the 4231a, and the addendum they
  * faxed me on the 4231.
- * The APC DMA controller support unfortunately is not documented. Thanks, Sun
+ * The APC DMA controller support unfortunately is not documented. Thanks, Sun.
  */
 
 #include <linux/config.h>
@@ -69,15 +71,17 @@ static int cs4231_record_gain(struct sparcaudio_driver *drv, int value,
 static int cs4231_play_gain(struct sparcaudio_driver *drv, int value, 
                             unsigned char balance);
 static void cs4231_ready(struct sparcaudio_driver *drv);
-static void cs4231_playintr(struct sparcaudio_driver *drv);
+static void cs4231_playintr(struct sparcaudio_driver *drv, int);
 static int cs4231_recintr(struct sparcaudio_driver *drv);
 static int cs4231_output_muted(struct sparcaudio_driver *drv, int value);
 static void cs4231_pollinput(struct sparcaudio_driver *drv);
-static void eb4231_pollinput(struct sparcaudio_driver *drv);
 static int cs4231_length_to_samplecount(struct audio_prinfo *thisdir, 
                                         unsigned int length);
 static void cs4231_getsamplecount(struct sparcaudio_driver *drv, 
                                   unsigned int length, unsigned int value);
+#ifdef EB4231_SUPPORT
+static void eb4231_pollinput(struct sparcaudio_driver *drv);
+#endif
 
 #define CHIP_READY udelay(100); cs4231_ready(drv); udelay(1000);
 
@@ -1228,11 +1232,20 @@ static void cs4231_release(struct inode * inode, struct file * file, struct spar
   MOD_DEC_USE_COUNT;
 }
 
-static void cs4231_playintr(struct sparcaudio_driver *drv)
+static void cs4231_playintr(struct sparcaudio_driver *drv, int push)
 {
   struct cs4231_chip *cs4231_chip = (struct cs4231_chip *)drv->private;
   int status = 0;
 
+  if (!push) {
+    if (!cs4231_chip->perchip_info.play.active) {
+      cs4231_chip->regs->dmapnva = cs4231_chip->output_next_dma_handle;
+      cs4231_chip->regs->dmapnc = cs4231_chip->output_next_dma_size;
+    }
+    sparcaudio_output_done(drv, 0);
+    return;
+  }
+
   if (cs4231_chip->playlen == 0 && cs4231_chip->output_size > 0) 
     cs4231_chip->playlen = cs4231_chip->output_size;
 
@@ -1484,14 +1497,16 @@ static void cs4231_start_output(struct sparcaudio_driver *drv, __u8 * buffer,
   cs4231_chip->perchip_info.play.active = 1;
   cs4231_chip->playing_count = 0;
 
-  cs4231_playintr(drv);
   if ((cs4231_chip->regs->dmacsr & APC_PPAUSE) || 
       !(cs4231_chip->regs->dmacsr & APC_PDMA_READY)) {
-    cs4231_chip->regs->dmacsr &= ~(APC_XINT_PLAY | APC_PPAUSE);
-    cs4231_chip->regs->dmacsr |= APC_GENL_INT | APC_XINT_ENA | APC_XINT_PLAY
-      | APC_XINT_GENL | APC_XINT_PENA | APC_PDMA_READY;
+    cs4231_chip->regs->dmacsr &= ~APC_XINT_PLAY;
+    cs4231_chip->regs->dmacsr &= ~APC_PPAUSE;
+    
+    cs4231_playintr(drv, cs4231_chip->regs->dmapnva == 0 ? 1 : 0);
+
+    cs4231_chip->regs->dmacsr |= APC_PLAY_SETUP;
     cs4231_enable_play(drv);
-  
+
     cs4231_ready(drv);
   }
 }
@@ -1544,11 +1559,8 @@ static void cs4231_stop_output(struct sparcaudio_driver *drv)
     cs4231_chip->output_next_dma_handle = 0;
     cs4231_chip->output_next_dma_size = 0;
   }
-#if 1 /* Not safe without shutting off the DMA controller as well. -DaveM */
+#if 0 /* Not safe without shutting off the DMA controller as well. -DaveM */
   /* Else subsequent speed setting changes are ignored by the chip. */
-  cs4231_chip->regs->dmacsr &= ~(APC_GENL_INT | APC_XINT_ENA | APC_XINT_PLAY 
-                                 | APC_XINT_GENL | APC_PDMA_READY 
-                                 | APC_XINT_PENA );
   cs4231_disable_play(drv);
 #endif
 }
@@ -1897,11 +1909,12 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    */
 
   if (dummy & APC_PLAY_INT) {
-    if (dummy & APC_XINT_PEMP) {
+    if (dummy & APC_XINT_PNVA) {
       cs4231_chip->perchip_info.play.samples += 
        cs4231_length_to_samplecount(&(cs4231_chip->perchip_info.play), 
                                     cs4231_chip->playlen); 
-      cs4231_playintr(drv);
+      if (!(dummy & APC_XINT_EMPT))
+        cs4231_playintr(drv, 1);
     } 
     /* Any other conditions we need worry about? */
   }
@@ -1933,7 +1946,7 @@ void cs4231_interrupt(int irq, void *dev_id, struct pt_regs *regs)
       cs4231_chip->perchip_info.play.error = 1;
     }
     cs4231_chip->perchip_info.play.active = 0;
-    cs4231_playintr(drv);
+    cs4231_playintr(drv, 0);
 
     cs4231_getsamplecount(drv, cs4231_chip->playlen, 0);
   }
index 892ed7e88c4d5193cfe7ccf486cfefef20372d7d..45702e86a04b9ae9978ab515dca26234de4288b7 100644 (file)
@@ -255,19 +255,19 @@ struct cs4231_chip {
 #define APC_XINT_PLAY   0x40000  /* Playback ext intr */
 #define APC_XINT_CAPT   0x20000  /* Capture ext intr */
 #define APC_XINT_GENL   0x10000  /* Error ext intr */
-#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt */
-#define APC_XINT_PEMP   0x4000   /* Play pipe empty */
+#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt (0 write to pva) */
+#define APC_XINT_PEMP   0x4000   /* Play pipe empty (pva and pnva not set) */
 #define APC_XINT_PNVA   0x2000   /* Playback NVA dirty */
 #define APC_XINT_PENA   0x1000   /* play pipe empty Int enable */
 #define APC_XINT_COVF   0x800    /* Cap data dropped on floor */
 #define APC_XINT_CNVA   0x400    /* Capture NVA dirty */
-#define APC_XINT_CEMP   0x200    /* Capture pipe empty interrupt */
+#define APC_XINT_CEMP   0x200    /* Capture pipe empty (cva and cnva not set) */
 #define APC_XINT_CENA   0x100    /* Cap. pipe empty int enable */
 #define APC_PPAUSE      0x80     /* Pause the play DMA */
 #define APC_CPAUSE      0x40     /* Pause the capture DMA */
 #define APC_CDC_RESET   0x20     /* CODEC RESET */
-#define APC_PDMA_READY     0x08     /* Play DMA Go */
-#define APC_CDMA_READY     0x04     /* Capture DMA Go */
+#define APC_PDMA_READY  0x08     /* Play DMA Go */
+#define APC_CDMA_READY  0x04     /* Capture DMA Go */
 #define APC_CHIP_RESET  0x01     /* Reset the chip */
 
 #define APC_INIT_SETUP  (APC_CDMA_READY | APC_PDMA_READY | APC_XINT_ENA | APC_XINT_PLAY | APC_XINT_GENL | APC_INT_PENDING | APC_PLAY_INT | APC_CAPT_INT | APC_GENL_INT) 
index 8d6c8b5b6912830da75152667ca61c8c749871dd..37db22a9a5b50f61b3cca91a5d64f2767c613e94 100644 (file)
@@ -38,6 +38,10 @@ enum {
 
   IPPROTO_PIM    = 103,                /* Protocol Independent Multicast       */
 
+  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
+  IPPROTO_AH = 51,             /* Authentication Header protocol       */
+  IPPROTO_COMP   = 108,                /* Compression Header protocol */
+
   IPPROTO_RAW   = 255,         /* Raw IP packets                       */
   IPPROTO_MAX
 };
index 4f31677b98f658c7603696158f85c9047afab56d..ca5e768ba94c5aebdbda047bad9da925ac2aea07 100644 (file)
@@ -129,8 +129,6 @@ struct in6_flowlabel_req
 #define IPPROTO_HOPOPTS                0       /* IPv6 hop-by-hop options      */
 #define IPPROTO_ROUTING                43      /* IPv6 routing header          */
 #define IPPROTO_FRAGMENT       44      /* IPv6 fragmentation header    */
-#define IPPROTO_ESP            50      /* encapsulating security payload */
-#define IPPROTO_AH             51      /* authentication header        */
 #define IPPROTO_ICMPV6         58      /* ICMPv6                       */
 #define IPPROTO_NONE           59      /* IPv6 no next header          */
 #define IPPROTO_DSTOPTS                60      /* IPv6 destination options     */
index 5bc5bf252a47ec86acb8488520116f0d00be5296..d681aa5ba9ba0c8d3b54d43e4368afe7f00670a5 100644 (file)
@@ -146,7 +146,7 @@ struct ucred {
 #define AF_DECnet      12      /* Reserved for DECnet project  */
 #define AF_NETBEUI     13      /* Reserved for 802.2LLC project*/
 #define AF_SECURITY    14      /* Security callback pseudo AF */
-#define pseudo_AF_KEY   15      /* PF_KEY key management API */
+#define AF_KEY   15      /* PF_KEY key management API */
 #define AF_NETLINK     16
 #define AF_ROUTE       AF_NETLINK /* Alias to emulate 4.4BSD */
 #define AF_PACKET      17      /* Packet family                */
@@ -174,7 +174,7 @@ struct ucred {
 #define PF_DECnet      AF_DECnet
 #define PF_NETBEUI     AF_NETBEUI
 #define PF_SECURITY    AF_SECURITY
-#define PF_KEY          pseudo_AF_KEY
+#define PF_KEY      AF_KEY
 #define PF_NETLINK     AF_NETLINK
 #define PF_ROUTE       AF_ROUTE
 #define PF_PACKET      AF_PACKET
index 87a7f051ab9680ffec6a759236aad90d6468a184..aba1498a73de81a44f80db82a4eab1e8defa0935 100644 (file)
@@ -470,6 +470,7 @@ extern int do_sysctl_strategy (ctl_table *table,
 
 extern ctl_handler sysctl_string;
 extern ctl_handler sysctl_intvec;
+extern ctl_handler sysctl_jiffies;
 
 extern int do_string (
        void *oldval, size_t *oldlenp, void *newval, size_t newlen,
index da5a65b1073a368b51cb0287cf0e5b8aeaac1de9..28cf919e76c35e8cc582fde0016689390ddac981 100644 (file)
@@ -300,6 +300,7 @@ struct tcp_opt {
 
        __u32   last_seg_size;  /* Size of last incoming segment */
        __u32   rcv_mss;        /* MSS used for delayed ACK decisions */ 
+       __u32   partial_writers; /* # of clients wanting at the head packet */
 
        struct open_request     *syn_wait_queue;
        struct open_request     **syn_wait_last;
@@ -672,6 +673,7 @@ static inline void lock_sock(struct sock *sk)
 #if 0
 /* debugging code: the test isn't even 100% correct, but it can catch bugs */
 /* Note that a double lock is ok in theory - it's just _usually_ a bug */
+/* Actually it can easily happen with multiple writers */ 
        if (atomic_read(&sk->sock_readers)) {
                printk("double lock on socket at %p\n", gethere());
 here:
index e8ab1f492f2ddb7e25768dd980853a62bf174693..38ea51d50133ccc0aace1b589a96fce58541e9da 100644 (file)
@@ -120,6 +120,8 @@ struct tcp_tw_bucket {
        /* These _must_ match the beginning of struct sock precisely.
         * XXX Yes I know this is gross, but I'd have to edit every single
         * XXX networking file if I created a "struct sock_header". -DaveM
+        * Just don't forget -fno-strict-aliasing, but it should be really
+        * fixed -AK
         */
        struct sock             *sklist_next;
        struct sock             *sklist_prev;
@@ -140,12 +142,13 @@ struct tcp_tw_bucket {
                                nonagle;
 
        /* And these are ours. */
-       __u32                   rcv_nxt;
+       __u32                   rcv_nxt,snd_nxt;
        struct tcp_func         *af_specific;
        struct tcp_bind_bucket  *tb;
        struct tcp_tw_bucket    *next_death;
        struct tcp_tw_bucket    **pprev_death;
        int                     death_slot;
+       
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        struct in6_addr         v6_daddr;
        struct in6_addr         v6_rcv_saddr;
@@ -476,7 +479,13 @@ extern int                 tcp_rcv_established(struct sock *sk,
                                                    struct tcphdr *th, 
                                                    unsigned len);
 
-extern int                     tcp_timewait_state_process(struct tcp_tw_bucket *tw,
+enum tcp_tw_status {
+               TCP_TW_SUCCESS = 0,
+               TCP_TW_RST = 1,
+               TCP_TW_ACK = 2
+               };
+
+extern enum tcp_tw_status tcp_timewait_state_process(struct tcp_tw_bucket *tw,
                                                           struct sk_buff *skb,
                                                           struct tcphdr *th,
                                                           unsigned len);
index e5132d822d1462f150eff5f16758cd72a8ab0fe1..a5f4c3a4fcc5688c72c42d17c693e1bc03beb50d 100644 (file)
@@ -1035,6 +1035,34 @@ int sysctl_intvec(ctl_table *table, int *name, int nlen,
        return 0;
 }
 
+/* Strategy function to convert jiffies to seconds */ 
+int sysctl_jiffies(ctl_table *table, int *name, int nlen,
+               void *oldval, size_t *oldlenp,
+               void *newval, size_t newlen, void **context)
+{
+       if (oldval) {
+               size_t olen;
+               if (oldlenp) { 
+                       if (get_user(olen, oldlenp))
+                               return -EFAULT;
+                       if (olen!=sizeof(int))
+                               return -EINVAL; 
+               }
+               if (put_user(*(int *)(table->data) / HZ, (int *)oldval) || 
+                   (oldlenp && put_user(sizeof(int),oldlenp)))
+                       return -EFAULT;
+       }
+       if (newval && newlen) { 
+               int new;
+               if (newlen != sizeof(int))
+                       return -EINVAL; 
+               if (get_user(new, (int *)newval))
+                       return -EFAULT;
+               *(int *)(table->data) = new*HZ; 
+       }
+       return 1;
+}
+
 int do_string (
        void *oldval, size_t *oldlenp, void *newval, size_t newlen,
        int rdwr, char *data, size_t max)
index 6350b5b14bbd753f9d8befc334913e1e681eb44c..e37eb6bd7eb0bde5d2687c61a6ac722b2a8612aa 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PF_INET protocol family socket handler.
  *
- * Version:    $Id: af_inet.c,v 1.87.2.4 1999/07/23 15:29:19 davem Exp $
+ * Version:    $Id: af_inet.c,v 1.87.2.5 1999/08/08 08:43:10 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -187,7 +187,6 @@ static __inline__ void kill_sk_later(struct sock *sk)
                        atomic_read(&sk->rmem_alloc),
                        atomic_read(&sk->wmem_alloc)));
 
-       sk->destroy = 1;
        sk->ack_backlog = 0;
        release_sock(sk);
        net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
@@ -202,9 +201,11 @@ void destroy_sock(struct sock *sk)
         */
        net_delete_timer(sk);
 
-       if (sk->prot->destroy)
+       if (sk->prot->destroy && !sk->destroy)
                sk->prot->destroy(sk);
 
+       sk->destroy = 1;
+
        kill_sk_queues(sk);
 
        /* Now if everything is gone we can free the socket
index 90fd7b6efcf607f3f06998251a77d558a66d595b..06eb5fe58cd190d4b67e7f2182ca12510e41237b 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             ROUTE - implementation of the IP router.
  *
- * Version:    $Id: route.c,v 1.67.2.2 1999/07/23 15:29:26 davem Exp $
+ * Version:    $Id: route.c,v 1.67.2.3 1999/08/08 08:43:12 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1927,16 +1927,30 @@ int ipv4_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp,
                return -EINVAL;
 }
 
+static int ipv4_sysctl_rtcache_flush_strategy(ctl_table *table, int *name, int nlen,
+                        void *oldval, size_t *oldlenp,
+                        void *newval, size_t newlen, 
+                        void **context)
+{
+       int delay;
+       if (newlen != sizeof(int))
+               return -EINVAL;
+       if (get_user(delay,(int *)newval))
+               return -EFAULT; 
+       rt_cache_flush(delay); 
+       return 0;
+}
+
 ctl_table ipv4_route_table[] = {
         {NET_IPV4_ROUTE_FLUSH, "flush",
-         &flush_delay, sizeof(int), 0200, NULL,
-         &ipv4_sysctl_rtcache_flush},
+         &flush_delay, sizeof(int), 0644, NULL,
+         &ipv4_sysctl_rtcache_flush, &ipv4_sysctl_rtcache_flush_strategy },
        {NET_IPV4_ROUTE_MIN_DELAY, "min_delay",
          &ip_rt_min_delay, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_ROUTE_MAX_DELAY, "max_delay",
          &ip_rt_max_delay, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_ROUTE_GC_THRESH, "gc_thresh",
          &ipv4_dst_ops.gc_thresh, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -1945,13 +1959,13 @@ ctl_table ipv4_route_table[] = {
          &proc_dointvec},
        {NET_IPV4_ROUTE_GC_MIN_INTERVAL, "gc_min_interval",
          &ip_rt_gc_min_interval, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_ROUTE_GC_TIMEOUT, "gc_timeout",
          &ip_rt_gc_timeout, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_ROUTE_GC_INTERVAL, "gc_interval",
          &ip_rt_gc_interval, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_ROUTE_REDIRECT_LOAD, "redirect_load",
          &ip_rt_redirect_load, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -1972,7 +1986,7 @@ ctl_table ipv4_route_table[] = {
          &proc_dointvec},
        {NET_IPV4_ROUTE_MTU_EXPIRES, "mtu_expires",
          &ip_rt_mtu_expires, sizeof(int), 0644, NULL,
-         &proc_dointvec_jiffies},
+         &proc_dointvec_jiffies, &sysctl_jiffies},
         {0}
 };
 #endif
index 6551764324764758030b1cd43bc0505a0ba3fabe..fb4e8f8007e0109cbddf74815116a3688bd044a7 100644 (file)
@@ -9,7 +9,7 @@
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  * 
- *  $Id: syncookies.c,v 1.7 1999/03/17 02:34:57 davem Exp $
+ *  $Id: syncookies.c,v 1.7.2.1 1999/08/08 08:43:13 davem Exp $
  *
  *  Missing: IPv6 support. 
  */
@@ -145,7 +145,10 @@ cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt)
        req->rmt_port = skb->h.th->source;
        req->af.v4_req.loc_addr = skb->nh.iph->daddr;
        req->af.v4_req.rmt_addr = skb->nh.iph->saddr;
-       req->class = &or_ipv4; /* for savety */
+       req->class = &or_ipv4; /* for safety */
+#ifdef CONFIG_IP_TRANSPARENT_PROXY 
+       req->lcl_port = skb->h.th->dest;
+#endif
 
        req->af.v4_req.opt = NULL;
 
index 10f5e9324dce9d0ebdea2996152871f4a70b60a0..e578e4e7c49f35e0e478ce7fbca667edb5c625c3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.38 1999/01/02 16:51:48 davem Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.38.2.1 1999/08/08 08:43:14 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -90,9 +90,23 @@ int ipv4_sysctl_forward(ctl_table *ctl, int write, struct file * filp,
        if (write && ipv4_devconf.forwarding != val)
                inet_forward_change();
 
-        return ret;
+       return ret;
 }
 
+static int ipv4_sysctl_forward_strategy(ctl_table *table, int *name, int nlen,
+                        void *oldval, size_t *oldlenp,
+                        void *newval, size_t newlen, 
+                        void **context)
+{
+       int new;
+       if (newlen != sizeof(int))
+               return -EINVAL;
+       if (get_user(new,(int *)newval))
+               return -EFAULT; 
+       if (new != ipv4_devconf.forwarding) 
+               inet_forward_change(); 
+       return 0; /* caller does change again and handles handles oldval */ 
+}
 
 ctl_table ipv4_table[] = {
         {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
@@ -109,7 +123,7 @@ ctl_table ipv4_table[] = {
          &proc_dointvec},
         {NET_IPV4_FORWARD, "ip_forward",
          &ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
-         &ipv4_sysctl_forward},
+         &ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy},
         {NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
          &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -132,12 +146,13 @@ ctl_table ipv4_table[] = {
         &sysctl_ip_masq_debug, sizeof(int), 0644, NULL, &proc_dointvec},
 #endif
        {NET_IPV4_IPFRAG_TIME, "ipfrag_time",
-        &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies},
+        &sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies, 
+        &sysctl_jiffies},
        {NET_IPV4_TCP_MAX_KA_PROBES, "tcp_max_ka_probes",
         &sysctl_tcp_max_ka_probes, sizeof(int), 0644, NULL, &proc_dointvec},
        {NET_IPV4_TCP_KEEPALIVE_TIME, "tcp_keepalive_time",
         &sysctl_tcp_keepalive_time, sizeof(int), 0644, NULL, 
-        &proc_dointvec_jiffies},
+        &proc_dointvec_jiffies, &sysctl_jiffies},
        {NET_IPV4_TCP_KEEPALIVE_PROBES, "tcp_keepalive_probes",
         &sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL, 
         &proc_dointvec},
@@ -148,7 +163,7 @@ ctl_table ipv4_table[] = {
         &sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec},
        {NET_IPV4_TCP_FIN_TIMEOUT, "tcp_fin_timeout",
         &sysctl_tcp_fin_timeout, sizeof(int), 0644, NULL, 
-        &proc_dointvec_jiffies},
+        &proc_dointvec_jiffies, &sysctl_jiffies},
 #ifdef CONFIG_SYN_COOKIES
        {NET_TCP_SYNCOOKIES, "tcp_syncookies",
         &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
index c4e0e78cb9e09f2677acf7e3b88abde4ed2e487d..9d644fc69911ad3e665a84cd4833be2441609c8b 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp.c,v 1.140.2.2 1999/08/07 10:56:35 davem Exp $
+ * Version:    $Id: tcp.c,v 1.140.2.3 1999/08/08 08:43:16 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -725,6 +725,24 @@ static void wait_for_tcp_memory(struct sock * sk)
        lock_sock(sk);
 }
 
+/*
+ * Wait for a buffer.
+ */ 
+static int wait_for_buffer(struct sock *sk) 
+{ 
+       struct wait_queue wait = { current, NULL }; 
+       int success = 0; 
+
+       release_sock(sk); 
+       add_wait_queue(sk->sleep, &wait); 
+       current->state = TASK_INTERRUPTIBLE; 
+       schedule(); 
+       current->state = TASK_RUNNING; 
+       remove_wait_queue(sk->sleep, &wait);
+       lock_sock(sk); 
+       return 0; 
+} 
+
 /* When all user supplied data has been queued set the PSH bit */
 #define PSH_NEEDED (seglen == 0 && iovlen == 0)
 
@@ -802,11 +820,21 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg)
                                    tp->snd_nxt < TCP_SKB_CB(skb)->end_seq) {
                                        int last_byte_was_odd = (copy % 4);
 
+                                       /* 
+                                        * Check for parallel writers sleeping in user access.
+                                        */ 
+                                       if (tp->partial_writers++ > 0) { 
+                                               wait_for_buffer(sk);
+                                               tp->partial_writers--;
+                                               continue; 
+                                       }
+                               
                                        copy = mss_now - copy;
                                        if(copy > skb_tailroom(skb))
                                                copy = skb_tailroom(skb);
                                        if(copy > seglen)
                                                copy = seglen;
+               
                                        if(last_byte_was_odd) {
                                                if(copy_from_user(skb_put(skb, copy),
                                                                  from, copy))
@@ -819,6 +847,7 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg)
                                                        from, skb_put(skb, copy),
                                                        copy, skb->csum, &err);
                                        }
+               
                                        /*
                                         * FIXME: the *_user functions should
                                         *        return how much data was
@@ -838,6 +867,10 @@ int tcp_do_sendmsg(struct sock *sk, struct msghdr *msg)
                                        seglen -= copy;
                                        if (PSH_NEEDED)
                                                TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_PSH;
+
+                                       if (--tp->partial_writers > 0) 
+                                               wake_up_interruptible(sk->sleep); 
+
                                        continue;
                                }
                        }
index 024227f4ddd7f53e55d9e1e3d9ca2dd187e46783..4c0fc202f6da8b0ab88346e8ab077d666070de3d 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_input.c,v 1.164.2.5 1999/06/30 09:27:05 davem Exp $
+ * Version:    $Id: tcp_input.c,v 1.164.2.6 1999/08/08 08:43:18 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -213,21 +213,19 @@ static __inline__ void tcp_bound_rto(struct tcp_opt *tp)
 extern __inline__ void tcp_replace_ts_recent(struct sock *sk, struct tcp_opt *tp,
                                             __u32 start_seq, __u32 end_seq)
 {
-       /* From draft-ietf-tcplw-high-performance: the correct
-        * test is last_ack_sent <= end_seq.
-        * (RFC1323 stated last_ack_sent < end_seq.)
-        *
-        * HOWEVER: The current check contradicts the draft statements.
-        * It has been done for good reasons.
-        * The implemented check improves security and eliminates
-        * unnecessary RTT overestimation.
-        *              1998/06/27  Andrey V. Savochkin <saw@msu.ru>
+       /* It is start_seq <= last_ack_seq combined
+          with in window check. If start_seq<=last_ack_seq<=rcv_nxt,
+          then segment is in window if end_seq>=rcv_nxt.
         */
-       if (!before(end_seq, tp->last_ack_sent - sk->rcvbuf) &&
-           !after(start_seq, tp->rcv_wup + tp->rcv_wnd)) {
+       if (!after(start_seq, tp->last_ack_sent) &&
+           !before(end_seq, tp->rcv_nxt)) {
                /* PAWS bug workaround wrt. ACK frames, the PAWS discard
                 * extra check below makes sure this can only happen
                 * for pure ACK frames.  -DaveM
+                *
+                * Plus: expired timestamps.
+                *
+                * Plus: resets failing PAWS.
                 */
                if((s32)(tp->rcv_tsval - tp->ts_recent) >= 0) {
                        tp->ts_recent = tp->rcv_tsval;
@@ -240,11 +238,10 @@ extern __inline__ void tcp_replace_ts_recent(struct sock *sk, struct tcp_opt *tp
 
 extern __inline__ int tcp_paws_discard(struct tcp_opt *tp, struct tcphdr *th, unsigned len)
 {
-       /* ts_recent must be younger than 24 days */
-       return (((s32)(tcp_time_stamp - tp->ts_recent_stamp) >= PAWS_24DAYS) ||
-               (((s32)(tp->rcv_tsval - tp->ts_recent) < 0) &&
-                /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */
-                (len != (th->doff * 4))));
+       return ((s32)(tp->rcv_tsval - tp->ts_recent) < 0 &&
+               (s32)(tcp_time_stamp - tp->ts_recent_stamp) < PAWS_24DAYS &&
+               /* Sorry, PAWS as specified is broken wrt. pure-ACKs -DaveM */
+               len != (th->doff * 4));
 }
 
 
@@ -946,11 +943,13 @@ void tcp_timewait_kill(struct tcp_tw_bucket *tw)
 
 /* We come here as a special case from the AF specific TCP input processing,
  * and the SKB has no owner.  Essentially handling this is very simple,
- * we just keep silently eating rx'd packets until none show up for the
- * entire timeout period.  The only special cases are for BSD TIME_WAIT
- * reconnects and SYN/RST bits being set in the TCP header.
+ * we just keep silently eating rx'd packets, acking them if necessary,
+ * until none show up for the entire timeout period. 
+ *
+ * Return 0, TCP_TW_ACK, TCP_TW_RST
  */
-int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
+enum tcp_tw_status 
+tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
                               struct tcphdr *th, unsigned len)
 {
        /*      RFC 1122:
@@ -971,7 +970,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
                struct tcp_func *af_specific = tw->af_specific;
                __u32 isn;
 
-               isn = tw->rcv_nxt + 128000;
+               isn = tw->snd_nxt + 128000;
                if(isn == 0)
                        isn++;
                tcp_tw_deschedule(tw);
@@ -984,7 +983,7 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
                skb_set_owner_r(skb, sk);
                af_specific = sk->tp_pinfo.af_tcp.af_specific;
                if(af_specific->conn_request(sk, skb, isn) < 0)
-                       return 1; /* Toss a reset back. */
+                       return TCP_TW_RST; /* Toss a reset back. */
                return 0; /* Discard the frame. */
        }
 
@@ -999,13 +998,17 @@ int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
                        tcp_timewait_kill(tw);
                }
                if(!th->rst)
-                       return 1; /* toss a reset back */
+                       return TCP_TW_RST; /* toss a reset back */
+               return 0;
        } else {
                /* In this case we must reset the TIMEWAIT timer. */
                if(th->ack)
                        tcp_tw_reschedule(tw);
        }
-       return 0; /* Discard the frame. */
+       /* Ack old packets if necessary */ 
+       if (!after(TCP_SKB_CB(skb)->end_seq, tw->rcv_nxt)) 
+               return TCP_TW_ACK; 
+       return 0; 
 }
 
 /* Enter the time wait state.  This is always called from BH
@@ -1064,6 +1067,7 @@ void tcp_time_wait(struct sock *sk)
                tw->family      = sk->family;
                tw->reuse       = sk->reuse;
                tw->rcv_nxt     = sk->tp_pinfo.af_tcp.rcv_nxt;
+               tw->snd_nxt = sk->tp_pinfo.af_tcp.snd_nxt;
                tw->af_specific = sk->tp_pinfo.af_tcp.af_specific;
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
@@ -1535,7 +1539,6 @@ static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len)
         * Now tell the user we may have some data.
         */
        if (!sk->dead) {
-               SOCK_DEBUG(sk, "Data wakeup.\n");
                sk->data_ready(sk,0);
        }
        return(1);
@@ -1975,7 +1978,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
        flg &= __constant_htonl(0x00170000);
        /* Only SYN set? */
        if (flg == __constant_htonl(0x00020000)) {
-               if (!after(TCP_SKB_CB(skb)->seq, req->rcv_isn)) {
+               if (TCP_SKB_CB(skb)->seq == req->rcv_isn) {
                        /*      retransmited syn.
                         */
                        req->class->rtx_syn_ack(sk, req); 
index 03c41eb5ad1bb41e48d57eec08b6391d04161970..b67bfd827713f7ee3421d0aa1b67c69fb153da00 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:    $Id: tcp_ipv4.c,v 1.175.2.7 1999/07/23 15:38:46 davem Exp $
+ * Version:    $Id: tcp_ipv4.c,v 1.175.2.8 1999/08/08 08:43:20 davem Exp $
  *
  *             IPv4 specific functions
  *
@@ -178,6 +178,17 @@ static __inline__ void __tcp_inherit_port(struct sock *sk, struct sock *child)
 {
        struct tcp_bind_bucket *tb = (struct tcp_bind_bucket *)sk->prev;
 
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+       if (child->num != sk->num) {
+               unsigned short snum = ntohs(child->num);
+               for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+                   tb && tb->port != snum;
+                   tb = tb->next)
+                       ;
+               if (tb == NULL)
+                       tb = (struct tcp_bind_bucket *)sk->prev;
+       }
+#endif
        if ((child->bind_next = tb->owners) != NULL)
                tb->owners->bind_pprev = &child->bind_next;
        tb->owners = child;
@@ -1008,6 +1019,46 @@ static void tcp_v4_send_reset(struct sk_buff *skb)
        tcp_statistics.TcpOutRsts++;
 }
 
+/* 
+ *     Send an ACK for a socket less packet (needed for time wait) 
+ *
+ *  FIXME: Does not echo timestamps yet.
+ *
+ *  Assumes that the caller did basic address and flag checks.
+ */
+static void tcp_v4_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack)
+{
+       struct tcphdr *th = skb->h.th;
+       struct tcphdr rth;
+       struct ip_reply_arg arg;
+
+       /* Swap the send and the receive. */
+       memset(&rth, 0, sizeof(struct tcphdr)); 
+       rth.dest = th->source;
+       rth.source = th->dest; 
+       rth.doff = sizeof(struct tcphdr)/4;
+
+       rth.seq = seq;
+       rth.ack_seq = ack; 
+       rth.ack = 1;
+
+       memset(&arg, 0, sizeof arg); 
+       arg.iov[0].iov_base = (unsigned char *)&rth; 
+       arg.iov[0].iov_len  = sizeof rth;
+       arg.csum = csum_tcpudp_nofold(skb->nh.iph->daddr, 
+                                     skb->nh.iph->saddr, /*XXX*/
+                                     sizeof(struct tcphdr),
+                                     IPPROTO_TCP,
+                                     0); 
+       arg.n_iov = 1;
+       arg.csumoffset = offsetof(struct tcphdr, check) / 2; 
+
+       ip_send_reply(tcp_socket->sk, skb, &arg, sizeof rth);
+
+       tcp_statistics.TcpOutSegs++;
+}
+
+
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 
 /*
@@ -1561,7 +1612,7 @@ static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb)
                        sk = tcp_check_req(sk, skb, req);
                }
 #ifdef CONFIG_SYN_COOKIES
-               else {
+               else if (flg == __constant_htonl(0x00120000))  {
                        sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt));
                }
 #endif
@@ -1721,10 +1772,19 @@ discard_it:
        return 0;
 
 do_time_wait:
-       if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
-                                     skb, th, skb->len))
-               goto no_tcp_socket;
-       goto discard_it;
+       /* Sorry for the ugly switch. 2.3 will have a better solution. */ 
+       switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+                                                          skb, th, skb->len)) {
+       case TCP_TW_ACK:
+               tcp_v4_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt,
+                                               ((struct tcp_tw_bucket *)sk)->rcv_nxt); 
+               break; 
+       case TCP_TW_RST:
+               goto no_tcp_socket; 
+       default:
+               goto discard_it; 
+       }
+       return 0;
 }
 
 static void __tcp_v4_rehash(struct sock *sk)
index 75a887d295bd7db4875f0d268bea2f728b52f6f9..834e8f28c65eafe9b88b7f3f2839103cee50a235 100644 (file)
@@ -5,7 +5,7 @@
  *     Authors:
  *     Pedro Roque             <roque@di.fc.ul.pt>     
  *
- *     $Id: tcp_ipv6.c,v 1.104.2.5 1999/07/23 15:38:49 davem Exp $
+ *     $Id: tcp_ipv6.c,v 1.104.2.6 1999/08/08 08:43:23 davem Exp $
  *
  *     Based on: 
  *     linux/net/ipv4/tcp.c
@@ -1121,6 +1121,57 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
        kfree_skb(buff);
 }
 
+static void tcp_v6_send_ack(struct sk_buff *skb, __u32 seq, __u32 ack)
+{
+       struct tcphdr *th = skb->h.th, *t1; 
+       struct sk_buff *buff;
+       struct flowi fl;
+
+       buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr), GFP_ATOMIC);
+       if (buff == NULL) 
+               return;
+
+       skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr));
+
+       t1 = (struct tcphdr *) skb_push(buff,sizeof(struct tcphdr));
+
+       /* Swap the send and the receive. */
+       memset(t1, 0, sizeof(*t1));
+       t1->dest = th->source;
+       t1->source = th->dest;
+       t1->doff = sizeof(*t1)/4;
+       t1->ack = 1;
+       t1->seq = seq;
+       t1->ack_seq = ack; 
+
+       buff->csum = csum_partial((char *)t1, sizeof(*t1), 0);
+
+       fl.nl_u.ip6_u.daddr = &skb->nh.ipv6h->saddr;
+       fl.nl_u.ip6_u.saddr = &skb->nh.ipv6h->daddr;
+       fl.fl6_flowlabel = 0;
+
+       t1->check = csum_ipv6_magic(fl.nl_u.ip6_u.saddr,
+                                   fl.nl_u.ip6_u.daddr, 
+                                   sizeof(*t1), IPPROTO_TCP,
+                                   buff->csum);
+
+       fl.proto = IPPROTO_TCP;
+       fl.oif = tcp_v6_iif(skb);
+       fl.uli_u.ports.dport = t1->dest;
+       fl.uli_u.ports.sport = t1->source;
+
+       /* sk = NULL, but it is safe for now. static socket required. */
+       buff->dst = ip6_route_output(NULL, &fl);
+
+       if (buff->dst->error == 0) {
+               ip6_xmit(NULL, buff, &fl, NULL);
+               tcp_statistics.TcpOutSegs++;
+               return;
+       }
+
+       kfree_skb(buff);
+}
+
 static struct open_request *tcp_v6_search_req(struct tcp_opt *tp,
                                              struct ipv6hdr *ip6h,
                                              struct tcphdr *th,
@@ -1409,10 +1460,19 @@ discard_it:
        return 0;
 
 do_time_wait:
-       if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
-                                     skb, th, skb->len))
+       switch (tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+                                                                          skb, th, skb->len)) {
+       case TCP_TW_ACK:
+               tcp_v6_send_ack(skb, ((struct tcp_tw_bucket *)sk)->snd_nxt,
+                                               ((struct tcp_tw_bucket *)sk)->rcv_nxt); 
+               break; 
+       case TCP_TW_RST:
                goto no_tcp_socket;
-       goto discard_it;
+       default:
+               goto discard_it; 
+       }
+
+       return 0;
 }
 
 static int tcp_v6_rebuild_header(struct sock *sk)
index a7ab547bc429c9754b56aa8b75aa1436dc07c21d..6d63862b9e57e2a5afb53394744e8c5cdb3827c4 100644 (file)
@@ -21,6 +21,7 @@
 #include <net/dst.h>
 #include <net/checksum.h>
 #include <linux/etherdevice.h>
+#include <net/route.h>
 #ifdef CONFIG_HIPPI
 #include <linux/hippidevice.h>
 #endif
@@ -177,6 +178,7 @@ EXPORT_SYMBOL(neigh_destroy);
 EXPORT_SYMBOL(neigh_parms_alloc);
 EXPORT_SYMBOL(neigh_parms_release);
 EXPORT_SYMBOL(neigh_rand_reach_time);
+EXPORT_SYMBOL(neigh_compat_output); 
 
 /*     dst_entry       */
 EXPORT_SYMBOL(dst_alloc);
@@ -238,6 +240,7 @@ EXPORT_SYMBOL(__ip_finish_output);
 EXPORT_SYMBOL(inet_dgram_ops);
 EXPORT_SYMBOL(ip_cmsg_recv);
 EXPORT_SYMBOL(__release_sock);
+EXPORT_SYMBOL(inet_addr_type); 
 
 /* Route manipulation */
 EXPORT_SYMBOL(ip_rt_ioctl);
@@ -321,7 +324,6 @@ EXPORT_SYMBOL(tcp_inherit_port);
 EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
 EXPORT_SYMBOL(tcp_v4_do_rcv);
 EXPORT_SYMBOL(tcp_v4_connect);
-EXPORT_SYMBOL(inet_addr_type);
 EXPORT_SYMBOL(net_reset_timer);
 EXPORT_SYMBOL(net_delete_timer);
 EXPORT_SYMBOL(udp_prot);