diff -u --recursive --new-file v2.1.73/linux/CREDITS linux/CREDITS
--- v2.1.73/linux/CREDITS	Wed Dec 10 11:12:42 1997
+++ linux/CREDITS	Fri Dec 19 15:24:20 1997
@@ -958,9 +958,10 @@
 S: University of Stuttgart, Germany and
 S: Ecole Nationale Superieure des Telecommunications, Paris
 
-N: Martin von Loewis
+N: Martin von Löwis
 E: loewis@informatik.hu-berlin.de
 D: script binary format
+D: NTFS driver
 
 N: Mark Lord
 E: mlord@pobox.com
diff -u --recursive --new-file v2.1.73/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.73/linux/Documentation/Configure.help	Fri Dec 19 15:52:47 1997
+++ linux/Documentation/Configure.help	Fri Dec 19 15:24:20 1997
@@ -4749,6 +4749,20 @@
   want). The module is called hpfs.o. If you want to compile it as a
   module, say M here and read Documentation/modules.txt. If unsure,
   say N.
+
+Windows NT NTFS support (read only)
+CONFIG_NTFS_FS
+  NTFS is the file system of Microsoft Windows NT. Say Y if you want
+  to access partitions using this file system. The Linux NTFS driver
+  supports most of the mount options of the VFAT driver, see
+  Documentation/filesystems/ntfs.txt. There is an experimental
+  write support available; use at your own risk.
+
+NTFS read-write support (experimental)
+CONFIG_NTFS_RW
+  The read-write support in NTFS is far from being complete and well
+  tested. If you enable this, be prepared to recover the NTFS volume
+  from tape.
   
 System V and Coherent filesystem support
 CONFIG_SYSV_FS
diff -u --recursive --new-file v2.1.73/linux/Documentation/filesystems/ntfs.txt linux/Documentation/filesystems/ntfs.txt
--- v2.1.73/linux/Documentation/filesystems/ntfs.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/filesystems/ntfs.txt	Fri Dec 19 15:24:20 1997
@@ -0,0 +1,30 @@
+NTFS Overview
+=============
+
+To mount an NTFS volume, use the filesystem type 'ntfs'. The driver
+currently works only in read-only mode, with no fault-tolerance
+supported.  If you enable the experimental write support, make sure
+you can recover from a complete loss of data. For ftdisk support,
+limit success was reported with volume sets on top of the md driver,
+although mirror and stripe sets should work as well - if the md
+driver can be talked into using the same lay-out as Windows NT.
+
+The ntfs driver supports the following mount options:
+iocharset=name      Character set to use when returning file names.
+                    Unlike VFAT, NTFS suppresses names that contain
+		    unconvertible characters
+utf8=<bool>	    Use UTF-8 for converting file names
+uni_xlate=<bool>,2  Use the VFAT-style encoding for file names outside
+		    the current character set. A boolean value will
+		    enable the feature, a value of 2 will enable the
+		    encoding as documented in vfat.txt:
+		    ':', (u & 0x3f), ((u>>6) & 0x3f), (u>>12),
+uid=
+gid=
+umask=              These options work as documented in mount(8).
+		    By default, the files are owned by root and
+		    not readable by somebody else.
+posix=<bool>	    If enabled, the file system distinguishes between
+		    upper and lower case. The 8.3 alias names are presented
+		    as hard links instead of being suppressed.
+
diff -u --recursive --new-file v2.1.73/linux/Documentation/stallion.txt linux/Documentation/stallion.txt
--- v2.1.73/linux/Documentation/stallion.txt	Tue Sep 23 16:48:46 1997
+++ linux/Documentation/stallion.txt	Fri Dec 19 12:30:53 1997
@@ -2,10 +2,10 @@
 Stallion Multiport Serial Driver Readme
 ---------------------------------------
 
-Copyright (C) 1994-1997,  Stallion Technologies (support@stallion.oz.au).
+Copyright (C) 1994-1997,  Stallion Technologies (support@stallion.com).
 
-Version:   5.3.4
-Date:      15SEP97
+Version:   5.4.1
+Date:      19DEC97
 
 
 
@@ -13,32 +13,36 @@
 
 There are two drivers that work with the different families of Stallion
 multiport serial boards. One is for the Stallion smart boards - that is
-EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent
-multiport boards - EasyConnection 8/64, ONboard and Brumby.
+EasyIO, EasyConnection 8/32 and EasyConnection 8/64-PCI, the other for
+the true Stallion intelligent multiport boards - EasyConnection 8/64
+(ISA and EISA), ONboard and Brumby.
 
 If you are using any of the Stallion intelligent multiport boards (Brumby,
-ONboard, EasyConnection 8/64) with Linux you will need to get the driver
-utility package. This package is available at most of the Linux archive
-sites (and on CD's that contain these archives). The file will be called
-stallion-X.X.X.tar.gz where X.X.X will be the version number. In particular
-this package contains the board embedded executable images that are
-required for these boards. It also contains the downloader program.
-These boards cannot be used without this.
-
-The following ftp sites (and their mirrors) definitely have the stallion
-driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu.
-
-ftp.stallion.com:/drivers/ata5/Linux/stallion-5.3.1.tar.gz
-tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.3.1.tar.gz
-sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.3.1.tar.gz
+ONboard, EasyConnection 8/64 (ISA or EISA)) with Linux you will need to
+get the driver utility package. This package is available at most of the
+Linux archive sites (and on CD's that contain these archives). The file
+will be called stallion-X.X.X.tar.gz where X.X.X will be the version
+number. In particular this package contains the board embedded executable
+images that are required for these boards. It also contains the downloader
+program. These boards cannot be used without this.
+
+The Stallion Technologies ftp site, ftp.stallion.com, will always have
+the latest version of the driver utility package. Other sites that usually
+have the latest version are tsx-11.mit.edu, sunsite.unc.edu and their
+mirrors.
+
+ftp.stallion.com:/drivers/ata5/Linux/stallion-5.4.0.tar.gz
+tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-5.4.0.tar.gz
+sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-5.4.0.tar.gz
 
 As of the printing of this document the latest version of the driver
-utility package is 5.3.1. If a later version is now available then you
+utility package is 5.4.0. If a later version is now available then you
 should use the latest version.
 
-If you are using the EasyIO or EasyConnection 8/32 boards then you don't
-need this package. Although it does have a handy script to create the
-/dev device nodes for these boards, and a serial stats display program.
+If you are using the EasyIO, EasyConnection 8/32 or EasyConnection 8/64-PCI
+boards then you don't need this package. Although it does have a handy
+script to create the /dev device nodes for these boards, and a serial stats
+display program.
 
 If you require DIP switch settings, EISA/MCA configuration files, or any
 other information related to Stallion boards then have a look at Stallion's
@@ -63,7 +67,7 @@
 card driver (the stallion.c driver) supports any combination of EasyIO and
 EasyConnection 8/32 boards (up to a total of 4). The intelligent driver
 supports any combination of ONboards, Brumbys, Stallions and EasyConnection
-8/64 boards (up to a total of 4).
+8/64 (ISA and EISA) boards (up to a total of 4).
 
 To set up the driver(s) for the boards that you want to use you need to
 edit the appropriate driver file and add configuration entries.
@@ -76,7 +80,8 @@
 	(the comments before this structure should help)
       - save and exit
 
-If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
+If using ONboard, Brumby, Stallion or EasyConnection 8/64 (ISA or EISA)
+boards then do:
    vi /usr/src/linux/drivers/char/istallion.c
       - find the definition of the stli_brdconf array (of structures)
         near the top of the file
@@ -114,11 +119,11 @@
    configuration files for *all* the EasyIO and EasyConnection 8/32 boards
    that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32
    EISA configuration files required are supplied by Stallion Technologies
-   on the EASY Utilities floppy (usually supplied in the box with the board
-   when purchased. If not, you can pick it up from Stallion's FTP site,
-   ftp.stallion.com). You will need to edit the board resources to choose
-   level triggered interrupts, and make sure to set each board's interrupt
-   to the same IRQ number.
+   on the EASY Utilities floppy diskette (usually supplied in the box with
+   the board when purchased. If not, you can pick it up from Stallion's FTP
+   site, ftp.stallion.com). You will need to edit the board resources to
+   choose level triggered interrupts, and make sure to set each board's
+   interrupt to the same IRQ number.
 
 You must complete both the above steps for this to work. When you reboot
 or load the driver your EasyIO and EasyConnection 8/32 boards will be
@@ -149,8 +154,8 @@
 the IRQ is software programmable, so if there is a conflict you may need to
 change the IRQ used for a board in the stallion.c configuration structure.
 There are no interrupts to worry about for ONboard, Brumby or EasyConnection
-8/64 boards. The memory region on EasyConnection 8/64 and ONboard boards is
-software programmable, but not on the Brumby boards.
+8/64 (ISA, EISA and MCA) boards. The memory region on EasyConnection 8/64 and
+ONboard boards is software programmable, but not on the Brumby boards.
 
 
 
diff -u --recursive --new-file v2.1.73/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.73/linux/MAINTAINERS	Fri Dec 19 15:52:48 1997
+++ linux/MAINTAINERS	Fri Dec 19 12:30:53 1997
@@ -433,6 +433,13 @@
 L:	linux-kernel@vger.rutgers.edu ?
 S:	Supported
 
+STALLION TECHNOLOGIES MULTIPORT SERIAL BOARDS
+P:	Greg Ungerer
+M:	support@stallion.oz.au
+M:	gerg@stallion.com
+W:	http://www.stallion.com
+S:	Supported
+
 MOUSE AND MISC DEVICES [GENERAL]
 P:	Alessandro Rubini
 M:	rubini@ipvvis.unipv.it
diff -u --recursive --new-file v2.1.73/linux/Makefile linux/Makefile
--- v2.1.73/linux/Makefile	Fri Dec 19 15:52:48 1997
+++ linux/Makefile	Fri Dec 19 09:49:10 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 73
+SUBLEVEL = 74
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
diff -u --recursive --new-file v2.1.73/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.73/linux/drivers/block/ide-cd.c	Fri Dec 19 15:52:56 1997
+++ linux/drivers/block/ide-cd.c	Fri Dec 19 09:35:05 1997
@@ -180,6 +180,8 @@
  *
  * 4.06  Dec 17, 1997  -- fixed endless "tray open" messages  -ml
  * 4.07  Dec 17, 1997  -- fallback to set pc->stat on "tray open"
+ * 4.08  Dec 18, 1997  -- spew less noise when tray is empty
+ *                     -- fix speed display for ACER 24X, 18X
  *
  *************************************************************************/
 
@@ -243,6 +245,15 @@
 		    failed_command->c[0] == SCMD_READ_SUBCHANNEL)
 			return;
 	}
+	if (reqbuf->error_code == 0x70 && reqbuf->sense_key  == 0x02
+	 && reqbuf->asc        == 0x3a && reqbuf->ascq       == 0x00)
+	{
+		/*
+		 * No disc in drive ("Medium not present"),
+		 * so keep the noise level down to a dull roar.
+		 */
+		return;
+	}
 
 #if VERBOSE_IDE_CD_ERRORS
 	{
@@ -466,7 +477,7 @@
 			/* Check for tray open. */
 			if (sense_key == NOT_READY) {
 				cdrom_saw_media_change (drive);
-
+#if 0	/* let the upper layers do the complaining */
 				/* Print an error message to the syslog.
 				   Exception: don't print anything if this
 				   is a read subchannel command.  This is
@@ -474,12 +485,13 @@
 				   with this command, and we don't want
 				   to uselessly fill up the syslog. */
 				if (pc->c[0] != SCMD_READ_SUBCHANNEL)
-					printk ("%s: tray open or drive not ready\n",
-						drive->name);
+					printk ("%s: tray open or drive not ready\n", drive->name);
+#endif
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
 				cdrom_saw_media_change (drive);
 				printk ("%s: media changed\n", drive->name);
+				return 0;
 			} else {
 				/* Otherwise, print an error. */
 				ide_dump_status (drive, "packet command error",
@@ -2734,12 +2746,13 @@
 		}
 	}
 
-	if (drive->id && drive->id->model[0]) {
-		CDROM_STATE_FLAGS (drive)->current_speed  = (ntohs(buf.cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
-	} else {  /* no-name ACERs (AOpen) have it backwards */
+	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
 		CDROM_STATE_FLAGS (drive)->current_speed  = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
 		CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+	} else {
+		CDROM_STATE_FLAGS (drive)->current_speed  = (ntohs(buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
 	}
 
         printk ("%s: ATAPI %dX CDROM", 
diff -u --recursive --new-file v2.1.73/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v2.1.73/linux/drivers/char/istallion.c	Wed Sep 24 20:05:46 1997
+++ linux/drivers/char/istallion.c	Fri Dec 19 12:30:54 1997
@@ -77,6 +77,8 @@
 #define BRD_ECPE	24
 #define	BRD_ECPMC	25
 #define	BRD_ECHPCI	26
+#define	BRD_ECH64PCI	27
+#define	BRD_EASYIOPCI	28
 
 #define	BRD_BRUMBY	BRD_BRUMBY4
 
@@ -166,8 +168,9 @@
  *	Define our local driver identity first. Set up stuff to deal with
  *	all the local structures required by a serial tty driver.
  */
-static char	*stli_drvname = "Stallion Intelligent Multiport Serial Driver";
-static char	*stli_drvversion = "5.3.4";
+static char	*stli_drvtitle = "Stallion Intelligent Multiport Serial Driver";
+static char	*stli_drvname = "istallion";
+static char	*stli_drvversion = "5.4.1";
 static char	*stli_serialname = "ttyE";
 static char	*stli_calloutname = "cue";
 
@@ -291,6 +294,8 @@
 	"EC8/64-EI",
 	"EC8/64-MC",
 	"EC8/32-PCI",
+	"EC8/64-PCI",
+	"EasyIO-PCI",
 };
 
 /*
@@ -484,9 +489,9 @@
 /*
  *	Define the maximal baud rate, and the default baud base for ports.
  */
-#define	STL_MAXBAUD	921600
+#define	STL_MAXBAUD	460800
 #define	STL_BAUDBASE	115200
-#define	STL_CLOSEDELAY	50
+#define	STL_CLOSEDELAY	(5 * HZ / 10)
 
 /*****************************************************************************/
 
@@ -539,18 +544,18 @@
 static void	stli_stop(struct tty_struct *tty);
 static void	stli_start(struct tty_struct *tty);
 static void	stli_flushbuffer(struct tty_struct *tty);
+static void	stli_breakctl(struct tty_struct *tty, int state);
+static void	stli_waituntilsent(struct tty_struct *tty, int timeout);
+static void	stli_sendxchar(struct tty_struct *tty, char ch);
 static void	stli_hangup(struct tty_struct *tty);
+static int	stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos);
 
-static inline int stli_initbrds(void);
-static inline int stli_initecp(stlibrd_t *brdp);
-static inline int stli_initonb(stlibrd_t *brdp);
-static inline int stli_findeisabrds(void);
-static inline int stli_initports(stlibrd_t *brdp);
-static int	stli_eisamemprobe(stlibrd_t *brdp);
 static int	stli_brdinit(stlibrd_t *brdp);
 static int	stli_startbrd(stlibrd_t *brdp);
-static long	stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count);
-static long	stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count);
+static int	stli_memopen(struct inode *ip, struct file *fp);
+static int	stli_memclose(struct inode *ip, struct file *fp);
+static ssize_t	stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp);
+static ssize_t	stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp);
 static int	stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
 static void	stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
 static void	stli_poll(unsigned long arg);
@@ -573,6 +578,7 @@
 static int	stli_setserial(stliport_t *portp, struct serial_struct *sp);
 static int	stli_getbrdstats(combrd_t *bp);
 static int	stli_getportstats(stliport_t *portp, comstats_t *cp);
+static int	stli_portcmdstats(stliport_t *portp);
 static int	stli_clrportstats(stliport_t *portp, comstats_t *cp);
 static int	stli_getportstruct(unsigned long arg);
 static int	stli_getbrdstruct(unsigned long arg);
@@ -613,6 +619,13 @@
 
 static stliport_t *stli_getport(int brdnr, int panelnr, int portnr);
 
+static inline int	stli_initbrds(void);
+static inline int	stli_initecp(stlibrd_t *brdp);
+static inline int	stli_initonb(stlibrd_t *brdp);
+static inline int	stli_findeisabrds(void);
+static inline int	stli_eisamemprobe(stlibrd_t *brdp);
+static inline int	stli_initports(stlibrd_t *brdp);
+
 /*****************************************************************************/
 
 /*
@@ -629,8 +642,8 @@
 	NULL,
 	stli_memioctl,
 	NULL,
-	NULL,
-	NULL,
+	stli_memopen,
+	stli_memclose,
 	NULL
 };
 
@@ -690,7 +703,8 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
+		stli_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -707,12 +721,14 @@
 	i = tty_unregister_driver(&stli_serial);
 	j = tty_unregister_driver(&stli_callout);
 	if (i || j) {
-		printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+		printk("STALLION: failed to un-register tty driver, "
+			"errno=%d,%d\n", -i, -j);
 		restore_flags(flags);
 		return;
 	}
 	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
-		printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+		printk("STALLION: failed to un-register serial memory device, "
+			"errno=%d\n", -i);
 
 	if (stli_tmpwritebuf != (char *) NULL)
 		kfree_s(stli_tmpwritebuf, STLI_TXBUFSIZE);
@@ -733,7 +749,9 @@
 		}
 
 		iounmap(brdp->membase);
-		if ((brdp->brdtype == BRD_ECP) || (brdp->brdtype == BRD_ECPE) || (brdp->brdtype == BRD_ECPMC))
+		if ((brdp->brdtype == BRD_ECP) ||
+		    (brdp->brdtype == BRD_ECPE) ||
+		    (brdp->brdtype == BRD_ECPMC))
 			release_region(brdp->iobase, ECP_IOSIZE);
 		else
 			release_region(brdp->iobase, ONB_IOSIZE);
@@ -767,7 +785,8 @@
 	int		brdnr, portnr, rc;
 
 #if DEBUG
-	printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+	printk("stli_open(tty=%x,filp=%x): device=%x\n", (int) tty,
+		(int) filp, tty->device);
 #endif
 
 	minordev = MINOR(tty->device);
@@ -789,6 +808,8 @@
 	if (portp->devnr < 1)
 		return(-ENODEV);
 
+	MOD_INC_USE_COUNT;
+
 /*
  *	Check if this port is in the middle of closing. If so then wait
  *	until it is closed then return error status based on flag settings.
@@ -853,10 +874,10 @@
 			return(-EBUSY);
 		if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
 			if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
-					(portp->session != current->session))
+			    (portp->session != current->session))
 				return(-EBUSY);
 			if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
-					(portp->pgrp != current->pgrp))
+			    (portp->pgrp != current->pgrp))
 				return(-EBUSY);
 		}
 		portp->flags |= ASYNC_CALLOUT_ACTIVE;
@@ -903,10 +924,14 @@
 	save_flags(flags);
 	cli();
 	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
+	if ((tty->count == 1) && (portp->refcount != 1))
+		portp->refcount = 1;
 	if (portp->refcount-- > 1) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -927,10 +952,8 @@
 	if (tty == stli_txcooktty)
 		stli_flushchars(tty);
 	tty->closing = 1;
-	if (test_bit(ST_TXBUSY, &portp->state)) {
-		if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-			tty_wait_until_sent(tty, portp->closing_wait);
-	}
+	if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, portp->closing_wait);
 
 	portp->flags &= ~ASYNC_INITIALIZED;
 	brdp = stli_brds[portp->brdnr];
@@ -940,7 +963,8 @@
 		if (test_bit(ST_CMDING, &portp->state))
 			set_bit(ST_DOSIGS, &portp->state);
 		else
-			stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig,
+				sizeof(asysigs_t), 0);
 	}
 	clear_bit(ST_TXBUSY, &portp->state);
 	clear_bit(ST_RXSTOP, &portp->state);
@@ -951,7 +975,6 @@
 	stli_flushbuffer(tty);
 
 	tty->closing = 0;
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 
 	if (portp->openwaitcnt) {
@@ -960,8 +983,10 @@
 		wake_up_interruptible(&portp->open_wait);
 	}
 
-	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE |
+		ASYNC_CLOSING);
 	wake_up_interruptible(&portp->close_wait);
+	MOD_DEC_USE_COUNT;
 	restore_flags(flags);
 }
 
@@ -992,23 +1017,27 @@
 	memset(&nt, 0, sizeof(asynotify_t));
 	nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
 	nt.signal = SG_DCD;
-	if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
+	    sizeof(asynotify_t), 0)) < 0)
 		return(rc);
 
 	tty = portp->tty;
 	if (tty == (struct tty_struct *) NULL)
 		return(-ENODEV);
 	stli_mkasyport(portp, &aport, tty->termios);
-	if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
+	    sizeof(asyport_t), 0)) < 0)
 		return(rc);
 
 	set_bit(ST_GETSIGS, &portp->state);
-	if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
+	    sizeof(asysigs_t), 1)) < 0)
 		return(rc);
 	if (test_and_clear_bit(ST_GETSIGS, &portp->state))
 		portp->sigs = stli_mktiocm(portp->asig.sigvalue);
 	stli_mkasysigs(&portp->asig, 1, 1);
-	if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+	if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+	    sizeof(asysigs_t), 0)) < 0)
 		return(rc);
 
 	return(0);
@@ -1032,7 +1061,8 @@
 	int			rc;
 
 #if DEBUG
-	printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+	printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
+		(int) brdp, (int) portp, (int) arg, wait);
 #endif
 
 /*
@@ -1065,7 +1095,8 @@
 	cp->openarg = arg;
 	cp->open = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
 
@@ -1111,7 +1142,8 @@
 	int			rc;
 
 #if DEBUG
-	printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait);
+	printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
+		(int) brdp, (int) portp, (int) arg, wait);
 #endif
 
 	save_flags(flags);
@@ -1139,7 +1171,8 @@
 	cp->closearg = arg;
 	cp->close = 1;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	EBRDDISABLE(brdp);
 
@@ -1182,7 +1215,9 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+	printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
+		"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
+		(int) arg, size, copyback);
 #endif
 
 	save_flags(flags);
@@ -1252,12 +1287,13 @@
 static void stli_delay(int len)
 {
 #if DEBUG
-	printk("stl_delay(len=%d)\n", len);
+	printk("stli_delay(len=%d)\n", len);
 #endif
 	if (len > 0) {
 		current->state = TASK_INTERRUPTIBLE;
 		current->timeout = jiffies + len;
 		schedule();
+		current->state = TASK_RUNNING;
 	}
 }
 
@@ -1274,7 +1310,8 @@
 	int		rc, doclocal;
 
 #if DEBUG
-	printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n", (int) brdp, (int) portp, (int) filp);
+	printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n",
+		(int) brdp, (int) portp, (int) filp);
 #endif
 
 	rc = 0;
@@ -1291,16 +1328,18 @@
 	save_flags(flags);
 	cli();
 	portp->openwaitcnt++;
-	if (portp->refcount > 0)
+	if (! tty_hung_up_p(filp))
 		portp->refcount--;
 
 	for (;;) {
 		if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) {
 			stli_mkasysigs(&portp->asig, 1, 1);
-			if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0)
+			if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+			    &portp->asig, sizeof(asysigs_t), 0)) < 0)
 				break;
 		}
-		if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+		if (tty_hung_up_p(filp) ||
+		    ((portp->flags & ASYNC_INITIALIZED) == 0)) {
 			if (portp->flags & ASYNC_HUP_NOTIFY)
 				rc = -EBUSY;
 			else
@@ -1308,8 +1347,8 @@
 			break;
 		}
 		if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
-				((portp->flags & ASYNC_CLOSING) == 0) &&
-				(doclocal || (portp->sigs & TIOCM_CD))) {
+		    ((portp->flags & ASYNC_CLOSING) == 0) &&
+		    (doclocal || (portp->sigs & TIOCM_CD))) {
 			break;
 		}
 		if (signal_pending(current)) {
@@ -1347,10 +1386,12 @@
 	unsigned long		flags;
 
 #if DEBUG
-	printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+	printk("stli_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",
+		(int) tty, from_user, (int) buf, count);
 #endif
 
-	if ((tty == (struct tty_struct *) NULL) || (stli_tmpwritebuf == (char *) NULL))
+	if ((tty == (struct tty_struct *) NULL) ||
+	    (stli_tmpwritebuf == (char *) NULL))
 		return(0);
 	if (tty == stli_txcooktty)
 		stli_flushchars(tty);
@@ -1384,7 +1425,8 @@
 		tail = (unsigned int) ap->txq.tail;
 		if (tail != ((unsigned int) ap->txq.tail))
 			tail = (unsigned int) ap->txq.tail;
-		len = (head >= tail) ? (portp->txsize - (head - tail) - 1) : (tail - head - 1);
+		len = (head >= tail) ? (portp->txsize - (head - tail) - 1) :
+			(tail - head - 1);
 		count = MIN(len, count);
 		EBRDDISABLE(brdp);
 
@@ -1439,7 +1481,8 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
 
@@ -1570,7 +1613,8 @@
 			ap->changed.data &= ~DT_TXEMPTY;
 	}
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_TXBUSY, &portp->state);
 
@@ -1736,12 +1780,14 @@
 	copy_from_user(&sio, sp, sizeof(struct serial_struct));
 	if (!suser()) {
 		if ((sio.baud_base != portp->baud_base) ||
-				(sio.close_delay != portp->close_delay) ||
-				((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+		    (sio.close_delay != portp->close_delay) ||
+		    ((sio.flags & ~ASYNC_USR_MASK) !=
+		    (portp->flags & ~ASYNC_USR_MASK)))
 			return(-EPERM);
 	} 
 
-	portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+	portp->flags = (portp->flags & ~ASYNC_USR_MASK) |
+		(sio.flags & ASYNC_USR_MASK);
 	portp->baud_base = sio.baud_base;
 	portp->close_delay = sio.close_delay;
 	portp->closing_wait = sio.closing_wait;
@@ -1763,7 +1809,8 @@
 	int		rc;
 
 #if DEBUG
-	printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+	printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n",
+		(int) tty, (int) file, cmd, (int) arg);
 #endif
 
 	if (tty == (struct tty_struct *) NULL)
@@ -1778,7 +1825,7 @@
 		return(0);
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- 			(cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+ 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 			return(-EIO);
 	}
@@ -1786,85 +1833,92 @@
 	rc = 0;
 
 	switch (cmd) {
-	case TCSBRK:
-		if ((rc = tty_check_change(tty)) == 0) {
-			tty_wait_until_sent(tty, 0);
-			if (! arg) {
-				lval = 250;
-				rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0);
-			}
-		}
-		break;
-	case TCSBRKP:
-		if ((rc = tty_check_change(tty)) == 0) {
-			tty_wait_until_sent(tty, 0);
-			lval = (arg ? (arg * 100) : 250);
-			rc = stli_cmdwait(brdp, portp, A_BREAK, &lval, sizeof(unsigned long), 0);
-		}
-		break;
 	case TIOCGSOFTCAR:
 		rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
-		    (unsigned int *) arg);
+			(unsigned int *) arg);
 		break;
 	case TIOCSSOFTCAR:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
+			tty->termios->c_cflag =
+				(tty->termios->c_cflag & ~CLOCAL) |
+				(ival ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
-			if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
+			if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS,
+			    &portp->asig, sizeof(asysigs_t), 1)) < 0)
 				return(rc);
 			lval = stli_mktiocm(portp->asig.sigvalue);
 			put_user(lval, (unsigned int *) arg);
 		}
 		break;
 	case TIOCMBIS:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((ival & TIOCM_DTR) ? 1 : -1),
+				((ival & TIOCM_RTS) ? 1 : -1));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMBIC:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((ival & TIOCM_DTR) ? 0 : -1),
+				((ival & TIOCM_RTS) ? 0 : -1));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCMSET:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stli_mkasysigs(&portp->asig, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0));
-			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+			stli_mkasysigs(&portp->asig,
+				((ival & TIOCM_DTR) ? 1 : 0),
+				((ival & TIOCM_RTS) ? 1 : 0));
+			rc = stli_cmdwait(brdp, portp, A_SETSIGNALS,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 		break;
 	case TIOCGSERIAL:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			stli_getserial(portp, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
-			rc = stli_setserial(portp, (struct serial_struct *) arg);
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
+			rc = stli_setserial(portp, (struct serial_struct *)arg);
 		break;
 	case STL_GETPFLAG:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned long))) == 0)
 			put_user(portp->pflag, (unsigned int *) arg);
 		break;
 	case STL_SETPFLAG:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned long))) == 0) {
 			get_user(portp->pflag, (unsigned int *) arg);
 			stli_setport(portp);
 		}
 		break;
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stli_getportstats(portp, (comstats_t *) arg);
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stli_clrportstats(portp, (comstats_t *) arg);
 		break;
 	case TIOCSERCONFIG:
@@ -1912,13 +1966,15 @@
 		return;
 
 	tiosp = tty->termios;
-	if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+	if ((tiosp->c_cflag == old->c_cflag) &&
+	    (tiosp->c_iflag == old->c_iflag))
 		return;
 
 	stli_mkasyport(portp, &aport, tiosp);
 	stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0);
 	stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1);
-	stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+	stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
+		sizeof(asysigs_t), 0);
 	if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0))
 		tty->hw_stopped = 0;
 	if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL))
@@ -2010,7 +2066,7 @@
 	memset(&actrl, 0, sizeof(asyctrl_t));
 	actrl.txctrl = CT_STOPFLOW;
 #if 0
-	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
 #endif
 }
 
@@ -2044,7 +2100,7 @@
 	memset(&actrl, 0, sizeof(asyctrl_t));
 	actrl.txctrl = CT_STARTFLOW;
 #if 0
-	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t));
+	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
 #endif
 }
 
@@ -2118,7 +2174,8 @@
 			set_bit(ST_DOFLUSHTX, &portp->state);
 			set_bit(ST_DOFLUSHRX, &portp->state);
 		} else {
-			stli_sendcmd(brdp, portp, A_SETSIGNALSF, &portp->asig, sizeof(asysigs_t), 0);
+			stli_sendcmd(brdp, portp, A_SETSIGNALSF,
+				&portp->asig, sizeof(asysigs_t), 0);
 		}
 	}
 	restore_flags(flags);
@@ -2126,7 +2183,6 @@
 	clear_bit(ST_TXBUSY, &portp->state);
 	clear_bit(ST_RXSTOP, &portp->state);
 	set_bit(TTY_IO_ERROR, &tty->flags);
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 	portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
 	portp->refcount = 0;
@@ -2178,17 +2234,258 @@
 			ftype |= FLUSHRX;
 			clear_bit(ST_DOFLUSHRX, &portp->state);
 		}
-		stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0);
+		stli_sendcmd(brdp, portp, A_FLUSH, &ftype,
+			sizeof(unsigned long), 0);
 	}
 	restore_flags(flags);
 
 	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 }
 
 /*****************************************************************************/
 
+static void stli_breakctl(struct tty_struct *tty, int state)
+{
+	stlibrd_t	*brdp;
+	stliport_t	*portp;
+	long		arg, savestate, savetime;
+
+#if DEBUG
+	printk("stli_breakctl(tty=%x,state=%d)\n", (int) tty, state);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stliport_t *) NULL)
+		return;
+	if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+		return;
+	brdp = stli_brds[portp->brdnr];
+	if (brdp == (stlibrd_t *) NULL)
+		return;
+
+/*
+ *	Due to a bug in the tty send_break() code we need to preserve
+ *	the current process state and timeout...
+ */
+	savetime = current->timeout;
+	savestate = current->state;
+
+	arg = (state == -1) ? BREAKON : BREAKOFF;
+	stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0);
+
+	current->timeout = savetime;
+	current->state = savestate;
+}
+
+/*****************************************************************************/
+
+static void stli_waituntilsent(struct tty_struct *tty, int timeout)
+{
+	stliport_t	*portp;
+	unsigned long	tend;
+
+#if DEBUG
+	printk("stli_waituntilsent(tty=%x,timeout=%x)\n", (int) tty, timeout);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stliport_t *) NULL)
+		return;
+
+	if (timeout == 0)
+		timeout = HZ;
+	tend = jiffies + timeout;
+
+	while (test_bit(ST_TXBUSY, &portp->state)) {
+		if (signal_pending(current))
+			break;
+		stli_delay(2);
+		if (jiffies >= tend)
+			break;
+	}
+}
+
+/*****************************************************************************/
+
+static void stli_sendxchar(struct tty_struct *tty, char ch)
+{
+	stlibrd_t	*brdp;
+	stliport_t	*portp;
+	asyctrl_t	actrl;
+
+#if DEBUG
+	printk("stli_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stliport_t *) NULL)
+		return;
+	if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+		return;
+	brdp = stli_brds[portp->brdnr];
+	if (brdp == (stlibrd_t *) NULL)
+		return;
+
+	memset(&actrl, 0, sizeof(asyctrl_t));
+	if (ch == STOP_CHAR(tty)) {
+		actrl.rxctrl = CT_STOPFLOW;
+	} else if (ch == START_CHAR(tty)) {
+		actrl.rxctrl = CT_STARTFLOW;
+	} else {
+		actrl.txctrl = CT_SENDCHR;
+		actrl.tximdch = ch;
+	}
+
+	stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
+}
+
+/*****************************************************************************/
+
+#define	MAXLINE		80
+
+/*
+ *	Format info for a specified port. The line is deliberately limited
+ *	to 80 characters. (If it is too long it will be truncated, if too
+ *	short then padded with spaces).
+ */
+
+static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos)
+{
+	char	*sp, *uart;
+	int	rc, cnt;
+
+	rc = stli_portcmdstats(portp);
+
+	uart = "UNKNOWN";
+	if (brdp->state & BST_STARTED) {
+		switch (stli_comstats.hwid) {
+		case 0:		uart = "2681"; break;
+		case 1:		uart = "SC26198"; break;
+		default:	uart = "CD1400"; break;
+		}
+	}
+
+	sp = pos;
+	sp += sprintf(sp, "%d: uart:%s ", portnr, uart);
+
+	if ((brdp->state & BST_STARTED) && (rc >= 0)) {
+		sp += sprintf(sp, "tx:%d rx:%d", (int) stli_comstats.txtotal,
+			(int) stli_comstats.rxtotal);
+
+		if (stli_comstats.rxframing)
+			sp += sprintf(sp, " fe:%d",
+				(int) stli_comstats.rxframing);
+		if (stli_comstats.rxparity)
+			sp += sprintf(sp, " pe:%d",
+				(int) stli_comstats.rxparity);
+		if (stli_comstats.rxbreaks)
+			sp += sprintf(sp, " brk:%d",
+				(int) stli_comstats.rxbreaks);
+		if (stli_comstats.rxoverrun)
+			sp += sprintf(sp, " oe:%d",
+				(int) stli_comstats.rxoverrun);
+
+		cnt = sprintf(sp, "%s%s%s%s%s ",
+			(stli_comstats.signals & TIOCM_RTS) ? "|RTS" : "",
+			(stli_comstats.signals & TIOCM_CTS) ? "|CTS" : "",
+			(stli_comstats.signals & TIOCM_DTR) ? "|DTR" : "",
+			(stli_comstats.signals & TIOCM_CD) ? "|DCD" : "",
+			(stli_comstats.signals & TIOCM_DSR) ? "|DSR" : "");
+		*sp = ' ';
+		sp += cnt;
+	}
+
+	for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++)
+		*sp++ = ' ';
+	if (cnt >= MAXLINE)
+		pos[(MAXLINE - 2)] = '+';
+	pos[(MAXLINE - 1)] = '\n';
+
+	return(MAXLINE);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Port info, read from the /proc file system.
+ */
+
+static int stli_readproc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	stlibrd_t	*brdp;
+	stliport_t	*portp;
+	int		brdnr, portnr, totalport;
+	int		curoff, maxoff;
+	char		*pos;
+
+#if DEBUG
+	printk("stli_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x,"
+		"data=%x\n", (int) page, (int) start, (int) off, count,
+		(int) eof, (int) data);
+#endif
+
+	pos = page;
+	totalport = 0;
+	curoff = 0;
+
+	if (off == 0) {
+		pos += sprintf(pos, "%s: version %s", stli_drvtitle,
+			stli_drvversion);
+		while (pos < (page + MAXLINE - 1))
+			*pos++ = ' ';
+		*pos++ = '\n';
+	}
+	curoff =  MAXLINE;
+
+/*
+ *	We scan through for each board, panel and port. The offset is
+ *	calculated on the fly, and irrelevant ports are skipped.
+ */
+	for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
+		brdp = stli_brds[brdnr];
+		if (brdp == (stlibrd_t *) NULL)
+			continue;
+		if (brdp->state == 0)
+			continue;
+
+		maxoff = curoff + (brdp->nrports * MAXLINE);
+		if (off >= maxoff) {
+			curoff = maxoff;
+			continue;
+		}
+
+		totalport = brdnr * STL_MAXPORTS;
+		for (portnr = 0; (portnr < brdp->nrports); portnr++,
+		    totalport++) {
+			portp = brdp->ports[portnr];
+			if (portp == (stliport_t *) NULL)
+				continue;
+			if (off >= (curoff += MAXLINE))
+				continue;
+			if ((pos - page + MAXLINE) > count)
+				goto stli_readdone;
+			pos += stli_portinfo(brdp, portp, totalport, pos);
+		}
+	}
+
+	*eof = 1;
+
+stli_readdone:
+	*start = page;
+	return(pos - page);
+}
+
+/*****************************************************************************/
+
 /*
  *	Generic send command routine. This will send a message to the slave,
  *	of the specified type with the specified argument. Must be very
@@ -2207,7 +2504,9 @@
 	unsigned long		flags;
 
 #if DEBUG
-	printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
+	printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
+		"copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
+		(int) arg, size, copyback);
 #endif
 
 	save_flags(flags);
@@ -2231,7 +2530,8 @@
 	cp->status = 0;
 	cp->cmd = cmd;
 	hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
-	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx;
+	bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+		portp->portidx;
 	*bits |= portp->portbit;
 	set_bit(ST_CMDING, &portp->state);
 	EBRDDISABLE(brdp);
@@ -2320,7 +2620,8 @@
 	int	cmd;
 
 	if (test_bit(ST_DOSIGS, &portp->state)) {
-		if (test_bit(ST_DOFLUSHTX, &portp->state) && test_bit(ST_DOFLUSHRX, &portp->state))
+		if (test_bit(ST_DOFLUSHTX, &portp->state) &&
+		    test_bit(ST_DOFLUSHRX, &portp->state))
 			cmd = A_SETSIGNALSF;
 		else if (test_bit(ST_DOFLUSHTX, &portp->state))
 			cmd = A_SETSIGNALSFTX;
@@ -2331,11 +2632,13 @@
 		clear_bit(ST_DOFLUSHTX, &portp->state);
 		clear_bit(ST_DOFLUSHRX, &portp->state);
 		clear_bit(ST_DOSIGS, &portp->state);
-		memcpy((void *) &(cp->args[0]), (void *) &portp->asig, sizeof(asysigs_t));
+		memcpy((void *) &(cp->args[0]), (void *) &portp->asig,
+			sizeof(asysigs_t));
 		cp->status = 0;
 		cp->cmd = cmd;
 		set_bit(ST_CMDING, &portp->state);
-	} else if (test_bit(ST_DOFLUSHTX, &portp->state) || test_bit(ST_DOFLUSHRX, &portp->state)) {
+	} else if (test_bit(ST_DOFLUSHTX, &portp->state) ||
+	    test_bit(ST_DOFLUSHRX, &portp->state)) {
 		cmd = ((test_bit(ST_DOFLUSHTX, &portp->state)) ? FLUSHTX : 0);
 		cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
 		clear_bit(ST_DOFLUSHTX, &portp->state);
@@ -2416,7 +2719,8 @@
 			if (rc > 0)
 				rc--;
 			if (portp->argp != (void *) NULL) {
-				memcpy(portp->argp, (void *) &(cp->args[0]), portp->argsize);
+				memcpy(portp->argp, (void *) &(cp->args[0]),
+					portp->argsize);
 				portp->argp = (void *) NULL;
 			}
 			cp->status = 0;
@@ -2443,12 +2747,14 @@
 			oldsigs = portp->sigs;
 			portp->sigs = stli_mktiocm(nt.sigvalue);
 			clear_bit(ST_GETSIGS, &portp->state);
-			if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0))
+			if ((portp->sigs & TIOCM_CD) &&
+			    ((oldsigs & TIOCM_CD) == 0))
 				wake_up_interruptible(&portp->open_wait);
-			if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
+			if ((oldsigs & TIOCM_CD) &&
+			    ((portp->sigs & TIOCM_CD) == 0)) {
 				if (portp->flags & ASYNC_CHECK_CD) {
 					if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
-							(portp->flags & ASYNC_CALLOUT_NOHUP))) {
+					    (portp->flags & ASYNC_CALLOUT_NOHUP))) {
 						if (tty != (struct tty_struct *) NULL)
 							queue_task(&portp->tqhangup, &tq_scheduler);
 					}
@@ -2460,7 +2766,8 @@
 			clear_bit(ST_TXBUSY, &portp->state);
 		if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
 			if (tty != (struct tty_struct *) NULL) {
-				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+				if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+				    tty->ldisc.write_wakeup) {
 					(tty->ldisc.write_wakeup)(tty);
 					EBRDENABLE(brdp);
 				}
@@ -2474,12 +2781,10 @@
 					tty->flip.count++;
 					*tty->flip.flag_buf_ptr++ = TTY_BREAK;
 					*tty->flip.char_buf_ptr++ = 0;
-#ifndef MODULE
 					if (portp->flags & ASYNC_SAK) {
 						do_SAK(tty);
 						EBRDENABLE(brdp);
 					}
-#endif
 					tty_schedule_flip(tty);
 				}
 			}
@@ -2538,7 +2843,8 @@
  *	8 service bits at a time in the inner loop, so we can bypass
  *	the lot if none of them want service.
  */
-	memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset), bitsize);
+	memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset),
+		bitsize);
 
 	memset(&slavebits[0], 0, bitsize);
 	slavebitchange = 0;
@@ -2621,7 +2927,8 @@
 static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
 {
 #if DEBUG
-	printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp);
+	printk("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n",
+		(int) portp, (int) pp, (int) tiosp);
 #endif
 
 	memset(pp, 0, sizeof(asyport_t));
@@ -2632,7 +2939,7 @@
 	pp->baudout = tiosp->c_cflag & CBAUD;
 	if (pp->baudout & CBAUDEX) {
 		pp->baudout &= ~CBAUDEX;
-		if ((pp->baudout < 1) || (pp->baudout > 5))
+		if ((pp->baudout < 1) || (pp->baudout > 4))
 			tiosp->c_cflag &= ~CBAUDEX;
 		else
 			pp->baudout += 15;
@@ -2643,6 +2950,10 @@
 			pp->baudout = 57600;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 			pp->baudout = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			pp->baudout = 230400;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			pp->baudout = 460800;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
 			pp->baudout = (portp->baud_base / portp->custom_divisor);
 	}
@@ -2723,7 +3034,10 @@
 /*
  *	Transfer any persistent flags into the asyport structure.
  */
-	pp->pflag = portp->pflag;
+	pp->pflag = (portp->pflag & 0xffff);
+	pp->vmin = (portp->pflag & P_RXIMIN) ? 1 : 0;
+	pp->vtime = (portp->pflag & P_RXITIME) ? 1 : 0;
+	pp->cc[1] = (portp->pflag & P_RXTHOLD) ? 1 : 0;
 }
 
 /*****************************************************************************/
@@ -2872,11 +3186,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -2959,11 +3276,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+	printk("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n",
+		(int) brdp, (int) offset, line);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3013,7 +3333,9 @@
 	unsigned char	val;
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3088,11 +3410,14 @@
 	void	*ptr;
 
 #if DEBUG
-	printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 	} else {
 		ptr = brdp->membase + (offset % ONB_ATPAGESIZE);
@@ -3175,11 +3500,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line);
+	printk("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n",
+		(int) brdp, (int) offset, line);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3241,11 +3569,14 @@
 	unsigned char	val;
 
 #if DEBUG
-	printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 		val = 0;
 	} else {
@@ -3299,11 +3630,14 @@
 	void	*ptr;
 
 #if DEBUG
-	printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset);
+	printk("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
+		(int) offset);
 #endif
 
 	if (offset > brdp->memsize) {
-		printk("STALLION: shared memory pointer=%x out of range at line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr);
+		printk("STALLION: shared memory pointer=%x out of range at "
+			"line=%d(%d), brd=%d\n", (int) offset, line,
+			__LINE__, brdp->brdnr);
 		ptr = 0;
 	} else {
 		ptr = brdp->membase + (offset % STAL_PAGESIZE);
@@ -3341,6 +3675,7 @@
 	cdkecpsig_t	sig;
 	cdkecpsig_t	*sigsp;
 	unsigned int	status, nxtid;
+	char		*name;
 	int		panelnr, nrports;
 
 #if DEBUG
@@ -3353,10 +3688,9 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
-	if (check_region(brdp->iobase, ECP_IOSIZE)) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
-			brdp->brdnr, brdp->iobase);
-	}
+	if (check_region(brdp->iobase, ECP_IOSIZE))
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->iobase);
 
 /*
  *	Based on the specific board type setup the common vars to access
@@ -3375,6 +3709,7 @@
 		brdp->getmemptr = stli_ecpgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpreset;
+		name = "serial(EC8/64)";
 		break;
 
 	case BRD_ECPE:
@@ -3388,6 +3723,7 @@
 		brdp->getmemptr = stli_ecpeigetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpeireset;
+		name = "serial(EC8/64-EI)";
 		break;
 
 	case BRD_ECPMC:
@@ -3401,6 +3737,7 @@
 		brdp->getmemptr = stli_ecpmcgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_ecpmcreset;
+		name = "serial(EC8/64-MCA)";
 		break;
 
 	default:
@@ -3430,10 +3767,11 @@
 	EBRDDISABLE(brdp);
 
 #if 0
-	printk("%s(%d): sig-> magic=%x romver=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
+	printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
 		__FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0],
-		(int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3],
-		(int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6],
+		(int) sig.panelid[1], (int) sig.panelid[2],
+		(int) sig.panelid[3], (int) sig.panelid[4],
+		(int) sig.panelid[5], (int) sig.panelid[6],
 		(int) sig.panelid[7]);
 #endif
 
@@ -3448,6 +3786,7 @@
 		status = sig.panelid[nxtid];
 		if ((status & ECH_PNLIDMASK) != nxtid)
 			break;
+
 		brdp->panelids[panelnr] = status;
 		nrports = (status & ECH_PNL16PORT) ? 16 : 8;
 		if ((nrports == 16) && ((status & ECH_PNLXPID) == 0))
@@ -3458,7 +3797,7 @@
 		brdp->nrpanels++;
 	}
 
-	request_region(brdp->iobase, ECP_IOSIZE, "serial(ECP)");
+	request_region(brdp->iobase, ECP_IOSIZE, name);
 	brdp->state |= BST_FOUND;
 	return(0);
 }
@@ -3474,6 +3813,7 @@
 {
 	cdkonbsig_t	sig;
 	cdkonbsig_t	*sigsp;
+	char		*name;
 	int		i;
 
 #if DEBUG
@@ -3486,10 +3826,9 @@
 	if ((brdp->iobase == 0) || (brdp->memaddr == 0))
 		return(-ENODEV);
 
-	if (check_region(brdp->iobase, ONB_IOSIZE)) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
-			brdp->brdnr, brdp->iobase);
-	}
+	if (check_region(brdp->iobase, ONB_IOSIZE))
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->iobase);
 
 /*
  *	Based on the specific board type setup the common vars to access
@@ -3516,6 +3855,7 @@
 			brdp->enabval = ONB_MEMENABHI;
 		else
 			brdp->enabval = ONB_MEMENABLO;
+		name = "serial(ONBoard)";
 		break;
 
 	case BRD_ONBOARDE:
@@ -3529,6 +3869,7 @@
 		brdp->getmemptr = stli_onbegetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_onbereset;
+		name = "serial(ONBoard/E)";
 		break;
 
 	case BRD_BRUMBY4:
@@ -3544,6 +3885,7 @@
 		brdp->getmemptr = stli_bbygetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_bbyreset;
+		name = "serial(Brumby)";
 		break;
 
 	case BRD_STALLION:
@@ -3557,6 +3899,7 @@
 		brdp->getmemptr = stli_stalgetmemptr;
 		brdp->intr = stli_ecpintr;
 		brdp->reset = stli_stalreset;
+		name = "serial(Stallion)";
 		break;
 
 	default:
@@ -3592,7 +3935,7 @@
 #endif
 
 	if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
-			(sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
+	    (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
 		return(-ENODEV);
 
 /*
@@ -3611,7 +3954,7 @@
 	}
 	brdp->panels[0] = brdp->nrports;
 
-	request_region(brdp->iobase, ONB_IOSIZE, "serial(ONB/BBY)");
+	request_region(brdp->iobase, ONB_IOSIZE, name);
 	brdp->state |= BST_FOUND;
 	return(0);
 }
@@ -3646,14 +3989,16 @@
 	nrdevs = hdrp->nrdevs;
 
 #if 0
-	printk("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x slavep=%x\n",
+	printk("%s(%d): CDK version %d.%d.%d --> "
+		"nrdevs=%d memp=%x hostp=%x slavep=%x\n",
 		 __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification,
 		 hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp,
 		 (int) hdrp->slavep);
 #endif
 
 	if (nrdevs < (brdp->nrports + 1)) {
-		printk("STALLION: slave failed to allocate memory for all devices, devices=%d\n", nrdevs);
+		printk("STALLION: slave failed to allocate memory for all "
+			"devices, devices=%d\n", nrdevs);
 		brdp->nrports = nrdevs - 1;
 	}
 	brdp->nrdevs = nrdevs;
@@ -3765,20 +4110,27 @@
 	case BRD_ECH:
 	case BRD_ECHMC:
 	case BRD_ECHPCI:
-		printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
+		printk("STALLION: %s board type not supported in this driver\n",
+			stli_brdnames[brdp->brdtype]);
 		return(ENODEV);
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+		printk("STALLION: unit=%d is unknown board type=%d\n",
+			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	if ((brdp->state & BST_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr);
+		printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n",
+			stli_brdnames[brdp->brdtype], brdp->brdnr,
+			brdp->iobase, (int) brdp->memaddr);
 		return(ENODEV);
 	}
 
 	stli_initports(brdp);
-	printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports);
+	printk("STALLION: %s found, unit=%d io=%x mem=%x "
+		"nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
+		brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
+		brdp->nrpanels, brdp->nrports);
 	return(0);
 }
 
@@ -3789,7 +4141,7 @@
  *	might be. This is a bit if hack, but it is the best we can do.
  */
 
-__initfunc(static int stli_eisamemprobe(stlibrd_t *brdp))
+static inline int stli_eisamemprobe(stlibrd_t *brdp)
 {
 	cdkecpsig_t	ecpsig, *ecpsigp;
 	cdkonbsig_t	onbsig, *onbsigp;
@@ -3841,15 +4193,19 @@
 			continue;
 
 		if (brdp->brdtype == BRD_ECPE) {
-			ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__);
+			ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp,
+				CDK_SIGADDR, __LINE__);
 			memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
 			if (ecpsig.magic == ECP_MAGIC)
 				foundit = 1;
 		} else {
-			onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__);
+			onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp,
+				CDK_SIGADDR, __LINE__);
 			memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t));
-			if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) &&
-					(onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3))
+			if ((onbsig.magic0 == ONB_MAGIC0) &&
+			    (onbsig.magic1 == ONB_MAGIC1) &&
+			    (onbsig.magic2 == ONB_MAGIC2) &&
+			    (onbsig.magic3 == ONB_MAGIC3))
 				foundit = 1;
 		}
 
@@ -3870,7 +4226,9 @@
 	if (! foundit) {
 		brdp->memaddr = 0;
 		brdp->membase = 0;
-		printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
+		printk("STALLION: failed to probe shared memory region for "
+			"%s in EISA slot=%d\n", stli_brdnames[brdp->brdtype],
+			(brdp->iobase >> 12));
 		return(-ENODEV);
 	}
 	return(0);
@@ -3936,7 +4294,8 @@
  *		info table.
  */
 		if (stli_nrbrds >= STL_MAXBRDS) {
-			printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS);
+			printk("STALLION: no room for more probed boards, "
+				"maximum supported %d\n", STL_MAXBRDS);
 			break;
 		}
 
@@ -3946,7 +4305,8 @@
  */
 		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
 		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlibrd_t));
 			return(-ENOMEM);
 		}
 		memset(brdp, 0, sizeof(stlibrd_t));
@@ -3988,7 +4348,8 @@
 #endif
 
 	if (stli_nrbrds > STL_MAXBRDS) {
-		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+		printk("STALLION: too many boards in configuration table, "
+			"truncating to %d\n", STL_MAXBRDS);
 		stli_nrbrds = STL_MAXBRDS;
 	}
 
@@ -4000,7 +4361,8 @@
 		confp = &stli_brdconf[i];
 		brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
 		if (brdp == (stlibrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlibrd_t));
 			return(-ENOMEM);
 		}
 		memset(brdp, 0, sizeof(stlibrd_t));
@@ -4034,7 +4396,9 @@
 				nxtbrdp = stli_brds[j];
 				if (nxtbrdp == (stlibrd_t *) NULL)
 					continue;
-				if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) {
+				if ((brdp->membase >= nxtbrdp->membase) &&
+				    (brdp->membase <= (nxtbrdp->membase +
+				    nxtbrdp->memsize - 1))) {
 					stli_shared++;
 					break;
 				}
@@ -4066,7 +4430,7 @@
  *	the slave image (and debugging :-)
  */
 
-static long stli_memread(struct inode *ip, struct file *fp, char *buf, unsigned long count)
+static ssize_t stli_memread(struct file *fp, char *buf, size_t count, loff_t *offp)
 {
 	unsigned long	flags;
 	void		*memptr;
@@ -4074,10 +4438,11 @@
 	int		brdnr, size, n;
 
 #if DEBUG
-	printk("stli_memread(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count);
+	printk("stli_memread(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp,
+		(int) buf, count, (int) offp);
 #endif
 
-	brdnr = MINOR(ip->i_rdev);
+	brdnr = MINOR(fp->f_dentry->d_inode->i_rdev);
 	if (brdnr >= stli_nrbrds)
 		return(-ENODEV);
 	brdp = stli_brds[brdnr];
@@ -4115,7 +4480,7 @@
  *	the slave image (and debugging :-)
  */
 
-static long stli_memwrite(struct inode *ip, struct file *fp, const char *buf, unsigned long count)
+static ssize_t stli_memwrite(struct file *fp, const char *buf, size_t count, loff_t *offp)
 {
 	unsigned long	flags;
 	void		*memptr;
@@ -4124,10 +4489,11 @@
 	int		brdnr, size, n;
 
 #if DEBUG
-	printk("stli_memwrite(ip=%x,fp=%x,buf=%x,count=%lu)\n", (int) ip, (int) fp, (int) buf, count);
+	printk("stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x)\n", (int) fp,
+		(int) buf, count, (int) offp);
 #endif
 
-	brdnr = MINOR(ip->i_rdev);
+	brdnr = MINOR(fp->f_dentry->d_inode->i_rdev);
 	if (brdnr >= stli_nrbrds)
 		return(-ENODEV);
 	brdp = stli_brds[brdnr];
@@ -4226,31 +4592,28 @@
  *	what port to get stats for (used through board control device).
  */
 
-static int stli_getportstats(stliport_t *portp, comstats_t *cp)
+static int stli_portcmdstats(stliport_t *portp)
 {
 	unsigned long	flags;
 	stlibrd_t	*brdp;
 	int		rc;
 
-	if (portp == (stliport_t *) NULL) {
-		copy_from_user(&stli_comstats, cp, sizeof(comstats_t));
-		portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port);
-		if (portp == (stliport_t *) NULL)
-			return(-ENODEV);
-	}
+	memset(&stli_comstats, 0, sizeof(comstats_t));
 
+	if (portp == (stliport_t *) NULL)
+		return(-ENODEV);
 	brdp = stli_brds[portp->brdnr];
 	if (brdp == (stlibrd_t *) NULL)
 		return(-ENODEV);
 
 	if (brdp->state & BST_STARTED) {
-		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
+		if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
+		    &stli_cdkstats, sizeof(asystats_t), 1)) < 0)
 			return(rc);
 	} else {
 		memset(&stli_cdkstats, 0, sizeof(asystats_t));
 	}
 
-	memset(&stli_comstats, 0, sizeof(comstats_t));
 	stli_comstats.brd = portp->brdnr;
 	stli_comstats.panel = portp->panelnr;
 	stli_comstats.port = portp->portnr;
@@ -4293,6 +4656,37 @@
 	stli_comstats.hwid = stli_cdkstats.hwid;
 	stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
 
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Return the port stats structure to user app. A NULL port struct
+ *	pointer passed in means that we need to find out from the app
+ *	what port to get stats for (used through board control device).
+ */
+
+static int stli_getportstats(stliport_t *portp, comstats_t *cp)
+{
+	stlibrd_t	*brdp;
+	int		rc;
+
+	if (portp == (stliport_t *) NULL) {
+		copy_from_user(&stli_comstats, cp, sizeof(comstats_t));
+		portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+			stli_comstats.port);
+		if (portp == (stliport_t *) NULL)
+			return(-ENODEV);
+	}
+
+	brdp = stli_brds[portp->brdnr];
+	if (brdp == (stlibrd_t *) NULL)
+		return(-ENODEV);
+
+	if ((rc = stli_portcmdstats(portp)) < 0)
+		return(rc);
+
 	copy_to_user(cp, &stli_comstats, sizeof(comstats_t));
 	return(0);
 }
@@ -4310,7 +4704,8 @@
 
 	if (portp == (stliport_t *) NULL) {
 		copy_from_user(&stli_comstats, cp, sizeof(comstats_t));
-		portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port);
+		portp = stli_getport(stli_comstats.brd, stli_comstats.panel,
+			stli_comstats.port);
 		if (portp == (stliport_t *) NULL)
 			return(-ENODEV);
 	}
@@ -4375,6 +4770,27 @@
 /*****************************************************************************/
 
 /*
+ *	Memory device open code. Need to keep track of opens and close
+ *	for module handling.
+ */
+
+static int stli_memopen(struct inode *ip, struct file *fp)
+{
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+static int stli_memclose(struct inode *ip, struct file *fp)
+{
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
  *	The "staliomem" device is also required to do some special operations on
  *	the board. We need to be able to send an interrupt to the board,
  *	reset it, and start/stop it.
@@ -4386,7 +4802,8 @@
 	int		brdnr, rc, done;
 
 #if DEBUG
-	printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+	printk("stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip,
+		(int) fp, cmd, (int) arg);
 #endif
 
 /*
@@ -4397,27 +4814,34 @@
 
 	switch (cmd) {
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stli_getportstats((stliport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stli_getportstats((stliport_t *) NULL,
+				(comstats_t *) arg);
 		done++;
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stli_clrportstats((stliport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stli_clrportstats((stliport_t *) NULL,
+				(comstats_t *) arg);
 		done++;
 		break;
 	case COM_GETBRDSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(combrd_t))) == 0)
 			rc = stli_getbrdstats((combrd_t *) arg);
 		done++;
 		break;
 	case COM_READPORT:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stliport_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stliport_t))) == 0)
 			rc = stli_getportstruct(arg);
 		done++;
 		break;
 	case COM_READBOARD:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlibrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlibrd_t))) == 0)
 			rc = stli_getbrdstruct(arg);
 		done++;
 		break;
@@ -4469,9 +4893,9 @@
 
 /*****************************************************************************/
 
-__initfunc(int stli_init())
+__initfunc(int stli_init(void))
 {
-	printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion);
 
 	stli_initbrds();
 
@@ -4480,10 +4904,12 @@
  */
 	stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
 	if (stli_tmpwritebuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STLI_TXBUFSIZE);
 	stli_txcookbuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
 	if (stli_txcookbuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STLI_TXBUFSIZE);
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STLI_TXBUFSIZE);
 
 /*
  *	Set up a character driver for the shared memory region. We need this
@@ -4498,6 +4924,7 @@
  */
 	memset(&stli_serial, 0, sizeof(struct tty_driver));
 	stli_serial.magic = TTY_DRIVER_MAGIC;
+	stli_serial.driver_name = stli_drvname;
 	stli_serial.name = stli_serialname;
 	stli_serial.major = STL_SERIALMAJOR;
 	stli_serial.minor_start = 0;
@@ -4526,11 +4953,16 @@
 	stli_serial.start = stli_start;
 	stli_serial.hangup = stli_hangup;
 	stli_serial.flush_buffer = stli_flushbuffer;
+	stli_serial.break_ctl = stli_breakctl;
+	stli_serial.wait_until_sent = stli_waituntilsent;
+	stli_serial.send_xchar = stli_sendxchar;
+	stli_serial.read_proc = stli_readproc;
 
 	stli_callout = stli_serial;
 	stli_callout.name = stli_calloutname;
 	stli_callout.major = STL_CALLOUTMAJOR;
 	stli_callout.subtype = STL_DRVTYPCALLOUT;
+	stli_callout.read_proc = 0;
 
 	if (tty_register_driver(&stli_serial))
 		printk("STALLION: failed to register serial driver\n");
diff -u --recursive --new-file v2.1.73/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v2.1.73/linux/drivers/char/stallion.c	Wed Dec 10 11:12:43 1997
+++ linux/drivers/char/stallion.c	Fri Dec 19 12:30:54 1997
@@ -66,6 +66,8 @@
 #define	BRD_ECH		21
 #define	BRD_ECHMC	22
 #define	BRD_ECHPCI	26
+#define	BRD_ECH64PCI	27
+#define	BRD_EASYIOPCI	28
 
 /*
  *	Define a configuration structure to hold the board configuration.
@@ -101,34 +103,11 @@
 	int		irqtype;
 } stlconf_t;
 
-/*static stlconf_t	stl_brdconf[] = {
-	{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
-};*/
-
-#ifdef MODULE
-static char *brdtype[STL_MAXBRDS] = {"\0", };
-static int io[STL_MAXBRDS] = { 0, };
-static int secio[STL_MAXBRDS] = { 0, };
-static int irq[STL_MAXBRDS] = { 0, };
-
-MODULE_PARM(brdtype, "1-" __MODULE_STRING(STL_MAXBRDS) "s");
-MODULE_PARM(io, "1-" __MODULE_STRING(STL_MAXBRDS) "i");
-MODULE_PARM(secio, "1-" __MODULE_STRING(STL_MAXBRDS) "i");
-MODULE_PARM(irq, "1-" __MODULE_STRING(STL_MAXBRDS) "i");
-
-static stlconf_t	stl_brdconf[STL_MAXBRDS];
-static int	stl_nrbrds = 0;
-
-#else
 static stlconf_t	stl_brdconf[] = {
 	{ BRD_EASYIO, 0x2a0, 0, 0, 10, 0 },
-	{ BRD_EASYIO, 0x2a8, 0, 0, 10, 0 },
-	{ BRD_EASYIO, 0x2b0, 0, 0, 10, 0 },
-	{ BRD_ECH, 0x2a0, 0x240, 0, 10, 0 },
 };
 
 static int	stl_nrbrds = sizeof(stl_brdconf) / sizeof(stlconf_t);
-#endif
 
 /*****************************************************************************/
 
@@ -162,8 +141,9 @@
  *	Define our local driver identity first. Set up stuff to deal with
  *	all the local structures required by a serial tty driver.
  */
-static char	*stl_drvname = "Stallion Multiport Serial Driver";
-static char	*stl_drvversion = "5.3.4";
+static char	*stl_drvtitle = "Stallion Multiport Serial Driver";
+static char	*stl_drvname = "stallion";
+static char	*stl_drvversion = "5.4.1";
 static char	*stl_serialname = "ttyE";
 static char	*stl_calloutname = "cue";
 
@@ -272,6 +252,8 @@
 	(char *) NULL,
 	(char *) NULL,
 	"EC8/32-PCI",
+	"EC8/64-PCI",
+	"EasyIO-PCI",
 };
 
 /*****************************************************************************/
@@ -320,12 +302,6 @@
 #define	ECH_ADDR2MASK	0x1e0
 
 /*
- *	Define real Stallion PCI vemdor and device ID.
- */
-#define	PCI_VENDOR_ID_STALLION	0x124d
-#define	PCI_DEVICE_ID_ECHPCI832	0x0000
-
-/*
  *	Define the vector mapping bits for the programmable interrupt board
  *	hardware. These bits encode the interrupt for the board to use - it
  *	is software selectable (except the EIO-8M).
@@ -354,9 +330,47 @@
 		outb((stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE),	\
 			stl_brds[(brdnr)]->ioctrl);
 
-#define	STL_MAXBAUD	921600
-#define	STL_BAUDBASE	115200
-#define	STL_CLOSEDELAY	50
+#define	STL_CD1400MAXBAUD	230400
+#define	STL_SC26198MAXBAUD	460800
+
+#define	STL_BAUDBASE		115200
+#define	STL_CLOSEDELAY		(5 * HZ / 10)
+
+/*****************************************************************************/
+
+/*
+ *	Define the Stallion PCI vendor and device IDs.
+ */
+#ifndef	PCI_VENDOR_ID_STALLION
+#define	PCI_VENDOR_ID_STALLION		0x124d
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI832
+#define	PCI_DEVICE_ID_ECHPCI832		0x0000
+#endif
+#ifndef PCI_DEVICE_ID_ECHPCI864
+#define	PCI_DEVICE_ID_ECHPCI864		0x0002
+#endif
+#ifndef PCI_DEVICE_ID_EIOPCI
+#define	PCI_DEVICE_ID_EIOPCI		0x0003
+#endif
+
+/*
+ *	Define structure to hold all Stallion PCI boards.
+ */
+typedef struct stlpcibrd {
+	unsigned short		vendid;
+	unsigned short		devid;
+	int			brdtype;
+} stlpcibrd_t;
+
+static stlpcibrd_t	stl_pcibrds[] = {
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI864, BRD_ECH64PCI },
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_EIOPCI, BRD_EASYIOPCI },
+	{ PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, BRD_ECHPCI },
+	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, BRD_ECHPCI },
+};
+
+static int	stl_nrpcibrds = sizeof(stl_pcibrds) / sizeof(stlpcibrd_t);
 
 /*****************************************************************************/
 
@@ -409,15 +423,19 @@
 static void	stl_stop(struct tty_struct *tty);
 static void	stl_start(struct tty_struct *tty);
 static void	stl_flushbuffer(struct tty_struct *tty);
+static void	stl_breakctl(struct tty_struct *tty, int state);
+static void	stl_waituntilsent(struct tty_struct *tty, int timeout);
+static void	stl_sendxchar(struct tty_struct *tty, char ch);
 static void	stl_hangup(struct tty_struct *tty);
+static int	stl_memopen(struct inode *ip, struct file *fp);
+static int	stl_memclose(struct inode *ip, struct file *fp);
 static int	stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static int	stl_portinfo(stlport_t *portp, int portnr, char *pos);
+static int	stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data);
 
-static inline int stl_initbrds(void);
-static inline int stl_initeio(stlbrd_t *brdp);
-static inline int stl_initech(stlbrd_t *brdp);
 static int	stl_brdinit(stlbrd_t *brdp);
 static int	stl_initports(stlbrd_t *brdp, stlpanel_t *panelp);
-static int	stl_mapirq(int irq);
+static int	stl_mapirq(int irq, char *name);
 static void	stl_getserial(stlport_t *portp, struct serial_struct *sp);
 static int	stl_setserial(stlport_t *portp, struct serial_struct *sp);
 static int	stl_getbrdstats(combrd_t *bp);
@@ -432,12 +450,18 @@
 static void	stl_echatintr(stlbrd_t *brdp);
 static void	stl_echmcaintr(stlbrd_t *brdp);
 static void	stl_echpciintr(stlbrd_t *brdp);
+static void	stl_echpci64intr(stlbrd_t *brdp);
 static void	stl_offintr(void *private);
 static void	*stl_memalloc(int len);
 static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
 
+static inline int	stl_initbrds(void);
+static inline int	stl_initeio(stlbrd_t *brdp);
+static inline int	stl_initech(stlbrd_t *brdp);
+
 #ifdef	CONFIG_PCI
-static inline int stl_findpcibrds(void);
+static inline int	stl_findpcibrds(void);
+static inline int	stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr);
 #endif
 
 /*
@@ -455,15 +479,19 @@
 static void	stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx);
 static void	stl_cd1400startrxtx(stlport_t *portp, int rx, int tx);
 static void	stl_cd1400disableintrs(stlport_t *portp);
-static void	stl_cd1400sendbreak(stlport_t *portp, long len);
+static void	stl_cd1400sendbreak(stlport_t *portp, int len);
 static void	stl_cd1400flowctrl(stlport_t *portp, int state);
+static void	stl_cd1400sendflow(stlport_t *portp, int state);
 static void	stl_cd1400flush(stlport_t *portp);
+static int	stl_cd1400datastate(stlport_t *portp);
 static void	stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase);
 static void	stl_cd1400echintr(stlpanel_t *panelp, unsigned int iobase);
 static void	stl_cd1400txisr(stlpanel_t *panelp, int ioaddr);
 static void	stl_cd1400rxisr(stlpanel_t *panelp, int ioaddr);
 static void	stl_cd1400mdmisr(stlpanel_t *panelp, int ioaddr);
 
+static inline int	stl_cd1400breakisr(stlport_t *portp, int ioaddr);
+
 /*
  *	SC26198 uart specific handling functions.
  */
@@ -479,9 +507,12 @@
 static void	stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx);
 static void	stl_sc26198startrxtx(stlport_t *portp, int rx, int tx);
 static void	stl_sc26198disableintrs(stlport_t *portp);
-static void	stl_sc26198sendbreak(stlport_t *portp, long len);
+static void	stl_sc26198sendbreak(stlport_t *portp, int len);
 static void	stl_sc26198flowctrl(stlport_t *portp, int state);
+static void	stl_sc26198sendflow(stlport_t *portp, int state);
 static void	stl_sc26198flush(stlport_t *portp);
+static int	stl_sc26198datastate(stlport_t *portp);
+static void	stl_sc26198wait(stlport_t *portp);
 static void	stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty);
 static void	stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase);
 static void	stl_sc26198txisr(stlport_t *port);
@@ -504,9 +535,11 @@
 	void	(*enablerxtx)(stlport_t *portp, int rx, int tx);
 	void	(*startrxtx)(stlport_t *portp, int rx, int tx);
 	void	(*disableintrs)(stlport_t *portp);
-	void	(*sendbreak)(stlport_t *portp, long len);
+	void	(*sendbreak)(stlport_t *portp, int len);
 	void	(*flowctrl)(stlport_t *portp, int state);
+	void	(*sendflow)(stlport_t *portp, int state);
 	void	(*flush)(stlport_t *portp);
+	int	(*datastate)(stlport_t *portp);
 	void	(*intr)(stlpanel_t *panelp, unsigned int iobase);
 } uart_t;
 
@@ -523,7 +556,9 @@
 #define	stl_disableintrs	(* ((uart_t *) portp->uartp)->disableintrs)
 #define	stl_sendbreak		(* ((uart_t *) portp->uartp)->sendbreak)
 #define	stl_flowctrl		(* ((uart_t *) portp->uartp)->flowctrl)
+#define	stl_sendflow		(* ((uart_t *) portp->uartp)->sendflow)
 #define	stl_flush		(* ((uart_t *) portp->uartp)->flush)
+#define	stl_datastate		(* ((uart_t *) portp->uartp)->datastate)
 
 /*****************************************************************************/
 
@@ -541,7 +576,9 @@
 	stl_cd1400disableintrs,
 	stl_cd1400sendbreak,
 	stl_cd1400flowctrl,
+	stl_cd1400sendflow,
 	stl_cd1400flush,
+	stl_cd1400datastate,
 	stl_cd1400eiointr
 };
 
@@ -585,7 +622,9 @@
 	stl_sc26198disableintrs,
 	stl_sc26198sendbreak,
 	stl_sc26198flowctrl,
+	stl_sc26198sendflow,
 	stl_sc26198flush,
+	stl_sc26198datastate,
 	stl_sc26198intr
 };
 
@@ -626,8 +665,8 @@
 	NULL,
 	stl_memioctl,
 	NULL,
-	NULL,
-	NULL,
+	stl_memopen,
+	stl_memclose,
 	NULL
 };
 
@@ -639,22 +678,9 @@
  *	Loadable module initialization stuff.
  */
 
-struct board_type_elem
-{
-	int type_id;
-	char *name;
-};
-
-struct board_type_elem board_types[] = {
-	{ BRD_EASYIO, "easyio" },
-	{ BRD_ECH, "ech" },
-	{ BRD_ECHMC, "echmc" },
-	{ BRD_ECHPCI, "echpci" } };
-
 int init_module()
 {
 	unsigned long	flags;
-	int i, j, num_board_types;
 
 #if DEBUG
 	printk("init_module()\n");
@@ -662,25 +688,6 @@
 
 	save_flags(flags);
 	cli();
-
-	num_board_types = sizeof(board_types) / sizeof(struct board_type_elem);
-	for (i = 0; (i < STL_MAXBRDS && io[i]); i++)
-	{
-		stl_brdconf[stl_nrbrds].brdtype = 0;
-		for (j = 0; j < num_board_types; j++)
-			if(strcmp(board_types[j].name, brdtype[i]) == 0)
-				stl_brdconf[stl_nrbrds].brdtype = board_types[j].type_id;
-		if(stl_brdconf[stl_nrbrds].brdtype != 0)
-		{
-			stl_brdconf[stl_nrbrds].ioaddr1 = io[i];
-			stl_brdconf[stl_nrbrds].ioaddr2 = secio[i];
-			stl_brdconf[stl_nrbrds].memaddr = 0;
-			stl_brdconf[stl_nrbrds].irq = irq[i];
-			stl_brdconf[stl_nrbrds].irqtype = 0;
-			stl_nrbrds++;
-		}
-	}
-
 	stl_init();
 	restore_flags(flags);
 
@@ -701,7 +708,8 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle,
+		stl_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -715,12 +723,14 @@
 	i = tty_unregister_driver(&stl_serial);
 	j = tty_unregister_driver(&stl_callout);
 	if (i || j) {
-		printk("STALLION: failed to un-register tty driver, errno=%d,%d\n", -i, -j);
+		printk("STALLION: failed to un-register tty driver, "
+			"errno=%d,%d\n", -i, -j);
 		restore_flags(flags);
 		return;
 	}
 	if ((i = unregister_chrdev(STL_SIOMEMMAJOR, "staliomem")))
-		printk("STALLION: failed to un-register serial memory device, errno=%d\n", -i);
+		printk("STALLION: failed to un-register serial memory device, "
+			"errno=%d\n", -i);
 
 	if (stl_tmpwritebuf != (char *) NULL)
 		kfree_s(stl_tmpwritebuf, STL_TXBUFSIZE);
@@ -729,33 +739,24 @@
 		brdp = stl_brds[i];
 		for (j = 0; (j < STL_MAXPANELS); j++) {
 			panelp = brdp->panels[j];
-			if (panelp != (stlpanel_t *) NULL) {
-				for (k = 0; (k < STL_PORTSPERPANEL); k++) {
-					portp = panelp->ports[k];
-					if (portp != (stlport_t *) NULL) {
-						if (portp->tty != (struct tty_struct *) NULL)
-							stl_hangup(portp->tty);
-						if (portp->tx.buf != (char *) NULL)
-							kfree_s(portp->tx.buf, STL_TXBUFSIZE);
-						kfree_s(portp, sizeof(stlport_t));
-					}
-				}
-				kfree_s(panelp, sizeof(stlpanel_t));
+			if (panelp == (stlpanel_t *) NULL)
+				continue;
+			for (k = 0; (k < STL_PORTSPERPANEL); k++) {
+				portp = panelp->ports[k];
+				if (portp == (stlport_t *) NULL)
+					continue;
+				if (portp->tty != (struct tty_struct *) NULL)
+					stl_hangup(portp->tty);
+				if (portp->tx.buf != (char *) NULL)
+					kfree_s(portp->tx.buf, STL_TXBUFSIZE);
+				kfree_s(portp, sizeof(stlport_t));
 			}
-			
+			kfree_s(panelp, sizeof(stlpanel_t));
 		}
 
-		if (brdp->brdtype == BRD_ECH) {
-			release_region(brdp->ioaddr1, 2);
-			release_region(brdp->ioaddr2, 32);
-		} else if (brdp->brdtype == BRD_ECHPCI) {
-			release_region(brdp->ioaddr1, 4);
-			release_region(brdp->ioaddr2, 8);
-		} else if (brdp->brdtype == BRD_ECHMC) {
-			release_region(brdp->ioaddr1, 64);
-		} else if (brdp->brdtype == BRD_EASYIO) {
-			release_region(brdp->ioaddr1, 8);
-		}
+		release_region(brdp->ioaddr1, brdp->iosize1);
+		if (brdp->iosize2 > 0)
+			release_region(brdp->ioaddr2, brdp->iosize2);
 
 		kfree_s(brdp, sizeof(stlbrd_t));
 		stl_brds[i] = (stlbrd_t *) NULL;
@@ -790,7 +791,8 @@
 	int		brdnr, panelnr, portnr, rc;
 
 #if DEBUG
-	printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty, (int) filp, tty->device);
+	printk("stl_open(tty=%x,filp=%x): device=%x\n", (int) tty,
+		(int) filp, tty->device);
 #endif
 
 	minordev = MINOR(tty->device);
@@ -817,6 +819,8 @@
 	if (portp == (stlport_t *) NULL)
 		return(-ENODEV);
 
+	MOD_INC_USE_COUNT;
+
 /*
  *	On the first open of the device setup the port hardware, and
  *	initialize the per port data structure.
@@ -865,10 +869,10 @@
 			return(-EBUSY);
 		if (portp->flags & ASYNC_CALLOUT_ACTIVE) {
 			if ((portp->flags & ASYNC_SESSION_LOCKOUT) &&
-					(portp->session != current->session))
+			    (portp->session != current->session))
 				return(-EBUSY);
 			if ((portp->flags & ASYNC_PGRP_LOCKOUT) &&
-					(portp->pgrp != current->pgrp))
+			    (portp->pgrp != current->pgrp))
 				return(-EBUSY);
 		}
 		portp->flags |= ASYNC_CALLOUT_ACTIVE;
@@ -878,10 +882,7 @@
 				return(-EBUSY);
 		} else {
 			if ((rc = stl_waitcarrier(portp, filp)) != 0)
-			{
-				MOD_INC_USE_COUNT;
 				return(rc);
-			}
 		}
 		portp->flags |= ASYNC_NORMAL_ACTIVE;
 	}
@@ -896,7 +897,6 @@
 
 	portp->session = current->session;
 	portp->pgrp = current->pgrp;
-	MOD_INC_USE_COUNT;
 	return(0);
 }
 
@@ -930,13 +930,14 @@
 	save_flags(flags);
 	cli();
 	portp->openwaitcnt++;
-	if (portp->refcount > 0)
+	if (! tty_hung_up_p(filp))
 		portp->refcount--;
 
 	for (;;) {
 		if ((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0)
 			stl_setsignals(portp, 1, 1);
-		if (tty_hung_up_p(filp) || ((portp->flags & ASYNC_INITIALIZED) == 0)) {
+		if (tty_hung_up_p(filp) ||
+		    ((portp->flags & ASYNC_INITIALIZED) == 0)) {
 			if (portp->flags & ASYNC_HUP_NOTIFY)
 				rc = -EBUSY;
 			else
@@ -944,8 +945,8 @@
 			break;
 		}
 		if (((portp->flags & ASYNC_CALLOUT_ACTIVE) == 0) &&
-				((portp->flags & ASYNC_CLOSING) == 0) &&
-				(doclocal || (portp->sigs & TIOCM_CD))) {
+		    ((portp->flags & ASYNC_CLOSING) == 0) &&
+		    (doclocal || (portp->sigs & TIOCM_CD))) {
 			break;
 		}
 		if (signal_pending(current)) {
@@ -981,11 +982,14 @@
 	save_flags(flags);
 	cli();
 	if (tty_hung_up_p(filp)) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
-	MOD_DEC_USE_COUNT;
+	if ((tty->count == 1) && (portp->refcount != 1))
+		portp->refcount = 1;
 	if (portp->refcount-- > 1) {
+		MOD_DEC_USE_COUNT;
 		restore_flags(flags);
 		return;
 	}
@@ -1000,14 +1004,14 @@
 
 /*
  *	May want to wait for any data to drain before closing. The BUSY
- *	flag keeps track of whether we are still sending or not - it allows
- *	for the FIFO in the cd1400.
+ *	flag keeps track of whether we are still sending or not - it is
+ *	very accurate for the cd1400, not quite so for the sc26198.
+ *	(The sc26198 has no "end-of-data" interrupt only empty FIFO)
  */
 	tty->closing = 1;
-	if (test_bit(ASYI_TXBUSY, &portp->istate)) {
-		if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-			tty_wait_until_sent(tty, portp->closing_wait);
-	}
+	if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, portp->closing_wait);
+	stl_waituntilsent(tty, (HZ / 2));
 
 	portp->flags &= ~ASYNC_INITIALIZED;
 	stl_disableintrs(portp);
@@ -1027,7 +1031,6 @@
 		(tty->ldisc.flush_buffer)(tty);
 
 	tty->closing = 0;
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 
 	if (portp->openwaitcnt) {
@@ -1036,8 +1039,10 @@
 		wake_up_interruptible(&portp->open_wait);
 	}
 
-	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
+	portp->flags &= ~(ASYNC_CALLOUT_ACTIVE | ASYNC_NORMAL_ACTIVE |
+		ASYNC_CLOSING);
 	wake_up_interruptible(&portp->close_wait);
+	MOD_DEC_USE_COUNT;
 	restore_flags(flags);
 }
 
@@ -1058,6 +1063,7 @@
 		current->state = TASK_INTERRUPTIBLE;
 		current->timeout = jiffies + len;
 		schedule();
+		current->state = TASK_RUNNING;
 	}
 }
 
@@ -1077,10 +1083,12 @@
 	char		*head, *tail;
 
 #if DEBUG
-	printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", (int) tty, from_user, (int) buf, count);
+	printk("stl_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",
+		(int) tty, from_user, (int) buf, count);
 #endif
 
-	if ((tty == (struct tty_struct *) NULL) || (stl_tmpwritebuf == (char *) NULL))
+	if ((tty == (struct tty_struct *) NULL) ||
+	    (stl_tmpwritebuf == (char *) NULL))
 		return(0);
 	portp = tty->driver_data;
 	if (portp == (stlport_t *) NULL)
@@ -1202,7 +1210,8 @@
 		return;
 
 #if 0
-	if (tty->stopped || tty->hw_stopped || (portp->tx.head == portp->tx.tail))
+	if (tty->stopped || tty->hw_stopped ||
+	    (portp->tx.head == portp->tx.tail))
 		return;
 #endif
 	stl_startrxtx(portp, -1, 1);
@@ -1327,12 +1336,14 @@
 	copy_from_user(&sio, sp, sizeof(struct serial_struct));
 	if (!suser()) {
 		if ((sio.baud_base != portp->baud_base) ||
-				(sio.close_delay != portp->close_delay) ||
-				((sio.flags & ~ASYNC_USR_MASK) != (portp->flags & ~ASYNC_USR_MASK)))
+		    (sio.close_delay != portp->close_delay) ||
+		    ((sio.flags & ~ASYNC_USR_MASK) !=
+		    (portp->flags & ~ASYNC_USR_MASK)))
 			return(-EPERM);
 	} 
 
-	portp->flags = (portp->flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK);
+	portp->flags = (portp->flags & ~ASYNC_USR_MASK) |
+		(sio.flags & ASYNC_USR_MASK);
 	portp->baud_base = sio.baud_base;
 	portp->close_delay = sio.close_delay;
 	portp->closing_wait = sio.closing_wait;
@@ -1350,7 +1361,8 @@
 	int		rc;
 
 #if DEBUG
-	printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n", (int) tty, (int) file, cmd, (int) arg);
+	printk("stl_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n",
+		(int) tty, (int) file, cmd, (int) arg);
 #endif
 
 	if (tty == (struct tty_struct *) NULL)
@@ -1360,7 +1372,7 @@
 		return(-ENODEV);
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- 			(cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
+ 	    (cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 			return(-EIO);
 	}
@@ -1368,67 +1380,68 @@
 	rc = 0;
 
 	switch (cmd) {
-	case TCSBRK:
-		if ((rc = tty_check_change(tty)) == 0) {
-			tty_wait_until_sent(tty, 0);
-			if (! arg)
-				stl_sendbreak(portp, 250);
-		}
-		break;
-	case TCSBRKP:
-		if ((rc = tty_check_change(tty)) == 0) {
-			tty_wait_until_sent(tty, 0);
-			stl_sendbreak(portp, (arg ? (arg * 100) : 250));
-		}
-		break;
 	case TIOCGSOFTCAR:
 		rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
-		    (unsigned int *) arg);
+			(unsigned int *) arg);
 		break;
 	case TIOCSSOFTCAR:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | (ival ? CLOCAL : 0);
+			tty->termios->c_cflag =
+				(tty->termios->c_cflag & ~CLOCAL) |
+				(ival ? CLOCAL : 0);
 		}
 		break;
 	case TIOCMGET:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			ival = stl_getsignals(portp);
 			put_user(ival, (unsigned int *) arg);
 		}
 		break;
 	case TIOCMBIS:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1), ((ival & TIOCM_RTS) ? 1 : -1));
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : -1),
+				((ival & TIOCM_RTS) ? 1 : -1));
 		}
 		break;
 	case TIOCMBIC:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1), ((ival & TIOCM_RTS) ? 0 : -1));
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 0 : -1),
+				((ival & TIOCM_RTS) ? 0 : -1));
 		}
 		break;
 	case TIOCMSET:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned int))) == 0) {
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(unsigned int))) == 0) {
 			get_user(ival, (unsigned int *) arg);
-			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0), ((ival & TIOCM_RTS) ? 1 : 0));
+			stl_setsignals(portp, ((ival & TIOCM_DTR) ? 1 : 0),
+				((ival & TIOCM_RTS) ? 1 : 0));
 		}
 		break;
 	case TIOCGSERIAL:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			stl_getserial(portp, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
-		if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
+		if ((rc = verify_area(VERIFY_READ, (void *) arg,
+		    sizeof(struct serial_struct))) == 0)
 			rc = stl_setserial(portp, (struct serial_struct *) arg);
 		break;
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stl_getportstats(portp, (comstats_t *) arg);
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
 			rc = stl_clrportstats(portp, (comstats_t *) arg);
 		break;
 	case TIOCSERCONFIG:
@@ -1464,11 +1477,13 @@
 		return;
 
 	tiosp = tty->termios;
-	if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
+	if ((tiosp->c_cflag == old->c_cflag) &&
+	    (tiosp->c_iflag == old->c_iflag))
 		return;
 
 	stl_setport(portp, tiosp);
-	stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0), -1);
+	stl_setsignals(portp, ((tiosp->c_cflag & (CBAUD & ~CBAUDEX)) ? 1 : 0),
+		-1);
 	if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) {
 		tty->hw_stopped = 0;
 		stl_start(tty);
@@ -1603,7 +1618,6 @@
 		portp->tx.head = (char *) NULL;
 		portp->tx.tail = (char *) NULL;
 	}
-	tty->driver_data = (void *) NULL;
 	portp->tty = (struct tty_struct *) NULL;
 	portp->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE);
 	portp->refcount = 0;
@@ -1628,12 +1642,219 @@
 
 	stl_flush(portp);
 	wake_up_interruptible(&tty->write_wait);
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
 		(tty->ldisc.write_wakeup)(tty);
 }
 
 /*****************************************************************************/
 
+static void stl_breakctl(struct tty_struct *tty, int state)
+{
+	stlport_t	*portp;
+
+#if DEBUG
+	printk("stl_breakctl(tty=%x,state=%d)\n", (int) tty, state);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	stl_sendbreak(portp, ((state == -1) ? 1 : 2));
+}
+
+/*****************************************************************************/
+
+static void stl_waituntilsent(struct tty_struct *tty, int timeout)
+{
+	stlport_t	*portp;
+	unsigned long	tend;
+
+#if DEBUG
+	printk("stl_waituntilsent(tty=%x,timeout=%d)\n", (int) tty, timeout);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	if (timeout == 0)
+		timeout = HZ;
+	tend = jiffies + timeout;
+
+	while (stl_datastate(portp)) {
+		if (signal_pending(current))
+			break;
+		stl_delay(2);
+		if (jiffies >= tend)
+			break;
+	}
+}
+
+/*****************************************************************************/
+
+static void stl_sendxchar(struct tty_struct *tty, char ch)
+{
+	stlport_t	*portp;
+
+#if DEBUG
+	printk("stl_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch);
+#endif
+
+	if (tty == (struct tty_struct *) NULL)
+		return;
+	portp = tty->driver_data;
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	if (ch == STOP_CHAR(tty))
+		stl_sendflow(portp, 0);
+	else if (ch == START_CHAR(tty))
+		stl_sendflow(portp, 1);
+	else
+		stl_putchar(tty, ch);
+}
+
+/*****************************************************************************/
+
+#define	MAXLINE		80
+
+/*
+ *	Format info for a specified port. The line is deliberately limited
+ *	to 80 characters. (If it is too long it will be truncated, if too
+ *	short then padded with spaces).
+ */
+
+static int stl_portinfo(stlport_t *portp, int portnr, char *pos)
+{
+	char	*sp;
+	int	sigs, cnt;
+
+	sp = pos;
+	sp += sprintf(sp, "%d: uart:%s tx:%d rx:%d",
+		portnr, (portp->hwid == 1) ? "SC26198" : "CD1400",
+		(int) portp->stats.txtotal, (int) portp->stats.rxtotal);
+
+	if (portp->stats.rxframing)
+		sp += sprintf(sp, " fe:%d", (int) portp->stats.rxframing);
+	if (portp->stats.rxparity)
+		sp += sprintf(sp, " pe:%d", (int) portp->stats.rxparity);
+	if (portp->stats.rxbreaks)
+		sp += sprintf(sp, " brk:%d", (int) portp->stats.rxbreaks);
+	if (portp->stats.rxoverrun)
+		sp += sprintf(sp, " oe:%d", (int) portp->stats.rxoverrun);
+
+	sigs = stl_getsignals(portp);
+	cnt = sprintf(sp, "%s%s%s%s%s ",
+		(sigs & TIOCM_RTS) ? "|RTS" : "",
+		(sigs & TIOCM_CTS) ? "|CTS" : "",
+		(sigs & TIOCM_DTR) ? "|DTR" : "",
+		(sigs & TIOCM_CD) ? "|DCD" : "",
+		(sigs & TIOCM_DSR) ? "|DSR" : "");
+	*sp = ' ';
+	sp += cnt;
+
+	for (cnt = (sp - pos); (cnt < (MAXLINE - 1)); cnt++)
+		*sp++ = ' ';
+	if (cnt >= MAXLINE)
+		pos[(MAXLINE - 2)] = '+';
+	pos[(MAXLINE - 1)] = '\n';
+
+	return(MAXLINE);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Port info, read from the /proc file system.
+ */
+
+static int stl_readproc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	stlbrd_t	*brdp;
+	stlpanel_t	*panelp;
+	stlport_t	*portp;
+	int		brdnr, panelnr, portnr, totalport;
+	int		curoff, maxoff;
+	char		*pos;
+
+#if DEBUG
+	printk("stl_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x,"
+		"data=%x\n", (int) page, (int) start, (int) off, count,
+		(int) eof, (int) data);
+#endif
+
+	pos = page;
+	totalport = 0;
+	curoff = 0;
+
+	if (off == 0) {
+		pos += sprintf(pos, "%s: version %s", stl_drvtitle,
+			stl_drvversion);
+		while (pos < (page + MAXLINE - 1))
+			*pos++ = ' ';
+		*pos++ = '\n';
+	}
+	curoff =  MAXLINE;
+
+/*
+ *	We scan through for each board, panel and port. The offset is
+ *	calculated on the fly, and irrelevant ports are skipped.
+ */
+	for (brdnr = 0; (brdnr < stl_nrbrds); brdnr++) {
+		brdp = stl_brds[brdnr];
+		if (brdp == (stlbrd_t *) NULL)
+			continue;
+		if (brdp->state == 0)
+			continue;
+
+		maxoff = curoff + (brdp->nrports * MAXLINE);
+		if (off >= maxoff) {
+			curoff = maxoff;
+			continue;
+		}
+
+		totalport = brdnr * STL_MAXPORTS;
+		for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) {
+			panelp = brdp->panels[panelnr];
+			if (panelp == (stlpanel_t *) NULL)
+				continue;
+
+			maxoff = curoff + (panelp->nrports * MAXLINE);
+			if (off >= maxoff) {
+				curoff = maxoff;
+				totalport += panelp->nrports;
+				continue;
+			}
+
+			for (portnr = 0; (portnr < panelp->nrports); portnr++,
+			    totalport++) {
+				portp = panelp->ports[portnr];
+				if (portp == (stlport_t *) NULL)
+					continue;
+				if (off >= (curoff += MAXLINE))
+					continue;
+				if ((pos - page + MAXLINE) > count)
+					goto stl_readdone;
+				pos += stl_portinfo(portp, totalport, pos);
+			}
+		}
+	}
+
+	*eof = 1;
+
+stl_readdone:
+	*start = page;
+	return(pos - page);
+}
+
+/*****************************************************************************/
+
 /*
  *	All board interrupts are vectored through here first. This code then
  *	calls off to the approrpriate board interrupt handlers.
@@ -1755,6 +1976,29 @@
 /*****************************************************************************/
 
 /*
+ *	Interrupt service routine for ECH-8/64-PCI board types.
+ */
+
+static void stl_echpci64intr(stlbrd_t *brdp)
+{
+	stlpanel_t	*panelp;
+	unsigned int	ioaddr;
+	int		bnknr;
+
+	while (inb(brdp->ioctrl) & 0x1) {
+		for (bnknr = 0; (bnknr < brdp->nrbnks); bnknr++) {
+			ioaddr = brdp->bnkstataddr[bnknr];
+			if (inb(ioaddr) & ECH_PNLINTRPEND) {
+				panelp = brdp->bnk2panel[bnknr];
+				(* panelp->isr)(panelp, (ioaddr & 0xfffc));
+			}
+		}
+	}
+}
+
+/*****************************************************************************/
+
+/*
  *	Service an off-level request for some channel.
  */
 
@@ -1777,7 +2021,8 @@
 		return;
 
 	if (test_bit(ASYI_TXLOW, &portp->istate)) {
-		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
 			(tty->ldisc.write_wakeup)(tty);
 		wake_up_interruptible(&tty->write_wait);
 	}
@@ -1790,7 +2035,7 @@
 		if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) {
 			if (portp->flags & ASYNC_CHECK_CD) {
 				if (! ((portp->flags & ASYNC_CALLOUT_ACTIVE) &&
-						(portp->flags & ASYNC_CALLOUT_NOHUP))) {
+				    (portp->flags & ASYNC_CALLOUT_NOHUP))) {
 					tty_hangup(tty);
 				}
 			}
@@ -1806,12 +2051,12 @@
  *	interrupt across multiple boards.
  */
 
-__initfunc(static int stl_mapirq(int irq))
+__initfunc(static int stl_mapirq(int irq, char *name))
 {
 	int	rc, i;
 
 #if DEBUG
-	printk("stl_mapirq(irq=%d)\n", irq);
+	printk("stl_mapirq(irq=%d,name=%s)\n", irq, name);
 #endif
 
 	rc = 0;
@@ -1820,8 +2065,9 @@
 			break;
 	}
 	if (i >= stl_numintrs) {
-		if (request_irq(irq, stl_intr, SA_INTERRUPT, stl_drvname, NULL) != 0) {
-			printk("STALLION: failed to register interrupt routine for irq=%d\n", irq);
+		if (request_irq(irq, stl_intr, SA_INTERRUPT, name, NULL) != 0) {
+			printk("STALLION: failed to register interrupt "
+				"routine for %s irq=%d\n", name, irq);
 			rc = -ENODEV;
 		} else {
 			stl_gotintrs[stl_numintrs++] = irq;
@@ -1854,7 +2100,8 @@
 	for (i = 0; (i < panelp->nrports); i++) {
 		portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
 		if (portp == (stlport_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlport_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlport_t));
 			break;
 		}
 		memset(portp, 0, sizeof(stlport_t));
@@ -1892,19 +2139,54 @@
 {
 	stlpanel_t	*panelp;
 	unsigned int	status;
+	char		*name;
 	int		rc;
 
 #if DEBUG
 	printk("stl_initeio(brdp=%x)\n", (int) brdp);
 #endif
 
-	if (check_region(brdp->ioaddr1, 8)) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
-			brdp->brdnr, brdp->ioaddr1);
-	}
- 
 	brdp->ioctrl = brdp->ioaddr1 + 1;
 	brdp->iostatus = brdp->ioaddr1 + 2;
+
+/*
+ *	Handle board specific stuff now. The real difference is PCI
+ *	or not PCI.
+ */
+	if (brdp->brdtype == BRD_EASYIOPCI) {
+		brdp->iosize1 = 0x80;
+		brdp->iosize2 = 0x80;
+		name = "serial(EIO-PCI)";
+		outb(0x41, (brdp->ioaddr2 + 0x4c));
+	} else {
+		brdp->iosize1 = 8;
+		name = "serial(EIO)";
+		if ((brdp->irq < 0) || (brdp->irq > 15) ||
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
+			return(-EINVAL);
+		}
+		outb((stl_vecmap[brdp->irq] |
+			((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)),
+			brdp->ioctrl);
+	}
+
+	if (check_region(brdp->ioaddr1, brdp->iosize1)) {
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, brdp->ioaddr1);
+	}
+	if (brdp->iosize2 > 0) {
+		if (check_region(brdp->ioaddr2, brdp->iosize2)) {
+			printk("STALLION: Warning, unit %d I/O address %x "
+				"conflicts with another device\n",
+				brdp->brdnr, brdp->ioaddr2);
+		}
+	}
+ 
+/*
+ *	Everything looks OK, so lets go ahead and probe for the hardware.
+ */
 	brdp->clk = CD1400_CLK;
 	brdp->isr = stl_eiointr;
 
@@ -1940,23 +2222,18 @@
 		return(-ENODEV);
 	}
 
-	request_region(brdp->ioaddr1, 8, "serial(EIO)");
-
 /*
- *	Check that the supplied IRQ is good and then use it to setup the
- *	programmable interrupt bits on EIO board. Also set the edge/level
- *	triggered interrupt bit.
+ *	We have verfied that the board is actually present, so now we
+ *	can complete the setup.
  */
-	if ((brdp->irq < 0) || (brdp->irq > 15) ||
-			(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-		printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
-		return(-EINVAL);
-	}
-	outb((stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE)), brdp->ioctrl);
+	request_region(brdp->ioaddr1, brdp->iosize1, name);
+	if (brdp->iosize2 > 0)
+		request_region(brdp->ioaddr2, brdp->iosize2, name);
 
 	panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
 	if (panelp == (stlpanel_t *) NULL) {
-		printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlpanel_t));
 		return(-ENOMEM);
 	}
 	memset(panelp, 0, sizeof(stlpanel_t));
@@ -1979,7 +2256,7 @@
 	brdp->nrpanels = 1;
 	brdp->state |= BRD_FOUND;
 	brdp->hwid = status;
-	rc = stl_mapirq(brdp->irq);
+	rc = stl_mapirq(brdp->irq, name);
 	return(rc);
 }
 
@@ -1990,11 +2267,12 @@
  *	dealing with all types of ECH board.
  */
 
-static inline int stl_initech(stlbrd_t *brdp)
+static int inline stl_initech(stlbrd_t *brdp)
 {
 	stlpanel_t	*panelp;
 	unsigned int	status, nxtid, ioaddr, conflict;
 	int		panelnr, banknr, i;
+	char		*name;
 
 #if DEBUG
 	printk("stl_initech(brdp=%x)\n", (int) brdp);
@@ -2008,69 +2286,101 @@
  *	bit between the different board types. So we need to handle each
  *	separately. Also do a check that the supplied IRQ is good.
  */
-	if (brdp->brdtype == BRD_ECH) {
+	switch (brdp->brdtype) {
+
+	case BRD_ECH:
 		brdp->isr = stl_echatintr;
 		brdp->ioctrl = brdp->ioaddr1 + 1;
 		brdp->iostatus = brdp->ioaddr1 + 1;
 		status = inb(brdp->iostatus);
 		if ((status & ECH_IDBITMASK) != ECH_ID)
 			return(-ENODEV);
-
 		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
 			return(-EINVAL);
 		}
 		status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1);
 		status |= (stl_vecmap[brdp->irq] << 1);
 		outb((status | ECH_BRDRESET), brdp->ioaddr1);
-		brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
-		outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
-		conflict = check_region(brdp->ioaddr1, 2) ? brdp->ioaddr1 : 0;
-		if (conflict == 0)
-			conflict = check_region(brdp->ioaddr2, 32) ? brdp->ioaddr2 : 0;
-		request_region(brdp->ioaddr1, 2, "serial(EC8/32)");
-		request_region(brdp->ioaddr2, 32, "serial(EC8/32-secondary)");
+		brdp->ioctrlval = ECH_INTENABLE |
+			((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE);
+		for (i = 0; (i < 10); i++)
+			outb((brdp->ioctrlval | ECH_BRDENABLE), brdp->ioctrl);
+		brdp->iosize1 = 2;
+		brdp->iosize2 = 32;
+		name = "serial(EC8/32)";
 		outb(status, brdp->ioaddr1);
-	} else if (brdp->brdtype == BRD_ECHMC) {
+		break;
+
+	case BRD_ECHMC:
 		brdp->isr = stl_echmcaintr;
 		brdp->ioctrl = brdp->ioaddr1 + 0x20;
 		brdp->iostatus = brdp->ioctrl;
 		status = inb(brdp->iostatus);
 		if ((status & ECH_IDBITMASK) != ECH_ID)
 			return(-ENODEV);
-
 		if ((brdp->irq < 0) || (brdp->irq > 15) ||
-				(stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
-			printk("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr);
+		    (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) {
+			printk("STALLION: invalid irq=%d for brd=%d\n",
+				brdp->irq, brdp->brdnr);
 			return(-EINVAL);
 		}
 		outb(ECHMC_BRDRESET, brdp->ioctrl);
 		outb(ECHMC_INTENABLE, brdp->ioctrl);
-		conflict = check_region(brdp->ioaddr1, 64) ? brdp->ioaddr1 : 0;
-		request_region(brdp->ioaddr1, 64, "serial(EC8/32-MC)");
-	} else if (brdp->brdtype == BRD_ECHPCI) {
+		brdp->iosize1 = 64;
+		name = "serial(EC8/32-MC)";
+		break;
+
+	case BRD_ECHPCI:
 		brdp->isr = stl_echpciintr;
 		brdp->ioctrl = brdp->ioaddr1 + 2;
-		conflict = check_region(brdp->ioaddr1, 4) ? brdp->ioaddr1 : 0;
-		if (conflict == 0)
-			conflict = check_region(brdp->ioaddr2, 8) ? brdp->ioaddr2 : 0;
-		request_region(brdp->ioaddr1, 4, "serial(EC8/32-PCI)");
-		request_region(brdp->ioaddr2, 8, "serial(EC8/32-PCI-secondary)");
+		brdp->iosize1 = 4;
+		brdp->iosize2 = 8;
+		name = "serial(EC8/32-PCI)";
+		break;
+
+	case BRD_ECH64PCI:
+		brdp->isr = stl_echpci64intr;
+		brdp->ioctrl = brdp->ioaddr2 + 0x40;
+		outb(0x43, (brdp->ioaddr1 + 0x4c));
+		brdp->iosize1 = 0x80;
+		brdp->iosize2 = 0x80;
+		name = "serial(EC8/64-PCI)";
+		break;
+
+	default:
+		printk("STALLION: unknown board type=%d\n", brdp->brdtype);
+		return(-EINVAL);
+		break;
 	}
 
+/*
+ *	Check boards for possible IO address conflicts. We won't actually
+ *	do anything about it here, just issue a warning...
+ */
+	conflict = check_region(brdp->ioaddr1, brdp->iosize1) ?
+		brdp->ioaddr1 : 0;
+	if ((conflict == 0) && (brdp->iosize2 > 0))
+		conflict = check_region(brdp->ioaddr2, brdp->iosize2) ?
+			brdp->ioaddr2 : 0;
 	if (conflict) {
-		printk("STALLION: Warning, unit %d I/O address %x conflicts with another device\n",
-			brdp->brdnr, conflict);
+		printk("STALLION: Warning, unit %d I/O address %x conflicts "
+			"with another device\n", brdp->brdnr, conflict);
 	}
 
-	brdp->clk = CD1400_CLK;
-	brdp->hwid = status;
+	request_region(brdp->ioaddr1, brdp->iosize1, name);
+	if (brdp->iosize2 > 0)
+		request_region(brdp->ioaddr2, brdp->iosize2, name);
 
 /*
  *	Scan through the secondary io address space looking for panels.
  *	As we find'em allocate and initialize panel structures for each.
  */
+	brdp->clk = CD1400_CLK;
+	brdp->hwid = status;
+
 	ioaddr = brdp->ioaddr2;
 	banknr = 0;
 	panelnr = 0;
@@ -2086,7 +2396,8 @@
 			break;
 		panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
 		if (panelp == (stlpanel_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlpanel_t));
 			break;
 		}
 		memset(panelp, 0, sizeof(stlpanel_t));
@@ -2107,7 +2418,8 @@
 				panelp->nrports = 16;
 				brdp->bnk2panel[banknr] = panelp;
 				brdp->bnkpageaddr[banknr] = nxtid;
-				brdp->bnkstataddr[banknr++] = ioaddr + 4 + ECH_PNLSTATUS;
+				brdp->bnkstataddr[banknr++] = ioaddr + 4 +
+					ECH_PNLSTATUS;
 			} else {
 				panelp->nrports = 8;
 			}
@@ -2121,7 +2433,8 @@
 					ioaddr += EREG_BANKSIZE;
 				brdp->bnk2panel[banknr] = panelp;
 				brdp->bnkpageaddr[banknr] = ++nxtid;
-				brdp->bnkstataddr[banknr++] = ioaddr + ECH_PNLSTATUS;
+				brdp->bnkstataddr[banknr++] = ioaddr +
+					ECH_PNLSTATUS;
 			} else {
 				panelp->nrports = 8;
 				panelp->ackmask = 0xc0;
@@ -2132,7 +2445,8 @@
 		ioaddr += EREG_BANKSIZE;
 		brdp->nrports += panelp->nrports;
 		brdp->panels[panelnr++] = panelp;
-		if (ioaddr >= (brdp->ioaddr2 + 0x20))
+		if ((brdp->brdtype != BRD_ECHPCI) &&
+		    (ioaddr >= (brdp->ioaddr2 + brdp->iosize2)))
 			break;
 	}
 
@@ -2142,7 +2456,7 @@
 		outb((brdp->ioctrlval | ECH_BRDDISABLE), brdp->ioctrl);
 
 	brdp->state |= BRD_FOUND;
-	i = stl_mapirq(brdp->irq);
+	i = stl_mapirq(brdp->irq, name);
 	return(i);
 }
 
@@ -2165,21 +2479,26 @@
 
 	switch (brdp->brdtype) {
 	case BRD_EASYIO:
+	case BRD_EASYIOPCI:
 		stl_initeio(brdp);
 		break;
 	case BRD_ECH:
 	case BRD_ECHMC:
 	case BRD_ECHPCI:
+	case BRD_ECH64PCI:
 		stl_initech(brdp);
 		break;
 	default:
-		printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+		printk("STALLION: unit=%d is unknown board type=%d\n",
+			brdp->brdnr, brdp->brdtype);
 		return(ENODEV);
 	}
 
 	stl_brds[brdp->brdnr] = brdp;
 	if ((brdp->state & BRD_FOUND) == 0) {
-		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq);
+		printk("STALLION: %s board not found, unit=%d io=%x irq=%d\n",
+			stl_brdnames[brdp->brdtype], brdp->brdnr,
+			brdp->ioaddr1, brdp->irq);
 		return(ENODEV);
 	}
 
@@ -2187,90 +2506,160 @@
 		if (brdp->panels[i] != (stlpanel_t *) NULL)
 			stl_initports(brdp, brdp->panels[i]);
 
-	printk("STALLION: %s found, unit=%d io=%x irq=%d nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels, brdp->nrports);
+	printk("STALLION: %s found, unit=%d io=%x irq=%d "
+		"nrpanels=%d nrports=%d\n", stl_brdnames[brdp->brdtype],
+		brdp->brdnr, brdp->ioaddr1, brdp->irq, brdp->nrpanels,
+		brdp->nrports);
 	return(0);
 }
 
 /*****************************************************************************/
 
+#ifdef	CONFIG_PCI
+
 /*
- *	Find any ECH-PCI boards that might be installed. Initialize each
+ *	We have a Stallion board. Allocate a board structure and
+ *	initialize it. Read its IO and IRQ resources from PCI
+ *	configuration space.
+ */
+
+static inline int stl_initpcibrd(int brdtype, unsigned char busnr, unsigned char devnr)
+{
+	unsigned int	bar[4];
+	stlbrd_t	*brdp;
+	int		i, rc;
+	unsigned char	irq;
+
+#if DEBUG
+	printk("stl_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n",
+		brdtype, busnr, devnr);
+#endif
+
+	brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
+	if (brdp == (stlbrd_t *) NULL) {
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			sizeof(stlbrd_t));
+		return(-ENOMEM);
+	}
+
+	memset(brdp, 0, sizeof(stlbrd_t));
+	brdp->magic = STL_BOARDMAGIC;
+	brdp->brdnr = stl_nrbrds++;
+	brdp->brdtype = brdtype;
+
+/*
+ *	Read in all the BAR registers from this board. Different Stallion
+ *	boards use these in different ways, so we just read in the whole
+ *	lot and then figure out what is what later.
+ */
+	for (i = 0; (i < 4); i++) {
+		rc = pcibios_read_config_dword(busnr, devnr,
+			(PCI_BASE_ADDRESS_0 + (i * 0x4)), &bar[i]);
+		if (rc) {
+			printk("STALLION: failed to read BAR register %d "
+				"from PCI board, errno=%x\n", i, rc);
+			return(0);
+		}
+	}
+
+	rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq);
+	if (rc) {
+		printk("STALLION: failed to read INTERRUPT register "
+			"from PCI board, errno=%x\n", rc);
+		return(0);
+	}
+
+#if DEBUG
+	printk("%s(%d): BAR[]=%x,%x,%x,%x IRQ=%x\n", __FILE__, __LINE__,
+		bar[0], bar[1], bar[2], bar[3], irq);
+#endif
+
+/*
+ *	We have all resources from the board, so lets setup the actual
+ *	board structure now.
+ */
+	switch (brdtype) {
+	case BRD_ECHPCI:
+		brdp->ioaddr2 = (bar[0] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	case BRD_ECH64PCI:
+		brdp->ioaddr2 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr1 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	case BRD_EASYIOPCI:
+		brdp->ioaddr1 = (bar[2] & PCI_BASE_ADDRESS_IO_MASK);
+		brdp->ioaddr2 = (bar[1] & PCI_BASE_ADDRESS_IO_MASK);
+		break;
+	default:
+		printk("STALLION: unknown PCI board type=%d\n", brdtype);
+		break;
+	}
+
+	brdp->irq = irq;
+	stl_brdinit(brdp);
+
+	return(0);
+}
+
+
+/*****************************************************************************/
+
+/*
+ *	Find all Stallion PCI boards that might be installed. Initialize each
  *	one as it is found.
  */
 
-#ifdef	CONFIG_PCI
 
 static inline int stl_findpcibrds()
 {
-	stlbrd_t	*brdp;
-	unsigned char	busnr, devnr, irq;
+	unsigned char	busnr, devnr;
 	unsigned short	class;
-	unsigned int	ioaddr;
-	int		i, rc;
+	int		i, rc, brdtypnr;
 
 #if DEBUG
 	printk("stl_findpcibrds()\n");
 #endif
 
-	if (pcibios_present()) {
-		for (i = 0; (i < STL_MAXBRDS); i++) {
-			if (pcibios_find_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECHPCI832, i, &busnr, &devnr))
-				if (pcibios_find_device(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, i, &busnr, &devnr))
-					break;
+	if (! pcibios_present())
+		return(0);
+
+	for (i = 0; (i < stl_nrpcibrds); i++) {
+		for (brdtypnr = 0; ; brdtypnr++) {
+
+			rc = pcibios_find_device(stl_pcibrds[i].vendid,
+				stl_pcibrds[i].devid, brdtypnr, &busnr, &devnr);
+			if (rc)
+				break;
 
 /*
- *			Found a device on the PCI bus that has our vendor and
- *			device ID. Need to check now that it is really us.
+ *			Check that we can handle more boards...
  */
-			if ((rc = pcibios_read_config_word(busnr, devnr, PCI_CLASS_DEVICE, &class))) {
-				printk("STALLION: failed to read class type from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			if (class == PCI_CLASS_STORAGE_IDE)
-				continue;
-
 			if (stl_nrbrds >= STL_MAXBRDS) {
-				printk("STALLION: too many boards found, maximum supported %d\n", STL_MAXBRDS);
+				printk("STALLION: too many boards found, "
+					"maximum supported %d\n", STL_MAXBRDS);
+				i = stl_nrpcibrds;
 				break;
 			}
 
 /*
- *			We have a Stallion board. Allocate a board structure
- *			and initialize it. Read its IO and IRQ resources
- *			from conf space.
- */
-			brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
-			if (brdp == (stlbrd_t *) NULL) {
-				printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
-				return(-ENOMEM);
-			}
-			memset(brdp, 0, sizeof(stlbrd_t));
-			brdp->magic = STL_BOARDMAGIC;
-			brdp->brdnr = stl_nrbrds++;
-			brdp->brdtype = BRD_ECHPCI;
-
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_0, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
-				continue;
-			}
-			brdp->ioaddr2 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
-
-			if ((rc = pcibios_read_config_dword(busnr, devnr, PCI_BASE_ADDRESS_1, &ioaddr))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+ *			Found a device on the PCI bus that has our vendor and
+ *			device ID. Need to check now that it is really us.
+ */
+			rc = pcibios_read_config_word(busnr, devnr,
+				PCI_CLASS_DEVICE, &class);
+			if (rc) {
+				printk("STALLION: failed to read class type "
+					"from PCI board, errno=%x\n", rc);
 				continue;
 			}
-			brdp->ioaddr1 = (ioaddr & PCI_BASE_ADDRESS_IO_MASK);
-#if DEBUG
-			printk("%s(%d): BAR0=%x BAR1=%x\n", __FILE__, __LINE__, brdp->ioaddr2, brdp->ioaddr1);
-#endif
-
-			if ((rc = pcibios_read_config_byte(busnr, devnr, PCI_INTERRUPT_LINE, &irq))) {
-				printk("STALLION: failed to read BAR register from PCI board, errno=%x\n", rc);
+			if (class == PCI_CLASS_STORAGE_IDE)
 				continue;
-			}
-			brdp->irq = irq;
 
-			stl_brdinit(brdp);
+			rc = stl_initpcibrd(stl_pcibrds[i].brdtype, busnr,
+				devnr);
+			if (rc)
+				return(rc);
 		}
 	}
 
@@ -2297,12 +2686,11 @@
 	printk("stl_initbrds()\n");
 #endif
 
-#ifndef MODULE
 	if (stl_nrbrds > STL_MAXBRDS) {
-		printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+		printk("STALLION: too many boards in configuration table, "
+			"truncating to %d\n", STL_MAXBRDS);
 		stl_nrbrds = STL_MAXBRDS;
 	}
-#endif
 
 /*
  *	Firstly scan the list of static boards configured. Allocate
@@ -2312,7 +2700,8 @@
 		confp = &stl_brdconf[i];
 		brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
 		if (brdp == (stlbrd_t *) NULL) {
-			printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t));
+			printk("STALLION: failed to allocate memory "
+				"(size=%d)\n", sizeof(stlbrd_t));
 			return(-ENOMEM);
 		}
 		memset(brdp, 0, sizeof(stlbrd_t));
@@ -2419,7 +2808,8 @@
 
 	if (portp == (stlport_t *) NULL) {
 		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+			stl_comstats.port);
 		if (portp == (stlport_t *) NULL)
 			return(-ENODEV);
 	}
@@ -2453,7 +2843,8 @@
 
 	head = portp->tx.head;
 	tail = portp->tx.tail;
-	portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)));
+	portp->stats.txbuffered = ((head >= tail) ? (head - tail) :
+		(STL_TXBUFSIZE - (tail - head)));
 
 	portp->stats.signals = (unsigned long) stl_getsignals(portp);
 
@@ -2471,7 +2862,8 @@
 {
 	if (portp == (stlport_t *) NULL) {
 		copy_from_user(&stl_comstats, cp, sizeof(comstats_t));
-		portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port);
+		portp = stl_getport(stl_comstats.brd, stl_comstats.panel,
+			stl_comstats.port);
 		if (portp == (stlport_t *) NULL)
 			return(-ENODEV);
 	}
@@ -2526,6 +2918,27 @@
 /*****************************************************************************/
 
 /*
+ *	Memory device open code. Need to keep track of opens and close
+ *	for module handling.
+ */
+
+static int stl_memopen(struct inode *ip, struct file *fp)
+{
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+static int stl_memclose(struct inode *ip, struct file *fp)
+{
+	MOD_DEC_USE_COUNT;
+	return(0);
+}
+
+/*****************************************************************************/
+
+/*
  *	The "staliomem" device is also required to do some special operations
  *	on the board and/or ports. In this driver it is mostly used for stats
  *	collection.
@@ -2536,7 +2949,8 @@
 	int	brdnr, rc;
 
 #if DEBUG
-	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip, (int) fp, cmd, (int) arg);
+	printk("stl_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n", (int) ip,
+		(int) fp, cmd, (int) arg);
 #endif
 
 	brdnr = MINOR(ip->i_rdev);
@@ -2546,23 +2960,30 @@
 
 	switch (cmd) {
 	case COM_GETPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_getportstats((stlport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stl_getportstats((stlport_t *) NULL,
+				(comstats_t *) arg);
 		break;
 	case COM_CLRPORTSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(comstats_t))) == 0)
-			rc = stl_clrportstats((stlport_t *) NULL, (comstats_t *) arg);
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(comstats_t))) == 0)
+			rc = stl_clrportstats((stlport_t *) NULL,
+				(comstats_t *) arg);
 		break;
 	case COM_GETBRDSTATS:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(combrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(combrd_t))) == 0)
 			rc = stl_getbrdstats((combrd_t *) arg);
 		break;
 	case COM_READPORT:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlport_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlport_t))) == 0)
 			rc = stl_getportstruct(arg);
 		break;
 	case COM_READBOARD:
-		if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(stlbrd_t))) == 0)
+		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
+		    sizeof(stlbrd_t))) == 0)
 			rc = stl_getbrdstruct(arg);
 		break;
 	default:
@@ -2577,7 +2998,7 @@
 
 __initfunc(int stl_init(void))
 {
-	printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
+	printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion);
 
 	stl_initbrds();
 
@@ -2586,7 +3007,8 @@
  */
 	stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
 	if (stl_tmpwritebuf == (char *) NULL)
-		printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE);
+		printk("STALLION: failed to allocate memory (size=%d)\n",
+			STL_TXBUFSIZE);
 
 /*
  *	Set up a character driver for per board stuff. This is mainly used
@@ -2601,6 +3023,7 @@
  */
 	memset(&stl_serial, 0, sizeof(struct tty_driver));
 	stl_serial.magic = TTY_DRIVER_MAGIC;
+	stl_serial.driver_name = stl_drvname;
 	stl_serial.name = stl_serialname;
 	stl_serial.major = STL_SERIALMAJOR;
 	stl_serial.minor_start = 0;
@@ -2629,11 +3052,16 @@
 	stl_serial.start = stl_start;
 	stl_serial.hangup = stl_hangup;
 	stl_serial.flush_buffer = stl_flushbuffer;
+	stl_serial.break_ctl = stl_breakctl;
+	stl_serial.wait_until_sent = stl_waituntilsent;
+	stl_serial.send_xchar = stl_sendxchar;
+	stl_serial.read_proc = stl_readproc;
 
 	stl_callout = stl_serial;
 	stl_callout.name = stl_calloutname;
 	stl_callout.major = STL_CALLOUTMAJOR;
 	stl_callout.subtype = STL_DRVTYPCALLOUT;
+	stl_callout.read_proc = 0;
 
 	if (tty_register_driver(&stl_serial))
 		printk("STALLION: failed to register serial driver\n");
@@ -2719,7 +3147,9 @@
 				break;
 		}
 		if ((j >= CCR_MAXWAIT) || (gfrcr < 0x40) || (gfrcr > 0x60)) {
-			printk("STALLION: cd1400 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+			printk("STALLION: cd1400 not responding, "
+				"brd=%d panel=%d chip=%d\n",
+				panelp->brdnr, panelp->panelnr, i);
 			continue;
 		}
 		chipmask |= (0x1 << i);
@@ -2744,7 +3174,8 @@
 		(int) brdp, (int) panelp, (int) portp);
 #endif
 
-	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
+	    (portp == (stlport_t *) NULL))
 		return;
 
 	portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
@@ -2776,7 +3207,8 @@
 		}
 	}
 
-	printk("STALLION: cd1400 device not responding, port=%d panel=%d brd=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	printk("STALLION: cd1400 not responding, port=%d panel=%d brd=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
 }
 
 /*****************************************************************************/
@@ -2887,7 +3319,7 @@
 	baudrate = tiosp->c_cflag & CBAUD;
 	if (baudrate & CBAUDEX) {
 		baudrate &= ~CBAUDEX;
-		if ((baudrate < 1) || (baudrate > 2))
+		if ((baudrate < 1) || (baudrate > 4))
 			tiosp->c_cflag &= ~CBAUDEX;
 		else
 			baudrate += 15;
@@ -2898,11 +3330,15 @@
 			baudrate = 57600;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 			baudrate = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			baudrate = 230400;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			baudrate = 460800;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
 			baudrate = (portp->baud_base / portp->custom_divisor);
 	}
-	if (baudrate > STL_MAXBAUD)
-		baudrate = STL_MAXBAUD;
+	if (baudrate > STL_CD1400MAXBAUD)
+		baudrate = STL_CD1400MAXBAUD;
 
 	if (baudrate > 0) {
 		for (clk = 0; (clk < CD1400_NUMCLKS); clk++) {
@@ -2949,11 +3385,16 @@
  */
 
 #if DEBUG
-	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
-	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5);
-	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff);
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
+	printk("    cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n",
+		cor1, cor2, cor3, cor4, cor5);
+	printk("    mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n",
+		mcor1, mcor2, rtpr, sreron, sreroff);
 	printk("    tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div);
-	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
 #endif
 
 	save_flags(flags);
@@ -3009,7 +3450,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+	printk("stl_cd1400setsignals(portp=%x,dtr=%d,rts=%d)\n",
+		(int) portp, dtr, rts);
 #endif
 
 	msvr1 = 0;
@@ -3059,10 +3501,14 @@
 	sigs = 0;
 	sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
 	sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0;
-	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
-	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
 	sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0;
 	sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0;
+#if 0
+	sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0;
+	sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0;
+#else
+	sigs |= TIOCM_DSR;
+#endif
 	return(sigs);
 }
 
@@ -3078,7 +3524,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+	printk("stl_cd1400enablerxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 	ccr = 0;
 
@@ -3114,8 +3561,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
-		rx, tx);
+	printk("stl_cd1400startrxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 
 	sreron = 0;
@@ -3135,7 +3582,8 @@
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
 	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
+	stl_cd1400setreg(portp, SRER,
+		((stl_cd1400getreg(portp, SRER) & ~sreroff) | sreron));
 	BRDDISABLE(portp->brdnr);
 	if (tx > 0)
 		set_bit(ASYI_TXBUSY, &portp->istate);
@@ -3166,24 +3614,25 @@
 
 /*****************************************************************************/
 
-static void stl_cd1400sendbreak(stlport_t *portp, long len)
+static void stl_cd1400sendbreak(stlport_t *portp, int len)
 {
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+	printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, len);
 #endif
 
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
 	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
-	stl_cd1400setreg(portp, COR2, (stl_cd1400getreg(portp, COR2) | COR2_ETC));
-	stl_cd1400setreg(portp, SRER, ((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY));
+	stl_cd1400setreg(portp, SRER,
+		((stl_cd1400getreg(portp, SRER) & ~SRER_TXDATA) |
+		SRER_TXEMPTY));
 	BRDDISABLE(portp->brdnr);
-	len = len / 5;
-	portp->brklen = (len > 255) ? 255 : len;
-	portp->stats.txbreaks++;
+	portp->brklen = len;
+	if (len == 1)
+		portp->stats.txbreaks++;
 	restore_flags(flags);
 }
 
@@ -3227,7 +3676,9 @@
  *		set the RTS line by hand.
  */
 		if (tty->termios->c_cflag & CRTSCTS) {
-			stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD));
+			stl_cd1400setreg(portp, MCOR1,
+				(stl_cd1400getreg(portp, MCOR1) |
+				FIFO_RTSTHRESHOLD));
 			stl_cd1400setreg(portp, MSVR2, MSVR2_RTS);
 			portp->stats.rxrtson++;
 		}
@@ -3239,7 +3690,8 @@
 			stl_cd1400ccrwait(portp);
 		}
 		if (tty->termios->c_cflag & CRTSCTS) {
-			stl_cd1400setreg(portp, MCOR1, (stl_cd1400getreg(portp, MCOR1) & 0xf0));
+			stl_cd1400setreg(portp, MCOR1,
+				(stl_cd1400getreg(portp, MCOR1) & 0xf0));
 			stl_cd1400setreg(portp, MSVR2, 0);
 			portp->stats.rxrtsoff++;
 		}
@@ -3251,6 +3703,46 @@
 
 /*****************************************************************************/
 
+/*
+ *	Send a flow control character...
+ */
+
+static void stl_cd1400sendflow(stlport_t *portp, int state)
+{
+	struct tty_struct	*tty;
+	unsigned long		flags;
+
+#if DEBUG
+	printk("stl_cd1400sendflow(portp=%x,state=%x)\n", (int) portp, state);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
+	if (state) {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_SENDSCHR1);
+		portp->stats.rxxon++;
+		stl_cd1400ccrwait(portp);
+	} else {
+		stl_cd1400ccrwait(portp);
+		stl_cd1400setreg(portp, CCR, CCR_SENDSCHR2);
+		portp->stats.rxxoff++;
+		stl_cd1400ccrwait(portp);
+	}
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
 static void stl_cd1400flush(stlport_t *portp)
 {
 	unsigned long	flags;
@@ -3277,6 +3769,27 @@
 /*****************************************************************************/
 
 /*
+ *	Return the current state of data flow on this port. This is only
+ *	really interresting when determining if data has fully completed
+ *	transmission or not... This is easy for the cd1400, it accurately
+ *	maintains the busy port flag.
+ */
+
+static int stl_cd1400datastate(stlport_t *portp)
+{
+#if DEBUG
+	printk("stl_cd1400datastate(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return(0);
+
+	return(test_bit(ASYI_TXBUSY, &portp->istate) ? 1 : 0);
+}
+
+/*****************************************************************************/
+
+/*
  *	Interrupt service routine for cd1400 EasyIO boards.
  */
 
@@ -3285,7 +3798,8 @@
 	unsigned char	svrtype;
 
 #if DEBUG
-	printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n", (int) panelp, iobase);
+	printk("stl_cd1400eiointr(panelp=%x,iobase=%x)\n",
+		(int) panelp, iobase);
 #endif
 
 	outb(SVRR, iobase);
@@ -3297,13 +3811,12 @@
 
 	if (svrtype & SVRR_RX)
 		stl_cd1400rxisr(panelp, iobase);
-	if (svrtype & SVRR_TX)
+	else if (svrtype & SVRR_TX)
 		stl_cd1400txisr(panelp, iobase);
-	if (svrtype & SVRR_MDM)
+	else if (svrtype & SVRR_MDM)
 		stl_cd1400mdmisr(panelp, iobase);
 }
 
-
 /*****************************************************************************/
 
 /*
@@ -3315,7 +3828,8 @@
 	unsigned char	svrtype;
 
 #if DEBUG
-	printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp, iobase);
+	printk("stl_cd1400echintr(panelp=%x,iobase=%x)\n", (int) panelp,
+		iobase);
 #endif
 
 	outb(SVRR, iobase);
@@ -3324,12 +3838,48 @@
 	svrtype |= inb(iobase + EREG_DATA);
 	if (svrtype & SVRR_RX)
 		stl_cd1400rxisr(panelp, iobase);
-	if (svrtype & SVRR_TX)
+	else if (svrtype & SVRR_TX)
 		stl_cd1400txisr(panelp, iobase);
-	if (svrtype & SVRR_MDM)
+	else if (svrtype & SVRR_MDM)
 		stl_cd1400mdmisr(panelp, iobase);
 }
 
+
+/*****************************************************************************/
+
+/*
+ *	Unfortunately we need to handle breaks in the TX data stream, since
+ *	this is the only way to generate them on the cd1400.
+ */
+
+static inline int stl_cd1400breakisr(stlport_t *portp, int ioaddr)
+{
+	if (portp->brklen == 1) {
+		outb((COR2 + portp->uartaddr), ioaddr);
+		outb((inb(ioaddr + EREG_DATA) | COR2_ETC),
+			(ioaddr + EREG_DATA));
+		outb((TDR + portp->uartaddr), ioaddr);
+		outb(ETC_CMD, (ioaddr + EREG_DATA));
+		outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
+		outb((SRER + portp->uartaddr), ioaddr);
+		outb((inb(ioaddr + EREG_DATA) & ~(SRER_TXDATA | SRER_TXEMPTY)),
+			(ioaddr + EREG_DATA));
+		return(1);
+	} else if (portp->brklen > 1) {
+		outb((TDR + portp->uartaddr), ioaddr);
+		outb(ETC_CMD, (ioaddr + EREG_DATA));
+		outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
+		portp->brklen = -1;
+		return(1);
+	} else {
+		outb((COR2 + portp->uartaddr), ioaddr);
+		outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC),
+			(ioaddr + EREG_DATA));
+		portp->brklen = 0;
+	}
+	return(0);
+}
+
 /*****************************************************************************/
 
 /*
@@ -3356,7 +3906,8 @@
 #endif
 
 	ioack = inb(ioaddr + EREG_TXACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
+	if (((ioack & panelp->ackmask) != 0) ||
+	    ((ioack & ACK_TYPMASK) != ACK_TYPTX)) {
 		printk("STALLION: bad TX interrupt ack value=%x\n", ioack);
 		return;
 	}
@@ -3367,29 +3918,15 @@
  *	this is the only way to generate them on the cd1400. Do it now if
  *	a break is to be sent.
  */
-	if (portp->brklen != 0) {
-		if (portp->brklen > 0) {
-			outb((TDR + portp->uartaddr), ioaddr);
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STARTBREAK, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_DELAY, (ioaddr + EREG_DATA));
-			outb(portp->brklen, (ioaddr + EREG_DATA));
-			outb(ETC_CMD, (ioaddr + EREG_DATA));
-			outb(ETC_STOPBREAK, (ioaddr + EREG_DATA));
-			portp->brklen = -1;
+	if (portp->brklen != 0)
+		if (stl_cd1400breakisr(portp, ioaddr))
 			goto stl_txalldone;
-		} else {
-			outb((COR2 + portp->uartaddr), ioaddr);
-			outb((inb(ioaddr + EREG_DATA) & ~COR2_ETC), (ioaddr + EREG_DATA));
-			portp->brklen = 0;
-		}
-	}
 
 	head = portp->tx.head;
 	tail = portp->tx.tail;
 	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+	if ((len == 0) || ((len < STL_TXBUFLOW) &&
+	    (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
 		set_bit(ASYI_TXLOW, &portp->istate);
 		queue_task(&portp->tqueue, &tq_scheduler);
 	}
@@ -3461,8 +3998,9 @@
 	if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) {
 		outb((RDCR + portp->uartaddr), ioaddr);
 		len = inb(ioaddr + EREG_DATA);
-		if ((tty == (struct tty_struct *) NULL) || (tty->flip.char_buf_ptr == (char *) NULL) ||
-				((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
+		if ((tty == (struct tty_struct *) NULL) ||
+		    (tty->flip.char_buf_ptr == (char *) NULL) ||
+		    ((buflen = TTY_FLIPBUF_SIZE - tty->flip.count) == 0)) {
 			outb((RDSR + portp->uartaddr), ioaddr);
 			insb((ioaddr + EREG_DATA), &stl_unwanted[0], len);
 			portp->stats.rxlost += len;
@@ -3499,16 +4037,15 @@
 				portp->stats.txxoff++;
 			goto stl_rxalldone;
 		}
-		if ((tty != (struct tty_struct *) NULL) && ((portp->rxignoremsk & status) == 0)) {
+		if ((tty != (struct tty_struct *) NULL) &&
+		    ((portp->rxignoremsk & status) == 0)) {
 			if (portp->rxmarkmsk & status) {
 				if (status & ST_BREAK) {
 					status = TTY_BREAK;
-#ifndef MODULE
 					if (portp->flags & ASYNC_SAK) {
 						do_SAK(tty);
 						BRDENABLE(portp->brdnr, portp->pagenr);
 					}
-#endif
 				} else if (status & ST_PARITY) {
 					status = TTY_PARITY;
 				} else if (status & ST_FRAMING) {
@@ -3559,7 +4096,8 @@
 #endif
 
 	ioack = inb(ioaddr + EREG_MDACK);
-	if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
+	if (((ioack & panelp->ackmask) != 0) ||
+	    ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) {
 		printk("STALLION: bad MODEM interrupt ack value=%x\n", ioack);
 		return;
 	}
@@ -3643,7 +4181,8 @@
 	int	nrchips, ioaddr;
 
 #if DEBUG
-	printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
+	printk("stl_sc26198panelinit(brdp=%x,panelp=%x)\n",
+		(int) brdp, (int) panelp);
 #endif
 
 	BRDENABLE(panelp->brdnr, panelp->pagenr);
@@ -3662,7 +4201,9 @@
 		outb(CR_RESETALL, (ioaddr + XP_DATA));
 		outb(TSTR, (ioaddr + XP_ADDR));
 		if (inb(ioaddr + XP_DATA) != 0) {
-			printk("STALLION: sc26198 not responding, brd=%d panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i);
+			printk("STALLION: sc26198 not responding, "
+				"brd=%d panel=%d chip=%d\n",
+				panelp->brdnr, panelp->panelnr, i);
 			continue;
 		}
 		chipmask |= (0x1 << i);
@@ -3689,7 +4230,8 @@
 		(int) brdp, (int) panelp, (int) portp);
 #endif
 
-	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) || (portp == (stlport_t *) NULL))
+	if ((brdp == (stlbrd_t *) NULL) || (panelp == (stlpanel_t *) NULL) ||
+	    (portp == (stlport_t *) NULL))
 		return;
 
 	portp->ioaddr = panelp->iobase + ((portp->portnr < 8) ? 0 : 4);
@@ -3735,7 +4277,8 @@
  */
 	portp->rxignoremsk = 0;
 	if (tiosp->c_iflag & IGNPAR)
-		portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING | SR_RXOVERRUN);
+		portp->rxignoremsk |= (SR_RXPARITY | SR_RXFRAMING |
+			SR_RXOVERRUN);
 	if (tiosp->c_iflag & IGNBRK)
 		portp->rxignoremsk |= SR_RXBREAK;
 
@@ -3795,7 +4338,7 @@
 	baudrate = tiosp->c_cflag & CBAUD;
 	if (baudrate & CBAUDEX) {
 		baudrate &= ~CBAUDEX;
-		if ((baudrate < 1) || (baudrate > 5))
+		if ((baudrate < 1) || (baudrate > 4))
 			tiosp->c_cflag &= ~CBAUDEX;
 		else
 			baudrate += 15;
@@ -3806,11 +4349,15 @@
 			baudrate = 57600;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 			baudrate = 115200;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			baudrate = 230400;
+		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			baudrate = 460800;
 		else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
 			baudrate = (portp->baud_base / portp->custom_divisor);
 	}
-	if (baudrate > STL_MAXBAUD)
-		baudrate = STL_MAXBAUD;
+	if (baudrate > STL_SC26198MAXBAUD)
+		baudrate = STL_SC26198MAXBAUD;
 
 	if (baudrate > 0) {
 		for (clk = 0; (clk < SC26198_NRBAUDS); clk++) {
@@ -3856,10 +4403,13 @@
  */
 
 #if DEBUG
-	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr);
+	printk("SETPORT: portnr=%d panelnr=%d brdnr=%d\n",
+		portp->portnr, portp->panelnr, portp->brdnr);
 	printk("    mr0=%x mr1=%x mr2=%x clk=%x\n", mr0, mr1, mr2, clk);
 	printk("    iopr=%x imron=%x imroff=%x\n", iopr, imron, imroff);
-	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
+	printk("    schr1=%x schr2=%x schr3=%x schr4=%x\n",
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP],
+		tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
 #endif
 
 	save_flags(flags);
@@ -3905,7 +4455,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts);
+	printk("stl_sc26198setsignals(portp=%x,dtr=%d,rts=%d)\n",
+		(int) portp, dtr, rts);
 #endif
 
 	iopioron = 0;
@@ -3956,6 +4507,7 @@
 	sigs |= (ipr & IPR_CTS) ? 0 : TIOCM_CTS;
 	sigs |= (ipr & IPR_DTR) ? 0: TIOCM_DTR;
 	sigs |= (ipr & IPR_RTS) ? 0: TIOCM_RTS;
+	sigs |= TIOCM_DSR;
 	return(sigs);
 }
 
@@ -3971,7 +4523,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx);
+	printk("stl_sc26198enablerxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 
 	ccr = portp->crenable;
@@ -4005,8 +4558,8 @@
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp,
-		rx, tx);
+	printk("stl_sc26198startrxtx(portp=%x,rx=%d,tx=%d)\n",
+		(int) portp, rx, tx);
 #endif
 
 	imr = portp->imr;
@@ -4055,28 +4608,23 @@
 
 /*****************************************************************************/
 
-static void stl_sc26198sendbreak(stlport_t *portp, long len)
+static void stl_sc26198sendbreak(stlport_t *portp, int len)
 {
 	unsigned long	flags;
 
 #if DEBUG
-	printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len);
+	printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, len);
 #endif
 
-	current->state = TASK_INTERRUPTIBLE;
-	current->timeout = jiffies + (len / (1000 / HZ));
-
 	save_flags(flags);
 	cli();
 	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
-	BRDDISABLE(portp->brdnr);
-	portp->stats.txbreaks++;
-
-	schedule();
-
-	BRDENABLE(portp->brdnr, portp->pagenr);
-	stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+	if (len == 1) {
+		stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
+		portp->stats.txbreaks++;
+	} else {
+		stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
+	}
 	BRDDISABLE(portp->brdnr);
 	restore_flags(flags);
 }
@@ -4114,6 +4662,7 @@
 			stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
 			mr0 |= MR0_SWFRX;
 			portp->stats.rxxon++;
+			stl_sc26198wait(portp);
 			stl_sc26198setreg(portp, MR0, mr0);
 		}
 /*
@@ -4136,6 +4685,7 @@
 			stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
 			mr0 &= ~MR0_SWFRX;
 			portp->stats.rxxoff++;
+			stl_sc26198wait(portp);
 			stl_sc26198setreg(portp, MR0, mr0);
 		}
 		if (tty->termios->c_cflag & CRTSCTS) {
@@ -4153,6 +4703,52 @@
 
 /*****************************************************************************/
 
+/*
+ *	Send a flow control character.
+ */
+
+static void stl_sc26198sendflow(stlport_t *portp, int state)
+{
+	struct tty_struct	*tty;
+	unsigned long		flags;
+	unsigned char		mr0;
+
+#if DEBUG
+	printk("stl_sc26198sendflow(portp=%x,state=%x)\n", (int) portp, state);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+	tty = portp->tty;
+	if (tty == (struct tty_struct *) NULL)
+		return;
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	if (state) {
+		mr0 = stl_sc26198getreg(portp, MR0);
+		stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+		stl_sc26198setreg(portp, SCCR, CR_TXSENDXON);
+		mr0 |= MR0_SWFRX;
+		portp->stats.rxxon++;
+		stl_sc26198wait(portp);
+		stl_sc26198setreg(portp, MR0, mr0);
+	} else {
+		mr0 = stl_sc26198getreg(portp, MR0);
+		stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
+		stl_sc26198setreg(portp, SCCR, CR_TXSENDXOFF);
+		mr0 &= ~MR0_SWFRX;
+		portp->stats.rxxoff++;
+		stl_sc26198wait(portp);
+		stl_sc26198setreg(portp, MR0, mr0);
+	}
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+}
+
+/*****************************************************************************/
+
 static void stl_sc26198flush(stlport_t *portp)
 {
 	unsigned long	flags;
@@ -4177,10 +4773,67 @@
 /*****************************************************************************/
 
 /*
+ *	Return the current state of data flow on this port. This is only
+ *	really interresting when determining if data has fully completed
+ *	transmission or not... The sc26198 interrupt scheme cannot
+ *	determine when all data has actually drained, so we need to
+ *	check the port statusy register to be sure.
+ */
+
+static int stl_sc26198datastate(stlport_t *portp)
+{
+	unsigned long	flags;
+	unsigned char	sr;
+
+#if DEBUG
+	printk("stl_sc26198datastate(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return(0);
+	if (test_bit(ASYI_TXBUSY, &portp->istate))
+		return(1);
+
+	save_flags(flags);
+	cli();
+	BRDENABLE(portp->brdnr, portp->pagenr);
+	sr = stl_sc26198getreg(portp, SR);
+	BRDDISABLE(portp->brdnr);
+	restore_flags(flags);
+
+	return((sr & SR_TXEMPTY) ? 0 : 1);
+}
+
+/*****************************************************************************/
+
+/*
+ *	Delay for a small amount of time, to give the sc26198 a chance
+ *	to process a command...
+ */
+
+static void stl_sc26198wait(stlport_t *portp)
+{
+	int	i;
+
+#if DEBUG
+	printk("stl_sc26198wait(portp=%x)\n", (int) portp);
+#endif
+
+	if (portp == (stlport_t *) NULL)
+		return;
+
+	for (i = 0; (i < 20); i++)
+		stl_sc26198getglobreg(portp, TSTR);
+}
+
+/*****************************************************************************/
+
+/*
  *	If we are TX flow controlled and in IXANY mode then we may
  *	need to unflow control here. We gotta do this because of the
  *	automatic flow control modes of the sc26198.
  */
+
 static inline void stl_sc26198txunflow(stlport_t *portp, struct tty_struct *tty)
 {
 	unsigned char	mr0;
@@ -4188,6 +4841,7 @@
 	mr0 = stl_sc26198getreg(portp, MR0);
 	stl_sc26198setreg(portp, MR0, (mr0 & ~MR0_SWFRXTX));
 	stl_sc26198setreg(portp, SCCR, CR_HOSTXON);
+	stl_sc26198wait(portp);
 	stl_sc26198setreg(portp, MR0, mr0);
 	clear_bit(ASYI_TXFLOWED, &portp->istate);
 }
@@ -4247,7 +4901,8 @@
 	head = portp->tx.head;
 	tail = portp->tx.tail;
 	len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head));
-	if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
+	if ((len == 0) || ((len < STL_TXBUFLOW) &&
+	    (test_bit(ASYI_TXLOW, &portp->istate) == 0))) {
 		set_bit(ASYI_TXLOW, &portp->istate);
 		queue_task(&portp->tqueue, &tq_scheduler);
 	}
@@ -4340,8 +4995,8 @@
  */
 	if (test_bit(ASYI_TXFLOWED, &portp->istate)) {
 		if ((tty != (struct tty_struct *) NULL) &&
-				(tty->termios != (struct termios *) NULL) &&
-				(tty->termios->c_iflag & IXANY)) {
+		    (tty->termios != (struct termios *) NULL) &&
+		    (tty->termios->c_iflag & IXANY)) {
 			stl_sc26198txunflow(portp, tty);
 		}
 	}
@@ -4371,16 +5026,14 @@
 		portp->stats.rxbreaks++;
 
 	if ((tty != (struct tty_struct *) NULL) &&
-		    	((portp->rxignoremsk & status) == 0)) {
+	    ((portp->rxignoremsk & status) == 0)) {
 		if (portp->rxmarkmsk & status) {
 			if (status & SR_RXBREAK) {
 				status = TTY_BREAK;
-#ifndef MODULE
 				if (portp->flags & ASYNC_SAK) {
 					do_SAK(tty);
 					BRDENABLE(portp->brdnr, portp->pagenr);
 				}
-#endif
 			} else if (status & SR_RXPARITY) {
 				status = TTY_PARITY;
 			} else if (status & SR_RXFRAMING) {
@@ -4487,7 +5140,6 @@
 		stl_sc26198rxbadchars(portp);
 		break;
 	default:
-		/*printk("%s(%d): unknown other intr cir=%x, iack=%x!\n", __FILE__, __LINE__, cir, iack);*/
 		break;
 	}
 }
diff -u --recursive --new-file v2.1.73/linux/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c
--- v2.1.73/linux/drivers/net/hamradio/scc.c	Thu Dec  4 14:53:55 1997
+++ linux/drivers/net/hamradio/scc.c	Fri Dec 19 10:56:30 1997
@@ -166,10 +166,7 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <time.h>
+#include <linux/ctype.h>
 #include <linux/kernel.h>
 #include <linux/proc_fs.h>
 
diff -u --recursive --new-file v2.1.73/linux/fs/Config.in linux/fs/Config.in
--- v2.1.73/linux/fs/Config.in	Wed Dec 10 11:12:44 1997
+++ linux/fs/Config.in	Fri Dec 19 15:24:21 1997
@@ -53,6 +53,12 @@
   tristate 'NCP filesystem support (to mount NetWare volumes)' CONFIG_NCP_FS
 fi
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+
+tristate 'NTFS  filesystem support (read only)' CONFIG_NTFS_FS
+if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool '   NTFS read-write support (experimental)' CONFIG_NTFS_RW
+fi
+
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
 tristate 'Amiga FFS filesystem support' CONFIG_AFFS_FS
 tristate 'ROM filesystem support' CONFIG_ROMFS_FS
diff -u --recursive --new-file v2.1.73/linux/fs/Makefile linux/fs/Makefile
--- v2.1.73/linux/fs/Makefile	Tue Dec  2 16:45:19 1997
+++ linux/fs/Makefile	Fri Dec 19 15:24:21 1997
@@ -16,7 +16,7 @@
 		inode.o dcache.o attr.o bad_inode.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
-ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos \
+ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
 		hpfs sysv smbfs ncpfs ufs affs romfs autofs lockd nfsd nls
 
 ifeq ($(CONFIG_QUOTA),y)
@@ -167,6 +167,14 @@
 else
   ifeq ($(CONFIG_HPFS_FS),m)
   MOD_SUB_DIRS += hpfs
+  endif
+endif
+
+ifeq ($(CONFIG_NTFS_FS),y)
+SUB_DIRS += ntfs
+else
+  ifeq ($(CONFIG_NTFS_FS),m)
+  MOD_SUB_DIRS += ntfs
   endif
 endif
 
diff -u --recursive --new-file v2.1.73/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.73/linux/fs/filesystems.c	Wed Nov 12 13:34:27 1997
+++ linux/fs/filesystems.c	Fri Dec 19 15:24:21 1997
@@ -24,6 +24,7 @@
 #include <linux/ufs_fs.h>
 #include <linux/romfs_fs.h>
 #include <linux/auto_fs.h>
+#include <linux/ntfs_fs.h>
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -103,6 +104,10 @@
 
 #ifdef CONFIG_HPFS_FS
 	init_hpfs_fs();
+#endif
+
+#ifdef CONFIG_NTFS_FS
+	init_ntfs_fs();
 #endif
 
 #ifdef CONFIG_AFFS_FS
diff -u --recursive --new-file v2.1.73/linux/fs/lockd/mon.c linux/fs/lockd/mon.c
--- v2.1.73/linux/fs/lockd/mon.c	Mon Apr  7 11:35:30 1997
+++ linux/fs/lockd/mon.c	Fri Dec 19 10:54:10 1997
@@ -33,29 +33,38 @@
 nsm_mon_unmon(struct nlm_host *host, char *what, u32 proc)
 {
 	struct rpc_clnt	*clnt;
+	int		status;
 	struct nsm_args	args;
 	struct nsm_res	res;
-	int		status;
 
 	dprintk("lockd: nsm_%s(%s)\n", what, host->h_name);
-	if (!(clnt = nsm_create()))
-		return -EACCES;
+	status = -EACCES;
+	clnt = nsm_create();
+	if (!clnt)
+		goto out;
 
 	args.addr = host->h_addr.sin_addr.s_addr;
 	args.prog = NLM_PROGRAM;
 	args.vers = 1;
 	args.proc = NLMPROC_NSM_NOTIFY;
 
-	if ((status = rpc_call(clnt, proc, &args, &res, 0)) < 0)
-		return status;
+	status = rpc_call(clnt, proc, &args, &res, 0);
+	if (status < 0) {
+		printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
+			status);
+		goto out;
+	}
 
+	status = -EACCES;
 	if (res.status != 0) {
 		printk(KERN_NOTICE "lockd: cannot %s %s\n", what, host->h_name);
-		return -EACCES;
+		goto out;
 	}
 
 	nsm_local_state = res.state;
-	return 0;
+	status = 0;
+out:
+	return status;
 }
 
 /*
@@ -66,7 +75,8 @@
 {
 	int		status;
 
-	if ((status = nsm_mon_unmon(host, "monitor", SM_MON)) >= 0)
+	status = nsm_mon_unmon(host, "monitor", SM_MON);
+	if (status >= 0)
 		host->h_monitored = 1;
 	return status;
 }
@@ -90,28 +100,32 @@
 static struct rpc_clnt *
 nsm_create(void)
 {
-	struct sockaddr_in	sin;
 	struct rpc_xprt		*xprt;
-	struct rpc_clnt		*clnt;
+	struct rpc_clnt		*clnt = NULL;
+	struct sockaddr_in	sin;
 
 	sin.sin_family = AF_INET;
 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 	sin.sin_port = 0;
 
-	if (!(xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL)))
-		return NULL;
+	xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
+	if (!xprt)
+		goto out;
 
 	clnt = rpc_create_client(xprt, "localhost",
 				&nsm_program, SM_VERSION,
 				RPC_AUTH_NULL);
-	if (!clnt) {
-		xprt_destroy(xprt);
-	} else {
-		clnt->cl_softrtry = 1;
-		clnt->cl_chatty   = 1;
-		clnt->cl_oneshot  = 1;
-	}
+	if (!clnt)
+		goto out_destroy;
+	clnt->cl_softrtry = 1;
+	clnt->cl_chatty   = 1;
+	clnt->cl_oneshot  = 1;
+out:
 	return clnt;
+
+out_destroy:
+	xprt_destroy(xprt);
+	goto out;
 }
 
 /*
diff -u --recursive --new-file v2.1.73/linux/fs/lockd/svc.c linux/fs/lockd/svc.c
--- v2.1.73/linux/fs/lockd/svc.c	Mon Dec  1 12:04:14 1997
+++ linux/fs/lockd/svc.c	Fri Dec 19 10:54:10 1997
@@ -18,7 +18,6 @@
 
 #include <linux/sched.h>
 #include <linux/errno.h>
-#include <linux/nfs.h>
 #include <linux/in.h>
 #include <linux/uio.h>
 #include <linux/version.h>
@@ -33,11 +32,11 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/lockd/lockd.h>
-
+#include <linux/nfs.h>
 
 #define NLMDBG_FACILITY		NLMDBG_SVC
 #define LOCKD_BUFSIZE		(1024 + NLMSSVC_XDRSIZE)
-#define BLOCKABLE_SIGS		(~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+#define ALLOWED_SIGS		(sigmask(SIGKILL) | sigmask(SIGSTOP))
 
 extern struct svc_program	nlmsvc_program;
 struct nlmsvc_binding *		nlmsvc_ops = NULL;
@@ -148,23 +147,22 @@
 		}
 
 		dprintk("lockd: request from %08x\n",
-				(unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
+			(unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
 
 		/*
 		 * Look up the NFS client handle. The handle is needed for
 		 * all but the GRANTED callback RPCs.
 		 */
+		rqstp->rq_client = NULL;
 		if (nlmsvc_ops) {
 			nlmsvc_ops->exp_readlock();
 			rqstp->rq_client =
 				nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
-		} else {
-			rqstp->rq_client = NULL;
 		}
 
-		/* Process request with all signals blocked.  */
+		/* Process request with signals blocked.  */
 		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv(&current->blocked, ~BLOCKABLE_SIGS);
+		siginitsetinv(&current->blocked, ALLOWED_SIGS);
 		recalc_sigpending(current);
 		spin_unlock_irq(&current->sigmask_lock);
 		
@@ -202,22 +200,6 @@
 }
 
 /*
- * Make a socket for lockd
- * FIXME: Move this to net/sunrpc/svc.c so that we can share this with nfsd.
- */
-static int
-lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
-{
-	struct sockaddr_in	sin;
-
-	dprintk("lockd: creating socket proto = %d\n", protocol);
-	sin.sin_family      = AF_INET;
-	sin.sin_addr.s_addr = INADDR_ANY;
-	sin.sin_port        = htons(port);
-	return svc_create_socket(serv, protocol, &sin);
-}
-
-/*
  * Bring up the lockd process if it's not already up.
  */
 int
@@ -252,8 +234,8 @@
 		goto out;
 	}
 
-	if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0 
-	 || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) {
+	if ((error = svc_makesock(serv, IPPROTO_UDP, 0)) < 0 
+	 || (error = svc_makesock(serv, IPPROTO_TCP, 0)) < 0) {
 		printk("lockd_up: makesock failed, error=%d\n", error);
 		goto destroy_and_out;
 	}
@@ -302,13 +284,17 @@
 	 * Wait for the lockd process to exit, but since we're holding
 	 * the lockd semaphore, we can't wait around forever ...
 	 */
-	current->timeout = jiffies + 5 * HZ;
+	current->sigpending = 0;
+	current->timeout = jiffies + HZ;
 	interruptible_sleep_on(&lockd_exit);
 	current->timeout = 0;
 	if (nlmsvc_pid) {
 		printk("lockd_down: lockd failed to exit, clearing pid\n");
 		nlmsvc_pid = 0;
 	}
+	spin_lock_irq(&current->sigmask_lock);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 out:
 	up(&nlmsvc_sema);
 }
diff -u --recursive --new-file v2.1.73/linux/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- v2.1.73/linux/fs/minix/bitmap.c	Thu Sep 11 09:02:23 1997
+++ linux/fs/minix/bitmap.c	Fri Dec 19 15:55:45 1997
@@ -21,17 +21,31 @@
 
 static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
 
-static unsigned long count_free(struct buffer_head *map[], unsigned numblocks)
+static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits)
 {
 	unsigned i, j, sum = 0;
 	struct buffer_head *bh;
   
-	for (i=0; i<numblocks; i++) {
+	for (i=0; i<numblocks-1; i++) {
 		if (!(bh=map[i])) 
 			return(0);
 		for (j=0; j<BLOCK_SIZE; j++)
-			sum += nibblemap[bh->b_data[j] & 0xf] 
-				+ nibblemap[(bh->b_data[j]>>4)&0xf];
+			sum += nibblemap[bh->b_data[j] & 0xf]
+				+ nibblemap[(bh->b_data[j]>>4) & 0xf];
+	}
+
+	if (numblocks==0 || !(bh=map[numblocks-1]))
+		return(0);
+	i = (numbits-(numblocks-1)*BLOCK_SIZE*8)/8;
+	for (j=0; j<i; j++) {
+		sum += nibblemap[bh->b_data[j] & 0xf]
+			+ nibblemap[(bh->b_data[j]>>4) & 0xf];
+	}
+
+	i = numbits%8;
+	if (i!=0) {
+		i = bh->b_data[j] | ~((1<<i) - 1);
+		sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf];
 	}
 	return(sum);
 }
@@ -108,8 +122,9 @@
 
 unsigned long minix_count_free_blocks(struct super_block *sb)
 {
-	return (count_free(sb->u.minix_sb.s_zmap,sb->u.minix_sb.s_zmap_blocks)
-		 << sb->u.minix_sb.s_log_zone_size);
+	return (count_free(sb->u.minix_sb.s_zmap, sb->u.minix_sb.s_zmap_blocks,
+		sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone + 1)
+		<< sb->u.minix_sb.s_log_zone_size);
 }
 
 static struct buffer_head *V1_minix_clear_inode(struct inode *inode)
@@ -266,5 +281,6 @@
 
 unsigned long minix_count_free_inodes(struct super_block *sb)
 {
-	return count_free(sb->u.minix_sb.s_imap,sb->u.minix_sb.s_imap_blocks);
+	return count_free(sb->u.minix_sb.s_imap, sb->u.minix_sb.s_imap_blocks,
+		sb->u.minix_sb.s_ninodes + 1);
 }
diff -u --recursive --new-file v2.1.73/linux/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- v2.1.73/linux/fs/nfsd/nfssvc.c	Thu Dec  4 14:53:56 1997
+++ linux/fs/nfsd/nfssvc.c	Fri Dec 19 10:54:10 1997
@@ -36,28 +36,13 @@
 #define NFSDDBG_FACILITY	NFSDDBG_SVC
 #define NFSD_BUFSIZE		(1024 + NFSSVC_MAXBLKSIZE)
 
-#define BLOCKABLE_SIGS	(~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-#define SHUTDOWN_SIGS	(sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+#define ALLOWED_SIGS	(sigmask(SIGKILL) | sigmask(SIGSTOP))
+#define SHUTDOWN_SIGS	(sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGTERM))
 
 extern struct svc_program	nfsd_program;
 static void			nfsd(struct svc_rqst *rqstp);
 struct timeval			nfssvc_boot = { 0, 0 };
 
-/*
- * Make a socket for nfsd
- */
-static int
-nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
-{
-	struct sockaddr_in	sin;
-
-	dprintk("nfsd: creating socket proto = %d\n", protocol);
-	sin.sin_family      = AF_INET;
-	sin.sin_addr.s_addr = INADDR_ANY;
-	sin.sin_port        = htons(port);
-	return svc_create_socket(serv, protocol, &sin);
-}
-
 int
 nfsd_svc(unsigned short port, int nrservs)
 {
@@ -65,17 +50,19 @@
 	int			error;
 
 	dprintk("nfsd: creating service\n");
+	error = -EINVAL;
 	if (nrservs < 0)
-		return -EINVAL;
+		goto out;
 	if (nrservs > NFSD_MAXSERVS)
 		nrservs = NFSD_MAXSERVS;
 
+	error = -ENOMEM;
 	serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
 	if (serv == NULL) 
-		return -ENOMEM;
+		goto out;
 
-	if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0
-	 || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0)
+	if ((error = svc_makesock(serv, IPPROTO_UDP, port)) < 0
+	 || (error = svc_makesock(serv, IPPROTO_TCP, port)) < 0)
 		goto failure;
 
 	while (nrservs--) {
@@ -86,6 +73,7 @@
 
 failure:
 	svc_destroy(serv);		/* Release server */
+out:
 	return error;
 }
 
@@ -98,16 +86,15 @@
 	struct svc_serv	*serv = rqstp->rq_server;
 	int		oldumask, err;
 
-	lock_kernel();
 	/* Lock module and set up kernel thread */
 	MOD_INC_USE_COUNT;
+	lock_kernel();
 	exit_mm(current);
 	current->session = 1;
 	current->pgrp = 1;
 	sprintf(current->comm, "nfsd");
 
 	oldumask = current->fs->umask;		/* Set umask to 0.  */
-	siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
 	current->fs->umask = 0;
 	nfssvc_boot = xtime;			/* record boot time */
 	lockd_up();				/* start lockd */
@@ -116,6 +103,12 @@
 	 * The main request loop
 	 */
 	for (;;) {
+		/* Block all but the shutdown signals */
+		spin_lock_irq(&current->sigmask_lock);
+		siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+		recalc_sigpending(current);
+		spin_unlock_irq(&current->sigmask_lock);
+
 		/*
 		 * Find a socket with data available and call its
 		 * recvfrom routine.
@@ -140,18 +133,13 @@
 			svc_drop(rqstp);
 			serv->sv_stats->rpcbadclnt++;
 		} else {
-			/* Process request with all signals blocked.  */
+			/* Process request with signals blocked.  */
 			spin_lock_irq(&current->sigmask_lock);
-			siginitsetinv(&current->blocked, ~BLOCKABLE_SIGS);
+			siginitsetinv(&current->blocked, ALLOWED_SIGS);
 			recalc_sigpending(current);
 			spin_unlock_irq(&current->sigmask_lock);
 			
 			svc_process(serv, rqstp);
-
-			spin_lock_irq(&current->sigmask_lock);
-			siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
-			recalc_sigpending(current);
-			spin_unlock_irq(&current->sigmask_lock);
 		}
 
 		/* Unlock export hash tables */
diff -u --recursive --new-file v2.1.73/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
--- v2.1.73/linux/fs/nfsd/vfs.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/nfsd/vfs.c	Fri Dec 19 10:51:50 1997
@@ -568,7 +568,8 @@
 		dchild = lookup_dentry(fname, dget(dentry), 0);
 		err = PTR_ERR(dchild);
 		if(IS_ERR(dchild))
-			return nfserrno(-err);
+			goto out_nfserr;
+		fh_compose(resfhp, fhp->fh_export, dchild);
 	} else
 		dchild = resfhp->fh_dentry;
 	/*
@@ -597,19 +598,14 @@
 	fh_unlock(fhp);
 
 	if (err < 0)
-		return nfserrno(-err);
+		goto out_nfserr;
 	if (EX_ISSYNC(fhp->fh_export))
 		write_inode_now(dirp);
 
 	/*
-	 * Assemble the file handle for the newly created file,
-	 * or update the filehandle to get the new inode info.
+	 * Update the filehandle to get the new inode info.
 	 */
-	if (!resfhp->fh_dverified) {
-		fh_compose(resfhp, fhp->fh_export, dchild);
-	} else {
-		fh_update(resfhp);
-	}
+	fh_update(resfhp);
 
 	/* Set file attributes. Mode has already been set and
 	 * setting uid/gid works only for root. Irix appears to
@@ -621,6 +617,10 @@
 		err = nfsd_setattr(rqstp, resfhp, iap);
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -694,19 +694,21 @@
 		goto out;
 
 	UPDATE_ATIME(inode);
+	/* N.B. Why does this call need a get_fs()?? */
 	oldfs = get_fs(); set_fs(KERNEL_DS);
 	err = inode->i_op->readlink(inode, buf, *lenp);
 	set_fs(oldfs);
 
 	if (err < 0)
-		err = nfserrno(-err);
-	else {
-		*lenp = err;
-		err = 0;
-	}
-
+		goto out_nfserr;
+	*lenp = err;
+	err = 0;
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -755,12 +757,14 @@
 		}
 	}
 	fh_compose(resfhp, fhp->fh_export, dnew);
-
-out_nfserr:
 	if (err)
-		err = nfserrno(-err);
+		goto out_nfserr;
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -821,11 +825,14 @@
 	}
 dput_and_out:
 	dput(dnew);
-out_nfserr:
 	if (err)
-		err = nfserrno(-err);
+		goto out_nfserr;
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -917,11 +924,14 @@
 
 out_dput_old:
 	dput(odentry);
-out_nfserr:
 	if (err)
-		err = nfserrno(-err);
+		goto out_nfserr;
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -965,14 +975,16 @@
 	fh_unlock(fhp);
 
 	dput(rdentry);
-	if (!err && EX_ISSYNC(fhp->fh_export))
-		write_inode_now(dirp);
-
-out_nfserr:
 	if (err)
-		err = nfserrno(-err);
+		goto out_nfserr;
+	if (EX_ISSYNC(fhp->fh_export))
+		write_inode_now(dirp);
 out:
 	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out;
 }
 
 /*
@@ -982,22 +994,23 @@
 nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, 
 			encode_dent_fn func, u32 *buffer, int *countp)
 {
-	struct readdir_cd cd;
 	struct inode	*inode;
-	struct file	file;
 	u32		*p;
 	int		oldlen, eof, err;
+	struct file	file;
+	struct readdir_cd cd;
 
+	err = 0;
 	if (offset > ~(u32) 0)
-		return 0;
+		goto out;
 
-	if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0)
-		return err;
+	err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file);
+	if (err)
+		goto out;
 
-	if (!file.f_op->readdir) {
-		nfsd_close(&file);
-		return nfserr_notdir;
-	}
+	err = nfserr_notdir;
+	if (!file.f_op->readdir)
+		goto out_close;
 	file.f_pos = offset;
 
 	/* Set up the readdir context */
@@ -1012,7 +1025,7 @@
 	 * may choose to do less.
 	 */
 	inode = file.f_dentry->d_inode;
-	do {
+	while (1) {
 		oldlen = cd.buflen;
 
 		/*
@@ -1020,16 +1033,16 @@
 			file.f_inode->i_dev, file.f_inode->i_ino,
 			(int) file.f_pos, (int) oldlen, (int) cd.buflen);
 		 */
-		err = file.f_op->readdir(&file,
-					 &cd, (filldir_t) func);
-
-		if (err < 0) {
-			nfsd_close(&file);
-			return nfserrno(-err);
-		}
+		down(&inode->i_sem);
+		err = file.f_op->readdir(&file, &cd, (filldir_t) func);
+		up(&inode->i_sem);
+		if (err < 0)
+			goto out_nfserr;
 		if (oldlen == cd.buflen)
 			break;
-	} while (oldlen != cd.buflen && !cd.eob);
+		if (cd.eob)
+			break;
+	}
 
 	/* If we didn't fill the buffer completely, we're at EOF */
 	eof = !cd.eob;
@@ -1040,9 +1053,6 @@
 	if (cd.offset && !eof)
 		*cd.offset = htonl(file.f_pos);
 
-	/* Close the file */
-	nfsd_close(&file);
-
 	p = cd.buffer;
 	*p++ = 0;			/* no more entries */
 	*p++ = htonl(eof);		/* end of directory */
@@ -1051,7 +1061,15 @@
 	dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n",
 				*countp, eof,
 				cd.offset? ntohl(*cd.offset) : -1);
-	return 0;
+	err = 0;
+out_close:
+	nfsd_close(&file);
+out:
+	return err;
+
+out_nfserr:
+	err = nfserrno(-err);
+	goto out_close;
 }
 
 /*
diff -u --recursive --new-file v2.1.73/linux/fs/nls/Config.in linux/fs/nls/Config.in
--- v2.1.73/linux/fs/nls/Config.in	Sat Nov  1 11:04:27 1997
+++ linux/fs/nls/Config.in	Fri Dec 19 15:24:21 1997
@@ -6,7 +6,8 @@
 comment 'Native Language Support'
 
 # msdos and Joliet want NLS
-if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" ]; then
+if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
+	-o "$CONFIG_NTFS_FS" != "n" ]; then
   define_bool CONFIG_NLS y
 else
   define_bool CONFIG_NLS n
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/Makefile linux/fs/ntfs/Makefile
--- v2.1.73/linux/fs/ntfs/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/Makefile	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,9 @@
+# Rules for making the NTFS driver
+
+O_TARGET := ntfs.o
+O_OBJS   := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o
+M_OBJS   := $(O_TARGET)
+EXTRA_CFLAGS = -DNTFS_IN_LINUX_KERNEL -DNTFS_VERSION=\"971219\"
+
+include $(TOPDIR)/Rules.make
+
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/attr.c linux/fs/ntfs/attr.c
--- v2.1.73/linux/fs/ntfs/attr.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/attr.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,519 @@
+/*
+ *  attr.c
+ *
+ *  Copyright (C) 1996-1997 Martin von Löwis
+ *  Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "attr.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "support.h"
+#include "util.h"
+#include "super.h"
+#include "inode.h"
+
+/* Look if an attribute already exists in the inode, and if not, create it */
+static int 
+new_attr(ntfs_inode *ino,int type,void *name,int namelen,int *pos, int *found)
+{
+	int do_insert=0;
+	int i;
+
+	for(i=0;i<ino->attr_count;i++)
+	{
+		int n=min(namelen,ino->attrs[i].namelen);
+		int s=ntfs_uni_strncmp(ino->attrs[i].name,name,n);
+		/*
+		 * We assume that each attribute can be uniquely 
+		 * identified by inode
+		 * number, attribute type and attribute name.
+		 */
+		if(ino->attrs[i].type==type && ino->attrs[i].namelen==namelen && !s){
+			*found=1;
+			*pos=i;
+			return 0;
+		}
+		/* attributes are ordered by type, then by name */
+		if(ino->attrs[i].type>type || (ino->attrs[i].type==type && s==1)){
+			do_insert=1;
+			break;
+		}
+	}
+
+	/* re-allocate space */
+	if(ino->attr_count % 8 ==0)
+	{
+		ntfs_attribute* old=ino->attrs;
+		ino->attrs = (ntfs_attribute*)ntfs_malloc((ino->attr_count+8)*
+			     sizeof(ntfs_attribute));
+		if(old){
+			ntfs_memcpy(ino->attrs,old,ino->attr_count*sizeof(ntfs_attribute));
+			ntfs_free(old);
+		}
+	}
+	if(do_insert)
+		ntfs_memcpy(ino->attrs+i+1,ino->attrs+i,(ino->attr_count-i)*
+			    sizeof(ntfs_attribute));
+	ino->attr_count++;
+	ino->attrs[i].type=type;
+	ino->attrs[i].namelen=namelen;
+	ino->attrs[i].name=name;
+	*pos=i;
+	*found=0;
+	return 0;
+}
+
+int 
+ntfs_make_attr_resident(ntfs_inode *ino,ntfs_attribute *attr)
+{
+	int size=attr->size;
+	if(size>0){
+		/* FIXME: read data, free clusters */
+		return EOPNOTSUPP;
+	}
+	attr->resident=1;
+	return 0;
+}
+
+/* Store in the inode readable information about a run */
+static void
+ntfs_insert_run(ntfs_attribute *attr,int cnum,int cluster,int len)
+{
+	/* (re-)allocate space if necessary */
+	if(attr->d.r.len % 8 == 0) {
+		ntfs_runlist* old;
+		old=attr->d.r.runlist;
+		attr->d.r.runlist=ntfs_malloc((attr->d.r.len+8)*sizeof(ntfs_runlist));
+		if(old) {
+			ntfs_memcpy(attr->d.r.runlist,old,attr->d.r.len
+				    *sizeof(ntfs_runlist));
+			ntfs_free(old);
+		}
+	}
+	if(attr->d.r.len>cnum)
+		ntfs_memcpy(attr->d.r.runlist+cnum+1,attr->d.r.runlist+cnum,
+			    (attr->d.r.len-cnum)*sizeof(ntfs_runlist));
+	attr->d.r.runlist[cnum].cluster=cluster;
+	attr->d.r.runlist[cnum].len=len;
+	attr->d.r.len++;
+}
+
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len,
+		int flags)
+{
+	int error=0;
+	ntfs_runlist *rl;
+	int rlen,cluster;
+	int clen;
+	if(attr->compressed)return EOPNOTSUPP;
+	if(attr->resident)return EOPNOTSUPP;
+	if(ino->record_count>1)return EOPNOTSUPP;
+	rl=attr->d.r.runlist;
+	rlen=attr->d.r.len-1;
+	if(rlen>=0)
+		cluster=rl[rlen].cluster+rl[rlen].len;
+	else
+		/* no preference for allocation space */
+		cluster=0;
+	/* round up to multiple of cluster size */
+	clen=(*len+ino->vol->clustersize-1)/ino->vol->clustersize;
+	/* FIXME: try to allocate smaller pieces */
+	error=ntfs_allocate_clusters(ino->vol,&cluster,&clen,
+				     flags|ALLOC_REQUIRE_SIZE);
+	if(error)return error;
+	attr->allocated+=clen;
+	*len=clen*ino->vol->clustersize;
+	/* contiguous chunk */
+	if(rlen>=0 && cluster==rl[rlen].cluster+rl[rlen].len){
+		rl[rlen].len+=clen;
+		return 0;
+	}
+	ntfs_insert_run(attr,rlen+1,cluster,*len);
+	return 0;
+}
+
+int
+ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
+{
+	void *data=attr->d.data;
+	int len=attr->size;
+	int error,alen;
+	ntfs_io io;
+	attr->d.r.len=0;
+	attr->d.r.runlist=0;
+	attr->resident=0;
+	attr->allocated=attr->initialized=0;
+	alen=len;
+	error=ntfs_extend_attr(ino,attr,&alen,ALLOC_REQUIRE_SIZE);
+	if(error)return error;/* FIXME: On error, restore old values */
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=data;
+	io.size=len;
+	io.do_read=0;
+	return ntfs_readwrite_attr(ino,attr,0,&io);
+}
+
+/* Resize the attribute to a newsize */
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize)
+{
+	int error=0;
+	int oldsize=attr->size;
+	int clustersize=ino->vol->clustersize;
+	int i,count,newlen,newcount;
+	ntfs_runlist *rl;
+
+	if(newsize==oldsize)
+		return 0;
+	/* modifying compressed attributes not supported yet */
+	if(attr->compressed)
+		/* extending is easy: just insert sparse runs */
+		return EOPNOTSUPP;
+	if(attr->resident){
+		void *v;
+		if(newsize>ino->vol->clustersize){
+			error=ntfs_make_attr_nonresident(ino,attr);
+			if(error)return error;
+			return ntfs_resize_attr(ino,attr,newsize);
+		}
+		v=attr->d.data;
+		if(newsize){
+			attr->d.data=ntfs_malloc(newsize);
+			if(!attr->d.data)
+				return ENOMEM;
+			ntfs_bzero(attr->d.data+oldsize,newsize);
+			ntfs_memcpy(attr->d.data,v,min(newsize,oldsize));
+		}else
+			attr->d.data=0;
+		ntfs_free(v);
+		attr->size=newsize;
+		return 0;
+	}
+	/* non-resident attribute */
+	rl=attr->d.r.runlist;
+	if(newsize<oldsize){
+		for(i=0,count=0;i<attr->d.r.len;i++){
+			if((count+rl[i].len)*clustersize>newsize)
+				break;
+			count+=rl[i].len;
+		}
+		newlen=i+1;
+		/* free unused clusters in current run, unless sparse */
+		newcount=count;
+		if(rl[i].cluster!=-1){
+			int rounded=newsize-count*clustersize;
+			rounded=(rounded+clustersize-1)/clustersize;
+			error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster+rounded,
+						       rl[i].len-rounded);
+			if(error)
+				return error; /* FIXME: incomplete operation */
+			rl[i].len=rounded;
+			newcount=count+rounded;
+		}
+		/* free all other runs */
+		for(i++;i<attr->d.r.len;i++)
+			if(rl[i].cluster!=-1){
+				error=ntfs_deallocate_clusters(ino->vol,rl[i].cluster,rl[i].len);
+				if(error)
+					return error; /* FIXME: incomplete operation */
+			}
+		/* FIXME? free space for extra runs in memory */
+		attr->d.r.len=newlen;
+	}else{
+		newlen=newsize;
+		error=ntfs_extend_attr(ino,attr,&newlen,ALLOC_REQUIRE_SIZE);
+		if(error)return error; /* FIXME: incomplete */
+		newcount=newlen/clustersize;
+	}
+	/* fill in new sizes */
+	attr->allocated = newcount*clustersize;
+	attr->size = newsize;
+	attr->initialized = newsize;
+	if(!newsize)
+		error=ntfs_make_attr_resident(ino,attr);
+	return error;
+}
+
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+	int dsize, ntfs_attribute **rattr)
+{
+	void *name;
+	int namelen;
+	int found,i;
+	int error;
+	ntfs_attribute *attr;
+	if(dsize>ino->vol->mft_recordsize)
+		/* FIXME: non-resident attributes */
+		return EOPNOTSUPP;
+	if(aname){
+		namelen=strlen(aname);
+		name=ntfs_malloc(2*namelen);
+		ntfs_ascii2uni(name,aname,namelen);
+	}else{
+		name=0;
+		namelen=0;
+	}
+	new_attr(ino,anum,name,namelen,&i,&found);
+	if(found){
+		ntfs_free(name);
+		return EEXIST;
+	}
+	*rattr=attr=ino->attrs+i;
+	/* allocate a new number.
+	   FIXME: Should this happen on inode writeback?
+	   FIXME: extensions records not supported */
+	error=ntfs_allocate_attr_number(ino,&i);
+	if(error)
+		return error;
+	attr->attrno=i;
+
+	attr->resident=1;
+	attr->compressed=attr->cengine=0;
+	attr->size=attr->allocated=attr->initialized=dsize;
+
+	/* FIXME: INDEXED information should come from $AttrDef
+	   Currently, only file names are indexed */
+	if(anum==ino->vol->at_file_name){
+		attr->indexed=1;
+	}else
+		attr->indexed=0;
+	attr->d.data=ntfs_malloc(dsize);
+	ntfs_memcpy(attr->d.data,data,dsize);
+	return 0;
+}
+
+/* Non-resident attributes are stored in runs (intervals of clusters).
+ *
+ * This function stores in the inode readable information about a non-resident
+ * attribute.
+ */
+static int 
+ntfs_process_runs(ntfs_inode *ino,ntfs_attribute* attr,unsigned char *data)
+{
+	int startvcn,endvcn;
+	int vcn,cnum;
+	int cluster,len,ctype;
+	startvcn = NTFS_GETU64(data+0x10);
+	endvcn = NTFS_GETU64(data+0x18);
+
+	/* check whether this chunk really belongs to the end */
+	for(cnum=0,vcn=0;cnum<attr->d.r.len;cnum++)
+		vcn+=attr->d.r.runlist[cnum].len;
+	if(vcn!=startvcn)
+	{
+		ntfs_error("Problem with runlist in extended record\n");
+		return -1;
+	}
+	if(!endvcn)
+	{
+		endvcn = NTFS_GETU64(data+0x28)-1; /* allocated length */
+		endvcn /= ino->vol->clustersize;
+	}
+	data=data+NTFS_GETU16(data+0x20);
+	cnum=attr->d.r.len;
+	cluster=0;
+	for(vcn=startvcn; vcn<=endvcn; vcn+=len)
+	{
+		if(ntfs_decompress_run(&data,&len,&cluster,&ctype))
+			return -1;
+		if(ctype)
+			ntfs_insert_run(attr,cnum,-1,len);
+		else
+			ntfs_insert_run(attr,cnum,cluster,len);
+		cnum++;
+	}
+	return 0;
+}
+  
+/* Insert the attribute starting at attr in the inode ino */
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata)
+{
+	int i,found;
+	int type;
+	short int *name;
+	int namelen;
+	void *data;
+	ntfs_attribute *attr;
+
+	type = NTFS_GETU32(attrdata);
+	namelen = NTFS_GETU8(attrdata+9);
+	/* read the attribute's name if it has one */
+	if(!namelen)
+		name=0;
+	else
+	{
+		/* 1 Unicode character fits in 2 bytes */
+		name=ntfs_malloc(2*namelen);
+		ntfs_memcpy(name,attrdata+NTFS_GETU16(attrdata+10),2*namelen);
+	}
+	new_attr(ino,type,name,namelen,&i,&found);
+	/* We can have in one inode two attributes with type 0x00000030 (File Name) 
+	   and without name */
+	if(found && /*FIXME*/type!=ino->vol->at_file_name)
+	{
+		ntfs_process_runs(ino,ino->attrs+i,attrdata);
+		return 0;
+	}
+	attr=ino->attrs+i;
+	attr->resident=NTFS_GETU8(attrdata+8)==0;
+	attr->compressed=NTFS_GETU16(attrdata+0xC);
+	attr->attrno=NTFS_GETU16(attrdata+0xE);
+  
+	if(attr->resident) {
+		attr->size=NTFS_GETU16(attrdata+0x10);
+		data=attrdata+NTFS_GETU16(attrdata+0x14);
+		attr->d.data = (void*)ntfs_malloc(attr->size);
+		ntfs_memcpy(attr->d.data,data,attr->size);
+		attr->indexed=NTFS_GETU16(attrdata+0x16);
+	}else{
+		attr->allocated=NTFS_GETU32(attrdata+0x28);
+		attr->size=NTFS_GETU32(attrdata+0x30);
+		attr->initialized=NTFS_GETU32(attrdata+0x38);
+		attr->cengine=NTFS_GETU16(attrdata+0x22);
+		if(attr->compressed)
+			attr->compsize=NTFS_GETU32(attrdata+0x40);
+		ino->attrs[i].d.r.runlist=0;
+		ino->attrs[i].d.r.len=0;
+		ntfs_process_runs(ino,attr,attrdata);
+	}
+	return 0;
+}
+
+/* process compressed attributes */
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+	ntfs_io *dest)
+{
+	int error=0;
+	int clustersize,l;
+	int s_vcn,rnum,vcn,cluster,len,chunk,got,cl1,l1,offs1,copied;
+	char *comp=0,*comp1;
+	char *decomp=0;
+	ntfs_io io;
+	ntfs_runlist *rl;
+
+	l=dest->size;
+	clustersize=ino->vol->clustersize;
+	/* starting cluster of potential chunk
+	   there are three situations:
+	   a) in a large uncompressable or sparse chunk, 
+	   s_vcn is in the middle of a run
+	   b) s_vcn is right on a run border
+	   c) when several runs make a chunk, s_vcn is before the chunks
+	*/
+	s_vcn=offset/clustersize;
+	/* round down to multiple of 16 */
+	s_vcn &= ~15;
+	rl=attr->d.r.runlist;
+	for(rnum=vcn=0;rnum<attr->d.r.len && vcn+rl->len<=s_vcn;rnum++,rl++)
+		vcn+=rl->len;
+	if(rnum==attr->d.r.len){
+		/* beyond end of file */
+		/* FIXME: check allocated/initialized */
+		dest->size=0;
+		return 0;
+	}
+	io.do_read=1;
+	io.fn_put=ntfs_put;
+	io.fn_get=0;
+	cluster=rl->cluster;
+	len=rl->len;
+	copied=0;
+	while(l){
+		chunk=0;
+		if(cluster==-1){
+			/* sparse cluster */
+			char *sparse=ntfs_calloc(512);
+			int l1;
+			if(!sparse)return ENOMEM;
+			if((len-(s_vcn-vcn)) & 15)
+				ntfs_error("unexpected sparse chunk size");
+			l1=chunk = min((vcn+len)*clustersize-offset,l);
+			while(l1){
+				int i=min(l1,512);
+				dest->fn_put(dest,sparse,i);
+				l1-=i;
+			}
+			ntfs_free(sparse);
+		}else if(dest->do_read){
+			if(!comp){
+				comp=ntfs_malloc(16*clustersize);
+				if(!comp){
+					error=ENOMEM;
+					goto out;
+				}
+			}
+			got=0;
+			/* we might need to start in the middle of a run */
+			cl1=cluster+s_vcn-vcn;
+			comp1=comp;
+			do{
+				io.param=comp1;
+				l1=min(len-max(s_vcn-vcn,0),16-got);
+				io.size=l1*clustersize;
+				error=ntfs_getput_clusters(ino->vol,cl1,0,&io);
+				if(error)goto out;
+				if(l1+max(s_vcn-vcn,0)==len){
+					rnum++;rl++;
+					vcn+=len;
+					cluster=cl1=rl->cluster;
+					len=rl->len;
+				}
+				got+=l1;
+				comp1+=l1*clustersize;
+			}while(cluster!=-1 && got<16); /* until empty run */
+			chunk=16*clustersize;
+			if(cluster!=-1 || got==16)
+				/* uncompressible */
+				comp1=comp;
+			else{
+				if(!decomp){
+					decomp=ntfs_malloc(16*clustersize);
+					if(!decomp){
+						error=ENOMEM;
+						goto out;
+					}
+				}
+				/* make sure there are null bytes
+				   after the last block */
+				*(ntfs_u32*)comp1=0;
+				ntfs_decompress(decomp,comp,chunk);
+				comp1=decomp;
+			}
+			offs1=offset-s_vcn*clustersize;
+			chunk=min(16*clustersize-offs1,chunk);
+			chunk=min(l,chunk);
+			dest->fn_put(dest,comp1+offs1,chunk);
+		}
+		l-=chunk;
+		copied+=chunk;
+		offset+=chunk;
+		s_vcn=offset/clustersize & ~15;
+		if(l && offset>=((vcn+len)*clustersize)){
+			rnum++;rl++;
+			vcn+=len;
+			cluster=rl->cluster;
+			len=rl->len;
+		}
+	}
+ out:
+	if(comp)ntfs_free(comp);
+	if(decomp)ntfs_free(decomp);
+	dest->size=copied;
+	return error;
+}
+
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+	ntfs_io *dest)
+{
+	return EOPNOTSUPP;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/attr.h linux/fs/ntfs/attr.h
--- v2.1.73/linux/fs/ntfs/attr.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/attr.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,17 @@
+/*
+ *  attr.h
+ *  Header file for attr.c
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, int *len,
+  int flags);
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, int newsize);
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+  ntfs_io *dest);
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+  ntfs_io *dest);
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+  int dsize, ntfs_attribute **rattr);
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/dir.c linux/fs/ntfs/dir.c
--- v2.1.73/linux/fs/ntfs/dir.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/dir.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,807 @@
+/*
+ *  dir.c
+ *
+ *  Copyright (C) 1995-1997 Martin von Löwis
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "dir.h"
+
+#include <errno.h>
+#include "super.h"
+#include "inode.h"
+#include "attr.h"
+#include "support.h"
+#include "util.h"
+
+static char I30[]="$I30";
+
+/* An index record should start with INDX, and the last word in each
+   block should contain the check value. If it passes, the original
+   values need to be restored */
+int ntfs_check_index_record(ntfs_inode *ino, char *record)
+{
+	return ntfs_fixup_record(ino->vol, record, "INDX", 
+				 ino->u.index.recordsize);
+}
+
+static inline int ntfs_is_top(long long stack)
+{
+	return stack==14;
+}
+
+static long long ntfs_pop(long long *stack)
+{
+	static int width[16]={1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
+	int res=-1;
+	switch(width[*stack & 15])
+	{
+	case 1:res=(*stack&15)>>1;
+		*stack>>=4;
+		break;
+	case 2:res=((*stack&63)>>2)+7;
+		*stack>>=6;
+		break;
+	case 3:res=((*stack & 255)>>3)+23;
+		*stack>>=8;
+		break;
+	case 4:res=((*stack & 1023)>>4)+55;
+		*stack>>=10;
+		break;
+	default:ntfs_error("Unknown encoding\n");
+	}
+	return res;
+}
+
+static inline unsigned int ntfs_top(void)
+{
+	return 14;
+}
+
+static long long ntfs_push(long long stack,int i)
+{
+	if(i<7)return (stack<<4)|(i<<1);
+	if(i<23)return (stack<<6)|((i-7)<<2)|1;
+	if(i<55)return (stack<<8)|((i-23)<<3)|3;
+	if(i<120)return (stack<<10)|((i-55)<<4)|7;
+	ntfs_error("Too many entries\n");
+	return -1;
+}
+
+#if 0
+static void ntfs_display_stack(long long stack)
+{
+	while(!ntfs_is_top(stack))
+	{
+		printf("%d ",ntfs_pop(&stack));
+	}
+	printf("\n");
+}
+#endif
+
+/* True if the entry points to another block of entries */
+static inline int ntfs_entry_has_subnodes(char* entry)
+{
+	return (int)NTFS_GETU8(entry+12)&1;
+}
+
+/* True if it is not the 'end of dir' entry */
+static inline int ntfs_entry_is_used(char* entry)
+{
+	return (int)(NTFS_GETU8(entry+12)&2)==0;
+}
+
+static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
+{
+	ntfs_attribute *allocation=0,*bitmap=0;
+	int error,size,i,bit;
+	ntfs_u8 *bmap;
+	ntfs_io io;
+	ntfs_volume *vol=walk->dir->vol;
+
+	/* check for allocation attribute */
+	allocation=ntfs_find_attr(walk->dir,vol->at_index_allocation,I30);
+	if(!allocation){
+		ntfs_u8 bmp[8];
+		/* create index allocation attribute */
+		error=ntfs_create_attr(walk->dir,vol->at_index_allocation,I30,
+				       0,0,&allocation);
+		if(error)return error;
+		ntfs_bzero(bmp,sizeof(bmp));
+		error=ntfs_create_attr(walk->dir,vol->at_bitmap,I30,
+				       bmp,sizeof(bmp),&bitmap);
+		if(error)return error;
+	}else
+		bitmap=ntfs_find_attr(walk->dir,vol->at_bitmap,I30);
+	if(!bitmap){
+		ntfs_error("Directory w/o bitmap\n");
+		return EINVAL;
+	}
+	size=bitmap->size;
+	bmap=ntfs_malloc(size);
+	if(!bmap)return ENOMEM;
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=bmap;
+	io.size=size;
+	error=ntfs_read_attr(walk->dir,vol->at_bitmap,I30,0,&io);
+	if(error){
+		ntfs_free(bmap);
+		return error;
+	}
+	if(io.size!=size){
+		ntfs_free(bmap);
+		return EIO;
+	}
+	
+	/* allocate a bit */
+	for(i=bit=0;i<size;i++){
+		if(bmap[i]==0xFF)continue;
+		for(bit=0;bit<8;bit++)
+			if(((bmap[i]>>bit) & 1) == 0)
+				break;
+		if(bit!=8)break;
+	}
+	if(i==size)
+		/* FIXME: extend bitmap */
+		return EOPNOTSUPP;
+	walk->newblock=(i*8+bit)*walk->dir->u.index.clusters_per_record;
+	bmap[i]|= 1<<bit;
+	io.param=bmap;
+	io.size=size;
+	error=ntfs_write_attr(walk->dir,vol->at_bitmap,I30,0,&io);
+	if(error || io.size!=size){
+		ntfs_free(bmap);
+		return error?error:EIO;
+	}
+	ntfs_free(bmap);
+
+	/* check whether record is out of allocated range */
+	size=allocation->size;
+	if(walk->newblock * vol->clustersize >= size){
+		/* build index record */
+		int s1=walk->dir->u.index.recordsize;
+		char *record=ntfs_malloc(s1);
+		int newlen;
+		ntfs_bzero(record,s1);
+		/* magic */
+		ntfs_memcpy(record,"INDX",4);
+		/* offset to fixups */
+		NTFS_PUTU16(record+4,0x28);
+		/* number of fixups */
+		NTFS_PUTU16(record+6,s1/vol->blocksize+1);
+		/* FIXME: log file number */
+		/* VCN of buffer */
+		NTFS_PUTU64(record+0x10,walk->newblock);
+		/* header size. FIXME */
+		NTFS_PUTU16(record+0x18,28);
+		/* total size of record */
+		NTFS_PUTU32(record+0x20,s1-0x18);
+		io.param=record;
+		newlen=walk->dir->u.index.recordsize;
+		/* allocate contiguous index record */
+		error=ntfs_extend_attr(walk->dir,allocation,&newlen,
+				       ALLOC_REQUIRE_SIZE);
+		if(error){
+			/* FIXME: try smaller allocations */
+			ntfs_free(record);
+			return ENOSPC;
+		}
+		io.size=s1;
+		error=ntfs_write_attr(walk->dir,vol->at_index_allocation,I30,
+				      size,&io);
+		if(error || io.size!=s1){
+			ntfs_free(record);
+			return error?error:EIO;
+		}
+		ntfs_free(record);
+	}
+
+	return 0;
+}
+
+static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
+	int used)
+{
+	ntfs_io io;
+	int error;
+	io.fn_put=0;
+	io.fn_get=ntfs_get;
+	io.param=buf;
+	if(walk->block==-1){
+		NTFS_PUTU16(buf+0x14,used-0x10);
+		/* 0x18 is a copy thereof */
+		NTFS_PUTU16(buf+0x18,used-0x10);
+		io.size=used;
+		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_root,
+				      I30,0,&io);
+		if(error)return error;
+		if(io.size!=used)return EIO;
+	}else{
+		NTFS_PUTU16(buf+0x1C,used-0x20);
+		ntfs_insert_fixups(buf,walk->dir->vol->blocksize);
+		io.size=walk->dir->u.index.recordsize;
+		error=ntfs_write_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
+				      walk->block*walk->dir->vol->clustersize,
+				      &io);
+		if(error)return error;
+		if(io.size!=walk->dir->u.index.recordsize)
+			return EIO;
+	}
+	return 0;
+}
+
+static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
+	int usize)
+{
+	char *entry,*prev;
+	ntfs_u8 *newbuf=0,*middle=0;
+	int error,othersize,mlen;
+	ntfs_io io;
+	ntfs_volume *vol=walk->dir->vol;
+	error=ntfs_allocate_index_block(walk);
+	if(error)
+		return error;
+	for(entry=prev=start+NTFS_GETU16(start+0x18)+0x18;
+	    entry-start<usize/2;
+	    entry+=NTFS_GETU16(entry+8))
+		prev=entry;
+	newbuf=ntfs_malloc(vol->index_recordsize);
+	if(!newbuf)
+		return ENOMEM;
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=newbuf;
+	io.size=vol->index_recordsize;
+	/* read in old header. FIXME: reading everything is overkill */
+	error=ntfs_read_attr(walk->dir,vol->at_index_allocation,I30,
+			     walk->newblock*vol->clustersize,&io);
+	if(error)goto out;
+	if(io.size!=vol->index_recordsize){
+		error=EIO;
+		goto out;
+	}
+	/* FIXME: adjust header */
+	/* copy everything from entry to new block */
+	othersize=usize-(entry-start);
+	ntfs_memcpy(newbuf+NTFS_GETU16(newbuf+0x18)+0x18,entry,othersize);
+	error=ntfs_index_writeback(walk,newbuf,walk->newblock,othersize);
+	if(error)goto out;
+
+	/* move prev to walk */
+	mlen=NTFS_GETU16(prev+0x8);
+	/* allow for pointer to subnode */
+	middle=ntfs_malloc(ntfs_entry_has_subnodes(prev)?mlen:mlen+8);
+	if(!middle){
+		error=ENOMEM;
+		goto out;
+	}
+	ntfs_memcpy(middle,prev,mlen);
+	/* set has_subnodes flag */
+	NTFS_PUTU8(middle+0xC, NTFS_GETU8(middle+0xC) | 1);
+	/* middle entry points to block, parent entry will point to newblock */
+	NTFS_PUTU64(middle+mlen-8,walk->block);
+	if(walk->new_entry)
+		ntfs_error("entry not reset");
+	walk->new_entry=middle;
+	walk->u.flags|=ITERATE_SPLIT_DONE;
+	/* write back original block */
+	error=ntfs_index_writeback(walk,start,walk->block,usize-(prev-start));
+ out:
+	if(newbuf)ntfs_free(newbuf);
+	if(middle)ntfs_free(middle);
+	return error;
+}
+
+static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
+{
+	int blocksize,usedsize,error,offset;
+	int do_split=0;
+	offset=entry-start;
+	if(walk->block==-1){ /*index root */
+		/* FIXME: adjust to maximum allowed index root value */
+		blocksize=walk->dir->vol->mft_recordsize;
+		usedsize=NTFS_GETU16(start+0x14)+0x10;
+	}else{
+		blocksize=walk->dir->u.index.recordsize;
+		usedsize=NTFS_GETU16(start+0x1C)+0x20;
+	}
+	if(usedsize+walk->new_entry_size > blocksize){
+		char* s1=ntfs_malloc(blocksize+walk->new_entry_size);
+		if(!s1)return ENOMEM;
+		ntfs_memcpy(s1,start,usedsize);
+		do_split=1;
+		/* adjust entry to s1 */
+		entry=s1+(entry-start);
+		start=s1;
+	}
+	ntfs_memmove(entry+walk->new_entry_size,entry,usedsize-offset);
+	ntfs_memcpy(entry,walk->new_entry,walk->new_entry_size);
+	usedsize+=walk->new_entry_size;
+	ntfs_free(walk->new_entry);
+	walk->new_entry=0;
+	/*FIXME: split root */
+	if(do_split){
+		error=ntfs_split_record(walk,start,blocksize,usedsize);
+		ntfs_free(start);
+	}else
+		ntfs_index_writeback(walk,start,walk->block,usedsize);
+	return 0;
+}
+
+/* The entry has been found. Copy the result in the caller's buffer */
+static int ntfs_copyresult(char *dest,char *source)
+{
+	int length=NTFS_GETU16(source+8);
+	ntfs_memcpy(dest,source,length);
+	return 1;
+}
+
+/* use $UpCase some day */
+static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
+{
+	/* we should read any pending rest of $UpCase here */
+	if(x >= vol->upcase_length)
+		return x;
+	return vol->upcase[x];
+}
+
+/* everything passed in walk and entry */
+static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
+{
+	int lu=*(entry+0x50);
+	int i;
+
+	ntfs_u16* name=(ntfs_u16*)(entry+0x52);
+	ntfs_volume *vol=walk->dir->vol;
+	for(i=0;i<lu && i<walk->namelen;i++)
+		if(ntfs_my_toupper(vol,name[i])!=ntfs_my_toupper(vol,walk->name[i]))
+			break;
+	if(i==lu && i==walk->namelen)return 0;
+	if(i==lu)return 1;
+	if(i==walk->namelen)return -1;
+	if(ntfs_my_toupper(vol,name[i])<ntfs_my_toupper(vol,walk->name[i]))return 1;
+	return -1;
+}
+
+/* Necessary forward declaration */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
+
+/* Parse a block of entries. Load the block, fix it up, and iterate
+   over the entries. The block is given as virtual cluster number */
+static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
+{
+	int length=walk->dir->u.index.recordsize;
+	char *record=(char*)ntfs_malloc(length);
+	char *offset;
+	int retval,error;
+	int oldblock;
+	ntfs_io io;
+
+	io.fn_put=ntfs_put;
+	io.param=record;
+	io.size=length;
+	/* Read the block from the index allocation attribute */
+	error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_allocation,I30,
+			     block*walk->dir->vol->clustersize,&io);
+	if(error || io.size!=length){
+		ntfs_error("read failed\n");
+		ntfs_free(record);
+		return 0;
+	}
+	if(!ntfs_check_index_record(walk->dir,record)){
+		ntfs_error("%x is not an index record\n",block);
+		ntfs_free(record);
+		return 0;
+	}
+	offset=record+NTFS_GETU16(record+0x18)+0x18;
+	oldblock=walk->block;
+	walk->block=block;
+	retval=ntfs_getdir_iterate(walk,record,offset);
+	walk->block=oldblock;
+	ntfs_free(record);
+	return retval;
+}
+
+/* go down to the next block of entries. These collate before
+   the current entry */
+static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
+{
+	int length=NTFS_GETU16(entry+8);
+	int nextblock=NTFS_GETU32(entry+length-8);
+	int error;
+
+	if(!ntfs_entry_has_subnodes(entry)) {
+		ntfs_error("illegal ntfs_descend call\n");
+		return 0;
+	}
+	error=ntfs_getdir_record(walk,nextblock);
+	if(!error && walk->type==DIR_INSERT && 
+	   (walk->u.flags & ITERATE_SPLIT_DONE)){
+		/* split has occured. adjust entry, insert new_entry */
+		NTFS_PUTU32(entry+length-8,walk->newblock);
+		/* reset flags, as the current block might be split again */
+		walk->u.flags &= ~ITERATE_SPLIT_DONE;
+		error=ntfs_dir_insert(walk,start,entry);
+	}
+	return error;
+}
+
+static int 
+ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk,char* start,char *entry)
+{
+	int retval=0;
+	int curpos=0,destpos=0;
+	int length;
+	if(walk->u.pos!=0){
+		if(ntfs_is_top(walk->u.pos))return 0;
+		destpos=ntfs_pop(&walk->u.pos);
+	}
+	while(1){
+		if(walk->u.pos==0)
+		{
+			if(ntfs_entry_has_subnodes(entry))
+				ntfs_descend(walk,start,entry);
+			else
+				walk->u.pos=ntfs_top();
+			if(ntfs_is_top(walk->u.pos) && !ntfs_entry_is_used(entry))
+			{
+				return 1;
+			}
+			walk->u.pos=ntfs_push(walk->u.pos,curpos);
+			return 1;
+		}
+		if(curpos==destpos)
+		{
+			if(!ntfs_is_top(walk->u.pos) && ntfs_entry_has_subnodes(entry))
+			{
+				retval=ntfs_descend(walk,start,entry);
+				if(retval){
+					walk->u.pos=ntfs_push(walk->u.pos,curpos);
+					return retval;
+				}else{
+					if(!ntfs_entry_is_used(entry))
+						return 0;
+					walk->u.pos=0;
+				}
+			}
+			if(ntfs_entry_is_used(entry))
+			{
+				retval=ntfs_copyresult(walk->result,entry);
+				walk->u.pos=0;
+			}else{
+				walk->u.pos=ntfs_top();
+				return 0;
+			}
+		}
+		curpos++;
+		if(!ntfs_entry_is_used(entry))break;
+		length=NTFS_GETU16(entry+8);
+		if(!length){
+			ntfs_error("infinite loop\n");
+			break;
+		}
+		entry+=length;
+	}
+	return -1;
+}
+	
+/* Iterate over a list of entries, either from an index block, or from
+   the index root. 
+   If searching BY_POSITION, pop the top index from the position. If the
+   position stack is empty then, return the item at the index and set the
+   position to the next entry. If the position stack is not empty, 
+   recursively proceed for subnodes. If the entry at the position is the
+   'end of dir' entry, return 'not found' and the empty stack.
+   If searching BY_NAME, walk through the items until found or until
+   one item is collated after the requested item. In the former case, return
+   the result. In the latter case, recursively proceed to the subnodes.
+   If 'end of dir' is reached, the name is not in the directory */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
+{
+	int length;
+	int retval=0;
+	int cmp;
+
+	if(walk->type==BY_POSITION)
+		return ntfs_getdir_iterate_byposition(walk,start,entry);
+	do{
+		/* if the current entry is a real one, compare with the
+		   requested item. If the current entry is the last item,
+		   it is always larger than the requested item */
+		cmp = ntfs_entry_is_used(entry) ? ntfs_my_strcmp(walk,entry) : -1;
+		switch(walk->type){
+		case BY_NAME:
+			switch(cmp)
+			{
+			case -1:return ntfs_entry_has_subnodes(entry)?
+				       ntfs_descend(walk,start,entry):0;
+			case  0:return ntfs_copyresult(walk->result,entry);
+			case  1:break;
+			}
+			break;
+		case DIR_INSERT:
+			switch(cmp){
+			case -1:return ntfs_entry_has_subnodes(entry)?
+				       ntfs_descend(walk,start,entry):
+					       ntfs_dir_insert(walk,start,entry);
+			case  0:return EEXIST;
+			case  1:break;
+			}
+			break;
+		default:
+			ntfs_error("TODO\n");
+		}
+		if(!ntfs_entry_is_used(entry))break;
+		length=NTFS_GETU16(entry+8);
+		if(!length){
+			ntfs_error("infinite loop\n");
+			break;
+		}
+		entry+=length;
+	}while(1);
+	return retval;
+}
+
+/*	Tree walking is done using position numbers. The following numbers have
+    a special meaning:
+        0    start (.)
+        -1   no more entries
+        -2   ..
+    All other numbers encode sequences of indices. The sequence a,b,c is 
+    encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
+    first few integers are encoded as follows:
+        0:    0000    1:    0010    2:    0100    3:    0110
+        4:    1000    5:    1010    6:    1100 stop:    1110
+        7:  000001    8:  000101	9:  001001   10:  001101
+    The least significant bits give the width of this encoding, the
+    other bits encode the value, starting from the first value of the 
+    interval.
+     tag     width  first value  last value
+     0       3      0            6
+     01      4      7            22
+     011     5      23           54
+     0111    6      55           119
+     More values are hopefully not needed, as the file position has currently
+     64 bits in total.
+*/
+
+/* Find an entry in the directory. Return 0 if not found, otherwise copy
+   the entry to the result buffer. */
+int ntfs_getdir(ntfs_iterate_s* walk)
+{
+	int length=walk->dir->vol->mft_recordsize;
+	int retval,error;
+	/* start at the index root.*/
+	char *root=ntfs_malloc(length);
+	ntfs_io io;
+
+	io.fn_put=ntfs_put;
+	io.param=root;
+	io.size=length;
+	error=ntfs_read_attr(walk->dir,walk->dir->vol->at_index_root,
+			     I30,0,&io);
+	if(error)
+	{
+		ntfs_error("Not a directory\n");
+		return 0;
+	}
+	walk->block=-1;
+	/* FIXME: move these to walk */
+	walk->dir->u.index.recordsize = NTFS_GETU32(root+0x8);
+	walk->dir->u.index.clusters_per_record = NTFS_GETU32(root+0xC);
+	/* FIXME: consistency check */
+	/* skip header */
+	retval = ntfs_getdir_iterate(walk,root,root+0x20);
+	ntfs_free(root);
+	return retval;
+}
+
+/* Find an entry in the directory by its position stack. Iteration starts
+   if the stack is 0, in which case the position is set to the first item
+   in the directory. If the position is nonzero, return the item at the
+   position and change the position to the next item. The position is -1
+   if there are no more items */
+int ntfs_getdir_byposition(ntfs_iterate_s *walk)
+{
+	walk->type=BY_POSITION;
+	return ntfs_getdir(walk);
+}
+
+/* Find an entry in the directory by its name. Return 0 if not found */
+int ntfs_getdir_byname(ntfs_iterate_s *walk)
+{
+	walk->type=BY_NAME;
+	return ntfs_getdir(walk);
+}
+
+int ntfs_getdir_unsorted(ntfs_inode *ino,ntfs_u32 *p_high,ntfs_u32* p_low,
+			 int(*cb)(ntfs_u8*,void*),void *param)
+{
+	char *buf=0,*entry=0;
+	ntfs_io io;
+	int length;
+	int block;
+	int start;
+	ntfs_attribute *attr;
+	ntfs_volume *vol=ino->vol;
+	int byte,bit;
+	int error=0;
+
+	if(!ino){
+		ntfs_error("No inode passed to getdir_unsorted\n");
+		return EINVAL;
+	}
+	if(!vol){
+		ntfs_error("Inode %d has no volume\n",ino->i_number);
+		return EINVAL;
+	}
+	/* are we still in the index root */
+	if(*p_high==0){
+		buf=ntfs_malloc(length=vol->mft_recordsize);
+		io.fn_put=ntfs_put;
+		io.param=buf;
+		io.size=length;
+		error=ntfs_read_attr(ino,vol->at_index_root,I30,0,&io);
+		if(error){
+			ntfs_free(buf);
+			return error;
+		}
+		ino->u.index.recordsize = NTFS_GETU32(buf+0x8);
+		ino->u.index.clusters_per_record = NTFS_GETU32(buf+0xC);
+		entry=buf+0x20;
+	}else{ /* we are in an index record */
+		length=ino->u.index.recordsize;
+		buf=ntfs_malloc(length);
+		io.fn_put=ntfs_put;
+		io.param=buf;
+		io.size=length;
+		/* 0 is index root, index allocation starts with 4 */
+		block = *p_high - ino->u.index.clusters_per_record;
+		error=ntfs_read_attr(ino,vol->at_index_allocation,I30,
+				     block*vol->clustersize,&io);
+		if(!error && io.size!=length)error=EIO;
+		if(error){
+			ntfs_error("read failed\n");
+			ntfs_free(buf);
+			return error;
+		}
+		if(!ntfs_check_index_record(ino,buf)){
+			ntfs_error("%x is not an index record\n",block);
+			ntfs_free(buf);
+			return ENOTDIR;
+		}
+		entry=buf+NTFS_GETU16(buf+0x18)+0x18;
+	}
+
+	/* process the entries */
+	start=*p_low;
+	while(ntfs_entry_is_used(entry)){
+		if(start)
+			start--; /* skip entries that were already processed */
+		else{
+			if((error=cb(entry,param)))
+				/* the entry could not be processed */
+				break;
+			(*p_low)++;
+		}
+		entry+=NTFS_GETU16(entry+8);
+	}
+
+	/* caller did not process all entries */
+	if(error){
+		ntfs_free(buf);
+		return error;
+	}
+
+	/* we have to locate the next record */
+	ntfs_free(buf);
+	buf=0;
+	*p_low=0;
+	attr=ntfs_find_attr(ino,vol->at_bitmap,I30);
+	if(!attr){
+		/* directory does not have index allocation */
+		*p_high=0xFFFFFFFF;
+		*p_low=0;
+		return 0;
+	}
+	buf=ntfs_malloc(length=attr->size);
+	io.param=buf;
+	io.size=length;
+	error=ntfs_read_attr(ino,vol->at_bitmap,I30,0,&io);
+	if(!error && io.size!=length)error=EIO;
+	if(error){
+		ntfs_free(buf);
+		return EIO;
+	}
+	attr=ntfs_find_attr(ino,vol->at_index_allocation,I30);
+	while(1){
+		if(*p_high*vol->clustersize > attr->size){
+			/* no more index records */
+			*p_high=0xFFFFFFFF;
+			ntfs_free(buf);
+			return 0;
+		}
+		*p_high+=ino->u.index.clusters_per_record;
+		byte=*p_high/ino->u.index.clusters_per_record-1;
+		bit  = 1 << (byte & 7);
+		byte = byte >> 3;
+		/* this record is allocated */
+		if(buf[byte] & bit)
+			break;
+	}
+	return 0;
+}
+
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
+{
+	ntfs_iterate_s walk;
+	int nsize,esize;
+	ntfs_u8* entry,*ndata;
+	int error;
+
+	walk.type=DIR_INSERT;
+	walk.dir=dir;
+	walk.u.flags=0;
+	nsize = name->size;
+	ndata = name->d.data;
+	walk.name=(ntfs_u16*)(ndata+0x42);
+	walk.namelen=NTFS_GETU8(ndata+0x40);
+	walk.new_entry_size = esize = ((nsize+0x18)/8)*8;
+	walk.new_entry=entry=ntfs_malloc(esize);
+	if(!entry)return ENOMEM;
+	ntfs_bzero(entry,esize);
+	NTFS_PUTINUM(entry,new);
+	NTFS_PUTU16(entry+0x8,esize); /* size of entry */
+	NTFS_PUTU16(entry+0xA,nsize); /* size of original name attribute */
+	NTFS_PUTU32(entry+0xC,0);     /* FIXME: D-F? */
+	ntfs_memcpy(entry+0x10,ndata,nsize);
+	error=ntfs_getdir(&walk);
+	if(walk.new_entry)
+		ntfs_free(walk.new_entry);
+	return error;
+}
+
+#if 0
+int ntfs_dir_add1(ntfs_inode *dir,const char* name,int namelen,ntfs_inode *ino)
+{
+	ntfs_iterate_s walk;
+	int error;
+	int nsize;
+	char *entry;
+	ntfs_attribute *name_attr;
+	error=ntfs_decodeuni(dir->vol,name,namelen,&walk.name,&walk.namelen);
+	if(error)
+		return error;
+	/* FIXME: set flags */
+	walk.type=DIR_INSERT;
+	walk.dir=dir;
+	/*walk.new=ino;*/
+	/* prepare new entry */
+	/* round up to a multiple of 8 */
+	walk.new_entry_size = nsize = ((0x52+2*walk.namelen+7)/8)*8;
+	walk.new_entry=entry=ntfs_malloc(nsize);
+	if(!entry)
+		return ENOMEM;
+	ntfs_bzero(entry,nsize);
+	NTFS_PUTINUM(entry,ino);
+	NTFS_PUTU16(entry+8,nsize);
+	NTFS_PUTU16(entry+0xA,0x42+2*namelen); /*FIXME: size of name attr*/
+	NTFS_PUTU32(entry+0xC,0); /*FIXME: D-F? */
+	name_attr=ntfs_find_attr(ino,vol->at_file_name,0); /* FIXME:multiple names */
+	if(!name_attr || !name_attr->resident)
+		return EIDRM;
+	/* directory, file stamps, sizes, filename */
+	ntfs_memcpy(entry+0x10,name_attr->d.data,0x42+2*namelen);
+	error=ntfs_getdir(&walk);
+	ntfs_free(walk.name);
+	return error;
+}
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/dir.h linux/fs/ntfs/dir.h
--- v2.1.73/linux/fs/ntfs/dir.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/dir.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,39 @@
+/*
+ *  dir.h
+ *  Header file for dir.c
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+#define ITERATE_SPLIT_DONE      1
+
+enum ntfs_iterate_e {
+    BY_POSITION,
+    BY_NAME,
+    DIR_INSERT
+};
+
+/* not all fields are used for all operations */
+typedef struct ntfs_iterate_s{
+	enum ntfs_iterate_e type;
+	ntfs_inode *dir;
+	union{
+		long long pos;
+		int flags;
+	}u;
+	char *result;      /* pointer to entry if found */
+	ntfs_u16* name;
+	int namelen;
+	int block;         /* current index record */
+	int newblock;      /* index record created in a split */
+	char *new_entry;
+	int new_entry_size;
+	/*ntfs_inode* new;*/
+} ntfs_iterate_s;
+
+int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
+  int(*cb)(ntfs_u8*,void*), void *param);
+int ntfs_getdir_byname(ntfs_iterate_s *walk);
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
+int ntfs_check_index_record(ntfs_inode *ino, char *record);
+int ntfs_getdir_byposition(ntfs_iterate_s *walk);
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.1.73/linux/fs/ntfs/fs.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/fs.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,968 @@
+/*
+ *  fs.c
+ *  NTFS driver for Linux 2.1
+ *
+ *  Copyright (C) 1995-1997 Martin von Löwis
+ *  Copyright (C) 1996 Richard Russon
+ *  Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/config.h>
+#endif
+
+#include "types.h"
+#include "struct.h"
+#include "util.h"
+#include "inode.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "macros.h"
+#include "sysctl.h"
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/nls.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#define ITEM_SIZE 2040
+
+/* io functions to user space */
+static void ntfs_putuser(ntfs_io* dest,void *src,ntfs_size_t len)
+{
+	copy_to_user(dest->param,src,len);
+	dest->param+=len;
+}
+
+#ifdef CONFIG_NTFS_RW
+static void ntfs_getuser(void *dest,ntfs_io *src,ntfs_size_t len)
+{
+	copy_from_user(dest,src->param,len);
+	src->param+=len;
+}
+#endif
+
+static ssize_t
+ntfs_read(struct file * filp, char *buf, size_t count, loff_t *off)
+{
+	int error;
+	ntfs_io io;
+	ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+	/* inode is not properly initialized */
+	if(!ino)return -EINVAL;
+	ntfs_debug(DEBUG_OTHER, "ntfs_read %x,%x,%x ->",
+		   (unsigned)ino->i_number,(unsigned)*off,(unsigned)count);
+	/* inode has no unnamed data attribute */
+	if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+		return -EINVAL;
+	
+	/* read the data */
+	io.fn_put=ntfs_putuser;
+	io.fn_get=0;
+	io.param=buf;
+	io.size=count;
+	error=ntfs_read_attr(ino,ino->vol->at_data,NULL,*off,&io);
+	if(error)return -error;
+	
+	*off+=io.size;
+	return io.size;
+}
+
+#ifdef CONFIG_NTFS_RW
+static ssize_t
+ntfs_write(struct file *filp,const char* buf,size_t count,loff_t *pos)
+{
+	struct super_block* sb;
+	int ret;
+	ntfs_io io;
+	ntfs_inode *ino=NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+	if(!ino)return -EINVAL;
+	ntfs_debug(DEBUG_OTHER, "ntfs_write %x,%x,%x ->",
+	       (unsigned)ino->i_number,(unsigned)*pos,(unsigned)count);
+	sb = filp->f_dentry->d_inode->i_sb;
+	/* Allows to lock fs ro at any time */
+	if(sb->s_flags & MS_RDONLY)
+		return -ENOSPC;
+	if(!ntfs_find_attr(ino,ino->vol->at_data,NULL))
+		return -EINVAL;
+
+	io.fn_put=0;
+	io.fn_get=ntfs_getuser;
+	io.param=(void*)buf; /* to get rid of the const */
+	io.size=count;
+	ret = ntfs_write_attr(ino,ino->vol->at_data,NULL,*pos,&io);
+	ntfs_debug(DEBUG_OTHER, "%x\n",ret);
+	if(ret<0)return -EINVAL;
+
+	*pos+=ret;
+	return ret;
+}
+#endif
+
+struct ntfs_filldir{
+	struct inode *dir;
+	filldir_t filldir;
+	unsigned int type;
+	ntfs_u32 ph,pl;
+	void *dirent;
+	char *name;
+	int namelen;
+};
+	
+static int ntfs_printcb(ntfs_u8 *entry,void *param)
+{
+	struct ntfs_filldir* nf=param;
+	int flags=NTFS_GETU8(entry+0x51);
+	int show_hidden=0,to_lower=0;
+	int length=NTFS_GETU8(entry+0x50);
+	int inum=NTFS_GETU32(entry);
+	int i,error;
+	switch(nf->type){
+	case ngt_dos:
+		/* Don't display long names */
+		if((flags & 2)==0)
+			return 0;
+		break;
+	case ngt_nt:
+		/* Don't display short-only names */
+		switch(flags&3){
+		case 2: return 0;
+		case 3: to_lower=1;
+		}
+		break;
+	case ngt_posix:
+		break;
+	case ngt_full:
+		show_hidden=1;
+		break;
+	}
+	if(!show_hidden && ((NTFS_GETU8(entry+0x48) & 2)==2)){
+		ntfs_debug(DEBUG_OTHER,"Skipping hidden file\n");
+		return 0;
+	}
+	nf->name=0;
+	if(ntfs_encodeuni(NTFS_INO2VOL(nf->dir),(ntfs_u16*)(entry+0x52),
+			  length,&nf->name,&nf->namelen)){
+		ntfs_debug(DEBUG_OTHER,"Skipping unrepresentable file\n");
+		if(nf->name)ntfs_free(nf->name);
+		return 0;
+	}
+	/* Do not return ".", as this is faked */
+	if(length==1 && *nf->name=='.')
+		return 0;
+	if(to_lower)
+		for(i=0;i<nf->namelen;i++)
+			/* This supports ASCII only. Since only DOS-only
+			   names get converted, and since those are restricted
+			   to ASCII, this should be correct */
+			if(nf->name[i]>='A' && nf->name[i]<='Z')
+				nf->name[i]+='a'-'A';
+	nf->name[nf->namelen]=0;
+	ntfs_debug(DEBUG_OTHER, "readdir got %s,len %d\n",nf->name,nf->namelen);
+	/* filldir expects an off_t rather than an loff_t.
+	   Hope we don't have more than 65535 index records */
+	error=nf->filldir(nf->dirent,nf->name,nf->namelen,
+			(nf->ph<<16)|nf->pl,inum);
+	ntfs_free(nf->name);
+	/* Linux filldir errors are negative, other errors positive */
+	return error;
+}
+
+/* readdir returns '..', then '.', then the directory entries in sequence
+   As the root directory contains a entry for itself, '.' is not emulated
+   for the root directory */
+static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
+{
+	struct ntfs_filldir cb;
+	int error;
+	struct inode *dir=filp->f_dentry->d_inode;
+
+	ntfs_debug(DEBUG_OTHER, "ntfs_readdir ino %x mode %x\n",
+	       (unsigned)dir->i_ino,(unsigned int)dir->i_mode);
+	if(!dir || (dir->i_ino==0) || !S_ISDIR(dir->i_mode))return -EBADF;
+
+	ntfs_debug(DEBUG_OTHER, "readdir: Looking for file %x dircount %d\n",
+	       (unsigned)filp->f_pos,dir->i_count);
+	cb.pl=filp->f_pos & 0xFFFF;
+	cb.ph=filp->f_pos >> 16;
+	/* end of directory */
+	if(cb.ph==0xFFFF){
+		/* FIXME: Maybe we can return those with the previous call */
+		switch(cb.pl){
+		case 0: filldir(dirent,".",1,filp->f_pos,dir->i_ino);
+			filp->f_pos=0xFFFF0001;
+			return 0;
+			/* FIXME: parent directory */
+		case 1: filldir(dirent,"..",2,filp->f_pos,0);
+			filp->f_pos=0xFFFF0002;
+			return 0;
+		}
+		ntfs_debug(DEBUG_OTHER, "readdir: EOD\n");
+		return 0;
+	}
+	cb.dir=dir;
+	cb.filldir=filldir;
+	cb.dirent=dirent;
+	cb.type=NTFS_INO2VOL(dir)->ngt;
+	do{
+		ntfs_debug(DEBUG_OTHER,"looking for next file\n");
+		error=ntfs_getdir_unsorted(NTFS_LINO2NINO(dir),&cb.ph,&cb.pl,
+				   ntfs_printcb,&cb);
+	}while(!error && cb.ph!=0xFFFFFFFF);
+	filp->f_pos=(cb.ph<<16)|cb.pl;
+	ntfs_debug(DEBUG_OTHER, "new position %x\n",(unsigned)filp->f_pos);
+        /* -EINVAL is on user buffer full. This is not considered 
+	   as an error by sys_getdents */
+	if(error<0) 
+		error=0;
+	/* Otherwise (device error, inconsistent data), switch the sign */
+	return -error;
+}
+
+/* Copied from vfat driver */
+static int simple_getbool(char *s, int *setval)
+{
+	if (s) {
+		if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) {
+			*setval = 1;
+		} else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) {
+			*setval = 0;
+		} else {
+			return 0;
+		}
+	} else {
+		*setval = 1;
+	}
+	return 1;
+}
+
+/* Parse the (re)mount options */
+static int parse_options(ntfs_volume* vol,char *opt)
+{
+	char *value;
+
+	vol->uid=vol->gid=0;
+	vol->umask=0077;
+	vol->ngt=ngt_nt;
+	vol->nls_map=0;
+	vol->nct=0;
+	if(!opt)goto done;
+
+	for(opt = strtok(opt,",");opt;opt=strtok(NULL,","))
+	{
+		if ((value = strchr(opt, '=')) != NULL)
+			*value++='\0';
+		if(strcmp(opt,"uid")==0)
+		{
+			if(!value || !*value)goto needs_arg;
+			vol->uid=simple_strtoul(value,&value,0);
+			if(*value){
+				printk(KERN_ERR "NTFS: uid invalid argument\n");
+				return 0;
+			}
+		}else if(strcmp(opt, "gid") == 0)
+		{
+			if(!value || !*value)goto needs_arg;
+			vol->gid=simple_strtoul(value,&value,0);
+			if(*value){
+				printk(KERN_ERR "gid invalid argument\n");
+				return 0;
+			}
+		}else if(strcmp(opt, "umask") == 0)
+		{
+			if(!value || !*value)goto needs_arg;
+			vol->umask=simple_strtoul(value,&value,0);
+			if(*value){
+				printk(KERN_ERR "umask invalid argument\n");
+				return 0;
+			}
+		}else if(strcmp(opt, "iocharset") == 0){
+			if(!value || !*value)goto needs_arg;
+			vol->nls_map=load_nls(value);
+			vol->nct |= nct_map;
+			if(!vol->nls_map){
+				printk(KERN_ERR "NTFS: charset not found");
+				return 0;
+			}
+		}else if(strcmp(opt, "posix") == 0){
+			int val;
+			if(!value || !*value)goto needs_arg;
+			if(!simple_getbool(value,&val))
+				goto needs_bool;
+			vol->ngt=val?ngt_posix:ngt_nt;
+		}else if(strcmp(opt,"utf8") == 0){
+			int val=0;
+			if(!value || !*value)
+				val=1;
+			else if(!simple_getbool(value,&val))
+				goto needs_bool;
+			if(val)
+				vol->nct|=nct_utf8;
+		}else if(strcmp(opt,"uni_xlate") == 0){
+			int val=0;
+			/* no argument: uni_vfat.
+			   boolean argument: uni_vfat.
+			   "2": uni.
+			*/
+			if(!value || !*value)
+				val=1;
+			else if(strcmp(value,"2")==0)
+				vol->nct |= nct_uni_xlate;
+			else if(!simple_getbool(value,&val))
+				goto needs_bool;
+			if(val)
+				vol->nct |= nct_uni_xlate_vfat | nct_uni_xlate;
+		}else{
+			printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
+			return 0;
+		}
+	}
+	if(vol->nct & nct_utf8 & (nct_map | nct_uni_xlate)){
+		printk(KERN_ERR "utf8 cannot be combined with iocharset or uni_xlate\n");
+		return 0;
+	}
+ done:
+	if((vol->nct & (nct_uni_xlate | nct_map | nct_utf8))==0)
+		/* default to UTF-8 */
+		vol->nct=nct_utf8;
+	if(!vol->nls_map)
+		vol->nls_map=load_nls_default();
+	return 1;
+
+ needs_arg:
+	printk(KERN_ERR "NTFS: %s needs an argument",opt);
+	return 0;
+ needs_bool:
+	printk(KERN_ERR "NTFS: %s needs boolean argument",opt);
+	return 0;
+}
+			
+static int ntfs_lookup(struct inode *dir, struct dentry *d)
+{
+	struct inode *res=0;
+	char *item=0;
+	ntfs_iterate_s walk;
+	int error;
+	ntfs_debug(DEBUG_OTHER, "Looking up %s in %x\n",d->d_name.name,
+		   (unsigned)dir->i_ino);
+	/* convert to wide string */
+	error=ntfs_decodeuni(NTFS_INO2VOL(dir),(char*)d->d_name.name,
+			     d->d_name.len,&walk.name,&walk.namelen);
+	if(error)
+		return error;
+	item=ntfs_malloc(ITEM_SIZE);
+	/* ntfs_getdir will place the directory entry into item,
+	   and the first long long is the MFT record number */
+	walk.type=BY_NAME;
+	walk.dir=NTFS_LINO2NINO(dir);
+	walk.result=item;
+	if(ntfs_getdir_byname(&walk))
+	{
+		res=iget(dir->i_sb,NTFS_GETU32(item));
+	}
+	d_add(d,res);
+	ntfs_free(item);
+	ntfs_free(walk.name);
+	return res?0:-ENOENT;
+}
+
+struct file_operations ntfs_file_operations_nommap = {
+	NULL, /* lseek */
+	ntfs_read,
+#ifdef CONFIG_NTFS_RW
+	ntfs_write,
+#else
+	NULL,
+#endif
+	NULL, /* readdir */
+	NULL, /* select */
+	NULL, /* ioctl */
+	NULL, /* mmap */
+	NULL, /* open */
+	NULL, /* release */
+	NULL, /* fsync */
+	NULL, /* fasync */
+	NULL, /* check_media_change */
+	NULL, /* revalidate */
+	NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations_nobmap = {
+	&ntfs_file_operations_nommap,
+	NULL, /* create */
+	NULL, /* lookup */
+	NULL, /* link */
+	NULL, /* unlink */
+	NULL, /* symlink */
+	NULL, /* mkdir */
+	NULL, /* rmdir */
+	NULL, /* mknod */
+	NULL, /* rename */
+	NULL, /* readlink */
+	NULL, /* follow_link */
+	NULL, /* readpage */
+	NULL, /* writepage */
+	NULL, /* bmap */
+	NULL, /* truncate */
+	NULL, /* permission */
+	NULL, /* smap */
+	NULL, /* updatepage */
+	NULL, /* revalidate */
+};
+
+#ifdef CONFIG_NTFS_RW
+static int
+ntfs_create(struct inode* dir,struct dentry *d,int mode)
+{
+	struct inode *r=0;
+	ntfs_inode *ino=0;
+	ntfs_volume *vol;
+	int error=0;
+	ntfs_attribute *si;
+
+	r=get_empty_inode();
+	if(!r){
+		error=ENOMEM;
+		goto fail;
+	}
+
+	ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n",d->d_name.name);
+	vol=NTFS_INO2VOL(dir);
+#ifdef NTFS_IN_LINUX_KERNEL
+	ino=NTFS_LINO2NINO(r);
+#else
+	ino=ntfs_malloc(sizeof(ntfs_inode));
+	if(!ino){
+		error=ENOMEM;
+		goto fail;
+	}
+	r->u.generic_ip=ino;
+#endif
+	error=ntfs_alloc_inode(NTFS_LINO2NINO(dir),ino,(char*)d->d_name.name,
+			       d->d_name.len);
+	if(error)goto fail;
+	error=ntfs_update_inode(ino);
+	if(error)goto fail;
+	error=ntfs_update_inode(NTFS_LINO2NINO(dir));
+	if(error)goto fail;
+
+	r->i_uid=vol->uid;
+	r->i_gid=vol->gid;
+	r->i_nlink=1;
+	r->i_sb=dir->i_sb;
+	/* FIXME: dirty? dev? */
+	/* get the file modification times from the standard information */
+	si=ntfs_find_attr(ino,vol->at_standard_information,NULL);
+	if(si){
+		char *attr=si->d.data;
+		r->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18));
+		r->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+		r->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8));
+	}
+	/* It's not a directory */
+	r->i_op=&ntfs_inode_operations_nobmap;
+	r->i_mode=S_IFREG|S_IRUGO;
+	r->i_mode &= ~vol->umask;
+
+	d_instantiate(d,r);
+	return 0;
+ fail:
+	#ifndef NTFS_IN_LINUX_KERNEL
+	if(ino)ntfs_free(ino);
+	#endif
+	if(r)iput(r);
+	return -error;
+}
+#endif
+
+static int 
+ntfs_bmap(struct inode *ino,int block)
+{
+	int ret=ntfs_vcn_to_lcn(NTFS_LINO2NINO(ino),block);
+	ntfs_debug(DEBUG_OTHER, "bmap of %lx,block %x is %x\n",
+	       ino->i_ino,block,ret);
+	return (ret==-1) ? 0:ret;
+}
+
+struct file_operations ntfs_file_operations = {
+	NULL, /* lseek */
+	ntfs_read,
+#ifdef CONFIG_NTFS_RW
+	ntfs_write,
+#else
+	NULL,
+#endif
+	NULL, /* readdir */
+	NULL, /* select */
+	NULL, /* ioctl */
+	generic_file_mmap,
+	NULL, /* open */
+	NULL, /* release */
+	NULL, /* fsync */
+	NULL, /* fasync */
+	NULL, /* check_media_change */
+	NULL, /* revalidate */
+	NULL, /* lock */
+};
+
+struct inode_operations ntfs_inode_operations = {
+	&ntfs_file_operations,
+	NULL, /* create */
+	NULL, /* lookup */
+	NULL, /* link */
+	NULL, /* unlink */
+	NULL, /* symlink */
+	NULL, /* mkdir */
+	NULL, /* rmdir */
+	NULL, /* mknod */
+	NULL, /* rename */
+	NULL, /* readlink */
+	NULL, /* follow_link */
+	generic_readpage,
+	NULL, /* writepage */
+	ntfs_bmap,
+	NULL, /* truncate */
+	NULL, /* permission */
+	NULL, /* smap */
+	NULL, /* updatepage */
+	NULL, /* revalidate */
+};
+
+struct file_operations ntfs_dir_operations = {
+	NULL, /* lseek */
+	NULL, /* read */
+	NULL, /* write */
+	ntfs_readdir, /* readdir */
+	NULL, /* poll */
+	NULL, /* ioctl */
+	NULL, /* mmap */
+	NULL, /* open */
+	NULL, /* release */
+	NULL, /* fsync */
+	NULL, /* fasync */
+	NULL, /* check_media_change */
+	NULL, /* revalidate */
+	NULL, /* lock */
+};
+
+struct inode_operations ntfs_dir_inode_operations = {
+	&ntfs_dir_operations,
+#ifdef CONFIG_NTFS_RW
+	ntfs_create, /* create */
+#else
+	NULL,
+#endif
+	ntfs_lookup, /* lookup */
+	NULL, /* link */
+	NULL, /* unlink */
+	NULL, /* symlink */
+	NULL, /* mkdir */
+	NULL, /* rmdir */
+	NULL, /* mknod */
+	NULL, /* rename */
+	NULL, /* readlink */
+	NULL, /* follow_link */
+	NULL, /* readpage */
+	NULL, /* writepage */
+	NULL, /* bmap */
+	NULL, /* truncate */
+	NULL, /* permission */
+	NULL, /* smap */
+	NULL, /* updatepage */
+	NULL, /* revalidate */
+};
+
+/* ntfs_read_inode is called by the Virtual File System (the kernel layer that
+ * deals with filesystems) when iget is called requesting an inode not already
+ * present in the inode table. Typically filesystems have separate
+ * inode_operations for directories, files and symlinks.
+ */
+static void ntfs_read_inode(struct inode* inode)
+{
+	ntfs_volume *vol;
+	int can_mmap=0;
+	ntfs_inode *ino;
+	ntfs_attribute *data;
+	ntfs_attribute *si;
+
+	vol=NTFS_INO2VOL(inode);
+	inode->i_op=NULL;
+	inode->i_mode=0;
+	ntfs_debug(DEBUG_OTHER, "ntfs_read_inode %x\n",(unsigned)inode->i_ino);
+
+	switch(inode->i_ino)
+	{
+		/* those are loaded special files */
+	case FILE_MFT:
+		ntfs_error("Trying to open MFT\n");return;
+	default:
+		#ifdef NTFS_IN_LINUX_KERNEL
+		ino=&inode->u.ntfs_i;
+		#else
+		ino=(ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+		inode->u.generic_ip=ino;
+		#endif
+		if(!ino || ntfs_init_inode(ino,
+					   NTFS_INO2VOL(inode),inode->i_ino))
+		{
+			ntfs_debug(DEBUG_OTHER, "NTFS:Error loading inode %x\n",
+			       (unsigned int)inode->i_ino);
+			return;
+		}
+	}
+	/* Set uid/gid from mount options */
+	inode->i_uid=vol->uid;
+	inode->i_gid=vol->gid;
+	inode->i_nlink=1;
+	/* Use the size of the data attribute as file size */
+	data = ntfs_find_attr(ino,vol->at_data,NULL);
+	if(!data)
+	{
+		inode->i_size=0;
+		can_mmap=0;
+	}
+	else
+	{
+		inode->i_size=data->size;
+		can_mmap=!data->resident && !data->compressed;
+	}
+	/* get the file modification times from the standard information */
+	si=ntfs_find_attr(ino,vol->at_standard_information,NULL);
+	if(si){
+		char *attr=si->d.data;
+		inode->i_atime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+0x18));
+		inode->i_ctime=ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+		inode->i_mtime=ntfs_ntutc2unixutc(NTFS_GETU64(attr+8));
+	}
+	/* if it has an index root, it's a directory */
+	if(ntfs_find_attr(ino,vol->at_index_root,"$I30"))
+	{
+		ntfs_attribute *at;
+		at = ntfs_find_attr (ino, vol->at_index_allocation, "$I30");
+		inode->i_size = at ? at->size : 0;
+	  
+		inode->i_op=&ntfs_dir_inode_operations;
+		inode->i_mode=S_IFDIR|S_IRUGO|S_IXUGO;
+	}
+	else
+	{
+		inode->i_op=can_mmap ? &ntfs_inode_operations : 
+			&ntfs_inode_operations_nobmap;
+		inode->i_mode=S_IFREG|S_IRUGO|S_IMMUTABLE;
+		inode->i_mode=S_IFREG|S_IRUGO;
+	}
+	inode->i_mode &= ~vol->umask;
+}
+
+static void ntfs_put_inode(struct inode *ino)
+{
+	ntfs_debug(DEBUG_OTHER, "ntfs_put_inode %lx\n",ino->i_ino);
+#ifdef NTFS_IN_LINUX_KERNEL
+	if(ino->i_ino!=FILE_MFT)
+		ntfs_clear_inode(&ino->u.ntfs_i);
+#else
+	if(ino->i_ino!=FILE_MFT && ino->u.generic_ip)
+	{
+		ntfs_clear_inode(ino->u.generic_ip);
+		ntfs_free(ino->u.generic_ip);
+		ino->u.generic_ip=0;
+	}
+#endif
+	clear_inode(ino);
+}
+
+/* Called when umounting a filesystem by do_umount() in fs/super.c */
+static void ntfs_put_super(struct super_block *sb)
+{
+	ntfs_volume *vol;
+
+	ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
+	/* Ensure that nobody uses the super block anymore */
+	lock_super(sb);
+	vol=NTFS_SB2VOL(sb);
+	/* Tell the kernel that the super block is no more used */
+	sb->s_dev = 0;
+	unlock_super(sb);
+	ntfs_release_volume(vol);
+	if(vol->nls_map)
+		unload_nls(vol->nls_map);
+#ifndef NTFS_IN_LINUX_KERNEL
+	ntfs_free(vol);
+#endif
+	ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
+	MOD_DEC_USE_COUNT;
+}
+
+/* Called by the kernel when asking for stats */
+static int ntfs_statfs(struct super_block *sb, struct statfs *sf, int bufsize)
+{
+	struct statfs fs;
+	struct inode *mft;
+	ntfs_volume *vol;
+
+	ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
+	vol=NTFS_SB2VOL(sb);
+	memset(&fs,0,sizeof(fs));
+	fs.f_type=NTFS_SUPER_MAGIC;
+	fs.f_bsize=vol->clustersize;
+
+	fs.f_blocks=ntfs_get_volumesize(NTFS_SB2VOL(sb));
+	fs.f_bfree=ntfs_get_free_cluster_count(vol->bitmap);
+	fs.f_bavail=fs.f_bfree;
+
+	/* Number of files is limited by free space only, so we lie here */
+	fs.f_ffree=0;
+	mft=iget(sb,FILE_MFT);
+	fs.f_files=mft->i_size/vol->mft_recordsize;
+	iput(mft);
+
+	/* should be read from volume */
+	fs.f_namelen=255;
+	copy_to_user(sf,&fs,bufsize);
+	return 0;
+}
+
+/* Called when remounting a filesystem by do_remount_sb() in fs/super.c */
+static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+	if(!parse_options(NTFS_SB2VOL(sb), options))
+		return -EINVAL;
+	return 0;
+}
+
+/* Define the super block operation that are implemented */
+struct super_operations ntfs_super_operations = {
+	ntfs_read_inode,
+	NULL, /* write_inode */
+	ntfs_put_inode,
+	NULL, /* delete_inode */
+	NULL, /* notify_change */
+	ntfs_put_super,
+	NULL, /* write_super */
+	ntfs_statfs,
+	ntfs_remount_fs, /* remount */
+};
+
+/* Called to mount a filesystem by read_super() in fs/super.c
+ * Return a super block, the main structure of a filesystem
+ *
+ * NOTE : Don't store a pointer to an option, as the page containing the
+ * options is freed after ntfs_read_super() returns.
+ *
+ * NOTE : A context switch can happen in kernel code only if the code blocks
+ * (= calls schedule() in kernel/sched.c).
+ */
+struct super_block * ntfs_read_super(struct super_block *sb, 
+				     void *options, int silent)
+{
+	ntfs_volume *vol;
+	struct buffer_head *bh;
+	int i;
+
+	/* When the driver is compiled as a module, kerneld must know when it
+	 * can safely remove it from memory. To do this, each module owns a
+	 * reference counter.
+	 */
+	MOD_INC_USE_COUNT;
+	/* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block
+	 * so this could lead to a race condition with kerneld.
+	 */
+	ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
+
+#ifdef NTFS_IN_LINUX_KERNEL
+	vol = NTFS_SB2VOL(sb);
+#else
+	if(!(vol = ntfs_malloc(sizeof(ntfs_volume))))
+		goto ntfs_read_super_dec;
+	NTFS_SB2VOL(sb)=vol;
+#endif
+	
+	if(!parse_options(vol,(char*)options))
+		goto ntfs_read_super_vol;
+
+	/* Ensure that the super block won't be used until it is completed */
+	lock_super(sb);
+	ntfs_debug(DEBUG_OTHER, "lock_super\n");
+#if 0
+	/* Set to read only, user option might reset it */
+	sb->s_flags |= MS_RDONLY;
+#endif
+
+	/* Assume a 512 bytes block device for now */
+	set_blocksize(sb->s_dev, 512);
+	/* Read the super block (boot block) */
+	if(!(bh=bread(sb->s_dev,0,512))) {
+		ntfs_error("Reading super block failed\n");
+		goto ntfs_read_super_unl;
+	}
+	ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
+
+	/* Check for 'NTFS' magic number */
+	if(!IS_NTFS_VOLUME(bh->b_data)){
+		ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
+		brelse(bh);
+		goto ntfs_read_super_unl;
+	}
+
+	ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
+	ntfs_init_volume(vol,bh->b_data);
+	ntfs_debug(DEBUG_OTHER, "MFT record at cluster 0x%X\n",vol->mft_cluster);
+	brelse(bh);
+	NTFS_SB(vol)=sb;
+	ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
+
+	/* Inform the kernel that a device block is a NTFS cluster */
+	sb->s_blocksize=vol->clustersize;
+	for(i=sb->s_blocksize,sb->s_blocksize_bits=0;i;i>>=1)
+		sb->s_blocksize_bits++;
+	set_blocksize(sb->s_dev,sb->s_blocksize);
+	ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
+
+	/* Allocate a MFT record (MFT record can be smaller than a cluster) */
+	if(!(vol->mft=ntfs_malloc(max(vol->mft_recordsize,vol->clustersize))))
+		goto ntfs_read_super_unl;
+
+	/* Read at least the MFT record for $MFT */
+	for(i=0;i<max(vol->mft_clusters_per_record,1);i++){
+		if(!(bh=bread(sb->s_dev,vol->mft_cluster+i,vol->clustersize))) {
+			ntfs_error("Could not read MFT record 0\n");
+			goto ntfs_read_super_mft;
+		}
+		ntfs_memcpy(vol->mft+i*vol->clustersize,bh->b_data,vol->clustersize);
+		brelse(bh);
+		ntfs_debug(DEBUG_OTHER, "Read cluster %x\n",vol->mft_cluster+i);
+	}
+
+	/* Check and fixup this MFT record */
+	if(!ntfs_check_mft_record(vol,vol->mft)){
+		ntfs_error("Invalid MFT record 0\n");
+		goto ntfs_read_super_mft;
+	}
+
+	/* Inform the kernel about which super operations are available */
+	sb->s_op = &ntfs_super_operations;
+	sb->s_magic = NTFS_SUPER_MAGIC;
+	
+	ntfs_debug(DEBUG_OTHER, "Reading special files\n");
+	if(ntfs_load_special_files(vol)){
+		ntfs_error("Error loading special files\n");
+		goto ntfs_read_super_mft;
+	}
+
+	ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
+	/* Get the root directory */
+	if(!(sb->s_root=d_alloc_root(iget(sb,FILE_ROOT),NULL))){
+		ntfs_error("Could not get root dir inode\n");
+		goto ntfs_read_super_mft;
+	}
+	unlock_super(sb);
+	ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+	ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+	return sb;
+
+ntfs_read_super_mft:
+	ntfs_free(vol->mft);
+ntfs_read_super_unl:
+	sb->s_dev = 0;
+	unlock_super(sb);
+	ntfs_debug(DEBUG_OTHER, "unlock_super\n");
+ntfs_read_super_vol:
+	ntfs_free(vol);
+ntfs_read_super_dec:
+	ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+	MOD_DEC_USE_COUNT;
+	return NULL;
+}
+
+/* Define the filesystem
+ *
+ * Define SECOND if you cannot unload ntfs, and want to avoid rebooting
+ * for just one more test
+ */
+struct file_system_type ntfs_fs_type = {
+/* Filesystem name, as used after mount -t */
+#ifndef SECOND
+	"ntfs",
+#else
+	"ntfs2",
+#endif
+/* This filesystem requires a device (a hard disk)
+ * May want to add FS_IBASKET when it works
+ */
+	FS_REQUIRES_DEV,
+/* Entry point of the filesystem */
+	ntfs_read_super,
+/* Will point to the next filesystem in the kernel table */
+	NULL
+};
+
+/* When this code is not compiled as a module, this is the main entry point,
+ * called by do_sys_setup() in fs/filesystems.c
+ *
+ * NOTE : __initfunc() is a macro used to remove this function from memory
+ * once initialization is done
+ */
+__initfunc(int init_ntfs_fs(void))
+{
+	/* Comment this if you trust klogd. There are reasons not to trust it
+	 */
+#if defined(DEBUG) && !defined(MODULE)
+	extern int console_loglevel;
+	console_loglevel=15;
+#endif
+	printk(KERN_NOTICE "NTFS version " NTFS_VERSION "\n");
+	SYSCTL(1);
+	ntfs_debug(DEBUG_OTHER, "registering %s\n",ntfs_fs_type.name);
+	/* add this filesystem to the kernel table of filesystems */
+	return register_filesystem(&ntfs_fs_type);
+}
+
+#ifdef MODULE
+/* A module is a piece of code which can be inserted in and removed
+ * from the running kernel whenever you want using lsmod, or on demand using
+ * kerneld
+ */
+
+/* No function of this module is needed by another module */
+EXPORT_NO_SYMBOLS;
+/* Only used for documentation purposes at the moment,
+ * see include/linux/module.h
+ */
+MODULE_AUTHOR("Martin von Löwis");
+MODULE_DESCRIPTION("NTFS driver");
+/* no MODULE_SUPPORTED_DEVICE() */
+/* Load-time parameter */
+MODULE_PARM(ntdebug, "i");
+MODULE_PARM_DESC(ntdebug, "Debug level");
+
+/* When this code is compiled as a module, if you use mount -t ntfs when no
+ * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
+ * fs/super.c asks kerneld to load the module named ntfs in memory.
+ *
+ * Therefore, this function is the main entry point in this case
+ */
+int init_module(void)
+{
+	return init_ntfs_fs();
+}
+
+/* Called by kerneld just before the kernel removes the module from memory */
+void cleanup_module(void)
+{
+	SYSCTL(0);
+	ntfs_debug(DEBUG_OTHER, "unregistering %s\n",ntfs_fs_type.name);
+	unregister_filesystem(&ntfs_fs_type);
+}
+#endif
+
+/*
+ * Local variables:
+ *  c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/inode.c linux/fs/ntfs/inode.c
--- v2.1.73/linux/fs/ntfs/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/inode.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,1196 @@
+/*
+ *  inode.c
+ *
+ *  Copyright (C) 1995-1997 Martin von Löwis
+ *  Copyright (C) 1996 Albert D. Cahalan
+ *  Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "inode.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "attr.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "util.h"
+
+typedef struct {
+	int recno;
+	unsigned char* record;
+} ntfs_mft_record;
+
+typedef struct {
+	int size;
+	int count;
+	ntfs_mft_record* records;
+} ntfs_disk_inode;
+
+static void
+fill_mft_header(ntfs_u8*mft,int record_size,int blocksize,
+		int sequence_number)
+{
+	int fixup_count = record_size / blocksize + 1;
+	int attr_offset = (0x2a + (2 * fixup_count) + 7) & ~7;
+	int fixup_offset = 0x2a;
+
+	NTFS_PUTU32(mft + 0x00, 0x454c4946);	     /* FILE */
+	NTFS_PUTU16(mft + 0x04, 0x2a);		     /* offset to fixup */
+	NTFS_PUTU16(mft + 0x06, fixup_count);	     /* Number of fixups */
+	NTFS_PUTU16(mft + 0x10, sequence_number);
+	NTFS_PUTU16(mft + 0x12, 1);                  /* hard link count */
+	NTFS_PUTU16(mft + 0x14, attr_offset);	     /* Offset to attributes */
+	NTFS_PUTU16(mft + 0x16, 1);                  /*FIXME: flags ?? */
+	NTFS_PUTU32(mft + 0x18, attr_offset + 0x08);	/* In use */
+	NTFS_PUTU32(mft + 0x1c, record_size);	     /* Total size */
+
+	NTFS_PUTU16(mft + fixup_offset, 1);		/* Fixup word */
+	NTFS_PUTU32(mft + attr_offset, 0xffffffff);	/* End marker */
+}
+
+/* Search in an inode an attribute by type and name */
+ntfs_attribute* 
+ntfs_find_attr(ntfs_inode *ino,int type,char *name)
+{
+	int i;
+	if(!ino){
+		ntfs_error("ntfs_find_attr: NO INODE!\n");
+		return 0;
+	}
+	for(i=0;i<ino->attr_count;i++)
+	{
+		if(type==ino->attrs[i].type)
+		{
+			if(!name && !ino->attrs[i].name)
+				return ino->attrs+i;
+			if(name && !ino->attrs[i].name)
+				return 0;
+			if(!name && ino->attrs[i].name)
+				return 0;
+			if(ntfs_ua_strncmp(ino->attrs[i].name,name,strlen(name))==0)
+				return ino->attrs+i;
+		}
+		if(type<ino->attrs[i].type)
+			return 0;
+	}
+	return 0;
+}
+
+/* FIXME: need better strategy to extend the MFT */
+static int 
+ntfs_extend_mft(ntfs_volume *vol)
+{
+	/* Try to allocate at least 0.1% of the remaining disk space
+	   for inodes. If the disk is almost full, make sure at least one
+	   inode is requested.
+	 */
+	int size,rcount,error,block;
+	ntfs_attribute* mdata,*bmp;
+	ntfs_u8 *buf;
+	ntfs_io io;
+
+	mdata=ntfs_find_attr(vol->mft_ino,vol->at_data,0);
+	/* first check whether there is uninitialized space */
+	if(mdata->allocated<mdata->size+vol->mft_recordsize){
+		size=ntfs_get_free_cluster_count(vol->bitmap)*vol->clustersize;
+		block=vol->mft_recordsize;
+		size=max(size/1000,block);
+		size=((size+block-1)/block)*block;
+		/* require this to be a single chunk */
+		error=ntfs_extend_attr(vol->mft_ino,mdata,&size,
+				       ALLOC_REQUIRE_SIZE);
+		/* Try again, now we have the largest available fragment */
+		if(error==ENOSPC){
+			/* round down to multiple of mft record size */
+			size=(size/vol->mft_recordsize)*vol->mft_recordsize;
+			if(!size)return ENOSPC;
+			error=ntfs_extend_attr(vol->mft_ino,mdata,&size,
+					       ALLOC_REQUIRE_SIZE);
+		}
+		if(error)
+			return error;
+	}
+	/* even though we might have allocated more than needed,
+	   we initialize only one record */
+	mdata->size+=vol->mft_recordsize;
+	
+	/* now extend the bitmap if necessary*/
+	rcount=mdata->size/vol->mft_recordsize;
+	bmp=ntfs_find_attr(vol->mft_ino,vol->at_bitmap,0);
+	if(bmp->size*8<rcount){ /* less bits than MFT records */
+		ntfs_u8 buf[1];
+		/* extend bitmap by one byte */
+		error=ntfs_resize_attr(vol->mft_ino,bmp,bmp->size+1);
+		if(error)return error;
+		/* write the single byte */
+		buf[0]=0;
+		io.fn_put=ntfs_put;
+		io.fn_get=ntfs_get;
+		io.param=buf;
+		io.size=1;
+		error=ntfs_write_attr(vol->mft_ino,vol->at_bitmap,0,
+				      bmp->size-1,&io);
+		if(error)return error;
+		if(io.size!=1)return EIO;
+	}
+
+	/* now fill in the MFT header for the new block */
+	buf=ntfs_calloc(vol->mft_recordsize);
+	if(!buf)return ENOMEM;
+	fill_mft_header(buf,vol->mft_recordsize,vol->blocksize,0);
+	ntfs_insert_fixups(buf,vol->blocksize);
+	io.param=buf;
+	io.size=vol->mft_recordsize;
+	error=ntfs_write_attr(vol->mft_ino,vol->at_data,0,
+			      (rcount-1)*vol->mft_recordsize,&io);
+	if(error)return error;
+	if(io.size!=vol->mft_recordsize)return EIO;
+	return 0;
+}
+
+/* Insert all attributes from the record mftno of the MFT in the inode ino */
+void ntfs_insert_mft_attributes(ntfs_inode* ino,char *mft,int mftno)
+{
+	int i;
+	char *it;
+	int type,len;
+	/* check for duplicate */
+	for(i=0;i<ino->record_count;i++)
+		if(ino->records[i]==mftno)
+			return;
+	/* (re-)allocate space if necessary */
+	if(ino->record_count % 8==0)
+	{
+		int *old=ino->records;
+		ino->records=ntfs_malloc((ino->record_count+8)*sizeof(int));
+		if(old) {
+			for(i=0;i<ino->record_count;i++)
+				ino->records[i]=old[i];
+			ntfs_free(old);
+		}
+	}
+	ino->records[ino->record_count]=mftno;
+	ino->record_count++;
+	it = mft + NTFS_GETU16(mft + 0x14);
+	do{
+		type=NTFS_GETU32(it);
+		len=NTFS_GETU32(it+4);
+		if(type!=-1)
+			ntfs_insert_attribute(ino,it);
+		it+=len;
+	}while(type!=-1); /* attribute list ends with type -1 */
+}
+
+/* Read and insert all the attributes of an 'attribute list' attribute
+   Return the number of remaining bytes in *plen
+*/
+static int parse_attributes(ntfs_inode *ino, void *alist, int *plen)
+{
+	char *mft;
+	int mftno,l,error;
+	int last_mft=-1;
+	int len=*plen;
+	mft=ntfs_malloc(ino->vol->mft_recordsize);
+	while(len>8)
+	{
+		l=NTFS_GETU16(alist+4);
+		if(l>len)break;
+	        /* process an attribute description */
+		mftno=NTFS_GETU32(alist+0x10); /* BUG: this is u64 */
+		if(mftno!=last_mft){
+			last_mft=mftno;
+			/* FIXME: avoid loading record if it's 
+			   already processed */
+			error=ntfs_read_mft_record(ino->vol,mftno,mft);
+			if(error)return error;
+			ntfs_insert_mft_attributes(ino,mft,mftno);
+		}
+		len-=l;
+		alist+=l;
+	}
+	ntfs_free(mft);
+	*plen=len;
+	return 0;
+}
+
+static void ntfs_load_attributes(ntfs_inode* ino)
+{
+	ntfs_attribute *alist;
+	int datasize;
+	int offset,len,delta;
+	char *buf;
+	ntfs_volume *vol=ino->vol;
+	ntfs_debug(DEBUG_OTHER, "load_attributes %x 1\n",ino->i_number);
+	ntfs_insert_mft_attributes(ino,ino->attr,ino->i_number);
+	ntfs_debug(DEBUG_OTHER, "load_attributes %x 2\n",ino->i_number);
+	alist=ntfs_find_attr(ino,vol->at_attribute_list,0);
+	ntfs_debug(DEBUG_OTHER, "load_attributes %x 3\n",ino->i_number);
+	if(!alist)
+		return;
+	ntfs_debug(DEBUG_OTHER, "load_attributes %x 4\n",ino->i_number);
+	datasize=alist->size;
+	if(alist->resident)
+	{
+		parse_attributes(ino,alist->d.data,&datasize);
+		return;
+	}
+	buf=ntfs_malloc(1024);
+	delta=0;
+	for(offset=0;datasize;datasize-=len)
+	{
+		ntfs_io io;
+		io.fn_put=ntfs_put;
+		io.fn_get=0;
+		io.param=buf+delta;
+		io.size=len=min(datasize,1024-delta);
+		if(ntfs_read_attr(ino,vol->at_attribute_list,0,offset,&io)){
+			ntfs_error("error in load_attributes\n");
+		}
+		delta=len;
+		parse_attributes(ino,buf,&delta);
+		if(delta)
+			/* move remaining bytes to buffer start */
+			ntfs_memmove(buf,buf+len-delta,delta);
+	}
+	ntfs_debug(DEBUG_OTHER, "load_attributes %x 5\n",ino->i_number);
+	ntfs_free(buf);
+}
+	
+int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum)
+{
+	char *buf;
+	int error;
+
+	ntfs_debug(DEBUG_OTHER, "Initializing inode %x\n",inum);
+	if(!vol)
+		ntfs_error("NO VOLUME!\n");
+	ino->i_number=inum;
+	ino->vol=vol;
+	ino->attr=buf=ntfs_malloc(vol->mft_recordsize);
+	error=ntfs_read_mft_record(vol,inum,ino->attr);
+	if(error){
+		ntfs_debug(DEBUG_OTHER, "init inode: %x failed\n",inum);
+		return error;
+	}
+	ntfs_debug(DEBUG_OTHER, "Init: got mft %x\n",inum);
+	ino->sequence_number=NTFS_GETU16(buf+0x10);
+	ino->attr_count=0;
+	ino->record_count=0;
+	ino->records=0;
+	ino->attrs=0;
+	ntfs_load_attributes(ino);
+	ntfs_debug(DEBUG_OTHER, "Init: done %x\n",inum);
+	return 0;
+}
+
+void ntfs_clear_inode(ntfs_inode *ino)
+{
+	int i;
+	ntfs_free(ino->attr);
+	ntfs_free(ino->records);
+	for(i=0;i<ino->attr_count;i++)
+	{
+		if(ino->attrs[i].name)
+			ntfs_free(ino->attrs[i].name);
+		if(ino->attrs[i].resident)
+		{
+			if(ino->attrs[i].d.data)
+				ntfs_free(ino->attrs[i].d.data);
+		}else{
+			if(ino->attrs[i].d.r.runlist)
+				ntfs_free(ino->attrs[i].d.r.runlist);
+		}
+	}
+	ntfs_free(ino->attrs);
+}
+
+/* Check and fixup a MFT record */
+int ntfs_check_mft_record(ntfs_volume *vol,char *record)
+{
+	return ntfs_fixup_record(vol, record, "FILE", vol->mft_recordsize);
+}
+
+/* Return (in result) the value indicating the next available attribute 
+   chunk number. Works for inodes w/o extension records only */
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
+{
+	if(ino->record_count!=1)
+		return EOPNOTSUPP;
+	*result=NTFS_GETU16(ino->attr+0x28);
+	NTFS_PUTU16(ino->attr+0x28, (*result)+1);
+	return 0;
+}
+
+/* find the location of an attribute in the inode. A name of NULL indicates
+   unnamed attributes. Return pointer to attribute or NULL if not found */
+char *
+ntfs_get_attr(ntfs_inode *ino,int attr,char *name)
+{
+	/* location of first attribute */
+	char *it= ino->attr + NTFS_GETU16(ino->attr + 0x14);
+	int type;
+	int len;
+	/* Only check for magic DWORD here, fixup should have happened before */
+	if(!IS_MFT_RECORD(ino->attr))return 0;
+	do{
+		type=NTFS_GETU32(it);
+		len=NTFS_GETU16(it+4);
+		/* We found the attribute type. Is the name correct, too? */
+		if(type==attr)
+		{
+			int namelen=NTFS_GETU8(it+9);
+			char *name_it;
+			/* match given name and attribute name if present,
+			   make sure attribute name is Unicode */
+			for(name_it=it+NTFS_GETU16(it+10);namelen;
+			    name++,name_it+=2,namelen--)
+				if(*name_it!=*name || name_it[1])break;
+			if(!namelen)break;
+		}
+		it+=len;
+	}while(type!=-1); /* attribute list end with type -1 */
+	if(type==-1)return 0;
+	return it;
+}
+
+int 
+ntfs_get_attr_size(ntfs_inode*ino,int type,char*name)
+{
+	ntfs_attribute *attr=ntfs_find_attr(ino,type,name);
+	if(!attr)return 0;
+	return attr->size;
+}
+	
+int 
+ntfs_attr_is_resident(ntfs_inode*ino,int type,char*name)
+{
+	ntfs_attribute *attr=ntfs_find_attr(ino,type,name);
+	if(!attr)return 0;
+	return attr->resident;
+}
+	
+/*
+ * A run is coded as a type indicator, an unsigned length, and a signed cluster
+ * offset.
+ * . To save space, length and offset are fields of variable length. The low
+ *   nibble of the type indicates the width of the length :), the high nibble
+ *   the width of the offset.
+ * . The first offset is relative to cluster 0, later offsets are relative to
+ *   the previous cluster.
+ *
+ * This function decodes a run. Length is an output parameter, data and cluster
+ * are in/out parameters.
+ */
+int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
+	int *ctype)
+{
+	unsigned char type=*(*data)++;
+	*ctype=0;
+	switch(type & 0xF)
+	{
+	case 1: *length=NTFS_GETU8(*data);(*data)++;break;
+	case 2: *length=NTFS_GETU16(*data);
+		*data+=2;
+		break;
+	case 3: *length = NTFS_GETU24(*data);
+		*data+=3;
+		break;
+		/* TODO: case 4-8 */
+	default:
+		ntfs_error("Can't decode run type field %x\n",type);
+		return -1;
+	}
+	switch(type & 0xF0)
+	{
+	case 0:    *ctype=2;break;
+	case 0x10: *cluster+=NTFS_GETS8(*data);(*data)++;break;
+	case 0x20: *cluster+=NTFS_GETS16(*data);
+		*data+=2;
+		break;
+	case 0x30: 	*cluster+=NTFS_GETS24(*data);
+		*data+=3;
+		break;
+		/* TODO: case 0x40-0x80 */
+	default:
+		ntfs_error("Can't decode run type field %x\n",type);
+		return -1;
+	}
+	return 0;
+}
+
+/* Reads l bytes of the attribute (attr,name) of ino starting at offset
+   on vol into buf. Returns the number of bytes read in the ntfs_io struct.
+   Returns 0 on success, errno on failure */
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+	ntfs_io *dest)
+{
+	int datasize,rnum;
+	int cluster,s_cluster,vcn,len,l,chunk,copied;
+	int s_vcn;
+	int clustersize;
+	int error;
+
+	clustersize=ino->vol->clustersize;
+	datasize=attr->size;
+	l=dest->size;
+	if(dest->do_read)
+	{
+		if(offset>=datasize){
+			dest->size=0;
+			return 0;
+		}
+		if(offset+l>=datasize) 
+			l=dest->size=datasize-offset;
+	}else { /* fixed by CSA: if writing beyond end, extend attribute */
+		if (offset+l>datasize) {
+			error=ntfs_resize_attr(ino,attr,offset+l);
+			if(error)
+				return error;
+		}
+	}
+	if(attr->resident)
+	{
+		if(dest->do_read)
+			dest->fn_put(dest,attr->d.data+offset,l);
+		else
+		{
+			dest->fn_get(attr->d.data+offset,dest,l);
+			ntfs_update_inode(ino);
+		}
+		dest->size=l;
+		return 0;
+	}
+	if(attr->compressed)
+		if(dest->do_read)
+			return ntfs_read_compressed(ino,attr,offset,dest);
+		else
+			return ntfs_write_compressed(ino,attr,offset,dest);
+	vcn=0;
+	s_vcn = offset/clustersize;
+	for(rnum=0;rnum<attr->d.r.len && 
+		    vcn+attr->d.r.runlist[rnum].len<=s_vcn;rnum++)
+		vcn+=attr->d.r.runlist[rnum].len;
+	if(rnum==attr->d.r.len)
+		/*FIXME: should extend runlist */
+		return EOPNOTSUPP;
+	
+	copied=0;
+	while(l)
+	{
+		s_vcn = offset/clustersize;
+		cluster=attr->d.r.runlist[rnum].cluster;
+		len=attr->d.r.runlist[rnum].len;
+
+		s_cluster = cluster+s_vcn-vcn;
+			
+		chunk=min((vcn+len)*clustersize-offset,l);
+		dest->size=chunk;
+		error=ntfs_getput_clusters(ino->vol,s_cluster,
+					   offset-s_vcn*clustersize,dest);
+		if(error)/* FIXME: maybe return failure */
+		{
+			ntfs_error("Read error\n");
+			dest->size=copied;
+			return 0;
+		}
+		l-=chunk;
+		copied+=chunk;
+		offset+=chunk;
+		if(l && offset>=((vcn+len)*clustersize))
+		{
+			rnum++;
+			vcn+=len;
+			cluster = attr->d.r.runlist[rnum].cluster;
+			len = attr->d.r.runlist[rnum].len;
+		}
+	}
+	dest->size=copied;
+	return 0;
+}
+
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
+        ntfs_io *buf)
+{
+	ntfs_attribute *attr;
+	buf->do_read=1;
+	attr=ntfs_find_attr(ino,type,name);
+	if(!attr)
+		return EINVAL;
+	return ntfs_readwrite_attr(ino,attr,offset,buf);
+}
+
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
+		ntfs_io *buf)
+{
+	ntfs_attribute *attr;
+	buf->do_read=0;
+	attr=ntfs_find_attr(ino,type,name);
+	if(!attr)
+		return EINVAL;
+	return ntfs_readwrite_attr(ino,attr,offset,buf);
+}
+
+int ntfs_vcn_to_lcn(ntfs_inode *ino,int vcn)
+{
+	int rnum;
+	ntfs_attribute *data=ntfs_find_attr(ino,ino->vol->at_data,0);
+	/* It's hard to give an error code */
+	if(!data)return -1;
+	if(data->resident)return -1;
+	if(data->compressed)return -1;
+	if(data->size<vcn*ino->vol->clustersize)return -1;
+
+	for(rnum=0;rnum<data->d.r.len && 
+		    vcn>data->d.r.runlist[rnum].len;rnum++)
+		vcn-=data->d.r.runlist[rnum].len;
+	
+	return data->d.r.runlist[rnum].cluster+vcn;
+}
+
+static int 
+allocate_store(ntfs_volume *vol,ntfs_disk_inode *store,int count)
+{
+	int i;
+	if(store->count>count)
+		return 0;
+	if(store->size<count){
+		ntfs_mft_record* n=ntfs_malloc((count+4)*sizeof(ntfs_mft_record));
+		if(!n)
+			return ENOMEM;
+		if(store->size){
+			for(i=0;i<store->size;i++)
+				n[i]=store->records[i];
+			ntfs_free(store->records);
+		}
+		store->size=count+4;
+		store->records=n;
+	}
+	for(i=store->count;i<count;i++){
+		store->records[i].record=ntfs_malloc(vol->mft_recordsize);
+		if(!store->records[i].record)
+			return ENOMEM;
+		store->count++;
+	}
+	return 0;
+}
+
+static void 
+deallocate_store(ntfs_disk_inode* store)
+{
+	int i;
+	for(i=0;i<store->count;i++)
+		ntfs_free(store->records[i].record);
+	ntfs_free(store->records);
+	store->count=store->size=0;
+	store->records=0;
+}
+
+int 
+layout_runs(ntfs_attribute *attr,char* rec,int* offs,int size)
+{
+	int i,cluster,rclus,len,offset,coffs;
+	ntfs_runlist *rl=attr->d.r.runlist;
+	cluster=0;
+	offset=*offs;
+	for(i=0;i<attr->d.r.len;i++){
+		rclus=rl[i].cluster-cluster;
+		len=rl[i].len;
+		rec[offset]=0;
+		if(offset+8>size)
+			return E2BIG; /* it might still fit, but this simplifies testing */
+		if(len<0x100){
+			*(rec+offset)|=1;
+			NTFS_PUTU8(rec+offset+1,len);
+			coffs=2;
+		}else if(len<0x10000){
+			*(rec+offset)|=2;
+			NTFS_PUTU16(rec+offset+1,len);
+			coffs=3;
+		}else if(len<0x1000000){
+			*(rec+offset)|=3;
+			NTFS_PUTU24(rec+offset+1,len);
+			coffs=4;
+		}else{
+			*(rec+offset)|=4;
+			NTFS_PUTU32(rec+offset+1,len);
+			coffs=5;
+		}
+    
+		if(rl[i].cluster==0) /*compressed run*/
+			/*nothing*/;
+		else if(rclus>-0x80 && rclus<0x7F){
+			*(rec+offset)|=0x10;
+			NTFS_PUTS8(rec+offset+coffs,rclus);
+			coffs+=1;
+		}else if(rclus>-0x8000 && rclus<0x7FFF){
+			*(rec+offset)|=0x20;
+			NTFS_PUTS16(rec+offset+coffs,rclus);
+			coffs+=2;
+		}else if(rclus>-0x800000 && rclus<0x7FFFFF){
+			*(rec+offset)|=0x30;
+			NTFS_PUTS24(rec+offset+coffs,rclus);
+			coffs+=3;
+		}else{
+			*(rec+offset)|=0x40;
+			NTFS_PUTS32(rec+offset+coffs,rclus);
+			coffs+=4;
+		}
+		offset+=coffs;
+		if(rl[i].cluster)
+			cluster=rl[i].cluster;
+	}
+	*offs=offset;
+	return 0;
+}
+
+static void 
+count_runs(ntfs_attribute *attr,char *buf)
+{
+	int first,count,last,i;
+	first=0;
+	for(i=0,count=0;i<attr->d.r.len;i++)
+		count+=attr->d.r.runlist[i].len;
+	last=first+count-1;
+
+	NTFS_PUTU32(buf+0x10,first);
+	NTFS_PUTU32(buf+0x18,last);
+} 
+
+static int
+layout_attr(ntfs_attribute* attr,char*buf, int size,int *psize)
+{
+	int asize,error;
+	if(size<10)return E2BIG;
+	NTFS_PUTU32(buf,attr->type);
+	/* fill in length later */
+	NTFS_PUTU8(buf+8,attr->resident ? 0:1);
+	NTFS_PUTU8(buf+9,attr->namelen);
+	/* fill in offset to name later */
+	NTFS_PUTU16(buf+0xA,0);
+	NTFS_PUTU16(buf+0xC,attr->compressed);
+	/* FIXME: assign attribute ID??? */
+	NTFS_PUTU16(buf+0xE,attr->attrno);
+	if(attr->resident){
+		if(size<attr->size+0x18+attr->namelen)return E2BIG;
+		asize=0x18;
+		NTFS_PUTU32(buf+0x10,attr->size);
+		NTFS_PUTU16(buf+0x16,attr->indexed);
+		if(attr->name){
+			ntfs_memcpy(buf+asize,attr->name,2*attr->namelen);
+			NTFS_PUTU16(buf+0xA,asize);
+			asize+=2*attr->namelen;
+			asize=(asize+7) & ~7;
+		}
+		NTFS_PUTU16(buf+0x14,asize);
+		ntfs_memcpy(buf+asize,attr->d.data,attr->size);
+		asize+=attr->size;
+	}else{
+		/* FIXME: fragments */
+		count_runs(attr,buf);
+		/* offset to data is added later */
+		NTFS_PUTU16(buf+0x22,attr->cengine);
+		NTFS_PUTU32(buf+0x24,0);
+		NTFS_PUTU64(buf+0x28,attr->allocated);
+		NTFS_PUTU64(buf+0x30,attr->size);
+		NTFS_PUTU64(buf+0x38,attr->initialized);
+		if(attr->compressed){
+			NTFS_PUTU64(buf+0x40,attr->compsize);
+			asize=0x48;
+		}else
+			asize=0x40;
+		if(attr->name){
+			NTFS_PUTU16(buf+0xA,asize);
+			ntfs_memcpy(buf+asize,attr->name,2*attr->namelen);
+			asize+=2*attr->namelen;
+			asize=(asize+7) & ~7;
+		}
+		/* asize points at the beginning of the data */
+		NTFS_PUTU16(buf+0x20,asize);
+		error=layout_runs(attr,buf,&asize,size);
+		/* now asize pointes at the end of the data */
+		if(error)
+			return error;
+	}
+	asize=(asize+7) & ~7;
+	NTFS_PUTU32(buf+4,asize);
+	*psize=asize;
+	return 0;
+}
+		
+		
+
+/* Try to layout ino into store. Return 0 on success,
+   E2BIG if it does not fit, 
+   ENOMEM if memory allocation problem,
+   EOPNOTSUP if beyound our capabilities 
+*/
+int 
+layout_inode(ntfs_inode *ino,ntfs_disk_inode *store)
+{
+	int offset,i;
+	ntfs_attribute *attr;
+	unsigned char *rec;
+	int size,psize;
+	int error;
+
+	if(ino->record_count>1)
+	{
+		ntfs_error("layout_inode: attribute lists not supported\n");
+		return EOPNOTSUPP;
+	}
+	error=allocate_store(ino->vol,store,1);
+	if(error)
+		return error;
+	rec=store->records[0].record;
+	size=ino->vol->mft_recordsize;
+	store->records[0].recno=ino->records[0];
+	/* copy header */
+	offset=NTFS_GETU16(ino->attr+0x14);
+	ntfs_memcpy(rec,ino->attr,offset);
+	for(i=0;i<ino->attr_count;i++){
+		attr=ino->attrs+i;
+		error=layout_attr(attr,rec+offset,size-offset,&psize);
+		if(error)return error;
+		offset+=psize;
+#if 0
+		/* copy attribute header */
+		ntfs_memcpy(rec+offset,attr->header,
+			    min(sizeof(attr->header),size-offset)); /* consider overrun */
+		if(attr->namelen)
+			/* named attributes are added later */
+			return EOPNOTSUPP;
+		/* FIXME: assign attribute ID??? */
+		if(attr->resident){
+			asize=attr->size;
+			aoffset=NTFS_GETU16(rec+offset+0x14);
+			if(offset+aoffset+asize>size)
+				return E2BIG;
+			ntfs_memcpy(rec+offset+aoffset,attr->d.data,asize);
+			next=offset+aoffset+asize;
+		}else{
+			count_runs(attr,rec+offset);
+			aoffset=NTFS_GETU16(rec+offset+0x20);
+			next=offset+aoffset;
+			error=layout_runs(attr,rec,&next,size);
+			if(error)
+				return error;
+		}
+		next=(next+7) & ~7; /* align to DWORD */
+		NTFS_PUTU16(rec+offset+4,next-offset);
+		offset=next;
+#endif
+	}
+	/* terminating attribute */
+	if(offset+8<size){
+		NTFS_PUTU32(rec+offset,0xFFFFFFFF);
+		offset+=4;
+		NTFS_PUTU32(rec+offset,0);
+		offset+=4;
+	}else
+		return E2BIG;
+	NTFS_PUTU32(rec+0x18,offset);
+	return 0;
+}
+  
+int ntfs_update_inode(ntfs_inode *ino)
+{
+	int error;
+	ntfs_disk_inode store;
+	ntfs_io io;
+	int i;
+
+	store.count=store.size=0;
+	store.records=0;
+	error=layout_inode(ino,&store);
+	if(error==E2BIG){
+		/* should try:
+		   make attributes non-resident
+		   introduce extension records
+		   */
+		ntfs_error("cannot handle saving inode %x\n",ino->i_number);
+		deallocate_store(&store);
+		return EOPNOTSUPP;
+	}
+	if(error){
+		deallocate_store(&store);
+		return error;
+	}
+	io.fn_get=ntfs_get;
+	io.fn_put=0;
+	for(i=0;i<store.count;i++){
+		ntfs_insert_fixups(store.records[i].record,ino->vol->blocksize);
+		io.param=store.records[i].record;
+		io.size=ino->vol->mft_recordsize;
+		/* FIXME: is this the right way? */
+		error=ntfs_write_attr(
+			ino->vol->mft_ino,ino->vol->at_data,0,
+			store.records[i].recno*ino->vol->mft_recordsize,&io);
+		if(error || io.size!=ino->vol->mft_recordsize){
+			/* big trouble, partially written file */
+			ntfs_error("Please unmount: write error in inode %x\n",ino->i_number);
+			deallocate_store(&store);
+			return error?error:EIO;
+		}
+	}
+	return 0;
+}
+
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
+{
+	int head,comp;
+	int copied=0;
+	unsigned char *stop;
+	int bits;
+	int tag=0;
+	int clear_pos;
+	while(1)
+	{
+		head = NTFS_GETU16(src) & 0xFFF;
+		/* high bit indicates that compression was performed */
+		comp = NTFS_GETU8(src) & 0x80;
+		comp = (head == 0xFFF);
+		src += 2;
+		stop = src+head;
+		bits = 0;
+		clear_pos=0;
+		if(head==0)
+			/* block is not used */
+			return;/* FIXME: copied */
+		if(!comp) /* uncompressible */
+		{
+			ntfs_memcpy(dest,src,0x1000);
+			dest+=0x1000;
+			copied+=0x1000;
+			src+=0x1000;
+			if(l==copied)
+				return;
+			continue;
+		}
+		while(src<=stop)
+		{
+			if(clear_pos>4096)
+			{
+				ntfs_error("Error 1 in decompress\n");
+				return;
+			}
+			if(!bits){
+				tag=NTFS_GETU8(src);
+				bits=8;
+				src++;
+				if(src>stop)
+					break;
+			}
+			if(tag & 1){
+				int i,len,delta,code,lmask,dshift;
+				code = NTFS_GETU16(src);
+				src+=2;
+				if(!clear_pos)
+				{
+					ntfs_error("Error 2 in decompress\n");
+					return;
+				}
+				for(i=clear_pos-1,lmask=0xFFF,dshift=12;i>=0x10;i>>=1)
+				{
+					lmask >>= 1;
+					dshift--;
+				}
+				delta = code >> dshift;
+				len = (code & lmask) + 3;
+				for(i=0; i<len; i++)
+				{
+					dest[clear_pos]=dest[clear_pos-delta-1];
+					clear_pos++;
+					copied++;
+					if(copied==l)
+						return;
+				}
+			}else{
+				dest[clear_pos++]=NTFS_GETU8(src);
+				src++;
+				copied++;
+				if(copied==l)
+					return;
+			}
+			tag>>=1;
+			bits--;
+		}
+		dest+=clear_pos;
+	}
+}
+
+/* Caveat: No range checking in either ntfs_set_bit or ntfs_clear_bit */
+void 
+ntfs_set_bit (unsigned char *byte, int bit)
+{
+	byte += (bit >> 3);
+	bit &= 7;
+	*byte |= (1 << bit);
+}
+
+void 
+ntfs_clear_bit (unsigned char *byte, int bit)
+{
+	byte += (bit >> 3);
+	bit &= 7;
+	*byte &= ~(1 << bit);
+}
+
+/* We have to skip the 16 metafiles and the 8 reserved entries */
+static int 
+new_inode (ntfs_volume* vol,int* result)
+{
+	int byte,error;
+	int bit;
+	int size,length;
+	unsigned char value;
+	ntfs_u8 *buffer;
+	ntfs_io io;
+	ntfs_attribute *data;
+
+	buffer=ntfs_malloc(2048);
+	if(!buffer)return ENOMEM;
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=buffer;
+	/* FIXME: bitmaps larger than 2048 bytes */
+	io.size=2048;
+	error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,0,&io);
+	if(error){
+		ntfs_free(buffer);
+		return error;
+	}
+	size=io.size;
+	data=ntfs_find_attr(vol->mft_ino,vol->at_data,0);
+	length=data->size/vol->mft_recordsize;
+
+	for (byte = 3; 8*byte < length; byte++)
+	{
+		value = buffer[byte];
+		if(value==0xFF)
+			continue;
+		for (bit = 0; (bit < 8) && (8*byte+bit<length); 
+		     bit++, value >>= 1)
+		{
+			if (!(value & 1)){
+				*result=byte*8+bit;
+				return 0;
+			}
+		}
+	}
+	/* There is no free space.  We must first extend the MFT. */
+	return ENOSPC;
+}
+
+static int 
+add_mft_header (ntfs_inode *ino)
+{
+	unsigned char* mft;
+	ntfs_volume *vol=ino->vol;
+	mft=ino->attr;
+
+	ntfs_bzero(mft, vol->mft_recordsize);
+	fill_mft_header(mft,vol->mft_recordsize,vol->blocksize,
+			ino->sequence_number);
+	return 0;
+}
+
+/* We need 0x48 bytes in total */
+static int 
+add_standard_information (ntfs_inode *ino)
+{
+	ntfs_time64_t now;
+	char data[0x30];
+	char *position=data;
+	int error;
+	ntfs_attribute *si;
+
+	now = ntfs_now();
+	NTFS_PUTU64(position + 0x00, now);		/* File creation */
+	NTFS_PUTU64(position + 0x08, now);		/* Last modification */
+	NTFS_PUTU64(position + 0x10, now);		/* Last mod for MFT */
+	NTFS_PUTU64(position + 0x18, now);		/* Last access */
+
+	NTFS_PUTU64(position + 0x20, 0x00);		/* MSDOS file perms */
+	NTFS_PUTU64(position + 0x28, 0);               /* unknown */
+	error=ntfs_create_attr(ino,ino->vol->at_standard_information,0,
+			       data,sizeof(data),&si);
+
+	return error;
+}
+
+static int 
+add_filename (ntfs_inode* ino, ntfs_inode* dir, 
+			 const unsigned char *filename, int length)
+{
+	unsigned char   *position;
+	unsigned int    size;
+	ntfs_time64_t   now;
+	int		count;
+	int error;
+	unsigned char* data;
+	ntfs_attribute *fn;
+
+	/* work out the size */
+	size = 0x42 + 2 * length;
+	data = ntfs_malloc(size);
+	ntfs_bzero(data,size);
+
+	/* search for a position */
+	position = data;
+
+	NTFS_PUTINUM(position, dir);	/* Inode num of dir */
+
+	now = ntfs_now();
+	NTFS_PUTU64(position + 0x08, now);		/* File creation */
+	NTFS_PUTU64(position + 0x10, now);		/* Last modification */
+	NTFS_PUTU64(position + 0x18, now);		/* Last mod for MFT */
+	NTFS_PUTU64(position + 0x20, now);		/* Last access */
+
+	/* Don't know */
+	NTFS_PUTU8(position+0x38, 0x0);  /*should match standard attributes*/
+
+	NTFS_PUTU8(position + 0x40, length);	      /* Filename length */
+	NTFS_PUTU8(position + 0x41, 0x0);	      /* only long name */
+
+	position += 0x42;
+	for (count = 0; count < length; count++)
+	{
+		NTFS_PUTU16(position + 2 * count, filename[count]);
+	}
+
+	error=ntfs_create_attr(ino,ino->vol->at_file_name,0,data,size,&fn);
+	if(!error)
+		error=ntfs_dir_add(dir,ino,fn);
+	ntfs_free(data);
+	return error;
+}
+
+int 
+add_security (ntfs_inode* ino, ntfs_inode* dir)
+{
+	int error;
+	char *buf;
+	int size;
+	ntfs_attribute* attr;
+	ntfs_io io;
+	ntfs_attribute *se;
+
+	attr=ntfs_find_attr(dir,ino->vol->at_security_descriptor,0);
+	if(!attr)
+		return EOPNOTSUPP; /* need security in directory */
+	size = attr->size;
+	if(size>512)
+		return EOPNOTSUPP;
+	buf=ntfs_malloc(size);
+	if(!buf)
+		return ENOMEM;
+	io.fn_get=ntfs_get;
+	io.fn_put=ntfs_put;
+	io.param=buf;
+	io.size=size;
+	error=ntfs_read_attr(dir,ino->vol->at_security_descriptor,0,0,&io);
+	if(!error && io.size!=size)ntfs_error("wrong size in add_security");
+	if(error){
+		ntfs_free(buf);
+		return error;
+	}
+	/* FIXME: consider ACL inheritance */
+	error=ntfs_create_attr(ino,ino->vol->at_security_descriptor,
+			       0,buf,size,&se);
+	ntfs_free(buf);
+	return error;
+}
+
+static int 
+add_data (ntfs_inode* ino, unsigned char *data, int length)
+{
+	int error;
+	ntfs_attribute *da;
+	error=ntfs_create_attr(ino,ino->vol->at_data,0,data,length,&da);
+	return error;
+}
+
+
+/* We _could_ use 'dir' to help optimise inode allocation */
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
+		int namelen)
+{
+	ntfs_io io;
+	int error;
+	ntfs_u8 buffer[1];
+	ntfs_volume* vol=dir->vol;
+	int byte,bit;
+
+	error=new_inode (vol,&(result->i_number));
+	if(error==ENOSPC){
+		error=ntfs_extend_mft(vol);
+		if(error)return error;
+		error=new_inode(vol,&(result->i_number));
+	}
+	if(error){
+		ntfs_error ("ntfs_get_empty_inode: no free inodes\n");
+		return error;
+	}
+	byte=result->i_number/8;
+	bit=result->i_number & 7;
+
+	io.fn_put = ntfs_put;
+	io.fn_get = ntfs_get;
+	io.param = buffer;
+	io.size=1;
+	/* set a single bit */
+	error=ntfs_read_attr(vol->mft_ino,vol->at_bitmap,0,byte,&io);
+	if(error)return error;
+	if(io.size!=1)
+		return EIO;
+	ntfs_set_bit (buffer, bit);
+	io.param = buffer;
+	io.size = 1;
+ 	error = ntfs_write_attr (vol->mft_ino, vol->at_bitmap, 0, byte, &io);
+	if(error)return error;
+	if (io.size != 1)
+		return EIO;
+	/*FIXME: Should change MFT on disk
+	  error=ntfs_update_inode(vol->mft_ino);
+	  if(error)return error;
+	  */
+	/* get the sequence number */
+	io.param = buffer;
+	io.size = 0x10;
+	error = ntfs_read_attr(vol->mft_ino, vol->at_data, 0, 
+			       result->i_number*vol->mft_recordsize+0x10,&io);
+	if(error)
+		return error;
+	result->sequence_number=NTFS_GETU16(buffer)+1;
+	result->vol=vol;
+	result->attr=ntfs_malloc(vol->mft_recordsize);
+	result->attr_count=0;
+	result->attrs=0;
+	result->record_count=1;
+	result->records=ntfs_malloc(8*sizeof(int));
+	result->records[0]=result->i_number;
+	error=add_mft_header(result);
+	if(error)
+		return error;
+	error=add_standard_information(result);
+	if(error)
+		return error;
+	error=add_filename(result,dir,filename,namelen);
+	if(error)
+		return error;
+	error=add_security(result,dir);
+	/*FIXME: check error */
+	error=add_data(result,0,0);
+	if(error)
+		return error;
+	return 0;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/inode.h linux/fs/ntfs/inode.h
--- v2.1.73/linux/fs/ntfs/inode.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/inode.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,25 @@
+/*
+ *  inode.h
+ *  Header file for inode.c
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, int offset,
+  ntfs_io *buf);
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, int offset,
+  ntfs_io *buf);
+int ntfs_init_inode(ntfs_inode *ino,ntfs_volume *vol,int inum);
+void ntfs_clear_inode(ntfs_inode *ino);
+int ntfs_check_mft_record(ntfs_volume *vol,char *record);
+int ntfs_alloc_inode (ntfs_inode *dir, ntfs_inode *result, char *filename,
+  int namelen);
+int ntfs_update_inode(ntfs_inode *ino);
+int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, int offset,
+  ntfs_io *dest);
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
+int ntfs_decompress_run(unsigned char **data, int *length, int *cluster,
+  int *ctype);
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/macros.h linux/fs/ntfs/macros.h
--- v2.1.73/linux/fs/ntfs/macros.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/macros.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,44 @@
+/*
+ *  macros.h
+ *
+ *  Copyright (C) 1995 Martin von Löwis
+ *  Copyright (C) 1996 Régis Duchesne
+ */
+
+#define NTFS_FD(vol)		((vol)->u.fd)
+
+/* Linux */
+#ifdef NTFS_IN_LINUX_KERNEL
+#define NTFS_SB(vol)		((struct super_block*)(vol)->sb)
+#define NTFS_SB2VOL(sb)         (&(sb)->u.ntfs_sb)
+#define NTFS_INO2VOL(ino)	(&((ino)->i_sb->u.ntfs_sb))
+#define NTFS_LINO2NINO(ino)     (&((ino)->u.ntfs_i))
+#else
+#define NTFS_SB(vol)		((struct super_block*)(vol)->u.sb)
+#define NTFS_SB2VOL(sb)		((ntfs_volume*)(sb)->u.generic_sbp)
+#define NTFS_INO2VOL(ino)	((ntfs_volume*)((ino)->i_sb->u.generic_sbp))
+#define NTFS_LINO2NINO(ino)     ((ntfs_inode*)((ino)->u.generic_ip))
+#endif
+
+/* BSD */
+#define NTFS_MNT(vol)		((struct mount*)(vol)->u.sb)
+#define NTFS_MNT2VOL(sb)	((ntfs_volume*)(sb)->mnt_data)
+#define NTFS_V2INO(ino)		((ntfs_inode*)((ino)->v_data))
+
+/* Classical min and max macros still missing in standard headers... */
+#define min(a,b)	((a) <= (b) ? (a) : (b))
+#define max(a,b)	((a) >= (b) ? (a) : (b))
+
+#define IS_MAGIC(a,b)		(*(int*)(a)==*(int*)(b))
+#define IS_MFT_RECORD(a)	IS_MAGIC((a),"FILE")
+#define IS_NTFS_VOLUME(a)	IS_MAGIC((a)+3,"NTFS")
+#define IS_INDEX_RECORD(a)	IS_MAGIC((a),"INDX")
+
+/* 'NTFS' in little endian */
+#define NTFS_SUPER_MAGIC	0x5346544E
+
+/*
+ * Local variables:
+ *  c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/struct.h linux/fs/ntfs/struct.h
--- v2.1.73/linux/fs/ntfs/struct.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/struct.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,155 @@
+/*
+ * struct.h
+ * Structure definitions
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Necessary forward definition */
+struct ntfs_inode;
+
+#ifdef __FreeBSD__
+#include <sys/queue.h>
+/* Define the struct ntfs_head type */
+LIST_HEAD(ntfs_head,ntfs_inode);
+#endif
+
+/* which files should be returned from a director listing */
+/* only short names, no hidden files */
+#define ngt_dos   1
+/* only long names, all-uppercase becomes all-lowercase, no hidden files */
+#define ngt_nt    2
+/* all names except hidden files */
+#define ngt_posix 3
+/* all entries */
+#define ngt_full  4
+
+#ifdef NTFS_IN_LINUX_KERNEL
+typedef struct ntfs_sb_info ntfs_volume;
+#else
+typedef struct _ntfs_volume{
+	/* NTFS_SB_INFO_START */
+	/* Configuration provided by user at mount time */
+	ntfs_uid_t uid;
+	ntfs_gid_t gid;
+	ntmode_t umask;
+        unsigned int nct;
+	void *nls_map;
+	unsigned int ngt;
+	/* Configuration provided by user with ntfstools */
+	ntfs_size_t partition_bias;	/* for access to underlying device */
+	/* Attribute definitions */
+	ntfs_u32 at_standard_information;
+	ntfs_u32 at_attribute_list;
+	ntfs_u32 at_file_name;
+	ntfs_u32 at_security_descriptor;
+	ntfs_u32 at_data;
+	ntfs_u32 at_index_root;
+	ntfs_u32 at_index_allocation;
+	ntfs_u32 at_bitmap;
+	ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
+	/* Data read from the boot file */
+	int blocksize;
+	int clusterfactor;
+	int clustersize;
+	int mft_recordsize;
+	int mft_clusters_per_record;
+	int index_recordsize;
+	int index_clusters_per_record;
+	int mft_cluster;
+	/* data read from special files */
+	unsigned char *mft;
+	unsigned short *upcase;
+	unsigned int upcase_length;
+	/* inodes we always hold onto */
+	struct ntfs_inode *mft_ino;
+	struct ntfs_inode *mftmirr;
+	struct ntfs_inode *bitmap;
+	/* NTFS_SB_INFO_END */
+	union{
+		int fd;		/* file descriptor for the tools */
+		void *sb;	/* pointer to super block for the kernel */
+	}u;
+#ifdef __FreeBSD__
+	dev_t rdev;
+	struct vnode *devvp;
+	struct ntfs_head *inode_hash;   /* not really a hash */
+#endif
+}ntfs_volume;
+#endif
+
+typedef struct {
+	ntfs_cluster_t cluster;
+	ntfs_cluster_t len;
+}ntfs_runlist;
+
+typedef struct ntfs_attribute{
+	int type;
+	ntfs_u16 *name;
+	int namelen;
+	int attrno;
+	int size,allocated,initialized,compsize;
+	int compressed,resident,indexed;
+	int cengine;
+	union{
+		void *data;             /* if resident */
+		struct {
+			ntfs_runlist *runlist;
+			int len;
+		}r;
+	}d;
+}ntfs_attribute;
+
+/* Structure to define IO to user buffer. do_read means that
+   the destination has to be written using fn_put, do_write means
+   that the destination has to read using fn_get. So, do_read is
+   from a user's point of view, while put and get are from the driver's
+   point of view. The first argument is always the destination of the IO
+*/
+#ifdef NTFS_IN_LINUX_KERNEL
+typedef struct ntfs_inode_info ntfs_inode;
+#else
+typedef struct ntfs_inode{
+	ntfs_volume *vol;
+	/* NTFS_INODE_INFO_START */
+	int i_number;                /* should be really 48 bits */
+	unsigned sequence_number;
+	unsigned char* attr;         /* array of the attributes */
+	int attr_count;              /* size of attrs[] */
+	struct ntfs_attribute *attrs;
+	int record_count;            /* size of records[] */
+	/* array of the record numbers of the MFT 
+	   whose attributes have been inserted in the inode */
+	int *records;
+	union{
+		struct{
+			int recordsize;
+			int clusters_per_record;
+		}index;
+	} u;	
+	/* NTFS_INODE_INFO_END */
+#ifdef __FreeBSD__
+	struct vnode *vp;
+	LIST_ENTRY(ntfs_inode) h_next;
+#endif
+}ntfs_inode;
+#endif
+
+typedef struct ntfs_io{
+	int do_read;
+	void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
+	void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
+	void *param;
+	int size;
+}ntfs_io;
+
+#if 0
+typedef struct {
+	ntfs_volume *vol;
+	ntfs_inode *ino;
+	int type;
+	char *name;
+	int mftno;
+	int start_vcn;
+} ntfs_attrlist_item;
+#endif
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/super.c linux/fs/ntfs/super.c
--- v2.1.73/linux/fs/ntfs/super.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/super.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,547 @@
+/*
+ *  super.c
+ *
+ *  Copyright (C) 1995-1997 Martin von Löwis
+ *  Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "super.h"
+
+#include <errno.h>
+#include "macros.h"
+#include "inode.h"
+#include "support.h"
+#include "util.h"
+
+/*
+ * All important structures in NTFS use 2 consistency checks :
+ * . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
+ * . a fixup technique : the last word of each sector (called a fixup) of a
+ *   structure's record should end with the word at offset <n> of the first
+ *   sector, and if it is the case, must be replaced with the words following
+ *   <n>. The value of <n> and the number of fixups is taken from the fields
+ *   at the offsets 4 and 6.
+ *
+ * This function perform these 2 checks, and _fails_ if :
+ * . the magic identifier is wrong
+ * . the size is given and does not match the number of sectors
+ * . a fixup is invalid
+ */
+int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size)
+{
+	int start, count, offset;
+	short fixup;
+
+	if(!IS_MAGIC(record,magic))
+		return 0;
+	start=NTFS_GETU16(record+4);
+	count=NTFS_GETU16(record+6);
+	count--;
+	if(size && vol->blocksize*count != size)
+		return 0;
+	fixup = NTFS_GETU16(record+start);
+	start+=2;
+	offset=vol->blocksize-2;
+	while(count--){
+		if(NTFS_GETU16(record+offset)!=fixup)
+			return 0;
+		NTFS_PUTU16(record+offset, NTFS_GETU16(record+start));
+		start+=2;
+		offset+=vol->blocksize;
+	}
+	return 1;
+}
+
+/* Get vital informations about the ntfs partition from the boot sector */
+int ntfs_init_volume(ntfs_volume *vol,char *boot)
+{
+	/* Historical default values, in case we don't load $AttrDef */
+	vol->at_standard_information=0x10;
+	vol->at_attribute_list=0x20;
+	vol->at_file_name=0x30;
+	vol->at_security_descriptor=0x50;
+	vol->at_data=0x80;
+	vol->at_index_root=0x90;
+	vol->at_index_allocation=0xA0;
+	vol->at_bitmap=0xB0;
+	vol->at_symlink=0xC0;
+
+	/* Sector size */
+	vol->blocksize=NTFS_GETU16(boot+0xB);
+	vol->clusterfactor=NTFS_GETU8(boot+0xD);
+	vol->mft_clusters_per_record=NTFS_GETS8(boot+0x40);
+	vol->index_clusters_per_record=NTFS_GETS8(boot+0x44);
+	
+	/* Just some consistency checks */
+	if(NTFS_GETU32(boot+0x40)>256)
+		ntfs_error("Unexpected data #1 in boot block\n");
+	if(NTFS_GETU32(boot+0x44)>256)
+		ntfs_error("Unexpected data #2 in boot block\n");
+	if(vol->index_clusters_per_record<0){
+		ntfs_error("Unexpected data #3 in boot block\n");
+		/* If this really means a fraction, setting it to 1
+		   should be safe. */
+		vol->index_clusters_per_record=1;
+	}
+	/* in some cases, 0xF6 meant 1024 bytes. Other strange values have not
+	   been observed */
+	if(vol->mft_clusters_per_record<0 && vol->mft_clusters_per_record!=-10)
+		ntfs_error("Unexpected data #4 in boot block\n");
+
+	vol->clustersize=vol->blocksize*vol->clusterfactor;
+	if(vol->mft_clusters_per_record>0)
+		vol->mft_recordsize=
+			vol->clustersize*vol->mft_clusters_per_record;
+	else
+		vol->mft_recordsize=1<<(-vol->mft_clusters_per_record);
+	vol->index_recordsize=vol->clustersize*vol->index_clusters_per_record;
+	/* FIXME: long long value */
+	vol->mft_cluster=NTFS_GETU64(boot+0x30);
+
+	/* This will be initialized later */
+	vol->upcase=0;
+	vol->upcase_length=0;
+	vol->mft_ino=0;
+	return 0;
+}
+
+static void 
+ntfs_init_upcase(ntfs_inode *upcase)
+{
+	ntfs_io io;
+#define UPCASE_LENGTH  256
+	upcase->vol->upcase = ntfs_malloc(2*UPCASE_LENGTH);
+	upcase->vol->upcase_length = UPCASE_LENGTH;
+	io.fn_put=ntfs_put;
+	io.fn_get=0;
+	io.param=upcase->vol->upcase;
+	io.size=2*UPCASE_LENGTH;
+	ntfs_read_attr(upcase,upcase->vol->at_data,0,0,&io);
+}
+
+static int
+process_attrdef(ntfs_inode* attrdef,ntfs_u8* def)
+{
+	int type = NTFS_GETU32(def+0x80);
+	int check_type = 0;
+	ntfs_volume *vol=attrdef->vol;
+	ntfs_u16* name = (ntfs_u16*)def;
+
+	if(ntfs_ua_strncmp(name,"$STANDARD_INFORMATION",64)==0){
+		vol->at_standard_information=type;
+		check_type=0x10;
+	}else if(ntfs_ua_strncmp(name,"$ATTRIBUTE_LIST",64)==0){
+		vol->at_attribute_list=type;
+		check_type=0x20;
+	}else if(ntfs_ua_strncmp(name,"$FILE_NAME",64)==0){
+		vol->at_file_name=type;
+		check_type=0x30;
+	}else if(ntfs_ua_strncmp(name,"$SECURITY_DESCRIPTOR",64)==0){
+		vol->at_file_name=type;
+	}else if(ntfs_ua_strncmp(name,"$DATA",64)==0){
+		vol->at_data=type;
+		check_type=0x80;
+	}else if(ntfs_ua_strncmp(name,"$INDEX_ROOT",64)==0){
+		vol->at_index_root=type;
+		check_type=0x90;
+	}else if(ntfs_ua_strncmp(name,"$INDEX_ALLOCATION",64)==0){
+		vol->at_index_allocation=type;
+		check_type=0xA0;
+	}else if(ntfs_ua_strncmp(name,"$BITMAP",64)==0){
+		vol->at_bitmap=type;
+		check_type=0xB0;
+	}else if(ntfs_ua_strncmp(name,"$SYMBOLIC_LINK",64) ||
+		 ntfs_ua_strncmp(name,"$REPARSE_POINT",64)){
+		vol->at_symlink=type;
+	}
+	if(check_type && check_type!=type){
+		ntfs_error("Unexpected type %x for %x\n",type,check_type);
+		return EINVAL;
+	}
+	return 0;
+}
+
+int
+ntfs_init_attrdef(ntfs_inode* attrdef)
+{
+	ntfs_u8 *buf;
+	ntfs_io io;
+	int offset,error,i;
+	ntfs_attribute *data;
+	buf=ntfs_malloc(4096);
+	if(!buf)return ENOMEM;
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.do_read=1;
+	offset=0;
+	data=ntfs_find_attr(attrdef,attrdef->vol->at_data,0);
+	if(!data){
+		ntfs_free(buf);
+		return EINVAL;
+	}
+	do{
+		io.param=buf;
+		io.size=4096;
+		error=ntfs_readwrite_attr(attrdef,data,offset,&io);
+		for(i=0;!error && i<io.size;i+=0xA0)
+			error=process_attrdef(attrdef,buf+i);
+		offset+=4096;
+	}while(!error && io.size);
+	ntfs_free(buf);
+	return error;
+}
+
+int ntfs_load_special_files(ntfs_volume *vol)
+{
+	int error;
+	ntfs_inode upcase,attrdef;
+
+	vol->mft_ino=(ntfs_inode*)ntfs_calloc(3*sizeof(ntfs_inode));
+	error=ENOMEM;
+	if(!vol->mft_ino || (error=ntfs_init_inode(vol->mft_ino,vol,FILE_MFT)))
+	{
+		ntfs_error("Problem loading MFT\n");
+		return error;
+	}
+	vol->mftmirr=vol->mft_ino+1;
+	if((error=ntfs_init_inode(vol->mftmirr,vol,FILE_MFTMIRR))){
+		ntfs_error("Problem %d loading MFTMirr\n",error);
+		return error;
+	}
+	vol->bitmap=vol->mft_ino+2;
+	if((error=ntfs_init_inode(vol->bitmap,vol,FILE_BITMAP))){
+		ntfs_error("Problem loading Bitmap\n");
+		return error;
+	}
+	error=ntfs_init_inode(&upcase,vol,FILE_UPCASE);
+	if(error)return error;
+	ntfs_init_upcase(&upcase);
+	ntfs_clear_inode(&upcase);
+	error=ntfs_init_inode(&attrdef,vol,FILE_ATTRDEF);
+	if(error)return error;
+	error=ntfs_init_attrdef(&attrdef);
+	ntfs_clear_inode(&attrdef);
+	if(error)return error;
+	return 0;
+}
+
+int ntfs_release_volume(ntfs_volume *vol)
+{
+	if(vol->mft_ino){
+		ntfs_clear_inode(vol->mft_ino);
+		ntfs_clear_inode(vol->mftmirr);
+		ntfs_clear_inode(vol->bitmap);
+		ntfs_free(vol->mft_ino);
+		vol->mft_ino=0;
+	}
+	ntfs_free(vol->mft);
+	ntfs_free(vol->upcase);
+	return 0;
+}
+
+int ntfs_get_volumesize(ntfs_volume *vol)
+{
+	ntfs_io io;
+	char *cluster0=ntfs_malloc(vol->clustersize);
+	int size;
+
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=cluster0;
+	io.do_read=1;
+	io.size=vol->clustersize;
+	ntfs_getput_clusters(vol,0,0,&io);
+	size=NTFS_GETU64(cluster0+0x28);
+	ntfs_free(cluster0);
+	size/=vol->clusterfactor;
+	return size;
+}
+
+static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
+
+int 
+ntfs_get_free_cluster_count(ntfs_inode *bitmap)
+{
+	unsigned char bits[2048];
+	int offset,error;
+	int clusters=0;
+	ntfs_io io;
+
+	offset=0;
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	while(1)
+	{
+		register int i;
+		io.param=bits;
+		io.size=2048;
+		error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,
+				     offset,&io);
+		if(error || io.size==0)break;
+		/* I never thought I would do loop unrolling some day */
+		for(i=0;i<io.size-8;){
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+		}
+		for(;i<io.size;){
+			clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+		}
+		offset+=io.size;
+	}
+	return clusters;
+}
+
+/* Insert the fixups for the record. The number and location of the fixes 
+   is obtained from the record header */
+void ntfs_insert_fixups(unsigned char *rec, int secsize)
+{
+	int first=NTFS_GETU16(rec+4);
+	int count=NTFS_GETU16(rec+6);
+	int offset=-2;
+	ntfs_u16 fix=NTFS_GETU16(rec+first);
+	fix=fix+1;
+	NTFS_PUTU16(rec+first,fix);
+	count--;
+	while(count--){
+		first+=2;
+		offset+=secsize;
+		NTFS_PUTU16(rec+first,NTFS_GETU16(rec+offset));
+		NTFS_PUTU16(rec+offset,fix);
+	};
+}
+
+/* search the bitmap bits of l bytes for *cnt zero bits. Return the bit
+   number in *loc, which is initially set to the number of the first bit.
+   Return the largest block found in *cnt. Return 0 on success, ENOSPC if
+   all bits are used */
+static int 
+search_bits(unsigned char* bits,int *loc,int *cnt,int l)
+{
+	unsigned char c=0;
+	int bc=0;
+	int bstart=0,bstop=0,found=0;
+	int start,stop=0,in=0;
+	/* special case searching for a single block */
+	if(*cnt==1){
+		while(l && *cnt==0xFF){
+			bits++;
+			*loc+=8;
+			l--;
+		}
+		if(!l)return ENOSPC;
+		for(c=*bits;c & 1;c>>=1)
+			(*loc)++;
+		return 0;
+	}
+	start=*loc;
+	while(l || bc){
+		if(bc==0){
+			c=*bits;
+			if(l){
+				l--;bits++;
+			}
+			bc=8;
+		}
+		if(in){
+			if((c&1)==0)
+				stop++;
+			else{ /* end of sequence of zeroes */
+				in=0;
+				if(!found || bstop-bstart<stop-start){
+					bstop=stop;bstart=start;found=1;
+					if(bstop-bstart>*cnt)
+						break;
+				}
+				start=stop+1;
+			}
+		}else{
+			if(c&1)
+				start++;
+			else{ /*start of sequence*/
+				in=1;
+				stop=start+1;
+			}
+		}
+		bc--;
+		c>>=1;
+	}
+	if(in && (!found || bstop-bstart<stop-start)){
+		bstop=stop;bstart=start;found=1;
+	}
+	if(!found)return ENOSPC;
+	*loc=bstart;
+	if(*cnt>bstop-bstart)
+		*cnt=bstop-bstart;
+	return 0;
+}
+
+int 
+ntfs_set_bitrange(ntfs_inode* bitmap,int loc,int cnt,int bit)
+{
+	int bsize,locit,error;
+	unsigned char *bits,*it;
+	ntfs_io io;
+
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	bsize=(cnt+loc%8+7)/8; /* round up */
+	bits=ntfs_malloc(bsize);
+	io.param=bits;
+	io.size=bsize;
+	if(!bits)
+		return ENOMEM;
+	error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+	if(error || io.size!=bsize){
+		ntfs_free(bits);
+		return error?error:EIO;
+	}
+	/* now set the bits */
+	it=bits;
+	locit=loc;
+	while(locit%8 && cnt){ /* process first byte */
+		if(bit)
+			*it |= 1<<(locit%8);
+		else
+			*it &= ~(1<<(locit%8));
+		cnt--;locit++;
+		if(locit%8==7)
+			it++;
+	}
+	while(cnt>8){ /*process full bytes */
+		*it= bit ? 0xFF : 0;
+		cnt-=8;
+		locit+=8;
+		it++;
+	}
+	while(cnt){ /*process last byte */
+		if(bit)
+			*it |= 1<<(locit%8);
+		else
+			*it &= ~(1<<(locit%8));
+		cnt--;locit++;
+	}
+	/* reset to start */
+	io.param=bits;
+	io.size=bsize;
+	error=ntfs_write_attr(bitmap,bitmap->vol->at_data,0,loc/8,&io);
+	ntfs_free(bits);
+	if(error)return error;
+	if(io.size!=bsize)
+		return EIO;
+	return 0;
+}
+  
+  
+	
+/* allocate count clusters around location. If location is -1,
+   it does not matter where the clusters are. Result is 0 if
+   success, in which case location and count says what they really got */
+int 
+ntfs_search_bits(ntfs_inode* bitmap, int *location, int *count, int flags)
+{
+	unsigned char *bits;
+	ntfs_io io;
+	int error=0,found=0;
+	int loc,cnt,bloc=-1,bcnt=0;
+	int start;
+
+	bits=ntfs_malloc(2048);
+	io.fn_put=ntfs_put;
+	io.fn_get=ntfs_get;
+	io.param=bits;
+
+	/* first search within +/- 8192 clusters */
+	start=*location/8;
+	start= start>1024 ? start-1024 : 0;
+	io.size=2048;
+	error=ntfs_read_attr(bitmap,bitmap->vol->at_data,0,start,&io);
+	if(error)goto fail;
+	loc=start*8;
+	cnt=*count;
+	error=search_bits(bits,&loc,&cnt,io.size);
+	if(error)
+		goto fail;
+	if(*count==cnt){
+		bloc=loc;
+		bcnt=cnt;
+		goto success;
+	}
+
+	/* now search from the beginning */
+	for(start=0;1;start+=2048)
+	{
+		io.param=bits;
+		io.size=2048;
+		error=ntfs_read_attr(bitmap,bitmap->vol->at_data,
+				     0,start,&io);
+		if(error)goto fail;
+		if(io.size==0)
+			if(found)
+				goto success;
+			else{
+				error=ENOSPC;
+				goto fail;
+			}
+		loc=start*8;
+		cnt=*count;
+		error=search_bits(bits,&loc,&cnt,io.size);
+		if(error)
+			goto fail;
+		if(*count==cnt)
+			goto success;
+		if(bcnt<cnt){
+			bcnt=cnt;
+			bloc=loc;
+			found=1;
+		}
+	}
+ success:
+	ntfs_free(bits);
+	/* check flags */
+	if((flags & ALLOC_REQUIRE_LOCATION) && *location!=bloc)
+		error=ENOSPC;
+	else if((flags & ALLOC_REQUIRE_SIZE) && *count!=bcnt)
+		error=ENOSPC;
+	else ntfs_set_bitrange(bitmap,bloc,bcnt,1);
+	/* If allocation failed due to the flags, tell the caller what he
+	   could have gotten */
+	*location=bloc;
+	*count=bcnt;
+	return 0;
+ fail:
+	*location=-1;
+	*count=0;
+	ntfs_free(bits);
+	return error;
+}
+
+int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+	int flags)
+{
+	int error;
+	error=ntfs_search_bits(vol->bitmap,location,count,flags);
+	return error;
+}
+
+int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count)
+{
+	int error;
+	error=ntfs_set_bitrange(vol->bitmap,location,count,0);
+	return error;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
+
+
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/super.h linux/fs/ntfs/super.h
--- v2.1.73/linux/fs/ntfs/super.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/super.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,21 @@
+/*
+ *  super.h
+ *  Header file for super.c
+ *
+ *  Copyright (C) 1995-1997 Martin von Löwis
+ *  Copyright (C) 1996-1997 Régis Duchesne
+ */
+
+#define ALLOC_REQUIRE_LOCATION 1
+#define ALLOC_REQUIRE_SIZE     2
+
+int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
+int ntfs_get_volumesize(ntfs_volume *vol);
+int ntfs_init_volume(ntfs_volume *vol,char *boot);
+int ntfs_load_special_files(ntfs_volume *vol);
+int ntfs_release_volume(ntfs_volume *vol);
+void ntfs_insert_fixups(unsigned char *rec, int secsize);
+int ntfs_fixup_record(ntfs_volume *vol, char *record, char *magic, int size);
+int ntfs_allocate_clusters(ntfs_volume *vol, int *location, int *count,
+  int flags);
+int ntfs_deallocate_clusters(ntfs_volume *vol, int location, int count);
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/support.c linux/fs/ntfs/support.c
--- v2.1.73/linux/fs/ntfs/support.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/support.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,313 @@
+/*
+ *  support.c
+ *  Specific support functions
+ *
+ *  Copyright (C) 1997 Martin von Löwis
+ *  Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#ifdef NTFS_IN_LINUX_KERNEL
+#include <linux/config.h>
+#endif
+#include "types.h"
+#include "struct.h"
+#include "support.h"
+
+#include <stdarg.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/nls.h>
+#include "util.h"
+#include "inode.h"
+#include "macros.h"
+
+static char print_buf[1024];
+
+#ifdef DEBUG
+#include "sysctl.h"
+#include <linux/kernel.h>
+
+/* Debugging output */
+void ntfs_debug(int mask, const char *fmt, ...)
+{
+	va_list ap;
+
+	/* Filter it with the debugging level required */
+	if(ntdebug & mask){
+		va_start(ap,fmt);
+		strcpy(print_buf, KERN_DEBUG);
+		vsprintf(print_buf + 3, fmt, ap);
+		printk(print_buf);
+		va_end(ap);
+	}
+}
+
+/* Verbose kmalloc */
+void *ntfs_malloc(int size)
+{
+	void *ret;
+
+	ret = kmalloc(size, GFP_KERNEL);
+	ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
+
+	return ret;
+}
+
+/* Verbose kfree() */
+void ntfs_free(void *block)
+{
+        ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
+	kfree(block);
+}
+#else
+void ntfs_debug(int mask, const char *fmt, ...)
+{
+}
+
+void *ntfs_malloc(int size)
+{
+	return kmalloc(size, GFP_KERNEL);
+}
+
+void ntfs_free(void *block)
+{
+	kfree(block);
+}
+#endif /* DEBUG */
+
+void ntfs_bzero(void *s, int n)
+{
+	memset(s, 0, n);
+}
+
+void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
+{
+	return memcpy(dest, src, n);
+}
+
+void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
+{
+	return memmove(dest, src, n);
+}
+
+/* Warn that an error occured */
+void ntfs_error(const char *fmt,...)
+{
+        va_list ap;
+
+        va_start(ap, fmt);
+        strcpy(print_buf, KERN_ERR);
+        vsprintf(print_buf + 3, fmt, ap);
+        printk(print_buf);
+        va_end(ap);
+}
+
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
+{
+	int error;
+	ntfs_io io;
+
+	ntfs_debug(DEBUG_OTHER, "read_mft_record %x\n",mftno);
+	if(mftno==FILE_MFT)
+	{
+		ntfs_memcpy(buf,vol->mft,vol->mft_recordsize);
+		return 0;
+	}
+	if(!vol->mft_ino)
+	{
+		printk("ntfs:something is terribly wrong here\n");
+		return ENODATA;
+	}
+ 	io.fn_put=ntfs_put;
+	io.fn_get=0;
+	io.param=buf;
+	io.size=vol->mft_recordsize;
+	error=ntfs_read_attr(vol->mft_ino,vol->at_data,NULL,
+			     mftno*vol->mft_recordsize,&io);
+	if(error || (io.size!=vol->mft_recordsize))
+	{
+		ntfs_debug(DEBUG_OTHER, "read_mft_record: read %x failed (%d,%d,%d)\n",
+			   mftno,error,io.size,vol->mft_recordsize);
+		return error?error:ENODATA;
+	}
+	ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read %x\n",mftno);
+	if(!ntfs_check_mft_record(vol,buf))
+	{
+		printk("Invalid MFT record for %x\n",mftno);
+		return EINVAL;
+	}
+	ntfs_debug(DEBUG_OTHER, "read_mft_record: Done %x\n",mftno);
+	return 0;
+}
+
+int ntfs_getput_clusters(ntfs_volume *vol, int cluster,	ntfs_size_t start_offs,
+	 ntfs_io *buf)
+{
+	struct super_block *sb=NTFS_SB(vol);
+	struct buffer_head *bh;
+	ntfs_size_t to_copy;
+	int length=buf->size;
+	if(buf->do_read)
+		ntfs_debug(DEBUG_OTHER, "get_clusters %d %d %d\n",cluster,start_offs,length);
+	else
+		ntfs_debug(DEBUG_OTHER, "put_clusters %d %d %d\n",cluster,start_offs,length);
+	while(length)
+	{
+		if(!(bh=bread(sb->s_dev,cluster,vol->clustersize)))
+		{
+			ntfs_debug(DEBUG_OTHER, "%s failed\n", buf->do_read?"Reading":"Writing");
+			return EIO;
+		}
+		to_copy=min(vol->clustersize-start_offs,length);
+		lock_buffer(bh);
+		if(buf->do_read)
+			buf->fn_put(buf,bh->b_data+start_offs,to_copy);
+		else
+		{
+			buf->fn_get(bh->b_data+start_offs,buf,to_copy);
+			mark_buffer_dirty(bh,1);
+		}
+		unlock_buffer(bh);
+		length-=to_copy;
+		start_offs=0;
+		cluster++;
+		brelse(bh);
+	}
+	return 0;
+}
+
+ntfs_time64_t ntfs_now(void)
+{
+	return ntfs_unixutc2ntutc(CURRENT_TIME);
+}
+
+/* when printing unicode characters base64, use this table.
+   It is not strictly base64, but the Linux vfat encoding.
+   base64 has the disadvantage of using the slash.
+*/
+static char uni2esc[64]=
+  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
+
+static unsigned char
+esc2uni(char c)
+{
+	if(c<'0')return 255;
+	if(c<='9')return c-'0';
+	if(c<'A')return 255;
+	if(c<='Z')return c-'A'+10;
+	if(c<'a')return 255;
+	if(c<='z')return c-'a'+36;
+	if(c=='+')return 62;
+	if(c=='-')return 63;
+	return 255;
+}
+
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+  int *out_len)
+{
+	int i,o,val;
+	char *result,*buf;
+	struct nls_table* nls=vol->nls_map;
+
+	result=ntfs_malloc(in_len+1);
+	if(!result)return ENOMEM;
+	*out_len=in_len;
+	result[in_len]='\0';
+	for(i=o=0;i<in_len;i++){
+		int cl,ch;
+		unsigned char* uni_page;
+		/* FIXME: byte order */
+		cl=in[i] & 0xFF;
+		ch=(in[i] >> 8) & 0xFF;
+		if(!nls){
+			if(!ch){
+				result[o++]=cl;
+				continue;
+			}
+		}else{
+			uni_page=nls->page_uni2charset[ch];
+			if(uni_page && uni_page[cl]){
+				result[o++]=uni_page[cl];
+				continue;
+			}
+		}
+		if(!(vol->nct & nct_uni_xlate))goto inval;
+		/* realloc */
+		buf=ntfs_malloc(*out_len+3);
+		memcpy(buf,result,o);
+		ntfs_free(result);
+		result=buf;
+		*out_len+=3;
+		result[o++]=':';
+		if(vol->nct & nct_uni_xlate_vfat){
+			val=(cl<<8)+ch;
+			result[o+2]=uni2esc[val & 0x3f];
+			val>>=6;
+			result[o+1]=uni2esc[val & 0x3f];
+			val>>=6;
+			result[o]=uni2esc[val & 0x3f];
+			o+=3;
+		}else{
+			val=(ch<<8)+cl;
+			result[o++]=uni2esc[val & 0x3f];
+			val>>=6;
+			result[o++]=uni2esc[val & 0x3f];
+			val>>=6;
+			result[o++]=uni2esc[val & 0x3f];
+		}
+	}
+	*out=result;
+	return 0;
+ inval:
+	ntfs_free(result);
+	*out=0;
+	return EILSEQ;
+}
+
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+  int *out_len)
+{
+	int i,o;
+	ntfs_u16* result;
+	struct nls_table* nls=vol->nls_map;
+
+	*out=result=ntfs_malloc(2*in_len);
+	if(!result)return ENOMEM;
+	*out_len=in_len;
+	for(i=o=0;i<in_len;i++,o++){
+		unsigned short cl,ch;
+		if(in[i]!=':' || (vol->nct & nct_uni_xlate)==0){
+			cl=nls->charset2uni[(unsigned char)in[i]].uni1;
+			ch=nls->charset2uni[(unsigned char)in[i]].uni2;
+		}else{
+			unsigned char c1,c2,c3;
+			*out_len-=3;
+			c1=esc2uni(in[++i]);
+			c2=esc2uni(in[++i]);
+			c3=esc2uni(in[++i]);
+			if(c1==255 || c2==255 || c3==255)
+				cl=ch=0;
+			else if(vol->nct & nct_uni_xlate_vfat){
+				cl = (c1 << 4) + (c2 >> 2);
+				ch = ((c2 & 0x3) << 6) + c3;
+			}else{
+				ch=(c3 << 4) + (c2 >> 2);
+				cl=((c2 & 0x3) << 6) + c1;
+			}
+		}			
+		/* FIXME: byte order */
+		result[o] = (ch<<8) | cl;
+		if(!result[o]){
+			ntfs_free(result);
+			return EILSEQ;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/support.h linux/fs/ntfs/support.h
--- v2.1.73/linux/fs/ntfs/support.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/support.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,32 @@
+/*
+ *  support.h
+ *  Header file for specific support.c
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Debug levels */
+#define DEBUG_OTHER	1
+#define DEBUG_MALLOC	2
+
+void ntfs_debug(int mask, const char *fmt, ...);
+void *ntfs_malloc(int size);
+void ntfs_free(void *block);
+void ntfs_bzero(void *s, int n);
+void *ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
+void *ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
+void ntfs_error(const char *fmt,...);
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
+int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
+        ntfs_io *buf);
+ntfs_time64_t ntfs_now(void);
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+        int *out_len);
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+        int *out_len);
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/sysctl.c linux/fs/ntfs/sysctl.c
--- v2.1.73/linux/fs/ntfs/sysctl.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/sysctl.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,62 @@
+/*
+ *  sysctl.c
+ *  System control stuff
+ *
+ *  Copyright (C) 1997 Martin von Löwis
+ *  Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#include "sysctl.h"
+
+#ifdef DEBUG
+#include <linux/locks.h>
+#include <linux/sysctl.h>
+
+int ntdebug = 0;
+
+/* Add or remove the debug sysctl
+ * Is this really the only file system with sysctls ?
+ */
+void ntfs_sysctl(int add)
+{
+#define FS_NTFS             1
+	/* Definition of the sysctl */
+	static ctl_table ntfs_sysctls[]={
+		{FS_NTFS,                  /* ID */
+		 "ntfs-debug",             /* name in /proc */
+		 &ntdebug,sizeof(ntdebug), /* data ptr, data size */
+		 0644,                     /* mode */
+		 0,                        /* child */
+		 proc_dointvec,            /* proc handler */
+		 0,                        /* strategy */
+		 0,                        /* proc control block */
+		 0,0},                     /* extra */
+		{0}
+	};
+	/* Define the parent file : /proc/sys/fs */
+	static ctl_table sysctls_root[]={
+		{CTL_FS,
+		 "fs",
+		 NULL,0,
+		 0555,
+		 ntfs_sysctls},
+		{0}
+	};
+	static struct ctl_table_header *sysctls_root_header = NULL;
+
+	if(add){
+		if(!sysctls_root_header)
+			sysctls_root_header = register_sysctl_table(sysctls_root, 0);
+	} else if(sysctls_root_header) {
+		unregister_sysctl_table(sysctls_root_header);
+		sysctls_root_header = NULL;
+	}
+}
+#endif /* DEBUG */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/sysctl.h linux/fs/ntfs/sysctl.h
--- v2.1.73/linux/fs/ntfs/sysctl.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/sysctl.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,24 @@
+/*
+ *  sysctl.h
+ *  Header file for sysctl.c
+ *
+ *  Copyright (C) 1997 Martin von Löwis
+ *  Copyright (C) 1997 Régis Duchesne
+ *
+ */
+
+#ifdef DEBUG
+extern int ntdebug;
+
+void ntfs_sysctl(int add);
+
+#define SYSCTL(x)	ntfs_sysctl(x)
+#else
+#define SYSCTL(x)
+#endif /* DEBUG */
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/types.h linux/fs/ntfs/types.h
--- v2.1.73/linux/fs/ntfs/types.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/types.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,128 @@
+/*
+ *  types.h
+ *  This file defines four things:
+ *   - generic platform independent fixed-size types (e.g. ntfs_u32)
+ *   - specific fixed-size types (e.g. ntfs_offset_t)
+ *   - macros that read and write those types from and to byte arrays
+ *   - types derived from OS specific ones
+ *
+ *  Copyright (C) 1996 Martin von Löwis
+ */
+
+#ifdef NTFS_IN_LINUX_KERNEL
+/* get installed types if we compile the kernel*/
+#include <linux/fs.h>
+#endif
+
+#if defined(i386) || defined(__i386__)
+
+/* unsigned integral types */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef unsigned char      ntfs_u8;
+typedef unsigned short     ntfs_u16;
+typedef unsigned int       ntfs_u32;
+typedef unsigned long long ntfs_u64;
+#endif
+
+/* unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef unsigned short     ntfs_wchar_t;
+#endif
+/* file offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef unsigned long long ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef unsigned long long ntfs_time64_t;
+#endif
+/* This is really unsigned long long. So we support only volumes up to 2 TB */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef unsigned int ntfs_cluster_t;
+#endif
+
+/* Macros reading unsigned integers from a byte pointer */
+/* these should work for all little endian machines */
+#define NTFS_GETU8(p)      (*(ntfs_u8*)(p))
+#define NTFS_GETU16(p)     (*(ntfs_u16*)(p))
+#define NTFS_GETU24(p)     (NTFS_GETU32(p) & 0xFFFFFF)
+#define NTFS_GETU32(p)     (*(ntfs_u32*)(p))
+#define NTFS_GETU64(p)     (*(ntfs_u64*)(p))
+
+/* Macros writing unsigned integers */
+#define NTFS_PUTU8(p,v)      (*(ntfs_u8*)(p))=(v)
+#define NTFS_PUTU16(p,v)     (*(ntfs_u16*)(p))=(v)
+#define NTFS_PUTU24(p,v)     NTFS_PUTU16(p,(v) & 0xFFFF);\
+                             NTFS_PUTU8(((char*)p)+2,(v)>>16)
+#define NTFS_PUTU32(p,v)     (*(ntfs_u32*)(p))=(v)
+#define NTFS_PUTU64(p,v)     (*(ntfs_u64*)(p))=(v)
+
+/* Macros reading signed integers, returning int */
+#define NTFS_GETS8(p)        ((int)(*(char*)(p)))
+#define NTFS_GETS16(p)       ((int)(*(short*)(p)))
+#define NTFS_GETS24(p)       (NTFS_GETU24(p) < 0x800000 ? (int)NTFS_GETU24(p) : (int)(NTFS_GETU24(p) | 0xFF000000))
+
+#define NTFS_PUTS8(p,v)      NTFS_PUTU8(p,v)
+#define NTFS_PUTS16(p,v)     NTFS_PUTU16(p,v)
+#define NTFS_PUTS24(p,v)     NTFS_PUTU24(p,v)
+#define NTFS_PUTS32(p,v)     NTFS_PUTU32(p,v)
+
+#else
+#error Put your machine description here
+#endif
+
+/* architecture independent macros */
+
+/* PUTU32 would not clear all bytes */
+#define NTFS_PUTINUM(p,i)    NTFS_PUTU64(p,i->i_number);\
+                             NTFS_PUTU16(((char*)p)+6,i->sequence_number)
+
+/* system dependent types */
+#ifdef __linux__
+/* We always need kernel types, because glibc makes them of different size */
+#include <asm/posix_types.h>
+/* Avoid a type redefinition with future include of glibc <stdlib.h> */
+#undef __FD_ZERO
+#undef __FD_SET
+#undef __FD_CLR
+#undef __FD_ISSET
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef __kernel_uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef __kernel_gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+typedef mode_t ntmode_t;
+typedef uid_t ntfs_uid_t;
+typedef gid_t ntfs_gid_t;
+typedef size_t ntfs_size_t;
+typedef time_t ntfs_time_t;
+#endif
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/util.c linux/fs/ntfs/util.c
--- v2.1.73/linux/fs/ntfs/util.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/util.c	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,343 @@
+/*
+ *  util.c
+ *  Miscellaneous support
+ *
+ *  Copyright (C) 1997 Martin von Löwis
+ *  Copyright (C) 1997 Régis Duchesne
+ *
+ *  The utf8 routines are copied from Python wstrop module,
+ */
+
+#include "types.h"
+#include "struct.h"
+#include "util.h"
+
+#include <errno.h>
+/* FreeBSD doesn't seem to have EILSEQ in errno.h */
+#ifndef EILSEQ
+# define EILSEQ	EINVAL
+#endif
+#include "support.h"
+
+/* Converts a single wide character to a sequence of utf8 bytes.
+ * Returns the number of bytes, or 0 on error.
+ */
+static int
+to_utf8(ntfs_u16 c,unsigned char* buf)
+{
+	if(c==0)
+		return 0; /* No support for embedded 0 runes */
+	if(c<0x80){
+		if(buf)buf[0]=c;
+		return 1;
+	}
+	if(c<0x800){
+		if(buf){
+			buf[0] = 0xc0 | (c>>6);
+			buf[1] = 0x80 | (c & 0x3f);
+		}
+		return 2;
+	}
+	if(c<0x10000){
+		if(buf){
+			buf[0] = 0xe0 | (c>>12);
+			buf[1] = 0x80 | ((c>>6) & 0x3f);
+			buf[2] = 0x80 | (c & 0x3f);
+		}
+		return 3;
+	}
+	/* We don't support characters above 0xFFFF in NTFS */
+	return 0;
+}
+
+/* Decodes a sequence of utf8 bytes into a single wide character.
+ * Returns the number of bytes consumed, or 0 on error
+ */
+static int
+from_utf8(const unsigned char* str,ntfs_u16 *c)
+{
+	int l=0,i;
+
+	if(*str<0x80){
+		*c = *str;
+		return 1;
+	}
+	if(*str<0xc0) /* lead byte must not be 10xxxxxx */
+		return 0;   /* is c0 a possible lead byte? */
+	if(*str<0xe0){         /* 110xxxxx */
+		*c = *str & 0x1f;
+		l=2;
+	}else if(*str<0xf0){   /* 1110xxxx */
+		*c = *str & 0xf;
+		l=3;
+	}else if(*str<0xf8){   /* 11110xxx */
+		*c = *str & 7;
+		l=4;
+	}else /* We don't support characters above 0xFFFF in NTFS */
+		return 0;
+	 
+
+	for(i=1;i<l;i++){
+		/* all other bytes must be 10xxxxxx */
+		if((str[i] & 0xc0) != 0x80)
+			return 0;
+		*c <<= 6;
+		*c |= str[i] & 0x3f;
+	}
+	return l;
+}
+
+/* Converts wide string to UTF-8. Expects two in- and two out-parameters.
+ * Returns 0 on success, or error code. 
+ * The caller has to free the result string.
+ * There is no support for UTF-16, yet
+ */
+static int ntfs_dupuni2utf8(ntfs_u16* in, int in_len,char **out,int *out_len)
+{
+	int i,tmp;
+	int len8;
+	unsigned char *result;
+
+	ntfs_debug(DEBUG_OTHER,"converting l=%d\n",in_len);
+	/* count the length of the resulting UTF-8 */
+	for(i=len8=0;i<in_len;i++){
+		tmp=to_utf8(in[i],0);
+		if(!tmp)
+			/* invalid character */
+			return EILSEQ;
+		len8+=tmp;
+	}
+	*out=result=ntfs_malloc(len8+1); /* allow for zero-termination */
+
+	if(!result)
+		return ENOMEM;
+	result[len8]='\0';
+	*out_len=len8;
+	for(i=len8=0;i<in_len;i++)
+		len8+=to_utf8(in[i],result+len8);
+	return 0;
+}
+
+/* Converts an UTF-8 sequence to a wide string. Same conventions as the
+ * previous function
+ */
+static int ntfs_duputf82uni(unsigned char* in, int in_len,ntfs_u16** out,int *out_len)
+{
+	int i,tmp;
+	int len16;
+
+	ntfs_u16* result;
+	ntfs_u16 wtmp;
+	for(i=len16=0;i<in_len;i+=tmp,len16++){
+		tmp=from_utf8(in+i,&wtmp);
+		if(!tmp)
+			return EILSEQ;
+	}
+	*out=result=ntfs_malloc(2*(len16+1));
+	if(!result)
+		return ENOMEM;
+	result[len16]=0;
+	*out_len=len16;
+	for(i=len16=0;i<in_len;i+=tmp,len16++)
+		tmp=from_utf8(in+i,result+len16);
+	return 0;
+}
+
+/* See above. Produces ISO-8859-1 from wide strings */
+static int ntfs_dupuni288591(ntfs_u16* in,int in_len,char** out,int *out_len)
+{
+	int i;
+	char *result;
+
+	/* check for characters out of range */
+	for(i=0;i<in_len;i++)
+		if(in[i]>=256)
+			return EILSEQ;
+	*out=result=ntfs_malloc(in_len+1);
+	if(!result)
+		return ENOMEM;
+	result[in_len]='\0';
+	*out_len=in_len;
+	for(i=0;i<in_len;i++)
+		result[i]=in[i];
+	return 0;
+}
+
+/* See above */
+static int ntfs_dup885912uni(unsigned char* in,int in_len,ntfs_u16 **out,int *out_len)
+{
+	int i;
+
+	ntfs_u16* result;
+	*out=result=ntfs_malloc(2*in_len);
+	if(!result)
+		return ENOMEM;
+	*out_len=in_len;
+	for(i=0;i<in_len;i++)
+		result[i]=in[i];
+	return 0;
+}
+
+/* Encodings dispatcher */
+int ntfs_encodeuni(ntfs_volume *vol,ntfs_u16 *in, int in_len,
+		   char **out, int *out_len)
+{
+	if(vol->nct & nct_utf8)
+		return ntfs_dupuni2utf8(in,in_len,out,out_len);
+	else if(vol->nct & nct_iso8859_1)
+		return ntfs_dupuni288591(in,in_len,out,out_len);
+	else if(vol->nct & (nct_map|nct_uni_xlate))
+		/* uni_xlate is handled inside map */
+		return ntfs_dupuni2map(vol,in,in_len,out,out_len);
+	else
+		return EINVAL; /* unknown encoding */
+}
+
+int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len,
+		   ntfs_u16 **out, int *out_len)
+{
+	if(vol->nct & nct_utf8)
+		return ntfs_duputf82uni(in,in_len,out,out_len);
+	else if(vol->nct & nct_iso8859_1)
+		return ntfs_dup885912uni(in,in_len,out,out_len);
+	else if(vol->nct & (nct_map | nct_uni_xlate))
+		return ntfs_dupmap2uni(vol,in,in_len,out,out_len);
+	else
+		return EINVAL;
+}
+
+/* Same address space copies */
+void ntfs_put(ntfs_io *dest,void *src,ntfs_size_t n)
+{
+	ntfs_memcpy(dest->param,src,n);
+	dest->param+=n;
+}
+
+void ntfs_get(void* dest,ntfs_io *src,ntfs_size_t n)
+{
+	ntfs_memcpy(dest,src->param,n);
+	src->param+=n;
+}
+
+void *ntfs_calloc(int size)
+{
+	void *result=ntfs_malloc(size);
+
+	if(result)
+		ntfs_bzero(result,size);
+	return result;
+}
+
+#if 0
+/* copy len unicode characters from from to to :) */
+void ntfs_uni2ascii(char *to,char *from,int len)
+{
+	int i;
+
+	for(i=0;i<len;i++)
+		to[i]=from[2*i];
+	to[i]='\0';
+}
+#endif
+
+/* copy len asci characters from from to to :) */
+void ntfs_ascii2uni(short int *to,char *from,int len)
+{
+	int i;
+
+	for(i=0;i<len;i++)
+		to[i]=from[i];
+	to[i]=0;
+}
+
+/* strncmp for Unicode strings */
+int ntfs_uni_strncmp(short int* a,short int *b,int n)
+{
+	int i;
+
+	for(i=0;i<n;i++)
+	{
+		if(a[i]<b[i])
+			return -1;
+		if(b[i]<a[i])
+			return 1;
+	}
+	return 0;
+}
+
+/* strncmp between Unicode and ASCII strings */
+int ntfs_ua_strncmp(short int* a,char* b,int n)
+{
+	int i;
+
+	for(i=0;i<n;i++)
+	{
+		if(a[i]<b[i])
+			return -1;
+		if(b[i]<a[i])
+			return 1;
+	}
+	return 0;
+}
+
+/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
+ * into Unix UTC (based 1.1.1970, in seconds)
+ */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
+{
+/*
+ * This is very gross because
+ * 1: We must do 64-bit division on a 32-bit machine
+ * 2: We can't use libgcc for long long operations in the kernel
+ * 3: Floating point math in the kernel would corrupt user data
+ */
+	const unsigned int D = 10000000;
+	unsigned int H = (ntutc >> 32);
+	unsigned int L = (unsigned int)ntutc;
+	unsigned int numerator2;
+	unsigned int lowseconds;
+	unsigned int result;
+
+	/* It is best to subtract 0x019db1ded53e8000 first. */
+	/* Then the 1601-based date becomes a 1970-based date. */
+	if(L < (unsigned)0xd53e8000) H--;
+	L -= (unsigned)0xd53e8000;
+	H -= (unsigned)0x019db1de;
+
+	/*
+	 * Now divide 64-bit numbers on a 32-bit machine :-)
+	 * With the subtraction already done, the result fits in 32 bits.
+	 * The numerator fits in 56 bits and the denominator fits
+	 * in 24 bits, so we can shift by 8 bits to make this work.
+	 */
+
+	numerator2  = (H<<8) | (L>>24);
+	result      = (numerator2 / D);   /* shifted 24 right!! */
+	lowseconds  = result << 24;
+
+	numerator2  = ((numerator2-result*D)<<8) | ((L>>16)&0xff);
+	result      = (numerator2 / D);   /* shifted 16 right!! */
+	lowseconds |= result << 16;
+
+	numerator2  = ((numerator2-result*D)<<8) | ((L>>8)&0xff);
+	result      = (numerator2 / D);   /* shifted 8 right!! */
+	lowseconds |= result << 8;
+
+	numerator2  = ((numerator2-result*D)<<8) | (L&0xff);
+	result      = (numerator2 / D);   /* not shifted */
+	lowseconds |= result;
+
+	return lowseconds;
+}
+
+/* Convert the Unix UTC into NT UTC */
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
+{
+	return ((t + (ntfs_time64_t)(369*365+89)*24*3600) * 10000000);
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/ntfs/util.h linux/fs/ntfs/util.h
--- v2.1.73/linux/fs/ntfs/util.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ntfs/util.h	Fri Dec 19 15:24:21 1997
@@ -0,0 +1,63 @@
+/*
+ *  util.h
+ *  Header file for util.c
+ *
+ *  Copyright (C) 1997 Régis Duchesne
+ */
+
+/* Which character set is used for file names */
+/*  Translate everything to UTF-8 */
+#define nct_utf8             1
+/*  Translate to 8859-1 */
+#define nct_iso8859_1        2
+/*  Quote unprintables with : */
+#define nct_uni_xlate        4
+/*  Do that in the vfat way instead of the documented way */
+#define nct_uni_xlate_vfat   8
+/*  Use a mapping table to determine printables */
+#define nct_map              16
+
+/* The first 11 inodes correspond to special files */
+#define FILE_MFT      0
+#define FILE_MFTMIRR  1
+#define FILE_LOGFILE  2
+#define FILE_VOLUME   3
+#define FILE_ATTRDEF  4
+#define FILE_ROOT     5
+#define FILE_BITMAP   6
+#define FILE_BOOT     7
+#define FILE_BADCLUS  8
+#define FILE_QUOTA    9
+#define FILE_UPCASE  10
+
+/* Memory management */
+void *ntfs_calloc(int size);
+
+/* String operations */
+/*  Copy Unicode <-> ASCII */
+#if 0
+void ntfs_uni2ascii(char *to,char *from,int len);
+#endif
+void ntfs_ascii2uni(short int *to,char *from,int len);
+/*  Comparison */
+int ntfs_uni_strncmp(short int* a,short int *b,int n);
+int ntfs_ua_strncmp(short int* a,char* b,int n);
+
+/* Same address space copies */
+void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
+void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
+
+/* Charset conversion */
+int ntfs_encodeuni(ntfs_volume *vol,ntfs_u16 *in, int in_len,char **out, int *out_len);
+int ntfs_decodeuni(ntfs_volume *vol,char *in, int in_len, ntfs_u16 **out, int *out_len);
+
+/* Time conversion */
+/*  NT <-> Unix */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
diff -u --recursive --new-file v2.1.73/linux/fs/select.c linux/fs/select.c
--- v2.1.73/linux/fs/select.c	Wed Dec 10 11:12:45 1997
+++ linux/fs/select.c	Fri Dec 19 15:52:10 1997
@@ -197,7 +197,6 @@
 		free_page((unsigned long) wait_table.entry);
 	}
 out_nowait:
-	current->timeout = 0;
 	unlock_kernel();
 	return retval;
 }
diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c
--- v2.1.73/linux/fs/smbfs/dir.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/smbfs/dir.c	Fri Dec 19 10:53:29 1997
@@ -74,40 +74,6 @@
 	return -EISDIR;
 }
 
-/*
- * Check whether a dentry already exists for the given name,
- * and return the inode number if it has an inode.  This is
- * needed to keep getcwd() working.
- */
-static ino_t
-find_inode_number(struct dentry *dir, struct qstr *name)
-{
-	struct dentry * dentry;
-	ino_t ino = 0;
-
-	/*
-	 * Check for a fs-specific hash function. Note that we must
-	 * calculate the standard hash first, as the d_op->d_hash()
-	 * routine may choose to leave the hash value unchanged.
-	 */
-	name->hash = full_name_hash(name->name, name->len);
-	if (dir->d_op && dir->d_op->d_hash)
-	{
-		if (dir->d_op->d_hash(dir, name) != 0)
-			goto out;
-	}
-
-	dentry = d_lookup(dir, name);
-	if (dentry)
-	{
-		if (dentry->d_inode)
-			ino = dentry->d_inode->i_ino;
-		dput(dentry);
-	}
-out:
-	return ino;
-}
-
 static int 
 smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
@@ -193,7 +159,7 @@
 
 /*
  * Note: in order to allow the smbclient process to open the
- * mount point, we don't revalidate for the connection pid.
+ * mount point, we don't revalidate if conn_pid is NULL.
  */
 static int
 smb_dir_open(struct inode *dir, struct file *file)
@@ -218,8 +184,6 @@
 
 	if (server->conn_pid)
 		error = smb_revalidate_inode(dir);
-	else
-		printk("smb_dir_open: smbclient process\n");
 	return error;
 }
 
diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- v2.1.73/linux/fs/smbfs/inode.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/smbfs/inode.c	Fri Dec 19 10:53:29 1997
@@ -364,6 +364,7 @@
 	       kill_proc(server->conn_pid, SIGTERM, 1);
 
 	kfree(server->mnt);
+	kfree(sb->u.smbfs_sb.temp_buf);
 	if (server->packet)
 		smb_vfree(server->packet);
 	sb->s_dev = 0;
@@ -376,18 +377,16 @@
 struct super_block *
 smb_read_super(struct super_block *sb, void *raw_data, int silent)
 {
-	struct smb_mount_data *mnt, *data = (struct smb_mount_data *) raw_data;
-	struct smb_fattr root;
-	kdev_t dev = sb->s_dev;
+	struct smb_mount_data *mnt;
 	struct inode *root_inode;
 	struct dentry *dentry;
+	struct smb_fattr root;
 
 	MOD_INC_USE_COUNT;
 
-	if (!data)
+	if (!raw_data)
 		goto out_no_data;
-		
-	if (data->version != SMB_MOUNT_VERSION)
+	if (((struct smb_mount_data *) raw_data)->version != SMB_MOUNT_VERSION)
 		goto out_wrong_data;
 
 	lock_super(sb);
@@ -396,7 +395,6 @@
 	sb->s_blocksize_bits = 10;
 	sb->s_magic = SMB_SUPER_MAGIC;
 	sb->s_flags = 0;
-	sb->s_dev = dev; /* shouldn't need this ... */
 	sb->s_op = &smb_sops;
 
 	sb->u.smbfs_sb.sock_file = NULL;
@@ -410,10 +408,16 @@
 	if (!sb->u.smbfs_sb.packet)
 		goto out_no_mem;
 
+	/* Allocate the global temp buffer */
+	sb->u.smbfs_sb.temp_buf = kmalloc(SMB_MAXPATHLEN + 20, GFP_KERNEL);
+	if (!sb->u.smbfs_sb.temp_buf)
+		goto out_no_temp;
+
+	/* Allocate the mount data structure */
 	mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
 	if (!mnt)
 		goto out_no_mount;
-	*mnt = *data;
+	*mnt = *((struct smb_mount_data *) raw_data);
 	/* ** temp ** pass config flags in file mode */
 	mnt->version = (mnt->file_mode >> 9);
 #ifdef CONFIG_SMB_WIN95
@@ -450,15 +454,14 @@
 	return sb;
 
 out_no_root:
-	printk(KERN_ERR "smb_read_super: get root inode failed\n");
 	iput(root_inode);
 	kfree(sb->u.smbfs_sb.mnt);
 out_no_mount:
+	kfree(sb->u.smbfs_sb.temp_buf);
+out_no_temp:
 	smb_vfree(sb->u.smbfs_sb.packet);
-	goto out_unlock;
 out_no_mem:
-	printk("smb_read_super: could not alloc packet\n");
-out_unlock:
+	printk(KERN_ERR "smb_read_super: allocation failure\n");
 	unlock_super(sb);
 	goto out_fail;
 out_wrong_data:
diff -u --recursive --new-file v2.1.73/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
--- v2.1.73/linux/fs/smbfs/proc.c	Fri Dec 19 15:53:01 1997
+++ linux/fs/smbfs/proc.c	Fri Dec 19 10:53:29 1997
@@ -975,7 +975,7 @@
 smb_close_fileid(struct dentry *dentry, __u16 fileid)
 {
 	struct smb_sb_info *server = server_from_dentry(dentry);
-	int result = 0;
+	int result;
 
 	smb_lock_server(server);
 	result = smb_proc_close(server, fileid, CURRENT_TIME);
@@ -1270,6 +1270,9 @@
 /*
  * Note that we are now returning the name as a reference to avoid
  * an extra copy, and that the upper/lower casing is done in place.
+ *
+ * Bugs Noted:
+ * (1) Pathworks servers may pad the name with extra spaces.
  */
 static __u8 *
 smb_decode_dirent(struct smb_sb_info *server, __u8 *p, 
@@ -1442,10 +1445,6 @@
  *
  * Bugs Noted:
  * (1) Win NT 4.0 appends a null byte to names and counts it in the length!
- * (2) When using Info Level 1 Win NT 4.0 truncates directory listings 
- * for certain patterns of names and/or lengths. The breakage pattern is
- * completely reproducible and can be toggled by the addition of a single
- * file to the directory. (E.g. echo hi >foo breaks, rm -f foo works.)
  */
 static char *
 smb_decode_long_dirent(struct smb_sb_info *server, char *p,
@@ -1509,24 +1508,29 @@
 	return result;
 }
 
+/*
+ * Bugs Noted:
+ * (1) When using Info Level 1 Win NT 4.0 truncates directory listings 
+ * for certain patterns of names and/or lengths. The breakage pattern
+ * is completely reproducible and can be toggled by the creation of a
+ * single file. (E.g. echo hi >foo breaks, rm -f foo works.)
+ */
 static int
 smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
 		      void *cachep)
 {
+	char *p, *mask, *lastname, *param = server->temp_buf;
+	__u16 command;
+	int first, entries, entries_seen;
+
 	/* Both NT and OS/2 accept info level 1 (but see note below). */
 	int info_level = 1;
 	const int max_matches = 512;
 
-	char *p, *mask, *lastname;
-	int first, entries, entries_seen;
-
 	unsigned char *resp_data = NULL;
 	unsigned char *resp_param = NULL;
 	int resp_data_len = 0;
 	int resp_param_len = 0;
-
-	__u16 command;
-
 	int ff_resume_key = 0; /* this isn't being used */
 	int ff_searchcount = 0;
 	int ff_eos = 0;
@@ -1534,8 +1538,6 @@
 	int ff_dir_handle = 0;
 	int loop_count = 0;
 	int mask_len, i, result;
-
-	char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */
 	static struct qstr star = { "*", 1, 0 };
 
 	/*
@@ -1553,7 +1555,7 @@
 	/*
 	 * Encode the initial path
 	 */
-	mask = &(param[12]);
+	mask = param + 12;
 	mask_len = smb_encode_path(server, mask, dir, &star) - mask;
 	first = 1;
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -1643,11 +1645,6 @@
 			entries = -smb_errno(server);
 			break;
 		}
-#ifdef SMBFS_PARANOIA
-if (resp_data + resp_data_len > server->packet + server->packet_size)
-printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n",
-resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size);
-#endif
 
 		/* parse out some important return info */
 		if (first != 0)
@@ -1799,15 +1796,14 @@
 smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
 			struct smb_fattr *attr)
 {
-	char *p;
-	int result;
+	char *p, *param = server->temp_buf;
 	__u16 date, time;
 	int off_date = 0, off_time = 2;
 	unsigned char *resp_data = NULL;
 	unsigned char *resp_param = NULL;
 	int resp_data_len = 0;
 	int resp_param_len = 0;
-	char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */
+	int result;
 
       retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
@@ -2023,14 +2019,12 @@
 			struct dentry *dir, struct smb_fattr *fattr)
 {
 	__u16 date, time;
-	char *p;
-	int result;
-
+	char *p, *param = server->temp_buf;
 	unsigned char *resp_data = NULL;
 	unsigned char *resp_param = NULL;
 	int resp_data_len = 0;
 	int resp_param_len = 0;
-	char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */
+	int result;
 	char data[26];
 
       retry:
@@ -2082,7 +2076,8 @@
  * Bugs Noted:
  * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message 
  * with info level 1 (INFO_STANDARD).
- * (2) Under the core protocol apparently the only way to set the
+ * (2) Win 95 seems not to support setting directory timestamps.
+ * (3) Under the core protocol apparently the only way to set the
  * timestamp is to open and close the file.
  */
 int
@@ -2092,7 +2087,7 @@
 	struct inode *inode = dentry->d_inode;
 	int result;
 
-#ifdef SMBFS_DEBUG_TIMESTAMP
+#ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_proc_settime: setting %s/%s, open=%d\n", 
 dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode));
 #endif
@@ -2127,16 +2122,6 @@
 		}
 	}
 
-#if 1 	/* temporary */
-	if (result)
-	{
-printk("smb_proc_settime: %s/%s failed, open=%d, res=%d, rcls=%d, err=%d\n", 
-dentry->d_parent->d_name.name, dentry->d_name.name, smb_is_open(inode),
-result, server->rcls, server->err);
-		/* squash errors for now */
-		result = 0;
-	}
-#endif
 	smb_unlock_server(server);
 	return result;
 }
diff -u --recursive --new-file v2.1.73/linux/include/linux/cdk.h linux/include/linux/cdk.h
--- v2.1.73/linux/include/linux/cdk.h	Mon Mar 17 14:54:34 1997
+++ linux/include/linux/cdk.h	Fri Dec 19 12:30:54 1997
@@ -304,6 +304,10 @@
 #define	P_DTRFOLLOW	0x20
 #define	P_FAKEDCD	0x40
 
+#define	P_RXIMIN	0x10000
+#define	P_RXITIME	0x20000
+#define	P_RXTHOLD	0x40000
+
 /*
  *	Define a structure to communicate serial port signal and data state
  *	information.
diff -u --recursive --new-file v2.1.73/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.73/linux/include/linux/fs.h	Fri Dec 19 15:53:04 1997
+++ linux/include/linux/fs.h	Fri Dec 19 15:24:22 1997
@@ -256,6 +256,7 @@
 #include <linux/minix_fs_i.h>
 #include <linux/ext2_fs_i.h>
 #include <linux/hpfs_fs_i.h>
+#include <linux/ntfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
 #include <linux/umsdos_fs_i.h>
 #include <linux/iso_fs_i.h>
@@ -355,6 +356,7 @@
 		struct minix_inode_info		minix_i;
 		struct ext2_inode_info		ext2_i;
 		struct hpfs_inode_info		hpfs_i;
+		struct ntfs_inode_info          ntfs_i;
 		struct msdos_inode_info		msdos_i;
 		struct umsdos_inode_info	umsdos_i;
 		struct iso_inode_info		isofs_i;
@@ -492,6 +494,7 @@
 #include <linux/minix_fs_sb.h>
 #include <linux/ext2_fs_sb.h>
 #include <linux/hpfs_fs_sb.h>
+#include <linux/ntfs_fs_sb.h>
 #include <linux/msdos_fs_sb.h>
 #include <linux/iso_fs_sb.h>
 #include <linux/nfs_fs_sb.h>
@@ -526,6 +529,7 @@
 		struct minix_sb_info	minix_sb;
 		struct ext2_sb_info	ext2_sb;
 		struct hpfs_sb_info	hpfs_sb;
+		struct ntfs_sb_info     ntfs_sb;
 		struct msdos_sb_info	msdos_sb;
 		struct isofs_sb_info	isofs_sb;
 		struct nfs_sb_info	nfs_sb;
diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs.h linux/include/linux/ntfs_fs.h
--- v2.1.73/linux/include/linux/ntfs_fs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/ntfs_fs.h	Fri Dec 19 15:24:22 1997
@@ -0,0 +1,7 @@
+#ifndef _LINUX_NTFS_FS_H
+#define _LINUX_NTFS_FS_H
+
+int init_ntfs_fs();
+
+#endif
+
diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs_i.h linux/include/linux/ntfs_fs_i.h
--- v2.1.73/linux/include/linux/ntfs_fs_i.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/ntfs_fs_i.h	Fri Dec 19 15:24:22 1997
@@ -0,0 +1,79 @@
+#ifndef _LINUX_NTFS_FS_I_H
+#define _LINUX_NTFS_FS_I_H
+
+/* Forward declarations, to keep number of mutual includes low */
+struct ntfs_attribute;
+struct ntfs_sb_info;
+
+/* Duplicate definitions from ntfs/types.h */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef unsigned char      ntfs_u8;
+typedef unsigned short     ntfs_u16;
+typedef unsigned int       ntfs_u32;
+typedef unsigned long long ntfs_u64;
+#endif
+
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef __kernel_uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef __kernel_gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+
+/* unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef unsigned short     ntfs_wchar_t;
+#endif
+/* file offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef unsigned long long ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef unsigned long long ntfs_time64_t;
+#endif
+/* This is really unsigned long long. So we support only volumes up to 2 TB */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef unsigned int ntfs_cluster_t;
+#endif
+
+/* Definition of NTFS in-memory inode structure */
+struct ntfs_inode_info{
+	struct ntfs_sb_info *vol;
+	int i_number;                /* should be really 48 bits */
+	unsigned sequence_number;
+	unsigned char* attr;         /* array of the attributes */
+	int attr_count;              /* size of attrs[] */
+	struct ntfs_attribute *attrs;
+	int record_count;            /* size of records[] */
+	/* array of the record numbers of the MFT 
+	   whose attributes have been inserted in the inode */
+	int *records;
+	union{
+		struct{
+			int recordsize;
+			int clusters_per_record;
+		}index;
+	} u;	
+};
+
+#endif
diff -u --recursive --new-file v2.1.73/linux/include/linux/ntfs_fs_sb.h linux/include/linux/ntfs_fs_sb.h
--- v2.1.73/linux/include/linux/ntfs_fs_sb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/ntfs_fs_sb.h	Fri Dec 19 15:24:22 1997
@@ -0,0 +1,44 @@
+#ifndef _LINUX_NTFS_FS_SB_H
+#define _LINUX_NTFS_FS_SB_H
+
+struct ntfs_sb_info{
+	/* Configuration provided by user at mount time */
+	ntfs_uid_t uid;
+	ntfs_gid_t gid;
+	ntmode_t umask;
+        unsigned int nct;
+	void *nls_map;
+	unsigned int ngt;
+	/* Configuration provided by user with ntfstools */
+	ntfs_size_t partition_bias;	/* for access to underlying device */
+	/* Attribute definitions */
+	ntfs_u32 at_standard_information;
+	ntfs_u32 at_attribute_list;
+	ntfs_u32 at_file_name;
+	ntfs_u32 at_security_descriptor;
+	ntfs_u32 at_data;
+	ntfs_u32 at_index_root;
+	ntfs_u32 at_index_allocation;
+	ntfs_u32 at_bitmap;
+	ntfs_u32 at_symlink; /* aka SYMBOLIC_LINK or REPARSE_POINT */
+	/* Data read from the boot file */
+	int blocksize;
+	int clusterfactor;
+	int clustersize;
+	int mft_recordsize;
+	int mft_clusters_per_record;
+	int index_recordsize;
+	int index_clusters_per_record;
+	int mft_cluster;
+	/* data read from special files */
+	unsigned char *mft;
+	unsigned short *upcase;
+	unsigned int upcase_length;
+	/* inodes we always hold onto */
+	struct ntfs_inode_info *mft_ino;
+	struct ntfs_inode_info *mftmirr;
+	struct ntfs_inode_info *bitmap;
+	struct super_block *sb;
+};
+
+#endif
diff -u --recursive --new-file v2.1.73/linux/include/linux/sc26198.h linux/include/linux/sc26198.h
--- v2.1.73/linux/include/linux/sc26198.h	Mon Mar 17 14:54:35 1997
+++ linux/include/linux/sc26198.h	Fri Dec 19 12:30:54 1997
@@ -28,15 +28,15 @@
 /*
  *	Define the number of async ports per sc26198 uart device.
  */
-#define	SC26198_PORTS	8
+#define	SC26198_PORTS		8
 
 /*
  *	Baud rate timing clocks. All derived from a master 14.7456 MHz clock.
  */
-#define	SC26198_MASTERCLOCK		14745600L
-#define	SC26198_DCLK			(SC26198_MASTERCLOCK)
-#define	SC26198_CCLK			(SC26198_MASTERCLOCK / 2)
-#define	SC26198_BCLK			(SC26198_MASTERCLOCK / 4)
+#define	SC26198_MASTERCLOCK	14745600L
+#define	SC26198_DCLK		(SC26198_MASTERCLOCK)
+#define	SC26198_CCLK		(SC26198_MASTERCLOCK / 2)
+#define	SC26198_BCLK		(SC26198_MASTERCLOCK / 4)
 
 /*
  *	Define internal FIFO sizes for the 26198 ports.
diff -u --recursive --new-file v2.1.73/linux/include/linux/smb_fs_sb.h linux/include/linux/smb_fs_sb.h
--- v2.1.73/linux/include/linux/smb_fs_sb.h	Sat Nov  1 11:04:27 1997
+++ linux/include/linux/smb_fs_sb.h	Fri Dec 19 10:53:29 1997
@@ -24,6 +24,7 @@
 	struct file * sock_file;
 
         struct smb_mount_data *mnt;
+        unsigned char *temp_buf;
 
 	/* Connections are counted. Each time a new socket arrives,
 	 * generation is incremented.
diff -u --recursive --new-file v2.1.73/linux/include/linux/stallion.h linux/include/linux/stallion.h
--- v2.1.73/linux/include/linux/stallion.h	Mon Mar 17 14:54:35 1997
+++ linux/include/linux/stallion.h	Fri Dec 19 12:30:54 1997
@@ -131,6 +131,8 @@
 	void		(*isr)(struct stlbrd *brdp);
 	unsigned int	ioaddr1;
 	unsigned int	ioaddr2;
+	unsigned int	iosize1;
+	unsigned int	iosize2;
 	unsigned int	iostatus;
 	unsigned int	ioctrl;
 	unsigned int	ioctrlval;
diff -u --recursive --new-file v2.1.73/linux/include/linux/sunrpc/svcsock.h linux/include/linux/sunrpc/svcsock.h
--- v2.1.73/linux/include/linux/sunrpc/svcsock.h	Thu Jun 12 15:30:33 1997
+++ linux/include/linux/sunrpc/svcsock.h	Fri Dec 19 10:54:10 1997
@@ -49,7 +49,7 @@
 /*
  * Function prototypes.
  */
-int		svc_create_socket(struct svc_serv *, int, struct sockaddr_in *);
+int		svc_makesock(struct svc_serv *, int, unsigned short);
 void		svc_delete_socket(struct svc_sock *);
 int		svc_recv(struct svc_serv *, struct svc_rqst *);
 int		svc_send(struct svc_rqst *);
diff -u --recursive --new-file v2.1.73/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.73/linux/net/ipv4/ip_fw.c	Mon Dec  1 12:04:16 1997
+++ linux/net/ipv4/ip_fw.c	Fri Dec 19 11:03:31 1997
@@ -152,9 +152,12 @@
 struct ip_fw *ip_fw_in_chain;
 struct ip_fw *ip_fw_out_chain;
 struct ip_fw *ip_acct_chain;
+struct ip_fw *ip_masq_chain;
 
 static struct ip_fw **chains[] =
-	{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain};
+	{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain,
+	 &ip_masq_chain
+	};
 #endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
  
 #ifdef CONFIG_IP_FIREWALL
diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.73/linux/net/sunrpc/clnt.c	Mon Dec  1 12:04:18 1997
+++ linux/net/sunrpc/clnt.c	Fri Dec 19 10:54:10 1997
@@ -753,8 +753,10 @@
 		rpc_exit(task, error);
 		return NULL;
 	}
-	if (!(p = rpcauth_checkverf(task, p)))
+	if (!(p = rpcauth_checkverf(task, p))) {
+		printk("call_verify: auth check failed\n");
 		goto garbage;		/* bad verifier, retry */
+	}
 	switch ((n = ntohl(*p++))) {
 	case RPC_SUCCESS:
 		return p;
@@ -768,7 +770,8 @@
 garbage:
 	dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
 	task->tk_client->cl_stats->rpcgarbage++;
-	if (0 && task->tk_garb_retry--) {
+	if (task->tk_garb_retry--) {
+		printk("RPC: garbage, retrying %4d\n", task->tk_pid);
 		task->tk_action = call_encode;
 		return NULL;
 	}
diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- v2.1.73/linux/net/sunrpc/sched.c	Tue Dec  2 16:45:21 1997
+++ linux/net/sunrpc/sched.c	Fri Dec 19 10:54:10 1997
@@ -388,6 +388,8 @@
 			/* sync task: sleep here */
 			dprintk("RPC: %4d sync task going to sleep\n",
 							task->tk_pid);
+			if (current->pid == rpciod_pid)
+				printk("RPC: rpciod waiting on sync task!\n");
 			current->timeout = 0;
 			sleep_on(&task->tk_wait);
 
@@ -792,29 +794,21 @@
 rpciod_killall(void)
 {
 	unsigned long flags;
-	sigset_t old_set;
-
-	/* FIXME: What had been going on before was saving and restoring 
-	   current->signal.  This as opposed to blocking signals?  Do we
-	   still need them to wake up out of schedule?  In any case it 
-	   isn't playing nice and a better way should be found.  */
-
-	spin_lock_irqsave(&current->sigmask_lock, flags);
-	old_set = current->blocked;
-	sigfillset(&current->blocked);
-	recalc_sigpending(current);
-	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
 	while (all_tasks) {
+		current->sigpending = 0;
 		rpc_killall_tasks(NULL);
 		__rpc_schedule();
-		current->timeout = jiffies + HZ / 100;
-		need_resched = 1;
-		schedule();
+		if (all_tasks) {
+printk("rpciod_killall: waiting for tasks to exit\n");
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + 1;
+			schedule();
+			current->timeout = 0;
+		}
 	}
 
 	spin_lock_irqsave(&current->sigmask_lock, flags);
-	current->blocked = old_set;
 	recalc_sigpending(current);
 	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 }
diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/sunrpc_syms.c linux/net/sunrpc/sunrpc_syms.c
--- v2.1.73/linux/net/sunrpc/sunrpc_syms.c	Mon Dec  1 12:04:18 1997
+++ linux/net/sunrpc/sunrpc_syms.c	Fri Dec 19 10:54:10 1997
@@ -62,7 +62,6 @@
 
 /* RPC server stuff */
 EXPORT_SYMBOL(svc_create);
-EXPORT_SYMBOL(svc_create_socket);
 EXPORT_SYMBOL(svc_create_thread);
 EXPORT_SYMBOL(svc_exit_thread);
 EXPORT_SYMBOL(svc_destroy);
@@ -70,6 +69,7 @@
 EXPORT_SYMBOL(svc_process);
 EXPORT_SYMBOL(svc_recv);
 EXPORT_SYMBOL(svc_wake_up);
+EXPORT_SYMBOL(svc_makesock);
 
 /* RPC statistics */
 #ifdef CONFIG_PROC_FS
diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c
--- v2.1.73/linux/net/sunrpc/svc.c	Fri Dec 19 15:53:06 1997
+++ linux/net/sunrpc/svc.c	Fri Dec 19 10:54:10 1997
@@ -172,7 +172,6 @@
 {
 	struct svc_program	*progp;
 	unsigned long		flags;
-	sigset_t		old_set;
 	int			i, error = 0, dummy;
 
 	progp = serv->sv_program;
@@ -180,18 +179,8 @@
 	dprintk("RPC: svc_register(%s, %s, %d)\n",
 		progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port);
 
-	/* FIXME: What had been going on before was saving and restoring 
-	   current->signal.  This as opposed to blocking signals?  Do we
-	   still need them to wake up out of schedule?  In any case it 
-	   isn't playing nice and a better way should be found.  */
-
-	if (!port) {
-		spin_lock_irqsave(&current->sigmask_lock, flags);
-		old_set = current->blocked;
-		sigfillset(&current->blocked);
-		recalc_sigpending(current);
-		spin_unlock_irqrestore(&current->sigmask_lock, flags);
-	}
+	if (!port)
+		current->sigpending = 0;
 
 	for (i = 0; i < progp->pg_nvers; i++) {
 		if (progp->pg_vers[i] == NULL)
@@ -207,7 +196,6 @@
 
 	if (!port) {
 		spin_lock_irqsave(&current->sigmask_lock, flags);
-		current->blocked = old_set;
 		recalc_sigpending(current);
 		spin_unlock_irqrestore(&current->sigmask_lock, flags);
 	}
@@ -235,7 +223,7 @@
 	bufp = argp->buf;
 
 	if (argp->len < 5)
-		goto dropit;
+		goto err_short_len;
 
 	dir  = ntohl(*bufp++);
 	vers = ntohl(*bufp++);
@@ -244,10 +232,8 @@
 	svc_putlong(resp, xdr_one);		/* REPLY */
 	svc_putlong(resp, xdr_zero);		/* ACCEPT */
 
-	if (dir != 0) {		/* direction != CALL */
-		serv->sv_stats->rpcbadfmt++;
-		goto dropit;			/* drop request */
-	}
+	if (dir != 0)		/* direction != CALL */
+		goto err_bad_dir;
 	if (vers != 2)		/* RPC version number */
 		goto err_bad_rpc;
 
@@ -281,7 +267,7 @@
 
 	procp = versp->vs_proc + proc;
 	if (proc >= versp->vs_nproc || !procp->pc_func)
-		goto err_unknown;
+		goto err_bad_proc;
 	rqstp->rq_server   = serv;
 	rqstp->rq_procinfo = procp;
 
@@ -329,21 +315,36 @@
 	if (procp->pc_release)
 		procp->pc_release(rqstp, NULL, rqstp->rq_resp);
 
-	if (procp->pc_encode != NULL)
-		return svc_send(rqstp);
+	if (procp->pc_encode == NULL)
+		goto dropit;
+sendit:
+	return svc_send(rqstp);
 
 dropit:
 	dprintk("svc: svc_process dropit\n");
 	svc_drop(rqstp);
 	return 0;
 
+err_short_len:
+#ifdef RPC_PARANOIA
+	printk("svc: short len %d, dropping request\n", argp->len);
+#endif
+	goto dropit;			/* drop request */
+
+err_bad_dir:
+#ifdef RPC_PARANOIA
+	printk("svc: bad direction %d, dropping request\n", dir);
+#endif
+	serv->sv_stats->rpcbadfmt++;
+	goto dropit;			/* drop request */
+
 err_bad_rpc:
 	serv->sv_stats->rpcbadfmt++;
 	resp->buf[-1] = xdr_one;	/* REJECT */
 	svc_putlong(resp, xdr_zero);	/* RPC_MISMATCH */
 	svc_putlong(resp, xdr_two);	/* Only RPCv2 supported */
 	svc_putlong(resp, xdr_two);
-	goto error;
+	goto sendit;
 
 err_bad_auth:
 	dprintk("svc: authentication failed (%ld)\n", ntohl(auth_stat));
@@ -351,7 +352,7 @@
 	resp->buf[-1] = xdr_one;	/* REJECT */
 	svc_putlong(resp, xdr_one);	/* AUTH_ERROR */
 	svc_putlong(resp, auth_stat);	/* status */
-	goto error;
+	goto sendit;
 
 err_bad_prog:
 #ifdef RPC_PARANOIA
@@ -359,7 +360,7 @@
 #endif
 	serv->sv_stats->rpcbadfmt++;
 	svc_putlong(resp, rpc_prog_unavail);
-	goto error;
+	goto sendit;
 
 err_bad_vers:
 #ifdef RPC_PARANOIA
@@ -369,15 +370,15 @@
 	svc_putlong(resp, rpc_prog_mismatch);
 	svc_putlong(resp, htonl(progp->pg_lovers));
 	svc_putlong(resp, htonl(progp->pg_hivers));
-	goto error;
+	goto sendit;
 
-err_unknown:
+err_bad_proc:
 #ifdef RPC_PARANOIA
 	printk("svc: unknown procedure (%d)\n", proc);
 #endif
 	serv->sv_stats->rpcbadfmt++;
 	svc_putlong(resp, rpc_proc_unavail);
-	goto error;
+	goto sendit;
 
 err_garbage:
 #ifdef RPC_PARANOIA
@@ -385,7 +386,5 @@
 #endif
 	serv->sv_stats->rpcbadfmt++;
 	svc_putlong(resp, rpc_garbage_args);
-
-error:
-	return svc_send(rqstp);
+	goto sendit;
 }
diff -u --recursive --new-file v2.1.73/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c
--- v2.1.73/linux/net/sunrpc/svcsock.c	Tue Dec  2 09:49:41 1997
+++ linux/net/sunrpc/svcsock.c	Fri Dec 19 10:54:10 1997
@@ -886,7 +886,7 @@
 /*
  * Create socket for RPC service.
  */
-int
+static int
 svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin)
 {
 	struct svc_sock	*svsk;
@@ -969,3 +969,19 @@
 		/* svsk->sk_server = NULL; */
 	}
 }
+
+/*
+ * Make a socket for nfsd and lockd
+ */
+int
+svc_makesock(struct svc_serv *serv, int protocol, unsigned short port)
+{
+	struct sockaddr_in	sin;
+
+	dprintk("svc: creating socket proto = %d\n", protocol);
+	sin.sin_family      = AF_INET;
+	sin.sin_addr.s_addr = INADDR_ANY;
+	sin.sin_port        = htons(port);
+	return svc_create_socket(serv, protocol, &sin);
+}
+
