]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.31pre9 2.0.31pre9
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:32 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:32 +0000 (15:11 -0500)
27 files changed:
Documentation/Configure.help
MAINTAINERS
arch/alpha/defconfig
arch/i386/defconfig
arch/m68k/defconfig
arch/mips/defconfig
arch/sparc/defconfig
drivers/char/serial.c
drivers/net/3c509.c
drivers/net/3c59x.c
drivers/net/eepro100.c
drivers/net/tulip.c
drivers/sound/Config.in
fs/Config.in
fs/Makefile
fs/autofs/Makefile [new file with mode: 0644]
fs/autofs/autofs_i.h [new file with mode: 0644]
fs/autofs/dir.c [new file with mode: 0644]
fs/autofs/dirhash.c [new file with mode: 0644]
fs/autofs/init.c [new file with mode: 0644]
fs/autofs/inode.c [new file with mode: 0644]
fs/autofs/root.c [new file with mode: 0644]
fs/autofs/symlink.c [new file with mode: 0644]
fs/autofs/waitq.c [new file with mode: 0644]
fs/filesystems.c
include/linux/auto_fs.h [new file with mode: 0644]
kernel/ksyms.c

index f35ccc47ebb0d6c65f1c789009ddd20d4fee7d9d..22b86689b3b47f372800901130be661948dbaf1f 100644 (file)
@@ -3211,6 +3211,17 @@ CONFIG_SYSV_FS
   Documentation/modules.txt. If you haven't heard about all of this
   before, it's safe to say N.
 
+Kernel automounter support (experimental)
+CONFIG_AUTOFS_FS
+  The automounter is a tool to automatically mount remote filesystems
+  on demand.  This implementation is partially kernel-based to reduce
+  overhead in the already-mounted case; this is unlike the BSD
+  automounter (amd), which is only in user space.  To use the
+  automounter you also need the user-space tools from
+  ftp.kernel.org:/pub/linux/daemons/autofs.  If you are not a part of
+  a fairly large, distributed network, you probably do not need an
+  automounter, and can say N here.
+
 BSD UFS filesystem support (read only)
 CONFIG_UFS_FS
   BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD
index 7621c0e142ace9e03f81ff91c5b6b5d14d96b24e..74996b0c9a91f4bea0706f68527798f114a85c4b 100644 (file)
@@ -406,6 +406,18 @@ P: John A. Martin
 M:     jam@acm.org
 S:     Maintained
 
+KERNEL AUTOMOUNTER (AUTOFS)
+P:     H. Peter Anvin
+M:     hpa@zytor.com
+L:     autofs@linux.kernel.org
+S:     Maintained
+
+DEVICE NUMBER REGISTRY
+P:     H. Peter Anvin
+M:     hpa@zytor.com
+L:     linux-kernel@vger.rutgers.edu
+S:     Maintained
+
 REST:
 P:     Linus Torvalds
 S:     Buried alive in email
index c6e8bd8dc77bbb248e4d43be87710c1cc3d2e3a0..9f33eeaa45afe87be8031aec32651c73d053adeb 100644 (file)
@@ -196,6 +196,7 @@ CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 
 #
 # Character devices
index b568a45b0e8547ed0a0601d31b23c8cb3c88d9f0..c23faa687c70e56cf4120b89e9c13212873fb99f 100644 (file)
@@ -150,6 +150,7 @@ CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_UFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 
 #
 # Character devices
index 3650350eb5b7ed16283cfcbef0f17cd611272159..7ae83b0b3437af20040d8c10b67e478fc008563d 100644 (file)
@@ -122,6 +122,7 @@ CONFIG_NFS_FS=y
 # CONFIG_ISO9660_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
+# CONFIG_AUTOFS_FS is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_UFS_FS is not set
 
index 923745b875b8bc273dc99484117101bcdcf7286d..199060456d2678cdf4480c4e2efdf99195984803 100644 (file)
@@ -61,6 +61,7 @@ CONFIG_PROC_FS=y
 # CONFIG_ISO9660_FS is not set
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
+# CONFIG_AUTOFS_FS is not set
 # CONFIG_SMB_FS is not set
 
 #
index df03b88d6f576ef6bfe50d22a26578435c6a4cee..1c98ff52995a6d21bdfb5bb329266f744f4a9112 100644 (file)
@@ -114,6 +114,7 @@ CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
+# CONFIG_AUTOFS_FS is not set
 CONFIG_UFS_FS=y
 
 #
index 76ab51082ecb74652ff8ce6c5e0cc2f04ef2ca79..8c64e4c9613c0cbb2b264479ba56a35d7253f0a3 100644 (file)
@@ -2960,7 +2960,7 @@ void cleanup_module(void)
                        release_region(rs_table[i].port, 8);
        }
        if (tmp_buf) {
-               free_page(tmp_buf);
+               free_page((unsigned long) tmp_buf);
                tmp_buf = NULL;
        }
 }
index f95e1f1e1d1c5900c32cf8270f32d19f462155e0..7c9013390939b7ab15b18f866dd3d86a820d96ee 100644 (file)
@@ -35,7 +35,7 @@
                                other cleanups.  -djb
 */
 
-static char *version = "3c509.c:1.11 4/22/97 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n";
 /* A few values that may be tweaked. */
 
 /* Time in jiffies before concluding the transmitter is hung. */
@@ -279,10 +279,8 @@ int el3_probe(struct device *dev)
        memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
        dev->base_addr = ioaddr;
        dev->irq = irq;
-       if (dev->mem_start)
-               dev->if_port = dev->mem_start & 3;
-       else
-               dev->if_port = if_port;
+       dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port;
+
        request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
 
        {
@@ -694,9 +692,8 @@ el3_rx(struct device *dev)
                                printk("%s: Couldn't allocate a sk_buff of size %d.\n",
                                           dev->name, pkt_len);
                }
-               outw(RxDiscard, ioaddr + EL3_CMD);
                lp->stats.rx_dropped++;
-               SLOW_DOWN_IO;
+               outw(RxDiscard, ioaddr + EL3_CMD);
                while (inw(ioaddr + EL3_STATUS) & 0x1000)
                        printk("        Waiting for 3c509 to discard packet, status %x.\n",
                                   inw(ioaddr + EL3_STATUS) );
@@ -727,7 +724,7 @@ set_multicast_list(struct device *dev)
                outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
        }
        else
-               outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+                outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
 
 static int
index 1427e2b97cb781ebc0ed436102efaca2061f9a09..609383240c0c5a2ff9c869b32a0bd1d1d2b44f83 100644 (file)
@@ -1,4 +1,4 @@
-/* 3c900.c: A 3Com EtherLink III XL "Boomerang" ethernet driver for linux. */
+/* EtherLink.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */
 /*
        Written 1996-1997 by Donald Becker.
 
 */
 
 static char *version =
-"3c59x.c/3c900.c:v0.42 7/15/97 Donald Becker linux-vortex@cesdis.gsfc.nasa.gov\n";
+"3c59x.c:v0.43 9/2/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
 
 /* "Knobs" that turn on special features. */
-/* Enable the automatic media selection code. */
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+   Setting to > 1512 effectively disables this feature. */
+static const rx_copybreak = 200;
+/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
+static const mtu = 1500;
+
+/* Enable the automatic media selection code -- usually set. */
 #define AUTOMEDIA 1
 
 /* Allow the use of fragment bus master transfers instead of only
@@ -36,12 +42,17 @@ static char *version =
 #define RX_RING_SIZE   32
 #define PKT_BUF_SZ             1536                    /* Size of each temporary Rx buffer.*/
 
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
-   Setting to > 1512 effectively disables this feature. */
-static const rx_copybreak = 200;
-
-#include <linux/version.h>
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -109,10 +120,6 @@ static const rx_copybreak = 200;
    debugging. */
 static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;
 
-/* Number of times to check to see if the Tx FIFO has space, used in some
-   limited cases. */
-#define WAIT_TX_AVAIL 200
-
 /* Operational parameter that usually are not changed. */
 
 /* The Vortex size is twice that of the original EtherLinkIII series: the
@@ -281,6 +288,7 @@ enum Window1 {
 enum Window0 {
        Wn0EepromCmd = 10,              /* Window 0: EEPROM command register. */
        Wn0EepromData = 12,             /* Window 0: EEPROM results register. */
+       IntrStatus=0x0E,                /* Valid in all windows. */
 };
 enum Win0_EEPROM_bits {
        EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
@@ -307,8 +315,8 @@ union wn3_config {
        } u;
 };
 
-enum Window4 {
-       Wn4_NetDiag = 6, Wn4_Media = 10,                /* Window 4: Xcvr/media bits. */
+enum Window4 {         /* Window 4: Xcvr/media bits. */
+       Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
 };
 enum Win4_Media_bits {
        Media_SQE = 0x0008,             /* Enable SQE error counting for AUI. */
@@ -328,22 +336,32 @@ enum MasterCtrl {
 /* The Rx and Tx descriptor lists.
    Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
    alignment contraint on tx_ring[] and rx_ring[]. */
+#define LAST_FRAG  0x80000000                  /* Last Addr/Len pair in descriptor. */
 struct boom_rx_desc {
-       u32 next;
+       u32 next;                                       /* Last entry points to 0.   */
        s32 status;
-       u32 addr;
-       s32 length;
+       u32 addr;                                       /* Up to addr/len possible.. */
+       s32 length;                                     /* set high bit to indicate last pair. */
 };
 /* Values for the Rx status entry. */
-#define RX_COMPLETE 0x00008000
+enum rx_desc_status {
+       RxDComplete=0x00008000, RxDError=0x4000,
+       /* See boomerang_rx() for actual error bits */
+};
 
 struct boom_tx_desc {
-       u32 next;
-       s32 status;
+       u32 next;                                       /* Last entry points to 0.   */
+       s32 status;                                     /* bits 0:12 length, others see below.  */
        u32 addr;
        s32 length;
 };
 
+/* Values for the Tx status entry. */
+enum tx_desc_status {
+       CRCDisable=0x2000, TxDComplete=0x8000,
+       TxIntrUploaded=0x80000000,              /* IRQ when in FIFO, but maybe not sent. */
+};
+
 struct vortex_private {
        char devname[8];                        /* "ethN" string, also for kernel debug. */
        const char *product_name;
@@ -370,11 +388,17 @@ struct vortex_private {
       tx_full:1;
        u16 capabilities;                       /* Adapter capabilities word. */
        u16 info1, info2;                       /* Software information information. */
+       unsigned char phys[2];          /* MII device addresses. */
 };
 
 /* The action to take with a media selection timer tick.
    Note that we deviate from the 3Com order by checking 10base2 before AUI.
  */
+enum xcvr_types {
+       XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
+       XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8,
+};
+
 static struct media_table {
   char *name;
   unsigned int media_bits:16,          /* Bits to set in Wn4_Media register. */
@@ -382,23 +406,24 @@ static struct media_table {
        next:8;                         /* The media type to try next. */
   short wait;                  /* Time before we check media status. */
 } media_tbl[] = {
-  {    "10baseT",   Media_10TP,0x08, 3 /* 10baseT->10base2 */, (14*HZ)/10},
-  { "10Mbs AUI", Media_SQE, 0x20, 8 /* AUI->default */, (1*HZ)/10},
-  { "undefined", 0,                    0x80, 0 /* Undefined */, 10000},
-  { "10base2",   0,                    0x10, 1 /* 10base2->AUI. */, (1*HZ)/10},
-  { "100baseTX", Media_Lnk, 0x02, 5 /* 100baseTX->100baseFX */, (14*HZ)/10},
-  { "100baseFX", Media_Lnk, 0x04, 6 /* 100baseFX->MII */, (14*HZ)/10},
-  { "MII",              0,                     0x40, 0 /* MII->10baseT */, (14*HZ)/10},
-  { "undefined", 0,                    0x01, 0 /* Undefined/100baseT4 */, 10000},
-  { "Default",  0,                     0xFF, 0 /* Use default */, 10000},
+  {    "10baseT",   Media_10TP,0x08, XCVR_10base2, (14*HZ)/10},
+  { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10},
+  { "undefined", 0,                    0x80, XCVR_10baseT, 10000},
+  { "10base2",   0,                    0x10, XCVR_AUI,         (1*HZ)/10},
+  { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10},
+  { "100baseFX", Media_Lnk, 0x04, XCVR_MII,            (14*HZ)/10},
+  { "MII",              0,                     0x40, XCVR_10baseT, 3*HZ },
+  { "undefined", 0,                    0x01, XCVR_10baseT, 10000},
+  { "Default",  0,                     0xFF, XCVR_10baseT, 10000},
 };
 
 static int vortex_scan(struct device *dev);
 static struct device *vortex_found_device(struct device *dev, int ioaddr,
                                                                                  int irq, int product_index,
-                                                                                 int options);
+                                                                                 int options, int card_idx);
 static int vortex_probe1(struct device *dev);
 static int vortex_open(struct device *dev);
+static int mdio_read(int ioaddr, int phy_id, int location);
 static void vortex_timer(unsigned long arg);
 static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
 static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev);
@@ -431,13 +456,15 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
 /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
 /* Note: this is the only limit on the number of cards supported!! */
 static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
 /* Variables to work-around the Compaq PCI BIOS32 problem. */
 static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0;
 
 #ifdef MODULE
 static int debug = -1;
-/* A list of all installed Vortex devices, for removing the driver module. */
-static struct device *root_vortex_dev = NULL;
 
 int
 init_module(void)
@@ -519,7 +546,8 @@ static int vortex_scan(struct device *dev)
 
                        dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
                                                                          board_index, dev && dev->mem_start
-                                                                         ? dev->mem_start : options[cards_found]);
+                                                                         ? dev->mem_start : options[cards_found],
+                                                                         cards_found);
 
                        if (dev) {
                                /* Get and check the bus-master and latency values.
@@ -572,7 +600,8 @@ static int vortex_scan(struct device *dev)
                                continue;
                        vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
                                                                product_index,  dev && dev->mem_start
-                                                               ? dev->mem_start : options[cards_found]);
+                                                               ? dev->mem_start : options[cards_found],
+                                                               cards_found);
                        dev = 0;
                        cards_found++;
                }
@@ -581,7 +610,8 @@ static int vortex_scan(struct device *dev)
        /* Special code to work-around the Compaq PCI BIOS32 problem. */
        if (compaq_ioaddr) {
                vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_prod_id,
-                                                       dev && dev->mem_start ? dev->mem_start : options[cards_found]);
+                                                       dev && dev->mem_start ? dev->mem_start
+                                                       : options[cards_found], cards_found);
                cards_found++;
                dev = 0;
        }
