]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.47 1.3.47
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:25 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:25 +0000 (15:10 -0500)
48 files changed:
CREDITS
Documentation/Configure.help
Makefile
arch/i386/config.in
arch/i386/defconfig
drivers/char/Makefile
drivers/char/tty_io.c
drivers/char/vt.c
drivers/net/3c503.c
drivers/net/8390.c
drivers/net/arcnet.c
drivers/net/e2100.c
drivers/net/net_init.c
drivers/net/smc-ultra.c
drivers/net/wd.c
drivers/scsi/aha152x.c
drivers/scsi/constants.c
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_syms.c
drivers/scsi/sd.c
fs/proc/mem.c
fs/super.c
include/asm-i386/pgtable.h
include/linux/net_alias.h [new file with mode: 0644]
include/linux/netdevice.h
include/linux/proc_fs.h
include/linux/resource.h
include/linux/vt.h
include/net/ip_alias.h [new file with mode: 0644]
ipc/shm.c
kernel/ksyms.c
mm/filemap.c
mm/memory.c
mm/mprotect.c
mm/swap.c
mm/vmalloc.c
net/Config.in
net/core/Makefile
net/core/dev.c
net/core/net_alias.c [new file with mode: 0644]
net/ipv4/Config.in
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/ip_alias.c [new file with mode: 0644]
net/ipv4/ip_input.c
scripts/Configure

diff --git a/CREDITS b/CREDITS
index e2138ee667dec1daa298cedf97ebbcd9a92cfc95..4b6ae7063def4f70035aa7ae758c968f2c97d018 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -134,6 +134,12 @@ S: Computer Science Department
 S: Baltimore, Maryland 21218
 S: USA
 
+N: Axel Boldt
+E: boldt@math.ucsb.edu
+W: http://math-www.uni-paderborn.de/~axel/
+D: Configuration help text support
+D: Linux CD and Support Giveaway List
+
 N: John Boyd
 E: boyd@cis.ohio-state.edu
 D: Co-author of wd7000 SCSI driver
@@ -214,7 +220,7 @@ S: United Kingdom
 
 N: Ray Dassen
 E: jdassen@wi.LeidenUniv.nl
-U: http://www.wi.leidenuniv.nl/~jdassen/
+W: http://www.wi.leidenuniv.nl/~jdassen/
 D: Debian GNU/Linux: www.debian.org maintainer, FAQ co-maintainer,
 D: packages testing, nit-picking & fixing. Enjoying BugFree (TM) kernels.
 S: Zuidsingel 10A
index f872fbba2533b01fb0bed3e53290eb735b175f84..1b41fcdb84c5239f655136c22fa77d82e575dae7 100644 (file)
@@ -1,4 +1,4 @@
-# LAST EDIT: Fri Oct 27 23:03:01 1995 by Axel Boldt (boldt@math.ucsb.edu) 
+# LAST EDIT: Thu Nov 30 22:39:07 1995 by Axel Boldt (boldt@math.ucsb.edu) 
 #
 # This version of the Linux kernel configuration help texts
 # corresponds to the kernel versions 1.3.x. Be aware that these
@@ -9,7 +9,7 @@
 #
 # Information about what a kernel is, what it does, how to patch and
 # compile it and much more is contained in the Kernel-HOWTO, available
-# via anonymous ftp from sunsite.unc.edu in the directory
+# via ftp (user: anonymous) from sunsite.unc.edu in the directory
 # /pub/Linux/docs/HOWTO. 
 #
 # Format: description<nl>variable<nl>helptext<nl><nl>. The help texts
 # All this was shamelessly stolen from several different sources. Many
 # thanks to all the contributors.  Feel free to use these help texts
 # in your own kernel configuration tools. The texts are copyrighted
-# (c) 1995 by Axel Boldt and governed by our beloved little Copyleft
-# virus, the GNU Public License. This essentially means that you can
-# do with them whatever you want unless you try to restrict someone
-# else's right to do whatever they want.
+# (c) 1995 by Axel Boldt and governed by the GNU Public License.
 #
 # Send comments to Axel Boldt <boldt@math.ucsb.edu>.
 
@@ -82,26 +79,26 @@ CONFIG_ST506
 Use old (reliable) disk-only driver for primary i/f
 CONFIG_BLK_DEV_HD
   As you might have guessed, there are now two drivers for IDE
-  harddrives around: the old reliable one and the new improved
-  one. The new driver can also handle IDE/ATAPI CDROM drives (ATAPI =
-  AT Attachment Packet Interface is a new protocol currently used for
-  controlling CDROM and tape drives, similar to the SCSI
-  protocol. Some newer CDROM drives such as NEC 260 and MITSUMI
-  triple/quad speed drives use it, but most MITSUMI CDROM drives
-  don't). The old driver supports up to two hard drives, while the new
-  one can deal with any mix of up to eight hard drives and IDE/ATAPI
-  CDROMs, two per IDE interface. Using the old driver makes sense if
-  you have older MFM/RLL/ESDI drives, since it is smaller and these
-  drives don't benefit from the additional features of the new
-  driver. If you have more than one IDE interface (=controller), you
-  can use the old driver on the first and the new one on the others,
-  if you like.  In that case (or if you have just one interface and
-  don't want to use the new driver at all) you would say Y here,
-  thereby enlarging your kernel by about 4 kB. If you want to use the
-  new driver exclusively, say N and answer Y to the following
-  question(s). Useful information about how to use large (>504MB) IDE
-  harddrives is contained in drivers/block/README.ide. If unsure, say
-  N.
+  harddrives around: the old one and the new improved one. The old one
+  is not any longer more reliable than the new one. The new driver can
+  also handle IDE/ATAPI CDROM drives (ATAPI = AT Attachment Packet
+  Interface is a new protocol currently used for controlling CDROM and
+  tape drives, similar to the SCSI protocol. Some newer CDROM drives
+  such as NEC 260 and MITSUMI triple/quad speed drives use it, but
+  most MITSUMI CDROM drives don't). The old driver supports up to two
+  hard drives, while the new one can deal with any mix of up to eight
+  hard drives and IDE/ATAPI CDROMs, two per IDE interface. Using the
+  old driver makes sense if you have older MFM/RLL/ESDI drives, since
+  it is smaller and these drives don't benefit from the additional
+  features of the new driver. If you have more than one IDE interface
+  (=controller), you can use the old driver on the first and the new
+  one on the others, if you like.  In that case (or if you have just
+  one interface and don't want to use the new driver at all) you would
+  say Y here, thereby enlarging your kernel by about 4 kB. If you want
+  to use the new driver exclusively, say N and answer Y to the
+  following question(s). Useful information about how to use large
+  (>504MB) IDE harddrives is contained in drivers/block/README.ide. If
+  unsure, say N.
 
 Use new IDE driver for primary/secondary i/f 
 CONFIG_BLK_DEV_IDE 
@@ -173,11 +170,11 @@ CONFIG_NET
 
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
-  This is support for floppy drives on Sun workstations. But this
-  support does not exist at this time, so you might as well say N.
+  This is support for floppy drives on Sun Sparc workstations. Say Y
+  if you have a floppy drive, otherwise N. Easy.
 
 Alpha system type
-CONFIG_ALPHA_JENSEN
+CONFIG_ALPHA_AVANTI
   Find out what type of Alpha system you are running. If you can't
   find one of the given names, then try "Noname". For this question,
   it suffices to give a unique prefix of the option you want to
@@ -204,6 +201,19 @@ CONFIG_ALPHA_SRM
 ##### Don't know what this is about.
 #####
 
+Echo console messages on /dev/ttyS1
+CONFIG_SERIAL_ECHO
+  If you enable this option, all kernel messages that would usually go
+  to the console will also be sent to the device /dev/ttyS1 which
+  corresponds to a serial port; this could be useful if you attached
+  a terminal or printer to that port.
+
+TGA Console Support
+CONFIG_TGA_CONSOLE
+#####
+##### Has something to do with Alpha.
+#####
+
 PCI bios support
 CONFIG_PCI
   Find out whether you have a PCI motherboard. PCI is the name of a
@@ -216,12 +226,9 @@ CONFIG_PCI
   valuable information about which PCI hardware works under Linux and
   which doesn't.  If some of PCI devices don't work and you get a
   warning during boot time, please follow the instructions at the top of
-  include/linux/pci.h. Information regarding the buggy PCTech RZ 1000 IDE
-  harddrive controller which is used in some PCI systems is on the WWW
-  at http://www.powerquest.com/hardware.html. (To browse the WWW, you
-  need to have access to a machine on the Internet that has one of the
-  programs lynx, netscape or Mosaic). The new IDE driver detects this
-  controller and works around this bug, though.
+  include/linux/pci.h. The buggy PCTech RZ 1000 IDE
+  harddrive controller which is used in some PCI systems is detected
+  and correctly handled by this driver.
 
 PCI bridge optimization (experimental)
 CONFIG_PCI_OPTIMIZE
@@ -241,17 +248,19 @@ CONFIG_BLK_DEV_TRITON
 
 System V IPC
 CONFIG_SYSVIPC
-  InterProcessCommunication is a suite of library functions and system
+  Inter Process Communication is a suite of library functions and system
   calls which let processes (= running programs) synchronize and
   exchange information. It is generally considered to be a good thing,
-  and some programs won't run unless you enable this. You can find
-  documentation about IPC in ipc.info, which is contained in
-  sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (available via ftp,
-  user: anonymous; extract with "tar xzvf filename"). These docs
-  are in the info format which is used to document GNU software and
-  can be read from within emacs ("Ctrl-h i") or with the program info
-  ("man info"). Enabling this option enlarges your kernel by about
-  7kB. Just say Y.
+  and some programs won't run unless you enable this. In particular,
+  if you want to run the DOS emulator dosemu under Linux (read the
+  DOSEMU-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO), you'll need to say Y here. You
+  can find documentation about IPC in ipc.info, which is contained in
+  sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (extract with "tar
+  xzvf filename"). These docs are in the info format which is used to
+  document GNU software and can be read from within emacs ("Ctrl-h i")
+  or with the program info ("man info"). Enabling this option enlarges
+  your kernel by about 7kB. Just say Y.
 
 Kernel support for ELF binaries
 CONFIG_BINFMT_ELF
@@ -291,27 +300,13 @@ CONFIG_KERNEL_ELF
   kernel in ELF by saying Y here and editing the variables CC
   and LD in the toplevel Makefile.
 
-Use -m486 flag for 486-specific optimizations
+Use 486-specific optimizations (does NOT work on i386)
 CONFIG_M486
-  If you have a 486 as opposed to a 386 or Pentium CPU, say Y here:
+  If you have a 486 or better, as opposed to a 386, say Y here:
   things will be slightly faster. However, it is not required: the
-  kernel will run on all CPUs with and without this option. If you are
-  not sure, say Y; apart from enlarging your kernel by about 6 kB, it
-  won't hurt.
-
-SMP Kernel (experimental - gcc2.5.8 only: see Documentation/SMP.txt)
-CONFIG_SMP
-  This is experimental support for multiprocessor Pentium machines
-  that agree with the Intel MP v1.1 specification. It can deal with up
-  to 32 processors. You can only compile it with gcc version 2.5.8
-  ("gcc -v"). For details, see Documentation/SMP.ez in the kernel
-  source (this document has been formatted using the ez andrew word
-  processor, available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/X11/andrew/auis63L3-wp.tgz) and
-  http://www.linux.org.uk/SMP/title.html on the WWW (to browse the
-  WWW, you need to have access to a machine on the Internet that has
-  one of the programs lynx, netscape or Mosaic). Please back up all
-  your harddrives before using kernels compiled with this option.
+  kernel will run on all CPUs without this option. If you are
+  not sure, say N; This option will make the kernel use some
+  instructions that are only available on 486+ machines.
 
 Set version information on all symbols for modules
 CONFIG_MODVERSIONS
@@ -384,8 +379,11 @@ CONFIG_IP_MULTICAST
   tables, require that this option be compiled in. You also need
   multicasting if you intend to participate in the MBONE, a high
   bandwidth network on top of the internet which carries audio and
-  video broadcasts. Information about the multicast capabilities of
-  the various network cards is contained in
+  video broadcasts. More information about the MBONE is on the WWW at
+  http://www.best.com/~prince/techinfo/mbone.html (to browse the WWW,
+  you need to have access to a machine on the Internet that has one of
+  the programs lynx, netscape or Mosaic). Information about the
+  multicast capabilities of the various network cards is contained in
   drivers/net/README.multicast. For most people, it's safe to say N.
 
 IP: firewalling
@@ -399,13 +397,14 @@ CONFIG_IP_FIREWALL
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will have to use
   the ipfw tool from the net-tools package, available via ftp (user:
   anonymous) from
-  ftp.linux.org.uk:/pub/linux/Networking/PROGRAMS/NetTools. It allows
-  selective blocking of internet traffic based on type, origin and
-  destination.  You need to enable IP firewalling in order to be able
-  to use IP masquerading (i.e. IP traffic from one of the local
-  computers and destined for an outside host is changed by your box so
-  that it appears to come from you). Chances are that you don't want
-  this, so say N.
+  ftp.linux.org.uk:/pub/linux/Networking/PROGRAMS/NetTools, or
+  preferably ipfwadm from ftp.xos.nl:/pub/linux/ipfwadm/. These
+  programs allow selective blocking of internet traffic based on type,
+  origin and destination.  You need to enable IP firewalling in order
+  to be able to use IP masquerading (i.e. IP traffic from one of the
+  local computers and destined for an outside host is changed by your
+  box so that it appears to come from you). Chances are that you don't
+  want this, so say N.
 
 IP: accounting
 CONFIG_IP_ACCT
@@ -487,7 +486,7 @@ CONFIG_INET_RARP
   Since you asked: if there are diskless machines on your network that
   know their hardware ethernet address but don't know their IP
   addresses upon startup, they send out a Reverse
-  AddressResolutionProtocol request to find out their own IP
+  Address Resolution Protocol request to find out their own IP
   addresses. If you want your Linux box to be able to *answer* such
   requests, say Y here; you'd use the program rarp ("man rarp"). If
   you want to compile this as a module ( = code which can be inserted
@@ -503,13 +502,21 @@ CONFIG_INET_SNARL
   links, between machines of your IP network, say N.  If in doubt, say
   Y.
 
+Disable Path MTU Discovery (normally enabled)
+CONFIG_NO_PATH_MTU_DISCOVERY
+  MTU (maximal transfer unit) is the size of the chunks we send out
+  over the net. "Path MTU Discovery" means that, instead of always
+  sending very small chunks, we start out sending big ones and if we
+  then discover that some host along the way likes its chunks smaller,
+  we adjust to a smaller size. This is good, so say N.
+
 Disable NAGLE algorithm (normally enabled)
 CONFIG_TCP_NAGLE_OFF
   The NAGLE algorithm works by requiring an acknowledgment before
-  sending small IP frames (= packets).  This keeps tiny packets from
-  telnet and rlogin from congesting Wide Area Networks.  You may wish
-  to disable it if you run your X-server from across the network, or
-  if multiple byte key sequences are delayed. Most people strongly
+  sending small IP frames (= packets).  This keeps tiny telnet and
+  rlogin packets from congesting Wide Area Networks.  You may wish to
+  disable it if you run your X-server from across the network, or if
+  multiple byte key sequences are delayed. Most people strongly
   recommend to say N here, though, thereby leaving NAGLE enabled.
 
 IP: Drop source routed frames
@@ -542,17 +549,19 @@ CONFIG_SKB_LARGE
 
 The IPX protocol
 CONFIG_IPX
-  This is support for the Novell networking protocol. You need it if
-  you want to access Novell Netware servers from within the Linux DOS
-  emulator dosemu (read the DOSEMU-HOWTO, available via ftp (user:
-  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO). It's very
-  limited and won't make your Linux box into a Novell server. It would
-  enlarge your kernel by about 5 kB. General information about how to
-  connect Linux, Windows machines and Macs is on the WWW at
-  http://eats.com/linux_mac_win.html (to browse the WWW, you need to
-  have access to a machine on the Internet that has one of the
-  programs lynx, netscape or Mosaic). Unless you have Novell computers
-  on your local network, say N.
+  This is support for the Novell networking protocol, IPX. You need it
+  if you want to access Novell Netware servers from within the Linux
+  DOS emulator dosemu (read the DOSEMU-HOWTO, available via ftp (user:
+  anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO). To
+  turn your Linux box into a fully featured Netware file server and
+  IPX router, say Y here and fetch lwared from
+  sunsite.unc.edu:/pub/Linux/system/Networking/daemons/. General
+  information about how to connect Linux, Windows machines and Macs is
+  on the WWW at http://eats.com/linux_mac_win.html (to browse the WWW,
+  you need to have access to a machine on the Internet that has one of
+  the programs lynx, netscape or Mosaic). This driver would enlarge
+  your kernel by about 5 kB. Unless you have Novell computers on your
+  local network, say N.
 
 Appletalk DDP
 CONFIG_ATALK
@@ -617,22 +626,22 @@ CONFIG_NETROM
 
 Kernel/User network link driver(ALPHA)
 CONFIG_NETLINK
-  This driver will allow for two-way communication between certain
-  parts of the kernel or modules and user processes; the user
-  processes will be able to read from and write to special files in
-  the /dev directory having major mode 18. So far, the kernel uses it
-  to publish some network related information if you enable "Routing
+  This driver allows for two-way communication between certain parts
+  of the kernel or modules and user processes; the user processes are
+  able to read from and write to character special files in the /dev
+  directory having major mode 18. So far, the kernel uses it to
+  publish some network related information if you enable "Routing
   messages", below. Say Y if you want to experiment with it; this is
   ALPHA code, which means that it need not be completely stable; it
   has nothing to do with the computer architecture of the same name.
 
 Routing messages
 CONFIG_RTNETLINK
-  If you enable this and create a special file with major number 18
-  and minor number 0 with mknod ("man mknod"), you can read some
-  network related information from that file. Everything you write to
-  that file will be discarded. Say Y, because otherwise the network
-  link driver is pointless.
+  If you enable this and create a character special file /dev/route
+  with major number 18 and minor number 0 using mknod ("man mknod"),
+  you can read some network related routing information from that
+  file. Everything you write to that file will be discarded. Say Y,
+  because otherwise the network link driver is pointless.
 
 SCSI support?
 CONFIG_SCSI
@@ -752,7 +761,9 @@ CONFIG_SCSI_BUSLOGIC
   the documentation in drivers/scsi/README.BusLogic for more information.
   BusLogic FlashPoint SCSI Host Adapters are not supported by this driver.
   If this driver does not work correctly without modification, please
-  consult the author.  This driver is not currently available as a module.
+  consult the author. This driver is not currently available as a module.
+  You might want to read the SCSI-HOWTO, available via ftp (user: 
+  anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.
  
 EATA-DMA (DPT,NEC&ATT for ISA,EISA,PCI) support
 CONFIG_SCSI_EATA_DMA
@@ -948,7 +959,9 @@ CONFIG_DUMMY
   handy, the default is Y. It won't enlarge your kernel either. What a
   deal.  If you want to compile this as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt.
+  say M here and read Documentation/modules.txt. If you want to use
+  more than one dummy device at a time, you need to compile it as a
+  module. 
   
 SLIP (serial line) support
 CONFIG_SLIP
@@ -1334,10 +1347,14 @@ CONFIG_EEXPRESS
   If you have a network (ethernet) card of this type, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the Intel
-  EtherExpress card is generally regarded to be a very poor choice and
-  the driver is not very reliable. If you want to compile this as a
-  module ( = code which can be inserted in and removed from the
-  running kernel whenever you want), say M here and read
+  EtherExpress16 card is generally regarded to be a very poor choice
+  and the driver is not very reliable. (Roger Wolff
+  (R.E.Wolff@et.tudelft.nl) is attempting to do something about
+  this. At the moment he could use 1) one or more etherexpress16 cards
+  to test locally 2) Alpha testers: people to try new versions of the
+  driver to see if things improve...)  If you want to compile this
+  driver as a module ( = code which can be inserted in and removed
+  from the running kernel whenever you want), say M here and read
   Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt. If you plan to use more
   than one network card under linux, read the
@@ -1503,6 +1520,19 @@ CONFIG_DE4X5
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
+ICL EtherTeam 16i/32 support
+CONFIG_ETH16I
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
 Zenith Z-Note support
 CONFIG_ZNET
   The Zenith Z-Note notebook computer has a built-in network
@@ -1517,9 +1547,6 @@ CONFIG_NET_POCKET
   port ("pocket adaptors"). If you have one of those, say Y and read
   the Ethernet-HOWTO, available via ftp (user: anonymous) from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you plan to use more than
-#####
-##### What should you say to CONFIG_PRINTER in order to use these?
-#####
   one network card under linux, read the Multiple-Ethernet-mini-HOWTO,
   available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you
   want to plug a network card into the PCMCIA slot of your laptop
@@ -1530,9 +1557,11 @@ CONFIG_NET_POCKET
   will just cause this configure script to skip all the questions
   about this class of network devices.  If you say Y, you will be
   asked for your specific device in the following questions.  If you
-  plan to use more than one network card under linux, read the
+  plan to use more than one network device under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use an
+  adaptor attaching to the parallel port as well as a parallel
+  printer, you should compile both drivers as modules (if possible).
 
 AT-LAN-TEC/RealTek pocket adaptor support
 CONFIG_ATP
@@ -1542,7 +1571,9 @@ CONFIG_ATP
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this. If
   you plan to use more than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use
+  this driver, you should have said N to the Parallel Printer support,
+  because the two drivers don't like each other.
 
 D-Link DE600 pocket adaptor support
 CONFIG_DE600
@@ -1552,10 +1583,11 @@ CONFIG_DE600
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this.  If
   you want to compile this as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want), say M
-  here and read Documentation/modules.txt. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  here and read Documentation/modules.txt. If you intend to use this
+  pocket adaptor as well as a parallel printer, you should compile
+  both drivers as modules. If you plan to use more than one network
+  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
+  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
 D-Link DE620 pocket adaptor support
 CONFIG_DE620
@@ -1565,10 +1597,11 @@ CONFIG_DE620
   sunsite.unc.edu:/pub/Linux/docs/HOWTO if you want to use this.  If
   you want to compile this as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want), say M
