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
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
# 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
# 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
# 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
# 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
#
# 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
#
release_region(rs_table[i].port, 8);
}
if (tmp_buf) {
- free_page(tmp_buf);
+ free_page((unsigned long) tmp_buf);
tmp_buf = NULL;
}
}
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. */
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");
{
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) );
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
-/* 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
#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>
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
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,
} 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. */
/* 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;
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. */
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);
/* 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)
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.
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++;
}
/* 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;
}
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;
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);
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;
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;
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
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;
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);
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);
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)
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)
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",
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",
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);
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();
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;
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);
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);
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);
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:
*/
/* 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.
*/
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. */
#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>
#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
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
/* 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);
\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)
{
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++;
}
}
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;
#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
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);
}
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"
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];
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;
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);
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);
"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;
/* 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;
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,
#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;
}
}
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);
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. */
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. */
#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);
#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. */
#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
}
/* 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. */
/* 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;
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;
}
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");
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. */
*setup_params++ = *eaddrs++;
*setup_params++ = *eaddrs++;
}
-
+
/* Disable interrupts while playing with the Tx Cmd list. */
save_flags(flags);
cli();
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;
root_speedo_dev = NULL;
cards_found = eepro100_init(NULL);
- return cards_found < 0 ? cards_found : 0;
+ return cards_found ? 0 : -ENODEV;
}
void
\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:
*/
/* 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
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. */
#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
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
"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[] =
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;
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. */
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);
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))
#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) {
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);
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,
}
}
-#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;
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. */
}
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. */
#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. */
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:
{
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++)
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;
}
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");
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]);
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];
}
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);
}
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;
}
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
#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
#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;
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;
}
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));
}
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];
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];
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);
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) {
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 */
/* 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. */
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),
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);
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:
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);
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. */
/* 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");
} 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");
/* 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,
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;
}
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]);
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);
} 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);
}
{
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);
/* 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++;
} 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));
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);
#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;
#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);
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;
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)
/* 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++;
#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;
}
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. */
}
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);
} 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
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) {
/* 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;
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);
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++)
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. */
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. */
}
\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;
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
\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
# 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
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
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
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
--- /dev/null
+#
+# 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
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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 */
+};
+
--- /dev/null
+/* -*- 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);
+ }
+ }
+}
--- /dev/null
+/* -*- 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
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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;
+ }
+}
--- /dev/null
+/* -*- 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 */
+};
--- /dev/null
+/* -*- 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;
+}
+
#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);
init_ufs_fs();
#endif
+#ifdef CONFIG_AUTOFS_FS
+ init_autofs_fs();
+#endif
mount_root();
return 0;
}
--- /dev/null
+/* -*- 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 */
#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;
X(generic_file_read),
X(generic_file_mmap),
X(generic_readpage),
+ X(__fput),
/* device registration */
X(register_chrdev),