@@ -594,7 +624,7 @@ static int vortex_scan(struct device *dev)
 
 static struct device *
 vortex_found_device(struct device *dev, int ioaddr, int irq,
-                                       int product_index, int options)
+                                       int product_index, int options, int card_idx)
 {
        struct vortex_private *vp;
 
@@ -614,13 +644,18 @@ vortex_found_device(struct device *dev, int ioaddr, int irq,
        dev->init = vortex_probe1;
        vp->product_name = product_names[product_index];
        vp->options = options;
+       if (card_idx >= 0) {
+               if (full_duplex[card_idx] >= 0)
+                       vp->full_duplex = full_duplex[card_idx];
+       } else
+               vp->full_duplex = (options >= 0 && (options & 0x10) ? 1 : 0);
+
        if (options >= 0) {
-               vp->media_override = ((options & 7) == 2)  ?  0  :  options & 7;
-               vp->full_duplex = (options & 8) ? 1 : 0;
+               vp->media_override = ((options & 7) == XCVR_10baseTOnly) ?
+                       XCVR_10baseT  :  options & 7;
                vp->bus_master = (options & 16) ? 1 : 0;
        } else {
                vp->media_override = 7;
-               vp->full_duplex = 0;
                vp->bus_master = 0;
        }
        ether_setup(dev);
@@ -637,6 +672,8 @@ vortex_found_device(struct device *dev, int ioaddr, int irq,
        dev = init_etherdev(dev, sizeof(struct vortex_private));
        dev->base_addr = ioaddr;
        dev->irq = irq;
+       dev->mtu = mtu;
+
        vp  = (struct vortex_private *)dev->priv;
        vp->product_name = product_names[product_index];
        vp->options = options;
@@ -717,11 +754,29 @@ static int vortex_probe1(struct device *dev)
                dev->if_port = vp->media_override;
        }
 
+       if (dev->if_port == XCVR_MII) {
+               int phy, phy_idx = 0;
+               EL3WINDOW(4);
+               for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
+                       int mii_status = mdio_read(ioaddr, phy, 0);
+                       if (mii_status != 0xffff  && mii_status != 0x0000) {
+                               vp->phys[phy_idx++] = phy;
+                               printk("%s: MII transceiver found at address %d.\n",
+                                          dev->name, phy);
+                       }
+               }
+               if (phy_idx == 0) {
+                       printk("%s: ***WARNING*** No MII transceivers found!\n",
+                                  dev->name);
+                       vp->phys[0] = 0;
+               }
+       }
+
        vp->info1 = eeprom[13];
        vp->info2 = eeprom[15];
        vp->capabilities = eeprom[16];
        if (vp->capabilities & 0x20) {
-         vp->full_bus_master_tx = 0;   /* TX bugs, force bus_master_tx to 0? */
+         vp->full_bus_master_tx = 1;
          printk("  Enabling bus-master transmits and %s receives.\n",
                         (vp->info2 & 1) ? "early" : "whole-frame" );
          vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
@@ -743,6 +798,54 @@ static int vortex_probe1(struct device *dev)
 
        return 0;
 }
+\f
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  The maxium data clock rate is 2.5 Mhz. */
+#define mdio_delay(microsecs) udelay(microsecs)
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN            0x00
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+       int i;
+       int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+       unsigned short retval = 0;
+       int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+       /* Shift the read command bits out. */
+       for (i = 17; i >= 0; i--) {
+               int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+               outw(dataval, mdio_addr);
+               mdio_delay(1);
+               outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay(1);
+               outw(dataval, mdio_addr);
+               mdio_delay(1);
+       }
+       outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+       outw(MDIO_ENB_IN, mdio_addr);
+
+       for (i = 16; i > 0; i--) {
+               outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay(1);
+               retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+               outw(MDIO_ENB_IN, mdio_addr);
+               mdio_delay(1);
+       }
+       /* Clear out extra bits.  Needed? */
+       for (i = 16; i > 0; i--) {
+               outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay(1);
+               outw(MDIO_ENB_IN, mdio_addr);
+               mdio_delay(1);
+       }
+       return retval;
+}
 
 \f
 static int
@@ -765,7 +868,7 @@ vortex_open(struct device *dev)
                dev->if_port = vp->media_override;
        } else if (vp->autoselect) {
                /* Find first available media type, starting with 100baseTx. */
-               dev->if_port = 4;
+               dev->if_port = XCVR_100baseTx;
                while (! (vp->available_media & media_tbl[dev->if_port].mask))
                        dev->if_port = media_tbl[dev->if_port].next;
 
@@ -784,6 +887,23 @@ vortex_open(struct device *dev)
        config.u.xcvr = dev->if_port;
        outl(config.i, ioaddr + Wn3_Config);
 
+       if (dev->if_port == XCVR_MII) {
+               int mii_reg1, mii_reg25;
+               /* We cheat here: we know that we are using the 83840 transceiver
+                  which summarizes the FD status in an extended register. */
+               EL3WINDOW(4);
+               /* Read BMSR (reg1) only to clear old status. */
+               mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+               mii_reg25 = mdio_read(ioaddr, vp->phys[0], 0x19);
+               if (vortex_debug > 1)
+                       printk("%s: MII #%d status %4.4x, duplex report %4.4x,"
+                                  " setting %s-duplex.\n", dev->name, vp->phys[0],
+                                  mii_reg1, mii_reg25, mii_reg25 & 0x0080 ? "full" : "half");
+               if (mii_reg25 & 0x0080)
+                       vp->full_duplex = 1;
+               EL3WINDOW(3);
+       }
+
        /* Set the full-duplex bit. */
        outb(((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
                 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
@@ -835,7 +955,7 @@ vortex_open(struct device *dev)
        for (; i < 12; i+=2)
                outw(0, ioaddr + i);
 
-       if (dev->if_port == 3)
+       if (dev->if_port == XCVR_10base2)
                /* Start the thinnet transceiver. We should really wait 50ms...*/
                outw(StartCoax, ioaddr + EL3_CMD);
        EL3WINDOW(4);
@@ -869,7 +989,7 @@ vortex_open(struct device *dev)
                        struct sk_buff *skb;
                        vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
                        vp->rx_ring[i].status = 0;      /* Clear complete bit. */
-                       vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
+                       vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG;
                        skb = dev_alloc_skb(PKT_BUF_SZ);
                        vp->rx_skbuff[i] = skb;
                        if (skb == NULL)
@@ -937,7 +1057,7 @@ static void vortex_timer(unsigned long data)
          EL3WINDOW(4);
          media_status = inw(ioaddr + Wn4_Media);
          switch (dev->if_port) {
-         case 0:  case 4:  case 5:             /* 10baseT, 100baseTX, 100baseFX  */
+         case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx:
                if (media_status & Media_LnkBeat) {
                  ok = 1;
                  if (vortex_debug > 1)
@@ -948,6 +1068,16 @@ static void vortex_timer(unsigned long data)
                                   dev->name, media_tbl[dev->if_port].name, media_status);
 
                break;
+         case XCVR_MII:
+                 {
+                         int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
+                         if (vortex_debug > 1)
+                                 printk("%s: MII #%d status register is %4.4x.\n",
+                                                dev->name, vp->phys[0], mii_reg1);
+                         if (mii_reg1 & 0x0004)
+                                 ok = 1;
+                         break;
+                 }
          default:                                      /* Other media types handled by Tx timeouts. */
                if (vortex_debug > 1)
                  printk("%s: Media %s is has no indication, %x.\n",
@@ -960,7 +1090,7 @@ static void vortex_timer(unsigned long data)
                do {
                        dev->if_port = media_tbl[dev->if_port].next;
                } while ( ! (vp->available_media & media_tbl[dev->if_port].mask));
-               if (dev->if_port == 8) { /* Go back to default. */
+               if (dev->if_port == XCVR_Default) { /* Go back to default. */
                  dev->if_port = vp->default_media;
                  if (vortex_debug > 1)
                        printk("%s: Media selection failing, using default %s port.\n",
@@ -980,7 +1110,8 @@ static void vortex_timer(unsigned long data)
                config.u.xcvr = dev->if_port;
                outl(config.i, ioaddr + Wn3_Config);
 
-               outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+               outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax,
+                        ioaddr + EL3_CMD);
          }
          EL3WINDOW(old_window);
        }   restore_flags(flags);
@@ -1201,8 +1332,8 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
                vp->tx_skbuff[entry] = skb;
                vp->tx_ring[entry].next = 0;
                vp->tx_ring[entry].addr = virt_to_bus(skb->data);
-               vp->tx_ring[entry].length = skb->len | 0x80000000;
-               vp->tx_ring[entry].status = skb->len | 0x80000000;
+               vp->tx_ring[entry].length = skb->len | LAST_FRAG;
+               vp->tx_ring[entry].status = skb->len | TxIntrUploaded;
 
                save_flags(flags);
                cli();
@@ -1223,7 +1354,7 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev)
                if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
                        vp->tx_full = 1;
                else {                                  /* Clear previous interrupt enable. */
-                       prev_entry->status &= ~0x80000000;
+                       prev_entry->status &= ~TxIntrUploaded;
                        dev->tbusy = 0;
                }
                dev->trans_start = jiffies;
@@ -1297,7 +1428,7 @@ static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
                                        dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
                                        lp->tx_skbuff[entry] = 0;
                                }
-                               lp->stats.tx_packets++;
+                               /* lp->stats.tx_packets++;  Counted below. */
                                dirty_tx++;
                        }
                        outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
@@ -1461,8 +1592,8 @@ boomerang_rx(struct device *dev)
        if (vortex_debug > 5)
                printk("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
                           inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
-       while ((rx_status = vp->rx_ring[entry].status) & RX_COMPLETE) {
-               if (rx_status & 0x4000) { /* Error, update stats. */
+       while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
+               if (rx_status & RxDError) { /* Error, update stats. */
                        unsigned char rx_error = rx_status >> 16;
                        if (vortex_debug > 4)
                                printk(" Rx error: status %2.2x.\n", rx_error);
@@ -1566,7 +1697,7 @@ vortex_close(struct device *dev)
        outw(RxDisable, ioaddr + EL3_CMD);
        outw(TxDisable, ioaddr + EL3_CMD);
 
-       if (dev->if_port == 3)
+       if (dev->if_port == XCVR_10base2)
                /* Turn off thinnet power.  Green! */
                outw(StopCoax, ioaddr + EL3_CMD);
 
@@ -1704,12 +1835,14 @@ cleanup_module(void)
                root_vortex_dev = next_dev;
        }
 }
-#endif /* MODULE */
+
+#endif  /* MODULE */
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
index ffa0e50fd7efd4a0b78f510d392a4ade35a50418..3f4c02ada1724e5d6d9f40059ffad88f2898a98d 100644 (file)
@@ -1,7 +1,7 @@
 /* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
 /*
    NOTICE: this version tested with kernels 1.3.72 and later only!
-       Written 1996 by Donald Becker.
+       Written 1996-1997 by Donald Becker.
 
        This software may be used and distributed according to the terms
        of the GNU Public License, incorporated herein by reference.
@@ -19,7 +19,7 @@
 */
 
 static const char *version =
-"eepro100.c:v0.32 4/8/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
+"eepro100.c:v0.34 8/30/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values that apply to all boards.
    First set are undocumented and spelled per Intel recommendations. */
@@ -36,14 +36,17 @@ static int rxdmacount = 0;  /* Rx DMA length, 0 means no preemption. */
 #define SKBUFF_RX_COPYBREAK 256
 
 #include <linux/config.h>
-#include <linux/version.h>
 #ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
 #else
 #define MOD_INC_USE_COUNT
 #define MOD_DEC_USE_COUNT
 #endif
 
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -112,6 +115,10 @@ struct device *init_etherdev(struct device *dev, int sizeof_priv,
 #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
 #endif
 
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
 /* The total I/O port extent of the board.  Nominally 0x18, but rounded up
    for PCI allocation. */
 #define SPEEDO3_TOTAL_SIZE 0x20
@@ -142,7 +149,7 @@ PCI bus devices are configured by the system at boot time, so no jumpers
 need to be set on the board.  The system BIOS should be set to assign the
 PCI INTA signal to an otherwise unused system IRQ line.  While it's
 possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it. 
+only recent kernels support it.
 
 III. Driver operation
 
@@ -422,14 +429,15 @@ const char basic_config_cmd[22] = {
 /* PHY media interface chips. */
 static const char *phys[] = {
        "None", "i82553-A/B", "i82553-C", "i82503",
-       "DP83840", "80c240", "80c24", "unknown-7",
+       "DP83840", "80c240", "80c24", "i82555",
        "unknown-8", "unknown-9", "DP83840A", "unknown-11",
        "unknown-12", "unknown-13", "unknown-14", "unknown-15", };
 enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
-                                        S80C24, PhyUndefined, DP83840A=10, };
+                                        S80C24, I82555, DP83840A=10, };
 static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
 
-static void speedo_found1(struct device *dev, int ioaddr, int irq, int options);
+static void speedo_found1(struct device *dev, int ioaddr, int irq,
+                                                 int options, int card_idx);
 
 static int read_eeprom(int ioaddr, int location);
 static int mdio_read(int ioaddr, int phy_id, int location);
@@ -450,16 +458,17 @@ static void set_rx_mode(struct device *dev);
 
 \f
 
-#ifdef MODULE
 /* The parameters that may be passed in... */
 /* 'options' is used to pass a transceiver override or full-duplex flag
    e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#ifdef MODULE
 static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
 static int debug = -1;                 /* The debug level */
+#endif
 
 /* A list of all installed Speedo devices, for removing the driver module. */
 static struct device *root_speedo_dev = NULL;
-#endif
 
 int eepro100_init(struct device *dev)
 {
@@ -512,11 +521,13 @@ int eepro100_init(struct device *dev)
                                printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
 
 #ifdef MODULE
-                       speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found]);
+                       speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found],
+                                                 cards_found);
 #else
                        speedo_found1(dev, pci_ioaddr, pci_irq_line,
-                                                 dev ? dev->mem_start : 0);
+                                                 dev ? dev->mem_start : 0, -1);
 #endif
+                       dev = NULL;
                        cards_found++;
                }
        }
@@ -524,7 +535,8 @@ int eepro100_init(struct device *dev)
        return cards_found;
 }
 