-  here and read Documentation/modules.txt. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+  here and read Documentation/modules.txt. If you intend to use this
+  pocket adaptor as well as a parallel printer, you should compile
+  both drivers as modules. If you plan to use more than one network
+  card under linux, read the Multiple-Ethernet-mini-HOWTO, available
+  from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
  
 Token Ring driver support
 CONFIG_TR
@@ -1673,17 +1706,17 @@ CONFIG_SBPCD2
 
 Aztech/Orchid/Okano/Wearnes (non IDE) CDROM support
 CONFIG_AZTCD
-  If you have a CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 CDROM
-  drive, say Y here and also to "ISO9660 cdrom filesystem support"
-  below. This is NOT for CDROM drives with IDE interface, such as
-  Aztech CDA269-031SE. (If you have one of those, you should have said
-  Y to the new IDE driver above.) You want to read
-  Documentation/cdrom/aztcd and include/linux/aztcd.h in the kernel
-  source and the CDROM-HOWTO, available via ftp (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO. If unsure, say N.  If you
-  want to compile this as a module ( = code which can be inserted in
-  and removed from the running kernel whenever you want), say M here
-  and read Documentation/modules.txt.
+  If you have a CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110 or
+  Conrad TXC CDROM drive, say Y here and also to "ISO9660 cdrom
+  filesystem support" below. This is NOT for CDROM drives with IDE
+  interface, such as Aztech CDA269-031SE. (If you have one of those,
+  you should have said Y to the new IDE driver above.) You want to
+  read Documentation/cdrom/aztcd and include/linux/aztcd.h in the
+  kernel source and the CDROM-HOWTO, available via ftp (user:
+  anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If unsure,
+  say N.  If you want to compile this as a module ( = code which can
+  be inserted in and removed from the running kernel whenever you
+  want), say M here and read Documentation/modules.txt.
 
 Sony CDU535 CDROM driver support
 CONFIG_CDU535
@@ -1790,13 +1823,13 @@ CONFIG_EXT2_FS
 
 xiafs filesystem support
 CONFIG_XIA_FS
-  This filesystem (= method to organize files on a harddisk partition
-  or a floppy disk) is only used rarely these days. This option would
-  enlarge your kernel by about 28 kB. Say N.  If you want to compile
-  this as a module ( = code which can be inserted in and removed from
-  the running kernel whenever you want), say M here and read
-  Documentation/modules.txt. Note that the filesystem of your root
-  partition cannot be compiled as a module.
+  This is an old filesystem (= method to organize files on a harddisk
+  partition or a floppy disk) and not in use anymore. This option
+  would enlarge your kernel by about 28 kB. Let's all kill this beast:
+  say N.  If you want to compile this as a module ( = code which can
+  be inserted in and removed from the running kernel whenever you
+  want), say M here and read Documentation/modules.txt. Note that the
+  filesystem of your root partition cannot be compiled as a module.
 
 msdos fs support
 CONFIG_MSDOS_FS
@@ -1847,31 +1880,29 @@ CONFIG_PROC_FS
   them. Also, you cannot read the files with less or more: you need to
   use cat. The filesystem is explained in the Kernel Hacker's Guide,
   available via ftp (user: anonymous) in
-  sunsite.unc.edu:/pub/Linux/docs/LDP.  This option will enlarge your
-  kernel by about 18 kB. It's totally cool; for example, "cat
-  /proc/interrupts" gives information about what the different IRQs
-  are used for at the moment (there is a small number of Interrupt
-  ReQuest lines in your computer that are used by the periphery to
-  gain the CPU's attention - often a source of trouble if two devices
-  are mistakenly configured to use the same IRQ). Several programs
-  depend on this, so everyone should say Y here.
+  sunsite.unc.edu:/pub/Linux/docs/LDP and also on the proc(8) manpage
+  ("man 8 proc").  This option will enlarge your kernel by about 18
+  kB. It's totally cool; for example, "cat /proc/interrupts" gives
+  information about what the different IRQs are used for at the moment
+  (there is a small number of Interrupt ReQuest lines in your computer
+  that are used by the periphery to gain the CPU's attention - often a
+  source of trouble if two devices are mistakenly configured to use
+  the same IRQ). Several programs depend on this, so everyone should
+  say Y here.
   
 NFS filesystem support
 CONFIG_NFS_FS
-  If you are connected to a network (using SLIP, PPP or ethernet, not
-  term [term is a program which gives you almost full Internet
-  connectivity if you have a regular dial up shell account on some
-  Internet connected Unix computer. Read the Term-HOWTO, available via
-  ftp (user: anonymous) on sunsite.unc.edu:/pub/Linux/docs/HOWTO]) and
-  want to mount files residing on another UNIX computer (the NFS
-  server) using the NetworkFileSharing protocol, say Y. "Mounting
-  files" means that the client can access the files with usual UNIX
-  commands as if they were sitting on the client's harddisk. For this
-  to work, the server must run the programs nfsd and mountd (but does
-  not need to have NFS filesystem support enabled). NFS is explained
-  in the Network Administrator's Guide, available via ftp (user:
-  anonymous) in sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man
-  page: "man nfs". There is also a NFS-FAQ in
+  If you are connected to some other (usually local) Unix computer
+  (using SLIP, PLIP, PPP or ethernet) and want to mount files
+  residing on that computer (the NFS server) using the Network
+  File Sharing protocol, say Y. "Mounting files" means that the client
+  can access the files with usual UNIX commands as if they were
+  sitting on the client's harddisk. For this to work, the server must
+  run the programs nfsd and mountd (but does not need to have NFS
+  filesystem support enabled). NFS is explained in the Network
+  Administrator's Guide, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/LDP, and on its man page: "man
+  nfs". There is also a NFS-FAQ in
   sunsite.unc.edu:/pub/Linux/docs/faqs which presumes that you know
   the basics of NFS already. If you say Y here, you should have said Y
   to TCP/IP networking also. This option would enlarge your kernel by
@@ -1881,6 +1912,18 @@ CONFIG_NFS_FS
   here and read Documentation/modules.txt. If you don't know what all
   this is about, say N.
 
+Root file system on NFS
+CONFIG_ROOT_NFS
+  If you want your Linux box to mount its whole root filesystem from
+  some other computer over the net via NFS (presumably because your
+  box doesn't have a harddisk), say Y here. You will then have to
+  specify the directory that should be remotely mounted with the
+  "root" kernel command line option. See the documentation of your
+  boot loader (lilo or loadlin) about how to pass options to the
+  kernel. The lilo procedure is also explained in the SCSI-HOWTO,
+  available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO.) Most people say N here.
+
 ISO9660 cdrom filesystem support
 CONFIG_ISO9660_FS
   If you have a CDROM and want to do more with it than just listen to
@@ -1909,25 +1952,27 @@ System V and Coherent filesystem support
 CONFIG_SYSV_FS
   SCO, Xenix and Coherent are commercial Unix systems for intel
   machines. Enabling this option would allow you to read and write to
-  and from their floppies and harddisk partitions. You need this if
-  you want to run iBCS2 (iBCS2 [Intel Binary Compatibility Standard]
-  is a kernel module which lets you run SCO, Xenix, Wyse, Unix Ware,
-  Dell Unix and System V programs under Linux and is often needed to
-  run commercial software, most prominently WordPerfect. It's in
-  tsx-11.mit.edu:/pub/linux/BETA). If you only intend to mount files
-  from some other Unix over the network using NFS, you don't need this
-  (but you need nfs filesystem support obviously). Note that this
-  option is generally not needed for floppies, since a good portable
-  way to transport files between unixes (and even to other operating
-  systems) is given by the tar program ("man tar").  Note also that
-  this option has nothing to do whatsoever with the option "System V
-  IPC". Read about the System V filesystem in
-  Documentation/filesystems/sysv-fs.txt. This option will enlarge your
-  kernel by about 34 kB. If you want to compile this as a module
-  ( = code which can be inserted in and removed from the running
-  kernel whenever you want), say M here and read
+  and from their floppies and harddisk partitions. If you have a
+  floppy or harddisk partition like that, it is probable that they
+  contain binaries from those other Unix systems; in order to run
+  these binaries, you will want to install iBCS2 (iBCS2 [Intel Binary
+  Compatibility Standard] is a kernel module which lets you run SCO,
+  Xenix, Wyse, Unix Ware, Dell Unix and System V programs under Linux
+  and is often needed to run commercial software, most prominently
+  WordPerfect. It's in tsx-11.mit.edu:/pub/linux/BETA). If you only
+  intend to mount files from some other Unix over the network using
+  NFS, you don't need this (but you need nfs filesystem support
+  obviously). Note that this option is generally not needed for
+  floppies, since a good portable way to transport files between
+  unixes (and even to other operating systems) is given by the tar
+  program ("man tar").  Note also that this option has nothing to do
+  whatsoever with the option "System V IPC". Read about the System V
+  filesystem in Documentation/filesystems/sysv-fs.txt. This option
+  will enlarge your kernel by about 34 kB. If you want to compile this
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want), say M here and read
   Documentation/modules.txt. If you haven't heard about all of this
-  before, it's safe to say N.  
+  before, it's safe to say N.
 
 SMB filesystem (to mount WfW shares etc..) support
 CONFIG_SMB_FS
@@ -1947,14 +1992,17 @@ CONFIG_SMB_FS
   the programs lynx, netscape or Mosaic).  If you want to compile this
   as a module ( = code which can be inserted in and removed from the
   running kernel whenever you want), say M here and read
-  Documentation/modules.txt. Most people say N here.
+  Documentation/modules.txt. Most people say N, however.
 
 Cyclades async mux support
 CONFIG_CYCLADES
   This is a card which gives you many serial ports. You would need
   something like this to connect more than two modems to your linux
-  box, for instance in order to become a BBS. If you haven't heard
-  about it, it's safe to say N.
+  box, for instance in order to become a BBS. If you want to compile
+  this as a module ( = code which can be inserted in and removed from
+  the running kernel whenever you want), say M here and read
+  Documentation/modules.txt. If you haven't heard about it, it's safe
+  to say N.
 
 Stallion multiport serial support 
 CONFIG_STALDRV
@@ -1989,9 +2037,10 @@ CONFIG_PRINTER
   running kernel whenever you want), say M here and read
   Documentation/modules.txt. If you intend to use PLIP (Parallel Line
   Internet Protocol is mainly used to create a mini network by
-  connecting the parallel ports of two local machines) and a parallel
-  printer, you should compile both as modules because the drivers
-  don't like each other.
+  connecting the parallel ports of two local machines) or a ethernet
+  network pocket adaptor attaching to the parallel port and a parallel
+  printer as well, you should compile both drivers as modules because
+  the drivers don't like each other.
 
 Logitech busmouse support
 CONFIG_BUSMOUSE
@@ -2163,12 +2212,12 @@ CONFIG_PROFILE_SHIFT
 # LocalWords:  cdrom harddisk diskless netboot nfs xzvf ATAPI MB harddrives ide
 # LocalWords:  HD harddisks CDROMs IDECD NEC MITSUMI filesystem XT XD PCI bios
 # LocalWords:  ISA EISA Microchannel VESA BIOSes bussystem IPC SYSVIPC ipc Ctrl
-# LocalWords:  InterProcessCommunication BINFMT Linkable http ac uk jo html GCC
+# LocalWords:  BINFMT Linkable http ac uk jo html GCC Sparc AVANTI CABRIOLET EB
 # LocalWords:  netscape gcc LD CC toplevel MODVERSIONS insmod rmmod modprobe IP
 # LocalWords:  genksyms INET loopback gatewaying ethernet internet PPP ARP Arp
 # LocalWords:  howto multicasting MULTICAST MBONE firewalling ipfw ACCT resp ip
 # LocalWords:  proc acct IPIP encapsulator decapsulator klogd PCTCP RARP EXT PS
-# LocalWords:  telneting AddressResolutionProtocol subnetted NAGLE rlogin NOSR
+# LocalWords:  telneting subnetted NAGLE rlogin NOSR ttyS TGA techinfo mbone nl
 # LocalWords:  Mb SKB IPX Novell Netware dosemu Appletalk DDP ATALK tapedrive
 # LocalWords:  SD CHR scsi thingy SG CD LUNs LUN jukebox Adaptec BusLogic EATA
 # LocalWords:  buslogic DMA DPT ATT eata dma PIO UltraStor fdomain umsdos ext
@@ -2196,5 +2245,7 @@ CONFIG_PROFILE_SHIFT
 # LocalWords:  Multisession STALDRV EasyIO EC EasyConnection ISTALLION ONboard
 # LocalWords:  Brumby pci TNC cis ohio faq usenet NETLINK dev hydra ca Tyne mem
 # LocalWords:  carleton Deskstation DECstation SUNFD JENSEN Noname XXXM SLiRP
-# LocalWords:  pppd Zilog ZS soundcards SRM bootloader SMP smp ez mainmenu rarp
-# LocalWords:  RTNETLINK mknod
+# LocalWords:  pppd Zilog ZS soundcards SRM bootloader ez mainmenu rarp ipfwadm
+# LocalWords:  RTNETLINK mknod xos MTU lwared Macs mac netatalk macs cs Wolff
+# LocalWords:  dartmouth flowerpt MultiMaster FlashPoint tudelft etherexpress
+# LocalWords:  ICL EtherTeam ETH IDESCSI TXC
index 6fc09d0b36bcd50eb720c230e5642b744d6040e7..9b31778360daef04976e96d21ab958cb5f93dc73 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 46
+SUBLEVEL = 47
 
 ARCH = i386
 
index cffc930109e3c8bf7f479058767e754e9ce4f173..d5e23744513b26b7b7e7ab560e03c62561aa17ff 100644 (file)
@@ -19,9 +19,9 @@ tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
   bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
 fi
-#bool 'Use -mpentium flag for Pentium-specific optimizations' CONFIG_M586
+#bool 'Use Pentium-specific optimizations (does NOT work on i386)' CONFIG_M586
 #if [ "$CONFIG_M586" = "n" ]; then
-bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486
+bool 'Use 486-specific optimizations (does NOT work on i386)' CONFIG_M486
 #fi
 
 mainmenu_option next_comment
index c6ec6bb9156f5ec605ba2f8458a9b8833f05ed26..a1b737ac9197796cb3ba6174f32e6eb83a927930 100644 (file)
@@ -41,6 +41,7 @@ CONFIG_BLK_DEV_IDECD=y
 # Networking options
 #
 # CONFIG_FIREWALL is not set
+# CONFIG_NET_ALIAS is not set
 CONFIG_INET=y
 # CONFIG_IP_FORWARD is not set
 # CONFIG_IP_MULTICAST is not set
@@ -122,10 +123,6 @@ CONFIG_ISO9660_FS=y
 # CONFIG_ATIXL_BUSMOUSE is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_APM is not set
-# CONFIG_APM_IGNORE_USER_SUSPEND is not set
-# CONFIG_APM_DO_ENABLE is not set
-CONFIG_APM_CPU_IDLE=y
-# CONFIG_APM_DISPLAY_BLANK is not set
 
 #
 # Sound
index c6e40d134007c8ff0ca2adcbdf09a79ad8b45e19..95273e5171bcdfbbe5b6effd828209ebc42f8891 100644 (file)
@@ -19,7 +19,6 @@ M_OBJS   :=
 L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
        tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
        defkeymap.o consolemap.o selection.o
-SYMTAB_OBJS :=
 
 ifeq ($(CONFIG_CYCLADES),y)
 L_OBJS += cyclades.o
@@ -98,8 +97,7 @@ L_OBJS += tpqic02.o
 endif
 
 ifdef CONFIG_APM
-L_OBJS += apm_bios.o
-SYMTAB_OBJS += apm_bios.o
+LX_OBJS += apm_bios.o
 endif
 
 ifdef M
index 7e49393a6ec3683a171c36e7e4503e9567d486fc..dcf53d7d899b71d229ab4a63a28b21f6358e217b 100644 (file)
@@ -41,6 +41,9 @@
  *
  * New TIOCLINUX variants added.
  *     -- mj@k332.feld.cvut.cz, 19-Nov-95
+ * 
+ * Restrict vt switching via ioctl()
+ *      -- grif@cs.ucr.edu, 5-Dec-95
  */
 
 #include <linux/config.h>
@@ -97,6 +100,7 @@ int last_console = 0;
 int kmsg_redirect = 0;
 struct tty_struct * redirect = NULL;
 struct wait_queue * keypress_wait = NULL;
+char vt_dont_switch = 0;
 
 static void initialize_tty_struct(struct tty_struct *tty);
 
@@ -523,7 +527,7 @@ void complete_change_console(unsigned int new_console)
 {
        unsigned char old_vc_mode;
 
-        if (new_console == fg_console)
+        if ((new_console == fg_console) || (vt_dont_switch))
                 return;
         if (!vc_cons_allocated(new_console))
                 return;
@@ -594,7 +598,7 @@ void complete_change_console(unsigned int new_console)
  */
 void change_console(unsigned int new_console)
 {
-        if (new_console == fg_console)
+        if ((new_console == fg_console) || (vt_dont_switch))
                 return;
         if (!vc_cons_allocated(new_console))
                return;
index c8a27fd321f23d47cae6fff5428f4c0b86aa4dc5..c2c5f17f1aa32e3361a02ee22c91ae458ab885a9 100644 (file)
@@ -5,6 +5,7 @@
  *
  *  Dynamic diacritical handling - aeb@cwi.nl - Dec 1993
  *  Dynamic keymap and string allocation - aeb@cwi.nl - May 1994
+ *  Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995
  */
 
 #include <linux/types.h>
@@ -28,6 +29,7 @@
 #include "diacr.h"
 #include "selection.h"
 
+extern char vt_dont_switch;
 extern struct tty_driver console_driver;
 
 #define VT_IS_IN_USE(i)        (console_driver.table[i] && console_driver.table[i]->count)
@@ -1094,7 +1096,16 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                  return i;
                return con_get_unimap(ct, &(ud->entry_ct), list);
              }
+       case VT_LOCKSWITCH:
+               if (!suser())
+                  return -EPERM;
+               vt_dont_switch = 1;
+               return 0;
+       case VT_UNLOCKSWITCH:
+               if (!suser())
+                  return -EPERM;
+               vt_dont_switch = 0;
+               return 0;
        default:
                return -ENOIOCTLCMD;
        }
index a98e2b8e5d0bd96246613c23bf3c913092167d69..ec09cfec53aa835cd0ef978e6c6247bb41319e33 100644 (file)
@@ -477,7 +477,12 @@ el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
     unsigned long hdr_start = dev->mem_start + ((ring_page - EL2_MB1_START_PG)<<8);
 
     if (dev->mem_start) {       /* Use the shared memory. */
+#ifdef notdef
+       /* Officially this is what we are doing, but the readl() is faster */
        memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+       ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
        return;
     }
 
index 789258b99493940525eaf1cf7f282e97adb132ab..7dd2ae37ed44a1b19d6123f6225dd0eb39e7e96b 100644 (file)
@@ -32,8 +32,6 @@ static const char *version =
   Much of this code should have been cleaned up, but every attempt 
   has broken some clone part.
   
-  Doesn't currently work on all shared memory cards.
-  
   Sources:
   The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
   */
@@ -512,8 +510,9 @@ static void ei_receive(struct device *dev)
        if (rx_pkt_count > high_water_mark)
                high_water_mark = rx_pkt_count;
 
-    /* Bug alert!  Reset ENISR_OVER to avoid spurious overruns! */
-    outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
+    /* We used to also ack ENISR_OVER here, but that would sometimes mask
+    a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+    outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
     return;
 }
 
@@ -550,7 +549,7 @@ static void ei_rx_overrun(struct device *dev)
     /* Remove packets right away. */
     ei_receive(dev);
     
-    outb_p(0xff, e8390_base+EN0_ISR);
+    outb_p(ENISR_OVER, e8390_base+EN0_ISR);
     /* Generic 8390 insns to start up again, same as in open_8390(). */
     outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
     outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
index f5084b9f20f4b703eca57dadbad4fe7572f2d7d2..b58d2d66ff41ff6d41907d06c9507e562f9e899a 100644 (file)
          
        **********************
 
+       v2.22 (95/12/08)
+         - IRQ is always printed as a decimal, instead of a hex number.
+         - Replaced most remaining printk's with BUGMSG macros.
+         - Moved variables for VERIFY_ACK from the Outgoing struct into
+           the lp struct; Outgoing is really for packet-splitting info
+           only.
+         - Simplified INTMASK handling using a memory variable to keep a
+           "running total."
+         - Tracked down more (maybe the last?) "missed IRQ" messages caused
+           by TX-done IRQ's occurring WHILE arcnet??_send_packet was in
+           progress.  These were pretty obscure and mostly harmless.
+         - Made an actual non-ALPHA release of the thing.
+
        v2.21 ALPHA (95/11/29)
          - "Unknown protocol ID" messages now also indicate the station
            which sent the unrecognized packet, to aid in debugging network
            mainly a portability fix.  This will confuse autoprobe a bit, so
            test that too.
          - What about cards with shared memory that can be "turned off?"
