diff -u --recursive --new-file v2.1.70/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.70/linux/Documentation/Configure.help	Tue Dec  2 16:45:17 1997
+++ linux/Documentation/Configure.help	Wed Dec  3 15:21:57 1997
@@ -326,6 +326,13 @@
   chipsets.  Most of these also require special kernel boot parameters
   to actually turn on the support at runtime.
 
+Generic 4 drives/port support
+CONFIG_BLK_DEV_4DRIVES
+  Certain older chipsets, including the Tekram 690CD, use a
+  single set of I/O ports at 0x1f0 to control up to four drives,
+  instead of the customary two drives per port.  Support for this
+  is enabled at runtime using the "ide0=four" kernel boot parameter.
+  
 DTC-2278 support
 CONFIG_BLK_DEV_DTC2278
   This driver is enabled at runtime using the "ide0=dtc2278" kernel
@@ -351,7 +358,14 @@
   "ide0=dc4030" kernel boot parameter.  See the Documentation/ide.txt
   and drivers/block/pdc4030.c files for more info.
 
-OPTi 82C621     support (EXPERIMENTAL)
+Tekram TRM290 support (EXPERIMENTAL)
+CONFIG_BLK_DEV_TRM290
+  This driver adds support for bus master DMA transfers
+  using the Tekram TRM290 PCI IDE chip.  Volunteers are
+  needed for further tweaking and development.
+  Please read the comments at the top of drivers/block/trm290.c.
+
+OPTi 82C621 support (EXPERIMENTAL)
 CONFIG_BLK_DEV_OPTI621
   This driver allows use of hdparm to change the PIO timings
   for drives attached to an OPTi MIDE controller.
@@ -4873,11 +4887,11 @@
   floppies however cannot be read with this driver due to an
   incompatibility of the floppy controller used in an Amiga and the
   standard floppy controller in PCs and workstations. Read
-  Documentation/filesystems/affs.txt. This filesystem is also
-  available as a module ( = code which can be inserted in and removed
-  from the running kernel whenever you want). The module is called
-  affs.o. If you want to compile it as a module, say M here and read
-  Documentation/modules.txt.  If unsure, say N.
+  Documentation/filesystems/affs.txt and fs/affs/Changes. This
+  filesystem is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module is called affs.o. If you want to compile it as a module,
+  say M here and read Documentation/modules.txt.  If unsure, say N.
 
 ROM filesystem support
 CONFIG_ROMFS_FS
@@ -4899,11 +4913,10 @@
 
 Console on virtual terminal
 CONFIG_VT_CONSOLE
-  If you enable this option, all kernel messages will be sent to the
-  device /dev/tty which corresponds to the virtual terminal you have
-  visible on your display. You should say N here only if you have some
-  other console device, in which case you probably want to say Y to
-  "Console on serial port", below. If unsure, say N.
+  If you enable this option, by default all kernel messages will be sent
+  to the device /dev/tty0 which corresponds to the virtual terminal you
+  have visible on your display. You should say Y here unless you only
+  want to have the kernel messages output on a serial port.
 
 Software generated cursor
 CONFIG_SOFTCURSOR
@@ -4932,15 +4945,12 @@
 
 Console on serial port
 CONFIG_SERIAL_CONSOLE
-  If you enable this option, all kernel messages will be sent to the
-  device /dev/ttyS0 which corresponds to a serial port; this could be
-  useful if you attached a terminal or printer to that port. (You can
-  change the number of the serial port used from 0 to something else
-  by setting the variable CONFIG_SERIAL_CONSOLE_PORT.) You can use
-  this option in combination with the option "Console on virtual
-  terminal" above, in which case you get the output on both the serial
-  port and on your display. Most people say N here so that they can
-  use the serial port for modem, mouse or some other device.
+  If you enable this option, it is possible to use a serial port as the
+  console. By default still the virtual console will be used at the
+  system console but you can alter that using a kernel command line
+  option. If you don't have a VGA card installed the kernel will
+  automatically use /dev/ttyS0 as system console if this option is
+  enabled.
 
 Comtrol Rocketport support
 CONFIG_ROCKETPORT
diff -u --recursive --new-file v2.1.70/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- v2.1.70/linux/Documentation/filesystems/affs.txt	Wed Jul 24 23:08:28 1996
+++ linux/Documentation/filesystems/affs.txt	Tue Dec  2 22:25:07 1997
@@ -38,12 +38,6 @@
 
 protect		If this option is set, the protection bits cannot be altered.
 
-uid[=uid]	This sets the uid of the root directory (i. e. the mount point
-		to uid or to the uid of the current user, if the =uid is
-		omitted.
-
-gid[=gid]	Same as above, but for gid.
-
 setuid[=uid]	This sets the owner of all files and directories in the file
 		system to uid or the uid of the current user, respectively.
 
@@ -69,7 +63,7 @@
 		mode changes.
 
 verbose		The volume name, file system type and block size will
-		be written to the syslog.
+		be written to the syslog when the filesystem is mounted.
 
 prefix=path	Path will be prefixed to every absolute path name of
 		symbolic links on an AFFS partition. Default = /
@@ -89,9 +83,9 @@
 
   - If both W and D are allowed, w will be set.
 
-  - If both R and S are set, x will be set.
+  - E maps to x.
 
-  - H, P and E are always retained and ignored under Linux.
+  - H and P are always retained and ignored under Linux.
 
   - A is always reset when written.
 
@@ -107,7 +101,7 @@
 
   - w permission will set W and D for user, group and others.
 
-  - x permission of the user will set S for plain files.
+  - x permission of the user will set E for plain files.
 
   - All other flags (suid, sgid, ...) are ignored and will
     not be retained.
@@ -152,12 +146,12 @@
 
 Quite a few things may not work as advertised. Not everything is
 tested, though several hundred MB have been read and written using
-this fs.
-
-Filenames are truncated to 30 characters without warning.
+this fs. For a most up-to-date list of bugs please consult
+fs/affs/Changes.
 
-Currently there are no checks against invalid characters (':')
-in filenames.
+Filenames are truncated to 30 characters without warning (this
+can be changed by setting the compile-time option AFFS_NO_TRUNCATE
+ina include/linux/amigaffs.h).
 
 Case is ignored by the affs in filename matching, but Linux shells
 do care about the case. Example (with /mnt being an affs mounted fs):
diff -u --recursive --new-file v2.1.70/linux/Documentation/serial-console.txt linux/Documentation/serial-console.txt
--- v2.1.70/linux/Documentation/serial-console.txt	Tue May 13 22:41:00 1997
+++ linux/Documentation/serial-console.txt	Wed Dec  3 15:21:57 1997
@@ -1,48 +1,67 @@
                        Linux Serial Console
 
-These examples are valid if you want to use /dev/ttyS1 (COM2)
-as the serial console. Replace as needed.
+It is possible to specify multiple devices for console output. You can
+define a new kernel command line option to select which device(s) to
+use for console output.
 
-1. Tell LILO to use the serial port.
+The format of this option is:
+
+	console=device,options
+
+	device:		tty0 for the foreground virtual console
+			ttyX for any other virtual console
+			ttySx for a serial port
+
+	options:	depend on the driver. For the serial port this
+			defines the baudrate/parity/bits of the port,
+			in the format BBBBPN, where BBBB is the speed,
+			P is parity (n/o/e), and N is bits. Default is
+			9600n8. The maximum baudrate is 115200.
+
+You can specify multiple console= options on the kernel command line.
+Output will appear on all of them. The first device will be used when
+you open /dev/console. So, for example:
+
+	console=tty0 console=ttyS1,9600
+
+defines that opening /dev/console will get you the current foreground
+virtual console, and kernel messages will appear on both the VGA
+console and the 2nd serial port (ttyS1 or COM2) at 9600 baud.
+
+Note that you can only define one console per device type (serial, video).
+
+If no console device is specified, the first device found capable of
+acting as a system console will be used. At this time, the system
+first looks for a VGA card and then for a serial port. So if you don't
+have a VGA card in your system the first serial port will automatically
+become the console.
+
+You will need to create a new device to use /dev/console. The official
+/dev/console is now character device 5,1.
+
+Here's an example that will use /dev/ttyS1 (COM2) as the console.
+Replace the sample values as needed.
+
+1. Create /dev/console (real console) and /dev/tty0 (master virtual
+   console):
+
+   cd /dev
+   rm -f console tty0
+   mknod -m 622 console c 5 1
+   mknod -m 622 tty0 c 4 0
+
+2. LILO can also take input from a serial device. This is a very
+   useful option. To tell LILO to use the serial port:
    In lilo.conf (global section): 
 
    serial  = 1,9600n8 (ttyS1, 9600 bd, no parity, 8 bits)
 
-2. Adjust to kernel flags for the new kernel,
+3. Adjust to kernel flags for the new kernel,
    again in lilo.conf (kernel section)
 
-   append = "console=1,9600,n8" 
-
-   (Note the extra comma needed if you want to supply parity/framing 
-   information.)
-
-3. Link /dev/console to the serial port.
+   append = "console=ttyS1,9600" 
 
-   Your probably want to save your old /dev/console (the "master" virtual
-   console). Check if it is a symbolic link first. If not, `mv' it to
-   `/dev/tty0':
-
-   ls -l /dev/console
-   mv /dev/console /dev/tty0
-
-   Now link the serial port you are going to use as the console to
-   /dev/console, for example ttyS1:
-
-   ln -s /dev/ttyS1 /dev/console
-
-   On some systems you might want to edit your bootup scripts to make sure
-   they don't reset this arrangement on boot. (On Debian, check
-   /etc/rc.boot/console and /etc/default/console). You probably also want
-   to put a getty on either /dev/console or /dev/ttyS1.
-
-4. Init and /dev/console.
-   Sysvinit will open /dev/console on boot. If this does not point
-   to the serial console device, the startup messages will be printed
-   to the wrong device. The kernel also passes the environment variable
-   "CONSOLE" to the init program. sysvinit-2.64 reckognizes this, and
-   opens that device instead. Boot scripts as mentioned in (3) can
-   also check this variable to see what device the system console is.
-   If CONSOLE is not set you can assume the console is /dev/tty0.
+4. Init and /etc/ioctl.save
 
    Sysvinit remembers its stty settings in a file in /etc, called
    `/etc/ioctl.save'. REMOVE THIS FILE before using the serial
@@ -51,28 +70,25 @@
 
 5. /dev/console and X
    Programs that want to do something with the virtual console usually
-   open /dev/console. XF86 does this, and probably SVGALIB as well.
-   IMO this is wrong; they should open /dev/tty0.
-   I have binary patched /usr/bin/X11/XF86_SVGA to use "tty0"
-   instead of "console".
-
-6. Notes.
-
-   If you compile the next little program, you will be able
-   to really "halt" the system. It will enter a little
-   monitor :)
-
-   main() { reboot(0xfee1dead, 672274793, 0xCDEF0123); }
-
-   This is just a call to the new "halt" function that later
-   kernels have. This is included the "halt" command of
-   the recent sysvinit versions.
-
-   The monitor will also be entered at a kernel panic, or
-   when you press "break". That last function does not
-   work at the moment I think, but it would be useful for
-   kernel debugging.  You don't have alt-scrollock on a serial
-   console to find out the current EIP...
+   open /dev/console. If you have created the new /dev/console device,
+   and your console is NOT the virtual console some programs will fail.
+   Those are programs that want to access the VT interface, and use
+   /dev/console instead of /dev/tty0. Some of those programs are:
+
+   Xfree86, svgalib, gpm, SVGATextMode
+
+   I have binary patched the above mentioned programs to use "tty0"
+   instead of "console".  This will be reported to the maintainers of
+   said programs.
+
+   Note that if you boot without a console= option (or with
+   console=/dev/tty0), /dev/console is the same as /dev/tty0. In that
+   case everything will still work.
+
+6. Thanks
+
+   Thanks to Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>
+   for porting the patches from 2.1.4x to 2.1.6x for taking care of
+   the integration of these patches into m68k, ppc and alpha.
 
-Miquel van Smoorenburg <miquels@cistron.nl>, 21-Jun-1996
-Stephen C. Tweedie <sct@dcs.ed.ac.uk>, 23-Dec-1996
+Miquel van Smoorenburg <miquels@cistron.nl>, 03-Dec-1997
diff -u --recursive --new-file v2.1.70/linux/Makefile linux/Makefile
--- v2.1.70/linux/Makefile	Tue Dec  2 16:45:17 1997
+++ linux/Makefile	Wed Dec  3 17:14:04 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 70
+SUBLEVEL = 71
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -133,7 +133,7 @@
 DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
 endif
 
-ifeq ($(CONFIG_CDROM),y)
+ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
 DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
 endif
 
@@ -310,6 +310,7 @@
 	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
 	if [ -f NLS_MODULES   ]; then inst_mod NLS_MODULES   fs;    fi; \
 	if [ -f CDROM_MODULES ]; then inst_mod CDROM_MODULES cdrom; fi; \
+	if [ -f HAM_MODULES   ]; then inst_mod HAM_MODULES   net;   fi; \
 	\
 	ls *.o > .allmods; \
 	echo $$MODULES | tr ' ' '\n' | sort | comm -23 .allmods - > .misc; \
diff -u --recursive --new-file v2.1.70/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.70/linux/arch/alpha/config.in	Tue Dec  2 16:45:17 1997
+++ linux/arch/alpha/config.in	Wed Dec  3 15:17:30 1997
@@ -134,6 +134,8 @@
   endmenu
 fi
 
+source drivers/net/hamradio/Config.in
+
 mainmenu_option next_comment
 comment 'ISDN subsystem'
 
diff -u --recursive --new-file v2.1.70/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- v2.1.70/linux/arch/alpha/kernel/signal.c	Tue Dec  2 09:49:38 1997
+++ linux/arch/alpha/kernel/signal.c	Wed Dec  3 15:26:59 1997
@@ -577,7 +577,7 @@
 
 	single_stepping = ptrace_cancel_bpt(current);
 
-	spin_lock_irq(current->sigmask_lock);
+	spin_lock_irq(&current->sigmask_lock);
 	if (!oldset) {
 		_oldset = current->blocked;
 		oldset = &_oldset;
diff -u --recursive --new-file v2.1.70/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.70/linux/arch/i386/config.in	Tue Dec  2 16:45:17 1997
+++ linux/arch/i386/config.in	Wed Dec  3 15:17:30 1997
@@ -84,6 +84,8 @@
   endmenu
 fi
 
+source drivers/net/hamradio/Config.in
+
 mainmenu_option next_comment
 comment 'ISDN subsystem'
 
diff -u --recursive --new-file v2.1.70/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.70/linux/arch/i386/defconfig	Tue Dec  2 16:45:17 1997
+++ linux/arch/i386/defconfig	Wed Dec  3 15:34:15 1997
@@ -103,7 +103,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-# CONFIG_AX25 is not set
 
 #
 # SCSI support
@@ -184,14 +183,20 @@
 # CONFIG_FDDI is not set
 # CONFIG_DLCI is not set
 # CONFIG_PPP is not set
-# CONFIG_NET_RADIO is not set
 # CONFIG_SLIP is not set
+# CONFIG_STRIP is not set
+# CONFIG_WAVELAN is not set
 # CONFIG_TR is not set
 # CONFIG_WAN_DRIVERS is not set
 # CONFIG_LAPBETHER is not set
 # CONFIG_X25_ASY is not set
 
 #
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
@@ -241,6 +246,7 @@
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
 CONFIG_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_MOUSE=y
diff -u --recursive --new-file v2.1.70/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.70/linux/arch/ppc/config.in	Tue Dec  2 16:45:17 1997
+++ linux/arch/ppc/config.in	Wed Dec  3 15:17:30 1997
@@ -89,6 +89,7 @@
   endmenu
 fi
 
+source drivers/net/hamradio/Config.in
 
 mainmenu_option next_comment
 comment 'ISDN subsystem'
diff -u --recursive --new-file v2.1.70/linux/drivers/Makefile linux/drivers/Makefile
--- v2.1.70/linux/drivers/Makefile	Mon Aug 18 18:19:45 1997
+++ linux/drivers/Makefile	Wed Dec  3 15:17:30 1997
@@ -71,4 +71,14 @@
 ALL_SUB_DIRS += ap1000
 endif
 
+# make will try to add $(MOD_SUB_DIRS).o to modules/MOD_LIST_NAME 
+# when MOD_LIST_NAME is set. We don't have hamradio.o and Linus
+# sort-of insisted on making hamradio a subdirectory to drivers/net.
+#
+
+ifeq ($(CONFIG_HAMRADIO),y)
+  SUB_DIRS += net/hamradio
+  MOD_SUB_DIRS += net/hamradio
+endif
+
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.70/linux/drivers/ap1000/ddv.c linux/drivers/ap1000/ddv.c
--- v2.1.70/linux/drivers/ap1000/ddv.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/ap1000/ddv.c	Wed Dec  3 15:26:21 1997
@@ -363,7 +363,10 @@
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "ddv_daemon");
-	current->blocked = ~0UL; /* block all signals */
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked); /* block all signals */
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
   
 	/* Give it a realtime priority. */
 	current->policy = SCHED_FIFO;
diff -u --recursive --new-file v2.1.70/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.70/linux/drivers/block/Config.in	Mon Dec  1 12:04:11 1997
+++ linux/drivers/block/Config.in	Wed Dec  3 04:36:30 1997
@@ -26,6 +26,7 @@
       bool '   PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
       if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+          bool '      Tekram TRM290 DMA support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
           bool '      OPTi 82C621 enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
         fi
       fi
@@ -33,14 +34,15 @@
     bool '   Other IDE chipset support' CONFIG_IDE_CHIPSETS
     if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
       comment 'Note: most of these also require special kernel boot parameters'
-      bool '      ALI M14xx       support' CONFIG_BLK_DEV_ALI14XX
-      bool '      DTC-2278        support' CONFIG_BLK_DEV_DTC2278
-      bool '      Holtek HT6560B  support' CONFIG_BLK_DEV_HT6560B
+      bool '      Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES
+      bool '      ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+      bool '      DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+      bool '      Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
       if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-        bool '      PROMISE DC4030  support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
+        bool '      PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030
       fi
-      bool '      QDI QD6580      support' CONFIG_BLK_DEV_QD6580
-      bool '      UMC 8672        support' CONFIG_BLK_DEV_UMC8672
+      bool '      QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+      bool '      UMC-8672 support' CONFIG_BLK_DEV_UMC8672
     fi
   fi
 fi
diff -u --recursive --new-file v2.1.70/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.70/linux/drivers/block/Makefile	Mon Dec  1 12:04:11 1997
+++ linux/drivers/block/Makefile	Wed Dec  3 04:36:30 1997
@@ -129,6 +129,10 @@
 L_OBJS += pdc4030.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_TRM290),y)
+L_OBJS += trm290.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
 L_OBJS += opti621.o
 endif
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ali14xx.c linux/drivers/block/ali14xx.c
--- v2.1.70/linux/drivers/block/ali14xx.c	Sun Aug  4 22:12:25 1996
+++ linux/drivers/block/ali14xx.c	Wed Dec  3 04:36:30 1997
@@ -211,6 +211,8 @@
 	ide_hwifs[1].chipset = ide_ali14xx;
 	ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
 	ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
 
 	/* initialize controller registers */
 	if (!initRegisters()) {
diff -u --recursive --new-file v2.1.70/linux/drivers/block/cmd640.c linux/drivers/block/cmd640.c
--- v2.1.70/linux/drivers/block/cmd640.c	Wed Nov  6 04:49:31 1996
+++ linux/drivers/block/cmd640.c	Wed Dec  3 04:36:30 1997
@@ -795,6 +795,8 @@
 		cmd_hwif0->serialized = 1;
 		cmd_hwif1->serialized = 1;
 		cmd_hwif1->chipset = ide_cmd640;
+		cmd_hwif0->mate = cmd_hwif1;
+		cmd_hwif1->mate = cmd_hwif0;
 #ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
 		cmd_hwif1->tuneproc = &cmd640_tune_drive;
 #endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
diff -u --recursive --new-file v2.1.70/linux/drivers/block/dtc2278.c linux/drivers/block/dtc2278.c
--- v2.1.70/linux/drivers/block/dtc2278.c	Sun Aug  4 22:12:25 1996
+++ linux/drivers/block/dtc2278.c	Wed Dec  3 04:36:30 1997
@@ -125,4 +125,6 @@
 	ide_hwifs[0].drives[1].no_unmask = 1;
 	ide_hwifs[1].drives[0].no_unmask = 1;
 	ide_hwifs[1].drives[1].no_unmask = 1;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
 }
diff -u --recursive --new-file v2.1.70/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.70/linux/drivers/block/floppy.c	Mon Dec  1 12:04:11 1997
+++ linux/drivers/block/floppy.c	Tue Dec  2 22:19:33 1997
@@ -3975,6 +3975,7 @@
 	fdc_state[0].address = FDC1;
 	if (fdc_state[0].address == -1) {
 		unregister_blkdev(MAJOR_NR,"fd");
+		del_timer(&fd_timeout);
 		return -ENODEV;
 	}
 #if N_FDC > 1
@@ -3983,6 +3984,7 @@
 
 	if (floppy_grab_irq_and_dma()){
 		unregister_blkdev(MAJOR_NR,"fd");
+		del_timer(&fd_timeout);
 		return -EBUSY;
 	}
 
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ht6560b.c linux/drivers/block/ht6560b.c
--- v2.1.70/linux/drivers/block/ht6560b.c	Sun Aug  4 22:12:25 1996
+++ linux/drivers/block/ht6560b.c	Wed Dec  3 04:36:30 1997
@@ -227,6 +227,8 @@
 			ide_hwifs[1].tuneproc = &tune_ht6560b;
 			ide_hwifs[0].serialized = 1;
 			ide_hwifs[1].serialized = 1;
+			ide_hwifs[0].mate = &ide_hwifs[1];
+			ide_hwifs[1].mate = &ide_hwifs[0];
 		} else
 			printk("\nht6560b: not found\n");
 	}
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.70/linux/drivers/block/ide-disk.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/ide-disk.c	Wed Dec  3 04:36:30 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-disk.c	Version 1.02  Nov  29, 1997
+ *  linux/drivers/block/ide-disk.c	Version 1.03  Nov  30, 1997
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -40,6 +40,7 @@
  *			support optional byte-swapping of all data
  * Version 1.01		fix previous byte-swapping code
  * Verions 1.02		remove ", LBA" from drive identification msgs
+ * Verions 1.03		fix display of id->buf_size for big-endian
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
@@ -601,6 +602,8 @@
 		if (drive->cyl > drive->bios_cyl)
 			drive->bios_cyl = drive->cyl;
 	}
+	/* fix byte-ordering of buffer size field */
+	id->buf_size = le16_to_cpu(id->buf_size);
 
 	(void) idedisk_capacity (drive); /* initialize LBA selection */
 
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ide-dma.c linux/drivers/block/ide-dma.c
--- v2.1.70/linux/drivers/block/ide-dma.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/ide-dma.c	Wed Dec  3 17:36:04 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-dma.c	Version 4.01  November 30, 1997
+ *  linux/drivers/block/ide-dma.c	Version 4.06  December 3, 1997
  *
  *  Copyright (c) 1995-1998  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -64,6 +64,7 @@
  *
  * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
  */
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
@@ -75,7 +76,6 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/init.h>
-#include <linux/config.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -141,14 +141,20 @@
 }
 
 /*
- * build_dmatable() prepares a dma request.
+ * ide_build_dmatable() prepares a dma request.
  * Returns 0 if all went okay, returns 1 otherwise.
+ * May also be invoked from trm290.c
  */
-static int build_dmatable (ide_drive_t *drive)
+int ide_build_dmatable (ide_drive_t *drive)
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	struct buffer_head *bh = rq->bh;
 	unsigned long size, addr, *table = HWIF(drive)->dmatable;
+#ifdef CONFIG_BLK_DEV_TRM290
+	unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
+#else
+	const int is_trm290_chipset = 0;
+#endif
 	unsigned int count = 0;
 
 	do {
@@ -172,32 +178,40 @@
 				size += bh->b_size;
 			}
 		}
-
 		/*
 		 * Fill in the dma table, without crossing any 64kB boundaries.
-		 * We assume 16-bit alignment of all blocks.
+		 * The hardware requires 16-bit alignment of all blocks
+		 * (trm290 requires 32-bit alignment).
 		 */
+		if ((addr & 3)) {
+			printk("%s: misaligned DMA buffer\n", drive->name);
+			return 0;
+		}
 		while (size) {
 			if (++count >= PRD_ENTRIES) {
 				printk("%s: DMA table too small\n", drive->name);
-				return 1; /* revert to PIO for this request */
+				return 0; /* revert to PIO for this request */
 			} else {
-				unsigned long bcount = 0x10000 - (addr & 0xffff);
+				unsigned long xcount, bcount = 0x10000 - (addr & 0xffff);
 				if (bcount > size)
 					bcount = size;
 				*table++ = addr;
-				*table++ = bcount & 0xffff;
+				xcount = bcount & 0xffff;
+				if (is_trm290_chipset)
+					xcount = ((xcount >> 2) - 1) << 16;
+				*table++ = xcount;
 				addr += bcount;
 				size -= bcount;
 			}
 		}
 	} while (bh != NULL);
 	if (count) {
-		*--table |= 0x80000000;	/* set End-Of-Table (EOT) bit */
-		return 0;
+		if (!is_trm290_chipset)
+			*--table |= 0x80000000;	/* set End-Of-Table (EOT) bit */
+		return count;
 	}
 	printk("%s: empty DMA table?\n", drive->name);
-	return 1;	/* let the PIO routines handle this weirdness */
+	return 0;
 }
 
 /*
@@ -214,11 +228,13 @@
  * Returns 0 if all went well.
  * Returns 1 if DMA read/write could not be started, in which case
  * the caller should revert to PIO for the current request.
+ * May also be invoked from trm290.c
  */
-static int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
 {
-	unsigned long dma_base = HWIF(drive)->dma_base;
-	unsigned int reading = 0;
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned long dma_base = hwif->dma_base;
+	unsigned int count, reading = 0;
 
 	switch (func) {
 		case ide_dma_off:
@@ -243,11 +259,11 @@
 			printk("ide_dmaproc: unsupported func: %d\n", func);
 			return 1;
 		case ide_dma_read:
-			reading = (1 << 3);
+			reading = 1 << 3;
 		case ide_dma_write:
-			if (build_dmatable (drive))
-				return 1;
-			outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
+			if (!(count = ide_build_dmatable(drive)))
+				return 1;	/* try PIO instead of DMA */
+			outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */
 			outb(reading, dma_base);			/* specify r/w */
 			outb(inb(dma_base+2)|0x06, dma_base+2);		/* clear status bits */
 			if (drive->media != ide_disk)
@@ -262,32 +278,85 @@
 static int config_drive_for_dma (ide_drive_t *drive)
 {
 	const char **list;
-
 	struct hd_driveid *id = drive->id;
+	ide_hwif_t *hwif = HWIF(drive);
+
 	if (id && (id->capability & 1)) {
 		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
 		if (id->field_valid & 4)	/* UltraDMA */
 			if  ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
-				return ide_dmaproc(ide_dma_on, drive);
+				return hwif->dmaproc(ide_dma_on, drive);
 		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
 		if (id->field_valid & 2)	/* regular DMA */
 			if  ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
-				return ide_dmaproc(ide_dma_on, drive);
+				return hwif->dmaproc(ide_dma_on, drive);
 		/* Consult the list of known "good" drives */
 		list = good_dma_drives;
 		while (*list) {
 			if (!strcmp(*list++,id->model))
-				return ide_dmaproc(ide_dma_on, drive);
+				return hwif->dmaproc(ide_dma_on, drive);
 		}
 	}
-	return ide_dmaproc(ide_dma_off_quietly, drive);
+	return hwif->dmaproc(ide_dma_off_quietly, drive);
 }
 
+void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports)
+{
+	static unsigned long dmatable = 0;
+	static unsigned leftover = 0;
+
+	printk("    %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase + num_ports - 1);
+	if (check_region(dmabase, num_ports)) {
+		printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
+		return;
+	}
+	request_region(dmabase, num_ports, hwif->name);
+	hwif->dma_base = dmabase;
+	if (leftover < (PRD_ENTRIES * PRD_BYTES)) {
+		/*
+		 * The BM-DMA uses full 32bit addr, so we can
+		 * safely use __get_free_page() here instead
+		 * of __get_dma_pages() -- no ISA limitations.
+		 */
+		dmatable = __get_free_pages(GFP_KERNEL,1,0);
+		leftover = dmatable ? PAGE_SIZE : 0;
+	}
+	if (!dmatable) {
+		printk(" -- ERROR, UNABLE TO ALLOCATE PRD TABLE\n");
+	} else {
+		hwif->dmatable = (unsigned long *) dmatable;
+		dmatable += (PRD_ENTRIES * PRD_BYTES);
+		leftover -= (PRD_ENTRIES * PRD_BYTES);
+		hwif->dmaproc = &ide_dmaproc;
+		if (hwif->chipset != ide_trm290) {
+			byte dma_stat = inb(dmabase+2);
+			printk(", BIOS DMA settings: %s:%s %s:%s",
+		 	 hwif->drives[0].name, (dma_stat & 0x20) ? "yes" : "no ",
+		 	 hwif->drives[1].name, (dma_stat & 0x40) ? "yes" : "no");
+		}
+		printk("\n");
+	}
+}
+
+#ifdef CONFIG_BLK_DEV_TRM290
+extern void ide_init_trm290(byte, byte, ide_hwif_t *);
+#define INIT_TRM290 (&ide_init_trm290)
+#else
+#define INIT_TRM290 (NULL)
+#endif	/* CONFIG_BLK_DEV_TRM290 */
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+extern void ide_init_opti621(byte, byte, ide_hwif_t *);
+#define INIT_OPTI (&ide_init_opti621)
+#else
+#define INIT_OPTI (NULL)
+#endif	/* CONFIG_BLK_DEV_OPTI621 */
+
 #define DEVID_PIIX	(PCI_VENDOR_ID_INTEL  |(PCI_DEVICE_ID_INTEL_82371_1   <<16))
 #define DEVID_PIIX3	(PCI_VENDOR_ID_INTEL  |(PCI_DEVICE_ID_INTEL_82371SB_1 <<16))
 #define DEVID_PIIX4	(PCI_VENDOR_ID_INTEL  |(PCI_DEVICE_ID_INTEL_82371AB   <<16))
 #define DEVID_VP_IDE 	(PCI_VENDOR_ID_VIA    |(PCI_DEVICE_ID_VIA_82C586_1    <<16))
-#define DEVID_PDC2046	(PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246   <<16))
+#define DEVID_PDC20246	(PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246   <<16))
 #define DEVID_RZ1000	(PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1000   <<16))
 #define DEVID_RZ1001	(PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1001   <<16))
 #define DEVID_CMD640	(PCI_VENDOR_ID_CMD    |(PCI_DEVICE_ID_CMD_640         <<16))
@@ -295,13 +364,9 @@
 #define DEVID_SIS5513	(PCI_VENDOR_ID_SI     |(PCI_DEVICE_ID_SI_5513         <<16))
 #define DEVID_OPTI	(PCI_VENDOR_ID_OPTI   |(PCI_DEVICE_ID_OPTI_82C621     <<16))
 #define DEVID_OPTI2	(PCI_VENDOR_ID_OPTI   |(0xd568 /* from datasheets */  <<16))
-
-#ifdef CONFIG_BLK_DEV_OPTI621
-extern void ide_init_opti621(byte, byte, ide_hwif_t *);
-#define INIT_OPTI (&ide_init_opti621)
-#else
-#define INIT_OPTI (NULL)
-#endif
+#define DEVID_TRM290	(PCI_VENDOR_ID_TEKRAM |(PCI_DEVICE_ID_TEKRAM_DC290    <<16))
+#define DEVID_NS87410	(PCI_VENDOR_ID_NS     |(PCI_DEVICE_ID_NS_87410        <<16))
+#define DEVID_HT6565	(PCI_VENDOR_ID_HOLTEK |(PCI_DEVICE_ID_HOLTEK_6565     <<16))
 
 typedef struct ide_pci_enablebit_s {
 	byte	reg;	/* byte pci reg holding the enable-bit */
@@ -321,14 +386,17 @@
 	{DEVID_PIIX3,	"PIIX3",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
 	{DEVID_PIIX4,	"PIIX4",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
 	{DEVID_VP_IDE,	"VP_IDE",	NULL,		{{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
-	{DEVID_PDC2046,	"PDC2046",	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
+	{DEVID_PDC20246,"PDC20246",	NULL,		{{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
 	{DEVID_RZ1000,	NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
 	{DEVID_RZ1001,	NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
 	{DEVID_CMD640,	NULL,		NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
 	{DEVID_OPTI,	"OPTI",		INIT_OPTI,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
 	{DEVID_OPTI2,	"OPTI2",	INIT_OPTI,	{{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
-	{DEVID_SIS5513,	"SIS5513",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+	{DEVID_SIS5513,	"SIS5513",	NULL,		{{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} },
 	{DEVID_CMD646,	"CMD646",	NULL,		{{0x00,0x00,0x00}, {0x51,0x80,0x80}} },
+	{DEVID_TRM290,	"TRM290",	INIT_TRM290,	{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+	{DEVID_NS87410,	"NS87410",	NULL,		{{0x43,0x08,0x08}, {0x47,0x08,0x08}} },
+	{DEVID_HT6565,	"HT6565",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
 	{0,		"PCI_IDE",	NULL,		{{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
 
 __initfunc(static ide_pci_device_t *lookup_devid(unsigned int devid))
@@ -339,38 +407,6 @@
 	return d;
 }
 
-__initfunc(static void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase))
-{
-	static unsigned long dmatable = 0;
-	static unsigned leftover = 0;
-
-	printk("    %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase+7);
-	if (check_region(dmabase, 8)) {
-		printk(" -- ERROR, PORTS ALREADY IN USE");
-	} else {
-		request_region(dmabase, 8, hwif->name);
-		hwif->dma_base = dmabase;
-		if (leftover < (PRD_ENTRIES * PRD_BYTES)) {
-			/*
-			 * The BM-DMA uses full 32bit addr, so we can
-			 * safely use __get_free_page() here instead
-			 * of __get_dma_pages() -- no ISA limitations.
-			 */
-			dmatable = __get_free_pages(GFP_KERNEL,1,0);
-			leftover = dmatable ? PAGE_SIZE : 0;
-		}
-		if (dmatable) {
-			printk(", PRD table at %08lx", dmatable);
-			hwif->dmatable = (unsigned long *) dmatable;
-			dmatable += (PRD_ENTRIES * PRD_BYTES);
-			leftover -= (PRD_ENTRIES * PRD_BYTES);
-			outl(virt_to_bus(hwif->dmatable), dmabase + 4);
-			hwif->dmaproc = &ide_dmaproc;
-		}
-	}
-	printk("\n");
-}
-
 /* The next two functions were stolen from cmd640.c, with
    a few modifications  */
 
@@ -501,7 +537,7 @@
 	}
 	return NULL;
 }
-
+				
 /*
  * ide_setup_pci_device() looks at the primary/secondary interfaces
  * on a PCI IDE device and, if they are enabled, prepares the IDE driver
@@ -516,11 +552,9 @@
 {
 	unsigned int port, at_least_one_hwif_enabled = 0;
 	unsigned short base = 0, ctl = 0;
-	byte tmp = 0, pciirq = 0;
-	ide_hwif_t *hwif;
+	byte tmp = 0;
+	ide_hwif_t *hwif, *mate = NULL;
 
-	if (pcibios_read_config_byte(bus, fn, 0x3c, &pciirq))
-		pciirq = 0;	/* probe later if not set */
 	for (port = 0; port <= 1; ++port) {
 		ide_pci_enablebit_t *e = &(d->enablebits[port]);
 		if (e->reg) {
@@ -542,25 +576,33 @@
 			continue;
 		}
 		hwif->chipset = ide_pci;
+		hwif->pci_port = port;
+		if (mate) {
+			hwif->mate = mate;
+			mate->mate = hwif;
+		}
+		mate = hwif;	/* for next iteration */
 		if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
 			ide_init_hwif_ports(hwif->io_ports, base, NULL);
 			hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2;
 		}
-		if (!hwif->irq)
-			hwif->irq = port ? 0 : pciirq;	/* always probe for secondary irq */
 		if (bmiba) {
 			if ((inb(bmiba+2) & 0x80)) {	/* simplex DMA only? */
 				printk("%s: simplex device:  DMA disabled\n", d->name);
 			} else {	/* supports simultaneous DMA on both channels */
-				ide_setup_dma(hwif, bmiba + (8 * port));
+				ide_setup_dma(hwif, bmiba + (8 * port), 8);
 			}
 		}
+		if (d->id) {  /* For "known" chipsets, allow other irqs during i/o */
+			hwif->drives[0].unmask = 1;
+			hwif->drives[1].unmask = 1;
+		}
 		if (d->init_hwif)  /* Call chipset-specific routine for each enabled hwif */
 			d->init_hwif(bus, fn, hwif);
 		at_least_one_hwif_enabled = 1;
 	}
 	if (!at_least_one_hwif_enabled)
-		printk("%s: neither IDE port is enabled\n", d->name);
+		printk("%s: neither IDE port enabled (BIOS)\n", d->name);
 }
 
 /*
@@ -570,9 +612,9 @@
 __initfunc(static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn))
 {
 	unsigned int devid, ccode;
-	unsigned short pcicmd;
+	unsigned short pcicmd, class;
 	ide_pci_device_t *d;
-	byte hedt;
+	byte hedt, progif;
 
 	if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt))
 		hedt = 0;
@@ -582,26 +624,36 @@
 		 || pcibios_read_config_dword(bus, fn, 0x08, &ccode))
 			return;
 		d = lookup_devid(devid);
-		if (d->name == NULL)	/* some chips (cmd640 & rz1000) are handled elsewhere */
+		if (d->name == NULL)	/* some chips (cmd640, rz1000) are handled elsewhere */
 			continue;
-		if (d->id || (ccode >> 16) == PCI_CLASS_STORAGE_IDE) {
-			printk("%s: %sIDE device on PCI bus %d function %d\n", d->name, d->id ? "" : "unknown ", bus, fn);
+		progif = (ccode >> 8) & 0xff;
+		class = ccode >> 16;
+		if (d->id || class == PCI_CLASS_STORAGE_IDE) {
+			if (d->id)
+				printk("%s: IDE device on PCI bus %d function %d\n", d->name, bus, fn);
+			else
+				printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
+					d->name, bus, fn, devid & 0xffff, devid >> 16);
 			/*
-	 		* See if IDE ports are enabled
-	 		*/
+	 		 * See if IDE ports are enabled
+	 		 */
 			if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) {
 				printk("%s: error accessing PCICMD\n", d->name);
 			} else if ((pcicmd & 1) == 0) {
-				printk("%s: device is disabled (BIOS)\n", d->name);
+				printk("%s: device disabled (BIOS)\n", d->name);
 			} else {
 				unsigned int bmiba = 0;
 				/*
-		 		 * Check for Bus-Master DMA capability
-		 		 */
-				if (!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name))) {
-					if ((ccode >> 16) == PCI_CLASS_STORAGE_RAID || (ccode && 0x8000))
-						printk("%s: Bus-Master DMA is disabled (BIOS)\n", d->name);
+			 	 * Check for Bus-Master DMA capability
+			 	 */
+				if (d->id == DEVID_PDC20246 || (class == PCI_CLASS_STORAGE_IDE && (progif & 0x80))) {
+					if ((!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name)))) {
+						printk("%s: Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, progif=0x%02x, bmiba=0x%04x\n", d->name, pcicmd, progif, bmiba);
+					}
 				}
+				/*
+				 * Setup the IDE ports
+				 */
 				ide_setup_pci_device(bus, fn, bmiba, d);
 			}
 		}
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.70/linux/drivers/block/ide-probe.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/ide-probe.c	Wed Dec  3 10:43:31 1997
@@ -123,11 +123,13 @@
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 		switch (type) {
 			case ide_floppy:
-				if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
-					printk("cdrom or floppy?, assuming ");
-				if (drive->media != ide_cdrom) {
-					printk ("FLOPPY");
-					break;
+				if (!strstr(id->model, "CD-ROM")) {
+					if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+						printk("cdrom or floppy?, assuming ");
+					if (drive->media != ide_cdrom) {
+						printk ("FLOPPY");
+						break;
+					}
 				}
 				type = ide_cdrom;	/* Early cdrom models used zero */
 			case ide_cdrom:
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.70/linux/drivers/block/ide.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/ide.c	Wed Dec  3 17:16:33 1997
@@ -2175,8 +2175,8 @@
 		/*
 		 * Be VERY CAREFUL changing this: note hardcoded indexes below
 		 */
-		const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
-			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "reset", NULL};
+		const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "qd6580",
+		 "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "four", "reset", NULL};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
 		i = match_parm(&s[4], ide_words, vals, 3);
@@ -2184,7 +2184,7 @@
 		/*
 		 * Cryptic check to ensure chipset not already set for hwif:
 		 */
-		if (i > 0 || (i <= -5 && i != -12)) {
+		if (i > 0 || (i <= -5 && i != -13)) {
 			if (hwif->chipset != ide_unknown)
 				goto bad_option;
 			if (i <= -5) {
@@ -2200,9 +2200,24 @@
 		}
 
 		switch (i) {
-			case -12: /* "reset" */
+			case -13: /* "reset" */
 				hwif->reset = 1;
 				goto done;
+#ifdef CONFIG_BLK_DEV_4DRIVES
+			case -12: /* "four" drives on one set of ports */
+			{
+				ide_hwif_t *mate = &ide_hwifs[hw^1];
+				mate->mate = hwif;
+				hwif->mate = mate;
+				hwif->chipset = mate->chipset = ide_4drives;
+				hwif->serialized = mate->serialized = 1;
+				mate->drives[0].select.all ^= 0x20;
+				mate->drives[1].select.all ^= 0x20;
+				memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
+				mate->irq = hwif->irq;
+				goto done;
+			}
+#endif /* CONFIG_BLK_DEV_4DRIVES */
 #ifdef CONFIG_BLK_DEV_PDC4030
 			case -11: /* "dc4030" */
 			{
diff -u --recursive --new-file v2.1.70/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.70/linux/drivers/block/ide.h	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/ide.h	Thu Dec  4 13:35:53 1997
@@ -164,7 +164,7 @@
 #define WAIT_CMD	(10*HZ)	/* 10sec  - maximum wait for an IRQ to happen */
 #define WAIT_MIN_SLEEP	(2*HZ/100)	/* 20msec - minimum sleep time */
 
-#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030)
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_TRM290)
 #define SELECT_DRIVE(hwif,drive)				\
 {								\
 	if (hwif->selectproc)					\
@@ -289,7 +289,7 @@
 typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
 
 /*
- * This is used to provide HT6560B & PDC4030 interface support.
+ * This is used to provide HT6560B & PDC4030 & TRM290 interface support.
  */
 typedef void (ide_selectproc_t) (ide_drive_t *);
 
@@ -300,7 +300,8 @@
 typedef enum {	ide_unknown,	ide_generic,	ide_pci,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd6580,	ide_umc8672,	ide_ht6560b,
-		ide_pdc4030,	ide_rz1000 }
+		ide_pdc4030,	ide_rz1000,	ide_trm290,
+		ide_4drives }
 	hwif_chipset_t;
 
 typedef struct hwif_s {
@@ -310,11 +311,12 @@
 	ide_drive_t	drives[MAX_DRIVES];	/* drive info */
 	struct gendisk	*gd;		/* gendisk structure */
 	ide_tuneproc_t	*tuneproc;	/* routine to tune PIO mode for drives */
-#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030)
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PDC4030) || defined(CONFIG_BLK_DEV_TRM290)
 	ide_selectproc_t *selectproc;	/* tweaks hardware to select drive */
 #endif
 	ide_dmaproc_t	*dmaproc;	/* dma read/write/abort routine */
 	unsigned long	*dmatable;	/* dma physical region descriptor table */
+	struct hwif_s	*mate;		/* other hwif from same PCI chip */
 	unsigned short	dma_base;	/* base addr for dma ports (triton) */
 	int		irq;		/* our irq number */
 	byte		major;		/* our major number */
@@ -329,6 +331,7 @@
 	unsigned	is_pdc4030_2: 1;/* 2nd i/f on pdc4030 */
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 	unsigned	reset      : 1; /* reset after probe */
+	unsigned	pci_port   : 1; /* for dual-port chips: 0=primary, 1=secondary */
 #if (DISK_RECOVERY_TIME > 0)
 	unsigned long	last_time;	/* time when previous rq was done */
 #endif
@@ -609,6 +612,12 @@
 ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n);
 int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
 int ide_unregister_subdriver (ide_drive_t *drive);
+
+#ifdef CONFIG_BLK_DEV_IDEDMA
+int ide_build_dmatable (ide_drive_t *drive);
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
+void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports);
+#endif
 
 #ifdef CONFIG_BLK_DEV_IDE
 int ideprobe_init (void);
diff -u --recursive --new-file v2.1.70/linux/drivers/block/nbd.c linux/drivers/block/nbd.c
--- v2.1.70/linux/drivers/block/nbd.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/block/nbd.c	Wed Dec  3 15:26:21 1997
@@ -78,7 +78,7 @@
 	oldfs = get_fs();
 	set_fs(get_ds());
 	do {
-		int save;
+		sigset_t oldset;
 
 		iov.iov_base = buf;
 		iov.iov_len = size;
@@ -91,13 +91,22 @@
 		msg.msg_namelen = 0;
 		msg.msg_flags = 0;
 
-		save = current->blocked;
-		current->blocked = ~0UL;
+		spin_lock_irq(&current->sigmask_lock);
+		oldset = current->blocked;
+		sigfillset(&current->blocked);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+
 		if (send)
 			result = sock_sendmsg(sock, &msg, size);
 		else
 			result = sock_recvmsg(sock, &msg, size, 0);
-		current->blocked = save;
+
+		spin_lock_irq(&current->sigmask_lock);
+		current->blocked = oldset;
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+
 		if (result <= 0) {
 #ifdef PARANOIA
 			printk(KERN_ERR "NBD: %s - sock=%d at buf=%d, size=%d returned %d.\n",
diff -u --recursive --new-file v2.1.70/linux/drivers/block/opti621.c linux/drivers/block/opti621.c
--- v2.1.70/linux/drivers/block/opti621.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/opti621.c	Wed Dec  3 04:39:27 1997
@@ -233,7 +233,7 @@
 
 	drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
 
-	cycle1 = ((first.data_time-1)<<4) | (first.recovery_time-2);
+	cycle1 = ((first.data_time-1)<<4)  | (first.recovery_time-2);
 	cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
 	misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
 
@@ -276,11 +276,9 @@
 /*
  * ide_init_opti621() is Called from idedma.c once for each hwif found at boot.
  */
-void ide_init_opti621 (byte bus, byte fn, ide_hwif_t *hwifs)
+void ide_init_opti621 (byte bus, byte fn, ide_hwif_t *hwif)
 {
-	if (hwif->io_ports[IDE_DATA_OFFSET]) {
-		hwif->drives[0].timing_data = PIO_DONT_KNOW;
-		hwif->drives[1].timing_data = PIO_DONT_KNOW;
-		hwif->tuneproc = &opti621_tune_drive;
-	}
+	hwif->drives[0].timing_data = PIO_DONT_KNOW;
+	hwif->drives[1].timing_data = PIO_DONT_KNOW;
+	hwif->tuneproc = &opti621_tune_drive;
 }
diff -u --recursive --new-file v2.1.70/linux/drivers/block/pdc4030.c linux/drivers/block/pdc4030.c
--- v2.1.70/linux/drivers/block/pdc4030.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/block/pdc4030.c	Wed Dec  3 04:36:30 1997
@@ -169,8 +169,10 @@
             default:   hwif->irq = 15; break;
 	}
 	printk("on IRQ %d\n",hwif->irq);
-	hwif->chipset    = second_hwif->chipset    = ide_pdc4030;
-	hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
+	hwif->chipset     = second_hwif->chipset    = ide_pdc4030;
+	hwif->mate        = second_hwif;
+	second_hwif->mate = hwif;
+	hwif->selectproc  = second_hwif->selectproc = &promise_selectproc;
 /* Shift the remaining interfaces down by one */
 	for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
 		ide_hwif_t *h = &ide_hwifs[i];
diff -u --recursive --new-file v2.1.70/linux/drivers/block/qd6580.c linux/drivers/block/qd6580.c
--- v2.1.70/linux/drivers/block/qd6580.c	Sun Aug  4 22:12:25 1996
+++ linux/drivers/block/qd6580.c	Wed Dec  3 04:36:30 1997
@@ -63,4 +63,6 @@
 	ide_hwifs[0].chipset = ide_qd6580;
 	ide_hwifs[1].chipset = ide_qd6580;
 	ide_hwifs[0].tuneproc = &tune_qd6580;
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
 }
diff -u --recursive --new-file v2.1.70/linux/drivers/block/trm290.c linux/drivers/block/trm290.c
--- v2.1.70/linux/drivers/block/trm290.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/trm290.c	Wed Dec  3 04:36:30 1997
@@ -0,0 +1,227 @@
+/*
+ *  linux/drivers/block/trm290.c	Version 1.00  December 3, 1997
+ *
+ *  Copyright (c) 1997-1998  Mark Lord
+ *  May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the bus-master IDE DMA function
+ * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
+ * including a "Precision Instruments" board.  The TRM290 pre-dates
+ * the sff-8038 standard (ide-dma.c) by a few months, and differs
+ * significantly enough to warrant separate routines for some functions,
+ * while re-using others from ide-dma.c.
+ *
+ * EXPERIMENTAL!  It works for me (a sample of one).
+ *
+ * Works reliably for me in DMA mode (READs only),
+ * DMA WRITEs are disabled by default (see #define below);
+ *
+ * DMA is not enabled automatically for this chipset,
+ * but can be turned on manually (with "hdparm -d1") at run time.
+ *
+ * I need volunteers with "spare" drives for further testing
+ * and development, and maybe to help figure out the peculiarities.
+ * Even knowing the registers (below), some things behave strangely.
+ */
+
+#define TRM290_NO_DMA_WRITES	/* DMA writes seem unreliable sometimes */
+
+/*
+ * TRM-290 PCI-IDE2 Bus Master Chip
+ * ================================
+ * The configuration registers are addressed in normal I/O port space
+ * and are used as follows:
+ *
+ * 0x3df2 when WRITTEN: chiptest register (byte, write-only)
+ *	bit7 must always be written as "1"
+ *	bits6-2 undefined
+ *	bit1 1=legacy_compatible_mode, 0=native_pci_mode
+ *	bit0 1=test_mode, 0=normal(default)
+ *
+ * 0x3df2 when READ: status register (byte, read-only)
+ *	bits7-2 undefined
+ *	bit1 channel0 busmaster interrupt status 0=none, 1=asserted
+ *	bit0 channel0 interrupt status 0=none, 1=asserted
+ *
+ * 0x3df3 Interrupt mask register
+ *	bits7-5 undefined
+ *	bit4 legacy_header: 1=present, 0=absent
+ *	bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
+ *	bit2 channel1 interrupt status 0=none, 1=asserted (read only)
+ *	bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
+ *	bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
+ *
+ * 0x3df1 "CPR" Config Pointer Register (byte)
+ *	bit7 1=autoincrement CPR bits 2-0 after each access of CDR
+ *	bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
+ *	bit5 0=enabled master burst access (default), 1=disable  (write only)
+ *	bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
+ *	bit3 0=primary IDE channel, 1=secondary IDE channel
+ *	bits2-0 register index for accesses through CDR port
+ *
+ * 0x3df0 "CDR" Config Data Register (word)
+ *	two sets of seven config registers,
+ *	selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
+ *	each index defined below:
+ *
+ * Index-0 Base address register for command block (word)
+ *	defaults: 0x1f0 for primary, 0x170 for secondary
+ *
+ * Index-1 general config register (byte)
+ *	bit7 1=DMA enable, 0=DMA disable
+ *	bit6 1=activate IDE_RESET, 0=no action (default)
+ *	bit5 1=enable IORDY, 0=disable IORDY (default)
+ *	bit4 0=16-bit data port(default), 1=8-bit (XT) data port
+ *	bit3 interrupt polarity: 1=active_low, 0=active_high(default)
+ *	bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
+ *	bit1 bus_master_mode(?): 1=enable, 0=disable(default)
+ *	bit0 enable_io_ports: 1=enable(default), 0=disable
+ *
+ * Index-2 read-ahead counter preload bits 0-7 (byte, write only)
+ *	bits7-0 bits7-0 of readahead count
+ *
+ * Index-3 read-ahead config register (byte, write only)
+ *	bit7 1=enable_readahead, 0=disable_readahead(default)
+ *	bit6 1=clear_FIFO, 0=no_action
+ *	bit5 undefined
+ *	bit4 mode4 timing control: 1=enable, 0=disable(default)
+ *	bit3 undefined
+ *	bit2 undefined
+ *	bits1-0 bits9-8 of read-ahead count
+ *
+ * Index-4 base address register for control block (word)
+ *	defaults: 0x3f6 for primary, 0x376 for secondary
+ *
+ * Index-5 data port timings (shared by both drives) (byte)
+ *	standard PCI "clk" (clock) counts, default value = 0xf5
+ *
+ *	bits7-6 setup time:  00=1clk, 01=2clk, 10=3clk, 11=4clk
+ *	bits5-3 hold time:	000=1clk, 001=2clk, 010=3clk,
+ *				011=4clk, 100=5clk, 101=6clk,
+ *				110=8clk, 111=12clk
+ *	bits2-0 active time:	000=2clk, 001=3clk, 010=4clk,
+ *				011=5clk, 100=6clk, 101=8clk,
+ *				110=12clk, 111=16clk
+ *
+ * Index-6 command/control port timings (shared by both drives) (byte)
+ *	same layout as Index-5, default value = 0xde
+ *
+ * Suggested CDR programming for PIO mode0 (600ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde	; secondary
+ *
+ * Suggested CDR programming for PIO mode3 (180ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0x09,0xde	; secondary
+ *
+ * Suggested CDR programming for PIO mode4 (120ns):
+ *	0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde	; primary
+ *	0x0170,0x21,0xff,0x80,0x0376,0x00,0xde	; secondary
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+
+#include "ide.h"
+
+static void select_dma_or_pio(ide_hwif_t *hwif, int dma)
+{
+	static int previous[2] = {-1,-1};
+	unsigned long flags;
+
+	if (previous[hwif->pci_port] != dma) {
+		unsigned short cfg1 = dma ? 0xa3 : 0x21;
+		previous[hwif->pci_port] = dma;
+		save_flags(flags);
+		cli();
+		outb(0x51|(hwif->pci_port<<3),0x3df1);
+		outw(cfg1,0x3df0);
+		restore_flags(flags);
+	}
+}
+
+/*
+ * trm290_dma_intr() is the handler for trm290 disk read/write DMA interrupts
+ */
+static void trm290_dma_intr (ide_drive_t *drive)
+{
+	byte stat;
+	int i;
+	struct request *rq = HWGROUP(drive)->rq;
+
+	stat = GET_STAT();	/* get drive status */
+	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+		unsigned short dma_stat = inw(HWIF(drive)->dma_base + 2);
+		if (dma_stat == 0x00ff) {
+			rq = HWGROUP(drive)->rq;
+			for (i = rq->nr_sectors; i > 0;) {
+				i -= rq->current_nr_sectors;
+				ide_end_request(1, HWGROUP(drive));
+			}
+			return;
+		}
+		printk("%s: bad trm290 DMA status: 0x%04x\n", drive->name, dma_stat);
+	}
+	sti();
+	ide_error(drive, "dma_intr", stat);
+}
+
+static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	unsigned int count, reading = 1 << 1;
+
+	if (drive->media == ide_disk) {
+		switch (func) {
+			case ide_dma_write:
+				reading = 0;
+#ifdef TRM290_NO_DMA_WRITES
+				break;	/* always use PIO for writes */
+#endif
+			case ide_dma_read:
+				if (!(count = ide_build_dmatable(drive)))
+					break;		/* try PIO instead of DMA */
+				select_dma_or_pio(hwif, 1);	/* select DMA mode */
+				outl_p(virt_to_bus(hwif->dmatable)|reading, hwif->dma_base);
+				outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
+				ide_set_handler(drive, &trm290_dma_intr, WAIT_CMD);
+				OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+				return 0;
+			default:
+				return ide_dmaproc(func, drive);
+		}
+	}
+	select_dma_or_pio(hwif, 0);	/* force PIO mode for this operation */
+	return 1;
+}
+
+static void trm290_selectproc (ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+
+	select_dma_or_pio(hwif, drive->using_dma);
+	OUT_BYTE(drive->select.all, hwif->io_ports[IDE_SELECT_OFFSET]);
+}
+
+/*
+ * Invoked from ide-dma.c at boot time.
+ */
+__initfunc(void ide_init_trm290 (byte bus, byte fn, ide_hwif_t *hwif))
+{
+	hwif->chipset = ide_trm290;
+	hwif->selectproc = trm290_selectproc;
+	hwif->drives[0].autotune = 2;	/* play it safe */
+	hwif->drives[1].autotune = 2;	/* play it safe */
+	ide_setup_dma(hwif, hwif->pci_port ? 0x3d74 : 0x3df4, 2);
+	hwif->dmaproc = &trm290_dmaproc;
+}
diff -u --recursive --new-file v2.1.70/linux/drivers/block/umc8672.c linux/drivers/block/umc8672.c
--- v2.1.70/linux/drivers/block/umc8672.c	Tue Oct 29 05:28:40 1996
+++ linux/drivers/block/umc8672.c	Wed Dec  3 04:36:30 1997
@@ -151,5 +151,6 @@
 	ide_hwifs[1].chipset = ide_umc8672;
 	ide_hwifs[0].tuneproc = &tune_umc;
 	ide_hwifs[1].tuneproc = &tune_umc;
-
+	ide_hwifs[0].mate = &ide_hwifs[1];
+	ide_hwifs[1].mate = &ide_hwifs[0];
 }
diff -u --recursive --new-file v2.1.70/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v2.1.70/linux/drivers/cdrom/aztcd.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/aztcd.c	Wed Dec  3 04:29:41 1997
@@ -160,6 +160,8 @@
 		Werner Zimmermann, Nov 29, 97
 */
 
+#include <linux/version.h>
+
 #define MAJOR_NR AZTECH_CDROM_MAJOR 
 
 #include <linux/blk.h>
diff -u --recursive --new-file v2.1.70/linux/drivers/cdrom/aztcd.h linux/drivers/cdrom/aztcd.h
--- v2.1.70/linux/drivers/cdrom/aztcd.h	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/aztcd.h	Wed Dec  3 04:29:41 1997
@@ -1,7 +1,7 @@
-/* $Id: aztcd.h,v 2.50 1996/05/17 16:15:43 root Exp root $
+/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $
  *
  * Definitions for a AztechCD268 CD-ROM interface
- *	Copyright (C) 1994, 1995  Werner Zimmermann
+ *	Copyright (C) 1994-98  Werner Zimmermann
  *
  *	based on Mitsumi CDROM driver by Martin Harriss
  *
@@ -20,11 +20,16 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
- *		October 1994 Email: zimmerma@rz.fht-esslingen.de
+ *		October 1994 Email: Werner.Zimmermann@fht-esslingen.de
  */
 
-/* *** change this to set the I/O port address of your CD-ROM drive*/
-#define AZT_BASE_ADDR		0x320
+/* *** change this to set the I/O port address of your CD-ROM drive,
+       set to '-1', if you want autoprobing */
+#define AZT_BASE_ADDR		-1
+
+/* list of autoprobing addresses (not more than 15), last value must be 0x000
+   Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */
+#define AZT_BASE_AUTO 		{ 0x320, 0x300, 0x310, 0x330, 0x000 }
 
 /* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard
    and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */
@@ -54,6 +59,9 @@
   chapter '5.1 Multisession support' in README.aztcd for details. Normally it's 
   uncritical to leave this setting untouched */
 #define AZT_MULTISESSION        1
+
+/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */
+/*#define AZT_KERNEL_PRIOR_2_1 */
 
 /*---------------------------------------------------------------------------*/
 /*-----nothing to be configured for normal applications below this line------*/
diff -u --recursive --new-file v2.1.70/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- v2.1.70/linux/drivers/cdrom/cdrom.c	Tue Dec  2 16:45:18 1997
+++ linux/drivers/cdrom/cdrom.c	Wed Dec  3 10:40:13 1997
@@ -111,8 +111,6 @@
 static int cdrom_ioctl(struct inode *ip, struct file *fp,
 				unsigned int cmd, unsigned long arg);
 static int cdrom_media_changed(kdev_t dev);
-static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
-                           void *buffer, size_t *lenp);
 static int open_for_data(struct cdrom_device_info * cdi);
 static int check_for_audio_disc(struct cdrom_device_info * cdi,
 			 struct cdrom_device_ops * cdo);
@@ -307,11 +305,15 @@
 		ret=-ENOMEDIUM;
 		goto clean_up_and_return;
 	}
+#if 0
+	/* this breaks CD-Players which don't use O_NONBLOCK, workman
+	 * for example, probably others too */
 	if (cdi->options & CDO_CHECK_TYPE && tracks.data==0) {
 		cdinfo(CD_OPEN, "bummer. wrong media type...\n"); 
 		ret=-EMEDIUMTYPE;
 		goto clean_up_and_return;
 	}
+#endif
 
 	cdinfo(CD_OPEN, "all seems well, opening the device...\n"); 
 
@@ -879,24 +881,85 @@
 EXPORT_SYMBOL(cdrom_fops);
 
 #ifdef CONFIG_SYSCTL
-#define CDROM_STR_SIZE 1000
-static char cdrom_drive_info[CDROM_STR_SIZE]="info\n";
 
-#ifdef MODULE
+#define CDROM_STR_SIZE 1000
 
-static struct ctl_table_header *cdrom_sysctl_header;
+static char cdrom_drive_info[CDROM_STR_SIZE]="info\n";
 
-static void cdrom_sysctl_register(void)
+int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
+                           void *buffer, size_t *lenp)
 {
-	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 0);
-}
+        int retv,pos;
+	struct cdrom_device_info *cdi;
 
-static void cdrom_sysctl_unregister(void)
-{
-	unregister_sysctl_table(cdrom_sysctl_header);
-}
+        pos = sprintf(cdrom_drive_info, "CD-ROM information\n");
+	
+	pos += sprintf(cdrom_drive_info+pos, "\ndrive name:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%s", cdi->name);
+
+	pos += sprintf(cdrom_drive_info+pos, "\ndrive speed:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->speed);
+
+	pos += sprintf(cdrom_drive_info+pos, "\ndrive # of slots:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d", cdi->capacity);
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan close tray:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_CLOSE_TRAY)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan open tray:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_OPEN_TRAY)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan lock tray:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_LOCK)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan change speed:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_SELECT_SPEED)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan select disk:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_SELECT_DISC)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan read multisession:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_MULTI_SESSION)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan read MCN:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_MCN)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nReports media changed:");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_MEDIA_CHANGED)!=0));
+
+	pos += sprintf(cdrom_drive_info+pos, "\nCan play audio:\t");
+	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next)
+	    pos += sprintf(cdrom_drive_info+pos, "\t%d",
+			   ((cdi->ops->capability & CDC_PLAY_AUDIO)!=0));
 
-#endif
+        strcpy(cdrom_drive_info+pos,"\n\n");
+	*lenp=pos+3;
+        if (!write) {
+        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
+        }
+        else
+        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
+        return retv;
+}
 
 /* Place files in /proc/sys/dev/cdrom */
 ctl_table cdrom_table[] = {
@@ -916,57 +979,26 @@
 	{0}
 	};
 
+#endif /* endif CONFIG_SYSCTL */
 
-int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp,
-                           void *buffer, size_t *lenp)
-{
-        int retv;
-	char buf[40];
-	struct cdrom_device_info *cdi;
 
-        sprintf(cdrom_drive_info, "CD-ROM information\n\n");
-	for (cdi=topCdromPtr;cdi!=NULL;cdi=cdi->next){
-           sprintf(buf, "drive name:\t\t%s\n", cdi->name);
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "drive speed:\t\t%d\n", cdi->speed);
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "drive # of slots:\t%d\n", cdi->capacity);
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can close tray:\t\t%d\n", ((cdi->ops->capability & CDC_CLOSE_TRAY)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can open tray:\t\t%d\n", ((cdi->ops->capability & CDC_OPEN_TRAY)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can lock tray:\t\t%d\n", ((cdi->ops->capability & CDC_LOCK)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can change speed:\t%d\n", ((cdi->ops->capability & CDC_SELECT_SPEED)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can select disk:\t%d\n", ((cdi->ops->capability & CDC_SELECT_DISC)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can read multisession:\t%d\n", ((cdi->ops->capability & CDC_MULTI_SESSION)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can read MCN:\t\t%d\n", ((cdi->ops->capability & CDC_MCN)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Reports media changed:\t%d\n", ((cdi->ops->capability & CDC_MEDIA_CHANGED)!=0));
-           strcat(cdrom_drive_info, buf);
-           sprintf(buf, "Can play audio:\t\t%d\n", ((cdi->ops->capability & CDC_PLAY_AUDIO)!=0));
-           strcat(cdrom_drive_info, buf);
-           strcat(cdrom_drive_info, "\n\n");
-	}
-	*lenp=sizeof(cdrom_drive_info);
+#ifdef MODULE
 
-        if (!write) {
-        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
-        }
-        else
-        	retv = proc_dostring(ctl, write, filp, buffer, lenp);
-        return retv;
-}
+#ifdef CONFIG_SYSCTL
 
+static struct ctl_table_header *cdrom_sysctl_header;
 
-#endif /* endif CONFIG_SYSCTL */
+static void cdrom_sysctl_register(void)
+{
+	cdrom_sysctl_header = register_sysctl_table(cdrom_root_table, 0);
+}
 
+static void cdrom_sysctl_unregister(void)
+{
+	unregister_sysctl_table(cdrom_sysctl_header);
+}
+#endif /* endif CONFIG_SYSCTL */
 
-#ifdef MODULE
 int init_module(void)
 {
 	cdrom_init();
diff -u --recursive --new-file v2.1.70/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.70/linux/drivers/char/Config.in	Tue Dec  2 16:45:18 1997
+++ linux/drivers/char/Config.in	Wed Dec  3 15:21:57 1997
@@ -6,16 +6,18 @@
 
 bool 'Virtual terminal' CONFIG_VT
 if [ "$CONFIG_VT" = "y" ]; then
-  bool 'Console on virtual terminal' CONFIG_VT_CONSOLE
+  bool 'Support for console on virtual terminal' CONFIG_VT_CONSOLE
 fi
 tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
+if [ "$CONFIG_SERIAL" = "y" ]; then
+   bool '   Support for console on serial port' CONFIG_SERIAL_CONSOLE
+fi
 bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED
 if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
    bool '   Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
    bool '   Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
    bool '   Support special multiport boards' CONFIG_SERIAL_MULTIPORT
    bool '   Support the Bell Technologies HUB6 card' CONFIG_HUB6
-   bool '   Console on serial port' CONFIG_SERIAL_CONSOLE
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
diff -u --recursive --new-file v2.1.70/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.70/linux/drivers/char/console.c	Thu Jun 26 12:33:38 1997
+++ linux/drivers/char/console.c	Wed Dec  3 15:21:57 1997
@@ -158,6 +158,7 @@
 extern void compute_shiftstate(void);
 extern void reset_palette(int currcons);
 extern void set_palette(void);
+extern int con_is_present(void);
 extern unsigned long con_type_init(unsigned long, const char **);
 extern void con_type_init_finish(void);
 extern int set_get_cmap(unsigned char *, int);
@@ -1836,7 +1837,7 @@
 }
 
 #ifdef CONFIG_VT_CONSOLE
-void vt_console_print(const char * b, unsigned count)
+void vt_console_print(struct console *co, const char * b, unsigned count)
 {
 	int currcons = fg_console;
 	unsigned char c;
@@ -1885,16 +1886,25 @@
 	printing = 0;
 }
 
-static int vt_console_device(void)
+static kdev_t vt_console_device(struct console *c)
 {
-	return MKDEV(TTY_MAJOR, fg_console + 1);
+	return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1);
 }
 
-extern void keyboard_wait_for_keypress(void);
+extern int keyboard_wait_for_keypress(struct console *);
 
 struct console vt_console_driver = {
-	vt_console_print, do_unblank_screen,
-        keyboard_wait_for_keypress, vt_console_device
+	"tty",
+	vt_console_print,
+	NULL,
+	vt_console_device,
+	keyboard_wait_for_keypress,
+	do_unblank_screen,
+	NULL,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
 };
 #endif
 
@@ -1983,6 +1993,9 @@
  *
  * Reads the information preserved by setup.s to determine the current display
  * type and sets everything accordingly.
+ *
+ * FIXME: return early if we don't _have_ a video card installed.
+ *
  */
 __initfunc(unsigned long con_init(unsigned long kmem_start))
 {
@@ -2117,7 +2130,7 @@
 	 * within the bus probing code... :-(
 	 */
 #ifdef CONFIG_VT_CONSOLE
-	if (video_type != VIDEO_TYPE_TGAC)
+	if (video_type != VIDEO_TYPE_TGAC && con_is_present())
 		register_console(&vt_console_driver);
 #endif
 
diff -u --recursive --new-file v2.1.70/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- v2.1.70/linux/drivers/char/cyclades.c	Tue Dec  2 16:45:19 1997
+++ linux/drivers/char/cyclades.c	Thu Dec  4 13:09:01 1997
@@ -1,6 +1,6 @@
 #define BLOCKMOVE
 static char rcsid[] =
-"$Revision: 2.1 $$Date: 1997/11/01 17:42:41 $";
+"$Revision: 2.1.1.1 $$Date: 1997/12/03 17:31:19 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -30,6 +30,12 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.1.1.1  1997/12/03 17:31:19 ivan
+ * Code review for the module cleanup routine (fixed memory leak);
+ * fixed RTS and DTR status report for new CD1400's in get_modem_info;
+ * purged conditional code for older kernels;
+ * includes anonymous changes regarding signal_pending
+ * 
  * Revision 2.1  1997/11/01 17:42:41 ivan
  * Changes in the driver to support Alpha systems (except 8Zo V_1);
  * BREAK fix for the Cyclades-Z boards;
@@ -524,8 +530,6 @@
 
 #include <linux/version.h>
 
-#if (LINUX_VERSION_CODE >= 0x020100)
-
 #include <asm/uaccess.h>
 #include <linux/init.h>
 
@@ -540,17 +544,6 @@
 	return result;
 }
 
-#else
-
-#define __initfunc(__arginit)	__arginit
-#define copy_from_user		memcpy_fromfs
-#define copy_to_user		memcpy_tofs
-#define cy_get_user		get_fs_long
-#define cy_put_user		put_fs_long
-#define ioremap			vremap
-
-#endif
-
 #ifndef MIN
 #define MIN(a,b)        ((a) < (b) ? (a) : (b))
 #endif
@@ -875,7 +868,6 @@
     if (!tty)
         return;
 
-#if (LINUX_VERSION_CODE >= 0x020125)
     if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) {
         tty_hangup(info->tty);
         wake_up_interruptible(&info->open_wait);
@@ -892,24 +884,6 @@
         }
         wake_up_interruptible(&tty->write_wait);
     }
-#else
-    if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
-        tty_hangup(info->tty);
-        wake_up_interruptible(&info->open_wait);
-        info->flags &= ~(ASYNC_NORMAL_ACTIVE|
-                             ASYNC_CALLOUT_ACTIVE);
-    }
-    if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
-        wake_up_interruptible(&info->open_wait);
-    }
-    if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
-        if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
-        && tty->ldisc.write_wakeup){
-            (tty->ldisc.write_wakeup)(tty);
-        }
-        wake_up_interruptible(&tty->write_wait);
-    }
-#endif
 } /* do_softint */
 
 
@@ -2339,7 +2313,7 @@
 			break;
 		}
 	    restore_flags(flags);
-	    if (signal_pending(current))  {
+	    if (signal_pending(current)) {
 		retval = -ERESTARTSYS;
 		break;
 	    }
@@ -3318,12 +3292,18 @@
 	    status |= cy_readb(base_addr+(CyMSVR2<<index));
 	restore_flags(flags);
 
-	result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
-		| ((status  & CyDTR) ? TIOCM_DTR : 0)
-		| ((status  & CyDCD) ? TIOCM_CAR : 0)
-		| ((status  & CyRI) ? TIOCM_RNG : 0)
-		| ((status  & CyDSR) ? TIOCM_DSR : 0)
-		| ((status  & CyCTS) ? TIOCM_CTS : 0);
+
+        if (info->rtsdtr_inv) {
+	    result =  ((status  & CyRTS) ? TIOCM_DTR : 0)
+		    | ((status  & CyDTR) ? TIOCM_RTS : 0);
+	} else {
+	    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
+		    | ((status  & CyDTR) ? TIOCM_DTR : 0);
+	}
+	result |=  ((status  & CyDCD) ? TIOCM_CAR : 0)
+		 | ((status  & CyRI) ? TIOCM_RNG : 0)
+		 | ((status  & CyDSR) ? TIOCM_DSR : 0)
+		 | ((status  & CyCTS) ? TIOCM_CTS : 0);
     } else {
 	base_addr = (unsigned char*) (cy_card[card].base_addr);
 
@@ -4329,7 +4309,7 @@
 
                 /* probe for CD1400... */
 
-#if !defined(__alpha__) && (LINUX_VERSION_CODE >= 0x020100)
+#if !defined(__alpha__) 
 		cy_isa_address = ioremap((unsigned int)cy_isa_address,
                                                        CyISA_Ywin);
 #endif
@@ -4483,9 +4463,6 @@
 	            continue;
                 }
 #else
-#if (LINUX_VERSION_CODE < 0x020100) 
-                if ((ulong)cy_pci_addr2 >= 0x100000)  /* above 1M? */
-#endif
                     cy_pci_addr2 = (ulong) ioremap(cy_pci_addr2, CyPCI_Ywin);
 #endif
 
@@ -5098,10 +5075,13 @@
     save_flags(flags);
     cli();
     remove_bh(CYCLADES_BH);
+
+    free_page((unsigned long)tmp_buf);
     if (tty_unregister_driver(&cy_callout_driver))
             printk("Couldn't unregister Cyclom callout driver\n");
     if (tty_unregister_driver(&cy_serial_driver))
             printk("Couldn't unregister Cyclom serial driver\n");
+
     restore_flags(flags);
 
     for (i = 0; i < NR_CARDS; i++) {
diff -u --recursive --new-file v2.1.70/linux/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- v2.1.70/linux/drivers/char/keyboard.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/char/keyboard.c	Wed Dec  3 15:21:57 1997
@@ -63,10 +63,12 @@
 extern void scrollfront(int);
 
 struct wait_queue * keypress_wait = NULL;
+struct console;
 
-void keyboard_wait_for_keypress(void)
+int keyboard_wait_for_keypress(struct console *co)
 {
 	sleep_on(&keypress_wait);
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.70/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.1.70/linux/drivers/char/n_tty.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/char/n_tty.c	Wed Dec  3 15:21:57 1997
@@ -40,6 +40,7 @@
 #include <asm/bitops.h>
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
+#define SYSCONS_DEV  MKDEV(TTYAUX_MAJOR,1)
 
 #ifndef MIN
 #define MIN(a,b)	((a) < (b) ? (a) : (b))
@@ -854,6 +855,7 @@
 	   check of the logic of this change. -- jlc */
 	/* don't stop on /dev/console */
 	if (file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
+	    file->f_dentry->d_inode->i_rdev != SYSCONS_DEV &&
 	    current->tty == tty) {
 		if (tty->pgrp <= 0)
 			printk("read_chan: tty->pgrp <= 0!\n");
@@ -1012,7 +1014,8 @@
 	ssize_t retval = 0, num;
 
 	/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-	if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV) {
+	if (L_TOSTOP(tty) && file->f_dentry->d_inode->i_rdev != CONSOLE_DEV &&
+	    file->f_dentry->d_inode->i_rdev != SYSCONS_DEV) {
 		retval = tty_check_change(tty);
 		if (retval)
 			return retval;
diff -u --recursive --new-file v2.1.70/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.70/linux/drivers/char/serial.c	Tue Dec  2 09:49:39 1997
+++ linux/drivers/char/serial.c	Wed Dec  3 15:21:57 1997
@@ -49,6 +49,9 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/console.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -152,6 +155,9 @@
 static volatile int rs_irq_triggered;
 static volatile int rs_triggered;
 static int rs_wild_int_mask;
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console sercons;
+#endif
 
 static void autoconfig(struct serial_state * info);
 static void change_speed(struct async_struct *info);
@@ -2752,7 +2758,13 @@
 			*tty->termios = info->state->callout_termios;
 		change_speed(info);
 	}
-
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (sercons.cflag && sercons.index == line) {
+		tty->termios->c_cflag = sercons.cflag;
+		sercons.cflag = 0;
+		change_speed(info);
+	}
+#endif
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
@@ -3195,7 +3207,18 @@
 		       sizeof(struct rs_multiport_struct));
 #endif
 	}
-	
+#ifdef CONFIG_SERIAL_CONSOLE
+	/*
+	 *	The interrupt of the serial console port
+	 *	can't be shared.
+	 */
+	if (sercons.flags & CON_FIRST) {
+		for(i = 0; i < NR_PORTS; i++)
+			if (i != sercons.index &&
+			    rs_table[i].irq == rs_table[sercons.index].irq)
+				rs_table[i].irq = 0;
+	}
+#endif
 	show_serial_version();
 
 	/* Initialize the tty_driver structure */
@@ -3398,53 +3421,51 @@
  */
 #ifdef CONFIG_SERIAL_CONSOLE
 
-#include <linux/console.h>
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 
 /*
- * this defines the index into rs_table for the port to use
+ *	Wait for transmitter & holding register to empty
  */
-#ifndef CONFIG_SERIAL_CONSOLE_PORT
-#define CONFIG_SERIAL_CONSOLE_PORT	0
-#endif
-
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
-/* Wait for transmitter & holding register to empty */
 static inline void wait_for_xmitr(struct serial_state *ser)
 {
 	int lsr;
+	unsigned int tmout = 1000000;
+
 	do {
 		lsr = inb(ser->port + UART_LSR);
+		if (--tmout == 0) break;
 	} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
 }
 
 /*
- * Print a string to the serial port trying not to disturb any possible
- * real use of the port...
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
  */
-static void serial_console_write(const char *s, unsigned count)
+static void serial_console_write(struct console *co, const char *s,
+				unsigned count)
 {
 	struct serial_state *ser;
 	int ier;
 	unsigned i;
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	ser = rs_table + co->index;
 	/*
-	 * First save the IER then disable the interrupts
+	 *	First save the IER then disable the interrupts
 	 */
 	ier = inb(ser->port + UART_IER);
 	outb(0x00, ser->port + UART_IER);
 
 	/*
-	 * Now, do each character
+	 *	Now, do each character
 	 */
 	for (i = 0; i < count; i++, s++) {
 		wait_for_xmitr(ser);
 
-		/* Send the character out. */
+		/*
+		 *	Send the character out.
+		 *	If a LF, also do CR...
+		 */
 		outb(*s, ser->port + UART_TX);
-
-		/* if a LF, also do CR... */
 		if (*s == 10) {
 			wait_for_xmitr(ser);
 			outb(13, ser->port + UART_TX);
@@ -3452,29 +3473,29 @@
 	}
 
 	/*
-	 * Finally, Wait for transmitter & holding register to empty
-	 *  and restore the IER
+	 *	Finally, Wait for transmitter & holding register to empty
+	 * 	and restore the IER
 	 */
 	wait_for_xmitr(ser);
 	outb(ier, ser->port + UART_IER);
 }
 
 /*
- * Receive character from the serial port
+ *	Receive character from the serial port
  */
-static void serial_console_wait_key(void)
+static int serial_console_wait_key(struct console *co)
 {
 	struct serial_state *ser;
 	int ier;
 	int lsr;
 	int c;
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	ser = rs_table + co->index;
 
 	/*
-	 * First save the IER then disable the interrupts so
-	 * that the real driver for the port does not get the
-	 * character.
+	 *	First save the IER then disable the interrupts so
+	 *	that the real driver for the port does not get the
+	 *	character.
 	 */
 	ier = inb(ser->port + UART_IER);
 	outb(0x00, ser->port + UART_IER);
@@ -3484,39 +3505,142 @@
 	} while (!(lsr & UART_LSR_DR));
 	c = inb(ser->port + UART_RX);
 
-	/* Restore the interrupts */
+	/*
+	 *	Restore the interrupts
+	 */
 	outb(ier, ser->port + UART_IER);
+
+	return c;
 }
 
-static int serial_console_device(void)
+static kdev_t serial_console_device(struct console *c)
 {
-	return MKDEV(TTYAUX_MAJOR, 64 + CONFIG_SERIAL_CONSOLE_PORT);
+	return MKDEV(TTY_MAJOR, 64 + c->index);
 }
 
-long serial_console_init(long kmem_start, long kmem_end)
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first rs_open()
+ *	- initialize the serial port
+ */
+__initfunc(static void serial_console_setup(struct console *co, char *options))
 {
-	static struct console console = {
-		serial_console_write, 0,
-                serial_console_wait_key, serial_console_device
-	};
 	struct serial_state *ser;
+	unsigned cval;
+	int	baud = 9600;
+	int	bits = 8;
+	int	parity = 'n';
+	int	cflag = CREAD | HUPCL | CLOCAL;
+	int	quot = 0;
+	char	*s;
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s) parity = *s++;
+		if (*s) bits   = *s - '0';
+	}
 
-	ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT;
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 2400:
+			cflag |= B2400;
+			break;
+		case 4800:
+			cflag |= B4800;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 57600:
+			cflag |= B57600;
+			break;
+		case 115200:
+			cflag |= B115200;
+			break;
+		case 9600:
+		default:
+			cflag |= B9600;
+			break;
+	}
+	switch(bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		default:
+		case 8:
+			cflag |= CS8;
+			break;
+	}
+	switch(parity) {
+		case 'o': case 'O':
+			cflag |= PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+	co->cflag = cflag;
 
-	/* Disable all interrupts, it works in polled mode */
-	outb(0x00, ser->port + UART_IER); 
+	/*
+	 *	Divisor, bytesize and parity
+	 */
+	ser = rs_table + co->index;
+	quot = BASE_BAUD / baud;
+	cval = cflag & (CSIZE | CSTOPB);
+#if defined(__powerpc__) || defined(__alpha__)
+	cval >>= 8;
+#else /* !__powerpc__ && !__alpha__ */
+	cval >>= 4;
+#endif /* !__powerpc__ && !__alpha__ */
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
 
 	/*
-	 * now do hardwired init
+	 *	Disable UART interrupts, set DTR and RTS high
+	 *	and set speed.
 	 */
-	outb(0x03, ser->port + UART_LCR); /* No parity, 8 data bits, 1 stop */
-	outb(0x83, ser->port + UART_LCR); /* Access divisor latch */
-	outb(0x00, ser->port + UART_DLM); /* 9600 baud */
-	outb(0x0c, ser->port + UART_DLL);
-	outb(0x03, ser->port + UART_LCR); /* Done with divisor */
+	outb(0, ser->port + UART_IER);
+	outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
+	outb(cval | UART_LCR_DLAB, ser->port + UART_LCR);	/* set DLAB */
+	outb(quot & 0xff, ser->port + UART_DLL);	/* LS of divisor */
+	outb(quot >> 8, ser->port + UART_DLM);		/* MS of divisor */
+	outb(cval, ser->port + UART_LCR);		/* reset DLAB */
 
-	register_console(&console);
-	return kmem_start;
 }
 
+static struct console sercons = {
+	"ttyS",
+	serial_console_write,
+	NULL,
+	serial_console_device,
+	serial_console_wait_key,
+	NULL,
+	serial_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+/*
+ *	Register console.
+ */
+__initfunc (long serial_console_init(long kmem_start, long kmem_end))
+{
+	register_console(&sercons);
+	return kmem_start;
+}
 #endif
diff -u --recursive --new-file v2.1.70/linux/drivers/char/specialix.c linux/drivers/char/specialix.c
--- v2.1.70/linux/drivers/char/specialix.c	Tue Dec  2 16:45:19 1997
+++ linux/drivers/char/specialix.c	Wed Dec  3 17:36:26 1997
@@ -70,6 +70,7 @@
  * ../../Documentation/specialix.txt 
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <asm/io.h>
@@ -1375,7 +1376,7 @@
 		    !(port->flags & ASYNC_CLOSING) &&
 		    (do_clocal || CD))
 			break;
-		if (current->signal & ~current->blocked) {
+		if (signal_pending(current)) {
 			retval = -ERESTARTSYS;
 			break;
 		}
diff -u --recursive --new-file v2.1.70/linux/drivers/char/tga.c linux/drivers/char/tga.c
--- v2.1.70/linux/drivers/char/tga.c	Mon Jun 16 16:35:55 1997
+++ linux/drivers/char/tga.c	Wed Dec  3 15:21:57 1997
@@ -443,6 +443,19 @@
 {
 }
 
+
+/*
+ *	See if we have a TGA card.
+ */
+__initfunc(int con_is_present())
+{
+	int status;
+
+	status = pcibios_find_device (PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
+				      0, &pci_bus, &pci_devfn);
+	return (status == PCIBIOS_DEVICE_NOT_FOUND) ? 0 : 1;
+}
+
 /*
  * video init code, called from within the PCI bus probing code;
  * when TGA console is configured, at the end of the probing code,
diff -u --recursive --new-file v2.1.70/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.70/linux/drivers/char/tty_io.c	Tue Dec  2 16:45:19 1997
+++ linux/drivers/char/tty_io.c	Thu Dec  4 13:08:49 1997
@@ -90,6 +90,7 @@
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 #define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
+#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
 
 #undef TTY_DEBUG_HANGUP
 
@@ -386,7 +387,8 @@
 			continue;
 		if (!filp->f_dentry->d_inode)
 			continue;
-		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV)
+		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||
+		    filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV)
 			continue;
 		if (filp->f_op != &tty_fops)
 			continue;
@@ -517,9 +519,7 @@
 void wait_for_keypress(void)
 {
         struct console *c = console_drivers;
-        while(c && !c->wait_key)
-                c = c->next;
-        if (c) c->wait_key();
+        if (c) c->wait_key(c);
 }
 
 void stop_tty(struct tty_struct *tty)
@@ -647,8 +647,13 @@
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
 
+	/*
+	 *      For now, we redirect writes from /dev/console as
+	 *      well as /dev/tty0.
+	 */
 	inode = file->f_dentry->d_inode;
-	is_console = (inode->i_rdev == CONSOLE_DEV);
+	is_console = (inode->i_rdev == SYSCONS_DEV ||
+		      inode->i_rdev == CONSOLE_DEV);
 
 	if (is_console && redirect)
 		tty = redirect;
@@ -1182,13 +1187,20 @@
 		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
 		/* noctty = 1; */
 	}
+#ifdef CONFIG_VT
 	if (device == CONSOLE_DEV) {
+		extern int fg_console;
+		device = MKDEV(TTY_MAJOR, fg_console + 1);
+		noctty = 1;
+	}
+#endif
+	if (device == SYSCONS_DEV) {
 		struct console *c = console_drivers;
 		while(c && !c->device)
 			c = c->next;
 		if (!c)
                         return -ENODEV;
-                device = c->device();
+                device = c->device(c);
 		noctty = 1;
 	}
 	minor = MINOR(device);
@@ -1369,7 +1381,8 @@
 
 static int tioccons(struct tty_struct *tty, struct tty_struct *real_tty)
 {
-	if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
+	if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE ||
+	    tty->driver.type == TTY_DRIVER_TYPE_SYSCONS) {
 		if (!suser())
 			return -EPERM;
 		redirect = NULL;
@@ -1466,6 +1479,19 @@
 	return 0;
 }
 
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+{
+	/*
+	 * (tty == real_tty) is a cheap way of
+	 * testing if the tty is NOT a master pty.
+	*/
+	if (tty == real_tty && current->tty != real_tty)
+		return -ENOTTY;
+	if (real_tty->session <= 0)
+		return -ENOTTY;
+	return put_user(real_tty->session, arg);
+}
+
 static int tiocttygstruct(struct tty_struct *tty, struct tty_struct *arg)
 {
 	if (copy_to_user(arg, tty, sizeof(*arg)))
@@ -1585,6 +1611,8 @@
 			return tiocgpgrp(tty, real_tty, (pid_t *) arg);
 		case TIOCSPGRP:
 			return tiocspgrp(tty, real_tty, (pid_t *) arg);
+		case TIOCGSID:
+			return tiocgsid(tty, real_tty, (pid_t *) arg);
 		case TIOCGETD:
 			return put_user(tty->ldisc.num, (int *) arg);
 		case TIOCSETD:
@@ -1890,16 +1918,17 @@
 	 * set up the console device so that later boot sequences can 
 	 * inform about problems etc..
 	 */
-#ifdef CONFIG_SERIAL_CONSOLE
-	kmem_start = serial_console_init(kmem_start, kmem_end);
-#endif
 #ifdef CONFIG_VT
 	kmem_start = con_init(kmem_start);
 #endif
+#ifdef CONFIG_SERIAL_CONSOLE
+	kmem_start = serial_console_init(kmem_start, kmem_end);
+#endif
 	return kmem_start;
 }
 
-static struct tty_driver dev_tty_driver, dev_console_driver;
+static struct tty_driver dev_tty_driver, dev_console_driver,
+	dev_syscons_driver;
 
 /*
  * Ok, now we can initialize the rest of the tty devices and can count
@@ -1930,17 +1959,28 @@
 	if (tty_register_driver(&dev_tty_driver))
 		panic("Couldn't register /dev/tty driver\n");
 
+	dev_syscons_driver = dev_tty_driver;
+	dev_syscons_driver.driver_name = "/dev/console";
+	dev_syscons_driver.name = dev_syscons_driver.driver_name + 5;
+	dev_syscons_driver.major = TTYAUX_MAJOR;
+	dev_syscons_driver.minor_start = 1;
+	dev_syscons_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+	dev_syscons_driver.subtype = SYSTEM_TYPE_SYSCONS;
+
+	if (tty_register_driver(&dev_syscons_driver))
+		panic("Couldn't register /dev/console driver\n");
+
+#ifdef CONFIG_VT
 	dev_console_driver = dev_tty_driver;
-	dev_console_driver.driver_name = "/dev/console";
+	dev_console_driver.driver_name = "/dev/tty0";
 	dev_console_driver.name = dev_console_driver.driver_name + 5;
 	dev_console_driver.major = TTY_MAJOR;
 	dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
 	dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE;
 
 	if (tty_register_driver(&dev_console_driver))
-		panic("Couldn't register /dev/console driver\n");
+		panic("Couldn't register /dev/tty0 driver\n");
 
-#ifdef CONFIG_VT
 	kbd_init();
 #endif
 #ifdef CONFIG_ESPSERIAL  /* init ESP before rs, so rs doesn't see the port */
diff -u --recursive --new-file v2.1.70/linux/drivers/char/vga.c linux/drivers/char/vga.c
--- v2.1.70/linux/drivers/char/vga.c	Mon Aug  4 16:25:37 1997
+++ linux/drivers/char/vga.c	Wed Dec  3 15:21:57 1997
@@ -153,6 +153,32 @@
 		hide_cursor();
 }
 
+__initfunc(int con_is_present(void))
+{
+	unsigned short saved;
+	unsigned short *p;
+
+	/*
+	 *	Find out if there is a graphics card present.
+	 *	Are there smarter methods around?
+	 */
+	p = (unsigned short *)(((ORIG_VIDEO_MODE == 7) ? 0xb0000 : 0xb8000) +
+			       + VGA_OFFSET);
+	saved = scr_readw(p);
+	scr_writew(0xAA55, p);
+	if (scr_readw(p) != 0xAA55) {
+		scr_writew(saved, p);
+		return 0;
+	}
+	scr_writew(0x55AA, p);
+	if (scr_readw(p) != 0x55AA) {
+		scr_writew(saved, p);
+		return 0;
+	}
+	scr_writew(saved, p);
+	return 1;
+}
+
 __initfunc(unsigned long
 con_type_init(unsigned long kmem_start, const char **display_desc))
 {
diff -u --recursive --new-file v2.1.70/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.70/linux/drivers/net/Config.in	Mon Dec  1 12:04:12 1997
+++ linux/drivers/net/Config.in	Wed Dec  3 15:17:30 1997
@@ -149,49 +149,15 @@
   comment 'CCP compressors for PPP are only built as modules.'
 fi
 
-bool 'Radio network interfaces' CONFIG_NET_RADIO
-if [ "$CONFIG_NET_RADIO" != "n" ]; then
-  if [ "$CONFIG_AX25" != "n" ]; then
-    tristate 'Serial port KISS driver for AX.25' CONFIG_MKISS
-    tristate 'BPQ Ethernet driver for AX.25' CONFIG_BPQETHER
-    tristate 'Gracilis PackeTwin driver for AX.25' CONFIG_PT
-    tristate 'Ottawa PI and PI2 driver for AX.25' CONFIG_PI
-    tristate 'Z8530 SCC driver for AX.25' CONFIG_SCC
-    if [ "$CONFIG_SCC" != "n" ]; then
-      bool '  additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
-      bool '  support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
-    fi
-  fi
-  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-    tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
-    tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
-    tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR
-    tristate 'Soundcard modem driver for AX.25' CONFIG_SOUNDMODEM
-    if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
-      bool 'Soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
-      bool 'Soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
-      bool 'Soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
-      bool 'Soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
-      bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
-      bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
-      bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
-    fi
-    tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
-    if [ "$CONFIG_HFMODEM" != "n" ]; then
-      bool 'HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
-      bool 'HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
-    fi
-  fi
-  tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
-  tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
-fi
-
 tristate 'SLIP (serial line) support' CONFIG_SLIP
 if [ "$CONFIG_SLIP" != "n" ]; then
   bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED
   bool ' Keepalive and linefill' CONFIG_SLIP_SMART
   bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6
 fi
+
+tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
+tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
 
 bool 'Token Ring driver support' CONFIG_TR
 if [ "$CONFIG_TR" = "y" ]; then
diff -u --recursive --new-file v2.1.70/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.70/linux/drivers/net/Makefile	Mon Dec  1 12:04:12 1997
+++ linux/drivers/net/Makefile	Wed Dec  3 15:38:48 1997
@@ -9,30 +9,32 @@
 
 SUB_DIRS     := 
 MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) hamradio
 
 L_TARGET := net.a
 L_OBJS   := auto_irq.o
 M_OBJS   :=
 MOD_LIST_NAME := NET_MODULES
 
-# Need these to keep track of whether the 8390 and SLHC modules should
+# Need these to keep track of whether the 8390, PPP and SLHC modules should
 # really go in the kernel or a module.
 CONFIG_8390_BUILTIN :=
 CONFIG_8390_MODULE  :=
 CONFIG_SLHC_BUILTIN :=
 CONFIG_SLHC_MODULE  :=
-CONFIG_HDLCDRV_BUILTIN :=
-CONFIG_HDLCDRV_MODULE  :=
+CONFIG_PPPDEF_BUILTIN :=
+CONFIG_PPPDEF_MODULE  :=
 
 ifeq ($(CONFIG_ISDN),y)
   ifeq ($(CONFIG_ISDN_PPP),y)
     CONFIG_SLHC_BUILTIN = y
+    CONFIG_PPPDEF_BUILTIN = y
   endif
 else
   ifeq ($(CONFIG_ISDN),m)
     ifeq ($(CONFIG_ISDN_PPP),y)
       CONFIG_SLHC_MODULE = y
+      CONFIG_PPPDEF_BUILTIN = y
     endif
   endif
 endif
@@ -186,9 +188,11 @@
 ifeq ($(CONFIG_PPP),y)
 LX_OBJS += ppp.o
 CONFIG_SLHC_BUILTIN = y
+CONFIG_PPPDEF_BUILTIN = y
 else
   ifeq ($(CONFIG_PPP),m)
   CONFIG_SLHC_MODULE = y
+  CONFIG_PPPDEF_MODULE = y
   MX_OBJS += ppp.o
   endif
 endif
@@ -201,11 +205,13 @@
 L_OBJS += slip.o
   ifeq ($(CONFIG_SLIP_COMPRESSED),y)
   CONFIG_SLHC_BUILTIN = y
+  CONFIG_PPPDEF_BUILTIN = y
   endif
 else
   ifeq ($(CONFIG_SLIP),m)
     ifeq ($(CONFIG_SLIP_COMPRESSED),y)
     CONFIG_SLHC_MODULE = y
+    CONFIG_PPPDEF_MODULE = y
   endif
   M_OBJS += slip.o
   endif
@@ -538,46 +544,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_SCC),y)
-L_OBJS += scc.o
-else
-  ifeq ($(CONFIG_SCC),m)
-  M_OBJS += scc.o
-  endif
-endif
-
-ifeq ($(CONFIG_MKISS),y)
-L_OBJS += mkiss.o
-else
-  ifeq ($(CONFIG_MKISS),m)
-  M_OBJS += mkiss.o
-  endif
-endif
-
-ifeq ($(CONFIG_PI),y)
-L_OBJS += pi2.o
-else
-  ifeq ($(CONFIG_PI),m)
-  M_OBJS += pi2.o
-  endif
-endif
-
-ifeq ($(CONFIG_PT),y)
-L_OBJS += pt.o
-else
-  ifeq ($(CONFIG_PT),m)
-  M_OBJS += pt.o
-  endif
-endif
-
-ifeq ($(CONFIG_BPQETHER),y)
-L_OBJS += bpqether.o
-else
-  ifeq ($(CONFIG_BPQETHER),m)
-  M_OBJS += bpqether.o
-  endif
-endif
-
 ifeq ($(CONFIG_LAPBETHER),y)
 L_OBJS += lapbether.o
 else
@@ -596,6 +562,15 @@
   endif
 endif
 
+# if anything built-in uses ppp_deflate, then build it into the kernel also.
+# If not, but a module uses it, build as a module:
+ifdef CONFIG_PPPDEF_BUILTIN
+LX_OBJS += ppp_deflate.o
+else
+  ifdef CONFIG_PPPDEF_MODULE
+  MX_OBJS += ppp_deflate.o
+  endif
+endif
 
 # If anything built-in uses the 8390, then build it into the kernel also.
 # If not, but a module uses it, build as a module.
@@ -728,63 +703,10 @@
   endif
 endif
 
-ifeq ($(CONFIG_BAYCOM_SER_FDX),y)
-L_OBJS += baycom_ser_fdx.o
-CONFIG_HDLCDRV_BUILTIN = y
-else
-  ifeq ($(CONFIG_BAYCOM_SER_FDX),m)
-  CONFIG_HDLCDRV_MODULE = y
-  M_OBJS += baycom_ser_fdx.o
-  endif
-endif
-
-ifeq ($(CONFIG_BAYCOM_SER_HDX),y)
-L_OBJS += baycom_ser_hdx.o
-CONFIG_HDLCDRV_BUILTIN = y
-else
-  ifeq ($(CONFIG_BAYCOM_SER_HDX),m)
-  CONFIG_HDLCDRV_MODULE = y
-  M_OBJS += baycom_ser_hdx.o
-  endif
-endif
-
-ifeq ($(CONFIG_BAYCOM_PAR),y)
-L_OBJS += baycom_par.o
-CONFIG_HDLCDRV_BUILTIN = y
-else
-  ifeq ($(CONFIG_BAYCOM_PAR),m)
-  CONFIG_HDLCDRV_MODULE = y
-  M_OBJS += baycom_par.o
-  endif
-endif
-
-ifeq ($(CONFIG_SOUNDMODEM),y)
-ALL_SUB_DIRS += soundmodem
-SUB_DIRS += soundmodem
-L_OBJS += soundmodem/soundmodem.o
-CONFIG_HDLCDRV_BUILTIN = y
-else
-  ifeq ($(CONFIG_SOUNDMODEM),m)
-  CONFIG_HDLCDRV_MODULE = y
-  ALL_SUB_DIRS += soundmodem
-  MOD_SUB_DIRS += soundmodem
-  endif
-endif
-
 ifeq ($(CONFIG_MACE),y)
 L_OBJS += mace.o
 endif
 
-# If anything built-in uses the hdlcdrv, then build it into the kernel also.
-# If not, but a module uses it, build as a module.
-ifdef CONFIG_HDLCDRV_BUILTIN
-LX_OBJS += hdlcdrv.o
-else
-  ifdef CONFIG_HDLCDRV_MODULE
-  MX_OBJS += hdlcdrv.o
-  endif
-endif
-
 ifeq ($(CONFIG_VENDOR_SANGOMA),y)
   M_OBJS += sdladrv.o
   M_OBJS += wanpipe.o
@@ -818,9 +740,6 @@
 
 3c503.o:	3c503.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(EL2_OPTS) -c $<
-
-pi2.o:  pi2.c CONFIG
-	$(CC) $(CPPFLAGS) $(CFLAGS) $(PI_OPTS) -c $<
 
 3c505.o: 3c505.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(ELP_OPTS) -c $<
diff -u --recursive --new-file v2.1.70/linux/drivers/net/baycom_par.c linux/drivers/net/baycom_par.c
--- v2.1.70/linux/drivers/net/baycom_par.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/net/baycom_par.c	Wed Dec 31 16:00:00 1969
@@ -1,661 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	baycom_par.c  -- baycom par96 and picpar radio modem driver.
- *
- *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Supported modems
- *
- *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
- *          The modem does all the filtering and regenerates the receiver clock.
- *          Data is transferred from and to the PC via a shift register.
- *          The shift register is filled with 16 bits and an interrupt is
- *          signalled. The PC then empties the shift register in a burst. This
- *          modem connects to the parallel port, hence the name. The modem
- *          leaves the implementation of the HDLC protocol and the scrambler
- *          polynomial to the PC. This modem is no longer available (at least
- *          from Baycom) and has been replaced by the PICPAR modem (see below).
- *          You may however still build one from the schematics published in
- *          cq-DL :-).
- *
- *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
- *          modem is protocol compatible to par96, but uses only three low
- *          power ICs and can therefore be fed from the parallel port and
- *          does not require an additional power supply. It features
- *          built in DCD circuitry. The driver should therefore be configured
- *          for hardware DCD.
- *
- *
- *  Command line options (insmod command line)
- *
- *  mode     driver mode string. Valid choices are par96 and picpar.
- *  iobase   base address of the port; common values are 0x378, 0x278, 0x3bc
- *
- *
- *  History:
- *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
- *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- *   0.3  26.04.97  init code/data tagged
- *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
- *   0.5  11.11.97  split into separate files for ser12/par96
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/hdlcdrv.h>
-#include <linux/baycom.h>
-#include <linux/parport.h>
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern __inline__ int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern __inline__ int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_DEBUG
-
-/*
- * modem options; bit mask
- */
-#define BAYCOM_OPTIONS_SOFTDCD  1
-
-/* --------------------------------------------------------------------- */
-
-static const char bc_drvname[] = "baycom_par";
-static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom_par: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-static struct device baycom_device[NR_PORTS];
-
-static struct {
-	const char *mode;
-	int iobase;
-} baycom_ports[NR_PORTS] = { { NULL, 0 }, };
-
-/* --------------------------------------------------------------------- */
-
-#define SER12_EXTENT 8
-
-#define LPT_DATA(dev)    ((dev)->base_addr+0)
-#define LPT_STATUS(dev)  ((dev)->base_addr+1)
-#define LPT_CONTROL(dev) ((dev)->base_addr+2)
-#define LPT_IRQ_ENABLE      0x10
-
-#define PAR96_BURSTBITS 16
-#define PAR96_BURST     4
-#define PAR96_PTT       2
-#define PAR96_TXBIT     1
-#define PAR96_ACK       0x40
-#define PAR96_RXBIT     0x20
-#define PAR96_DCD       0x10
-#define PAR97_POWER     0xf8
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct baycom_state {
-	struct hdlcdrv_state hdrv;
-
-	struct pardevice *pdev;
-	unsigned int options;
-
-	struct modem_state {
-		short arb_divider;
-		unsigned char flags;
-		unsigned int shreg;
-		struct modem_state_par96 {
-			int dcd_count;
-			unsigned int dcd_shreg;
-			unsigned long descram;
-			unsigned long scram;
-		} par96;
-	} modem;
-
-#ifdef BAYCOM_DEBUG
-	struct debug_vals {
-		unsigned long last_jiffies;
-		unsigned cur_intcnt;
-		unsigned last_intcnt;
-		int cur_pllcorr;
-		int last_pllcorr;
-	} debug_vals;
-#endif /* BAYCOM_DEBUG */
-};
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-static void __inline__ baycom_int_freq(struct baycom_state *bc)
-{
-#ifdef BAYCOM_DEBUG
-	unsigned long cur_jiffies = jiffies;
-	/*
-	 * measure the interrupt frequency
-	 */
-	bc->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
-		bc->debug_vals.last_jiffies = cur_jiffies;
-		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
-		bc->debug_vals.cur_intcnt = 0;
-		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
-		bc->debug_vals.cur_pllcorr = 0;
-	}
-#endif /* BAYCOM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== PAR96 specific routines =========================
- */
-
-#define PAR96_DESCRAM_TAP1 0x20000
-#define PAR96_DESCRAM_TAP2 0x01000
-#define PAR96_DESCRAM_TAP3 0x00001
-
-#define PAR96_DESCRAM_TAPSH1 17
-#define PAR96_DESCRAM_TAPSH2 12
-#define PAR96_DESCRAM_TAPSH3 0
-
-#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
-#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ void par96_tx(struct device *dev, struct baycom_state *bc)
-{
-	int i;
-	unsigned int data = hdlcdrv_getbits(&bc->hdrv);
-
-	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
-		unsigned char val = PAR97_POWER;
-		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
-					 (bc->modem.par96.scram & 1));
-		if (!(data & 1))
-			bc->modem.par96.scram ^= 1;
-		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
-			bc->modem.par96.scram ^=
-				(PAR96_SCRAM_TAPN << 1);
-		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
-			val |= PAR96_TXBIT;
-		outb(val, LPT_DATA(dev));
-		outb(val | PAR96_BURST, LPT_DATA(dev));
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ void par96_rx(struct device *dev, struct baycom_state *bc)
-{
-	int i;
-	unsigned int data, mask, mask2, descx;
-
-	/*
-	 * do receiver; differential decode and descramble on the fly
-	 */
-	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
-		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
-		if (inb(LPT_STATUS(dev)) & PAR96_RXBIT)
-			bc->modem.par96.descram |= 1;
-		descx = bc->modem.par96.descram ^
-			(bc->modem.par96.descram >> 1);
-		/* now the diff decoded data is inverted in descram */
-		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev));
-		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
-			  (descx >> PAR96_DESCRAM_TAPSH2));
-		data >>= 1;
-		if (!(descx & 1))
-			data |= 0x8000;
-		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, LPT_DATA(dev));
-	}
-	hdlcdrv_putbits(&bc->hdrv, data);
-	/*
-	 * do DCD algorithm
-	 */
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
-			| (data << 16);
-		/* search for flags and set the dcd counter appropriately */
-		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
-				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
-		/* check for abort/noise sequences */
-		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
-		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
-			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
-			    (bc->modem.par96.dcd_count >= 0))
-				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
-		/* decrement and set the dcd variable */
-		if (bc->modem.par96.dcd_count >= 0)
-			bc->modem.par96.dcd_count -= 2;
-		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
-	} else {
-		hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev)) & PAR96_DCD));
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct parport *pp = (struct parport *)dev_id;
-	struct pardevice *pd = pp->cad;
-	struct device *dev = (struct device *)pd->private;
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-
-	baycom_int_freq(bc);
-	/*
-	 * check if transmitter active
-	 */
-	if (hdlcdrv_ptt(&bc->hdrv))
-		par96_tx(dev, bc);
-	else {
-		par96_rx(dev, bc);
-		if (--bc->modem.arb_divider <= 0) {
-			bc->modem.arb_divider = 6;
-			sti();
-			hdlcdrv_arbitrate(dev, &bc->hdrv);
-		}
-	}
-	sti();
-	hdlcdrv_transmitter(dev, &bc->hdrv);
-	hdlcdrv_receiver(dev, &bc->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_preempt(void *handle)
-{
-	/* we cannot relinquish the port in the middle of an operation */
-	return 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void par96_wakeup(void *handle)
-{
-        struct device *dev = (struct device *)handle;
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);
-	if (!parport_claim(bc->pdev))
-		printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_open(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-	struct parport *pp = parport_enumerate();
-
-	if (!dev || !bc)
-		return -ENXIO;
-	while (pp && pp->base != dev->base_addr) 
-		pp = pp->next;
-	if (!pp) {
-		printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);
-		return -ENXIO;
-	}
-	if (pp->irq < 0) {
-		printk(KERN_ERR "baycom_par: parport at 0x%x has no irq\n", pp->base);
-		return -ENXIO;
-	}
-	memset(&bc->modem, 0, sizeof(bc->modem));
-	bc->hdrv.par.bitrate = 9600;
-	if (!(bc->pdev = parport_register_device(pp, dev->name, par96_preempt, par96_wakeup, 
-						 par96_interrupt, PARPORT_DEV_LURK, dev))) {
-		printk(KERN_ERR "baycom_par: cannot register parport at 0x%x\n", pp->base);
-		return -ENXIO;
-	}
-	if (parport_claim(bc->pdev)) {
-		printk(KERN_ERR "baycom_par: parport at 0x%x busy\n", pp->base);
-		parport_unregister_device(bc->pdev);
-		return -EBUSY;
-	}
-	dev->irq = pp->irq;
-	/* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP);  not yet implemented */
-	/* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
-	/*bc->pdev->port->ops->enable_irq(bc->pdev->port);  not yet implemented */
-        outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev));	
-	printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
-	       bc_drvname, dev->base_addr, dev->irq, bc->options);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int par96_close(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	if (!dev || !bc)
-		return -EINVAL;
-	/* disable interrupt */
-        outb(0, LPT_CONTROL(dev));
-	/*bc->pdev->port->ops->disable_irq(bc->pdev->port);  not yet implemented */
-	/* switch off PTT */
-	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
-	parport_release(bc->pdev);
-	parport_unregister_device(bc->pdev);
-	printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
-	       bc_drvname, dev->base_addr, dev->irq);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== hdlcdrv driver interface =========================
- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-static struct hdlcdrv_ops par96_ops = {
-	bc_drvname,
-	bc_drvinfo,
-	par96_open,
-	par96_close,
-	baycom_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_setmode(struct baycom_state *bc, const char *modestr)
-{
-	if (!strncmp(modestr, "picpar", 6))
-		bc->options = 0;
-	else if (!strncmp(modestr, "par96", 5))
-		bc->options = BAYCOM_OPTIONS_SOFTDCD;
-	else
-		bc->options = !!strchr(modestr, '*');
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct baycom_state *bc;
-	struct baycom_ioctl bi;
-	int cmd2;
-
-	if (!dev || !dev->priv ||
-	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
-		return -EINVAL;
-	}
-	bc = (struct baycom_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-	if (get_user(cmd2, (int *)ifr->ifr_data))
-		return -EFAULT;
-	switch (hi->cmd) {
-	default:
-		break;
-
-	case HDLCDRVCTL_GETMODE:
-		strcpy(hi->data.modename, bc->options ? "par96" : "picpar");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_SETMODE:
-		if (!suser() || dev->start)
-			return -EACCES;
-		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
-		return baycom_setmode(bc, hi->data.modename);
-
-	case HDLCDRVCTL_MODELIST:
-		strcpy(hi->data.modename, "par96,picpar");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_MODEMPARMASK:
-		return HDLCDRV_PARMASK_IOBASE;
-
-	}
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-#ifdef BAYCOM_DEBUG
-	case BAYCOMCTL_GETDEBUG:
-		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
-		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
-		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
-		break;
-#endif /* BAYCOM_DEBUG */
-
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-__initfunc(int baycom_par_init(void))
-{
-	int i, j, found = 0;
-	char set_hw = 1;
-	struct baycom_state *bc;
-	char ifname[HDLCDRV_IFNAMELEN];
-
-
-	printk(bc_drvinfo);
-	/*
-	 * register net devices
-	 */
-	for (i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		sprintf(ifname, "bcp%d", i);
-
-		if (!baycom_ports[i].mode)
-			set_hw = 0;
-		if (!set_hw)
-			baycom_ports[i].iobase = 0;
-		j = hdlcdrv_register_hdlcdrv(dev, &par96_ops,
-					     sizeof(struct baycom_state),
-					     ifname, baycom_ports[i].iobase, 0, 0);
-		if (!j) {
-			bc = (struct baycom_state *)dev->priv;
-			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
-				set_hw = 0;
-			found++;
-		} else {
-			printk(KERN_WARNING "%s: cannot register net device\n",
-			       bc_drvname);
-		}
-	}
-	if (!found)
-		return -ENXIO;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-/*
- * command line settable parameters
- */
-static const char *mode[NR_PORTS] = { "picpar", };
-static int iobase[NR_PORTS] = { 0x378, };
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
-MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");
-MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(iobase, "baycom io base address");
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
-
-#endif
-
-__initfunc(int init_module(void))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
-		baycom_ports[i].mode = mode[i];
-		baycom_ports[i].iobase = iobase[i];
-	}
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-	return baycom_par_init();
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-		if (bc) {
-			if (bc->hdrv.magic != HDLCDRV_MAGIC)
-				printk(KERN_ERR "baycom: invalid magic in "
-				       "cleanup_module\n");
-			else
-				hdlcdrv_unregister_hdlcdrv(dev);
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom_par=io,mode
- * mode: par96,picpar
- */
-
-__initfunc(void baycom_par_setup(char *str, int *ints))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 1)) {
-		printk(KERN_INFO "%s: too many or invalid interface "
-		       "specifications\n", bc_drvname);
-		return;
-	}
-	baycom_ports[i].mode = str;
-	baycom_ports[i].iobase = ints[1];
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/baycom_ser_fdx.c linux/drivers/net/baycom_ser_fdx.c
--- v2.1.70/linux/drivers/net/baycom_ser_fdx.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/net/baycom_ser_fdx.c	Wed Dec 31 16:00:00 1969
@@ -1,762 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	baycom_ser_fdx.c  -- baycom ser12 fullduplex radio modem driver.
- *
- *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Supported modems
- *
- *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
- *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
- *          is responsible for regenerating the receiver bit clock, as well as
- *          for handling the HDLC protocol. The modem connects to a serial port,
- *          hence the name. Since the serial port is not used as an async serial
- *          port, the kernel driver for serial ports cannot be used, and this
- *          driver only supports standard serial hardware (8250, 16450, 16550A)
- *
- *
- *  Command line options (insmod command line)
- *
- *  mode     * enables software DCD.
- *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
- *  baud     baud rate (between 300 and 4800)
- *  irq      interrupt line of the port; common values are 4,3
- *
- *
- *  History:
- *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
- *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- *   0.3  26.04.97  init code/data tagged
- *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
- *   0.5  11.11.97  ser12/par96 split into separate files
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/hdlcdrv.h>
-#include <linux/baycom.h>
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_DEBUG
-
-/*
- * modem options; bit mask
- */
-#define BAYCOM_OPTIONS_SOFTDCD  1
-
-/* --------------------------------------------------------------------- */
-
-static const char bc_drvname[] = "baycom_ser_fdx";
-static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom_ser_fdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-static struct device baycom_device[NR_PORTS];
-
-static struct {
-	char *mode;
-	int iobase, irq, baud;
-} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct baycom_state {
-	struct hdlcdrv_state hdrv;
-
-	unsigned int baud, baud_us8, baud_arbdiv;
-	unsigned int options;
-
-	struct modem_state {
-		short arb_divider;
-		unsigned char flags;
-		unsigned int shreg;
-		struct modem_state_ser12 {
-			unsigned char tx_bit;
-			int dcd_sum0, dcd_sum1, dcd_sum2;
-			unsigned char last_sample;
-			unsigned char last_rxbit;
-			unsigned int dcd_shreg;
-			unsigned int dcd_time;
-			unsigned int bit_pll;
-			unsigned long last_jiffies;
-			unsigned int pll_time;
-			unsigned int txshreg;
-		} ser12;
-	} modem;
-
-#ifdef BAYCOM_DEBUG
-	struct debug_vals {
-		unsigned long last_jiffies;
-		unsigned cur_intcnt;
-		unsigned last_intcnt;
-		int cur_pllcorr;
-		int last_pllcorr;
-	} debug_vals;
-#endif /* BAYCOM_DEBUG */
-};
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
-#ifdef BAYCOM_DEBUG
-	unsigned long cur_jiffies = jiffies;
-	/*
-	 * measure the interrupt frequency
-	 */
-	bc->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
-		bc->debug_vals.last_jiffies = cur_jiffies;
-		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
-		bc->debug_vals.cur_intcnt = 0;
-		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
-		bc->debug_vals.cur_pllcorr = 0;
-	}
-#endif /* BAYCOM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-/* --------------------------------------------------------------------- */
-
-extern inline unsigned int hweight16(unsigned short w)
-        __attribute__ ((unused));
-extern inline unsigned int hweight8(unsigned char w)
-        __attribute__ ((unused));
-
-extern inline unsigned int hweight16(unsigned short w)
-{
-        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
-        res = (res & 0x3333) + ((res >> 2) & 0x3333);
-        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
-        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
-}
-
-extern inline unsigned int hweight8(unsigned char w)
-{
-        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
-        res = (res & 0x33) + ((res >> 2) & 0x33);
-        return (res & 0x0F) + ((res >> 4) & 0x0F);
-}
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
-{
-	bc->modem.ser12.dcd_shreg <<= 1;
-	bc->modem.ser12.bit_pll += 0x2000;
-	if (bc->modem.ser12.last_sample != news) {
-		bc->modem.ser12.last_sample = news;
-		bc->modem.ser12.dcd_shreg |= 1;
-		if (bc->modem.ser12.bit_pll < 0x9000)
-			bc->modem.ser12.bit_pll += 0x1000;
-		else
-			bc->modem.ser12.bit_pll -= 0x1000;
-		bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
-			- hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
-	}
-	hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
-	if ((--bc->modem.ser12.dcd_time) <= 0) {
-		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
-					   bc->modem.ser12.dcd_sum1 + 
-					   bc->modem.ser12.dcd_sum2) < 0);
-		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
-		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
-		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
-		bc->modem.ser12.dcd_time = 120;
-	}
-	if (bc->modem.ser12.bit_pll >= 0x10000) {
-		bc->modem.ser12.bit_pll &= 0xffff;
-		bc->modem.shreg >>= 1;
-		if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
-			bc->modem.shreg |=  0x10000;
-		bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
-		if (bc->modem.shreg & 1) {
-			hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
-			bc->modem.shreg = 0x10000;
-		}
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
-{
-	unsigned long curjiff;
-	struct timeval tv;
-	unsigned int timediff;
-
-	/*
-	 * get current time
-	 */
-	curjiff = jiffies;
-	do_gettimeofday(&tv);
-	if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
-		/* long inactivity; clear HDLC and DCD */
-		bc->modem.ser12.dcd_sum1 = 0;
-		bc->modem.ser12.dcd_sum2 = 0;
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = 120;
-		hdlcdrv_setdcd(&bc->hdrv, 0);
-		hdlcdrv_putbits(&bc->hdrv, 0xffff);
-		bc->modem.ser12.last_jiffies = curjiff;
-		bc->modem.ser12.pll_time = tv.tv_usec;
-	}
-	bc->modem.ser12.last_jiffies = curjiff;
-	timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
-	timediff %= 1000000;
-	timediff /= bc->baud_us8;
-	bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (bc->baud_us8)) % 1000000;
-	for (; timediff > 1; timediff--)
-		ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
-	if (timediff >= 1)
-		ser12_rxsample(dev, bc, curs);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-	unsigned char iir, msr = 0;
-	unsigned int txcount = 0;
-	unsigned int rxcount = 0;
-
-	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	
-	for (;;) {
-		iir = inb(IIR(dev->base_addr));
-		if (iir & 1)
-			break;
-		switch (iir & 6) {
-		case 6:
-			inb(LSR(dev->base_addr));
-			continue;
-			
-		case 4:
-			inb(RBR(dev->base_addr));
-			continue;
-			
-		case 2:
-			/*
-			 * make sure the next interrupt is generated;
-			 * 0 must be used to power the modem; the modem draws its
-			 * power from the TxD line
-			 */
-			outb(0x00, THR(dev->base_addr));
-			bc->modem.arb_divider--;
-			baycom_int_freq(bc);
-			if (hdlcdrv_ptt(&bc->hdrv)) {
-				/*
-				 * first output the last bit (!) then call HDLC transmitter,
-				 * since this may take quite long
-				 */
-				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
-				txcount++;
-			} else
-				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
-			continue;
-			
-		default:
-			msr = inb(MSR(dev->base_addr));
-			if (msr & 1)  /* delta CTS interrupt */
-				rxcount++;
-			continue;
-		}
-	}
-	if (rxcount)
-		ser12_rx(dev, bc, msr & 0x10);
-	if (txcount) {
-#ifdef BAYCOM_DEBUG
-		if (bc->debug_vals.cur_pllcorr < txcount)
-			bc->debug_vals.cur_pllcorr = txcount;
-#endif /* BAYCOM_DEBUG */
-		if (bc->modem.ser12.txshreg <= 1)
-			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
-		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
-		bc->modem.ser12.txshreg >>= 1;
-	}
-	sti();
-	if (bc->modem.arb_divider <= 0) {
-		bc->modem.arb_divider = bc->baud_arbdiv;
-		hdlcdrv_arbitrate(dev, &bc->hdrv);
-	}
-	hdlcdrv_transmitter(dev, &bc->hdrv);
-	hdlcdrv_receiver(dev, &bc->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	    c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] = { 
-	"unknown", "8250", "16450", "16550", "16550A" 
-};
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	b1 = inb(MCR(iobase));
-	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
-	b2 = inb(MSR(iobase));
-	outb(0x1a, MCR(iobase));
-	b3 = inb(MSR(iobase)) & 0xf0;
-	outb(b1, MCR(iobase));			/* restore old values */
-	outb(b2, MSR(iobase));
-	if (b3 != 0x90)
-		return c_uart_unknown;
-	inb(RBR(iobase));
-	inb(RBR(iobase));
-	outb(0x01, FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, SCR(iobase));
-		b1 = inb(SCR(iobase));
-		outb(0xa5, SCR(iobase));
-		b2 = inb(SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5))
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_open(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-	enum uart u;
-
-	if (!dev || !bc)
-		return -ENXIO;
-	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
-	    dev->irq < 2 || dev->irq > 15)
-		return -ENXIO;
-	if (bc->baud < 300 || bc->baud > 4800)
-		return -EINVAL;
-	if (check_region(dev->base_addr, SER12_EXTENT))
-		return -EACCES;
-	memset(&bc->modem, 0, sizeof(bc->modem));
-	bc->hdrv.par.bitrate = bc->baud;
-	bc->baud_us8 = 125000/bc->baud;
-	bc->baud_arbdiv = bc->baud/100;
-	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
-		return -EIO;
-	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
-	outb(0x0d, MCR(dev->base_addr));
-	outb(0x0d, MCR(dev->base_addr));
-	outb(0, IER(dev->base_addr));
-	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
-			"baycom_ser_fdx", dev))
-		return -EBUSY;
-	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx");
-	/*
-	 * set the SIO to 6 Bits/character and 19600 baud, so that
-	 * we get exactly (hopefully) one interrupt per radio symbol
-	 */
-	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
-	outb(115200/8/bc->baud, DLL(dev->base_addr));
-	outb(0, DLM(dev->base_addr));
-	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
-	/*
-	 * enable transmitter empty interrupt and modem status interrupt
-	 */
-	outb(0x0a, IER(dev->base_addr));
-	/*
-	 * make sure the next interrupt is generated;
-	 * 0 must be used to power the modem; the modem draws its
-	 * power from the TxD line
-	 */
-	outb(0x00, THR(dev->base_addr));
-	printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u options "
-	       "0x%x baud %u uart %s\n", bc_drvname, dev->base_addr, dev->irq,
-	       bc->options, bc->baud, uart_str[u]);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_close(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	if (!dev || !bc)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(dev->base_addr));
-	outb(1, MCR(dev->base_addr));
-	free_irq(dev->irq, dev);
-	release_region(dev->base_addr, SER12_EXTENT);
-	printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
-	       bc_drvname, dev->base_addr, dev->irq);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== hdlcdrv driver interface =========================
- */
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-static struct hdlcdrv_ops ser12_ops = {
-	bc_drvname,
-	bc_drvinfo,
-	ser12_open,
-	ser12_close,
-	baycom_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_setmode(struct baycom_state *bc, const char *modestr)
-{
-	unsigned int baud;
-
-	if (!strncmp(modestr, "ser", 3)) {
-		baud = simple_strtoul(modestr+3, NULL, 10);
-		if (baud >= 3 && baud <= 48)
-			bc->baud = baud*100;
-	}
-	bc->options = !!strchr(modestr, '*');
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct baycom_state *bc;
-	struct baycom_ioctl bi;
-	int cmd2;
-
-	if (!dev || !dev->priv ||
-	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
-		return -EINVAL;
-	}
-	bc = (struct baycom_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-	if (get_user(cmd2, (int *)ifr->ifr_data))
-		return -EFAULT;
-	switch (hi->cmd) {
-	default:
-		break;
-
-	case HDLCDRVCTL_GETMODE:
-		sprintf(hi->data.modename, "ser%u", bc->baud / 100);
-		if (bc->options & 1)
-			strcat(hi->data.modename, "*");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_SETMODE:
-		if (!suser() || dev->start)
-			return -EACCES;
-		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
-		return baycom_setmode(bc, hi->data.modename);
-
-	case HDLCDRVCTL_MODELIST:
-		strcpy(hi->data.modename, "ser12,ser3,ser24");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_MODEMPARMASK:
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
-
-	}
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-#ifdef BAYCOM_DEBUG
-	case BAYCOMCTL_GETDEBUG:
-		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
-		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
-		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
-		break;
-#endif /* BAYCOM_DEBUG */
-
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-__initfunc(int baycom_ser_fdx_init(void))
-{
-	int i, j, found = 0;
-	char set_hw = 1;
-	struct baycom_state *bc;
-	char ifname[HDLCDRV_IFNAMELEN];
-
-
-	printk(bc_drvinfo);
-	/*
-	 * register net devices
-	 */
-	for (i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		sprintf(ifname, "bcsf%d", i);
-
-		if (!baycom_ports[i].mode)
-			set_hw = 0;
-		if (!set_hw)
-			baycom_ports[i].iobase = baycom_ports[i].irq = 0;
-		j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
-					     sizeof(struct baycom_state),
-					     ifname, baycom_ports[i].iobase,
-					     baycom_ports[i].irq, 0);
-		if (!j) {
-			bc = (struct baycom_state *)dev->priv;
-			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
-				set_hw = 0;
-			bc->baud = baycom_ports[i].baud;
-			found++;
-		} else {
-			printk(KERN_WARNING "%s: cannot register net device\n",
-			       bc_drvname);
-		}
-	}
-	if (!found)
-		return -ENXIO;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-/*
- * command line settable parameters
- */
-static char *mode[NR_PORTS] = { "ser12*", };
-static int iobase[NR_PORTS] = { 0x3f8, };
-static int irq[NR_PORTS] = { 4, };
-static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
-MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
-MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(iobase, "baycom io base address");
-MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(irq, "baycom irq number");
-MODULE_PARM(baud, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
-
-#endif
-
-__initfunc(int init_module(void))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
-		baycom_ports[i].mode = mode[i];
-		baycom_ports[i].iobase = iobase[i];
-		baycom_ports[i].irq = irq[i];
-		baycom_ports[i].baud = baud[i];
-	}
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-	return baycom_ser_fdx_init();
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-		if (bc) {
-			if (bc->hdrv.magic != HDLCDRV_MAGIC)
-				printk(KERN_ERR "baycom: invalid magic in "
-				       "cleanup_module\n");
-			else
-				hdlcdrv_unregister_hdlcdrv(dev);
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom_ser_=io,irq,mode
- * mode: [*]
- * * indicates sofware DCD
- */
-
-__initfunc(void baycom_ser_fdx_setup(char *str, int *ints))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 2)) {
-		printk(KERN_INFO "%s: too many or invalid interface "
-		       "specifications\n", bc_drvname);
-		return;
-	}
-	baycom_ports[i].mode = str;
-	baycom_ports[i].iobase = ints[1];
-	baycom_ports[i].irq = ints[2];
-	if (ints[0] >= 3)
-		baycom_ports[i].baud = ints[3];
-	else
-		baycom_ports[i].baud = 1200;
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/baycom_ser_hdx.c linux/drivers/net/baycom_ser_hdx.c
--- v2.1.70/linux/drivers/net/baycom_ser_hdx.c	Mon Dec  1 12:04:12 1997
+++ linux/drivers/net/baycom_ser_hdx.c	Wed Dec 31 16:00:00 1969
@@ -1,792 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
- *
- *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Supported modems
- *
- *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
- *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
- *          is responsible for regenerating the receiver bit clock, as well as
- *          for handling the HDLC protocol. The modem connects to a serial port,
- *          hence the name. Since the serial port is not used as an async serial
- *          port, the kernel driver for serial ports cannot be used, and this
- *          driver only supports standard serial hardware (8250, 16450, 16550A)
- *
- *
- *  Command line options (insmod command line)
- *
- *  mode     * enables software DCD.
- *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
- *  irq      interrupt line of the port; common values are 4,3
- *
- *
- *  History:
- *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
- *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- *   0.3  26.04.97  init code/data tagged
- *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
- *   0.5  11.11.97  ser12/par96 split into separate files
- */
-
-/*****************************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/hdlcdrv.h>
-#include <linux/baycom.h>
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#define BAYCOM_DEBUG
-
-/*
- * modem options; bit mask
- */
-#define BAYCOM_OPTIONS_SOFTDCD  1
-
-/* --------------------------------------------------------------------- */
-
-static const char bc_drvname[] = "baycom_ser_hdx";
-static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "baycom_ser_hdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-static struct device baycom_device[NR_PORTS];
-
-static struct {
-	char *mode;
-	int iobase, irq;
-} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
-
-/* --------------------------------------------------------------------- */
-
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define SER12_EXTENT 8
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct baycom_state {
-	struct hdlcdrv_state hdrv;
-
-	unsigned int options;
-
-	struct modem_state {
-		short arb_divider;
-		unsigned char flags;
-		unsigned int shreg;
-		struct modem_state_ser12 {
-			unsigned char tx_bit;
-			int dcd_sum0, dcd_sum1, dcd_sum2;
-			unsigned char last_sample;
-			unsigned char last_rxbit;
-			unsigned int dcd_shreg;
-			unsigned int dcd_time;
-			unsigned int bit_pll;
-			unsigned char interm_sample;
-		} ser12;
-	} modem;
-
-#ifdef BAYCOM_DEBUG
-	struct debug_vals {
-		unsigned long last_jiffies;
-		unsigned cur_intcnt;
-		unsigned last_intcnt;
-		int cur_pllcorr;
-		int last_pllcorr;
-	} debug_vals;
-#endif /* BAYCOM_DEBUG */
-};
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-static void inline baycom_int_freq(struct baycom_state *bc)
-{
-#ifdef BAYCOM_DEBUG
-	unsigned long cur_jiffies = jiffies;
-	/*
-	 * measure the interrupt frequency
-	 */
-	bc->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
-		bc->debug_vals.last_jiffies = cur_jiffies;
-		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
-		bc->debug_vals.cur_intcnt = 0;
-		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
-		bc->debug_vals.cur_pllcorr = 0;
-	}
-#endif /* BAYCOM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== SER12 specific routines =========================
- */
-
-static void inline ser12_set_divisor(struct device *dev,
-				     unsigned char divisor)
-{
-	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
-	outb(divisor, DLL(dev->base_addr));
-	outb(0, DLM(dev->base_addr));
-	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
-	/*
-	 * make sure the next interrupt is generated;
-	 * 0 must be used to power the modem; the modem draws its
-	 * power from the TxD line
-	 */
-	outb(0x00, THR(dev->base_addr));
-	/*
-	 * it is important not to set the divider while transmitting;
-	 * this reportedly makes some UARTs generating interrupts
-	 * in the hundredthousands per second region
-	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
-	 */
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * must call the TX arbitrator every 10ms
- */
-#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-			       36 : 24)
-#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
-				240 : 12)
-
-static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
-{
-	/* one interrupt per channel bit */
-	ser12_set_divisor(dev, 12);
-	/*
-	 * first output the last bit (!) then call HDLC transmitter,
-	 * since this may take quite long
-	 */
-	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
-	if (bc->modem.shreg <= 1)
-		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
-	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
-				   (bc->modem.shreg & 1));
-	bc->modem.shreg >>= 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
-{
-	unsigned char cur_s;
-	/*
-	 * do demodulator
-	 */
-	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
-	hdlcdrv_channelbit(&bc->hdrv, cur_s);
-	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
-		(cur_s != bc->modem.ser12.last_sample);
-	bc->modem.ser12.last_sample = cur_s;
-	if(bc->modem.ser12.dcd_shreg & 1) {
-		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-			unsigned int dcdspos, dcdsneg;
-
-			dcdspos = dcdsneg = 0;
-			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
-			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
-				dcdspos += 2;
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
-			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
-		} else
-			bc->modem.ser12.dcd_sum0--;
-	}
-	if(!bc->modem.ser12.dcd_time) {
-		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
-					   bc->modem.ser12.dcd_sum1 +
-					   bc->modem.ser12.dcd_sum2) < 0);
-		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
-		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
-		/* offset to ensure DCD off on silent input */
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
-	}
-	bc->modem.ser12.dcd_time--;
-	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
-		/*
-		 * PLL code for the improved software DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(dev, 4);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 7) {
-			case 1: /* transition too late */
-				ser12_set_divisor(dev, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 4:	/* transition too early */
-				ser12_set_divisor(dev, 3);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(dev, 4);
-				break;
-			}
-			bc->modem.shreg >>= 1;
-			if (bc->modem.ser12.last_sample ==
-			    bc->modem.ser12.last_rxbit)
-				bc->modem.shreg |= 0x10000;
-			bc->modem.ser12.last_rxbit =
-				bc->modem.ser12.last_sample;
-		}
-		if (++bc->modem.ser12.interm_sample >= 3)
-			bc->modem.ser12.interm_sample = 0;
-		/*
-		 * DCD stuff
-		 */
-		if (bc->modem.ser12.dcd_shreg & 1) {
-			unsigned int dcdspos, dcdsneg;
-
-			dcdspos = dcdsneg = 0;
-			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
-			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
-				<< 1;
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
-			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
-
-			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
-		}
-	} else {
-		/*
-		 * PLL algorithm for the hardware squelch DCD algorithm
-		 */
-		if (bc->modem.ser12.interm_sample) {
-			/*
-			 * intermediate sample; set timing correction to normal
-			 */
-			ser12_set_divisor(dev, 6);
-		} else {
-			/*
-			 * do PLL correction and call HDLC receiver
-			 */
-			switch (bc->modem.ser12.dcd_shreg & 3) {
-			case 1: /* transition too late */
-				ser12_set_divisor(dev, 7);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr++;
-#endif /* BAYCOM_DEBUG */
-				break;
-			case 2:	/* transition too early */
-				ser12_set_divisor(dev, 5);
-#ifdef BAYCOM_DEBUG
-				bc->debug_vals.cur_pllcorr--;
-#endif /* BAYCOM_DEBUG */
-				break;
-			default:
-				ser12_set_divisor(dev, 6);
-				break;
-			}
-			bc->modem.shreg >>= 1;
-			if (bc->modem.ser12.last_sample ==
-			    bc->modem.ser12.last_rxbit)
-				bc->modem.shreg |= 0x10000;
-			bc->modem.ser12.last_rxbit =
-				bc->modem.ser12.last_sample;
-		}
-		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
-		/*
-		 * DCD stuff
-		 */
-		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
-	}
-	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
-	if (bc->modem.shreg & 1) {
-		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
-		bc->modem.shreg = 0x10000;
-	}
-	if(!bc->modem.ser12.dcd_time) {
-		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
-					   bc->modem.ser12.dcd_sum1 +
-					   bc->modem.ser12.dcd_sum2) < 0);
-		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
-		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
-		/* offset to ensure DCD off on silent input */
-		bc->modem.ser12.dcd_sum0 = 2;
-		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
-	}
-	bc->modem.ser12.dcd_time--;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-
-	baycom_int_freq(bc);
-	/*
-	 * check if transmitter active
-	 */
-	if (hdlcdrv_ptt(&bc->hdrv))
-		ser12_tx(dev, bc);
-	else {
-		ser12_rx(dev, bc);
-		if (--bc->modem.arb_divider <= 0) {
-			bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
-			sti();
-			hdlcdrv_arbitrate(dev, &bc->hdrv);
-		}
-	}
-	sti();
-	hdlcdrv_transmitter(dev, &bc->hdrv);
-	hdlcdrv_receiver(dev, &bc->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	    c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] = { 
-	"unknown", "8250", "16450", "16550", "16550A" 
-};
-
-static enum uart ser12_check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	b1 = inb(MCR(iobase));
-	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
-	b2 = inb(MSR(iobase));
-	outb(0x1a, MCR(iobase));
-	b3 = inb(MSR(iobase)) & 0xf0;
-	outb(b1, MCR(iobase));			/* restore old values */
-	outb(b2, MSR(iobase));
-	if (b3 != 0x90)
-		return c_uart_unknown;
-	inb(RBR(iobase));
-	inb(RBR(iobase));
-	outb(0x01, FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, SCR(iobase));
-		b1 = inb(SCR(iobase));
-		outb(0xa5, SCR(iobase));
-		b2 = inb(SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5))
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_open(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-	enum uart u;
-
-	if (!dev || !bc)
-		return -ENXIO;
-	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
-	    dev->irq < 2 || dev->irq > 15)
-		return -ENXIO;
-	if (check_region(dev->base_addr, SER12_EXTENT))
-		return -EACCES;
-	memset(&bc->modem, 0, sizeof(bc->modem));
-	bc->hdrv.par.bitrate = 1200;
-	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
-		return -EIO;
-	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
-	outb(0x0d, MCR(dev->base_addr));
-	outb(0x0d, MCR(dev->base_addr));
-	outb(0, IER(dev->base_addr));
-	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
-			"baycom_ser12", dev))
-		return -EBUSY;
-	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
-	/*
-	 * enable transmitter empty interrupt
-	 */
-	outb(2, IER(dev->base_addr));
-	/*
-	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
-	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
-	 * depending on the usage of the software DCD routine
-	 */
-	ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
-	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
-	       "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
-	       bc->options, uart_str[u]);
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int ser12_close(struct device *dev)
-{
-	struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-	if (!dev || !bc)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	outb(0, IER(dev->base_addr));
-	outb(1, MCR(dev->base_addr));
-	free_irq(dev->irq, dev);
-	release_region(dev->base_addr, SER12_EXTENT);
-	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
-	       bc_drvname, dev->base_addr, dev->irq);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== hdlcdrv driver interface =========================
- */
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-static struct hdlcdrv_ops ser12_ops = {
-	bc_drvname,
-	bc_drvinfo,
-	ser12_open,
-	ser12_close,
-	baycom_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_setmode(struct baycom_state *bc, const char *modestr)
-{
-	bc->options = !!strchr(modestr, '*');
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct baycom_state *bc;
-	struct baycom_ioctl bi;
-	int cmd2;
-
-	if (!dev || !dev->priv ||
-	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
-		return -EINVAL;
-	}
-	bc = (struct baycom_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-	if (get_user(cmd2, (int *)ifr->ifr_data))
-		return -EFAULT;
-	switch (hi->cmd) {
-	default:
-		break;
-
-	case HDLCDRVCTL_GETMODE:
-		strcpy(hi->data.modename, "ser12");
-		if (bc->options & 1)
-			strcat(hi->data.modename, "*");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_SETMODE:
-		if (!suser() || dev->start)
-			return -EACCES;
-		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
-		return baycom_setmode(bc, hi->data.modename);
-
-	case HDLCDRVCTL_MODELIST:
-		strcpy(hi->data.modename, "ser12");
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_MODEMPARMASK:
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
-
-	}
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-#ifdef BAYCOM_DEBUG
-	case BAYCOMCTL_GETDEBUG:
-		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
-		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
-		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
-		break;
-#endif /* BAYCOM_DEBUG */
-
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-__initfunc(int baycom_ser_hdx_init(void))
-{
-	int i, j, found = 0;
-	char set_hw = 1;
-	struct baycom_state *bc;
-	char ifname[HDLCDRV_IFNAMELEN];
-
-
-	printk(bc_drvinfo);
-	/*
-	 * register net devices
-	 */
-	for (i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		sprintf(ifname, "bcsh%d", i);
-
-		if (!baycom_ports[i].mode)
-			set_hw = 0;
-		if (!set_hw)
-			baycom_ports[i].iobase = baycom_ports[i].irq = 0;
-		j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
-					     sizeof(struct baycom_state),
-					     ifname, baycom_ports[i].iobase,
-					     baycom_ports[i].irq, 0);
-		if (!j) {
-			bc = (struct baycom_state *)dev->priv;
-			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
-				set_hw = 0;
-			found++;
-		} else {
-			printk(KERN_WARNING "%s: cannot register net device\n",
-			       bc_drvname);
-		}
-	}
-	if (!found)
-		return -ENXIO;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-/*
- * command line settable parameters
- */
-static char *mode[NR_PORTS] = { "ser12*", };
-static int iobase[NR_PORTS] = { 0x3f8, };
-static int irq[NR_PORTS] = { 4, };
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
-MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
-MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(iobase, "baycom io base address");
-MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(irq, "baycom irq number");
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
-
-#endif
-
-__initfunc(int init_module(void))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
-		baycom_ports[i].mode = mode[i];
-		baycom_ports[i].iobase = iobase[i];
-		baycom_ports[i].irq = irq[i];
-	}
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-	return baycom_ser_hdx_init();
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct device *dev = baycom_device+i;
-		struct baycom_state *bc = (struct baycom_state *)dev->priv;
-
-		if (bc) {
-			if (bc->hdrv.magic != HDLCDRV_MAGIC)
-				printk(KERN_ERR "baycom: invalid magic in "
-				       "cleanup_module\n");
-			else
-				hdlcdrv_unregister_hdlcdrv(dev);
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: baycom_ser_=io,irq,mode
- * mode: [*]
- * * indicates sofware DCD
- */
-
-__initfunc(void baycom_ser_hdx_setup(char *str, int *ints))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 2)) {
-		printk(KERN_INFO "%s: too many or invalid interface "
-		       "specifications\n", bc_drvname);
-		return;
-	}
-	baycom_ports[i].mode = str;
-	baycom_ports[i].iobase = ints[1];
-	baycom_ports[i].irq = ints[2];
-	if (i < NR_PORTS-1)
-		baycom_ports[i+1].mode = NULL;
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.1.70/linux/drivers/net/bpqether.c	Sat Nov 29 11:25:09 1997
+++ linux/drivers/net/bpqether.c	Wed Dec 31 16:00:00 1969
@@ -1,669 +0,0 @@
-/*
- *	G8BPQ compatible "AX.25 via ethernet" driver release 003
- *
- *	This code REQUIRES 2.0.0 or higher/ NET3.029
- *
- *	This module:
- *		This module 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.
- *
- *	This is a "pseudo" network driver to allow AX.25 over Ethernet
- *	using G8BPQ encapsulation. It has been extracted from the protocol
- *	implementation because
- *
- *		- things got unreadable within the protocol stack
- *		- to cure the protocol stack from "feature-ism"
- *		- a protocol implementation shouldn't need to know on
- *		  which hardware it is running
- *		- user-level programs like the AX.25 utilities shouldn't
- *		  need to know about the hardware.
- *		- IP over ethernet encapsulated AX.25 was impossible
- *		- rxecho.c did not work
- *		- to have room for extensions
- *		- it just deserves to "live" as an own driver
- *
- *	This driver can use any ethernet destination address, and can be
- *	limited to accept frames from one dedicated ethernet card only.
- *
- *	Note that the driver sets up the BPQ devices automagically on
- *	startup or (if started before the "insmod" of an ethernet device)
- *	on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
- *	the ethernet device (in fact: as soon as another ethernet or bpq
- *	device gets "ifconfig"ured).
- *
- *	I have heard that several people are thinking of experiments
- *	with highspeed packet radio using existing ethernet cards.
- *	Well, this driver is prepared for this purpose, just add
- *	your tx key control and a txdelay / tailtime algorithm,
- *	probably some buffering, and /voila/...
- *
- *	History
- *	BPQ   001	Joerg(DL1BKE)		Extracted BPQ code from AX.25
- *						protocol stack and added my own
- *						yet existing patches
- *	BPQ   002	Joerg(DL1BKE)		Scan network device list on
- *						startup.
- *	BPQ   003	Joerg(DL1BKE)		Ethernet destination address
- *						and accepted source address
- *						can be configured by an ioctl()
- *						call.
- *						Fixed to match Linux networking
- *						changes - 2.1.15.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/segment.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/firewall.h>
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <net/ip.h>
-#include <net/arp.h>
-
-#include <linux/bpqether.h>
-
-static unsigned char ax25_bcast[AX25_ADDR_LEN] =
-	{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
-	{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
-static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
-
-static char bpq_eth_addr[6];
-
-static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *);
-static int bpq_device_event(struct notifier_block *, unsigned long, void *);
-static char *bpq_print_ethaddr(unsigned char *);
-
-static struct packet_type bpq_packet_type = {
-	0,		/* ntohs(ETH_P_BPQ),*/
-	0,		/* copy */
-	bpq_rcv,
-	NULL,
-	NULL,
-};
-
-static struct notifier_block bpq_dev_notifier = {
-	bpq_device_event,
-	0
-};
-
-
-#define MAXBPQDEV 100
-
-static struct bpqdev {
-	struct bpqdev *next;
-	char   ethname[14];		/* ether device name */
-	struct device *ethdev;		/* link to ethernet device */
-	struct device axdev;		/* bpq device (bpq#) */
-	struct net_device_stats stats;	/* some statistics */
-	char   dest_addr[6];		/* ether destination address */
-	char   acpt_addr[6];		/* accept ether frames from this address only */
-} *bpq_devices = NULL;
-
-
-/* ------------------------------------------------------------------------ */
-
-
-/*
- *	Get the ethernet device for a BPQ device
- */
-static __inline__ struct device *bpq_get_ether_dev(struct device *dev)
-{
-	struct bpqdev *bpq;
-
-	bpq = (struct bpqdev *)dev->priv;
-
-	return (bpq != NULL) ? bpq->ethdev : NULL;
-}
-
-/*
- *	Get the BPQ device for the ethernet device
- */
-static __inline__ struct device *bpq_get_ax25_dev(struct device *dev)
-{
-	struct bpqdev *bpq;
-
-	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
-		if (bpq->ethdev == dev)
-			return &bpq->axdev;
-
-	return NULL;
-}
-
-static __inline__ int dev_is_ethdev(struct device *dev)
-{
-	return (
-			dev->type == ARPHRD_ETHER
-			&& strncmp(dev->name, "dummy", 5)
-	);
-}
-
-/*
- *	Sanity check: remove all devices that ceased to exists and
- *	return '1' if the given BPQ device was affected.
- */
-static int bpq_check_devices(struct device *dev)
-{
-	struct bpqdev *bpq, *bpq_prev;
-	int result = 0;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	bpq_prev = NULL;
-
-	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
-		if (!dev_get(bpq->ethname)) {
-			if (bpq_prev)
-				bpq_prev->next = bpq->next;
-			else
-				bpq_devices = bpq->next;
-
-			if (&bpq->axdev == dev)
-				result = 1;
-
-			unregister_netdev(&bpq->axdev);
-			kfree(bpq);
-		}
-
-		bpq_prev = bpq;
-	}
-
-	restore_flags(flags);
-
-	return result;
-}
-
-
-/* ------------------------------------------------------------------------ */
-
-
-/*
- *	Receive an AX.25 frame via an ethernet interface.
- */
-static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
-{
-	int len;
-	char * ptr;
-	struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
-	struct bpqdev *bpq;
-
-	skb->sk = NULL;		/* Initially we don't know who it's for */
-
-	dev = bpq_get_ax25_dev(dev);
-
-	if (dev == NULL || dev->start == 0) {
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	/*
-	 * if we want to accept frames from just one ethernet device
-	 * we check the source address of the sender.
-	 */
-
-	bpq = (struct bpqdev *)dev->priv;
-
-	if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) {
-		printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source));
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}
-
-	len = skb->data[0] + skb->data[1] * 256 - 5;
-
-	skb_pull(skb, 2);	/* Remove the length bytes */
-	skb_trim(skb, len);	/* Set the length of the data */
-
-	((struct bpqdev *)dev->priv)->stats.rx_packets++;
-	((struct bpqdev *)dev->priv)->stats.rx_bytes+=len;
-
-	ptr = skb_push(skb, 1);
-	*ptr = 0;
-
-	skb->dev = dev;
-	skb->protocol = htons(ETH_P_AX25);
-	skb->mac.raw = skb->data;
-	skb->pkt_type = PACKET_HOST;
-
-	netif_rx(skb);
-
-	return 0;
-}
-
-/*
- * 	Send an AX.25 frame via an ethernet interface
- */
-static int bpq_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct sk_buff *newskb;
-	unsigned char *ptr;
-	struct bpqdev *bpq;
-	int size;
-
-	/*
-	 * Just to be *really* sure not to send anything if the interface
-	 * is down, the ethernet device may have gone.
-	 */
-	if (!dev->start) {
-		bpq_check_devices(dev);
-		kfree_skb(skb, FREE_WRITE);
-		return -ENODEV;
-	}
-
-	skb_pull(skb, 1);
-	size = skb->len;
-
-	/*
-	 * The AX.25 code leaves enough room for the ethernet header, but
-	 * sendto() does not.
-	 */
-	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
-		if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
-			printk(KERN_WARNING "bpqether: out of memory\n");
-			kfree_skb(skb, FREE_WRITE);
-			return -ENOMEM;
-		}
-
-		if (skb->sk != NULL)
-			skb_set_owner_w(newskb, skb->sk);
-
-		kfree_skb(skb, FREE_WRITE);
-		skb = newskb;
-	}
-
-	skb->protocol = htons(ETH_P_AX25);
-
-	ptr = skb_push(skb, 2);
-
-	*ptr++ = (size + 5) % 256;
-	*ptr++ = (size + 5) / 256;
-
-	bpq = (struct bpqdev *)dev->priv;
-
-	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
-		bpq->stats.tx_dropped++;
-		kfree_skb(skb, FREE_WRITE);
-		return -ENODEV;
-	}
-
-	skb->dev = dev;
-	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
-	bpq->stats.tx_packets++;
-	bpq->stats.tx_bytes+=skb->len;
-  
-	dev_queue_xmit(skb);
-
-	return 0;
-}
-
-/*
- *	Statistics
- */
-static struct net_device_stats *bpq_get_stats(struct device *dev)
-{
-	struct bpqdev *bpq;
-
-	bpq = (struct bpqdev *)dev->priv;
-
-	return &bpq->stats;
-}
-
-/*
- *	Set AX.25 callsign
- */
-static int bpq_set_mac_address(struct device *dev, void *addr)
-{
-    struct sockaddr *sa = (struct sockaddr *)addr;
-
-    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
-    return 0;
-}
-
-/*	Ioctl commands
- *
- *		SIOCSBPQETHOPT		reserved for enhancements
- *		SIOCSBPQETHADDR		set the destination and accepted
- *					source ethernet address (broadcast
- *					or multicast: accept all)
- */
-static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-	int err;
-	struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
-	struct bpqdev *bpq = dev->priv;
-	struct bpq_req req;
-
-	if (!suser())
-		return -EPERM;
-
-	if (bpq == NULL)		/* woops! */
-		return -ENODEV;
-
-	switch (cmd) {
-		case SIOCSBPQETHOPT:
-			if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
-				return err;
-			copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req));
-			switch (req.cmd) {
-				case SIOCGBPQETHPARAM:
-				case SIOCSBPQETHPARAM:
-				default:
-					return -EINVAL;
-			}
-
-			break;
-
-		case SIOCSBPQETHADDR:
-			if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
-				return err;
-			copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
-			copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
-			break;
-
-		default:
-			return -EINVAL;
-	}
-
-	return 0;
-}
-
-/*
- * open/close a device
- */
-static int bpq_open(struct device *dev)
-{
-	if (bpq_check_devices(dev))
-		return -ENODEV;		/* oops, it's gone */
-
-	dev->tbusy = 0;
-	dev->start = 1;
-
-	MOD_INC_USE_COUNT;
-
-	return 0;
-}
-
-static int bpq_close(struct device *dev)
-{
-	dev->tbusy = 1;
-	dev->start = 0;
-
-	MOD_DEC_USE_COUNT;
-
-	return 0;
-}
-
-/*
- * currently unused
- */
-static int bpq_dev_init(struct device *dev)
-{
-	return 0;
-}
-
-
-/* ------------------------------------------------------------------------ */
-
-
-/*
- *	Proc filesystem
- */
-static char * bpq_print_ethaddr(unsigned char *e)
-{
-	static char buf[18];
-
-	sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
-		e[0], e[1], e[2], e[3], e[4], e[5]);
-
-	return buf;
-}
-
-int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct bpqdev *bpqdev;
-	int len     = 0;
-	off_t pos   = 0;
-	off_t begin = 0;
-
-	cli();
-
-	len += sprintf(buffer, "dev   ether      destination        accept from\n");
-
-	for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) {
-		len += sprintf(buffer + len, "%-5s %-10s %s  ",
-			bpqdev->axdev.name, bpqdev->ethname,
-			bpq_print_ethaddr(bpqdev->dest_addr));
-
-		len += sprintf(buffer + len, "%s\n",
-			(bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr));
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-
-	sti();
-
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
-
-	if (len > length) len = length;
-
-	return len;
-}
-
-
-/* ------------------------------------------------------------------------ */
-
-
-/*
- *	Setup a new device.
- */
-static int bpq_new_device(struct device *dev)
-{
-	int k;
-	unsigned char *buf;
-	struct bpqdev *bpq, *bpq2;
-
-	if ((bpq = kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	memset(bpq, 0, sizeof(struct bpqdev));
-
-	bpq->ethdev = dev;
-
-	bpq->ethname[sizeof(bpq->ethname)-1] = '\0';
-	strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1);
-
-	memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
-	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
-
-	dev = &bpq->axdev;
-	buf = kmalloc(14, GFP_KERNEL);
-
-	for (k = 0; k < MAXBPQDEV; k++) {
-		struct device *odev;
-
-		sprintf(buf, "bpq%d", k);
-
-		if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev))
-			break;
-	}
-
-	if (k == MAXBPQDEV) {
-		kfree(bpq);
-		return -ENODEV;
-	}
-
-	dev->priv = (void *)bpq;	/* pointer back */
-	dev->name = buf;
-	dev->init = bpq_dev_init;
-
-	if (register_netdev(dev) != 0) {
-		kfree(bpq);
-                return -EIO;
-        }
-
-	dev_init_buffers(dev);
-
-	dev->hard_start_xmit = bpq_xmit;
-	dev->open	     = bpq_open;
-	dev->stop	     = bpq_close;
-	dev->set_mac_address = bpq_set_mac_address;
-	dev->get_stats	     = bpq_get_stats;
-	dev->do_ioctl	     = bpq_ioctl;
-
-	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
-	memcpy(dev->dev_addr,  ax25_defaddr, AX25_ADDR_LEN);
-
-	dev->flags      = 0;
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-	dev->hard_header     = ax25_encapsulate;
-	dev->rebuild_header  = ax25_rebuild_header;
-#endif
-
-	dev->type            = ARPHRD_AX25;
-	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
-	dev->mtu             = AX25_DEF_PACLEN;
-	dev->addr_len        = AX25_ADDR_LEN;
-
-	cli();
-
-	if (bpq_devices == NULL) {
-		bpq_devices = bpq;
-	} else {
-		for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next);
-		bpq2->next = bpq;
-	}
-
-	sti();
-
-	return 0;
-}
-
-
-/*
- *	Handle device status changes.
- */
-static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
-{
-	struct device *dev = (struct device *)ptr;
-
-	if (!dev_is_ethdev(dev))
-		return NOTIFY_DONE;
-
-	bpq_check_devices(NULL);
-
-	switch (event) {
-		case NETDEV_UP:		/* new ethernet device -> new BPQ interface */
-			if (bpq_get_ax25_dev(dev) == NULL)
-				bpq_new_device(dev);
-			break;
-
-		case NETDEV_DOWN:	/* ethernet device closed -> close BPQ interface */
-			if ((dev = bpq_get_ax25_dev(dev)) != NULL)
-				dev_close(dev);
-			break;
-
-		default:
-			break;
-	}
-
-	return NOTIFY_DONE;
-}
-
-
-/* ------------------------------------------------------------------------ */
-
-/*
- * Initialize driver. To be called from af_ax25 if not compiled as a
- * module
- */
-__initfunc(int bpq_init(void))
-{
-	struct device *dev;
-
-	bpq_packet_type.type  = htons(ETH_P_BPQ);
-	dev_add_pack(&bpq_packet_type);
-
-	register_netdevice_notifier(&bpq_dev_notifier);
-
-	printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
-
-#ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_AX25_BPQETHER, 8, "bpqether",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		bpq_get_info
-	});
-#endif
-
-	for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev_is_ethdev(dev))
-			bpq_new_device(dev);
-	}
-
-	return 0;
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
-MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
-
-int init_module(void)
-{
-	return bpq_init();
-}
-
-void cleanup_module(void)
-{
-	struct bpqdev *bpq;
-
-	dev_remove_pack(&bpq_packet_type);
-
-	unregister_netdevice_notifier(&bpq_dev_notifier);
-
-#ifdef CONFIG_PROC_FS
-	proc_net_unregister(PROC_NET_AX25_BPQETHER);
-#endif
-
-	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
-		unregister_netdev(&bpq->axdev);
-}
-#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/Config.in linux/drivers/net/hamradio/Config.in
--- v2.1.70/linux/drivers/net/hamradio/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/Config.in	Wed Dec  3 15:17:30 1997
@@ -0,0 +1,62 @@
+#
+# Amateur Radio protocols and AX.25 device configuration
+#
+# 19971130	Now in an own category to make correct compilation of the 
+#		AX.25 stuff easier...
+#		Joerg Reuter DL1BKE <jreuter@poboxes.com>
+
+mainmenu_option next_comment
+comment 'Amateur Radio support'
+bool 'Amateur Radio support' CONFIG_HAMRADIO
+
+if [ "$CONFIG_HAMRADIO" != "n" ] ; then
+  if [ "$CONFIG_NET" != "n" ] ; then
+    comment 'Packet Radio protocols'
+    tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25
+    if [ "$CONFIG_AX25" != "n" ]; then
+      bool '   AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+#     bool '   AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
+      dep_tristate '   Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
+      dep_tristate '   Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
+    fi
+    
+    if [ "$CONFIG_AX25" != "n" ]; then
+      comment 'AX.25 network device drivers'
+      tristate 'Serial port KISS driver' CONFIG_MKISS
+#     tristate 'Serial port 6PACK driver' CONFIG_6PACK
+      tristate 'BPQ Ethernet driver' CONFIG_BPQETHER
+      tristate 'Gracilis PackeTwin driver' CONFIG_PT
+      tristate 'Ottawa PI and PI2 driver' CONFIG_PI
+    
+      tristate 'Z8530 SCC driver' CONFIG_SCC
+      if [ "$CONFIG_SCC" != "n" ]; then
+        bool '   additional delay for PA0HZP OptoSCC compatible boards' CONFIG_SCC_DELAY
+        bool '   support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO
+      fi
+    
+      tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX
+      tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX
+      tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR
+    
+      tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM
+      if [ "$CONFIG_SOUNDMODEM" != "n" ]; then
+        bool '   soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC
+        bool '   soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS
+        bool '   soundmodem support for 1200 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK1200
+        bool '   soundmodem support for 2400 baud AFSK modulation (7.3728MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_7
+        bool '   soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
+        bool '   soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
+        bool '   soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
+      fi
+    fi
+  fi
+   
+  comment 'Misc. hamradio protocols'
+  tristate 'Shortwave radio modem driver' CONFIG_HFMODEM
+  if [ "$CONFIG_HFMODEM" != "n" ]; then
+    bool '   HFmodem support for Soundblaster and compatible cards' CONFIG_HFMODEM_SBC
+    bool '   HFmodem support for WSS and Crystal cards' CONFIG_HFMODEM_WSS
+  fi
+fi
+
+endmenu
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/Makefile linux/drivers/net/hamradio/Makefile
--- v2.1.70/linux/drivers/net/hamradio/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/Makefile	Wed Dec  3 15:17:30 1997
@@ -0,0 +1,120 @@
+# File: drivers/hamradio/Makefile
+#
+# Makefile for the Linux AX.25 and HFMODEM device drivers.
+#
+# 19971130 	Moved the amateur radio related network drivers from 
+#		drivers/net/ to drivers/hamradio for easier maintainance.
+#               Joerg Reuter DL1BKE <jreuter@poboxes.com>
+
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := hamradio.a
+L_OBJS   := 
+M_OBJS   :=
+MOD_LIST_NAME := HAM_MODULES
+
+# Need these to keep track of whether the hdlc module should
+# really go in the kernel or a module.
+CONFIG_HDLCDRV_BUILTIN :=
+CONFIG_HDLCDRV_MODULE  :=
+
+ifeq ($(CONFIG_SCC),y)
+L_OBJS += scc.o
+else
+  ifeq ($(CONFIG_SCC),m)
+  M_OBJS += scc.o
+  endif
+endif
+
+ifeq ($(CONFIG_MKISS),y)
+L_OBJS += mkiss.o
+else
+  ifeq ($(CONFIG_MKISS),m)
+  M_OBJS += mkiss.o
+  endif
+endif
+
+ifeq ($(CONFIG_PI),y)
+L_OBJS += pi2.o
+else
+  ifeq ($(CONFIG_PI),m)
+  M_OBJS += pi2.o
+  endif
+endif
+
+ifeq ($(CONFIG_PT),y)
+L_OBJS += pt.o
+else
+  ifeq ($(CONFIG_PT),m)
+  M_OBJS += pt.o
+  endif
+endif
+
+ifeq ($(CONFIG_BPQETHER),y)
+L_OBJS += bpqether.o
+else
+  ifeq ($(CONFIG_BPQETHER),m)
+  M_OBJS += bpqether.o
+  endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_SER_FDX),y)
+L_OBJS += baycom_ser_fdx.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM_SER_FDX),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom_ser_fdx.o
+  endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_SER_HDX),y)
+L_OBJS += baycom_ser_hdx.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM_SER_HDX),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom_ser_hdx.o
+  endif
+endif
+
+ifeq ($(CONFIG_BAYCOM_PAR),y)
+L_OBJS += baycom_par.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_BAYCOM_PAR),m)
+  CONFIG_HDLCDRV_MODULE = y
+  M_OBJS += baycom_par.o
+  endif
+endif
+
+ifeq ($(CONFIG_SOUNDMODEM),y)
+ALL_SUB_DIRS += soundmodem
+SUB_DIRS += soundmodem
+L_OBJS += soundmodem/soundmodem.o
+CONFIG_HDLCDRV_BUILTIN = y
+else
+  ifeq ($(CONFIG_SOUNDMODEM),m)
+  CONFIG_HDLCDRV_MODULE = y
+  ALL_SUB_DIRS += soundmodem
+  MOD_SUB_DIRS += soundmodem
+  endif
+endif
+
+# If anything built-in uses the hdlcdrv, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_HDLCDRV_BUILTIN
+LX_OBJS += hdlcdrv.o
+else
+  ifdef CONFIG_HDLCDRV_MODULE
+  MX_OBJS += hdlcdrv.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+clean:
+	rm -f core *.o *.a *.s
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c
--- v2.1.70/linux/drivers/net/hamradio/baycom_par.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/baycom_par.c	Sun Nov 30 10:30:19 1997
@@ -0,0 +1,661 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_par.c  -- baycom par96 and picpar radio modem driver.
+ *
+ *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  par96:  This is a modem for 9600 baud FSK compatible to the G3RUH standard.
+ *          The modem does all the filtering and regenerates the receiver clock.
+ *          Data is transferred from and to the PC via a shift register.
+ *          The shift register is filled with 16 bits and an interrupt is
+ *          signalled. The PC then empties the shift register in a burst. This
+ *          modem connects to the parallel port, hence the name. The modem
+ *          leaves the implementation of the HDLC protocol and the scrambler
+ *          polynomial to the PC. This modem is no longer available (at least
+ *          from Baycom) and has been replaced by the PICPAR modem (see below).
+ *          You may however still build one from the schematics published in
+ *          cq-DL :-).
+ *
+ *  picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The
+ *          modem is protocol compatible to par96, but uses only three low
+ *          power ICs and can therefore be fed from the parallel port and
+ *          does not require an additional power supply. It features
+ *          built in DCD circuitry. The driver should therefore be configured
+ *          for hardware DCD.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     driver mode string. Valid choices are par96 and picpar.
+ *  iobase   base address of the port; common values are 0x378, 0x278, 0x3bc
+ *
+ *
+ *  History:
+ *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.97  init code/data tagged
+ *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.97  split into separate files for ser12/par96
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+#include <linux/parport.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern __inline__ int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern __inline__ int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD  1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_par";
+static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_par: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	const char *mode;
+	int iobase;
+} baycom_ports[NR_PORTS] = { { NULL, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define SER12_EXTENT 8
+
+#define LPT_DATA(dev)    ((dev)->base_addr+0)
+#define LPT_STATUS(dev)  ((dev)->base_addr+1)
+#define LPT_CONTROL(dev) ((dev)->base_addr+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define PAR96_BURSTBITS 16
+#define PAR96_BURST     4
+#define PAR96_PTT       2
+#define PAR96_TXBIT     1
+#define PAR96_ACK       0x40
+#define PAR96_RXBIT     0x20
+#define PAR96_DCD       0x10
+#define PAR97_POWER     0xf8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	struct pardevice *pdev;
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_par96 {
+			int dcd_count;
+			unsigned int dcd_shreg;
+			unsigned long descram;
+			unsigned long scram;
+		} par96;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void __inline__ baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== PAR96 specific routines =========================
+ */
+
+#define PAR96_DESCRAM_TAP1 0x20000
+#define PAR96_DESCRAM_TAP2 0x01000
+#define PAR96_DESCRAM_TAP3 0x00001
+
+#define PAR96_DESCRAM_TAPSH1 17
+#define PAR96_DESCRAM_TAPSH2 12
+#define PAR96_DESCRAM_TAPSH3 0
+
+#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */
+#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_tx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data = hdlcdrv_getbits(&bc->hdrv);
+
+	for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) {
+		unsigned char val = PAR97_POWER;
+		bc->modem.par96.scram = ((bc->modem.par96.scram << 1) |
+					 (bc->modem.par96.scram & 1));
+		if (!(data & 1))
+			bc->modem.par96.scram ^= 1;
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1))
+			bc->modem.par96.scram ^=
+				(PAR96_SCRAM_TAPN << 1);
+		if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2))
+			val |= PAR96_TXBIT;
+		outb(val, LPT_DATA(dev));
+		outb(val | PAR96_BURST, LPT_DATA(dev));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void par96_rx(struct device *dev, struct baycom_state *bc)
+{
+	int i;
+	unsigned int data, mask, mask2, descx;
+
+	/*
+	 * do receiver; differential decode and descramble on the fly
+	 */
+	for(data = i = 0; i < PAR96_BURSTBITS; i++) {
+		bc->modem.par96.descram = (bc->modem.par96.descram << 1);
+		if (inb(LPT_STATUS(dev)) & PAR96_RXBIT)
+			bc->modem.par96.descram |= 1;
+		descx = bc->modem.par96.descram ^
+			(bc->modem.par96.descram >> 1);
+		/* now the diff decoded data is inverted in descram */
+		outb(PAR97_POWER | PAR96_PTT, LPT_DATA(dev));
+		descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^
+			  (descx >> PAR96_DESCRAM_TAPSH2));
+		data >>= 1;
+		if (!(descx & 1))
+			data |= 0x8000;
+		outb(PAR97_POWER | PAR96_PTT | PAR96_BURST, LPT_DATA(dev));
+	}
+	hdlcdrv_putbits(&bc->hdrv, data);
+	/*
+	 * do DCD algorithm
+	 */
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16)
+			| (data << 16);
+		/* search for flags and set the dcd counter appropriately */
+		for(mask = 0x1fe00, mask2 = 0xfc00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if ((bc->modem.par96.dcd_shreg & mask) == mask2)
+				bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4;
+		/* check for abort/noise sequences */
+		for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0;
+		    i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1)
+			if (((bc->modem.par96.dcd_shreg & mask) == mask2) &&
+			    (bc->modem.par96.dcd_count >= 0))
+				bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10;
+		/* decrement and set the dcd variable */
+		if (bc->modem.par96.dcd_count >= 0)
+			bc->modem.par96.dcd_count -= 2;
+		hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0);
+	} else {
+		hdlcdrv_setdcd(&bc->hdrv, !!(inb(LPT_STATUS(dev)) & PAR96_DCD));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct parport *pp = (struct parport *)dev_id;
+	struct pardevice *pd = pp->cad;
+	struct device *dev = (struct device *)pd->private;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		par96_tx(dev, bc);
+	else {
+		par96_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = 6;
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_preempt(void *handle)
+{
+	/* we cannot relinquish the port in the middle of an operation */
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void par96_wakeup(void *handle)
+{
+        struct device *dev = (struct device *)handle;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name);
+	if (!parport_claim(bc->pdev))
+		printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_open(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	struct parport *pp = parport_enumerate();
+
+	if (!dev || !bc)
+		return -ENXIO;
+	while (pp && pp->base != dev->base_addr) 
+		pp = pp->next;
+	if (!pp) {
+		printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr);
+		return -ENXIO;
+	}
+	if (pp->irq < 0) {
+		printk(KERN_ERR "baycom_par: parport at 0x%x has no irq\n", pp->base);
+		return -ENXIO;
+	}
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 9600;
+	if (!(bc->pdev = parport_register_device(pp, dev->name, par96_preempt, par96_wakeup, 
+						 par96_interrupt, PARPORT_DEV_LURK, dev))) {
+		printk(KERN_ERR "baycom_par: cannot register parport at 0x%x\n", pp->base);
+		return -ENXIO;
+	}
+	if (parport_claim(bc->pdev)) {
+		printk(KERN_ERR "baycom_par: parport at 0x%x busy\n", pp->base);
+		parport_unregister_device(bc->pdev);
+		return -EBUSY;
+	}
+	dev->irq = pp->irq;
+	/* bc->pdev->port->ops->change_mode(bc->pdev->port, PARPORT_MODE_PCSPP);  not yet implemented */
+	/* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
+	/*bc->pdev->port->ops->enable_irq(bc->pdev->port);  not yet implemented */
+        outb(LPT_IRQ_ENABLE, LPT_CONTROL(dev));	
+	printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n",
+	       bc_drvname, dev->base_addr, dev->irq, bc->options);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int par96_close(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/* disable interrupt */
+        outb(0, LPT_CONTROL(dev));
+	/*bc->pdev->port->ops->disable_irq(bc->pdev->port);  not yet implemented */
+	/* switch off PTT */
+	outb(PAR96_PTT | PAR97_POWER, LPT_DATA(dev));
+	parport_release(bc->pdev);
+	parport_unregister_device(bc->pdev);
+	printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops par96_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	par96_open,
+	par96_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	if (!strncmp(modestr, "picpar", 6))
+		bc->options = 0;
+	else if (!strncmp(modestr, "par96", 5))
+		bc->options = BAYCOM_OPTIONS_SOFTDCD;
+	else
+		bc->options = !!strchr(modestr, '*');
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	int cmd2;
+
+	if (!dev || !dev->priv ||
+	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	bc = (struct baycom_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (get_user(cmd2, (int *)ifr->ifr_data))
+		return -EFAULT;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		strcpy(hi->data.modename, bc->options ? "par96" : "picpar");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "par96,picpar");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_par_init(void))
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		sprintf(ifname, "bcp%d", i);
+
+		if (!baycom_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			baycom_ports[i].iobase = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &par96_ops,
+					     sizeof(struct baycom_state),
+					     ifname, baycom_ports[i].iobase, 0, 0);
+		if (!j) {
+			bc = (struct baycom_state *)dev->priv;
+			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       bc_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static const char *mode[NR_PORTS] = { "picpar", };
+static int iobase[NR_PORTS] = { 0x378, };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+		baycom_ports[i].mode = mode[i];
+		baycom_ports[i].iobase = iobase[i];
+	}
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+	return baycom_par_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+		if (bc) {
+			if (bc->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "baycom: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_par=io,mode
+ * mode: par96,picpar
+ */
+
+__initfunc(void baycom_par_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 1)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", bc_drvname);
+		return;
+	}
+	baycom_ports[i].mode = str;
+	baycom_ports[i].iobase = ints[1];
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- v2.1.70/linux/drivers/net/hamradio/baycom_ser_fdx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Sun Nov 30 10:30:19 1997
@@ -0,0 +1,762 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_ser_fdx.c  -- baycom ser12 fullduplex radio modem driver.
+ *
+ *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     * enables software DCD.
+ *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ *  baud     baud rate (between 300 and 4800)
+ *  irq      interrupt line of the port; common values are 4,3
+ *
+ *
+ *  History:
+ *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.97  init code/data tagged
+ *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.97  ser12/par96 split into separate files
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD  1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_fdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_fdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq, baud;
+} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	unsigned int baud, baud_us8, baud_arbdiv;
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char tx_bit;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			unsigned char last_sample;
+			unsigned char last_rxbit;
+			unsigned int dcd_shreg;
+			unsigned int dcd_time;
+			unsigned int bit_pll;
+			unsigned long last_jiffies;
+			unsigned int pll_time;
+			unsigned int txshreg;
+		} ser12;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+extern inline unsigned int hweight16(unsigned short w)
+        __attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rxsample(struct device *dev, struct baycom_state *bc, unsigned char news)
+{
+	bc->modem.ser12.dcd_shreg <<= 1;
+	bc->modem.ser12.bit_pll += 0x2000;
+	if (bc->modem.ser12.last_sample != news) {
+		bc->modem.ser12.last_sample = news;
+		bc->modem.ser12.dcd_shreg |= 1;
+		if (bc->modem.ser12.bit_pll < 0x9000)
+			bc->modem.ser12.bit_pll += 0x1000;
+		else
+			bc->modem.ser12.bit_pll -= 0x1000;
+		bc->modem.ser12.dcd_sum0 += 4 * hweight8(bc->modem.ser12.dcd_shreg & 0x38)
+			- hweight16(bc->modem.ser12.dcd_shreg & 0x7c0);
+	}
+	hdlcdrv_channelbit(&bc->hdrv, !!bc->modem.ser12.last_sample);
+	if ((--bc->modem.ser12.dcd_time) <= 0) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + 
+					   bc->modem.ser12.dcd_sum1 + 
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		bc->modem.ser12.dcd_sum0 = 2; /* slight bias */
+		bc->modem.ser12.dcd_time = 120;
+	}
+	if (bc->modem.ser12.bit_pll >= 0x10000) {
+		bc->modem.ser12.bit_pll &= 0xffff;
+		bc->modem.shreg >>= 1;
+		if (bc->modem.ser12.last_rxbit == bc->modem.ser12.last_sample)
+			bc->modem.shreg |=  0x10000;
+		bc->modem.ser12.last_rxbit = bc->modem.ser12.last_sample;
+		if (bc->modem.shreg & 1) {
+			hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+			bc->modem.shreg = 0x10000;
+		}
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static __inline__ void ser12_rx(struct device *dev, struct baycom_state *bc, unsigned char curs)
+{
+	unsigned long curjiff;
+	struct timeval tv;
+	unsigned int timediff;
+
+	/*
+	 * get current time
+	 */
+	curjiff = jiffies;
+	do_gettimeofday(&tv);
+	if ((signed)(curjiff - bc->modem.ser12.last_jiffies) >= HZ/4) {
+		/* long inactivity; clear HDLC and DCD */
+		bc->modem.ser12.dcd_sum1 = 0;
+		bc->modem.ser12.dcd_sum2 = 0;
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = 120;
+		hdlcdrv_setdcd(&bc->hdrv, 0);
+		hdlcdrv_putbits(&bc->hdrv, 0xffff);
+		bc->modem.ser12.last_jiffies = curjiff;
+		bc->modem.ser12.pll_time = tv.tv_usec;
+	}
+	bc->modem.ser12.last_jiffies = curjiff;
+	timediff = tv.tv_usec + 1000000 - bc->modem.ser12.pll_time;
+	timediff %= 1000000;
+	timediff /= bc->baud_us8;
+	bc->modem.ser12.pll_time = (bc->modem.ser12.pll_time + timediff * (bc->baud_us8)) % 1000000;
+	for (; timediff > 1; timediff--)
+		ser12_rxsample(dev, bc, bc->modem.ser12.last_sample);
+	if (timediff >= 1)
+		ser12_rxsample(dev, bc, curs);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	unsigned char iir, msr = 0;
+	unsigned int txcount = 0;
+	unsigned int rxcount = 0;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	
+	for (;;) {
+		iir = inb(IIR(dev->base_addr));
+		if (iir & 1)
+			break;
+		switch (iir & 6) {
+		case 6:
+			inb(LSR(dev->base_addr));
+			continue;
+			
+		case 4:
+			inb(RBR(dev->base_addr));
+			continue;
+			
+		case 2:
+			/*
+			 * make sure the next interrupt is generated;
+			 * 0 must be used to power the modem; the modem draws its
+			 * power from the TxD line
+			 */
+			outb(0x00, THR(dev->base_addr));
+			bc->modem.arb_divider--;
+			baycom_int_freq(bc);
+			if (hdlcdrv_ptt(&bc->hdrv)) {
+				/*
+				 * first output the last bit (!) then call HDLC transmitter,
+				 * since this may take quite long
+				 */
+				outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+				txcount++;
+			} else
+				outb(0x0d, MCR(dev->base_addr));       /* transmitter off */
+			continue;
+			
+		default:
+			msr = inb(MSR(dev->base_addr));
+			if (msr & 1)  /* delta CTS interrupt */
+				rxcount++;
+			continue;
+		}
+	}
+	if (rxcount)
+		ser12_rx(dev, bc, msr & 0x10);
+	if (txcount) {
+#ifdef BAYCOM_DEBUG
+		if (bc->debug_vals.cur_pllcorr < txcount)
+			bc->debug_vals.cur_pllcorr = txcount;
+#endif /* BAYCOM_DEBUG */
+		if (bc->modem.ser12.txshreg <= 1)
+			bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+		bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1));
+		bc->modem.ser12.txshreg >>= 1;
+	}
+	sti();
+	if (bc->modem.arb_divider <= 0) {
+		bc->modem.arb_divider = bc->baud_arbdiv;
+		hdlcdrv_arbitrate(dev, &bc->hdrv);
+	}
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	    c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = { 
+	"unknown", "8250", "16450", "16550", "16550A" 
+};
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (bc->baud < 300 || bc->baud > 4800)
+		return -EINVAL;
+	if (check_region(dev->base_addr, SER12_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = bc->baud;
+	bc->baud_us8 = 125000/bc->baud;
+	bc->baud_arbdiv = bc->baud/100;
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+		return -EIO;
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+			"baycom_ser_fdx", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx");
+	/*
+	 * set the SIO to 6 Bits/character and 19600 baud, so that
+	 * we get exactly (hopefully) one interrupt per radio symbol
+	 */
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(115200/8/bc->baud, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * enable transmitter empty interrupt and modem status interrupt
+	 */
+	outb(0x0a, IER(dev->base_addr));
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u options "
+	       "0x%x baud %u uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+	       bc->options, bc->baud, uart_str[u]);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	ser12_open,
+	ser12_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	unsigned int baud;
+
+	if (!strncmp(modestr, "ser", 3)) {
+		baud = simple_strtoul(modestr+3, NULL, 10);
+		if (baud >= 3 && baud <= 48)
+			bc->baud = baud*100;
+	}
+	bc->options = !!strchr(modestr, '*');
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	int cmd2;
+
+	if (!dev || !dev->priv ||
+	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	bc = (struct baycom_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (get_user(cmd2, (int *)ifr->ifr_data))
+		return -EFAULT;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		sprintf(hi->data.modename, "ser%u", bc->baud / 100);
+		if (bc->options & 1)
+			strcat(hi->data.modename, "*");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "ser12,ser3,ser24");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_ser_fdx_init(void))
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		sprintf(ifname, "bcsf%d", i);
+
+		if (!baycom_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			baycom_ports[i].iobase = baycom_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
+					     sizeof(struct baycom_state),
+					     ifname, baycom_ports[i].iobase,
+					     baycom_ports[i].irq, 0);
+		if (!j) {
+			bc = (struct baycom_state *)dev->priv;
+			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+				set_hw = 0;
+			bc->baud = baycom_ports[i].baud;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       bc_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+MODULE_PARM(baud, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+		baycom_ports[i].mode = mode[i];
+		baycom_ports[i].iobase = iobase[i];
+		baycom_ports[i].irq = irq[i];
+		baycom_ports[i].baud = baud[i];
+	}
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+	return baycom_ser_fdx_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+		if (bc) {
+			if (bc->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "baycom: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_ser_=io,irq,mode
+ * mode: [*]
+ * * indicates sofware DCD
+ */
+
+__initfunc(void baycom_ser_fdx_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 2)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", bc_drvname);
+		return;
+	}
+	baycom_ports[i].mode = str;
+	baycom_ports[i].iobase = ints[1];
+	baycom_ports[i].irq = ints[2];
+	if (ints[0] >= 3)
+		baycom_ports[i].baud = ints[3];
+	else
+		baycom_ports[i].baud = 1200;
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- v2.1.70/linux/drivers/net/hamradio/baycom_ser_hdx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Sun Nov 30 10:30:19 1997
@@ -0,0 +1,792 @@
+/*****************************************************************************/
+
+/*
+ *	baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
+ *
+ *	Copyright (C) 1997  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Supported modems
+ *
+ *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
+ *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
+ *          is responsible for regenerating the receiver bit clock, as well as
+ *          for handling the HDLC protocol. The modem connects to a serial port,
+ *          hence the name. Since the serial port is not used as an async serial
+ *          port, the kernel driver for serial ports cannot be used, and this
+ *          driver only supports standard serial hardware (8250, 16450, 16550A)
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     * enables software DCD.
+ *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
+ *  irq      interrupt line of the port; common values are 4,3
+ *
+ *
+ *  History:
+ *   0.1  26.06.96  Adapted from baycom.c and made network driver interface
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.3  26.04.97  init code/data tagged
+ *   0.4  08.07.97  alternative ser12 decoding algorithm (uses delta CTS ints)
+ *   0.5  11.11.97  ser12/par96 split into separate files
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/hdlcdrv.h>
+#include <linux/baycom.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define BAYCOM_DEBUG
+
+/*
+ * modem options; bit mask
+ */
+#define BAYCOM_OPTIONS_SOFTDCD  1
+
+/* --------------------------------------------------------------------- */
+
+static const char bc_drvname[] = "baycom_ser_hdx";
+static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "baycom_ser_hdx: version 0.5 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+static struct device baycom_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq;
+} baycom_ports[NR_PORTS] = { { NULL, 0, 0 }, };
+
+/* --------------------------------------------------------------------- */
+
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define SER12_EXTENT 8
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct baycom_state {
+	struct hdlcdrv_state hdrv;
+
+	unsigned int options;
+
+	struct modem_state {
+		short arb_divider;
+		unsigned char flags;
+		unsigned int shreg;
+		struct modem_state_ser12 {
+			unsigned char tx_bit;
+			int dcd_sum0, dcd_sum1, dcd_sum2;
+			unsigned char last_sample;
+			unsigned char last_rxbit;
+			unsigned int dcd_shreg;
+			unsigned int dcd_time;
+			unsigned int bit_pll;
+			unsigned char interm_sample;
+		} ser12;
+	} modem;
+
+#ifdef BAYCOM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		int cur_pllcorr;
+		int last_pllcorr;
+	} debug_vals;
+#endif /* BAYCOM_DEBUG */
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+static void inline baycom_int_freq(struct baycom_state *bc)
+{
+#ifdef BAYCOM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	bc->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - bc->debug_vals.last_jiffies) >= HZ) {
+		bc->debug_vals.last_jiffies = cur_jiffies;
+		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
+		bc->debug_vals.cur_intcnt = 0;
+		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
+		bc->debug_vals.cur_pllcorr = 0;
+	}
+#endif /* BAYCOM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== SER12 specific routines =========================
+ */
+
+static void inline ser12_set_divisor(struct device *dev,
+				     unsigned char divisor)
+{
+	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
+	outb(divisor, DLL(dev->base_addr));
+	outb(0, DLM(dev->base_addr));
+	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
+	/*
+	 * make sure the next interrupt is generated;
+	 * 0 must be used to power the modem; the modem draws its
+	 * power from the TxD line
+	 */
+	outb(0x00, THR(dev->base_addr));
+	/*
+	 * it is important not to set the divider while transmitting;
+	 * this reportedly makes some UARTs generating interrupts
+	 * in the hundredthousands per second region
+	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
+	 */
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * must call the TX arbitrator every 10ms
+ */
+#define SER12_ARB_DIVIDER(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+			       36 : 24)
+#define SER12_DCD_INTERVAL(bc) ((bc->options & BAYCOM_OPTIONS_SOFTDCD) ? \
+				240 : 12)
+
+static inline void ser12_tx(struct device *dev, struct baycom_state *bc)
+{
+	/* one interrupt per channel bit */
+	ser12_set_divisor(dev, 12);
+	/*
+	 * first output the last bit (!) then call HDLC transmitter,
+	 * since this may take quite long
+	 */
+	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
+	if (bc->modem.shreg <= 1)
+		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
+	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
+				   (bc->modem.shreg & 1));
+	bc->modem.shreg >>= 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void ser12_rx(struct device *dev, struct baycom_state *bc)
+{
+	unsigned char cur_s;
+	/*
+	 * do demodulator
+	 */
+	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
+	hdlcdrv_channelbit(&bc->hdrv, cur_s);
+	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
+		(cur_s != bc->modem.ser12.last_sample);
+	bc->modem.ser12.last_sample = cur_s;
+	if(bc->modem.ser12.dcd_shreg & 1) {
+		if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				dcdspos += 2;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		} else
+			bc->modem.ser12.dcd_sum0--;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+	if (bc->options & BAYCOM_OPTIONS_SOFTDCD) {
+		/*
+		 * PLL code for the improved software DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 4);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 7) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 4:	/* transition too early */
+				ser12_set_divisor(dev, 3);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 4);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		if (++bc->modem.ser12.interm_sample >= 3)
+			bc->modem.ser12.interm_sample = 0;
+		/*
+		 * DCD stuff
+		 */
+		if (bc->modem.ser12.dcd_shreg & 1) {
+			unsigned int dcdspos, dcdsneg;
+
+			dcdspos = dcdsneg = 0;
+			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
+			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
+				<< 1;
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
+			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
+
+			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
+		}
+	} else {
+		/*
+		 * PLL algorithm for the hardware squelch DCD algorithm
+		 */
+		if (bc->modem.ser12.interm_sample) {
+			/*
+			 * intermediate sample; set timing correction to normal
+			 */
+			ser12_set_divisor(dev, 6);
+		} else {
+			/*
+			 * do PLL correction and call HDLC receiver
+			 */
+			switch (bc->modem.ser12.dcd_shreg & 3) {
+			case 1: /* transition too late */
+				ser12_set_divisor(dev, 7);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr++;
+#endif /* BAYCOM_DEBUG */
+				break;
+			case 2:	/* transition too early */
+				ser12_set_divisor(dev, 5);
+#ifdef BAYCOM_DEBUG
+				bc->debug_vals.cur_pllcorr--;
+#endif /* BAYCOM_DEBUG */
+				break;
+			default:
+				ser12_set_divisor(dev, 6);
+				break;
+			}
+			bc->modem.shreg >>= 1;
+			if (bc->modem.ser12.last_sample ==
+			    bc->modem.ser12.last_rxbit)
+				bc->modem.shreg |= 0x10000;
+			bc->modem.ser12.last_rxbit =
+				bc->modem.ser12.last_sample;
+		}
+		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
+		/*
+		 * DCD stuff
+		 */
+		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
+	}
+	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
+	if (bc->modem.shreg & 1) {
+		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
+		bc->modem.shreg = 0x10000;
+	}
+	if(!bc->modem.ser12.dcd_time) {
+		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
+					   bc->modem.ser12.dcd_sum1 +
+					   bc->modem.ser12.dcd_sum2) < 0);
+		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
+		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
+		/* offset to ensure DCD off on silent input */
+		bc->modem.ser12.dcd_sum0 = 2;
+		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
+	}
+	bc->modem.ser12.dcd_time--;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void ser12_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+
+	baycom_int_freq(bc);
+	/*
+	 * check if transmitter active
+	 */
+	if (hdlcdrv_ptt(&bc->hdrv))
+		ser12_tx(dev, bc);
+	else {
+		ser12_rx(dev, bc);
+		if (--bc->modem.arb_divider <= 0) {
+			bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
+			sti();
+			hdlcdrv_arbitrate(dev, &bc->hdrv);
+		}
+	}
+	sti();
+	hdlcdrv_transmitter(dev, &bc->hdrv);
+	hdlcdrv_receiver(dev, &bc->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	    c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] = { 
+	"unknown", "8250", "16450", "16550", "16550A" 
+};
+
+static enum uart ser12_check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	b1 = inb(MCR(iobase));
+	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
+	b2 = inb(MSR(iobase));
+	outb(0x1a, MCR(iobase));
+	b3 = inb(MSR(iobase)) & 0xf0;
+	outb(b1, MCR(iobase));			/* restore old values */
+	outb(b2, MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(RBR(iobase));
+	inb(RBR(iobase));
+	outb(0x01, FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, SCR(iobase));
+		b1 = inb(SCR(iobase));
+		outb(0xa5, SCR(iobase));
+		b2 = inb(SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_open(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+	enum uart u;
+
+	if (!dev || !bc)
+		return -ENXIO;
+	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
+	    dev->irq < 2 || dev->irq > 15)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SER12_EXTENT))
+		return -EACCES;
+	memset(&bc->modem, 0, sizeof(bc->modem));
+	bc->hdrv.par.bitrate = 1200;
+	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown)
+		return -EIO;
+	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0x0d, MCR(dev->base_addr));
+	outb(0, IER(dev->base_addr));
+	if (request_irq(dev->irq, ser12_interrupt, SA_INTERRUPT,
+			"baycom_ser12", dev))
+		return -EBUSY;
+	request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12");
+	/*
+	 * enable transmitter empty interrupt
+	 */
+	outb(2, IER(dev->base_addr));
+	/*
+	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
+	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
+	 * depending on the usage of the software DCD routine
+	 */
+	ser12_set_divisor(dev, (bc->options & BAYCOM_OPTIONS_SOFTDCD) ? 4 : 6);
+	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u options "
+	       "0x%x uart %s\n", bc_drvname, dev->base_addr, dev->irq,
+	       bc->options, uart_str[u]);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int ser12_close(struct device *dev)
+{
+	struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+	if (!dev || !bc)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	outb(0, IER(dev->base_addr));
+	outb(1, MCR(dev->base_addr));
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, SER12_EXTENT);
+	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
+	       bc_drvname, dev->base_addr, dev->irq);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== hdlcdrv driver interface =========================
+ */
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static struct hdlcdrv_ops ser12_ops = {
+	bc_drvname,
+	bc_drvinfo,
+	ser12_open,
+	ser12_close,
+	baycom_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_setmode(struct baycom_state *bc, const char *modestr)
+{
+	bc->options = !!strchr(modestr, '*');
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int baycom_ioctl(struct device *dev, struct ifreq *ifr,
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct baycom_state *bc;
+	struct baycom_ioctl bi;
+	int cmd2;
+
+	if (!dev || !dev->priv ||
+	    ((struct baycom_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "bc_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	bc = (struct baycom_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+	if (get_user(cmd2, (int *)ifr->ifr_data))
+		return -EFAULT;
+	switch (hi->cmd) {
+	default:
+		break;
+
+	case HDLCDRVCTL_GETMODE:
+		strcpy(hi->data.modename, "ser12");
+		if (bc->options & 1)
+			strcat(hi->data.modename, "*");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return baycom_setmode(bc, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		strcpy(hi->data.modename, "ser12");
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_MODEMPARMASK:
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
+
+	}
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+#ifdef BAYCOM_DEBUG
+	case BAYCOMCTL_GETDEBUG:
+		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
+		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
+		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
+		break;
+#endif /* BAYCOM_DEBUG */
+
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int baycom_ser_hdx_init(void))
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct baycom_state *bc;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+
+	printk(bc_drvinfo);
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		sprintf(ifname, "bcsh%d", i);
+
+		if (!baycom_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			baycom_ports[i].iobase = baycom_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &ser12_ops,
+					     sizeof(struct baycom_state),
+					     ifname, baycom_ports[i].iobase,
+					     baycom_ports[i].irq, 0);
+		if (!j) {
+			bc = (struct baycom_state *)dev->priv;
+			if (set_hw && baycom_setmode(bc, baycom_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       bc_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode[NR_PORTS] = { "ser12*", };
+static int iobase[NR_PORTS] = { 0x3f8, };
+static int irq[NR_PORTS] = { 4, };
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
+MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
+MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(iobase, "baycom io base address");
+MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
+MODULE_PARM_DESC(irq, "baycom irq number");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (mode[i]); i++) {
+		baycom_ports[i].mode = mode[i];
+		baycom_ports[i].iobase = iobase[i];
+		baycom_ports[i].irq = irq[i];
+	}
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+	return baycom_ser_hdx_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = baycom_device+i;
+		struct baycom_state *bc = (struct baycom_state *)dev->priv;
+
+		if (bc) {
+			if (bc->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "baycom: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: baycom_ser_=io,irq,mode
+ * mode: [*]
+ * * indicates sofware DCD
+ */
+
+__initfunc(void baycom_ser_hdx_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (baycom_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 2)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", bc_drvname);
+		return;
+	}
+	baycom_ports[i].mode = str;
+	baycom_ports[i].iobase = ints[1];
+	baycom_ports[i].irq = ints[2];
+	if (i < NR_PORTS-1)
+		baycom_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/bpqether.c linux/drivers/net/hamradio/bpqether.c
--- v2.1.70/linux/drivers/net/hamradio/bpqether.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/bpqether.c	Sat Nov 29 10:33:19 1997
@@ -0,0 +1,669 @@
+/*
+ *	G8BPQ compatible "AX.25 via ethernet" driver release 003
+ *
+ *	This code REQUIRES 2.0.0 or higher/ NET3.029
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	This is a "pseudo" network driver to allow AX.25 over Ethernet
+ *	using G8BPQ encapsulation. It has been extracted from the protocol
+ *	implementation because
+ *
+ *		- things got unreadable within the protocol stack
+ *		- to cure the protocol stack from "feature-ism"
+ *		- a protocol implementation shouldn't need to know on
+ *		  which hardware it is running
+ *		- user-level programs like the AX.25 utilities shouldn't
+ *		  need to know about the hardware.
+ *		- IP over ethernet encapsulated AX.25 was impossible
+ *		- rxecho.c did not work
+ *		- to have room for extensions
+ *		- it just deserves to "live" as an own driver
+ *
+ *	This driver can use any ethernet destination address, and can be
+ *	limited to accept frames from one dedicated ethernet card only.
+ *
+ *	Note that the driver sets up the BPQ devices automagically on
+ *	startup or (if started before the "insmod" of an ethernet device)
+ *	on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing
+ *	the ethernet device (in fact: as soon as another ethernet or bpq
+ *	device gets "ifconfig"ured).
+ *
+ *	I have heard that several people are thinking of experiments
+ *	with highspeed packet radio using existing ethernet cards.
+ *	Well, this driver is prepared for this purpose, just add
+ *	your tx key control and a txdelay / tailtime algorithm,
+ *	probably some buffering, and /voila/...
+ *
+ *	History
+ *	BPQ   001	Joerg(DL1BKE)		Extracted BPQ code from AX.25
+ *						protocol stack and added my own
+ *						yet existing patches
+ *	BPQ   002	Joerg(DL1BKE)		Scan network device list on
+ *						startup.
+ *	BPQ   003	Joerg(DL1BKE)		Ethernet destination address
+ *						and accepted source address
+ *						can be configured by an ioctl()
+ *						call.
+ *						Fixed to match Linux networking
+ *						changes - 2.1.15.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <net/ip.h>
+#include <net/arp.h>
+
+#include <linux/bpqether.h>
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+	{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
+	{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+
+static char bpq_eth_addr[6];
+
+static int bpq_rcv(struct sk_buff *, struct device *, struct packet_type *);
+static int bpq_device_event(struct notifier_block *, unsigned long, void *);
+static char *bpq_print_ethaddr(unsigned char *);
+
+static struct packet_type bpq_packet_type = {
+	0,		/* ntohs(ETH_P_BPQ),*/
+	0,		/* copy */
+	bpq_rcv,
+	NULL,
+	NULL,
+};
+
+static struct notifier_block bpq_dev_notifier = {
+	bpq_device_event,
+	0
+};
+
+
+#define MAXBPQDEV 100
+
+static struct bpqdev {
+	struct bpqdev *next;
+	char   ethname[14];		/* ether device name */
+	struct device *ethdev;		/* link to ethernet device */
+	struct device axdev;		/* bpq device (bpq#) */
+	struct net_device_stats stats;	/* some statistics */
+	char   dest_addr[6];		/* ether destination address */
+	char   acpt_addr[6];		/* accept ether frames from this address only */
+} *bpq_devices = NULL;
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Get the ethernet device for a BPQ device
+ */
+static __inline__ struct device *bpq_get_ether_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	return (bpq != NULL) ? bpq->ethdev : NULL;
+}
+
+/*
+ *	Get the BPQ device for the ethernet device
+ */
+static __inline__ struct device *bpq_get_ax25_dev(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		if (bpq->ethdev == dev)
+			return &bpq->axdev;
+
+	return NULL;
+}
+
+static __inline__ int dev_is_ethdev(struct device *dev)
+{
+	return (
+			dev->type == ARPHRD_ETHER
+			&& strncmp(dev->name, "dummy", 5)
+	);
+}
+
+/*
+ *	Sanity check: remove all devices that ceased to exists and
+ *	return '1' if the given BPQ device was affected.
+ */
+static int bpq_check_devices(struct device *dev)
+{
+	struct bpqdev *bpq, *bpq_prev;
+	int result = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	bpq_prev = NULL;
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
+		if (!dev_get(bpq->ethname)) {
+			if (bpq_prev)
+				bpq_prev->next = bpq->next;
+			else
+				bpq_devices = bpq->next;
+
+			if (&bpq->axdev == dev)
+				result = 1;
+
+			unregister_netdev(&bpq->axdev);
+			kfree(bpq);
+		}
+
+		bpq_prev = bpq;
+	}
+
+	restore_flags(flags);
+
+	return result;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Receive an AX.25 frame via an ethernet interface.
+ */
+static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+	int len;
+	char * ptr;
+	struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
+	struct bpqdev *bpq;
+
+	skb->sk = NULL;		/* Initially we don't know who it's for */
+
+	dev = bpq_get_ax25_dev(dev);
+
+	if (dev == NULL || dev->start == 0) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	/*
+	 * if we want to accept frames from just one ethernet device
+	 * we check the source address of the sender.
+	 */
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	if (!(bpq->acpt_addr[0] & 0x01) && memcmp(eth->h_source, bpq->acpt_addr, ETH_ALEN)) {
+		printk(KERN_DEBUG "bpqether: wrong dest %s\n", bpq_print_ethaddr(eth->h_source));
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+	len = skb->data[0] + skb->data[1] * 256 - 5;
+
+	skb_pull(skb, 2);	/* Remove the length bytes */
+	skb_trim(skb, len);	/* Set the length of the data */
+
+	((struct bpqdev *)dev->priv)->stats.rx_packets++;
+	((struct bpqdev *)dev->priv)->stats.rx_bytes+=len;
+
+	ptr = skb_push(skb, 1);
+	*ptr = 0;
+
+	skb->dev = dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+
+	return 0;
+}
+
+/*
+ * 	Send an AX.25 frame via an ethernet interface
+ */
+static int bpq_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct sk_buff *newskb;
+	unsigned char *ptr;
+	struct bpqdev *bpq;
+	int size;
+
+	/*
+	 * Just to be *really* sure not to send anything if the interface
+	 * is down, the ethernet device may have gone.
+	 */
+	if (!dev->start) {
+		bpq_check_devices(dev);
+		kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb_pull(skb, 1);
+	size = skb->len;
+
+	/*
+	 * The AX.25 code leaves enough room for the ethernet header, but
+	 * sendto() does not.
+	 */
+	if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {	/* Ough! */
+		if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
+			printk(KERN_WARNING "bpqether: out of memory\n");
+			kfree_skb(skb, FREE_WRITE);
+			return -ENOMEM;
+		}
+
+		if (skb->sk != NULL)
+			skb_set_owner_w(newskb, skb->sk);
+
+		kfree_skb(skb, FREE_WRITE);
+		skb = newskb;
+	}
+
+	skb->protocol = htons(ETH_P_AX25);
+
+	ptr = skb_push(skb, 2);
+
+	*ptr++ = (size + 5) % 256;
+	*ptr++ = (size + 5) / 256;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	if ((dev = bpq_get_ether_dev(dev)) == NULL) {
+		bpq->stats.tx_dropped++;
+		kfree_skb(skb, FREE_WRITE);
+		return -ENODEV;
+	}
+
+	skb->dev = dev;
+	dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+	bpq->stats.tx_packets++;
+	bpq->stats.tx_bytes+=skb->len;
+  
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+
+/*
+ *	Statistics
+ */
+static struct net_device_stats *bpq_get_stats(struct device *dev)
+{
+	struct bpqdev *bpq;
+
+	bpq = (struct bpqdev *)dev->priv;
+
+	return &bpq->stats;
+}
+
+/*
+ *	Set AX.25 callsign
+ */
+static int bpq_set_mac_address(struct device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+
+    return 0;
+}
+
+/*	Ioctl commands
+ *
+ *		SIOCSBPQETHOPT		reserved for enhancements
+ *		SIOCSBPQETHADDR		set the destination and accepted
+ *					source ethernet address (broadcast
+ *					or multicast: accept all)
+ */
+static int bpq_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	int err;
+	struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
+	struct bpqdev *bpq = dev->priv;
+	struct bpq_req req;
+
+	if (!suser())
+		return -EPERM;
+
+	if (bpq == NULL)		/* woops! */
+		return -ENODEV;
+
+	switch (cmd) {
+		case SIOCSBPQETHOPT:
+			if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
+				return err;
+			copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req));
+			switch (req.cmd) {
+				case SIOCGBPQETHPARAM:
+				case SIOCSBPQETHPARAM:
+				default:
+					return -EINVAL;
+			}
+
+			break;
+
+		case SIOCSBPQETHADDR:
+			if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
+				return err;
+			copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
+			copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * open/close a device
+ */
+static int bpq_open(struct device *dev)
+{
+	if (bpq_check_devices(dev))
+		return -ENODEV;		/* oops, it's gone */
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int bpq_close(struct device *dev)
+{
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+/*
+ * currently unused
+ */
+static int bpq_dev_init(struct device *dev)
+{
+	return 0;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Proc filesystem
+ */
+static char * bpq_print_ethaddr(unsigned char *e)
+{
+	static char buf[18];
+
+	sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+		e[0], e[1], e[2], e[3], e[4], e[5]);
+
+	return buf;
+}
+
+int bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct bpqdev *bpqdev;
+	int len     = 0;
+	off_t pos   = 0;
+	off_t begin = 0;
+
+	cli();
+
+	len += sprintf(buffer, "dev   ether      destination        accept from\n");
+
+	for (bpqdev = bpq_devices; bpqdev != NULL; bpqdev = bpqdev->next) {
+		len += sprintf(buffer + len, "%-5s %-10s %s  ",
+			bpqdev->axdev.name, bpqdev->ethname,
+			bpq_print_ethaddr(bpqdev->dest_addr));
+
+		len += sprintf(buffer + len, "%s\n",
+			(bpqdev->acpt_addr[0] & 0x01) ? "*" : bpq_print_ethaddr(bpqdev->acpt_addr));
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len   = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			break;
+	}
+
+	sti();
+
+	*start = buffer + (offset - begin);
+	len   -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return len;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+
+/*
+ *	Setup a new device.
+ */
+static int bpq_new_device(struct device *dev)
+{
+	int k;
+	unsigned char *buf;
+	struct bpqdev *bpq, *bpq2;
+
+	if ((bpq = kmalloc(sizeof(struct bpqdev), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	memset(bpq, 0, sizeof(struct bpqdev));
+
+	bpq->ethdev = dev;
+
+	bpq->ethname[sizeof(bpq->ethname)-1] = '\0';
+	strncpy(bpq->ethname, dev->name, sizeof(bpq->ethname)-1);
+
+	memcpy(bpq->dest_addr, bcast_addr, sizeof(bpq_eth_addr));
+	memcpy(bpq->acpt_addr, bcast_addr, sizeof(bpq_eth_addr));
+
+	dev = &bpq->axdev;
+	buf = kmalloc(14, GFP_KERNEL);
+
+	for (k = 0; k < MAXBPQDEV; k++) {
+		struct device *odev;
+
+		sprintf(buf, "bpq%d", k);
+
+		if ((odev = dev_get(buf)) == NULL || bpq_check_devices(odev))
+			break;
+	}
+
+	if (k == MAXBPQDEV) {
+		kfree(bpq);
+		return -ENODEV;
+	}
+
+	dev->priv = (void *)bpq;	/* pointer back */
+	dev->name = buf;
+	dev->init = bpq_dev_init;
+
+	if (register_netdev(dev) != 0) {
+		kfree(bpq);
+                return -EIO;
+        }
+
+	dev_init_buffers(dev);
+
+	dev->hard_start_xmit = bpq_xmit;
+	dev->open	     = bpq_open;
+	dev->stop	     = bpq_close;
+	dev->set_mac_address = bpq_set_mac_address;
+	dev->get_stats	     = bpq_get_stats;
+	dev->do_ioctl	     = bpq_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_defaddr, AX25_ADDR_LEN);
+
+	dev->flags      = 0;
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header     = ax25_encapsulate;
+	dev->rebuild_header  = ax25_rebuild_header;
+#endif
+
+	dev->type            = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu             = AX25_DEF_PACLEN;
+	dev->addr_len        = AX25_ADDR_LEN;
+
+	cli();
+
+	if (bpq_devices == NULL) {
+		bpq_devices = bpq;
+	} else {
+		for (bpq2 = bpq_devices; bpq2->next != NULL; bpq2 = bpq2->next);
+		bpq2->next = bpq;
+	}
+
+	sti();
+
+	return 0;
+}
+
+
+/*
+ *	Handle device status changes.
+ */
+static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr)
+{
+	struct device *dev = (struct device *)ptr;
+
+	if (!dev_is_ethdev(dev))
+		return NOTIFY_DONE;
+
+	bpq_check_devices(NULL);
+
+	switch (event) {
+		case NETDEV_UP:		/* new ethernet device -> new BPQ interface */
+			if (bpq_get_ax25_dev(dev) == NULL)
+				bpq_new_device(dev);
+			break;
+
+		case NETDEV_DOWN:	/* ethernet device closed -> close BPQ interface */
+			if ((dev = bpq_get_ax25_dev(dev)) != NULL)
+				dev_close(dev);
+			break;
+
+		default:
+			break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Initialize driver. To be called from af_ax25 if not compiled as a
+ * module
+ */
+__initfunc(int bpq_init(void))
+{
+	struct device *dev;
+
+	bpq_packet_type.type  = htons(ETH_P_BPQ);
+	dev_add_pack(&bpq_packet_type);
+
+	register_netdevice_notifier(&bpq_dev_notifier);
+
+	printk(KERN_INFO "AX.25 ethernet driver version 0.01\n");
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_AX25_BPQETHER, 8, "bpqether",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		bpq_get_info
+	});
+#endif
+
+	for (dev = dev_base; dev != NULL; dev = dev->next) {
+		if (dev_is_ethdev(dev))
+			bpq_new_device(dev);
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@lykos.oche.de>");
+MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
+
+int init_module(void)
+{
+	return bpq_init();
+}
+
+void cleanup_module(void)
+{
+	struct bpqdev *bpq;
+
+	dev_remove_pack(&bpq_packet_type);
+
+	unregister_netdevice_notifier(&bpq_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_AX25_BPQETHER);
+#endif
+
+	for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
+		unregister_netdev(&bpq->axdev);
+}
+#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- v2.1.70/linux/drivers/net/hamradio/hdlcdrv.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/hdlcdrv.c	Sun Nov 30 10:30:19 1997
@@ -0,0 +1,1024 @@
+/*****************************************************************************/
+
+/*
+ *	hdlcdrv.c  -- HDLC packet radio network driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *  The driver was derived from Donald Beckers skeleton.c
+ *	Written 1993-94 by Donald Becker.
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines 
+ *                  (copy_{to,from}_user)
+ *   0.2  21.11.96  various small changes
+ *   0.3  03.03.97  fixed (hopefully) IP not working with ax.25 as a module
+ *   0.4  16.04.97  init code/data tagged
+ *   0.5  30.07.97  made HDLC buffers bigger (solves a problem with the
+ *                  soundmodem driver)
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/hdlcdrv.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+/* prototypes for ax25_encapsulate and ax25_rebuild_header */
+#include <net/ax25.h> 
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+
+/* make genksyms happy */
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20115
+extern __inline__ void dev_init_buffers(struct device *dev)
+{
+        int i;
+        for(i=0;i<DEV_NUMBUFFS;i++)
+        {
+                skb_queue_head_init(&dev->buffs[i]);
+        }
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE < 0x20125
+#define test_and_set_bit set_bit
+#define test_and_clear_bit clear_bit
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels
+ */
+
+static char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* --------------------------------------------------------------------- */
+
+#define KISS_VERBOSE
+
+/* --------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+/*
+ * the CRC routines are stolen from WAMPES
+ * by Dieter Deyke
+ */
+
+static const unsigned short crc_ccitt_table[] = {
+	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*---------------------------------------------------------------------------*/
+
+static inline void append_crc_ccitt(unsigned char *buffer, int len)
+{
+ 	unsigned int crc = 0xffff;
+
+	for (;len>0;len--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
+	crc ^= 0xffff;
+	*buffer++ = crc;
+	*buffer++ = crc >> 8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	return (crc & 0xffff) == 0xf0b8;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#if 0
+static int calc_crc_ccitt(const unsigned char *buf, int cnt)
+{
+	unsigned int crc = 0xffff;
+
+	for (; cnt > 0; cnt--)
+		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
+	crc ^= 0xffff;
+	return (crc & 0xffff);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
+
+/* ---------------------------------------------------------------------- */
+/*
+ * The HDLC routines
+ */
+
+static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, 
+			     int num)
+{
+	int added = 0;
+	
+	while (s->hdlcrx.rx_state && num >= 8) {
+		if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
+			s->hdlcrx.rx_state = 0;
+			return 0;
+		}
+		*s->hdlcrx.bp++ = bits >> (32-num);
+		s->hdlcrx.len++;
+		num -= 8;
+		added += 8;
+	}
+	return added;
+}
+
+static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
+{
+	struct sk_buff *skb;
+	int pkt_len;
+	unsigned char *cp;
+
+	if (s->hdlcrx.len < 4) 
+		return;
+	if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) 
+		return;
+	pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
+	if (!(skb = dev_alloc_skb(pkt_len))) {
+		printk("%s: memory squeeze, dropping packet\n", 
+		       s->ifname);
+		s->stats.rx_dropped++;
+		return;
+	}
+	skb->dev = dev;
+	cp = skb_put(skb, pkt_len);
+	*cp++ = 0; /* KISS kludge */
+	memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
+	s->stats.rx_packets++;
+}
+
+void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
+{
+	int i;
+	unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
+	
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
+		return;
+
+	while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
+		word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);	
+
+#ifdef HDLCDRV_DEBUG
+		hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
+#endif /* HDLCDRV_DEBUG */
+	       	s->hdlcrx.bitstream >>= 16;
+		s->hdlcrx.bitstream |= word << 16;
+		s->hdlcrx.bitbuf >>= 16;
+		s->hdlcrx.bitbuf |= word << 16;
+		s->hdlcrx.numbits += 16;
+		for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
+		    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
+		    i >= 0; 
+		    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
+		    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
+			if ((s->hdlcrx.bitstream & mask1) == mask1)
+				s->hdlcrx.rx_state = 0; /* abort received */
+			else if ((s->hdlcrx.bitstream & mask2) == mask3) {
+				/* flag received */
+				if (s->hdlcrx.rx_state) {
+					hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf 
+							  << (8+i),
+							  s->hdlcrx.numbits
+							  -8-i);
+					hdlc_rx_flag(dev, s);
+				}
+				s->hdlcrx.len = 0;
+				s->hdlcrx.bp = s->hdlcrx.buffer;
+				s->hdlcrx.rx_state = 1;
+				s->hdlcrx.numbits = i;
+			} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
+				/* stuffed bit */
+				s->hdlcrx.numbits--;
+				s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
+					((s->hdlcrx.bitbuf & mask6) << 1);
+			}
+		}
+		s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
+						       s->hdlcrx.numbits);
+	}
+	clear_bit(0, &s->hdlcrx.in_hdlc_rx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void inline do_kiss_params(struct hdlcdrv_state *s,
+				  unsigned char *data, unsigned long len)
+{
+
+#ifdef KISS_VERBOSE
+#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
+#else /* KISS_VERBOSE */	      
+#define PKP(a,b) 
+#endif /* KISS_VERBOSE */	      
+
+	if (len < 2)
+		return;
+	switch(data[0]) {
+	case PARAM_TXDELAY:
+		s->ch_params.tx_delay = data[1];
+		PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
+		break;
+	case PARAM_PERSIST:   
+		s->ch_params.ppersist = data[1];
+		PKP("p persistence = %u", s->ch_params.ppersist);
+		break;
+	case PARAM_SLOTTIME:  
+		s->ch_params.slottime = data[1];
+		PKP("slot time = %ums", s->ch_params.slottime);
+		break;
+	case PARAM_TXTAIL:    
+		s->ch_params.tx_tail = data[1];
+		PKP("TX tail = %ums", s->ch_params.tx_tail);
+		break;
+	case PARAM_FULLDUP:   
+		s->ch_params.fulldup = !!data[1];
+		PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
+		break;
+	default:
+		break;
+	}
+#undef PKP
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
+{
+	unsigned int mask1, mask2, mask3;
+	int i;
+	struct sk_buff *skb;
+	int pkt_len;
+
+	if (!s || s->magic != HDLCDRV_MAGIC) 
+		return;
+	if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
+		return;
+	for (;;) {
+		if (s->hdlctx.numbits >= 16) {
+			if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
+			s->hdlctx.bitbuf >>= 16;
+			s->hdlctx.numbits -= 16;
+		}
+		switch (s->hdlctx.tx_state) {
+		default:
+			clear_bit(0, &s->hdlctx.in_hdlc_tx);
+			return;
+		case 0:
+		case 1:
+			if (s->hdlctx.numflags) {
+				s->hdlctx.numflags--;
+				s->hdlctx.bitbuf |= 
+					0x7e7e << s->hdlctx.numbits;
+				s->hdlctx.numbits += 16;
+				break;
+			}
+			if (s->hdlctx.tx_state == 1) {
+				clear_bit(0, &s->hdlctx.in_hdlc_tx);
+				return;
+			}
+			if (!(skb = skb_dequeue(&s->send_queue))) {
+				int flgs = tenms_to_2flags
+					(s, s->ch_params.tx_tail);
+				if (flgs < 2)
+					flgs = 2;
+				s->hdlctx.tx_state = 1;
+				s->hdlctx.numflags = flgs;
+				break;
+			}
+			if (skb->data[0] != 0) {
+				do_kiss_params(s, skb->data, skb->len);
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			pkt_len = skb->len-1; /* strip KISS byte */
+			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				dev_kfree_skb(skb, FREE_WRITE);
+				break;
+			}
+			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
+			dev_kfree_skb(skb, FREE_WRITE);
+			s->hdlctx.bp = s->hdlctx.buffer;
+			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
+			s->hdlctx.len = pkt_len+2; /* the appended CRC */
+			s->hdlctx.tx_state = 2;
+			s->hdlctx.bitstream = 0;
+			s->stats.tx_packets++;
+			break;
+		case 2:
+			if (!s->hdlctx.len) {
+				s->hdlctx.tx_state = 0;
+				s->hdlctx.numflags = 1;
+				break;
+			}
+			s->hdlctx.len--;
+			s->hdlctx.bitbuf |= *s->hdlctx.bp <<
+				s->hdlctx.numbits;
+			s->hdlctx.bitstream >>= 8;
+			s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
+			mask1 = 0x1f000;
+			mask2 = 0x10000;
+			mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
+			s->hdlctx.numbits += 8;
+			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
+			    mask3 = (mask3 << 1) | 1) {
+				if ((s->hdlctx.bitstream & mask1) != mask1) 
+					continue;
+				s->hdlctx.bitstream &= ~mask2;
+				s->hdlctx.bitbuf = 
+					(s->hdlctx.bitbuf & mask3) |
+						((s->hdlctx.bitbuf & 
+						 (~mask3)) << 1);
+				s->hdlctx.numbits++;
+				mask3 = (mask3 << 1) | 1;
+			}
+			break;
+		}
+	}
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void start_tx(struct device *dev, struct hdlcdrv_state *s)
+{
+	s->hdlctx.tx_state = 0;
+	s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
+	s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
+	hdlcdrv_transmitter(dev, s);
+	s->hdlctx.ptt = 1;
+	s->ptt_keyed++;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned short random_seed;
+
+static inline unsigned short random_num(void)
+{
+	random_seed = 28629 * random_seed + 157;
+	return random_seed;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
+{
+	if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || 
+	    skb_queue_empty(&s->send_queue)) 
+		return;
+	if (s->ch_params.fulldup) {
+		start_tx(dev, s);
+		return;
+	}
+	if (s->hdlcrx.dcd) {
+		s->hdlctx.slotcnt = s->ch_params.slottime;
+		return;
+	}
+	if ((--s->hdlctx.slotcnt) > 0)
+		return;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	if ((random_num() % 256) > s->ch_params.ppersist)
+		return;
+	start_tx(dev, s);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== network driver interface =========================
+ */
+
+static inline int hdlcdrv_paranoia_check(struct device *dev,
+					const char *routine)
+{
+	if (!dev || !dev->priv || 
+	    ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
+		       "struct in routine %s\n", routine);
+		return 1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
+		return 0;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	skb_queue_tail(&sm->send_queue, skb);
+	dev->trans_start = jiffies;	
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
+	return 0;                                         
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20119
+static struct net_device_stats *hdlcdrv_get_stats(struct device *dev)
+#else
+static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
+#endif
+{
+	struct hdlcdrv_state *sm;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
+		return NULL;
+	sm = (struct hdlcdrv_state *)dev->priv;
+	/* 
+	 * Get the current statistics.  This may be called with the
+	 * card open or closed. 
+	 */
+	return &sm->stats;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int hdlcdrv_open(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	int i;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (dev->start)
+		return 0;
+	if (!s->ops || !s->ops->open)
+		return -ENODEV;
+
+	dev->start = 1;
+	/*
+	 * initialise some variables
+	 */
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+	i = s->ops->open(dev);
+	if (i) {
+		dev->start = 0;
+		return i;
+	}
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* 
+ * The inverse routine to hdlcdrv_open(). 
+ */
+
+static int hdlcdrv_close(struct device *dev)
+{
+	struct hdlcdrv_state *s;
+	struct sk_buff *skb;
+	int i = 0;
+
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (!dev->start)
+		return 0;
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (s->ops && s->ops->close)
+		i = s->ops->close(dev);
+        /* Free any buffers left in the hardware transmit queue */
+        while ((skb = skb_dequeue(&s->send_queue)))
+			dev_kfree_skb(skb, FREE_WRITE);
+	return i;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct hdlcdrv_state *s;
+	struct hdlcdrv_ioctl bi;
+		
+	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
+		return -EINVAL;
+	s = (struct hdlcdrv_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		if (s->ops && s->ops->ioctl)
+			return s->ops->ioctl(dev, ifr, &bi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETCHANNELPAR:
+		bi.data.cp.tx_delay = s->ch_params.tx_delay;
+		bi.data.cp.tx_tail = s->ch_params.tx_tail;
+		bi.data.cp.slottime = s->ch_params.slottime;
+		bi.data.cp.ppersist = s->ch_params.ppersist;
+		bi.data.cp.fulldup = s->ch_params.fulldup;
+		break;
+
+	case HDLCDRVCTL_SETCHANNELPAR:
+		if (!suser())
+			return -EACCES;
+		s->ch_params.tx_delay = bi.data.cp.tx_delay;
+		s->ch_params.tx_tail = bi.data.cp.tx_tail;
+		s->ch_params.slottime = bi.data.cp.slottime;
+		s->ch_params.ppersist = bi.data.cp.ppersist;
+		s->ch_params.fulldup = bi.data.cp.fulldup;
+		s->hdlctx.slotcnt = 1;
+		return 0;
+		
+	case HDLCDRVCTL_GETMODEMPAR:
+		bi.data.mp.iobase = dev->base_addr;
+		bi.data.mp.irq = dev->irq;
+		bi.data.mp.dma = dev->dma;
+		bi.data.mp.dma2 = s->ptt_out.dma2;
+		bi.data.mp.seriobase = s->ptt_out.seriobase;
+		bi.data.mp.pariobase = s->ptt_out.pariobase;
+		bi.data.mp.midiiobase = s->ptt_out.midiiobase;
+		break;
+
+	case HDLCDRVCTL_SETMODEMPAR:
+		if ((!suser()) || dev->start)
+			return -EACCES;
+		dev->base_addr = bi.data.mp.iobase;
+		dev->irq = bi.data.mp.irq;
+		dev->dma = bi.data.mp.dma;
+		s->ptt_out.dma2 = bi.data.mp.dma2;
+		s->ptt_out.seriobase = bi.data.mp.seriobase;
+		s->ptt_out.pariobase = bi.data.mp.pariobase;
+		s->ptt_out.midiiobase = bi.data.mp.midiiobase;
+		return 0;	
+	
+	case HDLCDRVCTL_GETSTAT:
+		bi.data.cs.ptt = hdlcdrv_ptt(s);
+		bi.data.cs.dcd = s->hdlcrx.dcd;
+		bi.data.cs.ptt_keyed = s->ptt_keyed;
+		bi.data.cs.tx_packets = s->stats.tx_packets;
+		bi.data.cs.tx_errors = s->stats.tx_errors;
+		bi.data.cs.rx_packets = s->stats.rx_packets;
+		bi.data.cs.rx_errors = s->stats.rx_errors;
+		break;		
+
+	case HDLCDRVCTL_OLDGETSTAT:
+		bi.data.ocs.ptt = hdlcdrv_ptt(s);
+		bi.data.ocs.dcd = s->hdlcrx.dcd;
+		bi.data.ocs.ptt_keyed = s->ptt_keyed;
+#if LINUX_VERSION_CODE < 0x20100
+		bi.data.ocs.stats = s->stats;
+#endif
+		break;		
+
+	case HDLCDRVCTL_CALIBRATE:
+		s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
+		return 0;
+
+	case HDLCDRVCTL_GETSAMPLES:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
+		s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
+			sizeof(s->bitbuf_channel.buffer);
+		break;
+#endif /* HDLCDRV_DEBUG */
+				
+	case HDLCDRVCTL_GETBITS:
+#ifndef HDLCDRV_DEBUG
+		return -EPERM;
+#else /* HDLCDRV_DEBUG */
+		if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) 
+			return -EAGAIN;
+		bi.data.bits = 
+			s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
+		s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
+			sizeof(s->bitbuf_hdlc.buffer);
+		break;		
+#endif /* HDLCDRV_DEBUG */
+
+	case HDLCDRVCTL_DRIVERNAME:
+		if (s->ops && s->ops->drvname) {
+			strncpy(bi.data.drivername, s->ops->drvname, 
+				sizeof(bi.data.drivername));
+			break;
+		}
+		bi.data.drivername[0] = '\0';
+		break;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+static int hdlcdrv_probe(struct device *dev)
+{
+	const struct hdlcdrv_channel_params dflt_ch_params = { 
+		20, 2, 10, 40, 0 
+	};
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -ENXIO;
+	/*
+	 * not a real probe! only initialize data structures
+	 */
+	s = (struct hdlcdrv_state *)dev->priv;
+	/*
+	 * initialize the hdlcdrv_state struct
+	 */
+	s->ch_params = dflt_ch_params;
+	s->ptt_keyed = 0;
+
+	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
+	s->hdlcrx.in_hdlc_rx = 0;
+	s->hdlcrx.rx_state = 0;
+	
+	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
+	s->hdlctx.in_hdlc_tx = 0;
+	s->hdlctx.tx_state = 1;
+	s->hdlctx.numflags = 0;
+	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
+	s->hdlctx.ptt = 0;
+	s->hdlctx.slotcnt = s->ch_params.slottime;
+	s->hdlctx.calibrate = 0;
+
+#ifdef HDLCDRV_DEBUG
+	s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
+	s->bitbuf_channel.shreg = 0x80;
+
+	s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
+	s->bitbuf_hdlc.shreg = 0x80;
+#endif /* HDLCDRV_DEBUG */
+
+	/*
+	 * initialize the device struct
+	 */
+	dev->open = hdlcdrv_open;
+	dev->stop = hdlcdrv_close;
+	dev->do_ioctl = hdlcdrv_ioctl;
+	dev->hard_start_xmit = hdlcdrv_send_packet;
+	dev->get_stats = hdlcdrv_get_stats;
+
+	/* Fill in the fields of the device structure */
+
+	dev_init_buffers(dev);
+
+	skb_queue_head_init(&s->send_queue);
+	
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header = ax25_encapsulate;
+	dev->rebuild_header = ax25_rebuild_header;
+#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->hard_header = NULL;
+	dev->rebuild_header = NULL;
+#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
+	dev->set_mac_address = hdlcdrv_set_mac_address;
+	
+	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */
+	dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
+
+	/* New style flags */
+	dev->flags = 0;
+
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
+			     unsigned int privsize, char *ifname,
+			     unsigned int baseaddr, unsigned int irq, 
+			     unsigned int dma) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev || !ops)
+		return -EACCES;
+	if (privsize < sizeof(struct hdlcdrv_state))
+		privsize = sizeof(struct hdlcdrv_state);
+	memset(dev, 0, sizeof(struct device));
+	if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
+		return -ENOMEM;
+	/*
+	 * initialize part of the hdlcdrv_state struct
+	 */
+	memset(s, 0, privsize);
+	s->magic = HDLCDRV_MAGIC;
+	strncpy(s->ifname, ifname, sizeof(s->ifname));
+	s->ops = ops;
+	/*
+	 * initialize part of the device struct
+	 */
+	dev->name = s->ifname;
+	dev->if_port = 0;
+	dev->init = hdlcdrv_probe;
+	dev->start = 0;
+	dev->tbusy = 1;
+	dev->base_addr = baseaddr;
+	dev->irq = irq;
+	dev->dma = dma;
+	if (register_netdev(dev)) {
+		printk(KERN_WARNING "hdlcdrv: cannot register net "
+		       "device %s\n", s->ifname);
+		kfree(dev->priv);
+		return -ENXIO;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+int hdlcdrv_unregister_hdlcdrv(struct device *dev) 
+{
+	struct hdlcdrv_state *s;
+
+	if (!dev)
+		return -EINVAL;
+	if (!(s = (struct hdlcdrv_state *)dev->priv))
+		return -EINVAL;
+	if (s->magic != HDLCDRV_MAGIC)
+		return -EINVAL;
+	if (dev->start && s->ops->close)
+		s->ops->close(dev);
+	unregister_netdev(dev);
+	kfree(s);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+EXPORT_SYMBOL(hdlcdrv_receiver);
+EXPORT_SYMBOL(hdlcdrv_transmitter);
+EXPORT_SYMBOL(hdlcdrv_arbitrate);
+EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv);
+EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
+
+#else
+
+static struct symbol_table hdlcdrv_syms = {
+#include <linux/symtab_begin.h>
+        X(hdlcdrv_receiver),
+        X(hdlcdrv_transmitter),
+        X(hdlcdrv_arbitrate),
+        X(hdlcdrv_register_hdlcdrv),
+        X(hdlcdrv_unregister_hdlcdrv),
+#include <linux/symtab_end.h>
+};
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+
+#endif
+
+/* --------------------------------------------------------------------- */
+
+__initfunc(int init_module(void))
+{
+	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
+	printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
+#if LINUX_VERSION_CODE < 0x20115
+        register_symtab(&hdlcdrv_syms);
+#endif
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	printk(KERN_INFO "hdlcdrv: cleanup\n");
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/mkiss.c linux/drivers/net/hamradio/mkiss.c
--- v2.1.70/linux/drivers/net/hamradio/mkiss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/mkiss.c	Sat Nov 29 10:33:19 1997
@@ -0,0 +1,1134 @@
+/*
+ *	MKISS Driver
+ *
+ *	This module:
+ *		This module 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.
+ *
+ * 		This module implements the AX.25 protocol for kernel-based
+ *		devices like TTYs. It interfaces between a raw TTY, and the
+ *		kernel's AX.25 protocol layers, just like slip.c.
+ *		AX.25 needs to be seperated from slip.c while slip.c is no
+ *		longer a static kernel device since it is a module.
+ *		This method clears the way to implement other kiss protocols
+ *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
+ *
+ * Hans Alblas <hans@esrac.ele.tue.nl>
+ *
+ *	History
+ *	Jonathan (G4KLX)	Fixed to match Linux networking changes - 2.1.15.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/major.h>
+#include <linux/init.h>
+
+#include <linux/timer.h>
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#include <net/ax25.h>
+
+#include "mkiss.h"
+
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+#ifdef MODULE
+#define AX25_VERSION    "AX25-MODULAR-NET3.019-NEWTTY"
+#define	min(a,b)	(a < b ? a : b)
+#else
+#define	AX25_VERSION	"AX25-NET3.019-NEWTTY"
+#endif
+
+#define NR_MKISS 4
+#define MKISS_SERIAL_TYPE_NORMAL 1
+
+struct mkiss_channel {
+	int magic;		/* magic word */
+	int init;		/* channel exists? */
+	struct tty_struct *tty; /* link to tty control structure */
+};
+
+typedef struct ax25_ctrl {
+	char if_name[8];	/* "ax0\0" .. "ax99999\0"	*/
+	struct ax_disp ctrl;	/* 				*/
+	struct device  dev;	/* the device			*/
+} ax25_ctrl_t;
+
+static ax25_ctrl_t **ax25_ctrls = NULL;
+
+int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
+
+static struct tty_ldisc	ax_ldisc;
+static struct tty_driver mkiss_driver;
+static int mkiss_refcount;
+static struct tty_struct *mkiss_table[NR_MKISS];
+static struct termios *mkiss_termios[NR_MKISS];
+static struct termios *mkiss_termios_locked[NR_MKISS];
+struct mkiss_channel MKISS_Info[NR_MKISS];
+
+static int ax25_init(struct device *);
+static int mkiss_init(void);
+static int mkiss_write(struct tty_struct *, int, const unsigned char *, int);
+static int kiss_esc(unsigned char *, unsigned char *, int);
+static void kiss_unesc(struct ax_disp *, unsigned char);
+
+/* Find a free channel, and link in this `tty' line. */
+static inline struct ax_disp *ax_alloc(void)
+{
+	ax25_ctrl_t *axp;
+	int i;
+
+	if (ax25_ctrls == NULL)		/* Master array missing ! */
+		return NULL;
+
+	for (i = 0; i < ax25_maxdev; i++) {
+		axp = ax25_ctrls[i];
+
+		/* Not allocated ? */
+		if (axp == NULL)
+			break;
+
+		/* Not in use ? */
+		if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags))
+			break;
+	}
+
+	/* Sorry, too many, all slots in use */
+	if (i >= ax25_maxdev)
+		return NULL;
+
+	/* If no channels are available, allocate one */
+	if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
+		axp = ax25_ctrls[i];
+		memset(axp, 0, sizeof(ax25_ctrl_t));
+
+		/* Initialize channel control data */
+		set_bit(AXF_INUSE, &axp->ctrl.flags);
+		sprintf(axp->if_name, "ax%d", i++);
+		axp->ctrl.tty      = NULL;
+		axp->dev.name      = axp->if_name;
+		axp->dev.base_addr = i;
+		axp->dev.priv      = (void *)&axp->ctrl;
+		axp->dev.next      = NULL;
+		axp->dev.init      = ax25_init;
+	}
+
+	if (axp != NULL) {
+		/*
+		 * register device so that it can be ifconfig'ed
+		 * ax25_init() will be called as a side-effect
+		 * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
+		 */
+		if (register_netdev(&axp->dev) == 0) {
+			/* (Re-)Set the INUSE bit.   Very Important! */
+			set_bit(AXF_INUSE, &axp->ctrl.flags);
+			axp->ctrl.dev = &axp->dev;
+			axp->dev.priv = (void *)&axp->ctrl;
+
+			return &axp->ctrl;
+		} else {
+			clear_bit(AXF_INUSE,&axp->ctrl.flags);
+			printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
+		}
+	}
+
+	return NULL;
+}
+
+/* Free an AX25 channel. */
+static inline void ax_free(struct ax_disp *ax)
+{
+	/* Free all AX25 frame buffers. */
+	if (ax->rbuff)
+		kfree(ax->rbuff);
+	ax->rbuff = NULL;
+	if (ax->xbuff)
+		kfree(ax->xbuff);
+	ax->xbuff = NULL;
+	if (!test_and_clear_bit(AXF_INUSE, &ax->flags))
+		printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
+}
+
+static void ax_changedmtu(struct ax_disp *ax)
+{
+	struct device *dev = ax->dev;
+	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+	int len;
+	unsigned long flags;
+
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	xbuff = kmalloc(len + 4, GFP_ATOMIC);
+	rbuff = kmalloc(len + 4, GFP_ATOMIC);
+
+	if (xbuff == NULL || rbuff == NULL)  {
+		printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
+		       ax->dev->name);
+		dev->mtu = ax->mtu;
+		if (xbuff != NULL)
+			kfree(xbuff);
+		if (rbuff != NULL)
+			kfree(rbuff);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+
+	oxbuff    = ax->xbuff;
+	ax->xbuff = xbuff;
+	orbuff    = ax->rbuff;
+	ax->rbuff = rbuff;
+
+	if (ax->xleft) {
+		if (ax->xleft <= len) {
+			memcpy(ax->xbuff, ax->xhead, ax->xleft);
+		} else  {
+			ax->xleft = 0;
+			ax->tx_dropped++;
+		}
+	}
+
+	ax->xhead = ax->xbuff;
+
+	if (ax->rcount) {
+		if (ax->rcount <= len) {
+			memcpy(ax->rbuff, orbuff, ax->rcount);
+		} else  {
+			ax->rcount = 0;
+			ax->rx_over_errors++;
+			set_bit(AXF_ERROR, &ax->flags);
+		}
+	}
+
+	ax->mtu      = dev->mtu + 73;
+	ax->buffsize = len;
+
+	restore_flags(flags);
+
+	if (oxbuff != NULL)
+		kfree(oxbuff);
+	if (orbuff != NULL)
+		kfree(orbuff);
+}
+
+
+/* Set the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_lock(struct ax_disp *ax)
+{
+	if (test_and_set_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "mkiss: %s: trying to lock already locked device!\n", ax->dev->name);
+}
+
+
+/* Clear the "sending" flag.  This must be atomic, hence the ASM. */
+static inline void ax_unlock(struct ax_disp *ax)
+{
+	if (!test_and_clear_bit(0, (void *)&ax->dev->tbusy))
+		printk(KERN_ERR "mkiss: %s: trying to unlock already unlocked device!\n", ax->dev->name);
+}
+
+/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
+static void ax_bump(struct ax_disp *ax)
+{
+	struct ax_disp *tmp_ax;
+	struct sk_buff *skb;
+	struct mkiss_channel *mkiss;
+	int count;
+
+        tmp_ax = ax;
+
+	if (ax->rbuff[0] > 0x0f) {
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+			if (mkiss->magic == MKISS_DRIVER_MAGIC)
+				tmp_ax = ax->mkiss;
+		}
+ 	}
+
+	count = ax->rcount;
+
+	if ((skb = dev_alloc_skb(count)) == NULL) {
+		printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
+		ax->rx_dropped++;
+		return;
+	}
+
+	skb->dev      = tmp_ax->dev;
+	memcpy(skb_put(skb,count), ax->rbuff, count);
+	skb->mac.raw  = skb->data;
+	skb->protocol = htons(ETH_P_AX25);
+	netif_rx(skb);
+	tmp_ax->rx_packets++;
+}
+
+/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
+static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
+{
+	unsigned char *p;
+	int actual, count;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+
+	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
+		ax_changedmtu(ax);
+
+	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
+		len = ax->mtu;
+		printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
+		ax->tx_dropped++;
+		ax_unlock(ax);
+		return;
+	}
+
+	p = icp;
+
+	if (mkiss->magic  != MKISS_DRIVER_MAGIC) {
+		count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
+		ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
+		ax->tx_packets++;
+		ax->dev->trans_start = jiffies;
+		ax->xleft = count - actual;
+		ax->xhead = ax->xbuff + actual;
+	} else {
+		count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
+		ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
+		ax->tx_packets++;
+		ax->mkiss->dev->trans_start = jiffies;
+		ax->mkiss->xleft = count - actual;
+		ax->mkiss->xhead = ax->mkiss->xbuff + actual;
+	}
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+static void ax25_write_wakeup(struct tty_struct *tty)
+{
+	int actual;
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct mkiss_channel *mkiss;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+	if (ax->xleft <= 0)  {
+		/* Now serial buffer is almost free & we can start
+		 * transmission of another packet
+		 */
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+		if (ax->mkiss != NULL) {
+			mkiss= ax->mkiss->tty->driver_data;
+	        	if (mkiss->magic  == MKISS_DRIVER_MAGIC)
+				ax_unlock(ax->mkiss);
+	        }
+
+		ax_unlock(ax);
+		mark_bh(NET_BH);
+		return;
+	}
+
+	actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
+	ax->xleft -= actual;
+	ax->xhead += actual;
+}
+
+/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
+static int ax_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	struct mkiss_channel *mkiss = ax->tty->driver_data;
+	struct ax_disp *tmp_ax;
+
+	tmp_ax = NULL;
+
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		if (skb->data[0] < 0x10)
+			skb->data[0] = skb->data[0] + 0x10;
+		tmp_ax = ax->mkiss;
+	}
+
+	if (!dev->start)  {
+		printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
+		return 1;
+	}
+
+	if (tmp_ax != NULL)
+		if (tmp_ax->dev->tbusy)
+			return 1;
+
+	if (tmp_ax != NULL)
+		if (dev->tbusy) {
+			printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
+			ax_unlock(ax);
+	        }
+
+	if (dev->tbusy) {
+		/*
+		 * May be we must check transmitter timeout here ?
+		 *      14 Oct 1994 Dmitry Gorodchanin.
+		 */
+		if (jiffies - dev->trans_start  < 20 * HZ) {
+			/* 20 sec timeout not reached */
+			return 1;
+		}
+
+		printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
+		       (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
+		       "bad line quality" : "driver error");
+
+		ax->xleft = 0;
+		ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		ax_unlock(ax);
+	}
+
+	/* We were not busy, so we are now... :-) */
+	if (skb != NULL) {
+		ax_lock(ax);
+		if (tmp_ax != NULL)
+			ax_lock(tmp_ax);
+		ax_encaps(ax, skb->data, skb->len);
+		kfree_skb(skb, FREE_WRITE);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+
+/* Return the frame type ID */
+static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+	  void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+	if (type != htons(ETH_P_AX25))
+		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+	return 0;
+}
+
+
+static int ax_rebuild_header(struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+	return ax25_rebuild_header(skb);
+#else
+	return 0;
+#endif
+}
+
+#endif	/* CONFIG_{AX25,AX25_MODULE} */
+
+/* Open the low-level part of the AX25 channel. Easy! */
+static int ax_open(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+	unsigned long len;
+
+	if (ax->tty == NULL)
+		return -ENODEV;
+
+	/*
+	 * Allocate the frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * xbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+	len = dev->mtu * 2;
+
+	/*
+	 * allow for arrival of larger UDP packets, even if we say not to
+	 * also fixes a bug in which SunOS sends 512-byte packets even with
+	 * an MSS of 128
+	 */
+	if (len < 576 * 2)
+		len = 576 * 2;
+
+	if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto norbuff;
+
+	if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+		goto noxbuff;
+
+	ax->mtu	     = dev->mtu + 73;
+	ax->buffsize = len;
+	ax->rcount   = 0;
+	ax->xleft    = 0;
+
+	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
+	dev->tbusy  = 0;
+	dev->start  = 1;
+
+	return 0;
+
+	/* Cleanup */
+	kfree(ax->xbuff);
+
+noxbuff:
+	kfree(ax->rbuff);
+
+norbuff:
+	return -ENOMEM;
+}
+
+
+/* Close the low-level part of the AX25 channel. Easy! */
+static int ax_close(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty == NULL)
+		return -EBUSY;
+
+	ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+static int ax25_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of data has been received, which can now be decapsulated
+ * and sent on to the AX.25 layer for further processing.
+ */
+static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+
+	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
+		return;
+
+	/*
+	 * Argh! mtu change time! - costs us the packet part received
+	 * at the change
+	 */
+	if (ax->mtu != ax->dev->mtu + 73)
+		ax_changedmtu(ax);
+
+	/* Read the characters out of the buffer */
+	while (count--) {
+		if (fp != NULL && *fp++) {
+			if (!test_and_set_bit(AXF_ERROR, &ax->flags))
+				ax->rx_errors++;
+			cp++;
+			continue;
+		}
+
+		kiss_unesc(ax, *cp++);
+	}
+}
+
+static int ax25_open(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	struct ax_disp *tmp_ax;
+	struct mkiss_channel *mkiss;
+	int err, cnt;
+
+	/* First make sure we're not already connected. */
+	if (ax && ax->magic == AX25_MAGIC)
+		return -EEXIST;
+
+	/* OK.  Find a free AX25 channel to use. */
+	if ((ax = ax_alloc()) == NULL)
+		return -ENFILE;
+
+	ax->tty = tty;
+	tty->disc_data = ax;
+
+	ax->mkiss = NULL;
+	tmp_ax    = NULL;
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	/* Restore default settings */
+	ax->dev->type = ARPHRD_AX25;
+
+	/* Perform the low-level AX25 initialization. */
+	if ((err = ax_open(ax->dev)))
+		return err;
+
+	mkiss= ax->tty->driver_data;
+
+	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
+		for (cnt = 1; cnt < ax25_maxdev; cnt++) {
+			if (ax25_ctrls[cnt]) {
+				if (ax25_ctrls[cnt]->dev.start) {
+					if (ax == &ax25_ctrls[cnt]->ctrl) {
+						cnt--;
+						tmp_ax = &ax25_ctrls[cnt]->ctrl;
+						break;
+					}
+				}
+			}
+		}
+	}
+
+	if (tmp_ax != NULL) {
+		ax->mkiss     = tmp_ax;
+		tmp_ax->mkiss = ax;
+	}
+
+	MOD_INC_USE_COUNT;
+
+	/* Done.  We have linked the TTY line to a channel. */
+	return ax->dev->base_addr;
+}
+
+static void ax25_close(struct tty_struct *tty)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int mkiss ;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return;
+
+	mkiss = ax->mode;
+	if (ax->dev->flags & IFF_UP)
+	{
+		dev_lock_wait();
+		dev_close(ax->dev);
+		dev_unlock_list();
+	}
+
+	tty->disc_data = 0;
+	ax->tty        = NULL;
+
+	/* VSV = very important to remove timers */
+	ax_free(ax);
+	unregister_netdev(ax->dev);
+
+	MOD_DEC_USE_COUNT;
+}
+
+
+static struct net_device_stats *ax_get_stats(struct device *dev)
+{
+	static struct net_device_stats stats;
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	memset(&stats, 0, sizeof(struct net_device_stats));
+
+	stats.rx_packets     = ax->rx_packets;
+	stats.tx_packets     = ax->tx_packets;
+	stats.rx_dropped     = ax->rx_dropped;
+	stats.tx_dropped     = ax->tx_dropped;
+	stats.tx_errors      = ax->tx_errors;
+	stats.rx_errors      = ax->rx_errors;
+	stats.rx_over_errors = ax->rx_over_errors;
+
+	return &stats;
+}
+
+
+/************************************************************************
+ *			   STANDARD ENCAPSULATION	        	 *
+ ************************************************************************/
+
+int kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+	unsigned char *ptr = d;
+	unsigned char c;
+
+	/*
+	 * Send an initial END character to flush out any
+	 * data that may have accumulated in the receiver
+	 * due to line noise.
+	 */
+
+	*ptr++ = END;
+
+	while (len-- > 0) {
+		switch (c = *s++) {
+			case END:
+				*ptr++ = ESC;
+				*ptr++ = ESC_END;
+				break;
+			case ESC:
+				*ptr++ = ESC;
+				*ptr++ = ESC_ESC;
+				break;
+			default:
+				*ptr++ = c;
+				break;
+		}
+	}
+
+	*ptr++ = END;
+
+	return ptr - d;
+}
+
+static void kiss_unesc(struct ax_disp *ax, unsigned char s)
+{
+	switch (s) {
+		case END:
+			/* drop keeptest bit = VSV */
+			if (test_bit(AXF_KEEPTEST, &ax->flags))
+				clear_bit(AXF_KEEPTEST, &ax->flags);
+
+			if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
+				ax_bump(ax);
+
+			clear_bit(AXF_ESCAPE, &ax->flags);
+			ax->rcount = 0;
+			return;
+
+		case ESC:
+			set_bit(AXF_ESCAPE, &ax->flags);
+			return;
+		case ESC_ESC:
+			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+				s = ESC;
+			break;
+		case ESC_END:
+			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
+				s = END;
+			break;
+	}
+
+	if (!test_bit(AXF_ERROR, &ax->flags)) {
+		if (ax->rcount < ax->buffsize) {
+			ax->rbuff[ax->rcount++] = s;
+			return;
+		}
+
+		ax->rx_over_errors++;
+		set_bit(AXF_ERROR, &ax->flags);
+	}
+}
+
+
+int ax_set_mac_address(struct device *dev, void *addr)
+{
+	int err;
+
+	if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0)
+		return err;
+
+	/* addr is an AX.25 shifted ASCII mac address */
+	copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+static int ax_set_dev_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = addr;
+
+	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+
+	return 0;
+}
+
+
+/* Perform I/O control on an active ax25 channel. */
+static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
+	int err;
+	unsigned int tmp;
+
+	/* First make sure we're connected. */
+	if (ax == NULL || ax->magic != AX25_MAGIC)
+		return -EINVAL;
+
+	switch (cmd) {
+	 	case SIOCGIFNAME:
+			if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0)
+				return err;
+			copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1);
+			return 0;
+
+		case SIOCGIFENCAP:
+			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0)
+				return err;
+			put_user(4, (int *)arg);
+			return 0;
+
+		case SIOCSIFENCAP:
+			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
+				return err;
+			get_user(tmp, (int *)arg);
+	 		ax->mode = tmp;
+			ax->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
+			ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+			ax->dev->type            = ARPHRD_AX25;
+			return 0;
+
+		 case SIOCSIFHWADDR:
+			return ax_set_mac_address(ax->dev, arg);
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+static int ax_open_dev(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	if (ax->tty==NULL)
+		return -ENODEV;
+
+	return 0;
+}
+
+/* Initialize AX25 control device -- register AX25 line discipline */
+__initfunc(int mkiss_init_ctrl_dev(void))
+{
+	int status;
+
+	if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
+
+	if ((ax25_ctrls = kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array !  No mkiss available\n");
+		return -ENOMEM;
+	}
+
+	/* Clear the pointer array, we allocate devices when we need them */
+	memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
+
+	/* Fill in our line protocol discipline, and register it */
+	memset(&ax_ldisc, 0, sizeof(ax_ldisc));
+	ax_ldisc.magic  = TTY_LDISC_MAGIC;
+	ax_ldisc.name   = "mkiss";
+	ax_ldisc.flags  = 0;
+	ax_ldisc.open   = ax25_open;
+	ax_ldisc.close  = ax25_close;
+	ax_ldisc.read   = NULL;
+	ax_ldisc.write  = NULL;
+	ax_ldisc.ioctl  = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl;
+	ax_ldisc.poll   = NULL;
+
+	ax_ldisc.receive_buf  = ax25_receive_buf;
+	ax_ldisc.receive_room = ax25_receive_room;
+	ax_ldisc.write_wakeup = ax25_write_wakeup;
+
+	if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0)
+		printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
+
+	mkiss_init();
+
+#ifdef MODULE
+	return status;
+#else
+	/*
+	 * Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
+	 */
+	return ENODEV;
+#endif
+}
+
+
+/* Initialize the driver.  Called by network startup. */
+
+static int ax25_init(struct device *dev)
+{
+	struct ax_disp *ax = (struct ax_disp*)dev->priv;
+
+	static char ax25_bcast[AX25_ADDR_LEN] =
+		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+	static char ax25_test[AX25_ADDR_LEN] =
+		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+	if (ax == NULL)		/* Allocation failed ?? */
+		return -ENODEV;
+
+	/* Set up the "AX25 Control Block". (And clear statistics) */
+	memset(ax, 0, sizeof (struct ax_disp));
+	ax->magic  = AX25_MAGIC;
+	ax->dev	   = dev;
+
+	/* Finish setting up the DEVICE info. */
+	dev->mtu             = AX_MTU;
+	dev->hard_start_xmit = ax_xmit;
+	dev->open            = ax_open_dev;
+	dev->stop            = ax_close;
+	dev->get_stats	     = ax_get_stats;
+#ifdef HAVE_SET_MAC_ADDR
+	dev->set_mac_address = ax_set_dev_mac_address;
+#endif
+	dev->hard_header_len = 0;
+	dev->addr_len        = 0;
+	dev->type            = ARPHRD_AX25;
+	dev->tx_queue_len    = 10;
+
+	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_test,  AX25_ADDR_LEN);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+	dev->hard_header     = ax_header;
+	dev->rebuild_header  = ax_rebuild_header;
+#endif
+
+	dev_init_buffers(dev);
+
+	/* New-style flags. */
+	dev->flags      = 0;
+
+	return 0;
+}
+
+static int mkiss_open(struct tty_struct *tty, struct file *filp)
+{
+	struct mkiss_channel *mkiss;
+	int chan;
+
+	chan = MINOR(tty->device) - tty->driver.minor_start;
+
+	if (chan < 0 || chan >= NR_MKISS)
+		return -ENODEV;
+
+	mkiss = &MKISS_Info[chan];
+
+	mkiss->magic =  MKISS_DRIVER_MAGIC;
+	mkiss->init  = 1;
+	mkiss->tty   = tty;
+
+	tty->driver_data = mkiss;
+
+	tty->termios->c_iflag  = IGNBRK | IGNPAR;
+	tty->termios->c_cflag  = B9600 | CS8 | CLOCAL;
+	tty->termios->c_cflag &= ~CBAUD;
+
+	return 0;
+}
+
+static void mkiss_close(struct tty_struct *tty, struct file * filp)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC)
+                return;
+
+	mkiss->tty   = NULL;
+	mkiss->init  = 0;
+	tty->stopped = 0;
+}
+
+static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	return 0;
+}
+
+static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	/* Ignore serial ioctl's */
+	switch (cmd) {
+		case TCSBRK:
+		case TIOCMGET:
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+		case TCSETS:
+		case TCSETSF:		/* should flush first, but... */
+		case TCSETSW:		/* should wait until flush, but... */
+			return 0;
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+
+static void mkiss_dummy(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return;
+
+	if (mkiss == NULL)
+		return;
+}
+
+
+static int mkiss_write_room(struct tty_struct * tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+
+static int mkiss_chars_in_buffer(struct tty_struct *tty)
+{
+	struct mkiss_channel *mkiss = tty->driver_data;
+
+	if (tty == NULL)
+		return 0;
+
+	if (mkiss == NULL)
+		return 0;
+
+	return 0;
+}
+
+
+static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	/* we don't do termios */
+}
+
+/* ******************************************************************** */
+/* * 			Init MKISS driver 			      * */
+/* ******************************************************************** */
+
+__initfunc(static int mkiss_init(void))
+{
+	memset(&mkiss_driver, 0, sizeof(struct tty_driver));
+
+	mkiss_driver.magic       = MKISS_DRIVER_MAGIC;
+	mkiss_driver.name        = "mkiss";
+	mkiss_driver.major       = MKISS_MAJOR;
+	mkiss_driver.minor_start = 0;
+	mkiss_driver.num         = NR_MKISS;
+	mkiss_driver.type        = TTY_DRIVER_TYPE_SERIAL;
+	mkiss_driver.subtype     = MKISS_SERIAL_TYPE_NORMAL;	/* not needed */
+
+	mkiss_driver.init_termios         = tty_std_termios;
+	mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
+	mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL;
+
+	mkiss_driver.flags           = TTY_DRIVER_REAL_RAW;
+	mkiss_driver.refcount        = &mkiss_refcount;
+	mkiss_driver.table           = mkiss_table;
+	mkiss_driver.termios         = (struct termios **)mkiss_termios;
+	mkiss_driver.termios_locked  = (struct termios **)mkiss_termios_locked;
+
+	mkiss_driver.ioctl           = mkiss_ioctl;
+	mkiss_driver.open            = mkiss_open;
+	mkiss_driver.close           = mkiss_close;
+	mkiss_driver.write           = mkiss_write;
+	mkiss_driver.write_room      = mkiss_write_room;
+	mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer;
+	mkiss_driver.set_termios     = mkiss_set_termios;
+
+	/* some unused functions */
+	mkiss_driver.flush_buffer = mkiss_dummy;
+	mkiss_driver.throttle     = mkiss_dummy;
+	mkiss_driver.unthrottle   = mkiss_dummy;
+	mkiss_driver.stop         = mkiss_dummy;
+	mkiss_driver.start        = mkiss_dummy;
+	mkiss_driver.hangup       = mkiss_dummy;
+	mkiss_driver.flush_chars  = mkiss_dummy;
+	mkiss_driver.put_char     = mkiss_dummy2;
+
+	if (tty_register_driver(&mkiss_driver)) {
+		printk(KERN_ERR "mkiss: couldn't register Mkiss device\n");
+		return -EIO;
+	}
+
+	printk(KERN_INFO "AX.25 Multikiss device enabled\n");
+
+	return 0;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_PARM(ax25_maxdev, "i");
+MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
+
+MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
+MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
+
+int init_module(void)
+{
+	return mkiss_init_ctrl_dev();
+}
+
+void cleanup_module(void)
+{
+	int i;
+
+	if (ax25_ctrls != NULL) {
+		for (i = 0; i < ax25_maxdev; i++) {
+			if (ax25_ctrls[i]) {
+				/*
+				 * VSV = if dev->start==0, then device
+				 * unregistred while close proc.
+				 */
+				if (ax25_ctrls[i]->dev.start)
+					unregister_netdev(&(ax25_ctrls[i]->dev));
+
+				kfree(ax25_ctrls[i]);
+				ax25_ctrls[i] = NULL;
+			}
+		}
+
+		kfree(ax25_ctrls);
+		ax25_ctrls = NULL;
+	}
+
+	if ((i = tty_register_ldisc(N_AX25, NULL)))
+		printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
+
+	if (tty_unregister_driver(&mkiss_driver))	/* remove devive */
+		printk(KERN_ERR "mkiss: can't unregister MKISS device\n");
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/mkiss.h linux/drivers/net/hamradio/mkiss.h
--- v2.1.70/linux/drivers/net/hamradio/mkiss.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/mkiss.h	Sun Nov 10 09:12:56 1996
@@ -0,0 +1,56 @@
+/****************************************************************************
+ *	Defines for the Multi-KISS driver.
+ ****************************************************************************/
+
+#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
+					   This can be overridden with
+					   insmod -oax25_maxdev=nnn	*/
+#define AX_MTU		236	
+
+/* SLIP/KISS protocol characters. */
+#define END             0300		/* indicates end of frame	*/
+#define ESC             0333		/* indicates byte stuffing	*/
+#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
+#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
+
+struct ax_disp {
+	int                magic;
+
+	/* Various fields. */
+	struct tty_struct  *tty;		/* ptr to TTY structure		*/
+	struct device      *dev;		/* easy for intr handling	*/
+	struct ax_disp     *mkiss;		/* mkiss txport if mkiss channel*/
+
+	/* These are pointers to the malloc()ed frame buffers. */
+	unsigned char      *rbuff;		/* receiver buffer		*/
+	int                rcount;		/* received chars counter       */
+	unsigned char      *xbuff;		/* transmitter buffer		*/
+	unsigned char      *xhead;		/* pointer to next byte to XMIT */
+	int                xleft;		/* bytes left in XMIT queue     */
+
+	/* SLIP interface statistics. */
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter      */
+	unsigned long      rx_errors;		/* Parity, etc. errors          */
+	unsigned long      tx_errors;		/* Planned stuff                */
+	unsigned long      rx_dropped;		/* No memory for skb            */
+	unsigned long      tx_dropped;		/* When MTU change              */
+	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
+
+	/* Detailed SLIP statistics. */
+	int                 mtu;		/* Our mtu (to spot changes!)   */
+	int                 buffsize;		/* Max buffers sizes            */
+
+
+	unsigned char       flags;		/* Flag values/ mode etc	*/
+#define AXF_INUSE	0		/* Channel in use               */
+#define AXF_ESCAPE	1               /* ESC received                 */
+#define AXF_ERROR	2               /* Parity, etc. error           */
+#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
+#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
+
+	int                 mode;
+};
+
+#define AX25_MAGIC		0x5316
+#define MKISS_DRIVER_MAGIC	1215
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/pi2.c linux/drivers/net/hamradio/pi2.c
--- v2.1.70/linux/drivers/net/hamradio/pi2.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/pi2.c	Sat Nov 29 10:33:19 1997
@@ -0,0 +1,1677 @@
+/*
+   pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface.
+   Copyright (c) 1994 David Perry
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2, as
+   published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 675 Mass Ave, Cambridge MA 02139, USA.
+
+   The file skeleton.c by Donald Becker was used as a starting point
+   for this driver.
+
+   Revision History
+
+   April 6, 1994  (dp) Created
+		       version 0.0 ALPHA
+   April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison.
+                       version 0.1 ALPHA
+   April 13, 1994 (dp) Included address probing from JPM, autoirq
+		       version 0.2 ALPHA
+   April 14, 1994 (ac) Sketched in the NET3 changes.
+   April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev()
+		       instead of kmalloc() to ensure that DMA buffers will
+		       reside under the 16 meg line.
+		       version 0.4 ALPHA
+   April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions.
+		       Fixed a nasty problem with DMA.
+		       version 0.5 ALPHA
+   June 6, 1994 (ac)   Fixed to match the buffer locking changes. Added a hack to
+   		       fix a funny I see (search for HACK) and fixed the calls in
+   		       init() so it doesn't migrate module based ethernet cards up
+   		       to eth2 Took out the old module ideas as they are no longer
+   		       relevant to the PI driver.
+   July 16, 1994 (dp)  Fixed the B channel rx overrun problem ac referred to
+   		       above. Also added a bit of a hack to improve the maximum
+   	               baud rate on the B channel (Search for STUFF2). Included
+   		       ioctl stuff from John Paul Morrison. version 0.6 ALPHA
+   Feb 9, 1995 (dp)    Updated for 1.1.90 kernel
+                       version 0.7 ALPHA
+   Apr 6, 1995 (ac)    Tweaks for NET3 pre snapshot 002 AX.25
+   April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program
+                       when changing the baud rate or clock mode.
+                       version 0.8 ALPHA
+   July 17, 1995 (ac)  Finally polishing of AX25.030+ support
+   Oct  29, 1995 (ac)  A couple of minor fixes before this, and this release changes
+   		       to the proper set_mac_address semantics which will break
+   		       a few programs I suspect.
+   Aug  18, 1996 (jsn) Converted to be used as a module.
+   Dec  13, 1996 (jsn) Fixed to match Linux networking changes.
+*/
+
+/* The following #define invokes a hack that will improve performance (baud)
+   for the B port. The up side is it makes 9600 baud work ok on the B port.
+   It may do 38400, depending on the host. The down side is it burns up
+   CPU cycles with ints locked for up to 1 character time, at the beginning
+   of each transmitted packet. If this causes you to lose sleep, #undefine it.
+*/
+
+/*#define STUFF2 1*/
+
+/* The default configuration */
+#define PI_DMA 3
+
+#define DEF_A_SPEED 0		/* 0 means external clock */
+#define DEF_A_TXDELAY 15	/* 15 mS transmit delay */
+#define DEF_A_PERSIST 128	/* 50% persistence */
+#define DEF_A_SLOTIME 15	/* 15 mS slot time */
+#define DEF_A_SQUELDELAY 1	/* 1 mS squelch delay - allows fcs and flag */
+#define DEF_A_CLOCKMODE 0	/* clock mode - 0 is normal */
+
+#define DEF_B_SPEED 1200	/* 1200 baud */
+#define DEF_B_TXDELAY 40	/* 400 mS */
+#define DEF_B_PERSIST 128	/* 50% */
+#define DEF_B_SLOTIME 30	/* 300 mS */
+#define DEF_B_SQUELDELAY 3	/* 30 mS */
+#define DEF_B_CLOCKMODE 0	/* Normal clock mode */
+
+/* The following #define is only really required for the PI card, not
+   the PI2 - but it's safer to leave it in. */
+#define REALLY_SLOW_IO 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/if_arp.h>
+#include <linux/pi2.h>
+#include <linux/init.h>
+#include "z8530.h"
+#include <net/ax25.h>
+
+struct mbuf {
+    struct mbuf *next;
+    int cnt;
+    char data[0];
+};
+
+/*
+ *	The actual devices we will use
+ */
+
+/*
+ *	PI device declarations.
+ */
+
+static int pi0_preprobe(struct device *dev){return 0;}	/* Dummy probe function */
+static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
+
+
+/* The number of low I/O ports used by the card. */
+#define PI_TOTAL_SIZE	8
+
+
+/* Index to functions, as function prototypes. */
+
+static int pi_probe(struct device *dev, int card_type);
+static int pi_open(struct device *dev);
+static int pi_send_packet(struct sk_buff *skb, struct device *dev);
+static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs);
+static int pi_close(struct device *dev);
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct net_device_stats *pi_get_stats(struct device *dev);
+static void rts(struct pi_local *lp, int x);
+static void b_rxint(struct device *dev, struct pi_local *lp);
+static void b_txint(struct pi_local *lp);
+static void b_exint(struct pi_local *lp);
+static void a_rxint(struct device *dev, struct pi_local *lp);
+static void a_txint(struct pi_local *lp);
+static void a_exint(struct pi_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int ext2_secrm_seed = 152;	/* Random generator base */
+
+extern inline unsigned char random(void)
+{
+	return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed
+			    * 69069l + 1);
+}
+
+extern inline void wrtscc(int cbase, int ctl, int sccreg, int val)
+{
+	/* assume caller disables interrupts! */
+	outb_p(0, cbase + DMAEN);	/* Disable DMA while we touch the scc */
+	outb_p(sccreg, ctl);		/* Select register */
+	outb_p(val, ctl);		/* Output value */
+	outb_p(1, cbase + DMAEN);	/* Enable DMA */
+}
+
+extern inline int rdscc(int cbase, int ctl, int sccreg)
+{
+	int retval;
+
+	/* assume caller disables interrupts! */
+	outb_p(0, cbase + DMAEN);	/* Disable DMA while we touch the scc */
+	outb_p(sccreg, ctl);	/* Select register */
+	retval = inb_p(ctl);
+	outb_p(1, cbase + DMAEN);	/* Enable DMA */
+	return retval;
+}
+
+static void switchbuffers(struct pi_local *lp)
+{
+	if (lp->rcvbuf == lp->rxdmabuf1)
+		lp->rcvbuf = lp->rxdmabuf2;
+	else
+		lp->rcvbuf = lp->rxdmabuf1;
+}
+
+static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb)
+{
+	char kickflag;
+	unsigned long flags;
+
+	lp->stats.tx_packets++;
+
+	save_flags(flags);
+	cli();
+	kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+	restore_flags(flags);
+
+	skb_queue_tail(&lp->sndq, skb);
+	if (kickflag) 
+	{
+		/* simulate interrupt to xmit */
+		switch (lp->base & 2) 
+		{
+			case 2:
+				a_txint(lp);	/* process interrupt */
+				break;
+			case 0:
+				save_flags(flags);
+				cli();
+				if (lp->tstate == IDLE)
+					b_txint(lp);
+				restore_flags(flags);
+				break;
+		}
+	}
+}
+
+static void setup_rx_dma(struct pi_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    unsigned long dma_abs;
+    unsigned dmachan;
+
+    save_flags(flags);
+    cli();
+
+    dma_abs = (unsigned long) (lp->rcvbuf->data);
+    dmachan = lp->dmachan;
+    cmd = lp->base + CTL;
+
+    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+	panic("PI: RX buffer violates DMA boundary!");
+
+    /* Get ready for RX DMA */
+    wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+    disable_dma(dmachan);
+    clear_dma_ff(dmachan);
+
+    /* Set DMA mode register to single transfers, incrementing address,
+     *  auto init, writes
+     */
+    set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+    set_dma_addr(dmachan, dma_abs);
+    set_dma_count(dmachan, lp->bufsiz);
+    enable_dma(dmachan);
+
+    /* If a packet is already coming in, this line is supposed to
+	   avoid receiving a partial packet.
+     */
+    wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+    /* Enable RX dma */
+    wrtscc(lp->cardbase, cmd, R1,
+      WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+    restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pi_local *lp, int length)
+{
+    unsigned long dma_abs;
+    unsigned long flags;
+    unsigned long dmachan;
+
+    save_flags(flags);
+    cli();
+
+    dmachan = lp->dmachan;
+    dma_abs = (unsigned long) (lp->txdmabuf);
+
+    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+	panic("PI: TX buffer violates DMA boundary!");
+
+    disable_dma(dmachan);
+    /* Set DMA mode register to single transfers, incrementing address,
+     *  no auto init, reads
+     */
+    set_dma_mode(dmachan, DMA_MODE_WRITE);
+    clear_dma_ff(dmachan);
+    set_dma_addr(dmachan, dma_abs);
+    /* output byte count */
+    set_dma_count(dmachan, length);
+
+    restore_flags(flags);
+}
+
+static void tdelay(struct pi_local *lp, int time)
+{
+    int port;
+    unsigned int t1;
+    unsigned char sc;
+
+    if (lp->base & 2) {		/* If A channel */
+	sc = SC1;
+	t1 = time;
+	port = lp->cardbase + TMR1;
+    } else {
+	sc = SC2;
+	t1 = 10 * time;		/* 10s of milliseconds for the B channel */
+	port = lp->cardbase + TMR2;
+	wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+    }
+
+    /* Setup timer sc */
+    outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD);
+
+    /* times 2 to make millisecs */
+    outb_p((t1 << 1) & 0xFF, port);
+    outb_p((t1 >> 7) & 0xFF, port);
+
+    /* Enable correct int for timeout */
+    wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE);
+    wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);
+}
+
+static void a_txint(struct pi_local *lp)
+{
+    int cmd;
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+    cmd = CTL + lp->base;
+
+    switch (lp->tstate) {
+    case IDLE:
+	/* Transmitter idle. Find a frame for transmission */
+	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+	    rts(lp, OFF);
+	    restore_flags(flags);
+	    return;
+	}
+	/* If a buffer to send, we drop thru here */
+    case DEFER:
+	/* we may have deferred prev xmit attempt */
+	/* Check DCD - debounce it
+         * See Intel Microcommunications Handbook, p2-308
+         */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, 100);
+	    /* defer until DCD transition or timeout */
+	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+	    restore_flags(flags);
+	    return;
+	}
+	if (random() > lp->persist) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, lp->slotime);
+	    restore_flags(flags);
+	    return;
+	}
+	/* Assert RTS early minimize collision window */
+	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+	rts(lp, ON);		/* Transmitter on */
+	lp->tstate = ST_TXDELAY;
+	tdelay(lp, lp->txdelay);
+	restore_flags(flags);
+	return;
+    default:
+	break;
+    }				/* end switch(lp->state) */
+
+    restore_flags(flags);
+}				/*a_txint */
+
+static void a_exint(struct pi_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    char st;
+    int length;
+
+    save_flags(flags);
+    cli();			/* disable interrupts */
+
+    st = rdscc(lp->cardbase, lp->base + CTL, R0);	/* Fetch status */
+
+    /* reset external status latch */
+    wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT);
+    cmd = lp->base + CTL;
+
+    if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) {
+	setup_rx_dma(lp);
+	lp->rstate = ACTIVE;
+    }
+    switch (lp->tstate) {
+    case ACTIVE:
+	kfree_skb(lp->sndbuf, FREE_WRITE);
+	lp->sndbuf = NULL;
+	lp->tstate = FLAGOUT;
+	tdelay(lp, lp->squeldelay);
+	break;
+    case FLAGOUT:
+	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+	    /* Nothing to send - return to receive mode */
+	    lp->tstate = IDLE;
+	    rts(lp, OFF);
+	    restore_flags(flags);
+	    return;
+	}
+	/* NOTE - fall through if more to send */
+    case ST_TXDELAY:
+	/* Disable DMA chan */
+	disable_dma(lp->dmachan);
+
+	/* Set up for TX dma */
+	wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+
+
+	/* Get all chars */
+	/* Strip KISS control byte */
+	length = lp->sndbuf->len - 1;
+	memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+
+
+	/* Setup DMA controller for tx */
+	setup_tx_dma(lp, length);
+
+	/* select transmit interrupts to enable */
+	/* Allow DMA on chan */
+	enable_dma(lp->dmachan);
+
+	/* reset CRC, Txint pend*/
+	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+
+	/* allow Underrun int only */
+	wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+	/* Enable TX DMA */
+	wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+
+	/* Send CRC on underrun */
+	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+
+	/* packet going out now */
+	lp->tstate = ACTIVE;
+	break;
+    case DEFER:
+	/* we have deferred prev xmit attempt
+         * See Intel Microcommunications Handbook, p2-308
+         */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, 100);
+	    /* Defer until dcd transition or 100mS timeout */
+	    wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE);
+	    restore_flags(flags);
+	    return;
+	}
+	if (random() > lp->persist) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, lp->slotime);
+	    restore_flags(flags);
+	    return;
+	}
+	/* Assert RTS early minimize collision window */
+	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+	rts(lp, ON);		/* Transmitter on */
+	lp->tstate = ST_TXDELAY;
+	tdelay(lp, lp->txdelay);
+	restore_flags(flags);
+	return;
+    }				/* switch(lp->tstate) */
+
+    restore_flags(flags);
+}				/* a_exint() */
+
+/* Receive interrupt handler for the A channel
+ */
+static void a_rxint(struct device *dev, struct pi_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    int bytecount;
+    char rse;
+    struct sk_buff *skb;
+    int sksize, pkt_len;
+    struct mbuf *cur_buf;
+    unsigned char *cfix;
+
+    save_flags(flags);
+    cli();			/* disable interrupts */
+    cmd = lp->base + CTL;
+
+    rse = rdscc(lp->cardbase, cmd, R1);	/* Get special condition bits from R1 */
+    if (rse & Rx_OVR)
+	lp->rstate = RXERROR;
+
+    if (rse & END_FR) {
+	/* If end of frame */
+	/* figure length of frame from 8237 */
+	clear_dma_ff(lp->dmachan);
+	bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+
+	if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) {
+	    if ((bytecount >= 10) && (rse & CRC_ERR)) {
+		lp->stats.rx_crc_errors++;
+	    }
+	    if (lp->rstate == RXERROR) {
+		lp->stats.rx_errors++;
+		lp->stats.rx_over_errors++;
+	    }
+	    /* Reset buffer pointers */
+	    lp->rstate = ACTIVE;
+	    setup_rx_dma(lp);
+	} else {
+	    /* Here we have a valid frame */
+	    /* Toss 2 crc bytes , add one for KISS */
+	    pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1;
+
+	    /* Get buffer for next frame */
+	    cur_buf = lp->rcvbuf;
+	    switchbuffers(lp);
+	    setup_rx_dma(lp);
+
+
+	    /* Malloc up new buffer. */
+	    sksize = pkt_len;
+
+	    skb = dev_alloc_skb(sksize);
+	    if (skb == NULL) {
+		printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		lp->stats.rx_dropped++;
+		restore_flags(flags);
+		return;
+	    }
+	    skb->dev = dev;
+
+	    /* KISS kludge -  prefix with a 0 byte */
+	    cfix=skb_put(skb,pkt_len);
+	    *cfix++=0;
+	    /* 'skb->data' points to the start of sk_buff data area. */
+	    memcpy(cfix, (char *) cur_buf->data,
+		   pkt_len - 1);
+	    skb->protocol=htons(ETH_P_AX25);
+	    skb->mac.raw=skb->data;
+	    netif_rx(skb);
+	    lp->stats.rx_packets++;
+	}			/* end good frame */
+    }				/* end EOF check */
+    wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES);	/* error reset */
+    restore_flags(flags);
+}
+
+static void b_rxint(struct device *dev, struct pi_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    char rse;
+    struct sk_buff *skb;
+    int sksize;
+    int pkt_len;
+    unsigned char *cfix;
+
+    save_flags(flags);
+    cli();			/* disable interrupts */
+    cmd = CTL + lp->base;
+
+    rse = rdscc(lp->cardbase, cmd, R1);	/* get status byte from R1 */
+
+    if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) {
+	/* there is a char to be stored
+         * read special condition bits before reading the data char
+         */
+	if (rse & Rx_OVR) {
+	    /* Rx overrun - toss buffer */
+	    /* reset buffer pointers */
+	    lp->rcp = lp->rcvbuf->data;
+	    lp->rcvbuf->cnt = 0;
+
+	    lp->rstate = RXERROR;	/* set error flag */
+	    lp->stats.rx_errors++;
+	    lp->stats.rx_over_errors++;
+	} else if (lp->rcvbuf->cnt >= lp->bufsiz) {
+	    /* Too large -- toss buffer */
+	    /* reset buffer pointers */
+	    lp->rcp = lp->rcvbuf->data;
+	    lp->rcvbuf->cnt = 0;
+	    lp->rstate = TOOBIG;/* when set, chars are not stored */
+	}
+	/* ok, we can store the received character now */
+	if (lp->rstate == ACTIVE) {	/* If no errors... */
+	    *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);	/* char to rcv buff */
+	    lp->rcvbuf->cnt++;	/* bump count */
+	} else {
+	    /* got to empty FIFO */
+	    (void) rdscc(lp->cardbase, cmd, R8);
+	    wrtscc(lp->cardbase, cmd, R0, ERR_RES);	/* reset err latch */
+	    lp->rstate = ACTIVE;
+	}
+    }
+    if (rse & END_FR) {
+	/* END OF FRAME -- Make sure Rx was active */
+	if (lp->rcvbuf->cnt > 0) {
+	    if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) {
+		if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) {
+		    lp->stats.rx_crc_errors++;
+		}
+		lp->rcp = lp->rcvbuf->data;
+		lp->rcvbuf->cnt = 0;
+	    } else {
+		/* Here we have a valid frame */
+		pkt_len = lp->rcvbuf->cnt -= 2;	/* Toss 2 crc bytes */
+		pkt_len += 1;	/* Make room for KISS control byte */
+
+		/* Malloc up new buffer. */
+		sksize = pkt_len;
+		skb = dev_alloc_skb(sksize);
+		if (skb == NULL) {
+		    printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
+		    lp->stats.rx_dropped++;
+		    restore_flags(flags);
+		    return;
+		}
+		skb->dev = dev;
+
+		/* KISS kludge -  prefix with a 0 byte */
+		cfix=skb_put(skb,pkt_len);
+		*cfix++=0;
+		/* 'skb->data' points to the start of sk_buff data area. */
+		memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
+		skb->protocol=ntohs(ETH_P_AX25);
+		skb->mac.raw=skb->data;
+		netif_rx(skb);
+		lp->stats.rx_packets++;
+		/* packet queued - initialize buffer for next frame */
+		lp->rcp = lp->rcvbuf->data;
+		lp->rcvbuf->cnt = 0;
+
+	    }			/* end good frame queued */
+	}			/* end check for active receive upon EOF */
+	lp->rstate = ACTIVE;	/* and clear error status */
+    }				/* end EOF check */
+    restore_flags(flags);
+}
+
+
+static void b_txint(struct pi_local *lp)
+{
+    unsigned long flags;
+    int cmd;
+    unsigned char c;
+
+    save_flags(flags);
+    cli();
+    cmd = CTL + lp->base;
+
+    switch (lp->tstate) {
+    case CRCOUT:
+	lp->tstate = FLAGOUT;
+	tdelay(lp, lp->squeldelay);
+	restore_flags(flags);
+	return;
+    case IDLE:
+	/* Transmitter idle. Find a frame for transmission */
+	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+	    /* Nothing to send - return to receive mode
+             * Tx OFF now - flag should have gone
+             */
+	    rts(lp, OFF);
+
+	    restore_flags(flags);
+	    return;
+	}
+	lp->txptr = lp->sndbuf->data;
+	lp->txptr++;		/* Ignore KISS control byte */
+	lp->txcnt = (int) lp->sndbuf->len - 1;
+	/* If a buffer to send, we drop thru here */
+    case DEFER:		/* we may have deferred prev xmit attempt */
+	/* Check DCD - debounce it */
+	/* See Intel Microcommunications Handbook, p2-308 */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, 100);
+	    /* defer until DCD transition or timeout */
+	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+	    restore_flags(flags);
+	    return;
+	}
+	if (random() > lp->persist) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, lp->slotime);
+	    restore_flags(flags);
+	    return;
+	}
+	rts(lp, ON);		/* Transmitter on */
+	lp->tstate = ST_TXDELAY;
+	tdelay(lp, lp->txdelay);
+	restore_flags(flags);
+	return;
+
+    case ACTIVE:
+	/* Here we are actively sending a frame */
+	if (lp->txcnt--) {
+	    c = *lp->txptr++;
+	    /* next char is gone */
+	    wrtscc(lp->cardbase, cmd, R8, c);
+	    /* stuffing a char satisfies Interrupt condition */
+	} else {
+	    /* No more to send */
+	    kfree_skb(lp->sndbuf, FREE_WRITE);
+	    lp->sndbuf = NULL;
+	    if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) {
+		/* Did we underrun? */
+		/* unexpected underrun */
+		lp->stats.tx_errors++;
+		lp->stats.tx_fifo_errors++;
+		wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+		lp->tstate = FLAGOUT;
+		tdelay(lp, lp->squeldelay);
+		restore_flags(flags);
+		return;
+	    }
+	    lp->tstate = UNDERRUN;	/* Now we expect to underrun */
+	    /* Send flags on underrun */
+	    if (lp->speed) {	/* If internally clocked */
+		wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+	    } else {
+		wrtscc(lp->cardbase, cmd, R10, CRCPS);
+	    }
+	    wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);	/* reset Tx Int Pend */
+	}
+	restore_flags(flags);
+	return;			/* back to wait for interrupt */
+    }				/* end switch */
+    restore_flags(flags);
+}
+
+/* Pi SIO External/Status interrupts (for the B channel)
+ * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
+ * Receiver automatically goes to Hunt on an abort.
+ *
+ * If the Tx Underrun interrupt hits, change state and
+ * issue a reset command for it, and return.
+ */
+static void b_exint(struct pi_local *lp)
+{
+    unsigned long flags;
+    char st;
+    int cmd;
+    char c;
+
+    cmd = CTL + lp->base;
+    save_flags(flags);
+    cli();			/* disable interrupts */
+    st = rdscc(lp->cardbase, cmd, R0);	/* Fetch status */
+    /* reset external status latch */
+    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+
+    switch (lp->tstate) {
+    case ACTIVE:		/* Unexpected underrun */
+	kfree_skb(lp->sndbuf, FREE_WRITE);
+	lp->sndbuf = NULL;
+	wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+	lp->tstate = FLAGOUT;
+	lp->stats.tx_errors++;
+	lp->stats.tx_fifo_errors++;
+	tdelay(lp, lp->squeldelay);
+	restore_flags(flags);
+	return;
+    case UNDERRUN:
+	lp->tstate = CRCOUT;
+	restore_flags(flags);
+	return;
+    case FLAGOUT:
+	/* Find a frame for transmission */
+	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
+	    /* Nothing to send - return to receive mode
+             * Tx OFF now - flag should have gone
+             */
+	    rts(lp, OFF);
+	    lp->tstate = IDLE;
+	    restore_flags(flags);
+	    return;
+	}
+	lp->txptr = lp->sndbuf->data;
+	lp->txptr++;		/* Ignore KISS control byte */
+	lp->txcnt = (int) lp->sndbuf->len - 1;
+	/* Get first char to send */
+	lp->txcnt--;
+	c = *lp->txptr++;
+	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);	/* reset for next frame */
+
+	/* Send abort on underrun */
+	if (lp->speed) {	/* If internally clocked */
+	    wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+	} else {
+	    wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+	}
+
+	wrtscc(lp->cardbase, cmd, R8, c);	/* First char out now */
+	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);	/* Reset end of message latch */
+
+#ifdef STUFF2
+        /* stuff an extra one if we can */
+	if (lp->txcnt) {
+	    lp->txcnt--;
+	    c = *lp->txptr++;
+	    /* Wait for tx buffer empty */
+	    while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+		;
+	    wrtscc(lp->cardbase, cmd, R8, c);
+	}
+#endif
+
+	/* select transmit interrupts to enable */
+
+	wrtscc(lp->cardbase, cmd, R15, TxUIE);	/* allow Underrun int only */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);	/* Tx/Ext ints */
+
+	lp->tstate = ACTIVE;	/* char going out now */
+	restore_flags(flags);
+	return;
+
+    case DEFER:
+	/* Check DCD - debounce it
+         * See Intel Microcommunications Handbook, p2-308
+         */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, 100);
+	    /* defer until DCD transition or timeout */
+	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
+	    restore_flags(flags);
+	    return;
+	}
+	if (random() > lp->persist) {
+	    lp->tstate = DEFER;
+	    tdelay(lp, lp->slotime);
+	    restore_flags(flags);
+	    return;
+	}
+	rts(lp, ON);		/* Transmitter on */
+	lp->tstate = ST_TXDELAY;
+	tdelay(lp, lp->txdelay);
+	restore_flags(flags);
+	return;
+
+    case ST_TXDELAY:
+
+	/* Get first char to send */
+	lp->txcnt--;
+	c = *lp->txptr++;
+	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);	/* reset for next frame */
+
+	/* Send abort on underrun */
+	if (lp->speed) {	/* If internally clocked */
+	    wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+	} else {
+	    wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
+	}
+
+	wrtscc(lp->cardbase, cmd, R8, c);	/* First char out now */
+	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);	/* Reset end of message latch */
+
+#ifdef STUFF2
+        /* stuff an extra one if we can */
+	if (lp->txcnt) {
+	    lp->txcnt--;
+	    c = *lp->txptr++;
+	    /* Wait for tx buffer empty */
+	    while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
+		;
+	    wrtscc(lp->cardbase, cmd, R8, c);
+	}
+#endif
+
+	/* select transmit interrupts to enable */
+
+	wrtscc(lp->cardbase, cmd, R15, TxUIE);	/* allow Underrun int only */
+	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	/* Tx/Extern ints on */
+	wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+
+	lp->tstate = ACTIVE;	/* char going out now */
+	restore_flags(flags);
+	return;
+    }
+
+    /* Receive Mode only
+     * This triggers when hunt mode is entered, & since an ABORT
+     * automatically enters hunt mode, we use that to clean up
+     * any waiting garbage
+     */
+    if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) {
+	(void) rdscc(lp->cardbase, cmd, R8);
+	(void) rdscc(lp->cardbase, cmd, R8);
+	(void) rdscc(lp->cardbase, cmd, R8);
+	lp->rcp = lp->rcvbuf->data;
+	lp->rcvbuf->cnt = 0;	/* rewind on DCD transition */
+    }
+    restore_flags(flags);
+}
+
+/* Probe for a PI card. */
+/* This routine also initializes the timer chip */
+
+__initfunc(static int hw_probe(int ioaddr))
+{
+    int time = 1000;		/* Number of milliseconds for test */
+    unsigned long start_time, end_time;
+
+    int base, tmr0, tmr1, tmrcmd;
+    int a = 1;
+    int b = 1;
+
+    base = ioaddr & 0x3f0;
+    tmr0 = TMR0 + base;
+    tmr1 = TMR1 + base;
+    tmrcmd = TMRCMD + base;
+
+    /* Set up counter chip timer 0 for 500 uS period square wave */
+    /* assuming a 3.68 mhz clock for now */
+    outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+    outb_p(922 & 0xFF, tmr0);
+    outb_p(922 >> 8, tmr0);
+
+    /* Setup timer control word for timer 1*/
+    outb_p(SC1 | LSB_MSB | MODE0, tmrcmd);
+    outb_p((time << 1) & 0xFF, tmr1);
+    outb_p((time >> 7) & 0XFF, tmr1);
+
+    /* wait until counter reg is loaded */
+    do {
+	/* Latch count for reading */
+	outb_p(SC1, tmrcmd);
+	a = inb_p(tmr1);
+	b = inb_p(tmr1);
+    } while (b == 0);
+    start_time = jiffies;
+    while (b != 0) {
+	/* Latch count for reading */
+	outb_p(SC1, tmrcmd);
+	a = inb_p(tmr1);
+	b = inb_p(tmr1);
+	end_time = jiffies;
+	/* Don't wait forever - there may be no card here */
+	if ((end_time - start_time) > 200)
+	    return 0;		/* No card found */
+    }
+    end_time = jiffies;
+    /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */
+    if ((end_time - start_time) > 65) {
+	return (1);		/* PI card found */
+    } else {
+	/* Faster crystal - tmr0 needs adjusting */
+	/* Set up counter chip */
+	/* 500 uS square wave */
+	outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
+	outb_p(1844 & 0xFF, tmr0);
+	outb_p(1844 >> 8, tmr0);
+	return (2);		/* PI2 card found */
+    }
+}
+
+static void rts(struct pi_local *lp, int x)
+{
+    int tc;
+    long br;
+    int cmd;
+    int dummy;
+
+    /* assumes interrupts are off */
+    cmd = CTL + lp->base;
+
+    /* Reprogram BRG and turn on transmitter to send flags */
+    if (x == ON) {		/* Turn Tx ON and Receive OFF */
+	/* Exints off first to avoid abort int */
+	wrtscc(lp->cardbase, cmd, R15, 0);
+	wrtscc(lp->cardbase, cmd, R3, Rx8);	/* Rx off */
+	lp->rstate = IDLE;
+	if (cmd & 2) {		/* if channel a */
+	    /* Set up for TX dma */
+	    wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+	} else {
+	    wrtscc(lp->cardbase, cmd, R1, 0);	/* No interrupts */
+	}
+
+	if (!lp->clockmode) {
+	    if (lp->speed) {	/* if internally clocked */
+		br = lp->speed;	/* get desired speed */
+		tc = (lp->xtal / br) - 2;	/* calc 1X BRG divisor */
+		wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
+		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
+	    }
+	}
+	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+	/* Transmitter now on */
+    } else {			/* Tx OFF and Rx ON */
+	lp->tstate = IDLE;
+	wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);	/*  TX off */
+
+	if (!lp->clockmode) {
+	    if (lp->speed) {	/* if internally clocked */
+		/* Reprogram BRG for 32x clock for receive DPLL */
+		/* BRG off, keep Pclk source */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC);
+		br = lp->speed;	/* get desired speed */
+		/* calc 32X BRG divisor */
+		tc = ((lp->xtal / 32) / br) - 2;
+		wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
+		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
+		/* SEARCH mode, BRG source */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+		/* Enable the BRG */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+	    }
+	}
+	/* Flush rx fifo */
+	wrtscc(lp->cardbase, cmd, R3, Rx8);	/* Make sure rx is off */
+	wrtscc(lp->cardbase, cmd, R0, ERR_RES);	/* reset err latch */
+	dummy = rdscc(lp->cardbase, cmd, R1);	/* get status byte from R1 */
+	(void) rdscc(lp->cardbase, cmd, R8);
+	(void) rdscc(lp->cardbase, cmd, R8);
+
+	(void) rdscc(lp->cardbase, cmd, R8);
+
+	/* Now, turn on the receiver and hunt for a flag */
+	wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8);
+	lp->rstate = ACTIVE;	/* Normal state */
+
+	if (cmd & 2) {		/* if channel a */
+	    setup_rx_dma(lp);
+	} else {
+	    /* reset buffer pointers */
+	    lp->rcp = lp->rcvbuf->data;
+	    lp->rcvbuf->cnt = 0;
+	    wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+	}
+	wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* allow ABORT int */
+    }
+}
+
+static void scc_init(struct device *dev)
+{
+    unsigned long flags;
+    struct pi_local *lp = (struct pi_local *) dev->priv;
+
+    int tc;
+    long br;
+    register int cmd;
+
+    /* Initialize 8530 channel for SDLC operation */
+
+    cmd = CTL + lp->base;
+    save_flags(flags);
+    cli();
+
+    switch (cmd & CHANA) {
+    case CHANA:
+	wrtscc(lp->cardbase, cmd, R9, CHRA);	/* Reset channel A */
+	wrtscc(lp->cardbase, cmd, R2, 0xff);	/* Initialize interrupt vector */
+	break;
+    default:
+	wrtscc(lp->cardbase, cmd, R9, CHRB);	/* Reset channel B */
+	break;
+    }
+
+    /* Deselect all Rx and Tx interrupts */
+    wrtscc(lp->cardbase, cmd, R1, 0);
+
+    /* Turn off external interrupts (like CTS/CD) */
+    wrtscc(lp->cardbase, cmd, R15, 0);
+
+    /* X1 clock, SDLC mode */
+    wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+
+    /* Tx/Rx parameters */
+    if (lp->speed) {		/* Use internal clocking */
+	wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+	if (!lp->clockmode)
+	    /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */
+	    wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+	else
+	    /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */
+	    wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI);
+    } else {			/* Use external clocking */
+	wrtscc(lp->cardbase, cmd, R10, CRCPS);
+	/* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */
+	wrtscc(lp->cardbase, cmd, R11, TCTRxCP);
+    }
+
+    /* Null out SDLC start address */
+    wrtscc(lp->cardbase, cmd, R6, 0);
+
+    /* SDLC flag */
+    wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+    /* Set up the Transmitter but don't enable it
+     *  DTR, 8 bit TX chars only
+     */
+    wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+    /* Receiver initial setup */
+    wrtscc(lp->cardbase, cmd, R3, Rx8);	/* 8 bits/char */
+
+    /* Setting up BRG now - turn it off first */
+    wrtscc(lp->cardbase, cmd, R14, BRSRC);	/* BRG off, keep Pclk source */
+
+    /* set the 32x time constant for the BRG in Receive mode */
+
+    if (lp->speed) {
+	br = lp->speed;		/* get desired speed */
+	tc = ((lp->xtal / 32) / br) - 2;	/* calc 32X BRG divisor */
+    } else {
+	tc = 14;
+    }
+
+    wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
+    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
+
+    /* Following subroutine sets up and ENABLES the receiver */
+    rts(lp, OFF);		/* TX OFF and RX ON */
+
+    if (lp->speed) {
+	/* DPLL frm BRG, BRG src PCLK */
+	wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+    } else {
+	/* DPLL frm rtxc,BRG src PCLK */
+	wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);
+    }
+    wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);	/* SEARCH mode, keep BRG src */
+    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);	/* Enable the BRG */
+
+    if (!(cmd & 2))		/* if channel b */
+	wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+    wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* ABORT int */
+
+    /* Now, turn on the receiver and hunt for a flag */
+    wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8);
+
+    restore_flags(flags);
+}
+
+static void chipset_init(struct device *dev)
+{
+    int cardbase;
+    unsigned long flags;
+
+    cardbase = dev->base_addr & 0x3f0;
+
+    save_flags(flags);
+    cli();
+    wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES);	/* Hardware reset */
+    /* Disable interrupts with master interrupt ctrl reg */
+    wrtscc(cardbase, dev->base_addr + CTL, R9, 0);
+    restore_flags(flags);
+
+}
+
+
+__initfunc(int pi_init(void))
+{
+    int *port;
+    int ioaddr = 0;
+    int card_type = 0;
+    int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
+
+    printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n");
+
+    /* Only one card supported for now */
+    for (port = &ports[0]; *port && !card_type; port++) {
+	ioaddr = *port;
+
+	if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
+	    printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr);
+	    card_type = hw_probe(ioaddr);
+	}
+    }
+
+    switch (card_type) {
+    case 1:
+	printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr);
+	break;
+    case 2:
+	printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr);
+	break;
+    default:
+	printk(KERN_ERR "PI: ERROR: No card found\n");
+	return -EIO;
+    }
+
+    /* Link a couple of device structures into the chain */
+    /* For the A port */
+    /* Allocate space for 4 buffers even though we only need 3,
+       because one of them may cross a DMA page boundary and
+       be rejected by get_dma_buffer().
+    */
+    register_netdev(&pi0a);
+
+    pi0a.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    pi0a.dma = PI_DMA;
+    pi0a.base_addr = ioaddr + 2;
+    pi0a.irq = 0;
+
+    /* And the B port */
+    register_netdev(&pi0b);
+    pi0b.base_addr = ioaddr;
+    pi0b.irq = 0;
+
+    pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    /* Now initialize them */
+    pi_probe(&pi0a, card_type);
+    pi_probe(&pi0b, card_type);
+
+    pi0b.irq = pi0a.irq;	/* IRQ is shared */
+
+    return 0;
+}
+
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize)
+{
+    if (((addr & 0xffff) + dev_buffsize) <= 0x10000)
+	return 1;
+    else
+	return 0;
+}
+
+static int pi_set_mac_address(struct device *dev, void *addr)
+{
+    struct sockaddr *sa = (struct sockaddr *)addr;
+    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);	/* addr is an AX.25 shifted ASCII */
+    return 0;						/* mac address */
+}
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char *
+get_dma_buffer(unsigned long *mem_ptr)
+{
+    char *ret;
+
+    ret = (char *)*mem_ptr;
+
+    if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){
+	*mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+	ret = (char *)*mem_ptr;
+    }
+    *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+    return (ret);
+}
+
+static int pi_probe(struct device *dev, int card_type)
+{
+    short ioaddr;
+    struct pi_local *lp;
+    unsigned long flags;
+    unsigned long mem_ptr;
+
+    ioaddr = dev->base_addr;
+
+    /* Initialize the device structure. */
+    /* Must be done before chipset_init */
+    /* Make certain the data structures used by the PI2 are aligned. */
+    dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+    lp = (struct pi_local *) dev->priv;
+
+    memset(dev->priv, 0, sizeof(struct pi_local));
+
+    /* Allocate some buffers which do not cross DMA page boundaries */
+    mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local);
+    lp->txdmabuf = get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+    /* Initialize rx buffer */
+    lp->rcvbuf = lp->rxdmabuf1;
+    lp->rcp = lp->rcvbuf->data;
+    lp->rcvbuf->cnt = 0;
+
+    /* Initialize the transmit queue head structure */
+    skb_queue_head_init(&lp->sndq);
+
+    /* These need to be initialized before scc_init is called. */
+    if (card_type == 1)
+	lp->xtal = (unsigned long) SINGLE / 2;
+    else
+	lp->xtal = (unsigned long) DOUBLE / 2;
+    lp->base = dev->base_addr;
+    lp->cardbase = dev->base_addr & 0x3f0;
+    if (dev->base_addr & CHANA) {
+	lp->speed = DEF_A_SPEED;
+	/* default channel access Params */
+	lp->txdelay = DEF_A_TXDELAY;
+	lp->persist = DEF_A_PERSIST;
+	lp->slotime = DEF_A_SLOTIME;
+	lp->squeldelay = DEF_A_SQUELDELAY;
+	lp->clockmode = DEF_A_CLOCKMODE;
+
+    } else {
+	lp->speed = DEF_B_SPEED;
+	/* default channel access Params */
+	lp->txdelay = DEF_B_TXDELAY;
+	lp->persist = DEF_B_PERSIST;
+	lp->slotime = DEF_B_SLOTIME;
+	lp->squeldelay = DEF_B_SQUELDELAY;
+	lp->clockmode = DEF_B_CLOCKMODE;
+    }
+    lp->bufsiz = DMA_BUFF_SIZE;
+    lp->tstate = IDLE;
+
+    chipset_init(dev);
+
+    if (dev->base_addr & CHANA) {	/* Do these things only for the A port */
+	/* Note that a single IRQ services 2 devices (A and B channels) */
+
+	lp->dmachan = dev->dma;
+	if (lp->dmachan < 1 || lp->dmachan > 3)
+	    printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan);
+
+	/* chipset_init() was already called */
+
+	if (dev->irq < 2) {
+	    autoirq_setup(0);
+	    save_flags(flags);
+	    cli();
+	    wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB);
+	    /* enable PI card interrupts */
+	    wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+	    restore_flags(flags);
+	    /* request a timer interrupt for 1 mS hence */
+	    tdelay(lp, 1);
+	    /* 20 "jiffies" should be plenty of time... */
+	    dev->irq = autoirq_report(20);
+	    if (!dev->irq) {
+		printk(KERN_ERR "PI: Failed to detect IRQ line.\n");
+	    }
+	    save_flags(flags);
+	    cli();
+	    wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES);	/* Hardware reset */
+	    /* Disable interrupts with master interrupt ctrl reg */
+	    wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0);
+	    restore_flags(flags);
+	}
+
+	printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n",
+	       dev->irq, dev->dma);
+
+	/* This board has jumpered interrupts. Snarf the interrupt vector
+		   now.  There is no point in waiting since no other device can use
+		   the interrupt, and this marks the 'irqaction' as busy. */
+	{
+	    int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev);
+	    if (irqval) {
+		printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
+		       dev->irq, irqval);
+		return EAGAIN;
+	    }
+	}
+
+	/* Grab the region */
+	request_region(ioaddr & 0x3f0, PI_TOTAL_SIZE, "pi2" );
+
+
+    }				/* Only for A port */
+    dev->open = pi_open;
+    dev->stop = pi_close;
+    dev->do_ioctl = pi_ioctl;
+    dev->hard_start_xmit = pi_send_packet;
+    dev->get_stats = pi_get_stats;
+
+    /* Fill in the fields of the device structure */
+
+    dev_init_buffers(dev);
+    
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+    dev->hard_header    = ax25_encapsulate;
+    dev->rebuild_header = ax25_rebuild_header;
+#endif
+
+    dev->set_mac_address = pi_set_mac_address;
+
+    dev->type = ARPHRD_AX25;			/* AF_AX25 device */
+    dev->hard_header_len = 73;			/* We do digipeaters now */
+    dev->mtu = 1500;				/* eth_mtu is the default */
+    dev->addr_len = 7;				/* sizeof an ax.25 address */
+    memcpy(dev->broadcast, ax25_bcast, 7);
+    memcpy(dev->dev_addr, ax25_test, 7);
+
+    /* New-style flags. */
+    dev->flags = 0;
+    return 0;
+}
+
+/* Open/initialize the board.  This is called (in the current kernel)
+   sometime after booting when the 'ifconfig' program is run.
+
+   This routine should set everything up anew at each open, even
+   registers that "should" only need to be set once at boot, so that
+   there is non-reboot way to recover if something goes wrong.
+   */
+static int pi_open(struct device *dev)
+{
+    unsigned long flags;
+    static first_time = 1;
+
+    struct pi_local *lp = (struct pi_local *) dev->priv;
+
+    if (dev->base_addr & 2) {	/* if A channel */
+	if (first_time) {
+	    if (request_dma(dev->dma,"pi2")) {
+		free_irq(dev->irq, dev);
+		return -EAGAIN;
+	    }
+	}
+	/* Reset the hardware here. */
+	chipset_init(dev);
+    }
+    lp->tstate = IDLE;
+
+    if (dev->base_addr & 2) {	/* if A channel */
+	scc_init(dev);		/* Called once for each channel */
+	scc_init(dev->next);
+    }
+    /* master interrupt enable */
+    save_flags(flags);
+    cli();
+    wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
+    restore_flags(flags);
+
+    lp->open_time = jiffies;
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+    first_time = 0;
+
+    MOD_INC_USE_COUNT;
+
+    return 0;
+}
+
+static int pi_send_packet(struct sk_buff *skb, struct device *dev)
+{
+    struct pi_local *lp = (struct pi_local *) dev->priv;
+
+    hardware_send_packet(lp, skb);
+    dev->trans_start = jiffies;
+
+    return 0;
+}
+
+/* The typical workload of the driver:
+   Handle the network interface interrupts. */
+static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs)
+{
+/*    int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/
+    struct pi_local *lp;
+    int st;
+    unsigned long flags;
+
+/*    dev_b = dev_a->next;	 Relies on the order defined in Space.c */
+
+#if 0
+    if (dev_a == NULL) {
+	printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq);
+	return;
+    }
+#endif
+    /* Read interrupt status register (only valid from channel A)
+     * Process all pending interrupts in while loop
+     */
+    lp = (struct pi_local *) pi0a.priv;	/* Assume channel A */
+    while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) {
+	if (st & CHBTxIP) {
+	    /* Channel B Transmit Int Pending */
+	    lp = (struct pi_local *) pi0b.priv;
+	    b_txint(lp);
+	} else if (st & CHARxIP) {
+	    /* Channel A Rcv Interrupt Pending */
+	    lp = (struct pi_local *) pi0a.priv;
+	    a_rxint(&pi0a, lp);
+	} else if (st & CHATxIP) {
+	    /* Channel A Transmit Int Pending */
+	    lp = (struct pi_local *) pi0a.priv;
+	    a_txint(lp);
+	} else if (st & CHAEXT) {
+	    /* Channel A External Status Int */
+	    lp = (struct pi_local *) pi0a.priv;
+	    a_exint(lp);
+	} else if (st & CHBRxIP) {
+	    /* Channel B Rcv Interrupt Pending */
+	    lp = (struct pi_local *) pi0b.priv;
+	    b_rxint(&pi0b, lp);
+	} else if (st & CHBEXT) {
+	    /* Channel B External Status Int */
+	    lp = (struct pi_local *) pi0b.priv;
+	    b_exint(lp);
+	}
+	/* Reset highest interrupt under service */
+	save_flags(flags);
+	cli();
+	wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+	restore_flags(flags);
+    }				/* End of while loop on int processing */
+    return;
+}
+
+/* The inverse routine to pi_open(). */
+static int pi_close(struct device *dev)
+{
+    unsigned long flags;
+    struct pi_local *lp;
+    struct sk_buff *ptr;
+
+    save_flags(flags);
+    cli();
+
+    lp = (struct pi_local *) dev->priv;
+    ptr = NULL;
+
+    chipset_init(dev);		/* reset the scc */
+    disable_dma(lp->dmachan);
+
+    lp->open_time = 0;
+
+    dev->tbusy = 1;
+    dev->start = 0;
+
+    /* Free any buffers left in the hardware transmit queue */
+    while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+	kfree_skb(ptr, FREE_WRITE);
+
+    restore_flags(flags);
+
+    MOD_DEC_USE_COUNT;
+
+    return 0;
+}
+
+static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+    unsigned long flags;
+    struct pi_req rq;
+    struct pi_local *lp = (struct pi_local *) dev->priv;
+
+    int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req));
+    if (ret)
+	return ret;
+
+    if(cmd!=SIOCDEVPRIVATE)
+    	return -EINVAL;
+
+    copy_from_user(&rq, ifr->ifr_data, sizeof(struct pi_req));
+
+    switch (rq.cmd) {
+    case SIOCSPIPARAM:
+
+	if (!suser())
+	    return -EPERM;
+	save_flags(flags);
+	cli();
+	lp->txdelay = rq.txdelay;
+	lp->persist = rq.persist;
+	lp->slotime = rq.slotime;
+	lp->squeldelay = rq.squeldelay;
+	lp->clockmode = rq.clockmode;
+	lp->speed = rq.speed;
+	pi_open(&pi0a); /* both channels get reset %%% */
+	restore_flags(flags);
+	ret = 0;
+	break;
+
+    case SIOCSPIDMA:
+
+	if (!suser())
+	    return -EPERM;
+	ret = 0;
+	if (dev->base_addr & 2) {   /* if A channel */
+	   if (rq.dmachan < 1 || rq.dmachan > 3)
+		return -EINVAL;
+	   save_flags(flags);
+	   cli();
+	   pi_close(dev);
+	   free_dma(lp->dmachan);
+	   dev->dma = lp->dmachan = rq.dmachan;
+	   if (request_dma(lp->dmachan,"pi2"))
+		ret = -EAGAIN;
+	   pi_open(dev);
+	   restore_flags(flags);
+	}
+	break;
+
+    case SIOCSPIIRQ:
+	ret = -EINVAL;      /* add this later */
+	break;
+
+    case SIOCGPIPARAM:
+    case SIOCGPIDMA:
+    case SIOCGPIIRQ:
+
+	rq.speed = lp->speed;
+	rq.txdelay = lp->txdelay;
+	rq.persist = lp->persist;
+	rq.slotime = lp->slotime;
+	rq.squeldelay = lp->squeldelay;
+	rq.clockmode = lp->clockmode;
+	rq.dmachan = lp->dmachan;
+	rq.irq = dev->irq;
+	copy_to_user(ifr->ifr_data, &rq, sizeof(struct pi_req));
+	ret = 0;
+	break;
+
+    default:
+	ret = -EINVAL;
+    }
+    return ret;
+}
+
+/* Get the current statistics.	This may be called with the card open or
+   closed. */
+static struct net_device_stats *pi_get_stats(struct device *dev)
+{
+	struct pi_local *lp = (struct pi_local *) dev->priv;
+
+	return &lp->stats;
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("David Perry <dp@hydra.carleton.ca>");
+MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards");
+
+int init_module(void)
+{
+    return pi_init();
+}
+
+void cleanup_module(void)
+{
+    free_irq(pi0a.irq, &pi0a);	/* IRQs and IO Ports are shared */
+    release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
+
+    kfree(pi0a.priv);
+    pi0a.priv = NULL;
+    unregister_netdev(&pi0a);
+
+    kfree(pi0b.priv);
+    pi0b.priv = NULL;
+    unregister_netdev(&pi0b);
+}
+#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/pt.c linux/drivers/net/hamradio/pt.c
--- v2.1.70/linux/drivers/net/hamradio/pt.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/pt.c	Sat Nov 29 10:33:20 1997
@@ -0,0 +1,1778 @@
+#undef PT_DEBUG 1
+/*
+ * pt.c: Linux device driver for the Gracilis PackeTwin.
+ * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge MA 02139, USA.
+ *
+ * This driver is largely based upon the PI driver by David Perry.
+ *
+ * Revision History
+ * 23/02/95 cs  Started again on driver, last one scrapped
+ * 27/02/95 cs  Program works, we have chan A only.  Tx stays on
+ * 28/02/95 cs  Fix Tx problem (& TxUIE instead of | )
+ *		Fix Chan B Tx timer problem, used TMR2 instead of TMR1
+ * 03/03/95 cs  Painfully found out (after 3 days) SERIAL_CFG is write only
+ *              created image of it and DMA_CFG
+ * 21/06/95 cs  Upgraded to suit PI driver 0.8 ALPHA
+ * 22/08/95 cs	Changed it all around to make it like pi driver
+ * 23/08/95 cs  It now works, got caught again by TMR2 and we must have
+ *				auto-enables for daughter boards.
+ * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
+ * 26/11/95 cs  Fixed for 1.3.43, ala 29/10 for pi2.c by ac
+ * 21/12/95 cs  Got rid of those nasty warnings when compiling, for 1.3.48
+ * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and
+ *		pt_loopback functions - they were unused.
+ * 13/12/96 jsn Fixed to match Linux networking changes.
+ */
+
+/*
+ * default configuration of the PackeTwin,
+ * ie What Craig uses his PT for.
+ */
+#define PT_DMA 3
+
+#define DEF_A_SPEED	4800		/* 4800 baud */
+#define DEF_A_TXDELAY	350		/* 350 mS */
+#define DEF_A_PERSIST	64		/* 25% persistence */
+#define DEF_A_SLOTIME	10		/* 10 mS */
+#define DEF_A_SQUELDELAY 30		/* 30 mS */
+#define DEF_A_CLOCKMODE	0		/* Normal clock mode */
+#define DEF_A_NRZI		1		/* NRZI mode */
+
+#define DEF_B_SPEED	0		/* 0 means external clock */
+#define DEF_B_TXDELAY	250		/* 250 mS */
+#define DEF_B_PERSIST	64		/* 25% */
+#define DEF_B_SLOTIME	10		/* 10 mS */
+#define DEF_B_SQUELDELAY 30		/* 30 mS */
+#define DEF_B_CLOCKMODE 0 		/* Normal clock mode ?!? */
+#define DEF_B_NRZI		1		/* NRZI mode */
+
+
+#define	PARAM_TXDELAY	1
+#define	PARAM_PERSIST	2
+#define	PARAM_SLOTTIME	3
+#define	PARAM_FULLDUP	5
+#define	PARAM_HARDWARE	6
+#define	PARAM_RETURN	255
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/if_arp.h>
+#include <linux/pt.h>
+#include <linux/init.h>
+#include "z8530.h"
+#include <net/ax25.h>
+
+struct mbuf {
+    struct mbuf *next;
+    int cnt;
+    char data[0];
+};
+
+/*
+ * The actual PT devices we will use
+ */
+static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */
+static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
+
+/* Ok, they shouldn't be here, but both channels share them */
+/* The Images of the Serial and DMA config registers */
+static unsigned char pt_sercfg = 0;
+static unsigned char pt_dmacfg = 0;
+
+/* The number of IO ports used by the card */
+#define PT_TOTAL_SIZE   16
+
+/* Index to functions, as function prototypes. */
+
+static int pt_probe(struct device *dev);
+static int pt_open(struct device *dev);
+static int pt_send_packet(struct sk_buff *skb, struct device *dev);
+static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int pt_close(struct device *dev);
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static struct net_device_stats *pt_get_stats(struct device *dev);
+static void pt_rts(struct pt_local *lp, int x);
+static void pt_rxisr(struct device *dev);
+static void pt_txisr(struct pt_local *lp);
+static void pt_exisr(struct pt_local *lp);
+static void pt_tmrisr(struct pt_local *lp);
+static char *get_dma_buffer(unsigned long *mem_ptr);
+static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
+static int hw_probe(int ioaddr);
+static void tdelay(struct pt_local *lp, int time);
+static void chipset_init(struct device *dev);
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+
+
+static int ext2_secrm_seed = 152;
+
+static inline unsigned char random(void)
+{
+    return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1);
+}
+
+static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val)
+{
+    outb_p(sccreg, ctl);        /* Select register */
+    outb_p(val, ctl);           /* Output value */
+}
+
+static inline unsigned char rdscc(int cbase, int ctl, int sccreg)
+{
+    unsigned char retval;
+
+    outb_p(sccreg, ctl);        /* Select register */
+    retval = inb_p(ctl);
+    return retval;
+}
+
+static void switchbuffers(struct pt_local *lp)
+{
+    if (lp->rcvbuf == lp->rxdmabuf1)
+	lp->rcvbuf = lp->rxdmabuf2;
+    else
+	lp->rcvbuf = lp->rxdmabuf1;
+}
+
+static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb)
+{
+	char kickflag;
+	unsigned long flags;
+	char *ptr;
+	struct device *dev;
+
+	/* First, let's see if this packet is actually a KISS packet */
+	ptr = skb->data;
+	if (ptr[0] != 0 && skb->len >= 2)
+	{
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
+#endif
+		/* Kludge to get device */
+		if ((struct pt_local*)(&pt0b.priv) == lp)
+			dev = &pt0b;
+		else
+			dev = &pt0a;
+		switch(ptr[0])
+		{
+
+			case PARAM_TXDELAY:
+				/*TxDelay is in 10mS increments */
+				lp->txdelay = ptr[1] * 10;
+				break;
+			case PARAM_PERSIST:
+				lp->persist = ptr[1];
+				break;
+			case PARAM_SLOTTIME:
+				lp->slotime = ptr[1];
+				break;
+			case PARAM_FULLDUP:
+				/* Yeah right, you wish!  Fullduplex is a little while to
+				 * go folks, but this is how you fire it up
+				 */
+				break;
+			/* Perhaps we should have txtail here?? */
+		} /*switch */
+		return;
+	}
+
+	lp->stats.tx_packets++;
+	lp->stats.tx_bytes+=skb->len;
+	save_flags(flags);
+	cli();
+	kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
+	restore_flags(flags);
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
+#endif
+	skb_queue_tail(&lp->sndq, skb);
+	if (kickflag) 
+	{
+        /* Simulate interrupt to transmit */
+        	if (lp->dmachan)
+			pt_txisr(lp);
+		else 
+		{
+            		save_flags(flags);
+	           	cli();
+            		if (lp->tstate == IDLE)
+                		pt_txisr(lp);
+            		restore_flags(flags);
+		}
+	}
+} /* hardware_send_packet() */
+
+static void setup_rx_dma(struct pt_local *lp)
+{
+	unsigned long flags;
+	int cmd;
+	unsigned long dma_abs;
+	unsigned char dmachan;
+
+	save_flags(flags);
+	cli();
+
+	dma_abs = (unsigned long) (lp->rcvbuf->data);
+	dmachan = lp->dmachan;
+	cmd = lp->base + CTL;
+
+	if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+		panic("PI: RX buffer violates DMA boundary!");
+
+	/* Get ready for RX DMA */
+	wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+	disable_dma(dmachan);
+	clear_dma_ff(dmachan);
+
+	/*
+	 *	Set DMA mode register to single transfers, incrementing address,
+	 *	auto init, writes
+	 */
+
+	set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
+	set_dma_addr(dmachan, dma_abs);
+	set_dma_count(dmachan, lp->bufsiz);
+	enable_dma(dmachan);
+
+	/*
+	 *	If a packet is already coming in, this line is supposed to
+	 *	avoid receiving a partial packet.
+	 */
+
+	wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
+
+	/* Enable RX dma */
+	wrtscc(lp->cardbase, cmd, R1,
+		WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
+
+	restore_flags(flags);
+}
+
+static void setup_tx_dma(struct pt_local *lp, int length)
+{
+    unsigned long dma_abs;
+    unsigned long flags;
+    unsigned long dmachan;
+
+    save_flags(flags);
+    cli();
+
+    dmachan = lp->dmachan;
+    dma_abs = (unsigned long) (lp->txdmabuf);
+
+    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
+	panic("PT: TX buffer violates DMA boundary!");
+
+    disable_dma(dmachan);
+    /* Set DMA mode register to single transfers, incrementing address,
+     *  no auto init, reads
+     */
+    set_dma_mode(dmachan, DMA_MODE_WRITE);
+    clear_dma_ff(dmachan);
+    set_dma_addr(dmachan, dma_abs);
+    /* output byte count */
+    set_dma_count(dmachan, length);
+
+    restore_flags(flags);
+}
+
+/*
+ * This sets up all the registers in the SCC for the given channel
+ * based upon tsync_hwint()
+ */
+static void scc_init(struct device *dev)
+{
+	unsigned long flags;
+	struct pt_local *lp = (struct pt_local*) dev->priv;
+	register int cmd = lp->base + CTL;
+	int tc, br;
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA);
+#endif
+	save_flags(flags);
+	cli();
+
+	/* We may put something here to enable_escc */
+
+	if (cmd & CHANA)
+	{
+	        wrtscc(lp->cardbase, cmd, R9, CHRA);	/* Reset channel A */
+	        wrtscc(lp->cardbase, cmd, R2, 0xff);	/* Initialise interrupt vector */
+	}
+	else
+	    	wrtscc(lp->cardbase, cmd, R9, CHRB);	/* Reset channel B */
+
+	/* Deselect all Rx and Tx interrupts */
+	wrtscc(lp->cardbase, cmd, R1, 0);
+
+	/* Turn off external interrupts (like CTS/CD) */
+	wrtscc(lp->cardbase, cmd, R15, 0);
+
+	/* X1 clock, SDLC mode */
+	wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
+
+	/* Preset CRC and set mode */
+	if (lp->nrzi)
+	    	/* Preset Tx CRC, put into NRZI mode */
+		wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+	else
+		/* Preset Tx CRC, put into NRZ mode */
+		wrtscc(lp->cardbase, cmd, R10, CRCPS);
+
+	/* Tx/Rx parameters */
+	if (lp->speed)		/* Use internal clocking */
+	       /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */
+	       wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
+	else	/* Use external clocking */
+	{
+		/* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */
+		wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR);
+        	wrtscc(lp->cardbase,cmd, R14, 0);	/* wiz1 */
+	}
+
+	/* Null out SDLC start address */
+	wrtscc(lp->cardbase, cmd, R6, 0);
+
+	/* SDLC flag */
+	wrtscc(lp->cardbase, cmd, R7, FLAG);
+
+	/* Setup Tx but don't enable it */
+	wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+
+	/* Setup Rx */
+	wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+
+	/* Setup the BRG, turn it off first */
+	wrtscc(lp->cardbase, cmd, R14, BRSRC);
+
+	/* set the 32x time constant for the BRG in Rx mode */
+	if (lp->speed)
+	{
+		br = lp->speed;
+		tc = ((lp->xtal / 32) / (br * 2)) - 2;
+		wrtscc(lp->cardbase, cmd, R12, tc & 0xff);		/* lower byte */
+   		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);	/* upper byte */
+	}
+
+	/* Turn transmitter off, to setup stuff */
+   	pt_rts(lp, OFF);
+
+	/* External clocking */
+	if (lp->speed)
+	{
+		/* DPLL frm BRG, BRG src PCLK */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);	/* SEARCH mode, keep BRG src */
+		wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);	/* Enable the BRG */
+
+	    /* Turn off external clock port */
+		if (lp->base & CHANA)
+			outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+		else
+			outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
+	}
+	else
+	{
+		/* DPLL frm rtxc,BRG src PCLK */
+		/* Turn on external clock port */
+		if (lp->base & CHANA)
+			outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
+		else
+			outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
+	}
+
+	if (!lp->dmachan)
+		wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
+
+	wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* ABORT int */
+
+	/* Turn on the DTR to tell modem we're alive */
+	if (lp->base & CHANA)
+		outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) );
+	else
+	    	outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) );
+
+	/* Now, turn on the receiver and hunt for a flag */
+	wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 );
+
+	restore_flags(flags);
+
+} /* scc_init() */
+
+/* Resets the given channel and whole SCC if both channels off */
+static void chipset_init(struct device *dev)
+{
+
+	struct pt_local *lp = (struct pt_local*) dev->priv;
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
+	printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);
+#endif
+	/* Reset SCC if both channels are to be canned */
+	if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
+			(!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) )
+	{
+		wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES);
+        	/* Reset int and dma registers */
+	        outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
+	        outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
+#endif
+	}
+	/* Reset individual channel */
+    	if (lp->base & CHANA) {
+        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA);
+        	outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG);
+    	} else {
+        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB);
+			outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG);
+	}
+} /* chipset_init() */
+
+
+
+__initfunc(int pt_init(void))
+{
+    int *port;
+    int ioaddr = 0;
+    int card_type = 0;
+    int ports[] =
+    { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
+      0x2b0, 0x300, 0x330, 0x3f0,  0};
+
+    printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (csmall@small.dropbear.id.au)\n");
+
+    for (port = &ports[0]; *port && !card_type; port++) {
+        ioaddr = *port;
+
+        if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
+            printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr);
+            card_type = hw_probe(ioaddr);
+        }
+    }
+    if (card_type) {
+        printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr);
+    } else {
+        printk(KERN_ERR "PT: ERROR: No card found.\n");
+        return -EIO;
+    }
+
+    /*
+     * Link a couple of device structures into the chain
+     *
+     * For the A port
+     * Allocate space for 4 buffers even though we only need 3,
+     * because one of them may cross a DMA page boundary and
+     * be rejected by get_dma_buffer().
+     */
+    register_netdev(&pt0a);
+
+    pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    pt0a.dma = 0;	/* wizzer - no dma yet */
+    pt0a.base_addr = ioaddr + CHANA;
+    pt0a.irq = 0;
+
+    /* And B port */
+    register_netdev(&pt0b);
+
+    pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
+
+    pt0b.base_addr = ioaddr + CHANB;
+    pt0b.irq = 0;
+
+    /* Now initialise them */
+    pt_probe(&pt0a);
+    pt_probe(&pt0b);
+
+    pt0b.irq = pt0a.irq;	/* IRQ is shared */
+
+    return 0;
+} /* pt_init() */
+
+/*
+ * Probe for PT card.  Also initialises the timers
+ */
+__initfunc(static int hw_probe(int ioaddr))
+{
+    int time = 1000;		/* Number of milliseconds to test */
+    int a = 1;
+    int b = 1;
+    unsigned long start_time, end_time;
+
+    inb_p(ioaddr + TMR1CLR);
+    inb_p(ioaddr + TMR2CLR);
+
+    /* Timer counter channel 0, 1mS period */
+    outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD);
+    outb_p(0x00, ioaddr + TMR0);
+    outb_p(0x18, ioaddr + TMR0);
+
+    /* Setup timer control word for timer 1 */
+    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    outb_p((time << 1) & 0xff, ioaddr + TMR1);
+    outb_p((time >> 7) & 0xff, ioaddr + TMR1);
+
+    /* wait until counter reg is loaded */
+    do {
+        /* Latch count for reading */
+        outb_p(SC1, ioaddr + TMRCMD);
+        a = inb_p(ioaddr + TMR1);
+        b = inb_p(ioaddr + TMR1);
+    } while (b == 0);
+    start_time = jiffies;
+    while(b != 0)
+    {
+        /* Latch count for reading */
+        outb_p(SC1, ioaddr + TMRCMD);
+        a = inb_p(ioaddr + TMR1);
+        b = inb_p(ioaddr + TMR1);
+        end_time = jiffies;
+        /* Don't wait forever - there may be no card here */
+        if ((end_time - start_time) > 200)
+        {
+        	inb_p(ioaddr + TMR1CLR);
+            return 0;
+        }
+    }
+
+    /* Now fix the timers up for general operation */
+
+    /* Clear the timers */
+    inb_p(ioaddr + TMR1CLR);
+    inb_p(ioaddr + TMR2CLR);
+
+    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    inb_p(ioaddr + TMR1CLR);
+
+    outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD);
+    /* Should this be tmr1 or tmr2? wiz3*/
+    inb_p(ioaddr + TMR1CLR);
+
+    return 1;
+} /* hw_probe() */
+
+
+static void pt_rts(struct pt_local *lp, int x)
+{
+	int tc;
+	long br;
+	int cmd = lp->base + CTL;
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
+#endif
+	if (x == ON) {
+	    /* Ex ints off to avoid int */
+	    wrtscc(lp->cardbase, cmd, R15, 0);
+	    wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);	/* Rx off */
+	    lp->rstate = IDLE;
+
+	    if(lp->dmachan)
+	    {
+	        /* Setup for Tx DMA */
+	        wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+	    } else {
+	        /* No interrupts */
+	        wrtscc(lp->cardbase, cmd, R1, 0);
+	    }
+
+            if (!lp->clockmode)
+            {
+                if (lp->speed)
+                {
+                    br = lp->speed;
+                    tc = (lp->xtal / (br * 2)) - 2;
+                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+                }
+            }
+            /* Turn on Tx by raising RTS */
+            wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
+            /* Transmitter on now */
+        } else {		/* turning off Tx */
+            lp->tstate = IDLE;
+
+            /* Turn off Tx by dropping RTS */
+            wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
+            if (!lp->clockmode)
+            {
+                if (lp->speed)		/* internally clocked */
+                {
+                    /* Reprogram BRG from 32x clock for Rx DPLL */
+                    /* BRG off, keep PClk source */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC);
+                    br = lp->speed;
+                    tc = ((lp->xtal / 32) / (br * 2)) - 2;
+                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
+                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
+
+                    /* SEARCH mode, BRG source */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
+                    /* Enable the BRG */
+                    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
+                }
+            }
+            /* Flush Rx fifo */
+            /* Turn Rx off */
+            wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
+
+            /* Reset error latch */
+            wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+
+            /* get status byte from R1 */
+            (void) rdscc(lp->cardbase, cmd, R1);
+
+            /* Read and dump data in queue */
+            (void) rdscc(lp->cardbase, cmd, R8);
+            (void) rdscc(lp->cardbase, cmd, R8);
+            (void) rdscc(lp->cardbase, cmd, R8);
+
+            /* Now, turn on Rx and hunt for a flag */
+              wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 );
+
+            lp->rstate = ACTIVE;
+
+            if (lp->dmachan)
+            {
+                setup_rx_dma(lp);
+            } else {
+                /* Reset buffer pointers */
+                lp->rcp = lp->rcvbuf->data;
+                lp->rcvbuf->cnt = 0;
+                /* Allow aborts to interrupt us */
+                wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB);
+
+	}
+	wrtscc(lp->cardbase, cmd, R15, BRKIE );
+    }
+} /* pt_rts() */
+
+
+static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize)
+{
+    if (((addr & 0xffff) + dev_bufsize) <= 0x10000)
+        return 1;
+    else
+        return 0;
+}
+
+static int pt_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *)addr;
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);		/* addr is an AX.25 shifted ASCII */
+	return 0;		/* mac address */
+}
+
+
+/* Allocate a buffer which does not cross a DMA page boundary */
+static char * get_dma_buffer(unsigned long *mem_ptr)
+{
+    char *ret;
+
+    ret = (char *) *mem_ptr;
+
+    if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) {
+        *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+        ret = (char *) *mem_ptr;
+    }
+    *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
+    return (ret);
+} /* get_dma_buffer() */
+
+
+/*
+ * Sets up all the structures for the PT device
+ */
+static int pt_probe(struct device *dev)
+{
+    short ioaddr;
+    struct pt_local *lp;
+    unsigned long flags;
+    unsigned long mem_ptr;
+
+    ioaddr = dev->base_addr;
+
+    /*
+     * Initialise the device structure.
+     * Must be done before chipset_init()
+     * Make sure data structures used by  the PT are aligned
+     */
+    dev->priv = (void *) (((int) dev->priv + 7) & ~7);
+    lp = (struct pt_local*) dev->priv;
+
+    memset(dev->priv, 0, sizeof(struct pt_local));
+
+    /* Allocate some buffers which do not cross DMA boundaries */
+    mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local);
+    lp->txdmabuf = get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+    lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
+
+    /* Initialise the Rx buffer */
+    lp->rcvbuf = lp->rxdmabuf1;
+    lp->rcp = lp->rcvbuf->data;
+    lp->rcvbuf->cnt = 0;
+
+    /* Initialise the transmit queue head structure */
+    skb_queue_head_init(&lp->sndq);
+
+    lp->base = dev->base_addr;
+    lp->cardbase = dev->base_addr & 0x3f0;
+
+    /* These need to be initialised before scc_init() is called.
+     */
+    lp->xtal = XTAL;
+
+    if (dev->base_addr & CHANA) {
+        lp->speed = DEF_A_SPEED;
+        lp->txdelay = DEF_A_TXDELAY;
+        lp->persist = DEF_A_PERSIST;
+        lp->slotime = DEF_A_SLOTIME;
+        lp->squeldelay = DEF_A_SQUELDELAY;
+        lp->clockmode = DEF_A_CLOCKMODE;
+        lp->nrzi = DEF_A_NRZI;
+    } else {
+        lp->speed = DEF_B_SPEED;
+        lp->txdelay = DEF_B_TXDELAY;
+        lp->persist = DEF_B_PERSIST;
+        lp->slotime = DEF_B_SLOTIME;
+        lp->squeldelay = DEF_B_SQUELDELAY;
+        lp->clockmode = DEF_B_CLOCKMODE;
+        lp->nrzi = DEF_B_NRZI;
+    }
+    lp->bufsiz = DMA_BUFF_SIZE;
+    lp->tstate = IDLE;
+
+    chipset_init(dev);
+
+    if (dev->base_addr & CHANA) {
+        /* Note that a single IRQ services 2 devices (A and B channels)
+        */
+
+	/*
+	 * We disable the dma for a while, we have to get ints working
+	 * properly first!!
+	 */
+	lp->dmachan = 0;
+
+        if (dev->irq < 2) {
+            autoirq_setup(0);
+
+            /* Turn on PT interrupts */
+            save_flags(flags);
+            cli();
+            outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
+            restore_flags(flags);
+
+            /* Set a timer interrupt */
+            tdelay(lp, 1);
+            dev->irq = autoirq_report(20);
+
+	    /* Turn off PT interrupts */
+	    save_flags(flags);
+	    cli();
+            outb_p( (pt_sercfg  &= ~ PT_EI), lp->cardbase + INT_CFG);
+            restore_flags(flags);
+
+            if (!dev->irq) {
+                printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
+            }
+        }
+
+        printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
+
+        /* This board has jumpered interrupts. Snarf the interrupt vector
+         * now.  There is no point in waiting since no other device can use
+         * the interrupt, and this marks the 'irqaction' as busy.
+         */
+        {
+            int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev);
+            if (irqval) {
+                printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
+                    dev->irq, irqval);
+                return EAGAIN;
+            }
+        }
+
+        /* Grab the region */
+        request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" );
+    } /* A port */
+    dev->open = pt_open;
+    dev->stop = pt_close;
+    dev->do_ioctl = pt_ioctl;
+    dev->hard_start_xmit = pt_send_packet;
+    dev->get_stats = pt_get_stats;
+
+    /* Fill in the fields of the device structure */
+    dev_init_buffers(dev);
+
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+    dev->hard_header    = ax25_encapsulate;
+    dev->rebuild_header = ax25_rebuild_header;
+#endif
+
+    dev->set_mac_address = pt_set_mac_address;
+
+    dev->type = ARPHRD_AX25;            /* AF_AX25 device */
+    dev->hard_header_len = 73;      /* We do digipeaters now */
+    dev->mtu = 1500;                /* eth_mtu is default */
+    dev->addr_len = 7;               /* sizeof an ax.25 address */
+    memcpy(dev->broadcast, ax25_bcast, 7);
+    memcpy(dev->dev_addr, ax25_test, 7);
+
+    /* New style flags */
+    dev->flags = 0;
+
+    return 0;
+} /* pt_probe() */
+
+
+/* Open/initialise the board.  This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that 'should' only be set once at boot, so that there is
+ * a non-reboot way to recover if something goes wrong.
+ * derived from last half of tsync_attach()
+ */
+static int pt_open(struct device *dev)
+{
+    unsigned long flags;
+    struct pt_local *lp = dev->priv;
+    static first_time = 1;
+
+    if (dev->base_addr & CHANA)
+    {
+        if (first_time)
+        {
+            if (request_dma(dev->dma, "pt"))
+            {
+                free_irq(dev->irq, dev);
+                return -EAGAIN;
+            }
+        }
+
+         /* Reset hardware */
+         chipset_init(dev);
+     }
+     lp->tstate = IDLE;
+
+     if (dev->base_addr & CHANA)
+     {
+         scc_init(dev);
+         scc_init(dev->next);
+     }
+     /* Save a copy of register RR0 for comparing with later on */
+     /* We always put 0 in zero count */
+     lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT;
+
+    /* master interrupt enable */
+    save_flags(flags);
+    cli();
+    wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV);
+    outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
+    restore_flags(flags);
+
+    lp->open_time = jiffies;
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+    first_time = 0;
+
+    MOD_INC_USE_COUNT;
+
+    return 0;
+} /* pt_open() */
+
+static int pt_send_packet(struct sk_buff *skb, struct device *dev)
+{
+	struct pt_local *lp = (struct pt_local *) dev->priv;
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA);
+#endif
+	hardware_send_packet(lp, skb);
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+
+
+/* The inverse routine to pt_open() */
+static int pt_close(struct device *dev)
+{
+	unsigned long flags;
+	struct pt_local *lp = dev->priv;
+	struct sk_buff *ptr = NULL;
+	int cmd;
+
+	cmd = lp->base + CTL;
+
+	save_flags(flags);
+	cli();
+
+	/* Reset SCC or channel */
+	chipset_init(dev);
+	disable_dma(lp->dmachan);
+
+	lp->open_time = 0;
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Free any buffers left in the hardware transmit queue */
+	while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
+		kfree_skb(ptr, FREE_WRITE);
+
+	restore_flags(flags);
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA);
+#endif
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+} /* pt_close() */
+
+
+static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+    unsigned long flags;
+    struct pt_req rq;
+    struct pt_local *lp = (struct pt_local *) dev->priv;
+
+    int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req));
+    if (ret)
+	return ret;
+
+    if (cmd != SIOCDEVPRIVATE)
+        return -EINVAL;
+
+    copy_from_user(&rq, ifr->ifr_data, sizeof(struct pt_req));
+
+    switch (rq.cmd) {
+    case SIOCSPIPARAM:
+
+	if (!suser())
+	    return -EPERM;
+	save_flags(flags);
+	cli();
+	lp->txdelay = rq.txdelay;
+	lp->persist = rq.persist;
+	lp->slotime = rq.slotime;
+	lp->squeldelay = rq.squeldelay;
+	lp->clockmode = rq.clockmode;
+	lp->speed = rq.speed;
+	pt_open(&pt0a);
+	restore_flags(flags);
+	ret = 0;
+	break;
+
+    case SIOCSPIDMA:
+
+	if (!suser())
+	    return -EPERM;
+	ret = 0;
+	if (dev->base_addr & CHANA) {   /* if A channel */
+	   if (rq.dmachan < 1 || rq.dmachan > 3)
+		return -EINVAL;
+	   save_flags(flags);
+	   cli();
+	   pt_close(dev);
+	   free_dma(lp->dmachan);
+	   dev->dma = lp->dmachan = rq.dmachan;
+	   if (request_dma(lp->dmachan,"pt"))
+		ret = -EAGAIN;
+	   pt_open(dev);
+	   restore_flags(flags);
+	}
+	break;
+
+    case SIOCSPIIRQ:
+	ret = -EINVAL;      /* add this later */
+	break;
+
+    case SIOCGPIPARAM:
+    case SIOCGPIDMA:
+    case SIOCGPIIRQ:
+
+	rq.speed = lp->speed;
+	rq.txdelay = lp->txdelay;
+	rq.persist = lp->persist;
+	rq.slotime = lp->slotime;
+	rq.squeldelay = lp->squeldelay;
+	rq.clockmode = lp->clockmode;
+	rq.dmachan = lp->dmachan;
+	rq.irq = dev->irq;
+	copy_to_user(ifr->ifr_data, &rq, sizeof(struct pt_req));
+	ret = 0;
+	break;
+
+    default:
+	ret = -EINVAL;
+    }
+    return ret;
+}
+
+/*
+ *	Get the current statistics.
+ *	This may be called with the card open or closed. 
+ */
+ 
+static struct net_device_stats *pt_get_stats(struct device *dev)
+{
+	struct pt_local *lp = (struct pt_local *) dev->priv;
+	return &lp->stats;
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  tab-width: 4
+ * End:
+ */
+
+
+static void tdelay(struct pt_local *lp, int time)
+{
+	/* For some reason, we turn off the Tx interrupts here! */
+	if (!lp->dmachan)
+		wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
+
+	if (lp->base & CHANA) 
+	{
+		outb_p(time & 0xff, lp->cardbase + TMR1);
+		outb_p((time >> 8)&0xff, lp->cardbase + TMR1);
+	}
+	else
+	{
+		outb_p(time & 0xff, lp->cardbase + TMR2);
+		outb_p((time >> 8)&0xff, lp->cardbase + TMR2);
+	}
+} /* tdelay */
+
+
+static void pt_txisr(struct pt_local *lp)
+{
+	unsigned long flags;
+	int cmd;
+	unsigned char c;
+
+	save_flags(flags);
+	cli();
+	cmd = lp->base + CTL;
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif
+
+	switch (lp->tstate)
+	{
+	case CRCOUT:
+	    lp->tstate = FLAGOUT;
+	    tdelay(lp, lp->squeldelay);
+	    restore_flags(flags);
+	    return;
+
+	case IDLE:
+	    /* Transmitter idle. Find a frame for transmission */
+	    if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+	    {
+	        /* Nothing to send - return to receive mode
+	         * Tx off now - flag should have gone
+	         */
+	        pt_rts(lp, OFF);
+
+	        restore_flags(flags);
+	        return;
+	    }
+	    if (!lp->dmachan)
+	    {
+		    lp->txptr = lp->sndbuf->data;
+		    lp->txptr++;		/* Ignore KISS control byte */
+		    lp->txcnt = (int) lp->sndbuf->len - 1;
+		}
+	    /* If a buffer to send, drop though here */
+
+	case DEFER:
+	    /* Check DCD - debounce it */
+	    /* See Intel Microcommunications Handbook p2-308 */
+	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+	    if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+	    {
+	        lp->tstate = DEFER;
+	        tdelay(lp, 100);
+	        /* DEFER until DCD transition or timeout */
+	        wrtscc(lp->cardbase, cmd, R15, DCDIE);
+	        restore_flags(flags);
+	        return;
+	    }
+	    if (random() > lp->persist)
+	    {
+	        lp->tstate = DEFER;
+	        tdelay(lp, lp->slotime);
+	        restore_flags(flags);
+	        return;
+	    }
+	    pt_rts(lp, ON);		/* Tx on */
+	    if (lp->dmachan)
+	    	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+	    lp->tstate = ST_TXDELAY;
+	    tdelay(lp, lp->txdelay);
+	    restore_flags(flags);
+	    return;
+
+	case ACTIVE:
+	    /* Here we are actively sending a frame */
+	    if (lp->txcnt--)
+	    {
+	        /* XLZ - checkout Gracilis PT code to see if the while
+	         * loop is better or not.
+	         */
+	        c = *lp->txptr++;
+	        /* next char is gone */
+	        wrtscc(lp->cardbase, cmd, R8, c);
+	        /* stuffing a char satisfies interrupt condition */
+	    } else {
+	        /* No more to send */
+	        kfree_skb(lp->sndbuf, FREE_WRITE);
+	        lp->sndbuf = NULL;
+	        if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
+	        {
+	            /* Did we underrun */
+	            lp->stats.tx_errors++;
+	            lp->stats.tx_fifo_errors++;
+	            wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+	            lp->tstate = FLAGOUT;
+	            tdelay(lp, lp->squeldelay);
+	            restore_flags(flags);
+	            return;
+	        }
+	        lp->tstate = UNDERRUN;
+	        /* Send flags on underrun */
+	       if (lp->nrzi)
+	       {
+	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
+	       } else {
+	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ);
+	       }
+	       /* Reset Tx interrupt pending */
+	       wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);
+	   }
+	   restore_flags(flags);
+	   return;
+	default:
+		printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
+		pt_rts(lp, OFF);
+		lp->tstate = IDLE;
+		break;
+    } 				/*switch */
+    restore_flags(flags);
+}
+
+static void pt_rxisr(struct device *dev)
+{
+    struct pt_local *lp = (struct pt_local*) dev->priv;
+    int cmd = lp->base + CTL;
+    int bytecount;
+    unsigned long flags;
+    char rse;
+    struct sk_buff *skb;
+    int sksize, pkt_len;
+    struct mbuf *cur_buf = NULL;
+    unsigned char *cfix;
+
+    save_flags(flags);
+    cli();
+
+    /* Get status byte from R1 */
+    rse = rdscc(lp->cardbase, cmd, R1);
+
+#ifdef PT_DEBUG
+    printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
+#endif
+
+	if (lp->dmachan && (rse & Rx_OVR))
+		lp->rstate = RXERROR;
+
+    if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan)
+    {
+        /* There is a char to be stored
+         * Read special condition bits before reading the data char
+         */
+        if (rse & Rx_OVR)
+        {
+             /* Rx overrun - toss buffer */
+             /* wind back the pointers */
+             lp->rcp = lp->rcvbuf->data;
+             lp->rcvbuf->cnt = 0;
+             lp->rstate = RXERROR;
+             lp->stats.rx_errors++;
+             lp->stats.rx_fifo_errors++;
+         } else if (lp->rcvbuf->cnt >= lp->bufsiz)
+             {
+                 /* Too large packet
+                  * wind back Rx buffer pointers
+                  */
+                 lp->rcp = lp->rcvbuf->data;
+                 lp->rcvbuf->cnt = 0;
+                 lp->rstate = TOOBIG;
+             }
+         /* ok, we can store the Rx char if no errors */
+         if (lp->rstate == ACTIVE)
+         {
+             *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);
+             lp->rcvbuf->cnt++;
+         } else {
+             /* we got an error, dump the FIFO */
+             (void) rdscc(lp->cardbase, cmd, R8);
+             (void) rdscc(lp->cardbase, cmd, R8);
+             (void) rdscc(lp->cardbase, cmd, R8);
+
+             /* Reset error latch */
+             wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+             lp->rstate = ACTIVE;
+
+             /* Resync the SCC */
+             wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+         }
+     }
+
+     if (rse & END_FR)
+     {
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
+#endif
+		if (lp->dmachan)
+		{
+			clear_dma_ff(lp->dmachan);
+			bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
+		} else {
+			bytecount = lp->rcvbuf->cnt;
+		}
+
+         /* END OF FRAME - Make sure Rx was active */
+         if (lp->rcvbuf->cnt > 0 || lp->dmachan)
+         {
+             if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10))
+             {
+                 if ((bytecount >= 10) && (rse & CRC_ERR))
+                 {
+                     lp->stats.rx_crc_errors++;
+                 }
+                 if (lp->dmachan)
+                 {
+                 	if (lp->rstate == RXERROR)
+                 	{
+                 		lp->stats.rx_errors++;
+                 		lp->stats.rx_over_errors++;
+                 	}
+                 	lp->rstate = ACTIVE;
+                 	setup_rx_dma(lp);
+                 } else {
+	                 /* wind back Rx buffer pointers */
+    	             lp->rcp = lp->rcvbuf->data;
+        	         lp->rcvbuf->cnt = 0;
+
+					/* Re-sync the SCC */
+					wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+        	     }
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
+#endif
+             } else {
+                 /* We have a valid frame */
+                 if (lp->dmachan)
+                 {
+                 	pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1;
+					/* Get buffer for next frame */
+                 	cur_buf = lp->rcvbuf;
+                 	switchbuffers(lp);
+                 	setup_rx_dma(lp);
+                 } else {
+	                 pkt_len = lp->rcvbuf->cnt -= 2;  /* Toss 2 CRC bytes */
+    	             pkt_len += 1;	/* make room for KISS control byte */
+        		}
+
+                 /* Malloc up new buffer */
+                 sksize = pkt_len;
+                 skb = dev_alloc_skb(sksize);
+                 if (skb == NULL)
+                 {
+                     printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name);
+                     lp->stats.rx_dropped++;
+                     restore_flags(flags);
+                     return;
+                 }
+                 skb->dev = dev;
+
+                 /* KISS kludge = prefix with a 0 byte */
+                 cfix=skb_put(skb,pkt_len);
+                 *cfix++=0;
+                 /* skb->data points to the start of sk_buff area */
+                 if (lp->dmachan)
+                 	memcpy(cfix, (char*)cur_buf->data, pkt_len - 1);
+                 else
+                    memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
+                 skb->protocol = ntohs(ETH_P_AX25);
+                 skb->mac.raw=skb->data;
+                 lp->stats.rx_bytes+=skb->len;
+                 netif_rx(skb);
+                 lp->stats.rx_packets++;
+                 if (!lp->dmachan)
+                 {
+	                 /* packet queued - wind back buffer for next frame */
+    	             lp->rcp = lp->rcvbuf->data;
+	                 lp->rcvbuf->cnt = 0;
+	             }
+             } /* good frame */
+         } /* check active Rx */
+         /* Clear error status */
+         lp->rstate = ACTIVE;
+         /* Reset error latch */
+     } /* end EOF check */
+     wrtscc(lp->cardbase, cmd, R0, ERR_RES);
+     restore_flags(flags);
+} /* pt_rxisr() */
+
+/*
+ * This handles the two timer interrupts.
+ * This is a real bugger, cause you have to rip it out of the pi's
+ * external status code.  They use the CTS line or something.
+ */
+static void pt_tmrisr(struct pt_local *lp)
+{
+    unsigned long flags;
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
+#endif
+
+    save_flags(flags);
+    cli();
+
+
+    switch (lp->tstate)
+    {
+    /* Most of this stuff is in pt_exisr() */
+    case FLAGOUT:
+    case ST_TXDELAY:
+    case DEFER:
+/*    case ACTIVE:
+    case UNDERRUN:*/
+        pt_exisr(lp);
+        break;
+
+    default:
+	if (lp->base & CHANA)
+ 	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
+	else
+	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
+	break;
+    } /* end switch */
+    restore_flags(flags);
+} /* pt_tmrisr() */
+
+
+/*
+ * This routine is called by the kernel when there is an interrupt for the
+ * PT.
+ */
+static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    /* It's a tad dodgy here, but we assume pt0a until proven otherwise */
+    struct device *dev = &pt0a;
+    struct pt_local *lp = dev->priv;
+    unsigned char intreg;
+    unsigned char st;
+    register int cbase = dev->base_addr & 0x3f0;
+    unsigned long flags;
+
+    /* Read the PT's interrupt register, this is not the SCC one! */
+    intreg = inb_p(cbase + INT_REG);
+    while(( intreg & 0x07) != 0x07) {
+        /* Read interrupt register pending from Channel A */
+        while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0)
+        {
+        	/* Read interrupt vector from R2, channel B */
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);
+#endif
+/*        	st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
+#ifdef PT_DEBUG
+		printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);
+#endif
+			if (st & CHARxIP) {
+			    /* Channel A Rx */
+	            lp = (struct pt_local*)pt0a.priv;
+	            pt_rxisr(&pt0a);
+	        } else if (st & CHATxIP) {
+	            /* Channel A Tx */
+	            lp = (struct pt_local*)pt0a.priv;
+	            pt_txisr(lp);
+	        } else if (st & CHAEXT) {
+	        	/* Channel A External Status */
+	        	lp = (struct pt_local*)pt0a.priv;
+	        	pt_exisr(lp);
+	        } else if (st & CHBRxIP) {
+	        	/* Channel B Rx */
+	            lp= (struct pt_local*)pt0b.priv;
+	            pt_rxisr(&pt0b);
+	        } else if (st & CHBTxIP) {
+            	/* Channel B Tx */
+	            lp = (struct pt_local*)pt0b.priv;
+	            pt_txisr(lp);
+			} else if (st & CHBEXT) {
+	        	/* Channel B External Status */
+	        	lp = (struct pt_local*)pt0b.priv;
+	        	pt_exisr(lp);
+	        }
+            /* Reset highest interrupt under service */
+            save_flags(flags);
+            cli();
+            wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
+            restore_flags(flags);
+        }  /* end of SCC ints */
+
+        if (!(intreg & PT_TMR1_MSK))
+        {
+            /* Clear timer 1 */
+            inb_p(cbase + TMR1CLR);
+
+            pt_tmrisr( (struct pt_local*)pt0a.priv);
+        }
+
+        if (!(intreg & PT_TMR2_MSK))
+        {
+            /* Clear timer 2 */
+            inb_p(cbase + TMR2CLR);
+
+            pt_tmrisr( (struct pt_local*)pt0b.priv);
+        }
+
+        /* Get the next PT interrupt vector */
+        intreg = inb_p(cbase + INT_REG);
+    } /* while (intreg) */
+} /* pt_interrupt() */
+
+
+static void pt_exisr(struct pt_local *lp)
+{
+    unsigned long flags;
+    int cmd = lp->base + CTL;
+    unsigned char st;
+    char c;
+    int length;
+
+    save_flags(flags);
+    cli();
+
+    /* Get external status */
+    st = rdscc(lp->cardbase, cmd, R0);
+
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
+#endif
+    /* Reset external status latch */
+    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+    if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan)
+    {
+    	setup_rx_dma(lp);
+    	lp->rstate = ACTIVE;
+    }
+
+    switch (lp->tstate)
+    {
+    case ACTIVE:		/* Unexpected underrun */
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
+#endif
+        kfree_skb(lp->sndbuf, FREE_WRITE);
+        lp->sndbuf = NULL;
+        if (!lp->dmachan)
+        {
+	        wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
+	        lp->stats.tx_errors++;
+	        lp->stats.tx_fifo_errors++;
+	    }
+        lp->tstate = FLAGOUT;
+        tdelay(lp, lp->squeldelay);
+        restore_flags(flags);
+        return;
+    case UNDERRUN:
+        lp->tstate = CRCOUT;
+        restore_flags(flags);
+        return;
+    case FLAGOUT:
+        /* squeldelay has timed out */
+        /* Find a frame for transmission */
+        if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
+        {
+            /* Nothing to send - return to Rx mode */
+            pt_rts(lp, OFF);
+            lp->tstate = IDLE;
+            restore_flags(flags);
+            return;
+        }
+        if (!lp->dmachan)
+        {
+	        lp->txptr = lp->sndbuf->data;
+    	    lp->txptr++;		/* Ignore KISS control byte */
+	        lp->txcnt = (int) lp->sndbuf->len - 1;
+	    }
+        /* Fall through if we have a packet */
+
+    case ST_TXDELAY:
+    	if (lp->dmachan)
+    	{
+    		/* Disable DMA chan */
+    		disable_dma(lp->dmachan);
+
+    		/* Set up for TX dma */
+    		wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
+
+    		length = lp->sndbuf->len - 1;
+    		memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
+
+    		/* Setup DMA controller for Tx */
+    		setup_tx_dma(lp, length);
+
+    		enable_dma(lp->dmachan);
+
+    		/* Reset CRC, Txint pending */
+    		wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
+
+    		/* Allow underrun only */
+    		wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+    		/* Enable TX DMA */
+    		wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
+
+    		/* Send CRC on underrun */
+    		wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+    		lp->tstate = ACTIVE;
+    		break;
+    	}
+        /* Get first char to send */
+        lp->txcnt--;
+        c = *lp->txptr++;
+        /* Reset CRC for next frame */
+        wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);
+
+        /* send abort on underrun */
+        if (lp->nrzi)
+        {
+            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
+        } else {
+            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER);
+        }
+        /* send first char */
+        wrtscc(lp->cardbase, cmd, R8, c);
+
+        /* Reset end of message latch */
+        wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
+
+        /* stuff an extra one in */
+/*        while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt)
+        {
+            lp->txcnt--;
+            c = *lp->txptr++;
+            wrtscc(lp->cardbase, cmd, R8, c);
+        }*/
+
+        /* select Tx interrupts to enable */
+        /* Allow underrun int only */
+        wrtscc(lp->cardbase, cmd, R15, TxUIE);
+
+        /* Reset external interrupts */
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+
+        /* Tx and Rx ints enabled */
+        wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
+
+        lp->tstate = ACTIVE;
+        restore_flags(flags);
+        return;
+
+        /* slotime has timed out */
+    case DEFER:
+        /* Check DCD - debounce it
+         * see Intel Microcommunications Handbook, p2-308
+         */
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
+        if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
+        {
+            lp->tstate = DEFER;
+            tdelay(lp, 100);
+            /* DEFER until DCD transition or timeout */
+            wrtscc(lp->cardbase, cmd, R15, DCDIE);
+            restore_flags(flags);
+            return;
+        }
+        if (random() > lp->persist)
+        {
+            lp->tstate = DEFER;
+            tdelay(lp, lp->slotime);
+            restore_flags(flags);
+            return;
+        }
+        if (lp->dmachan)
+        	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
+        pt_rts(lp, ON);			/* Tx on */
+        lp->tstate = ST_TXDELAY;
+        tdelay(lp, lp->txdelay);
+        restore_flags(flags);
+        return;
+
+ 	/* Only for int driven parts */
+ 	if (lp->dmachan)
+ 	{
+ 		restore_flags(flags);
+ 		return;
+ 	}
+
+    } /* end switch */
+    /*
+     * Rx mode only
+     * This triggers when hunt mode is entered, & since an ABORT
+     * automatically enters hunt mode, we use that to clean up
+     * any waiting garbage
+     */
+    if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
+    {
+#ifdef PT_DEBUG
+	printk(KERN_DEBUG "PT: exisr(): abort detected.\n");
+#endif
+  		/* read and dump all of SCC Rx FIFO */
+        (void) rdscc(lp->cardbase, cmd, R8);
+        (void) rdscc(lp->cardbase, cmd, R8);
+        (void) rdscc(lp->cardbase, cmd, R8);
+
+        lp->rcp = lp->rcvbuf->data;
+        lp->rcvbuf->cnt = 0;
+
+		/* Re-sync the SCC */
+		wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+
+    }
+
+    /* Check for DCD transitions */
+    if ( (st & DCD) != (lp->saved_RR0 & DCD))
+    {
+#ifdef PT_DEBUG
+        printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
+#endif
+		if (st & DCD)
+		{
+			/* Check that we don't already have some data */
+			if (lp->rcvbuf->cnt > 0)
+			{
+#ifdef PT_DEBUG
+				printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
+#endif
+				/* wind back buffers */
+				lp->rcp = lp->rcvbuf->data;
+				lp->rcvbuf->cnt = 0;
+			}
+		} else {  /* DCD off */
+
+			/* read and dump al SCC FIFO */
+			(void)rdscc(lp->cardbase, cmd, R8);
+			(void)rdscc(lp->cardbase, cmd, R8);
+			(void)rdscc(lp->cardbase, cmd, R8);
+
+			/* wind back buffers */
+			lp->rcp = lp->rcvbuf->data;
+			lp->rcvbuf->cnt = 0;
+
+			/* Re-sync the SCC */
+			wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
+		}
+
+    }
+    /* Update the saved version of register RR) */
+    lp->saved_RR0 = st &~ ZCOUNT;
+    restore_flags(flags);
+
+} /* pt_exisr() */
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Craig Small VK2XLZ <vk2xlz@vk2xlz.ampr.org>");
+MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card");
+
+int init_module(void)
+{
+	return pt_init();
+}
+
+void cleanup_module(void)
+{
+	free_irq(pt0a.irq, &pt0a);	/* IRQs and IO Ports are shared */
+	release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
+
+	kfree(pt0a.priv);
+	pt0a.priv = NULL;
+	unregister_netdev(&pt0a);
+
+	kfree(pt0b.priv);
+	pt0b.priv = NULL;
+	unregister_netdev(&pt0b);
+}
+#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c
--- v2.1.70/linux/drivers/net/hamradio/scc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/scc.c	Sat Nov 29 16:29:37 1997
@@ -0,0 +1,2257 @@
+#define RCS_ID "$Id: scc.c,v 1.71 1997/11/29 19:59:20 jreuter Exp jreuter $"
+
+#define VERSION "3.0"
+#define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
+
+/*
+ * Please use z8530drv-utils-3.0 with this version.
+ *            ------------------
+ */
+
+/*
+   ********************************************************************
+   *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *
+   ********************************************************************
+
+
+   ********************************************************************
+
+	Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
+
+	portions (c) 1993 Guido ten Dolle PE1NNZ
+
+   ********************************************************************
+   
+   The driver and the programs in the archive are UNDER CONSTRUCTION.
+   The code is likely to fail, and so your kernel could --- even 
+   a whole network. 
+
+   This driver is intended for Amateur Radio use. If you are running it
+   for commercial purposes, please drop me a note. I am nosy...
+
+   ...BUT:
+ 
+   ! You  m u s t  recognize the appropriate legislations of your country !
+   ! before you connect a radio to the SCC board and start to transmit or !
+   ! receive. The GPL allows you to use the  d r i v e r,  NOT the RADIO! !
+
+   For non-Amateur-Radio use please note that you might need a special
+   allowance/licence from the designer of the SCC Board and/or the
+   MODEM. 
+
+   This program is free software; you can redistribute it and/or modify 
+   it under the terms of the (modified) GNU General Public License 
+   delivered with the Linux kernel source.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should find a copy of the GNU General Public License in 
+   /usr/src/linux/COPYING; 
+   
+   ******************************************************************** 
+
+		
+   Incomplete history of z8530drv:
+   -------------------------------
+
+   940913	- started to write the driver, rescued most of my own
+		  code (and Hans Alblas' memory buffer pool concept) from 
+		  an earlier project "sccdrv" which was initiated by 
+		  Guido ten Dolle. Not much of the old driver survived, 
+		  though. The first version I put my hands on was sccdrv1.3
+		  from August 1993. The memory buffer pool concept
+		  appeared in an unauthorized sccdrv version (1.5) from
+		  August 1994.
+
+   950131	- changed copyright notice to GPL without limitations.
+   
+     .
+     .	<SNIP>
+     .
+   		  
+   961005	- New semester, new driver... 
+
+   		  * KISS TNC emulator removed (TTY driver)
+   		  * Source moved to drivers/net/
+   		  * Includes Z8530 defines from drivers/net/z8530.h
+   		  * Uses sk_buffer memory management
+   		  * Reduced overhead of /proc/net/z8530drv output
+   		  * Streamlined quite a lot things
+   		  * Invents brand new bugs... ;-)
+
+   		  The move to version number 3.0 reflects theses changes.
+   		  You can use 'kissbridge' if you need a KISS TNC emulator.
+
+   961213	- Fixed for Linux networking changes. (G4KLX)
+   970108	- Fixed the remaining problems.
+   970402	- Hopefully fixed the problems with the new *_timer()
+   		  routines, added calibration code.
+   971012	- made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
+
+   Thanks to all who contributed to this driver with ideas and bug
+   reports!
+   
+   NB -- if you find errors, change something, please let me know
+      	 first before you distribute it... And please don't touch
+   	 the version number. Just replace my callsign in
+   	 "v3.0.dl1bke" with your own. Just to avoid confusion...
+
+   If you want to add your modification to the linux distribution
+   please (!) contact me first.
+   
+   New versions of the driver will be announced on the linux-hams
+   mailing list on vger.rutgers.edu. To subscribe send an e-mail
+   to majordomo@vger.rutgers.edu with the following line in
+   the body of the mail:
+   
+	   subscribe linux-hams
+	   
+   The content of the "Subject" field will be ignored.
+
+   vy 73,
+   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
+		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
+		Internet: jreuter@poboxes.com
+		www     : http://www.rat.de/jr
+*/
+
+/* ----------------------------------------------------------------------- */
+
+#undef  SCC_LDELAY	1	/* slow it even a bit more down */
+#undef  DONT_CHECK		/* don't look if the SCCs you specified are available */
+
+#define MAXSCC          4       /* number of max. supported chips */
+#define BUFSIZE         384     /* must not exceed 4096 */
+#define MAXQUEUE	8	/* number of buffers we queue ourself */
+#undef  DISABLE_ALL_INTS	/* use cli()/sti() in ISR instead of */
+				/* enable_irq()/disable_irq()        */
+#undef	SCC_DEBUG
+
+#define DEFAULT_CLOCK	4915200 /* default pclock if nothing is specified */
+
+/* ----------------------------------------------------------------------- */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/init.h>
+
+#include <linux/scc.h>
+#include "z8530.h"
+
+#include <net/ax25.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+#endif
+
+int scc_init(void);
+
+static void t_dwait(unsigned long);
+static void t_txdelay(unsigned long);
+static void t_tail(unsigned long);
+static void t_busy(unsigned long);
+static void t_maxkeyup(unsigned long);
+static void t_idle(unsigned long);
+static void scc_tx_done(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
+static void scc_start_maxkeyup(struct scc_channel *);
+static void scc_start_defer(struct scc_channel *);
+
+static void z8530_init(void);
+
+static void init_channel(struct scc_channel *scc);
+static void scc_key_trx (struct scc_channel *scc, char tx);
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static void scc_init_timer(struct scc_channel *scc);
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
+static int scc_net_init(struct device *dev);
+static int scc_net_open(struct device *dev);
+static int scc_net_close(struct device *dev);
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
+static int scc_net_tx(struct sk_buff *skb, struct device *dev);
+static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int scc_net_set_mac_address(struct device *dev, void *addr);
+static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
+static struct net_device_stats * scc_net_get_stats(struct device *dev);
+
+static unsigned char *SCC_DriverName = "scc";
+
+static struct irqflags { unsigned char used : 1; } Ivec[16];
+	
+static struct scc_channel SCC_Info[2 * MAXSCC];	/* information per channel */
+
+static struct scc_ctrl {
+	io_port chan_A;
+	io_port chan_B;
+	int irq;
+} SCC_ctrl[MAXSCC+1];
+
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
+
+MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("scc");
+
+/* ******************************************************************** */
+/* *			Port Access Functions			      * */
+/* ******************************************************************** */
+
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+extern __inline__ unsigned char InReg(io_port port, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char r;
+	
+	save_flags(flags);
+	cli();
+#ifdef SCC_LDELAY
+	Outb(port, reg);
+	udelay(SCC_LDELAY);
+	r=Inb(port);
+	udelay(SCC_LDELAY);
+#else
+	Outb(port, reg);
+	r=Inb(port);
+#endif
+	restore_flags(flags);
+	return r;
+}
+
+extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+#ifdef SCC_LDELAY
+	Outb(port, reg); udelay(SCC_LDELAY);
+	Outb(port, val); udelay(SCC_LDELAY);
+#else
+	Outb(port, reg);
+	Outb(port, val);
+#endif
+	restore_flags(flags);
+}
+
+extern __inline__ void wr(struct scc_channel *scc, unsigned char reg,
+	unsigned char val)
+{
+	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
+}
+
+extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
+}
+
+extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
+}
+
+#ifdef DISABLE_ALL_INTS
+extern __inline__ void scc_cli(int irq)
+{ cli(); }
+extern __inline__ void scc_sti(int irq)
+{ sti(); }
+#else
+static __inline__ void scc_cli(int irq)
+{ disable_irq(irq); }
+static __inline__ void scc_sti(int irq)
+{ enable_irq(irq); }
+#endif
+
+/* ******************************************************************** */
+/* *			Some useful macros			      * */
+/* ******************************************************************** */
+
+
+extern __inline__ void scc_lock_dev(struct scc_channel *scc)
+{
+	scc->dev->tbusy = 1;
+}
+
+extern __inline__ void scc_unlock_dev(struct scc_channel *scc)
+{
+	scc->dev->tbusy = 0;
+}
+
+extern __inline__ void scc_discard_buffers(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	
+	if (scc->tx_buff != NULL)
+	{
+		dev_kfree_skb(scc->tx_buff, FREE_WRITE);
+		scc->tx_buff = NULL;
+	}
+	
+	while (skb_queue_len(&scc->tx_queue))
+		dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE);
+
+	restore_flags(flags);
+}
+
+
+
+/* ******************************************************************** */
+/* *			Interrupt Service Routines		      * */
+/* ******************************************************************** */
+
+
+/* ----> subroutines for the interrupt handlers <---- */
+
+extern __inline__ void scc_notify(struct scc_channel *scc, int event)
+{
+	struct sk_buff *skb;
+	char *bp;
+	
+        if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
+		return;
+
+	skb = dev_alloc_skb(2);
+	if (skb != NULL)
+	{
+		bp = skb_put(skb, 2);
+		*bp++ = PARAM_HWEVENT;
+		*bp++ = event;
+		scc_net_rx(scc, skb);
+	} else
+		scc->stat.nospace++;
+}
+
+extern __inline__ void flush_rx_FIFO(struct scc_channel *scc)
+{
+	int k;
+	
+	for (k=0; k<3; k++)
+		Inb(scc->data);
+		
+	if(scc->rx_buff != NULL)		/* did we receive something? */
+	{
+		scc->stat.rxerrs++;  /* then count it as an error */
+		kfree_skb(scc->rx_buff, FREE_READ);
+		scc->rx_buff = NULL;
+	}
+}
+
+
+/* ----> four different interrupt handlers for Tx, Rx, changing of	*/
+/*       DCD/CTS and Rx/Tx errors					*/
+
+/* Transmitter interrupt handler */
+extern __inline__ void scc_txint(struct scc_channel *scc)
+{
+	struct sk_buff *skb;
+
+	scc->stat.txints++;
+	skb = scc->tx_buff;
+	
+	/* send first octet */
+	
+	if (skb == NULL)
+	{
+		skb = skb_dequeue(&scc->tx_queue);
+		scc->tx_buff = skb;
+		scc_unlock_dev(scc);
+
+		if (skb == NULL)
+		{
+			scc_tx_done(scc);
+			Outb(scc->ctrl, RES_Tx_P);
+			return;
+		}
+		
+		if (skb->len == 0)		/* Paranoia... */
+		{
+			dev_kfree_skb(skb, FREE_WRITE);
+			scc->tx_buff = NULL;
+			scc_tx_done(scc);
+			Outb(scc->ctrl, RES_Tx_P);
+			return;
+		}
+
+		scc->stat.tx_state = TXS_ACTIVE;
+
+		OutReg(scc->ctrl, R0, RES_Tx_CRC);
+						/* reset CRC generator */
+		or(scc,R10,ABUNDER);		/* re-install underrun protection */
+		Outb(scc->data,*skb->data);	/* send byte */
+		skb_pull(skb, 1);
+
+		if (!scc->enhanced)		/* reset EOM latch */
+			Outb(scc->ctrl,RES_EOM_L);
+		return;
+	}
+	
+	/* End Of Frame... */
+	
+	if (skb->len == 0)
+	{
+		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
+		cl(scc, R10, ABUNDER);		/* send CRC */
+		dev_kfree_skb(skb, FREE_WRITE);
+		scc->tx_buff = NULL;
+		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
+		return;
+	} 
+	
+	/* send octet */
+	
+	Outb(scc->data,*skb->data);		
+	skb_pull(skb, 1);
+}
+
+
+/* External/Status interrupt handler */
+extern __inline__ void scc_exint(struct scc_channel *scc)
+{
+	unsigned char status,changes,chg_and_stat;
+
+	scc->stat.exints++;
+
+	status = InReg(scc->ctrl,R0);
+	changes = status ^ scc->status;
+	chg_and_stat = changes & status;
+	
+	/* ABORT: generated whenever DCD drops while receiving */
+
+	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
+		flush_rx_FIFO(scc);
+
+
+	/* DCD: on = start to receive packet, off = ABORT condition */
+	/* (a successfully received packet generates a special condition int) */
+	
+	if(changes & DCD)                       /* DCD input changed state */
+	{
+		if(status & DCD)                /* DCD is now ON */
+		{
+			if (scc->modem.clocksrc != CLK_EXTERNAL)
+				OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+				
+			or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+		} else {                        /* DCD is now OFF */
+			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
+			flush_rx_FIFO(scc);
+		}
+		
+		if (!scc->kiss.softdcd)
+			scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
+	}
+	
+	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+	
+	if (changes & SYNC_HUNT)
+	{
+		if (scc->kiss.softdcd)
+			scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
+		else
+			cl(scc,R15,SYNCIE);	/* oops, we were too lazy to disable this? */
+	}
+
+#ifdef notdef
+	/* CTS: use external TxDelay (what's that good for?!)
+	 * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+	 * own purposes) we _should_ use the "autoenable" feature
+	 * of the Z8530 and not this interrupt...
+	 */
+	 
+	if (chg_and_stat & CTS)			/* CTS is now ON */
+	{
+		if (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
+			scc_start_tx_timer(scc, t_txdelay, 0);
+	}
+#endif
+	
+	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
+	{
+		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
+		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
+
+		if (scc->tx_buff != NULL)
+		{
+			dev_kfree_skb(scc->tx_buff, FREE_WRITE);
+			scc->tx_buff = NULL;
+		}
+		
+		or(scc,R10,ABUNDER);
+		scc_start_tx_timer(scc, t_txdelay, 0);	/* restart transmission */
+	}
+		
+	scc->status = status;
+	Outb(scc->ctrl,RES_EXT_INT);
+}
+
+
+/* Receiver interrupt handler */
+extern __inline__ void scc_rxint(struct scc_channel *scc)
+{
+	struct sk_buff *skb;
+
+	scc->stat.rxints++;
+
+	if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
+	{
+		Inb(scc->data);		/* discard char */
+		or(scc,R3,ENT_HM);	/* enter hunt mode for next flag */
+		return;
+	}
+
+	skb = scc->rx_buff;
+	
+	if (skb == NULL)
+	{
+		skb = dev_alloc_skb(scc->stat.bufsize);
+		if (skb == NULL)
+		{
+			scc->dev_stat.rx_dropped++;
+			scc->stat.nospace++;
+			Inb(scc->data);
+			or(scc, R3, ENT_HM);
+			return;
+		}
+		
+		scc->rx_buff = skb;
+		*(skb_put(skb, 1)) = 0;	/* KISS data */
+	}
+	
+	if (skb->len >= scc->stat.bufsize)
+	{
+#ifdef notdef
+		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
+#endif
+		kfree_skb(skb, FREE_READ);
+		scc->rx_buff = NULL;
+		Inb(scc->data);
+		or(scc, R3, ENT_HM);
+		return;
+	}
+
+	*(skb_put(skb, 1)) = Inb(scc->data);
+}
+
+
+/* Receive Special Condition interrupt handler */
+extern __inline__ void scc_spint(struct scc_channel *scc)
+{
+	unsigned char status;
+	struct sk_buff *skb;
+
+	scc->stat.spints++;
+
+	status = InReg(scc->ctrl,R1);		/* read receiver status */
+	
+	Inb(scc->data);				/* throw away Rx byte */
+	skb = scc->rx_buff;
+
+	if(status & Rx_OVR)			/* receiver overrun */
+	{
+		scc->stat.rx_over++;             /* count them */
+		or(scc,R3,ENT_HM);               /* enter hunt mode for next flag */
+		
+		if (skb != NULL) 
+			kfree_skb(skb, FREE_READ);
+		scc->rx_buff = NULL;
+	}
+
+	if(status & END_FR && skb != NULL)	/* end of frame */
+	{
+		/* CRC okay, frame ends on 8 bit boundary and received something ? */
+		
+		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+		{
+			/* ignore last received byte (first of the CRC bytes) */
+			skb_trim(skb, skb->len-1);
+			scc_net_rx(scc, skb);
+			scc->rx_buff = NULL;
+			scc->stat.rxframes++;
+		} else {				/* a bad frame */
+			kfree_skb(skb, FREE_READ);
+			scc->rx_buff = NULL;
+			scc->stat.rxerrs++;
+		}
+	} 
+
+	Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+	switch (vector & VECTOR_MASK)
+	{
+		case TXINT: scc_txint(scc); break;
+		case EXINT: scc_exint(scc); break;
+		case RXINT: scc_rxint(scc); break;
+		case SPINT: scc_spint(scc); break;
+	}
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+   use it to get the number of the chip that generated the int.
+   If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char vector;	
+	struct scc_channel *scc;
+	struct scc_ctrl *ctrl;
+	int k;
+	
+	scc_cli(irq);
+
+	if (Vector_Latch)
+	{
+	    	for(k=0; k < SCC_IRQTIMEOUT; k++)
+    		{
+			Outb(Vector_Latch, 0);      /* Generate INTACK */
+        
+			/* Read the vector */
+			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
+			if (vector & 0x01) break;
+        	 
+		        scc=&SCC_Info[vector >> 3 ^ 0x01];
+			if (!scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+
+			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
+		}  
+		scc_sti(irq);
+
+		if (k == SCC_IRQTIMEOUT)
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+		return;
+	}
+
+	/* Find the SCC generating the interrupt by polling all attached SCCs
+	 * reading RR3A (the interrupt pending register)
+	 */
+
+	ctrl = SCC_ctrl;
+	while (ctrl->chan_A)
+	{
+		if (ctrl->irq != irq)
+		{
+			ctrl++;
+			continue;
+		}
+
+		scc = NULL;
+		for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+		{
+			vector=InReg(ctrl->chan_B,R2);	/* Read the vector */
+			if (vector & 0x01) break; 
+
+			scc = &SCC_Info[vector >> 3 ^ 0x01];
+		        if (!scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+		}
+
+		if (k == SCC_IRQTIMEOUT)
+		{
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+			break;
+		}
+
+		/* This looks wierd and it is. At least the BayCom USCC doesn't
+		 * use the Interrupt Daisy Chain, thus we'll have to start
+		 * all over again to be sure not to miss an interrupt from 
+		 * (any of) the other chip(s)...
+		 * Honestly, the situation *is* braindamaged...
+		 */
+
+		if (scc != NULL)
+		{
+			OutReg(scc->ctrl,R0,RES_H_IUS);
+			ctrl = SCC_ctrl; 
+		} else
+			ctrl++;
+	}
+	
+	scc_sti(irq);
+}
+
+
+
+/* ******************************************************************** */
+/* *			Init Channel					*/
+/* ******************************************************************** */
+
+
+/* ----> set SCC channel speed <---- */
+
+extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc)
+{
+	cl(scc,R14,BRENABL);		/* disable baudrate generator */
+	wr(scc,R12,tc & 255);		/* brg rate LOW */
+	wr(scc,R13,tc >> 8);   		/* brg rate HIGH */
+	or(scc,R14,BRENABL);		/* enable baudrate generator */
+}
+
+extern __inline__ void set_speed(struct scc_channel *scc)
+{
+	disable_irq(scc->irq);
+
+	if (scc->modem.speed > 0)	/* paranoia... */
+		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+	enable_irq(scc->irq);
+}
+
+
+/* ----> initialize a SCC channel <---- */
+
+extern __inline__ void init_brg(struct scc_channel *scc)
+{
+	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
+	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
+	OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]);	/* DPLL NRZI mode */
+}
+
+/*
+ * Initialization according to the Z8530 manual (SGS-Thomson's version):
+ *
+ * 1. Modes and constants
+ *
+ * WR9	11000000	chip reset
+ * WR4	XXXXXXXX	Tx/Rx control, async or sync mode
+ * WR1	0XX00X00	select W/REQ (optional)
+ * WR2	XXXXXXXX	program interrupt vector
+ * WR3	XXXXXXX0	select Rx control
+ * WR5	XXXX0XXX	select Tx control
+ * WR6	XXXXXXXX	sync character
+ * WR7	XXXXXXXX	sync character
+ * WR9	000X0XXX	select interrupt control
+ * WR10	XXXXXXXX	miscellaneous control (optional)
+ * WR11	XXXXXXXX	clock control
+ * WR12	XXXXXXXX	time constant lower byte (optional)
+ * WR13	XXXXXXXX	time constant upper byte (optional)
+ * WR14	XXXXXXX0	miscellaneous control
+ * WR14	XXXSSSSS	commands (optional)
+ *
+ * 2. Enables
+ *
+ * WR14	000SSSS1	baud rate enable
+ * WR3	SSSSSSS1	Rx enable
+ * WR5	SSSS1SSS	Tx enable
+ * WR0	10000000	reset Tx CRG (optional)
+ * WR1	XSS00S00	DMA enable (optional)
+ *
+ * 3. Interrupt status
+ *
+ * WR15	XXXXXXXX	enable external/status
+ * WR0	00010000	reset external status
+ * WR0	00010000	reset external status twice
+ * WR1	SSSXXSXX	enable Rx, Tx and Ext/status
+ * WR9	000SXSSS	enable master interrupt enable
+ *
+ * 1 = set to one, 0 = reset to zero
+ * X = user defined, S = same as previous init
+ *
+ *
+ * Note that the implementation differs in some points from above scheme.
+ *
+ */
+ 
+static void init_channel(struct scc_channel *scc)
+{
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
+	disable_irq(scc->irq);
+
+	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
+	wr(scc,R1,0);			/* no W/REQ operation */
+	wr(scc,R3,Rx8|RxCRC_ENAB);	/* RX 8 bits/char, CRC, disabled */	
+	wr(scc,R5,Tx8|DTR|TxCRC_ENAB);	/* TX 8 bits/char, disabled, DTR */
+	wr(scc,R6,0);			/* SDLC address zero (not used) */
+	wr(scc,R7,FLAG);		/* SDLC flag value */
+	wr(scc,R9,VIS);			/* vector includes status */
+	wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+	wr(scc,R14, 0);
+
+
+/* set clock sources:
+
+   CLK_DPLL: normal halfduplex operation
+   
+		RxClk: use DPLL
+		TxClk: use DPLL
+		TRxC mode DPLL output
+		
+   CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
+   
+  	        BayCom: 		others:
+  	        
+  	        TxClk = pin RTxC	TxClk = pin TRxC
+  	        RxClk = pin TRxC 	RxClk = pin RTxC
+  	     
+
+   CLK_DIVIDER:
+   		RxClk = use DPLL
+   		TxClk = pin RTxC
+   		
+   		BayCom:			others:
+   		pin TRxC = DPLL		pin TRxC = BRG
+   		(RxClk * 1)		(RxClk * 32)
+*/  
+
+   		
+	switch(scc->modem.clocksrc)
+	{
+		case CLK_DPLL:
+			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+			init_brg(scc);
+			break;
+
+		case CLK_DIVIDER:
+			wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+			init_brg(scc);
+			break;
+
+		case CLK_EXTERNAL:
+			wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+			OutReg(scc->ctrl, R14, DISDPLL);
+			break;
+
+	}
+	
+	set_speed(scc);			/* set baudrate */
+	
+	if(scc->enhanced)
+	{
+		or(scc,R15,SHDLCE|FIFOE);	/* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
+		wr(scc,R7,AUTOEOM);
+	}
+
+	if((InReg(scc->ctrl,R0)) & DCD)		/* DCD is now ON */
+	{
+		if (scc->modem.clocksrc != CLK_EXTERNAL)
+			or(scc,R14, SEARCH);
+			
+		or(scc,R3,ENT_HM|RxENABLE);	/* enable the receiver, hunt mode */
+	}
+	
+	/* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+	wr(scc,R15, BRKIE|TxUIE|DCDIE);
+	if (scc->kiss.softdcd)
+		or(scc,R15, SYNCIE);
+
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
+
+	or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+	
+	scc->status = InReg(scc->ctrl,R0);	/* read initial status */
+	
+	or(scc,R9,MIE);			/* master interrupt enable */
+	
+	scc_init_timer(scc);
+			
+	enable_irq(scc->irq);
+}
+
+
+
+
+/* ******************************************************************** */
+/* *			SCC timer functions			      * */
+/* ******************************************************************** */
+
+
+/* ----> scc_key_trx sets the time constant for the baudrate 
+         generator and keys the transmitter		     <---- */
+
+static void scc_key_trx(struct scc_channel *scc, char tx)
+{
+	unsigned int time_const;
+		
+	if (scc->brand & PRIMUS)
+		Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
+
+	if (scc->modem.speed < 300) 
+		scc->modem.speed = 1200;
+
+	time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
+
+	disable_irq(scc->irq);
+
+	if (tx)
+	{
+		or(scc, R1, TxINT_ENAB);	/* t_maxkeyup may have reset these */
+		or(scc, R15, TxUIE);
+	}
+
+	if (scc->modem.clocksrc == CLK_DPLL)
+	{				/* force simplex operation */
+		if (tx)
+		{
+#ifdef CONFIG_SCC_TRXECHO
+			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
+			cl(scc, R15, DCDIE);		/* No DCD changes, please */
+#endif
+			set_brg(scc, time_const);	/* reprogram baudrate generator */
+
+			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
+			wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
+			
+			or(scc,R5,RTS|TxENAB);		/* set the RTS line and enable TX */
+		} else {
+			cl(scc,R5,RTS|TxENAB);
+			
+			set_brg(scc, time_const);	/* reprogram baudrate generator */
+			
+			/* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
+			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+#ifdef CONFIG_SCC_TRXECHO
+			or(scc,R3,RxENABLE|ENT_HM);
+			or(scc,R15, DCDIE);
+#endif
+		}
+	} else {
+		if (tx)
+		{
+#ifdef CONFIG_SCC_TRXECHO
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				cl(scc, R3, RxENABLE);
+				cl(scc, R15, DCDIE);
+			}
+#endif
+				
+				
+			or(scc,R5,RTS|TxENAB);		/* enable tx */
+		} else {
+			cl(scc,R5,RTS|TxENAB);		/* disable tx */
+
+#ifdef CONFIG_SCC_TRXECHO
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				or(scc, R3, RxENABLE|ENT_HM);
+				or(scc, R15, DCDIE);
+			}
+#endif
+		}
+	}
+
+	enable_irq(scc->irq);
+}
+
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+	unsigned long flags;
+	
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_t);
+
+	if (when == 0)
+	{
+		handler((unsigned long) scc);
+	} else 
+	if (when != TIMER_OFF)
+	{
+		scc->tx_t.data = (unsigned long) scc;
+		scc->tx_t.function = handler;
+		scc->tx_t.expires = jiffies + (when*HZ)/100;
+		add_timer(&scc->tx_t);
+	}
+	
+	restore_flags(flags);
+}
+
+static void scc_start_defer(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_busy;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
+		add_timer(&scc->tx_wdog);
+	}
+	restore_flags(flags);
+}
+
+static void scc_start_maxkeyup(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_maxkeyup;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+		add_timer(&scc->tx_wdog);
+	}
+	
+	restore_flags(flags);
+}
+
+/* 
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void scc_tx_done(struct scc_channel *scc)
+{
+	/* 
+	 * trx remains keyed in fulldup mode 2 until t_idle expires.
+	 */
+				 
+	switch (scc->kiss.fulldup)
+	{
+		case KISS_DUPLEX_LINK:
+			scc->stat.tx_state = TXS_IDLE2;
+			if (scc->kiss.idletime != TIMER_OFF)
+			scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
+			break;
+		case KISS_DUPLEX_OPTIMA:
+			scc_notify(scc, HWEV_ALL_SENT);
+			break;
+		default:
+			scc->stat.tx_state = TXS_BUSY;
+			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+	}
+
+	scc_unlock_dev(scc);
+}
+
+
+static unsigned char Rand = 17;
+
+extern __inline__ int is_grouped(struct scc_channel *scc)
+{
+	int k;
+	struct scc_channel *scc2;
+	unsigned char grp1, grp2;
+
+	grp1 = scc->kiss.group;
+	
+	for (k = 0; k < (Nchips * 2); k++)
+	{
+		scc2 = &SCC_Info[k];
+		grp2 = scc2->kiss.group;
+		
+		if (scc2 == scc || !(scc2->dev && grp2))
+			continue;
+		
+		if ((grp1 & 0x3f) == (grp2 & 0x3f))
+		{
+			if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
+				return 1;
+			
+			if ( (grp1 & RXGROUP) && (scc2->status & DCD) )
+				return 1;
+		}
+	}
+	return 0;
+}
+
+/* DWAIT and SLOTTIME expired
+ *
+ * fulldup == 0:  DCD is active or Rand > P-persistence: start t_busy timer
+ *                else key trx and start txdelay
+ * fulldup == 1:  key trx and start txdelay
+ * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
+ */
+
+static void t_dwait(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	
+	if (scc->stat.tx_state == TXS_WAIT)	/* maxkeyup or idle timeout */
+	{
+		if (skb_queue_len(&scc->tx_queue) == 0)	/* nothing to send */
+		{
+			scc->stat.tx_state = TXS_IDLE;
+			scc_unlock_dev(scc);	/* t_maxkeyup locked it. */
+			return;
+		}
+
+		scc->stat.tx_state = TXS_BUSY;
+	}
+
+	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+	{
+		Rand = Rand * 17 + 31;
+		
+		if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD))  || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+		{
+			scc_start_defer(scc);
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
+			return ;
+		}
+	}
+
+	if ( !(scc->wreg[R5] & RTS) )
+	{
+		scc_key_trx(scc, TX_ON);
+		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+	} else {
+		scc_start_tx_timer(scc, t_txdelay, 0);
+	}
+}
+
+
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
+
+static void t_txdelay(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	scc_start_maxkeyup(scc);
+
+	if (scc->tx_buff == NULL)
+	{
+		disable_irq(scc->irq);
+		scc_txint(scc);	
+		enable_irq(scc->irq);
+	}
+}
+	
+
+/* TAILTIME expired
+ *
+ * switch off transmitter. If we were stopped by Maxkeyup restart
+ * transmission after 'mintime' seconds
+ */
+
+static void t_tail(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+	
+ 	save_flags(flags);
+ 	cli();
+ 
+ 	del_timer(&scc->tx_wdog);	
+ 	scc_key_trx(scc, TX_OFF);
+
+ 	restore_flags(flags);
+
+ 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
+ 	{
+ 		scc->stat.tx_state = TXS_WAIT;
+
+ 		if (scc->kiss.mintime != TIMER_OFF)	/* try it again */
+ 			scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ 		else
+ 			scc_start_tx_timer(scc, t_dwait, 0);
+ 		return;
+ 	}
+ 	
+ 	scc->stat.tx_state = TXS_IDLE;
+	scc_unlock_dev(scc);
+}
+
+
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void t_busy(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	del_timer(&scc->tx_t);
+	scc_lock_dev(scc);
+
+	scc_discard_buffers(scc);
+
+	scc->stat.txerrs++;
+	scc->stat.tx_state = TXS_IDLE;
+	
+	scc_unlock_dev(scc);
+}
+
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
+
+static void t_maxkeyup(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* 
+	 * let things settle down before we start to
+	 * accept new data.
+	 */
+
+	scc_lock_dev(scc);
+	scc_discard_buffers(scc);
+
+	del_timer(&scc->tx_t);
+
+	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
+	cl(scc, R15, TxUIE);		/* count it. */
+	OutReg(scc->ctrl, R0, RES_Tx_P);
+
+	restore_flags(flags);
+
+	scc->stat.txerrs++;
+	scc->stat.tx_state = TXS_TIMEOUT;
+	scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+}
+
+/* IDLE timeout
+ *
+ * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
+ * of inactivity. We will not restart transmission before 'mintime'
+ * expires.
+ */
+
+static void t_idle(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	
+	del_timer(&scc->tx_wdog);
+
+	scc_key_trx(scc, TX_OFF);
+
+	if (scc->kiss.mintime != TIMER_OFF)
+		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+	scc->stat.tx_state = TXS_WAIT;
+}
+
+static void scc_init_timer(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags); 
+	cli();
+	
+	scc->stat.tx_state = TXS_IDLE;
+
+	restore_flags(flags);
+}
+
+
+/* ******************************************************************** */
+/* *			Set/get L1 parameters			      * */
+/* ******************************************************************** */
+
+
+/*
+ * this will set the "hardware" parameters through KISS commands or ioctl()
+ */
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
+{
+	int dcd;
+
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	scc->kiss.txdelay=arg;		break;
+		case PARAM_PERSIST:	scc->kiss.persist=arg;		break;
+		case PARAM_SLOTTIME:	scc->kiss.slottime=arg;		break;
+		case PARAM_TXTAIL:	scc->kiss.tailtime=arg;		break;
+		case PARAM_FULLDUP:	scc->kiss.fulldup=arg;		break;
+		case PARAM_DTR:		break; /* does someone need this? */
+		case PARAM_GROUP:	scc->kiss.group=arg;		break;
+		case PARAM_IDLE:	scc->kiss.idletime=arg;		break;
+		case PARAM_MIN:		scc->kiss.mintime=arg;		break;
+		case PARAM_MAXKEY:	scc->kiss.maxkeyup=arg;		break;
+		case PARAM_WAIT:	scc->kiss.waittime=arg;		break;
+		case PARAM_MAXDEFER:	scc->kiss.maxdefer=arg;		break;
+		case PARAM_TX:		scc->kiss.tx_inhibit=arg;	break;
+
+		case PARAM_SOFTDCD:	
+			scc->kiss.softdcd=arg;
+			if (arg)
+				or(scc, R15, SYNCIE);
+			else
+				cl(scc, R15, SYNCIE);
+			break;
+				
+		case PARAM_SPEED:
+			if (arg < 256)
+				scc->modem.speed=arg*100;
+			else
+				scc->modem.speed=arg;
+
+			if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
+				set_speed(scc);
+			break;
+			
+		case PARAM_RTS:	
+			if ( !(scc->wreg[R5] & RTS) )
+			{
+				if (arg != TX_OFF)
+					scc_key_trx(scc, TX_ON);
+					scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+			} else {
+				if (arg == TX_OFF)
+				{
+					scc->stat.tx_state = TXS_BUSY;
+					scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+				}
+			}
+			break;
+			
+		case PARAM_HWEVENT:
+			dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
+			scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+			break;
+
+		default:		return -EINVAL;
+	}
+	
+	return 0;
+}
+
+
+ 
+static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
+{
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	return CAST(scc->kiss.txdelay);
+		case PARAM_PERSIST:	return CAST(scc->kiss.persist);
+		case PARAM_SLOTTIME:	return CAST(scc->kiss.slottime);
+		case PARAM_TXTAIL:	return CAST(scc->kiss.tailtime);
+		case PARAM_FULLDUP:	return CAST(scc->kiss.fulldup);
+		case PARAM_SOFTDCD:	return CAST(scc->kiss.softdcd);
+		case PARAM_DTR:		return CAST((scc->wreg[R5] & DTR)? 1:0);
+		case PARAM_RTS:		return CAST((scc->wreg[R5] & RTS)? 1:0);
+		case PARAM_SPEED:	return CAST(scc->modem.speed);
+		case PARAM_GROUP:	return CAST(scc->kiss.group);
+		case PARAM_IDLE:	return CAST(scc->kiss.idletime);
+		case PARAM_MIN:		return CAST(scc->kiss.mintime);
+		case PARAM_MAXKEY:	return CAST(scc->kiss.maxkeyup);
+		case PARAM_WAIT:	return CAST(scc->kiss.waittime);
+		case PARAM_MAXDEFER:	return CAST(scc->kiss.maxdefer);
+		case PARAM_TX:		return CAST(scc->kiss.tx_inhibit);
+		default:		return NO_SUCH_PARAM;
+	}
+
+}
+
+#undef CAST
+#undef SVAL
+
+/* ******************************************************************* */
+/* *			Send calibration pattern		     * */
+/* ******************************************************************* */
+
+static void scc_stop_calibrate(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	scc_key_trx(scc, TX_OFF);
+	wr(scc, R6, 0);
+	wr(scc, R7, FLAG);
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	scc_unlock_dev(scc);
+	
+	restore_flags(flags);
+}
+
+
+static void
+scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	scc_lock_dev(scc);
+	scc_discard_buffers(scc);
+
+	del_timer(&scc->tx_wdog);
+
+	scc->tx_wdog.data = (unsigned long) scc;
+	scc->tx_wdog.function = scc_stop_calibrate;
+	scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+	add_timer(&scc->tx_wdog);
+	
+	wr(scc, R6, 0);
+	wr(scc, R7, pattern);
+
+	/* 
+	 * Don't know if this works. 
+	 * Damn, where is my Z8530 programming manual...? 
+	 */
+
+	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
+	Outb(scc->ctrl,RES_EXT_INT);
+
+	scc_key_trx(scc, TX_ON);
+	
+	restore_flags(flags);
+}
+
+/* ******************************************************************* */
+/* *		Init channel structures, special HW, etc...	     * */
+/* ******************************************************************* */
+
+/*
+ * Reset the Z8530s and setup special hardware
+ */
+
+static void z8530_init(void)
+{
+	struct scc_channel *scc;
+	int chip, k;
+	unsigned long flags;
+	char *flag;
+
+
+	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
+	
+	flag=" ";
+	for (k = 0; k < 16; k++)
+		if (Ivec[k].used) 
+		{
+			printk("%s%d", flag, k);
+			flag=",";
+		}
+	printk("\n");
+
+
+	/* reset and pre-init all chips in the system */
+	for (chip = 0; chip < Nchips; chip++)
+	{
+		scc=&SCC_Info[2*chip];
+		if (!scc->ctrl) continue;
+
+		/* Special SCC cards */
+
+		if(scc->brand & EAGLE)			/* this is an EAGLE card */
+			Outb(scc->special,0x08);	/* enable interrupt on the board */
+			
+		if(scc->brand & (PC100 | PRIMUS))	/* this is a PC100/PRIMUS card */
+			Outb(scc->special,scc->option);	/* set the MODEM mode (0x22) */
+
+			
+		/* Reset and pre-init Z8530 */
+
+		save_flags(flags);
+		cli();
+		
+		Outb(scc->ctrl, 0);
+		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
+		udelay(100);				/* give it 'a bit' more time than required */
+		wr(scc, R2, chip*16);			/* interrupt vector */
+		wr(scc, R9, VIS);			/* vector includes status */
+		
+        	restore_flags(flags);
+        }
+
+ 
+	Driver_Initialized = 1;
+}
+
+/*
+ * Allocate device structure, err, instance, and register driver
+ */
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name)
+{
+	unsigned char *buf;
+	struct device *dev;
+
+	if (dev_get(name) != NULL)
+	{
+		printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+		return -EEXIST;
+	}
+
+	if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	dev = scc->dev;
+	memset(dev, 0, sizeof(struct device));
+
+	buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+	strcpy(buf, name);
+
+	dev->priv = (void *) scc;
+	dev->name = buf;
+	dev->init = scc_net_init;
+
+	if (register_netdev(dev) != 0)
+	{
+		kfree(dev);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+
+/* ******************************************************************** */
+/* *			    Network driver methods		      * */
+/* ******************************************************************** */
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* ----> Initialize device <----- */
+
+static int scc_net_init(struct device *dev)
+{
+	dev_init_buffers(dev);
+	
+	dev->tx_queue_len    = 16;	/* should be enough... */
+
+	dev->open            = scc_net_open;
+	dev->stop	     = scc_net_close;
+
+	dev->hard_start_xmit = scc_net_tx;
+	dev->hard_header     = scc_net_header;
+	dev->rebuild_header  = ax25_rebuild_header;
+	dev->set_mac_address = scc_net_set_mac_address;
+	dev->get_stats       = scc_net_get_stats;
+	dev->do_ioctl        = scc_net_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
+ 
+	dev->flags      = 0;
+
+	dev->type = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;
+	dev->addr_len = AX25_ADDR_LEN;
+
+	return 0;
+}
+
+/* ----> open network device <---- */
+
+static int scc_net_open(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+ 	if (!scc->init)
+		return -EINVAL;
+
+	MOD_INC_USE_COUNT;
+	
+	scc->tx_buff = NULL;
+	skb_queue_head_init(&scc->tx_queue);
+ 
+	init_channel(scc);
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	return 0;
+}
+
+/* ----> close network device <---- */
+
+static int scc_net_close(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+	MOD_DEC_USE_COUNT;
+
+	save_flags(flags); 
+	cli();
+	
+	Outb(scc->ctrl,0);		/* Make sure pointer is written */
+	wr(scc,R1,0);			/* disable interrupts */
+	wr(scc,R3,0);
+
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
+	restore_flags(flags);
+	
+	scc_discard_buffers(scc);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
+{
+	if (skb->len == 0)
+	{
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+		
+	scc->dev_stat.rx_packets++;
+
+	skb->dev      = scc->dev;
+	skb->protocol = htons(ETH_P_AX25);
+	skb->mac.raw  = skb->data;
+	
+	netif_rx(skb);
+	return;
+}
+
+/* ----> transmit frame <---- */
+
+static int scc_net_tx(struct sk_buff *skb, struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+	char kisscmd;
+	
+	if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy)
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	if (skb->len > scc->stat.bufsize || skb->len < 2)
+	{
+		scc->dev_stat.tx_dropped++;	/* bogus frame */
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+	
+	scc->dev_stat.tx_packets++;
+	scc->stat.txframes++;
+	
+	kisscmd = *skb->data & 0x1f;
+	skb_pull(skb, 1);
+
+	if (kisscmd)
+	{
+		scc_set_param(scc, kisscmd, *skb->data);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	save_flags(flags);
+	cli();
+	
+	if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
+	{
+		struct sk_buff *skb_del;
+		skb_del = __skb_dequeue(&scc->tx_queue);
+		dev_kfree_skb(skb_del, FREE_WRITE);
+	}
+	__skb_queue_tail(&scc->tx_queue, skb);
+
+	dev->trans_start = jiffies;
+
+	/*
+	 * Start transmission if the trx state is idle or
+	 * t_idle hasn't expired yet. Use dwait/persistance/slottime
+	 * algorithm for normal halfduplex operation.
+	 */
+
+	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
+	{
+		scc->stat.tx_state = TXS_BUSY;
+		if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
+		else
+			scc_start_tx_timer(scc, t_dwait, 0);
+	}
+
+	restore_flags(flags);
+
+	return 0;
+}
+
+/* ----> ioctl functions <---- */
+
+/*
+ * SIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
+ * SIOCSCCINI		- initialize driver	arg: ---
+ * SIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
+ * SIOCSCCSMEM		- set memory		arg: (struct scc_mem_config *) arg
+ * SIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
+ * SIOCSCCCAL		- send calib. pattern	arg: (struct scc_calibrate *) arg
+ */
+
+static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct scc_kiss_cmd kiss_cmd;
+	struct scc_mem_config memcfg;
+	struct scc_hw_config hwcfg;
+	struct scc_calibrate cal;
+	int chan;
+	unsigned char device_name[10];
+	void *arg;
+	struct scc_channel *scc;
+	
+	scc = (struct scc_channel *) dev->priv;
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -EINVAL;
+		
+	arg = (void *) ifr->ifr_data;
+	
+	if (!Driver_Initialized)
+	{
+		if (cmd == SIOCSCCCFG)
+		{
+			int found = 1;
+
+			if (!suser()) return -EPERM;
+			if (!arg) return -EFAULT;
+
+			if (Nchips >= MAXSCC) 
+				return -EINVAL;
+
+			if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
+				return -EFAULT;
+
+			if (hwcfg.irq == 2) hwcfg.irq = 9;
+
+			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
+			{
+				if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
+					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
+				else
+					Ivec[hwcfg.irq].used = 1;
+			}
+
+			if (hwcfg.vector_latch) 
+				Vector_Latch = hwcfg.vector_latch;
+
+			if (hwcfg.clock == 0)
+				hwcfg.clock = DEFAULT_CLOCK;
+
+#ifndef DONT_CHECK
+			disable_irq(hwcfg.irq);
+
+			check_region(scc->ctrl, 1);
+			Outb(hwcfg.ctrl_a, 0);
+			udelay(5);
+			OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
+			udelay(5);
+
+			if (InReg(hwcfg.ctrl_a,R13) != 0x55)
+				found = 0;
+
+			enable_irq(hwcfg.irq);
+#endif
+
+			if (found)
+			{
+				SCC_Info[2*Nchips  ].ctrl = hwcfg.ctrl_a;
+				SCC_Info[2*Nchips  ].data = hwcfg.data_a;
+				SCC_Info[2*Nchips  ].irq  = hwcfg.irq;
+				SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
+				SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+				SCC_Info[2*Nchips+1].irq  = hwcfg.irq;
+			
+				SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+				SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+				SCC_ctrl[Nchips].irq    = hwcfg.irq;
+			}
+
+
+			for (chan = 0; chan < 2; chan++)
+			{
+				sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+
+				SCC_Info[2*Nchips+chan].special = hwcfg.special;
+				SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
+				SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
+				SCC_Info[2*Nchips+chan].option = hwcfg.option;
+				SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
+
+#ifdef DONT_CHECK
+				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x\n",
+					device_name, 
+					SCC_Info[2*Nchips+chan].data, 
+					SCC_Info[2*Nchips+chan].ctrl);
+
+#else
+				printk(KERN_INFO "%s: data port = 0x%3.3lx  control port = 0x%3.3lx -- %s\n",
+					device_name,
+					chan? hwcfg.data_b : hwcfg.data_a, 
+					chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
+					found? "found" : "missing");
+#endif
+
+				if (found)
+				{
+					request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
+					request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+					if (Nchips+chan != 0)
+						scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
+				}
+			}
+			
+			if (found) Nchips++;
+			
+			return 0;
+		}
+		
+		if (cmd == SIOCSCCINI)
+		{
+			if (!suser())
+				return -EPERM;
+				
+			if (Nchips == 0)
+				return -EINVAL;
+
+			z8530_init();
+			return 0;
+		}
+		
+		return -EINVAL;	/* confuse the user */
+	}
+	
+	if (!scc->init)
+	{
+		if (cmd == SIOCSCCCHANINI)
+		{
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			
+			scc->stat.bufsize   = BUFSIZE;
+
+			if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
+				return -EINVAL;
+			
+			/* default KISS Params */
+		
+			if (scc->modem.speed < 4800)
+			{
+				scc->kiss.txdelay = 36;		/* 360 ms */
+				scc->kiss.persist = 42;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 16;	/* 160 ms */
+				scc->kiss.tailtime = 4;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 10;	/* 10 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
+			} else {
+				scc->kiss.txdelay = 10;		/* 100 ms */
+				scc->kiss.persist = 64;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 8;		/* 160 ms */
+				scc->kiss.tailtime = 1;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 7;		/* 7 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
+			}
+			
+			scc->tx_buff = NULL;
+			skb_queue_head_init(&scc->tx_queue);
+			scc->init = 1;
+			
+			return 0;
+		}
+		
+		return -EINVAL;
+	}
+	
+	switch(cmd)
+	{
+		case SIOCSCCRESERVED:
+			return -ENOIOCTLCMD;
+
+		case SIOCSCCSMEM:
+			if (!suser()) return -EPERM;
+			if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
+				return -EINVAL;
+			scc->stat.bufsize   = memcfg.bufsize;
+			return 0;
+		
+		case SIOCSCCGSTAT:
+			if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
+				return -EINVAL;
+			return 0;
+		
+		case SIOCSCCGKISS:
+			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+				return -EINVAL;
+			kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
+			if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
+				return -EINVAL;
+			return 0;
+		
+		case SIOCSCCSKISS:
+			if (!suser()) return -EPERM;
+			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
+				return -EINVAL;
+			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
+		
+		case SIOCSCCCAL:
+			if (!suser()) return -EPERM;
+			if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
+				return -EINVAL;
+
+			scc_start_calibrate(scc, cal.time, cal.pattern);
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		
+	}
+	
+	return -EINVAL;
+}
+
+/* ----> set interface callsign <---- */
+
+static int scc_net_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *) addr;
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+}
+
+/* ----> "hard" header <---- */
+
+static int  scc_net_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+	return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* ----> get statistics <---- */
+
+static struct net_device_stats *scc_net_get_stats(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return NULL;
+		
+	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+	scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+	return &scc->dev_stat;
+}
+
+/* ******************************************************************** */
+/* *		dump statistics to /proc/net/z8530drv		      * */
+/* ******************************************************************** */
+
+
+static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct scc_channel *scc;
+	struct scc_kiss *kiss;
+	struct scc_stat *stat;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	int k;
+
+	len += sprintf(buffer, "z8530drv-"VERSION"\n");
+
+	if (!Driver_Initialized)
+	{
+		len += sprintf(buffer+len, "not initialized\n");
+		goto done;
+	}
+
+	if (!Nchips)
+	{
+		len += sprintf(buffer+len, "chips missing\n");
+		goto done;
+	}
+
+	for (k = 0; k < Nchips*2; k++)
+	{
+		scc = &SCC_Info[k];
+		stat = &scc->stat;
+		kiss = &scc->kiss;
+
+		if (!scc->init)
+			continue;
+
+		/* dev	data ctrl irq clock brand enh vector special option 
+		 *	baud nrz clocksrc softdcd bufsize
+		 *	rxints txints exints spints
+		 *	rcvd rxerrs over / xmit txerrs under / nospace bufsize
+		 *	txd pers slot tail ful wait min maxk idl defr txof grp
+		 *	W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
+		 *	R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
+		 */
+
+		len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
+				scc->dev->name,
+				scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
+				scc->enhanced, Vector_Latch, scc->special,
+				scc->option);
+		len += sprintf(buffer+len, "\t%lu %d %d %d %d\n",
+				scc->modem.speed, scc->modem.nrz,
+				scc->modem.clocksrc, kiss->softdcd,
+				stat->bufsize);
+		len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n",
+				stat->rxints, stat->txints, stat->exints, stat->spints);
+		len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
+				stat->rxframes, stat->rxerrs, stat->rx_over,
+				stat->txframes, stat->txerrs, stat->tx_under,
+				stat->nospace,  stat->tx_state);
+
+#define K(x) kiss->x
+		len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n",
+				K(txdelay), K(persist), K(slottime), K(tailtime),
+				K(fulldup), K(waittime), K(mintime), K(maxkeyup),
+				K(idletime), K(maxdefer), K(tx_inhibit), K(group));
+#undef K
+#ifdef SCC_DEBUG
+		{
+			int reg;
+
+		len += sprintf(buffer+len, "\tW ");
+			for (reg = 0; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+			len += sprintf(buffer+len, "\n");
+			
+		len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+			for (reg = 3; reg < 8; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "XX ");
+			for (reg = 9; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "\n");
+		}
+#endif
+		len += sprintf(buffer+len, "\n");
+
+                pos = begin + len;
+
+                if (pos < offset) {
+                        len   = 0;
+                        begin = pos;
+                }
+
+                if (pos > offset + length)
+                        break;
+	}
+
+done:
+
+        *start = buffer + (offset - begin);
+        len   -= (offset - begin);
+
+        if (len > length) len = length;
+
+        return len;
+}
+
+#ifdef CONFIG_PROC_FS
+
+struct proc_dir_entry scc_proc_dir_entry = 
+{ 
+	PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
+	&proc_net_inode_operations, scc_net_get_info 
+};
+
+#define scc_net_procfs_init()   proc_net_register(&scc_proc_dir_entry);
+#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
+#else
+#define scc_net_procfs_init()
+#define scc_net_procfs_remove()
+#endif
+
+  
+/* ******************************************************************** */
+/* * 			Init SCC driver 			      * */
+/* ******************************************************************** */
+
+__initfunc(int scc_init (void))
+{
+	int chip, chan, k, result;
+	char devname[10];
+	
+	printk(KERN_INFO BANNER);
+	
+	memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
+	
+	/* pre-init channel information */
+	
+	for (chip = 0; chip < MAXSCC; chip++)
+	{
+		memset((char *) &SCC_Info[2*chip  ], 0, sizeof(struct scc_channel));
+		memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
+		
+		for (chan = 0; chan < 2; chan++)
+			SCC_Info[2*chip+chan].magic = SCC_MAGIC;
+	}
+
+	for (k = 0; k < 16; k++) Ivec[k].used = 0;
+
+	sprintf(devname,"%s0", SCC_DriverName);
+	
+	result = scc_net_setup(SCC_Info, devname);
+	if (result)
+	{
+		printk(KERN_ERR "z8530drv: cannot initialize module\n");
+		return result;
+	}
+
+	scc_net_procfs_init();
+
+	return 0;
+}
+
+/* ******************************************************************** */
+/* *			    Module support 			      * */
+/* ******************************************************************** */
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	int result = 0;
+	
+	result = scc_init();
+
+	if (result == 0)
+		printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n");
+		
+	return result;
+}
+
+void cleanup_module(void)
+{
+	long flags;
+	io_port ctrl;
+	int k;
+	struct scc_channel *scc;
+	
+	save_flags(flags); 
+	cli();
+
+	if (Nchips == 0)
+		unregister_netdev(SCC_Info[0].dev);
+
+	for (k = 0; k < Nchips; k++)
+		if ( (ctrl = SCC_ctrl[k].chan_A) )
+		{
+			Outb(ctrl, 0);
+			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
+			udelay(50);
+		}
+		
+	for (k = 0; k < Nchips*2; k++)
+	{
+		scc = &SCC_Info[k];
+		if (scc)
+		{
+			release_region(scc->ctrl, 1);
+			release_region(scc->data, 1);
+			if (scc->dev)
+			{
+				unregister_netdev(scc->dev);
+				kfree(scc->dev);
+			}
+		}
+	}
+	
+	for (k=0; k < 16 ; k++)
+		if (Ivec[k].used) free_irq(k, NULL);
+		
+	restore_flags(flags);
+
+	scc_net_procfs_remove();
+}
+#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/Makefile linux/drivers/net/hamradio/soundmodem/Makefile
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/Makefile	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,60 @@
+#
+# Makefile for the soundmodem device driver.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET	:= soundmodem.o
+
+O_OBJS		:= sm.o
+ifeq ($(CONFIG_SOUNDMODEM_SBC),y)
+O_OBJS		+= sm_sbc.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_WSS),y)
+O_OBJS		+= sm_wss.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y)
+O_OBJS		+= sm_afsk1200.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y)
+O_OBJS		+= sm_afsk2400_7.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y)
+O_OBJS		+= sm_afsk2400_8.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y)
+O_OBJS		+= sm_afsk2666.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y)
+O_OBJS		+= sm_hapn4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y)
+O_OBJS		+= sm_psk4800.o
+endif
+ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y)
+O_OBJS		+= sm_fsk9600.o
+endif
+
+M_OBJS		:= $(O_TARGET)
+
+all:		all_targets
+.PHONY:		all
+
+gentbl:		gentbl.c
+		$(HOSTCC) -Wall $< -o $@ -lm
+
+TBLHDR		:= sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h 
+TBLHDR		+= sm_tbl_afsk2666.h sm_tbl_psk4800.h
+TBLHDR		+= sm_tbl_hapn4800.h sm_tbl_fsk9600.h
+
+$(TBLHDR):	gentbl
+		./gentbl
+
+fastdep:	$(TBLHDR)
+
+include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/gentbl.c linux/drivers/net/hamradio/soundmodem/gentbl.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/gentbl.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/gentbl.c	Tue Aug  5 09:49:51 1997
@@ -0,0 +1,676 @@
+/*****************************************************************************/
+
+/*
+ *      gentbl.c  -- soundcard radio modem driver table generator.
+ *
+ *      Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *      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.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_offscostab(FILE *f, unsigned int nbits)
+{
+	int i;
+
+	fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
+		"#define OFFSCOSTABBITS %u\n"
+		"#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n", 
+		nbits);
+	fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
+	for (i = 0; i < (1<<nbits); i++) {
+		fprintf(f, "%4u", (int)
+			(128+127.0*cos(i*2.0*M_PI/(1<<nbits))));
+		if (i < (1<<nbits)-1) 
+			fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
+	}
+	fprintf(f, "\n};\n\n"
+		"#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
+		16-nbits, (1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_costab(FILE *f, unsigned int nbits)
+{
+        int i;
+
+	fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
+		"static const short costab[%d] = {", (1<<nbits));
+        for (i = 0; i < (1<<nbits); i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits))));
+                if (i != ((1<<nbits)-1))
+                        fprintf(f, ", ");
+        }
+        fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
+		"#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits,
+		(1<<nbits)-1);
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK12_SAMPLE_RATE 9600
+#define AFSK12_TX_FREQ_LO 1200
+#define AFSK12_TX_FREQ_HI 2200
+#define AFSK12_CORRLEN 8
+
+static void gentbl_afsk1200(FILE *f)
+{
+        int i, v, sum;
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
+
+	fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
+		"#define AFSK12_SAMPLE_RATE %u\n"
+		"#define AFSK12_TX_FREQ_LO %u\n"
+		"#define AFSK12_TX_FREQ_HI %u\n"
+		"#define AFSK12_CORRLEN %u\n\n",
+		AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, 
+		AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
+	fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGLO(i)));
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n"
+		"static const int afsk12_tx_lo_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*sin(ARGLO(i))); 
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n"
+		"static const int afsk12_tx_hi_i[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGHI(i))); 
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n"
+		"static const int afsk12_tx_hi_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK12_CORRLEN; i++)  {
+		sum += (v = 127.0*sin(ARGHI(i)));
+                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
+	}
+	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+}
+
+/* -------------------------------------------------------------------- */
+
+static const float fsk96_tx_coeff_4[32] = {
+              -0.001152,        0.000554,        0.002698,        0.002753,
+              -0.002033,       -0.008861,       -0.008002,        0.006607,
+               0.023727,        0.018905,       -0.018056,       -0.057957,
+              -0.044368,        0.055683,        0.207667,        0.322048,
+               0.322048,        0.207667,        0.055683,       -0.044368,
+              -0.057957,       -0.018056,        0.018905,        0.023727,
+               0.006607,       -0.008002,       -0.008861,       -0.002033,
+               0.002753,        0.002698,        0.000554,       -0.001152
+};
+
+static const float fsk96_tx_coeff_5[40] = {
+              -0.001009,       -0.000048,        0.001376,        0.002547,
+               0.002061,       -0.001103,       -0.005795,       -0.008170,
+              -0.004017,        0.006924,        0.018225,        0.019238,
+               0.002925,       -0.025777,       -0.048064,       -0.039683,
+               0.013760,        0.104144,        0.200355,        0.262346,
+               0.262346,        0.200355,        0.104144,        0.013760,
+              -0.039683,       -0.048064,       -0.025777,        0.002925,
+               0.019238,        0.018225,        0.006924,       -0.004017,
+              -0.008170,       -0.005795,       -0.001103,        0.002061,
+               0.002547,        0.001376,       -0.000048,       -0.001009
+};
+
+#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
+
+static inline float hamming(float x)
+{
+	return 0.54-0.46*cos(2*M_PI*x);
+}
+
+static inline float sinc(float x)
+{
+	if (x == 0)
+		return 1;
+	x *= M_PI;
+	return sin(x)/x;
+}
+
+static void gentbl_fsk9600(FILE *f)
+{
+        int i, j, k, l, m;
+	float s;
+	float c[44];
+	float min, max;
+
+	fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
+#else
+	for (i = 0; i < 29; i++)
+		c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 4; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 4; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 3 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+#if 0
+	memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
+#else
+	for (i = 0; i < 36; i++)
+		c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
+#endif
+	fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
+	for (i = 0; i < 5; i++) {
+		for (j = 0; j < 256; j++) {
+			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
+				if (j & k) {
+					for (m = 0; m < 5; m++, l++)
+						s += c[l];
+				} else {
+					for (m = 0; m < 5; m++, l++)
+						s -= c[l];
+				}
+			}
+			s *= 0.75;
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 4 || j < 255)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+}
+	
+/* -------------------------------------------------------------------- */
+
+#define AFSK26_SAMPLERATE  16000
+
+#define AFSK26_NUMCAR      2
+#define AFSK26_FIRSTCAR    2000
+#define AFSK26_MSK_LEN     6
+#define AFSK26_RXOVER      2
+
+#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
+
+#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
+
+#define AFSK26_AMPL(x) (((x)?1.0:0.7))
+
+#undef AFSK26_AMPL
+#define AFSK26_AMPL(x) 1
+
+static void gentbl_afsk2666(FILE *f)
+{
+        int i, j, k, l, o, v, sumi, sumq;
+        float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
+	int cfreq[AFSK26_NUMCAR];
+
+	fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
+		"#define AFSK26_DEMCORRLEN %d\n"
+		"#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
+		AFSK26_SAMPLERATE);
+	fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
+		AFSK26_NUMCAR);
+	for (i = 0; i < AFSK26_NUMCAR; i++) {
+		cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
+			0x10000*i/AFSK26_MSK_LEN/2;
+		fprintf(f, "0x%x", cfreq[i]);
+		if (i < AFSK26_NUMCAR-1)
+			fprintf(f, ", ");
+	}
+	fprintf(f, " };\n\n");
+        for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
+                window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
+						      AFSK26_RXOVER)) * 127.0;
+        fprintf(f, "\nstatic const struct {\n\t"
+               "int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n", 
+               AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR);
+        for (o = AFSK26_RXOVER-1; o >= 0; o--) {
+                fprintf(f, "\t{\n");
+                for (i = 0; i < AFSK26_NUMCAR; i++) {
+                        j = cfreq[i];
+                        fprintf(f, "\t\t{{ ");
+                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0; 
+                             l--, k = (k+j)&0xffffu) {
+				sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+					 cos(M_PI*k/32768.0));
+                                fprintf(f, "%6d%s", v, l ? ", " : " }, { ");
+			}
+                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0; 
+                             l--, k = (k+j)&0xffffu) {
+				sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
+					 sin(M_PI*k/32768.0));
+                                fprintf(f, "%6d%s", v, l ? ", " : " }}");
+			}
+                        if (i < 1)
+                                fprintf(f, ",");
+                        fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n"
+				"#define AFSK26_DEM_SUM_Q_%d_%d %d\n", 
+				AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq);
+                }
+                fprintf(f, "\t}%s\n", o ? "," : "");
+        }
+        fprintf(f, "};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+#define ATAN_TABLEN 1024
+
+static void gentbl_atantab(FILE *f)
+{
+        int i;
+        short x;
+
+	fprintf(f, "\n/*\n"
+		" * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
+		" */\n""#define ATAN_TABLEN %d\n\n"
+		"static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
+               ATAN_TABLEN);
+        for (i = 0; i <= ATAN_TABLEN; i++) {
+                if (!(i & 7))
+                        fprintf(f, "\n\t");
+                x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
+                fprintf(f, "%6d, ", x);
+        }
+        fprintf(f, "%6d\n};\n\n", x);
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+#define PSK48_TXF_OVERSAMPLING 5
+#define PSK48_TXF_NUMSAMPLES 16
+#define PSK48_RXF_LEN     64
+
+static const float psk48_tx_coeff[80] = {
+              -0.000379,       -0.000640,       -0.000000,        0.000772,
+               0.000543,       -0.000629,       -0.001187,       -0.000000,
+               0.001634,        0.001183,       -0.001382,       -0.002603,
+              -0.000000,        0.003481,        0.002472,       -0.002828,
+              -0.005215,       -0.000000,        0.006705,        0.004678,
+              -0.005269,       -0.009584,       -0.000000,        0.012065,
+               0.008360,       -0.009375,       -0.017028,       -0.000000,
+               0.021603,        0.015123,       -0.017229,       -0.032012,
+              -0.000000,        0.043774,        0.032544,       -0.040365,
+              -0.084963,       -0.000000,        0.201161,        0.374060,
+               0.374060,        0.201161,       -0.000000,       -0.084963,
+              -0.040365,        0.032544,        0.043774,       -0.000000,
+              -0.032012,       -0.017229,        0.015123,        0.021603,
+              -0.000000,       -0.017028,       -0.009375,        0.008360,
+               0.012065,       -0.000000,       -0.009584,       -0.005269,
+               0.004678,        0.006705,       -0.000000,       -0.005215,
+              -0.002828,        0.002472,        0.003481,       -0.000000,
+              -0.002603,       -0.001382,        0.001183,        0.001634,
+              -0.000000,       -0.001187,       -0.000629,        0.000543,
+               0.000772,       -0.000000,       -0.000640,       -0.000379
+};
+
+static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
+              -0.000219,        0.000360,        0.000873,        0.001080,
+               0.000747,       -0.000192,       -0.001466,       -0.002436,
+              -0.002328,       -0.000699,        0.002101,        0.004809,
+               0.005696,        0.003492,       -0.001633,       -0.007660,
+              -0.011316,       -0.009627,       -0.001780,        0.009712,
+               0.019426,        0.021199,        0.011342,       -0.008583,
+              -0.030955,       -0.044093,       -0.036634,       -0.002651,
+               0.054742,        0.123101,        0.184198,        0.220219,
+               0.220219,        0.184198,        0.123101,        0.054742,
+              -0.002651,       -0.036634,       -0.044093,       -0.030955,
+              -0.008583,        0.011342,        0.021199,        0.019426,
+               0.009712,       -0.001780,       -0.009627,       -0.011316,
+              -0.007660,       -0.001633,        0.003492,        0.005696,
+               0.004809,        0.002101,       -0.000699,       -0.002328,
+              -0.002436,       -0.001466,       -0.000192,        0.000747,
+               0.001080,        0.000873,        0.000360,       -0.000219
+};
+
+static void gentbl_psk4800(FILE *f)
+{
+        int i, j, k;
+        short x;
+
+	fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
+		"#define PSK48_TXF_OVERSAMPLING %d\n"
+		"#define PSK48_TXF_NUMSAMPLES %d\n\n"
+		"#define PSK48_SAMPLERATE  8000\n"
+		"#define PSK48_CAR_FREQ    2000\n"
+		"#define PSK48_PSK_LEN     5\n"
+		"#define PSK48_RXF_LEN     %u\n"
+		"#define PSK48_PHASEINC    (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
+		"#define PSK48_SPHASEINC   (0x10000/(2*PSK48_PSK_LEN))\n\n"
+		"static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
+		"PSK48_TXF_NUMSAMPLES*8*2] = {", 
+		PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
+        for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
+                for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * cos(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d, ", x);
+                        }
+                        fprintf(f, "\n\t");
+                        for (k = 0; k < 8; k++) {
+                                x = 32767.0 * sin(k*M_PI/4.0) * 
+                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
+                                fprintf(f, "%6d", x);
+                                if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 || 
+                                    i != PSK48_TXF_OVERSAMPLING-1)
+                                        fprintf(f, ", ");
+                        }
+                }
+        }
+        fprintf(f, "\n};\n\n");
+
+	fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
+	for (i = 0; i < PSK48_RXF_LEN; i++) {
+		fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
+		if (i < PSK48_RXF_LEN-1)
+			fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
+	}
+	fprintf(f, "\n};\n\n");
+}
+
+/* -------------------------------------------------------------------- */
+
+static void gentbl_hapn4800(FILE *f)
+{
+        int i, j, k, l;
+	float s;
+	float c[40];
+	float min, max;
+
+	fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
+	/*
+	 * firstly generate tables for the FM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 24; i++)
+		c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
+	for (i = 0; i < 24; i++)
+		c[i] -= c[i+8];
+	fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 30; i++)
+		c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
+	for (i = 0; i < 30; i++)
+		c[i] -= c[i+10];
+	fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	/*
+	 * secondly generate tables for the PM transmitter modulator
+	 */
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 25; i++)
+		c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
+	for (i = 0; i < 25; i++)
+		for (j = 1; j < 8; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
+				if (j & k)
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 7 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	min = max = 0;
+	memset(c, 0, sizeof(c));
+	for (i = 0; i < 31; i++)
+		c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
+	for (i = 0; i < 31; i++)
+		for (j = 1; j < 10; j++)
+			c[i] += c[i+j];
+	fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
+	for (i = 0; i < 10; i++) {
+		for (j = 0; j < 16; j++) {
+			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
+				if (j & k) 
+					s += c[l];
+				else 
+					s -= c[l];
+			}
+			if (s > max)
+				max = s;
+			if (s < min)
+				min = s;
+			fprintf(f, "%4d", (int)(128+127*s));
+			if (i < 9 || j < 15)
+				fprintf(f, ",%s", (j & 7) == 7 
+					? "\n\t" : "");
+		}
+	}
+	fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
+	fprintf(f, "\n};\n\n");
+	
+}
+
+/* -------------------------------------------------------------------- */
+
+#define AFSK24_SAMPLERATE  16000
+#define AFSK24_CORRLEN     14
+
+static void gentbl_afsk2400(FILE *f, float tcm3105clk)
+{
+	int i, sum, v;
+
+	fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
+		"#define AFSK24_TX_FREQ_LO %d\n"
+		"#define AFSK24_TX_FREQ_HI %d\n"
+		"#define AFSK24_BITPLL_INC %d\n"
+		"#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk, 
+		(int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0), 
+		0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE);
+
+#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE
+#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
+#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
+
+	fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n"
+		"static const int afsk24_tx_lo_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i)); 
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n"
+		"static const int afsk24_tx_hi_i[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
+		sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i)); 
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+        fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n"
+		"static const int afsk24_tx_hi_q[] = {\n\t", sum);
+        for(sum = i = 0; i < AFSK24_CORRLEN; i++)  {
+		sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
+                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
+	}
+	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
+#undef ARGLO
+#undef ARGHI
+#undef WINDOW
+}
+
+/* -------------------------------------------------------------------- */
+
+static char *progname;
+
+static void gentbl_banner(FILE *f)
+{
+	fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
+		"DO NOT EDIT!\n */\n\n", progname);
+}
+
+/* -------------------------------------------------------------------- */
+
+void main(int argc, char *argv[])
+{
+	FILE *f;
+	
+	progname = argv[0];
+	if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk1200(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2666(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_psk4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_psk4800(f);
+	gentbl_costab(f, 8);
+	gentbl_atantab(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_hapn4800(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_fsk9600(f);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2400(f, 8000000);
+	fclose(f);
+	if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
+		exit(1);
+	gentbl_banner(f);
+	gentbl_offscostab(f, 6);
+	gentbl_costab(f, 6);
+	gentbl_afsk2400(f, 7372800);
+	fclose(f);
+	exit(0);
+}
+
+
+/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm.c linux/drivers/net/hamradio/soundmodem/sm.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm.c	Tue Aug  5 09:49:51 1997
@@ -0,0 +1,898 @@
+/*****************************************************************************/
+
+/*
+ *	sm.c  -- soundcard radio modem driver.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  Command line options (insmod command line)
+ *
+ *  mode     mode string; eg. "wss:afsk1200"
+ *  iobase   base address of the soundcard; common values are 0x220 for sbc,
+ *           0x530 for wss
+ *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
+ *  dma      dma number; common values are 0 or 1
+ *
+ *
+ *  History:
+ *   0.1  21.09.96  Started
+ *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
+ *   0.4  21.01.97  Separately compileable soundcard/modem modules
+ *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
+ *   0.6  16.04.97  init code/data tagged
+ *   0.7  30.07.97  fixed halfduplex interrupt handlers/hotfix for CS423X
+ */
+
+/*****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include "sm.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= 0x20123
+#include <linux/init.h>
+#else
+#define __init
+#define __initdata
+#define __initfunc(x) x
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const char sm_drvname[] = "soundmodem";
+static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
+KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
+
+/* --------------------------------------------------------------------- */
+
+/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+	&sm_afsk2400_7_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+	&sm_afsk2400_8_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_tx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_tx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_tx,
+	&sm_hapn4800_10_tx,
+	&sm_hapn4800_pm8_tx,
+	&sm_hapn4800_pm10_tx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_tx,
+	&sm_fsk9600_5_tx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
+#ifdef CONFIG_SOUNDMODEM_AFSK1200
+	&sm_afsk1200_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
+	&sm_afsk2400_7_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
+	&sm_afsk2400_8_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
+#ifdef CONFIG_SOUNDMODEM_AFSK2666
+	&sm_afsk2666_rx,
+#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
+#ifdef CONFIG_SOUNDMODEM_PSK4800
+	&sm_psk4800_rx,
+#endif /* CONFIG_SOUNDMODEM_PSK4800 */
+#ifdef CONFIG_SOUNDMODEM_HAPN4800
+	&sm_hapn4800_8_rx,
+	&sm_hapn4800_10_rx,
+	&sm_hapn4800_pm8_rx,
+	&sm_hapn4800_pm10_rx,
+#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
+#ifdef CONFIG_SOUNDMODEM_FSK9600
+	&sm_fsk9600_4_rx,
+	&sm_fsk9600_5_rx,
+#endif /* CONFIG_SOUNDMODEM_FSK9600 */
+	NULL
+};
+
+static const struct hardware_info *sm_hardware_table[] = {
+#ifdef CONFIG_SOUNDMODEM_SBC
+	&sm_hw_sbc,
+	&sm_hw_sbcfdx,
+#endif /* CONFIG_SOUNDMODEM_SBC */
+#ifdef CONFIG_SOUNDMODEM_WSS
+	&sm_hw_wss,
+	&sm_hw_wssfdx,
+#endif /* CONFIG_SOUNDMODEM_WSS */
+	NULL
+};
+
+/* --------------------------------------------------------------------- */
+
+#define NR_PORTS 4
+
+/* --------------------------------------------------------------------- */
+
+static struct device sm_device[NR_PORTS];
+
+static struct {
+	char *mode;
+	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
+} sm_ports[NR_PORTS] = {
+	{ NULL, -1, 0, 0, 0, -1, -1, -1 },
+};
+
+/* --------------------------------------------------------------------- */
+
+#define UART_RBR(iobase) (iobase+0)
+#define UART_THR(iobase) (iobase+0)
+#define UART_IER(iobase) (iobase+1)
+#define UART_IIR(iobase) (iobase+2)
+#define UART_FCR(iobase) (iobase+2)
+#define UART_LCR(iobase) (iobase+3)
+#define UART_MCR(iobase) (iobase+4)
+#define UART_LSR(iobase) (iobase+5)
+#define UART_MSR(iobase) (iobase+6)
+#define UART_SCR(iobase) (iobase+7)
+#define UART_DLL(iobase) (iobase+0)
+#define UART_DLM(iobase) (iobase+1)
+
+#define SER_EXTENT 8
+
+#define LPT_DATA(iobase)    (iobase+0)
+#define LPT_STATUS(iobase)  (iobase+1)
+#define LPT_CONTROL(iobase) (iobase+2)
+#define LPT_IRQ_ENABLE      0x10
+
+#define LPT_EXTENT 3
+
+#define MIDI_DATA(iobase)     (iobase)
+#define MIDI_STATUS(iobase)   (iobase+1)
+#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
+#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
+
+#define MIDI_EXTENT 2
+
+/* ---------------------------------------------------------------------- */
+
+#define PARAM_TXDELAY   1
+#define PARAM_PERSIST   2
+#define PARAM_SLOTTIME  3
+#define PARAM_TXTAIL    4
+#define PARAM_FULLDUP   5
+#define PARAM_HARDWARE  6
+#define PARAM_RETURN    255
+
+#define SP_SER  1
+#define SP_PAR  2
+#define SP_MIDI 4
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== port checking routines ========================
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+/*
+ * returns 0 if ok and != 0 on error;
+ * the same behaviour as par96_check_lpt in baycom.c
+ */
+
+static int check_lpt(unsigned int iobase)
+{
+	unsigned char b1,b2;
+	int i;
+
+	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
+		return 0;
+	if (check_region(iobase, LPT_EXTENT))
+		return 0;
+	b1 = inb(LPT_DATA(iobase));
+	b2 = inb(LPT_CONTROL(iobase));
+	outb(0xaa, LPT_DATA(iobase));
+	i = inb(LPT_DATA(iobase)) == 0xaa;
+	outb(0x55, LPT_DATA(iobase));
+	i &= inb(LPT_DATA(iobase)) == 0x55;
+	outb(0x0a, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
+	outb(0x05, LPT_CONTROL(iobase));
+	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
+	outb(b1, LPT_DATA(iobase));
+	outb(b2, LPT_CONTROL(iobase));
+	return !i;
+}
+
+/* --------------------------------------------------------------------- */
+
+enum uart { c_uart_unknown, c_uart_8250,
+	c_uart_16450, c_uart_16550, c_uart_16550A};
+static const char *uart_str[] =
+	{ "unknown", "8250", "16450", "16550", "16550A" };
+
+static enum uart check_uart(unsigned int iobase)
+{
+	unsigned char b1,b2,b3;
+	enum uart u;
+	enum uart uart_tab[] =
+		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
+
+	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
+		return c_uart_unknown;
+	if (check_region(iobase, SER_EXTENT))
+		return c_uart_unknown;
+	b1 = inb(UART_MCR(iobase));
+	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
+	b2 = inb(UART_MSR(iobase));
+	outb(0x1a, UART_MCR(iobase));
+	b3 = inb(UART_MSR(iobase)) & 0xf0;
+	outb(b1, UART_MCR(iobase));	   /* restore old values */
+	outb(b2, UART_MSR(iobase));
+	if (b3 != 0x90)
+		return c_uart_unknown;
+	inb(UART_RBR(iobase));
+	inb(UART_RBR(iobase));
+	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
+	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
+	if (u == c_uart_16450) {
+		outb(0x5a, UART_SCR(iobase));
+		b1 = inb(UART_SCR(iobase));
+		outb(0xa5, UART_SCR(iobase));
+		b2 = inb(UART_SCR(iobase));
+		if ((b1 != 0x5a) || (b2 != 0xa5))
+			u = c_uart_8250;
+	}
+	return u;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int check_midi(unsigned int iobase)
+{
+	unsigned long timeout;
+	unsigned long flags;
+	unsigned char b;
+
+	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
+		return 0;
+	if (check_region(iobase, MIDI_EXTENT))
+		return 0;
+	timeout = jiffies + (HZ / 100);
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	save_flags(flags);
+	cli();
+	outb(0xff, MIDI_DATA(iobase));
+	b = inb(MIDI_STATUS(iobase));
+	restore_flags(flags);
+	if (!(b & MIDI_WRITE_EMPTY))
+		return 0;
+	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
+		if ((signed)(jiffies - timeout) > 0)
+			return 0;
+	return 1;
+}
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm)
+{
+	int invert_dcd = 0;
+	int invert_ptt = 0;
+
+	int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt;
+	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
+
+	if (sm->hdrv.ptt_out.flags & SP_SER) {
+		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
+		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_PAR) {
+		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
+	}
+	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
+		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_open(struct sm_state *sm)
+{
+	enum uart u = c_uart_unknown;
+
+	sm->hdrv.ptt_out.flags = 0;
+	if (sm->hdrv.ptt_out.seriobase > 0 &&
+	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
+	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
+		sm->hdrv.ptt_out.flags |= SP_SER;
+		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
+		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
+		/* 5 bits, 1 stop, no parity, no break, Div latch access */
+		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
+		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
+		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
+		/* LCR and MCR set by output_status */
+	}
+	if (sm->hdrv.ptt_out.pariobase > 0 &&
+	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
+	    !check_lpt(sm->hdrv.ptt_out.pariobase)) {
+		sm->hdrv.ptt_out.flags |= SP_PAR;
+		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
+	}
+	if (sm->hdrv.ptt_out.midiiobase > 0 &&
+	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
+	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
+		sm->hdrv.ptt_out.flags |= SP_MIDI;
+		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
+			       "sm midi ptt");
+	}
+	sm_output_status(sm);
+
+	printk(KERN_INFO "%s: ptt output:", sm_drvname);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
+		       uart_str[u]);
+	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
+	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
+	if (!sm->hdrv.ptt_out.flags)
+		printk(" none");
+	printk("\n");
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sm_output_close(struct sm_state *sm)
+{
+	/* release regions used for PTT output */
+	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
+	sm_output_status(sm);
+	if (sm->hdrv.ptt_out.flags & SP_SER)
+		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_PAR)
+		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
+       	if (sm->hdrv.ptt_out.flags & SP_MIDI)
+		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
+	sm->hdrv.ptt_out.flags = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev);
+static int sm_close(struct device *dev);
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd);
+
+/* --------------------------------------------------------------------- */
+
+static const struct hdlcdrv_ops sm_ops = {
+	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
+};
+
+/* --------------------------------------------------------------------- */
+
+static int sm_open(struct device *dev)
+{
+	struct sm_state *sm;
+	int err;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_open: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
+		return -ENODEV;
+	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
+	err = sm->hwdrv->open(dev, sm);
+	if (err)
+		return err;
+	sm_output_open(sm);
+	MOD_INC_USE_COUNT;
+	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
+	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_close(struct device *dev)
+{
+	struct sm_state *sm;
+	int err = -ENODEV;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_close: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+
+	if (sm->hwdrv && sm->hwdrv->close)
+		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
+	sm_output_close(sm);
+	MOD_DEC_USE_COUNT;
+	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
+	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
+	return err;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, ':');
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!cp)
+		cp = mode;
+	else {
+		*cp++ = '\0';
+		while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
+			hwp++;
+		if (!hwp || !*hwp || !(*hwp)->hw_name)
+			return -EINVAL;
+		if ((*hwp)->loc_storage > sizeof(sm->hw)) {
+			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
+			       sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
+			return -EINVAL;
+		}
+		sm->hwdrv = *hwp;
+	}
+	if (!*cp)
+		return 0;
+	if (sm->hwdrv && sm->hwdrv->sethw)
+		return sm->hwdrv->sethw(dev, sm, cp);
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sm_ioctl(struct device *dev, struct ifreq *ifr,
+		    struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_state *sm;
+	struct sm_ioctl bi;
+	unsigned long flags;
+	unsigned int newdiagmode;
+	unsigned int newdiagflags;
+	char *cp;
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp = sm_modem_rx_table;
+	const struct hardware_info **hwp = sm_hardware_table;
+
+	if (!dev || !dev->priv ||
+	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
+		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
+		return -EINVAL;
+	}
+	sm = (struct sm_state *)dev->priv;
+
+	if (cmd != SIOCDEVPRIVATE) {
+		if (!sm->hwdrv || !sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+	}
+	switch (hi->cmd) {
+	default:
+		if (sm->hwdrv && sm->hwdrv->ioctl)
+			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
+		return -ENOIOCTLCMD;
+
+	case HDLCDRVCTL_GETMODE:
+		cp = hi->data.modename;
+		if (sm->hwdrv && sm->hwdrv->hw_name)
+			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
+		else
+			cp += sprintf(cp, "<unspec>:");
+		if (sm->mode_tx && sm->mode_tx->name)
+			cp += sprintf(cp, "%s", sm->mode_tx->name);
+		else
+			cp += sprintf(cp, "<unspec>");
+		if (!sm->mode_rx || !sm->mode_rx ||
+		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
+			if (sm->mode_rx && sm->mode_rx->name)
+				cp += sprintf(cp, ",%s", sm->mode_rx->name);
+			else
+				cp += sprintf(cp, ",<unspec>");
+		}
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+	case HDLCDRVCTL_SETMODE:
+		if (!suser() || dev->start)
+			return -EACCES;
+		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
+		return sethw(dev, sm, hi->data.modename);
+
+	case HDLCDRVCTL_MODELIST:
+		cp = hi->data.modename;
+		while (*hwp) {
+			if ((*hwp)->hw_name)
+				cp += sprintf("%s:,", (*hwp)->hw_name);
+			hwp++;
+		}
+		while (*mtp) {
+			if ((*mtp)->name)
+				cp += sprintf(">%s,", (*mtp)->name);
+			mtp++;
+		}
+		while (*mrp) {
+			if ((*mrp)->name)
+				cp += sprintf("<%s,", (*mrp)->name);
+			mrp++;
+		}
+		cp[-1] = '\0';
+		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
+			return -EFAULT;
+		return 0;
+
+#ifdef SM_DEBUG
+	case SMCTL_GETDEBUG:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
+		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
+		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
+		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
+		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
+			sm->debug_vals.dma_residue = 0;
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+#endif /* SM_DEBUG */
+
+	case SMCTL_DIAGNOSE:
+		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+			return -EFAULT;
+		newdiagmode = bi.data.diag.mode;
+		newdiagflags = bi.data.diag.flags;
+		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
+			return -EINVAL;
+		bi.data.diag.mode = sm->diag.mode;
+		bi.data.diag.flags = sm->diag.flags;
+		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
+		if (sm->diag.mode != newdiagmode) {
+			save_flags(flags);
+			cli();
+			sm->diag.ptr = -1;
+			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+			sm->diag.mode = newdiagmode;
+			restore_flags(flags);
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (bi.data.diag.datalen > DIAGDATALEN)
+			bi.data.diag.datalen = DIAGDATALEN;
+		if (sm->diag.ptr < bi.data.diag.datalen) {
+			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+				return -EFAULT;
+			return 0;
+		}
+		if (copy_to_user(bi.data.diag.data, sm->diag.data,
+				 bi.data.diag.datalen * sizeof(short)))
+			return -EFAULT;
+		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
+		save_flags(flags);
+		cli();
+		sm->diag.ptr = -1;
+		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
+		sm->diag.mode = newdiagmode;
+		restore_flags(flags);
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return 0;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef __i386__
+
+int sm_x86_capability = 0;
+
+__initfunc(static void i386_capability(void))
+{
+	unsigned long flags;
+	unsigned long fl1;
+	union {
+		struct {
+			unsigned int ebx, edx, ecx;
+		} r;
+		unsigned char s[13];
+	} id;
+	unsigned int eax;
+
+	save_flags(flags);
+	flags |= 0x200000;
+	restore_flags(flags);
+	save_flags(flags);
+	fl1 = flags;
+	flags &= ~0x200000;
+	restore_flags(flags);
+	save_flags(flags);
+	if (!(fl1 & 0x200000) || (flags & 0x200000)) {
+		printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname);
+		return;
+	}
+	__asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
+		 "0" (0));
+	id.s[12] = 0;
+	if (eax < 1) {
+		printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
+		       "list\n", sm_drvname, id.s);
+		return;
+	}
+	printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s);
+	__asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx");
+	printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15,
+	       eax & 15, sm_x86_capability);
+}	
+#endif /* __i386__ */	
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+__initfunc(static int sm_init(void))
+#else /* MODULE */
+__initfunc(int sm_init(void))
+#endif /* MODULE */
+{
+	int i, j, found = 0;
+	char set_hw = 1;
+	struct sm_state *sm;
+	char ifname[HDLCDRV_IFNAMELEN];
+
+	printk(sm_drvinfo);
+#ifdef __i386__
+	i386_capability();
+#endif /* __i386__ */	
+	/*
+	 * register net devices
+	 */
+	for (i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		sprintf(ifname, "sm%d", i);
+
+		if (!sm_ports[i].mode)
+			set_hw = 0;
+		if (!set_hw)
+			sm_ports[i].iobase = sm_ports[i].irq = 0;
+		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
+					     ifname, sm_ports[i].iobase,
+					     sm_ports[i].irq, sm_ports[i].dma);
+		if (!j) {
+			sm = (struct sm_state *)dev->priv;
+			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
+			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
+			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
+			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
+			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
+				set_hw = 0;
+			found++;
+		} else {
+			printk(KERN_WARNING "%s: cannot register net device\n",
+			       sm_drvname);
+		}
+	}
+	if (!found)
+		return -ENXIO;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+/*
+ * command line settable parameters
+ */
+static char *mode = NULL;
+static int iobase = -1;
+static int irq = -1;
+static int dma = -1;
+static int dma2 = -1;
+static int serio = 0;
+static int pario = 0;
+static int midiio = 0;
+
+#if LINUX_VERSION_CODE >= 0x20115
+
+MODULE_PARM(mode, "s");
+MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
+MODULE_PARM(iobase, "i");
+MODULE_PARM_DESC(iobase, "soundmodem base address");
+MODULE_PARM(irq, "i");
+MODULE_PARM_DESC(irq, "soundmodem interrupt");
+MODULE_PARM(dma, "i");
+MODULE_PARM_DESC(dma, "soundmodem dma channel");
+MODULE_PARM(dma2, "i");
+MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
+MODULE_PARM(serio, "i");
+MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
+MODULE_PARM(pario, "i");
+MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
+MODULE_PARM(midiio, "i");
+MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
+
+#endif
+
+__initfunc(int init_module(void))
+{
+	if (mode) {
+		if (iobase == -1)
+			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
+		if (irq == -1)
+			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
+		if (dma == -1)
+			dma = 1;
+	}
+	sm_ports[0].mode = mode;
+	sm_ports[0].iobase = iobase;
+	sm_ports[0].irq = irq;
+	sm_ports[0].dma = dma;
+	sm_ports[0].dma2 = dma2;
+	sm_ports[0].seriobase = serio;
+	sm_ports[0].pariobase = pario;
+	sm_ports[0].midiiobase = midiio;
+	sm_ports[1].mode = NULL;
+
+	return sm_init();
+}
+
+/* --------------------------------------------------------------------- */
+
+void cleanup_module(void)
+{
+	int i;
+
+	printk(KERN_INFO "sm: cleanup_module called\n");
+
+	for(i = 0; i < NR_PORTS; i++) {
+		struct device *dev = sm_device+i;
+		struct sm_state *sm = (struct sm_state *)dev->priv;
+
+		if (sm) {
+			if (sm->hdrv.magic != HDLCDRV_MAGIC)
+				printk(KERN_ERR "sm: invalid magic in "
+				       "cleanup_module\n");
+			else
+				hdlcdrv_unregister_hdlcdrv(dev);
+		}
+	}
+}
+
+#else /* MODULE */
+/* --------------------------------------------------------------------- */
+/*
+ * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
+ * mode: hw:modem
+ * hw: sbc, wss, wssfdx
+ * modem: afsk1200, fsk9600
+ */
+
+__initfunc(void sm_setup(char *str, int *ints))
+{
+	int i;
+
+	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
+	if ((i >= NR_PORTS) || (ints[0] < 3)) {
+		printk(KERN_INFO "%s: too many or invalid interface "
+		       "specifications\n", sm_drvname);
+		return;
+	}
+	sm_ports[i].mode = str;
+	sm_ports[i].iobase = ints[1];
+	sm_ports[i].irq = ints[2];
+	sm_ports[i].dma = ints[3];
+	sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0;
+	sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0;
+	sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0;
+	sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0;
+	if (i < NR_PORTS-1)
+		sm_ports[i+1].mode = NULL;
+}
+
+#endif /* MODULE */
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm.h linux/drivers/net/hamradio/soundmodem/sm.h
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm.h	Mon Jun 16 16:35:56 1997
@@ -0,0 +1,382 @@
+/*****************************************************************************/
+
+/*
+ *	sm.h  --  soundcard radio modem driver internal header.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#ifndef _SM_H
+#define _SM_H
+
+/* ---------------------------------------------------------------------- */
+
+#include <linux/hdlcdrv.h>
+#include <linux/soundmodem.h>
+
+#define SM_DEBUG
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Information that need to be kept for each board.
+ */
+
+struct sm_state {
+	struct hdlcdrv_state hdrv;
+
+	const struct modem_tx_info *mode_tx;
+	const struct modem_rx_info *mode_rx;
+
+	const struct hardware_info *hwdrv;
+
+	/*
+	 * Hardware (soundcard) access routines state
+	 */
+	struct {
+		void *ibuf;
+		unsigned int ifragsz;
+		unsigned int ifragptr;
+		unsigned int i16bit;
+		void *obuf;
+		unsigned int ofragsz;
+		unsigned int ofragptr;
+		unsigned int o16bit;
+		int ptt_cnt;
+	} dma;
+
+	union {
+		long hw[32/sizeof(long)];
+	} hw;
+
+	/*
+	 * state of the modem code
+	 */
+	union {
+		long m[32/sizeof(long)];
+	} m;
+	union {
+		long d[256/sizeof(long)];
+	} d;
+
+#define DIAGDATALEN 64
+	struct diag_data {
+		unsigned int mode;
+		unsigned int flags;
+		volatile int ptr;
+		short data[DIAGDATALEN];
+	} diag;
+
+
+#ifdef SM_DEBUG
+	struct debug_vals {
+		unsigned long last_jiffies;
+		unsigned cur_intcnt;
+		unsigned last_intcnt;
+		unsigned mod_cyc;
+		unsigned demod_cyc;
+		unsigned dma_residue;
+	} debug_vals;
+#endif /* SM_DEBUG */
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Mode definition structure
+ */
+
+struct modem_tx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+        void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
+        void (*modulator_s16)(struct sm_state *, short *, unsigned int);
+        void (*init)(struct sm_state *);
+};
+
+struct modem_rx_info {
+	const char *name;
+	unsigned int loc_storage;
+	int srate;
+	int bitrate;
+	unsigned int overlap;
+	unsigned int sperbit;
+        void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
+        void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
+        void (*init)(struct sm_state *);
+};
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Soundcard driver definition structure
+ */
+
+struct hardware_info {
+	char *hw_name; /* used for request_{region,irq,dma} */
+	unsigned int loc_storage;
+	/*
+	 * mode specific open/close
+	 */
+	int (*open)(struct device *, struct sm_state *);
+	int (*close)(struct device *, struct sm_state *);
+	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
+		     struct hdlcdrv_ioctl *, int);
+	int (*sethw)(struct device *, struct sm_state *, char *);
+};
+
+/* --------------------------------------------------------------------- */
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+
+/* --------------------------------------------------------------------- */
+
+extern const char sm_drvname[];
+extern const char sm_drvinfo[];
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== diagnostics stuff ===============================
+ */
+
+extern inline void diag_trigger(struct sm_state *sm)
+{
+	if (sm->diag.ptr < 0)
+		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
+			sm->diag.ptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
+#define SHRT_MIN (-SHRT_MAX-1)
+
+extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
+{
+	int val;
+
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void diag_add_one(struct sm_state *sm, int val)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
+	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
+	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (val > SHRT_MAX)
+		val = SHRT_MAX;
+	if (val < SHRT_MIN)
+		val = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = val;
+}
+
+/* --------------------------------------------------------------------- */
+
+static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
+{
+	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
+	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
+		return;
+	/* clip */
+	if (vali > SHRT_MAX)
+		vali = SHRT_MAX;
+	if (vali < SHRT_MIN)
+		vali = SHRT_MIN;
+	if (valq > SHRT_MAX)
+		valq = SHRT_MAX;
+	if (valq < SHRT_MIN)
+		valq = SHRT_MIN;
+	sm->diag.data[sm->diag.ptr++] = vali;
+	sm->diag.data[sm->diag.ptr++] = valq;
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== utility functions ===============================
+ */
+
+extern inline unsigned int hweight32(unsigned int w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight16(unsigned short w)
+	__attribute__ ((unused));
+extern inline unsigned int hweight8(unsigned char w)
+        __attribute__ ((unused));
+
+extern inline unsigned int hweight32(unsigned int w)
+{
+        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+extern inline unsigned int hweight16(unsigned short w)
+{
+        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
+        res = (res & 0x3333) + ((res >> 2) & 0x3333);
+        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+extern inline unsigned int hweight8(unsigned char w)
+{
+        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
+        res = (res & 0x33) + ((res >> 2) & 0x33);
+        return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+	__attribute__ ((unused));
+
+extern inline unsigned int gcd(unsigned int x, unsigned int y)
+{
+	for (;;) {
+		if (!x)
+			return y;
+		if (!y)
+			return x;
+		if (x > y)
+			x %= y;
+		else
+			y %= x;
+	}
+}
+
+extern inline unsigned int lcm(unsigned int x, unsigned int y)
+{
+	return x * y / gcd(x, y);
+}
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== profiling =======================================
+ */
+
+
+#ifdef __i386__
+
+extern int sm_x86_capability;
+
+#define HAS_RDTSC (sm_x86_capability & 0x10)
+
+/*
+ * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
+ * in fact, overflowing modems would require over 2THz clock speeds :-)
+ */
+
+#define time_exec(var,cmd)                                              \
+({                                                                      \
+	if (HAS_RDTSC) {                                                \
+		unsigned int cnt1, cnt2, cnt3;                          \
+		__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
+		cmd;                                                    \
+		__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
+		var = cnt2-cnt1;                                        \
+	} else {                                                        \
+		cmd;                                                    \
+	}                                                               \
+})
+
+#else /* __i386__ */
+
+#define time_exec(var,cmd) cmd
+
+#endif /* __i386__ */
+
+/* --------------------------------------------------------------------- */
+
+extern const struct modem_tx_info sm_afsk1200_tx;
+extern const struct modem_tx_info sm_afsk2400_7_tx;
+extern const struct modem_tx_info sm_afsk2400_8_tx;
+extern const struct modem_tx_info sm_afsk2666_tx;
+extern const struct modem_tx_info sm_psk4800_tx;
+extern const struct modem_tx_info sm_hapn4800_8_tx;
+extern const struct modem_tx_info sm_hapn4800_10_tx;
+extern const struct modem_tx_info sm_hapn4800_pm8_tx;
+extern const struct modem_tx_info sm_hapn4800_pm10_tx;
+extern const struct modem_tx_info sm_fsk9600_4_tx;
+extern const struct modem_tx_info sm_fsk9600_5_tx;
+
+extern const struct modem_rx_info sm_afsk1200_rx;
+extern const struct modem_rx_info sm_afsk2400_7_rx;
+extern const struct modem_rx_info sm_afsk2400_8_rx;
+extern const struct modem_rx_info sm_afsk2666_rx;
+extern const struct modem_rx_info sm_psk4800_rx;
+extern const struct modem_rx_info sm_hapn4800_8_rx;
+extern const struct modem_rx_info sm_hapn4800_10_rx;
+extern const struct modem_rx_info sm_hapn4800_pm8_rx;
+extern const struct modem_rx_info sm_hapn4800_pm10_rx;
+extern const struct modem_rx_info sm_fsk9600_4_rx;
+extern const struct modem_rx_info sm_fsk9600_5_rx;
+
+extern const struct hardware_info sm_hw_sbc;
+extern const struct hardware_info sm_hw_sbcfdx;
+extern const struct hardware_info sm_hw_wss;
+extern const struct hardware_info sm_hw_wssfdx;
+
+extern const struct modem_tx_info *sm_modem_tx_table[];
+extern const struct modem_rx_info *sm_modem_rx_table[];
+extern const struct hardware_info *sm_hardware_table[];
+
+/* --------------------------------------------------------------------- */
+
+void sm_output_status(struct sm_state *sm);
+/*void sm_output_open(struct sm_state *sm);*/
+/*void sm_output_close(struct sm_state *sm);*/
+
+/* --------------------------------------------------------------------- */
+
+extern void inline sm_int_freq(struct sm_state *sm)
+{
+#ifdef SM_DEBUG
+	unsigned long cur_jiffies = jiffies;
+	/*
+	 * measure the interrupt frequency
+	 */
+	sm->debug_vals.cur_intcnt++;
+	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
+		sm->debug_vals.last_jiffies = cur_jiffies;
+		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
+		sm->debug_vals.cur_intcnt = 0;
+	}
+#endif /* SM_DEBUG */
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SM_H */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_afsk1200.c	Mon Jun 16 16:35:56 1997
@@ -0,0 +1,272 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk1200.c  -- soundcard radio modem driver, 1200 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk1200.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk12 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk12 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int dds_inc;
+	unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { 
+	AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
+	AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE 
+};
+
+static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, 
+			      unsigned int buflen)
+{
+	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!((st->txphase++) & 7)) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+		}
+		st->dds_inc = dds_inc[st->tx_bit & 1];
+		*buf++ = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->dds_inc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!((st->txphase++) & 7)) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+		}
+		st->dds_inc = dds_inc[st->tx_bit & 1];
+		*buf++ = COS(st->bit_pll);
+		st->bit_pll += st->dds_inc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_1200_u8(const unsigned char *buf)
+{
+	int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+	sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+	sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+	sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_1200_s16(const short *buf)
+{
+	int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
+	sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
+	sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
+	sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static const int pll_corr[2] = { -0x1000, 0x1000 };
+
+static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_1200_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr
+				[st->bit_pll < 0x9000];
+			j = 4 * hweight8(st->dcd_shreg & 0x38)
+				- hweight16(st->dcd_shreg & 0x7c0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_1200_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr
+				[st->bit_pll < 0x9000];
+			j = 4 * hweight8(st->dcd_shreg & 0x38)
+				- hweight16(st->dcd_shreg & 0x7c0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_1200(struct sm_state *sm)
+{
+	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk1200_tx = {
+	"afsk1200", sizeof(struct mod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk1200_rx = {
+	"afsk1200", sizeof(struct demod_state_afsk12), 
+	AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, 
+	demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2400_7.c  -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 7.3728MHz. The mark and space frequencies therefore
+ * lie at 3658 and 1996 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_7.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int tx_seq;
+	unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_7_tx = {
+	"afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
+	modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_7_rx = {
+	"afsk2400_7", sizeof(struct demod_state_afsk24), 
+	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
+	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c	Thu Sep  4 13:25:28 1997
@@ -0,0 +1,296 @@
+/*****************************************************************************/
+
+/*
+ *	sm_afsk2400_8.c  -- soundcard radio modem driver, 2400 baud AFSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+/*
+ * This driver is intended to be compatible with TCM3105 modems
+ * overclocked to 8MHz. The mark and space frequencies therefore
+ * lie at 3970 and 2165 Hz.
+ * Note that I do _not_ recommend the building of such links, I provide
+ * this only for the users who live in the coverage area of such
+ * a "legacy" link.
+ */
+
+#include "sm.h"
+#include "sm_tbl_afsk2400_8.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_afsk24 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	unsigned char last_rxbit;
+};
+
+struct mod_state_afsk24 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int bit_pll;
+	unsigned int tx_seq;
+	unsigned int phinc;
+};
+
+/* --------------------------------------------------------------------- */
+
+static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
+				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
+
+static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = OFFSCOS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (st->tx_seq < 0x5555) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
+			st->shreg >>= 1;
+			st->phinc = dds_inc[st->tx_bit & 1];
+		}
+		st->tx_seq += 0x5555;
+		st->tx_seq &= 0xffff;
+		*buf = COS(st->bit_pll);
+		st->bit_pll += st->phinc;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
+{
+	int sum = -0x80 * csum;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 7;
+	return sum * sum;
+}
+
+extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
+{
+	int sum = 0;
+	
+	sum += (st[0] * coeff[0]);
+	sum += (st[-1] * coeff[1]);
+	sum += (st[-2] * coeff[2]);
+	sum += (st[-3] * coeff[3]);
+	sum += (st[-4] * coeff[4]);
+	sum += (st[-5] * coeff[5]);
+	sum += (st[-6] * coeff[6]);
+	sum += (st[-7] * coeff[7]);
+	sum += (st[-8] * coeff[8]);
+	sum += (st[-9] * coeff[9]);
+	sum += (st[-10] * coeff[10]);
+	sum += (st[-11] * coeff[11]);
+	sum += (st[-12] * coeff[12]);
+	sum += (st[-13] * coeff[13]);
+
+	sum >>= 15;
+	return sum * sum;
+}
+
+extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
+{
+	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+extern __inline__ int do_filter_2400_s16(const short *buf)
+{
+	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
+	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
+	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
+	return sum;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_u8(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+	int j;
+	int sum;
+	unsigned char newsample;
+
+	for (; buflen > 0; buflen--, buf++) {
+		sum = do_filter_2400_s16(buf);
+		st->dcd_shreg <<= 1;
+		st->bit_pll += AFSK24_BITPLL_INC;
+		newsample = (sum > 0);
+		if (st->last_sample ^ newsample) {
+			st->last_sample = newsample;
+			st->dcd_shreg |= 1;
+			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
+				st->bit_pll += AFSK24_BITPLL_INC/2;
+			else
+				st->bit_pll -= AFSK24_BITPLL_INC/2;
+			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
+				- hweight16(st->dcd_shreg & 0x1e0);
+			st->dcd_sum0 += j;
+		}
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 120;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->shreg >>= 1;
+			st->shreg |= (!(st->last_rxbit ^
+					st->last_sample)) << 16;
+			st->last_rxbit = st->last_sample;
+			diag_trigger(sm);
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+		}
+		diag_add(sm, *buf, sum);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_2400(struct sm_state *sm)
+{
+	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
+
+       	st->dcd_time = 120;
+	st->dcd_sum0 = 2;
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_afsk2400_8_tx = {
+	"afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
+	modulator_2400_u8, modulator_2400_s16, NULL
+};
+
+const struct modem_rx_info sm_afsk2400_8_rx = {
+	"afsk2400_8", sizeof(struct demod_state_afsk24), 
+	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
+	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_fsk9600.c	Mon Jun 16 16:35:56 1997
@@ -0,0 +1,391 @@
+/*****************************************************************************/
+
+/*
+ *	sm_fsk9600.c  --  soundcard radio modem driver, 
+ *                        9600 baud G3RUH compatible FSK modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include "sm.h"
+#include "sm_tbl_fsk9600.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long descram;
+	unsigned int bit_pll;
+	unsigned char last_sample;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+};
+
+struct mod_state_fsk96 {
+	unsigned int shreg;
+	unsigned long scram;
+	unsigned char tx_bit;
+	unsigned char *txtbl;
+	unsigned int txphase;
+};
+
+/* --------------------------------------------------------------------- */
+
+#define DESCRAM_TAP1 0x20000
+#define DESCRAM_TAP2 0x01000
+#define DESCRAM_TAP3 0x00001
+
+#define DESCRAM_TAPSH1 17
+#define DESCRAM_TAPSH2 12
+#define DESCRAM_TAPSH3 0
+
+#define SCRAM_TAP1 0x20000 /* X^17 */
+#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 4)
+			st->txphase = 0;
+		*buf++ = *st->txtbl;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 4)
+			st->txphase = 0;
+		*buf++ = ((*st->txtbl)-0x80) << 8;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x4000;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x4000;
+		curbit = (*buf >= 0);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
+			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
+				!!(st->dcd_shreg & 0x10);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, *buf);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 5)
+			st->txphase = 0;
+		*buf++ = *st->txtbl;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
+
+	for (; buflen > 0; buflen--) {
+		if (!st->txphase++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->scram = (st->scram << 1) | (st->scram & 1);
+			st->scram ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			if (st->scram & (SCRAM_TAP1 << 1))
+				st->scram ^= SCRAM_TAPN << 1;
+			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
+			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
+		}
+		if (st->txphase >= 5)
+			st->txphase = 0;
+		*buf++ = ((*st->txtbl)-0x80)<<8;
+		st->txtbl += 0x100;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x3333;
+		curbit = (*buf >= 0x80);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
+				hweight8(st->dcd_shreg & 0x70);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+	static const int pll_corr[2] = { -0x1000, 0x1000 };
+	unsigned char curbit;
+	unsigned int descx;
+
+	for (; buflen > 0; buflen--, buf++) {
+		st->dcd_shreg <<= 1;
+		st->bit_pll += 0x3333;
+		curbit = (*buf >= 0);
+		if (st->last_sample ^ curbit) {
+			st->dcd_shreg |= 1;
+			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
+			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
+				hweight8(st->dcd_shreg & 0x70);
+		}
+		st->last_sample = curbit;
+		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->descram = (st->descram << 1) | curbit;
+			descx = st->descram ^ (st->descram >> 1);
+			descx ^= ((descx >> DESCRAM_TAPSH1) ^
+				  (descx >> DESCRAM_TAPSH2));
+			st->shreg >>= 1;
+			st->shreg |= (!(descx & 1)) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, *buf);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_9600(struct sm_state *sm)
+{
+	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
+
+	st->dcd_time = 240;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_4_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
+	modulator_9600_4_u8, modulator_9600_4_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_4_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
+	demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_fsk9600_5_tx = {
+	"fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
+	modulator_9600_5_u8, modulator_9600_5_s16, NULL
+};
+
+const struct modem_rx_info sm_fsk9600_5_rx = {
+	"fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, 
+	demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_hapn4800.c	Mon Jun 16 16:35:56 1997
@@ -0,0 +1,560 @@
+/*****************************************************************************/
+
+/*
+ *	sm_hapn4800.c  -- soundcard radio modem driver, 4800 baud HAPN modem
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ *
+ *  This module implements a (hopefully) HAPN (Hamilton Area Packet
+ *  Network) compatible 4800 baud modem.
+ *  The HAPN modem uses kind of "duobinary signalling" (not really,
+ *  duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
+ *  instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
+ *  Proakis, Digital Communications).
+ *  The code is untested. It is compatible with itself (i.e. it can decode
+ *  the packets it sent), but I could not test if it is compatible with
+ *  any "real" HAPN modem, since noone uses it in my region of the world.
+ *  Feedback therefore welcome.
+ */
+
+#include "sm.h"
+#include "sm_tbl_hapn4800.h"
+
+/* --------------------------------------------------------------------- */
+
+struct demod_state_hapn48 {
+	unsigned int shreg;
+	unsigned int bit_pll;
+	unsigned char last_bit;
+	unsigned char last_bit2;
+	unsigned int dcd_shreg;
+	int dcd_sum0, dcd_sum1, dcd_sum2;
+	unsigned int dcd_time;
+	int lvlhi, lvllo;
+};
+
+struct mod_state_hapn48 {
+	unsigned int shreg;
+	unsigned char tx_bit;
+	unsigned int tx_seq;
+	const unsigned char *tbl;
+};
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) { 
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = ((st->tx_bit << 1) |
+				      (st->tx_bit & 1));
+			st->tx_bit ^= (!(st->shreg & 1));
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 10)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = *st->tbl;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
+{
+	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
+
+	for (; buflen > 0; buflen--, buf++) {
+		if (!st->tx_seq++) {
+			if (st->shreg <= 1)
+				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
+			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
+			st->tx_bit ^= !(st->shreg & 1);
+			st->shreg >>= 1;
+			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
+		}
+		if (st->tx_seq >= 8)
+			st->tx_seq = 0;
+		*buf = ((*st->tbl)-0x80)<<8;
+		st->tbl += 0x10;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = ((int)(buf[-2])-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x199a;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
+				hweight32(st->dcd_shreg & 0xe739ce70);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = buf[-2];
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x199a;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
+				hweight32(st->dcd_shreg & 0xe739ce70);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = ((int)(buf[-2])-0x80) << 8;
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
+				hweight32(st->dcd_shreg & 0xbbbbbbbb);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+	static const int pll_corr[2] = { -0x800, 0x800 };
+	int curst, cursync;
+	int inv;
+
+	for (; buflen > 0; buflen--, buf++) {
+		inv = buf[-2];
+		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
+		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
+		if (inv > st->lvlhi)
+			st->lvlhi = inv;
+		if (inv < st->lvllo)
+			st->lvllo = inv;
+		if (buflen & 1)
+			st->dcd_shreg <<= 1;
+		st->bit_pll += 0x2000;
+		curst = cursync = 0;
+		if (inv > st->lvlhi >> 1) {
+			curst = 1;
+			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
+				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
+		} else if (inv < st->lvllo >> 1) {
+			curst = -1;
+			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
+				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
+		}
+		if (cursync) {
+			st->dcd_shreg |= cursync;
+			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
+			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
+				hweight32(st->dcd_shreg & 0xbbbbbbbb);
+		}
+		hdlcdrv_channelbit(&sm->hdrv, cursync);
+		if ((--st->dcd_time) <= 0) {
+			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
+						   st->dcd_sum1 + 
+						   st->dcd_sum2) < 0);
+			st->dcd_sum2 = st->dcd_sum1;
+			st->dcd_sum1 = st->dcd_sum0;
+			st->dcd_sum0 = 2; /* slight bias */
+			st->dcd_time = 240;
+		}
+		if (st->bit_pll >= 0x10000) {
+			st->bit_pll &= 0xffff;
+			st->last_bit2 = st->last_bit;
+			if (curst < 0)
+				st->last_bit = 0;
+			else if (curst > 0)
+				st->last_bit = 1;
+			st->shreg >>= 1;
+			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
+			if (st->shreg & 1) {
+				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
+				st->shreg = 0x10000;
+			}
+			diag_trigger(sm);
+		}
+		diag_add_one(sm, inv);
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+static void demod_init_hapn4800(struct sm_state *sm)
+{
+	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
+
+	st->dcd_time = 120;
+	st->dcd_sum0 = 2;	
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_8_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, 
+	modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_8_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
+	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_10_tx = {
+	"hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
+	modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_10_rx = {
+	"hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, 
+	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm8_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, 
+	modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm8_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
+	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
+
+const struct modem_tx_info sm_hapn4800_pm10_tx = {
+	"hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
+	modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
+};
+
+const struct modem_rx_info sm_hapn4800_pm10_rx = {
+	"hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
+	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_sbc.c linux/drivers/net/hamradio/soundmodem/sm_sbc.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_sbc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_sbc.c	Tue Aug  5 09:49:51 1997
@@ -0,0 +1,941 @@
+/*****************************************************************************/
+
+/*
+ *	sm_sbc.c  -- soundcard radio modem driver soundblaster hardware driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+#include "smdma.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_sbc {
+	unsigned char revhi, revlo;
+	unsigned char fmt[2];
+	unsigned int sr[2];
+};
+
+#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+/* 
+ * the sbc converter's registers 
+ */
+#define DSP_RESET(iobase)        (iobase+0x6)
+#define DSP_READ_DATA(iobase)    (iobase+0xa)
+#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
+#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
+#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
+#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
+#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
+#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
+#define SBC_EXTENT               16
+
+/* --------------------------------------------------------------------- */
+/*
+ * SBC commands
+ */
+#define SBC_OUTPUT             0x14
+#define SBC_INPUT              0x24
+#define SBC_BLOCKSIZE          0x48
+#define SBC_HI_OUTPUT          0x91 
+#define SBC_HI_INPUT           0x99 
+#define SBC_LO_OUTPUT_AUTOINIT 0x1c
+#define SBC_LO_INPUT_AUTOINIT  0x2c
+#define SBC_HI_OUTPUT_AUTOINIT 0x90 
+#define SBC_HI_INPUT_AUTOINIT  0x98
+#define SBC_IMMED_INT          0xf2
+#define SBC_GET_REVISION       0xe1
+#define ESS_GET_REVISION       0xe7
+#define SBC_SPEAKER_ON         0xd1
+#define SBC_SPEAKER_OFF        0xd3
+#define SBC_DMA_ON             0xd0
+#define SBC_DMA_OFF            0xd4
+#define SBC_SAMPLE_RATE        0x40
+#define SBC_SAMPLE_RATE_OUT    0x41
+#define SBC_SAMPLE_RATE_IN     0x42
+#define SBC_MONO_8BIT          0xa0
+#define SBC_MONO_16BIT         0xa4
+#define SBC_STEREO_8BIT        0xa8
+#define SBC_STEREO_16BIT       0xac
+
+#define SBC4_OUT8_AI           0xc6
+#define SBC4_IN8_AI            0xce
+#define SBC4_MODE_UNS_MONO     0x00
+#define SBC4_MODE_SIGN_MONO    0x10
+
+#define SBC4_OUT16_AI          0xb6
+#define SBC4_IN16_AI           0xbe
+
+/* --------------------------------------------------------------------- */
+
+static int inline reset_dsp(struct device *dev)
+{
+	int i;
+
+	outb(1, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0x100; i++)
+		SLOW_DOWN_IO;
+	outb(0, DSP_RESET(dev->base_addr));
+	for (i = 0; i < 0xffff; i++)
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
+			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
+				return 1;
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline write_dsp(struct device *dev, unsigned char data)
+{
+	int i;
+	
+	for (i = 0; i < 0xffff; i++)
+		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
+			outb(data, DSP_WRITE_DATA(dev->base_addr));
+			return;
+		}
+}
+
+/* --------------------------------------------------------------------- */
+
+static int inline read_dsp(struct device *dev, unsigned char *data)
+{
+	int i;
+
+	if (!data)
+		return 0;
+	for (i = 0; i < 0xffff; i++) 
+		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
+			*data = inb(DSP_READ_DATA(dev->base_addr));
+			return 1;
+		}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int config_resources(struct device *dev, struct sm_state *sm, int fdx)
+{
+	unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
+	unsigned long flags;
+
+	switch (dev->irq) {
+	case 2:
+	case 9:
+		irqreg |= 0x01;
+		break;
+
+	case 5:
+		irqreg |= 0x02;
+		break;
+
+	case 7:
+		irqreg |= 0x04;
+		break;
+
+	case 10:
+		irqreg |= 0x08;
+		break;
+		
+	default:
+		return -ENODEV;
+	}
+
+	switch (dev->dma) {
+	case 0:
+		dmareg |= 0x01;
+		break;
+
+	case 1:
+		dmareg |= 0x02;
+		break;
+
+	case 3:
+		dmareg |= 0x08;
+		break;
+
+	default:
+		return -ENODEV;
+	}
+		
+	if (fdx) {
+		switch (sm->hdrv.ptt_out.dma2) {
+		case 5:
+			dmareg |= 0x20;
+			break;
+			
+		case 6:
+			dmareg |= 0x40;
+			break;
+			
+		case 7:
+			dmareg |= 0x80;
+			break;
+			
+		default:
+			return -ENODEV;
+		}
+	}
+	save_flags(flags);
+	cli();
+	outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
+	outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
+	realirq = inb(DSP_MIXER_DATA(dev->base_addr));
+	outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
+	outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
+	realdma = inb(DSP_MIXER_DATA(dev->base_addr));
+	restore_flags(flags);
+	if ((~realirq) & irqreg || (~realdma) & dmareg) {
+		printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
+		       "and IRQ/DMA specified wrongly?\n", sm_drvname);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_8bit(struct device *dev)
+{
+	inb(DSP_DATA_AVAIL(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void inline sbc_int_ack_16bit(struct device *dev)
+{
+	inb(DSP_INTACK_16BIT(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char sbcmode[2][2] = {
+		{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
+		{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
+	};
+	static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
+	static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
+	unsigned int nsamps;
+
+	send = !!send;
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        sbc_int_ack_8bit(dev);
+        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
+        write_dsp(dev, SCSTATE->fmt[send]);
+        write_dsp(dev, sbcskr[send]); 
+	nsamps = dma_setup(sm, send, dev->dma) - 1;
+        sbc_int_ack_8bit(dev);
+	if (SCSTATE->revhi >= 4) {
+		write_dsp(dev, sbc4mode[send]);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, nsamps & 0xff);
+		write_dsp(dev, nsamps >> 8);
+	} else {
+		write_dsp(dev, SBC_BLOCKSIZE);
+		write_dsp(dev, nsamps & 0xff);
+		write_dsp(dev, nsamps >> 8);
+		write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
+		/* hispeed mode if sample rate > 13kHz */
+	}
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned int curfrag;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	cli();
+ 	sbc_int_ack_8bit(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
+	enable_dma(dev->dma);
+	sm_int_freq(sm);
+	sti();
+	if (sm->dma.ptt_cnt <= 0) {
+		dma_receive(sm, curfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+		if (hdlcdrv_ptt(&sm->hdrv)) {
+			/* starting to transmit */
+			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+			dma_start_transmit(sm);
+			setup_dma_dsp(dev, sm, 1);
+			dma_transmit(sm);
+		}
+	} else if (dma_end_transmit(sm, curfrag)) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+		sti();
+		dma_init_receive(sm);
+		setup_dma_dsp(dev, sm, 0);
+        } else
+		dma_transmit(sm);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_open(struct device *dev, struct sm_state *sm) 
+{
+	int err;
+	unsigned int dmasz, u;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev)) {
+		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+		       sm_drvname, dev->base_addr);
+		return -ENODEV;
+	}
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
+	       SCSTATE->revhi, SCSTATE->revlo);
+	if (SCSTATE->revhi < 2) {
+		printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
+		       "rev 2.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if (SCSTATE->revhi < 3 && 
+	    (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
+		printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
+		       "old, at least 3.00 required\n", sm_drvname,
+		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
+		return -ENODEV;
+	}
+	if (SCSTATE->revhi >= 4 && 
+	    (err = config_resources(dev, sm, 0))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
+	/*
+	 * initialize some variables
+	 */
+	dma_init_receive(sm);
+	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+	u = NUM_FRAGMENTS * sm->dma.ofragsz;
+	if (u > dmasz)
+		dmasz = u;
+	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_dsp(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		if (!(*mtp)->modulator_u8)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if (!(*mrp)->demodulator_u8)
+				continue;
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
+							 sm->mode_rx->srate);
+				SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
+							 sm->mode_tx->srate);
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				sm->dma.i16bit = sm->dma.o16bit = 0;
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	unsigned long flags;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SM_MIXER_INVALID;
+		switch (SCSTATE->revhi) {
+		case 2:
+			bi.data.mix.mixer_type = SM_MIXER_CT1335;
+			break;
+		case 3:
+			bi.data.mix.mixer_type = SM_MIXER_CT1345;
+			break;
+		case 4:
+			bi.data.mix.mixer_type = SM_MIXER_CT1745;
+			break;
+		}
+		if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
+		    bi.data.mix.reg < 0x80) {
+			save_flags(flags);
+			cli();
+			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+			bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
+			restore_flags(flags);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+		
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		switch (SCSTATE->revhi) {
+		case 2:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
+				return -EINVAL;
+			break;
+		case 3:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
+				return -EINVAL;
+			break;
+		case 4:
+			if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
+				return -EINVAL;
+			break;
+		default:
+			return -ENODEV;
+		}
+		if (bi.data.mix.reg >= 0x80)
+			return -EACCES;
+		save_flags(flags);
+		cli();
+		outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
+		outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
+		restore_flags(flags);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbc = {
+	"sbc", sizeof(struct sc_state_sbc), 
+	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned int isamps, osamps;
+
+        if (!reset_dsp(dev)) {
+                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
+                return;
+        }
+        save_flags(flags);
+        cli();
+        sbc_int_ack_8bit(dev);
+        sbc_int_ack_16bit(dev);
+	/* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
+	write_dsp(dev, SBC_SAMPLE_RATE_IN);
+	write_dsp(dev, SCSTATE->sr[0] >> 8);
+	write_dsp(dev, SCSTATE->sr[0] & 0xff);
+	write_dsp(dev, SBC_SAMPLE_RATE_OUT);
+	write_dsp(dev, SCSTATE->sr[1] >> 8);
+	write_dsp(dev, SCSTATE->sr[1] & 0xff);
+        write_dsp(dev, SBC_SPEAKER_ON);
+	if (sm->dma.o16bit) {
+		/*
+		 * DMA channel 1 (8bit) does input (capture),
+		 * DMA channel 2 (16bit) does output (playback)
+		 */
+		isamps = dma_setup(sm, 0, dev->dma) - 1;
+		osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
+		sbc_int_ack_8bit(dev);
+		sbc_int_ack_16bit(dev);
+		write_dsp(dev, SBC4_IN8_AI);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, isamps & 0xff);
+		write_dsp(dev, isamps >> 8);
+		write_dsp(dev, SBC4_OUT16_AI);
+		write_dsp(dev, SBC4_MODE_SIGN_MONO);
+		write_dsp(dev, osamps & 0xff);
+		write_dsp(dev, osamps >> 8);
+	} else {
+		/*
+		 * DMA channel 1 (8bit) does output (playback),
+		 * DMA channel 2 (16bit) does input (capture)
+		 */
+		isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+		osamps = dma_setup(sm, 1, dev->dma) - 1;
+		sbc_int_ack_8bit(dev);
+		sbc_int_ack_16bit(dev);
+		write_dsp(dev, SBC4_OUT8_AI);
+		write_dsp(dev, SBC4_MODE_UNS_MONO);
+		write_dsp(dev, osamps & 0xff);
+		write_dsp(dev, osamps >> 8);
+		write_dsp(dev, SBC4_IN16_AI);
+		write_dsp(dev, SBC4_MODE_SIGN_MONO);
+		write_dsp(dev, isamps & 0xff);
+		write_dsp(dev, isamps >> 8);
+	}
+	dma_init_receive(sm);
+	dma_init_transmit(sm);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned char intsrc, pbint = 0, captint = 0;
+	unsigned int ocfrag, icfrag;
+	unsigned long flags;
+
+	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	save_flags(flags);
+	cli();
+	outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
+	intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
+	if (intsrc & 0x01) {
+		sbc_int_ack_8bit(dev);
+		if (sm->dma.o16bit) {
+			captint = 1;
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			dma_ptr(sm, 0, dev->dma, &icfrag);
+			enable_dma(dev->dma);
+		} else {     
+			pbint = 1;
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			dma_ptr(sm, 1, dev->dma, &ocfrag);
+			enable_dma(dev->dma);
+		}
+	}
+	if (intsrc & 0x02) {
+		sbc_int_ack_16bit(dev);
+		if (sm->dma.o16bit) {
+			pbint = 1;
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		} else {
+			captint = 1;
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		}
+	}
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+	if (pbint) {
+		if (dma_end_transmit(sm, ocfrag))
+			dma_clear_transmit(sm);
+		dma_transmit(sm);
+	}
+	if (captint) { 
+		dma_receive(sm, icfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+	}
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	int err;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
+		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
+		       sizeof(struct sc_state_sbc), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, SBC_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (!reset_dsp(dev)) {
+		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
+		       sm_drvname, dev->base_addr);
+		return -ENODEV;
+	}
+	write_dsp(dev, SBC_GET_REVISION);
+	if (!read_dsp(dev, &SCSTATE->revhi) || 
+	    !read_dsp(dev, &SCSTATE->revlo))
+		return -ENODEV;
+	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
+	       SCSTATE->revhi, SCSTATE->revlo);
+	if (SCSTATE->revhi < 4) {
+		printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
+		return -ENODEV;
+	}
+	if ((err = config_resources(dev, sm, 1))) {
+		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
+		return err;
+	}
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+		kfree(sm->dma.ibuf);
+		return -ENOMEM;
+	}
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_fdx_dsp(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	reset_dsp(dev);	
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, SBC_EXTENT);
+	kfree(sm->dma.ibuf);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
+			    (*mrp)->srate == (*mtp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->sr[0] = sm->mode_rx->srate;
+				SCSTATE->sr[1] = sm->mode_tx->srate;
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
+					sm->dma.i16bit = 1;
+					sm->dma.o16bit = 0;
+					sm->dma.ifragsz <<= 1;
+				} else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
+					sm->dma.i16bit = 0;
+					sm->dma.o16bit = 1;
+					sm->dma.ofragsz <<= 1;
+				} else {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return sbc_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_sbcfdx = {
+	"sbcfdx", sizeof(struct sc_state_sbc), 
+	sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_wss.c linux/drivers/net/hamradio/soundmodem/sm_wss.c
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/sm_wss.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/sm_wss.c	Tue Aug  5 09:49:51 1997
@@ -0,0 +1,965 @@
+/*****************************************************************************/
+
+/*
+ *	sm_wss.c  -- soundcard radio modem driver, WSS (half duplex) driver
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/soundmodem.h>
+#include "sm.h"
+#include "smdma.h"
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * currently this module is supposed to support both module styles, i.e.
+ * the old one present up to about 2.1.9, and the new one functioning
+ * starting with 2.1.21. The reason is I have a kit allowing to compile
+ * this module also under 2.0.x which was requested by several people.
+ * This will go in 2.2
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x20100
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#include <linux/mm.h>
+
+#undef put_user
+#undef get_user
+
+#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
+#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
+
+extern inline int copy_from_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_READ, from, n);
+        if (i)
+                return i;
+        memcpy_fromfs(to, from, n);
+        return 0;
+}
+
+extern inline int copy_to_user(void *to, const void *from, unsigned long n)
+{
+        int i = verify_area(VERIFY_WRITE, to, n);
+        if (i)
+                return i;
+        memcpy_tofs(to, from, n);
+        return 0;
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
+struct sc_state_wss {
+	unsigned char revwss, revid, revv, revcid;
+	unsigned char fmt[2];
+	unsigned char crystal;
+};
+
+#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
+
+/* --------------------------------------------------------------------- */
+
+#define WSS_CONFIG(iobase)       (iobase+0)
+#define WSS_STATUS(iobase)       (iobase+3)
+#define WSS_CODEC_IA(iobase)     (iobase+4)
+#define WSS_CODEC_ID(iobase)     (iobase+5)
+#define WSS_CODEC_STATUS(iobase) (iobase+6)
+#define WSS_CODEC_DATA(iobase)   (iobase+7)
+
+#define WSS_EXTENT   8
+
+#define CS423X_HOTFIX
+
+/* --------------------------------------------------------------------- */
+
+static void write_codec(struct device *dev, unsigned char idx,
+			unsigned char data)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx, WSS_CODEC_IA(dev->base_addr));
+	outb(data, WSS_CODEC_ID(dev->base_addr));
+}
+
+
+/* --------------------------------------------------------------------- */
+
+static unsigned char read_codec(struct device *dev, unsigned char idx)
+{
+	int timeout = 900000;
+
+	/* wait until codec ready */
+	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
+		timeout--;
+	outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
+	return inb(WSS_CODEC_ID(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+extern void inline wss_ack_int(struct device *dev)
+{
+	outb(0, WSS_CODEC_STATUS(dev->base_addr));
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_srate_tab[16] = {
+	8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
+	-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
+};
+
+static int wss_srate_index(int srate)
+{
+	int i;
+
+	for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
+		if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
+			return i;
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, 
+			     unsigned char fmt2, char fdx, char fullcalib)
+{
+	unsigned long time;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	/* Clock and data format register */
+	write_codec(dev, 0x48, fmt);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 0x5c, fmt2 & 0xf0);
+		/* MCE and interface config reg */	
+		write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
+	} else 
+		/* MCE and interface config reg */
+		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
+	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
+	if (SCSTATE->crystal && !fullcalib)
+		return 0;
+	/*
+	 * wait for ACI start
+	 */
+	time = 1000;
+	while (!(read_codec(dev, 0x0b) & 0x20))
+		if (!(--time)) {
+			printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
+			       sm_drvname);
+			restore_flags(flags);
+			return -1;
+		}
+	/*
+	 * wait for ACI end
+	 */
+	sti();
+	time = jiffies + HZ/4;
+	while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
+	restore_flags(flags);
+	if ((signed)(jiffies - time) >= 0) {
+		printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
+		       sm_drvname);
+		return -1;
+	}
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, 
+			  unsigned char src_l, unsigned char src_r, 
+			  int igain_l, int igain_r,
+			  int ogain_l, int ogain_r)
+{
+	unsigned char tmp, reg0, reg1, reg6, reg7;
+	static const signed char irqtab[16] = 
+	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
+		  -1, -1 };
+	static const signed char dmatab[4] = { 1, 2, -1, 3 };
+	
+	tmp = inb(WSS_STATUS(dev->base_addr));
+	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
+	    (tmp & 0x3f) != 0x0f) {
+		printk(KERN_WARNING "sm: WSS card id register not found, "
+		       "address 0x%lx, ID register 0x%02x\n", 
+		       dev->base_addr, (int)tmp);
+		/* return -1; */
+		SCSTATE->revwss = 0;
+	} else {
+		if ((tmp & 0x80) && ((dev->dma == 0) || 
+				     ((dev->irq >= 8) && (dev->irq != 9)))) {
+			printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
+			       "(except IRQ9) cannot be used on an 8bit "
+			       "card\n", sm_drvname);
+			return -1;
+		}		
+		if (dev->irq > 15 || irqtab[dev->irq] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
+			       sm_drvname, (int)dev->irq);
+			return -1;
+		}
+		if (dev->dma > 3 || dmatab[dev->dma] == -1) {
+			printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
+			       sm_drvname, (int)dev->dma);
+			return -1;
+		}
+		tmp = irqtab[dev->irq] | dmatab[dev->dma];
+		/* irq probe */
+		outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
+		if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
+			outb(0, WSS_CONFIG(dev->base_addr));
+			printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
+			       sm_drvname, dev->irq);
+		}
+		outb(tmp, WSS_CONFIG(dev->base_addr));
+		SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
+	}
+	/*
+	 * initialize the codec
+	 */
+	if (igain_l < 0)
+		igain_l = 0;
+	if (igain_r < 0)
+		igain_r = 0;
+	if (ogain_l > 0)
+		ogain_l = 0;
+	if (ogain_r > 0)
+		ogain_r = 0;
+	reg0 = (src_l << 6) & 0xc0;
+	reg1 = (src_r << 6) & 0xc0;
+	if (reg0 == 0x80 && igain_l >= 20) {
+		reg0 |= 0x20;
+		igain_l -= 20;
+	}
+	if (reg1 == 0x80 && igain_r >= 20) {
+		reg1 |= 0x20;
+		igain_r -= 20;
+	}
+	if (igain_l > 23)
+		igain_l = 23;
+	if (igain_r > 23)
+		igain_r = 23;
+	reg0 |= igain_l * 2 / 3;
+	reg1 |= igain_r * 2 / 3;
+	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
+	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
+	write_codec(dev, 9, 0);
+	write_codec(dev, 0, 0x45);
+	if (read_codec(dev, 0) != 0x45)
+		goto codec_err;
+	write_codec(dev, 0, 0xaa);
+	if (read_codec(dev, 0) != 0xaa)
+		goto codec_err;
+	write_codec(dev, 12, 0x40); /* enable MODE2 */
+	write_codec(dev, 16, 0);
+	write_codec(dev, 0, 0x45);
+	SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
+	write_codec(dev, 0, 0xaa);
+	SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
+	if (SCSTATE->crystal) {
+		SCSTATE->revcid = read_codec(dev, 0x19);
+		SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
+		SCSTATE->revcid &= 7;
+		write_codec(dev, 0x10, 0x80); /* maximum output level */
+		write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
+		write_codec(dev, 0x12, 0x80); /* left line input control */
+		write_codec(dev, 0x13, 0x80); /* right line input control */
+		write_codec(dev, 0x16, 0); /* disable alternative freq sel */
+		write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
+		write_codec(dev, 0x1b, 0x00); /* left out no att */
+		write_codec(dev, 0x1d, 0x00); /* right out no att */
+	}
+
+	if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
+		goto codec_err;
+
+        write_codec(dev, 0, reg0); /* left input control */
+        write_codec(dev, 1, reg1); /* right input control */
+        write_codec(dev, 2, 0x80); /* left aux#1 input control */
+        write_codec(dev, 3, 0x80); /* right aux#1 input control */
+        write_codec(dev, 4, 0x80); /* left aux#2 input control */
+        write_codec(dev, 5, 0x80); /* right aux#2 input control */
+        write_codec(dev, 6, reg6); /* left dac control */
+        write_codec(dev, 7, reg7); /* right dac control */
+        write_codec(dev, 0xa, 0x2); /* pin control register */
+        write_codec(dev, 0xd, 0x0); /* digital mix control */
+	SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
+	/*
+	 * print revisions
+	 */
+	if (SCSTATE->crystal) 
+		printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
+		       " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
+		       (int)SCSTATE->revv, (int)SCSTATE->revcid);
+	else
+		printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
+		       sm_drvname, (int)SCSTATE->revwss, 
+		       (int)SCSTATE->revid);
+	return 0;
+ codec_err:
+	outb(0, WSS_CONFIG(dev->base_addr));
+	printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", 
+	       sm_drvname, dev->base_addr);
+	return -1;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
+{
+        unsigned long flags;
+        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
+	unsigned char oldcodecmode;
+	long abrt;
+	unsigned char fmt;
+	unsigned int numsamps;
+
+	send = !!send;
+	fmt = SCSTATE->fmt[send];
+	save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	wss_ack_int(dev);
+	if (read_codec(dev, 11) & 0x10) {
+		dma_setup(sm, oldcodecmode & 1, dev->dma);
+		abrt = 0;
+		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
+	}
+#ifdef CS423X_HOTFIX
+	if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
+		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#else /* CS423X_HOTFIX */
+	if (read_codec(dev, 0x8) != fmt)
+		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
+#endif /* CS423X_HOTFIX */
+	numsamps = dma_setup(sm, send, dev->dma) - 1;
+	write_codec(dev, 15, numsamps & 0xff);
+	write_codec(dev, 14, numsamps >> 8);
+	write_codec(dev, 9, codecmode[send]);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned int curfrag;
+	unsigned int nums;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	cli();
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	clear_dma_ff(dev->dma);
+	nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
+	write_codec(dev, 15, nums  & 0xff);
+	write_codec(dev, 14, nums >> 8);
+	enable_dma(dev->dma);
+	sm_int_freq(sm);
+	sti();
+	if (sm->dma.ptt_cnt <= 0) {
+		dma_receive(sm, curfrag);
+		hdlcdrv_arbitrate(dev, &sm->hdrv);
+		if (hdlcdrv_ptt(&sm->hdrv)) {
+			/* starting to transmit */
+			disable_dma(dev->dma);
+			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
+			dma_start_transmit(sm);
+			setup_dma_wss(dev, sm, 1);
+			dma_transmit(sm);
+		}
+	} else if (dma_end_transmit(sm, curfrag)) {
+		/* stopping transmission */
+		disable_dma(dev->dma);
+		dma_init_receive(sm);
+		setup_dma_wss(dev, sm, 0);
+        } else
+		dma_transmit(sm);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_open(struct device *dev, struct sm_state *sm) 
+{
+	unsigned int dmasz, u;
+
+	if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
+		printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", 
+		       sizeof(struct sc_state_wss), sizeof(sm->m));
+		return -ENODEV;
+	}
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	dma_init_receive(sm);
+	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
+	u = NUM_FRAGMENTS * sm->dma.ofragsz;
+	if (u > dmasz)
+		dmasz = u;
+	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		free_dma(dev->dma);
+		kfree_s(sm->dma.obuf, dmasz);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_dma_wss(dev, sm, 0);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i, j;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = j;
+				SCSTATE->fmt[1] = i;
+				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
+				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				/* prefer same data format if possible to minimize switching times */
+				sm->dma.i16bit = sm->dma.o16bit = 2;
+				if (sm->mode_rx->srate == sm->mode_tx->srate) {
+					if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
+						sm->dma.i16bit = sm->dma.o16bit = 1;
+					else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
+						sm->dma.i16bit = sm->dma.o16bit = 0;
+				}
+				if (sm->dma.i16bit == 2) {
+					if (sm->mode_rx->demodulator_s16)
+						sm->dma.i16bit = 1;
+					else if (sm->mode_rx->demodulator_u8)
+						sm->dma.i16bit = 0;
+				}
+				if (sm->dma.o16bit == 2) {
+					if (sm->mode_tx->modulator_s16)
+						sm->dma.o16bit = 1;
+					else if (sm->mode_tx->modulator_u8)
+						sm->dma.o16bit = 0;
+				}
+				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+#ifdef __BIG_ENDIAN
+				/* big endian 16bit only works on crystal cards... */
+				if (sm->dma.i16bit) {
+					SCSTATE->fmt[0] |= 0xc0;
+					sm->dma.ifragsz <<= 1;
+				}
+				if (sm->dma.o16bit) {
+					SCSTATE->fmt[1] |= 0xc0;
+					sm->dma.ofragsz <<= 1;
+				}
+#else /* __BIG_ENDIAN */
+				if (sm->dma.i16bit) {
+					SCSTATE->fmt[0] |= 0x40;
+					sm->dma.ifragsz <<= 1;
+				}
+				if (sm->dma.o16bit) {
+					SCSTATE->fmt[1] |= 0x40;
+					sm->dma.ofragsz <<= 1;
+				}
+#endif /* __BIG_ENDIAN */
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+		     struct hdlcdrv_ioctl *hi, int cmd)
+{
+	struct sm_ioctl bi;
+	int i;
+	
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
+			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
+
+	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
+		return -EFAULT;
+
+	switch (bi.cmd) {
+	default:
+		return -ENOIOCTLCMD;
+
+	case SMCTL_GETMIXER:
+		i = 0;
+		bi.data.mix.sample_rate = sm->mode_rx->srate;
+		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
+		bi.data.mix.mixer_type = SCSTATE->crystal ? 
+			SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
+		if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) 
+		     >> bi.data.mix.reg) & 1) {
+			bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
+			i = 1;
+		}
+		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+			return -EFAULT;
+		return i;
+
+	case SMCTL_SETMIXER:
+		if (!suser())
+			return -EACCES;
+		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
+		     !SCSTATE->crystal) &&
+		    (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
+		     bi.data.mix.reg >= 0x10))
+			return -EINVAL;
+		if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
+			return -EACCES;
+		write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
+		return 0;
+		
+	}
+	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
+		return -EFAULT;
+	return 0;
+
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wss = {
+	"wss", sizeof(struct sc_state_wss), 
+	wss_open, wss_close, wss_ioctl, wss_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
+{
+        unsigned long flags;
+	unsigned char oldcodecmode, codecdma;
+	long abrt;
+	unsigned int osamps, isamps;
+	
+        save_flags(flags);
+        cli();
+	/*
+	 * perform the final DMA sequence to disable the codec request
+	 */
+	oldcodecmode = read_codec(dev, 9);
+        write_codec(dev, 9, 0); /* disable codec DMA */
+	wss_ack_int(dev);
+	if ((codecdma = read_codec(dev, 11)) & 0x10) {
+		dma_setup(sm, 1, dev->dma);
+		dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
+		abrt = 0;
+		while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
+	}
+       	wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
+	osamps = dma_setup(sm, 1, dev->dma) - 1;
+	isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
+	write_codec(dev, 15, osamps & 0xff);
+	write_codec(dev, 14, osamps >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, isamps & 0xff);
+		write_codec(dev, 30, isamps >> 8);
+	}
+	write_codec(dev, 9, 3);
+        restore_flags(flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct sm_state *sm = (struct sm_state *)dev->priv;
+	unsigned long flags;
+	unsigned char cry_int_src;
+	unsigned icfrag, ocfrag, isamps, osamps;
+
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
+	    sm->hdrv.magic != HDLCDRV_MAGIC)
+		return;
+	save_flags(flags);
+	cli();
+	if (SCSTATE->crystal) { 
+		/* Crystal has an essentially different interrupt handler! */
+		cry_int_src = read_codec(dev, 0x18);
+		wss_ack_int(dev);
+		if (cry_int_src & 0x10) {       /* playback interrupt */
+			disable_dma(dev->dma);
+			clear_dma_ff(dev->dma);
+			osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+			write_codec(dev, 15, osamps & 0xff);
+			write_codec(dev, 14, osamps >> 8);
+			enable_dma(dev->dma);
+		}
+		if (cry_int_src & 0x20) {       /* capture interrupt */
+			disable_dma(sm->hdrv.ptt_out.dma2);
+			clear_dma_ff(sm->hdrv.ptt_out.dma2);
+			isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+			write_codec(dev, 31, isamps & 0xff);
+			write_codec(dev, 30, isamps >> 8);
+			enable_dma(sm->hdrv.ptt_out.dma2);
+		}
+		restore_flags(flags);
+		sm_int_freq(sm);
+		sti();
+		if (cry_int_src & 0x10) {
+			if (dma_end_transmit(sm, ocfrag))
+				dma_clear_transmit(sm);
+			dma_transmit(sm);
+		}
+		if (cry_int_src & 0x20) { 
+			dma_receive(sm, icfrag);
+			hdlcdrv_arbitrate(dev, &sm->hdrv);
+		}
+		sm_output_status(sm);
+		hdlcdrv_transmitter(dev, &sm->hdrv);
+		hdlcdrv_receiver(dev, &sm->hdrv);
+		return;
+	}
+	wss_ack_int(dev);
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+	clear_dma_ff(dev->dma);
+	clear_dma_ff(sm->hdrv.ptt_out.dma2);
+	osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
+	isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
+	write_codec(dev, 15, osamps & 0xff);
+	write_codec(dev, 14, osamps >> 8);
+	if (SCSTATE->crystal) {
+		write_codec(dev, 31, isamps & 0xff);
+		write_codec(dev, 30, isamps >> 8);
+	}
+	enable_dma(dev->dma);
+	enable_dma(sm->hdrv.ptt_out.dma2);
+	restore_flags(flags);
+	sm_int_freq(sm);
+	sti();
+	if (dma_end_transmit(sm, ocfrag))
+		dma_clear_transmit(sm);
+	dma_transmit(sm);
+	dma_receive(sm, icfrag);
+	hdlcdrv_arbitrate(dev, &sm->hdrv);
+	sm_output_status(sm);
+	hdlcdrv_transmitter(dev, &sm->hdrv);
+	hdlcdrv_receiver(dev, &sm->hdrv);
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_open(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
+		return -ENXIO;
+	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
+	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
+		return -ENXIO;
+	if (check_region(dev->base_addr, WSS_EXTENT))
+		return -EACCES;
+	/*
+	 * check if a card is available
+	 */
+	if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
+		return -ENODEV;
+	/*
+	 * initialize some variables
+	 */
+	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
+		return -ENOMEM;
+	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
+		kfree(sm->dma.ibuf);
+		return -ENOMEM;
+	}
+	dma_init_transmit(sm);
+	dma_init_receive(sm);
+
+	memset(&sm->m, 0, sizeof(sm->m));
+	memset(&sm->d, 0, sizeof(sm->d));
+	if (sm->mode_tx->init)
+		sm->mode_tx->init(sm);
+	if (sm->mode_rx->init)
+		sm->mode_rx->init(sm);
+
+	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		return -EBUSY;
+	}
+	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		return -EBUSY;
+	}
+	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
+			sm->hwdrv->hw_name, dev)) {
+		kfree(sm->dma.ibuf);
+		kfree(sm->dma.obuf);
+		free_dma(dev->dma);
+		free_dma(sm->hdrv.ptt_out.dma2);
+		return -EBUSY;
+	}
+	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
+	setup_fdx_dma_wss(dev, sm);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_close(struct device *dev, struct sm_state *sm) 
+{
+	if (!dev || !sm)
+		return -EINVAL;
+	/*
+	 * disable interrupts
+	 */
+	disable_dma(dev->dma);
+	disable_dma(sm->hdrv.ptt_out.dma2);
+        write_codec(dev, 9, 0xc); /* disable codec */
+	free_irq(dev->irq, dev);	
+	free_dma(dev->dma);	
+	free_dma(sm->hdrv.ptt_out.dma2);	
+	release_region(dev->base_addr, WSS_EXTENT);
+	kfree(sm->dma.ibuf);
+	kfree(sm->dma.obuf);
+	return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
+{
+	char *cp = strchr(mode, '.');
+	const struct modem_tx_info **mtp = sm_modem_tx_table;
+	const struct modem_rx_info **mrp;
+	int i;
+
+	if (!strcmp(mode, "off")) {
+		sm->mode_tx = NULL;
+		sm->mode_rx = NULL;
+		return 0;
+	}
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = mode;
+	for (; *mtp; mtp++) {
+		if ((*mtp)->loc_storage > sizeof(sm->m)) {
+			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
+			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
+			continue;
+		}
+		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
+			continue;
+		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
+			continue;
+		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
+			if ((*mrp)->loc_storage > sizeof(sm->d)) {
+				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
+				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
+				continue;
+			}
+			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
+			    (*mtp)->srate == (*mrp)->srate) {
+				sm->mode_tx = *mtp;
+				sm->mode_rx = *mrp;
+				SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
+				sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
+				if (sm->dma.ifragsz < sm->mode_rx->overlap)
+					sm->dma.ifragsz = sm->mode_rx->overlap;
+				sm->dma.i16bit = sm->dma.o16bit = 2;
+				if (sm->mode_rx->demodulator_s16) {
+					sm->dma.i16bit = 1;
+					sm->dma.ifragsz <<= 1;
+#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
+					SCSTATE->fmt[0] |= 0xc0;
+#else /* __BIG_ENDIAN */
+					SCSTATE->fmt[0] |= 0x40;
+#endif /* __BIG_ENDIAN */
+				} else if (sm->mode_rx->demodulator_u8)
+					sm->dma.i16bit = 0;
+				if (sm->mode_tx->modulator_s16) {
+					sm->dma.o16bit = 1;
+					sm->dma.ofragsz <<= 1;
+#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
+					SCSTATE->fmt[1] |= 0xc0;
+#else /* __BIG_ENDIAN */
+					SCSTATE->fmt[1] |= 0x40;
+#endif /* __BIG_ENDIAN */
+				} else if (sm->mode_tx->modulator_u8)
+					sm->dma.o16bit = 0;
+				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
+					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
+					       sm->mode_rx->name, sm->mode_tx->name);
+					sm->mode_tx = NULL;
+					sm->mode_rx = NULL;
+					return -EINVAL;
+				}
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
+			struct hdlcdrv_ioctl *hi, int cmd)
+{
+	if (cmd != SIOCDEVPRIVATE)
+		return -ENOIOCTLCMD;
+
+	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
+		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
+			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
+			HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
+			HDLCDRV_PARMASK_MIDIIOBASE;
+
+	return wss_ioctl(dev, sm, ifr, hi, cmd);
+}
+
+/* --------------------------------------------------------------------- */
+
+const struct hardware_info sm_hw_wssfdx = {
+	"wssfdx", sizeof(struct sc_state_wss), 
+	wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
+};
+
+/* --------------------------------------------------------------------- */
+
+#undef SCSTATE
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/soundmodem/smdma.h linux/drivers/net/hamradio/soundmodem/smdma.h
--- v2.1.70/linux/drivers/net/hamradio/soundmodem/smdma.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/soundmodem/smdma.h	Tue Aug  5 09:49:51 1997
@@ -0,0 +1,217 @@
+/*****************************************************************************/
+
+/*
+ *	smdma.h  --  soundcard radio modem driver dma buffer routines.
+ *
+ *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *	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.
+ *
+ *	This program is distributed in the hope that it will be useful,
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *	GNU General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License
+ *	along with this program; if not, write to the Free Software
+ *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Please note that the GPL allows you to use the driver, NOT the radio.
+ *  In order to use the radio, you need a license from the communications
+ *  authority of your country.
+ *
+ */
+
+#ifndef _SMDMA_H
+#define _SMDMA_H
+
+/* ---------------------------------------------------------------------- */
+
+#include "sm.h"
+
+/* ---------------------------------------------------------------------- */
+
+#define DMA_MODE_AUTOINIT      0x10
+#define NUM_FRAGMENTS          4
+
+/*
+ * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
+ * for the modulator to fill the whole DMA buffer without underrun
+ * at the highest possible baud rate, otherwise the TX state machine will
+ * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
+ */ 
+
+/* --------------------------------------------------------------------- */
+/*
+ * ===================== DMA buffer management ===========================
+ */
+
+/*
+ * returns the number of samples per fragment
+ */
+extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
+{
+	if (send) {
+		disable_dma(dmanr);
+		clear_dma_ff(dmanr);
+		set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
+		set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
+		set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
+		enable_dma(dmanr);
+		if (sm->dma.o16bit)
+			return sm->dma.ofragsz/2;
+		return sm->dma.ofragsz;
+	} else {
+		disable_dma(dmanr);
+		clear_dma_ff(dmanr);
+		set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
+		set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
+		set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
+		enable_dma(dmanr);
+		if (sm->dma.i16bit)
+			return sm->dma.ifragsz/2;
+		return sm->dma.ifragsz;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
+				       unsigned int *curfrag)
+{
+	unsigned int dmaptr, sz, frg, offs;
+	
+	dmaptr = get_dma_residue(dmanr);
+	if (send) {
+		sz = sm->dma.ofragsz * NUM_FRAGMENTS;
+		if (dmaptr == 0 || dmaptr > sz)
+			dmaptr = sz;
+		dmaptr--;
+		frg = dmaptr / sm->dma.ofragsz;
+		offs = (dmaptr % sm->dma.ofragsz) + 1;
+		*curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+			sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+		if (sm->dma.o16bit)
+			return offs/2;
+		return offs;
+	} else {
+		sz = sm->dma.ifragsz * NUM_FRAGMENTS;
+		if (dmaptr == 0 || dmaptr > sz)
+			dmaptr = sz;
+		dmaptr--;
+		frg = dmaptr / sm->dma.ifragsz;
+		offs = (dmaptr % sm->dma.ifragsz) + 1;
+		*curfrag = NUM_FRAGMENTS - 1 - frg;
+#ifdef SM_DEBUG
+		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
+			sm->debug_vals.dma_residue = offs;
+#endif /* SM_DEBUG */
+		if (sm->dma.i16bit)
+			return offs/2;
+		return offs;
+	}
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
+{
+	unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
+
+	sm->dma.ofragptr = curfrag;
+	if (sm->dma.ptt_cnt <= 0) {
+		sm->dma.ptt_cnt = 0;
+		return 0;
+	}
+	sm->dma.ptt_cnt -= diff;
+	if (sm->dma.ptt_cnt <= 0) {
+		sm->dma.ptt_cnt = 0;
+		return -1;
+	}
+	return 0;
+}
+
+extern __inline__ void dma_transmit(struct sm_state *sm)
+{
+	void *p;
+
+	while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
+		p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
+			((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
+		if (sm->dma.o16bit) {
+			time_exec(sm->debug_vals.mod_cyc, 
+				  sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
+		} else {
+			time_exec(sm->debug_vals.mod_cyc, 
+				  sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
+		}
+		sm->dma.ptt_cnt++;
+	}
+}
+
+extern __inline__ void dma_init_transmit(struct sm_state *sm)
+{
+	sm->dma.ofragptr = 0;
+	sm->dma.ptt_cnt = 0;
+}
+
+extern __inline__ void dma_start_transmit(struct sm_state *sm)
+{
+	sm->dma.ofragptr = 0;
+	if (sm->dma.o16bit) {
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
+	} else {
+		time_exec(sm->debug_vals.mod_cyc, 
+			  sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
+	}
+	sm->dma.ptt_cnt = 1;
+}
+
+extern __inline__ void dma_clear_transmit(struct sm_state *sm)
+{
+	sm->dma.ptt_cnt = 0;
+	memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
+{
+	void *p;
+
+	while (sm->dma.ifragptr != curfrag) {
+		if (sm->dma.ifragptr)
+			p = (unsigned char *)sm->dma.ibuf + 
+				sm->dma.ifragsz * sm->dma.ifragptr;
+		else {
+			p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
+			memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
+		}
+		if (sm->dma.o16bit) {
+			time_exec(sm->debug_vals.demod_cyc, 
+				  sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
+		} else {
+			time_exec(sm->debug_vals.demod_cyc, 
+				  sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
+		}
+		sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
+	}
+}
+
+extern __inline__ void dma_init_receive(struct sm_state *sm)
+{
+	sm->dma.ifragptr = 0;
+}
+
+/* --------------------------------------------------------------------- */
+#endif /* _SMDMA_H */
+
+
+
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hamradio/z8530.h linux/drivers/net/hamradio/z8530.h
--- v2.1.70/linux/drivers/net/hamradio/z8530.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/net/hamradio/z8530.h	Tue Oct 29 05:33:39 1996
@@ -0,0 +1,243 @@
+
+/* 8530 Serial Communications Controller Register definitions */
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENABLE	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+
+/* Write Register 4 */
+
+#define	PAR_ENA		0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENABL	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	ZCIE	2	/* Zero count IE */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC_HUNT	0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* 8580/85180/85280 Enhanced SCC register definitions */
+
+/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
+#define AUTOTXF	0x01		/* Auto Tx Flag */
+#define AUTOEOM 0x02		/* Auto EOM Latch Reset */
+#define AUTORTS	0x04		/* Auto RTS */
+#define TXDNRZI 0x08		/* TxD Pulled High in SDLC NRZI mode */
+#define FASTDTR 0x10		/* Fast DTR/REQ Mode */
+#define CRCCBCR	0x20		/* CRC Check Bytes Completely Received */
+#define EXTRDEN	0x40		/* Extended Read Enabled */
+
+/* Write Register 15 (external/status interrupt control) */
+#define SHDLCE	1		/* SDLC/HDLC Enhancements Enable */
+#define FIFOE	4		/* FIFO Enable */
+
+/* Read Register 6 (frame status FIFO) */
+#define BCLSB	0xff		/* LSB of 14 bits count */
+
+/* Read Register 7 (frame status FIFO) */
+#define BCMSB	0x3f		/* MSB of 14 bits count */
+#define FDA	0x40		/* FIFO Data Available Status */
+#define FOY	0x80		/* FIFO Overflow Status */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/hdlcdrv.c linux/drivers/net/hdlcdrv.c
--- v2.1.70/linux/drivers/net/hdlcdrv.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/hdlcdrv.c	Wed Dec 31 16:00:00 1969
@@ -1,1024 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	hdlcdrv.c  -- HDLC packet radio network driver.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *  The driver was derived from Donald Beckers skeleton.c
- *	Written 1993-94 by Donald Becker.
- *
- *  History:
- *   0.1  21.09.96  Started
- *        18.10.96  Changed to new user space access routines 
- *                  (copy_{to,from}_user)
- *   0.2  21.11.96  various small changes
- *   0.3  03.03.97  fixed (hopefully) IP not working with ax.25 as a module
- *   0.4  16.04.97  init code/data tagged
- *   0.5  30.07.97  made HDLC buffers bigger (solves a problem with the
- *                  soundmodem driver)
- */
-
-/*****************************************************************************/
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/net.h>
-#include <linux/in.h>
-#include <linux/if.h>
-#include <linux/malloc.h>
-#include <linux/errno.h>
-#include <asm/bitops.h>
-
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/hdlcdrv.h>
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-/* prototypes for ax25_encapsulate and ax25_rebuild_header */
-#include <net/ax25.h> 
-#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
-
-/* make genksyms happy */
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <linux/tcp.h>
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE < 0x20115
-extern __inline__ void dev_init_buffers(struct device *dev)
-{
-        int i;
-        for(i=0;i<DEV_NUMBUFFS;i++)
-        {
-                skb_queue_head_init(&dev->buffs[i]);
-        }
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE < 0x20125
-#define test_and_set_bit set_bit
-#define test_and_clear_bit clear_bit
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*
- * The name of the card. Is used for messages and in the requests for
- * io regions, irqs and dma channels
- */
-
-static char ax25_bcast[AX25_ADDR_LEN] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static char ax25_nocall[AX25_ADDR_LEN] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
-/* --------------------------------------------------------------------- */
-
-#define KISS_VERBOSE
-
-/* --------------------------------------------------------------------- */
-
-#define PARAM_TXDELAY   1
-#define PARAM_PERSIST   2
-#define PARAM_SLOTTIME  3
-#define PARAM_TXTAIL    4
-#define PARAM_FULLDUP   5
-#define PARAM_HARDWARE  6
-#define PARAM_RETURN    255
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-/*
- * the CRC routines are stolen from WAMPES
- * by Dieter Deyke
- */
-
-static const unsigned short crc_ccitt_table[] = {
-	0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
-	0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
-	0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
-	0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
-	0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
-	0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
-	0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
-	0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
-	0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
-	0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
-	0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
-	0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
-	0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
-	0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
-	0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
-	0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
-	0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
-	0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
-	0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
-	0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
-	0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
-	0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
-	0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
-	0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
-	0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
-	0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
-	0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
-	0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
-	0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
-	0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
-	0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
-	0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/*---------------------------------------------------------------------------*/
-
-static inline void append_crc_ccitt(unsigned char *buffer, int len)
-{
- 	unsigned int crc = 0xffff;
-
-	for (;len>0;len--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff];
-	crc ^= 0xffff;
-	*buffer++ = crc;
-	*buffer++ = crc >> 8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static inline int check_crc_ccitt(const unsigned char *buf, int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	return (crc & 0xffff) == 0xf0b8;
-}
-
-/*---------------------------------------------------------------------------*/
-
-#if 0
-static int calc_crc_ccitt(const unsigned char *buf, int cnt)
-{
-	unsigned int crc = 0xffff;
-
-	for (; cnt > 0; cnt--)
-		crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff];
-	crc ^= 0xffff;
-	return (crc & 0xffff);
-}
-#endif
-
-/* ---------------------------------------------------------------------- */
-
-#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16)
-
-/* ---------------------------------------------------------------------- */
-/*
- * The HDLC routines
- */
-
-static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, 
-			     int num)
-{
-	int added = 0;
-	
-	while (s->hdlcrx.rx_state && num >= 8) {
-		if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) {
-			s->hdlcrx.rx_state = 0;
-			return 0;
-		}
-		*s->hdlcrx.bp++ = bits >> (32-num);
-		s->hdlcrx.len++;
-		num -= 8;
-		added += 8;
-	}
-	return added;
-}
-
-static void hdlc_rx_flag(struct device *dev, struct hdlcdrv_state *s)
-{
-	struct sk_buff *skb;
-	int pkt_len;
-	unsigned char *cp;
-
-	if (s->hdlcrx.len < 4) 
-		return;
-	if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) 
-		return;
-	pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */
-	if (!(skb = dev_alloc_skb(pkt_len))) {
-		printk("%s: memory squeeze, dropping packet\n", 
-		       s->ifname);
-		s->stats.rx_dropped++;
-		return;
-	}
-	skb->dev = dev;
-	cp = skb_put(skb, pkt_len);
-	*cp++ = 0; /* KISS kludge */
-	memcpy(cp, s->hdlcrx.buffer, pkt_len - 1);
-	skb->protocol = htons(ETH_P_AX25);
-	skb->mac.raw = skb->data;
-	netif_rx(skb);
-	s->stats.rx_packets++;
-}
-
-void hdlcdrv_receiver(struct device *dev, struct hdlcdrv_state *s)
-{
-	int i;
-	unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word;
-	
-	if (!s || s->magic != HDLCDRV_MAGIC) 
-		return;
-	if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx))
-		return;
-
-	while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) {
-		word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf);	
-
-#ifdef HDLCDRV_DEBUG
-		hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word);
-#endif /* HDLCDRV_DEBUG */
-	       	s->hdlcrx.bitstream >>= 16;
-		s->hdlcrx.bitstream |= word << 16;
-		s->hdlcrx.bitbuf >>= 16;
-		s->hdlcrx.bitbuf |= word << 16;
-		s->hdlcrx.numbits += 16;
-		for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00,
-		    mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; 
-		    i >= 0; 
-		    i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, 
-		    mask5 <<= 1, mask6 = (mask6 << 1) | 1) {
-			if ((s->hdlcrx.bitstream & mask1) == mask1)
-				s->hdlcrx.rx_state = 0; /* abort received */
-			else if ((s->hdlcrx.bitstream & mask2) == mask3) {
-				/* flag received */
-				if (s->hdlcrx.rx_state) {
-					hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf 
-							  << (8+i),
-							  s->hdlcrx.numbits
-							  -8-i);
-					hdlc_rx_flag(dev, s);
-				}
-				s->hdlcrx.len = 0;
-				s->hdlcrx.bp = s->hdlcrx.buffer;
-				s->hdlcrx.rx_state = 1;
-				s->hdlcrx.numbits = i;
-			} else if ((s->hdlcrx.bitstream & mask4) == mask5) {
-				/* stuffed bit */
-				s->hdlcrx.numbits--;
-				s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) |
-					((s->hdlcrx.bitbuf & mask6) << 1);
-			}
-		}
-		s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf,
-						       s->hdlcrx.numbits);
-	}
-	clear_bit(0, &s->hdlcrx.in_hdlc_rx);
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void inline do_kiss_params(struct hdlcdrv_state *s,
-				  unsigned char *data, unsigned long len)
-{
-
-#ifdef KISS_VERBOSE
-#define PKP(a,b) printk(KERN_INFO "%s: channel params: " a "\n", s->ifname, b)
-#else /* KISS_VERBOSE */	      
-#define PKP(a,b) 
-#endif /* KISS_VERBOSE */	      
-
-	if (len < 2)
-		return;
-	switch(data[0]) {
-	case PARAM_TXDELAY:
-		s->ch_params.tx_delay = data[1];
-		PKP("TX delay = %ums", 10 * s->ch_params.tx_delay);
-		break;
-	case PARAM_PERSIST:   
-		s->ch_params.ppersist = data[1];
-		PKP("p persistence = %u", s->ch_params.ppersist);
-		break;
-	case PARAM_SLOTTIME:  
-		s->ch_params.slottime = data[1];
-		PKP("slot time = %ums", s->ch_params.slottime);
-		break;
-	case PARAM_TXTAIL:    
-		s->ch_params.tx_tail = data[1];
-		PKP("TX tail = %ums", s->ch_params.tx_tail);
-		break;
-	case PARAM_FULLDUP:   
-		s->ch_params.fulldup = !!data[1];
-		PKP("%s duplex", s->ch_params.fulldup ? "full" : "half");
-		break;
-	default:
-		break;
-	}
-#undef PKP
-}
-
-/* ---------------------------------------------------------------------- */
-
-void hdlcdrv_transmitter(struct device *dev, struct hdlcdrv_state *s)
-{
-	unsigned int mask1, mask2, mask3;
-	int i;
-	struct sk_buff *skb;
-	int pkt_len;
-
-	if (!s || s->magic != HDLCDRV_MAGIC) 
-		return;
-	if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx))
-		return;
-	for (;;) {
-		if (s->hdlctx.numbits >= 16) {
-			if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) {
-				clear_bit(0, &s->hdlctx.in_hdlc_tx);
-				return;
-			}
-			hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf);
-			s->hdlctx.bitbuf >>= 16;
-			s->hdlctx.numbits -= 16;
-		}
-		switch (s->hdlctx.tx_state) {
-		default:
-			clear_bit(0, &s->hdlctx.in_hdlc_tx);
-			return;
-		case 0:
-		case 1:
-			if (s->hdlctx.numflags) {
-				s->hdlctx.numflags--;
-				s->hdlctx.bitbuf |= 
-					0x7e7e << s->hdlctx.numbits;
-				s->hdlctx.numbits += 16;
-				break;
-			}
-			if (s->hdlctx.tx_state == 1) {
-				clear_bit(0, &s->hdlctx.in_hdlc_tx);
-				return;
-			}
-			if (!(skb = skb_dequeue(&s->send_queue))) {
-				int flgs = tenms_to_2flags
-					(s, s->ch_params.tx_tail);
-				if (flgs < 2)
-					flgs = 2;
-				s->hdlctx.tx_state = 1;
-				s->hdlctx.numflags = flgs;
-				break;
-			}
-			if (skb->data[0] != 0) {
-				do_kiss_params(s, skb->data, skb->len);
-				dev_kfree_skb(skb, FREE_WRITE);
-				break;
-			}
-			pkt_len = skb->len-1; /* strip KISS byte */
-			if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) {
-				s->hdlctx.tx_state = 0;
-				s->hdlctx.numflags = 1;
-				dev_kfree_skb(skb, FREE_WRITE);
-				break;
-			}
-			memcpy(s->hdlctx.buffer, skb->data+1, pkt_len);
-			dev_kfree_skb(skb, FREE_WRITE);
-			s->hdlctx.bp = s->hdlctx.buffer;
-			append_crc_ccitt(s->hdlctx.buffer, pkt_len);
-			s->hdlctx.len = pkt_len+2; /* the appended CRC */
-			s->hdlctx.tx_state = 2;
-			s->hdlctx.bitstream = 0;
-			s->stats.tx_packets++;
-			break;
-		case 2:
-			if (!s->hdlctx.len) {
-				s->hdlctx.tx_state = 0;
-				s->hdlctx.numflags = 1;
-				break;
-			}
-			s->hdlctx.len--;
-			s->hdlctx.bitbuf |= *s->hdlctx.bp <<
-				s->hdlctx.numbits;
-			s->hdlctx.bitstream >>= 8;
-			s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16;
-			mask1 = 0x1f000;
-			mask2 = 0x10000;
-			mask3 = 0xffffffff >> (31-s->hdlctx.numbits);
-			s->hdlctx.numbits += 8;
-			for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, 
-			    mask3 = (mask3 << 1) | 1) {
-				if ((s->hdlctx.bitstream & mask1) != mask1) 
-					continue;
-				s->hdlctx.bitstream &= ~mask2;
-				s->hdlctx.bitbuf = 
-					(s->hdlctx.bitbuf & mask3) |
-						((s->hdlctx.bitbuf & 
-						 (~mask3)) << 1);
-				s->hdlctx.numbits++;
-				mask3 = (mask3 << 1) | 1;
-			}
-			break;
-		}
-	}
-}
-
-/* ---------------------------------------------------------------------- */
-
-static void start_tx(struct device *dev, struct hdlcdrv_state *s)
-{
-	s->hdlctx.tx_state = 0;
-	s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay);
-	s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0;
-	hdlcdrv_transmitter(dev, s);
-	s->hdlctx.ptt = 1;
-	s->ptt_keyed++;
-}
-
-/* ---------------------------------------------------------------------- */
-
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
-	random_seed = 28629 * random_seed + 157;
-	return random_seed;
-}
-
-/* ---------------------------------------------------------------------- */
-
-void hdlcdrv_arbitrate(struct device *dev, struct hdlcdrv_state *s)
-{
-	if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || 
-	    skb_queue_empty(&s->send_queue)) 
-		return;
-	if (s->ch_params.fulldup) {
-		start_tx(dev, s);
-		return;
-	}
-	if (s->hdlcrx.dcd) {
-		s->hdlctx.slotcnt = s->ch_params.slottime;
-		return;
-	}
-	if ((--s->hdlctx.slotcnt) > 0)
-		return;
-	s->hdlctx.slotcnt = s->ch_params.slottime;
-	if ((random_num() % 256) > s->ch_params.ppersist)
-		return;
-	start_tx(dev, s);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== network driver interface =========================
- */
-
-static inline int hdlcdrv_paranoia_check(struct device *dev,
-					const char *routine)
-{
-	if (!dev || !dev->priv || 
-	    ((struct hdlcdrv_state *)dev->priv)->magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "hdlcdrv: bad magic number for hdlcdrv_state "
-		       "struct in routine %s\n", routine);
-		return 1;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int hdlcdrv_send_packet(struct sk_buff *skb, struct device *dev)
-{
-	struct hdlcdrv_state *sm;
-
-	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_send_packet"))
-		return 0;
-	sm = (struct hdlcdrv_state *)dev->priv;
-	skb_queue_tail(&sm->send_queue, skb);
-	dev->trans_start = jiffies;	
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int hdlcdrv_set_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa = (struct sockaddr *)addr;
-
-	/* addr is an AX.25 shifted ASCII mac address */
-	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); 
-	return 0;                                         
-}
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE >= 0x20119
-static struct net_device_stats *hdlcdrv_get_stats(struct device *dev)
-#else
-static struct enet_statistics *hdlcdrv_get_stats(struct device *dev)
-#endif
-{
-	struct hdlcdrv_state *sm;
-
-	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_get_stats"))
-		return NULL;
-	sm = (struct hdlcdrv_state *)dev->priv;
-	/* 
-	 * Get the current statistics.  This may be called with the
-	 * card open or closed. 
-	 */
-	return &sm->stats;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * Open/initialize the board. This is called (in the current kernel)
- * sometime after booting when the 'ifconfig' program is run.
- *
- * This routine should set everything up anew at each open, even
- * registers that "should" only need to be set once at boot, so that
- * there is non-reboot way to recover if something goes wrong.
- */
-
-static int hdlcdrv_open(struct device *dev)
-{
-	struct hdlcdrv_state *s;
-	int i;
-
-	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_open"))
-		return -EINVAL;
-	s = (struct hdlcdrv_state *)dev->priv;
-
-	if (dev->start)
-		return 0;
-	if (!s->ops || !s->ops->open)
-		return -ENODEV;
-
-	dev->start = 1;
-	/*
-	 * initialise some variables
-	 */
-	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
-	s->hdlcrx.in_hdlc_rx = 0;
-	s->hdlcrx.rx_state = 0;
-	
-	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
-	s->hdlctx.in_hdlc_tx = 0;
-	s->hdlctx.tx_state = 1;
-	s->hdlctx.numflags = 0;
-	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
-	s->hdlctx.ptt = 0;
-	s->hdlctx.slotcnt = s->ch_params.slottime;
-	s->hdlctx.calibrate = 0;
-
-	i = s->ops->open(dev);
-	if (i) {
-		dev->start = 0;
-		return i;
-	}
-
-	dev->tbusy = 0;
-	dev->interrupt = 0;
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-/* 
- * The inverse routine to hdlcdrv_open(). 
- */
-
-static int hdlcdrv_close(struct device *dev)
-{
-	struct hdlcdrv_state *s;
-	struct sk_buff *skb;
-	int i = 0;
-
-	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_close"))
-		return -EINVAL;
-	s = (struct hdlcdrv_state *)dev->priv;
-
-	if (!dev->start)
-		return 0;
-	dev->start = 0;
-	dev->tbusy = 1;
-
-	if (s->ops && s->ops->close)
-		i = s->ops->close(dev);
-        /* Free any buffers left in the hardware transmit queue */
-        while ((skb = skb_dequeue(&s->send_queue)))
-			dev_kfree_skb(skb, FREE_WRITE);
-	return i;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int hdlcdrv_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-	struct hdlcdrv_state *s;
-	struct hdlcdrv_ioctl bi;
-		
-	if (hdlcdrv_paranoia_check(dev, "hdlcdrv_ioctl"))
-		return -EINVAL;
-	s = (struct hdlcdrv_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE) {
-		if (s->ops && s->ops->ioctl)
-			return s->ops->ioctl(dev, ifr, &bi, cmd);
-		return -ENOIOCTLCMD;
-	}
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
-	default:
-		if (s->ops && s->ops->ioctl)
-			return s->ops->ioctl(dev, ifr, &bi, cmd);
-		return -ENOIOCTLCMD;
-
-	case HDLCDRVCTL_GETCHANNELPAR:
-		bi.data.cp.tx_delay = s->ch_params.tx_delay;
-		bi.data.cp.tx_tail = s->ch_params.tx_tail;
-		bi.data.cp.slottime = s->ch_params.slottime;
-		bi.data.cp.ppersist = s->ch_params.ppersist;
-		bi.data.cp.fulldup = s->ch_params.fulldup;
-		break;
-
-	case HDLCDRVCTL_SETCHANNELPAR:
-		if (!suser())
-			return -EACCES;
-		s->ch_params.tx_delay = bi.data.cp.tx_delay;
-		s->ch_params.tx_tail = bi.data.cp.tx_tail;
-		s->ch_params.slottime = bi.data.cp.slottime;
-		s->ch_params.ppersist = bi.data.cp.ppersist;
-		s->ch_params.fulldup = bi.data.cp.fulldup;
-		s->hdlctx.slotcnt = 1;
-		return 0;
-		
-	case HDLCDRVCTL_GETMODEMPAR:
-		bi.data.mp.iobase = dev->base_addr;
-		bi.data.mp.irq = dev->irq;
-		bi.data.mp.dma = dev->dma;
-		bi.data.mp.dma2 = s->ptt_out.dma2;
-		bi.data.mp.seriobase = s->ptt_out.seriobase;
-		bi.data.mp.pariobase = s->ptt_out.pariobase;
-		bi.data.mp.midiiobase = s->ptt_out.midiiobase;
-		break;
-
-	case HDLCDRVCTL_SETMODEMPAR:
-		if ((!suser()) || dev->start)
-			return -EACCES;
-		dev->base_addr = bi.data.mp.iobase;
-		dev->irq = bi.data.mp.irq;
-		dev->dma = bi.data.mp.dma;
-		s->ptt_out.dma2 = bi.data.mp.dma2;
-		s->ptt_out.seriobase = bi.data.mp.seriobase;
-		s->ptt_out.pariobase = bi.data.mp.pariobase;
-		s->ptt_out.midiiobase = bi.data.mp.midiiobase;
-		return 0;	
-	
-	case HDLCDRVCTL_GETSTAT:
-		bi.data.cs.ptt = hdlcdrv_ptt(s);
-		bi.data.cs.dcd = s->hdlcrx.dcd;
-		bi.data.cs.ptt_keyed = s->ptt_keyed;
-		bi.data.cs.tx_packets = s->stats.tx_packets;
-		bi.data.cs.tx_errors = s->stats.tx_errors;
-		bi.data.cs.rx_packets = s->stats.rx_packets;
-		bi.data.cs.rx_errors = s->stats.rx_errors;
-		break;		
-
-	case HDLCDRVCTL_OLDGETSTAT:
-		bi.data.ocs.ptt = hdlcdrv_ptt(s);
-		bi.data.ocs.dcd = s->hdlcrx.dcd;
-		bi.data.ocs.ptt_keyed = s->ptt_keyed;
-#if LINUX_VERSION_CODE < 0x20100
-		bi.data.ocs.stats = s->stats;
-#endif
-		break;		
-
-	case HDLCDRVCTL_CALIBRATE:
-		s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16;
-		return 0;
-
-	case HDLCDRVCTL_GETSAMPLES:
-#ifndef HDLCDRV_DEBUG
-		return -EPERM;
-#else /* HDLCDRV_DEBUG */
-		if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) 
-			return -EAGAIN;
-		bi.data.bits = 
-			s->bitbuf_channel.buffer[s->bitbuf_channel.rd];
-		s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) %
-			sizeof(s->bitbuf_channel.buffer);
-		break;
-#endif /* HDLCDRV_DEBUG */
-				
-	case HDLCDRVCTL_GETBITS:
-#ifndef HDLCDRV_DEBUG
-		return -EPERM;
-#else /* HDLCDRV_DEBUG */
-		if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) 
-			return -EAGAIN;
-		bi.data.bits = 
-			s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd];
-		s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) %
-			sizeof(s->bitbuf_hdlc.buffer);
-		break;		
-#endif /* HDLCDRV_DEBUG */
-
-	case HDLCDRVCTL_DRIVERNAME:
-		if (s->ops && s->ops->drvname) {
-			strncpy(bi.data.drivername, s->ops->drvname, 
-				sizeof(bi.data.drivername));
-			break;
-		}
-		bi.data.drivername[0] = '\0';
-		break;
-		
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * Check for a network adaptor of this type, and return '0' if one exists.
- * If dev->base_addr == 0, probe all likely locations.
- * If dev->base_addr == 1, always return failure.
- * If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
- */
-static int hdlcdrv_probe(struct device *dev)
-{
-	const struct hdlcdrv_channel_params dflt_ch_params = { 
-		20, 2, 10, 40, 0 
-	};
-	struct hdlcdrv_state *s;
-
-	if (!dev)
-		return -ENXIO;
-	/*
-	 * not a real probe! only initialize data structures
-	 */
-	s = (struct hdlcdrv_state *)dev->priv;
-	/*
-	 * initialize the hdlcdrv_state struct
-	 */
-	s->ch_params = dflt_ch_params;
-	s->ptt_keyed = 0;
-
-	s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0;
-	s->hdlcrx.in_hdlc_rx = 0;
-	s->hdlcrx.rx_state = 0;
-	
-	s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0;
-	s->hdlctx.in_hdlc_tx = 0;
-	s->hdlctx.tx_state = 1;
-	s->hdlctx.numflags = 0;
-	s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0;
-	s->hdlctx.ptt = 0;
-	s->hdlctx.slotcnt = s->ch_params.slottime;
-	s->hdlctx.calibrate = 0;
-
-#ifdef HDLCDRV_DEBUG
-	s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0;
-	s->bitbuf_channel.shreg = 0x80;
-
-	s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0;
-	s->bitbuf_hdlc.shreg = 0x80;
-#endif /* HDLCDRV_DEBUG */
-
-	/*
-	 * initialize the device struct
-	 */
-	dev->open = hdlcdrv_open;
-	dev->stop = hdlcdrv_close;
-	dev->do_ioctl = hdlcdrv_ioctl;
-	dev->hard_start_xmit = hdlcdrv_send_packet;
-	dev->get_stats = hdlcdrv_get_stats;
-
-	/* Fill in the fields of the device structure */
-
-	dev_init_buffers(dev);
-
-	skb_queue_head_init(&s->send_queue);
-	
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-	dev->hard_header = ax25_encapsulate;
-	dev->rebuild_header = ax25_rebuild_header;
-#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
-	dev->hard_header = NULL;
-	dev->rebuild_header = NULL;
-#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
-	dev->set_mac_address = hdlcdrv_set_mac_address;
-	
-	dev->type = ARPHRD_AX25;           /* AF_AX25 device */
-	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
-	dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */
-	dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */
-	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
-	memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
-
-	/* New style flags */
-	dev->flags = 0;
-
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-int hdlcdrv_register_hdlcdrv(struct device *dev, const struct hdlcdrv_ops *ops,
-			     unsigned int privsize, char *ifname,
-			     unsigned int baseaddr, unsigned int irq, 
-			     unsigned int dma) 
-{
-	struct hdlcdrv_state *s;
-
-	if (!dev || !ops)
-		return -EACCES;
-	if (privsize < sizeof(struct hdlcdrv_state))
-		privsize = sizeof(struct hdlcdrv_state);
-	memset(dev, 0, sizeof(struct device));
-	if (!(s = dev->priv = kmalloc(privsize, GFP_KERNEL)))
-		return -ENOMEM;
-	/*
-	 * initialize part of the hdlcdrv_state struct
-	 */
-	memset(s, 0, privsize);
-	s->magic = HDLCDRV_MAGIC;
-	strncpy(s->ifname, ifname, sizeof(s->ifname));
-	s->ops = ops;
-	/*
-	 * initialize part of the device struct
-	 */
-	dev->name = s->ifname;
-	dev->if_port = 0;
-	dev->init = hdlcdrv_probe;
-	dev->start = 0;
-	dev->tbusy = 1;
-	dev->base_addr = baseaddr;
-	dev->irq = irq;
-	dev->dma = dma;
-	if (register_netdev(dev)) {
-		printk(KERN_WARNING "hdlcdrv: cannot register net "
-		       "device %s\n", s->ifname);
-		kfree(dev->priv);
-		return -ENXIO;
-	}
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-int hdlcdrv_unregister_hdlcdrv(struct device *dev) 
-{
-	struct hdlcdrv_state *s;
-
-	if (!dev)
-		return -EINVAL;
-	if (!(s = (struct hdlcdrv_state *)dev->priv))
-		return -EINVAL;
-	if (s->magic != HDLCDRV_MAGIC)
-		return -EINVAL;
-	if (dev->start && s->ops->close)
-		s->ops->close(dev);
-	unregister_netdev(dev);
-	kfree(s);
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-EXPORT_SYMBOL(hdlcdrv_receiver);
-EXPORT_SYMBOL(hdlcdrv_transmitter);
-EXPORT_SYMBOL(hdlcdrv_arbitrate);
-EXPORT_SYMBOL(hdlcdrv_register_hdlcdrv);
-EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
-
-#else
-
-static struct symbol_table hdlcdrv_syms = {
-#include <linux/symtab_begin.h>
-        X(hdlcdrv_receiver),
-        X(hdlcdrv_transmitter),
-        X(hdlcdrv_arbitrate),
-        X(hdlcdrv_register_hdlcdrv),
-        X(hdlcdrv_unregister_hdlcdrv),
-#include <linux/symtab_end.h>
-};
-
-#endif
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
-
-#endif
-
-/* --------------------------------------------------------------------- */
-
-__initfunc(int init_module(void))
-{
-	printk(KERN_INFO "hdlcdrv: (C) 1996 Thomas Sailer HB9JNX/AE4WA\n");
-	printk(KERN_INFO "hdlcdrv: version 0.5 compiled " __TIME__ " " __DATE__ "\n");
-#if LINUX_VERSION_CODE < 0x20115
-        register_symtab(&hdlcdrv_syms);
-#endif
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	printk(KERN_INFO "hdlcdrv: cleanup\n");
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/mkiss.c linux/drivers/net/mkiss.c
--- v2.1.70/linux/drivers/net/mkiss.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/mkiss.c	Wed Dec 31 16:00:00 1969
@@ -1,1134 +0,0 @@
-/*
- *	MKISS Driver
- *
- *	This module:
- *		This module 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.
- *
- * 		This module implements the AX.25 protocol for kernel-based
- *		devices like TTYs. It interfaces between a raw TTY, and the
- *		kernel's AX.25 protocol layers, just like slip.c.
- *		AX.25 needs to be seperated from slip.c while slip.c is no
- *		longer a static kernel device since it is a module.
- *		This method clears the way to implement other kiss protocols
- *		like mkiss smack g8bpq ..... so far only mkiss is implemented.
- *
- * Hans Alblas <hans@esrac.ele.tue.nl>
- *
- *	History
- *	Jonathan (G4KLX)	Fixed to match Linux networking changes - 2.1.15.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/bitops.h>
-#include <asm/uaccess.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/inet.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/major.h>
-#include <linux/init.h>
-
-#include <linux/timer.h>
-
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-
-#include <net/ax25.h>
-
-#include "mkiss.h"
-
-#ifdef CONFIG_INET
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#endif
-
-#ifdef MODULE
-#define AX25_VERSION    "AX25-MODULAR-NET3.019-NEWTTY"
-#define	min(a,b)	(a < b ? a : b)
-#else
-#define	AX25_VERSION	"AX25-NET3.019-NEWTTY"
-#endif
-
-#define NR_MKISS 4
-#define MKISS_SERIAL_TYPE_NORMAL 1
-
-struct mkiss_channel {
-	int magic;		/* magic word */
-	int init;		/* channel exists? */
-	struct tty_struct *tty; /* link to tty control structure */
-};
-
-typedef struct ax25_ctrl {
-	char if_name[8];	/* "ax0\0" .. "ax99999\0"	*/
-	struct ax_disp ctrl;	/* 				*/
-	struct device  dev;	/* the device			*/
-} ax25_ctrl_t;
-
-static ax25_ctrl_t **ax25_ctrls = NULL;
-
-int ax25_maxdev = AX25_MAXDEV;		/* Can be overridden with insmod! */
-
-static struct tty_ldisc	ax_ldisc;
-static struct tty_driver mkiss_driver;
-static int mkiss_refcount;
-static struct tty_struct *mkiss_table[NR_MKISS];
-static struct termios *mkiss_termios[NR_MKISS];
-static struct termios *mkiss_termios_locked[NR_MKISS];
-struct mkiss_channel MKISS_Info[NR_MKISS];
-
-static int ax25_init(struct device *);
-static int mkiss_init(void);
-static int mkiss_write(struct tty_struct *, int, const unsigned char *, int);
-static int kiss_esc(unsigned char *, unsigned char *, int);
-static void kiss_unesc(struct ax_disp *, unsigned char);
-
-/* Find a free channel, and link in this `tty' line. */
-static inline struct ax_disp *ax_alloc(void)
-{
-	ax25_ctrl_t *axp;
-	int i;
-
-	if (ax25_ctrls == NULL)		/* Master array missing ! */
-		return NULL;
-
-	for (i = 0; i < ax25_maxdev; i++) {
-		axp = ax25_ctrls[i];
-
-		/* Not allocated ? */
-		if (axp == NULL)
-			break;
-
-		/* Not in use ? */
-		if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags))
-			break;
-	}
-
-	/* Sorry, too many, all slots in use */
-	if (i >= ax25_maxdev)
-		return NULL;
-
-	/* If no channels are available, allocate one */
-	if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
-		axp = ax25_ctrls[i];
-		memset(axp, 0, sizeof(ax25_ctrl_t));
-
-		/* Initialize channel control data */
-		set_bit(AXF_INUSE, &axp->ctrl.flags);
-		sprintf(axp->if_name, "ax%d", i++);
-		axp->ctrl.tty      = NULL;
-		axp->dev.name      = axp->if_name;
-		axp->dev.base_addr = i;
-		axp->dev.priv      = (void *)&axp->ctrl;
-		axp->dev.next      = NULL;
-		axp->dev.init      = ax25_init;
-	}
-
-	if (axp != NULL) {
-		/*
-		 * register device so that it can be ifconfig'ed
-		 * ax25_init() will be called as a side-effect
-		 * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
-		 */
-		if (register_netdev(&axp->dev) == 0) {
-			/* (Re-)Set the INUSE bit.   Very Important! */
-			set_bit(AXF_INUSE, &axp->ctrl.flags);
-			axp->ctrl.dev = &axp->dev;
-			axp->dev.priv = (void *)&axp->ctrl;
-
-			return &axp->ctrl;
-		} else {
-			clear_bit(AXF_INUSE,&axp->ctrl.flags);
-			printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
-		}
-	}
-
-	return NULL;
-}
-
-/* Free an AX25 channel. */
-static inline void ax_free(struct ax_disp *ax)
-{
-	/* Free all AX25 frame buffers. */
-	if (ax->rbuff)
-		kfree(ax->rbuff);
-	ax->rbuff = NULL;
-	if (ax->xbuff)
-		kfree(ax->xbuff);
-	ax->xbuff = NULL;
-	if (!test_and_clear_bit(AXF_INUSE, &ax->flags))
-		printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
-}
-
-static void ax_changedmtu(struct ax_disp *ax)
-{
-	struct device *dev = ax->dev;
-	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
-	int len;
-	unsigned long flags;
-
-	len = dev->mtu * 2;
-
-	/*
-	 * allow for arrival of larger UDP packets, even if we say not to
-	 * also fixes a bug in which SunOS sends 512-byte packets even with
-	 * an MSS of 128
-	 */
-	if (len < 576 * 2)
-		len = 576 * 2;
-
-	xbuff = kmalloc(len + 4, GFP_ATOMIC);
-	rbuff = kmalloc(len + 4, GFP_ATOMIC);
-
-	if (xbuff == NULL || rbuff == NULL)  {
-		printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
-		       ax->dev->name);
-		dev->mtu = ax->mtu;
-		if (xbuff != NULL)
-			kfree(xbuff);
-		if (rbuff != NULL)
-			kfree(rbuff);
-		return;
-	}
-
-	save_flags(flags);
-	cli();
-
-	oxbuff    = ax->xbuff;
-	ax->xbuff = xbuff;
-	orbuff    = ax->rbuff;
-	ax->rbuff = rbuff;
-
-	if (ax->xleft) {
-		if (ax->xleft <= len) {
-			memcpy(ax->xbuff, ax->xhead, ax->xleft);
-		} else  {
-			ax->xleft = 0;
-			ax->tx_dropped++;
-		}
-	}
-
-	ax->xhead = ax->xbuff;
-
-	if (ax->rcount) {
-		if (ax->rcount <= len) {
-			memcpy(ax->rbuff, orbuff, ax->rcount);
-		} else  {
-			ax->rcount = 0;
-			ax->rx_over_errors++;
-			set_bit(AXF_ERROR, &ax->flags);
-		}
-	}
-
-	ax->mtu      = dev->mtu + 73;
-	ax->buffsize = len;
-
-	restore_flags(flags);
-
-	if (oxbuff != NULL)
-		kfree(oxbuff);
-	if (orbuff != NULL)
-		kfree(orbuff);
-}
-
-
-/* Set the "sending" flag.  This must be atomic, hence the ASM. */
-static inline void ax_lock(struct ax_disp *ax)
-{
-	if (test_and_set_bit(0, (void *)&ax->dev->tbusy))
-		printk(KERN_ERR "mkiss: %s: trying to lock already locked device!\n", ax->dev->name);
-}
-
-
-/* Clear the "sending" flag.  This must be atomic, hence the ASM. */
-static inline void ax_unlock(struct ax_disp *ax)
-{
-	if (!test_and_clear_bit(0, (void *)&ax->dev->tbusy))
-		printk(KERN_ERR "mkiss: %s: trying to unlock already unlocked device!\n", ax->dev->name);
-}
-
-/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
-static void ax_bump(struct ax_disp *ax)
-{
-	struct ax_disp *tmp_ax;
-	struct sk_buff *skb;
-	struct mkiss_channel *mkiss;
-	int count;
-
-        tmp_ax = ax;
-
-	if (ax->rbuff[0] > 0x0f) {
-		if (ax->mkiss != NULL) {
-			mkiss= ax->mkiss->tty->driver_data;
-			if (mkiss->magic == MKISS_DRIVER_MAGIC)
-				tmp_ax = ax->mkiss;
-		}
- 	}
-
-	count = ax->rcount;
-
-	if ((skb = dev_alloc_skb(count)) == NULL) {
-		printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
-		ax->rx_dropped++;
-		return;
-	}
-
-	skb->dev      = tmp_ax->dev;
-	memcpy(skb_put(skb,count), ax->rbuff, count);
-	skb->mac.raw  = skb->data;
-	skb->protocol = htons(ETH_P_AX25);
-	netif_rx(skb);
-	tmp_ax->rx_packets++;
-}
-
-/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
-static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
-{
-	unsigned char *p;
-	int actual, count;
-	struct mkiss_channel *mkiss = ax->tty->driver_data;
-
-	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
-		ax_changedmtu(ax);
-
-	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
-		len = ax->mtu;
-		printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
-		ax->tx_dropped++;
-		ax_unlock(ax);
-		return;
-	}
-
-	p = icp;
-
-	if (mkiss->magic  != MKISS_DRIVER_MAGIC) {
-		count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
-		ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
-		ax->tx_packets++;
-		ax->dev->trans_start = jiffies;
-		ax->xleft = count - actual;
-		ax->xhead = ax->xbuff + actual;
-	} else {
-		count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
-		ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
-		ax->tx_packets++;
-		ax->mkiss->dev->trans_start = jiffies;
-		ax->mkiss->xleft = count - actual;
-		ax->mkiss->xhead = ax->mkiss->xbuff + actual;
-	}
-}
-
-/*
- * Called by the driver when there's room for more data.  If we have
- * more packets to send, we send them here.
- */
-static void ax25_write_wakeup(struct tty_struct *tty)
-{
-	int actual;
-	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
-	struct mkiss_channel *mkiss;
-
-	/* First make sure we're connected. */
-	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
-		return;
-	if (ax->xleft <= 0)  {
-		/* Now serial buffer is almost free & we can start
-		 * transmission of another packet
-		 */
-		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
-		if (ax->mkiss != NULL) {
-			mkiss= ax->mkiss->tty->driver_data;
-	        	if (mkiss->magic  == MKISS_DRIVER_MAGIC)
-				ax_unlock(ax->mkiss);
-	        }
-
-		ax_unlock(ax);
-		mark_bh(NET_BH);
-		return;
-	}
-
-	actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
-	ax->xleft -= actual;
-	ax->xhead += actual;
-}
-
-/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
-static int ax_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-	struct mkiss_channel *mkiss = ax->tty->driver_data;
-	struct ax_disp *tmp_ax;
-
-	tmp_ax = NULL;
-
-	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
-		if (skb->data[0] < 0x10)
-			skb->data[0] = skb->data[0] + 0x10;
-		tmp_ax = ax->mkiss;
-	}
-
-	if (!dev->start)  {
-		printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
-		return 1;
-	}
-
-	if (tmp_ax != NULL)
-		if (tmp_ax->dev->tbusy)
-			return 1;
-
-	if (tmp_ax != NULL)
-		if (dev->tbusy) {
-			printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
-			ax_unlock(ax);
-	        }
-
-	if (dev->tbusy) {
-		/*
-		 * May be we must check transmitter timeout here ?
-		 *      14 Oct 1994 Dmitry Gorodchanin.
-		 */
-		if (jiffies - dev->trans_start  < 20 * HZ) {
-			/* 20 sec timeout not reached */
-			return 1;
-		}
-
-		printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
-		       (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
-		       "bad line quality" : "driver error");
-
-		ax->xleft = 0;
-		ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-		ax_unlock(ax);
-	}
-
-	/* We were not busy, so we are now... :-) */
-	if (skb != NULL) {
-		ax_lock(ax);
-		if (tmp_ax != NULL)
-			ax_lock(tmp_ax);
-		ax_encaps(ax, skb->data, skb->len);
-		kfree_skb(skb, FREE_WRITE);
-	}
-
-	return 0;
-}
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-
-/* Return the frame type ID */
-static int ax_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-	  void *daddr, void *saddr, unsigned len)
-{
-#ifdef CONFIG_INET
-	if (type != htons(ETH_P_AX25))
-		return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
-#endif
-	return 0;
-}
-
-
-static int ax_rebuild_header(struct sk_buff *skb)
-{
-#ifdef CONFIG_INET
-	return ax25_rebuild_header(skb);
-#else
-	return 0;
-#endif
-}
-
-#endif	/* CONFIG_{AX25,AX25_MODULE} */
-
-/* Open the low-level part of the AX25 channel. Easy! */
-static int ax_open(struct device *dev)
-{
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-	unsigned long len;
-
-	if (ax->tty == NULL)
-		return -ENODEV;
-
-	/*
-	 * Allocate the frame buffers:
-	 *
-	 * rbuff	Receive buffer.
-	 * xbuff	Transmit buffer.
-	 * cbuff        Temporary compression buffer.
-	 */
-	len = dev->mtu * 2;
-
-	/*
-	 * allow for arrival of larger UDP packets, even if we say not to
-	 * also fixes a bug in which SunOS sends 512-byte packets even with
-	 * an MSS of 128
-	 */
-	if (len < 576 * 2)
-		len = 576 * 2;
-
-	if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
-		goto norbuff;
-
-	if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
-		goto noxbuff;
-
-	ax->mtu	     = dev->mtu + 73;
-	ax->buffsize = len;
-	ax->rcount   = 0;
-	ax->xleft    = 0;
-
-	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
-	dev->tbusy  = 0;
-	dev->start  = 1;
-
-	return 0;
-
-	/* Cleanup */
-	kfree(ax->xbuff);
-
-noxbuff:
-	kfree(ax->rbuff);
-
-norbuff:
-	return -ENOMEM;
-}
-
-
-/* Close the low-level part of the AX25 channel. Easy! */
-static int ax_close(struct device *dev)
-{
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-
-	if (ax->tty == NULL)
-		return -EBUSY;
-
-	ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
-	dev->tbusy = 1;
-	dev->start = 0;
-
-	return 0;
-}
-
-static int ax25_receive_room(struct tty_struct *tty)
-{
-	return 65536;  /* We can handle an infinite amount of data. :-) */
-}
-
-/*
- * Handle the 'receiver data ready' interrupt.
- * This function is called by the 'tty_io' module in the kernel when
- * a block of data has been received, which can now be decapsulated
- * and sent on to the AX.25 layer for further processing.
- */
-static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
-{
-	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
-
-	if (ax == NULL || ax->magic != AX25_MAGIC || !ax->dev->start)
-		return;
-
-	/*
-	 * Argh! mtu change time! - costs us the packet part received
-	 * at the change
-	 */
-	if (ax->mtu != ax->dev->mtu + 73)
-		ax_changedmtu(ax);
-
-	/* Read the characters out of the buffer */
-	while (count--) {
-		if (fp != NULL && *fp++) {
-			if (!test_and_set_bit(AXF_ERROR, &ax->flags))
-				ax->rx_errors++;
-			cp++;
-			continue;
-		}
-
-		kiss_unesc(ax, *cp++);
-	}
-}
-
-static int ax25_open(struct tty_struct *tty)
-{
-	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
-	struct ax_disp *tmp_ax;
-	struct mkiss_channel *mkiss;
-	int err, cnt;
-
-	/* First make sure we're not already connected. */
-	if (ax && ax->magic == AX25_MAGIC)
-		return -EEXIST;
-
-	/* OK.  Find a free AX25 channel to use. */
-	if ((ax = ax_alloc()) == NULL)
-		return -ENFILE;
-
-	ax->tty = tty;
-	tty->disc_data = ax;
-
-	ax->mkiss = NULL;
-	tmp_ax    = NULL;
-
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-
-	/* Restore default settings */
-	ax->dev->type = ARPHRD_AX25;
-
-	/* Perform the low-level AX25 initialization. */
-	if ((err = ax_open(ax->dev)))
-		return err;
-
-	mkiss= ax->tty->driver_data;
-
-	if (mkiss->magic  == MKISS_DRIVER_MAGIC) {
-		for (cnt = 1; cnt < ax25_maxdev; cnt++) {
-			if (ax25_ctrls[cnt]) {
-				if (ax25_ctrls[cnt]->dev.start) {
-					if (ax == &ax25_ctrls[cnt]->ctrl) {
-						cnt--;
-						tmp_ax = &ax25_ctrls[cnt]->ctrl;
-						break;
-					}
-				}
-			}
-		}
-	}
-
-	if (tmp_ax != NULL) {
-		ax->mkiss     = tmp_ax;
-		tmp_ax->mkiss = ax;
-	}
-
-	MOD_INC_USE_COUNT;
-
-	/* Done.  We have linked the TTY line to a channel. */
-	return ax->dev->base_addr;
-}
-
-static void ax25_close(struct tty_struct *tty)
-{
-	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
-	int mkiss ;
-
-	/* First make sure we're connected. */
-	if (ax == NULL || ax->magic != AX25_MAGIC)
-		return;
-
-	mkiss = ax->mode;
-	if (ax->dev->flags & IFF_UP)
-	{
-		dev_lock_wait();
-		dev_close(ax->dev);
-		dev_unlock_list();
-	}
-
-	tty->disc_data = 0;
-	ax->tty        = NULL;
-
-	/* VSV = very important to remove timers */
-	ax_free(ax);
-	unregister_netdev(ax->dev);
-
-	MOD_DEC_USE_COUNT;
-}
-
-
-static struct net_device_stats *ax_get_stats(struct device *dev)
-{
-	static struct net_device_stats stats;
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-
-	memset(&stats, 0, sizeof(struct net_device_stats));
-
-	stats.rx_packets     = ax->rx_packets;
-	stats.tx_packets     = ax->tx_packets;
-	stats.rx_dropped     = ax->rx_dropped;
-	stats.tx_dropped     = ax->tx_dropped;
-	stats.tx_errors      = ax->tx_errors;
-	stats.rx_errors      = ax->rx_errors;
-	stats.rx_over_errors = ax->rx_over_errors;
-
-	return &stats;
-}
-
-
-/************************************************************************
- *			   STANDARD ENCAPSULATION	        	 *
- ************************************************************************/
-
-int kiss_esc(unsigned char *s, unsigned char *d, int len)
-{
-	unsigned char *ptr = d;
-	unsigned char c;
-
-	/*
-	 * Send an initial END character to flush out any
-	 * data that may have accumulated in the receiver
-	 * due to line noise.
-	 */
-
-	*ptr++ = END;
-
-	while (len-- > 0) {
-		switch (c = *s++) {
-			case END:
-				*ptr++ = ESC;
-				*ptr++ = ESC_END;
-				break;
-			case ESC:
-				*ptr++ = ESC;
-				*ptr++ = ESC_ESC;
-				break;
-			default:
-				*ptr++ = c;
-				break;
-		}
-	}
-
-	*ptr++ = END;
-
-	return ptr - d;
-}
-
-static void kiss_unesc(struct ax_disp *ax, unsigned char s)
-{
-	switch (s) {
-		case END:
-			/* drop keeptest bit = VSV */
-			if (test_bit(AXF_KEEPTEST, &ax->flags))
-				clear_bit(AXF_KEEPTEST, &ax->flags);
-
-			if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
-				ax_bump(ax);
-
-			clear_bit(AXF_ESCAPE, &ax->flags);
-			ax->rcount = 0;
-			return;
-
-		case ESC:
-			set_bit(AXF_ESCAPE, &ax->flags);
-			return;
-		case ESC_ESC:
-			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
-				s = ESC;
-			break;
-		case ESC_END:
-			if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
-				s = END;
-			break;
-	}
-
-	if (!test_bit(AXF_ERROR, &ax->flags)) {
-		if (ax->rcount < ax->buffsize) {
-			ax->rbuff[ax->rcount++] = s;
-			return;
-		}
-
-		ax->rx_over_errors++;
-		set_bit(AXF_ERROR, &ax->flags);
-	}
-}
-
-
-int ax_set_mac_address(struct device *dev, void *addr)
-{
-	int err;
-
-	if ((err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN)) != 0)
-		return err;
-
-	/* addr is an AX.25 shifted ASCII mac address */
-	copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN);
-
-	return 0;
-}
-
-static int ax_set_dev_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa = addr;
-
-	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
-
-	return 0;
-}
-
-
-/* Perform I/O control on an active ax25 channel. */
-static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
-{
-	struct ax_disp *ax = (struct ax_disp *)tty->disc_data;
-	int err;
-	unsigned int tmp;
-
-	/* First make sure we're connected. */
-	if (ax == NULL || ax->magic != AX25_MAGIC)
-		return -EINVAL;
-
-	switch (cmd) {
-	 	case SIOCGIFNAME:
-			if ((err = verify_area(VERIFY_WRITE, arg, strlen(ax->dev->name) + 1)) != 0)
-				return err;
-			copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1);
-			return 0;
-
-		case SIOCGIFENCAP:
-			if ((err = verify_area(VERIFY_WRITE, arg, sizeof(int))) != 0)
-				return err;
-			put_user(4, (int *)arg);
-			return 0;
-
-		case SIOCSIFENCAP:
-			if ((err = verify_area(VERIFY_READ, arg, sizeof(int))) != 0)
-				return err;
-			get_user(tmp, (int *)arg);
-	 		ax->mode = tmp;
-			ax->dev->addr_len        = AX25_ADDR_LEN;	  /* sizeof an AX.25 addr */
-			ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
-			ax->dev->type            = ARPHRD_AX25;
-			return 0;
-
-		 case SIOCSIFHWADDR:
-			return ax_set_mac_address(ax->dev, arg);
-
-		default:
-			return -ENOIOCTLCMD;
-	}
-}
-
-static int ax_open_dev(struct device *dev)
-{
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-
-	if (ax->tty==NULL)
-		return -ENODEV;
-
-	return 0;
-}
-
-/* Initialize AX25 control device -- register AX25 line discipline */
-__initfunc(int mkiss_init_ctrl_dev(void))
-{
-	int status;
-
-	if (ax25_maxdev < 4) ax25_maxdev = 4; /* Sanity */
-
-	if ((ax25_ctrls = kmalloc(sizeof(void*) * ax25_maxdev, GFP_KERNEL)) == NULL) {
-		printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array !  No mkiss available\n");
-		return -ENOMEM;
-	}
-
-	/* Clear the pointer array, we allocate devices when we need them */
-	memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
-
-	/* Fill in our line protocol discipline, and register it */
-	memset(&ax_ldisc, 0, sizeof(ax_ldisc));
-	ax_ldisc.magic  = TTY_LDISC_MAGIC;
-	ax_ldisc.name   = "mkiss";
-	ax_ldisc.flags  = 0;
-	ax_ldisc.open   = ax25_open;
-	ax_ldisc.close  = ax25_close;
-	ax_ldisc.read   = NULL;
-	ax_ldisc.write  = NULL;
-	ax_ldisc.ioctl  = (int (*)(struct tty_struct *, struct file *, unsigned int, unsigned long))ax25_disp_ioctl;
-	ax_ldisc.poll   = NULL;
-
-	ax_ldisc.receive_buf  = ax25_receive_buf;
-	ax_ldisc.receive_room = ax25_receive_room;
-	ax_ldisc.write_wakeup = ax25_write_wakeup;
-
-	if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0)
-		printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
-
-	mkiss_init();
-
-#ifdef MODULE
-	return status;
-#else
-	/*
-	 * Return "not found", so that dev_init() will unlink
-	 * the placeholder device entry for us.
-	 */
-	return ENODEV;
-#endif
-}
-
-
-/* Initialize the driver.  Called by network startup. */
-
-static int ax25_init(struct device *dev)
-{
-	struct ax_disp *ax = (struct ax_disp*)dev->priv;
-
-	static char ax25_bcast[AX25_ADDR_LEN] =
-		{'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
-	static char ax25_test[AX25_ADDR_LEN] =
-		{'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
-
-	if (ax == NULL)		/* Allocation failed ?? */
-		return -ENODEV;
-
-	/* Set up the "AX25 Control Block". (And clear statistics) */
-	memset(ax, 0, sizeof (struct ax_disp));
-	ax->magic  = AX25_MAGIC;
-	ax->dev	   = dev;
-
-	/* Finish setting up the DEVICE info. */
-	dev->mtu             = AX_MTU;
-	dev->hard_start_xmit = ax_xmit;
-	dev->open            = ax_open_dev;
-	dev->stop            = ax_close;
-	dev->get_stats	     = ax_get_stats;
-#ifdef HAVE_SET_MAC_ADDR
-	dev->set_mac_address = ax_set_dev_mac_address;
-#endif
-	dev->hard_header_len = 0;
-	dev->addr_len        = 0;
-	dev->type            = ARPHRD_AX25;
-	dev->tx_queue_len    = 10;
-
-	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
-	memcpy(dev->dev_addr,  ax25_test,  AX25_ADDR_LEN);
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-	dev->hard_header     = ax_header;
-	dev->rebuild_header  = ax_rebuild_header;
-#endif
-
-	dev_init_buffers(dev);
-
-	/* New-style flags. */
-	dev->flags      = 0;
-
-	return 0;
-}
-
-static int mkiss_open(struct tty_struct *tty, struct file *filp)
-{
-	struct mkiss_channel *mkiss;
-	int chan;
-
-	chan = MINOR(tty->device) - tty->driver.minor_start;
-
-	if (chan < 0 || chan >= NR_MKISS)
-		return -ENODEV;
-
-	mkiss = &MKISS_Info[chan];
-
-	mkiss->magic =  MKISS_DRIVER_MAGIC;
-	mkiss->init  = 1;
-	mkiss->tty   = tty;
-
-	tty->driver_data = mkiss;
-
-	tty->termios->c_iflag  = IGNBRK | IGNPAR;
-	tty->termios->c_cflag  = B9600 | CS8 | CLOCAL;
-	tty->termios->c_cflag &= ~CBAUD;
-
-	return 0;
-}
-
-static void mkiss_close(struct tty_struct *tty, struct file * filp)
-{
-	struct mkiss_channel *mkiss = tty->driver_data;
-
-	if (mkiss == NULL || mkiss->magic != MKISS_DRIVER_MAGIC)
-                return;
-
-	mkiss->tty   = NULL;
-	mkiss->init  = 0;
-	tty->stopped = 0;
-}
-
-static int mkiss_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
-{
-	return 0;
-}
-
-static int mkiss_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
-{
-	/* Ignore serial ioctl's */
-	switch (cmd) {
-		case TCSBRK:
-		case TIOCMGET:
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-		case TCSETS:
-		case TCSETSF:		/* should flush first, but... */
-		case TCSETSW:		/* should wait until flush, but... */
-			return 0;
-		default:
-			return -ENOIOCTLCMD;
-	}
-}
-
-
-static void mkiss_dummy(struct tty_struct *tty)
-{
-	struct mkiss_channel *mkiss = tty->driver_data;
-
-	if (tty == NULL)
-		return;
-
-	if (mkiss == NULL)
-		return;
-}
-
-static void mkiss_dummy2(struct tty_struct *tty, unsigned char ch)
-{
-	struct mkiss_channel *mkiss = tty->driver_data;
-
-	if (tty == NULL)
-		return;
-
-	if (mkiss == NULL)
-		return;
-}
-
-
-static int mkiss_write_room(struct tty_struct * tty)
-{
-	struct mkiss_channel *mkiss = tty->driver_data;
-
-	if (tty == NULL)
-		return 0;
-
-	if (mkiss == NULL)
-		return 0;
-
-	return 65536;  /* We can handle an infinite amount of data. :-) */
-}
-
-
-static int mkiss_chars_in_buffer(struct tty_struct *tty)
-{
-	struct mkiss_channel *mkiss = tty->driver_data;
-
-	if (tty == NULL)
-		return 0;
-
-	if (mkiss == NULL)
-		return 0;
-
-	return 0;
-}
-
-
-static void mkiss_set_termios(struct tty_struct *tty, struct termios *old_termios)
-{
-	/* we don't do termios */
-}
-
-/* ******************************************************************** */
-/* * 			Init MKISS driver 			      * */
-/* ******************************************************************** */
-
-__initfunc(static int mkiss_init(void))
-{
-	memset(&mkiss_driver, 0, sizeof(struct tty_driver));
-
-	mkiss_driver.magic       = MKISS_DRIVER_MAGIC;
-	mkiss_driver.name        = "mkiss";
-	mkiss_driver.major       = MKISS_MAJOR;
-	mkiss_driver.minor_start = 0;
-	mkiss_driver.num         = NR_MKISS;
-	mkiss_driver.type        = TTY_DRIVER_TYPE_SERIAL;
-	mkiss_driver.subtype     = MKISS_SERIAL_TYPE_NORMAL;	/* not needed */
-
-	mkiss_driver.init_termios         = tty_std_termios;
-	mkiss_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
-	mkiss_driver.init_termios.c_cflag = B9600 | CS8 | CLOCAL;
-
-	mkiss_driver.flags           = TTY_DRIVER_REAL_RAW;
-	mkiss_driver.refcount        = &mkiss_refcount;
-	mkiss_driver.table           = mkiss_table;
-	mkiss_driver.termios         = (struct termios **)mkiss_termios;
-	mkiss_driver.termios_locked  = (struct termios **)mkiss_termios_locked;
-
-	mkiss_driver.ioctl           = mkiss_ioctl;
-	mkiss_driver.open            = mkiss_open;
-	mkiss_driver.close           = mkiss_close;
-	mkiss_driver.write           = mkiss_write;
-	mkiss_driver.write_room      = mkiss_write_room;
-	mkiss_driver.chars_in_buffer = mkiss_chars_in_buffer;
-	mkiss_driver.set_termios     = mkiss_set_termios;
-
-	/* some unused functions */
-	mkiss_driver.flush_buffer = mkiss_dummy;
-	mkiss_driver.throttle     = mkiss_dummy;
-	mkiss_driver.unthrottle   = mkiss_dummy;
-	mkiss_driver.stop         = mkiss_dummy;
-	mkiss_driver.start        = mkiss_dummy;
-	mkiss_driver.hangup       = mkiss_dummy;
-	mkiss_driver.flush_chars  = mkiss_dummy;
-	mkiss_driver.put_char     = mkiss_dummy2;
-
-	if (tty_register_driver(&mkiss_driver)) {
-		printk(KERN_ERR "mkiss: couldn't register Mkiss device\n");
-		return -EIO;
-	}
-
-	printk(KERN_INFO "AX.25 Multikiss device enabled\n");
-
-	return 0;
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-MODULE_PARM(ax25_maxdev, "i");
-MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
-
-MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
-MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
-
-int init_module(void)
-{
-	return mkiss_init_ctrl_dev();
-}
-
-void cleanup_module(void)
-{
-	int i;
-
-	if (ax25_ctrls != NULL) {
-		for (i = 0; i < ax25_maxdev; i++) {
-			if (ax25_ctrls[i]) {
-				/*
-				 * VSV = if dev->start==0, then device
-				 * unregistred while close proc.
-				 */
-				if (ax25_ctrls[i]->dev.start)
-					unregister_netdev(&(ax25_ctrls[i]->dev));
-
-				kfree(ax25_ctrls[i]);
-				ax25_ctrls[i] = NULL;
-			}
-		}
-
-		kfree(ax25_ctrls);
-		ax25_ctrls = NULL;
-	}
-
-	if ((i = tty_register_ldisc(N_AX25, NULL)))
-		printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
-
-	if (tty_unregister_driver(&mkiss_driver))	/* remove devive */
-		printk(KERN_ERR "mkiss: can't unregister MKISS device\n");
-}
-
-#endif /* MODULE */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/mkiss.h linux/drivers/net/mkiss.h
--- v2.1.70/linux/drivers/net/mkiss.h	Sun Nov 10 09:12:56 1996
+++ linux/drivers/net/mkiss.h	Wed Dec 31 16:00:00 1969
@@ -1,56 +0,0 @@
-/****************************************************************************
- *	Defines for the Multi-KISS driver.
- ****************************************************************************/
-
-#define AX25_MAXDEV	16		/* MAX number of AX25 channels;
-					   This can be overridden with
-					   insmod -oax25_maxdev=nnn	*/
-#define AX_MTU		236	
-
-/* SLIP/KISS protocol characters. */
-#define END             0300		/* indicates end of frame	*/
-#define ESC             0333		/* indicates byte stuffing	*/
-#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
-#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
-
-struct ax_disp {
-	int                magic;
-
-	/* Various fields. */
-	struct tty_struct  *tty;		/* ptr to TTY structure		*/
-	struct device      *dev;		/* easy for intr handling	*/
-	struct ax_disp     *mkiss;		/* mkiss txport if mkiss channel*/
-
-	/* These are pointers to the malloc()ed frame buffers. */
-	unsigned char      *rbuff;		/* receiver buffer		*/
-	int                rcount;		/* received chars counter       */
-	unsigned char      *xbuff;		/* transmitter buffer		*/
-	unsigned char      *xhead;		/* pointer to next byte to XMIT */
-	int                xleft;		/* bytes left in XMIT queue     */
-
-	/* SLIP interface statistics. */
-	unsigned long      rx_packets;		/* inbound frames counter	*/
-	unsigned long      tx_packets;		/* outbound frames counter      */
-	unsigned long      rx_errors;		/* Parity, etc. errors          */
-	unsigned long      tx_errors;		/* Planned stuff                */
-	unsigned long      rx_dropped;		/* No memory for skb            */
-	unsigned long      tx_dropped;		/* When MTU change              */
-	unsigned long      rx_over_errors;	/* Frame bigger then SLIP buf.  */
-
-	/* Detailed SLIP statistics. */
-	int                 mtu;		/* Our mtu (to spot changes!)   */
-	int                 buffsize;		/* Max buffers sizes            */
-
-
-	unsigned char       flags;		/* Flag values/ mode etc	*/
-#define AXF_INUSE	0		/* Channel in use               */
-#define AXF_ESCAPE	1               /* ESC received                 */
-#define AXF_ERROR	2               /* Parity, etc. error           */
-#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
-#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
-
-	int                 mode;
-};
-
-#define AX25_MAGIC		0x5316
-#define MKISS_DRIVER_MAGIC	1215
diff -u --recursive --new-file v2.1.70/linux/drivers/net/pi2.c linux/drivers/net/pi2.c
--- v2.1.70/linux/drivers/net/pi2.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/pi2.c	Wed Dec 31 16:00:00 1969
@@ -1,1677 +0,0 @@
-/*
-   pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface.
-   Copyright (c) 1994 David Perry
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2, as
-   published by the Free Software Foundation.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 675 Mass Ave, Cambridge MA 02139, USA.
-
-   The file skeleton.c by Donald Becker was used as a starting point
-   for this driver.
-
-   Revision History
-
-   April 6, 1994  (dp) Created
-		       version 0.0 ALPHA
-   April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison.
-                       version 0.1 ALPHA
-   April 13, 1994 (dp) Included address probing from JPM, autoirq
-		       version 0.2 ALPHA
-   April 14, 1994 (ac) Sketched in the NET3 changes.
-   April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev()
-		       instead of kmalloc() to ensure that DMA buffers will
-		       reside under the 16 meg line.
-		       version 0.4 ALPHA
-   April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions.
-		       Fixed a nasty problem with DMA.
-		       version 0.5 ALPHA
-   June 6, 1994 (ac)   Fixed to match the buffer locking changes. Added a hack to
-   		       fix a funny I see (search for HACK) and fixed the calls in
-   		       init() so it doesn't migrate module based ethernet cards up
-   		       to eth2 Took out the old module ideas as they are no longer
-   		       relevant to the PI driver.
-   July 16, 1994 (dp)  Fixed the B channel rx overrun problem ac referred to
-   		       above. Also added a bit of a hack to improve the maximum
-   	               baud rate on the B channel (Search for STUFF2). Included
-   		       ioctl stuff from John Paul Morrison. version 0.6 ALPHA
-   Feb 9, 1995 (dp)    Updated for 1.1.90 kernel
-                       version 0.7 ALPHA
-   Apr 6, 1995 (ac)    Tweaks for NET3 pre snapshot 002 AX.25
-   April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program
-                       when changing the baud rate or clock mode.
-                       version 0.8 ALPHA
-   July 17, 1995 (ac)  Finally polishing of AX25.030+ support
-   Oct  29, 1995 (ac)  A couple of minor fixes before this, and this release changes
-   		       to the proper set_mac_address semantics which will break
-   		       a few programs I suspect.
-   Aug  18, 1996 (jsn) Converted to be used as a module.
-   Dec  13, 1996 (jsn) Fixed to match Linux networking changes.
-*/
-
-/* The following #define invokes a hack that will improve performance (baud)
-   for the B port. The up side is it makes 9600 baud work ok on the B port.
-   It may do 38400, depending on the host. The down side is it burns up
-   CPU cycles with ints locked for up to 1 character time, at the beginning
-   of each transmitted packet. If this causes you to lose sleep, #undefine it.
-*/
-
-/*#define STUFF2 1*/
-
-/* The default configuration */
-#define PI_DMA 3
-
-#define DEF_A_SPEED 0		/* 0 means external clock */
-#define DEF_A_TXDELAY 15	/* 15 mS transmit delay */
-#define DEF_A_PERSIST 128	/* 50% persistence */
-#define DEF_A_SLOTIME 15	/* 15 mS slot time */
-#define DEF_A_SQUELDELAY 1	/* 1 mS squelch delay - allows fcs and flag */
-#define DEF_A_CLOCKMODE 0	/* clock mode - 0 is normal */
-
-#define DEF_B_SPEED 1200	/* 1200 baud */
-#define DEF_B_TXDELAY 40	/* 400 mS */
-#define DEF_B_PERSIST 128	/* 50% */
-#define DEF_B_SLOTIME 30	/* 300 mS */
-#define DEF_B_SQUELDELAY 3	/* 30 mS */
-#define DEF_B_CLOCKMODE 0	/* Normal clock mode */
-
-/* The following #define is only really required for the PI card, not
-   the PI2 - but it's safer to leave it in. */
-#define REALLY_SLOW_IO 1
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/timer.h>
-#include <linux/if_arp.h>
-#include <linux/pi2.h>
-#include <linux/init.h>
-#include "z8530.h"
-#include <net/ax25.h>
-
-struct mbuf {
-    struct mbuf *next;
-    int cnt;
-    char data[0];
-};
-
-/*
- *	The actual devices we will use
- */
-
-/*
- *	PI device declarations.
- */
-
-static int pi0_preprobe(struct device *dev){return 0;}	/* Dummy probe function */
-static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
-static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };
-
-
-/* The number of low I/O ports used by the card. */
-#define PI_TOTAL_SIZE	8
-
-
-/* Index to functions, as function prototypes. */
-
-static int pi_probe(struct device *dev, int card_type);
-static int pi_open(struct device *dev);
-static int pi_send_packet(struct sk_buff *skb, struct device *dev);
-static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs);
-static int pi_close(struct device *dev);
-static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static struct net_device_stats *pi_get_stats(struct device *dev);
-static void rts(struct pi_local *lp, int x);
-static void b_rxint(struct device *dev, struct pi_local *lp);
-static void b_txint(struct pi_local *lp);
-static void b_exint(struct pi_local *lp);
-static void a_rxint(struct device *dev, struct pi_local *lp);
-static void a_txint(struct pi_local *lp);
-static void a_exint(struct pi_local *lp);
-static char *get_dma_buffer(unsigned long *mem_ptr);
-static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
-
-static char ax25_bcast[7] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static char ax25_test[7] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
-static int ext2_secrm_seed = 152;	/* Random generator base */
-
-extern inline unsigned char random(void)
-{
-	return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed
-			    * 69069l + 1);
-}
-
-extern inline void wrtscc(int cbase, int ctl, int sccreg, int val)
-{
-	/* assume caller disables interrupts! */
-	outb_p(0, cbase + DMAEN);	/* Disable DMA while we touch the scc */
-	outb_p(sccreg, ctl);		/* Select register */
-	outb_p(val, ctl);		/* Output value */
-	outb_p(1, cbase + DMAEN);	/* Enable DMA */
-}
-
-extern inline int rdscc(int cbase, int ctl, int sccreg)
-{
-	int retval;
-
-	/* assume caller disables interrupts! */
-	outb_p(0, cbase + DMAEN);	/* Disable DMA while we touch the scc */
-	outb_p(sccreg, ctl);	/* Select register */
-	retval = inb_p(ctl);
-	outb_p(1, cbase + DMAEN);	/* Enable DMA */
-	return retval;
-}
-
-static void switchbuffers(struct pi_local *lp)
-{
-	if (lp->rcvbuf == lp->rxdmabuf1)
-		lp->rcvbuf = lp->rxdmabuf2;
-	else
-		lp->rcvbuf = lp->rxdmabuf1;
-}
-
-static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb)
-{
-	char kickflag;
-	unsigned long flags;
-
-	lp->stats.tx_packets++;
-
-	save_flags(flags);
-	cli();
-	kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
-	restore_flags(flags);
-
-	skb_queue_tail(&lp->sndq, skb);
-	if (kickflag) 
-	{
-		/* simulate interrupt to xmit */
-		switch (lp->base & 2) 
-		{
-			case 2:
-				a_txint(lp);	/* process interrupt */
-				break;
-			case 0:
-				save_flags(flags);
-				cli();
-				if (lp->tstate == IDLE)
-					b_txint(lp);
-				restore_flags(flags);
-				break;
-		}
-	}
-}
-
-static void setup_rx_dma(struct pi_local *lp)
-{
-    unsigned long flags;
-    int cmd;
-    unsigned long dma_abs;
-    unsigned dmachan;
-
-    save_flags(flags);
-    cli();
-
-    dma_abs = (unsigned long) (lp->rcvbuf->data);
-    dmachan = lp->dmachan;
-    cmd = lp->base + CTL;
-
-    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
-	panic("PI: RX buffer violates DMA boundary!");
-
-    /* Get ready for RX DMA */
-    wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
-
-    disable_dma(dmachan);
-    clear_dma_ff(dmachan);
-
-    /* Set DMA mode register to single transfers, incrementing address,
-     *  auto init, writes
-     */
-    set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
-    set_dma_addr(dmachan, dma_abs);
-    set_dma_count(dmachan, lp->bufsiz);
-    enable_dma(dmachan);
-
-    /* If a packet is already coming in, this line is supposed to
-	   avoid receiving a partial packet.
-     */
-    wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
-
-    /* Enable RX dma */
-    wrtscc(lp->cardbase, cmd, R1,
-      WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
-
-    restore_flags(flags);
-}
-
-static void setup_tx_dma(struct pi_local *lp, int length)
-{
-    unsigned long dma_abs;
-    unsigned long flags;
-    unsigned long dmachan;
-
-    save_flags(flags);
-    cli();
-
-    dmachan = lp->dmachan;
-    dma_abs = (unsigned long) (lp->txdmabuf);
-
-    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
-	panic("PI: TX buffer violates DMA boundary!");
-
-    disable_dma(dmachan);
-    /* Set DMA mode register to single transfers, incrementing address,
-     *  no auto init, reads
-     */
-    set_dma_mode(dmachan, DMA_MODE_WRITE);
-    clear_dma_ff(dmachan);
-    set_dma_addr(dmachan, dma_abs);
-    /* output byte count */
-    set_dma_count(dmachan, length);
-
-    restore_flags(flags);
-}
-
-static void tdelay(struct pi_local *lp, int time)
-{
-    int port;
-    unsigned int t1;
-    unsigned char sc;
-
-    if (lp->base & 2) {		/* If A channel */
-	sc = SC1;
-	t1 = time;
-	port = lp->cardbase + TMR1;
-    } else {
-	sc = SC2;
-	t1 = 10 * time;		/* 10s of milliseconds for the B channel */
-	port = lp->cardbase + TMR2;
-	wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
-    }
-
-    /* Setup timer sc */
-    outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD);
-
-    /* times 2 to make millisecs */
-    outb_p((t1 << 1) & 0xFF, port);
-    outb_p((t1 >> 7) & 0xFF, port);
-
-    /* Enable correct int for timeout */
-    wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE);
-    wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);
-}
-
-static void a_txint(struct pi_local *lp)
-{
-    int cmd;
-    unsigned long flags;
-
-    save_flags(flags);
-    cli();
-
-    cmd = CTL + lp->base;
-
-    switch (lp->tstate) {
-    case IDLE:
-	/* Transmitter idle. Find a frame for transmission */
-	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
-	    rts(lp, OFF);
-	    restore_flags(flags);
-	    return;
-	}
-	/* If a buffer to send, we drop thru here */
-    case DEFER:
-	/* we may have deferred prev xmit attempt */
-	/* Check DCD - debounce it
-         * See Intel Microcommunications Handbook, p2-308
-         */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, 100);
-	    /* defer until DCD transition or timeout */
-	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
-	    restore_flags(flags);
-	    return;
-	}
-	if (random() > lp->persist) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, lp->slotime);
-	    restore_flags(flags);
-	    return;
-	}
-	/* Assert RTS early minimize collision window */
-	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
-	rts(lp, ON);		/* Transmitter on */
-	lp->tstate = ST_TXDELAY;
-	tdelay(lp, lp->txdelay);
-	restore_flags(flags);
-	return;
-    default:
-	break;
-    }				/* end switch(lp->state) */
-
-    restore_flags(flags);
-}				/*a_txint */
-
-static void a_exint(struct pi_local *lp)
-{
-    unsigned long flags;
-    int cmd;
-    char st;
-    int length;
-
-    save_flags(flags);
-    cli();			/* disable interrupts */
-
-    st = rdscc(lp->cardbase, lp->base + CTL, R0);	/* Fetch status */
-
-    /* reset external status latch */
-    wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT);
-    cmd = lp->base + CTL;
-
-    if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) {
-	setup_rx_dma(lp);
-	lp->rstate = ACTIVE;
-    }
-    switch (lp->tstate) {
-    case ACTIVE:
-	kfree_skb(lp->sndbuf, FREE_WRITE);
-	lp->sndbuf = NULL;
-	lp->tstate = FLAGOUT;
-	tdelay(lp, lp->squeldelay);
-	break;
-    case FLAGOUT:
-	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
-	    /* Nothing to send - return to receive mode */
-	    lp->tstate = IDLE;
-	    rts(lp, OFF);
-	    restore_flags(flags);
-	    return;
-	}
-	/* NOTE - fall through if more to send */
-    case ST_TXDELAY:
-	/* Disable DMA chan */
-	disable_dma(lp->dmachan);
-
-	/* Set up for TX dma */
-	wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
-
-
-	/* Get all chars */
-	/* Strip KISS control byte */
-	length = lp->sndbuf->len - 1;
-	memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
-
-
-	/* Setup DMA controller for tx */
-	setup_tx_dma(lp, length);
-
-	/* select transmit interrupts to enable */
-	/* Allow DMA on chan */
-	enable_dma(lp->dmachan);
-
-	/* reset CRC, Txint pend*/
-	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
-
-	/* allow Underrun int only */
-	wrtscc(lp->cardbase, cmd, R15, TxUIE);
-
-	/* Enable TX DMA */
-	wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
-
-	/* Send CRC on underrun */
-	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
-
-
-	/* packet going out now */
-	lp->tstate = ACTIVE;
-	break;
-    case DEFER:
-	/* we have deferred prev xmit attempt
-         * See Intel Microcommunications Handbook, p2-308
-         */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, 100);
-	    /* Defer until dcd transition or 100mS timeout */
-	    wrtscc(lp->cardbase, CTL + lp->base, R15, CTSIE | DCDIE);
-	    restore_flags(flags);
-	    return;
-	}
-	if (random() > lp->persist) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, lp->slotime);
-	    restore_flags(flags);
-	    return;
-	}
-	/* Assert RTS early minimize collision window */
-	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
-	rts(lp, ON);		/* Transmitter on */
-	lp->tstate = ST_TXDELAY;
-	tdelay(lp, lp->txdelay);
-	restore_flags(flags);
-	return;
-    }				/* switch(lp->tstate) */
-
-    restore_flags(flags);
-}				/* a_exint() */
-
-/* Receive interrupt handler for the A channel
- */
-static void a_rxint(struct device *dev, struct pi_local *lp)
-{
-    unsigned long flags;
-    int cmd;
-    int bytecount;
-    char rse;
-    struct sk_buff *skb;
-    int sksize, pkt_len;
-    struct mbuf *cur_buf;
-    unsigned char *cfix;
-
-    save_flags(flags);
-    cli();			/* disable interrupts */
-    cmd = lp->base + CTL;
-
-    rse = rdscc(lp->cardbase, cmd, R1);	/* Get special condition bits from R1 */
-    if (rse & Rx_OVR)
-	lp->rstate = RXERROR;
-
-    if (rse & END_FR) {
-	/* If end of frame */
-	/* figure length of frame from 8237 */
-	clear_dma_ff(lp->dmachan);
-	bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
-
-	if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10)) {
-	    if ((bytecount >= 10) && (rse & CRC_ERR)) {
-		lp->stats.rx_crc_errors++;
-	    }
-	    if (lp->rstate == RXERROR) {
-		lp->stats.rx_errors++;
-		lp->stats.rx_over_errors++;
-	    }
-	    /* Reset buffer pointers */
-	    lp->rstate = ACTIVE;
-	    setup_rx_dma(lp);
-	} else {
-	    /* Here we have a valid frame */
-	    /* Toss 2 crc bytes , add one for KISS */
-	    pkt_len = lp->rcvbuf->cnt = bytecount - 2 + 1;
-
-	    /* Get buffer for next frame */
-	    cur_buf = lp->rcvbuf;
-	    switchbuffers(lp);
-	    setup_rx_dma(lp);
-
-
-	    /* Malloc up new buffer. */
-	    sksize = pkt_len;
-
-	    skb = dev_alloc_skb(sksize);
-	    if (skb == NULL) {
-		printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
-		lp->stats.rx_dropped++;
-		restore_flags(flags);
-		return;
-	    }
-	    skb->dev = dev;
-
-	    /* KISS kludge -  prefix with a 0 byte */
-	    cfix=skb_put(skb,pkt_len);
-	    *cfix++=0;
-	    /* 'skb->data' points to the start of sk_buff data area. */
-	    memcpy(cfix, (char *) cur_buf->data,
-		   pkt_len - 1);
-	    skb->protocol=htons(ETH_P_AX25);
-	    skb->mac.raw=skb->data;
-	    netif_rx(skb);
-	    lp->stats.rx_packets++;
-	}			/* end good frame */
-    }				/* end EOF check */
-    wrtscc(lp->cardbase, lp->base + CTL, R0, ERR_RES);	/* error reset */
-    restore_flags(flags);
-}
-
-static void b_rxint(struct device *dev, struct pi_local *lp)
-{
-    unsigned long flags;
-    int cmd;
-    char rse;
-    struct sk_buff *skb;
-    int sksize;
-    int pkt_len;
-    unsigned char *cfix;
-
-    save_flags(flags);
-    cli();			/* disable interrupts */
-    cmd = CTL + lp->base;
-
-    rse = rdscc(lp->cardbase, cmd, R1);	/* get status byte from R1 */
-
-    if ((rdscc(lp->cardbase, cmd, R0)) & Rx_CH_AV) {
-	/* there is a char to be stored
-         * read special condition bits before reading the data char
-         */
-	if (rse & Rx_OVR) {
-	    /* Rx overrun - toss buffer */
-	    /* reset buffer pointers */
-	    lp->rcp = lp->rcvbuf->data;
-	    lp->rcvbuf->cnt = 0;
-
-	    lp->rstate = RXERROR;	/* set error flag */
-	    lp->stats.rx_errors++;
-	    lp->stats.rx_over_errors++;
-	} else if (lp->rcvbuf->cnt >= lp->bufsiz) {
-	    /* Too large -- toss buffer */
-	    /* reset buffer pointers */
-	    lp->rcp = lp->rcvbuf->data;
-	    lp->rcvbuf->cnt = 0;
-	    lp->rstate = TOOBIG;/* when set, chars are not stored */
-	}
-	/* ok, we can store the received character now */
-	if (lp->rstate == ACTIVE) {	/* If no errors... */
-	    *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);	/* char to rcv buff */
-	    lp->rcvbuf->cnt++;	/* bump count */
-	} else {
-	    /* got to empty FIFO */
-	    (void) rdscc(lp->cardbase, cmd, R8);
-	    wrtscc(lp->cardbase, cmd, R0, ERR_RES);	/* reset err latch */
-	    lp->rstate = ACTIVE;
-	}
-    }
-    if (rse & END_FR) {
-	/* END OF FRAME -- Make sure Rx was active */
-	if (lp->rcvbuf->cnt > 0) {
-	    if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (lp->rcvbuf->cnt < 10)) {
-		if ((lp->rcvbuf->cnt >= 10) && (rse & CRC_ERR)) {
-		    lp->stats.rx_crc_errors++;
-		}
-		lp->rcp = lp->rcvbuf->data;
-		lp->rcvbuf->cnt = 0;
-	    } else {
-		/* Here we have a valid frame */
-		pkt_len = lp->rcvbuf->cnt -= 2;	/* Toss 2 crc bytes */
-		pkt_len += 1;	/* Make room for KISS control byte */
-
-		/* Malloc up new buffer. */
-		sksize = pkt_len;
-		skb = dev_alloc_skb(sksize);
-		if (skb == NULL) {
-		    printk(KERN_ERR "PI: %s: Memory squeeze, dropping packet.\n", dev->name);
-		    lp->stats.rx_dropped++;
-		    restore_flags(flags);
-		    return;
-		}
-		skb->dev = dev;
-
-		/* KISS kludge -  prefix with a 0 byte */
-		cfix=skb_put(skb,pkt_len);
-		*cfix++=0;
-		/* 'skb->data' points to the start of sk_buff data area. */
-		memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
-		skb->protocol=ntohs(ETH_P_AX25);
-		skb->mac.raw=skb->data;
-		netif_rx(skb);
-		lp->stats.rx_packets++;
-		/* packet queued - initialize buffer for next frame */
-		lp->rcp = lp->rcvbuf->data;
-		lp->rcvbuf->cnt = 0;
-
-	    }			/* end good frame queued */
-	}			/* end check for active receive upon EOF */
-	lp->rstate = ACTIVE;	/* and clear error status */
-    }				/* end EOF check */
-    restore_flags(flags);
-}
-
-
-static void b_txint(struct pi_local *lp)
-{
-    unsigned long flags;
-    int cmd;
-    unsigned char c;
-
-    save_flags(flags);
-    cli();
-    cmd = CTL + lp->base;
-
-    switch (lp->tstate) {
-    case CRCOUT:
-	lp->tstate = FLAGOUT;
-	tdelay(lp, lp->squeldelay);
-	restore_flags(flags);
-	return;
-    case IDLE:
-	/* Transmitter idle. Find a frame for transmission */
-	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
-	    /* Nothing to send - return to receive mode
-             * Tx OFF now - flag should have gone
-             */
-	    rts(lp, OFF);
-
-	    restore_flags(flags);
-	    return;
-	}
-	lp->txptr = lp->sndbuf->data;
-	lp->txptr++;		/* Ignore KISS control byte */
-	lp->txcnt = (int) lp->sndbuf->len - 1;
-	/* If a buffer to send, we drop thru here */
-    case DEFER:		/* we may have deferred prev xmit attempt */
-	/* Check DCD - debounce it */
-	/* See Intel Microcommunications Handbook, p2-308 */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, 100);
-	    /* defer until DCD transition or timeout */
-	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
-	    restore_flags(flags);
-	    return;
-	}
-	if (random() > lp->persist) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, lp->slotime);
-	    restore_flags(flags);
-	    return;
-	}
-	rts(lp, ON);		/* Transmitter on */
-	lp->tstate = ST_TXDELAY;
-	tdelay(lp, lp->txdelay);
-	restore_flags(flags);
-	return;
-
-    case ACTIVE:
-	/* Here we are actively sending a frame */
-	if (lp->txcnt--) {
-	    c = *lp->txptr++;
-	    /* next char is gone */
-	    wrtscc(lp->cardbase, cmd, R8, c);
-	    /* stuffing a char satisfies Interrupt condition */
-	} else {
-	    /* No more to send */
-	    kfree_skb(lp->sndbuf, FREE_WRITE);
-	    lp->sndbuf = NULL;
-	    if ((rdscc(lp->cardbase, cmd, R0) & 0x40)) {
-		/* Did we underrun? */
-		/* unexpected underrun */
-		lp->stats.tx_errors++;
-		lp->stats.tx_fifo_errors++;
-		wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
-		lp->tstate = FLAGOUT;
-		tdelay(lp, lp->squeldelay);
-		restore_flags(flags);
-		return;
-	    }
-	    lp->tstate = UNDERRUN;	/* Now we expect to underrun */
-	    /* Send flags on underrun */
-	    if (lp->speed) {	/* If internally clocked */
-		wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
-	    } else {
-		wrtscc(lp->cardbase, cmd, R10, CRCPS);
-	    }
-	    wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);	/* reset Tx Int Pend */
-	}
-	restore_flags(flags);
-	return;			/* back to wait for interrupt */
-    }				/* end switch */
-    restore_flags(flags);
-}
-
-/* Pi SIO External/Status interrupts (for the B channel)
- * This can be caused by a receiver abort, or a Tx UNDERRUN/EOM.
- * Receiver automatically goes to Hunt on an abort.
- *
- * If the Tx Underrun interrupt hits, change state and
- * issue a reset command for it, and return.
- */
-static void b_exint(struct pi_local *lp)
-{
-    unsigned long flags;
-    char st;
-    int cmd;
-    char c;
-
-    cmd = CTL + lp->base;
-    save_flags(flags);
-    cli();			/* disable interrupts */
-    st = rdscc(lp->cardbase, cmd, R0);	/* Fetch status */
-    /* reset external status latch */
-    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-
-
-    switch (lp->tstate) {
-    case ACTIVE:		/* Unexpected underrun */
-	kfree_skb(lp->sndbuf, FREE_WRITE);
-	lp->sndbuf = NULL;
-	wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
-	lp->tstate = FLAGOUT;
-	lp->stats.tx_errors++;
-	lp->stats.tx_fifo_errors++;
-	tdelay(lp, lp->squeldelay);
-	restore_flags(flags);
-	return;
-    case UNDERRUN:
-	lp->tstate = CRCOUT;
-	restore_flags(flags);
-	return;
-    case FLAGOUT:
-	/* Find a frame for transmission */
-	if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) {
-	    /* Nothing to send - return to receive mode
-             * Tx OFF now - flag should have gone
-             */
-	    rts(lp, OFF);
-	    lp->tstate = IDLE;
-	    restore_flags(flags);
-	    return;
-	}
-	lp->txptr = lp->sndbuf->data;
-	lp->txptr++;		/* Ignore KISS control byte */
-	lp->txcnt = (int) lp->sndbuf->len - 1;
-	/* Get first char to send */
-	lp->txcnt--;
-	c = *lp->txptr++;
-	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);	/* reset for next frame */
-
-	/* Send abort on underrun */
-	if (lp->speed) {	/* If internally clocked */
-	    wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
-	} else {
-	    wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
-	}
-
-	wrtscc(lp->cardbase, cmd, R8, c);	/* First char out now */
-	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);	/* Reset end of message latch */
-
-#ifdef STUFF2
-        /* stuff an extra one if we can */
-	if (lp->txcnt) {
-	    lp->txcnt--;
-	    c = *lp->txptr++;
-	    /* Wait for tx buffer empty */
-	    while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
-		;
-	    wrtscc(lp->cardbase, cmd, R8, c);
-	}
-#endif
-
-	/* select transmit interrupts to enable */
-
-	wrtscc(lp->cardbase, cmd, R15, TxUIE);	/* allow Underrun int only */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);	/* Tx/Ext ints */
-
-	lp->tstate = ACTIVE;	/* char going out now */
-	restore_flags(flags);
-	return;
-
-    case DEFER:
-	/* Check DCD - debounce it
-         * See Intel Microcommunications Handbook, p2-308
-         */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, 100);
-	    /* defer until DCD transition or timeout */
-	    wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE);
-	    restore_flags(flags);
-	    return;
-	}
-	if (random() > lp->persist) {
-	    lp->tstate = DEFER;
-	    tdelay(lp, lp->slotime);
-	    restore_flags(flags);
-	    return;
-	}
-	rts(lp, ON);		/* Transmitter on */
-	lp->tstate = ST_TXDELAY;
-	tdelay(lp, lp->txdelay);
-	restore_flags(flags);
-	return;
-
-    case ST_TXDELAY:
-
-	/* Get first char to send */
-	lp->txcnt--;
-	c = *lp->txptr++;
-	wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);	/* reset for next frame */
-
-	/* Send abort on underrun */
-	if (lp->speed) {	/* If internally clocked */
-	    wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
-	} else {
-	    wrtscc(lp->cardbase, cmd, R10, CRCPS | ABUNDER);
-	}
-
-	wrtscc(lp->cardbase, cmd, R8, c);	/* First char out now */
-	wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);	/* Reset end of message latch */
-
-#ifdef STUFF2
-        /* stuff an extra one if we can */
-	if (lp->txcnt) {
-	    lp->txcnt--;
-	    c = *lp->txptr++;
-	    /* Wait for tx buffer empty */
-	    while((rdscc(lp->cardbase, cmd, R0) & 0x04) == 0)
-		;
-	    wrtscc(lp->cardbase, cmd, R8, c);
-	}
-#endif
-
-	/* select transmit interrupts to enable */
-
-	wrtscc(lp->cardbase, cmd, R15, TxUIE);	/* allow Underrun int only */
-	wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	/* Tx/Extern ints on */
-	wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
-
-	lp->tstate = ACTIVE;	/* char going out now */
-	restore_flags(flags);
-	return;
-    }
-
-    /* Receive Mode only
-     * This triggers when hunt mode is entered, & since an ABORT
-     * automatically enters hunt mode, we use that to clean up
-     * any waiting garbage
-     */
-    if ((lp->rstate == ACTIVE) && (st & BRK_ABRT)) {
-	(void) rdscc(lp->cardbase, cmd, R8);
-	(void) rdscc(lp->cardbase, cmd, R8);
-	(void) rdscc(lp->cardbase, cmd, R8);
-	lp->rcp = lp->rcvbuf->data;
-	lp->rcvbuf->cnt = 0;	/* rewind on DCD transition */
-    }
-    restore_flags(flags);
-}
-
-/* Probe for a PI card. */
-/* This routine also initializes the timer chip */
-
-__initfunc(static int hw_probe(int ioaddr))
-{
-    int time = 1000;		/* Number of milliseconds for test */
-    unsigned long start_time, end_time;
-
-    int base, tmr0, tmr1, tmrcmd;
-    int a = 1;
-    int b = 1;
-
-    base = ioaddr & 0x3f0;
-    tmr0 = TMR0 + base;
-    tmr1 = TMR1 + base;
-    tmrcmd = TMRCMD + base;
-
-    /* Set up counter chip timer 0 for 500 uS period square wave */
-    /* assuming a 3.68 mhz clock for now */
-    outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
-    outb_p(922 & 0xFF, tmr0);
-    outb_p(922 >> 8, tmr0);
-
-    /* Setup timer control word for timer 1*/
-    outb_p(SC1 | LSB_MSB | MODE0, tmrcmd);
-    outb_p((time << 1) & 0xFF, tmr1);
-    outb_p((time >> 7) & 0XFF, tmr1);
-
-    /* wait until counter reg is loaded */
-    do {
-	/* Latch count for reading */
-	outb_p(SC1, tmrcmd);
-	a = inb_p(tmr1);
-	b = inb_p(tmr1);
-    } while (b == 0);
-    start_time = jiffies;
-    while (b != 0) {
-	/* Latch count for reading */
-	outb_p(SC1, tmrcmd);
-	a = inb_p(tmr1);
-	b = inb_p(tmr1);
-	end_time = jiffies;
-	/* Don't wait forever - there may be no card here */
-	if ((end_time - start_time) > 200)
-	    return 0;		/* No card found */
-    }
-    end_time = jiffies;
-    /* 87 jiffies, for a 3.68 mhz clock, half that for a double speed clock */
-    if ((end_time - start_time) > 65) {
-	return (1);		/* PI card found */
-    } else {
-	/* Faster crystal - tmr0 needs adjusting */
-	/* Set up counter chip */
-	/* 500 uS square wave */
-	outb_p(SC0 | LSB_MSB | MODE3, tmrcmd);
-	outb_p(1844 & 0xFF, tmr0);
-	outb_p(1844 >> 8, tmr0);
-	return (2);		/* PI2 card found */
-    }
-}
-
-static void rts(struct pi_local *lp, int x)
-{
-    int tc;
-    long br;
-    int cmd;
-    int dummy;
-
-    /* assumes interrupts are off */
-    cmd = CTL + lp->base;
-
-    /* Reprogram BRG and turn on transmitter to send flags */
-    if (x == ON) {		/* Turn Tx ON and Receive OFF */
-	/* Exints off first to avoid abort int */
-	wrtscc(lp->cardbase, cmd, R15, 0);
-	wrtscc(lp->cardbase, cmd, R3, Rx8);	/* Rx off */
-	lp->rstate = IDLE;
-	if (cmd & 2) {		/* if channel a */
-	    /* Set up for TX dma */
-	    wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
-	} else {
-	    wrtscc(lp->cardbase, cmd, R1, 0);	/* No interrupts */
-	}
-
-	if (!lp->clockmode) {
-	    if (lp->speed) {	/* if internally clocked */
-		br = lp->speed;	/* get desired speed */
-		tc = (lp->xtal / br) - 2;	/* calc 1X BRG divisor */
-		wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
-		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
-	    }
-	}
-	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
-	/* Transmitter now on */
-    } else {			/* Tx OFF and Rx ON */
-	lp->tstate = IDLE;
-	wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);	/*  TX off */
-
-	if (!lp->clockmode) {
-	    if (lp->speed) {	/* if internally clocked */
-		/* Reprogram BRG for 32x clock for receive DPLL */
-		/* BRG off, keep Pclk source */
-		wrtscc(lp->cardbase, cmd, R14, BRSRC);
-		br = lp->speed;	/* get desired speed */
-		/* calc 32X BRG divisor */
-		tc = ((lp->xtal / 32) / br) - 2;
-		wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
-		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
-		/* SEARCH mode, BRG source */
-		wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
-		/* Enable the BRG */
-		wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
-	    }
-	}
-	/* Flush rx fifo */
-	wrtscc(lp->cardbase, cmd, R3, Rx8);	/* Make sure rx is off */
-	wrtscc(lp->cardbase, cmd, R0, ERR_RES);	/* reset err latch */
-	dummy = rdscc(lp->cardbase, cmd, R1);	/* get status byte from R1 */
-	(void) rdscc(lp->cardbase, cmd, R8);
-	(void) rdscc(lp->cardbase, cmd, R8);
-
-	(void) rdscc(lp->cardbase, cmd, R8);
-
-	/* Now, turn on the receiver and hunt for a flag */
-	wrtscc(lp->cardbase, cmd, R3, RxENABLE | Rx8);
-	lp->rstate = ACTIVE;	/* Normal state */
-
-	if (cmd & 2) {		/* if channel a */
-	    setup_rx_dma(lp);
-	} else {
-	    /* reset buffer pointers */
-	    lp->rcp = lp->rcvbuf->data;
-	    lp->rcvbuf->cnt = 0;
-	    wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
-	}
-	wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* allow ABORT int */
-    }
-}
-
-static void scc_init(struct device *dev)
-{
-    unsigned long flags;
-    struct pi_local *lp = (struct pi_local *) dev->priv;
-
-    int tc;
-    long br;
-    register int cmd;
-
-    /* Initialize 8530 channel for SDLC operation */
-
-    cmd = CTL + lp->base;
-    save_flags(flags);
-    cli();
-
-    switch (cmd & CHANA) {
-    case CHANA:
-	wrtscc(lp->cardbase, cmd, R9, CHRA);	/* Reset channel A */
-	wrtscc(lp->cardbase, cmd, R2, 0xff);	/* Initialize interrupt vector */
-	break;
-    default:
-	wrtscc(lp->cardbase, cmd, R9, CHRB);	/* Reset channel B */
-	break;
-    }
-
-    /* Deselect all Rx and Tx interrupts */
-    wrtscc(lp->cardbase, cmd, R1, 0);
-
-    /* Turn off external interrupts (like CTS/CD) */
-    wrtscc(lp->cardbase, cmd, R15, 0);
-
-    /* X1 clock, SDLC mode */
-    wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
-
-    /* Tx/Rx parameters */
-    if (lp->speed) {		/* Use internal clocking */
-	wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
-	if (!lp->clockmode)
-	    /* Tx Clk from BRG. Rcv Clk from DPLL, TRxC pin outputs DPLL */
-	    wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
-	else
-	    /* Tx Clk from DPLL, Rcv Clk from DPLL, TRxC Outputs BRG */
-	    wrtscc(lp->cardbase, cmd, R11, TCDPLL | RCDPLL | TRxCBR | TRxCOI);
-    } else {			/* Use external clocking */
-	wrtscc(lp->cardbase, cmd, R10, CRCPS);
-	/* Tx Clk from Trxcl. Rcv Clk from Rtxcl, TRxC pin is input */
-	wrtscc(lp->cardbase, cmd, R11, TCTRxCP);
-    }
-
-    /* Null out SDLC start address */
-    wrtscc(lp->cardbase, cmd, R6, 0);
-
-    /* SDLC flag */
-    wrtscc(lp->cardbase, cmd, R7, FLAG);
-
-    /* Set up the Transmitter but don't enable it
-     *  DTR, 8 bit TX chars only
-     */
-    wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
-
-    /* Receiver initial setup */
-    wrtscc(lp->cardbase, cmd, R3, Rx8);	/* 8 bits/char */
-
-    /* Setting up BRG now - turn it off first */
-    wrtscc(lp->cardbase, cmd, R14, BRSRC);	/* BRG off, keep Pclk source */
-
-    /* set the 32x time constant for the BRG in Receive mode */
-
-    if (lp->speed) {
-	br = lp->speed;		/* get desired speed */
-	tc = ((lp->xtal / 32) / br) - 2;	/* calc 32X BRG divisor */
-    } else {
-	tc = 14;
-    }
-
-    wrtscc(lp->cardbase, cmd, R12, tc & 0xFF);	/* lower byte */
-    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xFF);	/* upper byte */
-
-    /* Following subroutine sets up and ENABLES the receiver */
-    rts(lp, OFF);		/* TX OFF and RX ON */
-
-    if (lp->speed) {
-	/* DPLL frm BRG, BRG src PCLK */
-	wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
-    } else {
-	/* DPLL frm rtxc,BRG src PCLK */
-	wrtscc(lp->cardbase, cmd, R14, BRSRC | SSRTxC);
-    }
-    wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);	/* SEARCH mode, keep BRG src */
-    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);	/* Enable the BRG */
-
-    if (!(cmd & 2))		/* if channel b */
-	wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
-
-    wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* ABORT int */
-
-    /* Now, turn on the receiver and hunt for a flag */
-    wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | Rx8);
-
-    restore_flags(flags);
-}
-
-static void chipset_init(struct device *dev)
-{
-    int cardbase;
-    unsigned long flags;
-
-    cardbase = dev->base_addr & 0x3f0;
-
-    save_flags(flags);
-    cli();
-    wrtscc(cardbase, dev->base_addr + CTL, R9, FHWRES);	/* Hardware reset */
-    /* Disable interrupts with master interrupt ctrl reg */
-    wrtscc(cardbase, dev->base_addr + CTL, R9, 0);
-    restore_flags(flags);
-
-}
-
-
-__initfunc(int pi_init(void))
-{
-    int *port;
-    int ioaddr = 0;
-    int card_type = 0;
-    int ports[] = {0x380, 0x300, 0x320, 0x340, 0x360, 0x3a0, 0};
-
-    printk(KERN_INFO "PI: V0.8 ALPHA April 23 1995 David Perry (dp@hydra.carleton.ca)\n");
-
-    /* Only one card supported for now */
-    for (port = &ports[0]; *port && !card_type; port++) {
-	ioaddr = *port;
-
-	if (check_region(ioaddr, PI_TOTAL_SIZE) == 0) {
-	    printk(KERN_INFO "PI: Probing for card at address %#3x\n",ioaddr);
-	    card_type = hw_probe(ioaddr);
-	}
-    }
-
-    switch (card_type) {
-    case 1:
-	printk(KERN_INFO "PI: Found a PI card at address %#3x\n", ioaddr);
-	break;
-    case 2:
-	printk(KERN_INFO "PI: Found a PI2 card at address %#3x\n", ioaddr);
-	break;
-    default:
-	printk(KERN_ERR "PI: ERROR: No card found\n");
-	return -EIO;
-    }
-
-    /* Link a couple of device structures into the chain */
-    /* For the A port */
-    /* Allocate space for 4 buffers even though we only need 3,
-       because one of them may cross a DMA page boundary and
-       be rejected by get_dma_buffer().
-    */
-    register_netdev(&pi0a);
-
-    pi0a.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
-
-    pi0a.dma = PI_DMA;
-    pi0a.base_addr = ioaddr + 2;
-    pi0a.irq = 0;
-
-    /* And the B port */
-    register_netdev(&pi0b);
-    pi0b.base_addr = ioaddr;
-    pi0b.irq = 0;
-
-    pi0b.priv = kmalloc(sizeof(struct pi_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
-
-    /* Now initialize them */
-    pi_probe(&pi0a, card_type);
-    pi_probe(&pi0b, card_type);
-
-    pi0b.irq = pi0a.irq;	/* IRQ is shared */
-
-    return 0;
-}
-
-static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize)
-{
-    if (((addr & 0xffff) + dev_buffsize) <= 0x10000)
-	return 1;
-    else
-	return 0;
-}
-
-static int pi_set_mac_address(struct device *dev, void *addr)
-{
-    struct sockaddr *sa = (struct sockaddr *)addr;
-    memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);	/* addr is an AX.25 shifted ASCII */
-    return 0;						/* mac address */
-}
-
-/* Allocate a buffer which does not cross a DMA page boundary */
-static char *
-get_dma_buffer(unsigned long *mem_ptr)
-{
-    char *ret;
-
-    ret = (char *)*mem_ptr;
-
-    if(!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))){
-	*mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
-	ret = (char *)*mem_ptr;
-    }
-    *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
-    return (ret);
-}
-
-static int pi_probe(struct device *dev, int card_type)
-{
-    short ioaddr;
-    struct pi_local *lp;
-    unsigned long flags;
-    unsigned long mem_ptr;
-
-    ioaddr = dev->base_addr;
-
-    /* Initialize the device structure. */
-    /* Must be done before chipset_init */
-    /* Make certain the data structures used by the PI2 are aligned. */
-    dev->priv = (void *) (((int) dev->priv + 7) & ~7);
-    lp = (struct pi_local *) dev->priv;
-
-    memset(dev->priv, 0, sizeof(struct pi_local));
-
-    /* Allocate some buffers which do not cross DMA page boundaries */
-    mem_ptr = (unsigned long) dev->priv + sizeof(struct pi_local);
-    lp->txdmabuf = get_dma_buffer(&mem_ptr);
-    lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
-    lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
-
-    /* Initialize rx buffer */
-    lp->rcvbuf = lp->rxdmabuf1;
-    lp->rcp = lp->rcvbuf->data;
-    lp->rcvbuf->cnt = 0;
-
-    /* Initialize the transmit queue head structure */
-    skb_queue_head_init(&lp->sndq);
-
-    /* These need to be initialized before scc_init is called. */
-    if (card_type == 1)
-	lp->xtal = (unsigned long) SINGLE / 2;
-    else
-	lp->xtal = (unsigned long) DOUBLE / 2;
-    lp->base = dev->base_addr;
-    lp->cardbase = dev->base_addr & 0x3f0;
-    if (dev->base_addr & CHANA) {
-	lp->speed = DEF_A_SPEED;
-	/* default channel access Params */
-	lp->txdelay = DEF_A_TXDELAY;
-	lp->persist = DEF_A_PERSIST;
-	lp->slotime = DEF_A_SLOTIME;
-	lp->squeldelay = DEF_A_SQUELDELAY;
-	lp->clockmode = DEF_A_CLOCKMODE;
-
-    } else {
-	lp->speed = DEF_B_SPEED;
-	/* default channel access Params */
-	lp->txdelay = DEF_B_TXDELAY;
-	lp->persist = DEF_B_PERSIST;
-	lp->slotime = DEF_B_SLOTIME;
-	lp->squeldelay = DEF_B_SQUELDELAY;
-	lp->clockmode = DEF_B_CLOCKMODE;
-    }
-    lp->bufsiz = DMA_BUFF_SIZE;
-    lp->tstate = IDLE;
-
-    chipset_init(dev);
-
-    if (dev->base_addr & CHANA) {	/* Do these things only for the A port */
-	/* Note that a single IRQ services 2 devices (A and B channels) */
-
-	lp->dmachan = dev->dma;
-	if (lp->dmachan < 1 || lp->dmachan > 3)
-	    printk(KERN_ERR "PI: DMA channel %d out of range\n", lp->dmachan);
-
-	/* chipset_init() was already called */
-
-	if (dev->irq < 2) {
-	    autoirq_setup(0);
-	    save_flags(flags);
-	    cli();
-	    wrtscc(lp->cardbase, CTL + lp->base, R1, EXT_INT_ENAB);
-	    /* enable PI card interrupts */
-	    wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
-	    restore_flags(flags);
-	    /* request a timer interrupt for 1 mS hence */
-	    tdelay(lp, 1);
-	    /* 20 "jiffies" should be plenty of time... */
-	    dev->irq = autoirq_report(20);
-	    if (!dev->irq) {
-		printk(KERN_ERR "PI: Failed to detect IRQ line.\n");
-	    }
-	    save_flags(flags);
-	    cli();
-	    wrtscc(lp->cardbase, dev->base_addr + CTL, R9, FHWRES);	/* Hardware reset */
-	    /* Disable interrupts with master interrupt ctrl reg */
-	    wrtscc(lp->cardbase, dev->base_addr + CTL, R9, 0);
-	    restore_flags(flags);
-	}
-
-	printk(KERN_INFO "PI: Autodetected IRQ %d, assuming DMA %d.\n",
-	       dev->irq, dev->dma);
-
-	/* This board has jumpered interrupts. Snarf the interrupt vector
-		   now.  There is no point in waiting since no other device can use
-		   the interrupt, and this marks the 'irqaction' as busy. */
-	{
-	    int irqval = request_irq(dev->irq, &pi_interrupt,0, "pi2", dev);
-	    if (irqval) {
-		printk(KERN_ERR "PI: unable to get IRQ %d (irqval=%d).\n",
-		       dev->irq, irqval);
-		return EAGAIN;
-	    }
-	}
-
-	/* Grab the region */
-	request_region(ioaddr & 0x3f0, PI_TOTAL_SIZE, "pi2" );
-
-
-    }				/* Only for A port */
-    dev->open = pi_open;
-    dev->stop = pi_close;
-    dev->do_ioctl = pi_ioctl;
-    dev->hard_start_xmit = pi_send_packet;
-    dev->get_stats = pi_get_stats;
-
-    /* Fill in the fields of the device structure */
-
-    dev_init_buffers(dev);
-    
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-    dev->hard_header    = ax25_encapsulate;
-    dev->rebuild_header = ax25_rebuild_header;
-#endif
-
-    dev->set_mac_address = pi_set_mac_address;
-
-    dev->type = ARPHRD_AX25;			/* AF_AX25 device */
-    dev->hard_header_len = 73;			/* We do digipeaters now */
-    dev->mtu = 1500;				/* eth_mtu is the default */
-    dev->addr_len = 7;				/* sizeof an ax.25 address */
-    memcpy(dev->broadcast, ax25_bcast, 7);
-    memcpy(dev->dev_addr, ax25_test, 7);
-
-    /* New-style flags. */
-    dev->flags = 0;
-    return 0;
-}
-
-/* Open/initialize the board.  This is called (in the current kernel)
-   sometime after booting when the 'ifconfig' program is run.
-
-   This routine should set everything up anew at each open, even
-   registers that "should" only need to be set once at boot, so that
-   there is non-reboot way to recover if something goes wrong.
-   */
-static int pi_open(struct device *dev)
-{
-    unsigned long flags;
-    static first_time = 1;
-
-    struct pi_local *lp = (struct pi_local *) dev->priv;
-
-    if (dev->base_addr & 2) {	/* if A channel */
-	if (first_time) {
-	    if (request_dma(dev->dma,"pi2")) {
-		free_irq(dev->irq, dev);
-		return -EAGAIN;
-	    }
-	}
-	/* Reset the hardware here. */
-	chipset_init(dev);
-    }
-    lp->tstate = IDLE;
-
-    if (dev->base_addr & 2) {	/* if A channel */
-	scc_init(dev);		/* Called once for each channel */
-	scc_init(dev->next);
-    }
-    /* master interrupt enable */
-    save_flags(flags);
-    cli();
-    wrtscc(lp->cardbase, CTL + lp->base, R9, MIE | NV);
-    restore_flags(flags);
-
-    lp->open_time = jiffies;
-
-    dev->tbusy = 0;
-    dev->interrupt = 0;
-    dev->start = 1;
-    first_time = 0;
-
-    MOD_INC_USE_COUNT;
-
-    return 0;
-}
-
-static int pi_send_packet(struct sk_buff *skb, struct device *dev)
-{
-    struct pi_local *lp = (struct pi_local *) dev->priv;
-
-    hardware_send_packet(lp, skb);
-    dev->trans_start = jiffies;
-
-    return 0;
-}
-
-/* The typical workload of the driver:
-   Handle the network interface interrupts. */
-static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs)
-{
-/*    int irq = -(((struct pt_regs *) reg_ptr)->orig_eax + 2);*/
-    struct pi_local *lp;
-    int st;
-    unsigned long flags;
-
-/*    dev_b = dev_a->next;	 Relies on the order defined in Space.c */
-
-#if 0
-    if (dev_a == NULL) {
-	printk(KERN_ERR "PI: pi_interrupt(): irq %d for unknown device.\n", irq);
-	return;
-    }
-#endif
-    /* Read interrupt status register (only valid from channel A)
-     * Process all pending interrupts in while loop
-     */
-    lp = (struct pi_local *) pi0a.priv;	/* Assume channel A */
-    while ((st = rdscc(lp->cardbase, pi0a.base_addr | CHANA | CTL, R3)) != 0) {
-	if (st & CHBTxIP) {
-	    /* Channel B Transmit Int Pending */
-	    lp = (struct pi_local *) pi0b.priv;
-	    b_txint(lp);
-	} else if (st & CHARxIP) {
-	    /* Channel A Rcv Interrupt Pending */
-	    lp = (struct pi_local *) pi0a.priv;
-	    a_rxint(&pi0a, lp);
-	} else if (st & CHATxIP) {
-	    /* Channel A Transmit Int Pending */
-	    lp = (struct pi_local *) pi0a.priv;
-	    a_txint(lp);
-	} else if (st & CHAEXT) {
-	    /* Channel A External Status Int */
-	    lp = (struct pi_local *) pi0a.priv;
-	    a_exint(lp);
-	} else if (st & CHBRxIP) {
-	    /* Channel B Rcv Interrupt Pending */
-	    lp = (struct pi_local *) pi0b.priv;
-	    b_rxint(&pi0b, lp);
-	} else if (st & CHBEXT) {
-	    /* Channel B External Status Int */
-	    lp = (struct pi_local *) pi0b.priv;
-	    b_exint(lp);
-	}
-	/* Reset highest interrupt under service */
-	save_flags(flags);
-	cli();
-	wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
-	restore_flags(flags);
-    }				/* End of while loop on int processing */
-    return;
-}
-
-/* The inverse routine to pi_open(). */
-static int pi_close(struct device *dev)
-{
-    unsigned long flags;
-    struct pi_local *lp;
-    struct sk_buff *ptr;
-
-    save_flags(flags);
-    cli();
-
-    lp = (struct pi_local *) dev->priv;
-    ptr = NULL;
-
-    chipset_init(dev);		/* reset the scc */
-    disable_dma(lp->dmachan);
-
-    lp->open_time = 0;
-
-    dev->tbusy = 1;
-    dev->start = 0;
-
-    /* Free any buffers left in the hardware transmit queue */
-    while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
-	kfree_skb(ptr, FREE_WRITE);
-
-    restore_flags(flags);
-
-    MOD_DEC_USE_COUNT;
-
-    return 0;
-}
-
-static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-    unsigned long flags;
-    struct pi_req rq;
-    struct pi_local *lp = (struct pi_local *) dev->priv;
-
-    int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pi_req));
-    if (ret)
-	return ret;
-
-    if(cmd!=SIOCDEVPRIVATE)
-    	return -EINVAL;
-
-    copy_from_user(&rq, ifr->ifr_data, sizeof(struct pi_req));
-
-    switch (rq.cmd) {
-    case SIOCSPIPARAM:
-
-	if (!suser())
-	    return -EPERM;
-	save_flags(flags);
-	cli();
-	lp->txdelay = rq.txdelay;
-	lp->persist = rq.persist;
-	lp->slotime = rq.slotime;
-	lp->squeldelay = rq.squeldelay;
-	lp->clockmode = rq.clockmode;
-	lp->speed = rq.speed;
-	pi_open(&pi0a); /* both channels get reset %%% */
-	restore_flags(flags);
-	ret = 0;
-	break;
-
-    case SIOCSPIDMA:
-
-	if (!suser())
-	    return -EPERM;
-	ret = 0;
-	if (dev->base_addr & 2) {   /* if A channel */
-	   if (rq.dmachan < 1 || rq.dmachan > 3)
-		return -EINVAL;
-	   save_flags(flags);
-	   cli();
-	   pi_close(dev);
-	   free_dma(lp->dmachan);
-	   dev->dma = lp->dmachan = rq.dmachan;
-	   if (request_dma(lp->dmachan,"pi2"))
-		ret = -EAGAIN;
-	   pi_open(dev);
-	   restore_flags(flags);
-	}
-	break;
-
-    case SIOCSPIIRQ:
-	ret = -EINVAL;      /* add this later */
-	break;
-
-    case SIOCGPIPARAM:
-    case SIOCGPIDMA:
-    case SIOCGPIIRQ:
-
-	rq.speed = lp->speed;
-	rq.txdelay = lp->txdelay;
-	rq.persist = lp->persist;
-	rq.slotime = lp->slotime;
-	rq.squeldelay = lp->squeldelay;
-	rq.clockmode = lp->clockmode;
-	rq.dmachan = lp->dmachan;
-	rq.irq = dev->irq;
-	copy_to_user(ifr->ifr_data, &rq, sizeof(struct pi_req));
-	ret = 0;
-	break;
-
-    default:
-	ret = -EINVAL;
-    }
-    return ret;
-}
-
-/* Get the current statistics.	This may be called with the card open or
-   closed. */
-static struct net_device_stats *pi_get_stats(struct device *dev)
-{
-	struct pi_local *lp = (struct pi_local *) dev->priv;
-
-	return &lp->stats;
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-MODULE_AUTHOR("David Perry <dp@hydra.carleton.ca>");
-MODULE_DESCRIPTION("AX.25 driver for the Ottawa PI and PI/2 HDLC cards");
-
-int init_module(void)
-{
-    return pi_init();
-}
-
-void cleanup_module(void)
-{
-    free_irq(pi0a.irq, &pi0a);	/* IRQs and IO Ports are shared */
-    release_region(pi0a.base_addr & 0x3f0, PI_TOTAL_SIZE);
-
-    kfree(pi0a.priv);
-    pi0a.priv = NULL;
-    unregister_netdev(&pi0a);
-
-    kfree(pi0b.priv);
-    pi0b.priv = NULL;
-    unregister_netdev(&pi0b);
-}
-#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/pt.c linux/drivers/net/pt.c
--- v2.1.70/linux/drivers/net/pt.c	Sat Nov 29 11:25:10 1997
+++ linux/drivers/net/pt.c	Wed Dec 31 16:00:00 1969
@@ -1,1778 +0,0 @@
-#undef PT_DEBUG 1
-/*
- * pt.c: Linux device driver for the Gracilis PackeTwin.
- * Copyright (c) 1995 Craig Small VK2XLZ (vk2xlz@vk2xlz.ampr.org.)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge MA 02139, USA.
- *
- * This driver is largely based upon the PI driver by David Perry.
- *
- * Revision History
- * 23/02/95 cs  Started again on driver, last one scrapped
- * 27/02/95 cs  Program works, we have chan A only.  Tx stays on
- * 28/02/95 cs  Fix Tx problem (& TxUIE instead of | )
- *		Fix Chan B Tx timer problem, used TMR2 instead of TMR1
- * 03/03/95 cs  Painfully found out (after 3 days) SERIAL_CFG is write only
- *              created image of it and DMA_CFG
- * 21/06/95 cs  Upgraded to suit PI driver 0.8 ALPHA
- * 22/08/95 cs	Changed it all around to make it like pi driver
- * 23/08/95 cs  It now works, got caught again by TMR2 and we must have
- *				auto-enables for daughter boards.
- * 07/10/95 cs  Fixed for 1.3.30 (hopefully)
- * 26/11/95 cs  Fixed for 1.3.43, ala 29/10 for pi2.c by ac
- * 21/12/95 cs  Got rid of those nasty warnings when compiling, for 1.3.48
- * 08/08/96 jsn Convert to use as a module. Removed send_kiss, empty_scc and
- *		pt_loopback functions - they were unused.
- * 13/12/96 jsn Fixed to match Linux networking changes.
- */
-
-/*
- * default configuration of the PackeTwin,
- * ie What Craig uses his PT for.
- */
-#define PT_DMA 3
-
-#define DEF_A_SPEED	4800		/* 4800 baud */
-#define DEF_A_TXDELAY	350		/* 350 mS */
-#define DEF_A_PERSIST	64		/* 25% persistence */
-#define DEF_A_SLOTIME	10		/* 10 mS */
-#define DEF_A_SQUELDELAY 30		/* 30 mS */
-#define DEF_A_CLOCKMODE	0		/* Normal clock mode */
-#define DEF_A_NRZI		1		/* NRZI mode */
-
-#define DEF_B_SPEED	0		/* 0 means external clock */
-#define DEF_B_TXDELAY	250		/* 250 mS */
-#define DEF_B_PERSIST	64		/* 25% */
-#define DEF_B_SLOTIME	10		/* 10 mS */
-#define DEF_B_SQUELDELAY 30		/* 30 mS */
-#define DEF_B_CLOCKMODE 0 		/* Normal clock mode ?!? */
-#define DEF_B_NRZI		1		/* NRZI mode */
-
-
-#define	PARAM_TXDELAY	1
-#define	PARAM_PERSIST	2
-#define	PARAM_SLOTTIME	3
-#define	PARAM_FULLDUP	5
-#define	PARAM_HARDWARE	6
-#define	PARAM_RETURN	255
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/malloc.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/uaccess.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/timer.h>
-#include <linux/if_arp.h>
-#include <linux/pt.h>
-#include <linux/init.h>
-#include "z8530.h"
-#include <net/ax25.h>
-
-struct mbuf {
-    struct mbuf *next;
-    int cnt;
-    char data[0];
-};
-
-/*
- * The actual PT devices we will use
- */
-static int pt0_preprobe(struct device *dev) {return 0;} /* Dummy probe function */
-static struct device pt0a = { "pt0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
-static struct device pt0b = { "pt0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pt0_preprobe };
-
-/* Ok, they shouldn't be here, but both channels share them */
-/* The Images of the Serial and DMA config registers */
-static unsigned char pt_sercfg = 0;
-static unsigned char pt_dmacfg = 0;
-
-/* The number of IO ports used by the card */
-#define PT_TOTAL_SIZE   16
-
-/* Index to functions, as function prototypes. */
-
-static int pt_probe(struct device *dev);
-static int pt_open(struct device *dev);
-static int pt_send_packet(struct sk_buff *skb, struct device *dev);
-static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static int pt_close(struct device *dev);
-static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static struct net_device_stats *pt_get_stats(struct device *dev);
-static void pt_rts(struct pt_local *lp, int x);
-static void pt_rxisr(struct device *dev);
-static void pt_txisr(struct pt_local *lp);
-static void pt_exisr(struct pt_local *lp);
-static void pt_tmrisr(struct pt_local *lp);
-static char *get_dma_buffer(unsigned long *mem_ptr);
-static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);
-static int hw_probe(int ioaddr);
-static void tdelay(struct pt_local *lp, int time);
-static void chipset_init(struct device *dev);
-
-static char ax25_bcast[7] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static char ax25_test[7] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
-
-
-static int ext2_secrm_seed = 152;
-
-static inline unsigned char random(void)
-{
-    return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 60691 + 1);
-}
-
-static inline void wrtscc(int cbase, int ctl, int sccreg, unsigned char val)
-{
-    outb_p(sccreg, ctl);        /* Select register */
-    outb_p(val, ctl);           /* Output value */
-}
-
-static inline unsigned char rdscc(int cbase, int ctl, int sccreg)
-{
-    unsigned char retval;
-
-    outb_p(sccreg, ctl);        /* Select register */
-    retval = inb_p(ctl);
-    return retval;
-}
-
-static void switchbuffers(struct pt_local *lp)
-{
-    if (lp->rcvbuf == lp->rxdmabuf1)
-	lp->rcvbuf = lp->rxdmabuf2;
-    else
-	lp->rcvbuf = lp->rxdmabuf1;
-}
-
-static void hardware_send_packet(struct pt_local *lp, struct sk_buff *skb)
-{
-	char kickflag;
-	unsigned long flags;
-	char *ptr;
-	struct device *dev;
-
-	/* First, let's see if this packet is actually a KISS packet */
-	ptr = skb->data;
-	if (ptr[0] != 0 && skb->len >= 2)
-	{
-#ifdef PT_DEBUG
-		printk(KERN_DEBUG "PT: Rx KISS... Control = %d, value = %d.\n", ptr[0], (skb->len > 1? ptr[1] : -1));
-#endif
-		/* Kludge to get device */
-		if ((struct pt_local*)(&pt0b.priv) == lp)
-			dev = &pt0b;
-		else
-			dev = &pt0a;
-		switch(ptr[0])
-		{
-
-			case PARAM_TXDELAY:
-				/*TxDelay is in 10mS increments */
-				lp->txdelay = ptr[1] * 10;
-				break;
-			case PARAM_PERSIST:
-				lp->persist = ptr[1];
-				break;
-			case PARAM_SLOTTIME:
-				lp->slotime = ptr[1];
-				break;
-			case PARAM_FULLDUP:
-				/* Yeah right, you wish!  Fullduplex is a little while to
-				 * go folks, but this is how you fire it up
-				 */
-				break;
-			/* Perhaps we should have txtail here?? */
-		} /*switch */
-		return;
-	}
-
-	lp->stats.tx_packets++;
-	lp->stats.tx_bytes+=skb->len;
-	save_flags(flags);
-	cli();
-	kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL);
-	restore_flags(flags);
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: hardware_send_packet(): kickflag = %d (%d).\n", kickflag, lp->base & CHANA);
-#endif
-	skb_queue_tail(&lp->sndq, skb);
-	if (kickflag) 
-	{
-        /* Simulate interrupt to transmit */
-        	if (lp->dmachan)
-			pt_txisr(lp);
-		else 
-		{
-            		save_flags(flags);
-	           	cli();
-            		if (lp->tstate == IDLE)
-                		pt_txisr(lp);
-            		restore_flags(flags);
-		}
-	}
-} /* hardware_send_packet() */
-
-static void setup_rx_dma(struct pt_local *lp)
-{
-	unsigned long flags;
-	int cmd;
-	unsigned long dma_abs;
-	unsigned char dmachan;
-
-	save_flags(flags);
-	cli();
-
-	dma_abs = (unsigned long) (lp->rcvbuf->data);
-	dmachan = lp->dmachan;
-	cmd = lp->base + CTL;
-
-	if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
-		panic("PI: RX buffer violates DMA boundary!");
-
-	/* Get ready for RX DMA */
-	wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
-
-	disable_dma(dmachan);
-	clear_dma_ff(dmachan);
-
-	/*
-	 *	Set DMA mode register to single transfers, incrementing address,
-	 *	auto init, writes
-	 */
-
-	set_dma_mode(dmachan, DMA_MODE_READ | 0x10);
-	set_dma_addr(dmachan, dma_abs);
-	set_dma_count(dmachan, lp->bufsiz);
-	enable_dma(dmachan);
-
-	/*
-	 *	If a packet is already coming in, this line is supposed to
-	 *	avoid receiving a partial packet.
-	 */
-
-	wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC);
-
-	/* Enable RX dma */
-	wrtscc(lp->cardbase, cmd, R1,
-		WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB);
-
-	restore_flags(flags);
-}
-
-static void setup_tx_dma(struct pt_local *lp, int length)
-{
-    unsigned long dma_abs;
-    unsigned long flags;
-    unsigned long dmachan;
-
-    save_flags(flags);
-    cli();
-
-    dmachan = lp->dmachan;
-    dma_abs = (unsigned long) (lp->txdmabuf);
-
-    if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf)))
-	panic("PT: TX buffer violates DMA boundary!");
-
-    disable_dma(dmachan);
-    /* Set DMA mode register to single transfers, incrementing address,
-     *  no auto init, reads
-     */
-    set_dma_mode(dmachan, DMA_MODE_WRITE);
-    clear_dma_ff(dmachan);
-    set_dma_addr(dmachan, dma_abs);
-    /* output byte count */
-    set_dma_count(dmachan, length);
-
-    restore_flags(flags);
-}
-
-/*
- * This sets up all the registers in the SCC for the given channel
- * based upon tsync_hwint()
- */
-static void scc_init(struct device *dev)
-{
-	unsigned long flags;
-	struct pt_local *lp = (struct pt_local*) dev->priv;
-	register int cmd = lp->base + CTL;
-	int tc, br;
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: scc_init(): (%d).\n", lp->base & CHANA);
-#endif
-	save_flags(flags);
-	cli();
-
-	/* We may put something here to enable_escc */
-
-	if (cmd & CHANA)
-	{
-	        wrtscc(lp->cardbase, cmd, R9, CHRA);	/* Reset channel A */
-	        wrtscc(lp->cardbase, cmd, R2, 0xff);	/* Initialise interrupt vector */
-	}
-	else
-	    	wrtscc(lp->cardbase, cmd, R9, CHRB);	/* Reset channel B */
-
-	/* Deselect all Rx and Tx interrupts */
-	wrtscc(lp->cardbase, cmd, R1, 0);
-
-	/* Turn off external interrupts (like CTS/CD) */
-	wrtscc(lp->cardbase, cmd, R15, 0);
-
-	/* X1 clock, SDLC mode */
-	wrtscc(lp->cardbase, cmd, R4, SDLC | X1CLK);
-
-	/* Preset CRC and set mode */
-	if (lp->nrzi)
-	    	/* Preset Tx CRC, put into NRZI mode */
-		wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
-	else
-		/* Preset Tx CRC, put into NRZ mode */
-		wrtscc(lp->cardbase, cmd, R10, CRCPS);
-
-	/* Tx/Rx parameters */
-	if (lp->speed)		/* Use internal clocking */
-	       /* Tx Clk from BRG. Rx Clk form DPLL, TRxC pin outputs DPLL */
-	       wrtscc(lp->cardbase, cmd, R11, TCBR | RCDPLL | TRxCDP | TRxCOI);
-	else	/* Use external clocking */
-	{
-		/* Tx Clk from TRxCL. Rx Clk from RTxCL, TRxC pin if input */
-		wrtscc(lp->cardbase, cmd, R11, TCTRxCP | RCRTxCP | TRxCBR);
-        	wrtscc(lp->cardbase,cmd, R14, 0);	/* wiz1 */
-	}
-
-	/* Null out SDLC start address */
-	wrtscc(lp->cardbase, cmd, R6, 0);
-
-	/* SDLC flag */
-	wrtscc(lp->cardbase, cmd, R7, FLAG);
-
-	/* Setup Tx but don't enable it */
-	wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
-
-	/* Setup Rx */
-	wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
-
-	/* Setup the BRG, turn it off first */
-	wrtscc(lp->cardbase, cmd, R14, BRSRC);
-
-	/* set the 32x time constant for the BRG in Rx mode */
-	if (lp->speed)
-	{
-		br = lp->speed;
-		tc = ((lp->xtal / 32) / (br * 2)) - 2;
-		wrtscc(lp->cardbase, cmd, R12, tc & 0xff);		/* lower byte */
-   		wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);	/* upper byte */
-	}
-
-	/* Turn transmitter off, to setup stuff */
-   	pt_rts(lp, OFF);
-
-	/* External clocking */
-	if (lp->speed)
-	{
-		/* DPLL frm BRG, BRG src PCLK */
-		wrtscc(lp->cardbase, cmd, R14, BRSRC | SSBR);
-		wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);	/* SEARCH mode, keep BRG src */
-		wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);	/* Enable the BRG */
-
-	    /* Turn off external clock port */
-		if (lp->base & CHANA)
-			outb_p( (pt_sercfg &= ~PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
-		else
-			outb_p( (pt_sercfg &= ~PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
-	}
-	else
-	{
-		/* DPLL frm rtxc,BRG src PCLK */
-		/* Turn on external clock port */
-		if (lp->base & CHANA)
-			outb_p( (pt_sercfg |= PT_EXTCLKA), (lp->cardbase + SERIAL_CFG) );
-		else
-			outb_p( (pt_sercfg |= PT_EXTCLKB), (lp->cardbase + SERIAL_CFG) );
-	}
-
-	if (!lp->dmachan)
-		wrtscc(lp->cardbase, cmd, R1, (INT_ALL_Rx | EXT_INT_ENAB));
-
-	wrtscc(lp->cardbase, cmd, R15, BRKIE);	/* ABORT int */
-
-	/* Turn on the DTR to tell modem we're alive */
-	if (lp->base & CHANA)
-		outb_p( (pt_sercfg |= PT_DTRA_ON), (lp->cardbase + SERIAL_CFG) );
-	else
-	    	outb_p( (pt_sercfg |= PT_DTRB_ON), (lp->cardbase + SERIAL_CFG) );
-
-	/* Now, turn on the receiver and hunt for a flag */
-	wrtscc(lp->cardbase, cmd, R3, RxENABLE | RxCRC_ENAB | AUTO_ENAB | Rx8 );
-
-	restore_flags(flags);
-
-} /* scc_init() */
-
-/* Resets the given channel and whole SCC if both channels off */
-static void chipset_init(struct device *dev)
-{
-
-	struct pt_local *lp = (struct pt_local*) dev->priv;
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: chipset_init(): pt0a tstate = %d.\n", ((struct pt_local*)pt0a.priv)->tstate);
-	printk(KERN_DEBUG "PT: chipset_init(): pt0b tstate = %d.\n", ((struct pt_local*)pt0b.priv)->tstate);
-#endif
-	/* Reset SCC if both channels are to be canned */
-	if ( ((lp->base & CHANA) && !(pt_sercfg & PT_DTRB_ON)) ||
-			(!(lp->base & CHANA) && !(pt_sercfg & PT_DTRA_ON)) )
-	{
-		wrtscc(lp->cardbase, lp->base + CTL, R9, FHWRES);
-        	/* Reset int and dma registers */
-	        outb_p((pt_sercfg = 0), lp->cardbase + SERIAL_CFG);
-	        outb_p((pt_dmacfg = 0), lp->cardbase + DMA_CFG);
-#ifdef PT_DEBUG
-		printk(KERN_DEBUG "PT: chipset_init() Resetting SCC, called by ch (%d).\n", lp->base & CHANA);
-#endif
-	}
-	/* Reset individual channel */
-    	if (lp->base & CHANA) {
-        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRA);
-        	outb_p( (pt_sercfg &= ~PT_DTRA_ON), lp->cardbase + SERIAL_CFG);
-    	} else {
-        	wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | DLC | NV | CHRB);
-			outb_p( (pt_sercfg &= ~PT_DTRB_ON), lp->cardbase + SERIAL_CFG);
-	}
-} /* chipset_init() */
-
-
-
-__initfunc(int pt_init(void))
-{
-    int *port;
-    int ioaddr = 0;
-    int card_type = 0;
-    int ports[] =
-    { 0x230, 0x240, 0x250, 0x260, 0x270, 0x280, 0x290, 0x2a0,
-      0x2b0, 0x300, 0x330, 0x3f0,  0};
-
-    printk(KERN_INFO "PT: 0.41 ALPHA 07 October 1995 Craig Small (csmall@small.dropbear.id.au)\n");
-
-    for (port = &ports[0]; *port && !card_type; port++) {
-        ioaddr = *port;
-
-        if (check_region(ioaddr, PT_TOTAL_SIZE) == 0) {
-            printk(KERN_INFO "PT: Probing for card at address %#3x\n", ioaddr);
-            card_type = hw_probe(ioaddr);
-        }
-    }
-    if (card_type) {
-        printk(KERN_INFO "PT: Found a PT at address %#3x\n",ioaddr);
-    } else {
-        printk(KERN_ERR "PT: ERROR: No card found.\n");
-        return -EIO;
-    }
-
-    /*
-     * Link a couple of device structures into the chain
-     *
-     * For the A port
-     * Allocate space for 4 buffers even though we only need 3,
-     * because one of them may cross a DMA page boundary and
-     * be rejected by get_dma_buffer().
-     */
-    register_netdev(&pt0a);
-
-    pt0a.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
-
-    pt0a.dma = 0;	/* wizzer - no dma yet */
-    pt0a.base_addr = ioaddr + CHANA;
-    pt0a.irq = 0;
-
-    /* And B port */
-    register_netdev(&pt0b);
-
-    pt0b.priv= kmalloc(sizeof(struct pt_local) + (DMA_BUFF_SIZE + sizeof(struct mbuf)) * 4, GFP_KERNEL | GFP_DMA);
-
-    pt0b.base_addr = ioaddr + CHANB;
-    pt0b.irq = 0;
-
-    /* Now initialise them */
-    pt_probe(&pt0a);
-    pt_probe(&pt0b);
-
-    pt0b.irq = pt0a.irq;	/* IRQ is shared */
-
-    return 0;
-} /* pt_init() */
-
-/*
- * Probe for PT card.  Also initialises the timers
- */
-__initfunc(static int hw_probe(int ioaddr))
-{
-    int time = 1000;		/* Number of milliseconds to test */
-    int a = 1;
-    int b = 1;
-    unsigned long start_time, end_time;
-
-    inb_p(ioaddr + TMR1CLR);
-    inb_p(ioaddr + TMR2CLR);
-
-    /* Timer counter channel 0, 1mS period */
-    outb_p(SC0 | LSB_MSB | MODE3, ioaddr + TMRCMD);
-    outb_p(0x00, ioaddr + TMR0);
-    outb_p(0x18, ioaddr + TMR0);
-
-    /* Setup timer control word for timer 1 */
-    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
-    outb_p((time << 1) & 0xff, ioaddr + TMR1);
-    outb_p((time >> 7) & 0xff, ioaddr + TMR1);
-
-    /* wait until counter reg is loaded */
-    do {
-        /* Latch count for reading */
-        outb_p(SC1, ioaddr + TMRCMD);
-        a = inb_p(ioaddr + TMR1);
-        b = inb_p(ioaddr + TMR1);
-    } while (b == 0);
-    start_time = jiffies;
-    while(b != 0)
-    {
-        /* Latch count for reading */
-        outb_p(SC1, ioaddr + TMRCMD);
-        a = inb_p(ioaddr + TMR1);
-        b = inb_p(ioaddr + TMR1);
-        end_time = jiffies;
-        /* Don't wait forever - there may be no card here */
-        if ((end_time - start_time) > 200)
-        {
-        	inb_p(ioaddr + TMR1CLR);
-            return 0;
-        }
-    }
-
-    /* Now fix the timers up for general operation */
-
-    /* Clear the timers */
-    inb_p(ioaddr + TMR1CLR);
-    inb_p(ioaddr + TMR2CLR);
-
-    outb_p(SC1 | LSB_MSB | MODE0, ioaddr + TMRCMD);
-    inb_p(ioaddr + TMR1CLR);
-
-    outb_p(SC2 | LSB_MSB | MODE0, ioaddr + TMRCMD);
-    /* Should this be tmr1 or tmr2? wiz3*/
-    inb_p(ioaddr + TMR1CLR);
-
-    return 1;
-} /* hw_probe() */
-
-
-static void pt_rts(struct pt_local *lp, int x)
-{
-	int tc;
-	long br;
-	int cmd = lp->base + CTL;
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_rts(): Transmitter status will be %d (%d).\n", x, lp->base & CHANA);
-#endif
-	if (x == ON) {
-	    /* Ex ints off to avoid int */
-	    wrtscc(lp->cardbase, cmd, R15, 0);
-	    wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);	/* Rx off */
-	    lp->rstate = IDLE;
-
-	    if(lp->dmachan)
-	    {
-	        /* Setup for Tx DMA */
-	        wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
-	    } else {
-	        /* No interrupts */
-	        wrtscc(lp->cardbase, cmd, R1, 0);
-	    }
-
-            if (!lp->clockmode)
-            {
-                if (lp->speed)
-                {
-                    br = lp->speed;
-                    tc = (lp->xtal / (br * 2)) - 2;
-                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
-                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
-                }
-            }
-            /* Turn on Tx by raising RTS */
-            wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8 | DTR);
-            /* Transmitter on now */
-        } else {		/* turning off Tx */
-            lp->tstate = IDLE;
-
-            /* Turn off Tx by dropping RTS */
-            wrtscc(lp->cardbase, cmd, R5, Tx8 | DTR);
-            if (!lp->clockmode)
-            {
-                if (lp->speed)		/* internally clocked */
-                {
-                    /* Reprogram BRG from 32x clock for Rx DPLL */
-                    /* BRG off, keep PClk source */
-                    wrtscc(lp->cardbase, cmd, R14, BRSRC);
-                    br = lp->speed;
-                    tc = ((lp->xtal / 32) / (br * 2)) - 2;
-                    wrtscc(lp->cardbase, cmd, R12, tc & 0xff);
-                    wrtscc(lp->cardbase, cmd, R13, (tc >> 8) & 0xff);
-
-                    /* SEARCH mode, BRG source */
-                    wrtscc(lp->cardbase, cmd, R14, BRSRC | SEARCH);
-                    /* Enable the BRG */
-                    wrtscc(lp->cardbase, cmd, R14, BRSRC | BRENABL);
-                }
-            }
-            /* Flush Rx fifo */
-            /* Turn Rx off */
-            wrtscc(lp->cardbase, cmd, R3, AUTO_ENAB | Rx8);
-
-            /* Reset error latch */
-            wrtscc(lp->cardbase, cmd, R0, ERR_RES);
-
-            /* get status byte from R1 */
-            (void) rdscc(lp->cardbase, cmd, R1);
-
-            /* Read and dump data in queue */
-            (void) rdscc(lp->cardbase, cmd, R8);
-            (void) rdscc(lp->cardbase, cmd, R8);
-            (void) rdscc(lp->cardbase, cmd, R8);
-
-            /* Now, turn on Rx and hunt for a flag */
-              wrtscc(lp->cardbase, cmd, R3, RxENABLE | AUTO_ENAB | Rx8 );
-
-            lp->rstate = ACTIVE;
-
-            if (lp->dmachan)
-            {
-                setup_rx_dma(lp);
-            } else {
-                /* Reset buffer pointers */
-                lp->rcp = lp->rcvbuf->data;
-                lp->rcvbuf->cnt = 0;
-                /* Allow aborts to interrupt us */
-                wrtscc(lp->cardbase, cmd, R1, INT_ALL_Rx | EXT_INT_ENAB);
-
-	}
-	wrtscc(lp->cardbase, cmd, R15, BRKIE );
-    }
-} /* pt_rts() */
-
-
-static int valid_dma_page(unsigned long addr, unsigned long dev_bufsize)
-{
-    if (((addr & 0xffff) + dev_bufsize) <= 0x10000)
-        return 1;
-    else
-        return 0;
-}
-
-static int pt_set_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa = (struct sockaddr *)addr;
-	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);		/* addr is an AX.25 shifted ASCII */
-	return 0;		/* mac address */
-}
-
-
-/* Allocate a buffer which does not cross a DMA page boundary */
-static char * get_dma_buffer(unsigned long *mem_ptr)
-{
-    char *ret;
-
-    ret = (char *) *mem_ptr;
-
-    if (!valid_dma_page(*mem_ptr, DMA_BUFF_SIZE + sizeof(struct mbuf))) {
-        *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
-        ret = (char *) *mem_ptr;
-    }
-    *mem_ptr += (DMA_BUFF_SIZE + sizeof(struct mbuf));
-    return (ret);
-} /* get_dma_buffer() */
-
-
-/*
- * Sets up all the structures for the PT device
- */
-static int pt_probe(struct device *dev)
-{
-    short ioaddr;
-    struct pt_local *lp;
-    unsigned long flags;
-    unsigned long mem_ptr;
-
-    ioaddr = dev->base_addr;
-
-    /*
-     * Initialise the device structure.
-     * Must be done before chipset_init()
-     * Make sure data structures used by  the PT are aligned
-     */
-    dev->priv = (void *) (((int) dev->priv + 7) & ~7);
-    lp = (struct pt_local*) dev->priv;
-
-    memset(dev->priv, 0, sizeof(struct pt_local));
-
-    /* Allocate some buffers which do not cross DMA boundaries */
-    mem_ptr = (unsigned long) dev->priv + sizeof(struct pt_local);
-    lp->txdmabuf = get_dma_buffer(&mem_ptr);
-    lp->rxdmabuf1 = (struct mbuf *) get_dma_buffer(&mem_ptr);
-    lp->rxdmabuf2 = (struct mbuf *) get_dma_buffer(&mem_ptr);
-
-    /* Initialise the Rx buffer */
-    lp->rcvbuf = lp->rxdmabuf1;
-    lp->rcp = lp->rcvbuf->data;
-    lp->rcvbuf->cnt = 0;
-
-    /* Initialise the transmit queue head structure */
-    skb_queue_head_init(&lp->sndq);
-
-    lp->base = dev->base_addr;
-    lp->cardbase = dev->base_addr & 0x3f0;
-
-    /* These need to be initialised before scc_init() is called.
-     */
-    lp->xtal = XTAL;
-
-    if (dev->base_addr & CHANA) {
-        lp->speed = DEF_A_SPEED;
-        lp->txdelay = DEF_A_TXDELAY;
-        lp->persist = DEF_A_PERSIST;
-        lp->slotime = DEF_A_SLOTIME;
-        lp->squeldelay = DEF_A_SQUELDELAY;
-        lp->clockmode = DEF_A_CLOCKMODE;
-        lp->nrzi = DEF_A_NRZI;
-    } else {
-        lp->speed = DEF_B_SPEED;
-        lp->txdelay = DEF_B_TXDELAY;
-        lp->persist = DEF_B_PERSIST;
-        lp->slotime = DEF_B_SLOTIME;
-        lp->squeldelay = DEF_B_SQUELDELAY;
-        lp->clockmode = DEF_B_CLOCKMODE;
-        lp->nrzi = DEF_B_NRZI;
-    }
-    lp->bufsiz = DMA_BUFF_SIZE;
-    lp->tstate = IDLE;
-
-    chipset_init(dev);
-
-    if (dev->base_addr & CHANA) {
-        /* Note that a single IRQ services 2 devices (A and B channels)
-        */
-
-	/*
-	 * We disable the dma for a while, we have to get ints working
-	 * properly first!!
-	 */
-	lp->dmachan = 0;
-
-        if (dev->irq < 2) {
-            autoirq_setup(0);
-
-            /* Turn on PT interrupts */
-            save_flags(flags);
-            cli();
-            outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
-            restore_flags(flags);
-
-            /* Set a timer interrupt */
-            tdelay(lp, 1);
-            dev->irq = autoirq_report(20);
-
-	    /* Turn off PT interrupts */
-	    save_flags(flags);
-	    cli();
-            outb_p( (pt_sercfg  &= ~ PT_EI), lp->cardbase + INT_CFG);
-            restore_flags(flags);
-
-            if (!dev->irq) {
-                printk(KERN_ERR "PT: ERROR: Failed to detect IRQ line, assuming IRQ7.\n");
-            }
-        }
-
-        printk(KERN_INFO "PT: Autodetected IRQ %d, assuming DMA %d\n", dev->irq, dev->dma);
-
-        /* This board has jumpered interrupts. Snarf the interrupt vector
-         * now.  There is no point in waiting since no other device can use
-         * the interrupt, and this marks the 'irqaction' as busy.
-         */
-        {
-            int irqval = request_irq(dev->irq, &pt_interrupt,0, "pt", dev);
-            if (irqval) {
-                printk(KERN_ERR "PT: ERROR: Unable to get IRQ %d (irqval = %d).\n",
-                    dev->irq, irqval);
-                return EAGAIN;
-            }
-        }
-
-        /* Grab the region */
-        request_region(ioaddr & 0x3f0, PT_TOTAL_SIZE, "pt" );
-    } /* A port */
-    dev->open = pt_open;
-    dev->stop = pt_close;
-    dev->do_ioctl = pt_ioctl;
-    dev->hard_start_xmit = pt_send_packet;
-    dev->get_stats = pt_get_stats;
-
-    /* Fill in the fields of the device structure */
-    dev_init_buffers(dev);
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-    dev->hard_header    = ax25_encapsulate;
-    dev->rebuild_header = ax25_rebuild_header;
-#endif
-
-    dev->set_mac_address = pt_set_mac_address;
-
-    dev->type = ARPHRD_AX25;            /* AF_AX25 device */
-    dev->hard_header_len = 73;      /* We do digipeaters now */
-    dev->mtu = 1500;                /* eth_mtu is default */
-    dev->addr_len = 7;               /* sizeof an ax.25 address */
-    memcpy(dev->broadcast, ax25_bcast, 7);
-    memcpy(dev->dev_addr, ax25_test, 7);
-
-    /* New style flags */
-    dev->flags = 0;
-
-    return 0;
-} /* pt_probe() */
-
-
-/* Open/initialise the board.  This is called (in the current kernel)
- * sometime after booting when the 'ifconfig' program is run.
- *
- * This routine should set everything up anew at each open, even
- * registers that 'should' only be set once at boot, so that there is
- * a non-reboot way to recover if something goes wrong.
- * derived from last half of tsync_attach()
- */
-static int pt_open(struct device *dev)
-{
-    unsigned long flags;
-    struct pt_local *lp = dev->priv;
-    static first_time = 1;
-
-    if (dev->base_addr & CHANA)
-    {
-        if (first_time)
-        {
-            if (request_dma(dev->dma, "pt"))
-            {
-                free_irq(dev->irq, dev);
-                return -EAGAIN;
-            }
-        }
-
-         /* Reset hardware */
-         chipset_init(dev);
-     }
-     lp->tstate = IDLE;
-
-     if (dev->base_addr & CHANA)
-     {
-         scc_init(dev);
-         scc_init(dev->next);
-     }
-     /* Save a copy of register RR0 for comparing with later on */
-     /* We always put 0 in zero count */
-     lp->saved_RR0 = rdscc(lp->cardbase, lp->base + CTL, R0) & ~ZCOUNT;
-
-    /* master interrupt enable */
-    save_flags(flags);
-    cli();
-    wrtscc(lp->cardbase, lp->base + CTL, R9, MIE | NV);
-    outb_p( pt_sercfg |= PT_EI, lp->cardbase + INT_CFG);
-    restore_flags(flags);
-
-    lp->open_time = jiffies;
-
-    dev->tbusy = 0;
-    dev->interrupt = 0;
-    dev->start = 1;
-    first_time = 0;
-
-    MOD_INC_USE_COUNT;
-
-    return 0;
-} /* pt_open() */
-
-static int pt_send_packet(struct sk_buff *skb, struct device *dev)
-{
-	struct pt_local *lp = (struct pt_local *) dev->priv;
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_send_packet(): (%d)\n", lp->base & CHANA);
-#endif
-	hardware_send_packet(lp, skb);
-	dev->trans_start = jiffies;
-
-	return 0;
-}
-
-
-
-/* The inverse routine to pt_open() */
-static int pt_close(struct device *dev)
-{
-	unsigned long flags;
-	struct pt_local *lp = dev->priv;
-	struct sk_buff *ptr = NULL;
-	int cmd;
-
-	cmd = lp->base + CTL;
-
-	save_flags(flags);
-	cli();
-
-	/* Reset SCC or channel */
-	chipset_init(dev);
-	disable_dma(lp->dmachan);
-
-	lp->open_time = 0;
-	dev->tbusy = 1;
-	dev->start = 0;
-
-	/* Free any buffers left in the hardware transmit queue */
-	while ((ptr = skb_dequeue(&lp->sndq)) != NULL)
-		kfree_skb(ptr, FREE_WRITE);
-
-	restore_flags(flags);
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_close(): Closing down channel (%d).\n", lp->base & CHANA);
-#endif
-
-	MOD_DEC_USE_COUNT;
-
-	return 0;
-} /* pt_close() */
-
-
-static int pt_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-    unsigned long flags;
-    struct pt_req rq;
-    struct pt_local *lp = (struct pt_local *) dev->priv;
-
-    int ret = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct pt_req));
-    if (ret)
-	return ret;
-
-    if (cmd != SIOCDEVPRIVATE)
-        return -EINVAL;
-
-    copy_from_user(&rq, ifr->ifr_data, sizeof(struct pt_req));
-
-    switch (rq.cmd) {
-    case SIOCSPIPARAM:
-
-	if (!suser())
-	    return -EPERM;
-	save_flags(flags);
-	cli();
-	lp->txdelay = rq.txdelay;
-	lp->persist = rq.persist;
-	lp->slotime = rq.slotime;
-	lp->squeldelay = rq.squeldelay;
-	lp->clockmode = rq.clockmode;
-	lp->speed = rq.speed;
-	pt_open(&pt0a);
-	restore_flags(flags);
-	ret = 0;
-	break;
-
-    case SIOCSPIDMA:
-
-	if (!suser())
-	    return -EPERM;
-	ret = 0;
-	if (dev->base_addr & CHANA) {   /* if A channel */
-	   if (rq.dmachan < 1 || rq.dmachan > 3)
-		return -EINVAL;
-	   save_flags(flags);
-	   cli();
-	   pt_close(dev);
-	   free_dma(lp->dmachan);
-	   dev->dma = lp->dmachan = rq.dmachan;
-	   if (request_dma(lp->dmachan,"pt"))
-		ret = -EAGAIN;
-	   pt_open(dev);
-	   restore_flags(flags);
-	}
-	break;
-
-    case SIOCSPIIRQ:
-	ret = -EINVAL;      /* add this later */
-	break;
-
-    case SIOCGPIPARAM:
-    case SIOCGPIDMA:
-    case SIOCGPIIRQ:
-
-	rq.speed = lp->speed;
-	rq.txdelay = lp->txdelay;
-	rq.persist = lp->persist;
-	rq.slotime = lp->slotime;
-	rq.squeldelay = lp->squeldelay;
-	rq.clockmode = lp->clockmode;
-	rq.dmachan = lp->dmachan;
-	rq.irq = dev->irq;
-	copy_to_user(ifr->ifr_data, &rq, sizeof(struct pt_req));
-	ret = 0;
-	break;
-
-    default:
-	ret = -EINVAL;
-    }
-    return ret;
-}
-
-/*
- *	Get the current statistics.
- *	This may be called with the card open or closed. 
- */
- 
-static struct net_device_stats *pt_get_stats(struct device *dev)
-{
-	struct pt_local *lp = (struct pt_local *) dev->priv;
-	return &lp->stats;
-}
-
-
-/*
- * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
- *  version-control: t
- *  kept-new-versions: 5
- *  tab-width: 4
- * End:
- */
-
-
-static void tdelay(struct pt_local *lp, int time)
-{
-	/* For some reason, we turn off the Tx interrupts here! */
-	if (!lp->dmachan)
-		wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB);
-
-	if (lp->base & CHANA) 
-	{
-		outb_p(time & 0xff, lp->cardbase + TMR1);
-		outb_p((time >> 8)&0xff, lp->cardbase + TMR1);
-	}
-	else
-	{
-		outb_p(time & 0xff, lp->cardbase + TMR2);
-		outb_p((time >> 8)&0xff, lp->cardbase + TMR2);
-	}
-} /* tdelay */
-
-
-static void pt_txisr(struct pt_local *lp)
-{
-	unsigned long flags;
-	int cmd;
-	unsigned char c;
-
-	save_flags(flags);
-	cli();
-	cmd = lp->base + CTL;
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_txisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
-#endif
-
-	switch (lp->tstate)
-	{
-	case CRCOUT:
-	    lp->tstate = FLAGOUT;
-	    tdelay(lp, lp->squeldelay);
-	    restore_flags(flags);
-	    return;
-
-	case IDLE:
-	    /* Transmitter idle. Find a frame for transmission */
-	    if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
-	    {
-	        /* Nothing to send - return to receive mode
-	         * Tx off now - flag should have gone
-	         */
-	        pt_rts(lp, OFF);
-
-	        restore_flags(flags);
-	        return;
-	    }
-	    if (!lp->dmachan)
-	    {
-		    lp->txptr = lp->sndbuf->data;
-		    lp->txptr++;		/* Ignore KISS control byte */
-		    lp->txcnt = (int) lp->sndbuf->len - 1;
-		}
-	    /* If a buffer to send, drop though here */
-
-	case DEFER:
-	    /* Check DCD - debounce it */
-	    /* See Intel Microcommunications Handbook p2-308 */
-	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-	    if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
-	    {
-	        lp->tstate = DEFER;
-	        tdelay(lp, 100);
-	        /* DEFER until DCD transition or timeout */
-	        wrtscc(lp->cardbase, cmd, R15, DCDIE);
-	        restore_flags(flags);
-	        return;
-	    }
-	    if (random() > lp->persist)
-	    {
-	        lp->tstate = DEFER;
-	        tdelay(lp, lp->slotime);
-	        restore_flags(flags);
-	        return;
-	    }
-	    pt_rts(lp, ON);		/* Tx on */
-	    if (lp->dmachan)
-	    	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
-	    lp->tstate = ST_TXDELAY;
-	    tdelay(lp, lp->txdelay);
-	    restore_flags(flags);
-	    return;
-
-	case ACTIVE:
-	    /* Here we are actively sending a frame */
-	    if (lp->txcnt--)
-	    {
-	        /* XLZ - checkout Gracilis PT code to see if the while
-	         * loop is better or not.
-	         */
-	        c = *lp->txptr++;
-	        /* next char is gone */
-	        wrtscc(lp->cardbase, cmd, R8, c);
-	        /* stuffing a char satisfies interrupt condition */
-	    } else {
-	        /* No more to send */
-	        kfree_skb(lp->sndbuf, FREE_WRITE);
-	        lp->sndbuf = NULL;
-	        if ((rdscc(lp->cardbase, cmd, R0) & TxEOM))
-	        {
-	            /* Did we underrun */
-	            lp->stats.tx_errors++;
-	            lp->stats.tx_fifo_errors++;
-	            wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
-	            lp->tstate = FLAGOUT;
-	            tdelay(lp, lp->squeldelay);
-	            restore_flags(flags);
-	            return;
-	        }
-	        lp->tstate = UNDERRUN;
-	        /* Send flags on underrun */
-	       if (lp->nrzi)
-	       {
-	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI);
-	       } else {
-	           wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ);
-	       }
-	       /* Reset Tx interrupt pending */
-	       wrtscc(lp->cardbase, cmd, R0, RES_Tx_P);
-	   }
-	   restore_flags(flags);
-	   return;
-	default:
-		printk(KERN_ERR "PT: pt_txisr(): Invalid tstate (%d) for chan %s.\n", lp->tstate, (cmd & CHANA? "A": "B") );
-		pt_rts(lp, OFF);
-		lp->tstate = IDLE;
-		break;
-    } 				/*switch */
-    restore_flags(flags);
-}
-
-static void pt_rxisr(struct device *dev)
-{
-    struct pt_local *lp = (struct pt_local*) dev->priv;
-    int cmd = lp->base + CTL;
-    int bytecount;
-    unsigned long flags;
-    char rse;
-    struct sk_buff *skb;
-    int sksize, pkt_len;
-    struct mbuf *cur_buf = NULL;
-    unsigned char *cfix;
-
-    save_flags(flags);
-    cli();
-
-    /* Get status byte from R1 */
-    rse = rdscc(lp->cardbase, cmd, R1);
-
-#ifdef PT_DEBUG
-    printk(KERN_DEBUG "PT: pt_rxisr(): R1 = %#3x. (%d)\n", rse, lp->base & CHANA);
-#endif
-
-	if (lp->dmachan && (rse & Rx_OVR))
-		lp->rstate = RXERROR;
-
-    if (rdscc(lp->cardbase, cmd, R0) & Rx_CH_AV && !lp->dmachan)
-    {
-        /* There is a char to be stored
-         * Read special condition bits before reading the data char
-         */
-        if (rse & Rx_OVR)
-        {
-             /* Rx overrun - toss buffer */
-             /* wind back the pointers */
-             lp->rcp = lp->rcvbuf->data;
-             lp->rcvbuf->cnt = 0;
-             lp->rstate = RXERROR;
-             lp->stats.rx_errors++;
-             lp->stats.rx_fifo_errors++;
-         } else if (lp->rcvbuf->cnt >= lp->bufsiz)
-             {
-                 /* Too large packet
-                  * wind back Rx buffer pointers
-                  */
-                 lp->rcp = lp->rcvbuf->data;
-                 lp->rcvbuf->cnt = 0;
-                 lp->rstate = TOOBIG;
-             }
-         /* ok, we can store the Rx char if no errors */
-         if (lp->rstate == ACTIVE)
-         {
-             *lp->rcp++ = rdscc(lp->cardbase, cmd, R8);
-             lp->rcvbuf->cnt++;
-         } else {
-             /* we got an error, dump the FIFO */
-             (void) rdscc(lp->cardbase, cmd, R8);
-             (void) rdscc(lp->cardbase, cmd, R8);
-             (void) rdscc(lp->cardbase, cmd, R8);
-
-             /* Reset error latch */
-             wrtscc(lp->cardbase, cmd, R0, ERR_RES);
-             lp->rstate = ACTIVE;
-
-             /* Resync the SCC */
-             wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
-
-         }
-     }
-
-     if (rse & END_FR)
-     {
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_rxisr() Got end of a %u byte frame.\n", lp->rcvbuf->cnt);
-#endif
-		if (lp->dmachan)
-		{
-			clear_dma_ff(lp->dmachan);
-			bytecount = lp->bufsiz - get_dma_residue(lp->dmachan);
-		} else {
-			bytecount = lp->rcvbuf->cnt;
-		}
-
-         /* END OF FRAME - Make sure Rx was active */
-         if (lp->rcvbuf->cnt > 0 || lp->dmachan)
-         {
-             if ((rse & CRC_ERR) || (lp->rstate > ACTIVE) || (bytecount < 10))
-             {
-                 if ((bytecount >= 10) && (rse & CRC_ERR))
-                 {
-                     lp->stats.rx_crc_errors++;
-                 }
-                 if (lp->dmachan)
-                 {
-                 	if (lp->rstate == RXERROR)
-                 	{
-                 		lp->stats.rx_errors++;
-                 		lp->stats.rx_over_errors++;
-                 	}
-                 	lp->rstate = ACTIVE;
-                 	setup_rx_dma(lp);
-                 } else {
-	                 /* wind back Rx buffer pointers */
-    	             lp->rcp = lp->rcvbuf->data;
-        	         lp->rcvbuf->cnt = 0;
-
-					/* Re-sync the SCC */
-					wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
-
-        	     }
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_rxisr() %s error.\n", (rse & CRC_ERR)? "CRC" : "state");
-#endif
-             } else {
-                 /* We have a valid frame */
-                 if (lp->dmachan)
-                 {
-                 	pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1;
-					/* Get buffer for next frame */
-                 	cur_buf = lp->rcvbuf;
-                 	switchbuffers(lp);
-                 	setup_rx_dma(lp);
-                 } else {
-	                 pkt_len = lp->rcvbuf->cnt -= 2;  /* Toss 2 CRC bytes */
-    	             pkt_len += 1;	/* make room for KISS control byte */
-        		}
-
-                 /* Malloc up new buffer */
-                 sksize = pkt_len;
-                 skb = dev_alloc_skb(sksize);
-                 if (skb == NULL)
-                 {
-                     printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name);
-                     lp->stats.rx_dropped++;
-                     restore_flags(flags);
-                     return;
-                 }
-                 skb->dev = dev;
-
-                 /* KISS kludge = prefix with a 0 byte */
-                 cfix=skb_put(skb,pkt_len);
-                 *cfix++=0;
-                 /* skb->data points to the start of sk_buff area */
-                 if (lp->dmachan)
-                 	memcpy(cfix, (char*)cur_buf->data, pkt_len - 1);
-                 else
-                    memcpy(cfix, lp->rcvbuf->data, pkt_len - 1);
-                 skb->protocol = ntohs(ETH_P_AX25);
-                 skb->mac.raw=skb->data;
-                 lp->stats.rx_bytes+=skb->len;
-                 netif_rx(skb);
-                 lp->stats.rx_packets++;
-                 if (!lp->dmachan)
-                 {
-	                 /* packet queued - wind back buffer for next frame */
-    	             lp->rcp = lp->rcvbuf->data;
-	                 lp->rcvbuf->cnt = 0;
-	             }
-             } /* good frame */
-         } /* check active Rx */
-         /* Clear error status */
-         lp->rstate = ACTIVE;
-         /* Reset error latch */
-     } /* end EOF check */
-     wrtscc(lp->cardbase, cmd, R0, ERR_RES);
-     restore_flags(flags);
-} /* pt_rxisr() */
-
-/*
- * This handles the two timer interrupts.
- * This is a real bugger, cause you have to rip it out of the pi's
- * external status code.  They use the CTS line or something.
- */
-static void pt_tmrisr(struct pt_local *lp)
-{
-    unsigned long flags;
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);
-#endif
-
-    save_flags(flags);
-    cli();
-
-
-    switch (lp->tstate)
-    {
-    /* Most of this stuff is in pt_exisr() */
-    case FLAGOUT:
-    case ST_TXDELAY:
-    case DEFER:
-/*    case ACTIVE:
-    case UNDERRUN:*/
-        pt_exisr(lp);
-        break;
-
-    default:
-	if (lp->base & CHANA)
- 	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate);
-	else
-	    printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate);
-	break;
-    } /* end switch */
-    restore_flags(flags);
-} /* pt_tmrisr() */
-
-
-/*
- * This routine is called by the kernel when there is an interrupt for the
- * PT.
- */
-static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-    /* It's a tad dodgy here, but we assume pt0a until proven otherwise */
-    struct device *dev = &pt0a;
-    struct pt_local *lp = dev->priv;
-    unsigned char intreg;
-    unsigned char st;
-    register int cbase = dev->base_addr & 0x3f0;
-    unsigned long flags;
-
-    /* Read the PT's interrupt register, this is not the SCC one! */
-    intreg = inb_p(cbase + INT_REG);
-    while(( intreg & 0x07) != 0x07) {
-        /* Read interrupt register pending from Channel A */
-        while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0)
-        {
-        	/* Read interrupt vector from R2, channel B */
-#ifdef PT_DEBUG
-		printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);
-#endif
-/*        	st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/
-#ifdef PT_DEBUG
-		printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);
-#endif
-			if (st & CHARxIP) {
-			    /* Channel A Rx */
-	            lp = (struct pt_local*)pt0a.priv;
-	            pt_rxisr(&pt0a);
-	        } else if (st & CHATxIP) {
-	            /* Channel A Tx */
-	            lp = (struct pt_local*)pt0a.priv;
-	            pt_txisr(lp);
-	        } else if (st & CHAEXT) {
-	        	/* Channel A External Status */
-	        	lp = (struct pt_local*)pt0a.priv;
-	        	pt_exisr(lp);
-	        } else if (st & CHBRxIP) {
-	        	/* Channel B Rx */
-	            lp= (struct pt_local*)pt0b.priv;
-	            pt_rxisr(&pt0b);
-	        } else if (st & CHBTxIP) {
-            	/* Channel B Tx */
-	            lp = (struct pt_local*)pt0b.priv;
-	            pt_txisr(lp);
-			} else if (st & CHBEXT) {
-	        	/* Channel B External Status */
-	        	lp = (struct pt_local*)pt0b.priv;
-	        	pt_exisr(lp);
-	        }
-            /* Reset highest interrupt under service */
-            save_flags(flags);
-            cli();
-            wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS);
-            restore_flags(flags);
-        }  /* end of SCC ints */
-
-        if (!(intreg & PT_TMR1_MSK))
-        {
-            /* Clear timer 1 */
-            inb_p(cbase + TMR1CLR);
-
-            pt_tmrisr( (struct pt_local*)pt0a.priv);
-        }
-
-        if (!(intreg & PT_TMR2_MSK))
-        {
-            /* Clear timer 2 */
-            inb_p(cbase + TMR2CLR);
-
-            pt_tmrisr( (struct pt_local*)pt0b.priv);
-        }
-
-        /* Get the next PT interrupt vector */
-        intreg = inb_p(cbase + INT_REG);
-    } /* while (intreg) */
-} /* pt_interrupt() */
-
-
-static void pt_exisr(struct pt_local *lp)
-{
-    unsigned long flags;
-    int cmd = lp->base + CTL;
-    unsigned char st;
-    char c;
-    int length;
-
-    save_flags(flags);
-    cli();
-
-    /* Get external status */
-    st = rdscc(lp->cardbase, cmd, R0);
-
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);
-#endif
-    /* Reset external status latch */
-    wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-
-    if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan)
-    {
-    	setup_rx_dma(lp);
-    	lp->rstate = ACTIVE;
-    }
-
-    switch (lp->tstate)
-    {
-    case ACTIVE:		/* Unexpected underrun */
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");
-#endif
-        kfree_skb(lp->sndbuf, FREE_WRITE);
-        lp->sndbuf = NULL;
-        if (!lp->dmachan)
-        {
-	        wrtscc(lp->cardbase, cmd, R0, SEND_ABORT);
-	        lp->stats.tx_errors++;
-	        lp->stats.tx_fifo_errors++;
-	    }
-        lp->tstate = FLAGOUT;
-        tdelay(lp, lp->squeldelay);
-        restore_flags(flags);
-        return;
-    case UNDERRUN:
-        lp->tstate = CRCOUT;
-        restore_flags(flags);
-        return;
-    case FLAGOUT:
-        /* squeldelay has timed out */
-        /* Find a frame for transmission */
-        if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL)
-        {
-            /* Nothing to send - return to Rx mode */
-            pt_rts(lp, OFF);
-            lp->tstate = IDLE;
-            restore_flags(flags);
-            return;
-        }
-        if (!lp->dmachan)
-        {
-	        lp->txptr = lp->sndbuf->data;
-    	    lp->txptr++;		/* Ignore KISS control byte */
-	        lp->txcnt = (int) lp->sndbuf->len - 1;
-	    }
-        /* Fall through if we have a packet */
-
-    case ST_TXDELAY:
-    	if (lp->dmachan)
-    	{
-    		/* Disable DMA chan */
-    		disable_dma(lp->dmachan);
-
-    		/* Set up for TX dma */
-    		wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB);
-
-    		length = lp->sndbuf->len - 1;
-    		memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length);
-
-    		/* Setup DMA controller for Tx */
-    		setup_tx_dma(lp, length);
-
-    		enable_dma(lp->dmachan);
-
-    		/* Reset CRC, Txint pending */
-    		wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P);
-
-    		/* Allow underrun only */
-    		wrtscc(lp->cardbase, cmd, R15, TxUIE);
-
-    		/* Enable TX DMA */
-    		wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB);
-
-    		/* Send CRC on underrun */
-    		wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
-
-    		lp->tstate = ACTIVE;
-    		break;
-    	}
-        /* Get first char to send */
-        lp->txcnt--;
-        c = *lp->txptr++;
-        /* Reset CRC for next frame */
-        wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC);
-
-        /* send abort on underrun */
-        if (lp->nrzi)
-        {
-            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER);
-        } else {
-            wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER);
-        }
-        /* send first char */
-        wrtscc(lp->cardbase, cmd, R8, c);
-
-        /* Reset end of message latch */
-        wrtscc(lp->cardbase, cmd, R0, RES_EOM_L);
-
-        /* stuff an extra one in */
-/*        while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt)
-        {
-            lp->txcnt--;
-            c = *lp->txptr++;
-            wrtscc(lp->cardbase, cmd, R8, c);
-        }*/
-
-        /* select Tx interrupts to enable */
-        /* Allow underrun int only */
-        wrtscc(lp->cardbase, cmd, R15, TxUIE);
-
-        /* Reset external interrupts */
-        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-
-        /* Tx and Rx ints enabled */
-        wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB);
-
-        lp->tstate = ACTIVE;
-        restore_flags(flags);
-        return;
-
-        /* slotime has timed out */
-    case DEFER:
-        /* Check DCD - debounce it
-         * see Intel Microcommunications Handbook, p2-308
-         */
-        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-        wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT);
-        if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0)
-        {
-            lp->tstate = DEFER;
-            tdelay(lp, 100);
-            /* DEFER until DCD transition or timeout */
-            wrtscc(lp->cardbase, cmd, R15, DCDIE);
-            restore_flags(flags);
-            return;
-        }
-        if (random() > lp->persist)
-        {
-            lp->tstate = DEFER;
-            tdelay(lp, lp->slotime);
-            restore_flags(flags);
-            return;
-        }
-        if (lp->dmachan)
-        	wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8);
-        pt_rts(lp, ON);			/* Tx on */
-        lp->tstate = ST_TXDELAY;
-        tdelay(lp, lp->txdelay);
-        restore_flags(flags);
-        return;
-
- 	/* Only for int driven parts */
- 	if (lp->dmachan)
- 	{
- 		restore_flags(flags);
- 		return;
- 	}
-
-    } /* end switch */
-    /*
-     * Rx mode only
-     * This triggers when hunt mode is entered, & since an ABORT
-     * automatically enters hunt mode, we use that to clean up
-     * any waiting garbage
-     */
-    if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) )
-    {
-#ifdef PT_DEBUG
-	printk(KERN_DEBUG "PT: exisr(): abort detected.\n");
-#endif
-  		/* read and dump all of SCC Rx FIFO */
-        (void) rdscc(lp->cardbase, cmd, R8);
-        (void) rdscc(lp->cardbase, cmd, R8);
-        (void) rdscc(lp->cardbase, cmd, R8);
-
-        lp->rcp = lp->rcvbuf->data;
-        lp->rcvbuf->cnt = 0;
-
-		/* Re-sync the SCC */
-		wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
-
-    }
-
-    /* Check for DCD transitions */
-    if ( (st & DCD) != (lp->saved_RR0 & DCD))
-    {
-#ifdef PT_DEBUG
-        printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );
-#endif
-		if (st & DCD)
-		{
-			/* Check that we don't already have some data */
-			if (lp->rcvbuf->cnt > 0)
-			{
-#ifdef PT_DEBUG
-				printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);
-#endif
-				/* wind back buffers */
-				lp->rcp = lp->rcvbuf->data;
-				lp->rcvbuf->cnt = 0;
-			}
-		} else {  /* DCD off */
-
-			/* read and dump al SCC FIFO */
-			(void)rdscc(lp->cardbase, cmd, R8);
-			(void)rdscc(lp->cardbase, cmd, R8);
-			(void)rdscc(lp->cardbase, cmd, R8);
-
-			/* wind back buffers */
-			lp->rcp = lp->rcvbuf->data;
-			lp->rcvbuf->cnt = 0;
-
-			/* Re-sync the SCC */
-			wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8);
-		}
-
-    }
-    /* Update the saved version of register RR) */
-    lp->saved_RR0 = st &~ ZCOUNT;
-    restore_flags(flags);
-
-} /* pt_exisr() */
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-MODULE_AUTHOR("Craig Small VK2XLZ <vk2xlz@vk2xlz.ampr.org>");
-MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card");
-
-int init_module(void)
-{
-	return pt_init();
-}
-
-void cleanup_module(void)
-{
-	free_irq(pt0a.irq, &pt0a);	/* IRQs and IO Ports are shared */
-	release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE);
-
-	kfree(pt0a.priv);
-	pt0a.priv = NULL;
-	unregister_netdev(&pt0a);
-
-	kfree(pt0b.priv);
-	pt0b.priv = NULL;
-	unregister_netdev(&pt0b);
-}
-#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/scc.c linux/drivers/net/scc.c
--- v2.1.70/linux/drivers/net/scc.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/net/scc.c	Wed Dec 31 16:00:00 1969
@@ -1,2257 +0,0 @@
-#define RCS_ID "$Id: scc.c,v 1.71 1997/11/29 19:59:20 jreuter Exp jreuter $"
-
-#define VERSION "3.0"
-#define BANNER  "Z8530 SCC driver version "VERSION".dl1bke (experimental) by DL1BKE\n"
-
-/*
- * Please use z8530drv-utils-3.0 with this version.
- *            ------------------
- */
-
-/*
-   ********************************************************************
-   *   SCC.C - Linux driver for Z8530 based HDLC cards for AX.25      *
-   ********************************************************************
-
-
-   ********************************************************************
-
-	Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
-
-	portions (c) 1993 Guido ten Dolle PE1NNZ
-
-   ********************************************************************
-   
-   The driver and the programs in the archive are UNDER CONSTRUCTION.
-   The code is likely to fail, and so your kernel could --- even 
-   a whole network. 
-
-   This driver is intended for Amateur Radio use. If you are running it
-   for commercial purposes, please drop me a note. I am nosy...
-
-   ...BUT:
- 
-   ! You  m u s t  recognize the appropriate legislations of your country !
-   ! before you connect a radio to the SCC board and start to transmit or !
-   ! receive. The GPL allows you to use the  d r i v e r,  NOT the RADIO! !
-
-   For non-Amateur-Radio use please note that you might need a special
-   allowance/licence from the designer of the SCC Board and/or the
-   MODEM. 
-
-   This program is free software; you can redistribute it and/or modify 
-   it under the terms of the (modified) GNU General Public License 
-   delivered with the Linux kernel source.
-   
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should find a copy of the GNU General Public License in 
-   /usr/src/linux/COPYING; 
-   
-   ******************************************************************** 
-
-		
-   Incomplete history of z8530drv:
-   -------------------------------
-
-   940913	- started to write the driver, rescued most of my own
-		  code (and Hans Alblas' memory buffer pool concept) from 
-		  an earlier project "sccdrv" which was initiated by 
-		  Guido ten Dolle. Not much of the old driver survived, 
-		  though. The first version I put my hands on was sccdrv1.3
-		  from August 1993. The memory buffer pool concept
-		  appeared in an unauthorized sccdrv version (1.5) from
-		  August 1994.
-
-   950131	- changed copyright notice to GPL without limitations.
-   
-     .
-     .	<SNIP>
-     .
-   		  
-   961005	- New semester, new driver... 
-
-   		  * KISS TNC emulator removed (TTY driver)
-   		  * Source moved to drivers/net/
-   		  * Includes Z8530 defines from drivers/net/z8530.h
-   		  * Uses sk_buffer memory management
-   		  * Reduced overhead of /proc/net/z8530drv output
-   		  * Streamlined quite a lot things
-   		  * Invents brand new bugs... ;-)
-
-   		  The move to version number 3.0 reflects theses changes.
-   		  You can use 'kissbridge' if you need a KISS TNC emulator.
-
-   961213	- Fixed for Linux networking changes. (G4KLX)
-   970108	- Fixed the remaining problems.
-   970402	- Hopefully fixed the problems with the new *_timer()
-   		  routines, added calibration code.
-   971012	- made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
-
-   Thanks to all who contributed to this driver with ideas and bug
-   reports!
-   
-   NB -- if you find errors, change something, please let me know
-      	 first before you distribute it... And please don't touch
-   	 the version number. Just replace my callsign in
-   	 "v3.0.dl1bke" with your own. Just to avoid confusion...
-
-   If you want to add your modification to the linux distribution
-   please (!) contact me first.
-   
-   New versions of the driver will be announced on the linux-hams
-   mailing list on vger.rutgers.edu. To subscribe send an e-mail
-   to majordomo@vger.rutgers.edu with the following line in
-   the body of the mail:
-   
-	   subscribe linux-hams
-	   
-   The content of the "Subject" field will be ignored.
-
-   vy 73,
-   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@poboxes.com
-		www     : http://www.rat.de/jr
-*/
-
-/* ----------------------------------------------------------------------- */
-
-#undef  SCC_LDELAY	1	/* slow it even a bit more down */
-#undef  DONT_CHECK		/* don't look if the SCCs you specified are available */
-
-#define MAXSCC          4       /* number of max. supported chips */
-#define BUFSIZE         384     /* must not exceed 4096 */
-#define MAXQUEUE	8	/* number of buffers we queue ourself */
-#undef  DISABLE_ALL_INTS	/* use cli()/sti() in ISR instead of */
-				/* enable_irq()/disable_irq()        */
-#undef	SCC_DEBUG
-
-#define DEFAULT_CLOCK	4915200 /* default pclock if nothing is specified */
-
-/* ----------------------------------------------------------------------- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/in.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/malloc.h>
-#include <linux/delay.h>
-
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/if_ether.h>
-#include <linux/if_arp.h>
-#include <linux/socket.h>
-#include <linux/init.h>
-
-#include <linux/scc.h>
-#include "z8530.h"
-
-#include <net/ax25.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/bitops.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <time.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
-
-#ifdef MODULE
-int init_module(void);
-void cleanup_module(void);
-#endif
-
-int scc_init(void);
-
-static void t_dwait(unsigned long);
-static void t_txdelay(unsigned long);
-static void t_tail(unsigned long);
-static void t_busy(unsigned long);
-static void t_maxkeyup(unsigned long);
-static void t_idle(unsigned long);
-static void scc_tx_done(struct scc_channel *);
-static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
-static void scc_start_maxkeyup(struct scc_channel *);
-static void scc_start_defer(struct scc_channel *);
-
-static void z8530_init(void);
-
-static void init_channel(struct scc_channel *scc);
-static void scc_key_trx (struct scc_channel *scc, char tx);
-static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
-static void scc_init_timer(struct scc_channel *scc);
-
-static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
-static int scc_net_init(struct device *dev);
-static int scc_net_open(struct device *dev);
-static int scc_net_close(struct device *dev);
-static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
-static int scc_net_tx(struct sk_buff *skb, struct device *dev);
-static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
-static int scc_net_set_mac_address(struct device *dev, void *addr);
-static int scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);
-static struct net_device_stats * scc_net_get_stats(struct device *dev);
-
-static unsigned char *SCC_DriverName = "scc";
-
-static struct irqflags { unsigned char used : 1; } Ivec[16];
-	
-static struct scc_channel SCC_Info[2 * MAXSCC];	/* information per channel */
-
-static struct scc_ctrl {
-	io_port chan_A;
-	io_port chan_B;
-	int irq;
-} SCC_ctrl[MAXSCC+1];
-
-static unsigned char Driver_Initialized = 0;
-static int Nchips = 0;
-static io_port Vector_Latch = 0;
-
-MODULE_AUTHOR("Joerg Reuter <jreuter@poboxes.com>");
-MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
-MODULE_SUPPORTED_DEVICE("scc");
-
-/* ******************************************************************** */
-/* *			Port Access Functions			      * */
-/* ******************************************************************** */
-
-/* These provide interrupt save 2-step access to the Z8530 registers */
-
-extern __inline__ unsigned char InReg(io_port port, unsigned char reg)
-{
-	unsigned long flags;
-	unsigned char r;
-	
-	save_flags(flags);
-	cli();
-#ifdef SCC_LDELAY
-	Outb(port, reg);
-	udelay(SCC_LDELAY);
-	r=Inb(port);
-	udelay(SCC_LDELAY);
-#else
-	Outb(port, reg);
-	r=Inb(port);
-#endif
-	restore_flags(flags);
-	return r;
-}
-
-extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-#ifdef SCC_LDELAY
-	Outb(port, reg); udelay(SCC_LDELAY);
-	Outb(port, val); udelay(SCC_LDELAY);
-#else
-	Outb(port, reg);
-	Outb(port, val);
-#endif
-	restore_flags(flags);
-}
-
-extern __inline__ void wr(struct scc_channel *scc, unsigned char reg,
-	unsigned char val)
-{
-	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
-}
-
-extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
-{
-	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
-}
-
-extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
-{
-	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
-}
-
-#ifdef DISABLE_ALL_INTS
-extern __inline__ void scc_cli(int irq)
-{ cli(); }
-extern __inline__ void scc_sti(int irq)
-{ sti(); }
-#else
-static __inline__ void scc_cli(int irq)
-{ disable_irq(irq); }
-static __inline__ void scc_sti(int irq)
-{ enable_irq(irq); }
-#endif
-
-/* ******************************************************************** */
-/* *			Some useful macros			      * */
-/* ******************************************************************** */
-
-
-extern __inline__ void scc_lock_dev(struct scc_channel *scc)
-{
-	scc->dev->tbusy = 1;
-}
-
-extern __inline__ void scc_unlock_dev(struct scc_channel *scc)
-{
-	scc->dev->tbusy = 0;
-}
-
-extern __inline__ void scc_discard_buffers(struct scc_channel *scc)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-	
-	if (scc->tx_buff != NULL)
-	{
-		dev_kfree_skb(scc->tx_buff, FREE_WRITE);
-		scc->tx_buff = NULL;
-	}
-	
-	while (skb_queue_len(&scc->tx_queue))
-		dev_kfree_skb(skb_dequeue(&scc->tx_queue), FREE_WRITE);
-
-	restore_flags(flags);
-}
-
-
-
-/* ******************************************************************** */
-/* *			Interrupt Service Routines		      * */
-/* ******************************************************************** */
-
-
-/* ----> subroutines for the interrupt handlers <---- */
-
-extern __inline__ void scc_notify(struct scc_channel *scc, int event)
-{
-	struct sk_buff *skb;
-	char *bp;
-	
-        if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
-		return;
-
-	skb = dev_alloc_skb(2);
-	if (skb != NULL)
-	{
-		bp = skb_put(skb, 2);
-		*bp++ = PARAM_HWEVENT;
-		*bp++ = event;
-		scc_net_rx(scc, skb);
-	} else
-		scc->stat.nospace++;
-}
-
-extern __inline__ void flush_rx_FIFO(struct scc_channel *scc)
-{
-	int k;
-	
-	for (k=0; k<3; k++)
-		Inb(scc->data);
-		
-	if(scc->rx_buff != NULL)		/* did we receive something? */
-	{
-		scc->stat.rxerrs++;  /* then count it as an error */
-		kfree_skb(scc->rx_buff, FREE_READ);
-		scc->rx_buff = NULL;
-	}
-}
-
-
-/* ----> four different interrupt handlers for Tx, Rx, changing of	*/
-/*       DCD/CTS and Rx/Tx errors					*/
-
-/* Transmitter interrupt handler */
-extern __inline__ void scc_txint(struct scc_channel *scc)
-{
-	struct sk_buff *skb;
-
-	scc->stat.txints++;
-	skb = scc->tx_buff;
-	
-	/* send first octet */
-	
-	if (skb == NULL)
-	{
-		skb = skb_dequeue(&scc->tx_queue);
-		scc->tx_buff = skb;
-		scc_unlock_dev(scc);
-
-		if (skb == NULL)
-		{
-			scc_tx_done(scc);
-			Outb(scc->ctrl, RES_Tx_P);
-			return;
-		}
-		
-		if (skb->len == 0)		/* Paranoia... */
-		{
-			dev_kfree_skb(skb, FREE_WRITE);
-			scc->tx_buff = NULL;
-			scc_tx_done(scc);
-			Outb(scc->ctrl, RES_Tx_P);
-			return;
-		}
-
-		scc->stat.tx_state = TXS_ACTIVE;
-
-		OutReg(scc->ctrl, R0, RES_Tx_CRC);
-						/* reset CRC generator */
-		or(scc,R10,ABUNDER);		/* re-install underrun protection */
-		Outb(scc->data,*skb->data);	/* send byte */
-		skb_pull(skb, 1);
-
-		if (!scc->enhanced)		/* reset EOM latch */
-			Outb(scc->ctrl,RES_EOM_L);
-		return;
-	}
-	
-	/* End Of Frame... */
-	
-	if (skb->len == 0)
-	{
-		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
-		cl(scc, R10, ABUNDER);		/* send CRC */
-		dev_kfree_skb(skb, FREE_WRITE);
-		scc->tx_buff = NULL;
-		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
-		return;
-	} 
-	
-	/* send octet */
-	
-	Outb(scc->data,*skb->data);		
-	skb_pull(skb, 1);
-}
-
-
-/* External/Status interrupt handler */
-extern __inline__ void scc_exint(struct scc_channel *scc)
-{
-	unsigned char status,changes,chg_and_stat;
-
-	scc->stat.exints++;
-
-	status = InReg(scc->ctrl,R0);
-	changes = status ^ scc->status;
-	chg_and_stat = changes & status;
-	
-	/* ABORT: generated whenever DCD drops while receiving */
-
-	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
-		flush_rx_FIFO(scc);
-
-
-	/* DCD: on = start to receive packet, off = ABORT condition */
-	/* (a successfully received packet generates a special condition int) */
-	
-	if(changes & DCD)                       /* DCD input changed state */
-	{
-		if(status & DCD)                /* DCD is now ON */
-		{
-			if (scc->modem.clocksrc != CLK_EXTERNAL)
-				OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
-				
-			or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
-		} else {                        /* DCD is now OFF */
-			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
-			flush_rx_FIFO(scc);
-		}
-		
-		if (!scc->kiss.softdcd)
-			scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
-	}
-	
-	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
-	
-	if (changes & SYNC_HUNT)
-	{
-		if (scc->kiss.softdcd)
-			scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
-		else
-			cl(scc,R15,SYNCIE);	/* oops, we were too lazy to disable this? */
-	}
-
-#ifdef notdef
-	/* CTS: use external TxDelay (what's that good for?!)
-	 * Anyway: If we _could_ use it (BayCom USCC uses CTS for
-	 * own purposes) we _should_ use the "autoenable" feature
-	 * of the Z8530 and not this interrupt...
-	 */
-	 
-	if (chg_and_stat & CTS)			/* CTS is now ON */
-	{
-		if (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
-			scc_start_tx_timer(scc, t_txdelay, 0);
-	}
-#endif
-	
-	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
-	{
-		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
-		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
-
-		if (scc->tx_buff != NULL)
-		{
-			dev_kfree_skb(scc->tx_buff, FREE_WRITE);
-			scc->tx_buff = NULL;
-		}
-		
-		or(scc,R10,ABUNDER);
-		scc_start_tx_timer(scc, t_txdelay, 0);	/* restart transmission */
-	}
-		
-	scc->status = status;
-	Outb(scc->ctrl,RES_EXT_INT);
-}
-
-
-/* Receiver interrupt handler */
-extern __inline__ void scc_rxint(struct scc_channel *scc)
-{
-	struct sk_buff *skb;
-
-	scc->stat.rxints++;
-
-	if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
-	{
-		Inb(scc->data);		/* discard char */
-		or(scc,R3,ENT_HM);	/* enter hunt mode for next flag */
-		return;
-	}
-
-	skb = scc->rx_buff;
-	
-	if (skb == NULL)
-	{
-		skb = dev_alloc_skb(scc->stat.bufsize);
-		if (skb == NULL)
-		{
-			scc->dev_stat.rx_dropped++;
-			scc->stat.nospace++;
-			Inb(scc->data);
-			or(scc, R3, ENT_HM);
-			return;
-		}
-		
-		scc->rx_buff = skb;
-		*(skb_put(skb, 1)) = 0;	/* KISS data */
-	}
-	
-	if (skb->len >= scc->stat.bufsize)
-	{
-#ifdef notdef
-		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
-#endif
-		kfree_skb(skb, FREE_READ);
-		scc->rx_buff = NULL;
-		Inb(scc->data);
-		or(scc, R3, ENT_HM);
-		return;
-	}
-
-	*(skb_put(skb, 1)) = Inb(scc->data);
-}
-
-
-/* Receive Special Condition interrupt handler */
-extern __inline__ void scc_spint(struct scc_channel *scc)
-{
-	unsigned char status;
-	struct sk_buff *skb;
-
-	scc->stat.spints++;
-
-	status = InReg(scc->ctrl,R1);		/* read receiver status */
-	
-	Inb(scc->data);				/* throw away Rx byte */
-	skb = scc->rx_buff;
-
-	if(status & Rx_OVR)			/* receiver overrun */
-	{
-		scc->stat.rx_over++;             /* count them */
-		or(scc,R3,ENT_HM);               /* enter hunt mode for next flag */
-		
-		if (skb != NULL) 
-			kfree_skb(skb, FREE_READ);
-		scc->rx_buff = NULL;
-	}
-
-	if(status & END_FR && skb != NULL)	/* end of frame */
-	{
-		/* CRC okay, frame ends on 8 bit boundary and received something ? */
-		
-		if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
-		{
-			/* ignore last received byte (first of the CRC bytes) */
-			skb_trim(skb, skb->len-1);
-			scc_net_rx(scc, skb);
-			scc->rx_buff = NULL;
-			scc->stat.rxframes++;
-		} else {				/* a bad frame */
-			kfree_skb(skb, FREE_READ);
-			scc->rx_buff = NULL;
-			scc->stat.rxerrs++;
-		}
-	} 
-
-	Outb(scc->ctrl,ERR_RES);
-}
-
-
-/* ----> interrupt service routine for the Z8530 <---- */
-
-static void scc_isr_dispatch(struct scc_channel *scc, int vector)
-{
-	switch (vector & VECTOR_MASK)
-	{
-		case TXINT: scc_txint(scc); break;
-		case EXINT: scc_exint(scc); break;
-		case RXINT: scc_rxint(scc); break;
-		case SPINT: scc_spint(scc); break;
-	}
-}
-
-/* If the card has a latch for the interrupt vector (like the PA0HZP card)
-   use it to get the number of the chip that generated the int.
-   If not: poll all defined chips.
- */
-
-#define SCC_IRQTIMEOUT 30000
-
-static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
-{
-	unsigned char vector;	
-	struct scc_channel *scc;
-	struct scc_ctrl *ctrl;
-	int k;
-	
-	scc_cli(irq);
-
-	if (Vector_Latch)
-	{
-	    	for(k=0; k < SCC_IRQTIMEOUT; k++)
-    		{
-			Outb(Vector_Latch, 0);      /* Generate INTACK */
-        
-			/* Read the vector */
-			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
-			if (vector & 0x01) break;
-        	 
-		        scc=&SCC_Info[vector >> 3 ^ 0x01];
-			if (!scc->dev) break;
-
-			scc_isr_dispatch(scc, vector);
-
-			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
-		}  
-		scc_sti(irq);
-
-		if (k == SCC_IRQTIMEOUT)
-			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
-
-		return;
-	}
-
-	/* Find the SCC generating the interrupt by polling all attached SCCs
-	 * reading RR3A (the interrupt pending register)
-	 */
-
-	ctrl = SCC_ctrl;
-	while (ctrl->chan_A)
-	{
-		if (ctrl->irq != irq)
-		{
-			ctrl++;
-			continue;
-		}
-
-		scc = NULL;
-		for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
-		{
-			vector=InReg(ctrl->chan_B,R2);	/* Read the vector */
-			if (vector & 0x01) break; 
-
-			scc = &SCC_Info[vector >> 3 ^ 0x01];
-		        if (!scc->dev) break;
-
-			scc_isr_dispatch(scc, vector);
-		}
-
-		if (k == SCC_IRQTIMEOUT)
-		{
-			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
-			break;
-		}
-
-		/* This looks wierd and it is. At least the BayCom USCC doesn't
-		 * use the Interrupt Daisy Chain, thus we'll have to start
-		 * all over again to be sure not to miss an interrupt from 
-		 * (any of) the other chip(s)...
-		 * Honestly, the situation *is* braindamaged...
-		 */
-
-		if (scc != NULL)
-		{
-			OutReg(scc->ctrl,R0,RES_H_IUS);
-			ctrl = SCC_ctrl; 
-		} else
-			ctrl++;
-	}
-	
-	scc_sti(irq);
-}
-
-
-
-/* ******************************************************************** */
-/* *			Init Channel					*/
-/* ******************************************************************** */
-
-
-/* ----> set SCC channel speed <---- */
-
-extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc)
-{
-	cl(scc,R14,BRENABL);		/* disable baudrate generator */
-	wr(scc,R12,tc & 255);		/* brg rate LOW */
-	wr(scc,R13,tc >> 8);   		/* brg rate HIGH */
-	or(scc,R14,BRENABL);		/* enable baudrate generator */
-}
-
-extern __inline__ void set_speed(struct scc_channel *scc)
-{
-	disable_irq(scc->irq);
-
-	if (scc->modem.speed > 0)	/* paranoia... */
-		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
-
-	enable_irq(scc->irq);
-}
-
-
-/* ----> initialize a SCC channel <---- */
-
-extern __inline__ void init_brg(struct scc_channel *scc)
-{
-	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
-	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
-	OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]);	/* DPLL NRZI mode */
-}
-
-/*
- * Initialization according to the Z8530 manual (SGS-Thomson's version):
- *
- * 1. Modes and constants
- *
- * WR9	11000000	chip reset
- * WR4	XXXXXXXX	Tx/Rx control, async or sync mode
- * WR1	0XX00X00	select W/REQ (optional)
- * WR2	XXXXXXXX	program interrupt vector
- * WR3	XXXXXXX0	select Rx control
- * WR5	XXXX0XXX	select Tx control
- * WR6	XXXXXXXX	sync character
- * WR7	XXXXXXXX	sync character
- * WR9	000X0XXX	select interrupt control
- * WR10	XXXXXXXX	miscellaneous control (optional)
- * WR11	XXXXXXXX	clock control
- * WR12	XXXXXXXX	time constant lower byte (optional)
- * WR13	XXXXXXXX	time constant upper byte (optional)
- * WR14	XXXXXXX0	miscellaneous control
- * WR14	XXXSSSSS	commands (optional)
- *
- * 2. Enables
- *
- * WR14	000SSSS1	baud rate enable
- * WR3	SSSSSSS1	Rx enable
- * WR5	SSSS1SSS	Tx enable
- * WR0	10000000	reset Tx CRG (optional)
- * WR1	XSS00S00	DMA enable (optional)
- *
- * 3. Interrupt status
- *
- * WR15	XXXXXXXX	enable external/status
- * WR0	00010000	reset external status
- * WR0	00010000	reset external status twice
- * WR1	SSSXXSXX	enable Rx, Tx and Ext/status
- * WR9	000SXSSS	enable master interrupt enable
- *
- * 1 = set to one, 0 = reset to zero
- * X = user defined, S = same as previous init
- *
- *
- * Note that the implementation differs in some points from above scheme.
- *
- */
- 
-static void init_channel(struct scc_channel *scc)
-{
-	del_timer(&scc->tx_t);
-	del_timer(&scc->tx_wdog);
-
-	disable_irq(scc->irq);
-
-	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
-	wr(scc,R1,0);			/* no W/REQ operation */
-	wr(scc,R3,Rx8|RxCRC_ENAB);	/* RX 8 bits/char, CRC, disabled */	
-	wr(scc,R5,Tx8|DTR|TxCRC_ENAB);	/* TX 8 bits/char, disabled, DTR */
-	wr(scc,R6,0);			/* SDLC address zero (not used) */
-	wr(scc,R7,FLAG);		/* SDLC flag value */
-	wr(scc,R9,VIS);			/* vector includes status */
-	wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
-	wr(scc,R14, 0);
-
-
-/* set clock sources:
-
-   CLK_DPLL: normal halfduplex operation
-   
-		RxClk: use DPLL
-		TxClk: use DPLL
-		TRxC mode DPLL output
-		
-   CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
-   
-  	        BayCom: 		others:
-  	        
-  	        TxClk = pin RTxC	TxClk = pin TRxC
-  	        RxClk = pin TRxC 	RxClk = pin RTxC
-  	     
-
-   CLK_DIVIDER:
-   		RxClk = use DPLL
-   		TxClk = pin RTxC
-   		
-   		BayCom:			others:
-   		pin TRxC = DPLL		pin TRxC = BRG
-   		(RxClk * 1)		(RxClk * 32)
-*/  
-
-   		
-	switch(scc->modem.clocksrc)
-	{
-		case CLK_DPLL:
-			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
-			init_brg(scc);
-			break;
-
-		case CLK_DIVIDER:
-			wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
-			init_brg(scc);
-			break;
-
-		case CLK_EXTERNAL:
-			wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
-			OutReg(scc->ctrl, R14, DISDPLL);
-			break;
-
-	}
-	
-	set_speed(scc);			/* set baudrate */
-	
-	if(scc->enhanced)
-	{
-		or(scc,R15,SHDLCE|FIFOE);	/* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
-		wr(scc,R7,AUTOEOM);
-	}
-
-	if((InReg(scc->ctrl,R0)) & DCD)		/* DCD is now ON */
-	{
-		if (scc->modem.clocksrc != CLK_EXTERNAL)
-			or(scc,R14, SEARCH);
-			
-		or(scc,R3,ENT_HM|RxENABLE);	/* enable the receiver, hunt mode */
-	}
-	
-	/* enable ABORT, DCD & SYNC/HUNT interrupts */
-
-	wr(scc,R15, BRKIE|TxUIE|DCDIE);
-	if (scc->kiss.softdcd)
-		or(scc,R15, SYNCIE);
-
-	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
-	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
-
-	or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
-	
-	scc->status = InReg(scc->ctrl,R0);	/* read initial status */
-	
-	or(scc,R9,MIE);			/* master interrupt enable */
-	
-	scc_init_timer(scc);
-			
-	enable_irq(scc->irq);
-}
-
-
-
-
-/* ******************************************************************** */
-/* *			SCC timer functions			      * */
-/* ******************************************************************** */
-
-
-/* ----> scc_key_trx sets the time constant for the baudrate 
-         generator and keys the transmitter		     <---- */
-
-static void scc_key_trx(struct scc_channel *scc, char tx)
-{
-	unsigned int time_const;
-		
-	if (scc->brand & PRIMUS)
-		Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
-
-	if (scc->modem.speed < 300) 
-		scc->modem.speed = 1200;
-
-	time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
-
-	disable_irq(scc->irq);
-
-	if (tx)
-	{
-		or(scc, R1, TxINT_ENAB);	/* t_maxkeyup may have reset these */
-		or(scc, R15, TxUIE);
-	}
-
-	if (scc->modem.clocksrc == CLK_DPLL)
-	{				/* force simplex operation */
-		if (tx)
-		{
-#ifdef CONFIG_SCC_TRXECHO
-			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
-			cl(scc, R15, DCDIE);		/* No DCD changes, please */
-#endif
-			set_brg(scc, time_const);	/* reprogram baudrate generator */
-
-			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
-			wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
-			
-			or(scc,R5,RTS|TxENAB);		/* set the RTS line and enable TX */
-		} else {
-			cl(scc,R5,RTS|TxENAB);
-			
-			set_brg(scc, time_const);	/* reprogram baudrate generator */
-			
-			/* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
-			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
-#ifdef CONFIG_SCC_TRXECHO
-			or(scc,R3,RxENABLE|ENT_HM);
-			or(scc,R15, DCDIE);
-#endif
-		}
-	} else {
-		if (tx)
-		{
-#ifdef CONFIG_SCC_TRXECHO
-			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
-			{
-				cl(scc, R3, RxENABLE);
-				cl(scc, R15, DCDIE);
-			}
-#endif
-				
-				
-			or(scc,R5,RTS|TxENAB);		/* enable tx */
-		} else {
-			cl(scc,R5,RTS|TxENAB);		/* disable tx */
-
-#ifdef CONFIG_SCC_TRXECHO
-			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
-			{
-				or(scc, R3, RxENABLE|ENT_HM);
-				or(scc, R15, DCDIE);
-			}
-#endif
-		}
-	}
-
-	enable_irq(scc->irq);
-}
-
-
-/* ----> SCC timer interrupt handler and friends. <---- */
-
-static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
-{
-	unsigned long flags;
-	
-	
-	save_flags(flags);
-	cli();
-
-	del_timer(&scc->tx_t);
-
-	if (when == 0)
-	{
-		handler((unsigned long) scc);
-	} else 
-	if (when != TIMER_OFF)
-	{
-		scc->tx_t.data = (unsigned long) scc;
-		scc->tx_t.function = handler;
-		scc->tx_t.expires = jiffies + (when*HZ)/100;
-		add_timer(&scc->tx_t);
-	}
-	
-	restore_flags(flags);
-}
-
-static void scc_start_defer(struct scc_channel *scc)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-
-	del_timer(&scc->tx_wdog);
-	
-	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
-	{
-		scc->tx_wdog.data = (unsigned long) scc;
-		scc->tx_wdog.function = t_busy;
-		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
-		add_timer(&scc->tx_wdog);
-	}
-	restore_flags(flags);
-}
-
-static void scc_start_maxkeyup(struct scc_channel *scc)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-
-	del_timer(&scc->tx_wdog);
-	
-	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
-	{
-		scc->tx_wdog.data = (unsigned long) scc;
-		scc->tx_wdog.function = t_maxkeyup;
-		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
-		add_timer(&scc->tx_wdog);
-	}
-	
-	restore_flags(flags);
-}
-
-/* 
- * This is called from scc_txint() when there are no more frames to send.
- * Not exactly a timer function, but it is a close friend of the family...
- */
-
-static void scc_tx_done(struct scc_channel *scc)
-{
-	/* 
-	 * trx remains keyed in fulldup mode 2 until t_idle expires.
-	 */
-				 
-	switch (scc->kiss.fulldup)
-	{
-		case KISS_DUPLEX_LINK:
-			scc->stat.tx_state = TXS_IDLE2;
-			if (scc->kiss.idletime != TIMER_OFF)
-			scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
-			break;
-		case KISS_DUPLEX_OPTIMA:
-			scc_notify(scc, HWEV_ALL_SENT);
-			break;
-		default:
-			scc->stat.tx_state = TXS_BUSY;
-			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
-	}
-
-	scc_unlock_dev(scc);
-}
-
-
-static unsigned char Rand = 17;
-
-extern __inline__ int is_grouped(struct scc_channel *scc)
-{
-	int k;
-	struct scc_channel *scc2;
-	unsigned char grp1, grp2;
-
-	grp1 = scc->kiss.group;
-	
-	for (k = 0; k < (Nchips * 2); k++)
-	{
-		scc2 = &SCC_Info[k];
-		grp2 = scc2->kiss.group;
-		
-		if (scc2 == scc || !(scc2->dev && grp2))
-			continue;
-		
-		if ((grp1 & 0x3f) == (grp2 & 0x3f))
-		{
-			if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
-				return 1;
-			
-			if ( (grp1 & RXGROUP) && (scc2->status & DCD) )
-				return 1;
-		}
-	}
-	return 0;
-}
-
-/* DWAIT and SLOTTIME expired
- *
- * fulldup == 0:  DCD is active or Rand > P-persistence: start t_busy timer
- *                else key trx and start txdelay
- * fulldup == 1:  key trx and start txdelay
- * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
- */
-
-static void t_dwait(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-	
-	if (scc->stat.tx_state == TXS_WAIT)	/* maxkeyup or idle timeout */
-	{
-		if (skb_queue_len(&scc->tx_queue) == 0)	/* nothing to send */
-		{
-			scc->stat.tx_state = TXS_IDLE;
-			scc_unlock_dev(scc);	/* t_maxkeyup locked it. */
-			return;
-		}
-
-		scc->stat.tx_state = TXS_BUSY;
-	}
-
-	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
-	{
-		Rand = Rand * 17 + 31;
-		
-		if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD))  || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
-		{
-			scc_start_defer(scc);
-			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
-			return ;
-		}
-	}
-
-	if ( !(scc->wreg[R5] & RTS) )
-	{
-		scc_key_trx(scc, TX_ON);
-		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
-	} else {
-		scc_start_tx_timer(scc, t_txdelay, 0);
-	}
-}
-
-
-/* TXDELAY expired
- *
- * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
- */
-
-static void t_txdelay(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-
-	scc_start_maxkeyup(scc);
-
-	if (scc->tx_buff == NULL)
-	{
-		disable_irq(scc->irq);
-		scc_txint(scc);	
-		enable_irq(scc->irq);
-	}
-}
-	
-
-/* TAILTIME expired
- *
- * switch off transmitter. If we were stopped by Maxkeyup restart
- * transmission after 'mintime' seconds
- */
-
-static void t_tail(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-	unsigned long flags;
-	
- 	save_flags(flags);
- 	cli();
- 
- 	del_timer(&scc->tx_wdog);	
- 	scc_key_trx(scc, TX_OFF);
-
- 	restore_flags(flags);
-
- 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
- 	{
- 		scc->stat.tx_state = TXS_WAIT;
-
- 		if (scc->kiss.mintime != TIMER_OFF)	/* try it again */
- 			scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
- 		else
- 			scc_start_tx_timer(scc, t_dwait, 0);
- 		return;
- 	}
- 	
- 	scc->stat.tx_state = TXS_IDLE;
-	scc_unlock_dev(scc);
-}
-
-
-/* BUSY timeout
- *
- * throw away send buffers if DCD remains active too long.
- */
-
-static void t_busy(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-
-	del_timer(&scc->tx_t);
-	scc_lock_dev(scc);
-
-	scc_discard_buffers(scc);
-
-	scc->stat.txerrs++;
-	scc->stat.tx_state = TXS_IDLE;
-	
-	scc_unlock_dev(scc);
-}
-
-/* MAXKEYUP timeout
- *
- * this is our watchdog.
- */
-
-static void t_maxkeyup(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	/* 
-	 * let things settle down before we start to
-	 * accept new data.
-	 */
-
-	scc_lock_dev(scc);
-	scc_discard_buffers(scc);
-
-	del_timer(&scc->tx_t);
-
-	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
-	cl(scc, R15, TxUIE);		/* count it. */
-	OutReg(scc->ctrl, R0, RES_Tx_P);
-
-	restore_flags(flags);
-
-	scc->stat.txerrs++;
-	scc->stat.tx_state = TXS_TIMEOUT;
-	scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
-}
-
-/* IDLE timeout
- *
- * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
- * of inactivity. We will not restart transmission before 'mintime'
- * expires.
- */
-
-static void t_idle(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-	
-	del_timer(&scc->tx_wdog);
-
-	scc_key_trx(scc, TX_OFF);
-
-	if (scc->kiss.mintime != TIMER_OFF)
-		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
-	scc->stat.tx_state = TXS_WAIT;
-}
-
-static void scc_init_timer(struct scc_channel *scc)
-{
-	unsigned long flags;
-	
-	save_flags(flags); 
-	cli();
-	
-	scc->stat.tx_state = TXS_IDLE;
-
-	restore_flags(flags);
-}
-
-
-/* ******************************************************************** */
-/* *			Set/get L1 parameters			      * */
-/* ******************************************************************** */
-
-
-/*
- * this will set the "hardware" parameters through KISS commands or ioctl()
- */
-
-#define CAST(x) (unsigned long)(x)
-
-static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
-{
-	int dcd;
-
-	switch (cmd)
-	{
-		case PARAM_TXDELAY:	scc->kiss.txdelay=arg;		break;
-		case PARAM_PERSIST:	scc->kiss.persist=arg;		break;
-		case PARAM_SLOTTIME:	scc->kiss.slottime=arg;		break;
-		case PARAM_TXTAIL:	scc->kiss.tailtime=arg;		break;
-		case PARAM_FULLDUP:	scc->kiss.fulldup=arg;		break;
-		case PARAM_DTR:		break; /* does someone need this? */
-		case PARAM_GROUP:	scc->kiss.group=arg;		break;
-		case PARAM_IDLE:	scc->kiss.idletime=arg;		break;
-		case PARAM_MIN:		scc->kiss.mintime=arg;		break;
-		case PARAM_MAXKEY:	scc->kiss.maxkeyup=arg;		break;
-		case PARAM_WAIT:	scc->kiss.waittime=arg;		break;
-		case PARAM_MAXDEFER:	scc->kiss.maxdefer=arg;		break;
-		case PARAM_TX:		scc->kiss.tx_inhibit=arg;	break;
-
-		case PARAM_SOFTDCD:	
-			scc->kiss.softdcd=arg;
-			if (arg)
-				or(scc, R15, SYNCIE);
-			else
-				cl(scc, R15, SYNCIE);
-			break;
-				
-		case PARAM_SPEED:
-			if (arg < 256)
-				scc->modem.speed=arg*100;
-			else
-				scc->modem.speed=arg;
-
-			if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
-				set_speed(scc);
-			break;
-			
-		case PARAM_RTS:	
-			if ( !(scc->wreg[R5] & RTS) )
-			{
-				if (arg != TX_OFF)
-					scc_key_trx(scc, TX_ON);
-					scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
-			} else {
-				if (arg == TX_OFF)
-				{
-					scc->stat.tx_state = TXS_BUSY;
-					scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
-				}
-			}
-			break;
-			
-		case PARAM_HWEVENT:
-			dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
-			scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
-			break;
-
-		default:		return -EINVAL;
-	}
-	
-	return 0;
-}
-
-
- 
-static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
-{
-	switch (cmd)
-	{
-		case PARAM_TXDELAY:	return CAST(scc->kiss.txdelay);
-		case PARAM_PERSIST:	return CAST(scc->kiss.persist);
-		case PARAM_SLOTTIME:	return CAST(scc->kiss.slottime);
-		case PARAM_TXTAIL:	return CAST(scc->kiss.tailtime);
-		case PARAM_FULLDUP:	return CAST(scc->kiss.fulldup);
-		case PARAM_SOFTDCD:	return CAST(scc->kiss.softdcd);
-		case PARAM_DTR:		return CAST((scc->wreg[R5] & DTR)? 1:0);
-		case PARAM_RTS:		return CAST((scc->wreg[R5] & RTS)? 1:0);
-		case PARAM_SPEED:	return CAST(scc->modem.speed);
-		case PARAM_GROUP:	return CAST(scc->kiss.group);
-		case PARAM_IDLE:	return CAST(scc->kiss.idletime);
-		case PARAM_MIN:		return CAST(scc->kiss.mintime);
-		case PARAM_MAXKEY:	return CAST(scc->kiss.maxkeyup);
-		case PARAM_WAIT:	return CAST(scc->kiss.waittime);
-		case PARAM_MAXDEFER:	return CAST(scc->kiss.maxdefer);
-		case PARAM_TX:		return CAST(scc->kiss.tx_inhibit);
-		default:		return NO_SUCH_PARAM;
-	}
-
-}
-
-#undef CAST
-#undef SVAL
-
-/* ******************************************************************* */
-/* *			Send calibration pattern		     * */
-/* ******************************************************************* */
-
-static void scc_stop_calibrate(unsigned long channel)
-{
-	struct scc_channel *scc = (struct scc_channel *) channel;
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-
-	del_timer(&scc->tx_wdog);
-	scc_key_trx(scc, TX_OFF);
-	wr(scc, R6, 0);
-	wr(scc, R7, FLAG);
-	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
-	Outb(scc->ctrl,RES_EXT_INT);
-
-	scc_unlock_dev(scc);
-	
-	restore_flags(flags);
-}
-
-
-static void
-scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
-{
-	unsigned long flags;
-	
-	save_flags(flags);
-	cli();
-
-	scc_lock_dev(scc);
-	scc_discard_buffers(scc);
-
-	del_timer(&scc->tx_wdog);
-
-	scc->tx_wdog.data = (unsigned long) scc;
-	scc->tx_wdog.function = scc_stop_calibrate;
-	scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
-	add_timer(&scc->tx_wdog);
-	
-	wr(scc, R6, 0);
-	wr(scc, R7, pattern);
-
-	/* 
-	 * Don't know if this works. 
-	 * Damn, where is my Z8530 programming manual...? 
-	 */
-
-	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
-	Outb(scc->ctrl,RES_EXT_INT);
-
-	scc_key_trx(scc, TX_ON);
-	
-	restore_flags(flags);
-}
-
-/* ******************************************************************* */
-/* *		Init channel structures, special HW, etc...	     * */
-/* ******************************************************************* */
-
-/*
- * Reset the Z8530s and setup special hardware
- */
-
-static void z8530_init(void)
-{
-	struct scc_channel *scc;
-	int chip, k;
-	unsigned long flags;
-	char *flag;
-
-
-	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
-	
-	flag=" ";
-	for (k = 0; k < 16; k++)
-		if (Ivec[k].used) 
-		{
-			printk("%s%d", flag, k);
-			flag=",";
-		}
-	printk("\n");
-
-
-	/* reset and pre-init all chips in the system */
-	for (chip = 0; chip < Nchips; chip++)
-	{
-		scc=&SCC_Info[2*chip];
-		if (!scc->ctrl) continue;
-
-		/* Special SCC cards */
-
-		if(scc->brand & EAGLE)			/* this is an EAGLE card */
-			Outb(scc->special,0x08);	/* enable interrupt on the board */
-			
-		if(scc->brand & (PC100 | PRIMUS))	/* this is a PC100/PRIMUS card */
-			Outb(scc->special,scc->option);	/* set the MODEM mode (0x22) */
-
-			
-		/* Reset and pre-init Z8530 */
-
-		save_flags(flags);
-		cli();
-		
-		Outb(scc->ctrl, 0);
-		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
-		udelay(100);				/* give it 'a bit' more time than required */
-		wr(scc, R2, chip*16);			/* interrupt vector */
-		wr(scc, R9, VIS);			/* vector includes status */
-		
-        	restore_flags(flags);
-        }
-
- 
-	Driver_Initialized = 1;
-}
-
-/*
- * Allocate device structure, err, instance, and register driver
- */
-
-static int scc_net_setup(struct scc_channel *scc, unsigned char *name)
-{
-	unsigned char *buf;
-	struct device *dev;
-
-	if (dev_get(name) != NULL)
-	{
-		printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
-		return -EEXIST;
-	}
-
-	if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	dev = scc->dev;
-	memset(dev, 0, sizeof(struct device));
-
-	buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
-	strcpy(buf, name);
-
-	dev->priv = (void *) scc;
-	dev->name = buf;
-	dev->init = scc_net_init;
-
-	if (register_netdev(dev) != 0)
-	{
-		kfree(dev);
-		return -EIO;
-	}
-
-	return 0;
-}
-
-
-
-/* ******************************************************************** */
-/* *			    Network driver methods		      * */
-/* ******************************************************************** */
-
-static unsigned char ax25_bcast[AX25_ADDR_LEN] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static unsigned char ax25_nocall[AX25_ADDR_LEN] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
-/* ----> Initialize device <----- */
-
-static int scc_net_init(struct device *dev)
-{
-	dev_init_buffers(dev);
-	
-	dev->tx_queue_len    = 16;	/* should be enough... */
-
-	dev->open            = scc_net_open;
-	dev->stop	     = scc_net_close;
-
-	dev->hard_start_xmit = scc_net_tx;
-	dev->hard_header     = scc_net_header;
-	dev->rebuild_header  = ax25_rebuild_header;
-	dev->set_mac_address = scc_net_set_mac_address;
-	dev->get_stats       = scc_net_get_stats;
-	dev->do_ioctl        = scc_net_ioctl;
-
-	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
-	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
- 
-	dev->flags      = 0;
-
-	dev->type = ARPHRD_AX25;
-	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
-	dev->mtu = AX25_DEF_PACLEN;
-	dev->addr_len = AX25_ADDR_LEN;
-
-	return 0;
-}
-
-/* ----> open network device <---- */
-
-static int scc_net_open(struct device *dev)
-{
-	struct scc_channel *scc = (struct scc_channel *) dev->priv;
-
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -ENODEV;
-
- 	if (!scc->init)
-		return -EINVAL;
-
-	MOD_INC_USE_COUNT;
-	
-	scc->tx_buff = NULL;
-	skb_queue_head_init(&scc->tx_queue);
- 
-	init_channel(scc);
-
-	dev->tbusy = 0;
-	dev->start = 1;
-
-	return 0;
-}
-
-/* ----> close network device <---- */
-
-static int scc_net_close(struct device *dev)
-{
-	struct scc_channel *scc = (struct scc_channel *) dev->priv;
-	unsigned long flags;
-
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -ENODEV;
-
-	MOD_DEC_USE_COUNT;
-
-	save_flags(flags); 
-	cli();
-	
-	Outb(scc->ctrl,0);		/* Make sure pointer is written */
-	wr(scc,R1,0);			/* disable interrupts */
-	wr(scc,R3,0);
-
-	del_timer(&scc->tx_t);
-	del_timer(&scc->tx_wdog);
-
-	restore_flags(flags);
-	
-	scc_discard_buffers(scc);
-
-	dev->tbusy = 1;
-	dev->start = 0;
-
-	return 0;
-}
-
-/* ----> receive frame, called from scc_rxint() <---- */
-
-static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
-{
-	if (skb->len == 0)
-	{
-		kfree_skb(skb, FREE_READ);
-		return;
-	}
-		
-	scc->dev_stat.rx_packets++;
-
-	skb->dev      = scc->dev;
-	skb->protocol = htons(ETH_P_AX25);
-	skb->mac.raw  = skb->data;
-	
-	netif_rx(skb);
-	return;
-}
-
-/* ----> transmit frame <---- */
-
-static int scc_net_tx(struct sk_buff *skb, struct device *dev)
-{
-	struct scc_channel *scc = (struct scc_channel *) dev->priv;
-	unsigned long flags;
-	char kisscmd;
-	
-	if (scc == NULL || scc->magic != SCC_MAGIC || dev->tbusy)
-	{
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-
-	if (skb->len > scc->stat.bufsize || skb->len < 2)
-	{
-		scc->dev_stat.tx_dropped++;	/* bogus frame */
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-	
-	scc->dev_stat.tx_packets++;
-	scc->stat.txframes++;
-	
-	kisscmd = *skb->data & 0x1f;
-	skb_pull(skb, 1);
-
-	if (kisscmd)
-	{
-		scc_set_param(scc, kisscmd, *skb->data);
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-
-	save_flags(flags);
-	cli();
-	
-	if (skb_queue_len(&scc->tx_queue) >= MAXQUEUE-1)
-	{
-		struct sk_buff *skb_del;
-		skb_del = __skb_dequeue(&scc->tx_queue);
-		dev_kfree_skb(skb_del, FREE_WRITE);
-	}
-	__skb_queue_tail(&scc->tx_queue, skb);
-
-	dev->trans_start = jiffies;
-
-	/*
-	 * Start transmission if the trx state is idle or
-	 * t_idle hasn't expired yet. Use dwait/persistance/slottime
-	 * algorithm for normal halfduplex operation.
-	 */
-
-	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
-	{
-		scc->stat.tx_state = TXS_BUSY;
-		if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
-			scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
-		else
-			scc_start_tx_timer(scc, t_dwait, 0);
-	}
-
-	restore_flags(flags);
-
-	return 0;
-}
-
-/* ----> ioctl functions <---- */
-
-/*
- * SIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
- * SIOCSCCINI		- initialize driver	arg: ---
- * SIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
- * SIOCSCCSMEM		- set memory		arg: (struct scc_mem_config *) arg
- * SIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
- * SIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
- * SIOCSCCGSTAT		- get driver status	arg: (struct scc_stat *) arg
- * SIOCSCCCAL		- send calib. pattern	arg: (struct scc_calibrate *) arg
- */
-
-static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
-{
-	struct scc_kiss_cmd kiss_cmd;
-	struct scc_mem_config memcfg;
-	struct scc_hw_config hwcfg;
-	struct scc_calibrate cal;
-	int chan;
-	unsigned char device_name[10];
-	void *arg;
-	struct scc_channel *scc;
-	
-	scc = (struct scc_channel *) dev->priv;
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return -EINVAL;
-		
-	arg = (void *) ifr->ifr_data;
-	
-	if (!Driver_Initialized)
-	{
-		if (cmd == SIOCSCCCFG)
-		{
-			int found = 1;
-
-			if (!suser()) return -EPERM;
-			if (!arg) return -EFAULT;
-
-			if (Nchips >= MAXSCC) 
-				return -EINVAL;
-
-			if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
-				return -EFAULT;
-
-			if (hwcfg.irq == 2) hwcfg.irq = 9;
-
-			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
-			{
-				if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
-					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
-				else
-					Ivec[hwcfg.irq].used = 1;
-			}
-
-			if (hwcfg.vector_latch) 
-				Vector_Latch = hwcfg.vector_latch;
-
-			if (hwcfg.clock == 0)
-				hwcfg.clock = DEFAULT_CLOCK;
-
-#ifndef DONT_CHECK
-			disable_irq(hwcfg.irq);
-
-			check_region(scc->ctrl, 1);
-			Outb(hwcfg.ctrl_a, 0);
-			udelay(5);
-			OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
-			udelay(5);
-
-			if (InReg(hwcfg.ctrl_a,R13) != 0x55)
-				found = 0;
-
-			enable_irq(hwcfg.irq);
-#endif
-
-			if (found)
-			{
-				SCC_Info[2*Nchips  ].ctrl = hwcfg.ctrl_a;
-				SCC_Info[2*Nchips  ].data = hwcfg.data_a;
-				SCC_Info[2*Nchips  ].irq  = hwcfg.irq;
-				SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
-				SCC_Info[2*Nchips+1].data = hwcfg.data_b;
-				SCC_Info[2*Nchips+1].irq  = hwcfg.irq;
-			
-				SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
-				SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
-				SCC_ctrl[Nchips].irq    = hwcfg.irq;
-			}
-
-
-			for (chan = 0; chan < 2; chan++)
-			{
-				sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
-
-				SCC_Info[2*Nchips+chan].special = hwcfg.special;
-				SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
-				SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
-				SCC_Info[2*Nchips+chan].option = hwcfg.option;
-				SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
-
-#ifdef DONT_CHECK
-				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x\n",
-					device_name, 
-					SCC_Info[2*Nchips+chan].data, 
-					SCC_Info[2*Nchips+chan].ctrl);
-
-#else
-				printk(KERN_INFO "%s: data port = 0x%3.3lx  control port = 0x%3.3lx -- %s\n",
-					device_name,
-					chan? hwcfg.data_b : hwcfg.data_a, 
-					chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
-					found? "found" : "missing");
-#endif
-
-				if (found)
-				{
-					request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
-					request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
-					if (Nchips+chan != 0)
-						scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
-				}
-			}
-			
-			if (found) Nchips++;
-			
-			return 0;
-		}
-		
-		if (cmd == SIOCSCCINI)
-		{
-			if (!suser())
-				return -EPERM;
-				
-			if (Nchips == 0)
-				return -EINVAL;
-
-			z8530_init();
-			return 0;
-		}
-		
-		return -EINVAL;	/* confuse the user */
-	}
-	
-	if (!scc->init)
-	{
-		if (cmd == SIOCSCCCHANINI)
-		{
-			if (!suser()) return -EPERM;
-			if (!arg) return -EINVAL;
-			
-			scc->stat.bufsize   = BUFSIZE;
-
-			if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
-				return -EINVAL;
-			
-			/* default KISS Params */
-		
-			if (scc->modem.speed < 4800)
-			{
-				scc->kiss.txdelay = 36;		/* 360 ms */
-				scc->kiss.persist = 42;		/* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 16;	/* 160 ms */
-				scc->kiss.tailtime = 4;		/* minimal reasonable value */
-				scc->kiss.fulldup = 0;		/* CSMA */
-				scc->kiss.waittime = 50;	/* 500 ms */
-				scc->kiss.maxkeyup = 10;	/* 10 s */
-				scc->kiss.mintime = 3;		/* 3 s */
-				scc->kiss.idletime = 30;	/* 30 s */
-				scc->kiss.maxdefer = 120;	/* 2 min */
-				scc->kiss.softdcd = 0;		/* hardware dcd */
-			} else {
-				scc->kiss.txdelay = 10;		/* 100 ms */
-				scc->kiss.persist = 64;		/* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 8;		/* 160 ms */
-				scc->kiss.tailtime = 1;		/* minimal reasonable value */
-				scc->kiss.fulldup = 0;		/* CSMA */
-				scc->kiss.waittime = 50;	/* 500 ms */
-				scc->kiss.maxkeyup = 7;		/* 7 s */
-				scc->kiss.mintime = 3;		/* 3 s */
-				scc->kiss.idletime = 30;	/* 30 s */
-				scc->kiss.maxdefer = 120;	/* 2 min */
-				scc->kiss.softdcd = 0;		/* hardware dcd */
-			}
-			
-			scc->tx_buff = NULL;
-			skb_queue_head_init(&scc->tx_queue);
-			scc->init = 1;
-			
-			return 0;
-		}
-		
-		return -EINVAL;
-	}
-	
-	switch(cmd)
-	{
-		case SIOCSCCRESERVED:
-			return -ENOIOCTLCMD;
-
-		case SIOCSCCSMEM:
-			if (!suser()) return -EPERM;
-			if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
-				return -EINVAL;
-			scc->stat.bufsize   = memcfg.bufsize;
-			return 0;
-		
-		case SIOCSCCGSTAT:
-			if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
-				return -EINVAL;
-			return 0;
-		
-		case SIOCSCCGKISS:
-			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
-				return -EINVAL;
-			kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
-			if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
-				return -EINVAL;
-			return 0;
-		
-		case SIOCSCCSKISS:
-			if (!suser()) return -EPERM;
-			if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
-				return -EINVAL;
-			return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
-		
-		case SIOCSCCCAL:
-			if (!suser()) return -EPERM;
-			if (!arg || copy_from_user(&cal, arg, sizeof(cal)))
-				return -EINVAL;
-
-			scc_start_calibrate(scc, cal.time, cal.pattern);
-			return 0;
-
-		default:
-			return -ENOIOCTLCMD;
-		
-	}
-	
-	return -EINVAL;
-}
-
-/* ----> set interface callsign <---- */
-
-static int scc_net_set_mac_address(struct device *dev, void *addr)
-{
-	struct sockaddr *sa = (struct sockaddr *) addr;
-	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-	return 0;
-}
-
-/* ----> "hard" header <---- */
-
-static int  scc_net_header(struct sk_buff *skb, struct device *dev, 
-	unsigned short type, void *daddr, void *saddr, unsigned len)
-{
-	return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
-}
-
-/* ----> get statistics <---- */
-
-static struct net_device_stats *scc_net_get_stats(struct device *dev)
-{
-	struct scc_channel *scc = (struct scc_channel *) dev->priv;
-	
-	if (scc == NULL || scc->magic != SCC_MAGIC)
-		return NULL;
-		
-	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
-	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
-	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
-	scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
-
-	return &scc->dev_stat;
-}
-
-/* ******************************************************************** */
-/* *		dump statistics to /proc/net/z8530drv		      * */
-/* ******************************************************************** */
-
-
-static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct scc_channel *scc;
-	struct scc_kiss *kiss;
-	struct scc_stat *stat;
-	int len = 0;
-	off_t pos = 0;
-	off_t begin = 0;
-	int k;
-
-	len += sprintf(buffer, "z8530drv-"VERSION"\n");
-
-	if (!Driver_Initialized)
-	{
-		len += sprintf(buffer+len, "not initialized\n");
-		goto done;
-	}
-
-	if (!Nchips)
-	{
-		len += sprintf(buffer+len, "chips missing\n");
-		goto done;
-	}
-
-	for (k = 0; k < Nchips*2; k++)
-	{
-		scc = &SCC_Info[k];
-		stat = &scc->stat;
-		kiss = &scc->kiss;
-
-		if (!scc->init)
-			continue;
-
-		/* dev	data ctrl irq clock brand enh vector special option 
-		 *	baud nrz clocksrc softdcd bufsize
-		 *	rxints txints exints spints
-		 *	rcvd rxerrs over / xmit txerrs under / nospace bufsize
-		 *	txd pers slot tail ful wait min maxk idl defr txof grp
-		 *	W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
-		 *	R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
-		 */
-
-		len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
-				scc->dev->name,
-				scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
-				scc->enhanced, Vector_Latch, scc->special,
-				scc->option);
-		len += sprintf(buffer+len, "\t%lu %d %d %d %d\n",
-				scc->modem.speed, scc->modem.nrz,
-				scc->modem.clocksrc, kiss->softdcd,
-				stat->bufsize);
-		len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n",
-				stat->rxints, stat->txints, stat->exints, stat->spints);
-		len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
-				stat->rxframes, stat->rxerrs, stat->rx_over,
-				stat->txframes, stat->txerrs, stat->tx_under,
-				stat->nospace,  stat->tx_state);
-
-#define K(x) kiss->x
-		len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n",
-				K(txdelay), K(persist), K(slottime), K(tailtime),
-				K(fulldup), K(waittime), K(mintime), K(maxkeyup),
-				K(idletime), K(maxdefer), K(tx_inhibit), K(group));
-#undef K
-#ifdef SCC_DEBUG
-		{
-			int reg;
-
-		len += sprintf(buffer+len, "\tW ");
-			for (reg = 0; reg < 16; reg++)
-				len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
-			len += sprintf(buffer+len, "\n");
-			
-		len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
-			for (reg = 3; reg < 8; reg++)
-				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
-			len += sprintf(buffer+len, "XX ");
-			for (reg = 9; reg < 16; reg++)
-				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
-			len += sprintf(buffer+len, "\n");
-		}
-#endif
-		len += sprintf(buffer+len, "\n");
-
-                pos = begin + len;
-
-                if (pos < offset) {
-                        len   = 0;
-                        begin = pos;
-                }
-
-                if (pos > offset + length)
-                        break;
-	}
-
-done:
-
-        *start = buffer + (offset - begin);
-        len   -= (offset - begin);
-
-        if (len > length) len = length;
-
-        return len;
-}
-
-#ifdef CONFIG_PROC_FS
-
-struct proc_dir_entry scc_proc_dir_entry = 
-{ 
-	PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
-	&proc_net_inode_operations, scc_net_get_info 
-};
-
-#define scc_net_procfs_init()   proc_net_register(&scc_proc_dir_entry);
-#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
-#else
-#define scc_net_procfs_init()
-#define scc_net_procfs_remove()
-#endif
-
-  
-/* ******************************************************************** */
-/* * 			Init SCC driver 			      * */
-/* ******************************************************************** */
-
-__initfunc(int scc_init (void))
-{
-	int chip, chan, k, result;
-	char devname[10];
-	
-	printk(KERN_INFO BANNER);
-	
-	memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
-	
-	/* pre-init channel information */
-	
-	for (chip = 0; chip < MAXSCC; chip++)
-	{
-		memset((char *) &SCC_Info[2*chip  ], 0, sizeof(struct scc_channel));
-		memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
-		
-		for (chan = 0; chan < 2; chan++)
-			SCC_Info[2*chip+chan].magic = SCC_MAGIC;
-	}
-
-	for (k = 0; k < 16; k++) Ivec[k].used = 0;
-
-	sprintf(devname,"%s0", SCC_DriverName);
-	
-	result = scc_net_setup(SCC_Info, devname);
-	if (result)
-	{
-		printk(KERN_ERR "z8530drv: cannot initialize module\n");
-		return result;
-	}
-
-	scc_net_procfs_init();
-
-	return 0;
-}
-
-/* ******************************************************************** */
-/* *			    Module support 			      * */
-/* ******************************************************************** */
-
-
-#ifdef MODULE
-int init_module(void)
-{
-	int result = 0;
-	
-	result = scc_init();
-
-	if (result == 0)
-		printk(KERN_INFO "Copyright 1993,1997 Joerg Reuter DL1BKE (jreuter@poboxes.com)\n");
-		
-	return result;
-}
-
-void cleanup_module(void)
-{
-	long flags;
-	io_port ctrl;
-	int k;
-	struct scc_channel *scc;
-	
-	save_flags(flags); 
-	cli();
-
-	if (Nchips == 0)
-		unregister_netdev(SCC_Info[0].dev);
-
-	for (k = 0; k < Nchips; k++)
-		if ( (ctrl = SCC_ctrl[k].chan_A) )
-		{
-			Outb(ctrl, 0);
-			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
-			udelay(50);
-		}
-		
-	for (k = 0; k < Nchips*2; k++)
-	{
-		scc = &SCC_Info[k];
-		if (scc)
-		{
-			release_region(scc->ctrl, 1);
-			release_region(scc->data, 1);
-			if (scc->dev)
-			{
-				unregister_netdev(scc->dev);
-				kfree(scc->dev);
-			}
-		}
-	}
-	
-	for (k=0; k < 16 ; k++)
-		if (Ivec[k].used) free_irq(k, NULL);
-		
-	restore_flags(flags);
-
-	scc_net_procfs_remove();
-}
-#endif
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/Makefile linux/drivers/net/soundmodem/Makefile
--- v2.1.70/linux/drivers/net/soundmodem/Makefile	Thu Sep  4 17:07:30 1997
+++ linux/drivers/net/soundmodem/Makefile	Wed Dec 31 16:00:00 1969
@@ -1,60 +0,0 @@
-#
-# Makefile for the soundmodem device driver.
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
-
-O_TARGET	:= soundmodem.o
-
-O_OBJS		:= sm.o
-ifeq ($(CONFIG_SOUNDMODEM_SBC),y)
-O_OBJS		+= sm_sbc.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_WSS),y)
-O_OBJS		+= sm_wss.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_AFSK1200),y)
-O_OBJS		+= sm_afsk1200.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_7),y)
-O_OBJS		+= sm_afsk2400_7.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_AFSK2400_8),y)
-O_OBJS		+= sm_afsk2400_8.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_AFSK2666),y)
-O_OBJS		+= sm_afsk2666.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_HAPN4800),y)
-O_OBJS		+= sm_hapn4800.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_PSK4800),y)
-O_OBJS		+= sm_psk4800.o
-endif
-ifeq ($(CONFIG_SOUNDMODEM_FSK9600),y)
-O_OBJS		+= sm_fsk9600.o
-endif
-
-M_OBJS		:= $(O_TARGET)
-
-all:		all_targets
-.PHONY:		all
-
-gentbl:		gentbl.c
-		$(HOSTCC) -Wall $< -o $@ -lm
-
-TBLHDR		:= sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h 
-TBLHDR		+= sm_tbl_afsk2666.h sm_tbl_psk4800.h
-TBLHDR		+= sm_tbl_hapn4800.h sm_tbl_fsk9600.h
-
-$(TBLHDR):	gentbl
-		./gentbl
-
-fastdep:	$(TBLHDR)
-
-include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/gentbl.c linux/drivers/net/soundmodem/gentbl.c
--- v2.1.70/linux/drivers/net/soundmodem/gentbl.c	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/soundmodem/gentbl.c	Wed Dec 31 16:00:00 1969
@@ -1,676 +0,0 @@
-/*****************************************************************************/
-
-/*
- *      gentbl.c  -- soundcard radio modem driver table generator.
- *
- *      Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *      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.
- *
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
- *
- *      You should have received a copy of the GNU General Public License
- *      along with this program; if not, write to the Free Software
- *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-
-/* -------------------------------------------------------------------- */
-
-static void gentbl_offscostab(FILE *f, unsigned int nbits)
-{
-	int i;
-
-	fprintf(f, "\n/*\n * small cosine table in U8 format\n */\n"
-		"#define OFFSCOSTABBITS %u\n"
-		"#define OFFSCOSTABSIZE (1<<OFFSCOSTABBITS)\n\n", 
-		nbits);
-	fprintf(f, "static unsigned char offscostab[OFFSCOSTABSIZE] = {\n\t");
-	for (i = 0; i < (1<<nbits); i++) {
-		fprintf(f, "%4u", (int)
-			(128+127.0*cos(i*2.0*M_PI/(1<<nbits))));
-		if (i < (1<<nbits)-1) 
-			fprintf(f, "%s", (i & 7) == 7 ? ",\n\t" : ",");
-	}
-	fprintf(f, "\n};\n\n"
-		"#define OFFSCOS(x) offscostab[((x)>>%d)&0x%x]\n\n",
-		16-nbits, (1<<nbits)-1);
-}
-
-/* -------------------------------------------------------------------- */
-
-static void gentbl_costab(FILE *f, unsigned int nbits)
-{
-        int i;
-
-	fprintf(f, "\n/*\n * more accurate cosine table\n */\n\n"
-		"static const short costab[%d] = {", (1<<nbits));
-        for (i = 0; i < (1<<nbits); i++) {
-                if (!(i & 7))
-                        fprintf(f, "\n\t");
-                fprintf(f, "%6d", (int)(32767.0*cos(i*2.0*M_PI/(1<<nbits))));
-                if (i != ((1<<nbits)-1))
-                        fprintf(f, ", ");
-        }
-        fprintf(f, "\n};\n\n#define COS(x) costab[((x)>>%d)&0x%x]\n"
-		"#define SIN(x) COS((x)+0xc000)\n\n", 16-nbits,
-		(1<<nbits)-1);
-}
-
-/* -------------------------------------------------------------------- */
-
-#define AFSK12_SAMPLE_RATE 9600
-#define AFSK12_TX_FREQ_LO 1200
-#define AFSK12_TX_FREQ_HI 2200
-#define AFSK12_CORRLEN 8
-
-static void gentbl_afsk1200(FILE *f)
-{
-        int i, v, sum;
-
-#define ARGLO(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_LO/(double)AFSK12_SAMPLE_RATE
-#define ARGHI(x) 2.0*M_PI*(double)x*(double)AFSK12_TX_FREQ_HI/(double)AFSK12_SAMPLE_RATE
-
-	fprintf(f, "\n/*\n * afsk1200 specific tables\n */\n"
-		"#define AFSK12_SAMPLE_RATE %u\n"
-		"#define AFSK12_TX_FREQ_LO %u\n"
-		"#define AFSK12_TX_FREQ_HI %u\n"
-		"#define AFSK12_CORRLEN %u\n\n",
-		AFSK12_SAMPLE_RATE, AFSK12_TX_FREQ_LO, 
-		AFSK12_TX_FREQ_HI, AFSK12_CORRLEN);
-	fprintf(f, "static const int afsk12_tx_lo_i[] = {\n\t");
-        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
-		sum += (v = 127.0*cos(ARGLO(i)));
-                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_I %d\n\n"
-		"static const int afsk12_tx_lo_q[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
-		sum += (v = 127.0*sin(ARGLO(i))); 
-                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_LO_Q %d\n\n"
-		"static const int afsk12_tx_hi_i[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK12_CORRLEN; i++) {
-		sum += (v = 127.0*cos(ARGHI(i))); 
-                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_I %d\n\n"
-		"static const int afsk12_tx_hi_q[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK12_CORRLEN; i++)  {
-		sum += (v = 127.0*sin(ARGHI(i)));
-                fprintf(f, " %4i%c", v, (i < AFSK12_CORRLEN-1) ? ',' : ' ');
-	}
-	fprintf(f, "\n};\n#define SUM_AFSK12_TX_HI_Q %d\n\n", sum);
-#undef ARGLO
-#undef ARGHI
-}
-
-/* -------------------------------------------------------------------- */
-
-static const float fsk96_tx_coeff_4[32] = {
-              -0.001152,        0.000554,        0.002698,        0.002753,
-              -0.002033,       -0.008861,       -0.008002,        0.006607,
-               0.023727,        0.018905,       -0.018056,       -0.057957,
-              -0.044368,        0.055683,        0.207667,        0.322048,
-               0.322048,        0.207667,        0.055683,       -0.044368,
-              -0.057957,       -0.018056,        0.018905,        0.023727,
-               0.006607,       -0.008002,       -0.008861,       -0.002033,
-               0.002753,        0.002698,        0.000554,       -0.001152
-};
-
-static const float fsk96_tx_coeff_5[40] = {
-              -0.001009,       -0.000048,        0.001376,        0.002547,
-               0.002061,       -0.001103,       -0.005795,       -0.008170,
-              -0.004017,        0.006924,        0.018225,        0.019238,
-               0.002925,       -0.025777,       -0.048064,       -0.039683,
-               0.013760,        0.104144,        0.200355,        0.262346,
-               0.262346,        0.200355,        0.104144,        0.013760,
-              -0.039683,       -0.048064,       -0.025777,        0.002925,
-               0.019238,        0.018225,        0.006924,       -0.004017,
-              -0.008170,       -0.005795,       -0.001103,        0.002061,
-               0.002547,        0.001376,       -0.000048,       -0.001009
-};
-
-#define HAMMING(x) (0.54-0.46*cos(2*M_PI*(x)));
-
-static inline float hamming(float x)
-{
-	return 0.54-0.46*cos(2*M_PI*x);
-}
-
-static inline float sinc(float x)
-{
-	if (x == 0)
-		return 1;
-	x *= M_PI;
-	return sin(x)/x;
-}
-
-static void gentbl_fsk9600(FILE *f)
-{
-        int i, j, k, l, m;
-	float s;
-	float c[44];
-	float min, max;
-
-	fprintf(f, "\n/*\n * fsk9600 specific tables\n */\n");
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-#if 0
-	memcpy(c+2, fsk96_tx_coeff_4, sizeof(fsk96_tx_coeff_4));
-#else
-	for (i = 0; i < 29; i++)
-		c[3+i] = sinc(1.2*((i-14.0)/4.0))*hamming(i/28.0)/3.5;
-#endif
-	fprintf(f, "static unsigned char fsk96_txfilt_4[] = {\n\t");
-	for (i = 0; i < 4; i++) {
-		for (j = 0; j < 256; j++) {
-			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
-				if (j & k) {
-					for (m = 0; m < 4; m++, l++)
-						s += c[l];
-				} else {
-					for (m = 0; m < 4; m++, l++)
-						s -= c[l];
-				}
-			}
-			s *= 0.75;
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 3 || j < 255)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "fsk9600: txfilt4: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-#if 0
-	memcpy(c+2, fsk96_tx_coeff_5, sizeof(fsk96_tx_coeff_5));
-#else
-	for (i = 0; i < 36; i++)
-		c[4+i] = sinc(1.2*((i-17.5)/5.0))*hamming(i/35.0)/4.5;
-#endif
-	fprintf(f, "static unsigned char fsk96_txfilt_5[] = {\n\t");
-	for (i = 0; i < 5; i++) {
-		for (j = 0; j < 256; j++) {
-			for (k = 1, s = 0, l = i; k < 256; k <<= 1) {
-				if (j & k) {
-					for (m = 0; m < 5; m++, l++)
-						s += c[l];
-				} else {
-					for (m = 0; m < 5; m++, l++)
-						s -= c[l];
-				}
-			}
-			s *= 0.75;
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 4 || j < 255)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "fsk9600: txfilt5: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-}
-	
-/* -------------------------------------------------------------------- */
-
-#define AFSK26_SAMPLERATE  16000
-
-#define AFSK26_NUMCAR      2
-#define AFSK26_FIRSTCAR    2000
-#define AFSK26_MSK_LEN     6
-#define AFSK26_RXOVER      2
-
-#define AFSK26_DEMCORRLEN (2*AFSK26_MSK_LEN)
-
-#define AFSK26_WINDOW(x) ((1-cos(2.0*M_PI*(x)))/2.0)
-
-#define AFSK26_AMPL(x) (((x)?1.0:0.7))
-
-#undef AFSK26_AMPL
-#define AFSK26_AMPL(x) 1
-
-static void gentbl_afsk2666(FILE *f)
-{
-        int i, j, k, l, o, v, sumi, sumq;
-        float window[AFSK26_DEMCORRLEN*AFSK26_RXOVER];
-	int cfreq[AFSK26_NUMCAR];
-
-	fprintf(f, "\n/*\n * afsk2666 specific tables\n */\n"
-		"#define AFSK26_DEMCORRLEN %d\n"
-		"#define AFSK26_SAMPLERATE %d\n\n", AFSK26_DEMCORRLEN,
-		AFSK26_SAMPLERATE);
-	fprintf(f, "static const unsigned int afsk26_carfreq[%d] = { ",
-		AFSK26_NUMCAR);
-	for (i = 0; i < AFSK26_NUMCAR; i++) {
-		cfreq[i] = 0x10000*AFSK26_FIRSTCAR/AFSK26_SAMPLERATE+
-			0x10000*i/AFSK26_MSK_LEN/2;
-		fprintf(f, "0x%x", cfreq[i]);
-		if (i < AFSK26_NUMCAR-1)
-			fprintf(f, ", ");
-	}
-	fprintf(f, " };\n\n");
-        for (i = 0; i < AFSK26_DEMCORRLEN*AFSK26_RXOVER; i++)
-                window[i] = AFSK26_WINDOW(((float)i)/(AFSK26_DEMCORRLEN*
-						      AFSK26_RXOVER)) * 127.0;
-        fprintf(f, "\nstatic const struct {\n\t"
-               "int i[%d];\n\tint q[%d];\n} afsk26_dem_tables[%d][%d] = {\n", 
-               AFSK26_DEMCORRLEN, AFSK26_DEMCORRLEN, AFSK26_RXOVER, AFSK26_NUMCAR);
-        for (o = AFSK26_RXOVER-1; o >= 0; o--) {
-                fprintf(f, "\t{\n");
-                for (i = 0; i < AFSK26_NUMCAR; i++) {
-                        j = cfreq[i];
-                        fprintf(f, "\t\t{{ ");
-                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumi = 0; l >= 0; 
-                             l--, k = (k+j)&0xffffu) {
-				sumi += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
-					 cos(M_PI*k/32768.0));
-                                fprintf(f, "%6d%s", v, l ? ", " : " }, { ");
-			}
-                        for (l = AFSK26_DEMCORRLEN-1, k = (j * o)/AFSK26_RXOVER, sumq = 0; l >= 0; 
-                             l--, k = (k+j)&0xffffu) {
-				sumq += (v = AFSK26_AMPL(i)*window[l*AFSK26_RXOVER+o]*
-					 sin(M_PI*k/32768.0));
-                                fprintf(f, "%6d%s", v, l ? ", " : " }}");
-			}
-                        if (i < 1)
-                                fprintf(f, ",");
-                        fprintf(f, "\n#define AFSK26_DEM_SUM_I_%d_%d %d\n"
-				"#define AFSK26_DEM_SUM_Q_%d_%d %d\n", 
-				AFSK26_RXOVER-1-o, i, sumi, AFSK26_RXOVER-1-o, i, sumq);
-                }
-                fprintf(f, "\t}%s\n", o ? "," : "");
-        }
-        fprintf(f, "};\n\n");
-}
-
-/* -------------------------------------------------------------------- */
-
-#define ATAN_TABLEN 1024
-
-static void gentbl_atantab(FILE *f)
-{
-        int i;
-        short x;
-
-	fprintf(f, "\n/*\n"
-		" * arctan table (indexed by i/q; should really be indexed by i/(i+q)\n"
-		" */\n""#define ATAN_TABLEN %d\n\n"
-		"static const unsigned short atan_tab[ATAN_TABLEN+2] = {",
-               ATAN_TABLEN);
-        for (i = 0; i <= ATAN_TABLEN; i++) {
-                if (!(i & 7))
-                        fprintf(f, "\n\t");
-                x = atan(i / (float)ATAN_TABLEN) / M_PI * 0x8000;
-                fprintf(f, "%6d, ", x);
-        }
-        fprintf(f, "%6d\n};\n\n", x);
-	
-}
-
-/* -------------------------------------------------------------------- */
-
-#define PSK48_TXF_OVERSAMPLING 5
-#define PSK48_TXF_NUMSAMPLES 16
-#define PSK48_RXF_LEN     64
-
-static const float psk48_tx_coeff[80] = {
-              -0.000379,       -0.000640,       -0.000000,        0.000772,
-               0.000543,       -0.000629,       -0.001187,       -0.000000,
-               0.001634,        0.001183,       -0.001382,       -0.002603,
-              -0.000000,        0.003481,        0.002472,       -0.002828,
-              -0.005215,       -0.000000,        0.006705,        0.004678,
-              -0.005269,       -0.009584,       -0.000000,        0.012065,
-               0.008360,       -0.009375,       -0.017028,       -0.000000,
-               0.021603,        0.015123,       -0.017229,       -0.032012,
-              -0.000000,        0.043774,        0.032544,       -0.040365,
-              -0.084963,       -0.000000,        0.201161,        0.374060,
-               0.374060,        0.201161,       -0.000000,       -0.084963,
-              -0.040365,        0.032544,        0.043774,       -0.000000,
-              -0.032012,       -0.017229,        0.015123,        0.021603,
-              -0.000000,       -0.017028,       -0.009375,        0.008360,
-               0.012065,       -0.000000,       -0.009584,       -0.005269,
-               0.004678,        0.006705,       -0.000000,       -0.005215,
-              -0.002828,        0.002472,        0.003481,       -0.000000,
-              -0.002603,       -0.001382,        0.001183,        0.001634,
-              -0.000000,       -0.001187,       -0.000629,        0.000543,
-               0.000772,       -0.000000,       -0.000640,       -0.000379
-};
-
-static const float psk48_rx_coeff[PSK48_RXF_LEN] = {
-              -0.000219,        0.000360,        0.000873,        0.001080,
-               0.000747,       -0.000192,       -0.001466,       -0.002436,
-              -0.002328,       -0.000699,        0.002101,        0.004809,
-               0.005696,        0.003492,       -0.001633,       -0.007660,
-              -0.011316,       -0.009627,       -0.001780,        0.009712,
-               0.019426,        0.021199,        0.011342,       -0.008583,
-              -0.030955,       -0.044093,       -0.036634,       -0.002651,
-               0.054742,        0.123101,        0.184198,        0.220219,
-               0.220219,        0.184198,        0.123101,        0.054742,
-              -0.002651,       -0.036634,       -0.044093,       -0.030955,
-              -0.008583,        0.011342,        0.021199,        0.019426,
-               0.009712,       -0.001780,       -0.009627,       -0.011316,
-              -0.007660,       -0.001633,        0.003492,        0.005696,
-               0.004809,        0.002101,       -0.000699,       -0.002328,
-              -0.002436,       -0.001466,       -0.000192,        0.000747,
-               0.001080,        0.000873,        0.000360,       -0.000219
-};
-
-static void gentbl_psk4800(FILE *f)
-{
-        int i, j, k;
-        short x;
-
-	fprintf(f, "\n/*\n * psk4800 specific tables\n */\n"
-		"#define PSK48_TXF_OVERSAMPLING %d\n"
-		"#define PSK48_TXF_NUMSAMPLES %d\n\n"
-		"#define PSK48_SAMPLERATE  8000\n"
-		"#define PSK48_CAR_FREQ    2000\n"
-		"#define PSK48_PSK_LEN     5\n"
-		"#define PSK48_RXF_LEN     %u\n"
-		"#define PSK48_PHASEINC    (0x10000*PSK48_CAR_FREQ/PSK48_SAMPLERATE)\n"
-		"#define PSK48_SPHASEINC   (0x10000/(2*PSK48_PSK_LEN))\n\n"
-		"static const short psk48_tx_table[PSK48_TXF_OVERSAMPLING*"
-		"PSK48_TXF_NUMSAMPLES*8*2] = {", 
-		PSK48_TXF_OVERSAMPLING, PSK48_TXF_NUMSAMPLES, PSK48_RXF_LEN);
-        for (i = 0; i < PSK48_TXF_OVERSAMPLING; i++) {
-                for (j = 0; j < PSK48_TXF_NUMSAMPLES; j++) {
-                        fprintf(f, "\n\t");
-                        for (k = 0; k < 8; k++) {
-                                x = 32767.0 * cos(k*M_PI/4.0) * 
-                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
-                                fprintf(f, "%6d, ", x);
-                        }
-                        fprintf(f, "\n\t");
-                        for (k = 0; k < 8; k++) {
-                                x = 32767.0 * sin(k*M_PI/4.0) * 
-                                        psk48_tx_coeff[j * PSK48_TXF_OVERSAMPLING + i];
-                                fprintf(f, "%6d", x);
-                                if (k != 7 || j != PSK48_TXF_NUMSAMPLES-1 || 
-                                    i != PSK48_TXF_OVERSAMPLING-1)
-                                        fprintf(f, ", ");
-                        }
-                }
-        }
-        fprintf(f, "\n};\n\n");
-
-	fprintf(f, "static const short psk48_rx_coeff[PSK48_RXF_LEN] = {\n\t");
-	for (i = 0; i < PSK48_RXF_LEN; i++) {
-		fprintf(f, "%6d", (int)(psk48_rx_coeff[i]*32767.0));
-		if (i < PSK48_RXF_LEN-1)
-			fprintf(f, ",%s", (i & 7) == 7 ? "\n\t" : "");
-	}
-	fprintf(f, "\n};\n\n");
-}
-
-/* -------------------------------------------------------------------- */
-
-static void gentbl_hapn4800(FILE *f)
-{
-        int i, j, k, l;
-	float s;
-	float c[40];
-	float min, max;
-
-	fprintf(f, "\n/*\n * hapn4800 specific tables\n */\n\n");
-	/*
-	 * firstly generate tables for the FM transmitter modulator
-	 */
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-	for (i = 0; i < 24; i++)
-		c[8+i] = sinc(1.5*((i-11.5)/8.0))*hamming(i/23.0)/2.4;
-	for (i = 0; i < 24; i++)
-		c[i] -= c[i+8];
-	fprintf(f, "static unsigned char hapn48_txfilt_8[] = {\n\t");
-	for (i = 0; i < 8; i++) {
-		for (j = 0; j < 16; j++) {
-			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
-				if (j & k)
-					s += c[l];
-				else 
-					s -= c[l];
-			}
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 7 || j < 15)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "hapn4800: txfilt8: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-	for (i = 0; i < 30; i++)
-		c[10+i] = sinc(1.5*((i-14.5)/10.0))*hamming(i/29.0)/2.4;
-	for (i = 0; i < 30; i++)
-		c[i] -= c[i+10];
-	fprintf(f, "static unsigned char hapn48_txfilt_10[] = {\n\t");
-	for (i = 0; i < 10; i++) {
-		for (j = 0; j < 16; j++) {
-			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
-				if (j & k) 
-					s += c[l];
-				else 
-					s -= c[l];
-			}
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 9 || j < 15)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "hapn4800: txfilt10: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-	/*
-	 * secondly generate tables for the PM transmitter modulator
-	 */
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-	for (i = 0; i < 25; i++)
-		c[i] = sinc(1.4*((i-12.0)/8.0))*hamming(i/24.0)/6.3;
-	for (i = 0; i < 25; i++)
-		for (j = 1; j < 8; j++)
-			c[i] += c[i+j];
-	fprintf(f, "static unsigned char hapn48_txfilt_pm8[] = {\n\t");
-	for (i = 0; i < 8; i++) {
-		for (j = 0; j < 16; j++) {
-			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 8) {
-				if (j & k)
-					s += c[l];
-				else 
-					s -= c[l];
-			}
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 7 || j < 15)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "hapn4800: txfiltpm8: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-	min = max = 0;
-	memset(c, 0, sizeof(c));
-	for (i = 0; i < 31; i++)
-		c[10+i] = sinc(1.4*((i-15.0)/10.0))*hamming(i/30.0)/7.9;
-	for (i = 0; i < 31; i++)
-		for (j = 1; j < 10; j++)
-			c[i] += c[i+j];
-	fprintf(f, "static unsigned char hapn48_txfilt_pm10[] = {\n\t");
-	for (i = 0; i < 10; i++) {
-		for (j = 0; j < 16; j++) {
-			for (k = 1, s = 0, l = i; k < 16; k <<= 1, l += 10) {
-				if (j & k) 
-					s += c[l];
-				else 
-					s -= c[l];
-			}
-			if (s > max)
-				max = s;
-			if (s < min)
-				min = s;
-			fprintf(f, "%4d", (int)(128+127*s));
-			if (i < 9 || j < 15)
-				fprintf(f, ",%s", (j & 7) == 7 
-					? "\n\t" : "");
-		}
-	}
-	fprintf(stderr, "hapn4800: txfiltpm10: min = %f; max = %f\n", min, max);
-	fprintf(f, "\n};\n\n");
-	
-}
-
-/* -------------------------------------------------------------------- */
-
-#define AFSK24_SAMPLERATE  16000
-#define AFSK24_CORRLEN     14
-
-static void gentbl_afsk2400(FILE *f, float tcm3105clk)
-{
-	int i, sum, v;
-
-	fprintf(f, "\n/*\n * afsk2400 specific tables (tcm3105 clk %7fHz)\n */\n"
-		"#define AFSK24_TX_FREQ_LO %d\n"
-		"#define AFSK24_TX_FREQ_HI %d\n"
-		"#define AFSK24_BITPLL_INC %d\n"
-		"#define AFSK24_SAMPLERATE %d\n\n", tcm3105clk, 
-		(int)(tcm3105clk/3694.0), (int)(tcm3105clk/2015.0), 
-		0x10000*2400/AFSK24_SAMPLERATE, AFSK24_SAMPLERATE);
-
-#define ARGLO(x) 2.0*M_PI*(double)x*(tcm3105clk/3694.0)/(double)AFSK24_SAMPLERATE
-#define ARGHI(x) 2.0*M_PI*(double)x*(tcm3105clk/2015.0)/(double)AFSK24_SAMPLERATE
-#define WINDOW(x) hamming((float)(x)/(AFSK24_CORRLEN-1.0))
-
-	fprintf(f, "static const int afsk24_tx_lo_i[] = {\n\t");
-        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
-		sum += (v = 127.0*cos(ARGLO(i))*WINDOW(i));
-                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_I %d\n\n"
-		"static const int afsk24_tx_lo_q[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
-		sum += (v = 127.0*sin(ARGLO(i))*WINDOW(i)); 
-                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_LO_Q %d\n\n"
-		"static const int afsk24_tx_hi_i[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK24_CORRLEN; i++) {
-		sum += (v = 127.0*cos(ARGHI(i))*WINDOW(i)); 
-                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-        fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_I %d\n\n"
-		"static const int afsk24_tx_hi_q[] = {\n\t", sum);
-        for(sum = i = 0; i < AFSK24_CORRLEN; i++)  {
-		sum += (v = 127.0*sin(ARGHI(i))*WINDOW(i));
-                fprintf(f, " %4i%c", v, (i < AFSK24_CORRLEN-1) ? ',' : ' ');
-	}
-	fprintf(f, "\n};\n#define SUM_AFSK24_TX_HI_Q %d\n\n", sum);
-#undef ARGLO
-#undef ARGHI
-#undef WINDOW
-}
-
-/* -------------------------------------------------------------------- */
-
-static char *progname;
-
-static void gentbl_banner(FILE *f)
-{
-	fprintf(f, "/*\n * THIS FILE IS GENERATED AUTOMATICALLY BY %s, "
-		"DO NOT EDIT!\n */\n\n", progname);
-}
-
-/* -------------------------------------------------------------------- */
-
-void main(int argc, char *argv[])
-{
-	FILE *f;
-	
-	progname = argv[0];
-	if (!(f = fopen("sm_tbl_afsk1200.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_offscostab(f, 6);
-	gentbl_costab(f, 6);
-	gentbl_afsk1200(f);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_afsk2666.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_offscostab(f, 6);
-	gentbl_costab(f, 6);
-	gentbl_afsk2666(f);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_psk4800.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_psk4800(f);
-	gentbl_costab(f, 8);
-	gentbl_atantab(f);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_hapn4800.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_hapn4800(f);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_fsk9600.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_fsk9600(f);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_afsk2400_8.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_offscostab(f, 6);
-	gentbl_costab(f, 6);
-	gentbl_afsk2400(f, 8000000);
-	fclose(f);
-	if (!(f = fopen("sm_tbl_afsk2400_7.h", "w")))
-		exit(1);
-	gentbl_banner(f);
-	gentbl_offscostab(f, 6);
-	gentbl_costab(f, 6);
-	gentbl_afsk2400(f, 7372800);
-	fclose(f);
-	exit(0);
-}
-
-
-/* -------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm.c linux/drivers/net/soundmodem/sm.c
--- v2.1.70/linux/drivers/net/soundmodem/sm.c	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/soundmodem/sm.c	Wed Dec 31 16:00:00 1969
@@ -1,898 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm.c  -- soundcard radio modem driver.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  Command line options (insmod command line)
- *
- *  mode     mode string; eg. "wss:afsk1200"
- *  iobase   base address of the soundcard; common values are 0x220 for sbc,
- *           0x530 for wss
- *  irq      interrupt number; common values are 7 or 5 for sbc, 11 for wss
- *  dma      dma number; common values are 0 or 1
- *
- *
- *  History:
- *   0.1  21.09.96  Started
- *        18.10.96  Changed to new user space access routines (copy_{to,from}_user)
- *   0.4  21.01.97  Separately compileable soundcard/modem modules
- *   0.5  03.03.97  fixed LPT probing (check_lpt result was interpreted the wrong way round)
- *   0.6  16.04.97  init code/data tagged
- *   0.7  30.07.97  fixed halfduplex interrupt handlers/hotfix for CS423X
- */
-
-/*****************************************************************************/
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/ptrace.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/ioport.h>
-#include <linux/net.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/bitops.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include "sm.h"
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE >= 0x20123
-#include <linux/init.h>
-#else
-#define __init
-#define __initdata
-#define __initfunc(x) x
-#endif
-
-/* --------------------------------------------------------------------- */
-
-/*static*/ const char sm_drvname[] = "soundmodem";
-static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-1997 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.7 compiled " __TIME__ " " __DATE__ "\n";
-
-/* --------------------------------------------------------------------- */
-
-/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
-#ifdef CONFIG_SOUNDMODEM_AFSK1200
-	&sm_afsk1200_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
-	&sm_afsk2400_7_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
-	&sm_afsk2400_8_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2666
-	&sm_afsk2666_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
-#ifdef CONFIG_SOUNDMODEM_PSK4800
-	&sm_psk4800_tx,
-#endif /* CONFIG_SOUNDMODEM_PSK4800 */
-#ifdef CONFIG_SOUNDMODEM_HAPN4800
-	&sm_hapn4800_8_tx,
-	&sm_hapn4800_10_tx,
-	&sm_hapn4800_pm8_tx,
-	&sm_hapn4800_pm10_tx,
-#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
-#ifdef CONFIG_SOUNDMODEM_FSK9600
-	&sm_fsk9600_4_tx,
-	&sm_fsk9600_5_tx,
-#endif /* CONFIG_SOUNDMODEM_FSK9600 */
-	NULL
-};
-
-/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
-#ifdef CONFIG_SOUNDMODEM_AFSK1200
-	&sm_afsk1200_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
-	&sm_afsk2400_7_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
-	&sm_afsk2400_8_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2666
-	&sm_afsk2666_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
-#ifdef CONFIG_SOUNDMODEM_PSK4800
-	&sm_psk4800_rx,
-#endif /* CONFIG_SOUNDMODEM_PSK4800 */
-#ifdef CONFIG_SOUNDMODEM_HAPN4800
-	&sm_hapn4800_8_rx,
-	&sm_hapn4800_10_rx,
-	&sm_hapn4800_pm8_rx,
-	&sm_hapn4800_pm10_rx,
-#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
-#ifdef CONFIG_SOUNDMODEM_FSK9600
-	&sm_fsk9600_4_rx,
-	&sm_fsk9600_5_rx,
-#endif /* CONFIG_SOUNDMODEM_FSK9600 */
-	NULL
-};
-
-static const struct hardware_info *sm_hardware_table[] = {
-#ifdef CONFIG_SOUNDMODEM_SBC
-	&sm_hw_sbc,
-	&sm_hw_sbcfdx,
-#endif /* CONFIG_SOUNDMODEM_SBC */
-#ifdef CONFIG_SOUNDMODEM_WSS
-	&sm_hw_wss,
-	&sm_hw_wssfdx,
-#endif /* CONFIG_SOUNDMODEM_WSS */
-	NULL
-};
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-/* --------------------------------------------------------------------- */
-
-static struct device sm_device[NR_PORTS];
-
-static struct {
-	char *mode;
-	int iobase, irq, dma, dma2, seriobase, pariobase, midiiobase;
-} sm_ports[NR_PORTS] = {
-	{ NULL, -1, 0, 0, 0, -1, -1, -1 },
-};
-
-/* --------------------------------------------------------------------- */
-
-#define UART_RBR(iobase) (iobase+0)
-#define UART_THR(iobase) (iobase+0)
-#define UART_IER(iobase) (iobase+1)
-#define UART_IIR(iobase) (iobase+2)
-#define UART_FCR(iobase) (iobase+2)
-#define UART_LCR(iobase) (iobase+3)
-#define UART_MCR(iobase) (iobase+4)
-#define UART_LSR(iobase) (iobase+5)
-#define UART_MSR(iobase) (iobase+6)
-#define UART_SCR(iobase) (iobase+7)
-#define UART_DLL(iobase) (iobase+0)
-#define UART_DLM(iobase) (iobase+1)
-
-#define SER_EXTENT 8
-
-#define LPT_DATA(iobase)    (iobase+0)
-#define LPT_STATUS(iobase)  (iobase+1)
-#define LPT_CONTROL(iobase) (iobase+2)
-#define LPT_IRQ_ENABLE      0x10
-
-#define LPT_EXTENT 3
-
-#define MIDI_DATA(iobase)     (iobase)
-#define MIDI_STATUS(iobase)   (iobase+1)
-#define MIDI_READ_FULL 0x80   /* attention: negative logic!! */
-#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
-
-#define MIDI_EXTENT 2
-
-/* ---------------------------------------------------------------------- */
-
-#define PARAM_TXDELAY   1
-#define PARAM_PERSIST   2
-#define PARAM_SLOTTIME  3
-#define PARAM_TXTAIL    4
-#define PARAM_FULLDUP   5
-#define PARAM_HARDWARE  6
-#define PARAM_RETURN    255
-
-#define SP_SER  1
-#define SP_PAR  2
-#define SP_MIDI 4
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== port checking routines ========================
- */
-
-/*
- * returns 0 if ok and != 0 on error;
- * the same behaviour as par96_check_lpt in baycom.c
- */
-
-/*
- * returns 0 if ok and != 0 on error;
- * the same behaviour as par96_check_lpt in baycom.c
- */
-
-static int check_lpt(unsigned int iobase)
-{
-	unsigned char b1,b2;
-	int i;
-
-	if (iobase <= 0 || iobase > 0x1000-LPT_EXTENT)
-		return 0;
-	if (check_region(iobase, LPT_EXTENT))
-		return 0;
-	b1 = inb(LPT_DATA(iobase));
-	b2 = inb(LPT_CONTROL(iobase));
-	outb(0xaa, LPT_DATA(iobase));
-	i = inb(LPT_DATA(iobase)) == 0xaa;
-	outb(0x55, LPT_DATA(iobase));
-	i &= inb(LPT_DATA(iobase)) == 0x55;
-	outb(0x0a, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x0a;
-	outb(0x05, LPT_CONTROL(iobase));
-	i &= (inb(LPT_CONTROL(iobase)) & 0xf) == 0x05;
-	outb(b1, LPT_DATA(iobase));
-	outb(b2, LPT_CONTROL(iobase));
-	return !i;
-}
-
-/* --------------------------------------------------------------------- */
-
-enum uart { c_uart_unknown, c_uart_8250,
-	c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
-	{ "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart check_uart(unsigned int iobase)
-{
-	unsigned char b1,b2,b3;
-	enum uart u;
-	enum uart uart_tab[] =
-		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
-	if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
-		return c_uart_unknown;
-	if (check_region(iobase, SER_EXTENT))
-		return c_uart_unknown;
-	b1 = inb(UART_MCR(iobase));
-	outb(b1 | 0x10, UART_MCR(iobase));	/* loopback mode */
-	b2 = inb(UART_MSR(iobase));
-	outb(0x1a, UART_MCR(iobase));
-	b3 = inb(UART_MSR(iobase)) & 0xf0;
-	outb(b1, UART_MCR(iobase));	   /* restore old values */
-	outb(b2, UART_MSR(iobase));
-	if (b3 != 0x90)
-		return c_uart_unknown;
-	inb(UART_RBR(iobase));
-	inb(UART_RBR(iobase));
-	outb(0x01, UART_FCR(iobase));		/* enable FIFOs */
-	u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
-	if (u == c_uart_16450) {
-		outb(0x5a, UART_SCR(iobase));
-		b1 = inb(UART_SCR(iobase));
-		outb(0xa5, UART_SCR(iobase));
-		b2 = inb(UART_SCR(iobase));
-		if ((b1 != 0x5a) || (b2 != 0xa5))
-			u = c_uart_8250;
-	}
-	return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int check_midi(unsigned int iobase)
-{
-	unsigned long timeout;
-	unsigned long flags;
-	unsigned char b;
-
-	if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
-		return 0;
-	if (check_region(iobase, MIDI_EXTENT))
-		return 0;
-	timeout = jiffies + (HZ / 100);
-	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
-		if ((signed)(jiffies - timeout) > 0)
-			return 0;
-	save_flags(flags);
-	cli();
-	outb(0xff, MIDI_DATA(iobase));
-	b = inb(MIDI_STATUS(iobase));
-	restore_flags(flags);
-	if (!(b & MIDI_WRITE_EMPTY))
-		return 0;
-	while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
-		if ((signed)(jiffies - timeout) > 0)
-			return 0;
-	return 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-void sm_output_status(struct sm_state *sm)
-{
-	int invert_dcd = 0;
-	int invert_ptt = 0;
-
-	int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt;
-	int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
-
-	if (sm->hdrv.ptt_out.flags & SP_SER) {
-		outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
-		outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
-	}
-	if (sm->hdrv.ptt_out.flags & SP_PAR) {
-		outb(ptt | (dcd << 1), LPT_DATA(sm->hdrv.ptt_out.pariobase));
-	}
-	if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) {
-		outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sm_output_open(struct sm_state *sm)
-{
-	enum uart u = c_uart_unknown;
-
-	sm->hdrv.ptt_out.flags = 0;
-	if (sm->hdrv.ptt_out.seriobase > 0 &&
-	    sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
-	    ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
-		sm->hdrv.ptt_out.flags |= SP_SER;
-		request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
-		outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
-		/* 5 bits, 1 stop, no parity, no break, Div latch access */
-		outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
-		outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
-		outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
-		/* LCR and MCR set by output_status */
-	}
-	if (sm->hdrv.ptt_out.pariobase > 0 &&
-	    sm->hdrv.ptt_out.pariobase <= 0x1000-LPT_EXTENT &&
-	    !check_lpt(sm->hdrv.ptt_out.pariobase)) {
-		sm->hdrv.ptt_out.flags |= SP_PAR;
-		request_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT, "sm par ptt");
-	}
-	if (sm->hdrv.ptt_out.midiiobase > 0 &&
-	    sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
-	    check_midi(sm->hdrv.ptt_out.midiiobase)) {
-		sm->hdrv.ptt_out.flags |= SP_MIDI;
-		request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
-			       "sm midi ptt");
-	}
-	sm_output_status(sm);
-
-	printk(KERN_INFO "%s: ptt output:", sm_drvname);
-	if (sm->hdrv.ptt_out.flags & SP_SER)
-		printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
-		       uart_str[u]);
-	if (sm->hdrv.ptt_out.flags & SP_PAR)
-		printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
-	if (sm->hdrv.ptt_out.flags & SP_MIDI)
-		printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
-	if (!sm->hdrv.ptt_out.flags)
-		printk(" none");
-	printk("\n");
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sm_output_close(struct sm_state *sm)
-{
-	/* release regions used for PTT output */
-	sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
-	sm_output_status(sm);
-	if (sm->hdrv.ptt_out.flags & SP_SER)
-		release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
-       	if (sm->hdrv.ptt_out.flags & SP_PAR)
-		release_region(sm->hdrv.ptt_out.pariobase, LPT_EXTENT);
-       	if (sm->hdrv.ptt_out.flags & SP_MIDI)
-		release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
-	sm->hdrv.ptt_out.flags = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct device *dev);
-static int sm_close(struct device *dev);
-static int sm_ioctl(struct device *dev, struct ifreq *ifr,
-		    struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-static const struct hdlcdrv_ops sm_ops = {
-	sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct device *dev)
-{
-	struct sm_state *sm;
-	int err;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_open: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-	if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
-		return -ENODEV;
-	sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
-	err = sm->hwdrv->open(dev, sm);
-	if (err)
-		return err;
-	sm_output_open(sm);
-	MOD_INC_USE_COUNT;
-	printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
-	       sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
-	       sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_close(struct device *dev)
-{
-	struct sm_state *sm;
-	int err = -ENODEV;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_close: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-
-	if (sm->hwdrv && sm->hwdrv->close)
-		err = sm->hwdrv && sm->hwdrv->close(dev, sm);
-	sm_output_close(sm);
-	MOD_DEC_USE_COUNT;
-	printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
-	       sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
-	return err;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, ':');
-	const struct hardware_info **hwp = sm_hardware_table;
-
-	if (!cp)
-		cp = mode;
-	else {
-		*cp++ = '\0';
-		while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
-			hwp++;
-		if (!hwp || !*hwp || !(*hwp)->hw_name)
-			return -EINVAL;
-		if ((*hwp)->loc_storage > sizeof(sm->hw)) {
-			printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
-			       sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
-			return -EINVAL;
-		}
-		sm->hwdrv = *hwp;
-	}
-	if (!*cp)
-		return 0;
-	if (sm->hwdrv && sm->hwdrv->sethw)
-		return sm->hwdrv->sethw(dev, sm, cp);
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_ioctl(struct device *dev, struct ifreq *ifr,
-		    struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_state *sm;
-	struct sm_ioctl bi;
-	unsigned long flags;
-	unsigned int newdiagmode;
-	unsigned int newdiagflags;
-	char *cp;
-	const struct modem_tx_info **mtp = sm_modem_tx_table;
-	const struct modem_rx_info **mrp = sm_modem_rx_table;
-	const struct hardware_info **hwp = sm_hardware_table;
-
-	if (!dev || !dev->priv ||
-	    ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
-		printk(KERN_ERR "sm_ioctl: invalid device struct\n");
-		return -EINVAL;
-	}
-	sm = (struct sm_state *)dev->priv;
-
-	if (cmd != SIOCDEVPRIVATE) {
-		if (!sm->hwdrv || !sm->hwdrv->ioctl)
-			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
-		return -ENOIOCTLCMD;
-	}
-	switch (hi->cmd) {
-	default:
-		if (sm->hwdrv && sm->hwdrv->ioctl)
-			return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
-		return -ENOIOCTLCMD;
-
-	case HDLCDRVCTL_GETMODE:
-		cp = hi->data.modename;
-		if (sm->hwdrv && sm->hwdrv->hw_name)
-			cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
-		else
-			cp += sprintf(cp, "<unspec>:");
-		if (sm->mode_tx && sm->mode_tx->name)
-			cp += sprintf(cp, "%s", sm->mode_tx->name);
-		else
-			cp += sprintf(cp, "<unspec>");
-		if (!sm->mode_rx || !sm->mode_rx ||
-		    strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
-			if (sm->mode_rx && sm->mode_rx->name)
-				cp += sprintf(cp, ",%s", sm->mode_rx->name);
-			else
-				cp += sprintf(cp, ",<unspec>");
-		}
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
-			return -EFAULT;
-		return 0;
-
-	case HDLCDRVCTL_SETMODE:
-		if (!suser() || dev->start)
-			return -EACCES;
-		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
-		return sethw(dev, sm, hi->data.modename);
-
-	case HDLCDRVCTL_MODELIST:
-		cp = hi->data.modename;
-		while (*hwp) {
-			if ((*hwp)->hw_name)
-				cp += sprintf("%s:,", (*hwp)->hw_name);
-			hwp++;
-		}
-		while (*mtp) {
-			if ((*mtp)->name)
-				cp += sprintf(">%s,", (*mtp)->name);
-			mtp++;
-		}
-		while (*mrp) {
-			if ((*mrp)->name)
-				cp += sprintf("<%s,", (*mrp)->name);
-			mrp++;
-		}
-		cp[-1] = '\0';
-		if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
-			return -EFAULT;
-		return 0;
-
-#ifdef SM_DEBUG
-	case SMCTL_GETDEBUG:
-		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-			return -EFAULT;
-		bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
-		bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
-		bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
-		bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
-		sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
-			sm->debug_vals.dma_residue = 0;
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return 0;
-#endif /* SM_DEBUG */
-
-	case SMCTL_DIAGNOSE:
-		if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-			return -EFAULT;
-		newdiagmode = bi.data.diag.mode;
-		newdiagflags = bi.data.diag.flags;
-		if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
-			return -EINVAL;
-		bi.data.diag.mode = sm->diag.mode;
-		bi.data.diag.flags = sm->diag.flags;
-		bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
-		if (sm->diag.mode != newdiagmode) {
-			save_flags(flags);
-			cli();
-			sm->diag.ptr = -1;
-			sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
-			sm->diag.mode = newdiagmode;
-			restore_flags(flags);
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (bi.data.diag.datalen > DIAGDATALEN)
-			bi.data.diag.datalen = DIAGDATALEN;
-		if (sm->diag.ptr < bi.data.diag.datalen) {
-			if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-				return -EFAULT;
-			return 0;
-		}
-		if (copy_to_user(bi.data.diag.data, sm->diag.data,
-				 bi.data.diag.datalen * sizeof(short)))
-			return -EFAULT;
-		bi.data.diag.flags |= SM_DIAGFLAG_VALID;
-		save_flags(flags);
-		cli();
-		sm->diag.ptr = -1;
-		sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
-		sm->diag.mode = newdiagmode;
-		restore_flags(flags);
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return 0;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef __i386__
-
-int sm_x86_capability = 0;
-
-__initfunc(static void i386_capability(void))
-{
-	unsigned long flags;
-	unsigned long fl1;
-	union {
-		struct {
-			unsigned int ebx, edx, ecx;
-		} r;
-		unsigned char s[13];
-	} id;
-	unsigned int eax;
-
-	save_flags(flags);
-	flags |= 0x200000;
-	restore_flags(flags);
-	save_flags(flags);
-	fl1 = flags;
-	flags &= ~0x200000;
-	restore_flags(flags);
-	save_flags(flags);
-	if (!(fl1 & 0x200000) || (flags & 0x200000)) {
-		printk(KERN_WARNING "%s: cpu does not support CPUID\n", sm_drvname);
-		return;
-	}
-	__asm__ ("cpuid" : "=a" (eax), "=b" (id.r.ebx), "=c" (id.r.ecx), "=d" (id.r.edx) :
-		 "0" (0));
-	id.s[12] = 0;
-	if (eax < 1) {
-		printk(KERN_WARNING "%s: cpu (vendor string %s) does not support capability "
-		       "list\n", sm_drvname, id.s);
-		return;
-	}
-	printk(KERN_INFO "%s: cpu: vendor string %s ", sm_drvname, id.s);
-	__asm__ ("cpuid" : "=a" (eax), "=d" (sm_x86_capability) : "0" (1) : "ebx", "ecx");
-	printk("fam %d mdl %d step %d cap 0x%x\n", (eax >> 8) & 15, (eax >> 4) & 15,
-	       eax & 15, sm_x86_capability);
-}	
-#endif /* __i386__ */	
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-__initfunc(static int sm_init(void))
-#else /* MODULE */
-__initfunc(int sm_init(void))
-#endif /* MODULE */
-{
-	int i, j, found = 0;
-	char set_hw = 1;
-	struct sm_state *sm;
-	char ifname[HDLCDRV_IFNAMELEN];
-
-	printk(sm_drvinfo);
-#ifdef __i386__
-	i386_capability();
-#endif /* __i386__ */	
-	/*
-	 * register net devices
-	 */
-	for (i = 0; i < NR_PORTS; i++) {
-		struct device *dev = sm_device+i;
-		sprintf(ifname, "sm%d", i);
-
-		if (!sm_ports[i].mode)
-			set_hw = 0;
-		if (!set_hw)
-			sm_ports[i].iobase = sm_ports[i].irq = 0;
-		j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state),
-					     ifname, sm_ports[i].iobase,
-					     sm_ports[i].irq, sm_ports[i].dma);
-		if (!j) {
-			sm = (struct sm_state *)dev->priv;
-			sm->hdrv.ptt_out.dma2 = sm_ports[i].dma2;
-			sm->hdrv.ptt_out.seriobase = sm_ports[i].seriobase;
-			sm->hdrv.ptt_out.pariobase = sm_ports[i].pariobase;
-			sm->hdrv.ptt_out.midiiobase = sm_ports[i].midiiobase;
-			if (set_hw && sethw(dev, sm, sm_ports[i].mode))
-				set_hw = 0;
-			found++;
-		} else {
-			printk(KERN_WARNING "%s: cannot register net device\n",
-			       sm_drvname);
-		}
-	}
-	if (!found)
-		return -ENXIO;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#ifdef MODULE
-
-/*
- * command line settable parameters
- */
-static char *mode = NULL;
-static int iobase = -1;
-static int irq = -1;
-static int dma = -1;
-static int dma2 = -1;
-static int serio = 0;
-static int pario = 0;
-static int midiio = 0;
-
-#if LINUX_VERSION_CODE >= 0x20115
-
-MODULE_PARM(mode, "s");
-MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
-MODULE_PARM(iobase, "i");
-MODULE_PARM_DESC(iobase, "soundmodem base address");
-MODULE_PARM(irq, "i");
-MODULE_PARM_DESC(irq, "soundmodem interrupt");
-MODULE_PARM(dma, "i");
-MODULE_PARM_DESC(dma, "soundmodem dma channel");
-MODULE_PARM(dma2, "i");
-MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
-MODULE_PARM(serio, "i");
-MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
-MODULE_PARM(pario, "i");
-MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
-MODULE_PARM(midiio, "i");
-MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
-
-#endif
-
-__initfunc(int init_module(void))
-{
-	if (mode) {
-		if (iobase == -1)
-			iobase = (!strncmp(mode, "sbc", 3)) ? 0x220 : 0x530;
-		if (irq == -1)
-			irq = (!strncmp(mode, "sbc", 3)) ? 5 : 11;
-		if (dma == -1)
-			dma = 1;
-	}
-	sm_ports[0].mode = mode;
-	sm_ports[0].iobase = iobase;
-	sm_ports[0].irq = irq;
-	sm_ports[0].dma = dma;
-	sm_ports[0].dma2 = dma2;
-	sm_ports[0].seriobase = serio;
-	sm_ports[0].pariobase = pario;
-	sm_ports[0].midiiobase = midiio;
-	sm_ports[1].mode = NULL;
-
-	return sm_init();
-}
-
-/* --------------------------------------------------------------------- */
-
-void cleanup_module(void)
-{
-	int i;
-
-	printk(KERN_INFO "sm: cleanup_module called\n");
-
-	for(i = 0; i < NR_PORTS; i++) {
-		struct device *dev = sm_device+i;
-		struct sm_state *sm = (struct sm_state *)dev->priv;
-
-		if (sm) {
-			if (sm->hdrv.magic != HDLCDRV_MAGIC)
-				printk(KERN_ERR "sm: invalid magic in "
-				       "cleanup_module\n");
-			else
-				hdlcdrv_unregister_hdlcdrv(dev);
-		}
-	}
-}
-
-#else /* MODULE */
-/* --------------------------------------------------------------------- */
-/*
- * format: sm=io,irq,dma[,dma2[,serio[,pario]]],mode
- * mode: hw:modem
- * hw: sbc, wss, wssfdx
- * modem: afsk1200, fsk9600
- */
-
-__initfunc(void sm_setup(char *str, int *ints))
-{
-	int i;
-
-	for (i = 0; (i < NR_PORTS) && (sm_ports[i].mode); i++);
-	if ((i >= NR_PORTS) || (ints[0] < 3)) {
-		printk(KERN_INFO "%s: too many or invalid interface "
-		       "specifications\n", sm_drvname);
-		return;
-	}
-	sm_ports[i].mode = str;
-	sm_ports[i].iobase = ints[1];
-	sm_ports[i].irq = ints[2];
-	sm_ports[i].dma = ints[3];
-	sm_ports[i].dma2 = (ints[0] >= 4) ? ints[4] : 0;
-	sm_ports[i].seriobase = (ints[0] >= 5) ? ints[5] : 0;
-	sm_ports[i].pariobase = (ints[0] >= 6) ? ints[6] : 0;
-	sm_ports[i].midiiobase = (ints[0] >= 7) ? ints[7] : 0;
-	if (i < NR_PORTS-1)
-		sm_ports[i+1].mode = NULL;
-}
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm.h linux/drivers/net/soundmodem/sm.h
--- v2.1.70/linux/drivers/net/soundmodem/sm.h	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm.h	Wed Dec 31 16:00:00 1969
@@ -1,382 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm.h  --  soundcard radio modem driver internal header.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#ifndef _SM_H
-#define _SM_H
-
-/* ---------------------------------------------------------------------- */
-
-#include <linux/hdlcdrv.h>
-#include <linux/soundmodem.h>
-
-#define SM_DEBUG
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct sm_state {
-	struct hdlcdrv_state hdrv;
-
-	const struct modem_tx_info *mode_tx;
-	const struct modem_rx_info *mode_rx;
-
-	const struct hardware_info *hwdrv;
-
-	/*
-	 * Hardware (soundcard) access routines state
-	 */
-	struct {
-		void *ibuf;
-		unsigned int ifragsz;
-		unsigned int ifragptr;
-		unsigned int i16bit;
-		void *obuf;
-		unsigned int ofragsz;
-		unsigned int ofragptr;
-		unsigned int o16bit;
-		int ptt_cnt;
-	} dma;
-
-	union {
-		long hw[32/sizeof(long)];
-	} hw;
-
-	/*
-	 * state of the modem code
-	 */
-	union {
-		long m[32/sizeof(long)];
-	} m;
-	union {
-		long d[256/sizeof(long)];
-	} d;
-
-#define DIAGDATALEN 64
-	struct diag_data {
-		unsigned int mode;
-		unsigned int flags;
-		volatile int ptr;
-		short data[DIAGDATALEN];
-	} diag;
-
-
-#ifdef SM_DEBUG
-	struct debug_vals {
-		unsigned long last_jiffies;
-		unsigned cur_intcnt;
-		unsigned last_intcnt;
-		unsigned mod_cyc;
-		unsigned demod_cyc;
-		unsigned dma_residue;
-	} debug_vals;
-#endif /* SM_DEBUG */
-};
-
-/* ---------------------------------------------------------------------- */
-/*
- * Mode definition structure
- */
-
-struct modem_tx_info {
-	const char *name;
-	unsigned int loc_storage;
-	int srate;
-	int bitrate;
-        void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
-        void (*modulator_s16)(struct sm_state *, short *, unsigned int);
-        void (*init)(struct sm_state *);
-};
-
-struct modem_rx_info {
-	const char *name;
-	unsigned int loc_storage;
-	int srate;
-	int bitrate;
-	unsigned int overlap;
-	unsigned int sperbit;
-        void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
-        void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
-        void (*init)(struct sm_state *);
-};
-
-/* ---------------------------------------------------------------------- */
-/*
- * Soundcard driver definition structure
- */
-
-struct hardware_info {
-	char *hw_name; /* used for request_{region,irq,dma} */
-	unsigned int loc_storage;
-	/*
-	 * mode specific open/close
-	 */
-	int (*open)(struct device *, struct sm_state *);
-	int (*close)(struct device *, struct sm_state *);
-	int (*ioctl)(struct device *, struct sm_state *, struct ifreq *,
-		     struct hdlcdrv_ioctl *, int);
-	int (*sethw)(struct device *, struct sm_state *, char *);
-};
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-extern const char sm_drvname[];
-extern const char sm_drvinfo[];
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== diagnostics stuff ===============================
- */
-
-extern inline void diag_trigger(struct sm_state *sm)
-{
-	if (sm->diag.ptr < 0)
-		if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
-			sm->diag.ptr = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
-#define SHRT_MIN (-SHRT_MAX-1)
-
-extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
-{
-	int val;
-
-	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
-	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
-	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
-		return;
-	val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
-	/* clip */
-	if (val > SHRT_MAX)
-		val = SHRT_MAX;
-	if (val < SHRT_MIN)
-		val = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-extern inline void diag_add_one(struct sm_state *sm, int val)
-{
-	if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
-	     sm->diag.mode != SM_DIAGMODE_DEMOD) ||
-	    sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
-		return;
-	/* clip */
-	if (val > SHRT_MAX)
-		val = SHRT_MAX;
-	if (val < SHRT_MIN)
-		val = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
-{
-	if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
-	    sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
-		return;
-	/* clip */
-	if (vali > SHRT_MAX)
-		vali = SHRT_MAX;
-	if (vali < SHRT_MIN)
-		vali = SHRT_MIN;
-	if (valq > SHRT_MAX)
-		valq = SHRT_MAX;
-	if (valq < SHRT_MIN)
-		valq = SHRT_MIN;
-	sm->diag.data[sm->diag.ptr++] = vali;
-	sm->diag.data[sm->diag.ptr++] = valq;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== utility functions ===============================
- */
-
-extern inline unsigned int hweight32(unsigned int w)
-	__attribute__ ((unused));
-extern inline unsigned int hweight16(unsigned short w)
-	__attribute__ ((unused));
-extern inline unsigned int hweight8(unsigned char w)
-        __attribute__ ((unused));
-
-extern inline unsigned int hweight32(unsigned int w)
-{
-        unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
-        res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
-        res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
-        res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
-        return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
-extern inline unsigned int hweight16(unsigned short w)
-{
-        unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
-        res = (res & 0x3333) + ((res >> 2) & 0x3333);
-        res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
-        return (res & 0x00FF) + ((res >> 8) & 0x00FF);
-}
-
-extern inline unsigned int hweight8(unsigned char w)
-{
-        unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
-        res = (res & 0x33) + ((res >> 2) & 0x33);
-        return (res & 0x0F) + ((res >> 4) & 0x0F);
-}
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
-	__attribute__ ((unused));
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
-	__attribute__ ((unused));
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
-{
-	for (;;) {
-		if (!x)
-			return y;
-		if (!y)
-			return x;
-		if (x > y)
-			x %= y;
-		else
-			y %= x;
-	}
-}
-
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
-{
-	return x * y / gcd(x, y);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== profiling =======================================
- */
-
-
-#ifdef __i386__
-
-extern int sm_x86_capability;
-
-#define HAS_RDTSC (sm_x86_capability & 0x10)
-
-/*
- * only do 32bit cycle counter arithmetic; we hope we won't overflow :-)
- * in fact, overflowing modems would require over 2THz clock speeds :-)
- */
-
-#define time_exec(var,cmd)                                              \
-({                                                                      \
-	if (HAS_RDTSC) {                                                \
-		unsigned int cnt1, cnt2, cnt3;                          \
-		__asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3));  \
-		cmd;                                                    \
-		__asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3));  \
-		var = cnt2-cnt1;                                        \
-	} else {                                                        \
-		cmd;                                                    \
-	}                                                               \
-})
-
-#else /* __i386__ */
-
-#define time_exec(var,cmd) cmd
-
-#endif /* __i386__ */
-
-/* --------------------------------------------------------------------- */
-
-extern const struct modem_tx_info sm_afsk1200_tx;
-extern const struct modem_tx_info sm_afsk2400_7_tx;
-extern const struct modem_tx_info sm_afsk2400_8_tx;
-extern const struct modem_tx_info sm_afsk2666_tx;
-extern const struct modem_tx_info sm_psk4800_tx;
-extern const struct modem_tx_info sm_hapn4800_8_tx;
-extern const struct modem_tx_info sm_hapn4800_10_tx;
-extern const struct modem_tx_info sm_hapn4800_pm8_tx;
-extern const struct modem_tx_info sm_hapn4800_pm10_tx;
-extern const struct modem_tx_info sm_fsk9600_4_tx;
-extern const struct modem_tx_info sm_fsk9600_5_tx;
-
-extern const struct modem_rx_info sm_afsk1200_rx;
-extern const struct modem_rx_info sm_afsk2400_7_rx;
-extern const struct modem_rx_info sm_afsk2400_8_rx;
-extern const struct modem_rx_info sm_afsk2666_rx;
-extern const struct modem_rx_info sm_psk4800_rx;
-extern const struct modem_rx_info sm_hapn4800_8_rx;
-extern const struct modem_rx_info sm_hapn4800_10_rx;
-extern const struct modem_rx_info sm_hapn4800_pm8_rx;
-extern const struct modem_rx_info sm_hapn4800_pm10_rx;
-extern const struct modem_rx_info sm_fsk9600_4_rx;
-extern const struct modem_rx_info sm_fsk9600_5_rx;
-
-extern const struct hardware_info sm_hw_sbc;
-extern const struct hardware_info sm_hw_sbcfdx;
-extern const struct hardware_info sm_hw_wss;
-extern const struct hardware_info sm_hw_wssfdx;
-
-extern const struct modem_tx_info *sm_modem_tx_table[];
-extern const struct modem_rx_info *sm_modem_rx_table[];
-extern const struct hardware_info *sm_hardware_table[];
-
-/* --------------------------------------------------------------------- */
-
-void sm_output_status(struct sm_state *sm);
-/*void sm_output_open(struct sm_state *sm);*/
-/*void sm_output_close(struct sm_state *sm);*/
-
-/* --------------------------------------------------------------------- */
-
-extern void inline sm_int_freq(struct sm_state *sm)
-{
-#ifdef SM_DEBUG
-	unsigned long cur_jiffies = jiffies;
-	/*
-	 * measure the interrupt frequency
-	 */
-	sm->debug_vals.cur_intcnt++;
-	if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
-		sm->debug_vals.last_jiffies = cur_jiffies;
-		sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
-		sm->debug_vals.cur_intcnt = 0;
-	}
-#endif /* SM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-#endif /* _SM_H */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_afsk1200.c linux/drivers/net/soundmodem/sm_afsk1200.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_afsk1200.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm_afsk1200.c	Wed Dec 31 16:00:00 1969
@@ -1,272 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_afsk1200.c  -- soundcard radio modem driver, 1200 baud AFSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk1200.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk12 {
-	unsigned int shreg;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-};
-
-struct mod_state_afsk12 {
-	unsigned int shreg;
-	unsigned char tx_bit;
-	unsigned int bit_pll;
-	unsigned int dds_inc;
-	unsigned int txphase;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = { 
-	AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
-	AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE 
-};
-
-static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, 
-			      unsigned int buflen)
-{
-	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!((st->txphase++) & 7)) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-		}
-		st->dds_inc = dds_inc[st->tx_bit & 1];
-		*buf++ = OFFSCOS(st->bit_pll);
-		st->bit_pll += st->dds_inc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!((st->txphase++) & 7)) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-		}
-		st->dds_inc = dds_inc[st->tx_bit & 1];
-		*buf++ = COS(st->bit_pll);
-		st->bit_pll += st->dds_inc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
-{
-	int sum = -0x80 * csum;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-
-	sum >>= 7;
-	return sum * sum;
-}
-
-extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
-{
-	int sum = 0;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-
-	sum >>= 15;
-	return sum * sum;
-}
-
-extern __inline__ int do_filter_1200_u8(const unsigned char *buf)
-{
-	int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
-	sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
-	sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
-	sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
-	return sum;
-}
-
-extern __inline__ int do_filter_1200_s16(const short *buf)
-{
-	int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
-	sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
-	sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
-	sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
-	return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static const int pll_corr[2] = { -0x1000, 0x1000 };
-
-static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_1200_u8(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x2000;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr
-				[st->bit_pll < 0x9000];
-			j = 4 * hweight8(st->dcd_shreg & 0x38)
-				- hweight16(st->dcd_shreg & 0x7c0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_1200_s16(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x2000;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr
-				[st->bit_pll < 0x9000];
-			j = 4 * hweight8(st->dcd_shreg & 0x38)
-				- hweight16(st->dcd_shreg & 0x7c0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, *buf, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_1200(struct sm_state *sm)
-{
-	struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
-
-       	st->dcd_time = 120;
-	st->dcd_sum0 = 2;	
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk1200_tx = {
-	"afsk1200", sizeof(struct mod_state_afsk12), 
-	AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk1200_rx = {
-	"afsk1200", sizeof(struct demod_state_afsk12), 
-	AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, 
-	demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_afsk2400_7.c linux/drivers/net/soundmodem/sm_afsk2400_7.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_afsk2400_7.c	Thu Sep  4 17:07:30 1997
+++ linux/drivers/net/soundmodem/sm_afsk2400_7.c	Wed Dec 31 16:00:00 1969
@@ -1,296 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_afsk2400_7.c  -- soundcard radio modem driver, 2400 baud AFSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-/*
- * This driver is intended to be compatible with TCM3105 modems
- * overclocked to 7.3728MHz. The mark and space frequencies therefore
- * lie at 3658 and 1996 Hz.
- * Note that I do _not_ recommend the building of such links, I provide
- * this only for the users who live in the coverage area of such
- * a "legacy" link.
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk2400_7.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk24 {
-	unsigned int shreg;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-};
-
-struct mod_state_afsk24 {
-	unsigned int shreg;
-	unsigned char tx_bit;
-	unsigned int bit_pll;
-	unsigned int tx_seq;
-	unsigned int phinc;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
-				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
-
-static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (st->tx_seq < 0x5555) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-			st->phinc = dds_inc[st->tx_bit & 1];
-		}
-		st->tx_seq += 0x5555;
-		st->tx_seq &= 0xffff;
-		*buf = OFFSCOS(st->bit_pll);
-		st->bit_pll += st->phinc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (st->tx_seq < 0x5555) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-			st->phinc = dds_inc[st->tx_bit & 1];
-		}
-		st->tx_seq += 0x5555;
-		st->tx_seq &= 0xffff;
-		*buf = COS(st->bit_pll);
-		st->bit_pll += st->phinc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
-{
-	int sum = -0x80 * csum;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-	sum += (st[-8] * coeff[8]);
-	sum += (st[-9] * coeff[9]);
-	sum += (st[-10] * coeff[10]);
-	sum += (st[-11] * coeff[11]);
-	sum += (st[-12] * coeff[12]);
-	sum += (st[-13] * coeff[13]);
-
-	sum >>= 7;
-	return sum * sum;
-}
-
-extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
-{
-	int sum = 0;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-	sum += (st[-8] * coeff[8]);
-	sum += (st[-9] * coeff[9]);
-	sum += (st[-10] * coeff[10]);
-	sum += (st[-11] * coeff[11]);
-	sum += (st[-12] * coeff[12]);
-	sum += (st[-13] * coeff[13]);
-
-	sum >>= 15;
-	return sum * sum;
-}
-
-extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
-{
-	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
-	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
-	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
-	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
-	return sum;
-}
-
-extern __inline__ int do_filter_2400_s16(const short *buf)
-{
-	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
-	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
-	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
-	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
-	return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_2400_u8(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += AFSK24_BITPLL_INC;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
-				st->bit_pll += AFSK24_BITPLL_INC/2;
-			else
-				st->bit_pll -= AFSK24_BITPLL_INC/2;
-			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
-				- hweight16(st->dcd_shreg & 0x1e0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_2400_s16(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += AFSK24_BITPLL_INC;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
-				st->bit_pll += AFSK24_BITPLL_INC/2;
-			else
-				st->bit_pll -= AFSK24_BITPLL_INC/2;
-			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
-				- hweight16(st->dcd_shreg & 0x1e0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, *buf, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_2400(struct sm_state *sm)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-
-       	st->dcd_time = 120;
-	st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk2400_7_tx = {
-	"afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
-	modulator_2400_u8, modulator_2400_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk2400_7_rx = {
-	"afsk2400_7", sizeof(struct demod_state_afsk24), 
-	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
-	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_afsk2400_8.c linux/drivers/net/soundmodem/sm_afsk2400_8.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_afsk2400_8.c	Thu Sep  4 17:07:31 1997
+++ linux/drivers/net/soundmodem/sm_afsk2400_8.c	Wed Dec 31 16:00:00 1969
@@ -1,296 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_afsk2400_8.c  -- soundcard radio modem driver, 2400 baud AFSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-/*
- * This driver is intended to be compatible with TCM3105 modems
- * overclocked to 8MHz. The mark and space frequencies therefore
- * lie at 3970 and 2165 Hz.
- * Note that I do _not_ recommend the building of such links, I provide
- * this only for the users who live in the coverage area of such
- * a "legacy" link.
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk2400_8.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk24 {
-	unsigned int shreg;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	unsigned char last_rxbit;
-};
-
-struct mod_state_afsk24 {
-	unsigned int shreg;
-	unsigned char tx_bit;
-	unsigned int bit_pll;
-	unsigned int tx_seq;
-	unsigned int phinc;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
-				AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
-
-static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (st->tx_seq < 0x5555) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-			st->phinc = dds_inc[st->tx_bit & 1];
-		}
-		st->tx_seq += 0x5555;
-		st->tx_seq &= 0xffff;
-		*buf = OFFSCOS(st->bit_pll);
-		st->bit_pll += st->phinc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (st->tx_seq < 0x5555) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
-			st->shreg >>= 1;
-			st->phinc = dds_inc[st->tx_bit & 1];
-		}
-		st->tx_seq += 0x5555;
-		st->tx_seq &= 0xffff;
-		*buf = COS(st->bit_pll);
-		st->bit_pll += st->phinc;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
-{
-	int sum = -0x80 * csum;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-	sum += (st[-8] * coeff[8]);
-	sum += (st[-9] * coeff[9]);
-	sum += (st[-10] * coeff[10]);
-	sum += (st[-11] * coeff[11]);
-	sum += (st[-12] * coeff[12]);
-	sum += (st[-13] * coeff[13]);
-
-	sum >>= 7;
-	return sum * sum;
-}
-
-extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
-{
-	int sum = 0;
-	
-	sum += (st[0] * coeff[0]);
-	sum += (st[-1] * coeff[1]);
-	sum += (st[-2] * coeff[2]);
-	sum += (st[-3] * coeff[3]);
-	sum += (st[-4] * coeff[4]);
-	sum += (st[-5] * coeff[5]);
-	sum += (st[-6] * coeff[6]);
-	sum += (st[-7] * coeff[7]);
-	sum += (st[-8] * coeff[8]);
-	sum += (st[-9] * coeff[9]);
-	sum += (st[-10] * coeff[10]);
-	sum += (st[-11] * coeff[11]);
-	sum += (st[-12] * coeff[12]);
-	sum += (st[-13] * coeff[13]);
-
-	sum >>= 15;
-	return sum * sum;
-}
-
-extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
-{
-	int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
-	sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
-	sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
-	sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
-	return sum;
-}
-
-extern __inline__ int do_filter_2400_s16(const short *buf)
-{
-	int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
-	sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
-	sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
-	sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
-	return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_2400_u8(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += AFSK24_BITPLL_INC;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
-				st->bit_pll += AFSK24_BITPLL_INC/2;
-			else
-				st->bit_pll -= AFSK24_BITPLL_INC/2;
-			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
-				- hweight16(st->dcd_shreg & 0x1e0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, (((int)*buf)-0x80) << 8, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-	int j;
-	int sum;
-	unsigned char newsample;
-
-	for (; buflen > 0; buflen--, buf++) {
-		sum = do_filter_2400_s16(buf);
-		st->dcd_shreg <<= 1;
-		st->bit_pll += AFSK24_BITPLL_INC;
-		newsample = (sum > 0);
-		if (st->last_sample ^ newsample) {
-			st->last_sample = newsample;
-			st->dcd_shreg |= 1;
-			if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
-				st->bit_pll += AFSK24_BITPLL_INC/2;
-			else
-				st->bit_pll -= AFSK24_BITPLL_INC/2;
-			j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
-				- hweight16(st->dcd_shreg & 0x1e0);
-			st->dcd_sum0 += j;
-		}
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 120;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->shreg >>= 1;
-			st->shreg |= (!(st->last_rxbit ^
-					st->last_sample)) << 16;
-			st->last_rxbit = st->last_sample;
-			diag_trigger(sm);
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-		}
-		diag_add(sm, *buf, sum);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_2400(struct sm_state *sm)
-{
-	struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-
-       	st->dcd_time = 120;
-	st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk2400_8_tx = {
-	"afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, 
-	modulator_2400_u8, modulator_2400_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk2400_8_rx = {
-	"afsk2400_8", sizeof(struct demod_state_afsk24), 
-	AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, 
-	demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_fsk9600.c linux/drivers/net/soundmodem/sm_fsk9600.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_fsk9600.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm_fsk9600.c	Wed Dec 31 16:00:00 1969
@@ -1,391 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_fsk9600.c  --  soundcard radio modem driver, 
- *                        9600 baud G3RUH compatible FSK modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_fsk9600.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_fsk96 {
-	unsigned int shreg;
-	unsigned long descram;
-	unsigned int bit_pll;
-	unsigned char last_sample;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-};
-
-struct mod_state_fsk96 {
-	unsigned int shreg;
-	unsigned long scram;
-	unsigned char tx_bit;
-	unsigned char *txtbl;
-	unsigned int txphase;
-};
-
-/* --------------------------------------------------------------------- */
-
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!st->txphase++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->scram = (st->scram << 1) | (st->scram & 1);
-			st->scram ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			if (st->scram & (SCRAM_TAP1 << 1))
-				st->scram ^= SCRAM_TAPN << 1;
-			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
-			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
-		}
-		if (st->txphase >= 4)
-			st->txphase = 0;
-		*buf++ = *st->txtbl;
-		st->txtbl += 0x100;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!st->txphase++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->scram = (st->scram << 1) | (st->scram & 1);
-			st->scram ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			if (st->scram & (SCRAM_TAP1 << 1))
-				st->scram ^= SCRAM_TAPN << 1;
-			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
-			st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
-		}
-		if (st->txphase >= 4)
-			st->txphase = 0;
-		*buf++ = ((*st->txtbl)-0x80) << 8;
-		st->txtbl += 0x100;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x4000;
-		curbit = (*buf >= 0x80);
-		if (st->last_sample ^ curbit) {
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
-			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
-				!!(st->dcd_shreg & 0x10);
-		}
-		st->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->descram = (st->descram << 1) | curbit;
-			descx = st->descram ^ (st->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			st->shreg >>= 1;
-			st->shreg |= (!(descx & 1)) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x4000;
-		curbit = (*buf >= 0);
-		if (st->last_sample ^ curbit) {
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr[st->bit_pll < 0xa000];
-			st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - 
-				!!(st->dcd_shreg & 0x10);
-		}
-		st->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->descram = (st->descram << 1) | curbit;
-			descx = st->descram ^ (st->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			st->shreg >>= 1;
-			st->shreg |= (!(descx & 1)) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, *buf);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!st->txphase++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->scram = (st->scram << 1) | (st->scram & 1);
-			st->scram ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			if (st->scram & (SCRAM_TAP1 << 1))
-				st->scram ^= SCRAM_TAPN << 1;
-			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
-			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
-		}
-		if (st->txphase >= 5)
-			st->txphase = 0;
-		*buf++ = *st->txtbl;
-		st->txtbl += 0x100;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
-	for (; buflen > 0; buflen--) {
-		if (!st->txphase++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->scram = (st->scram << 1) | (st->scram & 1);
-			st->scram ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			if (st->scram & (SCRAM_TAP1 << 1))
-				st->scram ^= SCRAM_TAPN << 1;
-			st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
-			st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
-		}
-		if (st->txphase >= 5)
-			st->txphase = 0;
-		*buf++ = ((*st->txtbl)-0x80)<<8;
-		st->txtbl += 0x100;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x3333;
-		curbit = (*buf >= 0x80);
-		if (st->last_sample ^ curbit) {
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
-			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
-				hweight8(st->dcd_shreg & 0x70);
-		}
-		st->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->descram = (st->descram << 1) | curbit;
-			descx = st->descram ^ (st->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			st->shreg >>= 1;
-			st->shreg |= (!(descx & 1)) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-	static const int pll_corr[2] = { -0x1000, 0x1000 };
-	unsigned char curbit;
-	unsigned int descx;
-
-	for (; buflen > 0; buflen--, buf++) {
-		st->dcd_shreg <<= 1;
-		st->bit_pll += 0x3333;
-		curbit = (*buf >= 0);
-		if (st->last_sample ^ curbit) {
-			st->dcd_shreg |= 1;
-			st->bit_pll += pll_corr[st->bit_pll < 0x9999];
-			st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - 
-				hweight8(st->dcd_shreg & 0x70);
-		}
-		st->last_sample = curbit;
-		hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->descram = (st->descram << 1) | curbit;
-			descx = st->descram ^ (st->descram >> 1);
-			descx ^= ((descx >> DESCRAM_TAPSH1) ^
-				  (descx >> DESCRAM_TAPSH2));
-			st->shreg >>= 1;
-			st->shreg |= (!(descx & 1)) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, *buf);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_9600(struct sm_state *sm)
-{
-	struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-
-	st->dcd_time = 240;
-	st->dcd_sum0 = 2;	
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_fsk9600_4_tx = {
-	"fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
-	modulator_9600_4_u8, modulator_9600_4_s16, NULL
-};
-
-const struct modem_rx_info sm_fsk9600_4_rx = {
-	"fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
-	demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_fsk9600_5_tx = {
-	"fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
-	modulator_9600_5_u8, modulator_9600_5_s16, NULL
-};
-
-const struct modem_rx_info sm_fsk9600_5_rx = {
-	"fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, 
-	demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_hapn4800.c linux/drivers/net/soundmodem/sm_hapn4800.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_hapn4800.c	Mon Jun 16 16:35:56 1997
+++ linux/drivers/net/soundmodem/sm_hapn4800.c	Wed Dec 31 16:00:00 1969
@@ -1,560 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_hapn4800.c  -- soundcard radio modem driver, 4800 baud HAPN modem
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- *
- *  This module implements a (hopefully) HAPN (Hamilton Area Packet
- *  Network) compatible 4800 baud modem.
- *  The HAPN modem uses kind of "duobinary signalling" (not really,
- *  duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
- *  instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
- *  Proakis, Digital Communications).
- *  The code is untested. It is compatible with itself (i.e. it can decode
- *  the packets it sent), but I could not test if it is compatible with
- *  any "real" HAPN modem, since noone uses it in my region of the world.
- *  Feedback therefore welcome.
- */
-
-#include "sm.h"
-#include "sm_tbl_hapn4800.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_hapn48 {
-	unsigned int shreg;
-	unsigned int bit_pll;
-	unsigned char last_bit;
-	unsigned char last_bit2;
-	unsigned int dcd_shreg;
-	int dcd_sum0, dcd_sum1, dcd_sum2;
-	unsigned int dcd_time;
-	int lvlhi, lvllo;
-};
-
-struct mod_state_hapn48 {
-	unsigned int shreg;
-	unsigned char tx_bit;
-	unsigned int tx_seq;
-	const unsigned char *tbl;
-};
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) { 
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = ((st->tx_bit << 1) |
-				      (st->tx_bit & 1));
-			st->tx_bit ^= (!(st->shreg & 1));
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 10)
-			st->tx_seq = 0;
-		*buf = *st->tbl;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) { 
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = ((st->tx_bit << 1) |
-				      (st->tx_bit & 1));
-			st->tx_bit ^= (!(st->shreg & 1));
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 10)
-			st->tx_seq = 0;
-		*buf = ((*st->tbl)-0x80)<<8;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
-			st->tx_bit ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 8)
-			st->tx_seq = 0;
-		*buf = *st->tbl;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
-			st->tx_bit ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 8)
-			st->tx_seq = 0;
-		*buf = ((*st->tbl)-0x80)<<8;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) { 
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = ((st->tx_bit << 1) |
-				      (st->tx_bit & 1));
-			st->tx_bit ^= (!(st->shreg & 1));
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 10)
-			st->tx_seq = 0;
-		*buf = *st->tbl;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) { 
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = ((st->tx_bit << 1) |
-				      (st->tx_bit & 1));
-			st->tx_bit ^= (!(st->shreg & 1));
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 10)
-			st->tx_seq = 0;
-		*buf = ((*st->tbl)-0x80)<<8;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
-			st->tx_bit ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 8)
-			st->tx_seq = 0;
-		*buf = *st->tbl;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
-	struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
-	for (; buflen > 0; buflen--, buf++) {
-		if (!st->tx_seq++) {
-			if (st->shreg <= 1)
-				st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
-			st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
-			st->tx_bit ^= !(st->shreg & 1);
-			st->shreg >>= 1;
-			st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
-		}
-		if (st->tx_seq >= 8)
-			st->tx_seq = 0;
-		*buf = ((*st->tbl)-0x80)<<8;
-		st->tbl += 0x10;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-	static const int pll_corr[2] = { -0x800, 0x800 };
-	int curst, cursync;
-	int inv;
-
-	for (; buflen > 0; buflen--, buf++) {
-		inv = ((int)(buf[-2])-0x80) << 8;
-		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
-		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
-		if (inv > st->lvlhi)
-			st->lvlhi = inv;
-		if (inv < st->lvllo)
-			st->lvllo = inv;
-		if (buflen & 1)
-			st->dcd_shreg <<= 1;
-		st->bit_pll += 0x199a;
-		curst = cursync = 0;
-		if (inv > st->lvlhi >> 1) {
-			curst = 1;
-			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
-				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
-		} else if (inv < st->lvllo >> 1) {
-			curst = -1;
-			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
-				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
-		}
-		if (cursync) {
-			st->dcd_shreg |= cursync;
-			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
-			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
-				hweight32(st->dcd_shreg & 0xe739ce70);
-		}
-		hdlcdrv_channelbit(&sm->hdrv, cursync);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->last_bit2 = st->last_bit;
-			if (curst < 0)
-				st->last_bit = 0;
-			else if (curst > 0)
-				st->last_bit = 1;
-			st->shreg >>= 1;
-			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, inv);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-	static const int pll_corr[2] = { -0x800, 0x800 };
-	int curst, cursync;
-	int inv;
-
-	for (; buflen > 0; buflen--, buf++) {
-		inv = buf[-2];
-		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
-		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
-		if (inv > st->lvlhi)
-			st->lvlhi = inv;
-		if (inv < st->lvllo)
-			st->lvllo = inv;
-		if (buflen & 1)
-			st->dcd_shreg <<= 1;
-		st->bit_pll += 0x199a;
-		curst = cursync = 0;
-		if (inv > st->lvlhi >> 1) {
-			curst = 1;
-			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
-				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
-		} else if (inv < st->lvllo >> 1) {
-			curst = -1;
-			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
-				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
-		}
-		if (cursync) {
-			st->dcd_shreg |= cursync;
-			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
-			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - 
-				hweight32(st->dcd_shreg & 0xe739ce70);
-		}
-		hdlcdrv_channelbit(&sm->hdrv, cursync);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->last_bit2 = st->last_bit;
-			if (curst < 0)
-				st->last_bit = 0;
-			else if (curst > 0)
-				st->last_bit = 1;
-			st->shreg >>= 1;
-			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, inv);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
-	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-	static const int pll_corr[2] = { -0x800, 0x800 };
-	int curst, cursync;
-	int inv;
-
-	for (; buflen > 0; buflen--, buf++) {
-		inv = ((int)(buf[-2])-0x80) << 8;
-		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
-		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
-		if (inv > st->lvlhi)
-			st->lvlhi = inv;
-		if (inv < st->lvllo)
-			st->lvllo = inv;
-		if (buflen & 1)
-			st->dcd_shreg <<= 1;
-		st->bit_pll += 0x2000;
-		curst = cursync = 0;
-		if (inv > st->lvlhi >> 1) {
-			curst = 1;
-			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
-				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
-		} else if (inv < st->lvllo >> 1) {
-			curst = -1;
-			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
-				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
-		}
-		if (cursync) {
-			st->dcd_shreg |= cursync;
-			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
-			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
-				hweight32(st->dcd_shreg & 0xbbbbbbbb);
-		}
-		hdlcdrv_channelbit(&sm->hdrv, cursync);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->last_bit2 = st->last_bit;
-			if (curst < 0)
-				st->last_bit = 0;
-			else if (curst > 0)
-				st->last_bit = 1;
-			st->shreg >>= 1;
-			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, inv);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
-	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-	static const int pll_corr[2] = { -0x800, 0x800 };
-	int curst, cursync;
-	int inv;
-
-	for (; buflen > 0; buflen--, buf++) {
-		inv = buf[-2];
-		st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
-		st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
-		if (inv > st->lvlhi)
-			st->lvlhi = inv;
-		if (inv < st->lvllo)
-			st->lvllo = inv;
-		if (buflen & 1)
-			st->dcd_shreg <<= 1;
-		st->bit_pll += 0x2000;
-		curst = cursync = 0;
-		if (inv > st->lvlhi >> 1) {
-			curst = 1;
-			cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
-				   buf[-2] > buf[-0] && buf[-2] > buf[-4]);
-		} else if (inv < st->lvllo >> 1) {
-			curst = -1;
-			cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
-				   buf[-2] < buf[-0] && buf[-2] < buf[-4]);
-		}
-		if (cursync) {
-			st->dcd_shreg |= cursync;
-			st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
-			st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - 
-				hweight32(st->dcd_shreg & 0xbbbbbbbb);
-		}
-		hdlcdrv_channelbit(&sm->hdrv, cursync);
-		if ((--st->dcd_time) <= 0) {
-			hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + 
-						   st->dcd_sum1 + 
-						   st->dcd_sum2) < 0);
-			st->dcd_sum2 = st->dcd_sum1;
-			st->dcd_sum1 = st->dcd_sum0;
-			st->dcd_sum0 = 2; /* slight bias */
-			st->dcd_time = 240;
-		}
-		if (st->bit_pll >= 0x10000) {
-			st->bit_pll &= 0xffff;
-			st->last_bit2 = st->last_bit;
-			if (curst < 0)
-				st->last_bit = 0;
-			else if (curst > 0)
-				st->last_bit = 1;
-			st->shreg >>= 1;
-			st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
-			if (st->shreg & 1) {
-				hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
-				st->shreg = 0x10000;
-			}
-			diag_trigger(sm);
-		}
-		diag_add_one(sm, inv);
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_hapn4800(struct sm_state *sm)
-{
-	struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-
-	st->dcd_time = 120;
-	st->dcd_sum0 = 2;	
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_8_tx = {
-	"hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, 
-	modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_8_rx = {
-	"hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
-	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_10_tx = {
-	"hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
-	modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_10_rx = {
-	"hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, 
-	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_pm8_tx = {
-	"hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, 
-	modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_pm8_rx = {
-	"hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, 
-	demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_pm10_tx = {
-	"hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
-	modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_pm10_rx = {
-	"hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
-	demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_sbc.c linux/drivers/net/soundmodem/sm_sbc.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_sbc.c	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/soundmodem/sm_sbc.c	Wed Dec 31 16:00:00 1969
@@ -1,941 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_sbc.c  -- soundcard radio modem driver soundblaster hardware driver
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include <linux/ptrace.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/ioport.h>
-#include <linux/soundmodem.h>
-#include "sm.h"
-#include "smdma.h"
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_sbc {
-	unsigned char revhi, revlo;
-	unsigned char fmt[2];
-	unsigned int sr[2];
-};
-
-#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-/* 
- * the sbc converter's registers 
- */
-#define DSP_RESET(iobase)        (iobase+0x6)
-#define DSP_READ_DATA(iobase)    (iobase+0xa)
-#define DSP_WRITE_DATA(iobase)   (iobase+0xc)
-#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
-#define DSP_DATA_AVAIL(iobase)   (iobase+0xe)
-#define DSP_MIXER_ADDR(iobase)   (iobase+0x4)
-#define DSP_MIXER_DATA(iobase)   (iobase+0x5)
-#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
-#define SBC_EXTENT               16
-
-/* --------------------------------------------------------------------- */
-/*
- * SBC commands
- */
-#define SBC_OUTPUT             0x14
-#define SBC_INPUT              0x24
-#define SBC_BLOCKSIZE          0x48
-#define SBC_HI_OUTPUT          0x91 
-#define SBC_HI_INPUT           0x99 
-#define SBC_LO_OUTPUT_AUTOINIT 0x1c
-#define SBC_LO_INPUT_AUTOINIT  0x2c
-#define SBC_HI_OUTPUT_AUTOINIT 0x90 
-#define SBC_HI_INPUT_AUTOINIT  0x98
-#define SBC_IMMED_INT          0xf2
-#define SBC_GET_REVISION       0xe1
-#define ESS_GET_REVISION       0xe7
-#define SBC_SPEAKER_ON         0xd1
-#define SBC_SPEAKER_OFF        0xd3
-#define SBC_DMA_ON             0xd0
-#define SBC_DMA_OFF            0xd4
-#define SBC_SAMPLE_RATE        0x40
-#define SBC_SAMPLE_RATE_OUT    0x41
-#define SBC_SAMPLE_RATE_IN     0x42
-#define SBC_MONO_8BIT          0xa0
-#define SBC_MONO_16BIT         0xa4
-#define SBC_STEREO_8BIT        0xa8
-#define SBC_STEREO_16BIT       0xac
-
-#define SBC4_OUT8_AI           0xc6
-#define SBC4_IN8_AI            0xce
-#define SBC4_MODE_UNS_MONO     0x00
-#define SBC4_MODE_SIGN_MONO    0x10
-
-#define SBC4_OUT16_AI          0xb6
-#define SBC4_IN16_AI           0xbe
-
-/* --------------------------------------------------------------------- */
-
-static int inline reset_dsp(struct device *dev)
-{
-	int i;
-
-	outb(1, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0x100; i++)
-		SLOW_DOWN_IO;
-	outb(0, DSP_RESET(dev->base_addr));
-	for (i = 0; i < 0xffff; i++)
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
-			if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
-				return 1;
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline write_dsp(struct device *dev, unsigned char data)
-{
-	int i;
-	
-	for (i = 0; i < 0xffff; i++)
-		if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
-			outb(data, DSP_WRITE_DATA(dev->base_addr));
-			return;
-		}
-}
-
-/* --------------------------------------------------------------------- */
-
-static int inline read_dsp(struct device *dev, unsigned char *data)
-{
-	int i;
-
-	if (!data)
-		return 0;
-	for (i = 0; i < 0xffff; i++) 
-		if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
-			*data = inb(DSP_READ_DATA(dev->base_addr));
-			return 1;
-		}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int config_resources(struct device *dev, struct sm_state *sm, int fdx)
-{
-	unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
-	unsigned long flags;
-
-	switch (dev->irq) {
-	case 2:
-	case 9:
-		irqreg |= 0x01;
-		break;
-
-	case 5:
-		irqreg |= 0x02;
-		break;
-
-	case 7:
-		irqreg |= 0x04;
-		break;
-
-	case 10:
-		irqreg |= 0x08;
-		break;
-		
-	default:
-		return -ENODEV;
-	}
-
-	switch (dev->dma) {
-	case 0:
-		dmareg |= 0x01;
-		break;
-
-	case 1:
-		dmareg |= 0x02;
-		break;
-
-	case 3:
-		dmareg |= 0x08;
-		break;
-
-	default:
-		return -ENODEV;
-	}
-		
-	if (fdx) {
-		switch (sm->hdrv.ptt_out.dma2) {
-		case 5:
-			dmareg |= 0x20;
-			break;
-			
-		case 6:
-			dmareg |= 0x40;
-			break;
-			
-		case 7:
-			dmareg |= 0x80;
-			break;
-			
-		default:
-			return -ENODEV;
-		}
-	}
-	save_flags(flags);
-	cli();
-	outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
-	outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
-	realirq = inb(DSP_MIXER_DATA(dev->base_addr));
-	outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
-	outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
-	realdma = inb(DSP_MIXER_DATA(dev->base_addr));
-	restore_flags(flags);
-	if ((~realirq) & irqreg || (~realdma) & dmareg) {
-		printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
-		       "and IRQ/DMA specified wrongly?\n", sm_drvname);
-		return -EINVAL;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline sbc_int_ack_8bit(struct device *dev)
-{
-	inb(DSP_DATA_AVAIL(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline sbc_int_ack_16bit(struct device *dev)
-{
-	inb(DSP_INTACK_16BIT(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_dsp(struct device *dev, struct sm_state *sm, int send)
-{
-        unsigned long flags;
-        static const unsigned char sbcmode[2][2] = {
-		{ SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, 
-		{ SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
-	};
-	static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
-	static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
-	unsigned int nsamps;
-
-	send = !!send;
-        if (!reset_dsp(dev)) {
-                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
-                return;
-        }
-        save_flags(flags);
-        cli();
-        sbc_int_ack_8bit(dev);
-        write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
-        write_dsp(dev, SCSTATE->fmt[send]);
-        write_dsp(dev, sbcskr[send]); 
-	nsamps = dma_setup(sm, send, dev->dma) - 1;
-        sbc_int_ack_8bit(dev);
-	if (SCSTATE->revhi >= 4) {
-		write_dsp(dev, sbc4mode[send]);
-		write_dsp(dev, SBC4_MODE_UNS_MONO);
-		write_dsp(dev, nsamps & 0xff);
-		write_dsp(dev, nsamps >> 8);
-	} else {
-		write_dsp(dev, SBC_BLOCKSIZE);
-		write_dsp(dev, nsamps & 0xff);
-		write_dsp(dev, nsamps >> 8);
-		write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
-		/* hispeed mode if sample rate > 13kHz */
-	}
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned int curfrag;
-
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	cli();
- 	sbc_int_ack_8bit(dev);
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
-	enable_dma(dev->dma);
-	sm_int_freq(sm);
-	sti();
-	if (sm->dma.ptt_cnt <= 0) {
-		dma_receive(sm, curfrag);
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-		if (hdlcdrv_ptt(&sm->hdrv)) {
-			/* starting to transmit */
-			disable_dma(dev->dma);
-			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
-			dma_start_transmit(sm);
-			setup_dma_dsp(dev, sm, 1);
-			dma_transmit(sm);
-		}
-	} else if (dma_end_transmit(sm, curfrag)) {
-		/* stopping transmission */
-		disable_dma(dev->dma);
-		sti();
-		dma_init_receive(sm);
-		setup_dma_dsp(dev, sm, 0);
-        } else
-		dma_transmit(sm);
-	sm_output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_open(struct device *dev, struct sm_state *sm) 
-{
-	int err;
-	unsigned int dmasz, u;
-
-	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
-		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
-		       sizeof(struct sc_state_sbc), sizeof(sm->m));
-		return -ENODEV;
-	}
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, SBC_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (!reset_dsp(dev)) {
-		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
-		       sm_drvname, dev->base_addr);
-		return -ENODEV;
-	}
-	write_dsp(dev, SBC_GET_REVISION);
-	if (!read_dsp(dev, &SCSTATE->revhi) || 
-	    !read_dsp(dev, &SCSTATE->revlo))
-		return -ENODEV;
-	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
-	       SCSTATE->revhi, SCSTATE->revlo);
-	if (SCSTATE->revhi < 2) {
-		printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
-		       "rev 2.00 required\n", sm_drvname);
-		return -ENODEV;
-	}
-	if (SCSTATE->revhi < 3 && 
-	    (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
-		printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
-		       "old, at least 3.00 required\n", sm_drvname,
-		       dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
-		return -ENODEV;
-	}
-	if (SCSTATE->revhi >= 4 && 
-	    (err = config_resources(dev, sm, 0))) {
-		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
-		return err;
-	}
-	/*
-	 * initialize some variables
-	 */
-	dma_init_receive(sm);
-	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
-	u = NUM_FRAGMENTS * sm->dma.ofragsz;
-	if (u > dmasz)
-		dmasz = u;
-	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	dma_init_transmit(sm);
-	dma_init_receive(sm);
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree_s(sm->dma.obuf, dmasz);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(sm->dma.obuf, dmasz);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
-	setup_dma_dsp(dev, sm, 0);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	reset_dsp(dev);	
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, SBC_EXTENT);
-	kfree(sm->dma.obuf);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info **mtp = sm_modem_tx_table;
-	const struct modem_rx_info **mrp;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; *mtp; mtp++) {
-		if ((*mtp)->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
-			continue;
-		}
-		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
-			continue;
-		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
-			continue;
-		if (!(*mtp)->modulator_u8)
-			continue;
-		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
-			if ((*mrp)->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
-				continue;
-			}
-			if (!(*mrp)->demodulator_u8)
-				continue;
-			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
-			    (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
-				sm->mode_tx = *mtp;
-				sm->mode_rx = *mrp;
-				SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
-							 sm->mode_rx->srate);
-				SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
-							 sm->mode_tx->srate);
-				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
-				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
-				if (sm->dma.ifragsz < sm->mode_rx->overlap)
-					sm->dma.ifragsz = sm->mode_rx->overlap;
-				sm->dma.i16bit = sm->dma.o16bit = 0;
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-		     struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_ioctl bi;
-	unsigned long flags;
-	int i;
-	
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
-			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case SMCTL_GETMIXER:
-		i = 0;
-		bi.data.mix.sample_rate = sm->mode_rx->srate;
-		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
-		bi.data.mix.mixer_type = SM_MIXER_INVALID;
-		switch (SCSTATE->revhi) {
-		case 2:
-			bi.data.mix.mixer_type = SM_MIXER_CT1335;
-			break;
-		case 3:
-			bi.data.mix.mixer_type = SM_MIXER_CT1345;
-			break;
-		case 4:
-			bi.data.mix.mixer_type = SM_MIXER_CT1745;
-			break;
-		}
-		if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
-		    bi.data.mix.reg < 0x80) {
-			save_flags(flags);
-			cli();
-			outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-			bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
-			restore_flags(flags);
-			i = 1;
-		}
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return i;
-		
-	case SMCTL_SETMIXER:
-		if (!suser())
-			return -EACCES;
-		switch (SCSTATE->revhi) {
-		case 2:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
-				return -EINVAL;
-			break;
-		case 3:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
-				return -EINVAL;
-			break;
-		case 4:
-			if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
-				return -EINVAL;
-			break;
-		default:
-			return -ENODEV;
-		}
-		if (bi.data.mix.reg >= 0x80)
-			return -EACCES;
-		save_flags(flags);
-		cli();
-		outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
-		outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
-		restore_flags(flags);
-		return 0;
-		
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_sbc = {
-	"sbc", sizeof(struct sc_state_sbc), 
-	sbc_open, sbc_close, sbc_ioctl, sbc_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_fdx_dsp(struct device *dev, struct sm_state *sm)
-{
-        unsigned long flags;
-	unsigned int isamps, osamps;
-
-        if (!reset_dsp(dev)) {
-                printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
-                return;
-        }
-        save_flags(flags);
-        cli();
-        sbc_int_ack_8bit(dev);
-        sbc_int_ack_16bit(dev);
-	/* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
-	write_dsp(dev, SBC_SAMPLE_RATE_IN);
-	write_dsp(dev, SCSTATE->sr[0] >> 8);
-	write_dsp(dev, SCSTATE->sr[0] & 0xff);
-	write_dsp(dev, SBC_SAMPLE_RATE_OUT);
-	write_dsp(dev, SCSTATE->sr[1] >> 8);
-	write_dsp(dev, SCSTATE->sr[1] & 0xff);
-        write_dsp(dev, SBC_SPEAKER_ON);
-	if (sm->dma.o16bit) {
-		/*
-		 * DMA channel 1 (8bit) does input (capture),
-		 * DMA channel 2 (16bit) does output (playback)
-		 */
-		isamps = dma_setup(sm, 0, dev->dma) - 1;
-		osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
-		sbc_int_ack_8bit(dev);
-		sbc_int_ack_16bit(dev);
-		write_dsp(dev, SBC4_IN8_AI);
-		write_dsp(dev, SBC4_MODE_UNS_MONO);
-		write_dsp(dev, isamps & 0xff);
-		write_dsp(dev, isamps >> 8);
-		write_dsp(dev, SBC4_OUT16_AI);
-		write_dsp(dev, SBC4_MODE_SIGN_MONO);
-		write_dsp(dev, osamps & 0xff);
-		write_dsp(dev, osamps >> 8);
-	} else {
-		/*
-		 * DMA channel 1 (8bit) does output (playback),
-		 * DMA channel 2 (16bit) does input (capture)
-		 */
-		isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
-		osamps = dma_setup(sm, 1, dev->dma) - 1;
-		sbc_int_ack_8bit(dev);
-		sbc_int_ack_16bit(dev);
-		write_dsp(dev, SBC4_OUT8_AI);
-		write_dsp(dev, SBC4_MODE_UNS_MONO);
-		write_dsp(dev, osamps & 0xff);
-		write_dsp(dev, osamps >> 8);
-		write_dsp(dev, SBC4_IN16_AI);
-		write_dsp(dev, SBC4_MODE_SIGN_MONO);
-		write_dsp(dev, isamps & 0xff);
-		write_dsp(dev, isamps >> 8);
-	}
-	dma_init_receive(sm);
-	dma_init_transmit(sm);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned char intsrc, pbint = 0, captint = 0;
-	unsigned int ocfrag, icfrag;
-	unsigned long flags;
-
-	if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	save_flags(flags);
-	cli();
-	outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
-	intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
-	if (intsrc & 0x01) {
-		sbc_int_ack_8bit(dev);
-		if (sm->dma.o16bit) {
-			captint = 1;
-			disable_dma(dev->dma);
-			clear_dma_ff(dev->dma);
-			dma_ptr(sm, 0, dev->dma, &icfrag);
-			enable_dma(dev->dma);
-		} else {     
-			pbint = 1;
-			disable_dma(dev->dma);
-			clear_dma_ff(dev->dma);
-			dma_ptr(sm, 1, dev->dma, &ocfrag);
-			enable_dma(dev->dma);
-		}
-	}
-	if (intsrc & 0x02) {
-		sbc_int_ack_16bit(dev);
-		if (sm->dma.o16bit) {
-			pbint = 1;
-			disable_dma(sm->hdrv.ptt_out.dma2);
-			clear_dma_ff(sm->hdrv.ptt_out.dma2);
-			dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
-			enable_dma(sm->hdrv.ptt_out.dma2);
-		} else {
-			captint = 1;
-			disable_dma(sm->hdrv.ptt_out.dma2);
-			clear_dma_ff(sm->hdrv.ptt_out.dma2);
-			dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
-			enable_dma(sm->hdrv.ptt_out.dma2);
-		}
-	}
-	restore_flags(flags);
-	sm_int_freq(sm);
-	sti();
-	if (pbint) {
-		if (dma_end_transmit(sm, ocfrag))
-			dma_clear_transmit(sm);
-		dma_transmit(sm);
-	}
-	if (captint) { 
-		dma_receive(sm, icfrag);
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-	}
-	sm_output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_open(struct device *dev, struct sm_state *sm) 
-{
-	int err;
-
-	if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
-		printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", 
-		       sizeof(struct sc_state_sbc), sizeof(sm->m));
-		return -ENODEV;
-	}
-	if (!dev || !sm)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, SBC_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (!reset_dsp(dev)) {
-		printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
-		       sm_drvname, dev->base_addr);
-		return -ENODEV;
-	}
-	write_dsp(dev, SBC_GET_REVISION);
-	if (!read_dsp(dev, &SCSTATE->revhi) || 
-	    !read_dsp(dev, &SCSTATE->revlo))
-		return -ENODEV;
-	printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, 
-	       SCSTATE->revhi, SCSTATE->revlo);
-	if (SCSTATE->revhi < 4) {
-		printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
-		return -ENODEV;
-	}
-	if ((err = config_resources(dev, sm, 1))) {
-		printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
-		return err;
-	}
-	/*
-	 * initialize some variables
-	 */
-	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
-		kfree(sm->dma.ibuf);
-		return -ENOMEM;
-	}
-	dma_init_transmit(sm);
-	dma_init_receive(sm);
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		return -EBUSY;
-	}
-	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		free_dma(dev->dma);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		free_dma(dev->dma);
-		free_dma(sm->hdrv.ptt_out.dma2);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
-	setup_dma_fdx_dsp(dev, sm);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	disable_dma(sm->hdrv.ptt_out.dma2);
-	reset_dsp(dev);	
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	free_dma(sm->hdrv.ptt_out.dma2);	
-	release_region(dev->base_addr, SBC_EXTENT);
-	kfree(sm->dma.ibuf);
-	kfree(sm->dma.obuf);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info **mtp = sm_modem_tx_table;
-	const struct modem_rx_info **mrp;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; *mtp; mtp++) {
-		if ((*mtp)->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
-			continue;
-		}
-		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
-			continue;
-		if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
-			continue;
-		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
-			if ((*mrp)->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
-				continue;
-			}
-			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
-			    (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
-			    (*mrp)->srate == (*mtp)->srate) {
-				sm->mode_tx = *mtp;
-				sm->mode_rx = *mrp;
-				SCSTATE->sr[0] = sm->mode_rx->srate;
-				SCSTATE->sr[1] = sm->mode_tx->srate;
-				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
-				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
-				if (sm->dma.ifragsz < sm->mode_rx->overlap)
-					sm->dma.ifragsz = sm->mode_rx->overlap;
-				if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
-					sm->dma.i16bit = 1;
-					sm->dma.o16bit = 0;
-					sm->dma.ifragsz <<= 1;
-				} else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
-					sm->dma.i16bit = 0;
-					sm->dma.o16bit = 1;
-					sm->dma.ofragsz <<= 1;
-				} else {
-					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
-					       sm->mode_rx->name, sm->mode_tx->name);
-					sm->mode_tx = NULL;
-					sm->mode_rx = NULL;
-					return -EINVAL;
-				}
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | 
-			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
-	return sbc_ioctl(dev, sm, ifr, hi, cmd);
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_sbcfdx = {
-	"sbcfdx", sizeof(struct sc_state_sbc), 
-	sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
-};
-
-/* --------------------------------------------------------------------- */
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/sm_wss.c linux/drivers/net/soundmodem/sm_wss.c
--- v2.1.70/linux/drivers/net/soundmodem/sm_wss.c	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/soundmodem/sm_wss.c	Wed Dec 31 16:00:00 1969
@@ -1,965 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	sm_wss.c  -- soundcard radio modem driver, WSS (half duplex) driver
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#include <linux/ptrace.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/ioport.h>
-#include <linux/soundmodem.h>
-#include "sm.h"
-#include "smdma.h"
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_READ, from, n);
-        if (i)
-                return i;
-        memcpy_fromfs(to, from, n);
-        return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
-        int i = verify_area(VERIFY_WRITE, to, n);
-        if (i)
-                return i;
-        memcpy_tofs(to, from, n);
-        return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_wss {
-	unsigned char revwss, revid, revv, revcid;
-	unsigned char fmt[2];
-	unsigned char crystal;
-};
-
-#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-
-#define WSS_CONFIG(iobase)       (iobase+0)
-#define WSS_STATUS(iobase)       (iobase+3)
-#define WSS_CODEC_IA(iobase)     (iobase+4)
-#define WSS_CODEC_ID(iobase)     (iobase+5)
-#define WSS_CODEC_STATUS(iobase) (iobase+6)
-#define WSS_CODEC_DATA(iobase)   (iobase+7)
-
-#define WSS_EXTENT   8
-
-#define CS423X_HOTFIX
-
-/* --------------------------------------------------------------------- */
-
-static void write_codec(struct device *dev, unsigned char idx,
-			unsigned char data)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx, WSS_CODEC_IA(dev->base_addr));
-	outb(data, WSS_CODEC_ID(dev->base_addr));
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static unsigned char read_codec(struct device *dev, unsigned char idx)
-{
-	int timeout = 900000;
-
-	/* wait until codec ready */
-	while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
-		timeout--;
-	outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
-	return inb(WSS_CODEC_ID(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-extern void inline wss_ack_int(struct device *dev)
-{
-	outb(0, WSS_CODEC_STATUS(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_srate_tab[16] = {
-	8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
-	-1, 37800, -1, 44100, 48000, 33075, 9600, 6620
-};
-
-static int wss_srate_index(int srate)
-{
-	int i;
-
-	for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
-		if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
-			return i;
-	return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_set_codec_fmt(struct device *dev, struct sm_state *sm, unsigned char fmt, 
-			     unsigned char fmt2, char fdx, char fullcalib)
-{
-	unsigned long time;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	/* Clock and data format register */
-	write_codec(dev, 0x48, fmt);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 0x5c, fmt2 & 0xf0);
-		/* MCE and interface config reg */	
-		write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
-	} else 
-		/* MCE and interface config reg */
-		write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
-	outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
-	if (SCSTATE->crystal && !fullcalib)
-		return 0;
-	/*
-	 * wait for ACI start
-	 */
-	time = 1000;
-	while (!(read_codec(dev, 0x0b) & 0x20))
-		if (!(--time)) {
-			printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", 
-			       sm_drvname);
-			restore_flags(flags);
-			return -1;
-		}
-	/*
-	 * wait for ACI end
-	 */
-	sti();
-	time = jiffies + HZ/4;
-	while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
-	restore_flags(flags);
-	if ((signed)(jiffies - time) >= 0) {
-		printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", 
-		       sm_drvname);
-		return -1;
-	}
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_init_codec(struct device *dev, struct sm_state *sm, char fdx, 
-			  unsigned char src_l, unsigned char src_r, 
-			  int igain_l, int igain_r,
-			  int ogain_l, int ogain_r)
-{
-	unsigned char tmp, reg0, reg1, reg6, reg7;
-	static const signed char irqtab[16] = 
-	{ -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
-		  -1, -1 };
-	static const signed char dmatab[4] = { 1, 2, -1, 3 };
-	
-	tmp = inb(WSS_STATUS(dev->base_addr));
-	if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && 
-	    (tmp & 0x3f) != 0x0f) {
-		printk(KERN_WARNING "sm: WSS card id register not found, "
-		       "address 0x%lx, ID register 0x%02x\n", 
-		       dev->base_addr, (int)tmp);
-		/* return -1; */
-		SCSTATE->revwss = 0;
-	} else {
-		if ((tmp & 0x80) && ((dev->dma == 0) || 
-				     ((dev->irq >= 8) && (dev->irq != 9)))) {
-			printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
-			       "(except IRQ9) cannot be used on an 8bit "
-			       "card\n", sm_drvname);
-			return -1;
-		}		
-		if (dev->irq > 15 || irqtab[dev->irq] == -1) {
-			printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", 
-			       sm_drvname, (int)dev->irq);
-			return -1;
-		}
-		if (dev->dma > 3 || dmatab[dev->dma] == -1) {
-			printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", 
-			       sm_drvname, (int)dev->dma);
-			return -1;
-		}
-		tmp = irqtab[dev->irq] | dmatab[dev->dma];
-		/* irq probe */
-		outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
-		if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
-			outb(0, WSS_CONFIG(dev->base_addr));
-			printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", 
-			       sm_drvname, dev->irq);
-		}
-		outb(tmp, WSS_CONFIG(dev->base_addr));
-		SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
-	}
-	/*
-	 * initialize the codec
-	 */
-	if (igain_l < 0)
-		igain_l = 0;
-	if (igain_r < 0)
-		igain_r = 0;
-	if (ogain_l > 0)
-		ogain_l = 0;
-	if (ogain_r > 0)
-		ogain_r = 0;
-	reg0 = (src_l << 6) & 0xc0;
-	reg1 = (src_r << 6) & 0xc0;
-	if (reg0 == 0x80 && igain_l >= 20) {
-		reg0 |= 0x20;
-		igain_l -= 20;
-	}
-	if (reg1 == 0x80 && igain_r >= 20) {
-		reg1 |= 0x20;
-		igain_r -= 20;
-	}
-	if (igain_l > 23)
-		igain_l = 23;
-	if (igain_r > 23)
-		igain_r = 23;
-	reg0 |= igain_l * 2 / 3;
-	reg1 |= igain_r * 2 / 3;
-	reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
-	reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
-	write_codec(dev, 9, 0);
-	write_codec(dev, 0, 0x45);
-	if (read_codec(dev, 0) != 0x45)
-		goto codec_err;
-	write_codec(dev, 0, 0xaa);
-	if (read_codec(dev, 0) != 0xaa)
-		goto codec_err;
-	write_codec(dev, 12, 0x40); /* enable MODE2 */
-	write_codec(dev, 16, 0);
-	write_codec(dev, 0, 0x45);
-	SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
-	write_codec(dev, 0, 0xaa);
-	SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
-	if (SCSTATE->crystal) {
-		SCSTATE->revcid = read_codec(dev, 0x19);
-		SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
-		SCSTATE->revcid &= 7;
-		write_codec(dev, 0x10, 0x80); /* maximum output level */
-		write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
-		write_codec(dev, 0x12, 0x80); /* left line input control */
-		write_codec(dev, 0x13, 0x80); /* right line input control */
-		write_codec(dev, 0x16, 0); /* disable alternative freq sel */
-		write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
-		write_codec(dev, 0x1b, 0x00); /* left out no att */
-		write_codec(dev, 0x1d, 0x00); /* right out no att */
-	}
-
-	if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
-		goto codec_err;
-
-        write_codec(dev, 0, reg0); /* left input control */
-        write_codec(dev, 1, reg1); /* right input control */
-        write_codec(dev, 2, 0x80); /* left aux#1 input control */
-        write_codec(dev, 3, 0x80); /* right aux#1 input control */
-        write_codec(dev, 4, 0x80); /* left aux#2 input control */
-        write_codec(dev, 5, 0x80); /* right aux#2 input control */
-        write_codec(dev, 6, reg6); /* left dac control */
-        write_codec(dev, 7, reg7); /* right dac control */
-        write_codec(dev, 0xa, 0x2); /* pin control register */
-        write_codec(dev, 0xd, 0x0); /* digital mix control */
-	SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
-	/*
-	 * print revisions
-	 */
-	if (SCSTATE->crystal) 
-		printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
-		       " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
-		       (int)SCSTATE->revv, (int)SCSTATE->revcid);
-	else
-		printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", 
-		       sm_drvname, (int)SCSTATE->revwss, 
-		       (int)SCSTATE->revid);
-	return 0;
- codec_err:
-	outb(0, WSS_CONFIG(dev->base_addr));
-	printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", 
-	       sm_drvname, dev->base_addr);
-	return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_wss(struct device *dev, struct sm_state *sm, int send)
-{
-        unsigned long flags;
-        static const unsigned char codecmode[2] = { 0x0e, 0x0d };
-	unsigned char oldcodecmode;
-	long abrt;
-	unsigned char fmt;
-	unsigned int numsamps;
-
-	send = !!send;
-	fmt = SCSTATE->fmt[send];
-	save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-	oldcodecmode = read_codec(dev, 9);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	wss_ack_int(dev);
-	if (read_codec(dev, 11) & 0x10) {
-		dma_setup(sm, oldcodecmode & 1, dev->dma);
-		abrt = 0;
-		while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
-	}
-#ifdef CS423X_HOTFIX
-	if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
-		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
-#else /* CS423X_HOTFIX */
-	if (read_codec(dev, 0x8) != fmt)
-		wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
-#endif /* CS423X_HOTFIX */
-	numsamps = dma_setup(sm, send, dev->dma) - 1;
-	write_codec(dev, 15, numsamps & 0xff);
-	write_codec(dev, 14, numsamps >> 8);
-	write_codec(dev, 9, codecmode[send]);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned int curfrag;
-	unsigned int nums;
-
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
-	    sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	cli();
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	clear_dma_ff(dev->dma);
-	nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
-	write_codec(dev, 15, nums  & 0xff);
-	write_codec(dev, 14, nums >> 8);
-	enable_dma(dev->dma);
-	sm_int_freq(sm);
-	sti();
-	if (sm->dma.ptt_cnt <= 0) {
-		dma_receive(sm, curfrag);
-		hdlcdrv_arbitrate(dev, &sm->hdrv);
-		if (hdlcdrv_ptt(&sm->hdrv)) {
-			/* starting to transmit */
-			disable_dma(dev->dma);
-			hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
-			dma_start_transmit(sm);
-			setup_dma_wss(dev, sm, 1);
-			dma_transmit(sm);
-		}
-	} else if (dma_end_transmit(sm, curfrag)) {
-		/* stopping transmission */
-		disable_dma(dev->dma);
-		dma_init_receive(sm);
-		setup_dma_wss(dev, sm, 0);
-        } else
-		dma_transmit(sm);
-	sm_output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_open(struct device *dev, struct sm_state *sm) 
-{
-	unsigned int dmasz, u;
-
-	if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
-		printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", 
-		       sizeof(struct sc_state_wss), sizeof(sm->m));
-		return -ENODEV;
-	}
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
-		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	dma_init_receive(sm);
-	dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
-	u = NUM_FRAGMENTS * sm->dma.ofragsz;
-	if (u > dmasz)
-		dmasz = u;
-	if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	dma_init_transmit(sm);
-	dma_init_receive(sm);
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree_s(sm->dma.obuf, dmasz);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		free_dma(dev->dma);
-		kfree_s(sm->dma.obuf, dmasz);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
-	setup_dma_wss(dev, sm, 0);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree(sm->dma.obuf);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info **mtp = sm_modem_tx_table;
-	const struct modem_rx_info **mrp;
-	int i, j;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; *mtp; mtp++) {
-		if ((*mtp)->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
-			continue;
-		}
-		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
-			continue;
-		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
-			continue;
-		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
-			if ((*mrp)->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
-				continue;
-			}
-			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
-			    ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
-				sm->mode_tx = *mtp;
-				sm->mode_rx = *mrp;
-				SCSTATE->fmt[0] = j;
-				SCSTATE->fmt[1] = i;
-				sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
-				sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
-				if (sm->dma.ifragsz < sm->mode_rx->overlap)
-					sm->dma.ifragsz = sm->mode_rx->overlap;
-				/* prefer same data format if possible to minimize switching times */
-				sm->dma.i16bit = sm->dma.o16bit = 2;
-				if (sm->mode_rx->srate == sm->mode_tx->srate) {
-					if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
-						sm->dma.i16bit = sm->dma.o16bit = 1;
-					else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
-						sm->dma.i16bit = sm->dma.o16bit = 0;
-				}
-				if (sm->dma.i16bit == 2) {
-					if (sm->mode_rx->demodulator_s16)
-						sm->dma.i16bit = 1;
-					else if (sm->mode_rx->demodulator_u8)
-						sm->dma.i16bit = 0;
-				}
-				if (sm->dma.o16bit == 2) {
-					if (sm->mode_tx->modulator_s16)
-						sm->dma.o16bit = 1;
-					else if (sm->mode_tx->modulator_u8)
-						sm->dma.o16bit = 0;
-				}
-				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
-					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
-					       sm->mode_rx->name, sm->mode_tx->name);
-					sm->mode_tx = NULL;
-					sm->mode_rx = NULL;
-					return -EINVAL;
-				}
-#ifdef __BIG_ENDIAN
-				/* big endian 16bit only works on crystal cards... */
-				if (sm->dma.i16bit) {
-					SCSTATE->fmt[0] |= 0xc0;
-					sm->dma.ifragsz <<= 1;
-				}
-				if (sm->dma.o16bit) {
-					SCSTATE->fmt[1] |= 0xc0;
-					sm->dma.ofragsz <<= 1;
-				}
-#else /* __BIG_ENDIAN */
-				if (sm->dma.i16bit) {
-					SCSTATE->fmt[0] |= 0x40;
-					sm->dma.ifragsz <<= 1;
-				}
-				if (sm->dma.o16bit) {
-					SCSTATE->fmt[1] |= 0x40;
-					sm->dma.ofragsz <<= 1;
-				}
-#endif /* __BIG_ENDIAN */
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-		     struct hdlcdrv_ioctl *hi, int cmd)
-{
-	struct sm_ioctl bi;
-	int i;
-	
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | 
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | 
-			HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
-	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
-		return -EFAULT;
-
-	switch (bi.cmd) {
-	default:
-		return -ENOIOCTLCMD;
-
-	case SMCTL_GETMIXER:
-		i = 0;
-		bi.data.mix.sample_rate = sm->mode_rx->srate;
-		bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
-		bi.data.mix.mixer_type = SCSTATE->crystal ? 
-			SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
-		if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) 
-		     >> bi.data.mix.reg) & 1) {
-			bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
-			i = 1;
-		}
-		if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-			return -EFAULT;
-		return i;
-
-	case SMCTL_SETMIXER:
-		if (!suser())
-			return -EACCES;
-		if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || 
-		     !SCSTATE->crystal) &&
-		    (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
-		     bi.data.mix.reg >= 0x10))
-			return -EINVAL;
-		if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
-			return -EACCES;
-		write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
-		return 0;
-		
-	}
-	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
-		return -EFAULT;
-	return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_wss = {
-	"wss", sizeof(struct sc_state_wss), 
-	wss_open, wss_close, wss_ioctl, wss_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-static void setup_fdx_dma_wss(struct device *dev, struct sm_state *sm)
-{
-        unsigned long flags;
-	unsigned char oldcodecmode, codecdma;
-	long abrt;
-	unsigned int osamps, isamps;
-	
-        save_flags(flags);
-        cli();
-	/*
-	 * perform the final DMA sequence to disable the codec request
-	 */
-	oldcodecmode = read_codec(dev, 9);
-        write_codec(dev, 9, 0); /* disable codec DMA */
-	wss_ack_int(dev);
-	if ((codecdma = read_codec(dev, 11)) & 0x10) {
-		dma_setup(sm, 1, dev->dma);
-		dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
-		abrt = 0;
-		while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
-	}
-       	wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
-	osamps = dma_setup(sm, 1, dev->dma) - 1;
-	isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
-	write_codec(dev, 15, osamps & 0xff);
-	write_codec(dev, 14, osamps >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, isamps & 0xff);
-		write_codec(dev, 30, isamps >> 8);
-	}
-	write_codec(dev, 9, 3);
-        restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	struct device *dev = (struct device *)dev_id;
-	struct sm_state *sm = (struct sm_state *)dev->priv;
-	unsigned long flags;
-	unsigned char cry_int_src;
-	unsigned icfrag, ocfrag, isamps, osamps;
-
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || 
-	    sm->hdrv.magic != HDLCDRV_MAGIC)
-		return;
-	save_flags(flags);
-	cli();
-	if (SCSTATE->crystal) { 
-		/* Crystal has an essentially different interrupt handler! */
-		cry_int_src = read_codec(dev, 0x18);
-		wss_ack_int(dev);
-		if (cry_int_src & 0x10) {       /* playback interrupt */
-			disable_dma(dev->dma);
-			clear_dma_ff(dev->dma);
-			osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
-			write_codec(dev, 15, osamps & 0xff);
-			write_codec(dev, 14, osamps >> 8);
-			enable_dma(dev->dma);
-		}
-		if (cry_int_src & 0x20) {       /* capture interrupt */
-			disable_dma(sm->hdrv.ptt_out.dma2);
-			clear_dma_ff(sm->hdrv.ptt_out.dma2);
-			isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
-			write_codec(dev, 31, isamps & 0xff);
-			write_codec(dev, 30, isamps >> 8);
-			enable_dma(sm->hdrv.ptt_out.dma2);
-		}
-		restore_flags(flags);
-		sm_int_freq(sm);
-		sti();
-		if (cry_int_src & 0x10) {
-			if (dma_end_transmit(sm, ocfrag))
-				dma_clear_transmit(sm);
-			dma_transmit(sm);
-		}
-		if (cry_int_src & 0x20) { 
-			dma_receive(sm, icfrag);
-			hdlcdrv_arbitrate(dev, &sm->hdrv);
-		}
-		sm_output_status(sm);
-		hdlcdrv_transmitter(dev, &sm->hdrv);
-		hdlcdrv_receiver(dev, &sm->hdrv);
-		return;
-	}
-	wss_ack_int(dev);
-	disable_dma(dev->dma);
-	disable_dma(sm->hdrv.ptt_out.dma2);
-	clear_dma_ff(dev->dma);
-	clear_dma_ff(sm->hdrv.ptt_out.dma2);
-	osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
-	isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
-	write_codec(dev, 15, osamps & 0xff);
-	write_codec(dev, 14, osamps >> 8);
-	if (SCSTATE->crystal) {
-		write_codec(dev, 31, isamps & 0xff);
-		write_codec(dev, 30, isamps >> 8);
-	}
-	enable_dma(dev->dma);
-	enable_dma(sm->hdrv.ptt_out.dma2);
-	restore_flags(flags);
-	sm_int_freq(sm);
-	sti();
-	if (dma_end_transmit(sm, ocfrag))
-		dma_clear_transmit(sm);
-	dma_transmit(sm);
-	dma_receive(sm, icfrag);
-	hdlcdrv_arbitrate(dev, &sm->hdrv);
-	sm_output_status(sm);
-	hdlcdrv_transmitter(dev, &sm->hdrv);
-	hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_open(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
-		return -ENXIO;
-	if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || 
-	    dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
-		return -ENXIO;
-	if (check_region(dev->base_addr, WSS_EXTENT))
-		return -EACCES;
-	/*
-	 * check if a card is available
-	 */
-	if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
-		return -ENODEV;
-	/*
-	 * initialize some variables
-	 */
-	if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
-		return -ENOMEM;
-	if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
-		kfree(sm->dma.ibuf);
-		return -ENOMEM;
-	}
-	dma_init_transmit(sm);
-	dma_init_receive(sm);
-
-	memset(&sm->m, 0, sizeof(sm->m));
-	memset(&sm->d, 0, sizeof(sm->d));
-	if (sm->mode_tx->init)
-		sm->mode_tx->init(sm);
-	if (sm->mode_rx->init)
-		sm->mode_rx->init(sm);
-
-	if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		return -EBUSY;
-	}
-	if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		free_dma(dev->dma);
-		return -EBUSY;
-	}
-	if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, 
-			sm->hwdrv->hw_name, dev)) {
-		kfree(sm->dma.ibuf);
-		kfree(sm->dma.obuf);
-		free_dma(dev->dma);
-		free_dma(sm->hdrv.ptt_out.dma2);
-		return -EBUSY;
-	}
-	request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
-	setup_fdx_dma_wss(dev, sm);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_close(struct device *dev, struct sm_state *sm) 
-{
-	if (!dev || !sm)
-		return -EINVAL;
-	/*
-	 * disable interrupts
-	 */
-	disable_dma(dev->dma);
-	disable_dma(sm->hdrv.ptt_out.dma2);
-        write_codec(dev, 9, 0xc); /* disable codec */
-	free_irq(dev->irq, dev);	
-	free_dma(dev->dma);	
-	free_dma(sm->hdrv.ptt_out.dma2);	
-	release_region(dev->base_addr, WSS_EXTENT);
-	kfree(sm->dma.ibuf);
-	kfree(sm->dma.obuf);
-	return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_sethw(struct device *dev, struct sm_state *sm, char *mode)
-{
-	char *cp = strchr(mode, '.');
-	const struct modem_tx_info **mtp = sm_modem_tx_table;
-	const struct modem_rx_info **mrp;
-	int i;
-
-	if (!strcmp(mode, "off")) {
-		sm->mode_tx = NULL;
-		sm->mode_rx = NULL;
-		return 0;
-	}
-	if (cp)
-		*cp++ = '\0';
-	else
-		cp = mode;
-	for (; *mtp; mtp++) {
-		if ((*mtp)->loc_storage > sizeof(sm->m)) {
-			printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
-			       sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
-			continue;
-		}
-		if (!(*mtp)->name || strcmp((*mtp)->name, mode))
-			continue;
-		if ((i = wss_srate_index((*mtp)->srate)) < 0) 
-			continue;
-		for (mrp = sm_modem_rx_table; *mrp; mrp++) {
-			if ((*mrp)->loc_storage > sizeof(sm->d)) {
-				printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
-				       sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
-				continue;
-			}
-			if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
-			    (*mtp)->srate == (*mrp)->srate) {
-				sm->mode_tx = *mtp;
-				sm->mode_rx = *mrp;
-				SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
-				sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
-				if (sm->dma.ifragsz < sm->mode_rx->overlap)
-					sm->dma.ifragsz = sm->mode_rx->overlap;
-				sm->dma.i16bit = sm->dma.o16bit = 2;
-				if (sm->mode_rx->demodulator_s16) {
-					sm->dma.i16bit = 1;
-					sm->dma.ifragsz <<= 1;
-#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
-					SCSTATE->fmt[0] |= 0xc0;
-#else /* __BIG_ENDIAN */
-					SCSTATE->fmt[0] |= 0x40;
-#endif /* __BIG_ENDIAN */
-				} else if (sm->mode_rx->demodulator_u8)
-					sm->dma.i16bit = 0;
-				if (sm->mode_tx->modulator_s16) {
-					sm->dma.o16bit = 1;
-					sm->dma.ofragsz <<= 1;
-#ifdef __BIG_ENDIAN    /* big endian 16bit only works on crystal cards... */
-					SCSTATE->fmt[1] |= 0xc0;
-#else /* __BIG_ENDIAN */
-					SCSTATE->fmt[1] |= 0x40;
-#endif /* __BIG_ENDIAN */
-				} else if (sm->mode_tx->modulator_u8)
-					sm->dma.o16bit = 0;
-				if (sm->dma.i16bit == 2 ||  sm->dma.o16bit == 2) {
-					printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, 
-					       sm->mode_rx->name, sm->mode_tx->name);
-					sm->mode_tx = NULL;
-					sm->mode_rx = NULL;
-					return -EINVAL;
-				}
-				return 0;
-			}
-		}
-	}
-	return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_ioctl(struct device *dev, struct sm_state *sm, struct ifreq *ifr, 
-			struct hdlcdrv_ioctl *hi, int cmd)
-{
-	if (cmd != SIOCDEVPRIVATE)
-		return -ENOIOCTLCMD;
-
-	if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
-		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
-			HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
-			HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
-			HDLCDRV_PARMASK_MIDIIOBASE;
-
-	return wss_ioctl(dev, sm, ifr, hi, cmd);
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_wssfdx = {
-	"wssfdx", sizeof(struct sc_state_wss), 
-	wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-#undef SCSTATE
diff -u --recursive --new-file v2.1.70/linux/drivers/net/soundmodem/smdma.h linux/drivers/net/soundmodem/smdma.h
--- v2.1.70/linux/drivers/net/soundmodem/smdma.h	Mon Aug 11 14:47:04 1997
+++ linux/drivers/net/soundmodem/smdma.h	Wed Dec 31 16:00:00 1969
@@ -1,217 +0,0 @@
-/*****************************************************************************/
-
-/*
- *	smdma.h  --  soundcard radio modem driver dma buffer routines.
- *
- *	Copyright (C) 1996  Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *	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.
- *
- *	This program is distributed in the hope that it will be useful,
- *	but WITHOUT ANY WARRANTY; without even the implied warranty of
- *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *	GNU General Public License for more details.
- *
- *	You should have received a copy of the GNU General Public License
- *	along with this program; if not, write to the Free Software
- *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  Please note that the GPL allows you to use the driver, NOT the radio.
- *  In order to use the radio, you need a license from the communications
- *  authority of your country.
- *
- */
-
-#ifndef _SMDMA_H
-#define _SMDMA_H
-
-/* ---------------------------------------------------------------------- */
-
-#include "sm.h"
-
-/* ---------------------------------------------------------------------- */
-
-#define DMA_MODE_AUTOINIT      0x10
-#define NUM_FRAGMENTS          4
-
-/*
- * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
- * for the modulator to fill the whole DMA buffer without underrun
- * at the highest possible baud rate, otherwise the TX state machine will
- * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
- */ 
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== DMA buffer management ===========================
- */
-
-/*
- * returns the number of samples per fragment
- */
-extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
-{
-	if (send) {
-		disable_dma(dmanr);
-		clear_dma_ff(dmanr);
-		set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
-		set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
-		set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
-		enable_dma(dmanr);
-		if (sm->dma.o16bit)
-			return sm->dma.ofragsz/2;
-		return sm->dma.ofragsz;
-	} else {
-		disable_dma(dmanr);
-		clear_dma_ff(dmanr);
-		set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
-		set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
-		set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
-		enable_dma(dmanr);
-		if (sm->dma.i16bit)
-			return sm->dma.ifragsz/2;
-		return sm->dma.ifragsz;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
-				       unsigned int *curfrag)
-{
-	unsigned int dmaptr, sz, frg, offs;
-	
-	dmaptr = get_dma_residue(dmanr);
-	if (send) {
-		sz = sm->dma.ofragsz * NUM_FRAGMENTS;
-		if (dmaptr == 0 || dmaptr > sz)
-			dmaptr = sz;
-		dmaptr--;
-		frg = dmaptr / sm->dma.ofragsz;
-		offs = (dmaptr % sm->dma.ofragsz) + 1;
-		*curfrag = NUM_FRAGMENTS - 1 - frg;
-#ifdef SM_DEBUG
-		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
-			sm->debug_vals.dma_residue = offs;
-#endif /* SM_DEBUG */
-		if (sm->dma.o16bit)
-			return offs/2;
-		return offs;
-	} else {
-		sz = sm->dma.ifragsz * NUM_FRAGMENTS;
-		if (dmaptr == 0 || dmaptr > sz)
-			dmaptr = sz;
-		dmaptr--;
-		frg = dmaptr / sm->dma.ifragsz;
-		offs = (dmaptr % sm->dma.ifragsz) + 1;
-		*curfrag = NUM_FRAGMENTS - 1 - frg;
-#ifdef SM_DEBUG
-		if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
-			sm->debug_vals.dma_residue = offs;
-#endif /* SM_DEBUG */
-		if (sm->dma.i16bit)
-			return offs/2;
-		return offs;
-	}
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
-{
-	unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
-
-	sm->dma.ofragptr = curfrag;
-	if (sm->dma.ptt_cnt <= 0) {
-		sm->dma.ptt_cnt = 0;
-		return 0;
-	}
-	sm->dma.ptt_cnt -= diff;
-	if (sm->dma.ptt_cnt <= 0) {
-		sm->dma.ptt_cnt = 0;
-		return -1;
-	}
-	return 0;
-}
-
-extern __inline__ void dma_transmit(struct sm_state *sm)
-{
-	void *p;
-
-	while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
-		p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
-			((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
-		if (sm->dma.o16bit) {
-			time_exec(sm->debug_vals.mod_cyc, 
-				  sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
-		} else {
-			time_exec(sm->debug_vals.mod_cyc, 
-				  sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
-		}
-		sm->dma.ptt_cnt++;
-	}
-}
-
-extern __inline__ void dma_init_transmit(struct sm_state *sm)
-{
-	sm->dma.ofragptr = 0;
-	sm->dma.ptt_cnt = 0;
-}
-
-extern __inline__ void dma_start_transmit(struct sm_state *sm)
-{
-	sm->dma.ofragptr = 0;
-	if (sm->dma.o16bit) {
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
-	} else {
-		time_exec(sm->debug_vals.mod_cyc, 
-			  sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
-	}
-	sm->dma.ptt_cnt = 1;
-}
-
-extern __inline__ void dma_clear_transmit(struct sm_state *sm)
-{
-	sm->dma.ptt_cnt = 0;
-	memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
-{
-	void *p;
-
-	while (sm->dma.ifragptr != curfrag) {
-		if (sm->dma.ifragptr)
-			p = (unsigned char *)sm->dma.ibuf + 
-				sm->dma.ifragsz * sm->dma.ifragptr;
-		else {
-			p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
-			memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
-		}
-		if (sm->dma.o16bit) {
-			time_exec(sm->debug_vals.demod_cyc, 
-				  sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
-		} else {
-			time_exec(sm->debug_vals.demod_cyc, 
-				  sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
-		}
-		sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
-	}
-}
-
-extern __inline__ void dma_init_receive(struct sm_state *sm)
-{
-	sm->dma.ifragptr = 0;
-}
-
-/* --------------------------------------------------------------------- */
-#endif /* _SMDMA_H */
-
-
-
diff -u --recursive --new-file v2.1.70/linux/drivers/net/z8530.h linux/drivers/net/z8530.h
--- v2.1.70/linux/drivers/net/z8530.h	Tue Oct 29 05:33:39 1996
+++ linux/drivers/net/z8530.h	Wed Dec 31 16:00:00 1969
@@ -1,243 +0,0 @@
-
-/* 8530 Serial Communications Controller Register definitions */
-#define	FLAG	0x7e
-
-/* Write Register 0 */
-#define	R0	0		/* Register selects */
-#define	R1	1
-#define	R2	2
-#define	R3	3
-#define	R4	4
-#define	R5	5
-#define	R6	6
-#define	R7	7
-#define	R8	8
-#define	R9	9
-#define	R10	10
-#define	R11	11
-#define	R12	12
-#define	R13	13
-#define	R14	14
-#define	R15	15
-
-#define	NULLCODE	0	/* Null Code */
-#define	POINT_HIGH	0x8	/* Select upper half of registers */
-#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
-#define	SEND_ABORT	0x18	/* HDLC Abort */
-#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
-#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
-#define	ERR_RES		0x30	/* Error Reset */
-#define	RES_H_IUS	0x38	/* Reset highest IUS */
-
-#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
-#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
-#define	RES_EOM_L	0xC0	/* Reset EOM latch */
-
-/* Write Register 1 */
-
-#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
-#define	TxINT_ENAB	0x2	/* Tx Int Enable */
-#define	PAR_SPEC	0x4	/* Parity is special condition */
-
-#define	RxINT_DISAB	0	/* Rx Int Disable */
-#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
-#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
-#define	INT_ERR_Rx	0x18	/* Int on error only */
-
-#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
-#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
-#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
-
-/* Write Register #2 (Interrupt Vector) */
-
-/* Write Register 3 */
-
-#define	RxENABLE	0x1	/* Rx Enable */
-#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
-#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
-#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
-#define	ENT_HM		0x10	/* Enter Hunt Mode */
-#define	AUTO_ENAB	0x20	/* Auto Enables */
-#define	Rx5		0x0	/* Rx 5 Bits/Character */
-#define	Rx7		0x40	/* Rx 7 Bits/Character */
-#define	Rx6		0x80	/* Rx 6 Bits/Character */
-#define	Rx8		0xc0	/* Rx 8 Bits/Character */
-
-/* Write Register 4 */
-
-#define	PAR_ENA		0x1	/* Parity Enable */
-#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
-
-#define	SYNC_ENAB	0	/* Sync Modes Enable */
-#define	SB1		0x4	/* 1 stop bit/char */
-#define	SB15		0x8	/* 1.5 stop bits/char */
-#define	SB2		0xc	/* 2 stop bits/char */
-
-#define	MONSYNC		0	/* 8 Bit Sync character */
-#define	BISYNC		0x10	/* 16 bit sync character */
-#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
-#define	EXTSYNC		0x30	/* External Sync Mode */
-
-#define	X1CLK		0x0	/* x1 clock mode */
-#define	X16CLK		0x40	/* x16 clock mode */
-#define	X32CLK		0x80	/* x32 clock mode */
-#define	X64CLK		0xC0	/* x64 clock mode */
-
-/* Write Register 5 */
-
-#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
-#define	RTS		0x2	/* RTS */
-#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
-#define	TxENAB		0x8	/* Tx Enable */
-#define	SND_BRK		0x10	/* Send Break */
-#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
-#define	Tx7		0x20	/* Tx 7 bits/character */
-#define	Tx6		0x40	/* Tx 6 bits/character */
-#define	Tx8		0x60	/* Tx 8 bits/character */
-#define	DTR		0x80	/* DTR */
-
-/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
-
-/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
-
-/* Write Register 8 (transmit buffer) */
-
-/* Write Register 9 (Master interrupt control) */
-#define	VIS	1	/* Vector Includes Status */
-#define	NV	2	/* No Vector */
-#define	DLC	4	/* Disable Lower Chain */
-#define	MIE	8	/* Master Interrupt Enable */
-#define	STATHI	0x10	/* Status high */
-#define	NORESET	0	/* No reset on write to R9 */
-#define	CHRB	0x40	/* Reset channel B */
-#define	CHRA	0x80	/* Reset channel A */
-#define	FHWRES	0xc0	/* Force hardware reset */
-
-/* Write Register 10 (misc control bits) */
-#define	BIT6	1	/* 6 bit/8bit sync */
-#define	LOOPMODE 2	/* SDLC Loop mode */
-#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
-#define	MARKIDLE 8	/* Mark/flag on idle */
-#define	GAOP	0x10	/* Go active on poll */
-#define	NRZ	0	/* NRZ mode */
-#define	NRZI	0x20	/* NRZI mode */
-#define	FM1	0x40	/* FM1 (transition = 1) */
-#define	FM0	0x60	/* FM0 (transition = 0) */
-#define	CRCPS	0x80	/* CRC Preset I/O */
-
-/* Write Register 11 (Clock Mode control) */
-#define	TRxCXT	0	/* TRxC = Xtal output */
-#define	TRxCTC	1	/* TRxC = Transmit clock */
-#define	TRxCBR	2	/* TRxC = BR Generator Output */
-#define	TRxCDP	3	/* TRxC = DPLL output */
-#define	TRxCOI	4	/* TRxC O/I */
-#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
-#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
-#define	TCBR	0x10	/* Transmit clock = BR Generator output */
-#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
-#define	RCRTxCP	0	/* Receive clock = RTxC pin */
-#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
-#define	RCBR	0x40	/* Receive clock = BR Generator output */
-#define	RCDPLL	0x60	/* Receive clock = DPLL output */
-#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
-
-/* Write Register 12 (lower byte of baud rate generator time constant) */
-
-/* Write Register 13 (upper byte of baud rate generator time constant) */
-
-/* Write Register 14 (Misc control bits) */
-#define	BRENABL	1	/* Baud rate generator enable */
-#define	BRSRC	2	/* Baud rate generator source */
-#define	DTRREQ	4	/* DTR/Request function */
-#define	AUTOECHO 8	/* Auto Echo */
-#define	LOOPBAK	0x10	/* Local loopback */
-#define	SEARCH	0x20	/* Enter search mode */
-#define	RMC	0x40	/* Reset missing clock */
-#define	DISDPLL	0x60	/* Disable DPLL */
-#define	SSBR	0x80	/* Set DPLL source = BR generator */
-#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
-#define	SFMM	0xc0	/* Set FM mode */
-#define	SNRZI	0xe0	/* Set NRZI mode */
-
-/* Write Register 15 (external/status interrupt control) */
-#define	ZCIE	2	/* Zero count IE */
-#define	DCDIE	8	/* DCD IE */
-#define	SYNCIE	0x10	/* Sync/hunt IE */
-#define	CTSIE	0x20	/* CTS IE */
-#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
-#define	BRKIE	0x80	/* Break/Abort IE */
-
-
-/* Read Register 0 */
-#define	Rx_CH_AV	0x1	/* Rx Character Available */
-#define	ZCOUNT		0x2	/* Zero count */
-#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
-#define	DCD		0x8	/* DCD */
-#define	SYNC_HUNT	0x10	/* Sync/hunt */
-#define	CTS		0x20	/* CTS */
-#define	TxEOM		0x40	/* Tx underrun */
-#define	BRK_ABRT	0x80	/* Break/Abort */
-
-/* Read Register 1 */
-#define	ALL_SNT		0x1	/* All sent */
-/* Residue Data for 8 Rx bits/char programmed */
-#define	RES3		0x8	/* 0/3 */
-#define	RES4		0x4	/* 0/4 */
-#define	RES5		0xc	/* 0/5 */
-#define	RES6		0x2	/* 0/6 */
-#define	RES7		0xa	/* 0/7 */
-#define	RES8		0x6	/* 0/8 */
-#define	RES18		0xe	/* 1/8 */
-#define	RES28		0x0	/* 2/8 */
-/* Special Rx Condition Interrupts */
-#define	PAR_ERR		0x10	/* Parity error */
-#define	Rx_OVR		0x20	/* Rx Overrun Error */
-#define	CRC_ERR		0x40	/* CRC/Framing Error */
-#define	END_FR		0x80	/* End of Frame (SDLC) */
-
-/* Read Register 2 (channel b only) - Interrupt vector */
-
-/* Read Register 3 (interrupt pending register) ch a only */
-#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
-#define	CHBTxIP	0x2		/* Channel B Tx IP */
-#define	CHBRxIP	0x4		/* Channel B Rx IP */
-#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
-#define	CHATxIP	0x10		/* Channel A Tx IP */
-#define	CHARxIP	0x20		/* Channel A Rx IP */
-
-/* Read Register 8 (receive data register) */
-
-/* Read Register 10  (misc status bits) */
-#define	ONLOOP	2		/* On loop */
-#define	LOOPSEND 0x10		/* Loop sending */
-#define	CLK2MIS	0x40		/* Two clocks missing */
-#define	CLK1MIS	0x80		/* One clock missing */
-
-/* Read Register 12 (lower byte of baud rate generator constant) */
-
-/* Read Register 13 (upper byte of baud rate generator constant) */
-
-/* Read Register 15 (value of WR 15) */
-
-/* 8580/85180/85280 Enhanced SCC register definitions */
-
-/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */
-#define AUTOTXF	0x01		/* Auto Tx Flag */
-#define AUTOEOM 0x02		/* Auto EOM Latch Reset */
-#define AUTORTS	0x04		/* Auto RTS */
-#define TXDNRZI 0x08		/* TxD Pulled High in SDLC NRZI mode */
-#define FASTDTR 0x10		/* Fast DTR/REQ Mode */
-#define CRCCBCR	0x20		/* CRC Check Bytes Completely Received */
-#define EXTRDEN	0x40		/* Extended Read Enabled */
-
-/* Write Register 15 (external/status interrupt control) */
-#define SHDLCE	1		/* SDLC/HDLC Enhancements Enable */
-#define FIFOE	4		/* FIFO Enable */
-
-/* Read Register 6 (frame status FIFO) */
-#define BCLSB	0xff		/* LSB of 14 bits count */
-
-/* Read Register 7 (frame status FIFO) */
-#define BCMSB	0x3f		/* MSB of 14 bits count */
-#define FDA	0x40		/* FIFO Data Available Status */
-#define FOY	0x80		/* FIFO Overflow Status */
diff -u --recursive --new-file v2.1.70/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.70/linux/drivers/pci/pci.c	Mon Dec  1 12:04:13 1997
+++ linux/drivers/pci/pci.c	Thu Dec  4 12:58:01 1997
@@ -60,9 +60,11 @@
 	DEVICE( NCR,		NCR_53C895,	"53c895"),
 	DEVICE( NCR,		NCR_53C885,	"53c885"),
 	DEVICE( NCR,		NCR_53C875,	"53c875"),
+	DEVICE( NCR,		NCR_53C876,	"53c876"),
 	DEVICE( ATI,		ATI_68800,      "68800AX"),
 	DEVICE( ATI,		ATI_215CT222,   "215CT222"),
 	DEVICE( ATI,		ATI_210888CX,   "210888CX"),
+	DEVICE( ATI,		ATI_215GB,	"Mach64 GB"),
 	DEVICE( ATI,		ATI_215GP,	"Mach64 GP (Rage Pro)"),
 	DEVICE( ATI,		ATI_215GT,	"Mach64 GT (Rage II)"),
 	DEVICE( ATI,		ATI_215GTB,	"Mach64 GT (Rage II)"),
@@ -72,7 +74,9 @@
 	DEVICE( VLSI,		VLSI_82C593,	"82C593-FC1"),
 	DEVICE( VLSI,		VLSI_82C594,	"82C594-AFC2"),
 	DEVICE( VLSI,		VLSI_82C597,	"82C597-AFC2"),
-	DEVICE( VLSI,		VLSI_VAS96011,	"VAS96011 PowerPC"),
+	DEVICE( VLSI,		VLSI_82C541,	"82C541 Lynx"),
+	DEVICE( VLSI,		VLSI_82C543,	"82C543 Lynx ISA"),
+	DEVICE( VLSI,		VLSI_VAS96011,	"VAS96011 (Golden Gate II)"),
 	DEVICE( ADL,		ADL_2301,	"2301"),
 	DEVICE( NS,		NS_87415,	"87415"),
 	DEVICE( NS,		NS_87410,	"87410"),
@@ -108,6 +112,7 @@
 	DEVICE( CIRRUS,		CIRRUS_7543,	"CL 7543"),
 	DEVICE( CIRRUS,		CIRRUS_7541,	"CL 7541"),
 	DEVICE( IBM,		IBM_FIRE_CORAL,	"Fire Coral"),
+	DEVICE( IBM,		IBM_TR,		"Token Ring"),
 	DEVICE( IBM,		IBM_82G2675,	"82G2675"),
 	DEVICE( IBM,		IBM_82351,	"82351"),
 	DEVICE( WD,		WD_7197,	"WD 7197"),
@@ -116,6 +121,7 @@
 	DEVICE( TRIDENT,	TRIDENT_9420,	"TG 9420"),
 	DEVICE( TRIDENT,	TRIDENT_9440,	"TG 9440"),
 	DEVICE( TRIDENT,	TRIDENT_9660,	"TG 9660"),
+	DEVICE( TRIDENT,	TRIDENT_9750,	"Image 975"),
 	DEVICE( AI,		AI_M1435,	"M1435"),
 	DEVICE( MATROX,		MATROX_MGA_2,	"Atlas PX2085"),
 	DEVICE( MATROX,		MATROX_MIL,	"Millennium"),
@@ -127,6 +133,7 @@
 	DEVICE(	CT,		CT_65550,	"65550"),
 	DEVICE( CT,		CT_65554,	"65554"),
 	DEVICE( MIRO,		MIRO_36050,	"ZR36050"),
+	DEVICE( NEC,		NEC_PCX2,	"PowerVR PCX2"),
 	DEVICE( FD,		FD_36C70,	"TMC-18C30"),
 	DEVICE( SI,		SI_6201,	"6201"),
 	DEVICE( SI,		SI_6202,	"6202"),
@@ -139,6 +146,7 @@
 	DEVICE( SI,		SI_5511,		"85C5511"),
 	DEVICE( SI,		SI_5513,		"85C5513"),
 	DEVICE( SI,		SI_5571,	"5571"),
+	DEVICE( SI,		SI_5597,	"5597"),
 	DEVICE( SI,		SI_7001,	"7001"),
 	DEVICE( HP,		HP_J2585A,	"J2585A"),
 	DEVICE( HP,		HP_J2585B,	"J2585B (Lassen)"),
@@ -149,12 +157,17 @@
 	DEVICE( OPTI,		OPTI_82C557,	"82C557"),
 	DEVICE( OPTI,		OPTI_82C558,	"82C558"),
 	DEVICE( OPTI,		OPTI_82C621,	"82C621"),
+	DEVICE( OPTI,		OPTI_82C700,	"82C700"),
+	DEVICE( OPTI,		OPTI_82C701,	"82C701 FireStar Plus"),
+	DEVICE( OPTI,		OPTI_82C814,	"82C814 Firebridge 1"),
 	DEVICE( OPTI,		OPTI_82C822,	"82C822"),
 	DEVICE( SGS,		SGS_2000,	"STG 2000X"),
 	DEVICE( SGS,		SGS_1764,	"STG 1764X"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_MULTIMASTER_NC, "MultiMaster NC"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_MULTIMASTER,    "MultiMaster"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_FLASHPOINT,     "FlashPoint"),
+	DEVICE( TI,		TI_TVP4010,	"TVP4010 Permedia"),
+	DEVICE( TI,		TI_TVP4020,	"TVP4020 Permedia 2"),
 	DEVICE( TI,		TI_PCI1130,	"PCI1130"),
 	DEVICE( TI,		TI_PCI1131,	"PCI1131"),
 	DEVICE( OAK,		OAK_OTI107,	"OTI107"),
@@ -229,9 +242,11 @@
 	DEVICE( AL,		AL_M4803,	"M4803"),
 	DEVICE( AL,		AL_M5219,	"M5219"),
 	DEVICE( AL,		AL_M5229,	"M5229 TXpro"),
+	DEVICE( SURECOM,	SURECOM_NE34,	"NE-34PCI LAN"),
 	DEVICE( NEOMAGIC,       NEOMAGIC_MAGICGRAPH_NM2070,     "Magicgraph NM2070"),
 	DEVICE( NEOMAGIC,	NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"),
 	DEVICE( NEOMAGIC,	NEOMAGIC_MAGICGRAPH_128ZV, "MagicGraph 128ZV"),
+	DEVICE( NEOMAGIC,	NEOMAGIC_MAGICGRAPH_NM2160, "MagicGraph NM2160"),
 	DEVICE( ASP,		ASP_ABP940,	"ABP940"),
 	DEVICE( ASP,		ASP_ABP940U,	"ABP940U"),
 	DEVICE( CERN,		CERN_SPSB_PMC,	"STAR/RD24 SCI-PCI (PMC)"),
@@ -301,6 +316,7 @@
 	DEVICE( ALLIANCE,	ALLIANCE_PROMOTIO, "Promotion-6410"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROVIDEO, "Provideo"),
 	DEVICE( ALLIANCE,	ALLIANCE_AT24,	"AT24"),
+	DEVICE( ALLIANCE,	ALLIANCE_AT3D,	"AT3D"),
 	DEVICE( VMIC,		VMIC_VME,	"VMIVME-7587"),
  	DEVICE( DIGI,		DIGI_RIGHTSWITCH, "RightSwitch SE-6"),
 	DEVICE( MUTECH,		MUTECH_MV1000,	"MV-1000"),
@@ -310,9 +326,11 @@
 	DEVICE( ZEITNET,	ZEITNET_1221,	"1221"),
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
 	DEVICE( OMEGA,		OMEGA_82C092G,	"82C092G"),
+	DEVICE( LITEON,		LITEON_LNE100TX,"LNE100TX"),
 	DEVICE( NP,		NP_PCI_FDDI,	"NP-PCI"),       
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
+	DEVICE( AURAVISION,	AURAVISION_VXP524,"VXP524"),
 	DEVICE( IKON,		IKON_10115,	"10115 Greensheet"),
 	DEVICE( IKON,		IKON_10117,	"10117 Greensheet"),
 	DEVICE( ZORAN,		ZORAN_36057,	"ZR36057"),
@@ -336,7 +354,7 @@
 	DEVICE( OPTIBASE,	OPTIBASE_VQUEST,"VideoQuest"),
 	DEVICE( ENSONIQ,	ENSONIQ_AUDIOPCI,"AudioPCI"),
 	DEVICE( PICTUREL,	PICTUREL_PCIVST,"PCIVST"),
-	DEVICE( NVIDIA,		NVIDIA_RIVA128,	"Riva 128"),
+	DEVICE( NVIDIA_SGS,	NVIDIA_SGS_RIVA128,	"Riva 128"),
 	DEVICE( SYMPHONY,	SYMPHONY_101,	"82C101"),
 	DEVICE( TEKRAM,		TEKRAM_DC290,	"DC-290"),
 	DEVICE( 3DLABS,		3DLABS_300SX,	"GLINT 300SX"),
@@ -390,11 +408,14 @@
 	DEVICE( INTEL,		INTEL_82371AB,	"82371AB PIIX4 IDE"),
 	DEVICE( INTEL,		INTEL_82371AB_2,"82371AB PIIX4 USB"),
 	DEVICE( INTEL,		INTEL_82371AB_3,"82371AB PIIX4 ACPI"),
+	DEVICE( INTEL,		INTEL_82443LX_0,"440LX - 82443LX PAC Host"),
+	DEVICE( INTEL,		INTEL_82443LX_1,"440LX - 82443LX PAC AGP"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
  	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
+	DEVICE( ADAPTEC,	ADAPTEC_5800,	"AIC-5800"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7861,	"AIC-7861"),
 	DEVICE( ADAPTEC,	ADAPTEC_7870,	"AIC-7870"),
@@ -402,6 +423,7 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7872,	"AIC-7872"),
 	DEVICE( ADAPTEC,	ADAPTEC_7873,	"AIC-7873"),
 	DEVICE( ADAPTEC,	ADAPTEC_7874,	"AIC-7874"),
+	DEVICE( ADAPTEC,	ADAPTEC_7895,	"AIC-7895U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7880,	"AIC-7880U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7881,	"AIC-7881U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7882,	"AIC-7882U"),
@@ -610,7 +632,7 @@
 	      case PCI_VENDOR_ID_NCR:		return "NCR";
 	      case PCI_VENDOR_ID_ATI:		return "ATI";
 	      case PCI_VENDOR_ID_VLSI:		return "VLSI";
-	      case PCI_VENDOR_ID_ADL:		return "Advance Logic";
+	      case PCI_VENDOR_ID_ADL:		return "Avance Logic";
 	      case PCI_VENDOR_ID_NS:		return "NS";
 	      case PCI_VENDOR_ID_TSENG:		return "Tseng'Lab";
 	      case PCI_VENDOR_ID_WEITEK:	return "Weitek";
@@ -661,9 +683,11 @@
 	      case PCI_VENDOR_ID_SMC:		return "SMC";
 	      case PCI_VENDOR_ID_AL:		return "Acer Labs";
 	      case PCI_VENDOR_ID_MITSUBISHI:	return "Mitsubishi";
+	      case PCI_VENDOR_ID_SURECOM:	return "Surecom";
 	      case PCI_VENDOR_ID_NEOMAGIC:	return "Neomagic";
 	      case PCI_VENDOR_ID_ASP:		return "Advanced System Products";
 	      case PCI_VENDOR_ID_CERN:		return "CERN";
+	      case PCI_VENDOR_ID_NVIDIA:	return "NVidia";
 	      case PCI_VENDOR_ID_IMS:		return "IMS";
 	      case PCI_VENDOR_ID_TEKRAM2:	return "Tekram";
 	      case PCI_VENDOR_ID_TUNDRA:	return "Tundra";
@@ -688,8 +712,10 @@
 	      case PCI_VENDOR_ID_RICOH:		return "Ricoh";
 	      case PCI_VENDOR_ID_ZEITNET:	return "ZeitNet";
 	      case PCI_VENDOR_ID_OMEGA:		return "Omega Micro";
+	      case PCI_VENDOR_ID_LITEON:	return "LiteOn";
 	      case PCI_VENDOR_ID_NP:		return "Network Peripherals";
 	      case PCI_VENDOR_ID_SPECIALIX:	return "Specialix";
+	      case PCI_VENDOR_ID_AURAVISION:	return "Auravision";
 	      case PCI_VENDOR_ID_IKON:		return "Ikon";
 	      case PCI_VENDOR_ID_ZORAN:		return "Zoran";
 	      case PCI_VENDOR_ID_COMPEX:	return "Compex";
@@ -700,7 +726,7 @@
 	      case PCI_VENDOR_ID_OPTIBASE:	return "Optibase";
 	      case PCI_VENDOR_ID_ENSONIQ:	return "Ensoniq";
 	      case PCI_VENDOR_ID_PICTUREL:	return "Picture Elements";
-	      case PCI_VENDOR_ID_NVIDIA:	return "NVidia/SGS Thomson";
+	      case PCI_VENDOR_ID_NVIDIA_SGS:	return "NVidia/SGS Thomson";
 	      case PCI_VENDOR_ID_SYMPHONY:	return "Symphony";
 	      case PCI_VENDOR_ID_TEKRAM:	return "Tekram";
 	      case PCI_VENDOR_ID_3DLABS:	return "3Dlabs";
diff -u --recursive --new-file v2.1.70/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.1.70/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Thu Aug 14 20:49:16 1997
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Tue Dec  2 20:41:21 1997
@@ -1079,10 +1079,26 @@
 	CUR_SCBID {
 		size		1
 	}
+	ARG_1 {
+		size		1
+		mask	SEND_MSG	0x80
+		mask	SEND_SENSE	0x40
+		mask	SEND_REJ	0x20
+		alias	RETURN_1
+	}
 	/*
 	 * Running count of commands placed in
 	 * the QOUTFIFO.  This is cleared by the
 	 * kernel driver every FIFODEPTH commands.
+	 *
+	 * NOTE: These scratch RAM registers are overlaying SCSICONF
+	 *       and SCSICONF2 and are only used on cards that are
+	 *       capable of SCB paging.   Currently, only the PCI
+	 *       controllers can do this, which is good because the
+	 *       AIC-7770 based controllers use the SCSICONF register
+	 *       to control termination.  In other words, do not
+	 *       destroy the contents of SCSICONF and SCSICONF2 for
+	 *       AIC-7770 based controllers.
 	 */
 	CMDOUTCNT {
 		size		1
@@ -1094,13 +1110,6 @@
 	FIFODEPTH {
 		size		1
 	}
-	ARG_1 {
-		size		1
-		mask	SEND_MSG	0x80
-		mask	SEND_SENSE	0x40
-		mask	SEND_REJ	0x20
-		alias	RETURN_1
-	}
 	/*
 	 * These are reserved registers in the card's scratch ram.  Some of
 	 * the values are specified in the AHA2742 technical reference manual
@@ -1108,6 +1117,11 @@
 	 */
 	SCSICONF {
 		address		0x05a
+		size		1
+		bit	RESET_SCSI	0x40
+	}
+        SCSICONF2 {
+		address		0x05b
 		size		1
 		bit	RESET_SCSI	0x40
 	}
diff -u --recursive --new-file v2.1.70/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.70/linux/drivers/scsi/aic7xxx.c	Thu Aug 14 20:49:16 1997
+++ linux/drivers/scsi/aic7xxx.c	Tue Dec  2 20:41:21 1997
@@ -147,6 +147,8 @@
 #define ALL_TARGETS -1
 #define ALL_CHANNELS '\0'
 #define ALL_LUNS -1
+#define MAX_TARGETS  16
+#define MAX_LUNS     8
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -301,7 +303,7 @@
 #ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
 typedef struct
 {
-  char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+  unsigned char tag_commands[16];   /* Allow for wide/twin channel adapters. */
 } adapter_tag_info_t;
 
 /*
@@ -871,7 +873,7 @@
     long r_total;                            /* total reads */
     long r_total512;                         /* 512 byte blocks read */
     long r_bins[10];                         /* binned reads */
-  } stats[2][16][8];                         /* channel, target, lun */
+  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
 #endif /* AIC7XXX_PROC_STATS */
 };
 
@@ -969,6 +971,7 @@
  * IO, we'll use them then.
  *
  ***************************************************************************/
+
 static inline unsigned char
 aic_inb(struct aic7xxx_host *p, long port)
 {
@@ -992,15 +995,24 @@
 {
   if (p->maddr != NULL)
   {
+#ifdef __alpha__
+    int i;
+
+    for (i=0; i < size; i++)
+    {
+      p->maddr[port] = valp[i];
+    }
+#else
     __asm __volatile("
       cld;
     1:  lodsb;
       movb %%al,(%0);
       loop 1b"      :
               :
-      "r" ((p)->maddr + (port)),
-      "S" ((valp)), "c" ((size))  :
+      "r" (p->maddr + port),
+      "S" (valp), "c" (size)  :
       "%esi", "%ecx", "%eax");
+#endif
   }
   else
   {
@@ -1439,7 +1451,7 @@
    * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
+  if ((*period <= aic7xxx_syncrates[i - 1].period) && *offset != 0)
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
@@ -1970,21 +1982,46 @@
   if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
     unsigned short mask;
+    int message_error = FALSE;
 
     mask = 0x01 << TARGET_INDEX(scb->cmd);
+
+    /*
+     * Check to see if we get an invalid message or a message error
+     * after failing to negotiate a wide or sync transfer message.
+     */
+    if ((scb->flags & SCB_SENSE) && 
+          ((scb->cmd->sense_buffer[12] == 0x43) ||  /* INVALID_MESSAGE */
+          (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR  */
+    {
+      message_error = TRUE;
+    }
+
     if (scb->flags & SCB_MSGOUT_WDTR)
     {
       p->wdtr_pending &= ~mask;
+      if (message_error)
+      {
+        p->needwdtr &= ~mask;
+        p->needwdtr_copy &= ~mask;
+      }
     }
     if (scb->flags & SCB_MSGOUT_SDTR)
     {
       p->sdtr_pending &= ~mask;
+      if (message_error)
+      {
+        p->needsdtr &= ~mask;
+        p->needsdtr_copy &= ~mask;
+      }
     }
   }
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
 #ifdef AIC7XXX_PROC_STATS
+  if ( (cmd->cmnd[0] != TEST_UNIT_READY) &&
+       (cmd->cmnd[0] != INQUIRY) )
   {
     int actual;
 
@@ -2000,7 +2037,7 @@
       long *ptr;
       int x;
 
-      sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+      sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
       sp->xfers++;
 
       if (cmd->request.cmd == WRITE)
@@ -3088,6 +3125,8 @@
             }
             else
             {
+              int send_reject = FALSE;
+
               /*
                * Send our own WDTR in reply.
                */
@@ -3110,14 +3149,25 @@
         	  {
                     bus_width = BUS_8_BIT;
                     scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
+                    send_reject = TRUE;
         	  }
         	  break;
 
         	default:
         	  break;
               }
-              aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
-              outb(SEND_MSG, p->base + RETURN_1);
+              if (send_reject)
+              {
+                outb(SEND_REJ, p->base + RETURN_1);
+                printk(KERN_WARNING "scsi%d: Target %d, channel %c, initiating "
+                       "wide negotiation on a narrow bus - rejecting!",
+                       p->host_no, target, channel);
+              }
+              else
+              {
+                aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+                outb(SEND_MSG, p->base + RETURN_1);
+              }
             }
             p->needwdtr &= ~target_mask;
             outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
@@ -4062,7 +4112,7 @@
 #ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
       device->queue_depth = default_depth;
 #else
-      if (p->instance > NUMBER(aic7xxx_tag_info))
+      if (p->instance >= NUMBER(aic7xxx_tag_info))
       {
         device->queue_depth = default_depth;
       }
@@ -5022,7 +5072,7 @@
   scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
 
-  for (i = 0; i <= NUMBER(p->device_status); i++)
+  for (i = 0; i < NUMBER(p->device_status); i++)
   {
     p->device_status[i].commands_sent = 0;
     p->device_status[i].flags = 0;
@@ -5305,8 +5355,17 @@
     */
   outb(p->qcntmask, p->base + QCNTMASK);
 
-  outb(p->qfullcount, p->base + FIFODEPTH);
-  outb(0, p->base + CMDOUTCNT);
+  /*
+   * Set FIFO depth and command out count.  These are only used when
+   * paging is enabled and should not be touched for AIC-7770 based
+   * adapters; FIFODEPTH and CMDOUTCNT overlay SCSICONF and SCSICONF+1
+   * which are used to control termination.
+   */
+  if (p->flags & PAGE_ENABLED)
+  {
+    outb(p->qfullcount, p->base + FIFODEPTH);
+    outb(0, p->base + CMDOUTCNT);
+  }
 
   /*
    * We don't have any waiting selections or disconnected SCBs.
@@ -5537,7 +5596,6 @@
   {
     case AIC_7770:  /* None of these adapters have seeproms. */
     case AIC_7771:
-    case AIC_7850:
     case AIC_7855:
       break;
 
@@ -5545,6 +5603,7 @@
       have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
       break;
 
+    case AIC_7850:  /* The 2910B is a 7850 with a seeprom. */
     case AIC_7861:
     case AIC_7870:
     case AIC_7871:
@@ -5654,7 +5713,11 @@
     scsi_conf = (p->scsi_id & 0x7);
     if (sc->adapter_control & CFSPARITY)
       scsi_conf |= ENSPCHK;
-    if (sc->adapter_control & CFRESETB)
+    /*
+     * The 7850 controllers with a seeprom, do not honor the CFRESETB
+     * flag in the seeprom.  Assume that we want to reset the SCSI bus.
+     */
+    if ((sc->adapter_control & CFRESETB) || (p->chip_class == AIC_7850))
       scsi_conf |= RESET_SCSI;
 
     if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
@@ -5721,7 +5784,7 @@
    * a NULL entry to indicate that no prior hosts have
    * been found/registered for that IRQ.
    */
-  for (i = 0; i <= NUMBER(aic7xxx_boards); i++)
+  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
   {
     aic7xxx_boards[i] = NULL;
   }
@@ -5787,6 +5850,14 @@
         hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
       outb(hcntrl | PAUSE, base + HCNTRL);
 
+      p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+      if (p == NULL)
+      {
+        printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+        continue;
+      }
+      aic7xxx_chip_reset(p);
+
       irq = inb(INTDEF + base) & 0x0F;
       switch (irq)
       {
@@ -5800,19 +5871,14 @@
 
         default:
           printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
-          "level, ignoring.\n");
+          "level %d, ignoring.\n", irq);
           irq = 0;
+          aic7xxx_free(p);
           break;
       }
 
       if (irq != 0)
       {
-        p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
-        if (p == NULL)
-        {
-          printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
-          continue;
-        }
         p->irq = irq & 0x0F;
         p->chip_class = AIC_777x;
 #ifdef AIC7XXX_PAGE_ENABLE
@@ -5823,7 +5889,6 @@
         {
           p->flags |= EXTENDED_TRANSLATION;
         }
-        aic7xxx_chip_reset(p);
 
         switch (p->chip_type)
         {
@@ -5985,7 +6050,6 @@
           flags = 0;
           switch (aic7xxx_pci_devices[i].chip_type)
           {
-            case AIC_7850:
             case AIC_7855:
               flags |= USE_DEFAULTS;
               break;
@@ -6887,7 +6951,7 @@
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
   channel = cmd->channel ? 'B': 'A';
-  tindex = (cmd->channel << 4) | cmd->target;
+  tindex = TARGET_INDEX(cmd);
 
 #ifdef 0   /* AIC7XXX_DEBUG_ABORT */
   if (scb != NULL)
@@ -7128,4 +7192,3 @@
  * tab-width: 8
  * End:
  */
-
diff -u --recursive --new-file v2.1.70/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.1.70/linux/drivers/scsi/aic7xxx_proc.c	Mon Aug 11 14:47:05 1997
+++ linux/drivers/scsi/aic7xxx_proc.c	Tue Dec  2 20:41:21 1997
@@ -21,13 +21,15 @@
  *  o Modified from the EATA-DMA /proc support.
  *  o Additional support for device block statistics provided by
  *    Matthew Jacob.
+ *  o Correction of overflow by Heinz Mauelshagen
+ *  o Adittional corrections by Doug Ledford
  *
  *  Dean W. Gehnert, deang@teleport.com, 05/01/96
  *
  *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
  *-M*************************************************************************/
 
-#define BLS buffer + len + size
+#define	BLS	(&aic7xxx_buffer[size])
 #define HDRB \
 "        < 512 512-1K   1-2K   2-4K   4-8K  8-16K 16-32K 32-64K 64-128K >128K"
 
@@ -49,6 +51,13 @@
 #  define proc_debug(fmt, args...)
 #endif /* PROC_DEBUG */
 
+static int aic7xxx_buffer_size = 0;
+static char *aic7xxx_buffer = NULL;
+static const char *bus_names[] = { "Single", "Twin", "Wide" };
+static const char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
+   "AIC-787x", "AIC-788x" };
+
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_set_info
@@ -63,6 +72,7 @@
   return (-ENOSYS);  /* Currently this is a no-op */
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_proc_info
@@ -71,20 +81,18 @@
  *   Return information to handle /proc support for the driver.
  *-F*************************************************************************/
 int
-aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length, 
-    int hostno, int inout)
+aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, 
+                    int hostno, int inout)
 {
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
-  int   i;
-  int   found = FALSE;
-  int   size = 0;
-  int   len = 0;
-  off_t begin = 0;
-  off_t pos = 0;
-  static char *bus_names[] = { "Single", "Twin", "Wide" };
-  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
-      "AIC-787x", "AIC-788x" };
+  int    found = FALSE;
+  int    size = 0;
+  unsigned char i;
+#ifdef AIC7XXX_PROC_STATS
+  struct aic7xxx_xferstats *sp;
+  unsigned char target, lun;
+#endif
 
   HBAptr = NULL;
   for (i=0; i < NUMBER(aic7xxx_boards); i++)
@@ -118,9 +126,15 @@
 
   if (HBAptr == NULL)
   {
-    size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno);
-    len += size; pos = begin + len; size = 0;
-    goto stop_output;
+    size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
+    if (size > length)
+    {
+      return (size);
+    }
+    else
+    {
+      return (length);
+    }
   }
 
   if (inout == TRUE) /* Has data been written to the file? */ 
@@ -130,21 +144,49 @@
 
   p = (struct aic7xxx_host *) HBAptr->hostdata;
 
+  /*
+   * It takes roughly 1K of space to hold all relevant card info, not
+   * counting any proc stats, so we start out with a 1.5k buffer size and
+   * if proc_stats is defined, then we sweep the stats structure to see
+   * how many drives we will be printing out for and add 384 bytes per
+   * device with active stats.
+   */
+
+  size = 1536;
+#ifdef AIC7XXX_PROC_STATS
+  for (target = 0; target < MAX_TARGETS; target++)
+  {
+    for (lun = 0; lun < MAX_LUNS; lun++)
+    {
+      if (p->stats[target][lun].xfers != 0)
+        size += 384;
+    }
+  }
+#endif
+  if (aic7xxx_buffer_size != size)
+  {
+    if (aic7xxx_buffer != NULL) 
+    {
+      kfree(aic7xxx_buffer);
+      aic7xxx_buffer_size = 0;
+    }
+    aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
+  }
+  if (aic7xxx_buffer == NULL)
+  {
+    size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
+        __LINE__);
+    return size;
+  }
+  aic7xxx_buffer_size = size;
+
+  size = 0;
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
   size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
   size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
 #if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
 #endif
-  if (size > 512)
-    printk(KERN_CRIT "aic7xxx: possible overflow at first position\n");
-  len += size; pos = begin + len; size = 0;
-  if (pos < offset)
-  {
-    begin = pos;
-    len = 0;
-  }
-
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
 #ifdef AIC7XXX_RESET_DELAY
@@ -168,30 +210,19 @@
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
-  if (size > 512)
-    printk(KERN_CRIT "aic7xxx: possible overflow at second position\n");
-  len += size; pos = begin + len; size = 0;
-  if (pos < offset)
-  {
-    begin = pos;
-    len = 0;
-  }
-  else if (pos >= offset + length)
-    goto stop_output;
-
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
-  size += sprintf(BLS, "          SCSI Adapter: %s\n",
+  size += sprintf(BLS, "           SCSI Adapter: %s\n",
       board_names[p->chip_type]);
-  size += sprintf(BLS, "                        (%s chipset)\n",
+  size += sprintf(BLS, "                         (%s chipset)\n",
       chip_names[p->chip_class]);
-  size += sprintf(BLS, "              Host Bus: %s\n", bus_names[p->bus_type]);
-  size += sprintf(BLS, "               Base IO: %#.4x\n", p->base);
-  size += sprintf(BLS, "        Base IO Memory: 0x%x\n", p->mbase);
-  size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
-  size += sprintf(BLS, "                  SCBs: Used %d, HW %d, Page %d\n",
+  size += sprintf(BLS, "               Host Bus: %s\n", bus_names[p->bus_type]);
+  size += sprintf(BLS, "                Base IO: %#.4x\n", p->base);
+  size += sprintf(BLS, "         Base IO Memory: 0x%x\n", p->mbase);
+  size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
+  size += sprintf(BLS, "                   SCBs: Used %d, HW %d, Page %d\n",
       p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
-  size += sprintf(BLS, "            Interrupts: %d", p->isr_count);
+  size += sprintf(BLS, "             Interrupts: %d", p->isr_count);
   if (p->chip_class == AIC_777x)
   {
     size += sprintf(BLS, " %s\n",
@@ -201,102 +232,81 @@
   {
     size += sprintf(BLS, "\n");
   }
-  size += sprintf(BLS, "         Serial EEPROM: %s\n",
+  size += sprintf(BLS, "          Serial EEPROM: %s\n",
       (p->flags & HAVE_SEEPROM) ? "True" : "False");
-  size += sprintf(BLS, "  Extended Translation: %sabled\n",
+  size += sprintf(BLS, "   Extended Translation: %sabled\n",
       (p->flags & EXTENDED_TRANSLATION) ? "En" : "Dis");
-  size += sprintf(BLS, "        SCSI Bus Reset: %sabled\n",
+  size += sprintf(BLS, "         SCSI Bus Reset: %sabled\n",
       aic7xxx_no_reset ? "Dis" : "En");
-  size += sprintf(BLS, "            Ultra SCSI: %sabled\n",
+  size += sprintf(BLS, "             Ultra SCSI: %sabled\n",
       (p->flags & ULTRA_ENABLED) ? "En" : "Dis");
-  size += sprintf(BLS, "     Target Disconnect: %sabled\n",
-      p->discenable ? "En" : "Dis");
-  if (size > 512)
-    printk(KERN_CRIT "aic7xxx: possible overflow at third position\n");
-  len += size; pos = begin + len; size = 0;
-  if (pos < offset)
-  {
-    begin = pos;
-    len = 0;
-  }
-  else if (pos >= offset + length)
-    goto stop_output;
-
+  size += sprintf(BLS, "Disconnect Enable Flags: 0x%x\n", p->discenable);
+  
 #ifdef AIC7XXX_PROC_STATS
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Statistics:\n");
+  for (target = 0; target < MAX_TARGETS; target++)
   {
-    struct aic7xxx_xferstats *sp;
-    int channel, target, lun;
-
-    /*
-     * XXX: Need to fix this to avoid overflow...
-     * Fixed - gordo.
-     */
-    size += sprintf(BLS, "\n");
-    size += sprintf(BLS, "Statistics:\n");
-    for (channel = 0; channel < 2; channel++)
+    for (lun = 0; lun < MAX_LUNS; lun++)
     {
-      for (target = 0; target < 16; target++)
+      sp = &p->stats[target][lun];
+      if (sp->xfers == 0)
       {
-        for (lun = 0; lun < 8; lun++)
-        {
-          sp = &p->stats[channel][target][lun];
-          if (sp->xfers == 0)
-          {
-            continue;
-          }
-          size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
-              'A' + channel, target, lun);
-          size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
-              sp->xfers, sp->r_total, sp->w_total);
-          size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
-              sp->r_total512, sp->w_total512);
-          size += sprintf(BLS, "%s\n", HDRB);
-          size += sprintf(BLS, " Reads:");
-          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[0],
-              sp->r_bins[1], sp->r_bins[2], sp->r_bins[3]);
-          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[4],
-              sp->r_bins[5], sp->r_bins[6], sp->r_bins[7]);
-          size += sprintf(BLS, "%6ld %6ld\n", sp->r_bins[8],
-              sp->r_bins[9]);
-          size += sprintf(BLS, "Writes:");
-          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[0],
-              sp->w_bins[1], sp->w_bins[2], sp->w_bins[3]);
-          size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[4],
-              sp->w_bins[5], sp->w_bins[6], sp->w_bins[7]);
-          size += sprintf(BLS, "%6ld %6ld\n", sp->w_bins[8],
-              sp->w_bins[9]);
-          size += sprintf(BLS, "\n");
-        }
-	if (size > 512)
-	  printk(KERN_CRIT "aic7xxx: possible overflow at loop %d:%d\n", target, lun);
-        len += size; pos = begin + len; size = 0;
-	if (pos < offset)
-	{
-	  begin = pos;
-	  len = 0;
-	}
-	else if (pos >= offset + length)
-	  goto stop_output;
+        continue;
       }
+      if (p->bus_type == AIC_TWIN)
+      {
+        size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
+            'A' + (target >> 3), (target & 0x7), lun);
+      }
+      else
+      {
+        size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
+            'A', target, lun);
+      }
+      size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
+          sp->xfers, sp->r_total, sp->w_total);
+      size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
+          sp->r_total512, sp->w_total512);
+      size += sprintf(BLS, "%s\n", HDRB);
+      size += sprintf(BLS, " Reads:");
+      for (i = 0; i < NUMBER(sp->r_bins); i++)
+      {
+        size += sprintf(BLS, "%6ld ", sp->r_bins[i]);
+      }
+      size += sprintf(BLS, "\n");
+      size += sprintf(BLS, "Writes:");
+      for (i = 0; i < NUMBER(sp->w_bins); i++)
+      {
+        size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
+      }
+      size += sprintf(BLS, "\n\n");
     }
   }
 #endif /* AIC7XXX_PROC_STATS */
 
-stop_output:
-  proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
-  *start = buffer + (offset - begin);   /* Start of wanted data */
-  len -= (offset - begin);      /* Start slop */
-  if (len < 0)
+  if (size >= aic7xxx_buffer_size)
   {
-    len = 0;			/* off end of file */
+    printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
   }
-  else if (len > length)
+
+  if (offset > size - 1)
   {
-    len = length;               /* Ending slop */
+    kfree(aic7xxx_buffer);
+    aic7xxx_buffer = NULL;
+    aic7xxx_buffer_size = length = 0;
+    *start = NULL;
   }
-  proc_debug("3pos: %ld offset: %ld len: %d\n", pos, offset, len);
-  
-  return (len);     
+  else
+  {
+    *start = &aic7xxx_buffer[offset];   /* Start of wanted data */
+    if (size - offset < length)
+    {
+      length = size - offset;
+    }
+  }
+
+  return (length);
 }
 
 /*
diff -u --recursive --new-file v2.1.70/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.1.70/linux/drivers/scsi/aic7xxx_reg.h	Thu Aug 14 20:49:16 1997
+++ linux/drivers/scsi/aic7xxx_reg.h	Tue Dec  2 20:41:21 1997
@@ -30,6 +30,13 @@
 #define		ACTNEGEN        	0x02
 #define		STPWEN          	0x01
 
+#define	SCSISIGI        		0x03
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
 #define	SCSISIGO        		0x03
 #define		CDO             	0x80
 #define		IOO             	0x40
@@ -40,13 +47,6 @@
 #define		REQO            	0x02
 #define		ACKO            	0x01
 
-#define	SCSISIGI        		0x03
-#define		ATNI            	0x10
-#define		SELI            	0x08
-#define		BSYI            	0x04
-#define		REQI            	0x02
-#define		ACKI            	0x01
-
 #define	SCSIRATE        		0x04
 #define		WIDEXFER        	0x80
 #define		SXFR            	0x70
@@ -252,24 +252,26 @@
 
 #define	CUR_SCBID       		0x58
 
-#define	CMDOUTCNT       		0x59
+#define	ARG_1           		0x59
+#define	RETURN_1        		0x59
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
 
 #define	SCSICONF        		0x5a
-#define		RESET_SCSI      	0x40
 
-#define	FIFODEPTH       		0x5a
+#define	CMDOUTCNT       		0x5a
 
-#define	ARG_1           		0x5b
-#define	RETURN_1        		0x5b
-#define		SEND_MSG        	0x80
-#define		SEND_SENSE      	0x40
-#define		SEND_REJ        	0x20
+#define	SCSICONF2       		0x5b
+#define		RESET_SCSI      	0x40
+
+#define	FIFODEPTH       		0x5b
 
 #define	HOSTCONF        		0x5d
 
 #define	HA_274_BIOSCTRL 		0x5f
-#define		BIOSMODE        	0x30
 #define		BIOSDISABLED    	0x30
+#define		BIOSMODE        	0x30
 #define		CHANNEL_B_PRIMARY	0x08
 
 #define	SEQCTL          		0x60
@@ -313,16 +315,16 @@
 
 #define	STACK           		0x6f
 
-#define	BCTL            		0x84
-#define		ACE             	0x08
-#define		ENABLE          	0x01
-
 #define	DSCOMMAND       		0x84
 #define		CACHETHEN       	0x80
 #define		DPARCKEN        	0x40
 #define		MPARCKEN        	0x20
 #define		EXTREQLCK       	0x10
 
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
 #define	BUSTIME         		0x85
 #define		BOFF            	0xf0
 #define		BON             	0x0f
@@ -372,18 +374,18 @@
 #define		BAD_PHASE       	0x01
 #define		SEQINT          	0x01
 
-#define	CLRINT          		0x92
-#define		CLRBRKADRINT    	0x08
-#define		CLRSCSIINT      	0x04
-#define		CLRCMDINT       	0x02
-#define		CLRSEQINT       	0x01
-
 #define	ERROR           		0x92
 #define		PARERR          	0x08
 #define		ILLOPCODE       	0x04
 #define		ILLSADDR        	0x02
 #define		ILLHADDR        	0x01
 
+#define	CLRINT          		0x92
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
 #define	DFCNTRL         		0x93
 
 #define	DFSTATUS        		0x94
@@ -408,6 +410,8 @@
 
 #define	QOUTCNT         		0x9e
 
+#define	SCB_BASE        		0xa0
+
 #define	SCB_CONTROL     		0xa0
 #define		MK_MESSAGE      	0x80
 #define		DISCENB         	0x40
@@ -417,8 +421,6 @@
 #define		DISCONNECTED    	0x04
 #define		SCB_TAG_TYPE    	0x03
 
-#define	SCB_BASE        		0xa0
-
 #define	SCB_TCL         		0xa1
 #define		TID             	0xf0
 #define		SELBUSB         	0x08
@@ -464,10 +466,10 @@
 #define		DI_2840         	0x01
 
 
-#define	BUS_8_BIT	0x00
-#define	MAX_OFFSET_8BIT	0x0f
-#define	BUS_16_BIT	0x01
 #define	MAX_OFFSET_16BIT	0x08
+#define	BUS_8_BIT	0x00
 #define	SCB_LIST_NULL	0xff
 #define	SG_SIZEOF	0x08
+#define	MAX_OFFSET_8BIT	0x0f
 #define	BUS_32_BIT	0x02
+#define	BUS_16_BIT	0x01
diff -u --recursive --new-file v2.1.70/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.1.70/linux/drivers/scsi/aic7xxx_seq.h	Thu Aug 14 20:49:16 1997
+++ linux/drivers/scsi/aic7xxx_seq.h	Tue Dec  2 20:41:21 1997
@@ -26,8 +26,8 @@
 	0x50, 0x6a, 0x60, 0x00,
 	0xff, 0x90, 0x4a, 0x02,
 	0x00, 0xa1, 0xa1, 0x17,
-	0xff, 0x6c, 0x5b, 0x02,
-	0xff, 0x5b, 0x27, 0x1c,
+	0xff, 0x6c, 0x59, 0x02,
+	0xff, 0x59, 0x27, 0x1c,
 	0xff, 0x4a, 0x90, 0x02,
 	0x00, 0x65, 0xaa, 0x17,
 	0x00, 0x6a, 0x52, 0x17,
@@ -37,7 +37,7 @@
 	0x00, 0x65, 0xbb, 0x17,
 	0x10, 0x6a, 0x60, 0x00,
 	0x00, 0x65, 0x03, 0x10,
-	0xff, 0x5b, 0x90, 0x02,
+	0xff, 0x59, 0x90, 0x02,
 	0xff, 0x58, 0xb3, 0x02,
 	0x10, 0x6a, 0x60, 0x00,
 	0x00, 0x65, 0x03, 0x10,
@@ -217,7 +217,7 @@
 	0x00, 0xb9, 0x77, 0x17,
 	0xff, 0xa2, 0xda, 0x1e,
 	0x71, 0x6a, 0x91, 0x00,
-	0x40, 0x5b, 0xda, 0x18,
+	0x40, 0x59, 0xda, 0x18,
 	0xff, 0xb9, 0xb3, 0x02,
 	0x00, 0x65, 0xe7, 0x10,
 	0x20, 0xa0, 0xe0, 0x1a,
@@ -226,9 +226,9 @@
 	0x00, 0xa1, 0xa1, 0x17,
 	0xff, 0x49, 0x6d, 0x02,
 	0xff, 0x4a, 0x90, 0x02,
-	0xff, 0x5a, 0x64, 0x02,
-	0x00, 0x59, 0xe1, 0x1c,
-	0x01, 0x59, 0x59, 0x06,
+	0xff, 0x5b, 0x64, 0x02,
+	0x00, 0x5a, 0xe1, 0x1c,
+	0x01, 0x5a, 0x5a, 0x06,
 	0xff, 0xb9, 0x9d, 0x02,
 	0x02, 0x6a, 0x91, 0x00,
 	0x08, 0xa0, 0xe7, 0x1e,
@@ -255,8 +255,8 @@
 	0xff, 0x66, 0x66, 0x06,
 	0xff, 0x64, 0xf7, 0x1a,
 	0x41, 0x6a, 0x91, 0x00,
-	0x20, 0x5b, 0xcd, 0x1c,
-	0x80, 0x5b, 0xcf, 0x18,
+	0x20, 0x59, 0xcd, 0x1c,
+	0x80, 0x59, 0xcf, 0x18,
 	0x10, 0x4c, 0x03, 0x00,
 	0x00, 0x65, 0xcf, 0x10,
 	0x04, 0xa0, 0xa0, 0x00,
@@ -276,10 +276,10 @@
 	0x07, 0x64, 0x64, 0x02,
 	0x00, 0x42, 0x42, 0x00,
 	0x00, 0x42, 0xa1, 0x17,
-	0xff, 0x6c, 0x5b, 0x02,
-	0xff, 0x5b, 0x28, 0x19,
-	0xff, 0x5b, 0x18, 0x1d,
-	0xff, 0x5b, 0x90, 0x02,
+	0xff, 0x6c, 0x59, 0x02,
+	0xff, 0x59, 0x28, 0x19,
+	0xff, 0x59, 0x18, 0x1d,
+	0xff, 0x59, 0x90, 0x02,
 	0x04, 0xa0, 0x2d, 0x1f,
 	0x00, 0x65, 0x2a, 0x11,
 	0xff, 0x06, 0x6a, 0x02,
@@ -289,11 +289,11 @@
 	0xe0, 0x4c, 0x2d, 0x19,
 	0x20, 0x12, 0x2d, 0x19,
 	0x20, 0x41, 0x41, 0x00,
-	0x5b, 0x6a, 0x3a, 0x17,
+	0x59, 0x6a, 0x3a, 0x17,
 	0xff, 0x3f, 0x64, 0x02,
-	0x00, 0x5b, 0x65, 0x06,
+	0x00, 0x59, 0x65, 0x06,
 	0x00, 0x65, 0x2d, 0x13,
-	0xff, 0x5b, 0x90, 0x02,
+	0xff, 0x59, 0x90, 0x02,
 	0xff, 0x42, 0x64, 0x02,
 	0x00, 0xa1, 0x2d, 0x19,
 	0x20, 0xa0, 0x2d, 0x1f,
@@ -341,7 +341,7 @@
 	0x40, 0x41, 0x4e, 0x1b,
 	0x21, 0x6a, 0x91, 0x01,
 	0xff, 0x65, 0x90, 0x02,
-	0xff, 0x5b, 0x64, 0x02,
+	0xff, 0x59, 0x64, 0x02,
 	0x00, 0xb9, 0x56, 0x19,
 	0x04, 0xa0, 0x60, 0x1b,
 	0x01, 0x65, 0x65, 0x06,
@@ -349,7 +349,7 @@
 	0x00, 0x65, 0x52, 0x19,
 	0x00, 0x6a, 0xad, 0x17,
 	0x0d, 0x6a, 0x3d, 0x00,
-	0x00, 0x5b, 0x77, 0x17,
+	0x00, 0x59, 0x77, 0x17,
 	0xff, 0xa8, 0x5e, 0x1f,
 	0x10, 0xa0, 0xa0, 0x00,
 	0x08, 0xa0, 0x4e, 0x1f,
diff -u --recursive --new-file v2.1.70/linux/fs/affs/Changes linux/fs/affs/Changes
--- v2.1.70/linux/fs/affs/Changes	Thu Jul 17 10:06:06 1997
+++ linux/fs/affs/Changes	Tue Dec  2 22:25:07 1997
@@ -12,15 +12,95 @@
   apparently not causing the failure, as directory
   reads basically work (but all files are of size 0).
   Alas, I've got no alpha to debug. :-(
-- If an affs mounted filesystem is exported via
-  nfs, it cannot be written to. 
-  As soon as I have my network up and running, I'll
-  try to fix this.
+
 - The partition checker (drivers/block/genhd.c)
   doesn't work with devices which have 256 byte
   blocks (some very old SCSI drives). 
 
+- The feature to automatically make the fs clean
+  might leave a trashed file system with the
+  bitmap flag set valid.
+
+- The blocks from deleted directories are 
+  sometimes reclaimed only at umount time.
+
 Please direct bug reports to: hjw@zvw.de
+
+Version 3.6
+-----------
+
+- dentry changes. (Thanks to Jes Sorensen for his help.)
+
+- Fixed bug in balloc(): Superblock was not set dirty after
+  the bitmap was changed, so the bitmap wasn't sync'd.
+
+- Fixed nasty bug in find_new_zone(): If the current
+  zone number was zero, the loop didn't terminate,
+  causing a solid lock-up.
+
+- Removed support for old-style directory reads.
+
+- Fixed bug in add_entry(): When doing a sorted insert,
+  the pointer to the next entry in the hash chain wasn't
+  correctly byte-swapped. Since most of the users of the
+  affs use it on a 68k, they didn't notice. But why did
+  I not find this during my tests?
+
+- Fixed some oversights (version wasn't updated on some
+  directory changes).
+
+- Handling of hard links rewritten. To the VFS
+  they appear now as normal unix links. They are
+  now resolved only once in lookup(). The backside
+  is that unlink(), rename() and rmdir() have to
+  be smart about them, but the result is worth the
+  effort. This also led to some code cleanup.
+
+- Changed name type to unsigned char; the test for
+  invalid filenames didn't work correctly.
+  (Thanks to Michael Krause for pointing at this.)
+
+- Changed mapping of executable flag.
+
+- Changed all network byte-order macros to the
+  recommended ones.
+
+- Added a remount function, so attempts to remount
+  a dircache filesystem or one with errors read/write
+  can be trapped. Previously, ro remounts didn't
+  flush the super block, and rw remounts didn't
+  create allocation zones ...
+
+- Call shrink_dcache_parent() in rmdir().
+  (Thanks to Bill Hawes.)
+
+- Permission checks in unlink().
+
+- Allow mounting of volumes with superfluous
+  bitmap pointers read only, also allows them
+  to be remounted read/write.
+
+- Owner/Group defaults now to the fs user (i.e.
+  the one that mounted it) instead of root. This
+  obsoletes the mount options uid and gid.
+
+- Argument to volume option could overflow the
+  name buffer. It is now silently truncated to
+  30 characters. (Damnit! This kind of bug
+  is too embarrassing.)
+
+- Split inode.c into 2 files, the superblock
+  routines desperately wanted their own file.
+
+- truncate() didn't allocate an extension block
+  cache. If a file was extended by means of
+  truncate(), this led to an Oops.
+
+- fsuser is now checked last.
+
+- rename() will not ignore changes in filename
+  casing any more (though mv(1) still won't allow
+  you to do "mv oldname OldName").
 
 Version 3.5
 -----------
diff -u --recursive --new-file v2.1.70/linux/fs/affs/Makefile linux/fs/affs/Makefile
--- v2.1.70/linux/fs/affs/Makefile	Mon May  6 02:44:31 1996
+++ linux/fs/affs/Makefile	Tue Dec  2 22:25:07 1997
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := affs.o
-O_OBJS   := namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
+O_OBJS   := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.70/linux/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c
--- v2.1.70/linux/fs/affs/amigaffs.c	Thu Jul 17 10:06:06 1997
+++ linux/fs/affs/amigaffs.c	Tue Dec  2 22:25:07 1997
@@ -8,6 +8,7 @@
  *  Please send bug reports to: hjw@zvw.de
  */
 
+#define DEBUG 0
 #include <stdarg.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
@@ -30,7 +31,7 @@
    pointed to by FH_DATA.  The length of the name is returned. */
 
 int
-affs_get_file_name(int bsize, void *fh_data, char **name)
+affs_get_file_name(int bsize, void *fh_data, unsigned char **name)
 {
 	struct file_end *file_end;
 
@@ -42,100 +43,142 @@
 		*name = "***BAD_FILE***";
 		return 14;
         }
-	*name = (char *)&file_end->file_name[1];
+	*name = (unsigned char *)&file_end->file_name[1];
         return file_end->file_name[0];
 }
 
-/* Find the predecessor in the hash chain */
+/* Insert a header block (file) into the directory (next).
+ * This routine assumes that the caller has the superblock locked.
+ */
 
 int
-affs_fix_hash_pred(struct inode *startino, int startoffset, s32 key, s32 newkey)
+affs_insert_hash(unsigned long next, struct buffer_head *file, struct inode *inode)
 {
-	struct buffer_head	*bh = NULL;
-	s32			 nextkey;
-	s32			 ptype, stype;
-	int			 retval;
+	struct buffer_head	*bh;
+	s32			 ino;
+	int			 offset;
+
+	offset = affs_hash_name(FILE_END(file->b_data,inode)->file_name+1,
+				FILE_END(file->b_data,inode)->file_name[0],
+				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+	ino    = be32_to_cpu(((struct dir_front *)file->b_data)->own_key);
+
+	pr_debug("AFFS: insert_hash(dir_ino=%lu,ino=%d)\n",next,ino);
+
+	FILE_END(file->b_data,inode)->parent = cpu_to_be32(next);
 
-	nextkey = startino->i_ino;
-	retval  = -ENOENT;
-	lock_super(startino->i_sb);
 	while (1) {
-		pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n",nextkey,startoffset);
-		if (nextkey == 0)
+		if (!(bh = affs_bread(inode->i_dev,next,AFFS_I2BSIZE(inode))))
+			return -EIO;
+		next = be32_to_cpu(((s32 *)bh->b_data)[offset]);
+		if (!next || next > ino)
 			break;
-		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+		offset = AFFS_I2BSIZE(inode) / 4 - 4;
+		affs_brelse(bh);
+	}
+
+	DIR_END(file->b_data,inode)->hash_chain = cpu_to_be32(next);
+	((s32 *)bh->b_data)[offset]             = cpu_to_be32(ino);
+	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+
+	return 0;
+}
+/* Remove a header block from it's hash table (directory).
+ * 'inode' may be any inode on the partition, it's only
+ * used for calculating the block size and superblock
+ * reference.
+ */
+
+int
+affs_remove_hash(struct buffer_head *dbh, struct inode *inode)
+{
+	s32			 ownkey;
+	s32			 key;
+	s32			 ptype;
+	s32			 stype;
+	int			 offset;
+	int			 retval;
+	struct buffer_head	*bh;
+
+	ownkey = be32_to_cpu(((struct dir_front *)dbh->b_data)->own_key);
+	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->parent);
+	offset = affs_hash_name(FILE_END(dbh->b_data,inode)->file_name+1,
+				FILE_END(dbh->b_data,inode)->file_name[0],
+				AFFS_I2FSTYPE(inode),AFFS_I2HSIZE(inode)) + 6;
+	pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n",key,ownkey,offset-6);
+	retval = -ENOENT;
+
+	lock_super(inode->i_sb);
+	while (key) {
+		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+			retval = -EIO;
 			break;
-		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+		}
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)
 		    || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
 					    stype != ST_LINKFILE && stype != ST_LINKDIR &&
 					    stype != ST_ROOT && stype != ST_SOFTLINK)) {
-			affs_error(startino->i_sb,"affs_fix_hash_pred",
-				"Bad block in link chain (ptype=%d, stype=%d)",ptype,stype);
+			affs_error(inode->i_sb,"affs_remove_hash",
+				"Bad block in hash chain (key=%d, ptype=%d, stype=%d, ownkey=%d)",
+				key,ptype,stype,ownkey);
 			affs_brelse(bh);
+			retval = -EINVAL;
 			break;
 		}
-		nextkey = htonl(((s32 *)bh->b_data)[startoffset]);
-		if (nextkey == key) {
-			((s32 *)bh->b_data)[startoffset] = newkey;
-			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+		key = be32_to_cpu(((s32 *)bh->b_data)[offset]);
+		if (ownkey == key) {
+			((s32 *)bh->b_data)[offset] = FILE_END(dbh->b_data,inode)->hash_chain;
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			retval = 0;
 			break;
 		}
 		affs_brelse(bh);
-		startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
+		offset = AFFS_I2BSIZE(inode) / 4 - 4;
 	}
-	unlock_super(startino->i_sb);
+	unlock_super(inode->i_sb);
 
 	return retval;
 }
 
-/* Remove inode from link chain */
+/* Remove header from link chain */
 
 int
-affs_fix_link_pred(struct inode *startino, s32 key, s32 newkey)
+affs_remove_link(struct buffer_head *dbh, struct inode *inode)
 {
-	struct buffer_head	*bh = NULL;
-	s32			 nextkey;
-	int			 offset;
-	s32			 etype = 0;
-	s32			 ptype, stype;
 	int			 retval;
-
-	offset  = AFFS_I2BSIZE(startino) / 4 - 10;
-	nextkey = startino->i_ino;
-	retval  = -ENOENT;
-	lock_super(startino->i_sb);
-	while (1) {
-		if (nextkey == 0)
-			break;
-		pr_debug("AFFS: find_link_pred(): next key=%d\n",nextkey);
-		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
-			break;
-		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
-		    || ptype != T_SHORT) {
-			affs_brelse(bh);
+	s32			 key;
+	s32			 ownkey;
+	s32			 ptype;
+	s32			 stype;
+	struct buffer_head	*bh;
+
+	ownkey = be32_to_cpu((DIR_FRONT(dbh)->own_key));
+	key    = be32_to_cpu(FILE_END(dbh->b_data,inode)->original);
+	retval = -ENOENT;
+
+	pr_debug("AFFS: remove_link(link=%d, original=%d)\n",ownkey,key);
+
+	lock_super(inode->i_sb);
+	while (key) {
+		if (!(bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+			retval = -EIO;
 			break;
 		}
-		if (!etype) {
-			if (stype != ST_FILE && stype != ST_USERDIR) {
-				affs_brelse(bh);
-				break;
-			}
-			if (stype == ST_FILE)
-				etype = ST_LINKFILE;
-			else
-				etype = ST_LINKDIR;
-		} else if (stype != etype) {
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype)) {
+			affs_error(inode->i_sb,"affs_remove_link","Checksum error (block %d)",key);
 			affs_brelse(bh);
-			retval = -EPERM;
+			retval = -EINVAL;
 			break;
 		}
-		nextkey = htonl(((s32 *)bh->b_data)[offset]);
-		if (nextkey == key) {
-			FILE_END(bh->b_data,startino)->link_chain = newkey;
-			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+		key = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
+		if (ownkey == key) {
+			FILE_END(bh->b_data,inode)->link_chain =
+						FILE_END(dbh->b_data,inode)->link_chain;
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
 			retval = 0;
@@ -143,10 +186,102 @@
 		}
 		affs_brelse(bh);
 	}
-	unlock_super(startino->i_sb);
+	unlock_super(inode->i_sb);
+
 	return retval;
 }
 
+/* Remove a filesystem object. If the object to be removed has
+ * links to it, one of the links must be changed to inherit
+ * the file or directory. As above, any inode will do.
+ * The buffer will not be freed. If the header is a link, the
+ * block will be marked as free.
+ * This function returns a negative error number in case of
+ * an error, else 0 if the inode is to be deleted or 1 if not.
+ */
+
+int
+affs_remove_header(struct buffer_head *bh, struct inode *inode)
+{
+	struct buffer_head	*link_bh;
+	struct inode		*dir;
+	unsigned long		 link_ino;
+	unsigned long		 orig_ino;
+	unsigned int		 dir_ino;
+	int			 error;
+
+	pr_debug("AFFS: remove_header(key=%ld)\n",be32_to_cpu(DIR_FRONT(bh)->own_key));
+
+	/* Mark directory as changed.  We do this before anything else,
+	 * as it must be done anyway and doesn't hurt even if an
+	 * error occures later.
+	 */
+	dir = iget(inode->i_sb,be32_to_cpu(FILE_END(bh->b_data,inode)->parent));
+	if (!dir)
+		return -EIO;
+	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version++;
+	mark_inode_dirty(dir);
+	iput(dir);
+
+	orig_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->original);
+	if (orig_ino) {		/* This is just a link. Nothing much to do. */
+		pr_debug("      Removing link.\n");
+		if ((error = affs_remove_link(bh,inode)))
+			return error;
+		if ((error = affs_remove_hash(bh,inode)))
+			return error;
+		affs_free_block(inode->i_sb,be32_to_cpu(DIR_FRONT(bh)->own_key));
+		return 1;
+	}
+	
+	link_ino = be32_to_cpu(FILE_END(bh->b_data,inode)->link_chain);
+	if (link_ino) {		/* This is the complicated case. Yuck. */
+		pr_debug("      Removing original with links to it.\n");
+		/* Unlink the object and its first link from their directories. */
+		if ((error = affs_remove_hash(bh,inode)))
+			return error;
+		if (!(link_bh = affs_bread(inode->i_dev,link_ino,AFFS_I2BSIZE(inode))))
+			return -EIO;
+		if ((error = affs_remove_hash(link_bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		/* Fix link chain. */
+		if ((error = affs_remove_link(link_bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		/* Rename link to object. */
+		memcpy(FILE_END(bh->b_data,inode)->file_name,
+			FILE_END(link_bh->b_data,inode)->file_name,32);
+		/* Insert object into dir the link was in. */
+		dir_ino = be32_to_cpu(FILE_END(link_bh->b_data,inode)->parent);
+		if ((error = affs_insert_hash(dir_ino,bh,inode))) {
+			affs_brelse(link_bh);
+			return error;
+		}
+		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+		mark_buffer_dirty(bh,1);
+		affs_brelse(link_bh);
+		affs_free_block(inode->i_sb,link_ino);
+		/* Mark the link's parent dir as changed, too. */
+		if (!(dir = iget(inode->i_sb,dir_ino)))
+			return -EIO;
+		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		dir->i_version++;
+		mark_inode_dirty(dir);
+		iput(dir);
+		return 1;
+	}
+	/* Plain file/dir. This is the simplest case. */
+	pr_debug("      Removing plain file/dir.\n");
+	if ((error = affs_remove_hash(bh,inode)))
+		return error;
+	return 0;
+}
+
+
 /* Checksum a block, do various consistency checks and optionally return
    the blocks type number.  DATA points to the block.  If their pointers
    are non-null, *PTYPE and *STYPE are set to the primary and secondary
@@ -162,14 +297,14 @@
 
 	bsize /= 4;
 	if (ptype)
-		*ptype = htonl(((s32 *)data)[0]);
+		*ptype = be32_to_cpu(((s32 *)data)[0]);
 	if (stype)
-		*stype = htonl(((s32 *)data)[bsize - 1]);
+		*stype = be32_to_cpu(((s32 *)data)[bsize - 1]);
 
 	sum    = 0;
 	p      = data;
 	while (bsize--)
-		sum += htonl(*p++);
+		sum += be32_to_cpu(*p++);
 	return sum;
 }
 
@@ -180,9 +315,9 @@
 	u32	 cs;
 
 	cs   = affs_checksum_block(bsize,data,NULL,NULL);
-	ocs  = htonl (((u32 *)data)[cspos]);
+	ocs  = be32_to_cpu(((u32 *)data)[cspos]);
 	ocs -= cs;
-	((u32 *)data)[cspos] = htonl(ocs);
+	((u32 *)data)[cspos] = be32_to_cpu(ocs);
 }
 
 void
@@ -191,7 +326,7 @@
 	u32	 days;
 	u32	 minute;
 
-	secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
+	secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
 	if (secs < 0)
 		secs = 0;
 	days    = secs / 86400;
@@ -199,9 +334,9 @@
 	minute  = secs / 60;
 	secs   -= minute * 60;
 
-	ds->ds_Days   = htonl(days);
-	ds->ds_Minute = htonl(minute);
-	ds->ds_Tick   = htonl(secs * 50);
+	ds->ds_Days   = be32_to_cpu(days);
+	ds->ds_Minute = be32_to_cpu(minute);
+	ds->ds_Tick   = be32_to_cpu(secs * 50);
 }
 
 int
@@ -268,6 +403,7 @@
 	if (!(sb->s_flags & MS_RDONLY))
 		printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
 	sb->s_flags |= MS_RDONLY;
+	sb->u.affs_sb.s_flags |= SF_READONLY;	/* Don't allow to remount rw */
 }
 
 void
@@ -279,6 +415,50 @@
 	vsprintf(ErrorBuffer,fmt,args);
 	va_end(args);
 
-	printk(KERN_WARNING "AFFS error (device %s): %s(): %s\n",kdevname(sb->s_dev),
+	printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n",kdevname(sb->s_dev),
 		function,ErrorBuffer);
+}
+
+/* Check if the name is valid for a affs object. */
+
+int
+affs_check_name(const unsigned char *name, int len)
+{
+	int	 i;
+
+	if (len > 30)
+#ifdef AFFS_NO_TRUNCATE
+		return -ENAMETOOLONG;
+#else
+		len = 30;
+#endif
+
+	for (i = 0; i < len; i++) {
+		if (name[i] < ' ' || name[i] == ':'
+		    || (name[i] > 0x7e && name[i] < 0xa0))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* This function copies name to bstr, with at most 30
+ * characters length. The bstr will be prepended by
+ * a length byte.
+ * NOTE: The name will must be already checked by
+ *       affs_check_name()!
+ */
+
+int
+affs_copy_name(unsigned char *bstr, const unsigned char *name)
+{
+	int	 len;
+
+	for (len = 0; len < 30; len++) {
+		bstr[len + 1] = name[len];
+		if (name[len] == '\0')
+			break;
+	}
+	bstr[0] = len;
+	return len;
 }
diff -u --recursive --new-file v2.1.70/linux/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- v2.1.70/linux/fs/affs/bitmap.c	Tue May 13 22:41:13 1997
+++ linux/fs/affs/bitmap.c	Tue Dec  2 22:25:07 1997
@@ -91,7 +91,7 @@
 				block);
 	else {
 		sb->u.affs_sb.s_alloc[zone_no].az_free++;
-		((u32 *)bm->bm_bh->b_data)[0] = ntohl(htonl(((u32 *)bm->bm_bh->b_data)[0]) - blk);
+		((u32 *)bm->bm_bh->b_data)[0] = cpu_to_be32(be32_to_cpu(((u32 *)bm->bm_bh->b_data)[0]) - blk);
 		mark_buffer_dirty(bm->bm_bh,1);
 		sb->s_dirt = 1;
 	}
@@ -136,7 +136,7 @@
 	fwb = zone->z_bm->bm_firstblk + (i - 1) * 32;
 	lock_super(sb);
 	zone->z_start = i;
-	w   = ~htonl(bm[i]);
+	w   = ~be32_to_cpu(bm[i]);
 	fb  = find_first_zero_bit(&w,32);
 	if (fb > 31 || !test_and_clear_bit(fb ^ BO_EXBITS,&bm[i])) {
 		unlock_super(sb);
@@ -163,10 +163,11 @@
 			az->az_free--;
 		}
 	}
-	w     = ~w - htonl(bm[i]);
-	bm[0] = ntohl(htonl(bm[0]) + w);
+	w     = ~w - be32_to_cpu(bm[i]);
+	bm[0] = cpu_to_be32(be32_to_cpu(bm[0]) + w);
 	unlock_super(sb);
 	mark_buffer_dirty(zone->z_bm->bm_bh,1);
+	sb->s_dirt = 1;
 	zone->z_lru_time = jiffies;
 
 	return block;
@@ -208,8 +209,6 @@
 
 	}
 	while (1) {
-		if (i >= sb->u.affs_sb.s_num_az)
-			i = 0;
 		az = &sb->u.affs_sb.s_alloc[i];
 		if (!az->az_count) {
 			if (az->az_free > min) {
@@ -223,7 +222,9 @@
 			lusers   = az->az_count;
 			bestused = i;
 		}
-		if (++i == zone->z_az_no) {		/* Seen all */
+		if (++i >= sb->u.affs_sb.s_num_az)
+			i = 0;
+		if (i == zone->z_az_no) {		/* Seen all */
 			if (bestno >= 0) {
 				i = bestno;
 			} else {
@@ -279,7 +280,7 @@
 	}
 init_block:
 	if (!(bh = getblk(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
-		affs_error(inode->i_sb,"new_header","Cannot read block %d",block);
+		affs_error(inode->i_sb,"new_header","Cannot get block %d",block);
 		return 0;
 	}
 	memset(bh->b_data,0,AFFS_I2BSIZE(inode));
@@ -357,7 +358,7 @@
 
 init_block:
 	if (!(bh = getblk(inode->i_dev,block,sb->s_blocksize))) {
-		affs_error(inode->i_sb,"new_data","Cannot read block %d",block);
+		affs_error(inode->i_sb,"new_data","Cannot get block %d",block);
 		return 0;
 	}
 	memset(bh->b_data,0,sb->s_blocksize);
diff -u --recursive --new-file v2.1.70/linux/fs/affs/dir.c linux/fs/affs/dir.c
--- v2.1.70/linux/fs/affs/dir.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/affs/dir.c	Tue Dec  2 22:25:07 1997
@@ -13,6 +13,7 @@
  *
  */
 
+#define DEBUG 0
 #include <asm/uaccess.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
@@ -24,8 +25,7 @@
 #include <linux/amigaffs.h>
 
 static int affs_readdir(struct file *, void *, filldir_t);
-static ssize_t affs_dir_read(struct file * filp, char * buf,
-			     size_t count, loff_t *ppos);
+static ssize_t affs_dir_read(struct file *, char *, size_t, loff_t *);
 
 static struct file_operations affs_dir_operations = {
 	NULL,			/* lseek - default */
@@ -55,11 +55,15 @@
 	NULL,			/* mknod */
 	affs_rename,		/* rename */
 	NULL,			/* readlink */
+	NULL,			/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permissions */
+	NULL,			/* permissions */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
 };
 
 static ssize_t
@@ -76,15 +80,14 @@
 	int			 hash_pos;
 	int			 chain_pos;
 	unsigned long		 ino;
-	unsigned long		 old;
 	int			 stored;
-	char			 *name;
-	struct buffer_head	 *dir_bh;
-	struct buffer_head	 *fh_bh;
-	struct inode		 *dir;
-	struct inode 		 *inode = filp->f_dentry->d_inode;
+	unsigned char		*name;
+	struct buffer_head	*dir_bh;
+	struct buffer_head	*fh_bh;
+	struct inode		*dir;
+	struct inode		*inode = filp->f_dentry->d_inode;
 
-	pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos);
+	pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos);
 
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
@@ -93,8 +96,7 @@
 	dir_bh = NULL;
 	fh_bh  = NULL;
 	dir    = NULL;
-	old    = filp->f_pos & 0x80000000;
-	filp->f_pos &= 0x7FFFFFFF;
+	ino    = inode->i_ino;
 
 	if (filp->f_pos == 0) {
 		filp->private_data = (void *)0;
@@ -106,18 +108,11 @@
 	}
 	if (filp->f_pos == 1) {
 		if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) {
-			filp->f_pos |= 0x80000000;
 			return stored;
 		}
 		filp->f_pos = 2;
 		stored++;
 	}
-
-	/* Read original if this is a link */
-	ino = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
-	if (!(dir = iget(inode->i_sb,ino)))
-		return stored;
-	
 	chain_pos = (filp->f_pos - 2) & 0xffff;
 	hash_pos  = (filp->f_pos - 2) >> 16;
 	if (chain_pos == 0xffff) {
@@ -126,27 +121,29 @@
 		hash_pos++;
 		filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
 	}
-	if (!(dir_bh = affs_bread(inode->i_dev,ino,AFFS_I2BSIZE(inode))))
+	if (!(dir_bh = affs_bread(inode->i_dev,inode->i_ino,
+				  AFFS_I2BSIZE(inode))))
 		goto readdir_done;
 
-	while (!stored || !old) {
+	while (1) {
 		while (hash_pos < AFFS_I2HSIZE(inode) &&
 		     !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
 			hash_pos++;
 		if (hash_pos >= AFFS_I2HSIZE(inode))
-			goto readdir_done;
+			break;
 		
-		i = htonl(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
+		i = be32_to_cpu(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
 		j = chain_pos;
+
 		/* If the directory hasn't changed since the last call to readdir(),
 		 * we can jump directly to where we left off.
 		 */
-		if (filp->private_data && filp->f_version == dir->i_version) {
+		if (filp->private_data && filp->f_version == inode->i_version) {
 			i = (s32)filp->private_data;
 			j = 0;
 			pr_debug("AFFS: readdir() left off=%d\n",i);
 		}
-		filp->f_version = dir->i_version;
+		filp->f_version = inode->i_version;
 		pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos);
 		while (i) {
 			if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
@@ -154,7 +151,7 @@
 				goto readdir_done;
 			}
 			ino = i;
-			i   = htonl(FILE_END(fh_bh->b_data,inode)->hash_chain);
+			i   = be32_to_cpu(FILE_END(fh_bh->b_data,inode)->hash_chain);
 			if (j == 0)
 				break;
 			affs_brelse(fh_bh);
@@ -163,7 +160,7 @@
 		}
 		if (fh_bh) {
 			namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
-			pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%d\n",
+			pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n",
 				 namelen,name,ino,i);
 			filp->private_data = (void *)ino;
 			if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0)
@@ -182,10 +179,8 @@
 	}
 
 readdir_done:
-	filp->f_pos |= old;
 	affs_brelse(dir_bh);
 	affs_brelse(fh_bh);
-	iput(dir);
 	pr_debug("AFFS: readdir()=%d\n",stored);
 	return stored;
 }
diff -u --recursive --new-file v2.1.70/linux/fs/affs/file.c linux/fs/affs/file.c
--- v2.1.70/linux/fs/affs/file.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/affs/file.c	Tue Dec  2 22:25:07 1997
@@ -36,13 +36,10 @@
 #endif
 
 static int affs_bmap(struct inode *inode, int block);
-static struct buffer_head * affs_getblock(struct inode *inode, s32 block);
-static ssize_t affs_file_read_ofs(struct file *filp, char *buf,
-				  size_t count, loff_t *ppos);
-static ssize_t affs_file_write(struct file *filp, const char *buf,
-			       size_t count, loff_t *ppos);
-static ssize_t affs_file_write_ofs(struct file *filp, const char *buf,
-				   size_t count, loff_t *ppos);
+static struct buffer_head *affs_getblock(struct inode *inode, s32 block);
+static ssize_t affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos);
+static ssize_t affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
+static ssize_t affs_file_write_ofs(struct file *filp, const char *buf, size_t cnt, loff_t *ppos);
 static int affs_release_file(struct inode *inode, struct file *filp);
 static int alloc_ext_cache(struct inode *inode);
 
@@ -56,7 +53,11 @@
 	generic_file_mmap,	/* mmap */
 	NULL,			/* no special open */
 	affs_release_file,	/* release */
-	file_fsync		/* brute force, but works */
+	file_fsync,		/* brute force, but works */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	NULL			/* lock */
 };
 
 struct inode_operations affs_file_inode_operations = {
@@ -71,12 +72,15 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* follow_link */
 	generic_readpage,	/* readpage */
 	NULL,			/* writepage */
 	affs_bmap,		/* bmap */
 	affs_truncate,		/* truncate */
 	NULL,			/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
 };
 
 static struct file_operations affs_file_operations_ofs = {
@@ -89,7 +93,11 @@
 	NULL,			/* mmap */
 	NULL,			/* no special open */
 	affs_release_file,	/* release */
-	file_fsync		/* brute force, but works */
+	file_fsync,		/* brute force, but works */
+	NULL,			/* fasync */
+	NULL,			/* check_media_change */
+	NULL,			/* revalidate */
+	NULL			/* lock */
 };
 
 struct inode_operations affs_file_inode_operations_ofs = {
@@ -104,12 +112,15 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	NULL,			/* readlink */
+	NULL,			/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	affs_truncate,		/* truncate */
 	NULL,			/* permission */
-	NULL			/* smap */
+	NULL,			/* smap */
+	NULL,			/* updatepage */
+	NULL			/* revalidate */
 };
 
 #define AFFS_ISINDEX(x)	((x < 129) ||				\
@@ -255,7 +266,7 @@
 
 	/* Try to find the requested key in the cache.
 	 * In order to speed this up as much as possible,
-	 * the cache line lookup is done in a seperate
+	 * the cache line lookup is done in a separate
 	 * step.
 	 */
 
@@ -306,7 +317,7 @@
 			affs_brelse(bh);
 			return 0;
 		}
-		nkey = htonl(FILE_END(bh->b_data,inode)->extension);
+		nkey = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
 		if (block < AFFS_I2HSIZE(inode)) {
 			/* Fill cache as much as possible */
 			if (keycount) {
@@ -314,7 +325,7 @@
 				keycount     = keycount < AFFS_I2HSIZE(inode) - block ? keycount :
 						AFFS_I2HSIZE(inode) - block;
 				for (i = 0; i < keycount; i++)
-					kc->kc_keys[i] = htonl(AFFS_BLOCK(bh->b_data,inode,block + i));
+					kc->kc_keys[i] = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block + i));
 				kc->kc_last = kc->kc_first + i - 1;
 			}
 			break;
@@ -331,11 +342,20 @@
 	kc->kc_this_key = key;
 	kc->kc_this_seq = ext;
 	kc->kc_next_key = nkey;
-	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+	key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	return key;
 }
 
+/* With the affs, getting a random block from a file is not
+ * a simple business. Since this fs does not allow holes,
+ * it may be neccessary to allocate all the missing blocks
+ * inbetween, as well as some new extension blocks. The OFS
+ * is even worse: All data blocks contain pointers to the
+ * next ones, so you have to fix [n-1] after allocating [n].
+ * What a mess.
+ */
+
 static struct buffer_head *
 affs_getblock(struct inode *inode, s32 block)
 {
@@ -368,7 +388,7 @@
 	pt     = ext ? T_LIST : T_SHORT;
 	pbh    = NULL;
 
-	for (;;) {
+	for (;;) {	/* Loop over header block and extension blocks */
 		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return NULL;
@@ -379,12 +399,12 @@
 			affs_brelse(bh);
 			return NULL;
 		}
-		j  = htonl(((struct file_front *)bh->b_data)->block_count);
+		j  = be32_to_cpu(((struct file_front *)bh->b_data)->block_count);
 		cf = 0;
 		while (j < AFFS_I2HSIZE(inode) && j <= block) {
 			if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) {
 				if (j > 0)
-					pbh = affs_bread(inode->i_dev,ntohl(AFFS_BLOCK(bh->b_data,inode,j - 1)),
+					pbh = affs_bread(inode->i_dev,cpu_to_be32(AFFS_BLOCK(bh->b_data,inode,j - 1)),
 							 AFFS_I2BSIZE(inode));
 				else
 					pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock);
@@ -406,7 +426,7 @@
 				continue;
 			}
 			unlock_super(inode->i_sb);
-			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(nkey);
+			AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey);
 			if (ofs) {
 				ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode));
 				if (!ebh) {
@@ -417,12 +437,12 @@
 					break;
 				}
 				inode->u.affs_i.i_lastblock++;
-				DATA_FRONT(ebh)->primary_type    = ntohl(T_DATA);
-				DATA_FRONT(ebh)->header_key      = ntohl(inode->i_ino);
-				DATA_FRONT(ebh)->sequence_number = ntohl(inode->u.affs_i.i_lastblock + 1);
+				DATA_FRONT(ebh)->primary_type    = cpu_to_be32(T_DATA);
+				DATA_FRONT(ebh)->header_key      = cpu_to_be32(inode->i_ino);
+				DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1);
 				if (pbh) {
-					DATA_FRONT(pbh)->data_size = ntohl(AFFS_I2BSIZE(inode) - 24);
-					DATA_FRONT(pbh)->next_data = ntohl(nkey);
+					DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24);
+					DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey);
 					affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5);
 					mark_buffer_dirty(pbh,0);
 					mark_buffer_dirty(ebh,0);
@@ -437,7 +457,7 @@
 			if (pt == T_SHORT)
 				((struct file_front *)bh->b_data)->first_data =
 								AFFS_BLOCK(bh->b_data,inode,0);
-			((struct file_front *)bh->b_data)->block_count = ntohl(j);
+			((struct file_front *)bh->b_data)->block_count = cpu_to_be32(j);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 		}
@@ -453,7 +473,7 @@
 		}
 
 		block -= AFFS_I2HSIZE(inode);
-		key    = htonl(FILE_END(bh->b_data,inode)->extension);
+		key    = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
 		if (!key) {
 			key = affs_new_header(inode);
 			if (!key) {
@@ -465,12 +485,12 @@
 				affs_free_block(inode->i_sb,key);
 				return NULL;
 			}
-			((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
-			((struct file_front *)ebh->b_data)->own_key      = ntohl(key);
-			FILE_END(ebh->b_data,inode)->secondary_type      = ntohl(ST_FILE);
-			FILE_END(ebh->b_data,inode)->parent              = ntohl(inode->i_ino);
+			((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST);
+			((struct file_front *)ebh->b_data)->own_key      = cpu_to_be32(key);
+			FILE_END(ebh->b_data,inode)->secondary_type      = cpu_to_be32(ST_FILE);
+			FILE_END(ebh->b_data,inode)->parent              = cpu_to_be32(inode->i_ino);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
-			FILE_END(bh->b_data,inode)->extension = ntohl(key);
+			FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,1);
 			affs_brelse(bh);
@@ -487,8 +507,8 @@
 	}
 	kc->kc_this_key = key;
 	kc->kc_this_seq = ext;
-	kc->kc_next_key = htonl(FILE_END(bh->b_data,inode)->extension);
-	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+	kc->kc_next_key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension);
+	key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block));
 	affs_brelse(bh);
 	if (!key)
 		return NULL;
@@ -499,14 +519,15 @@
 static ssize_t
 affs_file_read_ofs(struct file *filp, char *buf, size_t count, loff_t *ppos)
 {
-	struct inode *inode = filp->f_dentry->d_inode;
-	char *start;
-	ssize_t left, offset, size, sector;
-	ssize_t blocksize;
-	struct buffer_head *bh;
-	void *data;
+	struct inode		*inode = filp->f_dentry->d_inode;
+	char			*start;
+	ssize_t			 left, offset, size, sector;
+	ssize_t			 blocksize;
+	struct buffer_head	*bh;
+	void			*data;
 
-	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,(long)*ppos,count);
+	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,
+		 (unsigned long)*ppos,count);
 
 	if (!inode) {
 		affs_error(inode->i_sb,"file_read_ofs","Inode = NULL");
@@ -553,40 +574,25 @@
 	ssize_t			 c;
 	ssize_t			 blocksize;
 	struct buffer_head	*bh;
-	struct inode		*ino;
 	char			*p;
 
-	/* Not that I wanted to be POSIX compliant ... */
 	if (!count)
 		return 0;
 	pr_debug("AFFS: file_write(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
-		(unsigned long)*ppos,count);
+		 (unsigned long)*ppos,count);
 
-	ino = NULL;
 	if (!inode) {
 		affs_error(inode->i_sb,"file_write","Inode = NULL");
 		return -EINVAL;
 	}
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			affs_error(inode->i_sb,"file_write",
-				   "Could not follow link from inode %lu to %d",
-			           inode->i_ino,inode->u.affs_i.i_original);
-			return -EINVAL;
-		}
-		inode = ino;
-	}
 	if (!S_ISREG(inode->i_mode)) {
 		affs_error(inode->i_sb,"file_write",
 			   "Trying to write to non-regular file (mode=%07o)",
 			   inode->i_mode);
-		iput(inode);
 		return -EINVAL;
 	}
 	if (!inode->u.affs_i.i_ec) {
 		if (alloc_ext_cache(inode)) {
-			iput(inode);
 			return -ENOMEM;
 		}
 	}
@@ -617,8 +623,14 @@
 				break;
 			}
 		}
-		p = (pos % blocksize) + bh->b_data;
-		copy_from_user(p,buf,c);
+		p  = (pos % blocksize) + bh->b_data;
+		c -= copy_from_user(p,buf,c);
+		if (!c) {
+			affs_brelse(bh);
+			if (!written)
+				written = -EFAULT;
+			break;
+		}
 		update_vm_cache(inode,pos,p,c);
 		mark_buffer_uptodate(bh,1);
 		mark_buffer_dirty(bh,0);
@@ -630,22 +642,20 @@
 	if (pos > inode->i_size)
 		inode->i_size = pos;
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-	*ppos = pos;
+	*ppos          = pos;
 	mark_inode_dirty(inode);
-	iput(ino);
 	return written;
 }
 
 static ssize_t
 affs_file_write_ofs(struct file *filp, const char *buf, size_t count, loff_t *ppos)
 {
-	struct inode		*inode = filp->f_dentry->d_inode; 
+	struct inode		*inode = filp->f_dentry->d_inode;
 	off_t			 pos;
 	ssize_t			 written;
 	ssize_t			 c;
 	ssize_t			 blocksize;
 	struct buffer_head	*bh;
-	struct inode		*ino;
 	char			*p;
 
 	pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
@@ -657,24 +667,17 @@
 		affs_error(inode->i_sb,"file_write_ofs","Inode = NULL");
 		return -EINVAL;
 	}
-	ino = NULL;
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			affs_error(inode->i_sb,"file_write_ofs",
-				   "Could not follow link from inode %lu to %d",
-			           inode->i_ino,inode->u.affs_i.i_original);
-			return -EINVAL;
-		}
-		inode = ino;
-	}
 	if (!S_ISREG(inode->i_mode)) {
 		affs_error(inode->i_sb,"file_write_ofs",
 			   "Trying to write to non-regular file (mode=%07o)",
 			   inode->i_mode);
-		iput(inode);
 		return -EINVAL;
 	}
+	if (!inode->u.affs_i.i_ec) {
+		if (alloc_ext_cache(inode)) {
+			return -ENOMEM;
+		}
+	}
 	if (filp->f_flags & O_APPEND)
 		pos = inode->i_size;
 	else
@@ -703,14 +706,20 @@
 				break;
 			}
 		}
-		p = (pos % blocksize) + bh->b_data + 24;
-		copy_from_user(p,buf,c);
+		p  = (pos % blocksize) + bh->b_data + 24;
+		c -= copy_from_user(p,buf,c);
+		if (!c) {
+			affs_brelse(bh);
+			if (!written)
+				written = -EFAULT;
+			break;
+		}
 		update_vm_cache(inode,pos,p,c);
 
 		pos     += c;
 		buf     += c;
 		written += c;
-		DATA_FRONT(bh)->data_size = ntohl(htonl(DATA_FRONT(bh)->data_size) + c);
+		DATA_FRONT(bh)->data_size = cpu_to_be32(be32_to_cpu(DATA_FRONT(bh)->data_size) + c);
 		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 		mark_buffer_uptodate(bh,1);
 		mark_buffer_dirty(bh,0);
@@ -721,7 +730,6 @@
 	*ppos = pos;
 	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	mark_inode_dirty(inode);
-	iput(ino);
 	return written;
 }
 
@@ -730,7 +738,6 @@
 {
 	struct buffer_head	*bh;
 	struct buffer_head	*ebh;
-	struct inode		*ino;
 	struct affs_zone	*zone;
 	int	 first;
 	int	 block;
@@ -745,20 +752,16 @@
 
 	pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
 
-	ino = NULL;
-	if (inode->u.affs_i.i_original) {
-		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
-		if (!ino) {
-			affs_error(inode->i_sb,"truncate","Cannot follow link from %lu to %d",
-			           inode->i_ino,inode->u.affs_i.i_original);
-			return;
-		}
-		inode = ino;
-	}
 	blocksize = AFFS_I2BSIZE(inode) - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0);
 	first = (inode->i_size + blocksize - 1) / blocksize;
 	if (inode->u.affs_i.i_lastblock < first - 1) {
-			bh = affs_getblock(inode,first - 1);
+		if (!inode->u.affs_i.i_ec) {
+			if (alloc_ext_cache(inode)) {
+				/* Fine! No way to indicate an error. What can we do? */
+				return;
+			}
+		}
+		bh = affs_getblock(inode,first - 1);
 
 		while (inode->u.affs_i.i_pa_cnt) {		/* Free any preallocated blocks */
 			affs_free_block(inode->i_sb,
@@ -778,12 +781,11 @@
 			inode->i_size = blocksize * (inode->u.affs_i.i_lastblock + 1);
 		} else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) {
 			rem = inode->i_size % blocksize;
-			DATA_FRONT(bh)->data_size = ntohl(rem ? rem : blocksize);
+			DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : blocksize);
 			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
 			mark_buffer_dirty(bh,0);
 		}
 		affs_brelse(bh);
-		iput(ino);
 		return;
 	}
 	ekey  = inode->i_ino;
@@ -794,8 +796,8 @@
 			affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey);
 			break;
 		}
-		ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
-		stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+		ptype = be32_to_cpu(((struct file_front *)bh->b_data)->primary_type);
+		stype = be32_to_cpu(FILE_END(bh->b_data,inode)->secondary_type);
 		if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
 		    LINK_END(bh->b_data,inode)->original == 0) {
 			pr_debug("AFFS: truncate(): dumping link\n");
@@ -812,7 +814,7 @@
 		freethis = first == 0 && ekey != inode->i_ino;
 		for (block = first; block < AFFS_I2HSIZE(inode); block++) {
 			keyp = &AFFS_BLOCK(bh->b_data,inode,block);
-			key  = htonl(*keyp);
+			key  = be32_to_cpu(*keyp);
 			if (key) {
 				*keyp = 0;
 				affs_free_block(inode->i_sb,key);
@@ -822,18 +824,18 @@
 			}
 		}
 		keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
-		key  = htonl(*keyp);
+		key  = be32_to_cpu(*keyp);
 		if (first <= AFFS_I2HSIZE(inode)) {
-			((struct file_front *)bh->b_data)->block_count = htonl(first);
+			((struct file_front *)bh->b_data)->block_count = be32_to_cpu(first);
 			first = 0;
 			*keyp = 0;
 			if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) && first > 0) {
-				block = htonl(AFFS_BLOCK(bh->b_data,inode,first - 1));
+				block = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,first - 1));
 				if ((ebh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
 					if(!(affs_checksum_block(AFFS_I2BSIZE(inode),ebh->b_data,
 					     &ptype,NULL))) {
 						rem = inode->i_size % blocksize;
-						rem = ntohl(rem ? blocksize : rem);
+						rem = cpu_to_be32(rem ? blocksize : rem);
 						((struct data_front *)ebh->b_data)->data_size = rem;
 						((struct data_front *)ebh->b_data)->next_data = 0;
 						affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
@@ -866,7 +868,6 @@
 		}
 	}
 
-	iput(ino);
 }
 
 static int
@@ -899,6 +900,8 @@
 {
 	s32	 key;
 	int	 i;
+
+	pr_debug("AFFS: alloc_ext_cache(ino=%lu)\n",inode->i_ino);
 
 	lock_super(inode->i_sb);
 	if (!inode->u.affs_i.i_ec) {
diff -u --recursive --new-file v2.1.70/linux/fs/affs/inode.c linux/fs/affs/inode.c
--- v2.1.70/linux/fs/affs/inode.c	Thu Sep 11 09:02:23 1997
+++ linux/fs/affs/inode.c	Tue Dec  2 22:25:07 1997
@@ -10,7 +10,7 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
-#include <linux/module.h>
+#define DEBUG 0
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/malloc.h>
@@ -30,106 +30,17 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-/* AmigaOS allows file names with up to 30 characters length.
- * Names longer than that will be silently truncated. If you
- * want to disallow this, comment out the following #define.
- * Creating filesystem objects with longer names will then
- * result in an error (ENAMETOOLONG).
- */
-/*#define NO_TRUNCATE */
-
 extern int *blk_size[];
 extern struct timezone sys_tz;
 
 #define MIN(a,b) (((a)<(b))?(a):(b))
 
-static int affs_notify_change(struct inode *inode, struct iattr *attr);
-static void affs_put_inode(struct inode *inode);
-static void affs_read_inode(struct inode *inode);
-static void affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
-static void affs_write_inode(struct inode *inode);
-
-static void
-affs_put_super(struct super_block *sb)
-{
-	int	 i;
-
-	pr_debug("affs_put_super()\n");
-
-	lock_super(sb);
-	for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
-		affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
-	if (!(sb->s_flags & MS_RDONLY)) {
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(1);
-		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
-	}
-
-	if (sb->u.affs_sb.s_flags & SF_PREFIX)
-		kfree(sb->u.affs_sb.s_prefix);
-	kfree(sb->u.affs_sb.s_bitmap);
-	affs_brelse(sb->u.affs_sb.s_root_bh);
-
-	/* I'm not happy with this. It would be better to save the previous
-	 * value of this devices blksize_size[][] in the super block and
-	 * restore it here, but with the affs superblock being quite large
-	 * already ...
-	 */
-	set_blocksize(sb->s_dev,BLOCK_SIZE);
-
-	sb->s_dev = 0;
-	unlock_super(sb);
-	MOD_DEC_USE_COUNT;
-	return;
-}
-
-static void
-affs_write_super(struct super_block *sb)
-{
-	int	 i, clean = 2;
-
-	if (!(sb->s_flags & MS_RDONLY)) {
-		lock_super(sb);
-		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
-			if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
-				if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
-					clean = 0;
-					break;
-				}
-			}
-		}
-		unlock_super(sb);
-		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(clean);
-		secs_to_datestamp(CURRENT_TIME,
-				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
-		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
-		sb->s_dirt = !clean;	/* redo until bitmap synced */
-	} else
-		sb->s_dirt = 0;
-
-	pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
-}
-
-static struct super_operations affs_sops = {
-	affs_read_inode,
-	affs_notify_change,
-	affs_write_inode,
-	affs_put_inode,
-	affs_put_super,
-	affs_write_super,
-	affs_statfs,
-	NULL			/* remount */
-};
-
 unsigned long
 affs_parent_ino(struct inode *dir)
 {
 	int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
 
-	if (!S_ISDIR (dir->i_mode)) {
+	if (!S_ISDIR(dir->i_mode)) {
 		affs_error(dir->i_sb,"parent_ino","Trying to get parent of non-directory");
 		return root_ino;
 	}
@@ -138,524 +49,10 @@
 	return dir->u.affs_i.i_parent;
 }
 
-static int
-parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
-		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
-{
-	char	*this_char, *value;
-	int	 f;
-
-	/* Fill in defaults */
-
-	*uid        = 0;
-	*gid        = 0;
-	*reserved   = 2;
-	*root       = -1;
-	*blocksize  = -1;
-	*prefix     = "/";
-	volume[0]   = ':';
-	volume[1]   = 0;
-	*mount_opts = 0;
-	if (!options)
-		return 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
-		f = 0;
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
-		if (!strcmp(this_char,"protect")) {
-			if (value) {
-				printk("AFFS: Option protect does not take an argument\n");
-				return 0;
-			}
-			*mount_opts |= SF_IMMUTABLE;
-		}
-		else if (!strcmp(this_char,"verbose")) {
-			if (value) {
-				printk("AFFS: Option verbose does not take an argument\n");
-				return 0;
-			}
-			*mount_opts |= SF_VERBOSE;
-		}
-		else if ((f = !strcmp(this_char,"uid")) || !strcmp(this_char,"setuid")) {
-			if (!value)
-				*uid = current->uid;
-			else if (!*value) {
-				printk("AFFS: Argument for uid option missing\n");
-				return 0;
-			} else {
-				*uid = simple_strtoul(value,&value,0);
-				if (*value)
-					return 0;
-				if (!f)
-					*mount_opts |= SF_SETUID;
-			}
-		}
-		else if ((f = !strcmp(this_char,"gid")) || !strcmp(this_char,"setgid")) {
-			if (!value)
-				*gid = current->gid;
-			else if (!*value) {
-				printk("AFFS: Argument for gid option missing\n");
-				return 0;
-			} else {
-				*gid = simple_strtoul(value,&value,0);
-				if (*value)
-					return 0;
-				if (!f)
-					*mount_opts |= SF_SETGID;
-			}
-		}
-		else if (!strcmp(this_char,"prefix")) {
-			if (!value) {
-				printk("AFFS: The prefix option requires an argument\n");
-				return 0;
-			}
-			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
-			if (!*prefix)
-				return 0;
-			strcpy(*prefix,value);
-			*mount_opts |= SF_PREFIX;
-		}
-		else if (!strcmp(this_char,"volume")) {
-			if (!value) {
-				printk("AFFS: The volume option requires an argument\n");
-				return 0;
-			}
-			if (strlen(value) > 30)
-				value[30] = 0;
-			strcpy(volume,value);
-		}
-		else if (!strcmp(this_char,"mode")) {
-			if (!value || !*value) {
-				printk("AFFS: The mode option requires an argument\n");
-				return 0;
-			}
-			*mode = simple_strtoul(value,&value,8) & 0777;
-			if (*value)
-				return 0;
-			*mount_opts |= SF_SETMODE;
-		}
-		else if (!strcmp(this_char,"reserved")) {
-			if (!value || !*value) {
-				printk("AFFS: The reserved option requires an argument\n");
-				return 0;
-			}
-			*reserved = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"root")) {
-			if (!value || !*value) {
-				printk("AFFS: The root option requires an argument\n");
-				return 0;
-			}
-			*root = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-		}
-		else if (!strcmp(this_char,"bs")) {
-			if (!value || !*value) {
-				printk("AFFS: The bs option requires an argument\n");
-				return 0;
-			}
-			*blocksize = simple_strtoul(value,&value,0);
-			if (*value)
-				return 0;
-			if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
-			    && *blocksize != 4096) {
-				printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
-				return 0;
-			}
-		}
-		/* Silently ignore the quota options */
-		else if (!strcmp (this_char, "grpquota")
-			 || !strcmp (this_char, "noquota")
-			 || !strcmp (this_char, "quota")
-			 || !strcmp (this_char, "usrquota"))
-			;
-		else {
-			printk("AFFS: Unrecognized mount option %s\n", this_char);
-			return 0;
-		}
-	}
-	return 1;
-}
-
-/* This function definitely needs to be split up. Some fine day I'll
- * hopefully have the guts to do so. Until then: sorry for the mess.
- */
-
-static struct super_block *
-affs_read_super(struct super_block *s,void *data, int silent)
-{
-	struct buffer_head	*bh = NULL;
-	struct buffer_head	*bb;
-	kdev_t			 dev = s->s_dev;
-	s32			 root_block;
-	int			 size;
-	u32			 chksum;
-	u32			*bm;
-	s32			 ptype, stype;
-	int			 mapidx;
-	int			 num_bm;
-	int			 i, j;
-	s32			 key;
-	int			 blocksize;
-	uid_t			 uid;
-	gid_t			 gid;
-	int			 reserved;
-	int			 az_no;
-	unsigned long		 mount_flags;
-	unsigned long		 offset;
-
-	pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
-
-	MOD_INC_USE_COUNT;
-
-	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
-	    &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
-		s->s_dev = 0;
-		printk(KERN_ERR "AFFS: Error parsing options\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-	lock_super(s);
-
-	/* Get the size of the device in 512-byte blocks.
-	 * If we later see that the partition uses bigger
-	 * blocks, we will have to change it.
-	 */
-
-	size = blksize_size[MAJOR(dev)][MINOR(dev)];
-	size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
-
-	s->u.affs_sb.s_bitmap  = NULL;
-	s->u.affs_sb.s_root_bh = NULL;
-	s->u.affs_sb.s_flags   = mount_flags;
-	s->u.affs_sb.s_mode    = i;
-	s->u.affs_sb.s_uid     = uid;
-	s->u.affs_sb.s_gid     = gid;
-
-	if (size == 0) {
-		s->s_dev = 0;
-		unlock_super(s);
-		printk(KERN_ERR "AFFS: Could not determine device size\n");
-		goto out;
-	}
-	s->u.affs_sb.s_partition_size = size;
-	s->u.affs_sb.s_reserved       = reserved;
-
-	/* Try to find root block. Its location depends on the block size. */
-
-	s->u.affs_sb.s_hashsize = 0;
-	if (blocksize > 0) {
-		i = blocksize;
-		j = blocksize;
-	} else {
-		i = 512;
-		j = 4096;
-	}
-	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
-		if (root_block < 0)
-			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
-		else
-			s->u.affs_sb.s_root_block = root_block;
-		set_blocksize(dev,blocksize);
-
-		/* The root block location that was calculated above is not
-		 * correct if the partition size is an odd number of 512-
-		 * byte blocks, which will be rounded down to a number of
-		 * 1024-byte blocks, and if there were an even number of
-		 * reserved blocks. Ideally, all partition checkers should
-		 * report the real number of blocks of the real blocksize,
-		 * but since this just cannot be done, we have to try to
-		 * find the root block anyways. In the above case, it is one
-		 * block behind the calculated one. So we check this one, too.
-		 */
-		for (num_bm = 0; num_bm < 2; num_bm++) {
-			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %u, "
-				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
-				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
-			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
-			if (!bh) {
-				printk(KERN_ERR "AFFS: Cannot read root block\n");
-				goto out;
-			}
-			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
-			    ptype == T_SHORT && stype == ST_ROOT) {
-				s->s_blocksize             = blocksize;
-				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
-				s->u.affs_sb.s_root_block += num_bm;
-				key                        = 1;
-				break;
-			}
-		}
-		if (key)
-			break;
-		affs_brelse(bh);
-		bh = NULL;
-	}
-	if (!key) {
-		affs_brelse(bh);
-		if (!silent)
-			printk(KERN_ERR "AFFS: Cannot find a valid root block on device %s\n",
-				kdevname(dev));
-		goto out;
-	}
-	root_block = s->u.affs_sb.s_root_block;
-
-	s->u.affs_sb.s_partition_size   = size;
-	s->s_blocksize_bits             = blocksize == 512 ? 9 :
-					  blocksize == 1024 ? 10 :
-					  blocksize == 2048 ? 11 : 12;
-
-	/* Find out which kind of FS we have */
-	bb = affs_bread(dev,0,s->s_blocksize);
-	if (bb) {
-		chksum = htonl(*(u32 *)bb->b_data);
-
-		/* Dircache filesystems are compatible with non-dircache ones
-		 * when reading. As long as they aren't supported, writing is
-		 * not recommended.
-		 */
-		if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
-		     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
-			printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
-				kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-		}
-		switch (chksum) {
-			case MUFS_FS:
-			case MUFS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_INTLFFS:
-				s->u.affs_sb.s_flags |= SF_INTL;
-				break;
-			case MUFS_DCFFS:
-			case MUFS_FFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				break;
-			case FS_DCFFS:
-			case FS_FFS:
-				break;
-			case MUFS_OFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-				/* fall thru */
-			case FS_OFS:
-				s->u.affs_sb.s_flags |= SF_OFS;
-				break;
-			case MUFS_DCOFS:
-			case MUFS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_MUFS;
-			case FS_DCOFS:
-			case FS_INTLOFS:
-				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
-				break;
-			default:
-				printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
-					kdevname(dev),chksum);
-				affs_brelse(bb);
-				goto out;
-		}
-		affs_brelse(bb);
-	} else {
-		printk(KERN_ERR "AFFS: Cannot read boot block\n");
-		goto out;
-	}
-	if (mount_flags & SF_VERBOSE) {
-		chksum = ntohl(chksum);
-		printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
-			GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
-			&GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
-			(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
-	}
-
-	s->s_magic  = AFFS_SUPER_MAGIC;
-	s->s_flags |= MS_NODEV | MS_NOSUID;
-
-	/* Keep super block in cache */
-	if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
-		printk(KERN_ERR "AFFS: Cannot read root block\n");
-		goto out;
-	}
-
-	/* Allocate space for bitmaps, zones and others */
-
-	size   = s->u.affs_sb.s_partition_size - reserved;
-	num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
-	az_no  = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
-	ptype  = num_bm * sizeof(struct affs_bm_info) +
-		 az_no * sizeof(struct affs_alloc_zone) +
-		 MAX_ZONES * sizeof(struct affs_zone);
-	pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
-	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
-		printk(KERN_ERR "AFFS: Not enough memory\n");
-		goto out;
-	}
-	memset(s->u.affs_sb.s_bitmap,0,ptype);
-
-	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
-	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
-	s->u.affs_sb.s_num_az  = az_no;
-
-	mapidx = 0;
-
-	if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
-		if (!(s->s_flags & MS_RDONLY)) {
-			printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
-				kdevname(dev));
-			s->s_flags |= MS_RDONLY;
-		}
-		affs_brelse(bh);
-		bh = NULL;
-		goto nobitmap;
-	}
-
-	/* The following section is ugly, I know. Especially because of the
-	 * reuse of some variables that are not named properly.
-	 */
-
-	key    = root_block;
-	ptype  = s->s_blocksize / 4 - 49;
-	stype  = ptype + 25;
-	offset = s->u.affs_sb.s_reserved;
-	az_no  = 0;
-	while (bh) {
-		bm = (u32 *)bh->b_data;
-		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
-			if (mapidx >= num_bm) {
-				printk(KERN_ERR "AFFS: Not enough bitmap space!?\n");
-				goto out;
-			}
-			bb = affs_bread(s->s_dev,htonl(bm[i]),s->s_blocksize);
-			if (bb) {
-				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
-				    !(s->s_flags & MS_RDONLY)) {
-					printk(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
-					       "mounting %s read only.\n",mapidx,htonl(bm[i]),
-						kdevname(dev));
-					s->s_flags |= MS_RDONLY;
-				}
-				/* Mark unused bits in the last word as allocated */
-				if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
-					ptype = size / 32 + 1;		/* word number */
-					key   = size & 0x1F;		/* used bits */
-					if (key) {
-						chksum = ntohl(0x7FFFFFFF >> (31 - key));
-						((u32 *)bb->b_data)[ptype] &= chksum;
-						affs_fix_checksum(s->s_blocksize,bb->b_data,0);
-						mark_buffer_dirty(bb,1);
-					}
-					ptype = (size + 31) & ~0x1F;
-					size  = 0;
-					s->u.affs_sb.s_flags |= SF_BM_VALID;
-				} else {
-					ptype = s->s_blocksize * 8 - 32;
-					size -= ptype;
-				}
-				s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
-				s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
-				s->u.affs_sb.s_bitmap[mapidx].bm_key      = htonl(bm[i]);
-				s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
-				offset += ptype;
-
-				for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
-					key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
-					s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
-					s->u.affs_sb.s_alloc[az_no].az_free =
-						affs_count_free_bits(key / 8,bb->b_data +
-								     j * (AFFS_ZONE_SIZE / 8) + 4);
-				}
-				affs_brelse(bb);
-			} else {
-				printk(KERN_ERR "AFFS: Cannot read bitmap\n");
-				goto out;
-			}
-		}
-		key   = htonl(bm[stype]);		/* Next block of bitmap pointers	*/
-		ptype = 0;
-		stype = s->s_blocksize / 4 - 1;
-		affs_brelse(bh);
-		if (key) {
-			if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
-				printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
-				goto out;
-			}
-		} else
-			bh = NULL;
-	}
-	if (mapidx != num_bm) {
-		printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
-		goto out;
-	}
-nobitmap:
-	s->u.affs_sb.s_bm_count  = mapidx;
-
-	/* set up enough so that it can read an inode */
-
-	s->s_dev       = dev;
-	s->s_op        = &affs_sops;
-	s->s_root      = d_alloc_root(iget(s,root_block),NULL);
-	s->s_dirt      = 1;
-	unlock_super(s);
-
-	if (!(s->s_root)) {
-		s->s_dev = 0;
-		printk(KERN_ERR "AFFS: get root inode failed\n");
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-
-	/* create data zones if the fs is mounted r/w */
-
-	if (!(s->s_flags & MS_RDONLY)) {
-		ROOT_END(s->u.affs_sb.s_root_bh->b_data,s->s_root->d_inode)->bm_flag = 0;
-		secs_to_datestamp(CURRENT_TIME,&ROOT_END(s->u.affs_sb.s_root_bh->b_data,
-				  s->s_root->d_inode)->disk_altered);
-		affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
-		mark_buffer_dirty(s->u.affs_sb.s_root_bh,1);
-		affs_make_zones(s);
-	}
-
-	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
-	return s;
-
- out: /* Kick out for various error conditions */
-	affs_brelse (bh);
-	affs_brelse(s->u.affs_sb.s_root_bh);
-	if (s->u.affs_sb.s_bitmap)
-		kfree(s->u.affs_sb.s_bitmap);
-	set_blocksize(dev,BLOCK_SIZE);
-	s->s_dev = 0;
-	unlock_super(s);
-	MOD_DEC_USE_COUNT;
-	return NULL;
-}
-
-static void
-affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
-{
-	int		 free;
-	struct statfs	 tmp;
-
-	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
-	     sb->u.affs_sb.s_reserved);
-
-	free          = affs_count_free_blocks(sb);
-	tmp.f_type    = AFFS_SUPER_MAGIC;
-	tmp.f_bsize   = sb->s_blocksize;
-	tmp.f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
-	tmp.f_bfree   = free;
-	tmp.f_bavail  = free;
-	tmp.f_files   = 0;
-	tmp.f_ffree   = 0;
-	copy_to_user(buf,&tmp,bufsiz);
-}
-
-static void
+void
 affs_read_inode(struct inode *inode)
 {
-	struct buffer_head	*bh, *lbh;
+	struct buffer_head	*bh;
 	struct file_front	*file_front;
 	struct file_end		*file_end;
 	s32			 block;
@@ -665,7 +62,6 @@
 
 	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
 
-	lbh   = NULL;
 	block = inode->i_ino;
 	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
 		affs_error(inode->i_sb,"read_inode","Cannot read block %d",block);
@@ -680,10 +76,10 @@
 
 	file_front = (struct file_front *)bh->b_data;
 	file_end   = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
-	prot       = (htonl(file_end->protect) & ~0x10) ^ FIBF_OWNER;
+	prot       = (be32_to_cpu(file_end->protect) & ~0x10) ^ FIBF_OWNER;
 
 	inode->u.affs_i.i_protect      = prot;
-	inode->u.affs_i.i_parent       = htonl(file_end->parent);
+	inode->u.affs_i.i_parent       = be32_to_cpu(file_end->parent);
 	inode->u.affs_i.i_original     = 0;
 	inode->u.affs_i.i_zone         = 0;
 	inode->u.affs_i.i_hlink        = 0;
@@ -702,31 +98,28 @@
 
 	if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
 		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
-	else {
-		id = htons(file_end->owner_uid);
-		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
-			if (id == 0 || id == 0xFFFF)
-				id ^= ~0;
-		}
+	id = be16_to_cpu(file_end->owner_uid);
+	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
+		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
+	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+		inode->i_uid = 0;
+	else 
 		inode->i_uid = id;
-	}
-	if (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
+
+	id = be16_to_cpu(file_end->owner_gid);
+	if (id == 0 || inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
 		inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
-	else {
-		id = htons(file_end->owner_gid);
-		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
-			if (id == 0 || id == 0xFFFF)
-				id ^= ~0;
-		}
+	else if (id == 0xFFFF && inode->i_sb->u.affs_sb.s_flags & SF_MUFS)
+		inode->i_gid = 0;
+	else
 		inode->i_gid = id;
-	}
 
-	switch (htonl(file_end->secondary_type)) {
+	switch (be32_to_cpu(file_end->secondary_type)) {
 		case ST_ROOT:
 			inode->i_uid   = inode->i_sb->u.affs_sb.s_uid;
 			inode->i_gid   = inode->i_sb->u.affs_sb.s_gid;
 		case ST_USERDIR:
-			if (htonl(file_end->secondary_type) == ST_USERDIR ||
+			if (be32_to_cpu(file_end->secondary_type) == ST_USERDIR ||
 			    inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
 				if (inode->i_mode & S_IRUSR)
 					inode->i_mode |= S_IXUSR;
@@ -740,25 +133,16 @@
 			inode->i_size  = 0;
 			break;
 		case ST_LINKDIR:
-			inode->u.affs_i.i_original = htonl(file_end->original);
-			inode->u.affs_i.i_hlink    = 1;
-			inode->i_mode             |= S_IFDIR;
-			inode->i_size              = 0;
-			break;
+			affs_error(inode->i_sb,"read_inode","inode is LINKDIR");
+			affs_brelse(bh);
+			return;
 		case ST_LINKFILE:
-			inode->u.affs_i.i_original = htonl(file_end->original);
-			inode->u.affs_i.i_hlink    = 1;
-			if (!(lbh = affs_bread(inode->i_dev,inode->u.affs_i.i_original,
-			                  AFFS_I2BSIZE(inode)))) {
-				affs_brelse(bh);
-				affs_error(inode->i_sb,"read_inode","Cannot read block %lu",
-					   inode->i_ino);
-				return;
-			}
-			file_end = GET_END_PTR(struct file_end,lbh->b_data,AFFS_I2BSIZE(inode));
+			affs_error(inode->i_sb,"read_inode","inode is LINKFILE");
+			affs_brelse(bh);
+			return;
 		case ST_FILE:
 			inode->i_mode |= S_IFREG;
-			inode->i_size  = htonl(file_end->byte_size);
+			inode->i_size  = be32_to_cpu(file_end->byte_size);
 			if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
 				block = AFFS_I2BSIZE(inode) - 24;
 			else
@@ -772,13 +156,12 @@
 	}
 
 	inode->i_mtime = inode->i_atime = inode->i_ctime
-		       = (htonl(file_end->created.ds_Days) * (24 * 60 * 60) +
-		         htonl(file_end->created.ds_Minute) * 60 +
-			 htonl(file_end->created.ds_Tick) / 50 +
+		       = (be32_to_cpu(file_end->created.ds_Days) * (24 * 60 * 60) +
+		         be32_to_cpu(file_end->created.ds_Minute) * 60 +
+			 be32_to_cpu(file_end->created.ds_Tick) / 50 +
 			 ((8 * 365 + 2) * 24 * 60 * 60)) +
 			 sys_tz.tz_minuteswest * 60;
 	affs_brelse(bh);
-	affs_brelse(lbh);
 
 	inode->i_op = NULL;
 	if (S_ISREG(inode->i_mode)) {
@@ -793,7 +176,7 @@
 		inode->i_op = &affs_symlink_inode_operations;
 }
 
-static void
+void
 affs_write_inode(struct inode *inode)
 {
 	struct buffer_head	*bh;
@@ -810,11 +193,11 @@
 		return;
 	}
 	file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
-	if (file_end->secondary_type == htonl(ST_ROOT)) {
+	if (file_end->secondary_type == be32_to_cpu(ST_ROOT)) {
 		secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered);
 	} else {
-		file_end->protect   = ntohl(inode->u.affs_i.i_protect ^ FIBF_OWNER);
-		file_end->byte_size = ntohl(inode->i_size);
+		file_end->protect   = cpu_to_be32(inode->u.affs_i.i_protect ^ FIBF_OWNER);
+		file_end->byte_size = cpu_to_be32(inode->i_size);
 		secs_to_datestamp(inode->i_mtime,&file_end->created);
 		if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) {
 			uid = inode->i_uid;
@@ -826,9 +209,9 @@
 					gid = inode->i_gid ^ ~0;
 			}
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
-				file_end->owner_uid = ntohs(uid);
+				file_end->owner_uid = cpu_to_be16(uid);
 			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
-				file_end->owner_gid = ntohs(gid);
+				file_end->owner_gid = cpu_to_be16(gid);
 		}
 	}
 	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
@@ -836,7 +219,7 @@
 	brelse(bh);
 }
 
-static int
+int
 affs_notify_change(struct inode *inode, struct iattr *attr)
 {
 	int error;
@@ -864,19 +247,23 @@
 	return 0;
 }
 
-static void
+void
 affs_put_inode(struct inode *inode)
 {
 	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
 	lock_super(inode->i_sb);
 	if (inode->u.affs_i.i_ec) {
+		pr_debug("AFFS: freeing ext cache\n");
 		free_page((unsigned long)inode->u.affs_i.i_ec);
 		inode->u.affs_i.i_ec = NULL;
 	}
 	unlock_super(inode->i_sb);
-	if (inode->i_nlink) {
-		return;
-	}
+}
+
+void
+affs_delete_inode(struct inode *inode)
+{
+	pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n",inode->i_ino,inode->i_nlink);
 	inode->i_size = 0;
 	if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
 		affs_truncate(inode);
@@ -916,15 +303,15 @@
 	inode->i_blksize = 0;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
 
-	inode->u.affs_i.i_original     = 0;
-	inode->u.affs_i.i_parent       = dir->i_ino;
-	inode->u.affs_i.i_zone         = 0;
-	inode->u.affs_i.i_hlink        = 0;
-	inode->u.affs_i.i_pa_cnt       = 0;
-	inode->u.affs_i.i_pa_next      = 0;
-	inode->u.affs_i.i_pa_last      = 0;
-	inode->u.affs_i.i_ec           = NULL;
-	inode->u.affs_i.i_lastblock    = -1;
+	inode->u.affs_i.i_original  = 0;
+	inode->u.affs_i.i_parent    = dir->i_ino;
+	inode->u.affs_i.i_zone      = 0;
+	inode->u.affs_i.i_hlink     = 0;
+	inode->u.affs_i.i_pa_cnt    = 0;
+	inode->u.affs_i.i_pa_next   = 0;
+	inode->u.affs_i.i_pa_last   = 0;
+	inode->u.affs_i.i_ec        = NULL;
+	inode->u.affs_i.i_lastblock = -1;
 
 	insert_inode_hash(inode);
 	mark_inode_dirty(inode);
@@ -934,23 +321,27 @@
 
 int
 affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
-	       const char *name, int len, int type)
+	       struct dentry *dentry, int type)
 {
 	struct buffer_head	*dir_bh;
 	struct buffer_head	*inode_bh;
 	struct buffer_head	*link_bh;
-	struct buffer_head	*ibh;
 	int			 retval;
-	int			 i;
-	s32			 next;
+	const unsigned char	*name = dentry->d_name.name;
+	int			 len  = dentry->d_name.len;
 
-	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d\n",dir->i_ino,inode->i_ino,
+	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%d)\n",dir->i_ino,inode->i_ino,
 		 len,name,type);
 
-	dir_bh      = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
-	inode_bh    = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	link_bh     = NULL;
-	retval       = -EIO;
+	if ((retval = affs_check_name(name,len)))
+		return retval;
+	if (len > 30)
+		len = 30;
+
+	dir_bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	inode_bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	link_bh  = NULL;
+	retval   = -EIO;
 	if (!dir_bh || !inode_bh)
 		goto addentry_done;
 	if (link) {
@@ -958,57 +349,21 @@
 		if (!link_bh)
 			goto addentry_done;
 	}
-	((struct dir_front *)inode_bh->b_data)->primary_type = ntohl(T_SHORT);
-	((struct dir_front *)inode_bh->b_data)->own_key      = ntohl(inode->i_ino);
-
-	retval = -ENAMETOOLONG;
-	if (len > 30)
-#ifdef NO_TRUNCATE
-		goto addentry_done;
-#else
-		len = 30;
-#endif
-
-	/* Check if name is valid */
-	retval = -EINVAL;
-	for (i = 0; i < len; i++) {
-		if (name[i] < ' ' || name[i] == ':'
-		    || ((unsigned char)name[i] > 0x7e && (unsigned char)name[i] < 0xa0))
-			goto addentry_done;
-	}
-	retval = 0;
-	DIR_END(inode_bh->b_data,inode)->dir_name[0] = len;
+	((struct dir_front *)inode_bh->b_data)->primary_type = cpu_to_be32(T_SHORT);
+	((struct dir_front *)inode_bh->b_data)->own_key      = cpu_to_be32(inode->i_ino);
+	DIR_END(inode_bh->b_data,inode)->dir_name[0]         = len;
 	strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
-	DIR_END(inode_bh->b_data,inode)->secondary_type = ntohl(type);
-	DIR_END(inode_bh->b_data,inode)->parent         = ntohl(dir->i_ino);
-
-	i    = affs_hash_name(name,len,AFFS_I2FSTYPE(dir),AFFS_I2HSIZE(dir)) + 6;
-	next = dir->i_ino;
-
-	/* Alas, we have to search the insertion point with a locked sb */
+	DIR_END(inode_bh->b_data,inode)->secondary_type = cpu_to_be32(type);
+	DIR_END(inode_bh->b_data,inode)->parent         = cpu_to_be32(dir->i_ino);
 
 	lock_super(inode->i_sb);
-	while (1) {
-		if (!(ibh = affs_bread(dir->i_dev,next,AFFS_I2BSIZE(dir))))
-			goto addentry_done;
-		next = htonl(((s32 *)ibh->b_data)[i]);
-		if (!next || next > inode->i_ino)
-			break;
-		i = AFFS_I2BSIZE(dir) / 4 - 4;
-		affs_brelse(ibh);
-	}
-
-	DIR_END(inode_bh->b_data,inode)->hash_chain = next;
-	((s32 *)ibh->b_data)[i]                     = ntohl(inode->i_ino);
-	affs_fix_checksum(AFFS_I2BSIZE(dir),ibh->b_data,5);
-	mark_buffer_dirty(ibh,1);
-	affs_brelse(ibh);
+	retval = affs_insert_hash(dir->i_ino,inode_bh,dir);
 
 	if (link_bh) {
-		LINK_END(inode_bh->b_data,inode)->original   = ntohl(link->i_ino);
+		LINK_END(inode_bh->b_data,inode)->original   = cpu_to_be32(link->i_ino);
 		LINK_END(inode_bh->b_data,inode)->link_chain =
 						FILE_END(link_bh->b_data,link)->link_chain;
-		FILE_END(link_bh->b_data,link)->link_chain   = ntohl(inode->i_ino);
+		FILE_END(link_bh->b_data,link)->link_chain   = cpu_to_be32(inode->i_ino);
 		affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
 		link->i_version = ++event;
 		mark_inode_dirty(link);
@@ -1032,32 +387,3 @@
 
 	return retval;
 }
-
-static struct file_system_type affs_fs_type = {
-	"affs",
-	FS_REQUIRES_DEV,
-	affs_read_super,
-	NULL
-};
-
-__initfunc(int init_affs_fs(void))
-{
-	return register_filesystem(&affs_fs_type);
-}
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-
-int
-init_module(void)
-{
-	return register_filesystem(&affs_fs_type);
-}
-
-void
-cleanup_module(void)
-{
-	unregister_filesystem(&affs_fs_type);
-}
-
-#endif
diff -u --recursive --new-file v2.1.70/linux/fs/affs/namei.c linux/fs/affs/namei.c
--- v2.1.70/linux/fs/affs/namei.c	Mon Aug 18 18:19:46 1997
+++ linux/fs/affs/namei.c	Tue Dec  2 22:25:07 1997
@@ -8,6 +8,7 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
+#define DEBUG 0
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/kernel.h>
@@ -20,8 +21,6 @@
 
 #include <linux/errno.h>
 
-static int affs_fixup(struct buffer_head *bh, struct inode *inode);
-
 /* Simple toupper() for DOS\1 */
 
 static inline unsigned int
@@ -45,7 +44,7 @@
  */
 
 static int
-affs_match(const char *name, int len, const char *compare, int dlen, int intl)
+affs_match(const unsigned char *name, int len, const unsigned char *compare, int dlen, int intl)
 {
 	if (!compare)
 		return 0;
@@ -62,14 +61,14 @@
 		return 0;
 	if (intl) {
 		while (dlen--) {
-			if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+			if (affs_intl_toupper(*name) != affs_intl_toupper(*compare))
 				return 0;
 			name++;
 			compare++;
 		}
 	} else {
 		while (dlen--) {
-			if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+			if (affs_toupper(*name) != affs_toupper(*compare))
 				return 0;
 			name++;
 			compare++;
@@ -79,7 +78,7 @@
 }
 
 int
-affs_hash_name(const char *name, int len, int intl, int hashsize)
+affs_hash_name(const unsigned char *name, int len, int intl, int hashsize)
 {
 	unsigned int i, x;
 
@@ -97,14 +96,15 @@
 }
 
 static struct buffer_head *
-affs_find_entry(struct inode *dir, const char *name, int namelen,
-		unsigned long *ino)
+affs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *ino)
 {
-	struct buffer_head *bh;
-	int	 intl;
-	s32	 key;
+	struct buffer_head	*bh;
+	int			 intl;
+	s32			 key;
+	const char		*name = dentry->d_name.name;
+	int			 namelen = dentry->d_name.len;
 
-	pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
+	pr_debug("AFFS: find_entry(\"%.*s\")\n",namelen,name);
 
 	intl = AFFS_I2FSTYPE(dir);
 	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
@@ -123,7 +123,7 @@
 	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
 
 	for (;;) {
-		char *cname;
+		unsigned char *cname;
 		int cnamelen;
 
 		affs_brelse(bh);
@@ -137,154 +137,127 @@
 		cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
 		if (affs_match(name,namelen,cname,cnamelen,intl))
 			break;
-		key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
+		key = be32_to_cpu(FILE_END(bh->b_data,dir)->hash_chain);
 	}
 	*ino = key;
 	return bh;
 }
 
 int
-affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+affs_lookup(struct inode *dir, struct dentry *dentry)
 {
-	int res;
-	unsigned long ino;
-	struct buffer_head *bh;
-
-	pr_debug("AFFS: lookup(%.*s)\n",len,name);
+	unsigned long		 ino;
+	struct buffer_head	*bh;
+	struct inode		*inode;
 
-	*result = NULL;
-	if (!dir)
-		return -ENOENT;
+	pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
 
-	res = -ENOENT;
-	if (S_ISDIR(dir->i_mode)) {
-		if ((bh = affs_find_entry(dir,name,len,&ino))) {
-			if (FILE_END(bh->b_data,dir)->original)
-				ino = htonl(FILE_END(bh->b_data,dir)->original);
-			affs_brelse(bh);
-			if ((*result = iget(dir->i_sb,ino))) 
-				res = 0;
-			else
-				res = -EACCES;
-		}
+	inode = NULL;
+	bh = affs_find_entry(dir,dentry,&ino);
+	if (bh) {
+		if (FILE_END(bh->b_data,dir)->original)
+			ino = be32_to_cpu(FILE_END(bh->b_data,dir)->original);
+		affs_brelse(bh);
+		inode = iget(dir->i_sb,ino);
+		if (!inode)
+			return -EACCES;
 	}
-	iput(dir);
-	return res;
+	d_add(dentry,inode);
+	return 0;
 }
 
 int
-affs_unlink(struct inode *dir, const char *name, int len)
+affs_unlink(struct inode *dir, struct dentry *dentry)
 {
 	int			 retval;
 	struct buffer_head	*bh;
 	unsigned long		 ino;
 	struct inode		*inode;
 
-	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,len,name);
+	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
 	bh      = NULL;
-	inode   = NULL;
 	retval  = -ENOENT;
-	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
-		goto unlink_done;
-	}
-	if (!(inode = iget(dir->i_sb,ino))) {
+	if (!dir)
 		goto unlink_done;
-	}
-	if (S_ISDIR(inode->i_mode)) {
-		retval = -EPERM;
+	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto unlink_done;
-	}
 
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
+	inode  = dentry->d_inode;
+	retval = -EPERM;
+	if (S_ISDIR(inode->i_mode))
 		goto unlink_done;
-
-	if ((retval = affs_fixup(bh,inode)))
+	if (current->fsuid != inode->i_uid &&
+	    current->fsuid != dir->i_uid && !fsuser())
 		goto unlink_done;
 
-	inode->i_nlink=0;
-	mark_inode_dirty(inode);
+	if ((retval = affs_remove_header(bh,inode)) < 0)
+		goto unlink_done;
+	
+	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
+	mark_inode_dirty(inode);
+	retval = 0;
+	d_delete(dentry);
 unlink_done:
 	affs_brelse(bh);
-	iput(inode);
-	iput(dir);
 	return retval;
 }
 
 int
-affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+affs_create(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct inode	*inode;
 	int		 error;
 	
-	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
+		 dentry->d_name.name,mode);
 
-	*result = NULL;
-	if (!dir || !dir->i_sb) {
-		iput(dir);
-		return -EINVAL;
-	}
+	if (!dir)
+		return -ENOENT;
 	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput (dir);
+	if (!inode)
 		return -ENOSPC;
-	}
-	inode->i_mode = mode;
+
+	pr_debug(" -- ino=%lu\n",inode->i_ino);
 	if (dir->i_sb->u.affs_sb.s_flags & SF_OFS)
 		inode->i_op = &affs_file_inode_operations_ofs;
 	else
 		inode->i_op = &affs_file_inode_operations;
 
-	error = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+	error = affs_add_entry(dir,NULL,inode,dentry,ST_FILE);
 	if (error) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
-		return -ENOSPC;
+		return error;
 	}
+	inode->i_mode = mode;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-
-	iput(dir);
-	*result = inode;
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+	d_instantiate(dentry,inode);
 
 	return 0;
 }
 
 int
-affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct inode		*inode;
-	struct buffer_head	*bh;
-	unsigned long		 i;
 	int			 error;
 	
-	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name,mode);
 
-	if (!dir || !dir->i_sb) {
-		iput(dir);
-		return -EINVAL;
-	}
-	bh = affs_find_entry(dir,name,len,&i);
-	if (bh) {
-		affs_brelse(bh);
-		iput(dir);
-		return -EEXIST;
-	}
 	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput (dir);
+	if (!inode)
 		return -ENOSPC;
-	}
+
 	inode->i_op = &affs_dir_inode_operations;
-	error       = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+	error       = affs_add_entry(dir,NULL,inode,dentry,ST_USERDIR);
 	if (error) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
@@ -292,9 +265,9 @@
 	}
 	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
-
-	iput(dir);
-	iput(inode);
+	dir->i_version = ++event;
+	mark_inode_dirty(dir);
+	d_instantiate(dentry,inode);
 
 	return 0;
 }
@@ -310,26 +283,29 @@
 }
 
 int
-affs_rmdir(struct inode *dir, const char *name, int len)
+affs_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	int			 retval;
 	unsigned long		 ino;
 	struct inode		*inode;
 	struct buffer_head	*bh;
 
-	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,len,name);
+	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
 	inode  = NULL;
+	bh     = NULL;
 	retval = -ENOENT;
-	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+	if (!dir)
 		goto rmdir_done;
-	}
-	if (!(inode = iget(dir->i_sb,ino))) {
+	if (!(bh = affs_find_entry(dir,dentry,&ino)))
 		goto rmdir_done;
-	}
+
+	inode = dentry->d_inode;
+
 	retval = -EPERM;
-        if (!fsuser() && current->fsuid != inode->i_uid &&
-            current->fsuid != dir->i_uid)
+        if (current->fsuid != inode->i_uid &&
+            current->fsuid != dir->i_uid && !fsuser())
 		goto rmdir_done;
 	if (inode->i_dev != dir->i_dev)
 		goto rmdir_done;
@@ -339,6 +315,11 @@
 		retval = -ENOTDIR;
 		goto rmdir_done;
 	}
+	down(&inode->i_sem);
+	if (dentry->d_count > 1) {
+		shrink_dcache_parent(dentry);
+	}
+	up(&inode->i_sem);
 	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
 		retval = -ENOTEMPTY;
 		goto rmdir_done;
@@ -347,28 +328,21 @@
 		retval = -EBUSY;
 		goto rmdir_done;
 	}
-	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
-					 AFFS_I2HSIZE(dir)) + 6,ino,
-					 FILE_END(bh->b_data,dir)->hash_chain)))
+	if ((retval = affs_remove_header(bh,inode)) < 0)
 		goto rmdir_done;
-
-	if ((retval = affs_fixup(bh,inode)))
-		goto rmdir_done;
-
-	inode->i_nlink=0;
-	mark_inode_dirty(inode);
+	
+	inode->i_nlink = retval;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
-	dir->i_version = ++event;
-	mark_inode_dirty(dir);
+	retval         = 0;
+	mark_inode_dirty(inode);
+	d_delete(dentry);
 rmdir_done:
-	iput(dir);
-	iput(inode);
 	affs_brelse(bh);
 	return retval;
 }
 
 int
-affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
 	struct buffer_head	*bh;
 	struct inode		*inode;
@@ -377,20 +351,19 @@
 	int			 i, maxlen;
 	char			 c, lc;
 
-	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name,symname);
 	
 	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
 	inode  = affs_new_inode(dir);
-	if (!inode) {
-		iput(dir);
+	if (!inode)
 		return -ENOSPC;
-	}
+
 	inode->i_op   = &affs_symlink_inode_operations;
 	inode->i_mode = S_IFLNK | 0777;
 	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
 	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
 	if (!bh) {
-		iput(dir);
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
@@ -427,157 +400,135 @@
 	mark_buffer_dirty(bh,1);
 	affs_brelse(bh);
 	mark_inode_dirty(inode);
-	bh = affs_find_entry(dir,name,len,&tmp);
+	bh = affs_find_entry(dir,dentry,&tmp);
 	if (bh) {
 		inode->i_nlink = 0;
 		iput(inode);
 		affs_brelse(bh);
-		iput(dir);
 		return -EEXIST;
 	}
-	i = affs_add_entry(dir,NULL,inode,name,len,ST_SOFTLINK);
+	i = affs_add_entry(dir,NULL,inode,dentry,ST_SOFTLINK);
 	if (i) {
 		inode->i_nlink = 0;
 		mark_inode_dirty(inode);
 		iput(inode);
 		affs_brelse(bh);
-		iput(dir);
 		return i;
 	}
-	iput(dir);
-	iput(inode);
+	dir->i_version = ++event;
+	d_instantiate(dentry,inode);
 	
 	return 0;
 }
 
 int
-affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+affs_link(struct inode *oldinode, struct inode *dir, struct dentry *dentry)
 {
 	struct inode		*inode;
 	struct buffer_head	*bh;
 	unsigned long		 i;
 	int			 error;
 	
-	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,len,name);
+	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,
+		 (int)dentry->d_name.len,dentry->d_name.name);
 
-	bh = affs_find_entry(dir,name,len,&i);
+	bh = affs_find_entry(dir,dentry,&i);
 	if (bh) {
 		affs_brelse(bh);
-		iput(oldinode);
-		iput(dir);
 		return -EEXIST;
 	}
-	if (oldinode->u.affs_i.i_hlink) {
-		i = oldinode->u.affs_i.i_original;
-		iput(oldinode);
-		oldinode = iget(dir->i_sb,i);
-		if (!oldinode) {
-			affs_error(oldinode->i_sb,"link","Cannot get original from link");
-			iput(dir);
-			return -ENOENT;
-		}
+	if (oldinode->u.affs_i.i_hlink)	{	/* Cannot happen */
+		affs_warning(dir->i_sb,"link","Impossible link to link");
+		return -EINVAL;
 	}
-	inode = affs_new_inode(dir);
-	if (!inode) {
-		iput(oldinode);
-		iput(dir);
+	if (!(inode = affs_new_inode(dir)))
 		return -ENOSPC;
-	}
+
 	inode->i_op                = oldinode->i_op;
-	inode->i_mode              = oldinode->i_mode;
-	inode->i_uid               = oldinode->i_uid;
-	inode->i_gid               = oldinode->i_gid;
-	inode->u.affs_i.i_protect  = mode_to_prot(inode->i_mode);
+	inode->u.affs_i.i_protect  = mode_to_prot(oldinode->i_mode);
 	inode->u.affs_i.i_original = oldinode->i_ino;
 	inode->u.affs_i.i_hlink    = 1;
+	inode->i_mtime             = oldinode->i_mtime;
 
 	if (S_ISDIR(oldinode->i_mode))
-		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKDIR);
+		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR);
 	else
-		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
-	if (error) {
+		error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE);
+	if (error)
 		inode->i_nlink = 0;
-		mark_inode_dirty(inode);
+	else {
+		dir->i_version = ++event;
+		mark_inode_dirty(oldinode);
+		oldinode->i_count++;
+		d_instantiate(dentry,oldinode);
 	}
-	iput(dir);
+	mark_inode_dirty(inode);
 	iput(inode);
-	iput(oldinode);
 
 	return error;
 }
 
+/* This is copied from the ext2 fs. No need to reinvent the wheel. */
+
 static int
-subdir(struct inode *new_inode, struct inode *old_inode)
+subdir(struct dentry * new_dentry, struct dentry * old_dentry)
 {
-    int ino;
-    int result;
+	int result;
 
-    new_inode->i_count++;
-    result = 0;
-    for (;;) {
-        if (new_inode == old_inode) {
-	    result = 1;
-	    break;
-	}
-	if (new_inode->i_dev != old_inode->i_dev)
-	    break;
-	ino = new_inode->i_ino;
-	if (affs_lookup(new_inode,"..",2,&new_inode))
-	    break;
-	if (new_inode->i_ino == ino)
-	    break;
-    }
-    iput(new_inode);
-    return result;
+	result = 0;
+	for (;;) {
+		if (new_dentry != old_dentry) {
+			struct dentry * parent = new_dentry->d_parent;
+			if (parent == new_dentry)
+				break;
+			new_dentry = parent;
+			continue;
+		}
+		result = 1;
+		break;
+	}
+	return result;
 }
 
-/* I'm afraid this might not be race proof. Maybe next time. */
-
 int
-affs_rename(struct inode *old_dir, const char *old_name, int old_len,
-	    struct inode *new_dir, const char *new_name, int new_len)
+affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+	    struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode		*old_inode;
-	struct inode		*new_inode;
+	struct inode		*old_inode = old_dentry->d_inode;
+	struct inode		*new_inode = new_dentry->d_inode;
 	struct buffer_head	*old_bh;
 	struct buffer_head	*new_bh;
 	unsigned long		 old_ino;
 	unsigned long		 new_ino;
 	int			 retval;
 
-	pr_debug("AFFS: rename(old=%lu,\"%*s\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
-		 new_dir->i_ino,new_len,new_name);
+	pr_debug("AFFS: rename(old=%lu,\"%*s\" (inode=%p) to new=%lu,\"%*s\" (inode=%p) )\n",
+		 old_dir->i_ino,old_dentry->d_name.len,old_dentry->d_name.name,old_inode,
+		 new_dir->i_ino,new_dentry->d_name.len,new_dentry->d_name.name,new_inode);
 	
-	if (new_len > 30)
-		new_len = 30;
-	goto start_up;
-retry:
-	affs_brelse(old_bh);
-	affs_brelse(new_bh);
-	iput(new_inode);
-	iput(old_inode);
-	current->counter = 0;
-	schedule();
-start_up:
-	old_inode = new_inode = NULL;
-	old_bh    = new_bh = NULL;
-	retval    = -ENOENT;
+	if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+		return retval;
 
-	old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+	new_bh = NULL;
+	retval = -ENOENT;
+	old_bh = affs_find_entry(old_dir,old_dentry,&old_ino);
 	if (!old_bh)
 		goto end_rename;
-	old_inode = iget(old_dir->i_sb,old_ino);
-	if (!old_inode)
+
+	new_bh = affs_find_entry(new_dir,new_dentry,&new_ino);
+	if (new_bh && !new_inode) {
+		affs_error(old_inode->i_sb,"affs_rename",
+			   "No inode for entry found (key=%lu)\n",new_ino);
 		goto end_rename;
-	new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
-	if (new_bh) {
-		new_inode = iget(new_dir->i_sb,new_ino);
-		if (!new_inode) {		/* What does this mean? */
-			affs_brelse(new_bh);
-			new_bh = NULL;
-		}
 	}
-	if (new_inode == old_inode) {		/* Won't happen */
+	if (new_inode == old_inode) {
+		if (old_ino == new_ino) {	/* Filename might have changed case	*/
+			retval = new_dentry->d_name.len < 31 ? new_dentry->d_name.len : 30;
+			strncpy(DIR_END(old_bh->b_data,old_inode)->dir_name + 1,
+				new_dentry->d_name.name,retval);
+			DIR_END(old_bh->b_data,old_inode)->dir_name[0] = retval;
+			goto new_checksum;
+		}
 		retval = 0;
 		goto end_rename;
 	}
@@ -586,13 +537,13 @@
 		if (!S_ISDIR(old_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (subdir(new_dir,old_inode))
+		if (subdir(new_dentry,old_dentry))
 			goto end_rename;
 		retval = -ENOTEMPTY;
 		if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
 			goto end_rename;
 		retval = -EBUSY;
-		if (new_inode->i_count > 1)
+		if (new_dentry->d_count > 1)
 			goto end_rename;
 	}
 	if (S_ISDIR(old_inode->i_mode)) {
@@ -600,142 +551,43 @@
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
 			goto end_rename;
 		retval = -EINVAL;
-		if (subdir(new_dir,old_inode))
+		if (subdir(new_dentry,old_dentry))
 			goto end_rename;
 		if (affs_parent_ino(old_inode) != old_dir->i_ino)
 			goto end_rename;
 	}
-	/* Unlink destination if existent */
+	/* Unlink destination if it already exists */
 	if (new_inode) {
-		if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
-		                                 AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
-						 new_ino,
-						 FILE_END(new_bh->b_data,new_dir)->hash_chain)))
-			goto retry;
-		if ((retval = affs_fixup(new_bh,new_inode)))
-			goto retry;
-		mark_buffer_dirty(new_bh,1);
-		new_dir->i_version = ++event;
-		mark_inode_dirty(new_dir);
-		new_inode->i_nlink = 0;
+		if ((retval = affs_remove_header(new_bh,new_dir)) < 0)
+			goto end_rename;
+		new_inode->i_nlink = retval;
 		mark_inode_dirty(new_inode);
+		if (new_inode->i_ino == new_ino)
+			new_inode->i_nlink = 0;
 	}
-	retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
-				    AFFS_I2HSIZE(old_dir)) + 6,old_ino,
-				    FILE_END(old_bh->b_data,old_dir)->hash_chain);
-	if (retval)
-		goto retry;
-
-	retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
-				htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+	/* Remove header from its parent directory. */
+	if ((retval = affs_remove_hash(old_bh,old_dir)))
+		goto end_rename;
+	/* And insert it into the new directory with the new name. */
+	affs_copy_name(FILE_END(old_bh->b_data,old_inode)->file_name,new_dentry->d_name.name);
+	if ((retval = affs_insert_hash(new_dir->i_ino,old_bh,new_dir)))
+		goto end_rename;
+new_checksum:
+	affs_fix_checksum(AFFS_I2BSIZE(new_dir),old_bh->b_data,5);
 
-	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime
+			   = old_dir->i_mtime = CURRENT_TIME;
 	new_dir->i_version = ++event;
 	old_dir->i_version = ++event;
+	retval             = 0;
 	mark_inode_dirty(new_dir);
 	mark_inode_dirty(old_dir);
 	mark_buffer_dirty(old_bh,1);
+	d_move(old_dentry,new_dentry);
 	
 end_rename:
 	affs_brelse(old_bh);
 	affs_brelse(new_bh);
-	iput(new_inode);
-	iput(old_inode);
-	iput(old_dir);
-	iput(new_dir);
 
 	return retval;
-}
-
-static int
-affs_fixup(struct buffer_head *bh, struct inode *inode)
-{
-	s32			 key, link_key;
-	s32			 type;
-	struct buffer_head	*nbh;
-	struct inode		*ofinode;
-
-	type = htonl(FILE_END(bh->b_data,inode)->secondary_type);
-	if (type == ST_LINKFILE || type == ST_LINKDIR) {
-		key = htonl(LINK_END(bh->b_data,inode)->original);
-		LINK_END(bh->b_data,inode)->original = 0;
-		if (!key) {
-			affs_error(inode->i_sb,"fixup","Hard link without original: ino=%lu",
-				   inode->i_ino);
-			return -ENOENT;
-		}
-		if (!(ofinode = iget(inode->i_sb,key)))
-			return -ENOENT;
-		type = affs_fix_link_pred(ofinode,inode->i_ino,
-					  FILE_END(bh->b_data,inode)->link_chain);
-		iput(ofinode);
-		return type;
-	} else if (type == ST_FILE || type == ST_USERDIR) {
-		if ((key = htonl(FILE_END(bh->b_data,inode)->link_chain))) {
-			/* Get first link, turn it to a file */
-			if (!(ofinode = iget(inode->i_sb,key))) {
-				affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-				return -ENOENT;
-			}
-			if (!ofinode->u.affs_i.i_hlink) {
-				affs_error(inode->i_sb,"fixup",
-					   "First link to %lu (%d) is not a link",
-					   inode->i_ino,key);
-				iput(ofinode);
-				return -ENOENT;
-			}
-			if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
-				affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-				iput(ofinode);
-				return -ENOENT;
-			}
-			lock_super(inode->i_sb);
-			memcpy(nbh->b_data + 8,bh->b_data + 8,AFFS_I2BSIZE(inode) - 208);
-			FILE_END(nbh->b_data,inode)->byte_size = FILE_END(bh->b_data,inode)->
-									  byte_size;
-			FILE_END(nbh->b_data,inode)->extension = FILE_END(bh->b_data,inode)->
-									  extension;
-			FILE_END(nbh->b_data,inode)->secondary_type = FILE_END(bh->b_data,inode)->
-									  secondary_type;
-			FILE_END(nbh->b_data,inode)->original = 0;
-
-			ofinode->u.affs_i.i_original = 0;
-			ofinode->u.affs_i.i_hlink    = 0;
-			ofinode->i_size              = inode->i_size;
-			ofinode->i_uid               = inode->i_uid;
-			ofinode->i_gid               = inode->i_gid;
-			mark_inode_dirty(ofinode);
-			link_key                     = ofinode->i_ino;
-
-			/* Let all remaining links point to the new file */
-			while (1) {
-				affs_fix_checksum(AFFS_I2BSIZE(inode),nbh->b_data,5);
-				mark_buffer_dirty(nbh,1);
-				key = htonl(FILE_END(nbh->b_data,inode)->link_chain);
-				affs_brelse(nbh);
-				iput(ofinode);
-				if (!key || !(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode))))
-					break;
-				if ((ofinode = iget(inode->i_sb,key))) {
-					if (!ofinode->u.affs_i.i_hlink)
-						affs_error(inode->i_sb,"fixup",
-							   "Inode %d in link chain is not a link",
-							   key);
-					ofinode->u.affs_i.i_original = link_key;
-					mark_inode_dirty(ofinode);
-					FILE_END(nbh->b_data,inode)->original = htonl(link_key);
-				} else
-					affs_error(inode->i_sb,"fixup","Cannot read block %d",key);
-			}
-			/* Turn old inode to a link */
-			inode->u.affs_i.i_hlink = 1;
-			unlock_super(inode->i_sb);
-		}
-		return 0;
-	} else if (type == ST_SOFTLINK) {
-		return 0;
-	} else {
-		affs_error(inode->i_sb,"fixup","Bad secondary type (%d)",type);
-		return -EBADF;
-	}
 }
diff -u --recursive --new-file v2.1.70/linux/fs/affs/super.c linux/fs/affs/super.c
--- v2.1.70/linux/fs/affs/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/affs/super.c	Tue Dec  2 22:25:07 1997
@@ -0,0 +1,691 @@
+/*
+ *  linux/fs/affs/inode.c
+ *
+ *  (c) 1996  Hans-Joachim Widmaier - Rewritten
+ *
+ *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ *  (C) 1991  Linus Torvalds - minix filesystem
+ */
+
+#define DEBUG 0
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/errno.h>
+#include <linux/genhd.h>
+#include <linux/amigaffs.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern int *blk_size[];
+extern struct timezone sys_tz;
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+static int affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
+static int affs_remount (struct super_block *sb, int *flags, char *data);
+
+static void
+affs_put_super(struct super_block *sb)
+{
+	int	 i;
+
+	pr_debug("affs_put_super()\n");
+
+	lock_super(sb);
+	for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
+		affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(1);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+	}
+
+	if (sb->u.affs_sb.s_prefix)
+		kfree(sb->u.affs_sb.s_prefix);
+	kfree(sb->u.affs_sb.s_bitmap);
+	affs_brelse(sb->u.affs_sb.s_root_bh);
+
+	/* I'm not happy with this. It would be better to save the previous
+	 * value of this devices blksize_size[][] in the super block and
+	 * restore it here, but with the affs superblock being quite large
+	 * already ...
+	 */
+	set_blocksize(sb->s_dev,BLOCK_SIZE);
+
+	sb->s_dev = 0;
+	unlock_super(sb);
+	MOD_DEC_USE_COUNT;
+	return;
+}
+
+static void
+affs_write_super(struct super_block *sb)
+{
+	int	 i, clean = 2;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		lock_super(sb);
+		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
+			if (sb->u.affs_sb.s_bitmap[i].bm_bh) {
+				if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
+					clean = 0;
+					break;
+				}
+			}
+		}
+		unlock_super(sb);
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = be32_to_cpu(clean);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+		sb->s_dirt = !clean;	/* redo until bitmap synced */
+	} else
+		sb->s_dirt = 0;
+
+	pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+}
+
+static struct super_operations affs_sops = {
+	affs_read_inode,
+	affs_write_inode,
+	affs_put_inode,
+	affs_delete_inode,
+	affs_notify_change,
+	affs_put_super,
+	affs_write_super,
+	affs_statfs,
+	affs_remount
+};
+
+static int
+parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
+		int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
+{
+	char	*this_char, *value;
+	int	 f;
+
+	/* Fill in defaults */
+
+	*uid        = current->uid;
+	*gid        = current->gid;
+	*reserved   = 2;
+	*root       = -1;
+	*blocksize  = -1;
+	volume[0]   = ':';
+	volume[1]   = 0;
+	*mount_opts = 0;
+	if (!options)
+		return 1;
+	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+		f = 0;
+		if ((value = strchr(this_char,'=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char,"protect")) {
+			if (value) {
+				printk("AFFS: Option protect does not take an argument\n");
+				return 0;
+			}
+			*mount_opts |= SF_IMMUTABLE;
+		} else if (!strcmp(this_char,"verbose")) {
+			if (value) {
+				printk("AFFS: Option verbose does not take an argument\n");
+				return 0;
+			}
+			*mount_opts |= SF_VERBOSE;
+		} else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
+			if (value) {
+				if (!*value) {
+					printk("AFFS: Argument for set[ug]id option missing\n");
+					return 0;
+				} else {
+					(f ? *uid : *gid) = simple_strtoul(value,&value,0);
+					if (*value) {
+						printk("AFFS: Bad set[ug]id argument\n");
+						return 0;
+					}
+					*mount_opts |= f ? SF_SETUID : SF_SETGID;
+				}
+			}
+		} else if (!strcmp(this_char,"prefix")) {
+			if (!value || !*value) {
+				printk("AFFS: The prefix option requires an argument\n");
+				return 0;
+			}
+			if (*prefix)		/* Free any previous prefix */
+				kfree(*prefix);
+			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
+			if (!*prefix)
+				return 0;
+			strcpy(*prefix,value);
+			*mount_opts |= SF_PREFIX;
+		} else if (!strcmp(this_char,"volume")) {
+			if (!value || !*value) {
+				printk("AFFS: The volume option requires an argument\n");
+				return 0;
+			}
+			if (strlen(value) > 30)
+				value[30] = 0;
+			strncpy(volume,value,30);
+		} else if (!strcmp(this_char,"mode")) {
+			if (!value || !*value) {
+				printk("AFFS: The mode option requires an argument\n");
+				return 0;
+			}
+			*mode = simple_strtoul(value,&value,8) & 0777;
+			if (*value)
+				return 0;
+			*mount_opts |= SF_SETMODE;
+		} else if (!strcmp(this_char,"reserved")) {
+			if (!value || !*value) {
+				printk("AFFS: The reserved option requires an argument\n");
+				return 0;
+			}
+			*reserved = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		} else if (!strcmp(this_char,"root")) {
+			if (!value || !*value) {
+				printk("AFFS: The root option requires an argument\n");
+				return 0;
+			}
+			*root = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+		} else if (!strcmp(this_char,"bs")) {
+			if (!value || !*value) {
+				printk("AFFS: The bs option requires an argument\n");
+				return 0;
+			}
+			*blocksize = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+			if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
+			    && *blocksize != 4096) {
+				printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
+				return 0;
+			}
+		} else if (!strcmp (this_char, "grpquota")
+			 || !strcmp (this_char, "noquota")
+			 || !strcmp (this_char, "quota")
+			 || !strcmp (this_char, "usrquota"))
+			 /* Silently ignore the quota options */
+			;
+		else {
+			printk("AFFS: Unrecognized mount option %s\n", this_char);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* This function definitely needs to be split up. Some fine day I'll
+ * hopefully have the guts to do so. Until then: sorry for the mess.
+ */
+
+static struct super_block *
+affs_read_super(struct super_block *s,void *data, int silent)
+{
+	struct buffer_head	*bh = NULL;
+	struct buffer_head	*bb;
+	struct inode		*root_inode;
+	kdev_t			 dev = s->s_dev;
+	s32			 root_block;
+	int			 size;
+	u32			 chksum;
+	u32			*bm;
+	s32			 ptype, stype;
+	int			 mapidx;
+	int			 num_bm;
+	int			 i, j;
+	s32			 key;
+	int			 blocksize;
+	uid_t			 uid;
+	gid_t			 gid;
+	int			 reserved;
+	int			 az_no;
+	int			 bmalt = 0;
+	unsigned long		 mount_flags;
+	unsigned long		 offset;
+
+	pr_debug("affs_read_super(%s)\n",data ? (const char *)data : "no options");
+
+	MOD_INC_USE_COUNT;
+
+	s->u.affs_sb.s_prefix = NULL;
+	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+	    &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
+		s->s_dev = 0;
+		printk(KERN_ERR "AFFS: Error parsing options\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+	lock_super(s);
+
+	/* Get the size of the device in 512-byte blocks.
+	 * If we later see that the partition uses bigger
+	 * blocks, we will have to change it.
+	 */
+
+	size = blksize_size[MAJOR(dev)][MINOR(dev)];
+	size = (size ? size : BLOCK_SIZE) / 512 * blk_size[MAJOR(dev)][MINOR(dev)];
+
+	s->u.affs_sb.s_bitmap  = NULL;
+	s->u.affs_sb.s_root_bh = NULL;
+	s->u.affs_sb.s_flags   = mount_flags;
+	s->u.affs_sb.s_mode    = i;
+	s->u.affs_sb.s_uid     = uid;
+	s->u.affs_sb.s_gid     = gid;
+
+	if (size == 0) {
+		s->s_dev = 0;
+		unlock_super(s);
+		printk(KERN_ERR "AFFS: Could not determine device size\n");
+		goto out;
+	}
+	s->u.affs_sb.s_partition_size = size;
+	s->u.affs_sb.s_reserved       = reserved;
+
+	/* Try to find root block. Its location depends on the block size. */
+
+	s->u.affs_sb.s_hashsize = 0;
+	if (blocksize > 0) {
+		i = blocksize;
+		j = blocksize;
+	} else {
+		i = 512;
+		j = 4096;
+	}
+	for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+		if (root_block < 0)
+			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+		else
+			s->u.affs_sb.s_root_block = root_block;
+		set_blocksize(dev,blocksize);
+
+		/* The root block location that was calculated above is not
+		 * correct if the partition size is an odd number of 512-
+		 * byte blocks, which will be rounded down to a number of
+		 * 1024-byte blocks, and if there were an even number of
+		 * reserved blocks. Ideally, all partition checkers should
+		 * report the real number of blocks of the real blocksize,
+		 * but since this just cannot be done, we have to try to
+		 * find the root block anyways. In the above case, it is one
+		 * block behind the calculated one. So we check this one, too.
+		 */
+		for (num_bm = 0; num_bm < 2; num_bm++) {
+			pr_debug("AFFS: Dev %s - trying bs=%d bytes, root at %u, "
+				 "size=%d blocks, %d reserved\n",kdevname(dev),blocksize,
+				 s->u.affs_sb.s_root_block + num_bm,size,reserved);
+			bh = affs_bread(dev,s->u.affs_sb.s_root_block + num_bm,blocksize);
+			if (!bh) {
+				printk(KERN_ERR "AFFS: Cannot read root block\n");
+				goto out;
+			}
+			if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+			    ptype == T_SHORT && stype == ST_ROOT) {
+				s->s_blocksize             = blocksize;
+				s->u.affs_sb.s_hashsize    = blocksize / 4 - 56;
+				s->u.affs_sb.s_root_block += num_bm;
+				key                        = 1;
+				break;
+			}
+		}
+		if (key)
+			break;
+		affs_brelse(bh);
+		bh = NULL;
+	}
+	if (!key) {
+		affs_brelse(bh);
+		if (!silent)
+			printk(KERN_ERR "AFFS: Cannot find a valid root block on device %s\n",
+				kdevname(dev));
+		goto out;
+	}
+	root_block = s->u.affs_sb.s_root_block;
+
+	s->u.affs_sb.s_partition_size   = size;
+	s->s_blocksize_bits             = blocksize == 512 ? 9 :
+					  blocksize == 1024 ? 10 :
+					  blocksize == 2048 ? 11 : 12;
+
+	/* Find out which kind of FS we have */
+	bb = affs_bread(dev,0,s->s_blocksize);
+	if (bb) {
+		chksum = be32_to_cpu(*(u32 *)bb->b_data);
+
+		/* Dircache filesystems are compatible with non-dircache ones
+		 * when reading. As long as they aren't supported, writing is
+		 * not recommended.
+		 */
+		if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+		     || chksum == MUFS_DCOFS) && !(s->s_flags & MS_RDONLY)) {
+			printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+				kdevname(dev));
+			s->s_flags |= MS_RDONLY;
+			s->u.affs_sb.s_flags |= SF_READONLY;
+		}
+		switch (chksum) {
+			case MUFS_FS:
+			case MUFS_INTLFFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				/* fall thru */
+			case FS_INTLFFS:
+				s->u.affs_sb.s_flags |= SF_INTL;
+				break;
+			case MUFS_DCFFS:
+			case MUFS_FFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				break;
+			case FS_DCFFS:
+			case FS_FFS:
+				break;
+			case MUFS_OFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				/* fall thru */
+			case FS_OFS:
+				s->u.affs_sb.s_flags |= SF_OFS;
+				break;
+			case MUFS_DCOFS:
+			case MUFS_INTLOFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+			case FS_DCOFS:
+			case FS_INTLOFS:
+				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+				break;
+			default:
+				printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+					kdevname(dev),chksum);
+				affs_brelse(bb);
+				goto out;
+		}
+		affs_brelse(bb);
+	} else {
+		printk(KERN_ERR "AFFS: Cannot read boot block\n");
+		goto out;
+	}
+	if (mount_flags & SF_VERBOSE) {
+		chksum = cpu_to_be32(chksum);
+		printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
+			GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
+			&GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+			(char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
+	}
+
+	s->s_magic  = AFFS_SUPER_MAGIC;
+	s->s_flags |= MS_NODEV | MS_NOSUID;
+
+	/* Keep super block in cache */
+	if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
+		printk(KERN_ERR "AFFS: Cannot read root block\n");
+		goto out;
+	}
+
+	/* Allocate space for bitmaps, zones and others */
+
+	size   = s->u.affs_sb.s_partition_size - reserved;
+	num_bm = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
+	az_no  = (size + AFFS_ZONE_SIZE - 1) / (AFFS_ZONE_SIZE - 32);
+	ptype  = num_bm * sizeof(struct affs_bm_info) +
+		 az_no * sizeof(struct affs_alloc_zone) +
+		 MAX_ZONES * sizeof(struct affs_zone);
+	pr_debug("num_bm=%d, az_no=%d, sum=%d\n",num_bm,az_no,ptype);
+	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
+		printk(KERN_ERR "AFFS: Not enough memory\n");
+		goto out;
+	}
+	memset(s->u.affs_sb.s_bitmap,0,ptype);
+
+	s->u.affs_sb.s_zones   = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
+	s->u.affs_sb.s_alloc   = (struct affs_alloc_zone *)&s->u.affs_sb.s_zones[MAX_ZONES];
+	s->u.affs_sb.s_num_az  = az_no;
+
+	mapidx = 0;
+
+	if (ROOT_END_S(bh->b_data,s)->bm_flag == 0) {
+		if (!(s->s_flags & MS_RDONLY)) {
+			printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
+				kdevname(dev));
+			s->s_flags |= MS_RDONLY;
+		}
+		affs_brelse(bh);
+		bh = NULL;
+		goto nobitmap;
+	}
+
+	/* The following section is ugly, I know. Especially because of the
+	 * reuse of some variables that are not named properly.
+	 */
+
+	key    = root_block;
+	ptype  = s->s_blocksize / 4 - 49;
+	stype  = ptype + 25;
+	offset = s->u.affs_sb.s_reserved;
+	az_no  = 0;
+	while (bh) {
+		bm = (u32 *)bh->b_data;
+		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
+			if (mapidx >= num_bm) {
+				printk(KERN_ERR "AFFS: Extraneous bitmap pointer - "
+					       "mounting %s read only.\n",kdevname(dev));
+				s->s_flags |= MS_RDONLY;
+				s->u.affs_sb.s_flags |= SF_READONLY;
+				continue;
+			}
+			bb = affs_bread(s->s_dev,be32_to_cpu(bm[i]),s->s_blocksize);
+			if (bb) {
+				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) &&
+				    !(s->s_flags & MS_RDONLY)) {
+					printk(KERN_WARNING "AFFS: Bitmap (%d,key=%lu) invalid - "
+					       "mounting %s read only.\n",mapidx,be32_to_cpu(bm[i]),
+						kdevname(dev));
+					s->s_flags |= MS_RDONLY;
+					s->u.affs_sb.s_flags |= SF_READONLY;
+				}
+				/* Mark unused bits in the last word as allocated */
+				if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
+					ptype = size / 32 + 1;		/* word number */
+					key   = size & 0x1F;		/* used bits */
+					if (key && !(s->s_flags & MS_RDONLY)) {
+						chksum = cpu_to_be32(0x7FFFFFFF >> (31 - key));
+						((u32 *)bb->b_data)[ptype] &= chksum;
+						affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+						mark_buffer_dirty(bb,1);
+						bmalt = 1;
+					}
+					ptype = (size + 31) & ~0x1F;
+					size  = 0;
+					s->u.affs_sb.s_flags |= SF_BM_VALID;
+				} else {
+					ptype = s->s_blocksize * 8 - 32;
+					size -= ptype;
+				}
+				s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+				s->u.affs_sb.s_bitmap[mapidx].bm_bh       = NULL;
+				s->u.affs_sb.s_bitmap[mapidx].bm_key      = be32_to_cpu(bm[i]);
+				s->u.affs_sb.s_bitmap[mapidx].bm_count    = 0;
+				offset += ptype;
+
+				for (j = 0; ptype > 0; j++, az_no++, ptype -= key) {
+					key = MIN(ptype,AFFS_ZONE_SIZE);	/* size in bits */
+					s->u.affs_sb.s_alloc[az_no].az_size = key / 32;
+					s->u.affs_sb.s_alloc[az_no].az_free =
+						affs_count_free_bits(key / 8,bb->b_data +
+								     j * (AFFS_ZONE_SIZE / 8) + 4);
+				}
+				affs_brelse(bb);
+			} else {
+				printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+				goto out;
+			}
+		}
+		key   = be32_to_cpu(bm[stype]);		/* Next block of bitmap pointers	*/
+		ptype = 0;
+		stype = s->s_blocksize / 4 - 1;
+		affs_brelse(bh);
+		if (key) {
+			if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
+				printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+				goto out;
+			}
+		} else
+			bh = NULL;
+	}
+	if (mapidx < num_bm) {
+		printk(KERN_ERR "AFFS: Got only %d bitmap blocks, expected %d\n",mapidx,num_bm);
+		goto out;
+	}
+nobitmap:
+	s->u.affs_sb.s_bm_count = num_bm;
+
+	/* set up enough so that it can read an inode */
+
+	s->s_dev   = dev;
+	s->s_op    = &affs_sops;
+	s->s_dirt  = 1;
+	root_inode = iget(s,root_block);
+	s->s_root  = d_alloc_root(root_inode,NULL);
+	unlock_super(s);
+
+	if (!(s->s_root)) {
+		s->s_dev = 0;
+		affs_brelse(s->u.affs_sb.s_root_bh);
+		printk(KERN_ERR "AFFS: get root inode failed\n");
+		MOD_DEC_USE_COUNT;
+		return NULL;
+	}
+
+	/* Record date of last change if the bitmap was truncated and
+	 * create data zones if the volume is writable.
+	 */
+
+	if (!(s->s_flags & MS_RDONLY)) {
+		if (bmalt) {
+			secs_to_datestamp(CURRENT_TIME,&ROOT_END(
+				s->u.affs_sb.s_root_bh->b_data,root_inode)->disk_altered);
+			affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
+			mark_buffer_dirty(s->u.affs_sb.s_root_bh,1);
+		}
+		affs_make_zones(s);
+	}
+
+	pr_debug("AFFS: s_flags=%lX\n",s->s_flags);
+	return s;
+
+ out: /* Kick out for various error conditions */
+	affs_brelse (bh);
+	affs_brelse(s->u.affs_sb.s_root_bh);
+	if (s->u.affs_sb.s_bitmap)
+		kfree(s->u.affs_sb.s_bitmap);
+	set_blocksize(dev,BLOCK_SIZE);
+	s->s_dev = 0;
+	unlock_super(s);
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+static int
+affs_remount(struct super_block *sb, int *flags, char *data)
+{
+	int			 blocksize;
+	uid_t			 uid;
+	gid_t			 gid;
+	int			 mode;
+	int			 reserved;
+	int			 root_block;
+	unsigned long		 mount_flags;
+	unsigned long		 read_only = sb->u.affs_sb.s_flags & SF_READONLY;
+
+	pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
+
+	if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
+	    &blocksize,&sb->u.affs_sb.s_prefix,sb->u.affs_sb.s_volume,&mount_flags))
+		return -EINVAL;
+	sb->u.affs_sb.s_flags = mount_flags | read_only;
+	sb->u.affs_sb.s_mode  = mode;
+	sb->u.affs_sb.s_uid   = uid;
+	sb->u.affs_sb.s_gid   = gid;
+
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+		return 0;
+	if (*flags & MS_RDONLY) {
+		sb->s_dirt = 1;
+		while (sb->s_dirt)
+			affs_write_super(sb);
+		sb->s_flags |= MS_RDONLY;
+	} else if (!(sb->u.affs_sb.s_flags & SF_READONLY)) {
+		sb->s_flags &= ~MS_RDONLY;
+		affs_make_zones(sb);
+	} else {
+		affs_warning(sb,"remount","Cannot remount fs read/write because of errors");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
+{
+	int		 free;
+	struct statfs	 tmp;
+
+	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",sb->u.affs_sb.s_partition_size,
+	     sb->u.affs_sb.s_reserved);
+
+	free          = affs_count_free_blocks(sb);
+	tmp.f_type    = AFFS_SUPER_MAGIC;
+	tmp.f_bsize   = sb->s_blocksize;
+	tmp.f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+	tmp.f_bfree   = free;
+	tmp.f_bavail  = free;
+	tmp.f_files   = 0;
+	tmp.f_ffree   = 0;
+	return copy_to_user(buf,&tmp,bufsiz) ? -EFAULT : 0;
+}
+
+static struct file_system_type affs_fs_type = {
+	"affs",
+	FS_REQUIRES_DEV,
+	affs_read_super,
+	NULL
+};
+
+__initfunc(int init_affs_fs(void))
+{
+	return register_filesystem(&affs_fs_type);
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int
+init_module(void)
+{
+	return register_filesystem(&affs_fs_type);
+}
+
+void
+cleanup_module(void)
+{
+	unregister_filesystem(&affs_fs_type);
+}
+
+#endif
diff -u --recursive --new-file v2.1.70/linux/fs/affs/symlink.c linux/fs/affs/symlink.c
--- v2.1.70/linux/fs/affs/symlink.c	Mon Jun 16 16:35:56 1997
+++ linux/fs/affs/symlink.c	Tue Dec  2 22:25:07 1997
@@ -19,7 +19,8 @@
 
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 
-static int affs_readlink(struct inode *, char *, int);
+static int		 affs_readlink(struct inode *, char *, int);
+static struct dentry	*affs_follow_link(struct inode *inode, struct dentry *base);
 
 struct inode_operations affs_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -33,9 +34,13 @@
 	NULL,			/* mknod */
 	NULL,			/* rename */
 	affs_readlink,		/* readlink */
+	affs_follow_link,	/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	NULL			/* smap */
 };
 
 static int
@@ -46,21 +51,24 @@
 	int			 i, j;
 	char			 c;
 	char			 lc;
+	char			*pf;
 
 	pr_debug("AFFS: readlink(ino=%lu,buflen=%d)\n",inode->i_ino,buflen);
 
 	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
-	i  = 0;
-	j  = 0;
 	if (!bh) {
-		affs_error(inode->i_sb,"readlink","Cannot read block %lu\n",inode->i_ino);
-		goto symlink_end;
+		affs_warning(inode->i_sb,"follow_link","Unable to read i-node block %lu\n",
+				inode->i_ino);
+		return -EIO;
 	}
 	lf = (struct slink_front *)bh->b_data;
 	lc = 0;
+	i  = 0;
+	j  = 0;
+	pf = inode->i_sb->u.affs_sb.s_prefix ? inode->i_sb->u.affs_sb.s_prefix : "/";
 	
 	if (strchr(lf->symname,':')) {		/* Handle assign or volume name */
-		while (i < buflen && (c = inode->i_sb->u.affs_sb.s_prefix[i])) {
+		while (i < buflen && (c = pf[i])) {
 			put_user(c,buffer++);
 			i++;
 		}
@@ -84,8 +92,64 @@
 		lc = c;
 		i++, j++;
 	}
-symlink_end:
-	iput(inode);
 	affs_brelse(bh);
 	return i;
 }
+
+static struct dentry *
+affs_follow_link(struct inode *inode, struct dentry *base)
+{
+	struct buffer_head	*bh;
+	struct slink_front	*lf;
+	char			*buffer;
+	int			 i, j;
+	char			 c;
+	char			 lc;
+	char			*pf;
+
+	pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
+
+	if (!(buffer = kmalloc(1024,GFP_KERNEL))) {
+		dput(base);
+		return ERR_PTR(-ENOSPC);
+	}
+	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	if (!bh) {
+		affs_warning(inode->i_sb,"follow_link","Unable to read i-node block %lu\n",
+				inode->i_ino);
+		kfree(buffer);
+		dput(base);
+		return ERR_PTR(-EIO);
+	}
+	i  = 0;
+	j  = 0;
+	lf = (struct slink_front *)bh->b_data;
+	lc = 0;
+	pf = inode->i_sb->u.affs_sb.s_prefix ? inode->i_sb->u.affs_sb.s_prefix : "/";
+
+	if (strchr(lf->symname,':')) {		/* Handle assign or volume name */
+		while (i < 1023 && (c = pf[i]))
+			buffer[i++] = c;
+		while (i < 1023 && lf->symname[j] != ':')
+			buffer[i++] = lf->symname[j++];
+		if (i < 1023)
+			 buffer[i++] = '/';
+		j++;
+		lc = '/';
+	}
+	while (i < 1023 && (c = lf->symname[j])) {
+		if (c == '/' && lc == '/' && i < 1020) {	/* parent dir */
+			buffer[i++] = '.';
+			buffer[i++] = '.';
+		}
+		buffer[i++] = c;
+		lc = c;
+		j++;
+	}
+	buffer[i] = '\0';
+	affs_brelse(bh);
+	base = lookup_dentry(buffer,base,1);
+	kfree(buffer);
+	return base;
+}
+
diff -u --recursive --new-file v2.1.70/linux/fs/coda/sysctl.c linux/fs/coda/sysctl.c
--- v2.1.70/linux/fs/coda/sysctl.c	Tue Dec  2 16:45:20 1997
+++ linux/fs/coda/sysctl.c	Wed Dec  3 17:15:04 1997
@@ -1,5 +1,4 @@
 /* sysctl entries for Coda! */
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 #include <linux/sysctl.h>
diff -u --recursive --new-file v2.1.70/linux/fs/fat/file.c linux/fs/fat/file.c
--- v2.1.70/linux/fs/fat/file.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/fat/file.c	Wed Dec  3 04:24:29 1997
@@ -155,7 +155,7 @@
 /*
 	Read a file into user space
 */
-ssize_t fat_file_read(
+static ssize_t fat_file_read_text(
 	struct file *filp,
 	char *buf,
 	size_t count,
@@ -270,6 +270,17 @@
 	return buf-start;
 }
 
+ssize_t fat_file_read(
+	struct file *filp,
+	char *buf,
+	size_t count,
+	loff_t *ppos)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	if (!MSDOS_I(inode)->i_binary)
+		return fat_file_read_text(filp, buf, count, ppos);
+	return generic_file_read(filp, buf, count, ppos);
+}
 /*
 	Write to a file either from user space
 */
diff -u --recursive --new-file v2.1.70/linux/fs/isofs/namei.c linux/fs/isofs/namei.c
--- v2.1.70/linux/fs/isofs/namei.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/isofs/namei.c	Wed Dec  3 04:24:29 1997
@@ -66,7 +66,7 @@
 	unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
 	unsigned int block, i, f_pos, offset, 
 		inode_number = 0; /* shut gcc up */
-	struct buffer_head * bh;
+	struct buffer_head * bh , * retval = NULL;
 	unsigned int old_offset;
 	int dlen, match;
 	char * dpnt;
@@ -86,7 +86,7 @@
 	block = isofs_bmap(dir,f_pos >> bufbits);
 
 	if (!block || !(bh = bread(dir->i_dev,block,bufsize))) return NULL;
-  
+
 	while (f_pos < dir->i_size) {
 
 		/* if de is in kmalloc'd memory, do not point to the
@@ -118,13 +118,14 @@
 					 + ISOFS_BLOCK_SIZE);
 			}
 			brelse(bh);
+			bh = NULL;
 
 			if (f_pos >= dir->i_size) 
-				return 0;
+				break;
 
 			block = isofs_bmap(dir,f_pos>>bufbits);
 			if (!block || !(bh = bread(dir->i_dev,block,bufsize)))
-				return NULL;
+				break;
 
 			continue; /* Will kick out if past end of directory */
 		}
@@ -145,7 +146,7 @@
 			block = isofs_bmap(dir,f_pos>>bufbits);
 			if (!block || 
 			    !(bh_next = bread(dir->i_dev,block,bufsize)))
-				return NULL;
+				break;
 			
 			de = (struct iso_directory_record *)
 				kmalloc(offset - old_offset, GFP_KERNEL);
@@ -162,14 +163,14 @@
 
 		if (dir->i_sb->u.isofs_sb.s_rock ||
 		    dir->i_sb->u.isofs_sb.s_joliet_level) {
-			page = (unsigned char *)
-				__get_free_page(GFP_KERNEL);
-			if (!page) return NULL;
+			if (! page) {
+				page = (unsigned char *)
+					__get_free_page(GFP_KERNEL);
+				if (!page) break;
+			}
 		}
 		if (dir->i_sb->u.isofs_sb.s_rock &&
 		    ((i = get_rock_ridge_filename(de, page, dir)))) {
-			if (i == -1)
-				goto out;/* Relocated deep directory */
 			dlen = i;
 			dpnt = page;
 #ifdef CONFIG_JOLIET
@@ -203,8 +204,6 @@
 		{
 			match = isofs_match(namelen,name,dpnt,dlen);
 		}
-
-		if (page) free_page((unsigned long) page);
 		if (match) {
 			if(inode_number == -1) {
 				/* Should only happen for the '..' entry */
@@ -213,16 +212,16 @@
 					   find_rock_ridge_relocation(de,dir));
 			}
 			*ino = inode_number;
-			if(de_not_in_buf) 
-				kfree(de);
-			return bh;
+			retval = bh;
+			bh = NULL;
+			break;
 		}
 	}
- out:
-	brelse(bh);
+	if (page) free_page((unsigned long) page);
+	if (bh) brelse(bh);
 	if(de_not_in_buf) 
 		kfree(de);
-	return NULL;
+	return retval;
 }
 
 int isofs_lookup(struct inode * dir, struct dentry * dentry)
diff -u --recursive --new-file v2.1.70/linux/fs/locks.c linux/fs/locks.c
--- v2.1.70/linux/fs/locks.c	Sat Oct 25 02:44:17 1997
+++ linux/fs/locks.c	Tue Dec  2 22:20:18 1997
@@ -402,11 +402,11 @@
 	
 	switch (flock.l_type) {
 	case F_RDLCK:
-		if (!(filp->f_mode & 1))
+		if (!(filp->f_mode & FMODE_READ))
 			return (-EBADF);
 		break;
 	case F_WRLCK:
-		if (!(filp->f_mode & 2))
+		if (!(filp->f_mode & FMODE_WRITE))
 			return (-EBADF);
 		break;
 	case F_UNLCK:
diff -u --recursive --new-file v2.1.70/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.70/linux/fs/nfs/dir.c	Sat Nov 29 11:25:12 1997
+++ linux/fs/nfs/dir.c	Thu Dec  4 09:49:48 1997
@@ -1144,7 +1144,7 @@
 	 */
 	if (!list_empty(&new_dentry->d_hash)) {
 		d_drop(new_dentry);
-		rehash = 1;
+		rehash = update;
 	}
 	error = nfs_proc_rename(NFS_SERVER(old_dir),
 				NFS_FH(old_dir), old_dentry->d_name.name,
diff -u --recursive --new-file v2.1.70/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v2.1.70/linux/fs/nfs/file.c	Sat Nov  1 11:04:27 1997
+++ linux/fs/nfs/file.c	Wed Dec  3 17:18:42 1997
@@ -113,7 +113,8 @@
 	ssize_t result;
 
 	dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
-			inode->i_dev, inode->i_ino, count,
+			inode->i_dev, inode->i_ino,
+			(unsigned long) count,
 			(unsigned long) *ppos);
 
 	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
@@ -167,7 +168,7 @@
 
 	dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
 			inode->i_dev, inode->i_ino, inode->i_count,
-			count, (unsigned long) *ppos);
+			(unsigned long) count, (unsigned long) *ppos);
 
 	if (!inode) {
 		printk("nfs_file_write: inode = NULL\n");
diff -u --recursive --new-file v2.1.70/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.1.70/linux/fs/nfsd/nfssvc.c	Mon Dec  1 12:04:14 1997
+++ linux/fs/nfsd/nfssvc.c	Wed Dec  3 15:28:25 1997
@@ -165,7 +165,7 @@
 
 		for (signo = 1; signo <= _NSIG; signo++)
 			if (sigismember(&current->signal, signo) &&
-			    !sigismember(&current->signal, signo))
+			    !sigismember(&current->blocked, signo))
 				break;
 		printk(KERN_WARNING "nfsd: terminating on signal %d\n", signo);
 	}
diff -u --recursive --new-file v2.1.70/linux/fs/proc/proc_tty.c linux/fs/proc/proc_tty.c
--- v2.1.70/linux/fs/proc/proc_tty.c	Thu Mar 27 14:40:06 1997
+++ linux/fs/proc/proc_tty.c	Wed Dec  3 15:21:57 1997
@@ -49,8 +49,10 @@
 		case TTY_DRIVER_TYPE_SYSTEM:
 			if (p->subtype == SYSTEM_TYPE_TTY)
 				type = "system:/dev/tty";
-			else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+			else if (p->subtype == SYSTEM_TYPE_SYSCONS)
 				type = "system:console";
+			else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+				type = "system:vtmaster";
 			else
 				type = "system";
 			break;
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/Makefile linux/fs/ufs/Makefile
--- v2.1.70/linux/fs/ufs/Makefile	Mon Apr 22 00:59:39 1996
+++ linux/fs/ufs/Makefile	Wed Dec  3 06:18:33 1997
@@ -9,7 +9,7 @@
 
 O_TARGET := ufs.o
 O_OBJS   := ufs_dir.o ufs_file.o ufs_inode.o ufs_namei.o \
-		ufs_super.o ufs_symlink.o
+		ufs_super.o ufs_symlink.o ufs_swab.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_dir.c linux/fs/ufs/ufs_dir.c
--- v2.1.70/linux/fs/ufs/ufs_dir.c	Thu Sep 11 09:02:24 1997
+++ linux/fs/ufs/ufs_dir.c	Wed Dec  3 06:18:33 1997
@@ -6,12 +6,13 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * $Id: ufs_dir.c,v 1.10 1997/06/05 01:29:06 davem Exp $
+ * swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406
  *
  */
 
 #include <linux/fs.h>
-#include <linux/ufs_fs.h>
+
+#include "ufs_swab.h"
 
 /*
  * This is blatantly stolen from ext2fs
@@ -26,12 +27,18 @@
 	struct buffer_head * bh;
 	struct ufs_direct * de;
 	struct super_block * sb;
+	int de_reclen;
+	__u32 s_flags, bytesex;
 
+	/* Isn't that already done but the upper layer??? */
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
+
 	sb = inode->i_sb;
+        s_flags = sb->u.ufs_sb.s_flags;
+        bytesex = s_flags & UFS_BYTESEX;
 
-	if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+	if (s_flags & UFS_DEBUG) {
 	        printk("ufs_readdir: ino %lu  f_pos %lu\n",
 	               inode->i_ino, (unsigned long) filp->f_pos);
 	        ufs_print_inode(inode);
@@ -48,7 +55,8 @@
 		bh = bread (sb->s_dev, blk, sb->s_blocksize);
 		if (!bh) {
 	                /* XXX - error - skip to the next block */
-	                printk("ufs_readdir: dir inode %lu has a hole at offset %lu\n",
+	                printk("ufs_readdir: "
+			       "dir inode %lu has a hole at offset %lu\n",
 	                       inode->i_ino, (unsigned long int)filp->f_pos);
 			filp->f_pos += sb->s_blocksize - offset;
 			continue;
@@ -61,7 +69,7 @@
 		 * to make sure. */
 		if (filp->f_version != inode->i_version) {
 			for (i = 0; i < sb->s_blocksize && i < offset; ) {
-				de = (struct ufs_direct *) 
+				de = (struct ufs_direct *)
 					(bh->b_data + i);
 				/* It's too expensive to do a full
 				 * dirent test each time round this
@@ -69,28 +77,30 @@
 				 * least that it is non-zero.  A
 				 * failure will be detected in the
 				 * dirent test below. */
-				if (ufs_swab16(de->d_reclen) < 1)
+				de_reclen = SWAB16(de->d_reclen);
+				if (de_reclen < 1)
 					break;
-				i += ufs_swab16(de->d_reclen);
+				i += de_reclen;
 			}
 			offset = i;
 			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
 				| offset;
 			filp->f_version = inode->i_version;
 		}
-		
-		while (!error && filp->f_pos < inode->i_size 
+
+		while (!error && filp->f_pos < inode->i_size
 		       && offset < sb->s_blocksize) {
 			de = (struct ufs_direct *) (bh->b_data + offset);
 	                /* XXX - put in a real ufs_check_dir_entry() */
-	                if ((ufs_swab16(de->d_reclen) == 0)
-                             || (ufs_swab16(de->d_namlen) == 0)) {
+	                if ((de->d_reclen == 0) || (de->d_namlen == 0)) {
+			/* SWAB16() was unneeded -- compare to 0 */
 	                        filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) + sb->s_blocksize;
 	                        brelse(bh);
 	                        return stored;
 	                }
 #if 0
 			if (!ext2_check_dir_entry ("ext2_readdir", inode, de,
+			/* XXX - beware about de having to be swabped somehow */
 						   bh, offset)) {
 				/* On error, skip the f_pos to the
 	                           next block. */
@@ -100,8 +110,9 @@
 				return stored;
 			}
 #endif /* XXX */
-			offset += ufs_swab16(de->d_reclen);
-			if (ufs_swab32(de->d_ino)) {
+			offset += SWAB16(de->d_reclen);
+			if (de->d_ino) {
+			/* SWAB16() was unneeded -- compare to 0 */
 				/* We might block in the next section
 				 * if the data destination is
 				 * currently swapped out.  So, use a
@@ -110,18 +121,18 @@
 				 * during the copy operation. */
 				unsigned long version = inode->i_version;
 
-	                        if (inode->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
+	                        if (s_flags & UFS_DEBUG) {
 	                                printk("ufs_readdir: filldir(%s,%u)\n",
-	                                       de->d_name, ufs_swab32(de->d_ino));
+	                                       de->d_name, SWAB32(de->d_ino));
 	                        }
-				error = filldir(dirent, de->d_name, ufs_swab16(de->d_namlen), filp->f_pos, ufs_swab32(de->d_ino));
+				error = filldir(dirent, de->d_name, SWAB16(de->d_namlen), filp->f_pos, SWAB32(de->d_ino));
 				if (error)
 					break;
 				if (version != inode->i_version)
 					goto revalidate;
 				stored ++;
 			}
-			filp->f_pos += ufs_swab16(de->d_reclen);
+			filp->f_pos += SWAB16(de->d_reclen);
 		}
 		offset = 0;
 		brelse (bh);
@@ -140,7 +151,7 @@
 	NULL,			/* read */
 	NULL,			/* write */
 	ufs_readdir,		/* readdir */
-	NULL,			/* poll */
+	NULL,			/* select */
 	NULL,			/* ioctl */
 	NULL,			/* mmap */
 	NULL,			/* open */
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_file.c linux/fs/ufs/ufs_file.c
--- v2.1.70/linux/fs/ufs/ufs_file.c	Thu Jul 17 10:06:08 1997
+++ linux/fs/ufs/ufs_file.c	Wed Dec  3 06:18:33 1997
@@ -18,7 +18,7 @@
 	generic_file_read,	/* read */
 	NULL,			/* write */
 	NULL,			/* readdir */
-	NULL,			/* poll */
+	NULL,			/* select */
 	NULL,			/* ioctl */
 	generic_file_mmap,	/* mmap */
 	NULL,			/* open */
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_inode.c linux/fs/ufs/ufs_inode.c
--- v2.1.70/linux/fs/ufs/ufs_inode.c	Thu Jul 17 10:06:08 1997
+++ linux/fs/ufs/ufs_inode.c	Wed Dec  3 06:18:33 1997
@@ -6,7 +6,8 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * $Id: ufs_inode.c,v 1.9 1997/07/17 02:24:14 davem Exp $
+ * Clean swab support on 19970406
+ * by Francois-Rene Rideau <rideau@ens.fr>
  *
  */
 
@@ -14,6 +15,8 @@
 #include <linux/ufs_fs.h>
 #include <linux/sched.h>
 
+#include "ufs_swab.h"
+
 void ufs_print_inode(struct inode * inode)
 {
 	printk("ino %lu  mode 0%6.6o  lk %d  uid %d  gid %d"
@@ -41,14 +44,14 @@
 {
 	struct buffer_head *bh;
 	int tmp;
-
+	__u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
 	/* XXX Split in fsize big blocks (Can't bread 8Kb). */ 
 	tmp = nr >> (inode->i_sb->u.ufs_sb.s_fshift - 2);
 	bh = bread (inode->i_dev, block + tmp, inode->i_sb->u.ufs_sb.s_fsize);
 	if (!bh)
 		return 0;
 	nr &= ~(inode->i_sb->u.ufs_sb.s_fmask) >> 2;
-	tmp = ufs_swab32(((__u32 *)bh->b_data)[nr]);
+	tmp = SWAB32(((__u32 *)bh->b_data)[nr]);
 	brelse (bh);
 	return tmp;
 }
@@ -113,6 +116,7 @@
 	struct super_block * sb;
 	struct ufs_inode * ufsip;
 	struct buffer_head * bh;
+	__u32 bytesex = inode->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
 
 	sb = inode->i_sb;
 
@@ -144,8 +148,8 @@
 	/*
 	 * Copy data to the in-core inode.
 	 */
-	inode->i_mode = ufs_swab16(ufsip->ui_mode);
-	inode->i_nlink = ufs_swab16(ufsip->ui_nlink);
+	inode->i_mode = SWAB16(ufsip->ui_mode);
+	inode->i_nlink = SWAB16(ufsip->ui_nlink);
 	if (inode->i_nlink == 0) {
 	        /* XXX */
 	        printk("ufs_read_inode: zero nlink ino %lu  dev %u/%u\n",
@@ -158,7 +162,7 @@
 	               MAJOR(inode->i_dev), MINOR(inode->i_dev));
 	}
 	/* XXX - debugging */
-	if (ufs_swab32(ufsip->ui_gen) == 0) {
+	if (SWAB32(ufsip->ui_gen) == 0) {
 	        printk("ufs_read_inode: zero gen ino %lu pblk %lu dev %u/%u\n",
 	               inode->i_ino,
 	               ufs_cgimin(inode->i_sb, ufs_ino2cg(inode)) +
@@ -172,33 +176,33 @@
 	 * random users can't get at these files, since they get dynamically
 	 * "chown()ed" to root.
 	 */
-	if (ufs_swab16(ufsip->ui_suid) == UFS_USEEFT) {
+	if (SWAB16(ufsip->ui_suid) == UFS_USEEFT) {
 	        /* EFT */
 	        inode->i_uid = 0;
 	        printk("ufs_read_inode: EFT uid %u ino %lu dev %u/%u, using %u\n",
-	               ufs_swab32(ufsip->ui_uid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
+	               SWAB32(ufsip->ui_uid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
 	               inode->i_uid);
 	} else {
-	        inode->i_uid = ufs_swab16(ufsip->ui_suid);
+	        inode->i_uid = SWAB16(ufsip->ui_suid);
 	}
-	if (ufs_swab16(ufsip->ui_sgid) == UFS_USEEFT) {
+	if (SWAB16(ufsip->ui_sgid) == UFS_USEEFT) {
 	        /* EFT */
 	        inode->i_gid = 0;
 	        printk("ufs_read_inode: EFT gid %u ino %lu dev %u/%u, using %u\n",
-	               ufs_swab32(ufsip->ui_gid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
+	               SWAB32(ufsip->ui_gid), inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev),
 	               inode->i_gid);
 	} else {
-	        inode->i_gid = ufs_swab16(ufsip->ui_sgid);
+	        inode->i_gid = SWAB16(ufsip->ui_sgid);
 	}
 
 	/*
-	 * Linux i_size is 32 bits, so some files on a UFS filesystem may not
+	 * Linux i_size is 32 bits on most architectures,
+	 * so some files on a UFS filesystem may not
 	 * be readable.  I let people access the first 32 bits worth of them.
 	 * for the rw code, we may want to mark these inodes as read-only.
 	 * XXX - bug Linus to make i_size a __u64 instead of a __u32.
 	 */
-	inode->u.ufs_i.i_size = ((__u64)(ufs_swab32(ufsip->ui_size.val[0]))<<32) |
-				 (__u64)(ufs_swab32(ufsip->ui_size.val[1]));
+	inode->u.ufs_i.i_size = SWAB64(ufsip->ui_size);
 	/* KRR - Just type cast inode->u.ufs_i.i_size into off_t and
 	 * worry about overflow later
          */
@@ -210,11 +214,11 @@
 	 * want to keep this data, but for the moment I think I'll just write
 	 * zeros for these fields when writing out inodes.
 	 */
-	inode->i_atime = ufsip->ui_atime.tv_sec;
-	inode->i_mtime = ufsip->ui_mtime.tv_sec;
-	inode->i_ctime = ufsip->ui_ctime.tv_sec;
+	inode->i_atime = SWAB32(ufsip->ui_atime.tv_sec);
+	inode->i_mtime = SWAB32(ufsip->ui_mtime.tv_sec);
+	inode->i_ctime = SWAB32(ufsip->ui_ctime.tv_sec);
 	inode->i_blksize = sb->u.ufs_sb.s_fsize;
-	inode->i_blocks = ufs_swab32(ufsip->ui_blocks);
+	inode->i_blocks = SWAB32(ufsip->ui_blocks);
 	inode->i_version = ++event; /* see linux/kernel/sched.c */
 
 	if (S_ISREG(inode->i_mode)) {
@@ -246,11 +250,11 @@
 	        int i;
 
 	        for (i = 0; i < UFS_NDADDR; i++) {
-	                inode->u.ufs_i.i_data[i] = ufs_swab32(ufsip->ui_db[i]);
+	                inode->u.ufs_i.i_data[i] = SWAB32(ufsip->ui_db[i]);
 	        }
 	        for (i = 0; i < UFS_NINDIR; i++) {
 	                inode->u.ufs_i.i_data[UFS_IND_BLOCK + i] =
-							ufs_swab32(ufsip->ui_ib[i]);
+							SWAB32(ufsip->ui_ib[i]);
 	        }
 	}
 
@@ -261,18 +265,17 @@
 	 * the code.
 	 */
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
-		inode->i_rdev = (kdev_t)((__u64)(ufs_swab32(ufsip->ui_db[0]))<<32) |
-				 (__u64)(ufs_swab32(ufsip->ui_db[1]));
+	  inode->i_rdev = (kdev_t)SWAB64(*(__u64*)&ufsip->ui_db);
 	}
 
 	/* XXX - implement fast and slow symlinks */
 
-	inode->u.ufs_i.i_flags = ufs_swab32(ufsip->ui_flags);
-	inode->u.ufs_i.i_gen = ufs_swab32(ufsip->ui_gen); /* XXX - is this i_version? */
-	inode->u.ufs_i.i_shadow = ufs_swab32(ufsip->ui_shadow); /* XXX */
-	inode->u.ufs_i.i_uid = ufs_swab32(ufsip->ui_uid);
-	inode->u.ufs_i.i_gid = ufs_swab32(ufsip->ui_gid);
-	inode->u.ufs_i.i_oeftflag = ufs_swab32(ufsip->ui_oeftflag);
+	inode->u.ufs_i.i_flags = SWAB32(ufsip->ui_flags);
+	inode->u.ufs_i.i_gen = SWAB32(ufsip->ui_gen); /* XXX - is this i_version? */
+	inode->u.ufs_i.i_shadow = SWAB32(ufsip->ui_shadow); /* XXX */
+	inode->u.ufs_i.i_uid = SWAB32(ufsip->ui_uid);
+	inode->u.ufs_i.i_gid = SWAB32(ufsip->ui_gid);
+	inode->u.ufs_i.i_oeftflag = SWAB32(ufsip->ui_oeftflag);
 
 	brelse(bh);
 
@@ -307,4 +310,3 @@
 
 	return;
 }
-
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_namei.c linux/fs/ufs/ufs_namei.c
--- v2.1.70/linux/fs/ufs/ufs_namei.c	Mon Aug  4 16:25:39 1997
+++ linux/fs/ufs/ufs_namei.c	Wed Dec  3 06:18:33 1997
@@ -6,48 +6,62 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * $Id: ufs_namei.c,v 1.9 1997/07/22 06:40:12 davem Exp $
+ * Clean swab support by Francois-Rene Rideau <rideau@ens.fr> 19970406
+ * Ported to 2.1.62 by Francois-Rene Rideau <rideau@issy.cnet.fr> 19971109
  *
  */
 
 #include <linux/fs.h>
 #include <linux/ufs_fs.h>
-
 #include <linux/string.h>
+#include "ufs_swab.h"
 
 /*
  * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
  * stolen from ext2fs
  */
-static int ufs_match (int len, const char * const name, struct ufs_direct * d)
+static int ufs_match (int len, const char * const name, struct ufs_direct * d, __u32 bytesex)
 {
 	if (!d || len > UFS_MAXNAMLEN) /* XXX - name space */
 		return 0;
 	/*
 	 * "" means "." ---> so paths like "/usr/lib//libc.a" work
 	 */
-	if (!len && (ufs_swab16(d->d_namlen) == 1) && (d->d_name[0] == '.') &&
-	   (d->d_name[1] == '\0'))
-		return 1;
-	if (len != ufs_swab16(d->d_namlen))
+	if (!len && (SWAB16(d->d_namlen) == 1) && (d->d_name[0] == '.') &&
+  	   (d->d_name[1] == '\0'))
+  		return 1;
+	if (len != SWAB16(d->d_namlen))
 		return 0;
 	return !memcmp(name, d->d_name, len);
 }
 
-/* XXX - this is a mess, especially for endianity */
-int ufs_lookup (struct inode * dir, struct qstr *qname,
-	        struct inode ** result)
+int ufs_lookup (struct inode *dir, struct dentry *dentry)
 {
+  /* XXX - this is all fucked up! */
+  /* XXX - and it's been broken since linux has this new dentry interface:
+   * allows reading of files, but screws the whole dcache, even outside
+   * of the ufs partition, so that umount'ing won't suffice to fix it --
+   * reboot needed
+   */
 	unsigned long int lfragno, fragno;
 	struct buffer_head * bh;
 	struct ufs_direct * d;
-	const char *name = qname->name;
-	int len = qname->len;
+	const char *name = dentry->d_name.name;
+	int len = dentry->d_name.len;
+	__u32 bytesex;
+        struct inode *inode;
+
+	/* XXX - isn't that already done by the upper layer? */
+        if (!dir || !S_ISDIR(dir->i_mode))
+		return -EBADF;
+
+	bytesex = dir->i_sb->u.ufs_sb.s_flags & UFS_BYTESEX;
 
 	if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG)
 		printk("Passed name: %s\nPassed length: %d\n", name, len);
 
-	/*
+
+	/* debugging hacks:
 	 * Touching /xyzzy in a filesystem toggles debugging messages.
 	 */
 	if ((len == 5) && !(memcmp(name, "xyzzy", len)) &&
@@ -56,8 +70,8 @@
 	        printk("UFS debugging %s\n",
 	               (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) ?
 	               "on": "off");
-		iput(dir);
-	        return(-ENOENT);
+		goto not_found;
+	        /*return(-ENOENT);*/
 	}
 
 	/*
@@ -69,8 +83,8 @@
 	        printk("UFS inode debugging %s\n",
 	               (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_INODE) ?
 	               "on": "off");
-		iput(dir);
-	        return(-ENOENT);
+		goto not_found;
+	        /*return(-ENOENT);*/
 	}
 
 	if ((len == 7) && !(memcmp(name, "xyzzy.n", len)) &&
@@ -79,8 +93,8 @@
 	        printk("UFS namei debugging %s\n",
 	               (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_NAMEI) ?
 	               "on": "off");
-		iput(dir);
-	        return(-ENOENT);
+		goto not_found;
+	        /*return(-ENOENT);*/
 	}
 
 	if ((len == 7) && !(memcmp(name, "xyzzy.l", len)) &&
@@ -89,10 +103,13 @@
 	        printk("UFS symlink debugging %s\n",
 	               (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG_LINKS) ?
 	               "on": "off");
-		iput(dir);
-	        return(-ENOENT);
+		goto not_found;
+	        /*return(-ENOENT);*/
 	}
 
+
+	/* Now for the real thing */
+
 	if (dir->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_NAMEI)) {
 	        printk("ufs_lookup: called for ino %lu  name %s\n",
 	               dir->i_ino, name);
@@ -109,21 +126,22 @@
 	        }
 	        if (fragno == 0) {
 	                /* XXX - bug bug bug */
-			iput(dir);
-	                return(-ENOENT);
+			goto not_found;
+			/*return(-ENOENT);*/
 	        }
 	        bh = bread(dir->i_dev, fragno, dir->i_sb->s_blocksize);
 	        if (bh == NULL) {
-	                printk("ufs_lookup: bread failed: ino %lu, lfragno %lu",
+	                printk("ufs_lookup: bread failed: "
+                               "ino %lu, lfragno %lu",
 	                       dir->i_ino, lfragno);
-			iput(dir);
 	                return(-EIO);
 	        }
 	        d = (struct ufs_direct *)(bh->b_data);
-	        while (((char *)d - bh->b_data + ufs_swab16(d->d_reclen)) <=
+	        while (((char *)d - bh->b_data + SWAB16(d->d_reclen)) <=
 	               dir->i_sb->s_blocksize) {
 	                /* XXX - skip block if d_reclen or d_namlen is 0 */
-	                if ((ufs_swab16(d->d_reclen) == 0) || (ufs_swab16(d->d_namlen) == 0)) {
+	                if ((d->d_reclen == 0) || (d->d_namlen == 0)) {
+			/* no need to SWAB16(): test against 0 */
 	                        if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
 	                                printk("ufs_lookup: skipped space in directory, ino %lu\n",
 	                                       dir->i_ino);
@@ -131,32 +149,43 @@
 	                        break;
 	                }
 	                if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
-	                        printk("lfragno 0x%lx  direct d 0x%x  d_ino %u  "
-				       "d_reclen %u  d_namlen %u  d_name `%s'\n",
-	                               lfragno, (unsigned int)((unsigned long)d),
-				       ufs_swab32(d->d_ino), ufs_swab16(d->d_reclen),
-				       ufs_swab16(d->d_namlen), d->d_name);
+	                        printk("lfragno 0x%lx  "
+					"direct d 0x%x  "
+					"d_ino %u  "
+					"d_reclen %u  "
+					"d_namlen %u  d_name `%s'\n",
+	                        	lfragno,
+				        (unsigned int)((unsigned long)d),
+					SWAB32(d->d_ino),
+					SWAB16(d->d_reclen),
+					SWAB16(d->d_namlen),d->d_name);
 	                }
-	                if ((ufs_swab16(d->d_namlen) == len) &&
+	                if ((SWAB16(d->d_namlen) == len) &&
 	                    /* XXX - don't use strncmp() - see ext2fs */
-	                    (ufs_match(len, name, d))) {
+	                    (ufs_match(len, name, d, bytesex))) {
 	                        /* We have a match */
-	                        *result = iget(dir->i_sb, ufs_swab32(d->d_ino));
-	                        brelse(bh);
-				iput(dir);
+/* XXX - I only superficially understand how things work,
+ * so use at your own risk... -- Fare'
+ */
+				inode = iget(dir->i_sb, SWAB32(d->d_ino));
+				brelse(bh);
+				if(!inode) { return -EACCES; }
+                                d_add(dentry,inode);
 	                        return(0);
 	                } else {
 	                        /* XXX - bounds checking */
 	                        if (dir->i_sb->u.ufs_sb.s_flags & UFS_DEBUG) {
-	                                printk("ufs_lookup: wanted (%s,%d) got (%s,%d)\n",
-	                                       name, len, d->d_name, ufs_swab16(d->d_namlen));
+	                                printk("ufs_lookup: "
+						"wanted (%s,%d) got (%s,%d)\n",
+	                                       name, len,
+					       d->d_name, SWAB16(d->d_namlen));
 	                        }
 	                }
-	                d = (struct ufs_direct *)((char *)d + ufs_swab16(d->d_reclen));
+			d = (struct ufs_direct *)((char *)d + SWAB16(d->d_reclen));
 	        }
 	        brelse(bh);
 	}
-	iput(dir);
-	return(-ENOENT);
+   not_found:
+	d_add(dentry,NULL);
+	return(0);
 }
-
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_super.c linux/fs/ufs/ufs_super.c
--- v2.1.70/linux/fs/ufs/ufs_super.c	Thu Jul 17 10:06:08 1997
+++ linux/fs/ufs/ufs_super.c	Wed Dec  3 06:18:33 1997
@@ -8,8 +8,6 @@
  *
  * Copyright (C) 1996  Eddie C. Dost  (ecd@skynet.be)
  *
- * $Id: ufs_super.c,v 1.25 1997/07/17 02:24:15 davem Exp $
- *
  */
 
 /*
@@ -18,30 +16,31 @@
  *
  * Module usage counts added on 96/04/29 by
  * Gertjan van Wingerde <gertjan@cs.vu.nl>
+ *
+ * Clean swab support on 19970406 by
+ * Francois-Rene Rideau <rideau@ens.fr>
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/ufs_fs.h>
 #include <linux/locks.h>
-#include <linux/init.h>
-
 #include <asm/uaccess.h>
 
-int ufs_need_swab = 0;
+#include "ufs_swab.h"
 
 struct super_block * ufs_read_super(struct super_block * sb, void * data, int silent);
 void ufs_put_super (struct super_block * sb);
-void ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsize);
+int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsize);
 
 static struct super_operations ufs_super_ops = {
 	ufs_read_inode,
-	NULL,			/* notify_change() */
 	NULL,			/* XXX - ufs_write_inode() */
 	ufs_put_inode,
+	NULL,			/* XXX - ufs_delete_inode() */
+	NULL,			/* notify_change() */
 	ufs_put_super,
 	NULL,			/* XXX - ufs_write_super() */
 	ufs_statfs,
@@ -55,7 +54,8 @@
 	NULL
 };
 
-__initfunc(int init_ufs_fs(void))
+int
+init_ufs_fs(void)
 {
 	return(register_filesystem(&ufs_fs_type));
 }
@@ -113,8 +113,9 @@
 struct super_block *
 ufs_read_super(struct super_block * sb, void * data, int silent)
 {
-	struct ufs_superblock * usb;
+	struct ufs_superblock * usb;	/* normalized to local byteorder */
 	struct buffer_head * bh1, *bh2;
+	__u32 bytesex = 0;
 
 	/* sb->s_dev and sb->s_flags are set by our caller
 	 * data is the mystery argument to sys_mount()
@@ -132,9 +133,7 @@
 	if (!(bh1 = bread(sb->s_dev, UFS_SBLOCK/BLOCK_SIZE, BLOCK_SIZE)) ||
 	    !(bh2 = bread(sb->s_dev, (UFS_SBLOCK + BLOCK_SIZE)/BLOCK_SIZE,
 	                  BLOCK_SIZE))) {
-	        if (bh1) {
-	                brelse(bh1);
-	        }
+		brelse(bh1);
 	        printk ("ufs_read_super: unable to read superblock\n");
 
 		goto ufs_read_super_lose;
@@ -156,32 +155,35 @@
 	brelse(bh1);
 	brelse(bh2);
 
-	ufs_need_swab = 0;
-	sb->s_magic = ufs_swab32(usb->fs_magic);
-	if (sb->s_magic != UFS_MAGIC) {
-		ufs_need_swab = 1;
-		sb->s_magic = ufs_swab32(usb->fs_magic);
-		if (sb->s_magic != UFS_MAGIC) {
-	                printk ("ufs_read_super: bad magic number 0x%8.8lx "
-				"on dev %d/%d\n", sb->s_magic,
+	switch (le32_to_cpup(&usb->fs_magic)) {
+		case UFS_MAGIC:
+			bytesex = UFS_LITTLE_ENDIAN;
+			ufs_superblock_le_to_cpus(usb);
+			break;
+		case UFS_CIGAM:
+			bytesex = UFS_BIG_ENDIAN;
+			ufs_superblock_be_to_cpus(usb);
+			break;
+			/* usb is now normalized to local byteorder */
+		default:
+	                printk ("ufs_read_super: bad magic number 0x%8.8x "
+				"on dev %d/%d\n", usb->fs_magic,
 				MAJOR(sb->s_dev), MINOR(sb->s_dev));
-
 			goto ufs_read_super_lose;
-		}
 	}
 
 	/* We found a UFS filesystem on this device. */
 
 	/* XXX - parse args */
 
-	if (ufs_swab32(usb->fs_bsize) != UFS_BSIZE) {
-	        printk("ufs_read_super: fs_bsize %d != %d\n", ufs_swab32(usb->fs_bsize),
+	if (usb->fs_bsize != UFS_BSIZE) {
+	        printk("ufs_read_super: fs_bsize %d != %d\n", usb->fs_bsize,
 	               UFS_BSIZE);
 	        goto ufs_read_super_lose;
 	}
 
-	if (ufs_swab32(usb->fs_fsize) != UFS_FSIZE) {
-	        printk("ufs_read_super: fs_fsize %d != %d\n", ufs_swab32(usb->fs_fsize),
+	if (usb->fs_fsize != UFS_FSIZE) {
+	        printk("ufs_read_super: fs_fsize %d != %d\n", usb->fs_fsize,
 	               UFS_FSIZE);
 	        goto ufs_read_super_lose;
 	}
@@ -190,7 +192,7 @@
 	printk("ufs_read_super: fs last mounted on \"%s\"\n", usb->fs_fsmnt);
 #endif
 
-	if (ufs_swab32(usb->fs_state) == UFS_FSOK - ufs_swab32(usb->fs_time)) {
+	if (usb->fs_state == UFS_FSOK - usb->fs_time) {
 	        switch(usb->fs_clean) {
 	                case UFS_FSCLEAN:
 #ifdef DEBUG_UFS_SUPER
@@ -225,35 +227,35 @@
 	/* XXX - sanity check sb fields */
 
 	/* KRR - Why are we not using fs_bsize for blocksize? */
-	sb->s_blocksize = ufs_swab32(usb->fs_fsize);
-	sb->s_blocksize_bits = ufs_swab32(usb->fs_fshift);
+	sb->s_blocksize = usb->fs_fsize;
+	sb->s_blocksize_bits = usb->fs_fshift;
 	/* XXX - sb->s_lock */
 	sb->s_op = &ufs_super_ops;
 	sb->dq_op = 0; /* XXX */
-	/* KRR - defined above - sb->s_magic = usb->fs_magic; */
+	sb->s_magic = usb->fs_magic;
 	/* sb->s_time */
 	/* sb->s_wait */
 	/* XXX - sb->u.ufs_sb */
 	sb->u.ufs_sb.s_raw_sb = usb; /* XXX - maybe move this to the top */
-	sb->u.ufs_sb.s_flags = 0;
-	sb->u.ufs_sb.s_ncg = ufs_swab32(usb->fs_ncg);
-	sb->u.ufs_sb.s_ipg = ufs_swab32(usb->fs_ipg);
-	sb->u.ufs_sb.s_fpg = ufs_swab32(usb->fs_fpg);
-	sb->u.ufs_sb.s_fsize = ufs_swab32(usb->fs_fsize);
-	sb->u.ufs_sb.s_fmask = ufs_swab32(usb->fs_fmask);
-	sb->u.ufs_sb.s_fshift = ufs_swab32(usb->fs_fshift);
-	sb->u.ufs_sb.s_bsize = ufs_swab32(usb->fs_bsize);
-	sb->u.ufs_sb.s_bmask = ufs_swab32(usb->fs_bmask);
-	sb->u.ufs_sb.s_bshift = ufs_swab32(usb->fs_bshift);
-	sb->u.ufs_sb.s_iblkno = ufs_swab32(usb->fs_iblkno);
-	sb->u.ufs_sb.s_dblkno = ufs_swab32(usb->fs_dblkno);
-	sb->u.ufs_sb.s_cgoffset = ufs_swab32(usb->fs_cgoffset);
-	sb->u.ufs_sb.s_cgmask = ufs_swab32(usb->fs_cgmask);
-	sb->u.ufs_sb.s_inopb = ufs_swab32(usb->fs_inopb);
-	sb->u.ufs_sb.s_lshift = ufs_swab32(usb->fs_bshift) - ufs_swab32(usb->fs_fshift);
-	sb->u.ufs_sb.s_lmask = ~((ufs_swab32(usb->fs_fmask) - ufs_swab32(usb->fs_bmask))
-					>> ufs_swab32(usb->fs_fshift));
-	sb->u.ufs_sb.s_fsfrag = ufs_swab32(usb->fs_frag); /* XXX - rename this later */
+	sb->u.ufs_sb.s_flags = bytesex | UFS_DEBUG_INITIAL ;
+	sb->u.ufs_sb.s_ncg = usb->fs_ncg;
+	sb->u.ufs_sb.s_ipg = usb->fs_ipg;
+	sb->u.ufs_sb.s_fpg = usb->fs_fpg;
+	sb->u.ufs_sb.s_fsize = usb->fs_fsize;
+	sb->u.ufs_sb.s_fmask = usb->fs_fmask;
+	sb->u.ufs_sb.s_fshift = usb->fs_fshift;
+	sb->u.ufs_sb.s_bsize = usb->fs_bsize;
+	sb->u.ufs_sb.s_bmask = usb->fs_bmask;
+	sb->u.ufs_sb.s_bshift = usb->fs_bshift;
+	sb->u.ufs_sb.s_iblkno = usb->fs_iblkno;
+	sb->u.ufs_sb.s_dblkno = usb->fs_dblkno;
+	sb->u.ufs_sb.s_cgoffset = usb->fs_cgoffset;
+	sb->u.ufs_sb.s_cgmask = usb->fs_cgmask;
+	sb->u.ufs_sb.s_inopb = usb->fs_inopb;
+	sb->u.ufs_sb.s_lshift = usb->fs_bshift - usb->fs_fshift;
+	sb->u.ufs_sb.s_lmask = ~((usb->fs_fmask - usb->fs_bmask)
+					>> usb->fs_fshift);
+	sb->u.ufs_sb.s_fsfrag = usb->fs_frag; /* XXX - rename this later */
 	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO), NULL);
 
 #ifdef DEBUG_UFS_SUPER
@@ -285,6 +287,7 @@
 	sb->s_dev = 0;
 
 	/* XXX - free allocated kernel memory */
+	/* includes freeing usb page */
 
 	unlock_super (sb);
 	MOD_DEC_USE_COUNT;
@@ -292,11 +295,12 @@
 	return;
 }
 
-void ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz)
+int ufs_statfs(struct super_block * sb, struct statfs * buf, int bufsiz)
 {
 	struct statfs tmp;
 	struct statfs *sp = &tmp;
 	struct ufs_superblock *fsb = sb->u.ufs_sb.s_raw_sb;
+	/* fsb was already normalized during mounting */
 	unsigned long used, avail;
 
         if (sb->u.ufs_sb.s_flags & UFS_DEBUG) {
@@ -305,13 +309,13 @@
 
 	sp->f_type = sb->s_magic;
 	sp->f_bsize = sb->s_blocksize;
-	sp->f_blocks = ufs_swab32(fsb->fs_dsize);
-	sp->f_bfree = ufs_swab32(fsb->fs_cstotal.cs_nbfree) *
-			ufs_swab32(fsb->fs_frag) +
-			ufs_swab32(fsb->fs_cstotal.cs_nffree);
+	sp->f_blocks = fsb->fs_dsize;
+	sp->f_bfree = fsb->fs_cstotal.cs_nbfree *
+			fsb->fs_frag +
+			fsb->fs_cstotal.cs_nffree;
 
 	avail = sp->f_blocks - (sp->f_blocks / 100) *
-			ufs_swab32(fsb->fs_minfree);
+			fsb->fs_minfree;
 	used = sp->f_blocks - sp->f_bfree;
 	if (avail > used)
 		sp->f_bavail = avail - used;
@@ -319,11 +323,10 @@
 		sp->f_bavail = 0;
 
 	sp->f_files = sb->u.ufs_sb.s_ncg * sb->u.ufs_sb.s_ipg;
-	sp->f_ffree = ufs_swab32(fsb->fs_cstotal.cs_nifree);
-	sp->f_fsid.val[0] = ufs_swab32(fsb->fs_id[0]);
-	sp->f_fsid.val[1] = ufs_swab32(fsb->fs_id[1]);
+	sp->f_ffree = fsb->fs_cstotal.cs_nifree;
+	sp->f_fsid.val[0] = fsb->fs_id[0];
+	sp->f_fsid.val[1] = fsb->fs_id[1];
 	sp->f_namelen = UFS_MAXNAMLEN;
 
-	copy_to_user(buf, sp, bufsiz);
-	return;
+	return copy_to_user(buf, sp, bufsiz) ? -EFAULT : 0;
 }
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_swab.c linux/fs/ufs/ufs_swab.c
--- v2.1.70/linux/fs/ufs/ufs_swab.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ufs/ufs_swab.c	Wed Dec  3 06:18:33 1997
@@ -0,0 +1,138 @@
+/*
+ *  linux/fs/ufs/ufs_swab.c
+ *
+ * Copyright (C) 1997
+ * Francois-Rene Rideau <rideau@ens.fr>
+ *
+ */
+
+/*
+ * For inspiration, you might wanna check sys/ufs/ffs/fs.h from whateverBSD
+ *
+ * NOTES
+ * 19970406 - Fare <rideau@ens.fr>
+ *   1) I began from old very preliminary 2.0.x sources,
+ *	but it was underfeatured;
+ *	I later saw that 2.1.1 sources had a *global* UFS byteswap flag.
+ *	EVIL: imagine that a swabbed partition be mounted
+ *	while a non-swabbed partition are active (that sucks!)
+ *	I merged that source tree with mine.
+ *   2) I hope no one is using obNNUUXXIIous byteorder.
+ *	That's the only thing I might have broken,
+ *	though I rather think it's a fix:
+ *	instead of __u64 like BSD,
+ *	the former driver used an explicitly bigendian array of __u32!
+ *   3) I provide a few macros that use GCC C Extensions.
+ *	Port to other compilers would require avoiding them.
+ *	in any case, 64 bit (long long) support is required,
+ *	unless you're ready to workaround
+ *   4) the swab routines below depend on the precise name and order
+ *	of the structure elements. Watch out any modification in ufs_fs.h!!!
+ *   5) putting byteswapping stuff in ufs_swab* seems cleaner to me.
+ *   6) These sources should work with both 2.0 and 2.1 kernels...
+ *
+ * 19971013 - Fare <rideaufr@issy.cnet.fr>
+ *   1) Ported to 2.1.57
+ *   2) instead of byteswapping, use [bl]e_to_cpu:
+ *     it might be that we run on a VAX!
+ *
+ * HOWTO continue adding swab support:
+ *	basically, anywhere metadata is bread() (i.e. mapped to block device),
+ *      data should either be SWAB()ed on the fly,
+ *      or copied to a buffer and globally bswap_ufs_*() there.
+ *
+ */
+
+#include <linux/fs.h>
+#include "ufs_swab.h"
+
+static __inline__ void n_be16_to_cpus(__u16*p,unsigned n) {
+#ifndef __BIG_ENDIAN
+        unsigned i;
+        for(i=0;i<n;i++) {
+		be16_to_cpus(&p[i]);
+        }
+#endif
+}
+static __inline__ void n_be32_to_cpus(__u32*p,unsigned n) {
+#ifndef __BIG_ENDIAN
+        unsigned i;
+        for(i=0;i<n;i++) {
+		be32_to_cpus(&p[i]);
+        }
+#endif
+}
+static __inline__ void n_le16_to_cpus(__u16*p,unsigned n) {
+#ifndef __LITTLE_ENDIAN
+        unsigned i;
+        for(i=0;i<n;i++) {
+		le16_to_cpus(&p[i]);
+        }
+#endif
+}
+static __inline__ void n_le32_to_cpus(__u32*p,unsigned n) {
+#ifndef __LITTLE_ENDIAN
+        unsigned i;
+        for(i=0;i<n;i++) {
+		le32_to_cpus(&p[i]);
+        }
+#endif
+}
+
+#define __length_before(p,member) \
+        ((unsigned)(((char*)&((p)->member))-(char*)(p)))
+#define __length_since(p,member) \
+        ((unsigned)(sizeof(*p)-__length_before(p,member)))
+#define __length_between(p,begin,after_end) \
+        ((unsigned)(__length_before(p,after_end)-__length_before(p,begin)))
+#define be32_to_cpus__between(s,begin,after_end) \
+        n_be32_to_cpus((__u32*)&((s).begin), \
+                __length_between(&s,begin,after_end)/4)
+#define le32_to_cpus__between(s,begin,after_end) \
+        n_le32_to_cpus((__u32*)&((s).begin), \
+                __length_between(&s,begin,after_end)/4)
+#define be32_to_cpus__since(s,begin) \
+	n_be32_to_cpus((__u32*)&((s).begin), \
+                __length_since(&s,begin)/4)
+#define le32_to_cpus__since(s,begin) \
+	n_le32_to_cpus((__u32*)&((s).begin), \
+                __length_since(&s,begin)/4)
+#define be16_to_cpus__between(s,begin,after_end) \
+	n_be16_to_cpus((__u16*)&((s).begin), \
+                __length_between(&s,begin,after_end)/2)
+#define le16_to_cpus__between(s,begin,after_end) \
+	n_le16_to_cpus((__u16*)&((s).begin), \
+                __length_between(&s,begin,after_end)/2)
+
+/*
+ * Here are the whole-structure swabping routines...
+ */
+
+extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb) {
+#ifndef __BIG_ENDIAN
+	be32_to_cpus__between(*usb,fs_link,fs_fmod);
+        /* XXX - I dunno what to do w/ fs_csp,
+         * but it is unused by the current code, so that's ok for now.
+         */
+	be32_to_cpus(&usb->fs_cpc);
+        be16_to_cpus__between(*usb,fs_opostbl,fs_sparecon);
+        be32_to_cpus__between(*usb,fs_sparecon,fs_qbmask);
+        be64_to_cpus(&usb->fs_qbmask);
+        be64_to_cpus(&usb->fs_qfmask);
+	be32_to_cpus__between(*usb,fs_postblformat,fs_magic);
+#endif
+}
+extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb) {
+#ifndef __LITTLE_ENDIAN
+	le32_to_cpus__between(*usb,fs_link,fs_fmod);
+        /* XXX - I dunno what to do w/ fs_csp,
+         * but it is unused by the current code, so that's ok for now.
+         */
+	le32_to_cpus(&usb->fs_cpc);
+        le16_to_cpus__between(*usb,fs_opostbl,fs_sparecon);
+        le32_to_cpus__between(*usb,fs_sparecon,fs_qbmask);
+        le64_to_cpus(&usb->fs_qbmask);
+        le64_to_cpus(&usb->fs_qfmask);
+	le32_to_cpus__between(*usb,fs_postblformat,fs_magic);
+#endif
+}
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_swab.h linux/fs/ufs/ufs_swab.h
--- v2.1.70/linux/fs/ufs/ufs_swab.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ufs/ufs_swab.h	Wed Dec  3 06:18:33 1997
@@ -0,0 +1,59 @@
+/*
+ *  linux/fs/ufs/ufs_swab.h
+ *
+ * Copyright (C) 1997 
+ * Francois-Rene Rideau <rideau@ens.fr>
+ *
+ */
+
+#ifndef _UFS_SWAB_H
+#define _UFS_SWAB_H
+
+
+/*
+ * Notes:
+ * (1) HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes
+ *    in case there are ufs implementations that have strange bytesexes,
+ *    you'll need to modify code here as well as in ufs_super.c and ufs_fs.h
+ *    to support them.
+ * (2) for a read/write ufs driver, we should distinguish
+ *    between byteswapping for read or write accesses!
+ */
+
+#include <linux/ufs_fs.h>
+#include <asm/byteorder.h>
+
+/*
+ * These are only valid inside ufs routines,
+ * after bytesex has been initialized to sb->u.ufs_sb.s_flags&UFS_BYTESEX
+ */
+#define SWAB16(x) ufs_swab16(bytesex,x)
+#define SWAB32(x) ufs_swab32(bytesex,x)
+#define SWAB64(x) ufs_swab64(bytesex,x)
+
+extern __inline__ __const__ __u16 ufs_swab16(__u32 bytesex, __u16 x) {
+	if (bytesex == UFS_LITTLE_ENDIAN) {
+		return le16_to_cpu(x);
+	} else {
+		return be16_to_cpu(x);
+	}
+}
+extern __inline__ __const__ __u32 ufs_swab32(__u32 bytesex, __u32 x) {
+	if (bytesex == UFS_LITTLE_ENDIAN) {
+		return le32_to_cpu(x);
+	} else {
+		return be32_to_cpu(x);
+	}
+}
+extern __inline__ __const__ __u64 ufs_swab64(__u32 bytesex, __u64 x) {
+	if (bytesex == UFS_LITTLE_ENDIAN) {
+		return le64_to_cpu(x);
+	} else {
+		return be64_to_cpu(x);
+	}
+}
+
+extern void ufs_superblock_le_to_cpus(struct ufs_superblock * usb);
+extern void ufs_superblock_be_to_cpus(struct ufs_superblock * usb);
+
+#endif /* _UFS_SWAB_H */
diff -u --recursive --new-file v2.1.70/linux/fs/ufs/ufs_symlink.c linux/fs/ufs/ufs_symlink.c
--- v2.1.70/linux/fs/ufs/ufs_symlink.c	Mon Jun 16 16:35:59 1997
+++ linux/fs/ufs/ufs_symlink.c	Wed Dec  3 06:18:33 1997
@@ -6,7 +6,7 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * $Id: ufs_symlink.c,v 1.9 1997/06/05 01:29:11 davem Exp $
+ * Ported to 2.1.62 by Francois-Rene Rideau <rideau@issy.cnet.fr> 19971109
  *
  */
 
@@ -16,6 +16,8 @@
 
 #include <asm/uaccess.h>
 
+extern int ufs_bmap (struct inode *, int);
+
 static int
 ufs_readlink(struct inode * inode, char * buffer, int buflen)
 {
@@ -30,6 +32,10 @@
 	               inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
 	}
 
+	if (!S_ISLNK(inode->i_mode)) {
+		return -EINVAL;
+	}
+   
 	if (buflen > inode->i_sb->s_blocksize - 1)
 		buflen = inode->i_sb->s_blocksize - 1;
 	if (inode->i_blocks) {
@@ -38,16 +44,16 @@
 	        if (inode->i_sb->u.ufs_sb.s_flags &(UFS_DEBUG|UFS_DEBUG_LINKS)) {
 	                printk("ufs_readlink: bmap got %lu for ino %lu\n",
 	                       block, inode->i_ino);
-		} 
+		}
 	        bh = bread(inode->i_dev, block, BLOCK_SIZE);
 		if (!bh) {
-			iput (inode);
 	                printk("ufs_readlink: can't read block 0 for ino %lu on dev %u/%u\n",
 	                       inode->i_ino, MAJOR(inode->i_dev),
 	                       MINOR(inode->i_dev));
 			return 0;
 		}
 		link = bh->b_data;
+		/* no need to bswap */
 	}
 	else {
 	        link = (char *)&(inode->u.ufs_i.i_data[0]);
@@ -57,18 +63,54 @@
 		i++;
 		put_user (c, buffer++);
 	}
-	iput (inode);
-	if (bh)
-		brelse (bh);
+	brelse (bh);
 	return i;
 }
 
+/*
+ * XXX - blatantly stolen from minix fs
+ */
+static struct dentry *
+ufs_follow_link(struct inode * inode, struct dentry * base)
+{
+	unsigned long int block;
+	struct buffer_head * bh = NULL;
+	char * link;
+
+	if (inode->i_sb->u.ufs_sb.s_flags & (UFS_DEBUG|UFS_DEBUG_LINKS)) {
+	        printk("ufs_follow_link: called on ino %lu dev %u/%u\n",
+	               inode->i_ino, MAJOR(inode->i_dev), MINOR(inode->i_dev));
+	}
+
+	if (inode->i_blocks) {
+	        /* read the link from disk */
+	        /* XXX - error checking */
+	        block = ufs_bmap(inode, 0);
+	        bh = bread(inode->i_dev, block, BLOCK_SIZE);
+	        if (bh == NULL) {
+	                printk("ufs_follow_link: can't read block 0 for ino %lu on dev %u/%u\n",
+	                       inode->i_ino, MAJOR(inode->i_dev),
+	                       MINOR(inode->i_dev));
+			dput(base);
+	                return ERR_PTR(-EIO);
+	        }
+	        link = bh->b_data;
+	} else {
+	        /* fast symlink */
+	        link = (char *)&(inode->u.ufs_i.i_data[0]);
+	}
+	base = lookup_dentry(link, base, 1);
+	brelse (bh);
+	return base;
+}
+
+
 static struct file_operations ufs_symlink_operations = {
 	NULL,			/* lseek */
 	NULL,			/* read */
 	NULL,			/* write */
 	NULL,			/* readdir */
-	NULL,			/* poll */
+	NULL,			/* select */
 	NULL,			/* ioctl */
 	NULL,			/* mmap */
 	NULL,			/* open */
@@ -90,12 +132,11 @@
 	NULL,			/* rmdir */
 	NULL,			/* mknod */
 	NULL,			/* rename */
-	ufs_readlink,		/* readlink */
+	&ufs_readlink,		/* readlink */
+	&ufs_follow_link,	/* follow_link */
 	NULL,			/* readpage */
 	NULL,			/* writepage */
 	NULL,			/* bmap */
 	NULL,			/* truncate */
 	NULL,			/* permission */
-	NULL,			/* smap */
 };
-
diff -u --recursive --new-file v2.1.70/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v2.1.70/linux/fs/vfat/namei.c	Sat Nov  1 11:04:27 1997
+++ linux/fs/vfat/namei.c	Wed Dec  3 04:24:29 1997
@@ -5,7 +5,7 @@
  *
  *  Windows95/Windows NT compatible extended MSDOS filesystem
  *    by Gordon Chaffee Copyright (C) 1995.  Send bug reports for the
- *    VFAT filesystem to <chaffee@plateau.cs.berkeley.edu>.  Specify
+ *    VFAT filesystem to <chaffee@cs.berkeley.edu>.  Specify
  *    what file operation caused you trouble and if you can duplicate
  *    the problem, send a script that demonstrates it.
  */
@@ -20,6 +20,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/malloc.h>
@@ -38,12 +39,6 @@
 # define CHECK_STACK check_stack(__FILE__, __LINE__)
 #endif
 
-/*
- * XXX: It would be better to use the tolower from linux/ctype.h,
- * but _ctype is needed and it is not exported.
- */
-#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
-
 struct vfat_find_info {
 	const char *name;
 	int len;
@@ -229,10 +224,15 @@
 
 /* MS-DOS "device special files" */
 
-static const char *reserved_names[] = {
-	"CON     ","PRN     ","NUL     ","AUX     ",
-	"LPT1    ","LPT2    ","LPT3    ","LPT4    ",
-	"COM1    ","COM2    ","COM3    ","COM4    ",
+static const char *reserved3_names[] = {
+	"con     ", "prn     ", "nul     ", "aux     ", NULL
+};
+
+static const char *reserved4_names[] = {
+	"com1    ", "com2    ", "com3    ", "com4    ", "com5    ",
+	"com6    ", "com7    ", "com8    ", "com9    ",
+	"lpt1    ", "lpt2    ", "lpt3    ", "lpt4    ", "lpt5    ",
+	"lpt6    ", "lpt7    ", "lpt8    ", "lpt9    ",
 	NULL };
 
 
@@ -245,16 +245,32 @@
 		      int find_long,int new_filename,int is_dir,
 		      struct slot_info *sinfo_out);
 
-/* Checks the validity of an long MS-DOS filename */
+static int strnicmp(const char *s1, const char *s2, int len)
+{
+	int n = 0;
+	while (*s1 && *s2 && (tolower(*s1) == tolower(*s2))) {
+		s1++; s2++; n++;
+		if (n == len) return 0;
+	}
+	if (*s1 == 0 && *s2 == 0) return 0;
+	if (*s1 && *s2) {
+		if (*s1 > *s2) return 1;
+		return -1;
+	}
+	if (*s1) return 1;
+	return -1;
+}
+
+/* Checks the validity of a long MS-DOS filename */
 /* Returns negative number on error, 0 for a normal
  * return, and 1 for . or .. */
 
 static int vfat_valid_longname(const char *name, int len, int dot_dirs,
 			       int xlate)
 {
-	const char **reserved;
+	const char **reserved, *walk;
 	unsigned char c;
-	int i;
+	int i, baselen;
 
 	if (IS_FREE(name)) return -EINVAL;
 	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
@@ -271,9 +287,21 @@
 			return -EINVAL;
 		}
 	}
- 	if (len == 3 || len == 4) {
-		for (reserved = reserved_names; *reserved; reserved++)
-			if (!strncmp(name,*reserved,8)) return -EINVAL;
+ 	if (len < 3) return 0;
+
+	for (walk = name; *walk != 0 && *walk != '.'; walk++);
+	baselen = walk - name;
+
+	if (baselen == 3) {
+		for (reserved = reserved3_names; *reserved; reserved++) {
+			if (!strnicmp(name,*reserved,baselen))
+				return -EINVAL;
+		}
+	} else if (baselen == 4) {
+		for (reserved = reserved4_names; *reserved; reserved++) {
+			if (!strnicmp(name,*reserved,baselen))
+				return -EINVAL;
+		}
 	}
 	return 0;
 }
@@ -284,6 +312,7 @@
 	const char *walk, **reserved;
 	unsigned char c;
 	int space;
+	int baselen;
 
 	if (IS_FREE(name)) return -EINVAL;
 	if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
@@ -312,7 +341,9 @@
 		if (c != '.') return -EINVAL;
 	}
 	while (c != '.' && len--) c = *walk++;
+	baselen = walk - name;
 	if (c == '.') {
+		baselen--;
 		if (len >= 4) return -EINVAL;
 		while (len > 0 && walk-name < (MSDOS_NAME+1)) {
 			c = *walk++;
@@ -329,8 +360,13 @@
 		if (space) return -EINVAL;
 		if (len) return -EINVAL;
 	}
-	for (reserved = reserved_names; *reserved; reserved++)
-		if (!strncmp(name,*reserved,8)) return -EINVAL;
+	if (baselen == 3) {
+		for (reserved = reserved3_names; *reserved; reserved++)
+			if (!strnicmp(name,*reserved,baselen)) return -EINVAL;
+	} else if (baselen == 4) {
+		for (reserved = reserved4_names; *reserved; reserved++)
+			if (!strnicmp(name,*reserved,baselen)) return -EINVAL;
+	}
 
 	return 0;
 }
@@ -341,7 +377,7 @@
  */
 
 static int vfat_format_name(const char *name,int len,char *res,
-  int dot_dirs,int utf8)
+			    int dot_dirs,int utf8)
 {
 	char *walk;
 	const char **reserved;
@@ -396,8 +432,10 @@
 		if (len) return -EINVAL;
 	}
 	while (walk-res < MSDOS_NAME) *walk++ = ' ';
-	for (reserved = reserved_names; *reserved; reserved++)
-		if (!strncmp(res,*reserved,8)) return -EINVAL;
+	for (reserved = reserved3_names; *reserved; reserved++)
+		if (!strnicmp(res,*reserved,8)) return -EINVAL;
+	for (reserved = reserved4_names; *reserved; reserved++)
+		if (!strnicmp(res,*reserved,8)) return -EINVAL;
 
 	return 0;
 }
@@ -605,8 +643,8 @@
 					/* Directory slots of busy deleted files aren't available yet. */
 					done = !MSDOS_I(inode)->i_busy;
 					/* PRINTK(("inode %d still busy\n", ino)); */
+					iput(inode);
 				}
-				iput(inode);
 			}
 			if (done) {
 				row++;
@@ -995,7 +1033,6 @@
 int vfat_lookup(struct inode *dir,struct dentry *dentry)
 {
 	int res;
-	struct inode *next;
 	struct slot_info sinfo;
 	struct inode *result;
 	
@@ -1014,7 +1051,7 @@
 	if (!result->i_sb ||
 	    (result->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
 		/* crossed a mount point into a non-msdos fs */
-		d_add(dentry,result);
+		d_add(dentry,NULL);
 		return 0;
 	}
 	if (MSDOS_I(result)->i_busy) { /* mkdir in progress */
@@ -1023,18 +1060,7 @@
 		return 0;
 	}
 	PRINTK (("vfat_lookup 6\n"));
-	while (MSDOS_I(result)->i_old) {
-		next = MSDOS_I(result)->i_old;
-		iput(result);
-		if (!(result = iget(next->i_sb,next->i_ino))) {
-			fat_fs_panic(dir->i_sb,"vfat_lookup: Can't happen");
-			iput(dir);
-			return -ENOENT;
-		}
-	}
-	PRINTK (("vfat_lookup 7\n"));
 	d_add(dentry,result);
-	PRINTK (("vfat_lookup 8\n"));	
 	return 0;
 }
 
@@ -1091,10 +1117,12 @@
 	result=NULL;
 	fat_lock_creation();
 	res = vfat_create_entry(dir,&dentry->d_name,0,&result);
-	if (res < 0) PRINTK(("vfat_create: unable to get new entry\n"));
-
 	fat_unlock_creation();
-	d_instantiate(dentry,result);
+	if (res < 0) {
+		PRINTK(("vfat_create: unable to get new entry\n"));
+	} else {
+		d_instantiate(dentry,result);
+	}
 	return res;
 }
 
@@ -1123,9 +1151,10 @@
 	de->adate = de->cdate = de->date;
 	de->size = 0;
 	fat_mark_buffer_dirty(sb, bh, 1);
-	if ((dot = iget(dir->i_sb,ino)) != NULL)
-		vfat_read_inode(dot);
-	if (!dot) return -EIO;
+	dot = iget(dir->i_sb,ino);
+	if (!dot)
+		return -EIO;
+	vfat_read_inode(dot);
 	dot->i_mtime = dot->i_atime = CURRENT_TIME;
 	mark_inode_dirty(dot);
 	if (isdot) {
@@ -1192,8 +1221,15 @@
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 
-	if (dir->i_count > 1)
+	/*
+	 * Prune any child dentries, then verify that
+	 * the directory is empty and not in use.
+	 */
+	shrink_dcache_sb(sb); /* should be child prune */
+
+	if (dir->i_count > 1) {
 		return -EBUSY;
+	}
 	if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 		pos = 0;
 		bh = NULL;
@@ -1288,7 +1324,6 @@
 	for (i = sinfo->long_slots; i > 0; --i) {
 		res = fat_get_entry(dir, &offset, bh, &de);
 		if (res < 0) {
-			printk("vfat_remove_entry: problem 1\n");
 			continue;
 		}
 		de->name[0] = DELETED_FLAG;
@@ -1306,24 +1341,17 @@
 	struct buffer_head *bh;
 	struct slot_info sinfo;
 
-	bh = NULL;
-	res = -EPERM;
-	if (dentry->d_name.name[0] == '.' && 
-	    (dentry->d_name.len == 1 || (dentry->d_name.len == 2 && 
-					 dentry->d_name.name[1] == '.')))
-		goto rmdir_done;
 	res = vfat_find(dir,&dentry->d_name,1,0,0,&sinfo);
 
 	if (res >= 0 && sinfo.total_slots > 0) {
+		bh = NULL;
 		res = vfat_remove_entry(dir,&sinfo,&bh,dentry,1,0);
 		if (res > 0) {
 			res = 0;
 		}
 		dir->i_version = ++event;
+		if (bh) fat_brelse(sb, bh);
 	}
-
-rmdir_done:
-	fat_brelse(sb, bh);
 	return res;
 }
 
@@ -1331,9 +1359,10 @@
 int vfat_rmdir(struct inode *dir,struct dentry* dentry)
 {
 	int res;
-
 	res = vfat_rmdirx(dir, dentry);
-	d_delete(dentry);
+	if (res >= 0) {
+		d_delete(dentry);
+	}
 	return res;
 }
 
@@ -1357,7 +1386,7 @@
 		}
 	}
 
-	fat_brelse(sb, bh);
+	if (bh) fat_brelse(sb, bh);
 	return res;
 }
 
@@ -1394,7 +1423,9 @@
 	int res;
 
 	res = vfat_unlinkx (dir,dentry,1);
-	d_delete(dentry);
+	if (res >= 0) {
+		d_delete(dentry);
+	}
 	return res;
 }
 
@@ -1633,22 +1664,3 @@
 }
 
 #endif /* ifdef MODULE */
-
-
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff -u --recursive --new-file v2.1.70/linux/include/asm-alpha/byteorder.h linux/include/asm-alpha/byteorder.h
--- v2.1.70/linux/include/asm-alpha/byteorder.h	Mon Jun 16 16:35:59 1997
+++ linux/include/asm-alpha/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -1,36 +1,13 @@
 #ifndef _ALPHA_BYTEORDER_H
 #define _ALPHA_BYTEORDER_H
 
-#undef ntohl
-#undef ntohs
-#undef htonl
-#undef htons
-
-#ifndef __LITTLE_ENDIAN
-#define __LITTLE_ENDIAN 1234
-#endif
-
-#ifndef __LITTLE_ENDIAN_BITFIELD
-#define __LITTLE_ENDIAN_BITFIELD
-#endif
-
-extern unsigned int		ntohl(unsigned int);
-extern unsigned short int	ntohs(unsigned short int);
-extern unsigned int		htonl(unsigned int);
-extern unsigned short int	htons(unsigned short int);
-
-extern unsigned int		__ntohl(unsigned int);
-extern unsigned short int	__ntohs(unsigned short int);
+#include <asm/types.h>
 
 #ifdef __GNUC__
 
-extern unsigned int		__constant_ntohl(unsigned int);
-extern unsigned short int	__constant_ntohs(unsigned short int);
-
-extern __inline__ unsigned int
-__ntohl(unsigned int x)
+static __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
-	unsigned int t1, t2, t3;
+	__u64 t1, t2, t3;
 
 	/* Break the final or's out of the block so that gcc can
 	   schedule them at will.  Further, use add not or so that
@@ -51,17 +28,10 @@
 	return t3 + t2 + t1;
 }
 
-#define __constant_ntohl(x) \
-   ((unsigned int)((((x) & 0x000000ff) << 24) | \
-		   (((x) & 0x0000ff00) <<  8) | \
-		   (((x) & 0x00ff0000) >>  8) | \
-		   (((x) & 0xff000000) >> 24)))
-
-extern __inline__ unsigned short int
-__ntohs(unsigned short int x)
+static __inline__ __const__ __u16 ___arch__swab16(__u16 x)
 {
-	unsigned short int t1, t2;
-	
+	__u64 t1, t2;
+
 	__asm__(
 	"insbl	%2,1,%1		# %1 = bb00\n\t"
 	"extbl	%2,1,%0		# %0 = 00aa"
@@ -70,99 +40,13 @@
 	return t1 | t2;
 }
 
-#define __constant_ntohs(x) \
-((unsigned short int)((((x) & 0x00ff) << 8) | \
-		      (((x) & 0xff00) >> 8)))
-
-#define __htonl(x) __ntohl(x)
-#define __htons(x) __ntohs(x)
-#define __constant_htonl(x) __constant_ntohl(x)
-#define __constant_htons(x) __constant_ntohs(x)
-
-#ifdef  __OPTIMIZE__
-#  define ntohl(x) \
-(__builtin_constant_p((long)(x)) ? \
- __constant_ntohl((x)) : \
- __ntohl((x)))
-#  define ntohs(x) \
-(__builtin_constant_p((short)(x)) ? \
- __constant_ntohs((x)) : \
- __ntohs((x)))
-#  define htonl(x) \
-(__builtin_constant_p((long)(x)) ? \
- __constant_htonl((x)) : \
- __htonl((x)))
-#  define htons(x) \
-(__builtin_constant_p((short)(x)) ? \
- __constant_htons((x)) : \
- __htons((x)))
-#endif /* __OPTIMIZE__ */
+#define __arch__swab32(x) ___arch__swab32(x)
+#define __arch__swab16(x) ___arch__swab16(x)
 
 #endif /* __GNUC__ */
 
-#ifdef __KERNEL__
-
-/*
- * In-kernel byte order macros to handle stuff like
- * byte-order-dependent filesystems etc.
- */
-#define cpu_to_le32(x) (x)
-#define cpu_to_le16(x) (x)
-
-#define cpu_to_be32(x) htonl((x))
-#define cpu_to_be16(x) htons((x))
-
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return cpu_to_le16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return cpu_to_le32(*addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return cpu_to_be16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
-{
-	return cpu_to_be32(*addr);
-}
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-#define cpu_to_le16s(x) do { } while (0)
-#define cpu_to_le32s(x) do { } while (0)
-
-extern __inline__ void cpu_to_be16s(__u16 *addr)
-{
-	*addr = cpu_to_be16(*addr);
-}
-
-extern __inline__ void cpu_to_be32s(__u32 *addr)
-{
-	*addr = cpu_to_be32(*addr);
-}
-
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)  cpu_to_le16(x)
-#define le32_to_cpu(x)  cpu_to_le32(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
+#define __BYTEORDER_HAS_U64__
 
-#endif /* __KERNEL__ */
+#include <linux/byteorder_little_endian.h>
 
 #endif /* _ALPHA_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-alpha/ioctls.h linux/include/asm-alpha/ioctls.h
--- v2.1.70/linux/include/asm-alpha/ioctls.h	Thu Jun 26 12:33:39 1997
+++ linux/include/asm-alpha/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -85,6 +85,7 @@
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
 #define TIOCSBRK	0x5427  /* BSD compatibility */
 #define TIOCCBRK	0x5428  /* BSD compatibility */
+#define TIOCGSID	0x5429  /* Return the session ID of FD */
 
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
diff -u --recursive --new-file v2.1.70/linux/include/asm-alpha/siginfo.h linux/include/asm-alpha/siginfo.h
--- v2.1.70/linux/include/asm-alpha/siginfo.h	Mon Dec  1 12:04:14 1997
+++ linux/include/asm-alpha/siginfo.h	Tue Dec  2 16:26:07 1997
@@ -120,7 +120,7 @@
  * SIGSEGV si_codes
  */
 #define SEGV_MAPERR	1	/* address not mapped to object */
-#define SRGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
diff -u --recursive --new-file v2.1.70/linux/include/asm-i386/byteorder.h linux/include/asm-i386/byteorder.h
--- v2.1.70/linux/include/asm-i386/byteorder.h	Mon Jul  7 16:02:01 1997
+++ linux/include/asm-i386/byteorder.h	Thu Dec  4 13:35:39 1997
@@ -1,36 +1,16 @@
 #ifndef _I386_BYTEORDER_H
 #define _I386_BYTEORDER_H
 
-#undef ntohl
-#undef ntohs
-#undef htonl
-#undef htons
+#include <asm/types.h>
 
-#ifndef __LITTLE_ENDIAN
-#define __LITTLE_ENDIAN 1234
-#endif
-
-#ifndef __LITTLE_ENDIAN_BITFIELD
-#define __LITTLE_ENDIAN_BITFIELD
-#endif
+#ifdef __GNUC__
 
 /* For avoiding bswap on i386 */
 #ifdef __KERNEL__
 #include <linux/config.h>
 #endif
 
-extern unsigned long int	ntohl(unsigned long int);
-extern unsigned short int	ntohs(unsigned short int);
-extern unsigned long int	htonl(unsigned long int);
-extern unsigned short int	htons(unsigned short int);
-
-extern __inline__ unsigned long int	__ntohl(unsigned long int);
-extern __inline__ unsigned short int	__ntohs(unsigned short int);
-extern __inline__ unsigned long int	__constant_ntohl(unsigned long int);
-extern __inline__ unsigned short int	__constant_ntohs(unsigned short int);
-
-extern __inline__ unsigned long int
-__ntohl(unsigned long int x)
+static __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
 #if defined(__KERNEL__) && !defined(CONFIG_M386)
 	__asm__("bswap %0" : "=r" (x) : "0" (x));
@@ -40,115 +20,28 @@
 		"xchgb %b0,%h0"		/* swap higher bytes	*/
 		:"=q" (x)
 		: "0" (x));
-#endif	
-	return x;
-}
-
-#define __constant_ntohl(x) \
-	((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
-			     (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
-			     (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
-			     (((unsigned long int)(x) & 0xff000000U) >> 24)))
-
-extern __inline__ unsigned short int
-__ntohs(unsigned short int x)
-{
-	__asm__("xchgb %b0,%h0"		/* swap bytes		*/
-		: "=q" (x)
-		:  "0" (x));
-	return x;
-}
-
-#define __constant_ntohs(x) \
-	((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
-			      (((unsigned short int)(x) & 0xff00) >> 8))) \
-
-#define __htonl(x) __ntohl(x)
-#define __htons(x) __ntohs(x)
-#define __constant_htonl(x) __constant_ntohl(x)
-#define __constant_htons(x) __constant_ntohs(x)
-
-#ifdef  __OPTIMIZE__
-#  define ntohl(x) \
-(__builtin_constant_p((long)(x)) ? \
- __constant_ntohl((x)) : \
- __ntohl((x)))
-#  define ntohs(x) \
-(__builtin_constant_p((short)(x)) ? \
- __constant_ntohs((x)) : \
- __ntohs((x)))
-#  define htonl(x) \
-(__builtin_constant_p((long)(x)) ? \
- __constant_htonl((x)) : \
- __htonl((x)))
-#  define htons(x) \
-(__builtin_constant_p((short)(x)) ? \
- __constant_htons((x)) : \
- __htons((x)))
 #endif
-
-#ifdef __KERNEL__
-/*
- * In-kernel byte order macros to handle stuff like
- * byte-order-dependent filesystems etc.
- */
-#define cpu_to_le32(x) (x)
-#define cpu_to_le16(x) (x)
-
-#define cpu_to_be32(x) htonl((x))
-#define cpu_to_be16(x) htons((x))
-
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return cpu_to_le16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return cpu_to_le32(*addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return cpu_to_be16(*addr);
+	return x;
 }
 
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
+static __inline__ __const__ __u16 ___arch__swab16(__u16 x)
 {
-	return cpu_to_be32(*addr);
+	__asm__("xchgb %b0,%h0"		/* swap bytes		*/ \
+		: "=q" (x) \
+		:  "0" (x)); \
+		return x;
 }
 
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-#define cpu_to_le16s(x) do { } while (0)
-#define cpu_to_le32s(x) do { } while (0)
+#define __arch__swab32(x) ___arch__swab32(x)
+#define __arch__swab16(x) ___arch__swab16(x)
 
-extern __inline__ void cpu_to_be16s(__u16 *addr)
-{
-	*addr = cpu_to_be16(*addr);
-}
-
-extern __inline__ void cpu_to_be32s(__u32 *addr)
-{
-	*addr = cpu_to_be32(*addr);
-}
+#if !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+#  define __BYTEORDER_HAS_U64__
+#  define __SWAB_64_THRU_32__
+#endif
 
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)  cpu_to_le16(x)
-#define le32_to_cpu(x)  cpu_to_le32(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
+#endif /* __GNUC__ */
 
-#endif /* __KERNEL__ */
+#include <linux/byteorder_little_endian.h>
 
-#endif
+#endif /* _I386_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-i386/ioctls.h linux/include/asm-i386/ioctls.h
--- v2.1.70/linux/include/asm-i386/ioctls.h	Thu Jun 26 12:33:39 1997
+++ linux/include/asm-i386/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -46,6 +46,7 @@
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
 #define TIOCSBRK	0x5427  /* BSD compatibility */
 #define TIOCCBRK	0x5428  /* BSD compatibility */
+#define TIOCGSID	0x5429  /* Return the session ID of FD */
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
diff -u --recursive --new-file v2.1.70/linux/include/asm-i386/siginfo.h linux/include/asm-i386/siginfo.h
--- v2.1.70/linux/include/asm-i386/siginfo.h	Mon Dec  1 12:04:14 1997
+++ linux/include/asm-i386/siginfo.h	Tue Dec  2 16:26:00 1997
@@ -120,7 +120,7 @@
  * SIGSEGV si_codes
  */
 #define SEGV_MAPERR	1	/* address not mapped to object */
-#define SRGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
diff -u --recursive --new-file v2.1.70/linux/include/asm-m68k/bitops.h linux/include/asm-m68k/bitops.h
--- v2.1.70/linux/include/asm-m68k/bitops.h	Sat May 24 09:10:24 1997
+++ linux/include/asm-m68k/bitops.h	Wed Dec  3 06:18:17 1997
@@ -257,19 +257,7 @@
 	return (p - addr) * 32 + res;
 }
 
-/* Byte swapping. */
 
-extern __inline__ unsigned short
-swab16 (unsigned short val)
-{
-	return (val << 8) | (val >> 8);
-}
-
-extern __inline__ unsigned int
-swab32 (unsigned int val)
-{
-	__asm__ ("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
-	return val;
-}
+/* Byte swapping (swab16 and swab32) is now in asm/byteorder.h . */
 
 #endif /* _M68K_BITOPS_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-m68k/byteorder.h linux/include/asm-m68k/byteorder.h
--- v2.1.70/linux/include/asm-m68k/byteorder.h	Mon Jun 16 16:36:00 1997
+++ linux/include/asm-m68k/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -1,137 +1,24 @@
 #ifndef _M68K_BYTEORDER_H
 #define _M68K_BYTEORDER_H
 
-#ifndef __BIG_ENDIAN
-#define __BIG_ENDIAN 4321
-#endif
-
-#ifndef __BIG_ENDIAN_BITFIELD
-#define __BIG_ENDIAN_BITFIELD
-#endif
-
-#ifdef __KERNEL__
 #include <asm/types.h>
 
-/*
- * In-kernel byte order macros to handle stuff like
- * byte-order-dependent filesystems etc.
- */
+#ifdef __GNUC__
 
-extern __inline__ __u16 __swab16 (__u16 val)
+static __inline__ __const__ __u32 ___arch__swab32(__u32 val)
 {
-	return (val << 8) | (val >> 8);
-}
-
-extern __inline__ __u32 __constant_swab32 (__u32 val)
-{
-	return (val << 24) | ((val << 8) & 0xff0000) |
-	       ((val >> 8) & 0xff00) | (val >> 24);
-}
-
-extern __inline__ __u32 __swab32 (__u32 val)
-{
-	__asm__ ("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
+	__asm__("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val));
 	return val;
 }
-
-/* Convert from CPU byte order, to specified byte order. */
-#define cpu_to_le16(__val) __swab16(__val)
-#define cpu_to_le32(__val) \
-(__builtin_constant_p(__val) ? __constant_swab32(__val) : __swab32(__val))
-#define cpu_to_be16(x)  (x)
-#define cpu_to_be32(x)  (x)
-
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return cpu_to_le16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return cpu_to_le32(*addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return cpu_to_be16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
-{
-	return cpu_to_be32(*addr);
-}
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-extern __inline__ void cpu_to_le16s(__u16 *addr)
-{
-	*addr = cpu_to_le16(*addr);
-}
-
-extern __inline__ void cpu_to_le32s(__u32 *addr)
-{
-	*addr = cpu_to_le32(*addr);
-}
-
-#define cpu_to_be16s(x) do { } while (0)
-#define cpu_to_be32s(x) do { } while (0)
-
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)  cpu_to_le16(x)
-#define le32_to_cpu(x)  cpu_to_le32(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
+#define __arch__swab32(x) ___arch__swab32(x)
 
 #endif
 
-#undef ntohl
-#undef ntohs
-#undef htonl
-#undef htons
-
-extern unsigned long int	ntohl(unsigned long int);
-extern unsigned short int	ntohs(unsigned short int);
-extern unsigned long int	htonl(unsigned long int);
-extern unsigned short int	htons(unsigned short int);
-
-extern __inline__ unsigned long int	__ntohl(unsigned long int);
-extern __inline__ unsigned short int	__ntohs(unsigned short int);
-
-extern __inline__ unsigned long int
-__ntohl(unsigned long int x)
-{
-	return x;
-}
-
-extern __inline__ unsigned short int
-__ntohs(unsigned short int x)
-{
-	return x;
-}
-
-#define __htonl(x) __ntohl(x)
-#define __htons(x) __ntohs(x)
-
-#define __constant_htonl(x) (x)
-#define __constant_htons(x) (x)
-#define __constant_ntohl(x) (x)
-#define __constant_ntohs(x) (x)
-
-#ifdef __OPTIMIZE__
-#define ntohl(x) __ntohl(x)
-#define ntohs(x) __ntohs(x)
-#define htonl(x) __htonl(x)
-#define htons(x) __htons(x)
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+#  define __BYTEORDER_HAS_U64__
+#  define __SWAB_64_THRU_32__
 #endif
 
-#endif
+#include <linux/byteorder_big_endian.h>
+
+#endif /* _M68K_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-m68k/ioctls.h linux/include/asm-m68k/ioctls.h
--- v2.1.70/linux/include/asm-m68k/ioctls.h	Thu Jun 26 12:33:39 1997
+++ linux/include/asm-m68k/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -46,6 +46,7 @@
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
 #define TIOCSBRK	0x5427  /* BSD compatibility */
 #define TIOCCBRK	0x5428  /* BSD compatibility */
+#define TIOCGSID	0x5429  /* Return the session ID of FD */
 
 #define FIONCLEX	0x5450  /* these numbers need to be adjusted. */
 #define FIOCLEX		0x5451
diff -u --recursive --new-file v2.1.70/linux/include/asm-mips/byteorder.h linux/include/asm-mips/byteorder.h
--- v2.1.70/linux/include/asm-mips/byteorder.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-mips/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -1,175 +1,14 @@
-/*
- * Functions depending of the byteorder.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1995, 1996, 1997 by Ralf Baechle
- *
- * $Id: byteorder.h,v 1.6 1997/07/20 15:15:01 ralf Exp $
- */
-#ifndef __ASM_MIPS_BYTEORDER_H
-#define __ASM_MIPS_BYTEORDER_H
+#ifndef _MIPS_BYTEORDER_H
+#define _MIPS_BYTEORDER_H
 
-#define __swap32(x) \
-	((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
-			     (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
-			     (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
-			     (((unsigned long int)(x) & 0xff000000U) >> 24)))
-#define __swap16(x) \
-	((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
-			      (((unsigned short int)(x) & 0xff00) >> 8)))
+#include <asm/types.h>
 
 #if defined (__MIPSEB__)
-
-#ifndef __BIG_ENDIAN
-#define __BIG_ENDIAN 4321
-#endif
-
-#ifndef __BIG_ENDIAN_BITFIELD
-#define __BIG_ENDIAN_BITFIELD
-#endif
-
-#define __constant_ntohl(x) (x)
-#define __constant_ntohs(x) (x)
-#define __constant_htonl(x) (x)
-#define __constant_htons(x) (x)
-
-#elif defined (__MIPSEL__)
-
-#ifndef __LITTLE_ENDIAN
-#define __LITTLE_ENDIAN 1234
-#endif
-
-#ifndef __LITTLE_ENDIAN_BITFIELD
-#define __LITTLE_ENDIAN_BITFIELD
-#endif
-
-#define __constant_ntohl(x) __swap32(x)
-#define __constant_ntohs(x) __swap16(x)
-#define __constant_htonl(x) __swap32(x)
-#define __constant_htons(x) __swap16(x)
-
-#endif /* defined(__MIPSEL_) */
-
-#ifdef __KERNEL__
-
-#if defined (__MIPSEB__)
-
-/*
- * In-kernel byte order macros to handle stuff like
- * byte-order-dependent filesystems etc.
- */
-#define cpu_to_le32(x) __swap32((x))
-#define le32_to_cpu(x) __swap32((x))
-#define cpu_to_le16(x) __swap16((x))
-#define le16_to_cpu(x) __swap16((x))
-
-#define cpu_to_be32(x) (x)
-#define be32_to_cpu(x) (x)
-#define cpu_to_be16(x) (x)
-#define be16_to_cpu(x) (x)
-
+#  include <linux/byteorder_big_endian.h>
 #elif defined (__MIPSEL__)
-
-/*
- * In-kernel byte order macros to handle stuff like
- * byte-order-dependent filesystems etc.
- */
-#define cpu_to_le32(x) (x)
-#define le32_to_cpu(x) (x)
-#define cpu_to_le16(x) (x)
-#define le16_to_cpu(x) (x)
-
-#define cpu_to_be32(x) __swap32((x))
-#define be32_to_cpu(x) __swap32((x))
-#define cpu_to_be16(x) __swap16((x))
-#define be16_to_cpu(x) __swap16((x))
-
+#  include <linux/byteorder_little_endian.h>
 #else
-#error "MIPS but neither __MIPSEL__ nor __MIPSEB__?"
+#  error What's that? MIPS, but neither MIPSEB, nor MIPSEL???
 #endif
 
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return cpu_to_le16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return cpu_to_le32(*addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return cpu_to_be16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
-{
-	return cpu_to_be32(*addr);
-}
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-extern __inline__ void cpu_to_le16s(__u16 *addr)
-{
-	*addr = cpu_to_le16(*addr);
-}
-
-extern __inline__ void cpu_to_le32s(__u32 *addr)
-{
-	*addr = cpu_to_le32(*addr);
-}
-
-extern __inline__ void cpu_to_be16s(__u16 *addr)
-{
-	*addr = cpu_to_be16(*addr);
-}
-
-extern __inline__ void cpu_to_be32s(__u32 *addr)
-{
-	*addr = cpu_to_be32(*addr);
-}
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
-
-#endif /* __KERNEL__ */
-
-extern unsigned long int ntohl(unsigned long int __x);
-extern unsigned short int ntohs(unsigned short int __x);
-extern unsigned short int htons(unsigned short int __x);
-extern unsigned long int htonl(unsigned long int __x);
-
-
-extern __inline__ unsigned long int ntohl(unsigned long int __x)
-{
-	return __constant_ntohl(__x);
-}
-
-extern __inline__ unsigned short int ntohs(unsigned short int __x)
-{
-	return __constant_ntohs(__x);
-}
-
-extern __inline__ unsigned long int htonl(unsigned long int __x)
-{
-	return __constant_htonl(__x);
-}
-
-extern __inline__ unsigned short int htons(unsigned short int __x)
-{
-	return __constant_htons(__x);
-}
-
-#endif /* __ASM_MIPS_BYTEORDER_H */
+#endif /* _MIPS_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-mips/ioctls.h linux/include/asm-mips/ioctls.h
--- v2.1.70/linux/include/asm-mips/ioctls.h	Mon Aug  4 16:25:39 1997
+++ linux/include/asm-mips/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -98,6 +98,7 @@
 #define TIOCTTYGSTRUCT	0x5487  /* For debugging only */
 #define TIOCSBRK	0x5427  /* BSD compatibility */
 #define TIOCCBRK	0x5428  /* BSD compatibility */
+#define TIOCGSID	0x5429  /* Return the session ID of FD */
 
 #define TIOCSERCONFIG	0x5488
 #define TIOCSERGWILD	0x5489
diff -u --recursive --new-file v2.1.70/linux/include/asm-ppc/byteorder.h linux/include/asm-ppc/byteorder.h
--- v2.1.70/linux/include/asm-ppc/byteorder.h	Mon Aug 18 18:19:46 1997
+++ linux/include/asm-ppc/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -3,31 +3,8 @@
 
 #include <asm/types.h>
 
-#ifndef __BIG_ENDIAN
-#define __BIG_ENDIAN	4321
-#endif
-
-#ifndef __BIG_ENDIAN_BITFIELD
-#define __BIG_ENDIAN_BITFIELD
-#endif
-
-#define ntohl(x) ((unsigned long)(x))
-#define ntohs(x) ((unsigned short)(x))
-#define htonl(x) ((unsigned long)(x))
-#define htons(x) ((unsigned short)(x))
-
-#define __htonl(x) ntohl(x)
-#define __htons(x) ntohs(x)
-
-#define __constant_ntohs(x) ntohs(x)
-#define __constant_ntohl(x) ntohl(x)
-#define __constant_htonl(x) ntohl(x)
-#define __constant_htons(x) ntohs(x)
-
-#ifdef __KERNEL__
-/*
- * 16 and 32 bit little-endian loads and stores.
- */
+#ifdef __GNUC__
+
 extern inline unsigned ld_le16(volatile unsigned short *addr)
 {
 	unsigned val;
@@ -55,16 +32,10 @@
 }
 
 #if 0
-extern __inline__ __u16 cpu_to_le16(__u16 value)
-{
-	return ld_le16(&value);
-}
-extern __inline__ __u32 cpu_to_le32(__u32 value)
-{
-	return ld_le32(&value);
-}
+#  define __arch_swab16(x) ld_le16(&x)
+#  define __arch_swab32(x) ld_le32(&x)
 #else
-extern __inline__ __u16 cpu_to_le16(__u16 value)
+static __inline__ __const__ __u16 ___arch__swab16(__u16 value)
 {
 	__u16 result;
 
@@ -73,7 +44,8 @@
 	    : "r" (value), "0" (value >> 8));
 	return result;
 }
-extern __inline__ __u32 cpu_to_le32(__u32 value)
+
+static __inline__ __const__ __u32 ___arch__swab32(__u32 value)
 {
 	__u32 result;
 
@@ -84,68 +56,20 @@
 	    : "r" (value), "0" (value >> 24));
 	return result;
 }
+#define __arch__swab32(x) ___arch__swab32(x)
+#define __arch__swab16(x) ___arch__swab16(x)
 #endif /* 0 */
 
-#define cpu_to_be16(x)  (x)
-#define cpu_to_be32(x)  (x)
-
 /* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return ld_le16(addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return ld_le32(addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return *addr;
-}
-
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
-{
-	return *addr;
-}
+#define __arch_swab16p(addr) ld_le16(addr)
+#define __arch_swab32p(addr) ld_le32(addr)
 
 /* The same, but do the conversion in situ, ie. put the value back to addr. */
-extern __inline__ void cpu_to_le16s(__u16 *addr)
-{
-	st_le16(addr,*addr);
-}
-
-extern __inline__ void cpu_to_le32s(__u32 *addr)
-{
-	st_le32(addr,*addr);
-}
-
-#define cpu_to_be16s(x) do { } while (0)
-#define cpu_to_be32s(x) do { } while (0)
-
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)  cpu_to_le16(x)
-#define le32_to_cpu(x)  cpu_to_le32(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
-
-
-#endif /* __KERNEL__ */
-#endif /* !(_PPC_BYTEORDER_H) */
-
-
-
+#define __arch_swab16s(addr) st_le16(addr,*addr)
+#define __arch_swab32s(addr) st_le32(addr,*addr)
 
+#endif /* __GNUC__ */
 
+#include <linux/byteorder_big_endian.h>
 
+#endif /* _PPC_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-ppc/ioctls.h linux/include/asm-ppc/ioctls.h
--- v2.1.70/linux/include/asm-ppc/ioctls.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-ppc/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -85,6 +85,7 @@
 #define TIOCTTYGSTRUCT	0x5426  /* For debugging only */
 #define TIOCSBRK	0x5427  /* BSD compatibility */
 #define TIOCCBRK	0x5428  /* BSD compatibility */
+#define TIOCGSID	0x5429  /* Return the session ID of FD */
 
 #define TIOCSERCONFIG	0x5453
 #define TIOCSERGWILD	0x5454
diff -u --recursive --new-file v2.1.70/linux/include/asm-sparc/byteorder.h linux/include/asm-sparc/byteorder.h
--- v2.1.70/linux/include/asm-sparc/byteorder.h	Mon Jun 16 16:36:00 1997
+++ linux/include/asm-sparc/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -1,93 +1,13 @@
-/* $Id: byteorder.h,v 1.14 1997/05/28 11:35:38 jj Exp $ */
 #ifndef _SPARC_BYTEORDER_H
 #define _SPARC_BYTEORDER_H
 
-#define ntohl(x) ((unsigned long int)(x))
-#define ntohs(x) ((unsigned short int)(x))
-#define htonl(x) ((unsigned long int)(x))
-#define htons(x) ((unsigned short int)(x))
+#include <asm/types.h>
 
-/* Some programs depend upon these being around. */
-#define __constant_ntohl(x) ((unsigned long int)(x))
-#define __constant_ntohs(x) ((unsigned short int)(x))
-#define __constant_htonl(x) ((unsigned long int)(x))
-#define __constant_htons(x) ((unsigned short int)(x))
-
-#ifndef __BIG_ENDIAN
-#define __BIG_ENDIAN 4321
-#endif
-
-#ifndef __BIG_ENDIAN_BITFIELD
-#define __BIG_ENDIAN_BITFIELD
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__) || defined(__KERNEL__)
+#  define __BYTEORDER_HAS_U64__
+#  define __SWAB_64_THRU_32__
 #endif
 
-#ifdef __KERNEL__
-
-/* Convert from CPU byte order, to specified byte order. */
-extern __inline__ __u16 cpu_to_le16(__u16 value)
-{
-	return (value >> 8) | (value << 8);
-}
-
-extern __inline__ __u32 cpu_to_le32(__u32 value)
-{
-	return((value>>24) | ((value>>8)&0xff00) |
-	       ((value<<8)&0xff0000) | (value<<24));
-}
-#define cpu_to_be16(x)  (x)
-#define cpu_to_be32(x)  (x)
-
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
-{
-	return cpu_to_le16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
-{
-	return cpu_to_le32(*addr);
-}
-
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
-{
-	return cpu_to_be16(*addr);
-}
-
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
-{
-	return cpu_to_be32(*addr);
-}
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-extern __inline__ void cpu_to_le16s(__u16 *addr)
-{
-	*addr = cpu_to_le16(*addr);
-}
-
-extern __inline__ void cpu_to_le32s(__u32 *addr)
-{
-	*addr = cpu_to_le32(*addr);
-}
-
-#define cpu_to_be16s(x) do { } while (0)
-#define cpu_to_be32s(x) do { } while (0)
-
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)  cpu_to_le16(x)
-#define le32_to_cpu(x)  cpu_to_le32(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-
-#define le16_to_cpup(x) cpu_to_le16p(x)
-#define le32_to_cpup(x) cpu_to_le32p(x)
-#define be16_to_cpup(x) cpu_to_be16p(x)
-#define be32_to_cpup(x) cpu_to_be32p(x)
-
-#define le16_to_cpus(x) cpu_to_le16s(x)
-#define le32_to_cpus(x) cpu_to_le32s(x)
-#define be16_to_cpus(x) cpu_to_be16s(x)
-#define be32_to_cpus(x) cpu_to_be32s(x)
-
-#endif /* __KERNEL__ */
+#include <linux/byteorder_big_endian.h>
 
-#endif /* !(_SPARC_BYTEORDER_H) */
+#endif /* _SPARC_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-sparc/ioctls.h linux/include/asm-sparc/ioctls.h
--- v2.1.70/linux/include/asm-sparc/ioctls.h	Mon Jul  7 08:18:55 1997
+++ linux/include/asm-sparc/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -74,6 +74,7 @@
 #define TIOCSPGRP	_IOW('t', 130, int)
 #define TIOCGPGRP	_IOR('t', 131, int)
 #define TIOCSCTTY	_IO('t', 132)
+#define TIOCGSID	_IOR('t', 133, int)
 
 /* Little f */
 #define FIOCLEX		_IO('f', 1)
diff -u --recursive --new-file v2.1.70/linux/include/asm-sparc64/byteorder.h linux/include/asm-sparc64/byteorder.h
--- v2.1.70/linux/include/asm-sparc64/byteorder.h	Thu Jun 26 12:33:40 1997
+++ linux/include/asm-sparc64/byteorder.h	Wed Dec  3 06:18:17 1997
@@ -1,129 +1,46 @@
-/* $Id: byteorder.h,v 1.6 1997/06/14 17:35:07 davem Exp $ */
 #ifndef _SPARC64_BYTEORDER_H
 #define _SPARC64_BYTEORDER_H
 
+#include <asm/types.h>
 #include <asm/asi.h>
 
-#define ntohl(x) ((unsigned long int)(x))
-#define ntohs(x) ((unsigned short int)(x))
-#define htonl(x) ((unsigned long int)(x))
-#define htons(x) ((unsigned short int)(x))
-
-/* Some programs depend upon these being around. */
-#define __constant_ntohl(x) ((unsigned long int)(x))
-#define __constant_ntohs(x) ((unsigned short int)(x))
-#define __constant_htonl(x) ((unsigned long int)(x))
-#define __constant_htons(x) ((unsigned short int)(x))
-
-#ifndef __BIG_ENDIAN
-#define __BIG_ENDIAN 4321
-#endif
-
-#ifndef __BIG_ENDIAN_BITFIELD
-#define __BIG_ENDIAN_BITFIELD
-#endif
+#ifdef __GNUC__
 
-#ifdef __KERNEL__
-
-/* Convert from CPU byte order, to specified byte order. */
-extern __inline__ __u16 cpu_to_le16(__u16 value)
-{
-	return (value >> 8) | (value << 8);
-}
-
-extern __inline__ __u32 cpu_to_le32(__u32 value)
-{
-	return((value>>24) | ((value>>8)&0xff00) |
-	       ((value<<8)&0xff0000) | (value<<24));
-}
-
-extern __inline__ __u64 cpu_to_le64(__u64 value)
-{
-        return (((value>>56) & 0x00000000000000ffUL) |
-                ((value>>40) & 0x000000000000ff00UL) |
-                ((value>>24) & 0x0000000000ff0000UL) |
-                ((value>>8)  & 0x00000000ff000000UL) |
-                ((value<<8)  & 0x000000ff00000000UL) |
-                ((value<<24) & 0x0000ff0000000000UL) |
-                ((value<<40) & 0x00ff000000000000UL) |
-                ((value<<56) & 0xff00000000000000UL));
-}
-#define cpu_to_be16(x)  (x)
-#define cpu_to_be32(x)  (x)
-#define cpu_to_be64(x)	(x)
-
-/* The same, but returns converted value from the location pointer by addr. */
-extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
+static __inline__ __u16 ___arch__swab16p(__u16 *addr)
 {
 	__u16 ret;
+
 	__asm__ __volatile__ ("lduha [%1] %2, %0"
 			      : "=r" (ret)
 			      : "r" (addr), "i" (ASI_PL));
 	return ret;
 }
 
-extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
+static __inline__ __u32 ___arch__swab32p(__u32 *addr)
 {
 	__u32 ret;
+
 	__asm__ __volatile__ ("lduwa [%1] %2, %0"
 			      : "=r" (ret)
 			      : "r" (addr), "i" (ASI_PL));
 	return ret;
 }
 
-extern __inline__ __u64 cpu_to_le64p(__u64 *addr)
-{
+static __inline__ __u64 ___arch__swab64p(__u64 *addr) {
 	__u64 ret;
+
 	__asm__ __volatile__ ("ldxa [%1] %2, %0"
 			      : "=r" (ret)
 			      : "r" (addr), "i" (ASI_PL));
 	return ret;
 }
-extern __inline__ __u16 cpu_to_be16p(__u16 *addr) { return *addr; }
-extern __inline__ __u32 cpu_to_be32p(__u32 *addr) { return *addr; }
-extern __inline__ __u64 cpu_to_be64p(__u64 *addr) { return *addr; }
-
-/* The same, but do the conversion in situ, ie. put the value back to addr. */
-extern __inline__ void cpu_to_le16s(__u16 *addr)
-{
-	*addr = cpu_to_le16p(addr);
-}
 
-extern __inline__ void cpu_to_le32s(__u32 *addr)
-{
-	*addr = cpu_to_le32p(addr);
-}
+#define __arch__swab16p(x) ___arch__swab16p(x)
+#define __arch__swab32p(x) ___arch__swab32p(x)
+#define __arch__swab64p(x) ___arch__swab64p(x)
 
-extern __inline__ void cpu_to_le64s(__u64 *addr)
-{
-	*addr = cpu_to_le64p(addr);
-}
-#define cpu_to_be16s(x) do { } while (0)
-#define cpu_to_be32s(x) do { } while (0)
-#define cpu_to_be64s(x) do { } while (0)
-
-/* Convert from specified byte order, to CPU byte order. */
-#define le16_to_cpu(x)	cpu_to_le16(x)
-#define le32_to_cpu(x)	cpu_to_le32(x)
-#define le64_to_cpu(x)	cpu_to_le64(x)
-#define be16_to_cpu(x)  cpu_to_be16(x)
-#define be32_to_cpu(x)  cpu_to_be32(x)
-#define be64_to_cpu(x)	cpu_to_be64(x)
-
-#define le16_to_cpup(x)	cpu_to_le16p(x)
-#define le32_to_cpup(x)	cpu_to_le32p(x)
-#define le64_to_cpup(x)	cpu_to_le64p(x)
-#define be16_to_cpup(x)	cpu_to_be16p(x)
-#define be32_to_cpup(x)	cpu_to_be32p(x)
-#define be64_to_cpup(x)	cpu_to_be64p(x)
-
-#define le16_to_cpus(x)	cpu_to_le16s(x)
-#define le32_to_cpus(x)	cpu_to_le32s(x)
-#define le64_to_cpus(x)	cpu_to_le64s(x)
-#define be16_to_cpus(x)	cpu_to_be16s(x)
-#define be32_to_cpus(x)	cpu_to_be32s(x)
-#define be64_to_cpus(x)	cpu_to_be64s(x)
+#define __BYTEORDER_HAS_U64__
 
-#endif /* __KERNEL__ */
+#include <linux/byteorder_big_endian.h>
 
-#endif /* !(_SPARC64_BYTEORDER_H) */
+#endif /* _SPARC64_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/asm-sparc64/ioctls.h linux/include/asm-sparc64/ioctls.h
--- v2.1.70/linux/include/asm-sparc64/ioctls.h	Mon Aug 18 18:19:47 1997
+++ linux/include/asm-sparc64/ioctls.h	Thu Dec  4 13:09:01 1997
@@ -75,6 +75,7 @@
 #define TIOCSPGRP	_IOW('t', 130, int)
 #define TIOCGPGRP	_IOR('t', 131, int)
 #define TIOCSCTTY	_IO('t', 132)
+#define TIOCGSID	_IOR('t', 133, int)
 
 /* Little f */
 #define FIOCLEX		_IO('f', 1)
diff -u --recursive --new-file v2.1.70/linux/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- v2.1.70/linux/include/linux/affs_fs.h	Mon Jun 16 16:36:00 1997
+++ linux/include/linux/affs_fs.h	Tue Dec  2 22:25:07 1997
@@ -27,23 +27,26 @@
   u32 ds_Tick;
 };
 
-
 /* --- Prototypes -----------------------------------------------------------------------------	*/
 
 /* amigaffs.c */
 
 extern int	affs_get_key_entry(int bsize, void *data, int entry_pos);
-extern int	affs_get_file_name(int bsize, void *fh_data, char **name);
+extern int	affs_get_file_name(int bsize, void *fh_data, unsigned char **name);
 extern u32	affs_checksum_block(int bsize, void *data, s32 *ptype, s32 *stype);
 extern void	affs_fix_checksum(int bsize, void *data, int cspos);
 extern void	secs_to_datestamp(time_t secs, struct DateStamp *ds);
 extern int	prot_to_mode(unsigned int prot);
 extern u32	mode_to_prot(int mode);
-extern int	affs_fix_hash_pred(struct inode *startino, int startoffset,
-		     	      s32 key, s32 newkey);
-extern int	affs_fix_link_pred(struct inode *startino, s32 key, s32 newkey);
+extern int	affs_insert_hash(unsigned long dir_ino, struct buffer_head *header,
+			struct inode *inode);
+extern int	affs_remove_hash(struct buffer_head *bh, struct inode *inode);
+extern int	affs_remove_link(struct buffer_head *bh, struct inode *inode);
+extern int	affs_remove_header(struct buffer_head *bh, struct inode *inode);
 extern void	affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
 extern void	affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
+extern int	affs_check_name(const unsigned char *name, int len);
+extern int	affs_copy_name(unsigned char *bstr, const unsigned char *name);
 
 /* bitmap. c */
 
@@ -56,39 +59,44 @@
 
 /* namei.c */
 
-extern int	affs_hash_name(const char *name, int len, int intl, int hashsize);
-extern int	affs_lookup(struct inode *dir,const char *name, int len,
-		            struct inode **result);
-extern int	affs_unlink(struct inode *dir, const char *name, int len);
-extern int	affs_create(struct inode *dir, const char *name, int len, int mode,
-		            struct inode **result);
-extern int	affs_mkdir(struct inode *dir, const char *name, int len, int mode);
-extern int	affs_rmdir(struct inode *dir, const char *name, int len);
+extern int	affs_hash_name(const unsigned char *name, int len, int intl, int hashsize);
+extern int	affs_lookup(struct inode *dir, struct dentry *dentry);
+extern int	affs_unlink(struct inode *dir, struct dentry *dentry);
+extern int	affs_create(struct inode *dir, struct dentry *dentry, int mode);
+extern int	affs_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+extern int	affs_rmdir(struct inode *dir, struct dentry *dentry);
 extern int	affs_link(struct inode *oldinode, struct inode *dir,
-		          const char *name, int len);
-extern int	affs_symlink(struct inode *dir, const char *name, int len,
-		             const char *symname);
-extern int	affs_rename(struct inode *old_dir, const char *old_name, int old_len,
-			    struct inode *new_dir, const char *new_name, int new_len);
+			  struct dentry *dentry);
+extern int	affs_symlink(struct inode *dir, struct dentry *dentry,
+			     const char *symname);
+extern int	affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+			    struct inode *new_dir, struct dentry *new_dentry);
 
 /* inode.c */
 
-extern struct buffer_head *affs_bread(kdev_t dev, int block, int size);
-extern void		   affs_brelse(struct buffer_head *buf);
-extern unsigned long	   affs_parent_ino(struct inode *dir);
-extern struct inode	  *affs_new_inode(const struct inode *dir);
-extern int		   affs_add_entry(struct inode *dir, struct inode *link,
-					  struct inode *inode, const char *name,
-					  int len, s32 type);
+extern struct buffer_head	*affs_bread(kdev_t dev, int block, int size);
+extern void			 affs_brelse(struct buffer_head *buf);
+extern unsigned long		 affs_parent_ino(struct inode *dir);
+extern struct inode		*affs_new_inode(const struct inode *dir);
+extern int			 affs_notify_change(struct inode *inode, struct iattr *attr);
+extern int			 affs_add_entry(struct inode *dir, struct inode *link,
+					  struct inode *inode, struct dentry *dentry, s32 type);
+extern void			 affs_put_inode(struct inode *inode);
+extern void			 affs_delete_inode(struct inode *inode);
+extern void			 affs_read_inode(struct inode *inode);
+extern void			 affs_write_inode(struct inode *inode);
+
+/* super.c */
+
+extern int			 affs_fs(void);
 
 /* file.c */
 
-extern void		   affs_truncate(struct inode *);
-extern void		   affs_truncate_ofs(struct inode *);
+extern void   affs_truncate(struct inode *);
 
 /* dir.c */
 
-extern void		   affs_dir_truncate(struct inode *);
+extern void   affs_dir_truncate(struct inode *);
 
 /* jump tables */
 
@@ -99,5 +107,4 @@
 extern struct inode_operations	 affs_chrdev_inode_operations;
 extern struct inode_operations	 affs_blkdev_inode_operations;
 
-extern int init_affs_fs(void);
 #endif
diff -u --recursive --new-file v2.1.70/linux/include/linux/affs_fs_sb.h linux/include/linux/affs_fs_sb.h
--- v2.1.70/linux/include/linux/affs_fs_sb.h	Tue May 13 22:41:18 1997
+++ linux/include/linux/affs_fs_sb.h	Tue Dec  2 22:25:07 1997
@@ -68,5 +68,6 @@
 #define SF_OFS		0x0200		/* Old filesystem */
 #define SF_PREFIX	0x0400		/* Buffer for prefix is allocated */
 #define SF_VERBOSE	0x0800		/* Talk about fs when mounting */
+#define SF_READONLY	0x1000		/* Don't allow to remount rw */
 
 #endif
diff -u --recursive --new-file v2.1.70/linux/include/linux/amigaffs.h linux/include/linux/amigaffs.h
--- v2.1.70/linux/include/linux/amigaffs.h	Tue May 13 22:41:18 1997
+++ linux/include/linux/amigaffs.h	Tue Dec  2 22:25:07 1997
@@ -4,10 +4,18 @@
 #include <asm/byteorder.h>
 #include <linux/types.h>
 
+/* AmigaOS allows file names with up to 30 characters length.
+ * Names longer than that will be silently truncated. If you
+ * want to disallow this, comment out the following #define.
+ * Creating filesystem objects with longer names will then
+ * result in an error (ENAMETOOLONG).
+ */
+/*#define AFFS_NO_TRUNCATE */
+
 /* Ugly macros make the code more pretty. */
 
 #define GET_END_PTR(st,p,sz)		 ((st *)((char *)(p)+((sz)-sizeof(st))))
-#define AFFS_GET_HASHENTRY(data,hashkey) htonl(((struct dir_front *)data)->hashtable[hashkey])
+#define AFFS_GET_HASHENTRY(data,hashkey) be32_to_cpu(((struct dir_front *)data)->hashtable[hashkey])
 #define AFFS_BLOCK(data,ino,blk)	 ((struct file_front *)data)->blocks[AFFS_I2HSIZE(ino)-1-(blk)]
 
 #define FILE_END(p,i)	GET_END_PTR(struct file_end,p,AFFS_I2BSIZE(i))
@@ -16,6 +24,7 @@
 #define LINK_END(p,i)	GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
 #define ROOT_END_S(p,s)	GET_END_PTR(struct root_end,p,(s)->s_blocksize)
 #define DATA_FRONT(bh)	((struct data_front *)(bh)->b_data)
+#define DIR_FRONT(bh)	((struct dir_front *)(bh)->b_data)
 
 /* Only for easier debugging if need be */
 #define affs_bread	bread
@@ -206,14 +215,14 @@
 
 #define AFFS_UMAYWRITE(prot)	(((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
 #define AFFS_UMAYREAD(prot)	((prot) & FIBF_READ)
-#define AFFS_UMAYEXECUTE(prot)	(((prot) & (FIBF_SCRIPT|FIBF_READ)) == (FIBF_SCRIPT|FIBF_READ))
+#define AFFS_UMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 #define AFFS_GMAYWRITE(prot)	(((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
 							(FIBF_GRP_WRITE|FIBF_GRP_DELETE))
 #define AFFS_GMAYREAD(prot)	((prot) & FIBF_GRP_READ)
-#define AFFS_GMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_GRP_READ))==(FIBF_SCRIPT|FIBF_GRP_READ))
+#define AFFS_GMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 #define AFFS_OMAYWRITE(prot)	(((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
 							(FIBF_OTR_WRITE|FIBF_OTR_DELETE))
 #define AFFS_OMAYREAD(prot)	((prot) & FIBF_OTR_READ)
-#define AFFS_OMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_OTR_READ))==(FIBF_SCRIPT|FIBF_OTR_READ))
+#define AFFS_OMAYEXECUTE(prot)	((prot) & FIBF_EXECUTE)
 
 #endif
diff -u --recursive --new-file v2.1.70/linux/include/linux/byteorder_big_endian.h linux/include/linux/byteorder_big_endian.h
--- v2.1.70/linux/include/linux/byteorder_big_endian.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/byteorder_big_endian.h	Wed Dec  3 06:18:17 1997
@@ -0,0 +1,54 @@
+#ifndef _LINUX_BYTEORDER_BIG_ENDIAN_H
+#define _LINUX_BYTEORDER_BIG_ENDIAN_H
+
+#ifndef __BIG_ENDIAN
+#define __BIG_ENDIAN 4321
+#endif
+#ifndef __BIG_ENDIAN_BITFIELD
+#define __BIG_ENDIAN_BITFIELD
+#endif
+
+#define __constant_htonl(x) ((__u32)(x))
+#define __constant_ntohl(x) ((__u32)(x))
+#define __constant_htons(x) ((__u16)(x))
+#define __constant_ntohs(x) ((__u16)(x))
+#define __cpu_to_le64(x) __swab64((x))
+#define __le64_to_cpu(x) __swab64((x))
+#define __cpu_to_le32(x) __swab32((x))
+#define __le32_to_cpu(x) __swab32((x))
+#define __cpu_to_le16(x) __swab16((x))
+#define __le16_to_cpu(x) __swab16((x))
+#define __cpu_to_be64(x) ((__u64)(x))
+#define __be64_to_cpu(x) ((__u64)(x))
+#define __cpu_to_be32(x) ((__u32)(x))
+#define __be32_to_cpu(x) ((__u32)(x))
+#define __cpu_to_be16(x) ((__u16)(x))
+#define __be16_to_cpu(x) ((__u16)(x))
+#define __cpu_to_le64p(x) __swab64p((x))
+#define __le64_to_cpup(x) __swab64p((x))
+#define __cpu_to_le32p(x) __swab32p((x))
+#define __le32_to_cpup(x) __swab32p((x))
+#define __cpu_to_le16p(x) __swab16p((x))
+#define __le16_to_cpup(x) __swab16p((x))
+#define __cpu_to_be64p(x) (*(__u64*)(x))
+#define __be64_to_cpup(x) (*(__u64*)(x))
+#define __cpu_to_be32p(x) (*(__u32*)(x))
+#define __be32_to_cpup(x) (*(__u32*)(x))
+#define __cpu_to_be16p(x) (*(__u16*)(x))
+#define __be16_to_cpup(x) (*(__u16*)(x))
+#define __cpu_to_le64s(x) __swab64s((x))
+#define __le64_to_cpus(x) __swab64s((x))
+#define __cpu_to_le32s(x) __swab32s((x))
+#define __le32_to_cpus(x) __swab32s((x))
+#define __cpu_to_le16s(x) __swab16s((x))
+#define __le16_to_cpus(x) __swab16s((x))
+#define __cpu_to_be64s(x) do {} while (0)
+#define __be64_to_cpus(x) do {} while (0)
+#define __cpu_to_be32s(x) do {} while (0)
+#define __be32_to_cpus(x) do {} while (0)
+#define __cpu_to_be16s(x) do {} while (0)
+#define __be16_to_cpus(x) do {} while (0)
+
+#include <linux/byteorder_generic.h>
+
+#endif /* _LINUX_BYTEORDER_BIG_ENDIAN_H */
diff -u --recursive --new-file v2.1.70/linux/include/linux/byteorder_generic.h linux/include/linux/byteorder_generic.h
--- v2.1.70/linux/include/linux/byteorder_generic.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/byteorder_generic.h	Wed Dec  3 17:13:31 1997
@@ -0,0 +1,298 @@
+#ifndef _LINUX_BYTEORDER_GENERIC_H
+#define _LINUX_BYTEORDER_GENERIC_H
+
+/*
+ * linux/byteorder_generic.h
+ * Generic Byteswap support
+ *
+ * Francois-Rene Rideau <rideau@ens.fr> 19970707
+ *    gathered all the good ideas from all asm-foo/byteorder.h into one file,
+ *    cleaned them up.
+ *    I hope it is compliant with non-GCC compilers.
+ *    I decided to put __BYTEORDER_HAS_U64__ in byteorder.h,
+ *    because I wasn't sure it would be ok to put it in types.h
+ *    Upgraded it to 2.1.43
+ * Francois-Rene Rideau <rideau@ens.fr> 19971012
+ *    Upgraded it to 2.1.57
+ *    to please Linus T., replaced huge #ifdef's between little/big endian
+ *    by nestedly #include'd files.
+ *
+ * TODO:
+ *   = Regular kernel maintainers could also replace all these manual
+ *    byteswap macros that remain, disseminated among drivers,
+ *    after some grep or the sources...
+ *   = Linus might want to rename all these macros and files to fit his taste,
+ *    to fit his personal naming scheme.
+ *   = it seems that many drivers would also appreciate
+ *    nybble swapping support...
+ *   = every architecture could add their byteswap macro in asm/byteorder.h
+ *    see how some architectures already do (i386, alpha, ppc, etc)
+ */
+
+/*
+ * This file is included by both <linux/byteorder_little_endian.h> and
+ * <linux/byteorder_big_endian.h>. People porting from machines with
+ * bizarre bytedisorder (like the VAX?) will have to write a different one.
+ * Actually, this file mostly does byteswapping, and could be named
+ * <byteswap.h> or <swab.h> rather than <linux/byteorder_generic.h>
+ *
+ */
+
+/*
+ * The following macros are to be defined by <asm/byteorder.h>:
+ *
+ * Conversion of long and short int between network and host format
+ *	ntohl(__u32 x)
+ *	ntohs(__u16 x)
+ *	htonl(__u32 x)
+ *	htons(__u16 x)
+ * It seems that some programs (which? where? or perhaps a standard? POSIX?)
+ * might like the above to be functions, not macros (why?).
+ * if that's true, then detect them, and take measures.
+ * Anyway, the measure is: define only ___ntohl as a macro instead,
+ * and in a separate file, have
+ * unsigned long inline ntohl(x){return ___ntohl(x);}
+ *
+ * The same for constant arguments
+ *	__constant_ntohl(__u32 x)
+ *	__constant_ntohs(__u16 x)
+ *	__constant_htonl(__u32 x)
+ *	__constant_htons(__u16 x)
+ *
+ * Conversion of XX-bit integers (16- 32- or 64-)
+ * between native cpu format and little/big endian format
+ * 64-bit stuff only defined for proper architectures
+ *	cpu_to_[bl]eXX(__uXX x)
+ *	[bl]eXX_to_cpu(__uXX x)
+ *
+ * The same, but takes a pointer to the value to convert
+ *	cpu_to_[bl]eXXp(__uXX x)
+ *	[bl]eXX_to_cpup(__uXX x)
+ *
+ * The same, but change in situ
+ *	cpu_to_[bl]eXXs(__uXX x)
+ *	[bl]eXX_to_cpus(__uXX x)
+ *
+ * Byteswapping, independently from cpu endianness
+ *	swabXX[ps]?(foo)
+ *
+ *
+ * See asm-foo/byteorder.h for examples of how to provide
+ * architecture-optimized versions
+ *
+ */
+
+#include <asm/types.h>
+
+/*
+ * Generic byte swapping routines. We fall back on
+ * these if we don't have any optimized code, and
+ * when we have constants that we want the compiler
+ * to byte swap for us..
+ */
+#define ___swab16(x) \
+	((__u16)( \
+		(((__u16)(x) & 0x00ffU) << 8) | \
+		(((__u16)(x) & 0xff00U) >> 8) ))
+#define ___swab32(x) \
+	((__u32)( \
+		(((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
+		(((__u32)(x) & (__u32)0x0000ff00UL) <<  8) | \
+		(((__u32)(x) & (__u32)0x00ff0000UL) >>  8) | \
+		(((__u32)(x) & (__u32)0xff000000UL) >> 24) ))
+#define ___swab64(x) \
+	((__u64)( \
+		(__u64)(((__u64)(x) & (__u64)0x00000000000000ffULL) << 56) | \
+		(__u64)(((__u64)(x) & (__u64)0x000000000000ff00ULL) << 40) | \
+		(__u64)(((__u64)(x) & (__u64)0x0000000000ff0000ULL) << 24) | \
+		(__u64)(((__u64)(x) & (__u64)0x00000000ff000000ULL) <<  8) | \
+	        (__u64)(((__u64)(x) & (__u64)0x000000ff00000000ULL) >>  8) | \
+		(__u64)(((__u64)(x) & (__u64)0x0000ff0000000000ULL) >> 24) | \
+		(__u64)(((__u64)(x) & (__u64)0x00ff000000000000ULL) >> 40) | \
+		(__u64)(((__u64)(x) & (__u64)0xff00000000000000ULL) >> 56) ))
+
+/*
+ * These do constant folding - this allows the
+ * compiler to do any constants at compile
+ * time.  Any architecture inline asm optimizations
+ * would be pessimizations.
+ */
+#define __swab16(x) \
+	(__builtin_constant_p((__u16)(x)) ? \
+	 ___swab16((x)) : __fswab16((x)))
+#define __swab32(x) \
+	(__builtin_constant_p((__u32)(x)) ? \
+	 ___swab32((x)) : __fswab32((x)))
+#define __swab64(x) \
+	(__builtin_constant_p((__u64)(x)) ? \
+	 ___swab64((x)) : __fswab64((x)))
+
+
+/*
+ * provide defaults when no architecture-specific optimization is detected
+ */
+#ifndef __arch__swab16
+#  define __arch__swab16(x) ___swab16(x)
+#endif
+#ifndef __arch__swab32
+#  define __arch__swab32(x) ___swab32(x)
+#endif
+#ifndef __arch__swab64
+#  define __arch__swab64(x) ___swab64(x)
+#endif
+
+#ifndef __arch__swab16p
+#  define __arch__swab16p(x) __swab16(*(x))
+#endif
+#ifndef __arch__swab32p
+#  define __arch__swab32p(x) __swab32(*(x))
+#endif
+#ifndef __arch__swab64p
+#  define __arch__swab64p(x) __swab64(*(x))
+#endif
+
+#ifndef __arch__swab16s
+#  define __arch__swab16s(x) *(x) = __swab16p((x))
+#endif
+#ifndef __arch__swab32s
+#  define __arch__swab32s(x) *(x) = __swab32p((x))
+#endif
+#ifndef __arch__swab64s
+#  define __arch__swab64s(x) *(x) = __swab64p((x))
+#endif
+
+
+extern __inline__ __const__ __u16 __fswab16(__u16 x)
+{
+	return __arch__swab16(x);
+}
+extern __inline__ __u16 __swab16p(__u16 *x)
+{
+	return __arch__swab16p(x);
+}
+extern __inline__ void __swab16s(__u16 *addr)
+{
+	__arch__swab16s(addr);
+}
+
+extern __inline__ __const__ __u32 __fswab32(__u32 x)
+{
+	return __arch__swab32(x);
+}
+extern __inline__ __u32 __swab32p(__u32 *x)
+{
+	return __arch__swab32p(x);
+}
+extern __inline__ void __swab32s(__u32 *addr)
+{
+	__arch__swab32s(addr);
+}
+
+#ifdef __BYTEORDER_HAS_U64__
+extern __inline__ __const__ __u64 __fswab64(__u64 x)
+{
+#  ifdef __SWAB_64_THRU_32__
+	__u32 h = x >> 32;
+        __u32 l = x & ((1ULL<<32)-1);
+        return (((__u64)__swab32(l)) << 32) | ((__u64)(__swab32(h)));
+#  else
+	return __arch__swab64(x);
+#  endif
+}
+extern __inline__ __u64 __swab64p(__u64 *x)
+{
+	return __arch__swab64p(x);
+}
+extern __inline__ void __swab64s(__u64 *addr)
+{
+	__arch__swab64s(addr);
+}
+#endif /* __BYTEORDER_HAS_U64__ */
+
+#if defined(__KERNEL__) || defined(__REQUIRE_CPU_TO_XX)
+#define swab16 __swab16
+#define swab32 __swab32
+#define swab64 __swab64
+#define swab16p __swab16p
+#define swab32p __swab32p
+#define swab64p __swab64p
+#define swab16s __swab16s
+#define swab32s __swab32s
+#define swab64s __swab64s
+#define cpu_to_le64 __cpu_to_le64
+#define le64_to_cpu __le64_to_cpu
+#define cpu_to_le32 __cpu_to_le32
+#define le32_to_cpu __le32_to_cpu
+#define cpu_to_le16 __cpu_to_le16
+#define le16_to_cpu __le16_to_cpu
+#define cpu_to_be64 __cpu_to_be64
+#define be64_to_cpu __be64_to_cpu
+#define cpu_to_be32 __cpu_to_be32
+#define be32_to_cpu __be32_to_cpu
+#define cpu_to_be16 __cpu_to_be16
+#define be16_to_cpu __be16_to_cpu
+#define cpu_to_le64p __cpu_to_le64p
+#define le64_to_cpup __le64_to_cpup
+#define cpu_to_le32p __cpu_to_le32p
+#define le32_to_cpup __le32_to_cpup
+#define cpu_to_le16p __cpu_to_le16p
+#define le16_to_cpup __le16_to_cpup
+#define cpu_to_be64p __cpu_to_be64p
+#define be64_to_cpup __be64_to_cpup
+#define cpu_to_be32p __cpu_to_be32p
+#define be32_to_cpup __be32_to_cpup
+#define cpu_to_be16p __cpu_to_be16p
+#define be16_to_cpup __be16_to_cpup
+#define cpu_to_le64s __cpu_to_le64s
+#define le64_to_cpus __le64_to_cpus
+#define cpu_to_le32s __cpu_to_le32s
+#define le32_to_cpus __le32_to_cpus
+#define cpu_to_le16s __cpu_to_le16s
+#define le16_to_cpus __le16_to_cpus
+#define cpu_to_be64s __cpu_to_be64s
+#define be64_to_cpus __be64_to_cpus
+#define cpu_to_be32s __cpu_to_be32s
+#define be32_to_cpus __be32_to_cpus
+#define cpu_to_be16s __cpu_to_be16s
+#define be16_to_cpus __be16_to_cpus
+#endif
+
+/*
+ * Handle ntohl and suches. These have various compatibility
+ * issues - like we want to give the prototype even though we
+ * also have a macro for them in case some strange program
+ * wants to take the address of the thing or something..
+ *
+ * Note that these traditionally return a "long", even though
+ * long is often 64-bit these days.. Thus the casts.
+ *
+ * They have to be macros in order to do the constant folding
+ * correctly - if the argument passed into a inline function
+ * it is no longer constant according to gcc..
+ */
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+/*
+ * Do the prototypes. Somebody might want to take the
+ * address or some such sick thing..
+ */
+extern unsigned long int	ntohl(unsigned long int);
+extern unsigned short int	ntohs(unsigned short int);
+extern unsigned long int	htonl(unsigned long int);
+extern unsigned short int	htons(unsigned short int);
+
+#define ___htonl(x) __cpu_to_be32(x)
+#define ___htons(x) __cpu_to_be16(x)
+#define ___ntohl(x) __be32_to_cpu(x)
+#define ___ntohs(x) __be16_to_cpu(x)
+
+#define htonl(x) ((unsigned long)___htonl(x))
+#define htons(x) ___htons(x)
+#define ntohl(x) ((unsigned long)___ntohl(x))
+#define ntohs(x) ___ntohs(x)
+
+#endif /* _LINUX_BYTEORDER_H */
diff -u --recursive --new-file v2.1.70/linux/include/linux/byteorder_little_endian.h linux/include/linux/byteorder_little_endian.h
--- v2.1.70/linux/include/linux/byteorder_little_endian.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/byteorder_little_endian.h	Wed Dec  3 17:14:28 1997
@@ -0,0 +1,54 @@
+#ifndef _LINUX_BYTEORDER_LITTLE_ENDIAN_H
+#define _LINUX_BYTEORDER_LITTLE_ENDIAN_H
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#define __LITTLE_ENDIAN_BITFIELD
+#endif
+
+#define __constant_htonl(x) ___swab32((x))
+#define __constant_ntohl(x) ___swab32((x))
+#define __constant_htons(x) ___swab16((x))
+#define __constant_ntohs(x) ___swab16((x))
+#define __cpu_to_le64(x) ((__u64)(x))
+#define __le64_to_cpu(x) ((__u64)(x))
+#define __cpu_to_le32(x) ((__u32)(x))
+#define __le32_to_cpu(x) ((__u32)(x))
+#define __cpu_to_le16(x) ((__u16)(x))
+#define __le16_to_cpu(x) ((__u16)(x))
+#define __cpu_to_be64(x) __swab64((x))
+#define __be64_to_cpu(x) __swab64((x))
+#define __cpu_to_be32(x) __swab32((x))
+#define __be32_to_cpu(x) __swab32((x))
+#define __cpu_to_be16(x) __swab16((x))
+#define __be16_to_cpu(x) __swab16((x))
+#define __cpu_to_le64p(x) (*(__u64*)(x))
+#define __le64_to_cpup(x) (*(__u64*)(x))
+#define __cpu_to_le32p(x) (*(__u32*)(x))
+#define __le32_to_cpup(x) (*(__u32*)(x))
+#define __cpu_to_le16p(x) (*(__u16*)(x))
+#define __le16_to_cpup(x) (*(__u16*)(x))
+#define __cpu_to_be64p(x) __swab64p((x))
+#define __be64_to_cpup(x) __swab64p((x))
+#define __cpu_to_be32p(x) __swab32p((x))
+#define __be32_to_cpup(x) __swab32p((x))
+#define __cpu_to_be16p(x) __swab16p((x))
+#define __be16_to_cpup(x) __swab16p((x))
+#define __cpu_to_le64s(x) do {} while (0)
+#define __le64_to_cpus(x) do {} while (0)
+#define __cpu_to_le32s(x) do {} while (0)
+#define __le32_to_cpus(x) do {} while (0)
+#define __cpu_to_le16s(x) do {} while (0)
+#define __le16_to_cpus(x) do {} while (0)
+#define __cpu_to_be64s(x) __swab64s((x))
+#define __be64_to_cpus(x) __swab64s((x))
+#define __cpu_to_be32s(x) __swab32s((x))
+#define __be32_to_cpus(x) __swab32s((x))
+#define __cpu_to_be16s(x) __swab16s((x))
+#define __be16_to_cpus(x) __swab16s((x))
+
+#include <linux/byteorder_generic.h>
+
+#endif /* _LINUX_BYTEORDER_LITTLE_ENDIAN_H */
diff -u --recursive --new-file v2.1.70/linux/include/linux/byteorder_vaxendian.h linux/include/linux/byteorder_vaxendian.h
--- v2.1.70/linux/include/linux/byteorder_vaxendian.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/byteorder_vaxendian.h	Wed Dec  3 06:18:17 1997
@@ -0,0 +1,94 @@
+#ifndef _LINUX_BYTEORDER_VAX_ENDIAN_H
+#define _LINUX_BYTEORDER_VAX_ENDIAN_H
+
+/*
+ * Could have been named NUXI-endian
+ * This file isn't operational.
+ * It's the beginning of what vaxlinux implementers will have to do.
+ * I just hope we won't have to write standardized cpu_to_ve32() and suches!
+ * little endian is 1234; bigendian is 4321; vaxendian is 3412
+ * But what does a __u64 look like: is it 34127856 or 78563412 ???
+ * I don't dare imagine!
+ */
+
+#ifndef __VAX_ENDIAN
+#define __VAX_ENDIAN 3412
+#endif
+#ifndef __VAX_ENDIAN_BITFIELD
+#define __VAX_ENDIAN_BITFIELD
+#endif
+
+#define ___swahw32(x) \
+	((__u32)( \
+		(((__u32)(x) & (__u32)0x0000ffffUL) << 16) | \
+		(((__u32)(x) & (__u32)0xffff0000UL) >> 16) ))
+#define ___swahb32(x) \
+	((__u32)( \
+		(((__u32)(x) & (__u32)0x00ff00ffUL) << 16) | \
+		(((__u32)(x) & (__u32)0xff00ff00UL) >> 16) ))
+
+
+#ifndef __arch__swahw32
+#  define __arch__swahw32(x) ___swahw32(x)
+#endif
+#ifndef __arch__swahb32
+#  define __arch__swahb32(x) ___swahb32(x)
+#endif
+#ifndef __arch__swahw32p
+#  define __arch__swahw32p(x) __swahw32(*(x))
+#endif
+#ifndef __arch__swahb32p
+#  define __arch__swahb32p(x) __swahb32(*(x))
+#endif
+#ifndef __arch__swahw32s
+#  define __arch__swahw32s(x) *(x) = __swahw32p((x))
+#endif
+#ifndef __arch__swahb32s
+#  define __arch__swahb32s(x) *(x) = __swahb32p((x))
+#endif
+
+
+#define __constant_htonl(x) ___swahb32((x))
+#define __constant_ntohl(x) ___swahb32((x))
+#define __constant_htons(x) ___swab16((x))
+#define __constant_ntohs(x) ___swab16((x))
+#define __cpu_to_le64(x) I DON'T KNOW
+#define __le64_to_cpu(x) I DON'T KNOW
+#define __cpu_to_le32(x) ___swahw32((x))
+#define __le32_to_cpu(x) ___swahw32((x))
+#define __cpu_to_le16(x) ((__u16)(x)
+#define __le16_to_cpu(x) ((__u16)(x)
+#define __cpu_to_be64(x) I DON'T KNOW
+#define __be64_to_cpu(x) I DON'T KNOW
+#define __cpu_to_be32(x) __swahb32((x))
+#define __be32_to_cpu(x) __swahb32((x))
+#define __cpu_to_be16(x) __swab16((x))
+#define __be16_to_cpu(x) __swab16((x))
+#define __cpu_to_le64p(x) I DON'T KNOW
+#define __le64_to_cpup(x) I DON'T KNOW
+#define __cpu_to_le32p(x) ___swahw32p((x))
+#define __le32_to_cpup(x) ___swahw32p((x))
+#define __cpu_to_le16p(x) (*(__u16*)(x))
+#define __le16_to_cpup(x) (*(__u16*)(x))
+#define __cpu_to_be64p(x) I DON'T KNOW
+#define __be64_to_cpup(x) I DON'T KNOW
+#define __cpu_to_be32p(x) __swahb32p((x))
+#define __be32_to_cpup(x) __swahb32p((x))
+#define __cpu_to_be16p(x) __swab16p((x))
+#define __be16_to_cpup(x) __swab16p((x))
+#define __cpu_to_le64s(x) I DON'T KNOW
+#define __le64_to_cpus(x) I DON'T KNOW
+#define __cpu_to_le32s(x) ___swahw32s((x))
+#define __le32_to_cpus(x) ___swahw32s((x))
+#define __cpu_to_le16s(x) do {} while (0)
+#define __le16_to_cpus(x) do {} while (0)
+#define __cpu_to_be64s(x) I DON'T KNOW
+#define __be64_to_cpus(x) I DON'T KNOW
+#define __cpu_to_be32s(x) __swahb32s((x))
+#define __be32_to_cpus(x) __swahb32s((x))
+#define __cpu_to_be16s(x) __swab16s((x))
+#define __be16_to_cpus(x) __swab16s((x))
+
+#include <linux/byteorder_generic.h>
+
+#endif /* _LINUX_BYTEORDER_VAX_ENDIAN_H */
diff -u --recursive --new-file v2.1.70/linux/include/linux/coda_psdev.h linux/include/linux/coda_psdev.h
--- v2.1.70/linux/include/linux/coda_psdev.h	Tue Dec  2 16:45:20 1997
+++ linux/include/linux/coda_psdev.h	Wed Dec  3 17:35:33 1997
@@ -4,8 +4,6 @@
 #define CODA_PSDEV_MAJOR 67
 #define MAX_CODADEVS  5	   /* how many do we allow */
 
-#include <linux/config.h>
-
 extern struct vcomm psdev_vcomm[];
 
 #define CODA_SUPER_MAGIC	0x73757245
diff -u --recursive --new-file v2.1.70/linux/include/linux/console.h linux/include/linux/console.h
--- v2.1.70/linux/include/linux/console.h	Tue May 13 22:41:18 1997
+++ linux/include/linux/console.h	Wed Dec  3 15:21:57 1997
@@ -62,36 +62,40 @@
 struct tty_struct;
 int tioclinux(struct tty_struct *tty, unsigned long arg);
 
-/* The interface for /dev/console(s) and printk output */
-
-struct console
+/*
+ *	Array of consoles built from command line options (console=)
+ */
+struct console_cmdline
 {
-	/*
-	 * This function should not return before the string is written.
-	 */
-	void (*write)(const char*, unsigned);
-
-        /* To unblank the console in case of panic */
-        void (*unblank)(void);
+	char	name[8];			/* Name of the driver	    */
+	int	index;				/* Minor dev. to use	    */
+	char	*options;			/* Options for the driver   */
+};
+#define MAX_CMDLINECONSOLES 8
+extern struct console_cmdline console_list[MAX_CMDLINECONSOLES];
 
-	/*
-         * Only the console that was registered last with wait_key !=
-	 * NULL will be used. This blocks until there is a character
-	 * to give back, it does not schedule.
-         */
-	void (*wait_key)(void);
+/*
+ *	The interface for a console, or any other device that
+ *	wants to capture console messages (printer driver?)
+ */
 
-	/*
-	 * Return the device to use when opening /dev/console. Only the
-	 * last registered console will do.
-	 */
-	int (*device)(void);
+#define CON_PRINTBUFFER	(1)
+#define CON_FIRST	(2)
+#define CON_ENABLED	(4)
 
-	/* 
-	 * For a linked list of consoles for multiple output. Any console
-         * not at the head of the list is used only for output.
-	 */
-	struct console *next;
+struct console
+{
+	char	name[8];
+	void	(*write)(struct console *, const char *, unsigned);
+	int	(*read)(struct console *, const char *, unsigned);
+	kdev_t	(*device)(struct console *);
+	int	(*wait_key)(struct console *);
+        void	(*unblank)(void);
+	void	(*setup)(struct console *, char *);
+	short	flags;
+	short	index;
+	int	cflag;
+	struct	 console *next;
 };
 
 extern void register_console(struct console *);
diff -u --recursive --new-file v2.1.70/linux/include/linux/netrom.h linux/include/linux/netrom.h
--- v2.1.70/linux/include/linux/netrom.h	Thu May 29 21:53:11 1997
+++ linux/include/linux/netrom.h	Thu Dec  4 13:12:39 1997
@@ -7,7 +7,6 @@
 #ifndef	NETROM_KERNEL_H
 #define	NETROM_KERNEL_H
 
-#define PF_NETROM	AF_NETROM
 #define NETROM_MTU	236
 
 #define NETROM_T1	1
diff -u --recursive --new-file v2.1.70/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.70/linux/include/linux/pci.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/pci.h	Thu Dec  4 12:58:01 1997
@@ -241,11 +241,13 @@
 #define PCI_DEVICE_ID_NCR_53C895	0x000c
 #define PCI_DEVICE_ID_NCR_53C885	0x000d
 #define PCI_DEVICE_ID_NCR_53C875	0x000f
+#define PCI_DEVICE_ID_NCR_53C876	0x008f
 
 #define PCI_VENDOR_ID_ATI		0x1002
 #define PCI_DEVICE_ID_ATI_68800		0x4158
 #define PCI_DEVICE_ID_ATI_215CT222	0x4354
 #define PCI_DEVICE_ID_ATI_210888CX	0x4358
+#define PCI_DEVICE_ID_ATI_215GB		0x4742
 #define PCI_DEVICE_ID_ATI_215GP		0x4750
 #define PCI_DEVICE_ID_ATI_215GT		0x4754
 #define PCI_DEVICE_ID_ATI_215GTB	0x4755
@@ -257,6 +259,8 @@
 #define PCI_DEVICE_ID_VLSI_82C593	0x0006
 #define PCI_DEVICE_ID_VLSI_82C594	0x0007
 #define PCI_DEVICE_ID_VLSI_82C597	0x0009
+#define PCI_DEVICE_ID_VLSI_82C541	0x000c
+#define PCI_DEVICE_ID_VLSI_82C543	0x000d
 #define PCI_DEVICE_ID_VLSI_VAS96011	0x0702
 
 #define PCI_VENDOR_ID_ADL		0x1005
@@ -307,6 +311,7 @@
 
 #define PCI_VENDOR_ID_IBM		0x1014
 #define PCI_DEVICE_ID_IBM_FIRE_CORAL	0x000a
+#define PCI_DEVICE_ID_IBM_TR		0x0018
 #define PCI_DEVICE_ID_IBM_82G2675	0x001d
 #define PCI_DEVICE_ID_IBM_82351		0x0022
 
@@ -321,6 +326,7 @@
 #define PCI_DEVICE_ID_TRIDENT_9420	0x9420
 #define PCI_DEVICE_ID_TRIDENT_9440	0x9440
 #define PCI_DEVICE_ID_TRIDENT_9660	0x9660
+#define PCI_DEVICE_ID_TRIDENT_9750	0x9750
 
 #define PCI_VENDOR_ID_AI		0x1025
 #define PCI_DEVICE_ID_AI_M1435		0x1435
@@ -342,6 +348,7 @@
 #define PCI_DEVICE_ID_MIRO_36050	0x5601
 
 #define PCI_VENDOR_ID_NEC		0x1033
+#define PCI_DEVICE_ID_NEC_PCX2		0x0046
 
 #define PCI_VENDOR_ID_FD		0x1036
 #define PCI_DEVICE_ID_FD_36C70		0x0000
@@ -358,12 +365,16 @@
 #define PCI_DEVICE_ID_SI_5511		0x5511
 #define PCI_DEVICE_ID_SI_5513		0x5513
 #define PCI_DEVICE_ID_SI_5571		0x5571
+#define PCI_DEVICE_ID_SI_5597		0x5597
 #define PCI_DEVICE_ID_SI_7001		0x7001
 
 #define PCI_VENDOR_ID_HP		0x103c
 #define PCI_DEVICE_ID_HP_J2585A		0x1030
 #define PCI_DEVICE_ID_HP_J2585B		0x1031
 
+#define PCI_VENDOR_ID_HOLTEK		0x9412
+#define PCI_DEVICE_ID_HOLTEK_6565	0x6565
+
 #define PCI_VENDOR_ID_PCTECH		0x1042
 #define PCI_DEVICE_ID_PCTECH_RZ1000	0x1000
 #define PCI_DEVICE_ID_PCTECH_RZ1001	0x1001
@@ -376,6 +387,9 @@
 #define PCI_DEVICE_ID_OPTI_82C557	0xc557
 #define PCI_DEVICE_ID_OPTI_82C558	0xc558
 #define PCI_DEVICE_ID_OPTI_82C621	0xc621
+#define PCI_DEVICE_ID_OPTI_82C700	0xc700
+#define PCI_DEVICE_ID_OPTI_82C701	0xc701
+#define PCI_DEVICE_ID_OPTI_82C814	0xc814
 #define PCI_DEVICE_ID_OPTI_82C822	0xc822
 
 #define PCI_VENDOR_ID_SGS		0x104a
@@ -388,6 +402,8 @@
 #define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT     0x8130
 
 #define PCI_VENDOR_ID_TI		0x104c
+#define PCI_DEVICE_ID_TI_TVP4010	0x3d04
+#define PCI_DEVICE_ID_TI_TVP4020	0x3d07
 #define PCI_DEVICE_ID_TI_PCI1130	0xac12
 #define PCI_DEVICE_ID_TI_PCI1131	0xac15
 
@@ -518,10 +534,14 @@
 
 #define PCI_VENDOR_ID_MITSUBISHI	0x10ba
 
+#define PCI_VENDOR_ID_SURECOM		0x10bd
+#define PCI_DEVICE_ID_SURECOM_NE34	0x0e34
+
 #define PCI_VENDOR_ID_NEOMAGIC          0x10c8
 #define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2070 0x0001
 #define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128V 0x0002
 #define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_128ZV 0x0003
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2160 0x0004
 
 #define PCI_VENDOR_ID_ASP		0x10cd
 #define PCI_DEVICE_ID_ASP_ABP940	0x1200
@@ -531,6 +551,8 @@
 #define PCI_DEVICE_ID_CERN_SPSB_PMC	0x0001
 #define PCI_DEVICE_ID_CERN_SPSB_PCI	0x0002
 
+#define PCI_VENDOR_ID_NVIDIA		0x10de
+
 #define PCI_VENDOR_ID_IMS		0x10e0
 #define PCI_DEVICE_ID_IMS_8849		0x8849
 
@@ -627,6 +649,7 @@
 #define PCI_DEVICE_ID_ALLIANCE_PROMOTIO	0x3210
 #define PCI_DEVICE_ID_ALLIANCE_PROVIDEO	0x6422
 #define PCI_DEVICE_ID_ALLIANCE_AT24	0x6424
+#define PCI_DEVICE_ID_ALLIANCE_AT3D	0x643d
 
 #define PCI_VENDOR_ID_VMIC		0x114a
 #define PCI_DEVICE_ID_VMIC_VME		0x7587
@@ -653,6 +676,9 @@
 #define PCI_VENDOR_ID_OMEGA		0x119b
 #define PCI_DEVICE_ID_OMEGA_82C092G	0x1221
 
+#define PCI_VENDOR_ID_LITEON		0x11ad
+#define PCI_DEVICE_ID_LITEON_LNE100TX	0x0002
+
 #define PCI_VENDOR_ID_NP		0x11bc
 #define PCI_DEVICE_ID_NP_PCI_FDDI	0x0001
 
@@ -660,6 +686,9 @@
 #define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
+#define PCI_VENDOR_ID_AURAVISION	0x11d1
+#define PCI_DEVICE_ID_AURAVISION_VXP524	0x01f7
+
 #define PCI_VENDOR_ID_IKON		0x11d5
 #define PCI_DEVICE_ID_IKON_10115	0x0115
 #define PCI_DEVICE_ID_IKON_10117	0x0117
@@ -703,8 +732,8 @@
 #define PCI_VENDOR_ID_PICTUREL		0x12c5
 #define PCI_DEVICE_ID_PICTUREL_PCIVST	0x0081
 
-#define PCI_VENDOR_ID_NVIDIA		0x12d2
-#define PCI_DEVICE_ID_NVIDIA_RIVA128	0x0018
+#define PCI_VENDOR_ID_NVIDIA_SGS	0x12d2
+#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018
 
 #define PCI_VENDOR_ID_SYMPHONY		0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101	0x0001
@@ -772,6 +801,8 @@
 #define PCI_DEVICE_ID_INTEL_82371AB	0x7111
 #define PCI_DEVICE_ID_INTEL_82371AB_2	0x7112
 #define PCI_DEVICE_ID_INTEL_82371AB_3	0x7113
+#define PCI_DEVICE_ID_INTEL_82443LX_0	0x7180
+#define PCI_DEVICE_ID_INTEL_82443LX_1	0x7181
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
 #define PCI_DEVICE_ID_INTEL_82450GX	0x84c5
 
@@ -781,6 +812,7 @@
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
+#define PCI_DEVICE_ID_ADAPTEC_5800	0x5800
 #define PCI_DEVICE_ID_ADAPTEC_7860	0x6078
 #define PCI_DEVICE_ID_ADAPTEC_7861	0x6178
 #define PCI_DEVICE_ID_ADAPTEC_7870	0x7078
@@ -788,6 +820,7 @@
 #define PCI_DEVICE_ID_ADAPTEC_7872	0x7278
 #define PCI_DEVICE_ID_ADAPTEC_7873	0x7378
 #define PCI_DEVICE_ID_ADAPTEC_7874	0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7895	0x7895
 #define PCI_DEVICE_ID_ADAPTEC_7880	0x8078
 #define PCI_DEVICE_ID_ADAPTEC_7881	0x8178
 #define PCI_DEVICE_ID_ADAPTEC_7882	0x8278
diff -u --recursive --new-file v2.1.70/linux/include/linux/rose.h linux/include/linux/rose.h
--- v2.1.70/linux/include/linux/rose.h	Sat Nov 29 11:25:12 1997
+++ linux/include/linux/rose.h	Thu Dec  4 13:12:39 1997
@@ -7,7 +7,6 @@
 #ifndef	ROSE_KERNEL_H
 #define	ROSE_KERNEL_H
 
-#define PF_ROSE		AF_ROSE
 #define ROSE_MTU	251
 
 #define	ROSE_DEFER	1
diff -u --recursive --new-file v2.1.70/linux/include/linux/scc.h linux/include/linux/scc.h
--- v2.1.70/linux/include/linux/scc.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/scc.h	Wed Dec  3 17:35:22 1997
@@ -3,6 +3,8 @@
 #ifndef	_SCC_H
 #define	_SCC_H
 
+#include <linux/config.h>
+
 /* selection of hardware types */
 
 #define PA0HZP		0x00	/* hardware type for PA0HZP SCC card and compatible */
diff -u --recursive --new-file v2.1.70/linux/include/linux/time.h linux/include/linux/time.h
--- v2.1.70/linux/include/linux/time.h	Mon Dec  1 12:04:15 1997
+++ linux/include/linux/time.h	Wed Dec  3 04:24:29 1997
@@ -15,7 +15,7 @@
  * change timeval to jiffies, trying to avoid the
  * most obvious overflows..
  */
-static inline unsigned long
+static __inline__ unsigned long
 timespec_to_jiffies(struct timespec *value)
 {
 	unsigned long sec = value->tv_sec;
@@ -28,7 +28,7 @@
 	return HZ * sec + nsec;
 }
 
-static inline void
+static __inline__ void
 jiffies_to_timespec(unsigned long jiffies, struct timespec *value)
 {
 	value->tv_nsec = (jiffies % HZ) * (1000000000L / HZ);
diff -u --recursive --new-file v2.1.70/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- v2.1.70/linux/include/linux/tty_driver.h	Wed Nov 26 16:24:03 1997
+++ linux/include/linux/tty_driver.h	Thu Dec  4 13:35:40 1997
@@ -207,10 +207,12 @@
 #define TTY_DRIVER_TYPE_SERIAL		0x0003
 #define TTY_DRIVER_TYPE_PTY		0x0004
 #define TTY_DRIVER_TYPE_SCC		0x0005	/* scc driver */
+#define TTY_DRIVER_TYPE_SYSCONS		0x0006
 
 /* system subtypes (magic, used by tty_io.c) */
 #define SYSTEM_TYPE_TTY			0x0001
 #define SYSTEM_TYPE_CONSOLE		0x0002
+#define SYSTEM_TYPE_SYSCONS		0x0003
 
 /* pty subtypes (magic, used by tty_io.c) */
 #define PTY_TYPE_MASTER			0x0001
diff -u --recursive --new-file v2.1.70/linux/include/linux/ufs_fs.h linux/include/linux/ufs_fs.h
--- v2.1.70/linux/include/linux/ufs_fs.h	Thu Jul 17 10:06:09 1997
+++ linux/include/linux/ufs_fs.h	Wed Dec  3 06:18:33 1997
@@ -6,13 +6,17 @@
  * Laboratory for Computer Science Research Computing Facility
  * Rutgers, The State University of New Jersey
  *
- * $Id: ufs_fs.h,v 1.8 1997/07/17 02:17:54 davem Exp $
- *
+ * Clean swab support by Fare <rideau@ens.fr>
+ * just hope no one is using NNUUXXI on __?64 structure elements
  */
 
 #ifndef __LINUX_UFS_FS_H
 #define __LINUX_UFS_FS_H
 
+#undef UFS_HEAVY_DEBUG
+/*#define UFS_HEAVY_DEBUG 1*/
+/* Uncomment the line above when hacking ufs code */
+
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/time.h>
@@ -24,6 +28,7 @@
 #define UFS_SBSIZE 8192
 
 #define UFS_MAGIC 0x00011954
+#define UFS_CIGAM 0x54190100 /* byteswapped MAGIC */
 
 #define UFS_FSIZE 1024
 #define UFS_BSIZE 8192
@@ -51,6 +56,17 @@
 #define UFS_DEBUG_NAMEI 0x00000004
 #define UFS_DEBUG_LINKS 0x00000008
 
+#ifdef UFS_HEAVY_DEBUG
+#  define UFS_DEBUG_INITIAL UFS_DEBUG
+#else
+#  define UFS_DEBUG_INITIAL 0
+#endif
+
+/* (!) HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes */
+#define UFS_LITTLE_ENDIAN 0x00000000	/* 0x00010000 */
+#define UFS_BIG_ENDIAN    0x00010000	/* 0x00020000 */
+#define UFS_BYTESEX	  0x00010000	/* 0x00030000 */
+
 #define UFS_ADDR_PER_BLOCK(sb)		((sb)->u.ufs_sb.s_bsize >> 2)
 #define UFS_ADDR_PER_BLOCK_BITS(sb)	((sb)->u.ufs_sb.s_bshift - 2)
 
@@ -93,10 +109,6 @@
 	__u32	cs_nffree;	/* number of free frags */
 };
 
-typedef struct _ufsquad {
-	__u32 val[2];
-} ufsquad;
-
 /*
  * This is the actual superblock, as it is laid out on the disk.
  */
@@ -109,7 +121,7 @@
 	__u32	fs_dblkno;	/* offset of first data after cg */
 	__u32	fs_cgoffset;	/* cylinder group offset in cylinder */
 	__u32	fs_cgmask;	/* used to calc mod fs_ntrak */
-	time_t	fs_time;	/* last time written */
+	__u32	fs_time;	/* last time written -- time_t */
 	__u32	fs_size;	/* number of blocks in fs */
 	__u32	fs_dsize;	/* number of data blocks in fs */
 	__u32	fs_ncg;		/* number of cylinder groups */
@@ -177,15 +189,14 @@
 	__u16	fs_opostbl[16][8];	/* old rotation block list head */	
 	__s32	fs_sparecon[55];	/* reserved for future constants */
 	__s32	fs_state;		/* file system state time stamp */
-	ufsquad	fs_qbmask;		/* ~usb_bmask - for use with __s64 size */
-	ufsquad	fs_qfmask;		/* ~usb_fmask - for use with __s64 size */
+	__s64	fs_qbmask;		/* ~usb_bmask */
+	__s64	fs_qfmask;		/* ~usb_fmask */
 	__s32	fs_postblformat;	/* format of positional layout tables */
 	__s32	fs_nrpos;		/* number of rotational positions */
 	__s32	fs_postbloff;		/* (__s16) rotation block list head */
 	__s32	fs_rotbloff;		/* (__u8) blocks for each rotation */
 	__s32	fs_magic;		/* magic number */
 	__u8	fs_space[1];		/* list of blocks for each rotation */
-
 };
 
 /*
@@ -196,13 +207,13 @@
 	__u16	ui_nlink;		/*  0x2 */
 	__u16	ui_suid;		/*  0x4 */
 	__u16	ui_sgid;		/*  0x6 */
-	ufsquad	ui_size;		/*  0x8 */  /* XXX - should be __u64 */
-	struct timeval ui_atime;	/* 0x10 */
-	struct timeval ui_mtime;	/* 0x18 */
-	struct timeval ui_ctime;	/* 0x20 */
-	__u32	ui_db[UFS_NDADDR];		/* 0x28 data blocks */
-	__u32	ui_ib[UFS_NINDIR];		/* 0x58 indirect blocks */
-	__u32	ui_flags;		/* 0x64 unused */
+	__u64	ui_size;		/*  0x8 */
+	struct timeval ui_atime;	/* 0x10 access */
+	struct timeval ui_mtime;	/* 0x18 modification */
+	struct timeval ui_ctime;	/* 0x20 creation */
+	__u32	ui_db[UFS_NDADDR];	/* 0x28 data blocks */
+	__u32	ui_ib[UFS_NINDIR];	/* 0x58 indirect blocks */
+	__u32	ui_flags;		/* 0x64 unused -- "status flags (chflags)" ??? */
 	__u32	ui_blocks;		/* 0x68 blocks in use */
 	__u32	ui_gen;			/* 0x6c generation number XXX - what is this? */
 	__u32	ui_shadow;		/* 0x70 shadow inode XXX - what is this?*/
@@ -225,7 +236,7 @@
 extern void ufs_print_inode (struct inode *);
 
 /* ufs_namei.c */
-extern int ufs_lookup (struct inode *, struct qstr *, struct inode **);
+extern int ufs_lookup (struct inode *, struct dentry *);
 
 /* ufs_super.c */
 extern void ufs_warning (struct super_block *, const char *, const char *, ...)
@@ -247,23 +258,6 @@
 /* ufs_symlink.c */
 extern struct inode_operations ufs_symlink_inode_operations;
 extern struct file_operations ufs_symlink_operations;
-
-/* Byte swapping 32/16-bit quantities into little endian format. */
-extern int ufs_need_swab;
-
-extern __inline__ __u32 ufs_swab32(__u32 value)
-{
-	return (ufs_need_swab ? ((value >> 24) |
-				((value >> 8) & 0xff00) |
-				((value << 8) & 0xff0000) |
-				 (value << 24)) : value);
-}
-
-extern __inline__ __u16 ufs_swab16(__u16 value)
-{
-	return (ufs_need_swab ? ((value >> 8) |
-				 (value << 8)) : value);
-}
 
 #endif	/* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.70/linux/init/main.c linux/init/main.c
--- v2.1.70/linux/init/main.c	Tue Dec  2 16:45:20 1997
+++ linux/init/main.c	Wed Dec  3 15:21:57 1997
@@ -84,6 +84,7 @@
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
+extern void console_setup(char *str, int *ints);
 #ifdef CONFIG_PRINTER
 extern void lp_setup(char *str, int *ints);
 #endif
@@ -352,6 +353,7 @@
 	{ "swap=", swap_setup },
 	{ "buff=", buff_setup },
 	{ "panic=", panic_setup },
+	{ "console=", console_setup },
 #ifdef CONFIG_VT
 	{ "no-scroll", no_scroll },
 #endif
@@ -1022,7 +1024,7 @@
 
 	close(0);close(1);close(2);
 	setsid();
-	(void) open("/dev/tty1",O_RDWR,0);
+	(void) open("/dev/console",O_RDWR,0);
 	(void) dup(0);
 	(void) dup(0);
 	return execve(shell, argv, envp_init);
@@ -1106,8 +1108,8 @@
 
 	setup(1);
 	
-	if (open("/dev/console",O_RDWR,0) < 0)
-		printk("Unable to open an initial console.\n");
+	if (open("/dev/console", O_RDWR, 0) < 0)
+		printk("Warning: unable to open an initial console.\n");
 
 	(void) dup(0);
 	(void) dup(0);
diff -u --recursive --new-file v2.1.70/linux/kernel/printk.c linux/kernel/printk.c
--- v2.1.70/linux/kernel/printk.c	Sat Nov 29 11:25:12 1997
+++ linux/kernel/printk.c	Wed Dec  3 15:21:57 1997
@@ -53,6 +53,46 @@
 static char log_buf[LOG_BUF_LEN];
 static unsigned long log_start = 0;
 static unsigned long logged_chars = 0;
+struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
+static int selected_console = 0;
+
+/*
+ *	Setup a list of consoles. Called from init/main.c
+ */
+__initfunc(void console_setup(char *str, int *ints))
+{
+	char *s;
+	int i;
+	struct console_cmdline *c;
+
+	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
+		;
+	if (i == MAX_CMDLINECONSOLES)
+		return;
+	c = &console_cmdline[i];
+	selected_console = 1;
+
+	if (str[0] >= '0' && str[0] <= '9') {
+		strcpy(c->name, "ttyS");
+		strncpy(c->name + 4, str, sizeof(c->name) - 5);
+	} else
+		strncpy(c->name, str, sizeof(c->name) - 1);
+	if ((c->options = strchr(str, ',')) != NULL)
+		*(c->options++) = 0;
+#ifdef __sparc__
+	if (strcmp(str, "ttya"))
+		strcpy(c->name, "ttyS0");
+	if (strcmp(str, "ttyb"))
+		strcpy(c->name, "ttyS1");
+#endif
+
+	for(s = c->name; *s; s++)
+		if (*s >= '0' && *s <= '9')
+			break;
+	c->index = simple_strtoul(s, NULL, 10);
+	*s = 0;
+}
+
 
 /*
  * Commands to sys_syslog:
@@ -222,8 +262,8 @@
 		if (msg_level < console_loglevel && console_drivers) {
 			struct console *c = console_drivers;
 			while(c) {
-				if (c->write)
-					c->write(msg, p - msg + line_feed);
+				if ((c->flags & CON_ENABLED) && c->write)
+					c->write(c, msg, p - msg + line_feed);
 				c = c->next;
 			}
 		}
@@ -239,9 +279,10 @@
 {
 	struct console *c = console_drivers;
 	int len = strlen(s);
+
 	while(c) {
-		if (c->write)
-			c->write(s, len);
+		if ((c->flags & CON_ENABLED) && c->write)
+			c->write(c, s, len);
 		c = c->next;
 	}
 }
@@ -250,7 +291,7 @@
 {
 	struct console *c = console_drivers;
 	while(c) {
-		if (c->unblank)
+		if ((c->flags & CON_ENABLED) && c->unblank)
 			c->unblank();
 		c = c->next;
 	}
@@ -262,7 +303,7 @@
  * print any messages that were printed by the kernel before the
  * console driver was initialized.
  */
-__initfunc(void register_console(struct console * console))
+void register_console(struct console * console)
 {
 	int	i,j,len;
 	int	p = log_start;
@@ -270,9 +311,52 @@
 	signed char msg_level = -1;
 	char	*q;
 
-	console->next = console_drivers;
-	console_drivers = console;
+	/*
+	 *	See if we want to use this console driver. If we
+	 *	didn't select a console we take the first one
+	 *	that registers here.
+	 */
+	if (selected_console == 0) {
+		console->flags |= CON_ENABLED | CON_FIRST;
+		selected_console = 1;
+		if (console->setup)
+			console->setup(console, NULL);
+	}
+	/*
+	 *	See if this console matches one we selected on
+	 *	the command line.
+	 */
+	for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++) {
+		if (strcmp(console_cmdline[i].name, console->name) != 0)
+			continue;
+		if (console->index >= 0 &&
+		    console->index != console_cmdline[i].index)
+			continue;
+		console->flags |= CON_ENABLED;
+		console->index = console_cmdline[i].index;
+		if (i == 0)
+			console->flags |= CON_FIRST;
+		if (console->setup)
+			console->setup(console, console_cmdline[i].options);
+		break;
+	}
+
+	/*
+	 *	Put this console in the list - keep the
+	 *	preferred driver at the head of the list.
+	 */
+	if ((console->flags & CON_FIRST) || console_drivers == NULL) {
+		console->next = console_drivers;
+		console_drivers = console;
+	} else {
+		console->next = console_drivers->next;
+		console_drivers->next = console;
+	}
+	if ((console->flags & CON_PRINTBUFFER) == 0) return;
 
+	/*
+	 *	Print out buffered log messages.
+	 */
 	for (i=0,j=0; i < log_size; i++) {
 		buf[j++] = log_buf[p];
 		p++; p &= LOG_BUF_LEN-1;
@@ -287,7 +371,7 @@
 			len -= 3;
 		}
 		if (msg_level < console_loglevel)
-			console->write(q, len);
+			console->write(console, q, len);
 		if (buf[j-1] == '\n')
 			msg_level = -1;
 		j = 0;
diff -u --recursive --new-file v2.1.70/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.70/linux/kernel/signal.c	Tue Dec  2 16:45:20 1997
+++ linux/kernel/signal.c	Wed Dec  3 17:27:43 1997
@@ -651,7 +651,6 @@
 {
 	sigset_t these;
 	struct timespec ts;
-	unsigned long expire;
 	siginfo_t info;
 	int ret, sig;
 
@@ -680,17 +679,19 @@
 	if (!sig) {
 		/* None ready -- temporarily unblock those we're interested
 		   in so that we'll be awakened when they arrive.  */
+		unsigned long expire;
 		sigset_t oldblocked = current->blocked;
 		sigandsets(&current->blocked, &current->blocked, &these);
 		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 
+		expire = ~0UL;
 		if (uts) {
 			expire = (timespec_to_jiffies(&ts)
 				  + (ts.tv_sec || ts.tv_nsec));
 			expire += jiffies;
-			current->timeout = expire;
 		}
+		current->timeout = expire;
 
 		current->state = TASK_INTERRUPTIBLE;
 		schedule();
@@ -709,7 +710,11 @@
 				ret = -EFAULT;
 		}
 	} else {
-		ret = !uts || expire > jiffies ? -EINTR : -EAGAIN;
+		ret = -EAGAIN;
+		if (current->timeout != 0) {
+			current->timeout = 0;
+			ret = -EINTR;
+		}
 	}
 
 	return ret;
diff -u --recursive --new-file v2.1.70/linux/net/Config.in linux/net/Config.in
--- v2.1.70/linux/net/Config.in	Mon Dec  1 12:04:15 1997
+++ linux/net/Config.in	Wed Dec  3 15:17:30 1997
@@ -23,7 +23,7 @@
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
     if [ "$CONFIG_IPV6" != "n" ]; then
-	    source net/ipv6/Config.in	
+	    source net/ipv6/Config.in
     fi
   fi
 fi
@@ -34,13 +34,6 @@
   bool 'Full internal IPX network' CONFIG_IPX_INTERN
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
-tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
-if [ "$CONFIG_AX25" != "n" ]; then
-  bool 'AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
-# bool 'AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
-  dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
-  dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
-fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 #  tristate 'DECnet Support (NOT YET FUNCTIONAL)' CONFIG_DECNET
 #  if [ "$CONFIG_DECNET" != "n" ]; then
diff -u --recursive --new-file v2.1.70/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.70/linux/net/ax25/af_ax25.c	Mon Dec  1 12:04:15 1997
+++ linux/net/ax25/af_ax25.c	Wed Dec  3 15:16:39 1997
@@ -954,6 +954,7 @@
 #ifdef AX25_CONFIG_DAMA_SLAVE
 					case AX25_PROTO_DAMA_SLAVE:
 						ax25_stop_t3timer(sk->protinfo.ax25);
+						ax25_stop_idletimer(sk->protinfo.ax25);
 						break;
 #endif
 				}
diff -u --recursive --new-file v2.1.70/linux/net/ax25/ax25_ds_subr.c linux/net/ax25/ax25_ds_subr.c
--- v2.1.70/linux/net/ax25/ax25_ds_subr.c	Mon Dec  1 12:04:15 1997
+++ linux/net/ax25/ax25_ds_subr.c	Wed Dec  3 15:16:39 1997
@@ -115,7 +115,10 @@
 		if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
 			ax25_ds_t1_timeout(ax25o);
 
-		ax25_start_t3timer(ax25o);
+		/* do not start T3 for listening sockets (tnx DD8NE) */
+
+		if (ax25o->state != AX25_STATE_0)
+			ax25_start_t3timer(ax25o);
 	}
 }
 
@@ -210,8 +213,8 @@
 
 void ax25_dama_off(ax25_cb *ax25)
 {
-	ax25_dev_dama_off(ax25->ax25_dev);
 	ax25->condition &= ~AX25_COND_DAMA_MODE;
+	ax25_dev_dama_off(ax25->ax25_dev);
 }
 
 #endif
diff -u --recursive --new-file v2.1.70/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.70/linux/net/ax25/ax25_out.c	Mon Dec  1 12:04:15 1997
+++ linux/net/ax25/ax25_out.c	Wed Dec  3 15:16:39 1997
@@ -27,6 +27,9 @@
  *			Joerg(DL1BKE)	Fixed a problem with buffer allocation
  *					for fragments.
  *	AX.25 037	Jonathan(G4KLX)	New timer architecture.
+ *			Joerg(DL1BKE)	Fixed DAMA Slave mode: will work
+ *					on non-DAMA interfaces like AX25L2V2
+ *					again (this behaviour is _required_).
  */
 
 #include <linux/config.h>
@@ -196,9 +199,22 @@
 		skb_queue_tail(&ax25->write_queue, skb);	  /* Throw it on the queue */
 	}
 
-	if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX ||
-	    ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX)
-		ax25_kick(ax25);
+	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+		case AX25_PROTO_STD_SIMPLEX:
+		case AX25_PROTO_STD_DUPLEX:
+			ax25_kick(ax25);
+			break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+		/* 
+		 * A DAMA slave is _required_ to work as normal AX.25L2V2
+		 * if no DAMA master is available.
+		 */
+		case AX25_PROTO_DAMA_SLAVE:
+			if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25);
+			break;
+#endif
+	}
 }
 
 /* 
diff -u --recursive --new-file v2.1.70/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.70/linux/net/core/dev.c	Mon Dec  1 12:04:15 1997
+++ linux/net/core/dev.c	Wed Dec  3 04:28:11 1997
@@ -1291,7 +1291,6 @@
 			if(dev->set_multicast_list==NULL ||
 			   ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
 				return -EINVAL;
-			printk(KERN_DEBUG "SIOCADDMULTI ioctl is deprecated\n");
 			dev_mc_add(dev,ifr->ifr_hwaddr.sa_data, dev->addr_len, 1);
 			return 0;
 
@@ -1299,7 +1298,6 @@
 			if(dev->set_multicast_list==NULL ||
 			   ifr->ifr_hwaddr.sa_family!=AF_UNSPEC)
 				return -EINVAL;
-			printk(KERN_DEBUG "SIOCDELMULTI ioctl is deprecated\n");
 			dev_mc_delete(dev,ifr->ifr_hwaddr.sa_data,dev->addr_len, 1);
 			return 0;
 
diff -u --recursive --new-file v2.1.70/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.70/linux/net/netsyms.c	Tue Dec  2 16:45:21 1997
+++ linux/net/netsyms.c	Wed Dec  3 11:41:40 1997
@@ -145,6 +145,7 @@
 EXPORT_SYMBOL(max_files);
 EXPORT_SYMBOL(do_mknod);
 EXPORT_SYMBOL(memcpy_toiovec);
+EXPORT_SYMBOL(csum_partial);
 
 #ifdef CONFIG_IPX_MODULE
 EXPORT_SYMBOL(make_8023_client);
@@ -209,7 +210,6 @@
 
 EXPORT_SYMBOL(destroy_sock);
 EXPORT_SYMBOL(ip_queue_xmit);
-EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(memcpy_fromiovecend);
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
 EXPORT_SYMBOL(__release_sock);
diff -u --recursive --new-file v2.1.70/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.70/linux/net/unix/af_unix.c	Mon Dec  1 12:04:18 1997
+++ linux/net/unix/af_unix.c	Wed Dec  3 10:34:16 1997
@@ -1475,8 +1475,10 @@
 };
 
 #ifdef MODULE
+#ifdef CONFIG_SYSCTL
 extern void unix_sysctl_register(void);
 extern void unix_sysctl_unregister(void);
+#endif
 
 int init_module(void)
 #else
@@ -1503,7 +1505,9 @@
 #endif
 
 #ifdef MODULE
+#ifdef CONFIG_SYSCTL
 	unix_sysctl_register();
+#endif
 
 	return 0;
 #endif
@@ -1513,7 +1517,9 @@
 void cleanup_module(void)
 {
 	sock_unregister(AF_UNIX);
+#ifdef CONFIG_SYSCTL
 	unix_sysctl_unregister();
+#endif
 }
 #endif
 
diff -u --recursive --new-file v2.1.70/linux/net/wanrouter/wanproc.c linux/net/wanrouter/wanproc.c
--- v2.1.70/linux/net/wanrouter/wanproc.c	Thu Jul 17 10:06:10 1997
+++ linux/net/wanrouter/wanproc.c	Wed Dec  3 15:20:04 1997
@@ -57,8 +57,7 @@
 
 /* Proc filesystem interface */
 static int router_proc_perms (struct inode*, int);
-static long router_proc_read(struct inode* inode, struct file* file, char* buf,
-				unsigned long count);
+static ssize_t router_proc_read(struct file* file, char* buf, size_t count, 					loff_t *ppos);
 
 /* Methods for preparing data for reading proc entries */
 
@@ -337,9 +336,10 @@
  *		<0	error
  */
 
-static long router_proc_read(struct inode* inode, struct file* file, 
-				char* buf, unsigned long count)
+static ssize_t router_proc_read(struct file* file, char* buf, size_t count,
+				loff_t *ppos)
 {
+	struct inode *inode;
 	struct proc_dir_entry* dent;
 	char* page;
 	int pos, offs, len;