-static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
+static void speedo_found1(struct device *dev, int ioaddr, int irq, int options,
+                                                 int card_idx)
 {
        static int did_version = 0;                     /* Already printed version info. */
        struct speedo_private *sp;
@@ -600,12 +612,12 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
 #if defined(notdef)
                /* ToDo: Read and set PHY registers through MDIO port. */
                for (i = 0; i < 2; i++)
-                       printk("  MDIO register %d is %4.4x.\n",
+                       printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
                                   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
                for (i = 5; i < 7; i++)
-                       printk("  MDIO register %d is %4.4x.\n",
+                       printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
                                   i, mdio_read(ioaddr, eeprom[6] & 0x1f, i));
-               printk("  MDIO register %d is %4.4x.\n",
+               printk(KERN_INFO"  MDIO register %d is %4.4x.\n",
                           25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25));
 #endif
                if (((eeprom[6]>>8) & 0x3f) == DP83840
@@ -613,7 +625,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
                        int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
                        if (congenb)
                          mdi_reg23 |= 0x0100;
-                       printk("  DP83840 specific setup, setting register 23 to %4.4x.\n",
+                       printk(KERN_INFO"  DP83840 specific setup, setting register 23 to %4.4x.\n",
                                   mdi_reg23);
                        mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
                }
@@ -645,7 +657,7 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
                                   KERN_ERR " Verify that the card is a bus-master"
                                   " capable slot.\n",
                                   self_test_results[1]);
-               } else 
+               } else
                        printk(KERN_INFO "  General self-test: %s.\n"
                                   KERN_INFO "  Serial sub-system self-test: %s.\n"
                                   KERN_INFO "  Internal registers self-test: %s.\n"
@@ -673,7 +685,11 @@ static void speedo_found1(struct device *dev, int ioaddr, int irq, int options)
        root_speedo_dev = dev;
 #endif
 
-       sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
+       if (card_idx >= 0) {
+               if (full_duplex[card_idx] >= 0)
+                       sp->full_duplex = full_duplex[card_idx];
+       } else
+               sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0;
        sp->default_port = options >= 0 ? (options & 0x0f) : 0;
 
        sp->phy[0] = eeprom[6];
@@ -724,10 +740,10 @@ static int read_eeprom(int ioaddr, int location)
        unsigned short retval = 0;
        int ee_addr = ioaddr + SCBeeprom;
        int read_cmd = location | EE_READ_CMD;
-       
+
        outw(EE_ENB & ~EE_CS, ee_addr);
        outw(EE_ENB, ee_addr);
-       
+
        /* Shift the read command bits out. */
        for (i = 10; i >= 0; i--) {
                short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
@@ -739,7 +755,7 @@ static int read_eeprom(int ioaddr, int location)
                eeprom_delay(250);
        }
        outw(EE_ENB, ee_addr);
-       
+
        for (i = 15; i >= 0; i--) {
                outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
                eeprom_delay(100);
@@ -845,7 +861,7 @@ speedo_open(struct device *dev)
        outw(INT_MASK | RX_START, ioaddr + SCBCmd);
 
        /* Fill the first command with our physical address. */
-       { 
+       {
                unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
                unsigned short *setup_frm = (short *)&(sp->tx_ring[0].tx_desc_addr);
 
@@ -987,29 +1003,33 @@ static void speedo_tx_timeout(struct device *dev)
                   "command %4.4x.\n",
                   dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
 #ifndef final_version
-       printk("%s:  Tx timeout  fill index %d  scavenge index %d.\n",
+       printk(KERN_WARNING "%s:  Tx timeout  fill index %d  scavenge index %d.\n",
                   dev->name, sp->cur_tx, sp->dirty_tx);
-       printk("    Tx queue ");
+       printk(KERN_WARNING "    Tx queue ");
        for (i = 0; i < TX_RING_SIZE; i++)
          printk(" %8.8x", (int)sp->tx_ring[i].status);
-       printk(".\n    Rx ring ");
+       printk(".\n" KERN_WARNING "    Rx ring ");
        for (i = 0; i < RX_RING_SIZE; i++)
          printk(" %8.8x", (int)sp->rx_ringp[i]->status);
        printk(".\n");
-               
+
 #else
        dev->if_port ^= 1;
-       printk("  (Media type switching not yet implemented.)\n");
+       printk(KERN_WARNING "  (Media type switching not yet implemented.)\n");
        /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */
 #endif
        if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
-         printk("%s: Trying to restart the transmitter...\n", dev->name);
+         printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
+                        dev->name);
          outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
                   ioaddr + SCBPointer);
          outw(CU_START, ioaddr + SCBCmd);
        } else {
          outw(DRVR_INT, ioaddr + SCBCmd);
        }
+       /* Reset the MII transceiver. */
+       if ((sp->phy[0] & 0x8000) == 0)
+               mdio_write(ioaddr, sp->phy[0] & 0x1f, 0, 0x8000);
        sp->stats.tx_errors++;
        dev->trans_start = jiffies;
        return;
@@ -1032,7 +1052,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev)
        /* 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 this ever occurs the queue layer is doing something evil! */
-       if (set_bit(0, (void*)&dev->tbusy) != 0) {
+       if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
                int tickssofar = jiffies - dev->trans_start;
                if (tickssofar < TX_TIMEOUT - 2)
                        return 1;
@@ -1150,7 +1170,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
                  printk(KERN_WARNING"%s: The EEPro100 receiver left the ready"
                                 " state -- %4.4x!  Index %d (%d).\n", dev->name, status,
                                 sp->cur_rx, sp->cur_rx % RX_RING_SIZE);
-                 printk("   Rx ring:\n ");
+                 printk(KERN_WARNING "   Rx ring:\n ");
                  for (i = 0; i < RX_RING_SIZE; i++)
                        printk("   %d %8.8x %8.8x %8.8x %d %d.\n",
                                   i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link,
@@ -1193,7 +1213,8 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
 
 #ifndef final_version
                        if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
-                               printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+                               printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
+                                          " full=%d.\n",
                                           dirty_tx, sp->cur_tx, sp->tx_full);
                                dirty_tx += TX_RING_SIZE;
                        }
@@ -1211,7 +1232,7 @@ static void speedo_interrupt(int irq, struct pt_regs *regs)
                }
 
                if (--boguscnt < 0) {
-                       printk("%s: Too much work at interrupt, status=0x%4.4x.\n",
+                       printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
                                   dev->name, status);
                        /* Clear all interrupt sources. */
                        outl(0xfc00, ioaddr + SCBStatus);
@@ -1249,7 +1270,7 @@ speedo_rx(struct device *dev)
        struct speedo_private *sp = (struct speedo_private *)dev->priv;
        int entry = sp->cur_rx % RX_RING_SIZE;
        int status;
-               
+
        if (speedo_debug > 4)
                printk(KERN_DEBUG " In speedo_rx().\n");
        /* If we own the next entry, it's a new packet. Send it up. */
@@ -1259,12 +1280,12 @@ speedo_rx(struct device *dev)
                        printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status,
                                   sp->rx_ringp[entry]->count & 0x3fff);
                if (status & 0x0200) {
-                       printk("%s: Ethernet frame overran the Rx buffer, status %8.8x!\n",
-                                  dev->name, status);
+                       printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
+                                  "status %8.8x!\n", dev->name, status);
                } else if ( ! (status & 0x2000)) {
                        /* There was a fatal error.  This *should* be impossible. */
                        sp->stats.rx_errors++;
-                       printk("%s: Anomalous event in speedo_rx(), status %8.8x.\n",
+                       printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n",
                                   dev->name, status);
                } else {
                        /* Malloc up new buffer, compatible with net-2e. */
@@ -1283,7 +1304,7 @@ speedo_rx(struct device *dev)
 #ifdef KERNEL_1_2
                                temp = skb->data;
                                if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
-                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                       printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
                                                   " in speedo_rx: %p vs. %p / %p.\n", dev->name,
                                                   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
                                                   temp, skb->data);
@@ -1292,7 +1313,7 @@ speedo_rx(struct device *dev)
 #else
                                temp = skb_put(skb, pkt_len);
                                if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
-                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                       printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
                                                   " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
                                                   sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
                                /* Get a fresh skbuff to replace the filled one. */
@@ -1329,7 +1350,7 @@ speedo_rx(struct device *dev)
 #endif
                        if (skb == NULL) {
                                int i;
-                               printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+                               printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
                                /* Check that at least two ring entries are free.
                                   If not, free one and mark stats->rx_dropped++. */
                                /* ToDo: This is not correct!!!!  We should count the number
@@ -1376,7 +1397,7 @@ speedo_rx(struct device *dev)
                }
 
                /*      ToDo: This is better than before, but should be checked. */
-               { 
+               {
                        struct RxFD *rxf = sp->rx_ringp[entry];
                        rxf->status = 0xC0000003;               /* '3' for verification only */
                        rxf->link = 0;                  /* None yet. */
@@ -1444,19 +1465,20 @@ speedo_close(struct device *dev)
 
        /* Print a few items for debugging. */
        if (speedo_debug > 3) {
-               printk("%s:Printing Rx ring (next to receive into %d).\n",
+               int phy_num = sp->phy[0] & 0x1f;
+               printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
                           dev->name, sp->cur_rx);
 
                for (i = 0; i < RX_RING_SIZE; i++)
-                       printk("  Rx ring entry %d  %8.8x.\n",
+                       printk(KERN_DEBUG "  Rx ring entry %d  %8.8x.\n",
                                   i, (int)sp->rx_ringp[i]->status);
 
                for (i = 0; i < 5; i++)
-                       printk("  PHY index %d register %d is %4.4x.\n",
-                                  1, i, mdio_read(ioaddr, 1, i));
+                       printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
+                                  phy_num, i, mdio_read(ioaddr, phy_num, i));
                for (i = 21; i < 26; i++)
-                       printk("  PHY index %d register %d is %4.4x.\n",
-                                  1, i, mdio_read(ioaddr, 1, i));
+                       printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n",
+                                  phy_num, i, mdio_read(ioaddr, phy_num, i));
        }
        MOD_DEC_USE_COUNT;
 
@@ -1527,7 +1549,7 @@ set_rx_mode(struct device *dev)
 
        if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
          /* The Tx ring is full -- don't add anything!  Presumably the new mode
-            is in config_cmd_data and will be added anyway. */
+                is in config_cmd_data and will be added anyway. */
                sp->rx_mode = -1;
                return;
        }
@@ -1563,7 +1585,7 @@ set_rx_mode(struct device *dev)
                restore_flags(flags);
                if (speedo_debug > 5) {
                        int i;
-                       printk(" CmdConfig frame in entry %d.\n", entry);
+                       printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry);
                        for(i = 0; i < 32; i++)
                                printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
                        printk(".\n");
@@ -1616,20 +1638,19 @@ set_rx_mode(struct device *dev)
                        if (sp->mc_setup_frm)
                                kfree(sp->mc_setup_frm);
                        sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
-                       printk("%s: Allocating a setup frame of size %d.\n",
-                                  dev->name, sp->mc_setup_frm_len);
-                       sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len,
-                                                                          intr_count ? GFP_ATOMIC : GFP_KERNEL);
+                       sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
                        if (sp->mc_setup_frm == NULL) {
-                         printk("%s: Failed to allocate a setup frame.\n", dev->name);
+                         printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name);
                                sp->rx_mode = -1; /* We failed, try again. */
                                return;
                        }
                }
                mc_setup_frm = sp->mc_setup_frm;
                /* Construct the new setup frame. */
-               printk("%s: Constructing a setup frame at %p, %d bytes.\n",
-                          dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+               if (speedo_debug > 1)
+                       printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
+                                  "%d bytes.\n",
+                                  dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
                mc_setup_frm->status = 0;
                mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
                /* Link set below. */
@@ -1643,7 +1664,7 @@ set_rx_mode(struct device *dev)
                        *setup_params++ = *eaddrs++;
                        *setup_params++ = *eaddrs++;
                }
-               
+
                /* Disable interrupts while playing with the Tx Cmd list. */
                save_flags(flags);
                cli();
@@ -1667,8 +1688,9 @@ set_rx_mode(struct device *dev)
                outw(CU_RESUME, ioaddr + SCBCmd);
                sp->last_cmd = mc_setup_frm;
                restore_flags(flags);
-               printk("%s: Last command at %p is %4.4x.\n",
-                          dev->name, sp->last_cmd, sp->last_cmd->command);
+               if (speedo_debug > 1)
+                       printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n",
+                                  dev->name, sp->last_cmd, sp->last_cmd->command);
        }
 
        sp->rx_mode = new_rx_mode;
@@ -1691,7 +1713,7 @@ init_module(void)
 
        root_speedo_dev = NULL;
        cards_found = eepro100_init(NULL);
-       return cards_found < 0 ? cards_found : 0;
+       return cards_found ? 0 : -ENODEV;
 }
 
 void
@@ -1724,8 +1746,9 @@ int eepro100_probe(struct device *dev)
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -DCONFIG_MODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
index 0806c796497ccf397703d52f669a0ca706775ce4..3f74b8d0cb58c86b04ce58679981d39d08024773 100644 (file)
@@ -1,5 +1,6 @@
 /* tulip.c: A DEC 21040-family ethernet driver for linux. */
 /*
+   NOTICE: THIS IS THE ALPHA TEST VERSION!
        Written 1994-1997 by Donald Becker.
 
        This software may be used and distributed according to the terms
@@ -16,7 +17,7 @@
        http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static const char *version = "tulip.c:v0.78 7/25/97 becker@cesdis.gsfc.nasa.gov\n";
+static const char *version = "tulip.c:v0.79 9/3/97 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
@@ -35,7 +36,7 @@ static int reverse_probe = 0;
 #define RX_RING_SIZE   16
 
 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#define SKBUFF_RX_COPYBREAK 200
+static const rx_copybreak = 200;
 
 /* The following example shows how to always use the 10base2 port. */
 #ifdef notdef
@@ -176,12 +177,12 @@ This driver uses statically allocated rings of Rx and Tx descriptors, set at
 compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
 for the Rx ring buffers at open() time and passes the skb->data field to the
 Tulip as receive data buffers.  When an incoming frame is less than
-SKBUFF_RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
 copied to the new skbuff.  When the incoming frame is larger, the skbuff is
 passed directly up the protocol stack and replaced by a newly allocated
 skbuff.
 
-The SKBUFF_RX_COPYBREAK value is chosen to trade-off the memory wasted by
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
 using a full-sized skbuff for small frames vs. the copying costs of larger
 frames.  For small frames the copying cost is negligible (esp. considering
 that we are pre-loading the cache with immediately useful header
@@ -279,7 +280,7 @@ static const char * const medianame[] = {
   "10baseT", "10base2", "AUI", "100baseTx",
   "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
   "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
-  "", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+  "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
 };
 /* A full-duplex map for above. */
 static const char media_fd[] =
@@ -300,6 +301,14 @@ enum tulip_offsets {
        CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
        CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
 
+/* The bits in the CSR5 status registers, mostly interrupt sources. */
+enum status_bits {
+       TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10,
+       RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40,
+       TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02,
+       TxIntr=0x01,
+};
+
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
        s32 status;
@@ -348,7 +357,11 @@ struct tulip_private {
        int setup_frame[48];            /* Pseudo-Tx frame to init address table. */
        int chip_id;
        int revision;
+#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, cur_tx;            /* The next free ring entry */
        unsigned int dirty_rx, dirty_tx;        /* The ring entries to be free()ed. */
@@ -360,15 +373,16 @@ struct tulip_private {
        unsigned int mediasense:1;                      /* Media sensing in progress. */
        unsigned int csr6;                                      /* Current CSR6 control settings. */
        unsigned char eeprom[128];                      /* Serial EEPROM contents. */
+       signed char phys[4];                            /* MII device addresses. */
        struct mediatable *mtable;
        int cur_index;                                          /* Current media index. */
+       unsigned char pci_bus, pci_device_fn;
        int pad0, pad1;                                         /* Used for 8-byte alignment */
 };
 
-#ifdef MODULE
 /* Used to pass the full-duplex flag, etc. */
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#endif
+static int full_duplex[8] = {0, };
+static int options[8] = {0, };
 
 static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
                                                                   int chip_id, int options);
@@ -448,12 +462,12 @@ int tulip_probe(struct device *dev)
                                if (device == tulip_tbl[chip_idx].device_id)
                                        break;
                        if (tulip_tbl[chip_idx].chip_name == 0) {
-                               printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
-                                          " not configured.\n", device);
+                               printk(KERN_INFO "Unknown Digital PCI ethernet chip type"
+                                          " %4.4x"" detected: not configured.\n", device);
                                continue;
                        }
                        if (tulip_debug > 2)
-                               printk("Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
+                               printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
                                           pci_ioaddr, pci_irq_line);
 
                        if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
@@ -461,10 +475,9 @@ int tulip_probe(struct device *dev)
 
 #ifdef MODULE
                        dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
-                                                options[cards_found]);
+                                                cards_found);
 #else