-         - NFS mount freezes after several megabytes to SOSS for DOS. 
-          unmount/remount fixes it.  Is this arcnet-specific?  I don't know.
+           (or that have none at all, like the SMC PC500longboard)
          - Some newer ARCnets support promiscuous mode, supposedly.  If
            someone sends me information, I'll try to implement it.
          - Dump Linux 1.2 support and its ugly #ifdefs.
          - Add support for the new 1.3.x IP header cache features.
+         - Support printk's priority levels.
+         - Make arcnetE_send_packet use arcnet_prepare_tx for loading the
+           packet into ARCnet memory.
+         - Move the SKB and memory dump code into separate functions.
          - ATA protocol support?? 
          - Banyan VINES TCP/IP support??
 
                Donald Becker - it didn't work :)
         - skeleton.c v0.05 dated 11/16/93 by Donald Becker
                (from Linux Kernel 1.1.45)
+        - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
         - The official ARCnet data sheets (!) thanks to Ken Cornetet
                <kcornete@nyx10.cs.du.edu>
-        - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
-        - net/inet/eth.c (from kernel 1.1.50) for header-building info...
+        - net/inet/eth.c (from kernel 1.1.50) for header-building info.
         - Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su>
         - Textual information and more alternate source from Joachim Koenig
                <jojo@repas.de>
 */
 
 static const char *version =
- "arcnet.c: v2.21 ALPHA 95/11/29 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.22 95/12/08 Avery Pennarun <apenwarr@foxnet.net>\n";
 
  
 
@@ -368,6 +384,8 @@ extern struct device *irq2dev_map[16];
 #define COMMAND (ioaddr+1)     /* writable, returns random vals on read (?) */
 #define RESET  (ioaddr+8)              /* software reset writable */
 
+#define SETMASK outb(lp->intmask,INTMASK);
+
        /* Time needed for various things (in clock ticks, 1/100 sec) */
        /* We mostly don't bother with these - watch out. */
 #define RESETtime 40           /* reset */
@@ -396,12 +414,6 @@ extern struct device *irq2dev_map[16];
 #define RES2flag        0x40            /* unused */
 #define NORXflag        0x80            /* receiver inhibited */
 
-#ifdef DETECT_RECONFIGS
-       #define RECON_flag RECONflag
-#else
-       #define RECON_flag 0
-#endif
-
        /* in the command register, the following bits have these meanings:
         *                0-2     command
         *                3-4     page number (for enable rcv/xmt command)
@@ -530,26 +542,26 @@ struct Outgoing
              segnum,                   /* segment being sent */
              numsegs,                  /* number of segments */
              seglen;                   /* length of segment */
-#ifdef VERIFY_ACK
-       short lastload_dest,            /* can last loaded packet be acked? */
-             lasttrans_dest;           /* can last TX'd packet be acked? */
-#endif
-       
 };
 
 
 /* Information that needs to be kept for each board. */
 struct arcnet_local {
        struct enet_statistics stats;
-       u_char arcnum;          /* arcnet number - our 8-bit address */
        u_short sequence;       /* sequence number (incs with each packet) */
-       u_char recbuf,          /* receive buffer # (0 or 1) */
-              txbuf,           /* transmit buffer # (2 or 3) */
-              txready;         /* buffer where a packet is ready to send */
+       u_char stationid,       /* our 8-bit station address */
+               recbuf,         /* receive buffer # (0 or 1) */
+               txbuf,          /* transmit buffer # (2 or 3) */
+               txready,        /* buffer where a packet is ready to send */
+               intmask;        /* current value of INTMASK register */
        short intx,             /* in TX routine? */
              in_txhandler,     /* in TX_IRQ handler? */
              sending;          /* transmit in progress? */
-       short tx_left;          /* segments of split packet left to TX */
+
+#ifdef VERIFY_ACK
+       short lastload_dest,            /* can last loaded packet be acked? */
+             lasttrans_dest;           /* can last TX'd packet be acked? */
+#endif
 
 #if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
        time_t first_recon,     /* time of "first" RECON message to count */
@@ -685,7 +697,7 @@ arcnet_probe(struct device *dev)
 
        printk(version);
        
-#if 1
+#if 0
        BUGLVL(D_NORMAL)
        {
                printk("arcnet: ***\n");
@@ -696,7 +708,7 @@ arcnet_probe(struct device *dev)
                printk("arcnet: ***\n");
        }
 #else
-       BUGLVL(D_INIT)
+       BUGLVL(D_EXTRA)
        {
                printk("arcnet: ***\n");
                printk("arcnet: * Read README.arcnet for important release notes!\n");
@@ -707,7 +719,7 @@ arcnet_probe(struct device *dev)
        }
 #endif
 
-       BUGMSG(D_INIT,"given: base %lXh, IRQ %Xh, shmem %lXh\n",
+       BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
                        dev->base_addr,dev->irq,dev->mem_start);
 
 #ifndef MODULE
@@ -758,11 +770,9 @@ arcnet_probe(struct device *dev)
        if (!dev->base_addr || !dev->irq || !dev->mem_start 
                || !dev->rmem_start)
        {
-               printk("%6s: loadable modules can't autoprobe!\n",dev->name);
-               printk("%6s:  try using io=, irqnum=, and shmem= on the insmod line.\n",
-                       dev->name);
-               printk("%6s:  you may also need num= to change the device name. (ie. num=1 for arc1)\n",
-                       dev->name);
+               BUGMSG(D_NORMAL,"loadable modules can't autoprobe!\n");
+               BUGMSG(D_NORMAL,"try using io=, irqnum=, and shmem= on the insmod line.\n");
+               BUGMSG(D_NORMAL,"you may also need num= to change the device name. (ie. num=1 for arc1)\n");
                return ENODEV;
        }
 #endif
@@ -771,8 +781,8 @@ arcnet_probe(struct device *dev)
                int irqval = request_irq(dev->irq, &arcnet_interrupt, 0,
                        "arcnet");
                if (irqval) {
-                       printk("%6s: unable to get IRQ %d (irqval=%d).\n",
-                               dev->name,dev->irq, irqval);
+                       BUGMSG(D_NORMAL,"unable to get IRQ %d (irqval=%d).\n",
+                               dev->irq, irqval);
                        return EAGAIN;
                }
                
@@ -782,8 +792,8 @@ arcnet_probe(struct device *dev)
        /* Grab the region so we can find another board if autoIRQ fails. */
        request_region(dev->base_addr, ARCNET_TOTAL_SIZE,"arcnet");
        
-       printk("%6s: ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n", 
-               dev->name, dev->base_addr, dev->irq, dev->mem_start);
+       BUGMSG(D_NORMAL,"ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n", 
+               dev->base_addr, dev->irq, dev->mem_start);
 
        /* Initialize the device structure. */
        dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
@@ -819,14 +829,12 @@ arcnet_probe(struct device *dev)
        arcnet_reset(dev);
        JIFFER(50);
        BUGMSG(D_NORMAL,"We appear to be station %d (%02Xh)\n",
-                       lp->arcnum,lp->arcnum);
-       if (lp->arcnum==0)
-               printk("%6s: WARNING!  Station address 0 is reserved for broadcasts!\n",
-                       dev->name);
-       if (lp->arcnum==255)
-               printk("%6s: WARNING!  Station address 255 may confuse DOS networking programs!\n",
-                       dev->name);
-       dev->dev_addr[0]=lp->arcnum;
+                       lp->stationid,lp->stationid);
+       if (lp->stationid==0)
+               BUGMSG(D_NORMAL,"WARNING!  Station address 0 is reserved for broadcasts!\n");
+       if (lp->stationid==255)
+               BUGMSG(D_NORMAL,"WARNING!  Station address 255 may confuse DOS networking programs!\n");
+       dev->dev_addr[0]=lp->stationid;
        lp->sequence=1;
        lp->recbuf=0;
 
@@ -907,9 +915,6 @@ int arcnet_ioprobe(struct device *dev, short ioaddr)
        /* if we do this, we're sure to get an IRQ since the card has
         * just reset and the NORXflag is on until we tell it to start
         * receiving.
-        *
-        * However, this could, theoretically, cause a lockup.  Maybe I'm just
-        * not very good at theory! :)
         */
        outb(NORXflag,INTMASK);
        JIFFER(RESETtime);
@@ -919,8 +924,8 @@ int arcnet_ioprobe(struct device *dev, short ioaddr)
        outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
 
        airq = autoirq_report(0);
-       BUGLVL(D_INIT) if (airq)
-               printk("%6s:  autoirq is %d\n", dev->name, airq);
+       if (airq)
+               BUGMSG(D_INIT," autoirq is %d\n", airq);
 
        /* if there was no autoirq AND the user hasn't set any defaults,
         * give up.
@@ -1014,7 +1019,8 @@ int arcnet_reset(struct device *dev)
        int delayval,recbuf=lp->recbuf;
        u_char *cardmem;
        
-       outb(0,INTMASK);        /* no IRQ's, please! */
+       lp->intmask=0;
+       SETMASK;                /* no IRQ's, please! */
        
        BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
                        dev->name,inb(STATUS));
@@ -1034,7 +1040,7 @@ int arcnet_reset(struct device *dev)
                BUGMSG(D_INIT,"reset failed: TESTvalue not present.\n");
                return 1;
        }
-       lp->arcnum=cardmem[1];  /* save address for later use */
+       lp->stationid=cardmem[1];  /* save address for later use */
        
        /* clear out status variables */
        recbuf=lp->recbuf=0;
@@ -1051,7 +1057,11 @@ int arcnet_reset(struct device *dev)
        EnableReceiver();
 
        /* re-enable interrupts */
-       outb(NORXflag|RECON_flag,INTMASK);
+       lp->intmask|=NORXflag;
+#ifdef DETECT_RECONFIGS
+       lp->intmask|=RECONflag;
+#endif
+       SETMASK;
        
        /* done!  return success. */
        return 0;
@@ -1094,7 +1104,7 @@ static int arcnetE_init(struct device *dev)
 
        ether_setup(dev); /* we're emulating ether here, not ARCnet */
        dev->dev_addr[0]=0;
-       dev->dev_addr[5]=lp->arcnum;
+       dev->dev_addr[5]=lp->stationid;
        dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len-1;
        dev->open=NULL;
        dev->stop=NULL;
@@ -1114,7 +1124,7 @@ static int arcnetS_init(struct device *dev)
        arcnet_setup(dev);
        
        /* And now fill particular fields with arcnet values */
-       dev->dev_addr[0]=lp->arcnum;
+       dev->dev_addr[0]=lp->stationid;
        dev->hard_header_len=sizeof(struct S_ClientData);
        dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len
                + S_EXTRA_CLIENTDATA;
@@ -1204,11 +1214,11 @@ arcnet_open(struct device *dev)
        
        /* make sure we're ready to receive IRQ's.
         * arcnet_reset sets this for us, but if we receive one before
-        * START is set to 1, bad things happen.
+        * START is set to 1, it could be ignored.
         */
        outb(0,INTMASK);
        JIFFER(ACKtime);
-       outb(NORXflag|RECON_flag,INTMASK);
+       SETMASK;
        
        MOD_INC_USE_COUNT;
        return 0;
@@ -1227,7 +1237,8 @@ arcnet_close(struct device *dev)
        START=0;
 
        /* Flush TX and disable RX */
-       outb(0,INTMASK);        /* no IRQ's (except RESET, of course) */
+       lp->intmask=0;
+       SETMASK;        /* no IRQ's (except RESET, of course) */
        outb(NOTXcmd,COMMAND);  /* stop transmit */
        outb(NORXcmd,COMMAND);  /* disable receive */
        
@@ -1309,20 +1320,21 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
                        return 1;
                }
 
-               outb(0,INTMASK);
-
+               lp->intmask &= ~TXFREEflag;
+               SETMASK;
+               
                if (status&TXFREEflag)  /* transmit _DID_ finish */
                {
-                       BUGMSG(D_EXTRA,"tx timed out - missed IRQ? (status=%Xh, inTX=%d, inTXh=%d, tickssofar=%d)\n",
-                                       status,lp->intx,
-                                       lp->in_txhandler,tickssofar);
+                       BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, inTXh=%d, ticks=%d, mask=%Xh)\n",
+                                       status,lp->in_txhandler,tickssofar,
+                                       lp->intmask);
                        lp->stats.tx_errors++;
                }
                else
                {
-                       BUGMSG(D_NORMAL,"tx timed out (status=%Xh, inTX=%d, inTXh=%d, tickssofar=%d)\n",
-                                       status,lp->intx,
-                                       lp->in_txhandler,tickssofar);
+                       BUGMSG(D_NORMAL,"tx timed out (status=%Xh, inTXh=%d, tickssofar=%d, intmask=%Xh)\n",
+                                       status,lp->in_txhandler,tickssofar,
+                                       lp->intmask);
                        lp->stats.tx_errors++;
                        lp->stats.tx_aborted_errors++;
 
@@ -1340,8 +1352,6 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
                lp->txready=0;
                lp->sending=0;
 
-               outb(NORXflag|RECON_flag,INTMASK);
-
                return 1;
        }
 
@@ -1349,8 +1359,8 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
           we are passed NULL. Caution: dev_tint() handles the cli()/sti()
           itself. */
        if (skb == NULL) {
-               printk("%6s: tx passed null skb (status=%Xh, inTX=%d, tickssofar=%ld)\n",
-                       dev->name,inb(STATUS),lp->intx,jiffies-dev->trans_start);
+               BUGMSG(D_NORMAL,"tx passed null skb (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+                       inb(STATUS),lp->intx,jiffies-dev->trans_start);
                lp->stats.tx_errors++;
                dev_tint(dev);
                return 0;
@@ -1358,9 +1368,10 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
        
        if (lp->txready)        /* transmit already in progress! */
        {
-               printk("%6s: trying to start new packet while busy! (status=%Xh)\n",
-                       dev->name,inb(STATUS));
-               outb(0,INTMASK);
+               BUGMSG(D_NORMAL,"trying to start new packet while busy! (status=%Xh)\n",
+                       inb(STATUS));
+               lp->intmask &= ~TXFREEflag;
+               SETMASK;
                outb(NOTXcmd,COMMAND); /* abort current send */
                arcnet_inthandler(dev); /* fake an interrupt */
                lp->stats.tx_errors++;
@@ -1374,8 +1385,8 @@ arcnet_send_packet_bad(struct sk_buff *skb, struct device *dev)
           done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
         if (set_bit(0, (void*)&dev->tbusy) != 0)
         {
-            printk("%6s: transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
-                       dev->name,inb(STATUS),lp->intx,jiffies-dev->trans_start);
+            BUGMSG(D_NORMAL,"transmitter called with busy bit set! (status=%Xh, inTX=%d, tickssofar=%ld)\n",
+                       inb(STATUS),lp->intx,jiffies-dev->trans_start);
             lp->stats.tx_errors++;
             lp->stats.tx_fifo_errors++;
             return -EBUSY;
@@ -1394,9 +1405,6 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
        int ioaddr=dev->base_addr,bad;
        struct Outgoing *out=&(lp->outgoing);
 
-       BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
-                       inb(STATUS),lp->intx);
-
        lp->intx++;
        
        bad=arcnet_send_packet_bad(skb,dev);
@@ -1407,7 +1415,7 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
        }
        
        TBUSY=1;
-               
+       
        out->length = 1 < skb->len ? skb->len : 1;
        out->hdr=(struct ClientData*)skb->data;
        out->skb=skb;
@@ -1415,9 +1423,9 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
        BUGLVL(D_SKB)
        {
                short i;
-               for( i=0; i< skb->len; i++)
+               for(i=0; i<skb->len; i++)
                {
-                       if( i%16 == 0 ) printk("\n[%04hX] ",i);
+                       if(i%16 == 0) printk("\n[%04hX] ",i);
                        printk("%02hX ",((unsigned char*)skb->data)[i]);
                }
                printk("\n");
@@ -1425,16 +1433,14 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
 
        out->hdr->sequence=(lp->sequence++);
        
-       /*arcnet_go_tx(dev);*/  /* Make sure buffers are clear */
-
        /* fits in one packet? */
        if (out->length-EXTRA_CLIENTDATA<=XMTU)
        {
                BUGMSG(D_DURING,"not splitting %d-byte packet. (split_flag=%d)\n",
                                out->length,out->hdr->split_flag);
-               BUGLVL(D_EXTRA) if (out->hdr->split_flag)
-                       printk("%6s: short packet has split_flag set?! (split_flag=%d)\n",
-                               dev->name,out->hdr->split_flag);
+               if (out->hdr->split_flag)
+                       BUGMSG(D_NORMAL,"short packet has split_flag set?! (split_flag=%d)\n",
+                               out->hdr->split_flag);
                out->numsegs=1;
                out->segnum=1;
                arcnetAS_prepare_tx(dev,
@@ -1463,9 +1469,8 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
                                + sizeof(struct ClientData);
                out->dataleft=out->length-sizeof(struct ClientData);
                out->numsegs=(out->dataleft+maxsegsize-1)/maxsegsize;
-                               
                out->segnum=0;
-                       
+               
                BUGMSG(D_TX,"packet (%d bytes) split into %d fragments:\n",
                        out->length,out->numsegs);
 
@@ -1500,6 +1505,11 @@ arcnetA_send_packet(struct sk_buff *skb, struct device *dev)
 
        dev->trans_start=jiffies;
        lp->intx--;
+       
+       /* make sure we didn't ignore a TX IRQ while we were in here */
+       lp->intmask |= TXFREEflag;
+       SETMASK;
+
        return 0;
 }
 
@@ -1510,14 +1520,12 @@ static int
 arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
 {
        struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-       int bad;
+       int ioaddr=dev->base_addr,bad;
        union ArcPacket *arcpacket = 
                (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
        u_char *arcsoft,daddr;
        short offset,length=skb->len+1;
 
-       BUGMSG(D_DURING,"in arcnetE_send_packet (skb=%p)\n",skb);
-               
        lp->intx++;
        
        bad=arcnet_send_packet_bad(skb,dev);
@@ -1531,11 +1539,12 @@ arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
                
        if (length>XMTU)
        {
-               printk("%6s: MTU must be <= 493 for ethernet encap (length=%d).\n",
-                       dev->name,length);
-               printk("%6s: transmit aborted.\n",dev->name);
+               BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
+                       length);
+               BUGMSG(D_NORMAL,"transmit aborted.\n");
 
                dev_kfree_skb(skb,FREE_WRITE);
+               lp->intx--;
                return 0;
        }
                
@@ -1603,7 +1612,7 @@ arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
        }
 
 #ifdef VERIFY_ACK
-       lp->outgoing.lastload_dest=daddr;
+       lp->lastload_dest=daddr;
 #endif
        lp->txready=lp->txbuf;  /* packet is ready for sending */
 
@@ -1618,6 +1627,11 @@ arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
 
        dev->trans_start=jiffies;
        lp->intx--;
+       
+       /* make sure we didn't ignore a TX IRQ while we were in here */
+       lp->intmask |= TXFREEflag;
+       SETMASK;
+
        return 0;
 }
 
@@ -1633,9 +1647,6 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
 
        lp->intx++;
        
-       BUGMSG(D_DURING,"transmit requested (status=%Xh, inTX=%d)\n",
-                       inb(STATUS),lp->intx);
-
        bad=arcnet_send_packet_bad(skb,dev);
        if (bad)
        {
@@ -1652,15 +1663,12 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
                short i;
                for(i=0; i<skb->len; i++)
                {
-                       if( i%16 == 0 ) printk("\n[%04hX] ",i);
+                       if (i%16 == 0) printk("\n[%04hX] ",i);
                        printk("%02hX ",((unsigned char*)skb->data)[i]);
                }
                printk("\n");
        }
 
-       /*if (lp->txready && inb(STATUS)&TXFREEflag)
-               arcnet_go_tx(dev);*/
-
        /* fits in one packet? */
        if (length-S_EXTRA_CLIENTDATA<=XMTU)
        {
@@ -1683,7 +1691,7 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
        }
        else                    /* too big for one - not accepted */
        {
-               printk("arcnetS: packet too long (length=%d)\n",
+               BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
                        length);
                dev_kfree_skb(skb,FREE_WRITE);
                lp->stats.tx_dropped++;
@@ -1693,6 +1701,11 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
 
        dev->trans_start=jiffies;
        lp->intx--;
+
+       /* make sure we didn't ignore a TX IRQ while we were in here */
+       lp->intmask |= TXFREEflag;
+       SETMASK;
+
        return 0;
 }
 
@@ -1704,20 +1717,22 @@ arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
 static void arcnetA_continue_tx(struct device *dev)
 {
        struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-       int maxsegsize=XMTU-4;
+       int ioaddr=dev->base_addr,maxsegsize=XMTU-4;
        struct Outgoing *out=&(lp->outgoing);
        
+       BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
+               inb(STATUS),lp->intx,lp->in_txhandler,lp->intmask);
+       
        if (lp->txready)
        {
-               printk("%6s: continue_tx: called with packet in buffer!\n",
-                       dev->name);
+               BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
                return;
        }
-
+       
        if (out->segnum>=out->numsegs)
        {
-               printk("%6s: continue_tx: building segment %d of %d!\n",
-                       dev->name,out->segnum+1,out->numsegs);
+               BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
+                       out->segnum+1,out->numsegs);
        }
 
        if (!out->segnum)       /* first packet */
@@ -1840,13 +1855,20 @@ arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
        }
 
 #ifdef VERIFY_ACK
-       lp->outgoing.lastload_dest=daddr;
+       lp->lastload_dest=daddr;
 #endif
        lp->txready=lp->txbuf;  /* packet is ready for sending */
 }
 
 /* Actually start transmitting a packet that was placed in the card's
  * buffer by arcnetAS_prepare_tx.  Returns 1 if a Tx is really started.
+ *
+ * This should probably always be called with the INTMASK register set to 0,
+ * so go_tx is not called recursively.
+ *
+ * The enable_irq flag determines whether to actually write INTMASK value
+ * to the card; TXFREEflag is always OR'ed into the memory variable either
+ * way.
  */
 static int
 arcnet_go_tx(struct device *dev,int enable_irq)