-                       dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
-                                                dev ? dev->mem_start : 0);
+                       dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1);
 #endif
 
                        if (dev) {
@@ -472,7 +485,7 @@ int tulip_probe(struct device *dev)
                          pcibios_read_config_word(pci_bus, pci_device_fn,
                                                                           PCI_COMMAND, &pci_command);
                          if ( ! (pci_command & PCI_COMMAND_MASTER)) {
-                               printk("  PCI Master Bit has not been set! Setting...\n");
+                               printk(KERN_INFO "  PCI Master Bit has not been set! Setting...\n");
                                pci_command |= PCI_COMMAND_MASTER;
                                pcibios_write_config_word(pci_bus, pci_device_fn,
                                                                                  PCI_COMMAND, pci_command);
@@ -480,12 +493,13 @@ int tulip_probe(struct device *dev)
                          pcibios_read_config_byte(pci_bus, pci_device_fn,
                                                                           PCI_LATENCY_TIMER, &pci_latency);
                          if (pci_latency < 10) {
-                               printk("  PCI latency timer (CFLT) is unreasonably low at %d."
-                                          "  Setting to 64 clocks.\n", pci_latency);
+                               printk(KERN_INFO "  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);
                          } else if (tulip_debug > 1)
-                               printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+                               printk(KERN_INFO "  PCI latency timer (CFLT) is %#x.\n",
+                                          pci_latency);
                          /* Bring the 21143 out power-down mode. */
                          if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
                                pcibios_write_config_dword(pci_bus, pci_device_fn,
@@ -496,15 +510,11 @@ int tulip_probe(struct device *dev)
                }
        }
 
-#if defined (MODULE)
-       return cards_found;
-#else
-       return cards_found > 0 ? 0 : -ENODEV;
-#endif
+       return cards_found ? 0 : -ENODEV;
 }
 
 static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
-                                                                  int chip_id, int options)
+                                                                  int chip_id, int board_idx)
 {
        static int did_version = 0;                     /* Already printed version info. */
        struct tulip_private *tp;
@@ -515,11 +525,11 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
        unsigned short sum;
 
        if (tulip_debug > 0  &&  did_version++ == 0)
-               printk(version);
+               printk(KERN_INFO "%s", version);
 
        dev = init_etherdev(dev, 0);
 
-       printk("%s: DEC %s at %#3x,",
+       printk(KERN_INFO "%s: DEC %s at %#3x,",
                   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
        /* Stop the chip's Tx and Rx processes. */
@@ -585,7 +595,7 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
        }
        for (i = 0; i < 6; i++)
                printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
-       printk(", IRQ %d\n", irq);
+       printk(", IRQ %d.\n", irq);
        last_irq = irq;
 
        /* We do a request_region() only to register /proc/ioports info. */
@@ -617,11 +627,36 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
 #endif
 
        /* The lower four bits are the media type. */
-       if (options > 0) {
-               tp->full_duplex = (options & 16) ? 1 : 0;
-               tp->default_port = options & 15;
+       if (board_idx >= 0) {
+               tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0;
+               tp->default_port = options[board_idx] & 15;
                if (tp->default_port)
-                 tp->medialock = 1;
+                       tp->medialock = 1;
+       }
+
+       /* This is logically part of probe1(), but too complex to write inline. */
+       if (chip_id != DC21040)
+               parse_eeprom(dev);
+
+       if (tp->mtable  &&  tp->mtable->has_mii) {
+               int phy, phy_idx;
+               /* Find the connected MII xcvrs.
+                  Doing this in open() would allow detecting external xcvrs later,
+                  but takes much time. */
+               for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys);
+                        phy++) {
+                       int mii_status = mdio_read(ioaddr, phy, 0);
+                       if (mii_status != 0xffff  && mii_status != 0x0000) {
+                               tp->phys[phy_idx++] = phy;
+                               printk(KERN_INFO "%s:  MII transceiver found at MDIO address %d.\n",
+                                          dev->name, phy);
+                       }
+               }
+               if (phy_idx == 0) {
+                       printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n",
+                                  dev->name);
+                       tp->phys[0] = 1;
+               }
        }
 
        /* The Tulip-specific entries in the device structure. */
@@ -633,10 +668,6 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
        dev->set_multicast_list = &set_multicast_list;
 #endif
 