@@ -1854,19 +1876,15 @@ arcnet_go_tx(struct device *dev,int enable_irq)
        struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
        int ioaddr=dev->base_addr;
 
-       BUGMSG(D_DURING,"go_tx: status=%Xh\n",
-                       inb(STATUS));
-                       
-       outb(0,INTMASK);
+       BUGMSG(D_DURING,"go_tx: status=%Xh, intmask=%Xh, txready=%d, sending=%d\n",
+               inb(STATUS),lp->intmask,lp->txready,lp->sending);
 
        if (lp->sending || !lp->txready)
        {
-               if (enable_irq)
+               if (enable_irq && lp->sending)
                {
-                       if (lp->sending)
-                               outb(TXFREEflag|NORXflag|RECON_flag,INTMASK);
-                       else
-                               outb(NORXflag|RECON_flag,INTMASK);
+                       lp->intmask |= TXFREEflag;
+                       SETMASK;
                }
                return 0;
        }
@@ -1879,12 +1897,13 @@ arcnet_go_tx(struct device *dev,int enable_irq)
        lp->sending++;
 
 #ifdef VERIFY_ACK      
-       lp->outgoing.lasttrans_dest=lp->outgoing.lastload_dest;
-       lp->outgoing.lastload_dest=0;
+       lp->lasttrans_dest=lp->lastload_dest;
+       lp->lastload_dest=0;
 #endif
 
-       if (enable_irq)
-               outb(TXFREEflag|NORXflag|RECON_flag,INTMASK);
+       lp->intmask |= TXFREEflag;
+
+       if (enable_irq) SETMASK;
 
        return 1;
 }
@@ -1933,38 +1952,21 @@ arcnet_inthandler(struct device *dev)
 
        if (IF_INTERRUPT)
        {
-               printk("%6s: DRIVER PROBLEM!  Nested arcnet interrupts!\n",
-                       dev->name);
+               BUGMSG(D_NORMAL,"DRIVER PROBLEM!  Nested arcnet interrupts!\n");
                return; /* don't even try. */
        }
+       
        outb(0,INTMASK);
        INTERRUPT = 1;
 
-       BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh)\n",inb(STATUS));
+       BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
+               inb(STATUS),lp->intmask);
 
-#if 1 /* Whatever you do, don't set this to 0. */
        do
        {
                status = inb(STATUS);
                didsomething=0;
        
-#if 0 /* no longer necessary - doing checking in arcnet_interrupt now */
-               if (!dev->start)
-               {
-                       BUGMSG(D_DURING,"ARCnet not yet initialized.  irq ignored. (status=%Xh)\n",
-                                       status);
-                       if (!(status&NORXflag))
-                               outb(NORXflag|RECON_flag,INTMASK);
-
-                       /* using dev->interrupt here instead of INTERRUPT
-                        * because if dev->start is 0, the other devices
-                        * probably do not exist.
-                        */
-                       dev->interrupt=0;
-                       return;
-               }
-#endif
-       
                /* RESET flag was enabled - card is resetting and if RX
                 * is disabled, it's NOT because we just got a packet.
                 */
@@ -1975,7 +1977,7 @@ arcnet_inthandler(struct device *dev)
                                        status);
                }
 #ifdef DETECT_RECONFIGS
-               if (status & RECONflag)
+               if (status & (lp->intmask) & RECONflag)
                {
                        outb(CFLAGScmd|CONFIGclear,COMMAND);
                        lp->stats.tx_carrier_errors++;
@@ -1991,8 +1993,7 @@ arcnet_inthandler(struct device *dev)
                                jiffies-lp->last_recon > HZ*10)
                        {
                                if (lp->network_down)
-                                       printk("%6s: reconfiguration detected: cabling restored?\n",
-                                               dev->name);
+                                       BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
                                lp->first_recon=lp->last_recon=jiffies;
                                lp->num_recons=lp->network_down=0;
                                
@@ -2020,8 +2021,7 @@ arcnet_inthandler(struct device *dev)
                                    && lp->num_recons >= RECON_THRESHOLD)
                                {
                                        lp->network_down=1;
-                                       printk("%6s: many reconfigurations detected: cabling problem?\n",
-                                               dev->name);
+                                       BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
                                }
                                else if (!lp->network_down
                                    && lp->last_recon-lp->first_recon > HZ*60)
@@ -2039,7 +2039,7 @@ arcnet_inthandler(struct device *dev)
                else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
                {
                        if (lp->network_down)
-                               printk("%6s: cabling restored?\n",dev->name);
+                               BUGMSG(D_NORMAL,"cabling restored?\n");
                        lp->first_recon=lp->last_recon=0;
                        lp->num_recons=lp->network_down=0;
                        
@@ -2049,7 +2049,7 @@ arcnet_inthandler(struct device *dev)
 #endif /* DETECT_RECONFIGS */
 
                /* RX is inhibited - we must have received something. */
-               if (status & NORXflag)
+               if (status & lp->intmask & NORXflag)
                {
                        int recbuf=lp->recbuf=!lp->recbuf;
 
@@ -2065,24 +2065,27 @@ arcnet_inthandler(struct device *dev)
                }
                
                /* it can only be an xmit-done irq if we're xmitting :) */
-               if (status&TXFREEflag && !lp->in_txhandler && lp->sending)
+               /*if (status&TXFREEflag && !lp->in_txhandler && lp->sending)*/
+               if (status & lp->intmask & TXFREEflag)
                {
                        struct Outgoing *out=&(lp->outgoing);
+                       int was_sending=lp->sending;
                        
+                       lp->intmask &= ~TXFREEflag;
+
                        lp->in_txhandler++;
-                       lp->sending--;
-                       
+                       if (was_sending) lp->sending--;
+
                        BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
                                        status,out->numsegs,out->segnum,out->skb);
                                        
 #ifdef VERIFY_ACK
-                       if (!(status&TXACKflag))
+                       if (was_sending && !(status&TXACKflag))
                        {
-                               if (lp->outgoing.lasttrans_dest != 0)
+                               if (lp->lasttrans_dest != 0)
                                {
-                                       printk("%6s: transmit was not acknowledged! (status=%Xh, dest=%d)\n",
-                                               dev->name,status,
-                                               lp->outgoing.lasttrans_dest);
+                                       BUGMSG(D_NORMAL,"transmit was not acknowledged! (status=%Xh, dest=%d)\n",
+                                               status,lp->lasttrans_dest);
                                        lp->stats.tx_errors++;
                                        lp->stats.tx_carrier_errors++;
                                }
@@ -2090,7 +2093,7 @@ arcnet_inthandler(struct device *dev)
                                {
                                        BUGMSG(D_DURING,"broadcast was not acknowledged; that's normal (status=%Xh, dest=%d)\n",
                                                        status,
-                                                       lp->outgoing.lasttrans_dest);
+                                                       lp->lasttrans_dest);
                                }
                        }
 #endif
@@ -2100,6 +2103,8 @@ arcnet_inthandler(struct device *dev)
                        
                        if (lp->intx)
                        {
+                               BUGMSG(D_DURING,"TXDONE while intx! (status=%Xh, intx=%d)\n",
+                                       inb(STATUS),lp->intx);
                                lp->in_txhandler--;
                                continue;
                        }
@@ -2114,7 +2119,6 @@ arcnet_inthandler(struct device *dev)
                                        TBUSY=0;
                                        mark_bh(NET_BH);
                                }
-                               
                                lp->in_txhandler--;
                                continue;
                        }
@@ -2145,7 +2149,7 @@ arcnet_inthandler(struct device *dev)
                                }
                        }
                        didsomething++;
-                       
+
                        lp->in_txhandler--;
                }
                else if (lp->txready && !lp->sending && !lp->intx)
@@ -2160,17 +2164,7 @@ arcnet_inthandler(struct device *dev)
        BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n\n",
                        inb(STATUS),boguscount);
 
-       if (dev->start && (lp->sending || (lp->txready && !lp->intx)))
-               outb(NORXflag|TXFREEflag|RECON_flag,INTMASK);
-       else
-               outb(NORXflag|RECON_flag,INTMASK);
-
-#else /* Disable everything */
-
-       outb(RXcmd|(0<<3)|RXbcasts,COMMAND);
-       outb(NORXflag,INTMASK);
-
-#endif
+       SETMASK;        /* put back interrupt mask */
 
        INTERRUPT=0;
 }
@@ -2206,8 +2200,8 @@ arcnet_rx(struct device *dev,int recbuf)
        /* if source is 0, it's a "used" packet! */
        if (saddr==0)
        {
-               printk("%6s: discarding old packet. (status=%Xh)\n",
-                       dev->name,inb(STATUS));
+               BUGMSG(D_NORMAL,"discarding old packet. (status=%Xh)\n",
+                       inb(STATUS));
                lp->stats.rx_errors++;
                return;
        }
@@ -2249,8 +2243,8 @@ arcnet_rx(struct device *dev,int recbuf)
                break;
        case ARC_P_LANSOFT: /* don't understand.  fall through. */
        default:
-               printk("%6s: received unknown protocol %d (%Xh) from station %d.\n",
-                       dev->name,arcsoft[0],arcsoft[0],saddr);
+               BUGMSG(D_NORMAL,"received unknown protocol %d (%Xh) from station %d.\n",
+                       arcsoft[0],arcsoft[0],saddr);
                lp->stats.rx_errors++;
                lp->stats.rx_crc_errors++;
                break;
@@ -2324,7 +2318,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                        
                if (in->skb)    /* already assembling one! */
                {
-                       BUGMSG(D_EXTRA,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+                       BUGMSG(D_NORMAL,"aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
                                in->sequence,arcsoft->split_flag,
                                arcsoft->sequence);
                        kfree_skb(in->skb,FREE_WRITE);
@@ -2337,8 +2331,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
 
                skb = alloc_skb(length, GFP_ATOMIC);
                if (skb == NULL) {
-                       printk("%6s: Memory squeeze, dropping packet.\n",
-                               dev->name);
+                       BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
                        lp->stats.rx_dropped++;
                        return;
                }
@@ -2383,8 +2376,8 @@ arcnetA_rx(struct device *dev,u_char *buf,
                        }
                        else
                        {
-                               printk("%6s: funny-shaped ARP packet. (%Xh, %Xh)\n",
-                                       dev->name,arp->ar_hln,arp->ar_pln);
+                               BUGMSG(D_NORMAL,"funny-shaped ARP packet. (%Xh, %Xh)\n",
+                                       arp->ar_hln,arp->ar_pln);
                                lp->stats.rx_errors++;
                                lp->stats.rx_crc_errors++;
                        }
@@ -2393,7 +2386,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                BUGLVL(D_SKB)
                {
                    short i;
-                       for( i=0; i< skb->len; i++)
+                       for(i=0; i< skb->len; i++)
                                {
                                        if( i%16 == 0 ) printk("\n[%04hX] ",i);
                                        printk("%02hX ",((unsigned char*)skb->data)[i]);
@@ -2433,8 +2426,8 @@ arcnetA_rx(struct device *dev,u_char *buf,
 
                if (in->skb && in->sequence!=arcsoft->sequence)
                {
-                       BUGMSG(D_EXTRA,"wrong seq number, aborting assembly (expected=%d, seq=%d, splitflag=%d)\n",     
-                               in->sequence,arcsoft->sequence,
+                       BUGMSG(D_NORMAL,"wrong seq number, aborting assembly (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+                               saddr,in->sequence,arcsoft->sequence,
                                arcsoft->split_flag);
                        kfree_skb(in->skb,FREE_WRITE);
                        in->skb=NULL;
@@ -2449,7 +2442,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                                arcsoft->split_flag);
                        if (in->skb)    /* already assembling one! */
                        {
-                               BUGMSG(D_EXTRA,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
+                               BUGMSG(D_NORMAL,"aborting previous (seq=%d) assembly (splitflag=%d, seq=%d)\n",
                                        in->sequence,arcsoft->split_flag,
                                        arcsoft->sequence);
                                lp->stats.rx_errors++;
@@ -2463,7 +2456,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                        
                        if (in->numpackets>16)
                        {
-                               BUGMSG(D_EXTRA,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+                               BUGMSG(D_NORMAL,"incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
                                        arcsoft->split_flag);
                                lp->stats.rx_errors++;
                                lp->stats.rx_length_errors++;
@@ -2474,8 +2467,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                                        + sizeof(struct ClientData),
                                        GFP_ATOMIC);
                        if (skb == NULL) {
-                               printk("%6s: (split) memory squeeze, dropping packet.\n", 
-                                       dev->name);
+                               BUGMSG(D_NORMAL,"(split) memory squeeze, dropping packet.\n");
                                lp->stats.rx_dropped++;
                                return;
                        }
@@ -2504,7 +2496,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                         */                     
                        if (!in->skb)
                        {
-                               BUGMSG(D_EXTRA,"can't continue split without starting first! (splitflag=%d, seq=%d)\n",
+                               BUGMSG(D_NORMAL,"can't continue split without starting first! (splitflag=%d, seq=%d)\n",
                                        arcsoft->split_flag,arcsoft->sequence);
                                lp->stats.rx_errors++;
                                lp->stats.rx_missed_errors++;
@@ -2517,7 +2509,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                                /* harmless duplicate? ignore. */
                                if (packetnum<=in->lastpacket-1)
                                {
-                                       BUGMSG(D_EXTRA,"duplicate splitpacket ignored! (splitflag=%d)\n",
+                                       BUGMSG(D_NORMAL,"duplicate splitpacket ignored! (splitflag=%d)\n",
                                                arcsoft->split_flag);
                                        lp->stats.rx_errors++;
                                        lp->stats.rx_frame_errors++;
@@ -2525,7 +2517,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                                }
                                
                                /* "bad" duplicate, kill reassembly */
-                               BUGMSG(D_EXTRA,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",        
+                               BUGMSG(D_NORMAL,"out-of-order splitpacket, reassembly (seq=%d) aborted (splitflag=%d, seq=%d)\n",       
                                        in->sequence,arcsoft->split_flag,
                                        arcsoft->sequence);
                                kfree_skb(in->skb,FREE_WRITE);
@@ -2555,8 +2547,8 @@ arcnetA_rx(struct device *dev,u_char *buf,
                {
                        if (!skb || !in->skb)
                        {
-                               printk("%6s: ?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
-                                       dev->name,skb,in->skb);
+                               BUGMSG(D_NORMAL,"?!? done reassembling packet, no skb? (skb=%ph, in->skb=%ph)\n",
+                                       skb,in->skb);
                        }
                        else
                        {
@@ -2565,7 +2557,7 @@ arcnetA_rx(struct device *dev,u_char *buf,
                        
                                BUGLVL(D_SKB)
                                {
-                                   short i;
+                                       short i;
                                        for(i=0; i<skb->len; i++)
                                                {
                                                if( i%16 == 0 ) printk("\n[%04hX] ",i);
@@ -2600,7 +2592,7 @@ arcnetE_rx(struct device *dev,u_char *arcsoft,
 
                skb = alloc_skb(length, GFP_ATOMIC);
                if (skb == NULL) {
-                       printk("%6s: Memory squeeze, dropping packet.\n",dev->name);
+                       BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
                        lp->stats.rx_dropped++;
                        return;
                }
@@ -2653,7 +2645,7 @@ arcnetS_rx(struct device *dev,u_char *buf,
 
                skb = alloc_skb(length, GFP_ATOMIC);
                if (skb == NULL) {
-                       printk("arcnetS: Memory squeeze, dropping packet.\n");
+                       BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
                        lp->stats.rx_dropped++;
                        return;
                }
@@ -2780,8 +2772,8 @@ int arcnetA_header(struct sk_buff *skb,struct device *dev,
                head->protocol_id=ARC_P_ATALK;
                break;
        default:
-               printk("%6s: I don't understand protocol %d (%Xh)\n",
-                       dev->name,type,type);
+               BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+                       type,type);
                lp->stats.tx_errors++;
                lp->stats.tx_aborted_errors++;
                return 0;
@@ -2850,7 +2842,7 @@ int arcnetS_header(struct sk_buff *skb,struct device *dev,
                BUGMSG(D_DURING,"S_header: ARP_RFC1051 packet.\n");
                break;
        default:
-               printk("arcnetS: I don't understand protocol %d (%Xh)\n",
+               BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
                        type,type);
                lp->stats.tx_errors++;
                lp->stats.tx_aborted_errors++;
@@ -2901,8 +2893,8 @@ int arcnetA_rebuild_header(void *buff,struct device *dev,unsigned long dst,
         
        if(head->protocol_id != ARC_P_IP) 
        {
-               printk("%6s: I don't understand protocol type %d (%Xh) addresses!\n",
-                       dev->name,head->protocol_id,head->protocol_id);
+               BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+                       head->protocol_id,head->protocol_id);
                lp->stats.tx_errors++;
                lp->stats.tx_aborted_errors++;
                head->daddr=0;
@@ -2938,7 +2930,7 @@ int arcnetS_rebuild_header(void *buff,struct device *dev,unsigned long dst,
         
        if(head->protocol_id != ARC_P_IP_RFC1051) 
        {
-               printk("arcnetS: I don't understand protocol type %d (%Xh) addresses!\n",
+               BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
                        head->protocol_id,head->protocol_id);
                lp->stats.tx_errors++;
                lp->stats.tx_aborted_errors++;
@@ -2996,7 +2988,7 @@ unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
        case ARC_P_NOVELL_EC:
                return htons(ETH_P_802_3);
        default:
-               BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
+               BUGMSG(D_NORMAL,"received packet of unknown protocol id %d (%Xh)\n",
                                head->protocol_id,head->protocol_id);
                lp->stats.rx_errors++;
                lp->stats.rx_crc_errors++;
@@ -3037,8 +3029,8 @@ unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev)
        case ARC_P_ARP_RFC1051: return htons(ETH_P_ARP);
        case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
        default:
-               BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
-                               head->protocol_id,head->protocol_id);
+               BUGMSG(D_NORMAL,"received packet of unknown protocol id %d (%Xh)\n",
+                       head->protocol_id,head->protocol_id);
                lp->stats.rx_errors++;
                lp->stats.rx_crc_errors++;
                return 0;
index ac10e9041d8f014700c2c08be6b3dfcb16f8a4af..fb0f1de6f2adef1c694d15f98aa677ee0555c14d 100644 (file)
@@ -299,7 +299,12 @@ e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 
        mem_on(ioaddr, shared_mem, ring_page);
 
+#ifdef notdef
+       /* Officially this is what we are doing, but the readl() is faster */
        memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
+#else
+       ((unsigned int*)hdr)[0] = readl(shared_mem);
+#endif
 
        /* Turn off memory access: we would need to reprogram the window anyway. */
        mem_off(ioaddr);
index 921f20348f7e85fe3e6b2b99c6c24e8243a72c18..a1c151969afcb805d60190f4df1ca56f771c61dc 100644 (file)
@@ -34,6 +34,9 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/trdevice.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 
 /* The network devices currently exist only in the socket namespace, so these
    entries are unused.  The only ones that make sense are
@@ -293,6 +296,28 @@ void unregister_netdev(struct device *dev)
        /* else */
        if (dev->start)
                printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name);
+
+       /*
+        *      must jump over main_device+aliases
+        *      avoid alias devices unregistration so that only
+        *      net_alias module manages them
+        */
+#ifdef CONFIG_NET_ALIAS                
+       if (dev_base == dev)
+         dev_base = net_alias_nextdev(dev);
+       else
+       {
+           while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */
+                   d = net_alias_nextdev(d);
+         
+           if (d && (net_alias_nextdev(d) == dev))
+               {
+    /*
+        *      critical: bypass by consider devices as blocks (maindev+aliases)
+        */
+                   net_alias_nextdev_set(d, net_alias_nextdev(dev)); 
+               }
+#else
        if (dev_base == dev)
                dev_base = dev->next;
        else 
@@ -304,6 +329,7 @@ void unregister_netdev(struct device *dev)
                {
                        d->next = dev->next;
                }
+#endif
                else 
                {
                        printk("unregister_netdev: '%s' not found\n", dev->name);
index 20537baf4c84ac2ed815dc7e39b0e50f0d0a8d5c..f13cd0a78a3a1eb06b1749a3a1af98e8919adfbd 100644 (file)
@@ -270,7 +270,12 @@ ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
        unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
 
        outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);  /* shmem on */
+#ifdef notdef
+       /* Officially this is what we are doing, but the readl() is faster */
        memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+       ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
        outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
 }
 
index 84a6129bca1d9ff666c3597ece6be5fae0c979eb..5eaa6585bb66464f0716dcfbba54d6e8d9087294 100644 (file)
@@ -129,6 +129,13 @@ int wd_probe1(struct device *dev, int ioaddr)
                dev = init_etherdev(0, 0);
        }
 
+       /* Check for semi-valid mem_start/end values if supplied. */
+       if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+               printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+               dev->mem_start = 0;
+               dev->mem_end = 0;
+       }
+
        if (ei_debug  &&  version_printed++ == 0)
                printk(version);
 
@@ -246,7 +253,7 @@ int wd_probe1(struct device *dev, int ioaddr)
 
        /* Snarf the interrupt now.  There's no point in waiting since we cannot
           share and the board will usually be enabled. */
-       if (request_irq(dev->irq, ei_interrupt, 0, "wd")) {
+       if (request_irq(dev->irq, ei_interrupt, 0, model_name)) {
                printk (" unable to get IRQ %d.\n", dev->irq);
                return EAGAIN;
        }
@@ -259,7 +266,7 @@ int wd_probe1(struct device *dev, int ioaddr)
        }
 
        /* OK, were are certain this is going to work.  Setup the device. */
-       request_region(ioaddr, WD_IO_EXTENT,"wd");
+       request_region(ioaddr, WD_IO_EXTENT, model_name);
 
        ei_status.name = model_name;
        ei_status.word16 = word16;
@@ -352,7 +359,12 @@ wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
        if (ei_status.word16)
                outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
 
+#ifdef notdef
+       /* Officially this is what we are doing, but the readl() is faster */
        memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+       ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
 }
 
 /* Block input and output are easy on shared memory ethercards, and trivial
index 1e21f104f1c19279ccdbc392672e8c46179e5c20..1876721d2a92612bf0610237be890dfa69f963d0 100644 (file)
@@ -918,7 +918,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
 
       ptr->host_scribble = NULL;
       ptr->result = DID_ABORT << 16;
-      ptr->done(ptr);
+      ptr->scsi_done(ptr);
       return SCSI_ABORT_SUCCESS;
     }
 
@@ -942,7 +942,7 @@ int aha152x_abort( Scsi_Cmnd *SCpnt)
     /* target entered bus free before COMMAND COMPLETE, nothing to abort */
     restore_flags(flags);
     current_SC->result = DID_ERROR << 16;
-    current_SC->done(current_SC);
+    current_SC->scsi_done(current_SC);
     current_SC = (Scsi_Cmnd *) NULL;
     return SCSI_ABORT_SUCCESS;
   }
@@ -1063,7 +1063,7 @@ int aha152x_reset(Scsi_Cmnd * __unused)
         {
           current_SC->host_scribble = NULL;
           current_SC->result = DID_RESET << 16;
-          current_SC->done(current_SC);
+          current_SC->scsi_done(current_SC);
           current_SC=NULL;
         }
 
@@ -1083,7 +1083,7 @@ int aha152x_reset(Scsi_Cmnd * __unused)
   
               ptr->host_scribble = NULL;
               ptr->result        = DID_RESET << 16;
-              ptr->done(ptr);
+              ptr->scsi_done(ptr);
   
               ptr = next; 
             }
index 3b7b4eceb637d9f02acd9b19e053460c8ec621cc..65b1112140a472c75f8a783140b3551cad5bdae0 100644 (file)
@@ -23,6 +23,8 @@
 #define CONST_XSENSE    0x08
 #define CONST_CMND      0x10
 #define CONST_MSG       0x20
+#define CONST_HOST     0x40
+#define CONST_DRIVER   0x80
 
 static const char unknown[] = "UNKNOWN";
 
@@ -30,7 +32,8 @@ static const char unknown[] = "UNKNOWN";
 #ifdef CONSTANTS
 #undef CONSTANTS
 #endif
-#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE | CONST_CMND | CONST_MSG)
+#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE \
+                  | CONST_CMND | CONST_MSG | CONST_HOST | CONST_DRIVER)
 #endif
 
 #if (CONSTANTS & CONST_COMMAND)
@@ -568,6 +571,64 @@ void print_Scsi_Cmnd (Scsi_Cmnd *cmd) {
     print_command (cmd->cmnd);
 }
 
+#if (CONSTANTS & CONST_HOST)
+static const char * hostbyte_table[]={
+"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET", 
+"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",NULL};
+
+void print_hostbyte(int scsiresult)
+{   static int maxcode=0;
+    int i;
+   
+    if(!maxcode) {
+       for(i=0;hostbyte_table[i];i++) ;
+       maxcode=i-1;
+    }
+    printk("Hostbyte=0x%02x",host_byte(scsiresult));
+    if(host_byte(scsiresult)>maxcode) {
+       printk("is invalid "); 
+       return;
+    }
+    printk("(%s) ",hostbyte_table[host_byte(scsiresult)]);
+}
+#else
+void print_hostbyte(int scsiresult)
+{   printk("Hostbyte=0x%02x ",hostbyte(scsiresult));
+}
+#endif
+
+#if (CONSTANTS & CONST_DRIVER)
+static const char * driverbyte_table[]={
+"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT",  "DRIVER_MEDIA", "DRIVER_ERROR", 
+"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",NULL };
+
+static const char * driversuggest_table[]={"SUGGEST_OK",
+"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
+unknown,unknown,unknown, "SUGGEST_SENSE",NULL};
+
+
+void print_driverbyte(int scsiresult)
+{   static int driver_max=0,suggest_max=0;
+    int i,dr=driver_byte(scsiresult)&DRIVER_MASK, 
+       su=(driver_byte(scsiresult)&SUGGEST_MASK)>>4;
+
+    if(!driver_max) {
+        for(i=0;driverbyte_table[i];i++) ;
+        driver_max=i;
+       for(i=0;driversuggest_table[i];i++) ;
+       suggest_max=i;
+    }
+    printk("Driverbyte=0x%02x",driver_byte(scsiresult));
+    printk("(%s,%s) ",
+       dr<driver_max  ? driverbyte_table[dr]:"invalid",
+       su<suggest_max ? driversuggest_table[su]:"invalid");
+}
+#else
+void print_driverbyte(int scsiresult)
+{   printk("Driverbyte=0x%02x ",driver_byte(scsiresult));
+}
+#endif
+
 /*
  * Overrides for Emacs so that we almost follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index de48287ab964925d7ae833c2d7fe1b5c99ce0f19..d566d59ceae0e024cb5481c624392034d347ff7b 100644 (file)
@@ -65,6 +65,7 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
 static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
                  Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
                  struct Scsi_Host *shpnt, char * scsi_result);
+void scsi_build_commandblocks(Scsi_Device * SDpnt);
 
 
 static unsigned char * dma_malloc_freelist = NULL;
@@ -272,18 +273,6 @@ static int get_device_flags(unsigned char * response_data){
     return 0;
 }
 
-/*
- *  As the actual SCSI command runs in the background, we must set up a
- *  flag that tells scan_scsis() when the result it has is valid.
- *  scan_scsis can set the_result to -1, and watch for it to become the
- *  actual return code for that call.  the scan_scsis_done function() is
- *  our user specified completion function that is passed on to the
- *  scsi_do_cmd() function.
- */
-
-volatile int in_scan_scsis = 0;
-static int the_result;
-
 void scsi_make_blocked_list(void)  {
     int block_count = 0, index;
     unsigned int flags;
@@ -385,9 +374,10 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
   int max_dev_lun;
   Scsi_Cmnd *SCpnt;
 
-  in_scan_scsis++;
   SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
   SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+  memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+
 
   /* Make sure we have something that is valid for DMA purposes */
   scsi_result = ((!dma_malloc_freelist || !shpnt->unchecked_isa_dma)
@@ -398,9 +388,17 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
     goto leave;
   }
 
-  shpnt->host_queue = SCpnt;    /* We need this so that commands can time out */
+  /* We must chain ourself in the host_queue, so commands can time out */
+  if(shpnt->host_queue)
+      shpnt->host_queue->prev = SCpnt;
+  SCpnt->next = shpnt->host_queue;
+  SCpnt->prev = NULL;
+  shpnt->host_queue = SCpnt;
+
 
   if (hardcoded == 1) {
+    Scsi_Device *oldSDpnt=SDpnt;
+    struct Scsi_Device_Template * sdtpnt;
     channel = hchannel;
     if(channel > shpnt->max_channel) goto leave;
     dev = hid;
@@ -409,11 +407,31 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
     if(lun >= shpnt->max_lun) goto leave;
     scan_scsis_single (channel, dev, lun, &max_dev_lun,
                    &SDpnt, SCpnt, shpnt, scsi_result);
+    if(SDpnt!=oldSDpnt) {
+
+       /* it could happen the blockdevice hasn't yet been inited */
+    for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+        if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+            oldSDpnt->scsi_request_fn = NULL;
+            for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+                if(sdtpnt->attach) {
+                 (*sdtpnt->attach)(oldSDpnt);
+                  if(oldSDpnt->attached) scsi_build_commandblocks(oldSDpnt);}
+           resize_dma_pool();
+  
+        for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+            if(sdtpnt->finish && sdtpnt->nr_dev)
+                {(*sdtpnt->finish)();}
+       }
+    }
+
   }
   else {
     for (channel = 0; channel <= shpnt->max_channel; channel++) {
       for (dev = 0; dev < shpnt->max_id; ++dev) {
         if (shpnt->this_id != dev) {
+
           /*
            * We need the for so our continue, etc. work fine. We put this in
            * a variable so that we can override it during the scan if we
@@ -421,20 +439,32 @@ static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
            */
           max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
                          max_scsi_luns : shpnt->max_lun);
-
           for (lun = 0; lun < max_dev_lun; ++lun) {
-
             if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
                                     &SDpnt, SCpnt, shpnt, scsi_result))
-              break; /* break means don't probe for luns!=0 */
+              break; /* break means don't probe further for luns!=0 */
           }                     /* for lun ends */
         }                       /* if this_id != id ends */
       }                         /* for dev ends */
     }                           /* for channel ends */
-
-leave:
-    shpnt->host_queue = NULL;   /* No longer needed here */
-
+  }                            /* if/else hardcoded */
+
+  leave:
+
+  {/* Unchain SCpnt from host_queue */
+    Scsi_Cmnd *prev,*next,*hqptr;
+    for(hqptr=shpnt->host_queue; hqptr!=SCpnt; hqptr=hqptr->next) ;
+    if(hqptr) {
+      prev=hqptr->prev;
+      next=hqptr->next;
+      if(prev) 
+       prev->next=next;
+      else 
+       shpnt->host_queue=next;
+      if(next) next->prev=prev;
+    }
+  }
      /* Last device block does not exist.  Free memory. */
     if (SDpnt != NULL)
       scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
@@ -446,14 +476,12 @@ leave:
     if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
       scsi_free (scsi_result, 512);
 
-    in_scan_scsis--;
-  }
 }
 
 /*
  * The worker for scan_scsis.
  * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
- * Global variables used : scsi_devices(linked list), the_result
+ * Global variables used : scsi_devices(linked list)
  */
 int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
     Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt, struct Scsi_Host * shpnt, 
@@ -490,7 +518,6 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
   scsi_cmd[1] = lun << 5;
   scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
 
-  memset (SCpnt, 0, sizeof (Scsi_Cmnd));
   SCpnt->host = SDpnt->host;
   SCpnt->device = SDpnt;
   SCpnt->target = SDpnt->id;
@@ -507,8 +534,10 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
   }
 
 #if defined(DEBUG) || defined(DEBUG_INIT)
-  printk ("scsi: scan_scsis_single id %d lun %d. Return code %d\n",
+  printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
           dev, lun, SCpnt->result);
+  print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result);
+  printk("\n");
 #endif
 
   if (SCpnt->result) {
@@ -546,13 +575,12 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
     down (&sem);
   }
 
-  the_result = SCpnt->result;
 #if defined(DEBUG) || defined(DEBUG_INIT)
   printk ("scsi: INQUIRY %s with code 0x%x\n",
-          the_result ? "failed" : "successful", the->result);
+          SCpnt->result ? "failed" : "successful", SCpnt->result);
 #endif
 
-  if (the_result)
+  if (SCpnt->result)
     return 0;     /* assume no peripheral if any sort of error */
 
   /*
@@ -696,7 +724,7 @@ int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
   SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
   *SDpnt2=SDpnt;
   if (!SDpnt)
-    printk ("scsi: scan_scsis_single: No memory\n");
+    printk ("scsi: scan_scsis_single: Cannot malloc\n");
 
 
   /*
@@ -750,7 +778,7 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid)
     switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
     {
     case NORMAL_TIMEOUT:
-       if (!in_scan_scsis) {
+       {
 #ifdef DEBUG_TIMEOUT
            scsi_dump_status();
 #endif
@@ -1460,7 +1488,7 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
                        printk ("Internal error %s %d \n", __FILE__,
                                __LINE__);
                    }
-               }
+               } /* end WAS_SENSE */
                else
                {
 #ifdef DEBUG
@@ -1734,8 +1762,7 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
            SCpnt->internal_timeout |= IN_ABORT;
            oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
            
-           if ((SCpnt->flags & IS_RESETTING) &&
-               SCpnt->device->soft_reset) {
+           if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
                /* OK, this command must have died when we did the
                 *  reset.  The device itself must have lied. 
                 */
@@ -1797,6 +1824,8 @@ int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
                /* We should have already aborted this one.  No
                 * need to adjust timeout 
                 */
+                 SCpnt->internal_timeout &= ~IN_ABORT;
+                 return 0;
            case SCSI_ABORT_NOT_RUNNING:
                SCpnt->internal_timeout &= ~IN_ABORT;
                update_timeout(SCpnt, 0);
@@ -1837,10 +1866,8 @@ int scsi_reset (Scsi_Cmnd * SCpnt, int bus_reset_flag)
     Scsi_Cmnd * SCpnt1;
     struct Scsi_Host * host = SCpnt->host;
 
-#ifdef DEBUG
-    printk("Danger Will Robinson! - SCSI bus for host %d is being reset.\n",
+    printk("SCSI bus is being reset for host %d.\n",
           host->host_no);
-#endif
  
     /*
      * First of all, we need to make a recommendation to the low-level
@@ -1862,10 +1889,8 @@ int scsi_reset (Scsi_Cmnd * SCpnt, int bus_reset_flag)
     SCpnt1 = host->host_queue;
     while(SCpnt1) {
        if( SCpnt1->request.rq_status != RQ_INACTIVE
-          && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
-       {
-            break;
-       }
+           && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
+               break;
         SCpnt1 = SCpnt1->next;
        }
     if( SCpnt1 == NULL ) {
@@ -2154,7 +2179,7 @@ int scsi_free(void *obj, unsigned int len)
     unsigned long flags;
     
 #ifdef DEBUG
-    printk("Sfree %p %d\n",obj, len);
+    printk("scsi_free %p %d\n",obj, len);
 #endif
     
     offset = -1;
@@ -2167,20 +2192,20 @@ int scsi_free(void *obj, unsigned int len)
            break;
        }
     
-    if (page == (dma_sectors >> 3)) panic("Bad offset");
+    if (page == (dma_sectors >> 3)) panic("scsi_free:Bad offset");
     sector = offset >> 9;
-    if(sector >= dma_sectors) panic ("Bad page");
+    if(sector >= dma_sectors) panic ("scsi_free:Bad page");
     
     sector = (offset >> 9) & (sizeof(*dma_malloc_freelist) * 8 - 1);
     nbits = len >> 9;
     mask = (1 << nbits) - 1;
     
-    if ((mask << sector) > 0xffff) panic ("Bad memory alignment");
+    if ((mask << sector) > 0xffff) panic ("scsi_free:Bad memory alignment");
     
     save_flags(flags);
     cli();
     if((dma_malloc_freelist[page] & (mask << sector)) != (mask<<sector))
-       panic("Trying to free unused memory");
+       panic("scsi_free:Trying to free unused memory");
     
     dma_free_sectors += nbits;
     dma_malloc_freelist[page] &= ~(mask << sector);
@@ -2438,8 +2463,13 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
     /*
      * Usage: echo "scsi singledevice 0 1 2 3" >/proc/scsi/scsi
      * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
-     * Consider this feature ALPHA, as you can easily hang your
-     * scsi system (depending on your low level driver).
+     * Consider this feature BETA.
+     *     CAUTION: This is not for hotplugging your peripherals. As
+     *     SCSI was not designed for this you could damage your
+     *     hardware !  
+     * However perhaps it is legal to switch on an
+     * already connected device. It is perhaps not 
+     * guaranteed this device doesn't corrupt an ongoing data transfer.
      */
     if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
        return(-EINVAL);
index e6a532bc154e3b9bcb8b1bc6a3b4a6fd8c490a7c..4289b228a50e21982cbf44a2b8b4e71145093321 100644 (file)
@@ -89,6 +89,7 @@ extern const unsigned char scsi_command_size[8];
 #define DRIVER_INVALID      0x05
 #define DRIVER_TIMEOUT      0x06
 #define DRIVER_HARD         0x07
+#define DRIVER_SENSE       0x08
 
 #define SUGGEST_RETRY       0x10
 #define SUGGEST_ABORT       0x20 
@@ -97,8 +98,6 @@ extern const unsigned char scsi_command_size[8];
 #define SUGGEST_SENSE       0x80
 #define SUGGEST_IS_OK       0xff
 
-#define DRIVER_SENSE        0x08
-
 #define DRIVER_MASK         0x0f
 #define SUGGEST_MASK        0xf0
 
@@ -448,6 +447,8 @@ extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
 
 extern void print_command(unsigned char *);
 extern void print_sense(const char *, Scsi_Cmnd *);
+extern void print_driverbyte(int scsiresult);
+extern void print_hostbyte(int scsiresult);
 
 extern void scsi_mark_host_bus_reset(struct Scsi_Host *Host);
 
index 30045df8c135fa2e966996217cd96a3f438c61db..db0a0b808d63f05ce5768eb6b7415cc4c69e8054 100644 (file)
@@ -59,7 +59,6 @@ struct symbol_table scsi_symbol_table = {
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
     X(request_queueable),
-    X(in_scan_scsis),
 #if defined(CONFIG_PROC_FS)
     X(proc_print_scsidevice),
 #endif
index dcf246ed1e0c3699890717dfe8ce5117ef55a67a..47bf2263700e87fc1b16df8d758d2e1e1b2a98cc 100644 (file)
@@ -1304,12 +1304,14 @@ static void sd_finish()
        if (!rscsi_disks[i].capacity && 
            rscsi_disks[i].device)
        {
-           i = sd_init_onedisk(i);
            if (MODULE_FLAG
                && !rscsi_disks[i].has_part_table) {
                sd_sizes[i << 4] = rscsi_disks[i].capacity;
+               /* revalidate does sd_init_onedisk via MAYBE_REINIT*/
                revalidate_scsidisk(MKDEV(MAJOR_NR, i << 4), 0);
            }
+           else
+               i=sd_init_onedisk(i);
            rscsi_disks[i].has_part_table = 1;
        }
     
index 20d14bd074d0bb0a877ce1570810137110756c4a..cd3f06120092a6ccf7ee8e468d840c5bf10cdbd5 100644 (file)
@@ -297,7 +297,8 @@ int mem_mmap(struct inode * inode, struct file * file,
                dtmp += PAGE_SIZE;
        }
 
-       invalidate();
+       invalidate_range(vma->vm_mm, vma->vm_start, vma->vm_end);
+       invalidate_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end);
        return 0;
 }
 
index 9893a76b3d8ae845fc56ba4e12cb95e34243bcd0..46cbb98f91d7cd268ea2042af1dc364cb578898c 100644 (file)
@@ -91,13 +91,13 @@ struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_na
        lptr->mnt_sem.count = 1;
        if (dev_name && !getname(dev_name, &tmp)) {
                if ((lptr->mnt_devname =
-                   (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+                   (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
                        strcpy(lptr->mnt_devname, tmp);
                putname(tmp);
        }
        if (dir_name && !getname(dir_name, &tmp)) {
                if ((lptr->mnt_dirname =
-                   (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+                   (char *) kmalloc(strlen(tmp)+1, GFP_KERNEL)) != (char *)NULL)
                        strcpy(lptr->mnt_dirname, tmp);
                putname(tmp);
        }
index f1bd7899e8243c014f635f2f45d7c3238531385d..597c7bd104b03eb869f47c8f9214580c55da14b7 100644 (file)
  *  - invalidate_page(mm, vmaddr) invalidates one page
  *  - invalidate_range(mm, start, end) invalidates a range of pages
  *
- * ..but the i386 has somewhat limited invalidation capabilities.
+ * ..but the i386 has somewhat limited invalidation capabilities,
+ * and page-granular invalidates are available only on i486 and up.
  */
+
+#define __invalidate() \
+__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+
+#ifdef __i486__
+#define __invalidate_one(addr) \
+__asm__ __volatile__("invlpg %0": :"m" (*(char *) addr))
+#else
+#define __invalidate_one(addr) invalidate()
+#endif
  
 #ifndef __SMP__
-#define invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+
+#define invalidate() __invalidate()
+#define invalidate_all() __invalidate()
+
+static inline void invalidate_mm(struct mm_struct *mm)
+{
+       if (mm == current->mm)
+               __invalidate();
+}
+
+static inline void invalidate_page(struct mm_struct *mm,
+       unsigned long addr)
+{
+       if (mm == current->mm)
+               __invalidate_one(addr);
+}
+
+static inline void invalidate_range(struct mm_struct *mm,
+       unsigned long start, unsigned long end)
+{
+       if (mm == current->mm)
+               __invalidate();
+}
+
 #else
+
+/*
+ * We aren't very clever about this yet -  SMP could certainly
+ * avoid some global invalidates..
+ */
+
 #include <asm/smp.h>
+
 #define local_invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+       __invalidate()
+
 #define invalidate() \
-       smp_invalidate();
-#endif
+       smp_invalidate()
 
-/*
- * We aren't very clever about this yet. On a 486+ we could actually do
- * page-granularity invalidates for better performance in some cases.
- * And SMP could certainly avoid some global invalidates..
- */
 #define invalidate_all() invalidate()
-#define invalidate_mm(mm_struct) \
-do { if ((mm_struct) == current->mm) invalidate(); } while (0)
-#define invalidate_page(mm_struct,addr) \
-do { if ((mm_struct) == current->mm) invalidate(); } while (0)
-#define invalidate_range(mm_struct,start,end) \
-do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+
+static inline void invalidate_mm(struct mm_struct *mm)
+{
+       invalidate();
+}
+
+static inline void invalidate_page(struct mm_struct *mm,
+       unsigned long addr)
+{
+       invalidate();
+}
+
+static inline void invalidate_range(struct mm_struct *mm,
+       unsigned long start, unsigned long end)
+{
+       invalidate();
+}
+
+#endif
+
 
 /* Certain architectures need to do special things when pte's
  * within a page table are directly modified.  Thus, the following
diff --git a/include/linux/net_alias.h b/include/linux/net_alias.h
new file mode 100644 (file)
index 0000000..d8fba09
--- /dev/null
@@ -0,0 +1,176 @@
+#ifndef _NET_ALIAS_H
+#define _NET_ALIAS_H
+
+#include <linux/types.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/in.h>          /* for default IP behavior */
+
+
+/*
+ * max. alias slot number allowed 
+ */
+
+#define NET_ALIAS_MAX_SLOT  256
+
+struct net_alias;
+struct net_alias_info;
+struct net_alias_type;
+
+
+/*
+ * main alias structure
+ * note that *defines* dev & devname
+ */
+
+struct net_alias
+{
+  struct device dev;           /* alias device defn*/
+  char name[IFNAMSIZ];         /* device name defn */
+  unsigned hash;               /* my hash value: for quick rehash */
+  unsigned slot;               /* slot number */
+  void *data;                  /* private data */
+  struct device *main_dev;     /* pointer to main device */
+  struct net_alias_type *nat;  /* alias type bound */
+  struct net_alias *next;      /* next alias (hashed linked list) */
+};
+
+
+/*
+ *  alias structure pointed by main device
+ *  it holds main device's alias hash table
+ */
+
+struct net_alias_info
+{
+  int n_aliases;               /* num aliases */
+  struct device *taildev;      /* my last (alias) device */
+  struct net_alias *hash_tab[16]; /* hashed alias table */
+};
+
+/*
+ * net_alias_type class
+ * declares a generic (AF_ independent) structure that will
+ * manage generic to family-specific behavior.
+ */
+
+struct net_alias_type
+{
+  int type;                    /* aliasing type: address family */
+  int n_attach;                        /* number of aliases attached */
+  char name[16];               /* af_name */
+  __u32 (*get_addr32)          /* get __u32 addr 'representation'*/
+    (struct sockaddr*);        
+  int (*addr_chk)              /* address checking func: */
+    (struct device *, struct sockaddr *);
+  int (*alias_init_1)          /* called after alias creation: */
+    (struct net_alias *alias, struct sockaddr *sa);
+  int (*alias_done_1)          /* called before alias deletion */
+    (struct net_alias *alias);
+  int (*alias_print_1) 
+    (char *buf, int len, struct net_alias *alias);
+  struct net_alias_type *next; /* link */
+};
+
+
+/*
+ * is dev an alias?
+ */
+
+static __inline__ int
+net_alias_is(struct device *dev)
+{
+  return (dev->my_alias != 0);
+}
+
+
+/*
+ * does dev have aliases?
+ */
+
+static __inline__ int
+net_alias_has(struct device *dev)
+{
+  return (dev->alias_info != 0);
+}
+
+
+extern void net_alias_init(void);
+
+extern struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data);
+extern int net_alias_rehash(struct net_alias *alias, struct sockaddr *sa);
+
+extern int net_alias_getinfo(char *buf, char **, off_t , int , int );
+extern int net_alias_types_getinfo(char *buf, char **, off_t , int , int );
+
+extern int register_net_alias_type(struct net_alias_type *nat, int type);
+extern int unregister_net_alias_type(struct net_alias_type *nat);
+
+extern struct device * net_alias_chk(struct device *dev, struct sockaddr *sa, int flags_1, int flags_0);
+extern struct device * net_alias_chk32(struct device *dev, int family, __u32 addr32, int flags_1, int flags_0);
+
+
+/*
+ * returns MY 'true' main device
+ * intended for alias devices
+ */
+
+static __inline__ struct device *net_alias_main_dev(struct device *dev)
+{
+  return (net_alias_is(dev))? dev->my_alias->main_dev : dev;
+}
+
+
+/*
+ * returns NEXT 'true' device
+ * intended for true devices
+ */
+
+static __inline__ struct device *
+net_alias_nextdev(struct device *dev)
+{
+  return (dev->alias_info)? dev->alias_info->taildev->next : dev->next;
+}
+
+
+/*
+ * sets NEXT 'true' device
+ * intended for main devices (treat main device as block: dev+aliases).
+ */
+
+static __inline__ struct device *
+net_alias_nextdev_set(struct device *dev, struct device *nextdev)
+{
+  struct device *pdev = dev;
+  if (net_alias_has(dev))
+  {
+    pdev = dev->alias_info->taildev; /* point to last dev alias */
+  }
+  pdev->next = nextdev;
+  return nextdev;
+}
+
+
+/*
+ * addr_chk wrapper: check given generic address with (UP) aliases
+ */
+
+static __inline__ struct device *
+net_alias_addr_chk(struct device *dev, struct sockaddr *sa)
+{
+  return net_alias_chk(dev, sa, IFF_UP, 0);
+}
+
+
+/*
+ * addr_chk32 wrapper: check given u32 address with (UP) aliases
+ */
+
+static __inline__ struct device *
+net_alias_addr_chk32(struct device *dev, int family, __u32 addr32)
+{
+  return net_alias_chk32(dev, family, addr32, IFF_UP, 0);
+}
+
+#endif  /* _NET_ALIAS_H */
index 766d02059d30b8a8dbdab1d94fe4916aa6e75dc5..b771d960ce9d805489188a056b9e2169ce6a2b52 100644 (file)
@@ -152,6 +152,8 @@ struct device
   
   unsigned long                   pkt_queue;   /* Packets queued */
   struct device                  *slave;       /* Slave device */
+  struct net_alias_info                *alias_info;    /* main dev alias info */
+  struct net_alias             *my_alias;      /* alias devs */
   
 
   /* Pointer to the interface buffers. */
index 0888e39da5d747025db085eae686d54631fa65bd..73a31bf83993c2f50ba661a5937080451ab4b725 100644 (file)
@@ -89,6 +89,8 @@ enum net_directory_inos {
        PROC_NET_SOCKSTAT,
        PROC_NET_RTCACHE,
        PROC_NET_AX25_BPQETHER,
+       PROC_NET_ALIAS_TYPES,
+       PROC_NET_ALIASES,
        PROC_NET_LAST
 };
 
index e6f62ea8d566cb47784814a56f8c5a5f8922a4f2..f3bffbd7f3780679d2140c67abb1140af29ea1f4 100644 (file)
@@ -44,8 +44,8 @@ struct rlimit {
        long    rlim_max;
 };
 
-#define        PRIO_MIN        (-99)
-#define        PRIO_MAX        14
+#define        PRIO_MIN        (-20)
+#define        PRIO_MAX        20
 
 #define        PRIO_PROCESS    0
 #define        PRIO_PGRP       1
index 9e12b351e77397d5051755356b384a4ff081a895..768a6b80475a2408a8d946258c255f31f03f06a2 100644 (file)
@@ -48,5 +48,7 @@ struct vt_consize {
        ushort v_ccol;          /* number of pixel columns per character */
 };
 #define VT_RESIZEX      0x560A  /* set kernel's idea of screensize + more */
+#define VT_LOCKSWITCH   0x560B  /* disallow vt switching */
+#define VT_UNLOCKSWITCH 0x560C  /* allow vt switching */
 
 #endif /* _LINUX_VT_H */
diff --git a/include/net/ip_alias.h b/include/net/ip_alias.h
new file mode 100644 (file)
index 0000000..e2590a0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _IP_ALIAS_H
+#define _IP_ALIAS_H
+
+/* 
+ * IP alias specific prototypes
+ */
+
+#include <linux/net_alias.h>
+
+extern int ip_alias_init(void);
+extern int ip_alias_done(void);
+
+#endif  /* _IP_ALIAS_H */
index d8406e6559608a8883c58ea9d7e8172103801557..19af4736d05242a1bb2f73052554055b8dd95e3b 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -445,7 +445,7 @@ static int shm_map (struct vm_area_struct *shmd)
                        return -ENOMEM;
                set_pte(page_table, __pte(shm_sgn));
        }
-       invalidate();
+       invalidate_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
        return 0;
 }
 
@@ -680,7 +680,7 @@ int shm_swap (int prio, unsigned long limit)
        struct vm_area_struct *shmd;
        unsigned long swap_nr;
        unsigned long id, idx;
-       int loop = 0, invalid = 0;
+       int loop = 0;
        int counter;
        
        counter = shm_rss >> prio;
@@ -716,8 +716,6 @@ int shm_swap (int prio, unsigned long limit)
 
        if (--counter < 0) { /* failed */
                failed:
-               if (invalid)
-                       invalidate();
                swap_free (swap_nr);
                return 0;
        }
@@ -766,7 +764,7 @@ int shm_swap (int prio, unsigned long limit)
                mem_map[MAP_NR(pte_page(pte))].count--;
                if (shmd->vm_mm->rss > 0)
                        shmd->vm_mm->rss--;
-               invalid++;
+               invalidate_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end);
            /* continue looping through circular list */
            } while (0);
            if ((shmd = shmd->vm_next_share) == shp->attaches)
@@ -776,8 +774,6 @@ int shm_swap (int prio, unsigned long limit)
        if (mem_map[MAP_NR(pte_page(page))].count != 1)
                goto check_table;
        shp->shm_pages[idx] = swap_nr;
-       if (invalid)
-               invalidate();
        write_swap_page (swap_nr, (char *) pte_page(page));
        free_page(pte_page(page));
        swap_successes++;
index 5e6b6a764c63593da354bb3063e9e45fb4cbe106..0e9e96e99defbbb8c6098a79e638be47e948215e 100644 (file)
@@ -76,6 +76,9 @@ extern void __remqu (void);
 #include "../drivers/net/slhc.h"
 #endif
 #endif
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 #endif
 #ifdef CONFIG_PCI
 #include <linux/bios32.h>
@@ -412,6 +415,10 @@ struct symbol_table symbol_table = {
        /* Device callback registration */
        X(register_netdevice_notifier),
        X(unregister_netdevice_notifier),
+#ifdef CONFIG_NET_ALIAS
+       X(register_net_alias_type),
+       X(unregister_net_alias_type),
+#endif
 #endif
 
        /* support for loadable net drivers */
@@ -452,11 +459,7 @@ struct symbol_table symbol_table = {
         * So we add it here too.  There is a duplicate set in scsi.c
         * that is used when the entire scsi subsystem is a loadable
         * module.
-        * 
-        * in_scan_scsis is a hack, and should go away once the new 
-        * memory allocation code is in the NCR driver 
         */
-       X(in_scan_scsis),
        X(scsi_register_module),
        X(scsi_unregister_module),
        X(scsi_free),
index 89e5d36fc78c852fd12f2c37f71702b7d078f90a..9c66cc5fd479dcc807a9a532d0988419e5ca6f29 100644 (file)
@@ -141,7 +141,8 @@ int filemap_swapout(struct vm_area_struct * vma,
        unsigned long entry = SWP_ENTRY(SHM_SWP_TYPE, MAP_NR(page));
 
        set_pte(page_table, __pte(entry));
-       invalidate();
+       /* Yuck, perhaps a slightly modified swapout parameter set? */
+       invalidate_page(vma->vm_mm, (offset + vma->vm_start - vma->vm_offset));
        error = filemap_write_page(vma, offset, page);
        if (pte_val(*page_table) == entry)
                pte_clear(page_table);
@@ -179,13 +180,14 @@ static inline int filemap_sync_pte(pte_t * ptep, struct vm_area_struct *vma,
                if (!pte_dirty(pte))
                        return 0;
                set_pte(ptep, pte_mkclean(pte));
+               invalidate_page(vma->vm_mm, address);
                page = pte_page(pte);
                mem_map[MAP_NR(page)].count++;
        } else {
                if (pte_none(pte))
                        return 0;
                pte_clear(ptep);
-               invalidate();
+               invalidate_page(vma->vm_mm, address);
                if (!pte_present(pte)) {
                        swap_free(pte_val(pte));
                        return 0;
@@ -274,7 +276,7 @@ static int filemap_sync(struct vm_area_struct * vma, unsigned long address,
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_range(vma->vm_mm, end - size, end);
        return error;
 }
 
index 8d94adcfdd219ec9be699476b616a701b82b2f69..45e14adf8a683fc4e482bd18613aa0ee5129c05e 100644 (file)
@@ -134,7 +134,7 @@ void clear_page_tables(struct task_struct * tsk)
        }
        for (i = 0 ; i < USER_PTRS_PER_PGD ; i++)
                free_one_pgd(page_dir + i);
-       invalidate();
+       invalidate_mm(tsk->mm);
 }
 
 /*
@@ -153,12 +153,12 @@ void free_page_tables(struct task_struct * tsk)
                printk("%s trying to free kernel page-directory: not good\n", tsk->comm);
                return;
        }
+       invalidate_mm(tsk->mm);
        SET_PAGE_DIR(tsk, swapper_pg_dir);
        tsk->mm->pgd = swapper_pg_dir;  /* or else... */
        for (i = 0 ; i < PTRS_PER_PGD ; i++)
                free_one_pgd(page_dir + i);
        pgd_free(page_dir);
-       invalidate();
 }
 
 int new_page_tables(struct task_struct * tsk)
@@ -171,6 +171,7 @@ int new_page_tables(struct task_struct * tsk)
        page_dir = pgd_offset(&init_mm, 0);
        for (i = USER_PTRS_PER_PGD ; i < PTRS_PER_PGD ; i++)
                new_pg[i] = page_dir[i];
+       invalidate_mm(tsk->mm);
        SET_PAGE_DIR(tsk, new_pg);
        tsk->mm->pgd = new_pg;
        return 0;
@@ -285,7 +286,9 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src,
                        break;
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
        }
-       invalidate();
+       /* Note that the src ptes get c-o-w treatment, so they change too. */
+       invalidate_range(src, vma->vm_start, vma->vm_end);
+       invalidate_range(dst, vma->vm_start, vma->vm_end);
        return error;
 }
 
@@ -369,7 +372,7 @@ int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long si
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_range(mm, end - size, end);
        return 0;
 }
 
@@ -429,7 +432,7 @@ int zeromap_page_range(unsigned long address, unsigned long size, pgprot_t prot)
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_range(current->mm, end - size, end);
        return error;
 }
 
@@ -499,7 +502,7 @@ int remap_page_range(unsigned long from, unsigned long offset, unsigned long siz
                from = (from + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_range(current->mm, from - size, from);
        return error;
 }
 
@@ -514,7 +517,7 @@ static void put_page(pte_t * page_table, pte_t pte)
                return;
        }
 /* no need for invalidate */
-       *page_table = pte;
+       set_pte(page_table, pte);
 }
 
 /*
@@ -547,7 +550,7 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
        if (!pte_none(*pte)) {
                printk("put_dirty_page: page already exists\n");
                pte_clear(pte);
-               invalidate();
+               invalidate_page(tsk->mm, address);
        }
        set_pte(pte, pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY))));
 /* no need for invalidate */
@@ -610,17 +613,17 @@ void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma,
                        copy_page(old_page,new_page);
                        set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
                        free_page(old_page);
-                       invalidate();
+                       invalidate_page(vma->vm_mm, address);
                        return;
                }
                set_pte(page_table, BAD_PAGE);
                free_page(old_page);
                oom(tsk);
-               invalidate();
+               invalidate_page(vma->vm_mm, address);
                return;
        }
        set_pte(page_table, pte_mkdirty(pte_mkwrite(pte)));
-       invalidate();
+       invalidate_page(vma->vm_mm, address);
        if (new_page)
                free_page(new_page);
        return;
@@ -842,7 +845,7 @@ static int try_to_share(unsigned long to_address, struct vm_area_struct * to_are
                return 1;
 /* ok, need to mark it read-only, so invalidate any possible old TB entry */
        set_pte(from_table, pte_wrprotect(from));
-       invalidate();
+       invalidate_page(from_area->vm_mm, from_address);
        return 1;
 }
 
index 420d3daba14ea593d5954968a8293a42662c8ed7..41dcb3d0e5142184d8286876801606336a06279a 100644 (file)
@@ -72,6 +72,7 @@ static inline void change_pmd_range(pgd_t * pgd, unsigned long address,
 static void change_protection(unsigned long start, unsigned long end, pgprot_t newprot)
 {
        pgd_t *dir;
+       unsigned long beg = start;
 
        dir = pgd_offset(current->mm, start);
        while (start < end) {
@@ -79,7 +80,7 @@ static void change_protection(unsigned long start, unsigned long end, pgprot_t n
                start = (start + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_range(current->mm, beg, end);
        return;
 }
 
index 0c437c6b3af72281cfc4874f2c1765c9f08e18ce..b13d748d326f5161a08c58d9f32b57da2fa70be5 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -457,7 +457,7 @@ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
                                return 0;
                        vma->vm_mm->rss--;
                        set_pte(page_table, __pte(entry));
-                       invalidate();
+                       invalidate_page(vma->vm_mm, address);
                        tsk->nswap++;
                        write_swap_page(entry, (char *) page);
                }
@@ -472,13 +472,13 @@ static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struc
                }
                vma->vm_mm->rss--;
                set_pte(page_table, __pte(entry));
-               invalidate();
+               invalidate_page(vma->vm_mm, address);
                free_page(page);
                return 1;
        } 
        vma->vm_mm->rss--;
        pte_clear(page_table);
-       invalidate();
+       invalidate_page(vma->vm_mm, address);
        entry = mem_map[MAP_NR(page)].count;
        free_page(page);
        return entry;
index 3ae6d882303bf4e3d893c5a43c98bbc33e473929..3f15f9e05eb0a18b7b2c848745f3dd90bd1312e7 100644 (file)
@@ -105,7 +105,7 @@ static void free_area_pages(unsigned long address, unsigned long size)
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_all();
 }
 
 static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size)
@@ -166,7 +166,7 @@ static int alloc_area_pages(unsigned long address, unsigned long size)
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_all();
        return 0;
 }
 
@@ -227,7 +227,7 @@ static int remap_area_pages(unsigned long address, unsigned long offset, unsigne
                address = (address + PGDIR_SIZE) & PGDIR_MASK;
                dir++;
        }
-       invalidate();
+       invalidate_all();
        return 0;
 }
 
index 14abef40c3ffc6f9b096f4f918996aa3df709449..59ec5aca61d42f30696a0255c5d5e37253e9d6f1 100644 (file)
@@ -4,6 +4,7 @@
 mainmenu_option next_comment
 comment 'Networking options'
 bool 'Network firewalls' CONFIG_FIREWALL
+bool 'Network aliasing'  CONFIG_NET_ALIAS
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
   source net/ipv4/Config.in
index be1b33ae4053da4cd2ebd7f40914f198c2c17e1f..502d135a0a392490dc445bc284d626c6f201ade6 100644 (file)
@@ -19,6 +19,10 @@ ifdef CONFIG_FIREWALL
 O_OBJS += firewall.o
 endif
 
+ifdef CONFIG_NET_ALIAS
+O_OBJS += net_alias.o
+endif
+
 endif
 
 include $(TOPDIR)/Rules.make
index 3c5c670b2132ead44df3f13f1dd19ab4f59872c8..f1bbb2c94af12a35997144c63e0a8ed4161691c6 100644 (file)
@@ -70,6 +70,9 @@
 #include <net/arp.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 
 /*
  *     The list of packet types we will receive (as opposed to discard)
@@ -356,6 +359,19 @@ void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
                return;
        }
 
+       /*
+        *
+        *      If dev is an alias, switch to its main device.
+        *      "arp" resolution has been made with alias device, so
+        *      arp entries refer to alias, not main.
+        *
+        */
+
+#ifdef CONFIG_NET_ALIAS
+       if (net_alias_is(dev))
+               skb->dev = dev = net_alias_main_dev(dev);
+#endif
+
        save_flags(flags);
        cli();  
        if (!where)             /* Always keep order. It helps other hosts
@@ -561,7 +577,7 @@ void dev_transmit(void)
 {
        struct device *dev;
 
-       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       for (dev = dev_base; dev != NULL; dev = dev->next)
        {
                if (dev->flags != 0 && !dev->tbusy) {
                        /*
@@ -753,6 +769,13 @@ void dev_tint(struct device *dev)
        struct sk_buff *skb;
        unsigned long flags;
        
+       /*
+        * aliases do not trasmit (by now :)
+        */
+
+#ifdef CONFIG_NET_ALIAS
+       if (net_alias_is(dev)) return;
+#endif
        save_flags(flags);      
        /*
         *      Work the queues in priority order
@@ -984,9 +1007,20 @@ static int dev_ifsioc(void *arg, unsigned int getset)
         *      See which interface the caller is talking about. 
         */
         
+       /*
+        *
+        *      net_alias_dev_get(): dev_get() with added alias naming magic.
+        *      only allow alias creation/deletion if (getset==SIOCSIFADDR)
+        *
+        */
+
+#ifdef CONFIG_NET_ALIAS
+       if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+               return(err);
+#else
        if ((dev = dev_get(ifr.ifr_name)) == NULL) 
                return(-ENODEV);
-
+#endif
        switch(getset) 
        {
                case SIOCGIFFLAGS:      /* Get interface flags */
@@ -1079,6 +1113,16 @@ static int dev_ifsioc(void *arg, unsigned int getset)
                        }
                        else
                        {
+
+                               /*
+                                *      if dev is an alias, must rehash to update
+                                *      address change
+                                */
+
+#ifdef CONFIG_NET_ALIAS
+                               if (net_alias_is(dev))
+                               net_alias_rehash(dev->my_alias,&ifr.ifr_addr);
+#endif
                                dev->pa_addr = (*(struct sockaddr_in *)
                                         &ifr.ifr_addr).sin_addr.s_addr;
                                dev->family = ifr.ifr_addr.sa_family;
@@ -1382,6 +1426,18 @@ int net_dev_init(void)
                dev_get_info
        });
 
+       /*      
+        *      Initialise net_alias engine 
+        *
+        *              - register net_alias device notifier
+        *              - register proc entries:        /proc/net/alias_types
+        *                                                                      /proc/net/aliases
+        */
+
+#ifdef CONFIG_NET_ALIAS
+       net_alias_init();
+#endif
+
        bh_base[NET_BH].routine = net_bh;
        enable_bh(NET_BH);
        return 0;
diff --git a/net/core/net_alias.c b/net/core/net_alias.c
new file mode 100644 (file)
index 0000000..e423d34
--- /dev/null
@@ -0,0 +1,1164 @@
+/*
+ *             NET_ALIAS device aliasing module.
+ *
+ * Version:    @(#)net_alias.c 0.42   12/11/95
+ *
+ * Authors:    Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *             Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
+ *
+ * Features:
+ *     -       AF_ independent: net_alias_type objects
+ *     -       AF_INET optimized
+ *     -       ACTUAL alias devices inserted in dev chain
+ *     -       fast hashed alias address lookup
+ *     -       net_alias_type objs registration/unreg., module-ables.
+ *     -       /proc/net/aliases & /proc/net/alias_types entries
+ *
+ * FIXME:
+ *     - User calls sleep/wake_up locking.
+ *     - Define a way to select the "best" alias device for an incoming
+ *       packet to allow xxx_rcv() device switching based ALSO on pkt's
+ *       src address (this would require a routing query).
+ *       Related stuff:
+ *             IP: Test routing between aliases (possible ICMP redirects).
+ *             IP: ARP proxy entries attached to aliases are not visible.
+ *
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *     
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/notifier.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+
+#ifdef ALIAS_USER_LAND_DEBUG
+#include "net_alias.h"
+#include "user_stubs.h"
+#endif
+
+#include <linux/net_alias.h>
+
+/*
+ * Only allow the following flags to pass from main device to aliases
+ */
+
+#define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+
+static struct net_alias_type * nat_getbytype(int type);
+static int nat_attach_chg(struct net_alias_type *nat, int delta);
+static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa);
+static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias);
+
+
+static int net_alias_devinit(struct device *dev);
+static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
+static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
+static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias);
+static struct device *net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data);
+static struct device *net_alias_dev_delete(struct device *main_dev, int slot, int *err);
+static void net_alias_free(struct device *dev);
+                                          
+/*
+ * net_alias_type base array, will hold net_alias_type objects.
+ */
+
+struct net_alias_type *nat_base[16];
+
+
+/*
+ * get net_alias_type ptr by type
+ */
+
+static __inline__ struct net_alias_type *
+nat_getbytype(int type)
+{
+  struct net_alias_type *nat;
+  for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+  {
+    if (nat->type == type) return nat;
+  }
+  return NULL;
+}
+
+
+/*
+ * get addr32 representation (pre-hashing) of address.
+ * if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
+ */
+
+static __inline__ __u32
+nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
+{
+  if (nat->get_addr32)
+    return nat->get_addr32(sa);
+  else
+    return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
+}
+
+
+/*
+ * hashing code for alias_info->hash_tab entries
+ * 4 bytes -> 1/2 byte using xor condimented by af
+ */
+
+static __inline__ unsigned
+HASH(__u32 addr, int af)
+{
+  unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
+  tmp ^= (tmp>>8);                  /* 2 -> 1 */
+  return (tmp^(tmp>>4)^af) & 0x0f;         /* 1 -> 1/2 */
+}
+
+
+/*
+ * get hash key for supplied net alias type and address
+ * nat must be !NULL
+ * the purpose here is to map an net_alias_type and a generic
+ * address to a hash code.
+ */
+
+static __inline__ int
+nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+{
+  return HASH(nat_addr32(nat,sa), sa->sa_family);
+}
+
+
+/*
+ * change net_alias_type number of attachments (bindings)
+ */
+
+static int
+nat_attach_chg(struct net_alias_type *nat, int delta)
+{
+  unsigned long flags;
+  int n_at;
+  if (!nat) return -1;
+  save_flags(flags);
+  cli();
+  n_at = nat->n_attach + delta;
+  if (n_at < 0)
+  {
+    restore_flags(flags);
+    printk("net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
+          nat->type);
+    return -1;
+  }
+  nat->n_attach = n_at;
+  restore_flags(flags);
+  return 0;
+}
+
+
+/*
+ * bind alias to its type (family) object and call initialization hook
+ */
+
+static __inline__ int
+nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa)
+{
+  if (nat->alias_init_1) nat->alias_init_1(alias, sa);
+  return nat_attach_chg(nat, +1);
+}
+
+
+/*
+ * unbind alias from type object and call 'done' hook
+ */
+
+static __inline__ int
+nat_unbind(struct net_alias_type *nat, struct net_alias *alias)
+{
+  if (nat->alias_done_1) nat->alias_done_1(alias);
+  return nat_attach_chg(nat, -1);
+}
+
+
+/*
+ * compare device address with given. if NULL nat->addr_chk,
+ * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
+ */
+
+static __inline__ int nat_addr_chk(struct net_alias_type *nat,
+                                  struct device *dev, struct sockaddr *sa)
+{
+  if (nat->addr_chk)
+    return nat->addr_chk(dev, sa);
+  else
+    return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
+}
+
+
+/*
+ * alias device init()
+ * do nothing.
+ */
+
+static int 
+net_alias_devinit(struct device *dev)
+{
+#ifdef ALIAS_USER_LAND_DEBUG
+  printk("net_alias_devinit(%s) called.\n", dev->name);
+#endif
+  return 0;
+}
+
+
+/*
+ * hard_start_xmit() should not be called.
+ * ignore ... but shout!.
+ */
+
+static int
+net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+  printk("net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
+  dev_kfree_skb(skb, FREE_WRITE);
+  return 0;
+}
+
+
+/*
+ * setups a new (alias) device 
+ */
+
+static int
+net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat,
+               struct sockaddr *sa)
+{
+  struct device *main_dev;
+  struct device *dev;
+  int family;
+  int i;
+
+  /*
+   *
+   * generic device setup based on main_dev info
+   *
+   * FIXME: is NULL bitwise 0 for all Linux platforms?
+   */
+  
+  main_dev = alias->main_dev;
+  dev = &alias->dev;
+  memset(dev, '\0', sizeof(struct device));
+  family = (sa)? sa->sa_family : main_dev->family;
+
+  dev->alias_info = NULL;      /* no aliasing recursion */
+  dev->my_alias = alias;       /* point to alias */
+  dev->name = alias->name;
+  dev->type = main_dev->type;
+  dev->hard_header_len = main_dev->hard_header_len;
+  memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
+  memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
+  dev->addr_len = main_dev->addr_len;
+  dev->init = net_alias_devinit;
+  dev->hard_start_xmit = net_alias_hard_start_xmit;
+  dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
+
+  /*
+   * only makes sense if same family
+   */
+  
+  if (family == main_dev->family)
+  {
+    dev->metric = main_dev->metric;
+    dev->mtu = main_dev->mtu;
+    dev->pa_alen = main_dev->pa_alen;
+    dev->hard_header = main_dev->hard_header;
+    dev->rebuild_header = main_dev->rebuild_header;
+  }
+  
+  /*
+   *   Fill in the generic fields of the device structure.
+   *    not actually used, avoids some dev.c #ifdef's
+   */
+  
+  for (i = 0; i < DEV_NUMBUFFS; i++)
+    skb_queue_head_init(&dev->buffs[i]);
+  
+  dev->family = family;
+  return 0;
+}
+
+
+/*
+ * slow alias find (parse the whole hash_tab)
+ * returns: alias' pointer address
+ */
+
+static struct net_alias **
+net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias)
+{
+  unsigned idx, n_aliases;
+  struct net_alias **aliasp;
+
+  /*
+   * for each alias_info's hash_tab entry, for every alias ...
+   */
+  
+  n_aliases = alias_info->n_aliases;
+  for (idx=0; idx < 16 ; idx++)
+    for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
+      if (*aliasp == alias)
+       return aliasp;
+      else
+       if (--n_aliases == 0) break; /* faster give up */
+  return NULL;
+}
+
+
+/*
+ * create alias device for main_dev with given slot num.
+ * if sa==NULL will create a same_family alias device 
+ */
+
+static struct device *
+net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data)
+{
+  struct net_alias_info *alias_info;
+  struct net_alias *alias, **aliasp;
+  struct net_alias_type *nat;
+  struct device *dev;
+  unsigned long flags;
+  int family;
+  __u32 addr32;
+  
+  /* FIXME: lock */
+  alias_info = main_dev->alias_info;
+
+  /*
+   * if NULL address given, take family from main_dev
+   */
+  
+  family = (sa)? sa->sa_family : main_dev->family;
+  
+  /*
+   * check if wanted family has a net_alias_type object registered
+   */
+  
+  nat = nat_getbytype(family);
+  if (!nat)
+  {
+    printk("net_alias_dev_create(%s:%d): unregistered family==%d\n",
+          main_dev->name, slot, family);
+    /* *err = -EAFNOSUPPORT; */
+    *err = -EINVAL;
+    return NULL;
+  }
+  
+  /*
+   * do not allow creation over downed devices
+   */
+
+  *err = -EIO;
+  
+  if (! (main_dev->flags & IFF_UP) )
+    return NULL;
+  
+  /*
+   * if first alias, must also create alias_info
+   */
+      
+  *err = -ENOMEM;
+
+  if (!alias_info)
+  { 
+    alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
+    if (!alias_info) return NULL; /* ENOMEM */
+    memset(alias_info, 0, sizeof(struct net_alias_info));
+  }
+  
+  if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
+    return NULL;               /* ENOMEM */
+
+  /*
+   * FIXME: is NULL bitwise 0 for all Linux platforms?
+   */
+  
+  memset(alias, 0, sizeof(struct net_alias));
+  alias->slot = slot;
+  alias->main_dev = main_dev;
+  alias->nat = nat;
+  alias->next = NULL;
+  alias->data = data;
+  sprintf(alias->name, "%s:%d", main_dev->name, slot);
+  
+  /*
+   * initialise alias' device structure
+   */
+  
+  net_alias_devsetup(alias, nat, sa);
+
+  dev = &alias->dev;
+  
+  save_flags(flags);
+  cli();
+
+  /*
+   * bind alias to its object type
+   * nat_bind calls nat->alias_init_1
+   */
+  
+  nat_bind(nat, alias, sa);
+
+  /*
+   * if no address passed, take from device (could have been
+   * set by nat->alias_init_1)
+   */
+  
+  addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
+  
+  /*
+   * store hash key in alias: will speed-up rehashing and deletion
+   */
+  
+  alias->hash = HASH(addr32, family);
+
+  /*
+   * insert alias in hashed linked list
+   */
+  
+  aliasp = &alias_info->hash_tab[alias->hash];
+  alias->next = *aliasp;       
+  *aliasp = alias;
+
+  /*
+   * if first alias ...
+   */
+  
+  if (!alias_info->n_aliases++)
+  {
+    alias_info->taildev = main_dev;
+    main_dev->alias_info = alias_info;
+  }
+  
+  /*
+   * add device at tail (just after last main_dev alias)
+   */
+  
+  dev->next = alias_info->taildev->next;
+  alias_info->taildev->next = dev;
+  alias_info->taildev = dev;
+  restore_flags(flags);
+  return dev;
+}
+
+
+/*
+ * delete one main_dev alias (referred by its slot num)
+ */
+
+static struct device *
+net_alias_dev_delete(struct device *main_dev, int slot, int *err)
+{
+  struct net_alias_info *alias_info;
+  struct net_alias *alias, **aliasp;
+  struct device *dev;
+  unsigned n_aliases;
+  unsigned long flags;
+  struct net_alias_type *nat;
+  struct device *prevdev;
+  
+  /* FIXME: lock */
+  *err = -ENODEV;
+  
+  if (main_dev == NULL) return NULL;
+
+  /*
+   * does main_dev have aliases?
+   */
+  
+  alias_info = main_dev->alias_info;
+  if (!alias_info) return NULL;        /* ENODEV */
+  
+  n_aliases = alias_info->n_aliases;
+  
+  /*
+   * find device that holds the same slot number (could also
+   * be strcmp() ala dev_get).
+   */
+  
+  for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next)
+  {
+    if (!(alias = prevdev->next->my_alias))
+    {
+      printk("ERROR: net_alias_dev_delete(): incorrect non-alias device after maindev\n");
+      continue;                        /* or should give up? */
+    }
+    if (alias->slot == slot) break;
+    alias = NULL;
+    n_aliases--;
+  }
+  
+  if (!alias) return NULL;     /* ENODEV */
+  
+  dev = &alias->dev;
+  
+  /*
+   * find alias hashed entry
+   */
+  
+  for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
+    if(*aliasp == alias) break;
+
+  /*
+   * if not found (???), try a full search
+   */
+  
+  if (*aliasp != alias)
+    if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+      printk("net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
+    else
+    {
+      printk("ERROR: net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
+      return NULL;             /* ENODEV */
+    }
+  
+  nat = alias->nat;
+
+  save_flags(flags);
+  cli();
+  
+  /*
+   * unbind alias from alias_type obj.
+   */
+  
+  nat_unbind(nat, alias);
+  
+  /*
+   * is alias at tail?
+   */
+  
+  if ( dev == alias_info->taildev )
+    alias_info->taildev = prevdev;
+  
+  /*
+   * unlink and close device
+   */
+  prevdev->next = dev->next;
+  dev_close(dev);
+  
+  /*
+   * unlink alias
+   */
+  
+  *aliasp = (*aliasp)->next;
+
+  if (--alias_info->n_aliases == 0) /* last alias */
+    main_dev->alias_info = NULL;
+  restore_flags(flags);
+
+  /*
+   * now free structures
+   */
+  
+  kfree_s(alias, sizeof(struct net_alias));
+  if (main_dev->alias_info == NULL)
+    kfree_s(alias_info, sizeof(struct net_alias_info));
+
+  /*
+   * deletion ok (*err=0), NULL device returned.
+   */
+  
+  *err = 0;
+  return NULL;
+}
+
+
+/*
+ * dev_get() with added alias naming magic.
+ */
+
+struct device *
+net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
+                 struct sockaddr *sa, void *data)
+{
+  struct device *dev;
+  char *sptr,*eptr;
+  int slot = 0;
+  int delete = 0;
+  
+  *err = -ENODEV;
+  if ((dev=dev_get(dev_name)))
+    return dev;
+
+  /*
+   * want alias naming magic?
+   */
+  
+  if (!aliasing_ok) return NULL;
+
+  if (!dev_name || !*dev_name)
+    return NULL;
+  
+  /*
+   * find the first ':' , must be followed by, at least, 1 char
+   */
+  
+  for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break;
+  if (!*sptr || !*(sptr+1))
+    return NULL;
+  
+  /*
+   * seems to be an alias name, fetch main device
+   */
+  
+  *sptr='\0';
+  if (!(dev=dev_get(dev_name)))
+    return NULL;
+  *sptr++=':';
+  
+  /*
+   * fetch slot number
+   */
+  
+  slot = simple_strtoul(sptr,&eptr,10);
+  if (slot >= NET_ALIAS_MAX_SLOT)
+    return NULL;
+
+  /*
+   * if last char is '-', it is a deletion request
+   */
+  
+  if (eptr[0] == '-' && !eptr[1] ) delete++;
+  else if (eptr[0])
+    return NULL;
+  
+  /*
+   * well... let's work.
+   */
+  
+  if (delete)
+    return net_alias_dev_delete(dev, slot, err);
+  else
+    return net_alias_dev_create(dev, slot, err, sa, data);
+}
+
+
+/*
+ * rehash alias with address supplied. 
+ */
+
+int
+net_alias_rehash(struct net_alias *alias, struct sockaddr *sa)
+{
+  struct net_alias_info *alias_info;
+  struct net_alias **aliasp;
+  struct device *dev;
+  unsigned long flags;
+  struct net_alias_type *o_nat, *n_nat;
+  unsigned n_hash;
+  
+  /*
+   * defensive ...
+   */
+  
+  if (!sa)
+  {
+    printk("ERROR: net_alias_rehash(): NULL sockaddr passed\n");
+    return -1;
+  }
+
+  /*
+   * defensive. should not happen.
+   */
+  
+  if (!(dev = alias->main_dev))
+  {
+    printk("ERROR: net_alias_rehash for %s: NULL maindev\n", alias->name);
+    return -1;
+  }
+
+  /*
+   * defensive. should not happen.
+   */
+
+  if (!(alias_info=dev->alias_info))
+  {
+    printk("ERROR: net_alias_rehash for %s: NULL alias_info\n", alias->name);
+    return -1;
+  }
+  
+  /*
+   * will the request also change device family?
+   */
+  
+  o_nat = alias->nat;
+  if (!o_nat)
+  {
+    printk("ERROR: net_alias_rehash(%s): unbound alias.\n", alias->name);
+    return -1;
+  }
+
+  /*
+   * point to new alias_type obj.
+   */
+  
+  if (o_nat->type == sa->sa_family)
+    n_nat = o_nat;
+  else
+  {
+    n_nat = nat_getbytype(sa->sa_family);
+    if (!n_nat)
+    {
+      printk("ERROR: net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
+      return -1;
+    }
+  }
+  
+  /*
+   * new hash key. if same as old AND same type (family) return;
+   */
+  
+  n_hash = nat_hash_key(n_nat, sa);
+  if (n_hash == alias->hash && o_nat == n_nat )
+    return 0;
+
+  /*
+   * find alias in hashed list
+   */
+  
+  for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
+    if (*aliasp == alias) break;
+  
+  /*
+   * not found (???). try a full search
+   */
+  
+  if(!*aliasp)
+    if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+      printk("net_alias_rehash(%s): bad hashing recovered\n", alias->name);
+    else
+    {
+      printk("ERROR: net_alias_rehash(%s): unhashed alias!\n", alias->name);
+      return -1;
+    }
+  
+  save_flags(flags);
+  cli();
+  
+  /*
+   * if type (family) changed unlink from old type object (o_nat)
+   * will call o_nat->alias_done_1()
+   */
+  
+  if (o_nat != n_nat)
+    nat_unbind(o_nat, alias);
+
+  /*
+   * if diff hash key, change alias position in hashed list
+   */
+  
+  if (n_hash != alias->hash)
+  {
+    *aliasp = (*aliasp)->next;
+    alias->hash = n_hash;
+    aliasp = &alias_info->hash_tab[n_hash];
+    alias->next = *aliasp;
+    *aliasp = alias;
+  }
+  
+  /*
+   * if type (family) changed link to new type object (n_nat)
+   * will call n_nat->alias_init_1()
+   */
+  
+  if (o_nat != n_nat)
+    nat_bind(n_nat, alias, sa);
+  
+  restore_flags(flags);
+  return 0;
+}
+
+
+/*
+ * free all main device aliasing stuff
+ * will be called on dev_close(main_dev)
+ */
+
+static void
+net_alias_free(struct device *main_dev)
+{
+  struct net_alias_info *alias_info;
+  struct net_alias *alias;
+  struct net_alias_type *nat;
+  struct device *dev;
+  unsigned long flags;
+
+  /*
+   * do I really have aliases?
+   */
+  
+  if (!(alias_info = main_dev->alias_info))    return;
+
+  /*
+   * fast device link "short-circuit": set main_dev->next to
+   * device after last alias
+   */
+  
+  save_flags(flags);
+  cli();
+  
+  dev =  main_dev->next;
+  main_dev->next = alias_info->taildev->next;
+  main_dev->alias_info = NULL;
+  alias_info->taildev->next = NULL;
+  
+  restore_flags(flags);
+
+  /*
+   * loop over alias devices, free and dev_close()
+   */
+  
+  while (dev)
+  {
+    if (net_alias_is(dev))
+    {
+      alias = dev->my_alias;
+      if (alias->main_dev == main_dev)
+      {
+       /*
+        * unbind alias from alias_type object
+        */
+       
+       nat = alias->nat;
+       if (nat)
+       {
+         nat_unbind(nat, alias);
+       } /*  else error/printk ??? */
+       
+       dev_close(dev);
+       dev = dev->next;
+       
+       kfree_s(alias, sizeof(struct net_alias));
+       continue;
+      }
+      else
+       printk("net_alias_free(%s): '%s' is not my alias\n",
+              main_dev->name, alias->name);
+    }
+    else
+      printk("net_alias_free(%s): found a non-alias after device!\n",
+            main_dev->name);
+    dev = dev->next;
+  }
+  
+  kfree_s(alias_info, sizeof(alias_info));
+  return;
+}
+
+
+/*
+ *  implements /proc/net/alias_types entry
+ *  shows net_alias_type objects registered.
+ */
+
+int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+  off_t pos=0, begin=0;
+  int len=0;
+  struct net_alias_type *nat;
+  unsigned idx;
+  len=sprintf(buffer,"type    name            n_attach\n");
+  for (idx=0 ; idx < 16 ; idx++)
+    for (nat = nat_base[idx]; nat ; nat = nat->next)
+    {
+      len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
+                    nat->type, nat->name,nat->n_attach);
+      pos=begin+len;
+      if(pos<offset)
+      {
+       len=0;
+       begin=pos;
+      }
+      if(pos>offset+length)
+       break;
+    }
+  *start=buffer+(offset-begin);
+  len-=(offset-begin);
+  if(len>length)
+    len=length;        
+  return len;
+}
+
+
+/*
+ *  implements /proc/net/aliases entry, shows alias devices.
+ *   calls alias nat->alias_print_1 if not NULL and formats everything
+ *   to a fixed rec. size without using local (stack) buffers
+ *
+ */
+
+#define NAT_REC_SIZE 64
+int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+  off_t pos=0, begin=0;
+  int len=0;
+  int dlen;
+  struct net_alias_type *nat;
+  struct net_alias *alias;
+  struct device *dev;
+
+  len=sprintf(buffer,"%-*s\n",NAT_REC_SIZE-1,"device           family address");
+  for (dev = dev_base; dev ; dev = dev->next)
+    if (net_alias_is(dev))
+    {
+      alias = dev->my_alias;
+      nat = alias->nat;
+      dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
+      
+      /*
+       * call alias_type specific print function.
+       */
+      
+      if (nat->alias_print_1)
+       dlen += nat->alias_print_1(buffer+len+dlen, NAT_REC_SIZE - dlen, alias);
+      else
+       dlen += sprintf(buffer+len+dlen, "-");
+
+      /*
+       * fill with spaces if needed 
+       */
+      
+      if (dlen < NAT_REC_SIZE) memset(buffer+len+dlen, ' ', NAT_REC_SIZE - dlen);
+      /*
+       * truncate to NAT_REC_SIZE
+       */
+      
+      len += NAT_REC_SIZE;
+      buffer[len-1] = '\n';
+      
+      pos=begin+len;
+      if(pos<offset)
+      {
+       len=0;
+       begin=pos;
+      }
+      if(pos>offset+length)
+       break;
+    }
+  *start=buffer+(offset-begin);
+  len-=(offset-begin);
+  if(len>length)
+    len=length;        
+  return len;
+}
+
+
+/*
+ * notifier for devices events
+ */
+
+int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+  struct device *dev = ptr;
+
+  if (event == NETDEV_DOWN)
+  {
+#ifdef ALIAS_USER_LAND_DEBUG
+    printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
+#endif
+    if (net_alias_has(dev))
+      net_alias_free(dev);
+  }
+
+  if (event == NETDEV_UP)
+  {
+#ifdef ALIAS_USER_LAND_DEBUG
+    printk("net_alias: NETDEV_UP for %s received\n", dev->name);
+#endif
+    dev->alias_info = 0;
+  }
+
+  return NOTIFY_DONE;
+}
+
+
+/*
+ * returns alias device with specified address AND flags_1 on AND flags_0 off.
+ *   intended for main devices.
+ *   typically called on xxx_rcv() to check if packet's dest address is one
+ *   of main_dev's alias address.
+ */
+
+struct device *
+net_alias_chk(struct device *dev, struct sockaddr *sa,int flags_1, int flags_0)
+{
+  struct net_alias_info *alias_info = dev->alias_info;
+  struct net_alias_type *nat;
+  struct net_alias *alias;
+  
+  if (!alias_info) return NULL;        /* has aliases? */
+  
+  /*
+   * get alias_type object for sa->sa_family.
+   */
+  
+  nat = nat_getbytype(sa->sa_family);
+  if (!nat)
+    return 0;
+  
+  for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+      alias; alias = alias->next)
+  {
+    if (alias->dev.family != sa->sa_family) continue;
+
+    /*
+     * nat_addr_chk will call type specific address cmp function.
+     */
+    
+    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
+       nat_addr_chk(nat,&alias->dev,sa))
+      return &alias->dev;
+  }
+  return NULL;
+}
+
+/*
+ * addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
+ */
+
+struct device *
+net_alias_chk32(struct device *dev, int family, __u32 addr32,
+               int flags_1, int flags_0)
+{
+  struct net_alias_info *alias_info = dev->alias_info;
+  struct net_alias *alias;
+  
+  if (!alias_info) return NULL;        /* has aliases? */
+  
+  for (alias=alias_info->hash_tab[HASH(addr32,family)];
+       alias; alias=alias->next)
+  {
+    if (alias->dev.family != family) continue;
+    
+    /*
+     * "hard" (static) comparison between addr32 and pa_addr.
+     */
+    
+    if (alias->dev.flags & flags_1 && !(alias->dev.flags & flags_0) &&
+       addr32 == alias->dev.pa_addr)
+      return &alias->dev;
+  }
+  return NULL;
+}
+
+
+/*
+ * device event hook
+ */
+
+static struct notifier_block net_alias_dev_notifier = {
+  net_alias_device_event,
+  NULL,
+  0
+};
+
+
+/*
+ * net_alias initialisation
+ * called from net_dev_init().
+ */
+
+void net_alias_init(void)
+{
+
+  /*
+   * register dev events notifier
+   */
+  
+  register_netdevice_notifier(&net_alias_dev_notifier);
+
+  /*
+   * register /proc/net entries
+   */
+  
+#ifndef ALIAS_USER_LAND_DEBUG
+  proc_net_register(&(struct proc_dir_entry) {
+    PROC_NET_ALIAS_TYPES, 11, "alias_types",
+    S_IFREG | S_IRUGO, 1, 0, 0,
+    0, &proc_net_inode_operations,
+    net_alias_types_getinfo
+  });
+  proc_net_register(&(struct proc_dir_entry) {
+    PROC_NET_ALIASES, 7, "aliases",
+    S_IFREG | S_IRUGO, 1, 0, 0,
+    0, &proc_net_inode_operations,
+    net_alias_getinfo
+  });
+#endif
+  
+}
+
+/*
+ * net_alias type object registering func.
+ */
+int register_net_alias_type(struct net_alias_type *nat, int type)
+{
+  unsigned hash;
+  unsigned long flags;
+  if (!nat)
+  {
+    printk("register_net_alias_type(): NULL arg\n");
+    return -EINVAL;
+  }
+  nat->type = type;
+  nat->n_attach = 0;
+  hash = nat->type & 0x0f;
+  save_flags(flags);
+  cli();
+  nat->next = nat_base[hash];
+  nat_base[hash] = nat;
+  restore_flags(flags);
+  return 0;
+}
+
+/*
+ * net_alias type object unreg.
+ */
+int unregister_net_alias_type(struct net_alias_type *nat)
+{
+  struct net_alias_type **natp;
+  unsigned hash;
+  unsigned long flags;
+  
+  if (!nat)
+  {
+    printk("unregister_net_alias_type(): NULL arg\n");
+    return -EINVAL;
+  }
+
+  /*
+   * only allow unregistration if it has no attachments
+   */
+  if (nat->n_attach)
+  {
+    printk("unregister_net_alias_type(): has %d attachments. failed\n",
+          nat->n_attach);
+    return -EINVAL;
+  }
+  hash = nat->type & 0x0f;
+  save_flags(flags);
+  cli();
+  for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+  {
+    if (nat==(*natp))
+    {
+      *natp = nat->next;
+      restore_flags(flags);
+      return 0;
+    }
+  }
+  restore_flags(flags);
+  printk("unregister_net_alias_type(type=%d): not found!\n", nat->type);
+  return -EINVAL;
+}
index d9930d0e467a39fdd2da2c2bb281b46a1f4aba23..93b2f0284d3ba342dfc35cb98c600ba2b2549b08 100644 (file)
@@ -17,6 +17,9 @@ if [ "$CONFIG_IP_FORWARD" = "y" ]; then
     bool 'IP: multicast routing(in progress)' CONFIG_IP_MROUTE
   fi
 fi