-       /* This is logically part of probe1(), but too complex to write inline. */
-       if (chip_id != DC21040)
-               parse_eeprom(dev);
-
        /* Reset the xcvr interface and turn on heartbeat. */
        switch (chip_id) {
        case DC21041:
@@ -708,9 +739,8 @@ static void parse_eeprom(struct device *dev)
        {
          static int done_did_that = 0;
          if (done_did_that++ == 0)
-               printk("\n  THIS IS AN ALPHA TEST DRIVER.\n"
-                          "  The following verbose information is emitted for\n"
-                          "  bug reports on media selection.\n");
+               printk(KERN_INFO"  The following verbose information is emitted for\n"
+                          KERN_INFO"  bug reports on media selection.\n");
        }
        tp->mtable = 0;
        for (i = 0; i < EEPROM_SIZE/2; i++)
@@ -725,13 +755,13 @@ static void parse_eeprom(struct device *dev)
          if (ee_data[0] == 0xff) {
                if (last_mediatable) {
                  controller_index++;
-                 printk("%s: Controller %d of multiport board.\n",
+                 printk(KERN_INFO "%s:  Controller %d of multiport board.\n",
                                 dev->name, controller_index);
                  tp->mtable = last_mediatable;
                  ee_data = last_ee_data;
                  goto subsequent_board;
                } else
-               printk("%s:  Missing EEPROM, this device may not work correctly!\n",
+               printk(KERN_INFO "%s:  Missing EEPROM, this device may not work correctly!\n",
                           dev->name);
                return;
          }
@@ -744,22 +774,22 @@ static void parse_eeprom(struct device *dev)
                          i++;                  /* An Accton EN1207, not an outlaw Maxtech. */
                  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
                                 sizeof(eeprom_fixups[i].newtable));
-                 printk("\n%s: Old format EEPROM on '%s' board.  Using substitute"
-                                " media control info.\n",
+                 printk(KERN_INFO "%s: Old format EEPROM on '%s' board.  Using"
+                                " substitute media control info.\n",
                                 dev->name, eeprom_fixups[i].name);
                  break;
                }
          }
          if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
-               printk("\n %s: Old style EEPROM -- no media selection information.\n",
+               printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n",
                           dev->name);
                return;
          }
        }
        if (tulip_debug > 1) {
-         printk("\nread_eeprom:");
+         printk(KERN_DEBUG "\nread_eeprom:");
          for (i = 0; i < 64; i++) {
-               printk("%c%4.4x", (i & 7) == 0 ? '\n':' ',
+               printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
                           read_eeprom(ioaddr, i));
          }
          printk("\n");
@@ -776,13 +806,13 @@ subsequent_board:
                short media = *(u16 *)p;
                int count = p[2];
 
-               printk("%s:21041 Media information at %d, default media %4.4x"
-                          " (%s).\n", dev->name, ee_data[27], media,
+               printk(KERN_INFO "%s:21041 Media information at %d, default media "
+                          "%4.4x (%s).\n", dev->name, ee_data[27], media,
                           media & 0x0800 ? "Autosense" : medianame[media & 15]);
                for (i = 0; i < count; i++) {
                        unsigned char media_code = p[3 + i*7];
                        unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1];
-                       printk("%s:  21041 media %2.2x (%s),"
+                       printk(KERN_INFO "%s:  21041 media %2.2x (%s),"
                                   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
                                   dev->name, media_code & 15, medianame[media_code & 15],
                                   csrvals[0], csrvals[1], csrvals[2]);
@@ -808,7 +838,7 @@ subsequent_board:
                mtable->csr12dir = csr12dir;
                mtable->has_mii = 0;
 
-               printk("%s: EEPROM default media type %s.\n", dev->name,
+               printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name,
                           media & 0x0800 ? "Autosense" : medianame[media & 15]);
                for (i = 0; i < count; i++) {
                        struct medialeaf *leaf = &mtable->mleaf[i];
@@ -830,20 +860,22 @@ subsequent_board:
                        }
                        if (tulip_debug > 1  &&  leaf->media == 11) {
                                unsigned char *bp = leaf->leafdata;
-                               printk("%s:  MII interface PHY %d, setup/reset sequences"
-                                          " %d/%d long, capabilities %2.2x %2.2x.\n",
+                               printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset "
+                                          "sequences %d/%d long, capabilities %2.2x %2.2x.\n",
                                           dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
                                           bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
-                               if (tulip_debug > 2) { /* DEBUG only, should be > 3 */
+                               if (tulip_debug > 2) {
                                        int mii_reg;
-                                       printk("%s:  MII xcvr control registers:", dev->name);
+                                       printk(KERN_DEBUG "%s:  MII xcvr control registers:",
+                                                  dev->name);
                                        for (mii_reg = 0; mii_reg < 32; mii_reg++)
                                                printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg));
                                        printk(".\n");
                                }
                        }
 
-                       printk("%s: Index #%d - Media %s (#%d) described by a %s (%d) block.\n",
+                       printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described "
+                                  "by a %s (%d) block.\n",
                                   dev->name, i, medianame[leaf->media], leaf->media,
                                   block_name[leaf->type], leaf->type);
                }
@@ -913,35 +945,59 @@ static int read_eeprom(int ioaddr, int location)
    MDIO protocol.  It is just different enough from the EEPROM protocol
    to not share code.  The maxium data clock rate is 2.5 Mhz. */
 #define MDIO_SHIFT_CLK 0x10000
-#define MDIO_DATA_WRITE 0x20000
-#define MDIO_ENB               0x40000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB               0x00000         /* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN            0x40000
 #define MDIO_DATA_READ 0x80000
+#ifdef _LINUX_DELAY_H
+#define mdio_delay()   udelay(1)
+#else
+#define mdio_delay()   __SLOW_DOWN_IO
+#endif
+
 static int mdio_read(int ioaddr, int phy_id, int location)
 {
        int i;
        int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
        unsigned short retval = 0;
-       int ee_addr = ioaddr + CSR9;
-
+       int mdio_addr = ioaddr + CSR9;
+
+       /* Establish sync by sending at least 32 logic ones. */ 
+       for (i = 32; i >= 0; i--) {
+               outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
+               mdio_delay();
+               outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay();
+       }
        /* Shift the read command bits out. */
-       for (i = 18; i >= 0; i--) {
-               int dataval =
-                 (read_cmd & (1 << i)) ? MDIO_DATA_WRITE : 0;
-
-               outl(MDIO_ENB | dataval, ee_addr);
-               eeprom_delay(100);
-               outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, ee_addr);
-               eeprom_delay(250);
-               outl(MDIO_ENB | dataval, ee_addr);
-               eeprom_delay(150);
+       for (i = 17; i >= 0; i--) {
+               int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+               outl(dataval, mdio_addr);
+               mdio_delay();
+               outl(dataval | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay();
+               outl(dataval, mdio_addr);
+               mdio_delay();
        }
+       outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+       mdio_delay();
+       outl(MDIO_ENB_IN, mdio_addr);
 
        for (i = 16; i > 0; i--) {
-               outl(MDIO_SHIFT_CLK, ee_addr);
-               eeprom_delay(250);
-               retval = (retval << 1) | ((inl(ee_addr) & MDIO_DATA_READ) ? 1 : 0);
-               outl(0, ee_addr);
-               eeprom_delay(250);
+               outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay();
+               retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+               outl(MDIO_ENB_IN, mdio_addr);
+               mdio_delay();
+       }
+       /* Clear out extra bits. */
+       for (i = 16; i > 0; i--) {
+               outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+               mdio_delay();
+               outl(MDIO_ENB_IN, mdio_addr);
+               mdio_delay();
        }
        return retval;
 }
@@ -975,7 +1031,7 @@ tulip_open(struct device *dev)
                C000    32  longwords           0400 4 longwords
           Wait the specified 50 PCI cycles after a reset by initializing
           Tx and Rx queues and the address filter list. */
-#if defined(__alpha)
+#if defined(__alpha__)
        /* ToDo: Alpha setting could be better. */
        outl(0x00200000 | 0xE000, ioaddr + CSR0);
 #else
@@ -985,7 +1041,8 @@ tulip_open(struct device *dev)
 #else
        outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
        if (x86 <= 4)
-         printk("This is a 386/486 PCI system, setting cache alignment to %x.\n",
+         printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
+                        "alignment to %x.\n", dev->name,
                         0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
 #endif
 #endif
@@ -1006,7 +1063,7 @@ tulip_open(struct device *dev)
 #endif
 
        if (tulip_debug > 1)
-               printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+               printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
 
        MOD_INC_USE_COUNT;
 
@@ -1052,14 +1109,14 @@ tulip_open(struct device *dev)
                for (i = 0; i < tp->mtable->leafcount; i++)
                  if (tp->mtable->mleaf[i].media ==
                          (dev->if_port == 12 ? 0 : dev->if_port)) {
-                       printk("%s: Using user-specified media %s.\n",
+                       printk(KERN_INFO "%s: Using user-specified media %s.\n",
                                   dev->name, medianame[dev->if_port]);
                        goto media_picked;
                  }
        if ((tp->mtable->defaultmedia & 0x0800) == 0)
                for (i = 0; i < tp->mtable->leafcount; i++)
                  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
-                       printk("%s: Using EEPROM-set media %s.\n",
+                       printk(KERN_INFO "%s: Using EEPROM-set media %s.\n",
                                   dev->name, medianame[tp->mtable->mleaf[i].media]);
                        goto media_picked;
                  }
@@ -1087,7 +1144,7 @@ media_picked:
        outl(0, ioaddr + CSR2);         /* Rx poll demand */
 
        if (tulip_debug > 2) {
-               printk("%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+               printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
                           dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
                           inl(ioaddr + CSR13));
        }
@@ -1109,7 +1166,7 @@ static void select_media(struct device *dev, int startup)
        struct tulip_private *tp = (struct tulip_private *)dev->priv;
        struct mediatable *mtable = tp->mtable;
        u32 new_csr6;
-       int i;
+       int check_mii =0, i;
 
        if (mtable) {
                struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
@@ -1117,7 +1174,7 @@ static void select_media(struct device *dev, int startup)
                switch (mleaf->type) {
                case 0:                                 /* 21140 non-MII xcvr. */
                        if (tulip_debug > 1)
-                               printk("%s: Using a 21140 non-MII transceiver with control"
+                               printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control"
                                           " setting %2.2x.\n",
                                           dev->name, p[1]);
                        dev->if_port = p[0];
@@ -1131,23 +1188,24 @@ static void select_media(struct device *dev, int startup)
                                outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
                                dev->if_port = 11;
                                if (tulip_debug > 2)
-                                       printk("%s:  Doing a reset sequence of length %d.\n",
+                                       printk(KERN_DEBUG "%s:  Doing a reset sequence of length %d.\n",
                                                   dev->name, p[2 + p[1]]);
                                for (i = 0; i < p[2 + p[1]]; i++)
                                        outl(p[3 + p[1] + i], ioaddr + CSR12);
                                if (tulip_debug > 2)
-                                       printk("%s  Doing a transceiver setup sequence of length %d.\n",
+                                       printk(KERN_DEBUG "%s  Doing a transceiver setup sequence of length %d.\n",
                                                   dev->name, p[1]);
                                for (i = 0; i < p[1]; i++)
                                        outl(p[2 + i], ioaddr + CSR12);
                        }
+                       check_mii = 1;
                        new_csr6 = 0x020C0000;
                        break;
                case 2: case 4: {
                        u16 *setup = (u16*)&p[1];
                        dev->if_port = p[0] & 15;
                        if (tulip_debug > 1)
-                               printk("%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+                               printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
                                           dev->name, medianame[dev->if_port], setup[0], setup[1]);
                        if (p[0] & 0x40) {      /* SIA (CSR13-15) setup values are provided. */
                                outl(0, ioaddr + CSR13);
@@ -1175,24 +1233,25 @@ static void select_media(struct device *dev, int startup)
                        dev->if_port = 11;
                        if (startup) {
                                if (tulip_debug > 2)
-                                       printk("%s:  Doing a 21142 reset sequence of length %d.\n",
+                                       printk(KERN_DEBUG "%s:  Doing a 21142 reset sequence of length %d.\n",
                                                   dev->name, reset_length);
                                for (i = 0; i < reset_length; i++)
                                        outl(reset_sequence[i] << 16, ioaddr + CSR15);
                        }
                        if (tulip_debug > 2)
-                               printk("%s: Doing a 21142 xcvr setup sequence of length %d.\n",
+                               printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n",
                                           dev->name, init_length);
                        for (i = 0; i < init_length; i++)
                                outl(init_sequence[i] << 16, ioaddr + CSR15);
-                       new_csr6 = 0x020C0000 | (tp->full_duplex ? 0x0200 : 0);
+                       check_mii = 1;
+                       new_csr6 = 0x020C0000;
                        break;
                }
                default:
                  new_csr6 = 0x020C0000;
                }
                if (tulip_debug > 1)
-                       printk("%s: Using media type %s, CSR12 is %2.2x.\n",
+                       printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n",
                                   dev->name, medianame[dev->if_port],
                                   inl(ioaddr + CSR12) & 0xff);
        } else if (tp->chip_id == DC21140) {
@@ -1200,12 +1259,12 @@ static void select_media(struct device *dev, int startup)
                new_csr6 = 0x020C0000;
                dev->if_port = 11;
                if (tulip_debug > 1) {
-                       printk("%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
+                       printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
                                   dev->name, inl(ioaddr + CSR12) & 0xff);
                }
        } else if (tp->chip_id == DC21041) {
                if (tulip_debug > 1)
-                       printk("%s: 21041 using media %s, CSR12 is %4.4x.\n",
+                       printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n",
                                   dev->name, medianame[dev->if_port & 15],
                                   inl(ioaddr + CSR12) & 0xffff);
                outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
@@ -1217,7 +1276,7 @@ static void select_media(struct device *dev, int startup)
                /* Turn on the xcvr interface. */
                int csr12 = inl(ioaddr + CSR12);
                if (tulip_debug > 1)
-                       printk("%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+                       printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n",
                                   dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
                new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
                /* Set the full duplux match frame. */
@@ -1239,7 +1298,7 @@ static void tulip_timer(unsigned long data)
        int next_tick = 0;
 
        if (tulip_debug > 3) {
-               printk("%s: Media selection tick, status %8.8x mode %8.8x "
+               printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x "
                           "SIA %8.8x %8.8x %8.8x %8.8x.\n",
                           dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
                           csr12, inl(ioaddr + CSR13),
@@ -1248,7 +1307,7 @@ static void tulip_timer(unsigned long data)
        switch (tp->chip_id) {
        case DC21040:
                if (csr12 & 0x0002) { /* Network error */
-                       printk("%s: No 10baseT link beat found, switching to %s media.\n",
+                       printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n",
                                   dev->name, dev->if_port ? "10baseT" : "AUI");
                        dev->if_port ^= 1;
                        outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
@@ -1257,7 +1316,7 @@ static void tulip_timer(unsigned long data)
                break;
        case DC21041:
                if (tulip_debug > 2)
-                       printk("%s: 21041 media tick  CSR12 %8.8x.\n",
+                       printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n",
                                   dev->name, csr12);
                switch (dev->if_port) {
                case 0: case 3: case 4:
@@ -1268,7 +1327,7 @@ static void tulip_timer(unsigned long data)
                                dev->if_port = 2;
                        else
                                dev->if_port = 1;
-                       printk("%s: No 21041 10baseT link beat, Media switched to %s.\n",
+                       printk(KERN_INFO "%s: No 21041 10baseT link beat, Media switched to %s.\n",
                                   dev->name, medianame[dev->if_port]);
                        outl(0, ioaddr + CSR13); /* Reset */
                        outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
@@ -1284,7 +1343,7 @@ static void tulip_timer(unsigned long data)
                        next_tick = (30*HZ);                    /* 30 sec. */
                        tp->mediasense = 0;
                  } else if ((csr12 & 0x0004) == 0) {
-                       printk("%s: 21041 media switched to 10baseT.\n", dev->name);
+                       printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name);
                        dev->if_port = 0;
                        select_media(dev, 0);
                        next_tick = (24*HZ)/10;                         /* 2.4 sec. */
@@ -1309,7 +1368,7 @@ static void tulip_timer(unsigned long data)
                                /* Stop the transmit process. */
                                tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
                                outl(tp->csr6 | 0x0002, ioaddr + CSR6);
-                               printk("%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
+                               printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
                                           " %s media.\n", dev->name,
                                           csr12 & 0xff,
                                           dev->if_port ? "100baseTx" : "10baseT");
@@ -1319,7 +1378,7 @@ static void tulip_timer(unsigned long data)
                        } else {
                                next_tick = 10*HZ;
                                if (tulip_debug > 2)
-                                       printk("%s: network media monitor 0x%2.2x, link"
+                                       printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link"
                                                   " beat detected as %s.\n", dev->name,
                                                   csr12 & 0xff,
                                                   dev->if_port ? "100baseTx" : "10baseT");
@@ -1333,7 +1392,7 @@ static void tulip_timer(unsigned long data)
                /* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
                  s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
                  if (tulip_debug > 2)
-                         printk("%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
+                         printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
                                         " %d, expecting %d.\n",
                                         dev->name, csr12, (bitnum >> 1) & 7,
                                         (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
@@ -1342,7 +1401,7 @@ static void tulip_timer(unsigned long data)
                  if ((bitnum < 0) !=
                          ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
                          if (tulip_debug > 1)
-                                 printk("%s: Link beat detected for %s.\n", dev->name,
+                                 printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name,
                                                 medianame[mleaf->media]);
                          break;
                  }
@@ -1357,7 +1416,7 @@ static void tulip_timer(unsigned long data)
                  if (media_fd[dev->if_port])
                        goto select_next_media; /* Skip FD entries. */
                  if (tulip_debug > 1)
-                         printk("%s: No link beat on media %s,"
+                         printk(KERN_DEBUG "%s: No link beat on media %s,"
                                         " trying transceiver type %s.\n",
                                         dev->name, medianame[mleaf->media & 15],
                                         medianame[tp->mtable->mleaf[tp->cur_index].media]);
@@ -1369,16 +1428,32 @@ static void tulip_timer(unsigned long data)
                  break;
          }
          case 1:
-                 printk(" %s: MII monitoring tick: CSR12 status %2.2x.\n",
-                                dev->name, csr12);
+                 {
+                         int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
+                         printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, "
+                                        "Link partner report %4.4x.\n",
+                                        dev->name, csr12, mii_reg5);
+                         if (mii_reg5 != 0xffff
+                                 && mdio_read(ioaddr, tp->phys[0], 1) & 0x0020) {
+                                 int full_duplex = mii_reg5 & 0x0100 ? 1 : 0;
+                                 if (full_duplex != tp->full_duplex) {
+                                         tp->full_duplex = full_duplex;
+                                         tp->csr6 ^= 0x0200;
+                                         outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+                                         outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+                                 }
+                                 if (tulip_debug > 0) /* Gurppp, should be >1 */
+                                         printk(KERN_INFO "%s: Setting %s-duplex based on MII"
+                                                        " Xcvr #%d parter capability of %4.4x.\n",
+                                                        dev->name, full_duplex ? "full" : "half",
+                                                        tp->phys[0], mii_reg5);
+                         }
+                 }
+
                  /* Hack for D-Link: Full duplex indication is on bit 3. */
                  if (dev->dev_addr[0] == 0  &&  dev->dev_addr[1] == 0x80
                          && dev->dev_addr[2] == 0xC8) {
-                         /* The first message is for information only. */
-                         if (tp->full_duplex) {
-                                 printk("%s: D-Link card in full-duplex mode, csr6 setting"
-                                                " %8.8x.\n", dev->name, tp->csr6);
-                         } else if (csr12 & 0x08) {
+                         if (csr12 & 0x08) {
                                  tp->full_duplex = 0;
                                  tp->csr6 &= ~0x0200;
                                  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
@@ -1386,7 +1461,6 @@ static void tulip_timer(unsigned long data)
                          } else {
                                  tp->full_duplex = 1;
                                  tp->csr6 |= 0x0200;
-                                 printk("%s: Switching D-Link card to full-duplex.\n", dev->name);
                                  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
                                  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
                          }
@@ -1413,15 +1487,15 @@ static void tulip_tx_timeout(struct device *dev)
 {
   struct tulip_private *tp = (struct tulip_private *)dev->priv;
   int ioaddr = dev->base_addr;
-  int i;
 
   if (tp->mtable && tp->mtable->has_mii) {
        /* Do nothing -- the media monitor should handle this. */
        if (tulip_debug > 1)
-         printk("%s: Transmit timeout using MII device.\n", dev->name);
+         printk(KERN_WARNING "%s: Transmit timeout using MII device.\n",
+                        dev->name);
   } else if (tp->chip_id == DC21040) {
          if (inl(ioaddr + CSR12) & 0x0002) {
-                 printk("%s: transmit timed out, switching to %s media.\n",
+                 printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n",
                                 dev->name, dev->if_port ? "10baseT" : "AUI");
                  dev->if_port ^= 1;
                  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
@@ -1432,10 +1506,11 @@ static void tulip_tx_timeout(struct device *dev)
        /* Stop the transmit process. */
        outl(tp->csr6 | 0x0002, ioaddr + CSR6);
        dev->if_port ^= 1;
-       printk("%s: 21140 transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+       printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, "
+                  "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
                   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
                   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
-       printk("%s: transmit timed out, switching to %s media.\n",
+       printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n",
                   dev->name, dev->if_port ? "100baseTx" : "10baseT");
        outl(tp->csr6 | 0x2002, ioaddr + CSR6);
        tp->stats.tx_errors++;
@@ -1444,7 +1519,7 @@ static void tulip_tx_timeout(struct device *dev)
   } else if (tp->chip_id == DC21041) {
        u32 csr12 = inl(ioaddr + CSR12);
 
-       printk("%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
+       printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
                   " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
                   dev->name, inl(ioaddr + CSR5), csr12,
                   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
@@ -1461,10 +1536,10 @@ static void tulip_tx_timeout(struct device *dev)
        dev->trans_start = jiffies;
        return;
   } else
-       printk("%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
+       printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
                   " resetting...\n",
                   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
-#ifndef __alpha__
+#ifdef way_too_many_messages
   printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
   for (i = 0; i < RX_RING_SIZE; i++)
        printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
@@ -1543,7 +1618,7 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 
 #ifndef final_version
        if (skb == NULL || skb->len <= 0) {
-               printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+               printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n",
                           dev->name);
                dev_tint(dev);
                return 0;
@@ -1607,7 +1682,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
 #endif
 
        struct tulip_private *lp;
-       int csr5, ioaddr, boguscnt = 10;
+       int csr5, ioaddr, boguscnt = 12;
 
        if (dev == NULL) {
                printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
@@ -1617,7 +1692,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
        ioaddr = dev->base_addr;
        lp = (struct tulip_private *)dev->priv;
        if (dev->interrupt)
-               printk("%s: Re-entering the interrupt handler.\n", dev->name);
+               printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
 
        dev->interrupt = 1;
 
@@ -1627,7 +1702,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                outl(csr5 & 0x0001ffff, ioaddr + CSR5);
 
                if (tulip_debug > 4)
-                       printk("%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+                       printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
                                   dev->name, csr5, inl(dev->base_addr + CSR5));
 
                if ((csr5 & 0x00018000) == 0)
@@ -1653,7 +1728,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                                        /* There was an major error, log it. */
 #ifndef final_version
                                        if (tulip_debug > 1)
-                                               printk("%s: Transmit error, Tx status %8.8x.\n",
+                                               printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
                                                           dev->name, status);
 #endif
                                        lp->stats.tx_errors++;
@@ -1681,7 +1756,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
 
 #ifndef final_version
                        if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
-                               printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+                               printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
                                           dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
                                dirty_tx += TX_RING_SIZE;
                        }
@@ -1712,7 +1787,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                                lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
                        }
                        if (csr5 & 0x0800) {
-                               printk("%s: Something Wicked happened! %8.8x.\n",
+                               printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n",
                                           dev->name, csr5);
                                /* Hmmmmm, it's not clear what to do here. */
                        }
@@ -1720,7 +1795,7 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                        outl(0x000f7ba, ioaddr + CSR5);
                }
                if (--boguscnt < 0) {
-                       printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+                       printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n",
                                   dev->name, csr5);
                        /* Clear all interrupt sources. */
                        outl(0x0001ffff, ioaddr + CSR5);
@@ -1729,15 +1804,17 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
        } while (1);
 
        if (tulip_debug > 3)
-               printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+               printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
                           dev->name, inl(ioaddr + CSR5));
 
        /* Code that should never be run!  Perhaps remove after testing.. */
        {
                static int stopit = 10;
                if (dev->start == 0  &&  --stopit < 0) {
-                       printk("%s: Emergency stop, looping startup interrupt.\n",
-                                  dev->name);
+                       printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n"
+                                  KERN_ERR "%s: Disabling interrupt handler %d to avoid "
+                                  "locking up the machine.\n",
+                                  dev->name, dev->name, dev->irq);
 #ifdef SA_SHIRQ
                        free_irq(irq, dev);
 #else
@@ -1757,18 +1834,16 @@ tulip_rx(struct device *dev)
        int entry = lp->cur_rx % RX_RING_SIZE;
 
        if (tulip_debug > 4)
-               printk(" In tulip_rx(), entry %d %8.8x.\n", entry,
+               printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry,
                           lp->rx_ring[entry].status);
        /* If we own the next entry, it's a new packet. Send it up. */
        while (lp->rx_ring[entry].status >= 0) {
                int status = lp->rx_ring[entry].status;
 
-               if (tulip_debug > 4)
-                       printk("  tulip_rx() status was %8.8x.\n", status);
                if ((status & 0x0300) != 0x0300) {
                        if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
-                         printk("%s: Oversized Ethernet frame spanned multiple buffers,"
-                                        " status %8.8x!\n", dev->name, status);
+                         printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+                                        "multiple buffers, status %8.8x!\n", dev->name, status);
                          lp->stats.rx_length_errors++;
                        }
                } else if (status & 0x8000) {
@@ -1787,7 +1862,7 @@ tulip_rx(struct device *dev)
 
                        /* Check if the packet is long enough to just accept without
                           copying to a properly sized skbuff. */
-                       if (pkt_len > SKBUFF_RX_COPYBREAK) {
+                       if (pkt_len > rx_copybreak) {
                                struct sk_buff *newskb;
                                char *temp;
 
@@ -1801,7 +1876,8 @@ tulip_rx(struct device *dev)
                                skb = lp->rx_skbuff[entry];
                                temp = skb_put(skb, pkt_len);
                                if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp)
-                                       printk("%s: Warning -- the skbuff addresses do not match"
+                                       printk(KERN_ERR "%s: Internal consistency error -- the "
+                                                  "skbuff addresses do not match"
                                                   " in tulip_rx: %p vs. %p / %p.\n", dev->name,
                                                   bus_to_virt(lp->rx_ring[entry].buffer1),
                                                   skb->head, temp);
@@ -1815,7 +1891,8 @@ tulip_rx(struct device *dev)
                        memory_squeeze:
                        if (skb == NULL) {
                                int i;
-                               printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+                               printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n",
+                                          dev->name);
                                /* Check that at least two ring entries are free.
                                   If not, free one and mark stats->rx_dropped++. */
                                for (i = 0; i < RX_RING_SIZE; i++)
@@ -1862,7 +1939,7 @@ tulip_close(struct device *dev)
        dev->tbusy = 1;
 
        if (tulip_debug > 1)
-               printk("%s: Shutting down ethercard, status was %2.2x.\n",
+               printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
                           dev->name, inl(ioaddr + CSR5));
 
        /* Disable interrupts by clearing the interrupt mask. */
@@ -1964,7 +2041,7 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
        if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
                outl(csr6 | 0x00C0, ioaddr + CSR6);
                /* Unconditionally log net taps. */
-               printk("%s: Promiscuous mode enabled.\n", dev->name);
+               printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
                tp->csr6 |= 0xC0;
        } else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
                /* Too many to filter perfectly -- accept all multicasts. */
@@ -2057,6 +2134,13 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
 }
 \f
 #ifdef MODULE
+#if LINUX_VERSION_CODE > 0x20118
+MODULE_PARM(debug, "i");
+MODULE_PARM(reverse_probe, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+#endif
 
 /* An additional parameter that may be passed in... */
 static int debug = -1;
@@ -2064,15 +2148,11 @@ static int debug = -1;
 int
 init_module(void)
 {
-       int cards_found;
-
        if (debug >= 0)
                tulip_debug = debug;
 
        root_tulip_dev = NULL;
-       cards_found = tulip_probe(0);
-
-       return cards_found ? 0 : -ENODEV;
+       return tulip_probe(NULL);
 }
 
 void
@@ -2094,7 +2174,7 @@ cleanup_module(void)
 \f
 /*
  * Local variables:
- *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
+ *  compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
  *  c-indent-level: 4
  *  c-basic-offset: 4
  *  tab-width: 4
index 22cba705d75939b4bd16b45380855bb018e2e251..b1bc3607904bd99befe4226407f4ba54ee59935f 100644 (file)
@@ -2,10 +2,10 @@
 # Sound driver configuration
 #
 #--------
-# There is another confic script which is compatible with rest of
+# There is another config script which is compatible with rest of
 # the kernel. It can be activated by running 'make mkscript' in this
 # directory. Please note that this is an _experimental_ feature which
-# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
+# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui).
 #--------
 #
 $MAKE -C drivers/sound config || exit 1
index 431219c90ca1471f4cb45406bd8d47e421df8e70..639bb77a997d0430a90628b2574767af79fb467b 100644 (file)
@@ -37,6 +37,9 @@ fi
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS
+fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
   if [ "$CONFIG_AFFS_FS" != "n" ]; then
index 0096c2c92988a5240d4eca63f46d03e10b3c1cf2..3586e059da9feaf808ea8ab3c974fb006a385706 100644 (file)
@@ -17,7 +17,7 @@ O_OBJS    = open.o read_write.o inode.o devices.o file_table.o buffer.o \
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
-               hpfs sysv smbfs ncpfs ufs affs
+               hpfs sysv smbfs ncpfs ufs affs autofs
 
 ifeq ($(CONFIG_QUOTA),y)
 O_OBJS += dquot.o
@@ -157,6 +157,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_AUTOFS_FS),y)
+SUB_DIRS += autofs
+else
+  ifeq ($(CONFIG_AUTOFS_FS),m)
+  MOD_SUB_DIRS += autofs
+  endif
+endif
+
 ifeq ($(CONFIG_BINFMT_ELF),y)
 BINFMTS += binfmt_elf.o
 else
diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile
new file mode 100644 (file)
index 0000000..1681c3d
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+# We can build this either out of the kernel tree or the autofs tools tree.
+#
+
+O_TARGET := autofs.o
+O_OBJS   := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o
+
+M_OBJS   := $(O_TARGET)
+
+ifdef TOPDIR
+#
+# Part of the kernel code
+#
+include $(TOPDIR)/Rules.make
+else
+#
+# Standalone (handy for development)
+#
+include ../Makefile.rules
+
+CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS)
+
+all: $(O_TARGET)
+
+$(O_TARGET): $(O_OBJS)
+       $(LD) -r -o $(O_TARGET) $(O_OBJS)
+
+install: $(O_TARGET)
+       install -c $(O_TARGET) /lib/modules/`uname -r`/fs
+
+clean:
+       rm -f *.o *.s
+endif
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
new file mode 100644 (file)
index 0000000..cc38577
--- /dev/null
@@ -0,0 +1,187 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *   
+ * linux/fs/autofs/autofs_i.h
+ *
+ *   Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Internal header file for autofs */
+
+#include <linux/auto_fs.h>
+
+/* This is the range of ioctl() numbers we claim as ours */
+#define AUTOFS_IOC_FIRST     AUTOFS_IOC_READY
+#define AUTOFS_IOC_COUNT     32
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+
+#define kver(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 
+
+#if LINUX_VERSION_CODE < kver(2,1,0)
+
+/* Segmentation stuff for pre-2.1 kernels */
+#include <asm/segment.h>
+
+static inline int copy_to_user(void *dst, void *src, unsigned long len)
+{
+       int rv = verify_area(VERIFY_WRITE, dst, len);
+       if ( rv )
+               return -1;
+       memcpy_tofs(dst,src,len);
+       return 0;
+}
+
+static inline int copy_from_user(void *dst, void *src, unsigned long len)
+{
+       int rv = verify_area(VERIFY_READ, src, len);
+       if ( rv )
+               return -1;
+       memcpy_fromfs(dst,src,len);
+       return 0;
+}
+
+#else
+
+/* Segmentation stuff for post-2.1 kernels */
+#include <asm/uaccess.h>
+#define register_symtab(x)     ((void)0)
+
+#endif
+
+#ifdef DEBUG
+#define DPRINTK(D) printk D;
+#else
+#define DPRINTK(D)
+#endif
+
+#define AUTOFS_SUPER_MAGIC 0x0187
+
+/* Structures associated with the root directory hash */
+
+#define AUTOFS_HASH_SIZE 67
+
+typedef u32 autofs_hash_t;     /* Type returned by autofs_hash() */
+
+struct autofs_dir_ent {
+       autofs_hash_t hash;
+       struct autofs_dir_ent *next;
+       struct autofs_dir_ent **back;
+       char *name;
+       int len;
+       ino_t ino;
+       /* The following entries are for the expiry system */
+       unsigned long last_usage;
+       struct autofs_dir_ent *exp_next;
+       struct autofs_dir_ent *exp_prev;
+};
+
+struct autofs_dirhash {
+       struct autofs_dir_ent *h[AUTOFS_HASH_SIZE];
+       struct autofs_dir_ent expiry_head;
+};
+
+struct autofs_wait_queue {
+       unsigned long wait_queue_token;
+       struct wait_queue *queue;
+       struct autofs_wait_queue *next;
+       /* We use the following to see what we are waiting for */
+       autofs_hash_t hash;
+       int len;
+       char *name;
+       /* This is for status reporting upon return */
+       int status;
+       int wait_ctr;
+};
+
+struct autofs_symlink {
+       int len;
+       char *data;
+       time_t mtime;
+};
+
+#define AUTOFS_MAX_SYMLINKS 256
+
+#define AUTOFS_ROOT_INO      1
+#define AUTOFS_FIRST_SYMLINK 2
+#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS)
+
+#define AUTOFS_SYMLINK_BITMAP_LEN ((AUTOFS_MAX_SYMLINKS+31)/32)
+
+#ifndef END_OF_TIME
+#define END_OF_TIME ((time_t)((unsigned long)((time_t)(~0UL)) >> 1))
+#endif
+
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
+struct autofs_sb_info {
+       u32 magic;
+       struct file *pipe;
+       pid_t oz_pgrp;
+       int catatonic;
+       unsigned long exp_timeout;
+       ino_t next_dir_ino;
+       struct autofs_wait_queue *queues; /* Wait queue pointer */
+       struct autofs_dirhash dirhash; /* Root directory hash */
+       struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS];
+       u32 symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN];
+};
+
+/* autofs_oz_mode(): do we see the man behind the curtain? */
+static inline int autofs_oz_mode(struct autofs_sb_info *sbi) {
+       return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
+}
+
+/* Debug the mysteriously disappearing wait list */
+
+#ifdef DEBUG_WAITLIST
+#define CHECK_WAITLIST(S,O) autofs_check_waitlist_integrity(S,O)
+void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *);
+#else
+#define CHECK_WAITLIST(S,O)
+#endif
+
+/* Hash operations */
+
+autofs_hash_t autofs_hash(const char *,int);
+void autofs_initialize_hash(struct autofs_dirhash *);
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int);
+void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
+void autofs_hash_delete(struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *);
+void autofs_hash_nuke(struct autofs_dirhash *);
+
+/* Expiration-handling functions */
+
+void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *,unsigned long);
+
+/* Operations structures */
+
+extern struct inode_operations autofs_root_inode_operations;
+extern struct inode_operations autofs_symlink_inode_operations;
+extern struct inode_operations autofs_dir_inode_operations;
+
+/* Initializing function */
+
+struct super_block *autofs_read_super(struct super_block *, void *,int);
+
+/* Queue management functions */
+
+int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int);
+int autofs_wait_release(struct autofs_sb_info *,unsigned long,int);
+void autofs_catatonic_mode(struct autofs_sb_info *);
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len);
+#else
+#define autofs_say(n,l)
+#endif
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
new file mode 100644 (file)
index 0000000..461688e
--- /dev/null
@@ -0,0 +1,90 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dir.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+static int autofs_dir_readdir(struct inode *inode, struct file *filp,
+                              void *dirent, filldir_t filldir)
+{
+       if (!inode || !S_ISDIR(inode->i_mode))
+               return -ENOTDIR;
+
+       switch((unsigned long) filp->f_pos)
+       {
+       case 0:
+               if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
+                       return 0;
+               filp->f_pos++;
+               /* fall through */
+       case 1:
+               if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0)
+                       return 0;
+               filp->f_pos++;
+               /* fall through */
+       }
+       return 1;
+}
+
+static int autofs_dir_lookup(struct inode *dir, const char *name, int len,
+                             struct inode **result)
+{
+       *result = dir;
+       if (!len)
+               return 0;
+       if (name[0] == '.') {
+               if (len == 1)
+                       return 0;
+               if (name[1] == '.' && len == 2) {
+                       /* Return the root directory */
+                       *result = iget(dir->i_sb,AUTOFS_ROOT_INO);
+                       iput(dir);
+                       return 0;
+               }
+       }
+       *result = NULL;
+       iput(dir);
+       return -ENOENT;         /* No other entries */
+}
+
+static struct file_operations autofs_dir_operations = {
+       NULL,                   /* lseek */
+       NULL,                   /* read */
+       NULL,                   /* write */
+       autofs_dir_readdir,     /* readdir */
+       NULL,                   /* select */
+       NULL,                   /* ioctl */
+       NULL,                   /* mmap */
+       NULL,                   /* open */
+       NULL,                   /* release */
+       NULL                    /* fsync */
+};
+
+struct inode_operations autofs_dir_inode_operations = {
+       &autofs_dir_operations, /* file operations */
+       NULL,                   /* create */
+       autofs_dir_lookup,      /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* read_page */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL                    /* permission */
+};
+
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
new file mode 100644 (file)
index 0000000..90c1869
--- /dev/null
@@ -0,0 +1,172 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dirhash.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+/* Functions for maintenance of expiry queue */
+
+static void autofs_init_usage(struct autofs_dirhash *dh,
+                             struct autofs_dir_ent *ent)
+{
+       ent->exp_next = &dh->expiry_head;
+       ent->exp_prev = dh->expiry_head.exp_prev;
+       dh->expiry_head.exp_prev->exp_next = ent;
+       dh->expiry_head.exp_prev = ent;
+       ent->last_usage = jiffies;
+}
+
+static void autofs_delete_usage(struct autofs_dir_ent *ent)
+{
+       ent->exp_prev->exp_next = ent->exp_next;
+       ent->exp_next->exp_prev = ent->exp_prev;
+}
+
+void autofs_update_usage(struct autofs_dirhash *dh,
+                        struct autofs_dir_ent *ent)
+{
+       autofs_delete_usage(ent);   /* Unlink from current position */
+       autofs_init_usage(dh,ent);  /* Relink at queue tail */
+}
+
+struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh,
+                                    unsigned long timeout)
+{
+       struct autofs_dir_ent *ent;
+
+       ent = dh->expiry_head.exp_next;
+
+       if ( ent == &(dh->expiry_head) ) return NULL;
+       return (jiffies - ent->last_usage >= timeout) ? ent : NULL;
+}
+
+/* Adapted from the Dragon Book, page 436 */
+/* This particular hashing algorithm requires autofs_hash_t == u32 */
+autofs_hash_t autofs_hash(const char *name, int len)
+{
+       autofs_hash_t h = 0;
+       while ( len-- ) {
+               h = (h << 4) + (unsigned char) (*name++);
+               h ^= ((h & 0xf0000000) >> 24);
+       }
+       return h;
+}
+
+void autofs_initialize_hash(struct autofs_dirhash *dh) {
+       memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
+       dh->expiry_head.exp_next = dh->expiry_head.exp_prev =
+               &dh->expiry_head;
+}
+
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len)
+{
+       struct autofs_dir_ent *dhn;
+
+       DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash));
+       autofs_say(name,len);
+
+       for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
+               if ( hash == dhn->hash &&
+                    len == dhn->len &&
+                    !memcmp(name, dhn->name, len) )
+                       break;
+       }
+
+       return dhn;
+}
+
+void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
+{
+       struct autofs_dir_ent **dhnp;
+
+       DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash));
+       autofs_say(ent->name,ent->len);
+
+       autofs_init_usage(dh,ent);
+
+       dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE];
+       ent->next = *dhnp;
+       ent->back = dhnp;
+       *dhnp = ent;
+}
+
+void autofs_hash_delete(struct autofs_dir_ent *ent)
+{
+       *(ent->back) = ent->next;
+
+       autofs_delete_usage(ent);
+
+       kfree(ent->name);
+       kfree(ent);
+}
+
+/*
+ * Used by readdir().  We must validate "ptr", so we can't simply make it
+ * a pointer.  Values below 0xffff are reserved; calling with any value
+ * <= 0x10000 will return the first entry found.
+ */
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr)
+{
+       int bucket, ecount, i;
+       struct autofs_dir_ent *ent;
+
+       bucket = (*ptr >> 16) - 1;
+       ecount = *ptr & 0xffff;
+
+       if ( bucket < 0 ) {
+               bucket = ecount = 0;
+       } 
+
+       DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount));
+
+       ent = NULL;
+
+       while  ( bucket < AUTOFS_HASH_SIZE ) {
+               ent = dh->h[bucket];
+               for ( i = ecount ; ent && i ; i-- )
+                       ent = ent->next;
+
+               if (ent) {
+                       ecount++; /* Point to *next* entry */
+                       break;
+               }
+
+               bucket++; ecount = 0;
+       }
+
+#ifdef DEBUG
+       if ( !ent )
+               printk("autofs_hash_enum: nothing found\n");
+       else {
+               printk("autofs_hash_enum: found hash %08x, name", ent->hash);
+               autofs_say(ent->name,ent->len);
+       }
+#endif
+
+       *ptr = ((bucket+1) << 16) + ecount;
+       return ent;
+}
+
+/* Delete everything.  This is used on filesystem destruction, so we
+   make no attempt to keep the pointers valid */
+void autofs_hash_nuke(struct autofs_dirhash *dh)
+{
+       int i;
+       struct autofs_dir_ent *ent, *nent;
+
+       for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
+               for ( ent = dh->h[i] ; ent ; ent = nent ) {
+                       nent = ent->next;
+                       kfree(ent->name);
+                       kfree(ent);
+               }
+       }
+}
diff --git a/fs/autofs/init.c b/fs/autofs/init.c
new file mode 100644 (file)
index 0000000..1b3f6f1
--- /dev/null
@@ -0,0 +1,58 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/init.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include "autofs_i.h"
+
+#if LINUX_VERSION_CODE < kver(2,1,36)
+#define __initfunc(X) X
+#else
+#include <linux/init.h>
+#endif
+
+static struct file_system_type autofs_fs_type = {
+       autofs_read_super, "autofs", 0, NULL
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+       int status;
+       
+       if ((status = register_filesystem(&autofs_fs_type)) == 0)
+               register_symtab(0);
+       return status;
+}
+
+void cleanup_module(void)
+{
+       unregister_filesystem(&autofs_fs_type);
+}
+
+#else /* MODULE */
+
+__initfunc(int init_autofs_fs(void))
+{
+       return register_filesystem(&autofs_fs_type);
+}
+
+#endif /* !MODULE */
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len)
+{
+       printk("(%d: ", len);
+       while ( len-- )
+               printk("%c", *name++);
+       printk(")\n");
+}
+#endif
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
new file mode 100644 (file)
index 0000000..20ca090
--- /dev/null
@@ -0,0 +1,277 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/inode.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/file.h>
+#include <linux/locks.h>
+#include <asm/bitops.h>
+#include "autofs_i.h"
+#define __NO_VERSION__
+#include <linux/module.h>
+
+static void autofs_put_inode(struct inode *inode)
+{
+        if (inode->i_nlink)
+                return;
+        inode->i_size = 0;
+}
+
+static void autofs_put_super(struct super_block *sb)
+{
+       struct autofs_sb_info *sbi =
+               (struct autofs_sb_info *) sb->u.generic_sbp;
+       unsigned int n;
+
+       if ( !sbi->catatonic )
+               autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */
+
+        lock_super(sb);
+       autofs_hash_nuke(&sbi->dirhash);
+       for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) {
+               if ( test_bit(n, sbi->symlink_bitmap) )
+                       kfree(sbi->symlink[n].data);
+       }
+
+        sb->s_dev = 0;
+       kfree(sb->u.generic_sbp);
+        unlock_super(sb);
+
+       DPRINTK(("autofs: shutting down\n"));
+       
+#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+#endif
+}
+
+static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static void autofs_read_inode(struct inode *inode);
+static void autofs_write_inode(struct inode *inode);
+
+static struct super_operations autofs_sops = {
+        autofs_read_inode,
+        NULL,
+        autofs_write_inode,
+        autofs_put_inode,
+        autofs_put_super,
+        NULL,
+        autofs_statfs,
+        NULL
+};
+
+static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
+{
+        char *this_char, *value;
+        
+        *uid = current->uid;
+        *gid = current->gid;
+       *pgrp = current->pgrp;
+
+       *minproto = *maxproto = AUTOFS_PROTO_VERSION;
+
+        *pipefd = -1;
+
+        if ( !options ) return 1;
+        for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+                if ((value = strchr(this_char,'=')) != NULL)
+                        *value++ = 0;
+                if (!strcmp(this_char,"fd")) {
+                        if (!value || !*value)
+                                return 1;
+                        *pipefd = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"uid")) {
+                        if (!value || !*value)
+                                return 1;
+                        *uid = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"gid")) {
+                        if (!value || !*value)
+                                return 1;
+                        *gid = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"pgrp")) {
+                        if (!value || !*value)
+                                return 1;
+                        *pgrp = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"minproto")) {
+                        if (!value || !*value)
+                                return 1;
+                        *minproto = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else if (!strcmp(this_char,"maxproto")) {
+                        if (!value || !*value)
+                                return 1;
+                        *maxproto = simple_strtoul(value,&value,0);
+                        if (*value)
+                                return 1;
+                }
+                else break;
+        }
+        return (*pipefd < 0);
+}
+
+struct super_block *autofs_read_super(struct super_block *s, void *data,
+                                      int silent)
+{
+        int pipefd;
+       struct autofs_sb_info *sbi;
+       int minproto, maxproto;
+
+       MOD_INC_USE_COUNT;
+
+        lock_super(s);
+        sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+       if ( !sbi ) {
+               s->s_dev = 0;
+               MOD_DEC_USE_COUNT;
+               return NULL;
+       }
+       DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
+
+       s->u.generic_sbp = sbi;
+       sbi->magic = AUTOFS_SBI_MAGIC;
+       sbi->catatonic = 0;
+       sbi->exp_timeout = 0;
+       sbi->oz_pgrp = current->pgrp;
+       autofs_initialize_hash(&sbi->dirhash);
+       sbi->queues = NULL;
+       memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN);
+       sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO;
+        s->s_blocksize = 1024;
+        s->s_blocksize_bits = 10;
+        s->s_magic = AUTOFS_SUPER_MAGIC;
+        s->s_op = &autofs_sops;
+        unlock_super(s);
+        if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) {
+                s->s_dev = 0;
+               kfree(sbi);
+                printk("autofs: get root inode failed\n");
+               MOD_DEC_USE_COUNT;
+                return NULL;
+        }
+
+        if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+               iput(s->s_mounted);
+                s->s_dev = 0;
+               kfree(sbi);
+                printk("autofs: called with bogus options\n");
+               MOD_DEC_USE_COUNT;
+                return NULL;
+        }
+
+       if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
+               iput(s->s_mounted);
+               s->s_dev = 0;
+               kfree(sbi);
+               printk("autofs: kernel does not match daemon version\n");
+               MOD_DEC_USE_COUNT;
+               return NULL;
+       }
+
+       DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
+       sbi->pipe = fget(pipefd);
+       if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) {
+               if ( sbi->pipe ) {
+                       fput(sbi->pipe, sbi->pipe->f_inode);
+                       printk("autofs: pipe file descriptor does not contain proper ops\n");
+               } else {
+                       printk("autofs: could not open pipe file descriptor\n");
+               }
+               iput(s->s_mounted);
+               s->s_dev = 0;
+               kfree(sbi);
+               MOD_DEC_USE_COUNT;
+               return NULL;
+       }
+        return s;
+}
+
+static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+        struct statfs tmp;
+
+        tmp.f_type = AUTOFS_SUPER_MAGIC;
+        tmp.f_bsize = 1024;
+        tmp.f_blocks = 0;
+        tmp.f_bfree = 0;
+        tmp.f_bavail = 0;
+        tmp.f_files = 0;
+        tmp.f_ffree = 0;
+        tmp.f_namelen = NAME_MAX;
+        copy_to_user(buf, &tmp, bufsiz);
+}
+
+static void autofs_read_inode(struct inode *inode)
+{
+        ino_t ino = inode->i_ino;
+       unsigned int n;
+        struct autofs_sb_info *sbi =
+               (struct autofs_sb_info *) inode->i_sb->u.generic_sbp;
+
+        inode->i_op = NULL;
+        inode->i_mode = 0;
+        inode->i_nlink = 2;
+        inode->i_size = 0;
+        inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+        inode->i_blocks = 0;
+        inode->i_blksize = 1024;
+
+        if ( ino == AUTOFS_ROOT_INO ) {
+                inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+                inode->i_op = &autofs_root_inode_operations;
+               inode->i_uid = inode->i_gid = 0; /* Changed in read_super */
+                return;
+        } 
+       
+        inode->i_uid = inode->i_sb->s_mounted->i_uid;
+        inode->i_gid = inode->i_sb->s_mounted->i_gid;
+        
+       if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
+               /* Symlink inode - should be in symlink list */
+               struct autofs_symlink *sl;
+
+               n = ino - AUTOFS_FIRST_SYMLINK;
+               if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) {
+                       printk("autofs: Looking for bad symlink inode 0x%08x\n", (unsigned int) ino);
+                       return;
+               }
+               
+               inode->i_op = &autofs_symlink_inode_operations;
+               sl = &sbi->symlink[n];
+               inode->u.generic_ip = sl;
+               inode->i_mode = S_IFLNK | S_IRWXUGO;
+               inode->i_mtime = inode->i_ctime = sl->mtime;
+               inode->i_size = sl->len;
+               inode->i_nlink = 1;
+       } else {
+               /* All non-root directory inodes look the same */
+               inode->i_op = &autofs_dir_inode_operations;
+               inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       }
+}
+
+static void autofs_write_inode(struct inode *inode)
+{
+        inode->i_dirt = 0;
+}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
new file mode 100644 (file)
index 0000000..69e62f8
--- /dev/null
@@ -0,0 +1,445 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/root.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include "autofs_i.h"
+
+static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t);
+static int autofs_root_lookup(struct inode *,const char *,int,struct inode **);
+static int autofs_root_symlink(struct inode *,const char *,int,const char *);
+static int autofs_root_unlink(struct inode *,const char *,int);
+static int autofs_root_rmdir(struct inode *,const char *,int);
+static int autofs_root_mkdir(struct inode *,const char *,int,int);
+static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+
+static struct file_operations autofs_root_operations = {
+        NULL,                   /* lseek */
+        NULL,                   /* read */
+        NULL,                   /* write */
+        autofs_root_readdir,    /* readdir */
+        NULL,                   /* select */
+        autofs_root_ioctl,     /* ioctl */
+        NULL,                   /* mmap */
+        NULL,                   /* open */
+        NULL,                   /* release */
+        NULL                    /* fsync */
+};
+
+struct inode_operations autofs_root_inode_operations = {
+        &autofs_root_operations, /* file operations */
+        NULL,                   /* create */
+        autofs_root_lookup,     /* lookup */
+        NULL,                   /* link */
+        autofs_root_unlink,     /* unlink */
+        autofs_root_symlink,    /* symlink */
+        autofs_root_mkdir,      /* mkdir */
+        autofs_root_rmdir,      /* rmdir */
+        NULL,                   /* mknod */
+        NULL,                   /* rename */
+        NULL,                   /* readlink */
+        NULL,                   /* follow_link */
+        NULL,                   /* readpage */
+        NULL,                   /* writepage */
+        NULL,                   /* bmap */
+        NULL,                   /* truncate */
+        NULL                    /* permission */
+};
+
+static int autofs_root_readdir(struct inode *inode, struct file *filp,
+                              void *dirent, filldir_t filldir)
+{
+       struct autofs_dir_ent *ent;
+       struct autofs_dirhash *dirhash;
+       off_t onr, nr;
+
+       if (!inode || !S_ISDIR(inode->i_mode))
+               return -ENOTDIR;
+
+       dirhash = &((struct autofs_sb_info *)inode->i_sb->u.generic_sbp)->dirhash;
+       nr = filp->f_pos;
+
+       switch(nr)
+       {
+       case 0:
+               if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0)
+                       return 0;
+               filp->f_pos = ++nr;
+               /* fall through */
+       case 1:
+               if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0)
+                       return 0;
+               filp->f_pos = ++nr;
+               /* fall through */
+       default:
+               while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) {
+                       if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0)
+                               return 0;
+                       filp->f_pos = nr;
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int autofs_root_lookup(struct inode *dir, const char *name, int len,
+                             struct inode **result)
+{
+       struct autofs_sb_info *sbi;
+       struct autofs_dir_ent *ent;
+       struct inode *res;
+       autofs_hash_t hash;
+       int status, oz_mode;
+
+       DPRINTK(("autofs_root_lookup: name = "));
+       autofs_say(name,len);
+
+       *result = NULL;
+       if (!dir)
+               return -ENOENT;
+       if (!S_ISDIR(dir->i_mode)) {
+               iput(dir);
+               return -ENOTDIR;
+       }
+
+       /* Handle special cases: . and ..; since this is a root directory,
+          they both point to the inode itself */
+       *result = dir;
+       if (!len)
+               return 0;
+       if (name[0] == '.') {
+               if (len == 1)
+                       return 0;
+               if (name[1] == '.' && len == 2)
+                       return 0;
+       }
+
+       *result = res = NULL;
+       sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+
+       hash = autofs_hash(name,len);
+
+       oz_mode = autofs_oz_mode(sbi);
+       DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode));
+
+       do {
+               while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
+                       DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
+                       
+                       if ( oz_mode ) {
+                               iput(dir);
+                               return -ENOENT;
+                       } else {
+                               status = autofs_wait(sbi,hash,name,len);
+                               DPRINTK(("autofs_wait returned %d\n", status));
+                               if ( status ) {
+                                       iput(dir);
+                                       return status;
+                               }
+                       }
+               }
+
+               DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
+
+               if (!(res = iget(dir->i_sb,ent->ino))) {
+                       printk("autofs: iget returned null!\n");
+                       iput(dir);
+                       return -EACCES;
+               }
+               
+               if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) {
+                       /* Not a mount point yet, call 1-800-DAEMON */
+                       DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp));
+                       iput(res);
+                       res = NULL;
+                       status = autofs_wait(sbi,hash,name,len);
+                       if ( status ) {
+                               iput(dir);
+                               return status;
+                       }
+               }
+       } while(!res);
+       autofs_update_usage(&sbi->dirhash,ent);
+       
+       *result = res;
+       iput(dir);
+       return 0;
+}
+
+static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+       struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+       struct autofs_dirhash *dh = &sbi->dirhash;
+       autofs_hash_t hash = autofs_hash(name,len);
+       struct autofs_dir_ent *ent;
+       unsigned int n;
+       int slsize;
+       struct autofs_symlink *sl;
+
+       DPRINTK(("autofs_root_symlink: %s <- ", symname));
+       autofs_say(name,len);
+
+       if ( !autofs_oz_mode(sbi) ) {
+               iput(dir);
+               return -EPERM;
+       }
+       if ( autofs_hash_lookup(dh,hash,name,len) ) {
+               iput(dir);
+               return -EEXIST;
+       }
+       n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
+       if ( n >= AUTOFS_MAX_SYMLINKS ) {
+               iput(dir);
+               return -ENOSPC;
+       }
+       set_bit(n,sbi->symlink_bitmap);
+       sl = &sbi->symlink[n];
+       sl->len = strlen(symname);
+       sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
+       if ( !sl->data ) {
+               clear_bit(n,sbi->symlink_bitmap);
+               iput(dir);
+               return -ENOSPC;
+       }
+       ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+       if ( !ent ) {
+               kfree(sl->data);
+               clear_bit(n,sbi->symlink_bitmap);
+               iput(dir);
+               return -ENOSPC;
+       }
+       ent->name = kmalloc(len, GFP_KERNEL);
+       if ( !ent->name ) {
+               kfree(sl->data);
+               kfree(ent);
+               clear_bit(n,sbi->symlink_bitmap);
+               iput(dir);
+               return -ENOSPC;
+       }
+       memcpy(sl->data,symname,slsize);
+       sl->mtime = CURRENT_TIME;
+
+       ent->ino = AUTOFS_FIRST_SYMLINK + n;
+       ent->hash = hash;
+       memcpy(ent->name,name,ent->len = len);
+
+       autofs_hash_insert(dh,ent);
+       iput(dir);
+
+       return 0;
+}
+
+static int autofs_root_unlink(struct inode *dir, const char *name, int len)
+{
+       struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+       struct autofs_dirhash *dh = &sbi->dirhash;
+       autofs_hash_t hash = autofs_hash(name,len);
+       struct autofs_dir_ent *ent;
+       unsigned int n;
+
+       iput(dir);              /* Nothing below can sleep */
+
+       if ( !autofs_oz_mode(sbi) )
+               return -EPERM;
+
+       ent = autofs_hash_lookup(dh,hash,name,len);
+       if ( !ent )
+               return -ENOENT;
+
+       n = ent->ino - AUTOFS_FIRST_SYMLINK;
+       if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
+               return -EINVAL; /* Not a symlink inode, can't unlink */
+
+       autofs_hash_delete(ent);
+       clear_bit(n,sbi->symlink_bitmap);
+       kfree(sbi->symlink[n].data);
+       
+       return 0;
+}
+
+static int autofs_root_rmdir(struct inode *dir, const char *name, int len)
+{
+       struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+       struct autofs_dirhash *dh = &sbi->dirhash;
+       autofs_hash_t hash = autofs_hash(name,len);
+       struct autofs_dir_ent *ent;
+
+       if ( !autofs_oz_mode(sbi) ) {
+               iput(dir);
+               return -EPERM;
+       }
+       ent = autofs_hash_lookup(dh,hash,name,len);
+       if ( !ent ) {
+               iput(dir);
+               return -ENOENT;
+       }
+       if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) {
+               iput(dir);
+               return -ENOTDIR; /* Not a directory */
+       }
+       autofs_hash_delete(ent);
+       dir->i_nlink--;
+       iput(dir);
+
+       return 0;
+}
+
+static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+       struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+       struct autofs_dirhash *dh = &sbi->dirhash;
+       autofs_hash_t hash = autofs_hash(name,len);
+       struct autofs_dir_ent *ent;
+
+       if ( !autofs_oz_mode(sbi) ) {
+               iput(dir);
+               return -EPERM;
+       }
+       ent = autofs_hash_lookup(dh,hash,name,len);
+       if ( ent ) {
+               iput(dir);
+               return -EEXIST;
+       }
+       if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
+               printk("autofs: Out of inode numbers -- what the heck did you do??\n");
+               iput(dir);
+               return -ENOSPC;
+       }
+       ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+       if ( !ent ) {
+               iput(dir);
+               return -ENOSPC;
+       }
+       ent->name = kmalloc(len, GFP_KERNEL);
+       if ( !ent->name ) {
+               kfree(ent);
+               iput(dir);
+               return -ENOSPC;
+       }
+       ent->hash = hash;
+       memcpy(ent->name, name, ent->len = len);
+       ent->ino = sbi->next_dir_ino++;
+       autofs_hash_insert(dh,ent);
+       dir->i_nlink++;
+       iput(dir);
+
+       return 0;
+}
+
+/* Get/set timeout ioctl() operation */
+static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
+                                        unsigned long *p)
+{
+       int rv;
+       unsigned long ntimeout;
+
+#if LINUX_VERSION_CODE < kver(2,1,0)
+       if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(unsigned long))) )
+               return rv;
+       ntimeout = get_user(p);
+       put_user(sbi->exp_timeout/HZ, p);
+#else
+       if ( (rv = get_user(ntimeout, p)) ||
+            (rv = put_user(sbi->exp_timeout/HZ, p)) )
+               return rv;
+#endif
+
+       if ( ntimeout > ULONG_MAX/HZ )
+               sbi->exp_timeout = 0;
+       else
+               sbi->exp_timeout = ntimeout * HZ;
+
+       return 0;
+}
+
+/* Return protocol version */
+static inline int autofs_get_protover(int *p)
+{
+#if LINUX_VERSION_CODE < kver(2,1,0)
+       int rv;
+       if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(int))) )
+               return rv;
+       put_user(AUTOFS_PROTO_VERSION, p);
+       return 0;
+#else
+       return put_user(AUTOFS_PROTO_VERSION, p);
+#endif
+}
+
+/* Perform an expiry operation */
+static inline int autofs_expire_run(struct autofs_sb_info *sbi,
+                                   struct autofs_packet_expire *pkt_p)
+{
+       struct autofs_dir_ent *ent;
+       struct autofs_packet_expire pkt;
+       struct autofs_dirhash *dh = &(sbi->dirhash);
+       
+       memset(&pkt,0,sizeof pkt);
+
+       pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+       pkt.hdr.type = autofs_ptype_expire;
+
+       if ( !sbi->exp_timeout ||
+            !(ent = autofs_expire(dh,sbi->exp_timeout)) )
+               return -EAGAIN;
+
+       pkt.len = ent->len;
+       memcpy(pkt.name, ent->name, pkt.len);
+       pkt.name[pkt.len] = '\0';
+
+       if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
+               return -EFAULT;
+       
+       autofs_update_usage(dh,ent);
+
+       return 0;
+}
+
+/*
+ * ioctl()'s on the root directory is the chief method for the daemon to
+ * generate kernel reactions
+ */
+static int autofs_root_ioctl(struct inode *inode, struct file *filp,
+                            unsigned int cmd, unsigned long arg)
+{
+       struct autofs_sb_info *sbi =
+               (struct autofs_sb_info *)inode->i_sb->u.generic_sbp;
+
+       DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp));
+
+       if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
+            _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
+               return -ENOTTY;
+       
+       if ( !autofs_oz_mode(sbi) && !fsuser() )
+               return -EPERM;
+       
+       switch(cmd) {
+       case AUTOFS_IOC_READY:  /* Wait queue: go ahead and retry */
+               return autofs_wait_release(sbi,arg,0);
+       case AUTOFS_IOC_FAIL:   /* Wait queue: fail with ENOENT */
+               return autofs_wait_release(sbi,arg,-ENOENT);
+       case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+               autofs_catatonic_mode(sbi);
+               return 0;
+       case AUTOFS_IOC_PROTOVER: /* Get protocol version */
+               return autofs_get_protover((int *)arg);
+       case AUTOFS_IOC_SETTIMEOUT:
+               return autofs_get_set_timeout(sbi,(unsigned long *)arg);
+       case AUTOFS_IOC_EXPIRE:
+               return autofs_expire_run(sbi,(struct autofs_packet_expire *)arg);
+       default:
+               return -ENOSYS;
+       }
+}
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
new file mode 100644 (file)
index 0000000..46c3331
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/symlink.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include "autofs_i.h"
+
+static int autofs_follow_link(struct inode *dir, struct inode *inode,
+                             int flag, int mode, struct inode **res_inode)
+{
+       int error;
+       char *link;
+
+       *res_inode = NULL;
+       if (!dir) {
+               dir = current->fs->root;
+               dir->i_count++;
+       }
+       if (!inode) {
+               iput(dir);
+               return -ENOENT;
+       }
+       if (!S_ISLNK(inode->i_mode)) {
+               iput(dir);
+               *res_inode = inode;
+               return 0;
+       }
+       if (current->link_count > 5) {
+               iput(dir);
+               iput(inode);
+               return -ELOOP;
+       }
+       link = ((struct autofs_symlink *)inode->u.generic_ip)->data;
+       current->link_count++;
+       error = open_namei(link,flag,mode,res_inode,dir);
+       current->link_count--;
+       iput(inode);
+       return error;
+}
+
+static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
+{
+       struct autofs_symlink *sl;
+       int len;
+
+       if (!S_ISLNK(inode->i_mode)) {
+               iput(inode);
+               return -EINVAL;
+       }
+       sl = (struct autofs_symlink *)inode->u.generic_ip;
+       len = sl->len;
+       if (len > buflen) len = buflen;
+       copy_to_user(buffer,sl->data,len);
+       iput(inode);
+       return len;
+}
+
+struct inode_operations autofs_symlink_inode_operations = {
+       NULL,                   /* file operations */
+       NULL,                   /* create */
+       NULL,                   /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       autofs_readlink,        /* readlink */
+       autofs_follow_link,     /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL                    /* permission */
+};
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
new file mode 100644 (file)
index 0000000..cbe270a
--- /dev/null
@@ -0,0 +1,171 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/waitq.c
+ *
+ *  Copyright 1997 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/file.h>
+#include "autofs_i.h"
+
+/* We make this a static variable rather than a part of the superblock; it
+   is better if we don't reassign numbers easily even across filesystems */
+static int autofs_next_wait_queue = 1;
+
+void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+{
+       struct autofs_wait_queue *wq, *nwq;
+
+       DPRINTK(("autofs: entering catatonic mode\n"));
+
+       sbi->catatonic = 1;
+       wq = sbi->queues;
+       sbi->queues = NULL;     /* Erase all wait queues */
+       while ( wq ) {
+               nwq = wq->next;
+               wq->status = -ENOENT; /* Magic is gone - report failure */
+               kfree(wq->name);
+               wq->name = NULL;
+               wake_up(&wq->queue);
+               wq = nwq;
+       }
+       fput(sbi->pipe, sbi->pipe->f_inode);    /* Close the pipe */
+}
+
+static int autofs_write(struct file *file, const void *addr, int bytes)
+{
+       unsigned short fs;
+       unsigned long old_signal;
+       const char *data = (const char *)addr;
+       int written = 0;
+
+       /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
+
+       /* Save pointer to user space and point back to kernel space */
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+
+       old_signal = current->signal;
+
+       while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) {
+               data += written;
+               bytes -= written;
+       }
+
+       if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) {
+               /* Keep the currently executing process from receiving a
+                  SIGPIPE unless it was already supposed to get one */
+               current->signal &= ~(1 << (SIGPIPE-1));
+       }
+       set_fs(fs);
+
+       return (bytes > 0);
+}
+       
+static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq)
+{
+       struct autofs_packet_missing pkt;
+
+       DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token));
+       autofs_say(wq->name,wq->len);
+
+       memset(&pkt,0,sizeof pkt); /* For security reasons */
+
+       pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+       pkt.hdr.type = autofs_ptype_missing;
+       pkt.wait_queue_token = wq->wait_queue_token;
+       pkt.len = wq->len;
+        memcpy(pkt.name, wq->name, pkt.len);
+       pkt.name[pkt.len] = '\0';
+
+       if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) )
+               autofs_catatonic_mode(sbi);
+}
+
+int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len)
+{
+       struct autofs_wait_queue *wq;
+       int status;
+
+       for ( wq = sbi->queues ; wq ; wq = wq->next ) {
+               if ( wq->hash == hash &&
+                    wq->len == len &&
+                    wq->name && !memcmp(wq->name,name,len) )
+                       break;
+       }
+       
+       if ( !wq ) {
+               /* Create a new wait queue */
+               wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
+               if ( !wq )
+                       return -ENOMEM;
+
+               wq->name = kmalloc(len,GFP_KERNEL);
+               if ( !wq->name ) {
+                       kfree(wq);
+                       return -ENOMEM;
+               }
+               wq->wait_queue_token = autofs_next_wait_queue++;
+               init_waitqueue(&wq->queue);
+               wq->hash = hash;
+               wq->len = len;
+               wq->status = -EINTR; /* Status return if interrupted */
+               memcpy(wq->name, name, len);
+               wq->next = sbi->queues;
+               sbi->queues = wq;
+
+               /* autofs_notify_daemon() may block */
+               wq->wait_ctr = 2;
+               autofs_notify_daemon(sbi,wq);
+       } else
+               wq->wait_ctr++;
+
+       if ( wq->name ) {
+               /* wq->name is NULL if and only if the lock is released */
+               interruptible_sleep_on(&wq->queue);
+       } else {
+               DPRINTK(("autofs_wait: skipped sleeping\n"));
+       }
+
+       status = wq->status;
+
+       if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */
+               kfree(wq);
+
+       return status;
+}
+
+
+int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status)
+{
+       struct autofs_wait_queue *wq, **wql;
+
+       for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
+               if ( wq->wait_queue_token == wait_queue_token )
+                       break;
+       }
+       if ( !wq )
+               return -EINVAL;
+
+       *wql = wq->next;        /* Unlink from chain */
+       kfree(wq->name);
+       wq->name = NULL;        /* Do not wait on this queue */
+
+       wq->status = status;
+
+       if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */
+               kfree(wq);
+       else
+               wake_up(&wq->queue);
+
+       return 0;
+}
+
index dacda9315ab441073733bd748d4aa5721514d9c2..8cacd9e7df7d50fde2941b501817f8e7acbfc266 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/ncp_fs.h>
 #include <linux/affs_fs.h>
 #include <linux/ufs_fs.h>