+if [ "$CONFIG_NET_ALIAS" = "y" ]; then
+       tristate 'IP: aliasing support' CONFIG_IP_ALIAS
+fi
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
index 8f9816e99369374fa3723e02bc7b6b4114a4a41e..9266741496906552c3974b8f33a94333b81e392d 100644 (file)
@@ -36,6 +36,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_IP_ALIAS),y)
+IPV4_OBJS += ip_alias.o
+else
+  ifeq ($(CONFIG_IP_ALIAS),m)
+  M_OBJS += ip_alias.o
+  endif
+endif
+
 ifdef CONFIG_INET
 O_OBJS := $(IPV4_OBJS)
 endif
index c021360d76283911f216be4bef2ad2c1d2b6dfd7..dad8d0bf1f653efd539a20e000a7ec2f62d41637 100644 (file)
@@ -88,6 +88,9 @@
 #include <net/raw.h>
 #include <net/icmp.h>
 #include <linux/ip_fw.h>
+#ifdef CONFIG_IP_ALIAS
+#include <net/ip_alias.h>
+#endif
 
 #define min(a,b)       ((a)<(b)?(a):(b))
 
@@ -1530,6 +1533,13 @@ void inet_proto_init(struct net_proto *pro)
        ip_mr_init();
 #endif
 