+#include <linux/auto_fs.h>
 #include <linux/major.h>
 
 extern void device_setup(void);
@@ -110,6 +111,9 @@ asmlinkage int sys_setup(void)
        init_ufs_fs();
 #endif
 
+#ifdef CONFIG_AUTOFS_FS
+       init_autofs_fs();
+#endif
        mount_root();
        return 0;
 }
diff --git a/include/linux/auto_fs.h b/include/linux/auto_fs.h
new file mode 100644 (file)
index 0000000..2b25d29
--- /dev/null
@@ -0,0 +1,62 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *   
+ * linux/include/linux/auto_fs.h
+ *
+ *   Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+#ifndef _LINUX_AUTO_FS_H
+#define _LINUX_AUTO_FS_H
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <asm/types.h>
+
+#define AUTOFS_PROTO_VERSION 3
+
+enum autofs_packet_type {
+       autofs_ptype_missing,   /* Missing entry (mount request) */
+       autofs_ptype_expire,    /* Expire entry (umount request) */
+};
+
+struct autofs_packet_hdr {
+       int proto_version;            /* Protocol version */
+       enum autofs_packet_type type; /* Type of packet */
+};
+
+struct autofs_packet_missing {
+       struct autofs_packet_hdr hdr;
+        unsigned long wait_queue_token;
+       int len;
+       char name[NAME_MAX+1];
+};     
+
+struct autofs_packet_expire {
+       struct autofs_packet_hdr hdr;
+       int len;
+       char name[NAME_MAX+1];
+};
+
+#define AUTOFS_IOC_READY      _IO(0x93,0x60)
+#define AUTOFS_IOC_FAIL       _IO(0x93,0x61)
+#define AUTOFS_IOC_CATATONIC  _IO(0x93,0x62)
+#define AUTOFS_IOC_PROTOVER   _IOR(0x93,0x63,int)
+#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
+#define AUTOFS_IOC_EXPIRE     _IOR(0x93,0x65,struct autofs_packet_expire)
+
+#ifdef __KERNEL__
+
+/* Init function */
+int init_autofs_fs(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_AUTO_FS_H */
index 453bdfcc422c880dcee45554ddb783e9fa3d4e2e..9d43604de57213fa5aa9ba6dc28b7bc21211ca62 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/genhd.h>
 #include <linux/swap.h>
 #include <linux/ctype.h>
+#include <linux/file.h>
 
 extern unsigned char aux_device_present, kbd_read_mask;
 
@@ -170,6 +171,7 @@ struct symbol_table symbol_table = {
        X(generic_file_read),
        X(generic_file_mmap),
        X(generic_readpage),
+       X(__fput),
 
        /* device registration */
        X(register_chrdev),