+       /*
+        *  Initialise AF_INET alias type (register net_alias_type)
+        */
+
+#if defined(CONFIG_IP_ALIAS)
+       ip_alias_init();
+#endif
        /*
         *      Create all the /proc entries.
         */
index 94b5e9bb6f1066433148a29a055cc94f1d460b27..60422172f5277ee4205916edf724515a5524ea28 100644 (file)
@@ -96,6 +96,9 @@
 #include <net/netrom.h>
 #endif
 #endif
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -891,6 +894,19 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
  *  cache.
  */
 
+/*
+ *     try to switch to alias device whose address is tip, if any
+ */
+
+#ifdef CONFIG_NET_ALIAS
+       if (net_alias_has(dev))
+       {
+               struct device *adev;
+               adev = net_alias_chk32(dev,AF_INET,tip,IFF_UP,IFF_NOARP);
+               if (adev != NULL) dev = adev;
+       }
+#endif
+
        if (arp->ar_op == htons(ARPOP_REQUEST))
        { 
 /*
@@ -1019,7 +1035,15 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                entry->timer.data = (unsigned long)entry;
                memcpy(entry->ha, sha, dev->addr_len);
                entry->last_updated = entry->last_used = jiffies;
+/*
+ *     make entry point to     'correct' device
+ */
+
+#ifdef CONFIG_NET_ALIAS
+               entry->dev = dev;
+#else
                entry->dev = skb->dev;
+#endif
                skb_queue_head_init(&entry->skb);
                if (arp_lock == 1)
                {
diff --git a/net/ipv4/ip_alias.c b/net/ipv4/ip_alias.c
new file mode 100644 (file)
index 0000000..e8ea2b0
--- /dev/null
@@ -0,0 +1,108 @@
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/inet.h>
+
+#ifdef ALIAS_USER_LAND_DEBUG
+#include "net_alias.h"
+#include "ip_alias.h"
+#include "user_stubs.h"
+#endif
+
+#include <linux/net_alias.h>
+#include <net/ip_alias.h>
+
+/*
+ * AF_INET alias init
+ */
+static int 
+ip_alias_init_1(struct net_alias *alias, struct sockaddr *sa)
+{
+#ifdef ALIAS_USER_LAND_DEBUG
+  printk("alias_init(%s) called.\n", alias->name);
+#endif
+  MOD_INC_USE_COUNT;
+  return 0;
+}
+
+/*
+ * AF_INET alias done
+ */
+static int
+ip_alias_done_1(struct net_alias *alias)
+{
+#ifdef ALIAS_USER_LAND_DEBUG
+  printk("alias_done(%s) called.\n", alias->name);
+#endif
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+/*
+ * print address info
+ */
+
+int
+ip_alias_print_1(char *buf, int len, struct net_alias *alias)
+{
+  char *p;
+
+  p = (char *) &alias->dev.pa_addr;
+  return sprintf(buf, "%d.%d.%d.%d",
+                (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
+}
+
+/*
+ * net_alias AF_INET type defn.
+ */
+
+struct net_alias_type ip_alias_type =
+{
+  AF_INET,                     /* type */
+  0,                           /* n_attach */
+  "ip",                                /* name */
+  NULL,                                /* get_addr32() */
+  NULL,                                /* addr_chk() */
+  ip_alias_init_1,             /* alias_init_1() */
+  ip_alias_done_1,             /* alias_done_1() */
+  ip_alias_print_1,            /* alias_print_1() */
+  NULL                         /* next */
+};
+
+/*
+ * ip_alias module initialization
+ */
+
+int ip_alias_init(void)
+{
+  return register_net_alias_type(&ip_alias_type, AF_INET);
+}
+
+/*
+ * ip_alias module done
+ */
+
+int ip_alias_done(void)
+{
+  return unregister_net_alias_type(&ip_alias_type);
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+  if (ip_alias_init() != 0)
+    return -EIO;
+  return 0;
+}
+
+void cleanup_module(void)
+{
+  if (ip_alias_done() != 0)
+    printk("ip_alias: can't remove module");
+}
+
+#endif /* MODULE */
index edd1bd2738136f9dfc9145f6860afcdf093672e3..772ef8cb0e4f36d8283f8aaf995780ee85cf99d7 100644 (file)
 #include <linux/firewall.h>
 #include <linux/mroute.h>
 #include <net/netlink.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 
 extern int last_retran;
 extern void sort_send(struct sock *sk);
@@ -313,7 +316,18 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
         *      function entry.
         */
 
+       /*
+        *      also check device aliases address : will avoid
+        *      a full lookup over device chain
+        */
+
+#ifdef CONFIG_NET_ALIAS
+       if ( iph->daddr == skb->dev->pa_addr ||
+           ( net_alias_has(skb->dev) && net_alias_addr_chk32(skb->dev,AF_INET, iph->daddr )) ||
+           (brd = ip_chk_addr(iph->daddr)) != 0)
+#else
        if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
+#endif
        {
                if (opt && opt->srr) 
                {
index 138dd519a8a203ba3fd83fe31da0b114017fdb27..0fe261b1b8eee066ea87f1966c2ab7a1e4fd5652 100644 (file)
@@ -20,6 +20,8 @@
 # 180995 Bernhard Kaindl (bkaindl@ping.at) - added dummy functions for
 # use with a config.in modified for make menuconfig.
 #
+# 301195 (boldt@math.ucsb.edu) - added help text support
+#
 
 #
 # Make sure we're really running bash.
@@ -44,6 +46,37 @@ function mainmenu_name () {
        :
 }
 
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+#       help variable
+#
+function help () {
+  if [ -f Documentation/Configure.help ]
+  then
+     #first escape regexp special characters in the argument:
+     var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+     #now pick out the right help text:
+     text=$(sed -n "/^$var[    ]*\$/,\${
+                        /^$var[        ]*\$/b
+                        /^#.*/b;/^[    ]*\$/q
+                        p
+                    }" Documentation/Configure.help)
+     if [ -z "$text" ]
+     then
+          echo; echo "  Sorry, no help available for this option yet.";echo
+     else
+          (echo; echo "$text"; echo) | more
+     fi
+  else
+     echo;
+     echo "  Can't access the file Documentation/Configure.help which"
+     echo "  should contain the help texts."
+     echo
+  fi
+}
+
+
 #
 # readln reads a line into $ans.
 #
@@ -78,17 +111,17 @@ function comment () {
 #
 function define_bool () {
         case "$2" in
-         "y" | "Y")
+         "y")
                echo "$1=y" >>$CONFIG
                echo "#define $1 1" >>$CONFIG_H
                ;;
 
-         "m" | "M")
+         "m")
                echo "$1=m" >>$CONFIG
                echo "#undef  $1" >>$CONFIG_H
                ;;
 
-         "n" | "N")
+         "n")
                echo "# $1 is not set" >>$CONFIG
                echo "#undef  $1" >>$CONFIG_H
                 ;;
@@ -102,18 +135,24 @@ function define_bool () {
 #      bool question define
 #
 function bool () {
-       ans=""
        def=$(eval echo "\${$2:-'n'}")
         case "$def" in
-         "y") defprompt="Y/n"
+         "y") defprompt="Y/n/?"
               ;;
-         "n") defprompt="N/y"
+         "n") defprompt="N/y/?"
               ;;
         esac
-       while [ "$ans" != "y" -a "$ans" != "n" ]; do
-               readln "$1 ($2) [$defprompt] " "$def" 
-       done
-       define_bool "$2" "$ans"
+        while :; do
+          readln "$1 ($2) [$defprompt] " "$def"
+          case "$ans" in
+            [yY] | [yY]es ) define_bool "$2" "y"
+                            break;;
+            [nN] | [nN]o )  define_bool "$2" "n"
+                            break;;
+            * )             help "$2"
+                            ;;
+          esac
+        done
 }
 
 #
@@ -122,20 +161,28 @@ function bool () {
 #      tristate question define
 #
 function tristate () {
-       ans=""
        def=$(eval echo "\${$2:-'n'}")
         case "$def" in
-         "y") defprompt="Y/m/n"
+         "y") defprompt="Y/m/n/?"
               ;;
-         "m") defprompt="M/n/y"
+         "m") defprompt="M/n/y/?"
               ;;
-         "n") defprompt="N/y/m"
+         "n") defprompt="N/y/m/?"
               ;;
         esac
-       while [ "$ans" != "y" -a "$ans" != "n" -a "$ans" != "m" ]; do
-               readln "$1 ($2) [$defprompt] " "$def"
-       done
-       define_bool "$2" "$ans"
+        while :; do
+          readln "$1 ($2) [$defprompt] " "$def"
+          case "$ans" in
+            [yY] | [yY]es ) define_bool "$2" "y"
+                            break ;;
+            [nN] | [nN]o )  define_bool "$2" "n"
+                            break ;;
+            [mM] )          define_bool "$2" "m"
+                            break ;;
+           * )             help "$2"
+                            ;;
+          esac
+        done
 }
 
 #
@@ -153,18 +200,32 @@ function dep_tristate () {
        if [ "$3" != "m" ]; then
                tristate "$1" "$2"
        else
-               ans=""
                case "$def" in
-                "y" | "m") defprompt="M/n"
+                "y" | "m") defprompt="M/n/?"
                      def="m"
                      ;;
-                "n") defprompt="N/m"
+                "n") defprompt="N/m/?"
                      ;;
                esac
-               while [ "$ans" != "n" -a "$ans" != "m" ]; do
-                       readln "$1 ($2) [$defprompt] " "$def"
-               done
-               define_bool "$2" "$ans"
+                while :; do
+                  readln "$1 ($2) [$defprompt] " "$def"
+                  case "$ans" in
+                      [nN] | [nN]o )  define_bool "$2" "n"
+                                      break ;;
+                      [mM] )          define_bool "$2" "m"
+                                      break ;;
+                      [yY] | [yY]es ) echo 
+   echo "  This answer is not allowed, because it is not consistent with"
+   echo "  your other choices."
+   echo "  This driver depends on another one which you chose to compile"
+   echo "  as a module. This means that you can either compile this one"
+   echo "  as a module as well (with M) or leave it out altogether (N)."
+                                      echo
+                                      ;;
+                      * )             help "$2"
+                                      ;;
+                  esac
+                done
        fi
 }
 
@@ -185,13 +246,17 @@ function define_int () {
 #      int question define default
 #
 function int () {
-       # Slimier hack to get bash to rescan a line.
-       ans="x"
        def=$(eval echo "\${$2:-$3}")
-       while [ $[$ans+0] != "$ans" ]; do
-               readln "$1 ($2) [$def] " "$def"
-       done
-       define_int "$2" "$ans"
+        while :; do
+          readln "$1 ($2) [$def] " "$def"
+          case "$ans" in
+             [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) 
+                 define_int "$2" "$ans"
+                 break;;
+             * ) help "$2"
+                 ;;
+          esac
+        done
 }
 
 #
@@ -216,6 +281,7 @@ function choice () {
        # determine default answer:
        names=""
        set -- $choices
+        firstvar=$2
        while [ -n "$2" ]; do
                if [ -n "$names" ]; then
                        names="$names, $1"
@@ -230,29 +296,34 @@ function choice () {
 
        val=""
        while [ -z "$val" ]; do
+                ambg=n
                readln "$question ($names) [$def] " "$def"
                ans=$(echo $ans | tr a-z A-Z)
                set -- $choices
-               val=""
                while [ -n "$1" ]; do
                        name=$(echo $1 | tr a-z A-Z)
                        case "$name" in
-                               ${ans}*)
+                               "$ans"* )
                                        if [ "$name" = "$ans" ]; then
                                                val="$2"
                                                break   # stop on exact match
                                        fi
                                        if [ -n "$val" ]; then
-                                               echo \
+                                               echo;echo \
                "  Sorry, \"$ans\" is ambiguous; please enter a longer string."
+                                                echo
                                                val=""
+                                                ambg=y
                                                break
                                        else
                                                val="$2"
                                        fi;;
                        esac
                        shift; shift
-               done
+                done
+               if [ "$val" = "" -a "$ambg" = "n" ]; then
+                        help "$firstvar"
+                fi
        done
        set -- $choices
        while [ -n "$2" ]; do